跳至主要內容
返回策略研究回測方法論

回測的三個陷阱:倖存者偏差、前視偏差、過度配適

這三類問題不會讓回測報錯,只會讓它變好看。它們的共同點是:錯誤發生在資料與流程裡,而結果依然是一條平滑上揚的曲線。

發布於 2026/06/30閱讀 12 分鐘倖存者偏差前視偏差過度配適樣本外

一個能跑通的回測和一個可信的回測之間,隔著三類系統性錯誤。它們不會拋例外,不會讓曲線崩掉,恰恰相反——它們全都會讓結果變得更漂亮。這也是它們難以被自己發現的原因。

一、倖存者偏差:只在活下來的公司上做測試

假設你用「當前標普 500 成分股」的清單去回測一套 2015 年至今的策略。這份清單裡的每一家公司,都通過了一道極強的篩選:它們在 2026 年還在指數裡。破產的、被併購的、因為市值萎縮被剔除的,全都不在名單上。

於是你實際測的不是「一套策略在美股上的表現」,而是「一套策略在已知會成功的公司上的表現」。這兩件事的差距不是幾個百分點。美國上市公司的歷史下市率相當可觀,大量標的因為併購、私有化、破產、不符合上市條件而退出交易;把它們排除掉,等於事先剔除了策略最應該被檢驗的那部分樣本——恰恰是停損規則要處理的那部分。

怎麼避免: 標的池必須包含在回測區間內存在過、但如今已下市的標的,並且要用它們下市當時的真實價格序列,而不是把序列在下市日截斷後當作正常結束。我們的標的庫保留已下市標的且不做倖存者過濾,行情介面在回傳時會明確標註該標的的下市狀態。

一個反直覺的推論: 倖存者偏差對買進並持有基準的抬高,往往比對策略本身更大。所以在有偏樣本上,策略的超額報酬會被系統性低估——它同時汙染了兩邊,只是汙染程度不同。

二、前視偏差:用了當時還不知道的資訊

前視偏差(look-ahead bias)比倖存者偏差隱蔽得多,因為它常常只差一格。

最常見的一格:用今天的資料算今天的訊號。 例如「突破 22 日新高進場」,如果 22 日最高價的視窗包含了今天,那麼「今天收盤價 ≥ 22 日最高價」這個條件幾乎恆成立——今天的最高價本來就在視窗裡。正確的寫法是視窗只到昨天為止:

hh = rolling_max(high, 22)
prev_hh = np.roll(hh, 1)      # 向後挪一格,視窗截止到昨日
prev_hh[0] = np.nan
entry = (close > ma) & (close >= prev_hh)

這一格的差別,能把一套毫無價值的規則變成一條完美的報酬曲線。

其他幾種常見型態:

  • 用收盤價決策、又用收盤價成交。 嚴格說來,你要等到收盤才知道收盤價,因此不可能以該價格成交。可接受的近似是收盤價決策、隔日開盤成交;如果堅持用收盤價成交,至少要在滑價裡為此留出餘裕。
  • 還原因子的追溯改寫。 後還原序列會在每次配息、分割之後被整體改寫。用今天下載的還原序列回測三年前的策略,等於用了三年前不存在的調整資訊。
  • 資料修訂。 財務資料、成交量、甚至部分交易所的日線都可能事後被修正。回測裡用的是修正後的版本,實單當時拿到的是修正前的。
  • 指數成分的生效時間。 「納入標普 500」是先公告、後生效的。用生效日之前的日期去假設你已經知道這件事,就是把公告的價格效應白送給了策略。

怎麼自查: 對每一個指標問一句「計算它的那一刻,這個數在現實裡已經存在了嗎」。這一句能擋掉九成的前視偏差。

三、過度配適:把雜訊當成規律

這是三者中最難對付的,因為它不是一個 bug,而是一個流程問題——每一步看起來都很合理。

吊燈停損策略有六個可調參數。假設你只在一個不算大的網格上搜尋:

sma_period      6 個取值
breakout_period 5 個取值
atr_period      3 個取值
entry_mult      6 個取值
exit_mult       5 個取值
adx_threshold   4 個取值
--------------------------
組合數 = 6 × 5 × 3 × 6 × 5 × 4 = 10800

一萬多個組合裡必然有一個表現最好。問題在於:即使底層資料是純粹的隨機漫步,最好的那個組合看起來也會相當漂亮。 你搜尋的組合越多,「最佳結果」中來自運氣的成分就越大。當你報告最佳組合的報酬率時,你報告的是一個被挑選過的極值,而不是一個期望。

更隱蔽的形式是「手工過度配適」:你沒有跑網格搜尋,只是看了結果不滿意,把 SMA 從 100 改成 120,好一點;再改成 150,更好一點。這與自動搜尋沒有本質區別,只是你沒有記錄嘗試次數,因此連自己搜尋了多少次都不知道。

四條能實際降低過度配適的做法:

  1. 留出樣本外區間。 在前 70% 的資料上選參數,在後 30% 上只跑一次。如果樣本外的結果顯著變差,代表選出的是雜訊。注意「只跑一次」是字面意思——看過樣本外結果再回去調參,樣本外就變成樣本內了。
  2. 要平台,不要尖峰。 把參數的鄰域一起看:如果 sma_period 取 100 時年化 24%,取 90 和 110 時只有 8%,這個 24% 是運氣。真實的規律在參數空間裡表現為一片平緩的高地,而不是一根針。
  3. 減少參數,而不是增加。 每加一個過濾器(ADX、成交量、星期效應)都會讓樣本內結果變好,也都會讓樣本外結果變差得更快。一個參數需要被加入的門檻,應該是「它有一個說得通的市場機制」,而不是「加了以後曲線更好看」。
  4. 跨標的驗證。 同一組參數在 20 個標的上跑,看結論是否一致。只在一個標的上成立的參數,是那個標的這段歷史的特徵,不是策略的特徵。

四、還有幾件同樣會讓結論失真的事

  • 交易成本與滑價。 一套年化 15% 的策略,如果一年交易 200 次,單邊成本 0.1% 就吃掉 20 個百分點。高週轉策略必須把成本當作核心參數而不是事後修正項。
  • 資料口徑混用。 用單一交易所口徑的收盤價算突破、用合併口徑算成交量過濾,這種混搭會產生現實中不存在的訊號。實測兩種口徑的收盤價偏差可達 7%,詳見《合併成交量與單場成交量》。
  • 回測區間太短。 一段只涵蓋單邊上漲的歷史,測不出任何風控規則的價值。我們的合併日線自 2024-07-01 起可用,這是資料源的客觀邊界;在這樣的區間上得到的結論,其適用範圍也就到這裡為止——把它說成「五年驗證」是不誠實的。
  • 倖存的是策略本身。 你今天在研究趨勢追蹤,是因為它在過去幾十年被寫進了很多書。那些同樣被認真研究過、但沒有效果的策略,你根本沒機會聽說。這一層偏差無法用資料修正,只能靠意識到它的存在來打折。

收尾:一個不太舒服的標準

判斷一個回測結論是否可信,有一個簡單粗暴的檢驗:把你為了得到這個結論所做過的全部嘗試次數寫下來。 如果寫不出來,或者寫出來發現是幾百次,那麼這條結論的可信度遠低於你以為的水準。

回測的價值不在於找到最好的參數,而在於排除掉明顯行不通的想法。把它當成一個證偽工具,它非常有用;把它當成一個尋優工具,它幾乎必然會騙你。

本文僅用於量化技術研究與教育目的,不構成任何投資建議。歷史回測結果不代表未來報酬,交易風險由投資人自行承擔。

把文章裡的結論自己跑一遍

文中每一條說法都可以在回測工具裡重現。訪客無需註冊即可試用。