3、多因子模型构建:等权加权、市值加权、ICIR加权、最优化加权
好,咱们进入正题。因子选出来了,也处理干净了,接下来就是怎么把它们「捏」到一起。说白了,多因子模型的核心就一句话:每个因子该给多少权重?
我见过不少新手,一上来就搞复杂的优化模型,结果过拟合得一塌糊涂。我个人习惯是:从简单到复杂,先跑通,再优化。今天咱们就聊聊四种最常见的加权方式——等权、市值加权、ICIR加权,还有最优化加权。
核心观点:没有绝对最好的加权方式,只有最适合你策略场景的加权方式。实盘中,我通常会同时跑几种加权方案,做个对比。
3.1 等权加权——最朴素的 baseline
等权加权,顾名思义,每个因子权重一样。比如你有5个因子,每个权重就是20%。
为什么还要讲这个?因为它简单、稳健、不容易过拟合。我在做策略回测时,永远先用等权加权跑一遍,当作基准。如果后面复杂模型跑出来的结果还不如等权,那说明你的优化方向可能有问题。
# 等权加权示例
import pandas as pd
import numpy as np
# 假设有3个因子:因子A、因子B、因子C
factors = ['因子A', '因子B', '因子C']
weights_equal = np.array([1/3, 1/3, 1/3])
# 合成综合得分
df['综合得分_等权'] = (df['因子A'] * weights_equal[0] +
df['因子B'] * weights_equal[1] +
df['因子C'] * weights_equal[2])
我的经验:等权加权适合因子之间相关性较低、且没有明显优劣之分的情况。如果某个因子明显比其他因子更稳定,等权就会「拖后腿」。
3.2 市值加权——让大市值股票说了算
市值加权,就是按股票的市值大小来分配权重。市值越大,权重越高。这其实是很多指数编制的方法,比如沪深300就是市值加权。
但注意,这里说的市值加权有两种理解:
- 因子内部的市值加权:计算因子值时,按市值加权平均(比如计算行业因子时常用)
- 组合构建的市值加权:选完股票后,按市值分配仓位
咱们这里主要讲第二种。市值加权的优点是流动性好、容量大。但缺点也很明显——容易过度集中在少数大市值股票上。我曾经回测过一个策略,市值加权后前10只股票占了60%的仓位,结果一只股票暴雷,整个组合跟着遭殃。
# 市值加权示例
# 假设已经选出了top 100只股票
df['市值权重'] = df['市值'] / df['市值'].sum()
df['综合得分_市值加权'] = (df['因子A'] * 0.3 +
df['因子B'] * 0.3 +
df['因子C'] * 0.4) * df['市值权重']
避坑指南:我曾经在2018年用市值加权做回测,结果因为某只超大市值股票权重过高,导致组合波动率飙升。后来我加了个「单只股票权重上限」——比如不超过10%,效果好了很多。
3.3 ICIR加权——让「准」的因子多说话
ICIR加权,说白了就是:哪个因子预测得准,就给它更大的权重。
IC(信息系数)衡量的是因子值与未来收益的相关性。IR(信息比率)是IC的均值除以标准差,衡量的是因子的稳定性。ICIR加权就是结合了「准不准」和「稳不稳」两个维度。
具体做法:
- 计算每个因子在过去N期的IC序列
- 计算IC的均值(代表预测能力)
- 计算IC的标准差(代表稳定性)
- ICIR = IC均值 / IC标准差
- 权重 = 该因子的ICIR / 所有因子ICIR之和
# ICIR加权示例
import numpy as np
# 假设有3个因子,过去20期的IC数据
ic_data = {
'因子A': [0.05, 0.03, 0.07, 0.02, 0.06, ...], # 20个值
'因子B': [0.01, 0.04, -0.02, 0.03, 0.05, ...],
'因子C': [0.08, 0.06, 0.09, 0.07, 0.10, ...]
}
# 计算ICIR
icir_weights = {}
for factor, ic_list in ic_data.items():
ic_mean = np.mean(ic_list)
ic_std = np.std(ic_list)
icir = ic_mean / ic_std if ic_std != 0 else 0
icir_weights[factor] = icir
# 归一化
total_icir = sum(icir_weights.values())
for factor in icir_weights:
icir_weights[factor] /= total_icir
print("ICIR权重:", icir_weights)
注意:ICIR加权有个坑——如果某个因子过去表现特别好,ICIR会很高,导致权重过大。但市场风格一变,这个因子可能就失效了。我建议用滚动窗口(比如过去12个月)来计算ICIR,并且定期调整权重。
3.4 最优化加权——数学家的玩具?
最优化加权,就是通过数学优化模型,求解一组权重,使得某个目标函数最优。常见的目标函数有:
- 最大化组合ICIR
- 最小化组合波动率
- 最大化夏普比率
- 风险预算(Risk Parity)
听起来很高级对吧?但我要泼盆冷水——最优化加权是最容易过拟合的。我见过有人用100个因子做最优化,结果权重全集中在两三个因子上,其他因子权重几乎为零。这明显是过拟合了。
举个最简单的例子——最大化组合ICIR:
# 最优化加权示例(最大化组合ICIR)
from scipy.optimize import minimize
import numpy as np
# 假设因子收益率的协方差矩阵
cov_matrix = np.array([
[0.01, 0.002, 0.001],
[0.002, 0.008, 0.0015],
[0.001, 0.0015, 0.012]
])
# 因子IC均值向量
ic_means = np.array([0.05, 0.03, 0.07])
# 目标函数:最大化组合ICIR = (w^T * IC) / sqrt(w^T * cov * w)
def objective(weights):
port_ic = np.dot(weights, ic_means)
port_risk = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
return -port_ic / port_risk # 取负号因为minimize是求最小值
# 约束:权重和为1
constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
# 边界:权重在0到1之间
bounds = [(0, 1) for _ in range(3)]
# 初始权重(等权)
initial_weights = [1/3, 1/3, 1/3]
# 求解
result = minimize(objective, initial_weights,
method='SLSQP', bounds=bounds,
constraints=constraints)
print("最优权重:", result.x)
我的忠告:最优化加权一定要加约束条件。比如:
- 单个因子权重不超过30%
- 权重之和为1
- 做空限制(权重不能为负)
不加约束的最优化,结果往往很极端。我曾经吃过这个亏,回测曲线漂亮得不像话,实盘一跑就崩。
3.5 四种加权方式的对比
| 加权方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 等权加权 | 简单、稳健、不易过拟合 | 忽略因子质量差异 | 初步探索、基准模型 |
| 市值加权 | 流动性好、容量大 | 集中度高、受大市值影响大 | 大资金、指数跟踪 |
| ICIR加权 | 动态调整、自适应 | 依赖历史数据、有滞后性 | 因子稳定性较高的策略 |
| 最优化加权 | 理论上最优、可定制 | 易过拟合、对输入敏感 | 有丰富因子历史数据、风控严格 |
3.6 知识体系总览
下面这张图,帮你理清这四种加权方式的关系和选择逻辑:
嗯,这张图其实是我自己平时做决策时的思考路径。从「是否需要简单基准」开始,一步步走到最适合的加权方式。你想想看,是不是比直接套用一个模型要靠谱得多?
最后说一句:不管用哪种加权方式,一定要做样本外测试。我见过太多人在样本内跑得飞起,一到样本外就拉胯。记住,回测是历史,实盘才是未来。
公众号:蓝海资料掘金营,微信deep3321