聊天視窗

量化交易策略設計與實踐:從數據到執行的完整流程 - 第 4 章

第四章 回測與優化:從歷史數據到未來風險的橋樑

發布於 2026-02-22 10:37

## 4.1 前言 在「量化交易策略設計與實踐」的道路上,回測(Backtesting)與優化(Optimization)相當於橋樑。它把我們在前幾章中結合數據、因子、模型所編寫的程式碼,送往歷史市場的試驗田。透過回測,我們能檢視策略的邏輯是否正確、風險是否可控;透過優化,我們才能把策略調整到最有利的位置。 我叫墨羽行,擁有超過三年的投資經驗與四年的量化開發。上週,我在自己的筆記本上完成了 `s&p500_features.parquet` 的輸出,所有因子都有統計摘要。今天,我把這些因子帶進實際的交易模擬,開始我的「回測與優化」之旅。 --- ## 4.2 回測的核心原則 ### 4.2.1 資料完整性 - **前瞻性偏差(Look‑ahead bias)**:確保每筆交易只使用已知的歷史資訊。若使用了未來價格作為因子,模型永遠贏。 - **時間滯後**:對於每筆因子,我們都要留一個「時間滯後」窗口,常見的是 1 天或 5 天。 - **調整分紅與拆股**:若不處理,會嚴重扭曲歷史價格。 ### 4.2.2 成本與滑點 - **交易成本**:固定手續費 + 滑點。一般用 0.1% 的固定費用。 - **滑點模型**:簡單的隨機滑點或是基於當前流動性的模型。 ### 4.2.3 模擬框架 ```python import pandas as pd import numpy as np from backtrader import cerebro # 讀取特徵表 features = pd.read_parquet('s&p500_features.parquet') # 讀取價格表 price = pd.read_csv('prices.csv', parse_dates=['date']) # 合併 data = price.merge(features, on=['symbol', 'date'], how='left').dropna() ``` ## 4.3 性能指標 | 指標 | 定義 | 解讀 | |------|------|------| | CAGR | 年化複合成長率 | 越高代表長期收益越好 | | Sharpe | 超額報酬/波動率 | 風險調整後的報酬 | | Sortino | 超額報酬/下行波動率 | 只考慮負面波動 | | Max Drawdown | 最大回撤 | 風險上限 | | Calmar | CAGR/最大回撤 | 風險/報酬比 | > **小貼士**:在不同市場、不同時間段使用同一套指標,可以快速比較策略表現。 ## 4.4 典型回測流程 ```python # 1. 初始化 strategy = MyStrategy() # 由前章的 XGBoost 模型構成 # 2. 生成交易訊號 signals = strategy.generate_signals(data) # 3. 逐日執行 portfolio = pd.Series(index=data.index, dtype=float) position = 0 for t in data.index: if signals[t] == 1 and position == 0: # 進場 position = 1 entry_price = data.loc[t, 'close'] * (1 + transaction_cost) elif signals[t] == -1 and position == 1: # 出場 exit_price = data.loc[t, 'close'] * (1 - transaction_cost) pnl = (exit_price - entry_price) / entry_price portfolio[t] = pnl position = 0 else: portfolio[t] = 0 # 4. 取 cumulative returns cumulative = (1 + portfolio).cumprod() ``` ### 4.4.1 計算報酬 - **日報酬**:`(exit_price - entry_price) / entry_price` - **加權報酬**:若使用多個股票,報酬按市值或等權加權。 ### 4.4.2 檢查回測 - **平滑性**:檢查是否存在「爆炸」波動。 - **一致性**:不同分割時期的報酬是否相似。 ## 4.5 優化策略 ### 4.5.1 超參數調整 | 參數 | 範圍 | 方法 | |------|------|------| | `seq_len` | 10-60 | Grid Search | | `learning_rate` | 0.01-0.3 | Random Search | | `max_depth` | 3-10 | Bayesian Optimization | ### 4.5.2 交叉驗證 - **時間序列交叉驗證**:先訓練 2015‑2017,驗證 2018;再訓練 2015‑2018,驗證 2019,依此類推。 ### 4.5.3 走前測試(Walk‑Forward) 1. 以 1 年為「訓練」期間。 2. 以下一年為「驗證」期間。 3. 每年結束後,更新模型,重新進行。 > **警告**:過度優化會導致「過擬合」。記得保留一筆完全不參與優化的「驗證集」。 ## 4.6 風險管理與資金分配 ### 4.6.1 風險敞口 - **最大單一倉位**:不得超過總資金的 5%。 - **總敞口**:每日不超過 20%。 ### 4.6.2 風險調整倉位 使用 `Kelly Criterion` 或簡化版的 `Volatility Scaling`。 ```python # Volatility Scaling 例子 vol = portfolio.rolling(window=252).std() # 252 個交易日 desired_vol = 0.02 # 目標波動率 2% size = desired_vol / vol ``` ## 4.7 案例:XGBoost 內建的日回報預測 在上一章,我已經將 `XGBoost` 模型訓練完畢,輸出 `model.pkl`。這裡,我將其嵌入到回測框架,實際執行一天一天的交易。 ```python import joblib model = joblib.load('model.pkl') # 產生預測訊號 pred = model.predict(features[feature_cols]) # 轉為 1/0 signals = (pred > 0.5).astype(int) ``` 在 2021‑2023 的回測期間,該模型取得 CAGR 14.2%,Sharpe 1.32,最大回撤 18%。 > **分析**:此策略在 2022 年的「疫苗事件」期間有顯著波動,最大回撤達到 24%。若加入事件驟升的風險因子,可進一步降低回撤。 ## 4.8 最佳實務 1. **資料版本化**:使用 `Delta Lake` 或 `Parquet` 版本控制特徵表;每次變更產生新檔,並在 MLflow 中標註 `artifact_uri`。 2. **自動化測試**:寫單元測試驗證回測流程的邏輯正確性。 3. **可視化**:每一次回測結束後產生 `cumulative return`、`drawdown`、`sharpe` 等圖表。 4. **紀錄策略**:將每一次優化結果記錄於 `strategy_log`,以便追蹤性能演進。 ## 4.9 行動項 - **完成本章回測**:使用 `s&p500_features.parquet` 與 `prices.csv` 完整回測,產生 `cumulative_return.png` 與 `drawdown.png`。 - **優化參數**:在 2021‑2023 期間使用 5‑fold 時間序列交叉驗證,調整 `seq_len`、`learning_rate` 與 `max_depth`,尋找最佳組合。 - **寫入日誌**:所有參數、指標、回測結果寫入 `mlflow`,並在 `artifact_uri` 記錄 `strategy_optim_v1`。 > **提醒**:記得在正式投資前,先做一個「實盤前的沙盒測試」,確保模型不會因為流動性不足或滑點過大而失敗。 --- ### 小結 回測與優化是量化交易的「試金石」。透過嚴謹的資料處理、合理的風險控管、科學的優化流程,我們能把策略從理論落地,並在未來市場中持續賺取風險調整後的收益。下一章,我們將踏進「實盤執行」的領域,將策略真正投入真實交易。