前瞻偏誤 回測 殺手 定義
前瞻偏誤是回測中使用了未來數據,導致績效虛胖。本文定義三種機制:同根K線決策、倖存者偏差與修正數據,並以SPY十年數據量化其影響。
前瞻偏誤是指在回測中使用了模擬交易下單當下無人能掌握的資訊。這樣的權益曲線會持續攀升,統計數據看似亮眼,但一旦面對真實訂單便完全失效。本文將定義此偏誤,說明三種產生機制,並以實際市場數據量化每種機制的影響。
前瞻偏誤的定義
回測是對一項決策的主張:在某個時間點、依據特定資訊,某規則會產生某種結果。唯有當規則讀取的每項輸入,在該時間點之前都已存在且為最終版本時,此主張才成立。前瞻偏誤就是違反此條件的狀況,且它不會發出任何警訊。當試算表或腳本中的某一列,回傳了一個時間戳記晚於其所餵養決策的數值時,系統不會提出任何異議。
實務上,最常見的違規情況主要有三種:決策讀取了與交易執行在同一根K線內的價格;篩選的股票池是由今日仍掛牌的公司所組成;以及使用了在日期標記之後才被修正過的數據欄位。
機制一:在交易執行的同一根K線上下決策
先從一個看似無害的規則說起。買入強勢股,也就是在基金收盤價高於開盤價的交易日進場。若編碼不慎,測試會在該交易日的開盤價進場、收盤價出場,而篩選條件卻是同一根K線的收盤價。在開盤時,沒有人知道收盤價是多少。
下方的圖表在SPY(S&P 500 ETF)上,橫跨十年,同時執行了兩種版本。第一欄是無法實現的版本。第二欄則將完全相同的條件延後一個交易日,這是有資金帳戶能據以行動的最早時機。
每個數據背後的精確 SQL 語法
WITH bars AS (
SELECT date,
toFloat64(open) AS o,
toFloat64(close) AS c,
toFloat64(close) > toFloat64(open) AS up_day
FROM global_markets.stocks_daily_aggs
WHERE ticker = 'SPY'
AND date >= toDate('2015-12-01')
AND date <= toDate('2025-12-31')
),
lagged AS (
SELECT date, o, c, up_day,
any(up_day) OVER (ORDER BY date ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS prior_up
FROM bars
)
SELECT toYear(date) AS year,
countIf(up_day) AS signal_days,
round(100 * avgIf(c / o - 1, up_day), 3) AS same_bar_avg_pct,
round(100 * avgIf(c / o - 1, prior_up), 3) AS lagged_avg_pct,
round(100 * (avgIf(c / o - 1, up_day) - avgIf(c / o - 1, prior_up)), 3) AS gap_pp
FROM lagged
WHERE date >= toDate('2016-01-01')
GROUP BY year
ORDER BY year兩者之間的差異並不微小。在 2016 年,同根K線版本在每個符合條件的交易日平均獲利 0.447%,而延後版本平均獲利 -0.008%,兩者差距為每日 0.455 個百分點。在 2025 年,兩者分別為 0.636% 和 -0.03%。假設一年約有 140 個符合條件的交易日,每日半個百分點的差距,經過複利計算後,會產生一條沒有任何真實帳戶能達成的權益曲線。
這整個優勢來自於定義本身。篩選出收盤價高於開盤價的交易日,然後衡量這些交易日的開盤到收盤報酬,這是在衡量一個保證為正的數值。該規則發現的其實是自己的篩選條件。
此類變體經常隱藏在日線資料中:包含今日收盤價並用以進行今日交易的移動平均線、根據尚未發生的股票分割調整後價格計算出的訊號、以及設定在尚未出現的日內高點上的停損單。它們的共同特徵是,決策的時間戳記早於其某項輸入資料的時間戳記。
機制二:由今日的倖存者組成的股票池
第二種機制從不觸及規則本身。它存在於規則被允許看到的股票清單中。
從目前的資料供應商取得一份股票代碼清單,然後回測十年,這個樣本會悄悄地排除所有在此期間破產、被收購或下市的公司。該規則從未被提供那些失敗的股票。下方的圖表衡量了此遺漏的規模:在特定日曆年度中,所有交易超過200個交易日且股價高於1美元的美國掛牌股票,這些股票中在2026年7月仍持續報價的比例,以及整個群體與僅倖存者群體的中位數日曆年度報酬。
每個數據背後的精確 SQL 語法
WITH recent AS (
SELECT DISTINCT ticker
FROM global_markets.stocks_daily_aggs
WHERE date >= toDate('2026-06-15')
AND date <= toDate('2026-07-28')
),
per_name AS (
SELECT toYear(date) AS year,
ticker,
argMin(toFloat64(close), date) AS first_close,
argMax(toFloat64(close), date) AS last_close,
count() AS sessions
FROM global_markets.stocks_daily_aggs
WHERE date >= toDate('2015-01-01')
AND date <= toDate('2024-12-31')
AND close > 1
GROUP BY year, ticker
HAVING sessions >= 200
)
SELECT year,
uniqExact(ticker) AS names_trading,
round(100 * uniqExactIf(ticker, ticker IN (SELECT ticker FROM recent)) / uniqExact(ticker), 1) AS still_listed_pct,
round(100 * quantileDeterministic(0.5)(last_close / first_close - 1, cityHash64(ticker)), 2) AS median_return_all_pct,
round(100 * quantileDeterministicIf(0.5)(last_close / first_close - 1, cityHash64(ticker), ticker IN (SELECT ticker FROM recent)), 2) AS median_return_survivors_pct,
round(100 * (quantileDeterministicIf(0.5)(last_close / first_close - 1, cityHash64(ticker), ticker IN (SELECT ticker FROM recent))
- quantileDeterministic(0.5)(last_close / first_close - 1, cityHash64(ticker))), 2) AS survivor_gap_pp
FROM per_name
GROUP BY year
ORDER BY year在 2015 年符合條件的 7063 檔股票中,有 52.5% 檔在2026年7月仍在交易。對於 2024 年的群體,此比例為 87.5%,這主要與時間流逝有關:2024年的股票有兩年時間可能消失,而2015年的股票則有十一年。
報酬欄位直接顯示了偏誤本身。在 2017 年,整個群體的中位數報酬為 7.51%,而倖存者群體的中位數報酬為 11.32%,兩者差距達 3.81 個百分點,這是不勞而獲的優勢。在圖表顯示的 10 年中,此差距每年皆為正值。在倖存者身上測試的策略,在做出任何決策之前就已繼承了此優勢,而篩選弱勢股的規則繼承的更多,因為消失的股票不成比例地落在該類別中。
修正方法是使用「時間點」股票池:即在每個再平衡日期當下存在的股票清單,包含已下市的股票,且下市股票的持倉應以其最終報價平倉,而非直接從記錄中刪除。
機制三:在日期標記之後被修正過的數據
價格是即時標記的。基本面數據則不然。季度數據帶有該季度的結束日期,但會因後續的申報文件而修正,而僅儲存最新版本的資料庫,會樂意地將修正後的數字提供給原始日期。一個以此數字為篩選條件的規則,讀取的是耗時數月才出現的修正。
同樣的模式也存在於:根據當前成分股重建的指數成分股清單、被最終共識覆蓋的分析師預估歷史、以及向後應用於整個價格序列的公司事件調整。在每種情況下,儲存的記錄都是一個帶有過去日期戳記的「現在式」快照。
有兩種做法可以避免此問題。使用保留「版本」的資料來源,也就是儲存每個日期當時所知的數值,而非現在所知的數值。若無法取得版本資料,則應採用足夠寬鬆的報告滯後期,以涵蓋真實的滯後時間,並將此滯後期的長度視為一個值得測試的參數,而非一個可以猜測的細節。
當未來資訊滲入時,曲線會是什麼樣子
前瞻偏誤有一個特徵:績效遠高於標的市場本身所能提供的報酬。一個有用的參考點是「後見之明天花板」,即完美的一日預測在單一工具上所能產生的增長。
每個數據背後的精確 SQL 語法
WITH bars AS (
SELECT date,
toFloat64(close) AS c,
any(toFloat64(close)) OVER (ORDER BY date ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS prev_c
FROM global_markets.stocks_daily_aggs
WHERE ticker = 'SPY'
AND date >= toDate('2015-12-01')
AND date <= toDate('2025-12-31')
),
rets AS (
SELECT toYear(date) AS year,
c / prev_c - 1 AS r
FROM bars
WHERE date >= toDate('2016-01-01')
AND prev_c > 0
)
SELECT year,
count() AS sessions,
round(100 * (exp(sum(log(1 + r))) - 1), 1) AS buy_and_hold_pct,
round(100 * (exp(sumIf(log(1 + r), r < 0.02)) - 1), 1) AS without_big_up_days_pct,
round(exp(sum(log(1 + abs(r)))), 1) AS hindsight_ceiling_multiple
FROM rets
GROUP BY year
ORDER BY year這個天花板非常巨大。在 2020 年,對SPY的完美一日預測,在 253 個交易日內將資本增長了 27.7 倍,而單純持有該ETF的增長為 16.2% 倍。在這十年中最平穩的一年,2017 年,同樣的完美預測仍帶來了 2.2 倍的資本回報。
中間欄位顯示了這個天花板有多麼集中。剔除所有漲幅超過2%的交易日後,2020 年的回報從 16.2% 倍降至 -44.9% 倍。每年僅洩漏少數這類交易日的資訊,就能讓回測結果大幅偏離現實。一條擁有淺回撤、高勝率,且最佳交易日集中在市場最劇烈波動時段的曲線,值得接受審計。
校準比任何單一門檻都更重要。一個聲稱在十年間能穩定獲得40%年化報酬率的規則,等於是在聲稱它獲得了後見之明天花板的很大一部分,而誠實的問題是:是哪項輸入告訴它即將發生什麼事?
如何測試回測是否存在前瞻偏誤
- 將所有訊號延後一根K線。 將每個訊號都向後移動一個完整週期,然後重新執行。真正的優勢只會略微衰減。而資訊洩漏則會崩潰,通常會歸零。
- 擾動未來數據。 取得每個決策時間點之後的數據,用雜訊或隨機重排取代,然後重新執行決策邏輯。每個決策的結果都應完全相同。任何結果發生變化的決策,都代表它讀取了當時無法得知的資訊。
- 將時間戳記視為數據。 對於每項輸入,儲存該數值變得可用的時刻,以及它所描述的時刻。一個從不比較這兩個欄位的資料表連接,無法強制執行此規則。
- 以當日為基準重建股票池。 包含已下市和被收購的公司,以其最後報價平倉,並在兩個股票池上執行相同的規則,以量化倖存者偏誤的影響。
- 對照天花板進行衡量。 將策略的報酬與同一時間區間的「買入並持有」報酬以及「完美預測」報酬進行比較。與天花板的距離,是夏普比率單獨無法提供的背景資訊。
部位規模的設定是這一切的後續環節。被誇大的勝率會導致被誇大的下注規模,而這正是 凱利公式部位規模設定 將會計錯誤轉變為重大回撤的關鍵點。在投入真實資金之前,同樣的紀律也適用:模擬交易測試下單機制,而自動化交易系統則以機器速度承載著相同的風險,這在 多智能體AI交易系統 中有詳細說明。那些能夠經得起嚴謹的「時間點」測試考驗的效應,例如 低波動異常,正是因為這個原因而顯得有趣。
常見問題
什麼是回測中的前瞻偏誤?
它是指在模擬交易中使用了在實際下單時點尚未可得的數據。常見的形式包括:在交易執行的同一根K線內做決策、篩選今日仍掛牌的公司所組成的股票池、以及讀取在日期標記之後才被修正過的財務數據。
如何偵測前瞻偏誤?
將每個訊號都延後一個完整週期,然後重新執行:資訊洩漏通常會崩潰,而真正的優勢只會溫和衰減。更嚴格的測試是,用雜訊取代每個決策時間點之後的所有數據,並確認每個決策的結果都保持不變。
倖存者偏誤和前瞻偏誤是同一回事嗎?
它們不同,但經常同時出現。倖存者偏誤是指樣本由存活下來的股票組成,而知道哪些股票存活下來本身就是來自未來的資訊。因此,僅包含倖存者的股票池,是一種存在於數據中而非規則中的前瞻性資訊洩漏。
為什麼帶有前瞻偏誤的回測結果看起來這麼好?
資訊洩漏是選擇了結果,而非預測了結果。在上方的圖表中,根據自身收盤價篩選交易日,在 2016 年產生的平均交易日獲利,比延後版本高出約 0.455 個百分點,這是一種來自定義的優勢,而非憑實力賺來的。
什麼是時間點數據?
時間點數據儲存了每個歷史日期當時所知的數值,包含原始數字和所有後續修正,以及當日存在的證券清單。這是回測所需用來證明其輸入數據在決策之前就已存在的參考依據。
以上每個數字都是基於真實日線數據的、已儲存且可追溯版本的查詢結果。您可以在Strasmore終端機上展開任何圖表的SQL,或對您自己的規則執行滯後測試。