策略參數優化
當你開發了一個交易策略後,如何找出最佳的條件組合? finlab.optimize 模組提供了 sim_conditions() 函數,可以自動測試所有條件組合,並透過視覺化工具快速比較績效,幫助你找出最強的策略。
為什麼需要策略優化?
在開發交易策略時,我們常常會有多個選股條件:
- 技術面:均線交叉、創新高、RSI 指標
- 基本面:本益比、營收成長、獲利能力
- 籌碼面:投信買超、融資變化
每個條件單獨使用可能績效普通,但多個條件組合後可能產生意想不到的效果。手動測試所有組合非常耗時,sim_conditions() 可以自動化這個過程。
快速上手
基礎範例:3 個條件的所有組合
假設我們有 3 個選股條件:
from finlab import data
from finlab.backtest import sim
from finlab.optimize.combinations import sim_conditions
close = data.get("price:收盤價")
rev = data.get('monthly_revenue:當月營收')
營業利益成長率 = data.get('fundamental_features:營業利益成長率')
# 定義 3 個條件
c1 = (close > close.average(20)) & (close > close.average(60)) # 技術面:突破均線
c2 = 營業利益成長率 > 0 # 基本面:獲利成長
c3 = rev.average(3) / rev.average(12) > 1.1 # 基本面:營收加速
# 設定出場條件
exits = close < close.average(20)
# 組合字典,key 為條件名稱
conditions = {'c1': c1, 'c2': c2, 'c3': c3}
# 測試所有組合
report_collection = sim_conditions(
conditions=conditions,
hold_until={'exit': exits, 'stop_loss': 0.1}, # 跌破均線出場,停損 10%
resample='M', # 每月調整
position_limit=0.1, # 單一持股上限 10%
upload=False # 不上傳至雲端
)
這段程式碼會自動測試以下 7 種組合:
c1(僅技術面)c2(僅獲利成長)c3(僅營收加速)c1 & c2(技術面 + 獲利成長)c1 & c3(技術面 + 營收加速)c2 & c3(獲利成長 + 營收加速)c1 & c2 & c3(三個條件全部)
視覺化比較策略績效
1. 累積報酬率折線圖
比較各組合的淨值曲線:

從圖中可以看出哪個組合的長期績效最好,以及各組合的穩定性。
2. 指標分群棒狀圖

並排顯示所有組合的 12 種關鍵指標,方便快速比較。
3. 指標分級熱力圖

熱力圖使用顏色深淺表示各指標的排名百分位(0-100%),數值越大代表排名越前:
- avg_score: 各指標平均分數,分數越高代表整體評價越好
- 預設以
avg_score降冪排序,最好的組合在最上方 - 顏色越亮(黃色)代表該指標在所有組合中排名越前
如何解讀熱力圖
- 尋找 avg_score 最高的組合(通常在最上方)
- 檢查該組合是否在關鍵指標(夏普率、勝率、最大回撤)都有不錯的表現
- 避免選擇某些指標特別差(深紫色)的組合
取得詳細績效指標
會回傳一個 DataFrame,包含 12 種指標:
策略層級指標
| 指標 | 說明 | 評估方向 |
|---|---|---|
daily_mean |
年化報酬率 | 越高越好 |
daily_sharpe |
年化夏普率(風險調整後報酬) | 越高越好 |
daily_sortino |
年化索提諾比率(下檔風險調整後報酬) | 越高越好 |
max_drawdown |
最大回撤率(負值) | 絕對值越小越好 |
avg_drawdown |
平均回撤率(負值) | 絕對值越小越好 |
交易層級指標
| 指標 | 說明 | 評估方向 |
|---|---|---|
win_ratio |
每筆交易勝率 | 越高越好 |
avg_return |
每筆交易平均獲利率 | 越高越好 |
avg_mae |
每筆交易平均最大不利方向幅度(負值) | 絕對值越小越好 |
avg_bmfe |
MAE 發生前的平均最大有利方向幅度 | 越高越好 |
avg_gmfe |
每筆交易平均最大有利方向幅度 | 越高越好 |
avg_mdd |
每筆交易平均最大回撤率(負值) | 絕對值越小越好 |
avg_bmfe 的意義
avg_bmfe 代表在停損觸發前,股價曾經漲到多高。若此數值越高,代表越有機會在停損前先執行停利,是優化停利點的重要參考。
進階範例:5 個條件的優化
當條件增加到 5 個時,組合數會達到 31 種 (2^5 - 1)。手動測試非常困難,但 sim_conditions() 可以輕鬆處理:
from finlab import data
from finlab.optimize.combinations import sim_conditions
close = data.get("price:收盤價")
pe = data.get('price_earning_ratio:本益比')
rev = data.get('monthly_revenue:當月營收').index_str_to_date()
rev_ma3 = rev.average(3)
rev_ma12 = rev.average(12)
# 5 個條件
c1 = (close > close.average(20)) & (close > close.average(60)) # 均線多頭排列
c2 = (close == close.rolling(20).max()) # 創 20 日新高
c3 = pe < 15 # 低本益比
c4 = rev_ma3 / rev_ma12 > 1.1 # 營收加速成長
c5 = rev / rev.shift(1) > 0.9 # 營收月增率 > -10%
exits = close < close.average(20)
conditions = {'c1': c1, 'c2': c2, 'c3': c3, 'c4': c4, 'c5': c5}
report_collection = sim_conditions(
conditions=conditions,
hold_until={'exit': exits, 'stop_loss': 0.1},
resample='M',
position_limit=0.1,
upload=False
)
# 查看熱力圖,快速找出最佳組合
report_collection.plot_stats('heatmap')
結果解讀範例
假設熱力圖顯示 c1 & c3 & c4 組合的 avg_score 最高:
- 檢視累積報酬曲線: 確認淨值成長是否穩定
- 檢查關鍵指標:
- 夏普率 > 1.5? (風險調整後報酬是否足夠)
- 最大回撤 < -30%? (最差情況能否承受)
- 勝率 > 50%? (交易勝率是否合理)
- 分析組合意義:
c1 & c3 & c4代表「技術面突破 + 估值便宜 + 營收成長」,這個組合有其商業邏輯
自訂要顯示的指標
如果只關心特定指標,可以使用 indicators 參數:
# 只顯示報酬率、夏普率、最大回撤
report_collection.plot_stats('bar', indicators=['daily_mean', 'daily_sharpe', 'max_drawdown']).show()
# 熱力圖以夏普率排序
report_collection.plot_stats('heatmap', heatmap_sort_by='daily_sharpe')
# 熱力圖以多個指標排序
report_collection.plot_stats('heatmap', heatmap_sort_by=['daily_sharpe', 'win_ratio'])
結合停損停利優化
sim_conditions() 的 hold_until 參數支援多種出場邏輯:
# 1. 僅設定出場訊號
hold_until = {'exit': exits}
# 2. 出場訊號 + 停損
hold_until = {'exit': exits, 'stop_loss': 0.1}
# 3. 出場訊號 + 停利
hold_until = {'exit': exits, 'stop_profit': 0.2}
# 4. 出場訊號 + 停損 + 停利
hold_until = {'exit': exits, 'stop_loss': 0.1, 'stop_profit': 0.2}
# 5. 出場訊號 + 停損 + 移動停利
hold_until = {'exit': exits, 'stop_loss': 0.1, 'trailing_stop_profit': 0.15}
停損停利設定建議
- 先執行
report.display_mae_mfe_analysis()分析波動特性 - 根據 MAE 分布設定停損點(避免過度停損)
- 根據 MFE 分布設定停利點(確保獲利能實現)
- 使用
sim_conditions()測試不同停損停利組合的效果
常見問題與最佳實踐
Q1: 組合太多導致運算時間過長?
當條件數量 > 6 時,組合數會超過 63 種。建議:
- 先篩選重要條件: 使用單一條件回測,移除績效過差的條件
- 分批測試: 將條件分成技術面、基本面、籌碼面分別優化
- 使用更長的 resample: 改用
resample='Q'(季度)減少計算量
Q2: 如何避免過度配適(overfitting)?
- 樣本外測試: 使用歷史資料優化,用最近資料驗證
- 避免條件過多: 條件數 > 5 時要特別小心
- 檢查組合邏輯: 最佳組合是否有商業邏輯支撐?
- 多個指標綜合評估: 不要只看報酬率,也要看夏普率、回撤、勝率
Q3: 條件的資料頻率不一致怎麼辦?
FinLab 會自動對齊資料頻率:
# 日資料
close = data.get("price:收盤價") # 每日更新
# 月資料
rev = data.get('monthly_revenue:當月營收') # 每月更新
# 季資料
eps = data.get('financial_statement:每股盈餘') # 每季更新
# 混合使用沒問題,FinLab 會自動 forward fill
conditions = {
'c1': close > close.average(20),
'c2': rev.average(3) > rev.average(12),
'c3': eps > 0
}
Q4: 為什麼某些組合回測失敗?
可能原因:
- 條件過嚴格: 交集後沒有任何股票符合
- 資料缺失: 某些條件的資料範圍不足
- 記憶體不足: 條件組合過多導致 OOM
可從 log 中查看具體錯誤訊息。
Q5: 如何存取特定組合的回測報告?
# report_collection.reports 是一個 dict
print(report_collection.reports.keys())
# 輸出: dict_keys(['c1', 'c2', 'c3', 'c1 & c2', 'c1 & c3', ...])
# 取得特定組合的報告
report = report_collection.reports['c1 & c3']
report.display()
實戰工作流程建議
- 定義候選條件 (5-8 個)
- 單一條件回測: 確認每個條件都有基本績效
- 使用
sim_conditions(): 測試所有組合 - 視覺化分析:
- 用熱力圖找出 top 3 組合
- 用折線圖確認淨值穩定性
- 用棒狀圖比較關鍵指標
- 深入分析 top 3:
- 執行 MAE/MFE 分析
- 檢查流動性風險
- 執行樣本外測試
- 選擇最終策略: 綜合考量績效、風險、邏輯性