第四章 多因子模型入门:因子投资思想、常见因子分类与单因子测试框架
各位同学,欢迎来到多因子模型的第一课。
说实话,因子投资这个概念,刚入行的时候我也觉得挺玄乎的。什么「因子」啊、「暴露」啊,听着像物理课。但干久了你会发现,它其实就是一套系统化的选股逻辑——把那些能赚钱的规律,用数学语言描述出来。
4.1 因子投资的核心思想
因子投资的本质,说白了就是「找规律,做组合」。
我们不去猜哪只股票明天会涨,而是去找那些长期来看能带来超额收益的「共性特征」。比如:便宜的股票是不是比贵的股票表现好?过去涨得好的股票,接下来是不是还能涨?
这些「共性特征」,就是因子。
核心公式:
预期收益 = 无风险收益 + Σ(因子暴露 × 因子收益)
你想想看,这个公式其实跟做菜很像——因子就是调料,因子暴露就是放多少,因子收益就是调料带来的味道提升。
我个人习惯把因子投资拆成三步:
- 找因子——发现能赚钱的规律
- 测因子——验证这个规律是不是真的有效
- 用因子——把多个因子组合起来做选股
嗯,这里要注意:不是所有看起来有效的规律都能用。我在项目中遇到过很多「回测漂亮、实盘拉胯」的因子,原因后面会讲。
4.2 常见因子分类
因子的分类方式很多,我按最经典的三大类来讲:价值、动量、质量。
4.2.1 价值因子
价值因子是最古老的因子之一。它的逻辑很简单:买便宜的股票。
常用的指标有:
- 市盈率(PE)——越低越便宜
- 市净率(PB)——破净股往往被低估
- 股息率——高分红说明公司现金流好
避坑指南:我曾经用PE选股,结果选了一堆银行股。银行PE确实低,但增长也慢。后来我加了「PE分位数」这个条件,只选那些处于历史低位的股票,效果好了不少。
4.2.2 动量因子
动量因子的逻辑是「强者恒强,弱者恒弱」。
常用的有:
- 过去N个月收益率——比如过去12个月涨得好的股票
- 均线乖离率——股价偏离均线的程度
- RSI指标——超买超卖信号
但动量因子有个坑:它容易在震荡市里反复打脸。我记得2018年那会儿,动量因子回撤了将近30%,很多同行都扛不住砍掉了。
4.2.3 质量因子
质量因子关注的是公司本身的「质地」。
| 指标 | 含义 | 计算方式 |
|---|---|---|
| ROE | 净资产收益率 | 净利润 / 净资产 |
| 毛利率 | 产品竞争力 | (收入-成本) / 收入 |
| 资产负债率 | 财务风险 | 总负债 / 总资产 |
质量因子有个好处:它跟其他因子的相关性低。你想想看,便宜的股票不一定质量好,质量好的股票不一定便宜。所以把质量因子跟价值因子搭配,往往能起到分散风险的作用。
4.3 因子数据处理
拿到原始因子数据后,不能直接用。为什么?因为数据里有噪音、有极端值、还有各种「脏东西」。
4.3.1 去极值
去极值就是处理那些离谱的数据点。比如某只股票PE突然变成1000倍,这明显是异常。
常用的方法有两种:
- MAD法:用中位数和绝对偏差来识别异常值
- 百分位法:直接砍掉上下1%或5%的数据
def winsorize_series(s, limits=[0.01, 0.01]):
"""
去极值:用百分位法
"""
lower = s.quantile(limits[0])
upper = s.quantile(1 - limits[1])
return s.clip(lower, upper)
# 举个例子
import pandas as pd
pe_series = pd.Series([10, 12, 15, 1000, 11, 13])
cleaned = winsorize_series(pe_series)
print(cleaned)
# 输出:10, 12, 15, 15, 11, 13
# 那个1000被砍成了15
注意:去极值不是删除数据,而是把极端值拉到边界上。我曾经见过有人直接删掉异常值,结果导致样本量不均匀,回测结果严重失真。
4.3.2 标准化
标准化是为了让不同量纲的因子可以比较。比如PE的数值范围是0-100,而ROE是0-1,不标准化没法直接对比。
def standardize(s):
"""
Z-score标准化
"""
return (s - s.mean()) / s.std()
# 标准化后,所有因子的均值都是0,标准差都是1
4.3.3 中性化
中性化是消除因子中的「杂质」。比如市值大的公司PE普遍偏低,如果你直接用PE选股,选出来的可能都是大盘股,而不是真正的「便宜股」。
中性化的做法:把因子对市值、行业等变量做回归,取残差作为「纯净」的因子值。
import statsmodels.api as sm
def neutralize(factor, market_cap, industry_dummies):
"""
对市值和行业做中性化
"""
X = pd.concat([market_cap, industry_dummies], axis=1)
X = sm.add_constant(X)
model = sm.OLS(factor, X).fit()
return model.resid # 残差就是中性化后的因子
个人经验:中性化这一步,很多人会忽略。但我建议你一定要做。我2019年做过一个测试:同样的因子,中性化前后的IC值差了将近0.05,效果天差地别。
4.4 单因子测试框架
单因子测试,就是验证一个因子到底有没有用。我常用的框架分四步:
- 分层回测:把股票按因子值分成10组,看每组的表现
- IC分析:计算因子值与未来收益的相关性
- 多空组合:做多因子值最高的组,做空最低的组
- 稳定性检验:看因子在不同市场环境下的表现
下面是一个简单的分层回测代码:
def factor_backtest(factor, returns, n_groups=10):
"""
单因子分层回测
factor: 因子值
returns: 未来一期收益
"""
# 按因子值分组
groups = pd.qcut(factor, n_groups, labels=False)
# 计算每组平均收益
group_returns = returns.groupby(groups).mean()
# 计算多空收益
long_short = group_returns.iloc[-1] - group_returns.iloc[0]
return group_returns, long_short
# 假设我们有因子数据和收益数据
# group_ret, ls_ret = factor_backtest(pe_factor, next_month_returns)
# print(f"多空收益: {ls_ret:.4f}")
嗯,这里有个关键点:单因子测试的样本量要足够大。我一般要求至少3年的数据,覆盖完整的牛熊周期。否则你测出来的结果,可能只是运气好。
4.5 本章知识体系
为了让你更直观地理解本章的内容,我画了一张流程图:
这张图把本章的核心内容串起来了。你想想看,从因子思想出发,我们知道了要「找规律」;然后通过因子分类,我们知道了「找什么规律」;接着通过数据处理,我们学会了「怎么把数据洗干净」;最后用测试框架,我们验证「这个规律到底行不行」。
好了,这一章的内容就到这里。因子投资的门算是打开了,后面我们会一步步深入,把每个环节都吃透。
公众号:蓝海资料掘金营,微信deep3321