返回目錄
A
從資料到洞察:金融量化交易的系統化方法 - 第 4 章
第四章 模型構建與評估
發布於 2026-02-25 23:27
# 第四章 模型構建與評估
在量化交易中,模型是將市場信號轉化為交易決策的核心。這一章將系統性地說明
1. 監督式學習模型的類型與適用情境。
2. 無監督與半監督方法在資料缺失或非標籤環境下的運用。
3. 交叉驗證、滑動窗口與早停策略的設計。
4. 評估指標與實盤風險的綜合考量。
> **學習目標**
> - 掌握主流監督學習模型的優缺點。
> - 理解滑動窗口交叉驗證對時間序列資料的重要性。
> - 能夠根據實盤條件設計適合的評估框架。
---
## 4.1 監督式學習模型
| 模型 | 主要特徵 | 典型應用 | 優缺點 |
|------|----------|----------|--------|
| 線性回歸 / Lasso / Ridge | 參數可解釋性高,計算成本低 | 長期均值回歸、基本因子模型 | 受限於線性假設,易受噪聲影響 |
| 決策樹 (CART) | 可處理非線性關係,易於解釋 | 事件驅動策略、分類風險 | 易過度擬合,缺乏平滑性 |
| 隨機森林 / XGBoost | 高預測準確,抗過擬合 | 量化因子選擇、價格預測 | 參數調整繁瑣,計算成本較高 |
| 支持向量機 (SVM) | 能處理高維度資料,核技巧 | 交易信號分類 | 訓練時間長,對噪聲敏感 |
| LSTM / Transformer | 處理序列資料,捕捉長期依賴 | 預測收盤價、波動率 | 需要大量資料與GPU,難以解釋 |
### 4.1.1 線性回歸與正則化
python
import pandas as pd
import statsmodels.api as sm
# 假設 df 為已經構造好的特徵表
X = df.drop(columns=['target'])
X = sm.add_constant(X) # 加常數項
y = df['target']
model = sm.OLS(y, X).fit()
print(model.summary())
- **正則化**:在特徵維度較高時,使用 Lasso(L1)或 Ridge(L2)可防止過擬合。
- **模型診斷**:檢查殘差的自相關(Durbin–Watson 指標)與多重共線性(VIF)。
### 4.1.2 樹模型與集成方法
python
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False)
rf = RandomForestRegressor(n_estimators=500, max_depth=10, random_state=42)
rf.fit(X_train, y_train)
print('Test R^2:', rf.score(X_test, y_test))
- **特徵重要性**:利用 `feature_importances_` 確定關鍵因子。
- **過擬合防止**:調整 `max_depth`、`min_samples_leaf`、`max_features`。
### 4.1.3 深度學習
python
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
class LSTMModel(nn.Module):
def __init__(self, input_dim, hidden_dim, num_layers, output_dim):
super().__init__()
self.lstm = nn.LSTM(input_dim, hidden_dim, num_layers, 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)
# 假設 train_loader 已經準備好
model = LSTMModel(input_dim=10, hidden_dim=64, num_layers=2, output_dim=1)
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
for epoch in range(50):
for X_batch, y_batch in train_loader:
optimizer.zero_grad()
pred = model(X_batch)
loss = criterion(pred, y_batch)
loss.backward()
optimizer.step()
print(f'Epoch {epoch+1}, Loss: {loss.item():.4f}')
- **時間步長**:根據策略特性調整序列長度。
- **梯度裁剪**:防止梯度爆炸。
## 4.2 無監督與半監督方法
| 方法 | 目的 | 範例 |
|------|------|------|
| 主成份分析 (PCA) | 降維、去噪 | 生成交易信號前的特徵投影 |
| 聚類 (K‑means, DBSCAN) | 風格識別、策略分群 | 透過風格因子分組優化風險平衡 |
| Autoencoder | 雜訊抑制、特徵學習 | 生成可解釋的深度特徵 |
| Label‑Propagate | 標籤傳播 | 在缺乏交易訊號的市場區間推斷潛在買賣點 |
> **實務提示**:無監督方法多用於策略探索或特徵前處理;若有部分標籤,可考慮半監督,提升模型泛化。
## 4.3 交叉驗證與滑動窗口
### 4.3.1 滑動窗口交叉驗證
對於時間序列資料,傳統 K‑fold 會破壞時間順序,導致資訊泄露。滑動窗口交叉驗證(Time‑Series CV)保證歷史資料只能用於訓練。
python
from sklearn.model_selection import TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits=5, test_size=200, gap=20)
for train_idx, test_idx in tscv.split(df):
X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]
y_train, y_test = y.iloc[train_idx], y.iloc[test_idx]
model.fit(X_train, y_train)
print('Fold R^2:', model.score(X_test, y_test))
- **gap**:避免緊鄰資料影響。
- **test_size**:根據策略週期選擇。
### 4.3.2 早停 (Early Stopping)
早停可防止模型在驗證集上過擬合,常見於深度學習與梯度提升。
python
best_loss = float('inf')
patience = 5
counter = 0
for epoch in range(200):
# 訓練迴圈…
val_loss = evaluate(model, val_loader)
if val_loss < best_loss:
best_loss = val_loss
torch.save(model.state_dict(), 'best_model.pt')
counter = 0
else:
counter += 1
if counter >= patience:
print('Early stopping at epoch', epoch)
break
- **patience**:容忍的退化週期數。
- **min_delta**:微小變化是否算退化。
## 4.4 評估指標與實盤風險
| 指標 | 定義 | 為什麼重要 |
|------|------|--------------|
| MSE / MAE | 目標預測誤差 | 衡量回歸精度 |
| R² / Adjusted R² | 解釋變異比例 | 直觀評估模型優度 |
| Sharpe Ratio | 超額報酬 / 風險 | 兼顧收益與波動 |
| Information Ratio | 超額報酬 / 追蹤誤差 | 檢測模型對基準的超越 |
| Hit Rate | 正確交易比例 | 交易頻率高時較重要 |
| Profit Factor | 總獲利 / 總虧損 | 風險承受度指標 |
| Max Drawdown | 最大回撤 | 投資者承受程度 |
> **實盤注意**:在計算 Sharpe / Information Ratio 時,務必使用扣除交易成本、滑點後的實際報酬。
## 4.5 模型選擇與部署
| 選擇條件 | 推薦模型 |
|----------|----------|
| 資料量 < 1M | 線性回歸 + Lasso | 低成本、易調參 |
| 風格多變、特徵維度高 | 隨機森林 + 特徵重要性 | 可快速迭代 |
| 長期預測需求 | LSTM / Transformer | 捕捉長期依賴 |
| 低頻交易 | 樹模型 + 交易成本修正 | 高解釋度 |
> **部署流程**:
> 1. 先在歷史資料上完成滑動窗口 CV,得到參數。
> 2. 在最新窗口上進行一次「實時」訓練,並保存模型。
> 3. 以微批次(mini‑batch)方式持續更新,確保模型與市場同步。
---
## 4.6 案例:基於多因子回歸的日內套利模型
1. **資料**:日內 5‑分鐘價格、成交量、技術指標。
2. **特徵**:使用 PCA 先降維,選取前 5 個主成分。
3. **模型**:XGBoost 回歸,預測明日收盤價差。
4. **驗證**:TimeSeriesSplit(n_splits=10),每個測試集 300 筆。
5. **評估**:資訊比率 (IR)、最大回撤、交易成本扣除後的 Sharpe。
python
import xgboost as xgb
# 1. 準備資料
features = pd.read_csv('features.csv')
X = features.drop(columns=['target'])
y = features['target']
# 2. 滑動窗口 CV
tscv = TimeSeriesSplit(n_splits=10, test_size=300, gap=50)
metrics = []
for train_idx, test_idx in tscv.split(features):
X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]
y_train, y_test = y.iloc[train_idx], y.iloc[test_idx]
dtrain = xgb.DMatrix(X_train, label=y_train)
dtest = xgb.DMatrix(X_test, label=y_test)
params = {
'objective': 'reg:squarederror',
'eval_metric': 'rmse',
'max_depth': 8,
'learning_rate': 0.05,
'n_estimators': 400
}
bst = xgb.train(params, dtrain, num_boost_round=400, early_stopping_rounds=30, evals=[(dtest, 'eval')])
preds = bst.predict(dtest)
ir = np.corrcoef(preds, y_test)[0, 1] / np.std(y_test - preds)
metrics.append({'rmse': bst.best_score, 'ir': ir})
print(metrics)
> **結果解讀**:若 IR > 0.6 且 max drawdown < 5%,可視為可行的日內套利策略。
---
## 4.7 小結
- **模型選擇**:需結合資料量、特徵性質、交易頻率與可解釋性需求。
- **驗證設計**:滑動窗口交叉驗證是避免資訊泄露的關鍵;早停可提升泛化。
- **評估框架**:不僅要看統計指標,還必須考慮交易成本、滑點、持倉風險。
> **實務建議**:在策略上線前,先在歷史上模擬「回測 + 前向驗證」三階段流程;確保每一步都無資訊泄露。
---
### 延伸閱讀
- 《Python 機器學習實戰》 (黃柏軒, 2021)
- 《時序資料機器學習》 (林曉雲, 2019)
- 《深度學習與金融市場》 (何智仁, 2020)
---
> **操作說明**:此章節已提供完整程式碼,請將其貼至 Jupyter Notebook 或 PyCharm,依據自身資料集進行參數微調與結果驗證。