3. 因子计算与预处理:因子值计算、去极值、标准化、中性化处理

原始因子数据通常包含噪声、极端值和多重共线性,直接用于选股或回测会导致虚假信号。本章系统讲解因子值计算后的三大预处理步骤:去极值、标准化与中性化,确保因子信号稳定、可比且逻辑纯净。

3.1 因子值计算基础

因子值计算是将原始行情、财务或另类数据转化为数值信号的过程。常见因子类型及计算方式如下:

因子类型 典型因子 计算公式(简写) 数据来源
估值因子 市盈率(PE) 收盘价 / 每股收益(TTM) 日行情 + 财报
动量因子 过去N日收益率 (Pt - Pt-N) / Pt-N 日收盘价
波动率因子 历史波动率 std(ln(Pt/Pt-1), N) 日收益率序列
质量因子 ROE 净利润 / 股东权益 季度/年度财报
成长因子 营收增长率 (营收t - 营收t-4) / 营收t-4 同比财报数据
实战提示:因子计算需对齐截面时间戳。例如每月末计算因子时,应使用当月最后一个交易日的数据,并确保所有股票使用同一截止日期,避免未来信息泄漏。

3.2 去极值(Winsorization / MAD)

极端值会严重扭曲因子排序和回归结果。去极值的核心目标是将极端值压缩到合理边界内,而非直接删除。

3.2.1 常用方法对比

方法 原理 适用场景 优点 缺点
MAD(中位数绝对偏差) 以中位数为中心,MAD = median(|xi - median(x)|),将超出 median ± n·MAD 的值截断 因子分布偏态严重时 稳健,不受极端值影响 计算稍复杂
百分位截断(Winsorization) 将低于 p% 分位数的值设为 p% 分位数,高于 (1-p)% 的值设为 (1-p)% 分位数 正态性较好的因子 简单直观,易于实现 对尾部信息丢失较多
标准差截断(Z-score) 将超出 mean ± k·std 的值截断 近似正态分布 计算最快 对极端值敏感,均值易被拉偏

3.2.2 MAD 去极值代码示例

import numpy as np

def mad_winsorize(factor_series, n=5):
    """
    使用MAD方法去极值
    factor_series: pandas Series, 截面因子值
    n: 倍数,通常取3~5
    """
    median = factor_series.median()
    mad = np.median(np.abs(factor_series - median))
    upper = median + n * mad
    lower = median - n * mad
    return factor_series.clip(lower, upper)
避坑提醒:去极值必须在每个截面(如每月末)独立进行,不能跨截面使用全局统计量。不同截面的因子分布可能差异很大,统一阈值会导致信号失真。

3.3 标准化(Standardization / Normalization)

标准化使不同量纲的因子具有可比性,避免量级大的因子在组合权重或回归中主导结果。

3.3.1 两种主流标准化方法

方法 公式 输出范围 适用场景
Z-score 标准化 z = (x - μ) / σ 均值0,标准差1 因子后续用于回归或机器学习模型
Min-Max 归一化 z = (x - min) / (max - min) [0, 1] 因子用于排序打分或需要固定边界

3.3.2 Z-score 标准化代码示例

def zscore_standardize(factor_series):
    """
    截面Z-score标准化
    """
    mean = factor_series.mean()
    std = factor_series.std()
    return (factor_series - mean) / std
实战提示:标准化同样按截面独立进行。若因子值在截面内存在大量缺失值,应先填充或剔除缺失样本,再计算均值和标准差,避免统计量被缺失值扭曲。

3.4 中性化(Neutralization)

中性化的目的是剔除因子中与已知风险因子(如市值、行业、波动率)相关的部分,使因子暴露纯粹反映选股能力,而非隐含的风格或行业赌注。

3.4.1 中性化流程

  1. 确定中性化变量:通常包括行业虚拟变量(申万一级/二级行业)、市值对数、BP等。
  2. 建立回归模型:以因子值为因变量,中性化变量为自变量,进行截面回归。
  3. 提取残差:回归残差即为中性化后的纯净因子。

3.4.2 行业与市值中性化代码示例

import statsmodels.api as sm

def neutralize_factor(factor_series, industry_dummies, log_market_cap):
    """
    对因子进行行业+市值中性化
    factor_series: 待中性化因子值
    industry_dummies: DataFrame, 行业虚拟变量(已one-hot)
    log_market_cap: Series, 对数市值
    """
    X = pd.concat([industry_dummies, log_market_cap], axis=1)
    X = sm.add_constant(X)  # 添加截距项
    model = sm.OLS(factor_series, X, missing='drop').fit()
    residual = factor_series - model.predict(X)
    return residual
避坑提醒:中性化回归时务必检查多重共线性。行业虚拟变量若包含所有行业(如申万一级31个行业),必须删除一个行业作为基准类别,否则回归矩阵不满秩,导致系数无法估计。

3.5 预处理全流程架构

下图展示了因子计算完成后,从原始因子到纯净因子的完整预处理流水线:

因子预处理全流程架构 原始因子值 去极值(MAD/百分位) 标准化(Z-score / Min-Max) 中性化(行业+市值) 纯净因子(残差) 关键原则 • 每个截面独立处理 • 先去极值 → 再标准化 → 最后中性化 • 中性化需避免多重共线性

3.6 预处理顺序与注意事项

三个步骤的执行顺序不可随意调换:

  • 先去极值,再标准化:极端值会拉偏均值和标准差,导致标准化后的因子仍包含异常信号。
  • 先标准化,再中性化:标准化使因子量纲统一,中性化回归时系数更稳定;若先中性化再标准化,会破坏中性化后的残差结构。
  • 缺失值处理前置:在去极值之前,应至少剔除或填充缺失比例过高的样本(如缺失率 > 30% 的股票直接剔除该截面)。
最佳实践总结:一个稳健的因子预处理流水线为:
原始因子 → 缺失值处理 → MAD去极值(n=5) → Z-score标准化 → 行业+市值中性化 → 纯净因子。
该流程适用于绝大多数截面选股因子,能有效提升因子IC的稳定性和选股区分度。