【Python量化因子实战 #07】因子加权怎么选?等权 / IC 加权 / 最大化 IR 三种策略 PK

把多个因子合成一个选股分数,有很多种加权方式。最常用、也最容易被问到的三种:

  1. 等权:每个因子各占 50%,简单直接。
  2. IC 加权:因子过去预测能力越强,权重越高。
  3. 最大化 IR:在 IC 基础上再考虑稳定性,IC 均值高且波动小的因子权重更高。

本文用 30 只大盘股截面 + 60 日动量 + 20 日低波因子,把三种方案跑一遍,看它们对最终选股的差异。

一、为什么加权方式会影响结果

假设你已经有了两个因子:

  • mom_60d:60 日动量(不含最近 5 天,避开短期反转)。
  • vol_20d:20 日年化波动率,低波通常更稳,所以取 -vol_20d 作为得分方向。

如果不加权,两个因子量纲不同(一个是收益率,一个是波动率),直接相加没意义。通常先做 截面 z-score 标准化,再按权重求和:

score = w1 * z(mom) + w2 * z(-vol)

不同的 w1 / w2 就是不同的加权哲学。

二、本文你将得到什么

  • 三种加权方案的权重计算公式。
  • 一段可运行的 Python 代码,直接替换证书就能跑。
  • 30 只股票截面的分组收益对比(Top 5 vs Bottom 5)。
  • 一个常见坑:IC 加权不一定总是赢。

三、三种加权方案

方案 权重逻辑 公式
等权 因子一视同仁 w1 = w2 = 0.5
IC 加权 按 IC
最大化 IR 按 IR = IC均值 / IC标准差 wi = |IR_i| / Σ|IR_j|

IC(Information Coefficient)用 Spearman 秩相关 计算:因子值与未来收益的相关性。|IC| 越大,预测能力越强。

四、可运行代码

import pandas as pd
import numpy as np
from scipy.stats import spearmanr
from mairui import Client

公开演示证书;实际项目请换成自己的正式证书

client = Client("LICENCE-66D8-9F96-0C7F0FBCD073")
UNIVERSE = ["600519", "000001", "000002", "600036", "601318",
"601398", "600276", "000333", "600030", "601166"]

def fetch_panel():
rows = []
for code in UNIVERSE:
raw = client.stock_history(code, lt=120, dividend="n")
df = pd.DataFrame(raw)
df["code"] = code
rows.append(df[["code", "t", "c"]])
return pd.concat(rows)

def momentum(s):
return s.iloc[-6] / s.iloc[-66] - 1 # 60 日,跳过最近 5 日

def volatility(s):
return s.pct_change().tail(20).std() * np.sqrt(252)

def zscore(x):
return (x - x.mean()) / x.std()

panel = fetch_panel()
snap = []
for code, df in panel.groupby("code"):
df = df.sort_values("t")
snap.append({
"code": code,
"mom": momentum(df["c"]),
"vol_score": -volatility(df["c"]),
"fwd": df["c"].iloc[-1] / df["c"].iloc[-21] - 1,
})
snap = pd.DataFrame(snap).dropna()

ic_mom, _ = spearmanr(snap["mom"], snap["fwd"])
ic_vol, _ = spearmanr(snap["vol_score"], snap["fwd"])

三种权重

w_equal = {"mom": 0.5, "vol": 0.5}
abs_sum = abs(ic_mom) + abs(ic_vol)
w_ic = {"mom": abs(ic_mom) / abs_sum, "vol": abs(ic_vol) / abs_sum}
ir_mom, ir_vol = ic_mom / 0.05, ic_vol / 0.05 # 示例 std,仅演示公式
ir_sum = abs(ir_mom) + abs(ir_vol)
w_ir = {"mom": abs(ir_mom) / ir_sum, "vol": abs(ir_vol) / ir_sum}

for name, w in [("等权", w_equal), ("IC加权", w_ic), ("Max-IR", w_ir)]:
snap["score"] = w["mom"] * zscore(snap["mom"]) + w["vol"] * zscore(snap["vol_score"])
top5 = snap.nlargest(5, "score")["fwd"].mean() * 100
bot5 = snap.nsmallest(5, "score")["fwd"].mean() * 100
print(f"{name}: top5={top5:+.2f}% bot5={bot5:+.2f}% spread={top5-bot5:+.2f}%")

注意:stock_history 的返回字段以官方文档为准;演示证书可能返回有限数据,替换为正式证书后即可获得完整截面。

五、运行结果示例

等权 : top5=+0.22% bot5=-1.58% spread=+1.80%
IC加权 : top5=-2.77% bot5=-1.23% spread=-1.54%
Max-IR : top5=-2.77% bot5=-1.23% spread=-1.54%

本例中 等权反而更稳。原因是演示数据下低波因子 IC 接近 0,IC/Max-IR 加权把权重过度集中在动量上,导致组合失衡。实际交易中,因子权重需要结合样本外滚动 IC 与稳健性一起调。

六、常见坑

  1. IC 只是样本内指标:历史 IC 高不等于未来 IC 高,建议用滚动窗口跟踪。
  2. 权重不要过度集中:单一因子权重超过 70% 时,组合风险会逼近该单因子。
  3. 方向要先定对:波动率、换手率这类因子通常是“低者更好”,合成前要先取负号,否则权重越高的方向越错。

七、小结

  • 等权是最鲁棒的起点,适合因子同质化或 IC 差异不大时。
  • IC 加权 / Max-IR 加权更适合“你已经信任历史 IC 统计”的场景。
  • 真正上线前,应该用滚动 IC、样本外回测、相关性去重再校验一次。

下一篇:因子去重 —— 相关性高的因子留哪个?


免责声明

本文仅供技术学习与交流,不构成投资建议。文中示例数据不代表实时行情,投资有风险,入市需谨慎。

代码与文档

源码与接口文档见:

https://github.com/MaiRuiApi

posted @ 2026-09-28 10:40  量化分析码农  阅读(18)  评论(0)    收藏  举报