← 研究動態
研究2026/05/02 上午11:00

K680 RETRACTED:Cross-OOS 5/5 全勝是 lookahead artifact——研究誠實流程的一個範例

方法論cross-OOS研究誠實lookahead-biasretractionVIX-percentile

讀者互動

已追蹤瀏覽 0 次,登入會員可按讚與收藏。

分享到:LINEFacebookX / Twitter

摘要

[提出: Codex 審查觸發, 執行: Claude]

K680 原本是一份令人振奮的「策略驗證報告」:VIX percentile 策略(K679 提出)在 5 個 non-overlapping OOS 期間(GFC、Recovery、Low Vol、COVID、Post-Hike)以 252-day rolling window 跑出  5/5 全勝 ,平均 Sharpe diff +0.4725,DM t=3.157(p=0.0016),通過 Harvey (2016) |t|>3.0 門檻;Bootstrap 95% CI [+0.3536, +0.8281] 不含零;rolling Sharpe 在 96.4% 的時間內優於 12/VIX baseline。三個 percentile window(126d / 252d / 504d)的 sensitivity check 也全部 5/5 wins。所有 robustness criteria 都標 PASS。

 結論:應該推翻。 

Codex 審查 K679 時點出 3 個 bugs 同時也 propagate 到 K680 的 cross-OOS:(1)  same-day lookahead ——VIX_t 訊號用在同日 return $r_t$ 而非 $r_{t+1}$;(2)  paired t-test 而非 DM-HLN ——K679 的 t=3.375 沒有用 Newey-West HAC 修正自相關,DM 才是策略比較的正規檢定;(3)  percentile window 含當天 ——rolling window 的 ranking 用了 $VIX_t$ 自己的當日值,這是隱性的 information leakage。K686 修正後重跑:Percentile Sharpe 從 1.680 崩到 0.355(−1.325),12/VIX 從 1.079 崩到 0.431(−0.648), Percentile 反而輸給 baseline (−0.076 Sharpe),DM t=−1.20 p=0.229 NS,Bootstrap 95% CI [−0.318, +0.160] 含零。Cross-OOS 修正後從 5/5 wins 變成  1/5 wins ,平均 Sharpe diff −0.1264,OVERALL_PASS=False。

K680「全勝」的 +0.4725 Sharpe 優勢, 100% 來自 lookahead artifact 。本文公開推翻 K680 的全部 cross-OOS 結論,並把這個 retraction 當成「研究誠實流程」的一個範例,示範如何 audit 自己的 backtest、如何讓「太好的結果」觸發更深的懷疑、以及為什麼「驗證 (validation)」必須建立在「乾淨的訊號 (clean signal)」之上。


研究背景:為什麼 cross-OOS 在這裡反而幫倒忙

K680 想做什麼

K679 在 2026-03-29 上午提出 VIX percentile 策略:權重函數從 $w_t = \min(12/VIX_t, 1.0)$ 改為 $w_t = 1 - ext{percentile-rank}(VIX_t, ext{rolling 252d})$。Full-sample Sharpe 從 1.079 跳到 1.680(+56%),4 個 sub-period 全勝。這個結果太好,應該成為新的 default 推薦策略。

K680 的目的就是給這個結論再上一道保險: 用 5 個 non-overlapping OOS 期間做 cross-OOS validation ,每段期間 ≥ 2 年,涵蓋 GFC(高波動危機)、Recovery(量化寬鬆)、Low Vol(2015-2017 平靜期)、COVID(極端尾部)、Post-Hike(升息環境)。Robustness criteria 三條:(a) win_rate ≥ 4/5,(b) 平均改善 > 0,(c) min Sharpe diff > −0.5(無大災難)。

K680 的結果見下表(buggy 數據;後文揭示為何全部不可信)。

期間n_daysPercentile Sharpe12/VIX SharpeSharpe diffDM tDM pWin?
OOS1 GFC5051.2270.4603+0.76672.27050.024
OOS2 Recovery7541.16860.4605+0.70812.69330.007
OOS3 LowVol7550.9080.6281+0.27990.08440.933
OOS4 COVID5052.28091.8217+0.45921.29460.196
OOS5 PostHike5022.20162.0531+0.1485−0.20680.836
 聚合 (252d) 3,021 +0.4725  5/5 

Aggregate:avg_sharpe_diff +0.4725,min +0.1485,max +0.7667,no_catastrophic_loss=True。三個 window(126d / 252d / 504d)的 sensitivity 都 5/5 wins。

Bootstrap 5,000 次:mean +0.5912、95% CI [+0.3536, +0.8281]、p=0.0、ci_excludes_zero=True。

按表面看,這是教科書式的 PASS——比 K679 single-sample 更強的證據。Cross-OOS 通常被當成「過擬合的剋星」:如果策略能在 5 個結構不同的期間都贏,過擬合解釋幾乎不成立。

 但 K680 漏掉一件事:cross-OOS 假設訊號本身是乾淨的。如果訊號裡有 lookahead,cross-OOS 只會把這個 lookahead artifact 在 5 個期間都複製一遍,而不是揪出它。 

與 K459、K474 的差異

我們在 K459(2025-12 cross-OOS lesson)已記過一次教訓:53% false positive rate 的 cross-OOS。當時的問題是「fold 之間 leakage」(normalize 用了 full sample stats)。K680 與 K459 的差異點在於:K459 是  訓練集污染測試集 ,本案是  同日訊號污染同日 return ——前者是 cross-validation 內部 contamination,後者是 strategy backtest 內部 timing bug。 Cross-OOS 對前者有抵抗力(如果 fold 之間切乾淨),對後者完全沒有抵抗力。 

這是 K680 retraction 最重要的一個方法論教訓: cross-OOS 不是 lookahead 的解藥,而 cross-OOS 通過反而會讓你誤以為訊號乾淨。 


方法與數據

項目設定
資產SPY (S&P 500 ETF), GLD (Gold ETF), ^VIX
期間2006-01-04 ~ 2026-03-26(5,088 個交易日)
策略$w_t = 1 - ext{percentile-rank}(VIX_t, w)$,cash 補餘下,4% 年化 RF
Baseline$w_t = \min(12 / VIX_t, 1.0)$
Portfolio50/50 SPY/GLD(風險資產),cash 用 RF 計息
交易成本5 bps one-way
Bootstrap5,000 replications
Percentile windows126d / 252d / 504d
OOS 期間OOS1 2008-012009-12, OOS2 20112013, OOS3 20152017, OOS4 20202021, OOS5 2023~2024
統計門檻Harvey (2016) |t|>3.0;DM with Newey-West HAC
數據來源yfinance (SPY, GLD, ^VIX)

K680 全部使用 K679 的策略代碼直接擴展—— 這意味著 K679 的所有 bug 自動繼承到 K680 。當訊號層出問題,下游驗證層做得再嚴謹都救不回。


核心發現

圖1:左:full-sample Sharpe,原版 unlagged(K679/K680)vs 修正版 lagged t-1(K686)。Percentile 從 1.680 崩到 0.355(−1.325),12/VIX 從 1.079 崩到 0.431(−0.648),advantage 從 +0.601(spurious)變 −0.076(NS, baseline 反贏)。右:5 個 OOS 期間的 Sharpe diff,K680 buggy 5/5 全綠(橘 bars),K686 修正 1/5(綠 bars,僅 OOS1 GFC 仍勝),平均從 +0.4725 變 −0.1264。

發現一:「太好的結果」是 audit 訊號,不是慶祝訊號

K679 的 Sharpe 1.680 與 t=3.375 在 quant strategy literature 是極為少見的數字。Harvey, Liu & Zhu (2016, RFS) 在「...and the Cross-Section of Expected Returns」對 factor zoo 提出 |t|>3.0 的 multi-test corrected 門檻,能跨過這個門檻的 anchor 結果在 finance literature 是稀有事件。一個從 12/VIX baseline 改成 percentile rank 就能把 Sharpe 拉高 +56% 的策略,第一反應應該是「找 bug」,不是「歡呼」。

我們在 K1121(FRED publication delay)也踩過同類教訓:alt-data allocation Sharpe 1.250 看起來不錯,但 NFCI 觀測時間(週五)與公佈時間(週三)差 5 個 calendar days——隱性 lookahead;修正 shift(5) 後 Sharpe 1.250 → 1.283,但與 baseline 1.309 完全 tied。教訓 E062: 「結果太好」第一反應應該是「找 bug」不是「歡呼」 。K680 是這個教訓的下一個 case study。

發現二:3 個 bugs 同時存在,效應累乘

Codex 審查 K679 時點出三個 timing-related bugs,它們不是獨立的,它們相互放大:

Bug 1:same-day lookahead(最大的污染源)

K679 原始程式碼把 $VIX_t$(當天收盤的 VIX)直接餵進當天的權重 $w_t$,然後計算當天的策略報酬 $r_t^{strategy} = w_t \cdot r_t^{SPY/GLD}$。問題是: $VIX_t$ 與 $r_t$ 是同步觀測的 ——VIX 在 t 日收盤計算,使用了 t 日的 SPX option implied vol;交易者要使用 $VIX_t$ 必須等到 t 日收盤後,但那時 $r_t$ 已經實現。K679 的計算等同於「在 t 日盤前就知道 $VIX_t$」,這是教科書 same-day lookahead。

正確做法:所有訊號 lag 1 天,$w_t = f(VIX_{t-1})$,$r_t^{strategy} = w_t \cdot r_t^{asset}$。pandas 寫法:signal.shift(1)。本研究計畫所有規則手冊都明定 signal from t-1, return at t

Bug 2:paired t-test on gross returns 而非 DM-HLN test on net returns

K679 報告 t=3.375 用的是 paired Student t-test on gross returns(不扣 transaction cost)。這在策略比較 literature 是錯的兩層:

  1.  應該用 DM-HLN test (Diebold-Mariano with Harvey-Leybourne-Newbold small-sample correction):策略報酬之間有 serial correlation(同樣 underlying SPY/GLD),paired t 假設 i.i.d. 不成立。DM 用 Newey-West HAC 修正,bandwidth 通常選 $\lfloor 4(N/100)^{2/9} \rfloor$(K680 case 是 16)。
  2.  應該用 net returns :transaction cost 必扣。Percentile 策略 turnover 17.22%/yr,12/VIX 是 8.16%/yr——percentile 的 cost drag 是 baseline 的 2.11 倍。這個差異在 gross 比較中完全被吞掉。

Bug 3:percentile window 含當天的 VIX

percentile_rank(VIX_t, rolling_252d) 計算時,rolling window 包含了 $VIX_t$ 自己的當日值,這讓 ranking 可以「看到」自己。實務上 percentile rank 應該用 $VIX_t$ 對 ${VIX_{t-252}, ..., VIX_{t-1}}$ 比較, 嚴格 exclude current 。

三個 bugs 的累積效應

三 bugs 加起來的破壞力可從一個簡單實驗看出:純粹 lag 1 天 + DM net + percentile exclude current 後,Percentile Sharpe 從  1.680 崩到 0.355 (−1.325),12/VIX 從 1.079 崩到 0.431(−0.648)。Percentile 因為更依賴 same-day VIX 資訊(rolling rank 對 noise 更敏感),跌幅更大。 修正後 Percentile 反而輸給 12/VIX (−0.076 Sharpe diff),DM t=−1.20 p=0.229 NS。

規格Strategy SharpeBaseline SharpeDiff
K679/K680 unlagged (BUGGY)1.6801.079 +0.601 表面 advantage
K686 lagged t-1 (CORRECTED)0.3550.431 −0.076 NS, baseline 反贏

 +0.601 → −0.076 的差距全部由 lookahead 製造。  換言之,K679 的 +56% Sharpe 改善「看似真實」的部分是 0%——是 100% 的測量誤差。

發現三:cross-OOS 5/5 變 1/5

K686 把修正後的策略重新跑同樣 5 個 OOS 期間。結果如下:

期間Percentile Sharpe12/VIX SharpeSharpe diffDM tDM pWin?
OOS1 GFC0.233−0.152+0.3850.9540.340
OOS2 Recovery−0.280−0.147−0.133−0.5670.571
OOS3 LowVol−0.4230.140−0.563−1.8200.069
OOS4 COVID0.7920.902−0.110−0.2990.765
OOS5 PostHike1.2901.501−0.211−1.2270.221
 聚合  −0.1264  1/5 

Aggregate:avg_sharpe_diff −0.1264,min −0.563(OOS3 LowVol,min 沒過 −0.5 catastrophic threshold),max +0.385,OVERALL_PASS=False。

 K680 的 5/5 全勝徹底翻轉成 K686 的 1/5 全敗。 

Bootstrap 修正後:mean diff −0.0749,95% CI [−0.318, +0.160],含零,pct_positive=26.4%,significant_95=False。

DM full-sample 修正後:t=−1.2037(gross t=−0.6448),都不顯著,Harvey |t|>3.0 FAIL。

這 5/5 → 1/5 的翻轉就是「lookahead artifact」這個術語最直白的證據: bug 在不同時間段的表現一致,因為 bug 本身與時間段結構無關 ——它是 timing 上的 leak,無論 GFC、Recovery 還是 LowVol,每個 OOS 期間裡 percentile 都「偷看」當天 VIX,每段都贏;修正後每段的「偷看優勢」消失,剩下的是真實 noise,Percentile 就輸了 4/5。

圖2:K679 → K680 → Codex audit → K686 → Verdict 的完整 conclusion-overturn timeline。所有事件發生在 2026-03-29 同一天,從原始 PASS(綠)到 Codex 找出 3 個 bugs(橘)到修正後 OVERTURNED(紅)共 6 小時 21 分。這是「audit chain」應該有的速度。

發現四:時間軸——6 小時內從 PASS 到 NS

完整推翻過程的 audit timeline(圖 2):

  •  2026-03-29 10:17  K679 發表(Sharpe 1.680, t=3.375 paired, "本研究計畫最重要的策略創新之一")
  •  2026-03-29 10:23  K680 cross-OOS validation(5/5 wins, avg +0.4725, DM t=3.157, Bootstrap CI [+0.35, +0.83], "should be new default strategy")
  •  2026-03-29 中段  Codex audit(找出 3 bugs:lookahead / t-test / percentile window)
  •  2026-03-29 12:38  K686 corrected re-run(Sharpe 0.355 vs 0.431, DM t=−1.20 NS, 1/5 cross-OOS, "DISAPPEARS — Advantage was an ARTIFACT")

從 PASS 到 OVERTURNED 共 6 小時 21 分。 這是 audit chain 應該有的速度,當你把「結果太好」當訊號,並且立即派 Codex 做 second-pair-of-eyes,時間成本是可接受的。 


實務意義:讀者如何 audit 自己的 backtest

很多讀者跑自己的 backtest 都遇過「Sharpe 高得不像真的」的瞬間。本節給一個實作 audit checklist——如果你在自己策略上看到任何下面的 red flag,先停止慶祝,先做 lookahead audit。

Audit Layer 1:明確的 lag 檢查

任何使用 signal_t → return_t 而沒有顯式 signal.shift(1) 的代碼都該被質疑。Pandas 慣例:

# 錯誤(K679 原版)
df["weight"] = 1 - df["VIX"].rolling(252).rank(pct=True)
df["strat_ret"] = df["weight"] * df["asset_ret"]   # ← lookahead!

# 正確
df["weight"] = (1 - df["VIX"].rolling(252).rank(pct=True)).shift(1)
df["strat_ret"] = df["weight"] * df["asset_ret"]

實作核對:對每個 signal source(VIX、ATR、IV、ML model output)grep 你的 codebase 找 .shift(——應該與 signal source 數量相符。沒有 shift 的 signal 99% 是 lookahead bug。

Audit Layer 2:time-of-day 檢查

對 daily backtest 而言:t 日收盤時你能知道什麼?VIX 收盤值是 t 日收盤後 5 分鐘才公佈,當日的 IV signal 必須等到 t+1 開盤才能交易。對 high-frequency backtest,1 分鐘 bar 訊號用在同 bar return 也是常見 lookahead——bar 60 的 close 是 bar 61 開始才知道的。

實務上:把每個 signal 的 publication time 寫進 metadata,每個策略代碼開頭列「t=X 時可知 vs 不可知」表,再對照訊號 vs 報酬的 indexing。

Audit Layer 3:「結果太好」門檻

設定一個個人的 sanity check threshold:在你的市場、資產類別、頻率下,過去 20 年文獻里 Sharpe 超過 1.5 的 single-asset strategy 有幾個?不多。如果你的 backtest Sharpe > 1.5 而代碼是「下午寫的」,幾乎一定有 bug。 Sharpe > 2.0 的策略應該觸發強制 review ——找另一個人(或另一個 LLM)逐行審代碼。

Audit Layer 4:cross-OOS 不能取代 lookahead audit

K680 教訓最關鍵:cross-OOS 不是 lookahead 的解藥。如果你的 lookahead 是 timing 層級的(signal 與 return 同期),它在每個 fold 都會贏,cross-OOS 會給你 5/5 PASS。 cross-OOS 只能抓 overfit,抓不了 leak。 

正確的 audit 順序:(1) 先做 lookahead 證明(顯式 lag、time-of-day 檢查),(2) 再做 cross-OOS(fold 之間切乾淨),(3) 再做 robustness(windows、costs、subperiods)。順序錯了,cross-OOS 給的「PASS」其實是 false confidence。

Audit Layer 5:DM-HLN 才是 strategy comparison 的正確檢定

K679 用 paired t-test on gross returns 給 t=3.375,是雙重錯誤:(a) 沒扣 transaction cost,(b) 沒 HAC 修正自相關。Strategy comparison 的 standard test 是 Diebold-Mariano with Harvey-Leybourne-Newbold (HLN) small-sample correction,loss function 用 squared residual 或 strategy return 本身,HAC bandwidth 用 Newey-West optimal。本實驗計畫所有 DM 統一遵守此規範。


限制與穩健性

  1.  本文是 K680 的 retraction notice,數值層面以 K686 為準 ——K680 的 cross-OOS 數字(5/5、avg +0.4725、Bootstrap [+0.35, +0.83])全部來自 buggy 程式碼,僅作為「lookahead 後果」的對照樣本展示, 不應該被引用為 percentile 策略的支持證據 。
  2.  K686 是修正版 :lag 後 Sharpe 0.355 / 0.431,Cross-OOS 1/5 wins,DM NS——這是 percentile 策略相對 12/VIX 的真實表現。意即:原假設「VIX percentile 顯著優於 12/VIX」 無法拒絕零假設 。
  3.  修正後絕對水準下降 :兩個策略的 Sharpe 都 < 0.5,比 SPY buy-and-hold 還差。這提示 12/VIX 與 percentile 在這個 50/50 SPY/GLD framework 內可能本身就不是 attractive timing strategy——但這不是本文 retract 的重點,本文僅推翻 K680 對 percentile 「優於 12/VIX」的論斷。
  4.  cross-OOS 期間定義固定 :5 段期間是 K680 設計時就決定,並非 data-mined 出來。期間設計沒問題,問題在訊號層。
  5.  K680 dependent 文章/實驗  我們已 audit:K684(percentile implementation)、K685(live sim)也全部 OVERTURNED;K689 paper trading 中發現 daily_update.py 用了 same-day VIX × return(corr 0.9999 with strategy),歷史 backfill 已標 lookahead 並從產品下架。完整 retraction 鍊在 knowledge.json 中可追溯(K679 / K680 / K684 / K685 / K686 / K689 / K692 / K1027)。
  6.  本研究計畫未來規則  寫進 .claude/rules/experiments.md:lookahead 為最高優先風險,所有 strategy backtest 強制 explicit .shift(1),所有 strategy comparison 強制 DM-HLN with Newey-West,所有 percentile/rank/quantile rolling 強制 exclude current。Codex(或 fresh-context subagent fallback)必審每一個新策略代碼。

結論

K680「VIX percentile 5/5 cross-OOS 全勝、Bootstrap CI 不含零、Harvey PASS」 全部 RETRACTED 。原因:底層 K679 訊號有 same-day lookahead + paired t-test on gross returns + percentile window 含當天三個獨立 bugs,cross-OOS 只是把 lookahead artifact 在 5 個期間複製五次,並非真正的 robustness。K686 修正後 percentile  反而輸給 12/VIX baseline (Sharpe 0.355 vs 0.431, DM t=−1.20 NS),cross-OOS 從 5/5 變 1/5。原優勢 +0.601 Sharpe 是 100% lookahead 製造。

 核心方法論教訓 :

 Cross-OOS validation 是 overfitting 的剋星,但不是 lookahead 的剋星。如果訊號層有 timing leak,cross-OOS 反而會給你 false confidence 的 PASS。Audit 順序必須是:先 prove no lookahead → 再 cross-OOS → 再 robustness。順序錯了,PASS 是危險的。 

這篇 retraction 也是「研究誠實流程」的一個範例:發現太好的結果 → Codex audit → 找出 bugs → 修正重跑 → 公開推翻舊結論 → 寫教訓進規則手冊 → 永久 prevent 同類問題。從 PASS 到 OVERTURNED 共 6 小時。 研究誠實的代價不是不出 bug,而是發現 bug 的速度與公開的勇氣。 


資料來源:yfinance (SPY / GLD / ^VIX),期間 2006-01-04 ~ 2026-03-26,N=5,088 個交易日。

被推翻的 K:K680(cross-OOS validation, 5/5 wins, avg Sharpe diff +0.4725, DM t=3.157)。連帶推翻的下游 K:K679(strategy proposal, Sharpe 1.680)、K684(implementation)、K685(live sim)、K689(paper trading lookahead corr=0.9999)。修正後的 ground-truth K:K686(Percentile Sharpe 0.355 vs 12/VIX 0.431, DM t=−1.20 NS, 1/5 cross-OOS, Bootstrap CI [−0.318, +0.160] 含零)。

實驗檔案:experiments/k680/k680_results.json(buggy ground truth),experiments/k686/k686_results.json(corrected ground truth),experiments/k686/k686_percentile_corrected.py(修正後的策略代碼,含三個 bug fix 註解)。

相關文獻與規則:Harvey, Liu & Zhu (2016) RFS |t|>3.0 multi-test threshold;Diebold & Mariano (1995) JBES + Harvey, Leybourne & Newbold (1997) IJF small-sample correction;Patton (2011) JoE QLIKE loss;本研究計畫 .claude/rules/experiments.md lookahead clause;docs/error_log.md E062(FRED publication delay)與 K1124(TAIFEX active contract lookahead)為近期同類教訓。

相關文章

先讀正式關聯,若無則使用標籤與主題相似度補齊

📄
K1605:區域銀行 M/B 折價與後續波動,橫斷面穩健、OOS 不過關
# K1605:區域銀行 M/B 折價與後續波動,橫斷面穩健、OOS 不過關 *[提出: publication-candidates, 執行: Codex]* ## 摘要 K1605 檢驗一個銀行風險問題:市場價格相對帳面淨值的折價,能不能提前指出區域銀行後續已實現波動率上升。樣本使用 yfinance 免費資料,包含 27 家仍上市美國區域銀行,以及 KRE、KBE 兩個銀行 ETF;主...
📄
新興市場主權債的波動率,能提前預告 EM 股市風暴嗎?一次誠實的否定(K1621)
新興市場的美元主權債,波動起來的時候,會不會比股市早一步聞到火藥味?如果會,那些免費就能取得的債券 ETF 與信用利差,或許能當作跨資產的預警訊號。我們用 2015 到 2026 十一年的資料把這個念頭認真測了一遍,結論很乾脆:**不會**。主權信用的「波動率」和新興市場股票的波動率幾乎是同一時間一起動的,不存在可以拿來做日頻預測的領先關係。 這是一個 NULL 結果,但它是乾淨、可複現、而且對...
📄
K1582:HARQ / SHARK-style 測量誤差修正 HAR-RV 的台指期日盤試驗
## 摘要 [提出: Claude, 執行: Claude] K1582 檢驗 realized quarticity 測量誤差修正與 signed intraday components,是否能改善標準 HAR-RV 的一步期 realized variance 預測。正式可判斷樣本是 TAIFEX TX active-contract 日盤,原始日資料 2,219 筆,樣本外預測 1,697...