3. 单变量因子构建:基础因子(动量、反转、波动率)的计算逻辑与Python实现
单变量因子是量化选股模型中最基本的构建单元。本章聚焦于三大经典基础因子——动量因子、反转因子与波动率因子,深入解析其金融学逻辑、数学定义及Python实现细节。掌握这些因子的计算,是后续进行变量衍生与IC分析的前提。
3.1 因子构建核心流程
所有单变量因子的构建均遵循统一的数据处理流水线。理解此流程有助于标准化代码实现,避免常见错误。
核心流程: 原始行情数据 → 缺失值处理 → 核心计算逻辑 → 去极值/标准化 → 行业市值中性化(可选) → 因子值输出
下表总结了三大因子的核心属性对比:
| 因子类别 | 金融逻辑 | 典型计算窗口 | 预期IC方向 | 常见陷阱 |
|---|---|---|---|---|
| 动量因子 | 强者恒强,过去收益延续 | 过去12个月(剔除最近1个月) | 正(短期动量可能为负) | 幸存者偏差、跳跃性收益干扰 |
| 反转因子 | 物极必反,短期过度反应后回调 | 过去1周至1个月 | 负(过去涨得多的未来跌) | 交易成本吞噬收益、流动性不足 |
| 波动率因子 | 低波动异象,低风险股票超额收益 | 过去20个交易日(1个月) | 负(低波动跑赢高波动) | 波动率聚集效应、非同步交易 |
3.2 动量因子:趋势的量化表达
动量因子衡量股票在过去一段时间内的价格表现。学术研究中通常使用累计收益率作为动量指标,且常跳过最近一个月以规避短期反转噪音。
3.2.1 计算逻辑
- 定义: 过去N个月的累计收益率(含股息调整)。
- 标准参数: N=12(月),跳过最近1个月,即计算 t-12 至 t-1 的收益率。
- 数学公式:
Momentum = (Pt-1 / Pt-12) - 1
3.2.2 Python实现
import pandas as pd
import numpy as np
def calc_momentum_factor(price_df, window=12, skip=1):
"""
计算动量因子(月度频率)
:param price_df: DataFrame, index=日期, columns=股票代码, values=复权价格
:param window: 回溯窗口(月数)
:param skip: 跳过的最近月份数
:return: DataFrame, 动量因子值
"""
# 计算月度收益率
monthly_ret = price_df.pct_change()
# 滚动累计收益率(跳过最近skip个月)
momentum = (price_df.shift(skip) / price_df.shift(window + skip)) - 1
# 处理缺失值:至少需要window/2个有效数据点
min_periods = max(int(window / 2), 1)
momentum = momentum.where(
price_df.shift(skip).notna().rolling(window).sum() >= min_periods
)
return momentum
# 示例调用
# momentum_factor = calc_momentum_factor(adj_close_df, window=12, skip=1)
实现要点: 使用
shift() 实现时间对齐,避免未来信息泄露。对于A股市场,建议使用后复权价格计算,以准确反映真实收益。
3.3 反转因子:短期过度反应的捕捉
反转因子与动量因子在时间维度上互补。短期反转策略认为,过去几天或几周涨幅过大的股票,未来大概率下跌。
3.3.1 计算逻辑
- 定义: 过去短期(如5日或20日)的累计收益率。
- 标准参数: 窗口=5(交易日),或窗口=20(交易日)。
- 数学公式:
Reversal = (Pt / Pt-N) - 1(通常取负值或直接使用收益率)
3.3.2 Python实现
def calc_reversal_factor(price_df, window=5):
"""
计算反转因子(日度频率)
:param price_df: DataFrame, 日度复权价格
:param window: 回溯窗口(交易日数)
:return: DataFrame, 反转因子值(正值表示过去上涨)
"""
# 计算过去N日累计收益率
reversal = price_df.pct_change(periods=window)
# 处理极端值:使用MAD方法去极值
def mad_winsorize(series, n=5):
median = series.median()
mad = (series - median).abs().median()
upper = median + n * mad
lower = median - n * mad
return series.clip(lower, upper)
reversal = reversal.apply(mad_winsorize, axis=0)
return reversal
# 示例调用
# reversal_factor = calc_reversal_factor(adj_close_df, window=5)
注意事项: 反转因子对交易成本极为敏感。在实际策略中,需考虑冲击成本与佣金,通常仅对流动性排名前20%的股票实施反转策略。
3.4 波动率因子:风险溢价的另类视角
波动率因子基于“低波动异象”——低波动股票往往比高波动股票获得更高的风险调整后收益。该因子在Barra模型中属于风险因子,但在选股中常作为alpha信号使用。
3.4.1 计算逻辑
- 定义: 过去N个交易日的日收益率标准差(年化)。
- 标准参数: 窗口=20(交易日),年化因子=√252。
- 数学公式:
Volatility = σ(Rt-19, ..., Rt) × √252
3.4.2 Python实现
def calc_volatility_factor(price_df, window=20, annualize=True):
"""
计算波动率因子
:param price_df: DataFrame, 日度复权价格
:param window: 滚动窗口(交易日数)
:param annualize: 是否年化
:return: DataFrame, 波动率因子值
"""
# 计算日收益率
daily_ret = price_df.pct_change()
# 滚动标准差
volatility = daily_ret.rolling(window=window, min_periods=int(window/2)).std()
# 年化处理
if annualize:
volatility = volatility * np.sqrt(252)
# 处理缺失值:删除全为NaN的列
volatility = volatility.dropna(how='all', axis=1)
return volatility
# 示例调用
# vol_factor = calc_volatility_factor(adj_close_df, window=20)
3.5 因子构建流程总览
下图展示了从原始行情数据到最终因子值的完整处理流程,以及三大因子在流程中的位置关系。
3.6 因子质量检验
构建完成后,需对因子进行初步质量检验,确保其符合金融直觉且无数据错误。
| 检验项目 | 方法 | 预期结果 | 异常处理 |
|---|---|---|---|
| 覆盖率 | 计算每期有效股票数量 | > 80% 的样本池 | 检查停牌、退市股票 |
| 截面分布 | 绘制直方图或Q-Q图 | 近似正态分布(去极值后) | 调整去极值参数 |
| 自相关性 | 计算因子值的时间序列自相关 | 动量因子自相关较高(>0.5) | 检查窗口设置是否合理 |
| 行业暴露 | 计算因子在行业间的均值差异 | 不应有系统性行业偏差 | 考虑行业中性化处理 |