怎麼制定一個 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 就放棄」 |
第三題最常被跳過。 沒有預設的失敗標準,就會不斷調參數直到好看為止—— 那不是研究,是過度配適。
提問範本
最後那句很重要——它讓我先給你 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 是什麼型態
② 每個欄位的單位是什麼
③ 資料日到哪一天
④ 有沒有「看起來有但其實沒有」的欄位
先不要寫策略
實例:月營收的時間軸
搞錯這個就是未來函數——用還沒公告的資料選股,回測會漂亮得不真實。
③ 資料清洗
這一階最容易被跳過,也最容易埋雷。
四個必做
a. 倖存者偏差
pools = api.MSMP.list
# ❌ 只用現在還在市場上的
universe = pools['tse']
# ✅ 想清楚要不要含已下市的 145 檔
pools['delist'] # 已下市
只用現存股票回測,等於事先知道誰沒倒。這會系統性高估報酬。
b. 股池純度
ETF 沒有月營收、KY 股財報認列不同、DR 是存託憑證。混在一起排名沒有意義。
c. 單位
# ❌ 成交額_百萬 當成千元
last_vol = tvol.iloc[-1] / 1e5 # 錯!只剩 1 檔通過
# ✅ 百萬 -> 億是 /100
last_vol = tvol.iloc[-1] / 100 # 對,569 檔通過
這是真實發生過的錯誤。過濾條件從「篩掉不流動的」變成「篩掉全部」, 而且不會報錯,只是選股結果莫名其妙。
d. 極端值
+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.0 - p——不要用負號 - 缺值填
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,切前後兩段分別驗 |
相關頁
- 怎麼問,才問得到你要的東西:單句提問的三組案例
- 案例集:小白怎麼問回測:三個回測案例的完整程式碼
- 錯誤訊息速查:13 項「不報錯但結果是錯的」
- 資料頻率與歷史長度:②資料盤點的依據
- 商品規格與績效報告:⑥回測的成本與部位設定
- 技術指標與參數尋優:尋優的過度配適警告