第四章:多因子合成——从单因子到组合的艺术
说实话,多因子合成这一步,是很多量化团队的分水岭。
单因子研究做得再好,如果不会合成,就像手里有一堆好食材却不会做菜。我见过不少团队,单因子IC都挺漂亮,一组合起来反而跑不过基准。问题出在哪?就在合成方法上。
今天咱们就把五种主流方法掰开揉碎讲清楚。等权、市值加权、ICIR加权、PCA降维、因子正交化——每种我都踩过坑,咱们边讲边避雷。
4.1 等权加权法——最简单的往往最稳健
等权加权,说白了就是给每个因子相同的权重。比如你有5个因子,每个权重就是20%。
你可能会问:「这也太简单了吧?」
嗯,我刚开始做量化时也这么想。但后来发现,在因子相关性较高的情况下,等权加权反而比很多复杂方法更稳定。为什么?因为它不会过度依赖某个因子,天然分散风险。
import pandas as pd
import numpy as np
def equal_weight_synthesis(factor_df):
"""
等权加权合成
factor_df: DataFrame, 每列为一个因子,行为股票
"""
# 先做横截面标准化
zscore_df = factor_df.apply(lambda x: (x - x.mean()) / x.std())
# 等权合成
composite = zscore_df.mean(axis=1)
return composite
4.2 市值加权法——让大股票多说话
市值加权,就是按股票市值大小分配权重。市值大的股票,在因子合成中影响力更大。
这个方法在A股有个天然问题——大市值股票往往被机构重仓,因子收益已经被充分挖掘。我曾经用市值加权合成过一组反转因子,结果回测曲线惨不忍睹。后来发现,小市值股票的反转效应更强,但市值加权把它们压得太低了。
def market_cap_weighted_synthesis(factor_df, market_cap):
"""
市值加权合成
market_cap: Series, 每只股票的市值
"""
# 市值归一化作为权重
weights = market_cap / market_cap.sum()
# 因子标准化
zscore_df = factor_df.apply(lambda x: (x - x.mean()) / x.std())
# 加权合成
composite = (zscore_df.multiply(weights, axis=0)).sum(axis=1)
return composite
4.3 ICIR加权法——让预测能力强的因子多说话
ICIR加权,是我个人最常用的方法之一。它结合了IC(信息系数)和IR(信息比率),本质上是「用历史表现来分配权重」。
具体来说:IC衡量因子与未来收益的相关性,IR衡量这个相关性的稳定性。ICIR = IC均值 / IC标准差。ICIR越高,说明因子预测能力越强且越稳定。
def icir_weighted_synthesis(factor_df, forward_returns):
"""
ICIR加权合成
forward_returns: DataFrame, 未来N日收益
"""
weights = {}
for col in factor_df.columns:
# 计算每日IC
daily_ic = factor_df[col].rolling(20).corr(forward_returns)
# ICIR = IC均值 / IC标准差
ic_mean = daily_ic.mean()
ic_std = daily_ic.std()
weights[col] = ic_mean / ic_std if ic_std != 0 else 0
# 归一化权重
total = sum(weights.values())
weights = {k: v/total for k, v in weights.items()}
# 合成
zscore_df = factor_df.apply(lambda x: (x - x.mean()) / x.std())
composite = sum(zscore_df[col] * weights[col] for col in factor_df.columns)
return composite
4.4 主成分分析(PCA)降维——从10个因子到3个主成分
PCA降维,说白了就是把多个因子压缩成少数几个「主成分」。这些主成分是原始因子的线性组合,彼此正交(不相关)。
我刚开始用PCA时犯过一个错误:直接对所有因子做PCA,然后取前几个主成分。结果发现第一个主成分解释方差超过60%,但它其实是个「市场因子」,把所有股票都拉在一起了。这导致选股效果很差。
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
def pca_synthesis(factor_df, n_components=3):
"""
PCA降维合成
n_components: 保留的主成分数量
"""
# 标准化
scaler = StandardScaler()
scaled_data = scaler.fit_transform(factor_df)
# PCA
pca = PCA(n_components=n_components)
pca_data = pca.fit_transform(scaled_data)
# 用解释方差比作为权重,合成一个综合得分
explained_ratio = pca.explained_variance_ratio_
composite = pca_data @ explained_ratio
return composite, pca
4.5 因子正交化处理——消除冗余,保留纯净信号
因子正交化,是我认为最「硬核」的合成方法。它的核心思想是:把因子之间的相关性剥离掉,让每个因子只贡献「独特」的信息。
举个例子:你有一个估值因子(PE)和一个盈利因子(ROE)。它们天然相关——高ROE的公司往往PE也高。如果不做正交化,这两个因子会互相「抢功劳」,导致组合后的信号有冗余。
import statsmodels.api as sm
def factor_orthogonalization(factor_df, target_factor):
"""
因子正交化:将target_factor对其他因子做回归,取残差
"""
X = factor_df.drop(columns=[target_factor])
X = sm.add_constant(X)
y = factor_df[target_factor]
model = sm.OLS(y, X).fit()
# 残差就是正交化后的纯净因子
orthogonal_factor = model.resid
return orthogonal_factor
4.6 五种方法对比——怎么选?
| 方法 | 优点 | 缺点 | 我的推荐场景 |
|---|---|---|---|
| 等权加权 | 简单、稳健、不易过拟合 | 忽略因子质量差异 | 新手入门、因子数量少 |
| 市值加权 | 贴近市场真实结构 | 小盘股被压制 | 大盘股策略 |
| ICIR加权 | 动态调整、自适应 | 容易过拟合 | 成熟因子库、有足够历史数据 |
| PCA降维 | 降维、去噪 | 主成分含义不明确 | 因子数量多(>10个) |
| 因子正交化 | 消除冗余、信号纯净 | 可能过度削除信号 | 因子间高度相关 |
4.7 知识体系图——多因子合成的完整流程
这张图把整个流程串起来了。从原始因子出发,经过标准化预处理,然后选择一种或多种合成方法,最终得到合成因子得分。我个人建议:先用等权加权跑通流程,再逐步尝试复杂方法。