聊天視窗

從數據到利潤:量化投資策略設計與實踐 - 第 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`,報告其對最大回撤的影響。