返回目錄
A
量化交易策略設計與實踐:從數據到執行的完整流程 - 第 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`。
> **提醒**:記得在正式投資前,先做一個「實盤前的沙盒測試」,確保模型不會因為流動性不足或滑點過大而失敗。
---
### 小結
回測與優化是量化交易的「試金石」。透過嚴謹的資料處理、合理的風險控管、科學的優化流程,我們能把策略從理論落地,並在未來市場中持續賺取風險調整後的收益。下一章,我們將踏進「實盤執行」的領域,將策略真正投入真實交易。