因子的各种处理方法

易斋易斋··137 浏览

你的因子就像一个素颜的选手——底子不错,但稍加修饰就能惊艳全场。本文从"为什么需要处理"出发,到"用什么方法处理什么问题",再到"每招能改善哪个指标",帮你打通因子预处理的完整逻辑链。


一、从一张成绩单说起

假设你刚跑完一个因子回测,拿到这样一份报告:
::: hljs-center

指标 数值 评价
年化收益 18.60% 还行
夏普比率 0.6856 中等
最大回撤 24.71% 有点大
IC均值 0.0171 信号偏弱
Rank IC 0.0361 比IC均值好
IC标准差 0.0576 波动太大
ICIR 0.2977 偏低
t统计量 2.4909 显著
p-value 0.0152 显著
P(IC<-0.02) 27.14% 失效期太多
P(IC>0.02) 44.29% 有效期不到一半
单调性 0.81 尚可

:::

一句话总结:因子有信号,但不够稳定、不够纯净、偶尔还"翻脸"。

问题出在哪?——————缺少一些处理,方便面没放调料包

二、为什么原始因子需要处理?

量化因子的原始数据,往往有四大"缺陷":

缺陷1:分布偏得离谱

市值因子是最典型的例子。A股最大公司市值2万亿,最小公司20亿,差了1000倍。如果你直接用原始市值做分组,第一组全是一两百亿的公司,最后一组就一个茅台——分组根本不均匀,分组收益自然也不单调。

受害者指标:单调性、IC均值、Rank IC

缺陷2:极端值横行

利润增速因子,去年赚1万今年赚100万,增速9900%。PE因子,微利公司PE能到5000倍。这些极端值就像班上考了0分和100分的同学——把平均分拉偏了,也把相关性拉偏了。

受害者指标:IC标准差、最大回撤、ICIR

缺陷3:行业"自带属性"

银行股天生市值大、ROE高、估值低;科技股天生市值小、ROE低、估值高。如果你不做处理,ROE因子其实在"选行业"而不是"选个股"——当银行板块下跌时,ROE因子就"翻脸"了。

受害者指标:IC均值、P(IC<-0.02)、ICIR

缺陷4:量纲各不相同

PE是比率,市值是绝对值,换手率是百分比——它们放在一起就像用"米""斤""度"同时描述一个人的身高。多因子合成时,谁量大谁说了算,这不公平。

受害者指标:多因子合成后的夏普比率、ICIR


三、八大"美容"手法,逐一拆解

手法1:对数化——专治"偏态脸"

一句话:把差几个数量级的值,压缩到同一个"量级赛场"上。

原理:log变换就像一个"压缩器",大值压缩得多,小值压缩得少。20000亿变5.3,500亿变3.7,5亿变1.7——原来差1000倍,现在差不到4倍。

操作

factor_log = np.log(factor)  # 确保因子为正

适用因子:市值、换手率、交易量、波动率等右偏因子

改善指标

指标 可能的改善方向 原因
IC均值 分布对称后,因子与收益的线性关系更真实
Rank IC 中间段排序更合理
IC标准差 消除长尾后,每期IC波动减小
单调性 分组更均匀,组间收益递变更自然

手法2:缩尾——极值处理

一句话:不删除极端值,但把"刺头"按回合理范围。

原理:把超过1%和99%分位数的值,直接"拉回"到分位数边界。就像考试时,超过100分的按100分算,低于0分的按0分算——人还在,但分数被"截"住了。

操作:

from scipy.stats import mstats
factor_win = mstats.winsorize(factor, limits=[0.01, 0.01])

适用因子:PE、利润增速、动量、ROE等偶有极端值但不严重右偏的因子

改善指标:

指标 改善方向 原因
IC标准差 极端值是IC波动的主要来源,截断后IC更稳定
最大回撤 极端持仓被抑制,不会因个别股票导致大幅回撤
ICIR IC标准差下降的直接结果
t统计量 分母减小,显著性增强
p-value 更显著

手法3:MAD去极值——缩尾的"升级版"

一句话:缩尾是"一刀切",MAD是"看情况切"。

原理:缩尾固定用1%/99%分位数,不管极端值多还是少。MAD(中位数绝对偏差)则基于中位数来判断什么是"极端"——如果数据本身就很分散,阈值就宽松;如果数据很集中,阈值就严格。自适应,更精准。

操作:


python
median = np.median(factor)
mad = np.median(np.abs(factor - median))
upper = median + 5 * mad
lower = median - 5 * mad
factor_mad = np.clip(factor, lower, upper)

适用因子:与缩尾相同,但效果通常更好

改善指标:与缩尾相同,IC标准差和最大回撤通常比缩尾再降5%-10%

手法4:排名标准化——"万能选手"

一句话:不管原始值长什么样,我只看谁大谁小。

原理:把因子值完全替换为排名百分比。市值最大的股票标1.0,最小的标0.0,中间的按比例排。原始值是20000亿还是5亿?不重要,我只知道你排第几。

操作:


python
from scipy.stats import rankdata
factor_rank = rankdata(factor) / len(factor)

适用因子:几乎所有因子,尤其是分布不规则、含负值、无法取对数的因子(如利润增速、PE)

改善指标:
|指标 | 改善方向 | 原因|
I|C标准差 ↓↓ 排名值有界[0,1],天然抗极端值,IC波动大幅减小

ICIR ↑↑ IC标准差大幅下降的直接结果

单调性 ↑ 排名分组天然均匀,每组股票数量相同

最大回撤 ↓ 不会因极端值导致持仓过于集中

P(IC<-0.02) ↓ IC更稳定,负向极端IC减少

代价:IC均值可能轻微下降——因为你丢失了原始值之间的"距离"信息。比如市值100亿和200亿差100亿,但排名可能只差0.01。不过通常ICIR的提升远比IC均值的轻微下降更划算。

生活类比:排名标准化就像高考只看省排名,不看具体分数——你考了680还是700不重要,重要的是你排第几名。

手法5:Box-Cox / Yeo-Johnson变换——"智能对数化"

一句话:对数化是手动选λ=0,Box-Cox让数据自己选最优λ。

原理:对数化是Box-Cox变换的特例(λ=0)。Box-Cox通过搜索找到让数据最接近正态分布的λ值——可能λ=0.3比λ=0更好,也可能λ=0.5更好。Yeo-Johnson是Box-Cox的升级版,还能处理零值和负值。

操作:


python
Box-Cox(要求正值)
from scipy.stats import boxcox
factor_bc, lambda_opt = boxcox(factor + 1e-6)

Yeo-Johnson(允许零值和负值)
from sklearn.preprocessing import PowerTransformer
pt = PowerTransformer(method='yeo-johnson')
factor_yj = pt.fit_transform(factor.reshape(-1, 1))

适用因子:对数化效果不够好时的"升级选项",尤其是负值多的因子用Yeo-Johnson

改善指标:与对数化相同,但ICIR和单调性可能比对数化再提升5%-15%

手法6:行业中性化——"卸掉行业包袱"

一句话:不让因子"替行业打工",只让它"选个股"。

原理:将因子对行业哑变量回归,取残差。残差代表的是"同一个行业里,你比别人好多少"——这才是因子的真实选股能力。

操作:


python
import statsmodels.api as sm
X = pd.get_dummies(industry)
factor_neutral = sm.OLS(factor, X).fit().resid

适用因子:几乎所有基本面因子,尤其是市值、ROE、PE

改善指标:

指标 改善方向 原因
IC均值 去掉行业混杂后,因子信号更纯净
ICIR IC更稳定,不再受行业轮动干扰
t统计量 IC均值提升 + IC标准差下降
P(IC<-0.02) 行业轮动导致的"翻脸"期减少
夏普比率 收益更稳定

生活类比:行业中性化就像"控制变量法"——比较两个学生的成绩,不能只看总分,要看同班级里的排名。银行股ROE高是"班级平均分高",不是"这个学生特别优秀"。

手法7:正交化——"去掉重复信号"

一句话:新因子必须带来"增量信息",不能和已有因子说一样的话。

原理:将因子对其他已知因子(如市值、行业)回归取残差。残差代表的是新因子"独有的、别人没有的"那部分信息。

操作:


python
X = sm.add_constant(existing_factors)
factor_orth = sm.OLS(factor, X).fit().resid

适用场景:因子合成前,确保新因子有增量信息

改善指标:

指标 改善方向 原因
ICIR 独立信号更稳定
t统计量 独立信号的t值更显著
P(IC<-0.02) 不再受其他因子周期性影响
夏普比率 多因子组合中,正交因子贡献更分散

生活类比:正交化就像面试时问"你有什么别人没有的特长"——如果你只会Java,但团队里已经有三个Java高手,那你的"增量价值"就是零。

手法8:Z-Score标准化——"统一度量衡"

一句话:让所有因子站在同一条起跑线上。

原理:减去均值,除以标准差。变换后均值为0,标准差为1。不改变排序,不改变分布形态,只改变"刻度"。

操作:


python
factor_z = (factor - np.mean(factor)) / np.std(factor)

关键认知:Z-Score是"对齐"操作,不是"改善"操作。单独一个因子做Z-Score,IC不会变。但多因子合成时,如果PE是0-100的量级,换手率是0-0.5的量级,不做Z-Score的话PE会完全主导合成结果。

改善指标:

指标 改善方向 原因
(多因子合成时) 夏普比率 ↑ 各因子贡献均衡
(多因子合成时) ICIR ↑ 合成信号更稳定

生活类比:Z-Score就像把"人民币""美元""日元"统一换算成"购买力平价"——不是改变钱的价值,而是让它们可以公平比较。

四、实战组合拳——"因子的标准护肤流程"

就像护肤有"清洁→爽肤→精华→面霜"的顺序,因子处理也有标准流程:

原始因子


① 行业中性化 ──→ 卸掉行业包袱,让因子"做自己的事"


② 对数化 / Box-Cox ──→ 把偏态"掰正",让分布更对称


③ 缩尾 / MAD ──→ 把极端值"按住",不让它们捣乱


④ 排名标准化(可选)───→ 如果以上还不够稳,用排名"兜底"


⑤ Z-Score ──→ 统一刻度,为多因子合成做准备

常见因子的"定制套餐":
因子 推荐处理流程
市值 log → 缩尾 → 行业中性化 → Z-Score

PE 取倒数→EP → 缩尾 → 排名标准化 → Z-Score

动量 缩尾 → Z-Score(不取对数)

换手率 log → 缩尾 → 行业中性化 → Z-Score

波动率 log → 缩尾 → Z-Score

ROE 缩尾 → 行业中性化 → 排名标准化

利润增速 缩尾(宽阈值)→ 排名标准化

五、对症下药——你的因子哪里弱,就补哪里

回到我们开头那份成绩单,逐项分析:

问题1:ICIR = 0.2977,太低了

病因:IC标准差太大(0.0576),信号时有时无

处方:

首选:排名标准化 → IC标准差大幅下降,ICIR通常能到0.5+
次选:缩尾/MAD → 截断极端值,减少IC波动
辅助:行业中性化 → 去掉行业混杂,IC更稳定

问题2:最大回撤 = 24.71%,太疼了

病因:极端值导致某期持仓过于集中

处方:

首选:缩尾 → 极端值被截住,不会重仓异常股票
次选:排名标准化 → 持仓天然分散
辅助:行业中性化 → 避免行业集中度风险

问题3:P(IC<-0.02) = 27.14%,失效期太多

病因:行业轮动和极端值导致IC周期性"翻脸"

处方:

首选:行业中性化 → 去掉行业混杂,IC不再随行业轮动反转
次选:正交化 → 去掉与其他因子的共线,减少周期性干扰
辅助:排名标准化 → IC更稳定,极端负值减少

问题4:IC均值 = 0.0171,信号偏弱

病因:分布偏态导致因子与收益的真实关系被掩盖

处方:

首选:行业中性化 → 释放被行业混杂掩盖的个股选择能力
次选:对数化/Box-Cox → 分布对称后,线性关系更显著
注意:排名标准化可能让IC均值轻微下降,但ICIR的提升通常更划算

问题5:单调性 = 0.81,还有提升空间

病因:分组不均匀,某组被极端值"拉偏"

处方:

首选:对数化 → 压缩极端值,分组更均匀
次选:排名标准化 → 每组股票数量相同,天然均匀
辅助:Box-Cox → 可能比对数化效果更好

六、一张图总结全文

        因子原始问题                    处理方法                 改善指标
       ┌──────────┐               ┌──────────┐            ┌──────────┐
       │  偏态严重  │───────→──────│  对数化    │───────→───│ IC均值↑  │
       │  (市值等)  │               │ Box-Cox   │            │ 单调性↑  │
       └──────────┘               └──────────┘            └──────────┘
       ┌──────────┐               ┌──────────┐            ┌──────────┐
       │  极端值多  │───────→──────│   缩尾    │───────→───│ IC标准差↓│
       │  (PE/增速) │               │   MAD    │            │ 最大回撤↓│
       └──────────┘               └──────────┘            └──────────┘
       ┌──────────┐               ┌──────────┐            ┌──────────┐
       │  分布不规则 │───────→──────│ 排名标准化 │───────→───│ ICIR↑↑  │
       │  (含负值)  │               │          │            │ 单调性↑  │
       └──────────┘               └──────────┘            └──────────┘
       ┌──────────┐               ┌──────────┐            ┌──────────┐
       │ 行业混杂   │───────→──────│ 行业中性化 │───────→───│ IC均值↑  │
       │ (基本面因子)│               │          │            │ P(失效)↓ │
       └──────────┘               └──────────┘            └──────────┘
       ┌──────────┐               ┌──────────┐            ┌──────────┐
       │ 因子共线   │───────→──────│  正交化   │───────→───│ ICIR↑   │
       │ (多因子)   │               │          │            │ t统计量↑ │
       └──────────┘               └──────────┘            └──────────┘
       ┌──────────┐               ┌──────────┐            ┌──────────┐
       │ 量纲不统一 │───────→──────│ Z-Score  │───────→───│ 合成夏普↑│
       │ (多因子)   │               │          │            │ 合成ICIR↑│
       └──────────┘               └──────────┘            └──────────┘

七、最后的话

因子预处理不是"造假",而是"还原真相"。

原始因子数据里,有太多"噪音"在掩盖信号——偏态让分组不均匀,极端值让IC波动,行业混杂让因子"替行业打工"。预处理的本质,是把这些噪音去掉,让因子真实的选股能力浮出水面。

记住一句话:

对数化治偏态,缩尾治极端,排名治波动,中性化治混杂,正交化治共线,Z-Score治量纲。

你的因子底子不错——IC显著、单调性尚可。只需稍加"修饰",就能从"还行"变成"真香"。
本文完整代码及因子处理工具包,可参考前文各方法代码片段组合使用。

评论

还没有评论,来说点什么吧
0 / 2000