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加权就是结合了「准不准」和「稳不稳」两个维度。

具体做法:

  1. 计算每个因子在过去N期的IC序列
  2. 计算IC的均值(代表预测能力)
  3. 计算IC的标准差(代表稳定性)
  4. ICIR = IC均值 / IC标准差
  5. 权重 = 该因子的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 知识体系总览

下面这张图,帮你理清这四种加权方式的关系和选择逻辑:

多因子模型加权方式选择流程 因子合成 是否需要简单稳健的基准? 等权加权 是否考虑流动性? 市值加权 因子是否稳定? ICIR加权 最优化加权

嗯,这张图其实是我自己平时做决策时的思考路径。从「是否需要简单基准」开始,一步步走到最适合的加权方式。你想想看,是不是比直接套用一个模型要靠谱得多?

最后说一句:不管用哪种加权方式,一定要做样本外测试。我见过太多人在样本内跑得飞起,一到样本外就拉胯。记住,回测是历史,实盘才是未来。


公众号:蓝海资料掘金营,微信deep3321