Share Notes

chundev

View the Project on GitHub latteouka/share-notes

策略為什麼有效:用機制檢驗取代重跑回測

日期:2026-05-22 主題:量化策略的機制驗證|資料:期貨交易所三大法人持倉


TL;DR

一個回測績效漂亮、統計檢定也過關的策略,仍然可能是過度擬合——因為「績效好」和「機制說得通」是兩回事。與其再跑十次回測,不如直接檢驗策略敘事的因果鏈。本文記錄四個可轉移的檢驗方法:跨商品轉移測試、把敘事拆成可否證環節、用零和恆等式做資料分解、換語言獨立重算。實作後發現:策略的核心機制成立,但長年沿用的「敘事」有一半是錯的——而策略一樣有效,因為它靠的不是錯的那半。


背景

手上有一個以 Put/Call Ratio(P/C)為核心進場訊號的期貨策略:P/C 衝高時做多。回測八年、勝率約 70%,而且做過完整的統計驗證——隨機進場檢定(p < 0.0001)、Bootstrap 信賴區間、Walk-Forward、Runs Test 全部過關。

問題是:這些驗證都只回答「績效是不是運氣」,沒有回答「為什麼會賺」。一個過度擬合的策略,在它失效之前,長相和一個真有 edge 的策略一模一樣。少了機制解釋,你無法分辨「最近一段回撤」是正常波動還是 edge 已經消失——你沒有熄火判斷標準(kill criterion)

所以這次不再跑回測,改成檢驗機制本身。


為什麼「再跑回測」不夠

統計驗證和機制驗證回答的是不同問題:

工具 回答的問題 回答不了的問題
統計驗證(隨機進場、Bootstrap、WF) 績效是不是運氣? 為什麼會賺?何時會失效?
機制驗證 edge 的因果來源是什麼? ——

再跑一次回測,只是把第一欄的事重做一遍。要回答第二欄,得換工具。


方法一:跨商品轉移測試 — 一次幾乎免費的外樣本

策略原本交易某權值龍頭股的期貨。把「完全相同的進出場訊號」改成交易另一個標的(對應大盤的兩倍槓桿 ETF),不動任何參數。

結果:勝率 70.0% → 68.6%,每筆平均報酬 +2.4% → +3.1%——幾乎重疊。

這是一次幾乎免費的外樣本測試。如果策略是過度擬合原標的的特異性,把訊號搬到另一個標的應該崩掉;它沒崩,代表 edge 抓的是整個大盤的 beta/趨勢,不是單一個股的特性。

換標的而 edge 存活,比「在原標的再測十次」更能反駁過度擬合——因為它真的換了一組獨立的價格資料。


方法二:把因果敘事拆成可否證的環節

策略長年的敘事是一句話:「P/C 高 = 大戶在漲勢中買 put 避險 = 趨勢會延續」。這句話太滑溜,整句沒辦法檢驗。把它拆成兩個各自可否證的環節:

拆開的好處是:兩個環節用不同資料、可以各自成立或失敗。混在一句話裡,你只會得到二元的「成立/不成立」,看不出是哪一段斷掉。

用交易所公布的三大法人持倉(期貨/選擇權未平倉量,2019–2026,1789 個交易日)對 P/C 檢定:

環節 A — ✅ 成立。 P/C 與三大法人指數期貨淨多單,Spearman 相關 +0.32;分桶看更清楚:

P/C 桶 三大法人指數期貨淨多單(口)
< 90(冷清) −4,579
90–130(正常) −340
130–150 +6,561
≥ 150(極高) +8,527

策略實際進場的那些日子,法人淨多單 +7,489 口 vs 全樣本平均 +1,174 口。→ 訊號可靠地篩出「法人重壓指數多單」的市場狀態。

環節 B — ⚠️ 敘事需修正。 P/C 與三大法人的 put 淨部位反而是負相關。再拆到各類法人:大型外資的 put 避險部位是「常態固定」的、跟 P/C 高低幾乎無關;隨 P/C 波動的,其實是造市商在賣 put、以及散戶在買。「大戶加碼買 put」這個因果說法,不精確。

關鍵收穫:策略的 edge 靠環節 A,不靠環節 B。 敘事錯的那半(B)只是色彩說明;承重的那半(A)成立。敘事可以修,策略不用動。


方法三:零和恆等式 — 缺資料時的分解技巧

環節 B 想追問「P/C 衝高時,put 到底是誰在買」。直覺要市場總成交量,但資料源沒有、逐檔抓又太重(數百萬列)。

繞過去的方法是用未平倉量(open interest)的零和性質:每一口合約必有一個多方、一個空方,所以全市場「多方未平倉總和 = 空方未平倉總和」,把多方記正、空方記負,全體加總恆為 0。

於是:

非三大法人的 put 淨部位 = −(三大法人的 put 淨部位)

這是恆等式,不是估計。三大法人的部位資料拿得到,一相減就得到「散戶+其他」的淨部位,完全不需要市場總量。

結果:P/C 從正常區到極高區,put 淨多單從三大法人「移轉」到散戶手上(三大法人 +15,764 → −4,659 口;散戶相應 −15,764 → +4,659 口)。

通用心法:當你缺一份資料,先看看有沒有守恆律(零和、總量守恆、會計恆等式)能讓你從已有的部分反推出缺的那塊。


方法四:換語言獨立重算 — 排除實作 bug

主分析是 TypeScript 寫的。要確認結論不是程式 bug 或數字抄錯,我用 Python 重寫一份獨立驗證:

結果:1789 個交易日逐筆比對,0 筆不符;分桶均值、進場日統計、相關係數全部一致。

心法:同一個人用同一種語言重看自己的程式,容易把同樣的 bug 再讀過去一次。換一種語言、從原始資料重來,才能真正排除實作錯誤和轉錄錯誤。 兩份獨立實作得到同樣答案,信心才足夠。


更深的一點:分清「承重牆」與「敘事色彩」

這次最值得記的一點:一個策略的說明文字裡,有些句子是承重牆(拿掉策略就垮),有些只是敘事色彩(好聽、幫助理解,但不影響盈虧)。

過去的敘事把一個正確的結論綁在一個不精確的機制上。檢驗之後才看清楚:結論對、機制要修,而結論才是承重的那個。

做策略研究(或任何模型)時值得定期問自己:我這套說法裡,哪幾句真的承重?哪幾句只是讓故事順?分清楚,你才知道哪裡禁得起打、哪裡其實可以放手改。


學到的事


參考資料