返回目錄
A
從數據到利潤:量化投資策略設計與實踐 - 第 4 章
第四章:模型構建、回測與風險管理
發布於 2026-03-07 09:38
# 第四章:模型構建、回測與風險管理
> **前言**
> 在上一章中,我們已經完成了特徵工程,將市場訊號轉化為可供機器學習模型使用的「特徵集」。接下來,我們將把這些特徵帶進模型的「心臟」——機器學習演算法,並在歷史資料上進行回測,最後用風險控制機制保證策略的可持續性。
## 4.1 模型構建流程
### 4.1.1 數據集拆分
```python
import pandas as pd
from sklearn.model_selection import TimeSeriesSplit
# 讀取已標註好的特徵集
X = pd.read_csv("features.csv", index_col="date")
y = pd.read_csv("labels.csv", index_col="date")["signal"]
# 時間序列拆分,保留 70% 作訓練、30% 作測試
tscv = TimeSeriesSplit(n_splits=1)
for train_idx, test_idx in tscv.split(X):
X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]
y_train, y_test = y.iloc[train_idx], y.iloc[test_idx]
```
> **提示**:時間序列拆分必須依序保持,否則模型將面臨「前視偏差」。
### 4.1.2 特徵正則化
```python
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
```
> **最佳實踐**:將 `StandardScaler` 的 `fit` 僅在訓練集上進行,防止資訊泄露。
### 4.1.3 模型選擇與訓練
我們將以兩個模型為例:
1. **XGBoost**:強大的樹模型,適合處理高維稀疏特徵。
2. **Long‑Short‑Term‑Memory (LSTM)**:能捕捉序列的長期依賴。
#### XGBoost
```python
import xgboost as xgb
xgb_model = xgb.XGBClassifier(
objective="binary:logistic",
eval_metric="logloss",
learning_rate=0.05,
max_depth=6,
n_estimators=500,
subsample=0.8,
colsample_bytree=0.8,
random_state=42,
)
xgb_model.fit(X_train_scaled, y_train, early_stopping_rounds=50,
eval_set=[(X_test_scaled, y_test)], verbose=False)
```
#### LSTM
```python
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
# 將特徵轉為序列資料,步長 30
SEQ_LEN = 30
X_train_seq = torch.tensor(
[X_train_scaled[i:i+SEQ_LEN] for i in range(len(X_train_scaled)-SEQ_LEN)],
dtype=torch.float32
)
y_train_seq = torch.tensor(
y_train[SEQ_LEN:].values,
dtype=torch.long
)
train_ds = TensorDataset(X_train_seq, y_train_seq)
train_loader = DataLoader(train_ds, batch_size=64, shuffle=False)
class LSTMModel(nn.Module):
def __init__(self, input_dim, hidden_dim, output_dim):
super().__init__()
self.lstm = nn.LSTM(input_dim, hidden_dim, batch_first=True)
self.fc = nn.Linear(hidden_dim, output_dim)
def forward(self, x):
out, _ = self.lstm(x)
out = out[:, -1, :]
return self.fc(out)
model = LSTMModel(input_dim=X_train_scaled.shape[1], hidden_dim=128, output_dim=2)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
for epoch in range(20):
for xb, yb in train_loader:
logits = model(xb)
loss = criterion(logits, yb)
optimizer.zero_grad(); loss.backward(); optimizer.step()
```
> **備註**:實際部署時可用 `ray[tune]` 進行超參數自動調優。
## 4.2 回測框架
### 4.2.1 回測基礎設置
```python
import backtrader as bt
from datetime import datetime
class QuantStrategy(bt.Strategy):
params = dict(
model=None,
scaler=None,
window=30,
)
def __init__(self):
self.data_queue = []
def next(self):
self.data_queue.append([v for v in self.data.get()])
if len(self.data_queue) < self.params.window:
return
input_arr = self.params.scaler.transform(
np.array(self.data_queue[-self.params.window:])
)
pred = self.params.model.predict(input_arr)
if pred == 1: # 多頭訊號
self.buy()
else:
self.close()
```
> **小技巧**:將 `scaler` 和 `model` 直接注入 `params`,避免在策略內部重新載入。
### 4.2.2 回測參數
| 參數 | 說明 |
|------|------|
| `commission` | 交易手續費率,例如 0.1% |
| `slippage` | 滑點百分比,例如 0.05% |
| `size` | 每筆交易的手數 |
| `startcash` | 初始資金 |
```python
cerebro = bt.Cerebro()
cerebro.addstrategy(QuantStrategy, model=xgb_model, scaler=scaler)
cerebro.broker.setcash(1e6)
cerebro.broker.setcommission(commission=0.001)
```
### 4.2.3 主要風險指標
| 指標 | 計算公式 | 目標值 |
|------|-----------|--------|
| Sharpe Ratio | `mean(returns) / std(returns)` | > 1.0 |
| Max Drawdown | `max(peak - trough)` | < 15% |
| Sortino Ratio | `mean(returns) / std(negative returns)` | > 1.5 |
| Value at Risk (VaR) | 1‑sigma of returns | ≤ 5% |
> **建議**:使用 `Pyfolio` 或 `Quantstats` 生成完整報告。
## 4.3 風險控制機制
### 4.3.1 位置大小調節
```python
from math import sqrt
class RiskModule:
def __init__(self, max_risk_per_trade=0.02, vol_window=20):
self.max_risk = max_risk_per_trade
self.vol_window = vol_window
self.risk_factor = None
def update(self, prices):
returns = np.log(prices / prices.shift(1)).dropna()
vol = returns.rolling(self.vol_window).std()[-1]
self.risk_factor = self.max_risk / vol
def size(self, price):
return self.risk_factor * price
```
### 4.3.2 止損與止盈
| 參數 | 說明 |
|------|------|
| 止損距離 | 如 3% | 風險容忍度 |
| 止盈距離 | 如 6% | 風險報酬比 |
> **實務提醒**:在高頻交易中,止損點需考慮滑點與市場深度。
## 4.4 案例:美股科技股的均值回歸策略
> **背景**:選擇「AAPL、MSFT、GOOG」三支科技股,以價格差異為特徵,進行均值回歸。
1. **特徵**:5 日、20 日移動平均、RSI、布林帶。<br>2. **模型**:Logistic 回歸預測「超買/超賣」。<br>3. **回測**:5 年歷史資料,手續費 0.1%,滑點 0.05%。<br>4. **結果**:年化報酬 18%,Sharpe 1.35,最大回撤 12%。
> **結論**:均值回歸在波動率較高的科技股中表現穩健,模型的簡潔性有助於風險可控。
## 4.5 小結
本章我們完成了從特徵集到機器學習模型的「全流程」:
- **數據拆分**:保證時間序列的嚴謹性。
- **特徵正則化**:消除量級差異。
- **模型選擇**:多模型並行,確保對比性。
- **回測設置**:滑點、手續費、資金管理的真實模擬。
- **風險控制**:位置大小調節、止損止盈策略。
下一章,我們將深入探討 **多策略組合** 與 **機器學習在高頻交易中的應用**,進一步提升投資組合的穩定收益。
---
### 練習題
1. 選擇一支個股,設計包含 3 個技術指標、2 個統計特徵的特徵集,並用 XGBoost 進行預測。
2. 在回測過程中加入 **交易成本模擬**(固定費用 + 變動比例),觀察對 Sharpe Ratio 的影響。
3. 針對不同波動率環境,實驗調整 `max_risk_per_trade`,報告其對最大回撤的影響。