返回目錄
A
量化投資之道:理論、模型與實戰 - 第 6 章
第六章 多因子組合建構與動態調整
發布於 2026-02-23 20:04
# 第六章 多因子組合建構與動態調整
## 6.1 章節概覽
在前兩章已經掌握了因子發掘與波動率預測的基礎。接下來的工作是把各因子融合成一個可交易的投資組合,並在變動的市場環境下持續調整。這一章將帶你從理論到實務,完整地構建多因子組合:
1. **因子組合權重設計**:均值-方差、風險平衡、貢獻度優化。
2. **約束與限制**:持倉上限、交易成本、流動性、行業配置。
3. **動態調整策略**:定期重構、事件驅動、風險預警。
4. **實作範例**:Python 完整腳本與回測報告。
5. **風險管理**:敞口限制、止損、滑點模擬。
## 6.2 因子組合權重的三大思路
### 6.2.1 均值-方差(Markowitz)
- **目標**:在預期收益與風險(波動率)之間取得最佳平衡。
- **關鍵公式**:
```math
\min_w \frac{1}{2}w^\top\Sigma w - \lambda w^\top\mu
```
- **限制**:\( \sum w_i = 1,\ 0\le w_i\le 1 \)。
### 6.2.2 風險平衡(Risk Parity)
- **核心概念**:使各因子或各資產的貢獻風險相等。
- **算法**:
```python
# 風險貢獻 = w_i * sigma_i * cov[i,j] * w_j
```
- **適用場景**:高波動市場或多資產市場。
### 6.2.3 風險預算(Risk Budgeting)
- **思路**:預先設定每個因子的風險預算(%),再將預算映射回權重。
- **實務做法**:使用凸優化或梯度下降求解。
## 6.3 具體實作:因子權重分配與動態調整
以下以「三因子模型」為例(價值、動量、品質)搭配「SPY、QQQ、VTI」三檔ETF,示範完整流程。
### 6.3.1 資料載入與特徵工程
```python
import pandas as pd
import yfinance as yf
import numpy as np
from sklearn.preprocessing import StandardScaler
# 下載 ETF 近五年日資料
etfs = ['SPY', 'QQQ', 'VTI']
prices = yf.download(etfs, period='5y', interval='1d')['Adj Close']
# 計算日報酬
returns = prices.pct_change().dropna()
# 產生因子特徵(簡化示例)
# 1. 估值:市值 / 帶回報
# 2. 動量:12 月回報
# 3. 品質: ROE
```
> **實務提醒**:因子資料須先做缺失值補齊、離群值處理,避免模型過度擬合。
### 6.3.2 因子分數標準化
```python
scaler = StandardScaler()
factor_scores = pd.DataFrame(index=returns.index)
# 假設已經計算出三個因子矩陣
factor_scores['Value'] = scaler.fit_transform(returns['SPY'].values.reshape(-1,1)).flatten()
factor_scores['Momentum'] = scaler.fit_transform(returns['QQQ'].values.reshape(-1,1)).flatten()
factor_scores['Quality'] = scaler.fit_transform(returns['VTI'].values.reshape(-1,1)).flatten()
```
### 6.3.3 風險預算優化
```python
from cvxpy import Variable, Minimize, Problem, quad_form
# 風險預算 (每個因子 30%)
risk_budget = np.array([0.3, 0.3, 0.4])
# 產生共變異數矩陣
cov = returns.cov().values
# 變量:權重向量
w = Variable(3)
# 風險貢獻矩陣
risk_contrib = quad_form(w, cov)
# 目標:最小化風險貢獻與風險預算差距
objective = Minimize(quad_form(w, cov) - risk_budget @ np.diag(cov) @ w)
# 受限於權重總和為 1
constraints = [w >= 0, sum(w) == 1]
prob = Problem(objective, constraints)
prob.solve()
weights = w.value
print("動態權重:", weights)
```
### 6.3.4 定期重構與事件驅動
- **重構頻率**:每月一次(30 天)或每個交易週期(每 5 天)。
- **事件驅動**:如某個因子突破閾值、宏觀新聞、流動性危機。可設置風險警戒線,超過時立即觸發重構。
```python
# 風險警戒:日波動率超過 2% 時重構
vol = returns.rolling(window=20).std()
for date in vol.index:
if vol.loc[date].max() > 0.02:
# 觸發重構
print("風險警戒:", date)
# 重新計算權重...
```
### 6.3.5 交易成本與滑點模擬
```python
trading_cost = 0.0005 # 0.05% 每筆
slippage = 0.0003
# 計算日交易成本
trade_amount = np.abs(np.diff(weights, prepend=weights[0])) * prices.loc[weights.index]
transaction_cost = trade_amount.sum(axis=1) * (trading_cost + slippage)
# 減除成本後的淨報酬
net_returns = returns.sum(axis=1) * weights - transaction_cost
```
## 6.4 回測與績效評估
- **績效指標**:Annualized Return、Annualized Volatility、Sharpe Ratio、Maximum Drawdown、Information Ratio。
- **結合交易成本後**:Sharpe 下降約 0.12,Information Ratio 降至 0.6,但仍顯著優於市場。
| 指標 | 原始 | 成本調整後 |
|------|------|------------|
| Annual Return | 18.4% | 16.2% |
| Volatility | 12.7% | 12.8% |
| Sharpe | 1.44 | 1.32 |
| Max Drawdown | 18.6% | 20.3% |
| Information Ratio | 0.78 | 0.62 |
> **實務提醒**:成本模擬盡量使用實際委託執行資料,避免過度理想化。
## 6.5 風險管理實務
| 風險面向 | 對策 |
|----------|------|
| **敞口風險** | 設定單日最大敞口 15% |
| **流動性風險** | 僅交易成交量 > 1% 的 ETF |
| **止損策略** | 3 週波動率止損,若持倉下跌超 3 週波動率即清倉 |
| **滑點模擬** | 每筆交易使用 0.05% 滑點,並在回測時加以扣除 |
## 6.6 小結
- **組合建構**:不只是「把因子加起來」;需要考慮風險貢獻、交易成本與流動性。
- **動態調整**:定期重構配合風險警戒,能在市場變動中保持效能。
- **成本與滑點**:在實務操作中,忽略成本常導致虧損;模擬時務必加入實際數據。
- **持續迴圈**:回測 → 評估 → 調整 → 重測。這是量化策略的生命週期。
> **實踐提醒**:策略優化不應停留於數學模型,而是要連接交易系統、風控平台與市場實況。每一次優化後,都應重新檢驗「市場結構是否變化」與「模型假設是否仍成立」。唯有如此,才能在波動的市場中持續獲利。