選擇偏誤潛入量化回測的六個入口:一份研究流程稽核清單

你只跑了三十次回測,有效試驗次數卻可能是一百萬。本文拆解選擇偏誤潛入量化回測的六個入口,每個入口給機制、自我偵測方法與試驗次數的計數規則。

你沒有跑一百萬次回測。你只跑了三十次。但如果那三十次裡的每一個決策都是看著績效做的,你的有效試驗次數仍然是一百萬。

多數人對選擇偏誤的理解停在一句話:試了很多組參數,只報最好那組。那是六個入口裡最顯眼、也最好補的一個。剩下五個藏在你根本不覺得自己在「選」的地方:標的宇宙怎麼定義、特徵怎麼寫、樣本從哪一天開始算、哪些變體進得了最終組合,以及那支你從 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 未知。

  1. 我調過幾個常數是看了績效才改的(含停損、手續費、暖機期)
  2. 我的回測宇宙,起點日的成分有多少比例活到今天
  3. 我的每個特徵,定義是在看到它跟報酬的相關性之前寫死的嗎
  4. 起點往後平移 24 次,Sharpe 的分布有多寬
  5. 我的變體之間,N_eff 是多少
  6. 這個想法有多少部分來自我看過的公開資料或別人的結果

判讀規則很簡單。六題全部答得出具體數字,你的 N 可算,DSR、SPA、PBO 這些工具就有可信輸入,照算就好。有任何一題答不出來,你的 N 是未知的,這時候任何校正工具的輸出都只是在買心安。誠實的做法是把門檻直接拉高(N 不明時,t 大於 3 是下限而不是目標),並且把初始部位規模縮到即使策略是雜訊也不影響整體的水位。

有一個成本很低、效果最好的做法值得單獨提:從研究的第一天開一個試驗計數器,每碰一組參數、一個特徵、一個宇宙定義就加一,包括看一眼就丟的。研究結束後這個數字無法誠實重建,因為人會系統性地少記自己丟掉的東西。它必須是即時記錄,不能是事後回憶。

這套稽核擋不住什麼

計數靠自律,無法被外部驗證。除非你做版本控制加事前登記,否則沒有人能查核你報的 N 是不是真的。這在自己交易時是誠實問題,在對外募資時是揭露問題。

保險庫資料只能用一次。切出來禁區、最後驗收用一次,這條紀律的效力在你第二次拿它調參數的當下就歸零了,而且沒有辦法補救。

N_eff 的估計本身有誤差。相關矩陣是用歷史報酬估的,樣本一短,特徵值分解就不穩,算出來的有效自由度只能當量級參考,不能當精確值用。

最後,就算你把 N 算對、校正做足,策略仍然可能死。選擇偏誤處理的是統計上的假訊號,它管不到市場結構換人這一類問題。一支通過所有多重檢定校正的策略,遇上造市商退場或保證金規則改版,照樣會失效。這是另一個問題類別,回測過擬合的九種死法那篇把兩者的邊界拆得比較清楚。

結論

收攏成五句話:

  1. 判準不是你跑了幾次,是你的決策有沒有依賴資料。只要決策看著績效做,沒跑過的路徑一樣要算進搜尋空間。
  2. 六個入口分別是參數與隱形參數、標的宇宙與存活者偏誤、特徵變換自由度、樣本期間與異常值處理、訊號家族與組合層、社群與發表層(含你自己的記憶)。
  3. 六個入口是相乘的。一個相當典型的研究專案,有效搜尋空間輕鬆到百萬量級,對應的純雜訊期望最佳 Sharpe 就是 2.4。
  4. 每個入口都有成本很低的自我偵測:宇宙存活率、起點敏感度分析、變體的 N_eff、外來策略的失敗版本在哪裡。
  5. 稽核有六題答不出來時,唯一誠實的處理是提高門檻並縮小規模,不是換一個更精密的校正工具。

回測的可信度從來不寫在權益曲線上。它寫在你有沒有辦法回答「我試了幾次、怎麼選的」這個問題。這六個入口的用途,是把這個問題從一句自我感覺良好的宣稱,變成六個可以查、可以算、可以被別人檢驗的數字。

我每週會把這類驗證框架與實際市場數據的交叉檢驗寫進電子報,包括流動性條件、衍生品結構與鏈上籌碼的例行審視。想讓自己的決策流程多一層濾網的,可以訂閱追蹤。

個人頭像照片
AD.Trader Lab

AD.Trader Lab 創辦人 | 量化交易員 專注於在充滿雜訊的市場中,尋找數學上的必然性。AD.Trader Lab 實驗室主理人,帶你用數據看透漲跌背後的真相。

每日更新 · 免費

每日選幣清單

我每天在 Telegram 發一份選幣名單——標的、為什麼選它、我自己有沒有進場。

  • 每日選幣名單,附上選它的理由
  • 新文章與研究上架,第一時間通知
  • 討論區:有問題可以直接問我
免費加入 Telegram

340+ 人已經在頻道裡

已經在交易了?用我的連結註冊 OKX,手續費終身退 20% 月成交 20 萬 U,一年約退你 NT$6,000 — 交易越多退越多