你沒有跑一百萬次回測。你只跑了三十次。但如果那三十次裡的每一個決策都是看著績效做的,你的有效試驗次數仍然是一百萬。
多數人對選擇偏誤的理解停在一句話:試了很多組參數,只報最好那組。那是六個入口裡最顯眼、也最好補的一個。剩下五個藏在你根本不覺得自己在「選」的地方:標的宇宙怎麼定義、特徵怎麼寫、樣本從哪一天開始算、哪些變體進得了最終組合,以及那支你從 GitHub 抄下來的策略。它們共同的特徵是不留痕跡,等到實盤把答案揭曉時,你已經無法重建當時的決策過程。
這篇文章逐一拆這六個入口。每個入口給三樣東西:它怎麼在你不知情的狀況下發生、你今天就能跑的自我偵測方法、以及試驗次數該怎麼算。機制層的部分(多重測試如何把假陽性機率灌大、FWER 與 FDR 與 SPA 與 DSR 各管哪一塊)在選擇偏誤與多重測試那篇拆過,這裡不重複,只談入口與稽核。
先立一個判準:選擇偏誤來自決策依賴資料,不是來自次數
多數人稽核自己的研究流程時,問的是「我跑了幾次回測」。這個問題問錯了。
Gelman 與 Loken 在 The Garden of Forking Paths 裡把這件事講得最清楚:研究者不需要真的跑遍一百萬條路徑,只要他的分析選擇取決於看到的資料,那一百萬條路徑就全都要算進去。
因為如果資料長得不一樣,他會走另一條路。這片森林是他事實上的搜尋空間,即使他本人只走了一條路。
放進量化研究,判準可以收成三個問題:
- 這個決策是在看到任何績效數字之前做的嗎
- 如果資料長得不一樣,我會做同樣的決策嗎
- 我現在能不能誠實重建當時考慮過、但放棄掉的選項
三題有任何一題答不出來,那個決策就是一次選擇,要進計數器。Simmons、Nelson 與 Simonsohn 在 False-Positive Psychology 裡做過一次量化示範:只要研究者保留四種相當常見的分析彈性(樣本何時停收、要不要納入共變量、報哪一個依變數、要不要剔除某些觀察值),在虛無假設完全為真的情況下,得到 p 小於 0.05 的機率會從 5% 升到 61%。他們一次回測都沒多跑,只是保留了事後選擇的空間。
量化研究者的自由度比社會科學多得多。以下六個入口,就是這些自由度在回測流程裡的實際落點。
選擇偏誤的六個入口:全表對照
| 入口 | 你以為自己在做的事 | 實際上在選的東西 | 最快的自我偵測 |
|---|---|---|---|
| 一、參數與超參數 | 找最適參數 | 從績效分布取最大值 | 數清楚隱形參數有幾個 |
| 二、標的宇宙 | 選流動性夠的幣 | 事後已知的倖存者 | 起點日清單與今日清單的交集比例 |
| 三、特徵與變換 | 用一個技術指標 | 該指標的幾十種寫法裡最顯著的 | 特徵定義是否在看相關性之前寫死 |
| 四、樣本期間 | 挑資料品質好的區間 | 策略最順的那一段 | 起點敏感度分析 |
| 五、訊號家族與組合 | 挑最強的變體 | 整個家族的最大值 | 變體間報酬相關矩陣 |
| 六、社群與發表層 | 站在別人的肩膀上 | 一次你看不見的大規模搜尋的末端 | 這支策略失敗的版本在哪裡 |
六個入口的殺傷力不平均。入口一最容易被抓到,因為它留下 log。入口六最難防,因為搜尋不是你做的。
入口一:參數搜尋裡的隱形參數
顯性參數大家都會數:均線長度、回看視窗、進場閾值。真正被漏掉的是隱形參數,那些你調過、但從來沒寫進報告的東西。
一份常見的隱形參數清單:停損停利水位、再平衡頻率、訊號延遲幾根 K、手續費與滑價假設、資料頻率(1 小時還是 4 小時)、暖機期長度、部位規模規則、以及「這個幣成交量太低所以剔除」的那個門檻。
算一下量級。三個顯性參數各掃 10 檔,是 1000 格。再加四個隱形參數,每個你只試過三種設定,乘上去是 81 倍。搜尋空間變成 81,000。你的報告上寫的是「掃了 1000 組參數」,誠實的數字是八萬。
自我偵測:打開你的 notebook 或 git log,把每一個「跑完看了結果才改掉」的常數列出來。列不完的那些,就是你低報 N 的來源。
計數規則:每一個看過績效才決定的常數,乘進去,不是加進去。看一眼就丟掉的那些格子照樣要算,因為你丟掉它們的理由是績效。
防治:非核心參數在碰資料之前寫死,並且套用到所有標的與所有變體。手續費用交易所實際費率,滑價用保守估計,不因為策略跑不出來就回頭調鬆。這條紀律的成本是你會少看到一些漂亮曲線,收益是剩下的那些比較可能是真的。
入口二:標的宇宙與存活者偏誤
這個入口在加密市場的殺傷力遠高於傳統市場,原因在資料供應鏈本身。
多數 API 給你的是「現在還在交易的交易對」。下架的幣種,它的 K 線經常直接從端點上消失,不是變成一段歸零的價格序列。你不需要主動篩選,資料源已經幫你把死掉的標的刪乾淨了。你以為自己拿到的是歷史,實際上拿到的是倖存者名單。
跑一個可以複製的檢查。把 2022 年 1 月幣安現貨成交量前 100 名的清單抓出來,跟今天的前 100 名做交集,數還在名單裡的有幾個。這個數字幾乎不會超過一半。剩下那些不在今天清單裡的幣,如果你的回測宇宙是用今天的清單定義的,它們在你的資料裡等於從來沒存在過。
把這件事翻成算術更直白。假設一支月度輪動策略的宇宙有 100 檔,三年裡有 20 檔在某個時點崩掉九成以上並最終下架。用今天的清單回測,那 20 檔的虧損全部不會出現在你的權益曲線上,而它們原本會出現在最容易被輪動策略選中的位置:暴漲之後、成交量最大、動量指標最強的那個當下。存活者偏誤在這裡不是均勻扣分,它專門刪掉策略最痛的那幾筆交易。
宇宙定義還有幾個容易忽略的選擇層:只用一家交易所的資料、只用有永續合約的幣、只用有足夠歷史長度的幣。第三個特別隱蔽,因為「至少要有兩年資料才納入」聽起來像資料品質要求,實際上它把所有活不到兩年的標的排除了,那正是死亡率最高的一群。
自我偵測:算你回測宇宙的存活率。如果起點日的成分股有 90% 以上活到今天,你的宇宙幾乎確定是事後定義的。
計數規則:每一種宇宙定義算一次試驗。你試過前 50、前 100、加成交量門檻三種,那是三倍。
防治:資料層保留 point-in-time 宇宙,含已下市標的與它們的最後價格,並且宇宙定義只能有一個、事前決定。這部分屬於基礎建設而不是研究技巧,量化交易數據層的建法那篇拆過完整的儲存與快照設計。實驗室裡的資料層討論通常從這一步開始,因為後面所有校正工具都建立在它上面,Lab 社群有實際的快照腳本可以參考。
入口三:特徵挖掘與變換自由度
「我試了 20 個指標」這句話低報的倍數通常最大。
一個指標不是一次試驗。以 RSI 為例:視窗長度(7、14、21、28)、用 level 還是用差分、要不要平滑、平滑幾期、閾值取固定值還是滾動分位、分位視窗多長、訊號要不要延遲一根。這些選擇彼此獨立,乘起來,單一個 RSI 輕鬆到 50 種以上的寫法。20 個指標的真實搜尋空間是四位數,不是 20。
更麻煩的是這些選擇多半不是一次跑完再挑,而是邊看邊改。你先用 14 期,看起來沒訊號,改成 21 期,好一點,再把閾值從固定 70 改成滾動 90 分位,突然通了。每一步你都在看著結果調整,這正是 forking paths 描述的那個過程。最終報告上只會有那條走通的路徑。
自我偵測:對每個進入最終模型的特徵,問一句「這個定義是在我看到它跟報酬的相關性之前寫死的嗎」。答不出來的,都算多重檢定的一部分。
計數規則:候選特徵數乘上每個特徵的變換組合數。粗估就好,量級對了比精確重要。
防治:特徵定義事前登記,同一組變換規則套用到所有候選特徵,不對單一特徵客製化。想試新的變換,就對全部特徵一起試,並且把試過的組合數記下來。這麼做的好處是你的 N 從不可知變成可算,後面的 Deflated Sharpe Ratio 或 SPA 檢定才有可信的輸入。
入口四:樣本期間與異常值處理
時間軸上的選擇有三種型態,第一種最常見,後兩種最不容易察覺。
起點偏誤。回測從哪一天開始,通常是隨手決定的:資料完整度、某個週期的起點、或者純粹是「從 2023 年開始比較乾淨」。問題是不同起點會給出差很多的績效,而你會留下比較好看的那個。
剔除異常期間。312、FTX 崩盤、任何一次超大清算,這些日子的資料經常被當成雜訊剔除,理由通常聽起來很正當:交易所當時撮合異常、報價失真、那是黑天鵝不具代表性。但一支策略在極端行情下的表現,正是你最需要知道的那一段。剔除它們等於把最重要的樣本從檢驗裡拿掉。
暖機期與資料截斷。指標需要暖機,暖機期多長是一個選擇。你把暖機期從 200 根改成 100 根,回測就多了兩個月,那兩個月的績效會影響你的判斷。
起點敏感度分析是這裡最直接的工具,成本很低。把回測起點以 30 天為間隔往後平移 24 次,記錄每一次的年化 Sharpe,看這 24 個數字的分布。常見的形狀是這樣:報告裡那個起點給出 Sharpe 2.1,往後移 60 天掉到 0.9,再往後移三個月變成 1.4。分布這麼寬,代表你報告的那個數字有很大一部分是起點選出來的,不是策略選出來的。分布如果窄,這一項就過關。
計數規則:試過的起點數、剔除規則的版本數、暖機期設定數,相乘。
防治:全樣本鎖定,起點由資料可得性決定而不是由績效決定。異常值處理規則事前寫死、對所有標的一視同仁,並且把「含異常期間」與「剔除異常期間」兩個版本的績效同時報出來。兩個版本差距大的策略,你至少知道它靠什麼吃飯。
入口五:訊號家族與組合層
你寫了 20 個變體,挑最強的一支寫報告。這個入口的關鍵不在「20 次要不要校正」,而在這 20 次彼此有多像。
近親變體高度相關。同一個動量邏輯的 20 種寫法,報酬序列的兩兩相關係數可能落在 0.8 以上。這時候用 Bonferroni 把門檻除以 20,會嚴過頭,因為你實際上沒有做 20 次獨立檢定。反過來,如果 20 個變體邏輯各異、相關係數只有 0.3,那 20 次的懲罰又不夠。
有效自由度可以直接算。取變體報酬的相關矩陣,做特徵值分解,用參與比(participation ratio):
N_eff = (Σλ)² / Σλ²
代兩個數字進去。20 個變體、平均相關 0.8,相關矩陣的特徵值是一個 16.2 加上 19 個 0.2,算出來 N_eff 約 1.5。也就是說那 20 個變體,資訊量只等於一次半的獨立嘗試。同樣 20 個變體,平均相關降到 0.3,N_eff 升到約 7.4。
這個數字有兩個用途。一是給多重檢定校正一個比「變體數」更貼近實際的 N。二是當作研究品質的體檢:如果你的 20 個變體算出 N_eff 只有 1.5,代表你其實只有一個想法,換了 20 種寫法。多樣性是假的。
組合層還有第二次選擇。哪些訊號進最終的 ensemble、權重怎麼給、要不要加波動率目標,這些決定如果是看著組合績效做的,就是在整個組合空間裡取最大值。正確的做法是把整個家族一起丟進 White’s Reality Check 或 SPA 這類檢定,讓它處理相關結構,而不是只檢定贏家。校正工具的分工與使用時機,多重檢定校正那篇有逐個工具的對照。
入口六:社群、發表層與你自己的記憶
前五個入口的搜尋是你做的,所以理論上可以計數。第六個入口的搜尋是別人做的,而且規模不可知。
你在 GitHub 上看到一支星星數三千的策略,回測曲線很漂亮。你看到它,正是因為它漂亮。一千個人各自對著同一批公開 OHLCV 跑 optimizer,只有運氣最好的那幾個把結果推上來,另外九百多個沒有發文。你以為自己站在別人的肩膀上,實際上你接手的是一次規模未知的搜尋的末端產物,而它的 N 你永遠算不出來。
學術界有同樣的過濾器,而且有量測。McLean 與 Pontiff 在 Does Academic Research Destroy Stock Return Predictability? 裡追蹤了 97 個被發表過的報酬預測因子,發現它們在原論文樣本期之外的報酬平均衰減約 26%,在論文發表之後再衰減到約 58%。前一段衰減主要來自統計偏誤(發表的都是運氣好的那些),後一段來自套利者進場。
同一件事在加密市場的週期短得多,一個公開的策略從有效到失效,通常不需要幾年。
還有一個更難防的版本:你自己的記憶。你知道 SOL 在 2023 年漲了多少,你知道 2022 年是熊市,你知道哪些幣後來歸零。任何一個「我選這個標的、這段期間、這個 regime」的決定,都已經被你腦中的未來資訊污染過。這是前視偏誤的認知版本,而且沒有任何程式檢查抓得到它,因為洩漏發生在你的腦袋裡,不在資料流裡。
自我偵測:對任何外來策略問一句「這支策略失敗的版本在哪裡」。找不到失敗版本的地方,你看到的就是倖存者樣本。
防治:外來策略進入流程時,直接假設它的 N 未知且巨大,要求它在你自己鎖起來、從未被它碰過的保險庫資料上先過一關。過不了就丟掉,別優化它,優化它等於幫別人的過擬合再加一層你自己的。
二階效應:六個入口是相乘的
分開看,每個入口的倍數都不誇張。合起來的量級才是問題。
假設一個相當典型的研究專案:5 個訊號家族,每個家族 20 個候選特徵乘 4 種變換(80),參數格 500,宇宙定義試過 3 種,樣本期間切法 2 種。
5 × 80 × 500 × 3 × 2 = 1,200,000
你當然沒有真的跑一百二十萬次回測。但按照 forking paths 的判準,只要這些決策是看著績效做的,這一百二十萬就是你事實上的搜尋空間。
把這個數字放回極值理論的表裡看後果。純雜訊情況下,N 次嘗試的期望最大 t 值,以及它在四年日線資料上對應的年化 Sharpe:
| 有效試驗次數 N | 純雜訊的期望最大 t | 對應年化 Sharpe(四年) |
|---|---|---|
| 1,000 | 3.24 | 1.62 |
| 10,000 | 3.85 | 1.93 |
| 100,000 | 4.38 | 2.19 |
| 1,000,000 | 4.86 | 2.43 |
| 10,000,000 | 5.30 | 2.65 |
一百二十萬次對應的期望最大 t 大約 4.9,年化 Sharpe 大約 2.4。這個數字值得停一下:一支底層完全沒有訊號的策略,在這個規模的搜尋下,期望能撈到的最佳回測 Sharpe 就是 2.4。而 2.4 正好落在多數人看到會眼睛一亮、覺得「這支可以上」的區間。
結論是反直覺的:在大型搜尋之後,回測 Sharpe 2.4 不構成任何證據。它剛好是雜訊在那個搜尋規模下該有的樣子。要讓它構成證據,你需要的不是更高的 Sharpe,是更小的 N,或者更長的樣本。這也是回測過擬合機率(PBO)那類指標存在的理由,它直接估的就是「樣本內冠軍在樣本外掉到後段班」的機率。
一份可以今天跑的稽核清單
六題,一個入口一題。答不出來的就當作那一項的 N 未知。
- 我調過幾個常數是看了績效才改的(含停損、手續費、暖機期)
- 我的回測宇宙,起點日的成分有多少比例活到今天
- 我的每個特徵,定義是在看到它跟報酬的相關性之前寫死的嗎
- 起點往後平移 24 次,Sharpe 的分布有多寬
- 我的變體之間,N_eff 是多少
- 這個想法有多少部分來自我看過的公開資料或別人的結果
判讀規則很簡單。六題全部答得出具體數字,你的 N 可算,DSR、SPA、PBO 這些工具就有可信輸入,照算就好。有任何一題答不出來,你的 N 是未知的,這時候任何校正工具的輸出都只是在買心安。誠實的做法是把門檻直接拉高(N 不明時,t 大於 3 是下限而不是目標),並且把初始部位規模縮到即使策略是雜訊也不影響整體的水位。
有一個成本很低、效果最好的做法值得單獨提:從研究的第一天開一個試驗計數器,每碰一組參數、一個特徵、一個宇宙定義就加一,包括看一眼就丟的。研究結束後這個數字無法誠實重建,因為人會系統性地少記自己丟掉的東西。它必須是即時記錄,不能是事後回憶。
這套稽核擋不住什麼
計數靠自律,無法被外部驗證。除非你做版本控制加事前登記,否則沒有人能查核你報的 N 是不是真的。這在自己交易時是誠實問題,在對外募資時是揭露問題。
保險庫資料只能用一次。切出來禁區、最後驗收用一次,這條紀律的效力在你第二次拿它調參數的當下就歸零了,而且沒有辦法補救。
N_eff 的估計本身有誤差。相關矩陣是用歷史報酬估的,樣本一短,特徵值分解就不穩,算出來的有效自由度只能當量級參考,不能當精確值用。
最後,就算你把 N 算對、校正做足,策略仍然可能死。選擇偏誤處理的是統計上的假訊號,它管不到市場結構換人這一類問題。一支通過所有多重檢定校正的策略,遇上造市商退場或保證金規則改版,照樣會失效。這是另一個問題類別,回測過擬合的九種死法那篇把兩者的邊界拆得比較清楚。
結論
收攏成五句話:
- 判準不是你跑了幾次,是你的決策有沒有依賴資料。只要決策看著績效做,沒跑過的路徑一樣要算進搜尋空間。
- 六個入口分別是參數與隱形參數、標的宇宙與存活者偏誤、特徵變換自由度、樣本期間與異常值處理、訊號家族與組合層、社群與發表層(含你自己的記憶)。
- 六個入口是相乘的。一個相當典型的研究專案,有效搜尋空間輕鬆到百萬量級,對應的純雜訊期望最佳 Sharpe 就是 2.4。
- 每個入口都有成本很低的自我偵測:宇宙存活率、起點敏感度分析、變體的 N_eff、外來策略的失敗版本在哪裡。
- 稽核有六題答不出來時,唯一誠實的處理是提高門檻並縮小規模,不是換一個更精密的校正工具。
回測的可信度從來不寫在權益曲線上。它寫在你有沒有辦法回答「我試了幾次、怎麼選的」這個問題。這六個入口的用途,是把這個問題從一句自我感覺良好的宣稱,變成六個可以查、可以算、可以被別人檢驗的數字。
我每週會把這類驗證框架與實際市場數據的交叉檢驗寫進電子報,包括流動性條件、衍生品結構與鏈上籌碼的例行審視。想讓自己的決策流程多一層濾網的,可以訂閱追蹤。




