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 因子构建流程总览

下图展示了从原始行情数据到最终因子值的完整处理流程,以及三大因子在流程中的位置关系。

单变量因子构建流程总览 原始行情数据 缺失值处理 动量因子 反转因子 波动率因子 去极值 标准化 因子值矩阵 (T × N) 关键说明: 1. 所有因子计算必须使用复权价格,避免分红除权带来的虚假收益。 2. 动量与反转因子在时间窗口上互补:动量看长期(12个月),反转看短期(5-20天)。 3. 波动率因子计算时需注意:使用对数收益率更符合正态分布假设。 4. 去极值推荐使用MAD方法(中位数绝对偏差),对异常值鲁棒性优于Z-score。

3.6 因子质量检验

构建完成后,需对因子进行初步质量检验,确保其符合金融直觉且无数据错误。

检验项目 方法 预期结果 异常处理
覆盖率 计算每期有效股票数量 > 80% 的样本池 检查停牌、退市股票
截面分布 绘制直方图或Q-Q图 近似正态分布(去极值后) 调整去极值参数
自相关性 计算因子值的时间序列自相关 动量因子自相关较高(>0.5) 检查窗口设置是否合理
行业暴露 计算因子在行业间的均值差异 不应有系统性行业偏差 考虑行业中性化处理