跳轉到

怎麼制定一個 plan

所屬章節:入門 從「我想做一個策略」到「可以交易的東西」中間的完整流程 配合 how-to-ask.md 使用:那頁教怎麼問一句話,這頁教怎麼規劃一件事

為什麼要有 plan

how-to-ask.md 的案例 2 示範了一件事: 「用月營收 YoY 選前 10 檔」這句話每個字都被正確執行, 結果是 +95.77%、輸給 0050 的 +296.42%。

問題不在執行,在沒有 plan——直接從「想法」跳到「回測」, 中間的資料清洗、特徵檢驗、防洩漏全部跳過了。

這頁講的就是中間那段。


六階段流程

① 定義問題    你在賭什麼?用什麼衡量?
     ↓
② 資料盤點    有什麼、多長、什麼單位、什麼頻率
     ↓
③ 資料清洗    倖存者偏差、單位、對齊、極端值
     ↓
④ 特徵工程    原始欄位 → 可比較的分數
     ↓
⑤ 訊號驗證    在回測之前先確認特徵有用
     ↓
⑥ 回測        才輪到這一步

大部分人直接從 ① 跳到 ⑥。 每跳過一階,錯誤就會被埋進結果裡, 而且不會報錯——見 errors.md 的 13 項「不報錯但結果是錯的」。


① 定義問題

開工前先用一句話回答這三題:

題目 爛答案 好答案
你在賭什麼? 「找會漲的股票」 「營收動能領先股價反應,公告後一個月有超額報酬」
拿什麼比? (沒想過) 「0050 買進持有」或「加權指數」
多爛算失敗? (沒想過) 「Sharpe < 1 或輸給 0050 就放棄」

第三題最常被跳過。 沒有預設的失敗標準,就會不斷調參數直到好看為止—— 那不是研究,是過度配適。

提問範本

我想驗證一個假設:⟨假設⟩
對照基準用 ⟨0050 / 加權指數⟩
如果 ⟨Sharpe < 1 / 輸給基準 / 交易次數 < 30⟩ 我就放棄這個方向

先幫我規劃流程,不要急著寫回測

最後那句很重要——它讓我先給你 plan 而不是直接跳到程式碼。


② 資料盤點

在寫任何一行邏輯之前,先確認資料的邊界。

四個必問

問題 為什麼 在哪查
有多長? 日頻硬上限 1000 筆(約 4.1 年)。「近五年」做不到 data-history.md
什麼單位? _千 ÷1e5 得億、_百萬 ÷1e4 得億。成交量整股是張、期貨是口 msmp-tables.md
什麼頻率/index 型態? 月營收 index 是字串 '2026-M08',不是日期;季頻是 '2026-Q2' data-history.md
資料日到哪? api.MSMP 只到前一交易日;美股比台股晚一天 msmp.md

提問範本

我要用 ⟨月營收 / 季財報 / 籌碼⟩ 做研究,先幫我盤點:
① 這張表有多長歷史、index 是什麼型態
② 每個欄位的單位是什麼
③ 資料日到哪一天
④ 有沒有「看起來有但其實沒有」的欄位

先不要寫策略

實例:月營收的時間軸

月_營收 index:  '2026-M08'  (字串,營收所屬月份)
INDEX['M']:     2026-09-10  (Timestamp,公告日)
                    ↑
            差一個月!8 月營收在 9/10 公告

搞錯這個就是未來函數——用還沒公告的資料選股,回測會漂亮得不真實。


③ 資料清洗

這一階最容易被跳過,也最容易埋雷。

四個必做

a. 倖存者偏差

pools = api.MSMP.list
# ❌ 只用現在還在市場上的
universe = pools['tse']

# ✅ 想清楚要不要含已下市的 145 檔
pools['delist']        # 已下市

只用現存股票回測,等於事先知道誰沒倒。這會系統性高估報酬。

b. 股池純度

universe = set(pools['tse']) - set(pools['etf']) - set(pools['KY']) - set(pools['DR'])

ETF 沒有月營收、KY 股財報認列不同、DR 是存託憑證。混在一起排名沒有意義。

c. 單位

# ❌ 成交額_百萬 當成千元
last_vol = tvol.iloc[-1] / 1e5        # 錯!只剩 1 檔通過

# ✅ 百萬 -> 億是 /100
last_vol = tvol.iloc[-1] / 100        # 對,569 檔通過

這是真實發生過的錯誤。過濾條件從「篩掉不流動的」變成「篩掉全部」, 而且不會報錯,只是選股結果莫名其妙。

d. 極端值

入選股 YoY 中位數 +464%,最大 +1,073,121%
YoY > 200% 佔 83.8%

+1,073,121% 不是成長,是除以零——去年同期營收趨近於零。 純排序會讓這種公司佔滿所有名額。

處理方式三選一:設上限、改用累積值、加規模門檻。

提問範本

在算任何特徵之前,先幫我做資料清洗:
① 股池要排除 ETF / KY / DR,並告訴我要不要含已下市
② 每個欄位的單位換算列出來給我確認
③ 檢查極端值分布,告訴我前 1% 長什麼樣
④ 檢查缺失值比例

做完給我一份「清洗前 vs 清洗後」的檔數對照

最後那句讓你看見每一層砍掉多少——就是上面那個 /1e5 錯誤被抓出來的方式。


④ 特徵工程

原始欄位不能直接比較,要先轉成同一個尺度。

三種常見做法

做法 寫法 什麼時候用
Percentile 排名 d.rank(axis=1, pct=True) 跨股票比較。不受極端值影響
Z-score (d - d.mean()) / d.std() 需要保留距離資訊時
分位數分組 pd.qcut(d, 5) 要看單調性(第 1 組到第 5 組是否遞增)

多因子合成的標準寫法

sc = None
for tbl, col, direction in FACTORS:
    src = getattr(api.MSMP, tbl)
    d = getattr(src, col)
    # ★ 先套可交易遮罩再排名,母體才一致
    p = d.where(tradable).rank(axis=1, pct=True)
    p = p if direction == 'long' else 1.0 - p
    sc = p.fillna(0.5) if sc is None else sc + p.fillna(0.5)
sc = (sc / len(FACTORS)).where(tradable)

三個關鍵:

  1. 先遮罩再排名——否則排名的母體包含買不到的股票
  2. 反向因子用 1.0 - p——不要用負號
  3. 缺值填 0.5——中性,不獎勵也不懲罰

對齊:最容易錯的一步

財報/營收的公告日不會剛好落在換股日:

# ❌ 直接 reindex,公告日對不上換股日就全變 NaN
d = src.reindex(R)

# ✅ 先 union 再 ffill 再 reindex
d = src.reindex(src.index.union(R)).ffill().reindex(R)

ffill 的意思是「用最近一次已公告的值」——這才符合真實交易情境。

提問範本

幫我把這些原始欄位轉成可比較的特徵:
⟨欄位清單,標註正向/反向⟩

要求:
① 用 percentile 排名,先套可交易遮罩再排名
② 反向因子用 1-p,缺值填 0.5
③ 財報對齊用 union → ffill → reindex,不要直接 reindex
④ 做完給我每個因子的分布統計和缺值比例

⑤ 訊號驗證

這一階是回測的前置,不是回測本身。 目的是在花時間跑回測之前, 先確認特徵跟未來報酬有沒有關係。

三個檢驗

檢驗 做什麼 看什麼
IC(資訊係數) 特徵值 vs 未來 N 日報酬的相關係數 絕對值 > 0.03 算有訊號
分組單調性 按特徵分 5 組,看各組平均報酬 第 1 組到第 5 組要單調遞增
換手率 W.diff().abs().sum(axis=1) 太高會被成本吃掉

為什麼重要

案例 2a 那個策略,跑完整個回測才發現選到的是財報異常公司。 如果先做分組檢驗,第一步就會看到最高分那組的報酬不是最好的。

提問範本

在回測之前先幫我驗證訊號:
① 算每個因子的 IC(特徵 vs 未來 20 日報酬)
② 按因子分 5 組,看各組平均報酬是否單調
③ 算換手率,估算成本會吃掉多少

如果 IC 太低或不單調,直接告訴我這個因子沒用,不用跑回測

最後那句省下大量時間。


⑥ 回測

前五階做完,這一階反而最單純。

三個必設

【部位】固定金額(qty >= 1, unit=1)還是目標權重(qty < 1)?
        → 固定金額:capital 不生效、無複利
        → 目標權重:capital 生效、有複利
【成本】台股個股 fee=0.001425, tax_short=0.003, unit=1
        台股 ETF  tax_short=0.001
        台指期    fee=500, unit=200, tick_map=[(inf,1)]
【對照】generate_report(core_dic, benchmark=...)

必問的兩句

① 扣成本前的毛損益是多少?成本佔投入資金幾 %?
② 回測期間實際是哪一段?

案例 1 的 MA20 版本總報酬 -11.31%,但毛損益是 +22,000—— 虧損幾乎全是成本。看到負數就放棄是誤判。


完整的 plan 提問範本

把整個流程一次講清楚:

我想驗證:⟨假設⟩
對照基準:⟨0050 / 加權指數⟩
失敗標準:⟨Sharpe < 1 就放棄⟩

請按這個順序做,每一階做完先給我結果再繼續:

① 資料盤點——歷史長度、單位、index 型態、資料日
② 資料清洗——股池純度、單位換算、極端值分布、缺值比例
   ※ 給我「清洗前 vs 清洗後」的檔數對照
③ 特徵工程——percentile 排名、先遮罩再排名、union→ffill→reindex
④ 訊號驗證——IC、分組單調性、換手率
   ※ 如果訊號太弱就直接說,不要硬跑回測
⑤ 回測——目標權重輪動、正確成本、加 benchmark
⑥ 跑完告訴我這個規格有什麼缺陷

不要跳階段,也不要在②之前寫任何策略邏輯

「每一階做完先給我結果再繼續」 是最重要的一句—— 它讓你在每個檢查點都能喊停,而不是等到最後拿到一個沒法信任的數字。


常見的流程錯誤

錯誤 後果 怎麼避免
直接從想法跳到回測 埋一堆不報錯的雷 按六階段走
沒定義失敗標準 調參數調到好看為止 ① 先寫死標準
用現存股票回測 倖存者偏差,高估報酬 ③ 想清楚要不要含 delist
直接 reindex 財報 全變 NaN 或用到未公告資料 ④ union → ffill → reindex
跳過訊號驗證 浪費時間跑沒用的回測 ⑤ 先看 IC 和單調性
只看總報酬 看不出是策略還是成本的問題 ⑥ 一定要問毛損益和成本佔比
尋優後就相信 過度配適 看 TotalTrades,切前後兩段分別驗

相關頁