K680 RETRACTED:Cross-OOS 5/5 全勝是 lookahead artifact——研究誠實流程的一個範例
讀者互動
已追蹤瀏覽 0 次,登入會員可按讚與收藏。
摘要
[提出: 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_days | Percentile Sharpe | 12/VIX Sharpe | Sharpe diff | DM t | DM p | Win? |
|---|---|---|---|---|---|---|---|
| OOS1 GFC | 505 | 1.227 | 0.4603 | +0.7667 | 2.2705 | 0.024 | ✓ |
| OOS2 Recovery | 754 | 1.1686 | 0.4605 | +0.7081 | 2.6933 | 0.007 | ✓ |
| OOS3 LowVol | 755 | 0.908 | 0.6281 | +0.2799 | 0.0844 | 0.933 | ✓ |
| OOS4 COVID | 505 | 2.2809 | 1.8217 | +0.4592 | 1.2946 | 0.196 | ✓ |
| OOS5 PostHike | 502 | 2.2016 | 2.0531 | +0.1485 | −0.2068 | 0.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)$ |
| Portfolio | 50/50 SPY/GLD(風險資產),cash 用 RF 計息 |
| 交易成本 | 5 bps one-way |
| Bootstrap | 5,000 replications |
| Percentile windows | 126d / 252d / 504d |
| OOS 期間 | OOS1 2008-01 |
| 統計門檻 | Harvey (2016) |t|>3.0;DM with Newey-West HAC |
| 數據來源 | yfinance (SPY, GLD, ^VIX) |
K680 全部使用 K679 的策略代碼直接擴展—— 這意味著 K679 的所有 bug 自動繼承到 K680 。當訊號層出問題,下游驗證層做得再嚴謹都救不回。
核心發現

發現一:「太好的結果」是 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 是錯的兩層:
- 應該用 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)。
- 應該用 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 Sharpe | Baseline Sharpe | Diff | 注 |
|---|---|---|---|---|
| K679/K680 unlagged (BUGGY) | 1.680 | 1.079 | +0.601 | 表面 advantage |
| K686 lagged t-1 (CORRECTED) | 0.355 | 0.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 Sharpe | 12/VIX Sharpe | Sharpe diff | DM t | DM p | Win? |
|---|---|---|---|---|---|---|
| OOS1 GFC | 0.233 | −0.152 | +0.385 | 0.954 | 0.340 | ✓ |
| OOS2 Recovery | −0.280 | −0.147 | −0.133 | −0.567 | 0.571 | ✗ |
| OOS3 LowVol | −0.423 | 0.140 | −0.563 | −1.820 | 0.069 | ✗ |
| OOS4 COVID | 0.792 | 0.902 | −0.110 | −0.299 | 0.765 | ✗ |
| OOS5 PostHike | 1.290 | 1.501 | −0.211 | −1.227 | 0.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。

發現四:時間軸——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 統一遵守此規範。
限制與穩健性
- 本文是 K680 的 retraction notice,數值層面以 K686 為準 ——K680 的 cross-OOS 數字(5/5、avg +0.4725、Bootstrap [+0.35, +0.83])全部來自 buggy 程式碼,僅作為「lookahead 後果」的對照樣本展示, 不應該被引用為 percentile 策略的支持證據 。
- K686 是修正版 :lag 後 Sharpe 0.355 / 0.431,Cross-OOS 1/5 wins,DM NS——這是 percentile 策略相對 12/VIX 的真實表現。意即:原假設「VIX percentile 顯著優於 12/VIX」 無法拒絕零假設 。
- 修正後絕對水準下降 :兩個策略的 Sharpe 都 < 0.5,比 SPY buy-and-hold 還差。這提示 12/VIX 與 percentile 在這個 50/50 SPY/GLD framework 內可能本身就不是 attractive timing strategy——但這不是本文 retract 的重點,本文僅推翻 K680 對 percentile 「優於 12/VIX」的論斷。
- cross-OOS 期間定義固定 :5 段期間是 K680 設計時就決定,並非 data-mined 出來。期間設計沒問題,問題在訊號層。
- 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)。
- 本研究計畫未來規則 寫進
.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)為近期同類教訓。
相關文章
先讀正式關聯,若無則使用標籤與主題相似度補齊