第三章 单因子构建基础:基于行情数据的经典因子
做量化投资,说白了就是找规律。而找规律的第一步,就是构建因子。
我个人习惯把因子理解为「股票的某种特征」。比如这只股票最近涨得猛,那只股票波动特别大——这些特征如果能预测未来收益,那就是有效因子。
这一章,我们聚焦三个最经典的因子:动量因子、反转因子和波动率因子。它们都基于行情数据,是量化入门的必修课。
核心观点:因子不是算出来就完事了。你得理解它背后的逻辑,知道它什么时候失效,才能用好它。
3.1 动量因子:追涨杀跌的数学表达
动量因子的逻辑很简单:过去涨得好的股票,未来一段时间还会继续涨。这听起来像废话,但背后有行为金融学的支撑——投资者反应不足、信息传播缓慢。
我在项目中遇到过这样的情况:用过去12个月的动量做多因子模型,回测收益很漂亮。但一到市场风格切换时,动量因子就「翻车」了。嗯,这里要注意,动量因子在震荡市里表现很差。
计算方式其实不复杂:
import pandas as pd
import numpy as np
def momentum_factor(close, window=20):
"""
计算动量因子
close: DataFrame, 每列是一只股票的收盘价
window: 回溯窗口
返回: 过去window天的累计收益率
"""
# 计算收益率
ret = close.pct_change()
# 滚动求和
momentum = ret.rolling(window=window).sum()
return momentum
# 举个栗子
# 假设我们有5只股票,100个交易日的数据
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=100, freq='D')
stocks = ['股票A', '股票B', '股票C', '股票D', '股票E']
close_data = pd.DataFrame(
np.random.randn(100, 5).cumsum(axis=0) + 100,
index=dates,
columns=stocks
)
mom_20 = momentum_factor(close_data, window=20)
print(mom_20.tail())
我的小技巧:窗口期的选择很关键。我个人习惯用20天(约一个月)和60天(一个季度)两个窗口。短周期动量更灵敏,长周期动量更稳定。你可以都试试,看哪个更适合你的策略。
3.2 反转因子:均值回归的信仰
反转因子和动量因子正好相反。它认为过去涨得多的股票,未来会跌回来。说白了就是「涨多了会跌,跌多了会涨」。
你想想看,这个逻辑在A股市场是不是特别常见?很多股票短期暴涨后,很快就回调了。我曾经用5日反转因子做过一个短线策略,效果还不错。但要注意,反转因子在趋势行情里会亏得很惨。
计算反转因子,其实就是把动量因子取个负号:
def reversal_factor(close, window=5):
"""
计算反转因子
返回: 过去window天的累计收益率的相反数
"""
ret = close.pct_change()
reversal = -ret.rolling(window=window).sum()
return reversal
rev_5 = reversal_factor(close_data, window=5)
print(rev_5.tail())
避坑指南:我曾经犯过一个错误——直接用日收益率累加来计算短期反转。结果发现,如果股票中间有停牌,累加出来的收益率会失真。正确的做法是:用复权价格计算连续收益率,或者用对数收益率累加。
3.3 波动率因子:风险就是收益?
波动率因子衡量的是股票价格的震荡幅度。低波动率的股票,往往有更好的未来收益——这就是著名的「低波动异象」。
为什么会这样?我个人理解是:机构投资者偏好高波动股票来博收益,导致高波动股票被高估。而低波动股票被忽视,反而有超额收益。
计算波动率,最常用的是历史波动率:
def volatility_factor(close, window=20):
"""
计算波动率因子
返回: 过去window天收益率的标准差
"""
ret = close.pct_change()
vol = ret.rolling(window=window).std()
return vol
vol_20 = volatility_factor(close_data, window=20)
print(vol_20.tail())
除了历史波动率,还有几个变种:
- 已实现波动率:用日内高频数据计算,更精确
- 波动率偏度:衡量波动率的不对称性
- 波动率聚集:高波动之后往往跟着高波动
3.4 因子构建的通用流程
做多了你会发现,所有因子的构建流程都差不多。我总结了一个四步法:
- 数据准备:获取行情数据,做复权处理
- 因子计算:用公式算出原始因子值
- 因子处理:去极值、标准化、中性化
- 因子评价:看IC、IR、分组收益等指标
下面这张图,展示了因子构建的完整流程:
3.5 因子处理:别让极端值毁了你的模型
原始因子值算出来后,不能直接用。为什么?因为数据里经常有极端值。比如某只股票因为重大利好连续涨停,动量因子值会特别大。如果不处理,这个极端值会严重影响后续的模型训练。
我常用的处理方法有三个:
| 处理方法 | 说明 | 代码实现 |
|---|---|---|
| 去极值 | 把超过3倍标准差的值,拉回到3倍标准差 | np.clip(factor, mean-3*std, mean+3*std) |
| 标准化 | 减去均值,除以标准差,变成均值为0、标准差为1的分布 | (factor - mean) / std |
| 中性化 | 剔除市值、行业等风格因素的影响 | 用回归取残差 |
我的经验:去极值和标准化几乎是必做的。中性化则要看情况——如果你做的是纯因子策略,中性化会降低因子的纯度。我一般会在多因子模型里做中性化,单因子测试时不做。
3.6 实战:构建一个完整的因子测试框架
说了这么多,我们来写一个完整的因子测试函数。这个函数可以计算因子、做处理、然后输出一些评价指标。
def factor_test(close, factor_func, window=20, method='momentum'):
"""
完整的因子测试框架
"""
# 1. 计算因子
factor = factor_func(close, window)
# 2. 去极值
mean = factor.mean()
std = factor.std()
factor_clipped = np.clip(factor, mean - 3*std, mean + 3*std)
# 3. 标准化
factor_std = (factor_clipped - factor_clipped.mean()) / factor_clipped.std()
# 4. 计算未来收益率
future_ret = close.pct_change().shift(-1)
# 5. 计算IC(秩相关系数)
ic = factor_std.rank(axis=1).corrwith(future_ret.rank(axis=1), axis=1)
return {
'factor': factor_std,
'ic_mean': ic.mean(),
'ic_std': ic.std(),
'ic_ir': ic.mean() / ic.std() if ic.std() != 0 else 0
}
# 测试动量因子
result_mom = factor_test(close_data, momentum_factor, window=20)
print(f"动量因子 - IC均值: {result_mom['ic_mean']:.3f}, IC_IR: {result_mom['ic_ir']:.3f}")
# 测试反转因子
result_rev = factor_test(close_data, reversal_factor, window=5)
print(f"反转因子 - IC均值: {result_rev['ic_mean']:.3f}, IC_IR: {result_rev['ic_ir']:.3f}")
# 测试波动率因子
result_vol = factor_test(close_data, volatility_factor, window=20)
print(f"波动率因子 - IC均值: {result_vol['ic_mean']:.3f}, IC_IR: {result_vol['ic_ir']:.3f}")
注意:上面的代码用的是随机生成的数据,所以IC值会接近0。在实际项目中,你需要用真实的行情数据来测试。我建议先用沪深300成分股试试,数据质量高,也容易获取。
3.7 三个因子的对比与选择
最后,我们来对比一下这三个因子:
| 因子 | 核心逻辑 | 适用场景 | 常见窗口 | 注意事项 |
|---|---|---|---|---|
| 动量因子 | 强者恒强 | 趋势行情 | 20天、60天 | 震荡市容易亏钱 |
| 反转因子 | 均值回归 | 震荡行情 | 5天、10天 | 趋势行情容易亏钱 |
| 波动率因子 | 低波动异象 | 长期持有 | 20天、60天 | 极端行情下会失效 |
我个人建议:刚开始做因子研究时,不要贪多。先把这三个经典因子吃透,理解它们的逻辑和适用场景。等你对因子有了感觉,再去研究更复杂的因子。
记住一句话:好的因子,逻辑要自洽,回测要稳健,实盘要能赚钱。三者缺一不可。
公众号:蓝海资料掘金营,微信deep3321