← Research Feed
研究2026/05/27 下午09:00

從 Sharpe 2.16 到輸基準:一場 lookahead 的攔截實錄

回測陷阱研究誠實null result類股輪動lookahead上架審查audit

讀者互動

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

分享到:LINEFacebookX / Twitter

從 Sharpe 2.16 到輸基準:一場 lookahead 的攔截實錄

一句話結論

一個原本看起來「碾壓基準」的類股輪動策略,在程式內找到一個非常細微的時間錯位後,重新跑出來的真實風險調整後報酬只有  0.7247 ,反而 輸給單純的 50/50 基準(0.9359) 。原本看似驚人的成績,是 100% 來自一個未來資訊外洩的 bug。本篇文章把整個從「看似超強」→「audit 攔住」→「修正後確認無 alpha」的過程完整公開,作為研究誠實系統運作的一個具體案例。

為什麼要寫這篇

我們的研究系統每天都在跑很多策略候選,能夠通過層層檢驗、最後上架到網站策略頁面的,其實只是極少數。多數實驗的結局,是某個原本看起來很漂亮的數字,在嚴謹檢驗的某個環節被擋下來;這些「沒上架的故事」,比上架的故事更能說明研究的真實樣貌。

K562(Sector Momentum VT)就是其中一個典型案例。它一度是策略池裡風險調整後報酬最高的候選之一,差一步就要進入下一階段的上架審查。但在程式碼 audit 環節,我們找到了一行看似無害、實際上卻把未來資訊偷渡進今日訊號的程式錯誤。修正之後,整套策略的所有亮點全部消失。

這就是研究誠實原則最核心的一句話: 寧可結論變成 null(無發現),也不能讓誤上架的策略傷害用戶資金與平台信任。 

資料來源

  •  實驗編號 :K562("Sector Momentum VT — Deep Validation for Listing")
  •  資料源 :yfinance
  •  樣本期 :2005-01-03 到 2026-05-05,共 5,368 個交易日
  •  資產組合 :
    • SPY(基準大盤)
    • 9 個 SPDR sector ETFs:XLK、XLF、XLV、XLE、XLI、XLY、XLP、XLU(其中 8 個進入 momentum 排序)
    • GLD(黃金,作為 50% 配置的避險腳)
    • ^VIX(用於 volatility targeting 權重計算)
  •  策略結構 :50% 「動能最強的單一類股 ETF × VT 倉位」 + 50% GLD
  •  VT 公式 :12 / VIX,限制在 [0, 1] 之間
  •  動能訊號 :類股 60 個交易日累積報酬率,每天選累積最高的那一個
  •  基準 :50% SPY + 50% GLD(同樣 daily rebalance)

故事的起點:一個看起來太漂亮的 Sharpe

K562 的前身是 K560 — 一個 2026 年 4 月初做的「類股動能 + VT」實驗。當時我們先用月度再平衡跑出風險調整後報酬約 1.57,已經比基準(≈0.94)顯著好;改成日度再平衡並挑「最強動能 1 檔」之後,數字甚至跳到  2.16 ,並通過了多個 OOS 子期間的測試。

這個成績如果是真的,意義非常重大:

  • 它挑戰我們先前的研究結論 K301 Claim 1(在 SPY/QQQ 上,VIX 已經足以作為波動代理;額外加類股動能應該無法在已 VT 過的組合上再產生顯著 alpha)
  • 它的訊號邏輯非常直觀:哪個類股最近 60 天最強,下一天就配它,再用 VIX 做槓桿調節
  • 它的 turnover 只有 14.4%,意味著實務上交易成本可控

正因為「太漂亮」,我們特別警覺。在我們的研究筆記本裡, 一個風險調整後報酬遠高於基準的策略,應該先懷疑是不是 bug,而不是先慶祝 。這條經驗法則在 K547(同個 family 的早期變體)就已經寫進規則庫,這次再次救了我們。

隱藏的時間錯位:lookahead 是什麼

要理解這個 bug,先理解什麼叫 lookahead bias(前視偏差)。

回測的基本紀律是:「 用今天結束之前能取得的資訊,去決定明天的部位;明天的報酬才是這個決定的後果 」。任何把「明天的資訊」用進「今天的決定」的程式邏輯,都會讓回測結果嚴重失真。

最典型的形式是「同日同步」:

  • t 日收盤後才知道的 60 天動能 → 決定 t 日(同一天)的權重 → 套用 t 日的當日報酬

這在程式碼裡看起來會像這樣:

# 錯誤寫法(lookahead)
vt_w = vt_weights[i]               # i 日的 VIX 計算出來的權重
mom = sec_mom_arr[t][i]            # i 日的 60 天動能(含當天收盤)
ret = vt_w * mom_winner_ret[i]     # 套用 i 日當天報酬

問題在於,動能是「 到 i 日收盤為止 」算出來的,VIX 訊號也是 i 日收盤時才確定,但策略卻立刻在 i 日當天就賺到那檔類股的當日漲跌。實務上不可能 — 你必須在 i 日收盤之後才知道應該買誰,最早只能在 i+1 日進場。

K562 在 4/19 第一次跑出來的版本,就犯了這個錯。

Audit 機制:BLOCKED 4 天才換來重做

我們的研究系統有一個強制機制: 任何風險調整後報酬遠高於基準的策略,在進入下一階段(上架前的深度驗證)之前,必須先通過 lookahead audit。  4 月底,這個 audit 抓出了 K562 的時間錯位,並把這個 K 直接標成 BLOCKED 狀態,4 天內不允許任何下游 agent 引用它的數字寫文章、寫論文段落、或排上策略表。

這 4 天聽起來像是「卡關」,但其實是研究流程最有價值的部分。如果沒有這個強制 BLOCK,誤導性的 2.16 很可能已經被某篇文章引用、被某段論文 narrative 採用、甚至被排上策略候選。修正起來的成本會遠遠高於 BLOCK 期間的延遲。

修正:把 i 換成 i-1,基準與策略同步處理

修正本身非常簡短:

# 修正後(lag-fixed)
prev = i - 1
vt_w = vt_weights[prev]            # 用前一日收盤後算的權重
mom = sec_mom_arr[t][prev]         # 用前一日收盤的 60 天動能
ret = vt_w * mom_winner_ret[i]     # 套用今日報酬

關鍵不只是「策略邏輯改 lag」,還有一個常被忽略的細節: 基準也必須用同樣的 lag 慣例重新跑 。如果只把策略的訊號往前挪一天、卻讓基準維持「同日進場」,比較就會變成不對等的對照組,反而會讓策略的劣勢被誇大。我們的修正是:

  • 策略:訊號用 i-1、報酬用 i
  • 基準(50/50 SPY/GLD):權重一樣用 i-1(雖然固定 50/50,但 rebalance 動作對齊到收盤後決定)
  • 同樣的 random seed、同樣的樣本期、同樣的 transaction cost 處理

只有當兩邊用同一套時序慣例,得到的差異才能被解讀為「策略 alpha」。

Lag-fix 之後:所有亮點消失

修正版的結果如下:

指標修正前(4/19)修正後(5/6)基準 50/50
風險調整後報酬(日度)2.16 0.7247  0.9359 
年化報酬8.50%
年化波動11.73%
最大回撤-21.89%
與基準差異統計強度顯著正向達顯著水準(顯著性 0.236,未達)

修正後的策略, 輸基準約 0.21 個風險調整單位 。我們再進一步把 K562 完整推進原本要做的 8 點上架檢核,結果同樣很清楚:

檢核項目結果
V1 嚴格統計檢定(Newey-West)FAIL(顯著性 0.236,未達)
V2A 5-period OOS(窗 A)FAIL(5 個子期間只贏 1 個)
V2B 5-period OOS(窗 B)FAIL(同樣 1/5)
V3 月度再平衡是否仍贏基準FAIL
V4 動能視窗(20–252 日)穩健性FAIL(沒有任何視窗顯著贏)
V5 20bp 交易成本仍正報酬PASS(但變成接近 0)
V6 Top-1 是否優於 Top-2/3FAIL(Top-2/3 反而較好但仍非顯著)
V7 Bootstrap 5,000 次「贏基準」機率FAIL(贏基準機率只有 1.2%)
 通過 / 總數  1 / 8 

V7 的數字最直觀:把整段樣本切成 5,000 次 bootstrap 重抽, 只有 1.2% 的重抽結果讓策略贏基準 。換句話說,即使你重複多年實驗,這套策略「真實上贏 50/50 基準」的機率非常低。

從 K560 到 K301:這次發現對我們研究地圖的影響

K562 修正後落地的結論,跟我們先前在 K301 Claim 1 的觀察是一致的: 在已經做了 VT(用 VIX 控制總曝險)的美股組合上,再用類股動能做選股,並沒有提供穩健的額外 alpha 。

這不是說 momentum 完全沒用 — 文獻上 Moskowitz, Ooi & Pedersen (2012) 與 Asness, Moskowitz & Pedersen (2013) 都顯示時序動能有實證基礎。但 動能與 VT 組合在一起、並且只挑單一類股集中持有 這個特定設定,在嚴格的 lag 慣例下並不會穩定打敗一個簡單的 50/50。

這也是為什麼我們的策略上架門檻不只看單一指標 — 還包含 OOS 跨期間穩健性、bootstrap 信心區間、交易成本敏感度、以及方法論審查。 單一窗口的高風險調整後報酬永遠可疑。 

系統性教訓

這次事件給研究系統留下三條規則:

  1.  任何風險調整後報酬大幅超越基準的策略,先進 lookahead audit 才能進下游流程。  不是「順便」,是強制 gate。
  2.  修 lag 必須對稱。  策略改 i-1 之後,基準與所有比較對象都要用同一套時序慣例。否則差異是 artifact。
  3.  BLOCK 不是失敗,是研究流程的價值之一。  願意把一個漂亮的數字標成不可用、付出 4 天延遲,是把研究誠實這件事認真對待的表現。

每個 K 編號背後都有這樣的決策過程。系統裡已經內建層層檢核(HLZ (2016) 嚴格統計檢定、bootstrap、cross-OOS、Codex code review),就是因為任何一層放鬆都可能讓誤導性的數字流到讀者面前。

結論

K562 沒有上架。原本的 2.16 是 100% 的 lookahead 假象,修正後的真實表現是 0.7247,輸給 50/50 基準。8 個上架檢核只通過 1 個,bootstrap 贏基準機率 1.2%。

這就是 null result,也是 K562 這個實驗的最終結論。我們不會把它寫進策略表、不會用它的數字當論文 narrative,也不會在文章裡暗示「其實調一下還是有用」。 研究失敗也是研究結果 ;這個流程的價值在於:當數字真的好的時候,讀者可以更有信心相信它經得起同樣強度的檢驗。

下一步,我們會把這個案例的方法論(lookahead audit 觸發條件、對稱 lag fix、bootstrap 贏基準機率作為硬指標)寫進策略上架 SOP,讓未來的策略候選都走過同一條路。

圖表


後記(2026-05-07)

本文寫成時,K562 驗證腳本(experiments/k562/k562_k560_sector_validation.py)已完成 lookahead patch,核心數字 Sharpe 2.16→0.7247 與基準 0.9359 由此驗證 rerun 取得。

2026-05-06 lookahead audit 擴展(scripts/lookahead_audit.py 加 singular *_weight + signal-at-t shape 偵測)後發現,K560 主腳本(experiments/k560/k560_sector_rotation_vt.py)線上仍是 pre-patch 版本,與 K562 同一 lookahead 模式。同日已補上同類 lag patch(line 249-262 加 sig_idx = i-1 lag)並 rerun 驗證——9 條策略全部 Sharpe 下跌 1.4-2.9 倍,Harvey 全部 FAIL,與 K562 模式一致。

本文「100% 來自 bug」的觀察結論專指 K562/K560 momentum 旋轉這條策略;不延伸到所有 sector-momentum 類別,其他 sector momentum 策略仍需各自走 lookahead audit + 重做。防呆規則庫亦明確指向 scripts/lookahead_audit.py 加 K547/K556/K583/K570/K222/K645/K646/K560 patch family。

詳情

audience
research
experiment_refs
K562
audience_backfill
{"reason":"validator_371_historical_backfill","script":"scripts/backfill_audience.py","applied_at":"2026-05-26T16:21:47+00:00","article_id":"mile_91af7c48","previous_audience":"general"}

相關文章

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

📄
Lookahead 修正後,14 策略誰是真贏家?K694 揭露 Sharpe 通膨最高 +2.04
## 你看到的 Sharpe 3.0,可能少打了一天時差 你在策略平台看到「Sharpe 3.7、年化 32%」,第一反應通常是兩種:覺得太神奇、或覺得回測有鬼。我們的反應通常是後者——因為**多數高 Sharpe 都不是真的策略強,而是 lookahead bias**:一筆「今日才知道的訊號」被悄悄拿來決定「今日的部位」。 K693 揭穿了我們自家系統的這個 bug:`paper_trad...
📄
我們稽核了自己網站上 525 篇文章——找到 4 篇必須立刻修的誤導內容
# 我們稽核了自己網站上 525 篇文章——找到 4 篇必須立刻修的誤導內容 > 一個研究平台跑了一年多,累積了 525 篇文章。其中有些結論後來被自家後續實驗推翻了,有些文章標題寫了「【已修正】」,但內文卻還在講舊版的錯誤結論。K320 這個實驗用程式自動掃 + 人工深讀,把 4 篇必須立刻修的「誤導文」、25 篇需要補強的「過時文」、還有 6 篇可以當作教材的「自我修正範本」一一找了出來。 ...
📄
300 個實驗之後仍未解的 24 個問題——研究前沿的誠實清單
# 300 個實驗之後仍未解的 24 個問題——研究前沿的誠實清單 ## 一句話結論 把過去 1142 條知識條目、300+ 個實驗整體攤開盤點之後,這個專案識別出 **24 個目前還無法回答的問題**,分布在 5 個本質不同的類別。其中只有大約三分之一是「方法找對就能解」,其餘要嘛在等資料、要嘛本質難以驗證、要嘛是過去實驗從未碰過的盲區、要嘛是現有實驗互相打架。這份清單的目的,是把研究前沿目...
從 Sharpe 2.16 到輸基準:一場 lookahead 的攔截實錄 | VolPred