Skip to content

5.4 多因子组合与风险管理

本章目标:把多个策略/因子组合成一个完整的投资组合,并系统性分析策略的风险——单因子有风险,多因子更稳健;仅看夏普不够,需要 VaR、回撤等全面的风险管理。

一句话

多因子 = 不赌单一策略,而是让多个策略"投票"——东边不亮西边亮。 风险管理 = 回答三个问题:亏多少?什么时候亏?为什么亏?

学习目标

学完本章你能:

  • 理解多因子模型的基本架构(打分法 vs 回归法)
  • 构建一个包含 3 个因子的简单多因子组合
  • 用等权/风险平价两种方式分配因子权重

核心内容

1. 为什么需要多因子?

python
import numpy as np
import pandas as pd
import yfinance as yf

data = yf.download('AAPL', start='2020-01-01', end='2024-12-31')
close = data['Adj Close']

# 两个单因子策略
def momentum_factor(close, window=60):
    """动量因子:过去收益"""
    return close.pct_change(window)

def value_factor(close, window=20):
    """价值因子:当前价格 vs 均线(便宜的回到均线以下)"""
    ma = close.rolling(window).mean()
    return (close - ma) / ma * -1  # 低于均线 = 高价值分

mom = momentum_factor(close)
val = value_factor(close)

# 因子之间的相关性
factor_corr = pd.concat([mom, val], axis=1).dropna()
factor_corr.columns = ['动量', '价值']
print("因子相关性:\n", factor_corr.corr())

关键洞察: 好的因子组合中,因子之间相关性低——一个因子亏钱的时候另一个赚。

2. 打分法——最简单的多因子

python
import numpy as np
import pandas as pd

def score_stocks(close_df, factor_functions, weights=None):
    """
    多因子打分法
    
    对每只股票:
    1. 计算各因子得分
    2. 因子得分归一化(z-score)
    3. 加权求和得到总分
    4. 选总分最高的股票
    """
    if weights is None:
        weights = [1/len(factor_functions)] * len(factor_functions)
    
    total_score = pd.Series(0, index=close_df.columns)
    
    for func, weight in zip(factor_functions, weights):
        factor_scores = func(close_df)
        # 归一化
        zscore = (factor_scores - factor_scores.mean()) / factor_scores.std()
        total_score += weight * zscore
    
    return total_score.sort_values(ascending=False)

# 示例:对多只股票打分
tickers = ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'META', 'TSLA', 'NVDA']
multi_data = yf.download(tickers, start='2024-01-01', end='2024-12-31')['Adj Close']

# 两个因子
factors = [
    momentum_factor,  # 动量因子
    value_factor,     # 价值因子
]

scores = score_stocks(multi_data, factors, weights=[0.6, 0.4])
print("多因子打分排名:")
for rank, (ticker, score) in enumerate(scores.items(), 1):
    print(f"  {rank}. {ticker}: {score:.3f}")

3. 等权组合——多个策略的投票

python
import numpy as np
import pandas as pd
import yfinance as yf

# 假设我们有3个策略,先分别回测
results = {}
for ticker in ['SPY']:
    d = yf.download(ticker, start='2020-01-01', end='2024-12-31')
    c = d['Adj Close']
    r = c.pct_change()
    
    # 三个策略
    # 策略1: 动量
    s1 = momentum_strategy(c, 60, 20)  # 从2.1章
    
    # 策略2: 布林带
    from bollinger_strategy import bollinger_strategy as s2  # 假设已定义
    # 这里用简化版
    s2 = pd.Series(0, index=c.index)
    ma = c.rolling(20).mean()
    std = c.rolling(20).std()
    s2[c > ma + 2*std] = -1
    s2[c < ma - 2*std] = 1
    
    # 策略3: 海龟
    s3 = turtle_strategy(d['High'], d['Low'], c)  # 从2.1章
    
    # 等权组合
    combined_signal = (s1 + s2 + s3) / 3
    combo_rets = combined_signal.shift(1) * r
    
    results['strategy_1'] = (1 + s1.shift(1) * r).prod() - 1
    results['strategy_2'] = (1 + s2.shift(1) * r).prod() - 1
    results['strategy_3'] = (1 + s3.shift(1) * r).prod() - 1
    results['combined'] = (1 + combo_rets.dropna()).prod() - 1
    
    print("单策略 vs 组合收益:")
    for k, v in results.items():
        print(f"  {k}: {v:.2%}")

关键点: 组合的夏普比率通常高于任何一个单策略——这就是"分散化的免费午餐"。

4. 风险平价——更聪明的权重分配

等权分配风险,而不是分配资金:

python
import numpy as np
import pandas as pd

def risk_parity_weights(returns_df):
    """
    风险平价:让每个策略贡献相等的风险
    
    简单实现:权重与历史波动率成反比
    """
    vols = returns_df.std()
    weights = (1 / vols) / (1 / vols).sum()
    return weights

# 模拟三个策略的收益率
np.random.seed(42)
strategy_rets = pd.DataFrame({
    '动量': np.random.normal(0.0005, 0.01, 500),
    '均值回归': np.random.normal(0.0003, 0.008, 500),
    '套利': np.random.normal(0.0004, 0.005, 500),
})

# 等权
equal_weight = np.array([1/3, 1/3, 1/3])
equal_portfolio = strategy_rets @ equal_weight

# 风险平价
rp_weights = risk_parity_weights(strategy_rets)
rp_portfolio = strategy_rets @ rp_weights

print(f"等权组合 夏普: {equal_portfolio.mean()/equal_portfolio.std() * np.sqrt(252):.2f}")
print(f"风险平价 夏普: {rp_portfolio.mean()/rp_portfolio.std() * np.sqrt(252):.2f}")
print(f"风险平价权重: {rp_weights.values.round(3)}")

5. 一个完整的多因子组合框架

python
import numpy as np
import pandas as pd

class MultiFactorPortfolio:
    """多因子组合框架"""
    
    def __init__(self, factors: dict):
        """
        factors: {'因子名': (信号函数, 权重)}
        """
        self.factors = factors
    
    def generate_signals(self, data):
        """生成组合信号"""
        all_signals = []
        
        for name, (signal_func, weight) in self.factors.items():
            signal = signal_func(data)
            all_signals.append(signal * weight)
        
        combined = sum(all_signals)
        return combined / sum(w for _, w in self.factors.values())
    
    def backtest(self, data):
        """组合回测"""
        signal = self.generate_signals(data)
        close = data['Adj Close']
        returns = close.pct_change()
        strategy_rets = signal.shift(1) * returns
        cumulative = (1 + strategy_rets).cumprod()
        
        return {
            'signal': signal,
            'returns': strategy_rets,
            'equity': cumulative,
            'sharpe': strategy_rets.mean() / strategy_rets.std() * np.sqrt(252),
        }

6. 组合风险管理

因子组合的收益很重要,但风险管理同样关键。

6.1 风险价值(VaR)

\text{VaR}_{95\%} = \text{在 95% 的情况下,亏损不会超过这个数}

python
import numpy as np

def var(returns, confidence=0.95):
    """计算 VaR(历史模拟法)"""
    return np.percentile(returns, (1 - confidence) * 100)

def cvar(returns, confidence=0.95):
    """CVaR(条件VaR):超过VaR时的平均亏损"""
    threshold = var(returns, confidence)
    return returns[returns < threshold].mean()

6.2 回撤分析

python
def drawdown_analysis(equity_curve):
    """全面回撤分析"""
    peak = equity_curve.expanding().max()
    drawdown = (equity_curve - peak) / peak
    return {
        'max_drawdown': drawdown.min(),
        'current_drawdown': drawdown.iloc[-1],
        'avg_drawdown': drawdown.mean(),
    }

6.3 压力测试

一个策略如果在 2020 年 3 月崩盘时亏了 50% 但你扛得住,那就没问题——前提是你真的扛得住

6.4 风险度量速查

指标度量内容越大越好?
夏普比率单位总风险的超额收益✅ 越大越好
索提诺比率单位下行风险的超额收益✅ 越大越好(比夏普更合理)
最大回撤从峰值到谷底的最大跌幅❌ 越小越好
卡玛比率年化收益 / 最大回撤✅ 越大越好
VaR(95%)95%置信下的最大损失❌ 越小越好
CVaR(95%)尾部事件的平均损失❌ 越小越好

深度阅读

练习

选择题

  1. 多因子组合的核心优势是:

    • A. 每个因子都加倍赚钱
    • B. 因子间相关性低,分散风险
    • C. 减少交易次数
    • D. 降低手续费
  2. 打分法中各因子的得分需要先:

    • A. 求平均值
    • B. 归一化(z-score或标准化)
    • C. 乘以交易量
    • D. 取绝对值
  3. 风险平价的权重依据是:

    • A. 各策略的历史收益
    • B. 各策略的历史风险(波动率)
    • C. 投资者的主观判断
    • D. 各策略的夏普比率
  4. 以下哪个是好的多因子组合?

    • A. 三个动量因子
    • B. 动量+均值回归+套利
    • C. 两个完全正相关的因子
    • D. 一个因子重复三次
  5. 组合的夏普比率通常___每个单策略的夏普:

    • A. 低于
    • B. 高于
    • C. 等于
    • D. 不确定

编程题

作业: 构建一个由动量 + 布林带 + RSI 组成的三因子组合,在 SPY 上做回测。比较组合 vs 每个单因子的夏普比率和最大回撤。

填空题

多因子组合中,最好选择因子之间 ____ 越 ____(高/低)越好。

掌握检查

📝 练习

💬 论述题

Built with VitePress