4. 数据准备与预处理:获取股票行情数据、处理缺失值、去极值、标准化、中性化处理

在量化因子研究流程中,数据准备与预处理是决定因子有效性的基石。原始行情数据通常包含噪声、缺失值、极端值以及市场风格干扰,若不经处理直接用于Rank IC分层试验,会导致因子信号失真、分层效果混乱。本章系统讲解从数据获取到中性化处理的完整流水线。

核心原则:预处理的目标不是“美化数据”,而是让因子值更真实地反映个股的横截面差异,剔除市场整体波动、行业风格和异常交易带来的干扰。

4.1 数据获取:行情数据源与字段

获取高质量的日频行情数据是第一步。常用数据源包括Wind、聚宽、Tushare等。对于Rank IC分层试验,至少需要以下字段:

字段名称 说明 用途
trade_date 交易日期 时间序列对齐
stock_code 股票代码 截面标识
adj_close 后复权收盘价 计算收益率、因子值
volume 成交量(股) 流动性因子计算
turnover 换手率 换手率因子
market_cap 总市值(元) 市值中性化
industry_code 行业代码(申万一级) 行业中性化
数据对齐提示:获取数据后,务必构建 (date, stock) 的Panel结构(DataFrame MultiIndex),确保每个时间截面下所有股票对齐到同一日期。

4.2 缺失值处理

因子数据中常出现缺失值,原因包括停牌、新股上市、数据源缺失等。处理策略需根据缺失比例和因子特性选择:

  • 删除法:若某只股票在某个时间截面缺失因子值,直接删除该股票在该截面的记录。适用于缺失比例 < 5% 的情况。
  • 填充法:使用截面中位数或行业均值填充。适用于缺失比例在 5%~20% 之间,且因子具有行业聚集性。
  • 标记法:将缺失值单独编码(如 -999),在后续分层时单独归为一层。适用于缺失本身包含信息(如停牌后复牌)的场景。
# 示例:按日期截面填充缺失值为行业均值
import pandas as pd
import numpy as np

def fill_missing_by_industry(factor_df, industry_col='industry_code'):
    """
    factor_df: DataFrame, 包含 'date', 'stock', 'factor_value', 'industry_code'
    返回填充后的DataFrame
    """
    def fill_group(group):
        median_val = group['factor_value'].median()
        group['factor_value'].fillna(median_val, inplace=True)
        return group
    
    filled_df = factor_df.groupby(['date', industry_col], group_keys=False).apply(fill_group)
    return filled_df
注意:切勿使用全局均值填充,会破坏截面可比性。必须按日期+行业分组填充。

4.3 去极值处理

极端值(outliers)会严重扭曲Rank IC计算和分层边界。常用去极值方法有三种:

方法 原理 适用场景
MAD法 基于中位数绝对偏差,将超出中位数±3×MAD的值截断 因子分布偏态严重时
百分位法 将上下1%或5%分位以外的值替换为分位边界值 因子分布较对称时
标准差法 将超出均值±3σ的值截断 因子近似正态分布时
# 示例:MAD法去极值
def winsorize_mad(series, n=3):
    median = series.median()
    mad = np.median(np.abs(series - median))
    upper = median + n * mad
    lower = median - n * mad
    return series.clip(lower, upper)

# 按日期截面应用
factor_df['factor_winsorized'] = factor_df.groupby('date')['factor_value'].transform(winsorize_mad)
推荐:Rank IC分层试验中,MAD法对异常值鲁棒性最强,建议作为默认去极值方案。

4.4 标准化处理

标准化使不同量纲的因子值具有可比性,避免因子值尺度差异影响分层边界。常用方法:

  • Z-score标准化: (x - mean) / std,使因子均值为0,标准差为1。
  • Rank标准化:将因子值转换为百分位排名(0~1),再映射到标准正态分位数。适用于因子分布非正态时。
# Z-score标准化(按日期截面)
def standardize_zscore(series):
    return (series - series.mean()) / series.std()

factor_df['factor_zscore'] = factor_df.groupby('date')['factor_winsorized'].transform(standardize_zscore)

# Rank标准化(映射到标准正态)
from scipy.stats import norm

def rank_gaussian(series):
    rank = series.rank(pct=True)
    rank = rank.clip(0.001, 0.999)  # 避免无穷值
    return norm.ppf(rank)

factor_df['factor_ranknorm'] = factor_df.groupby('date')['factor_winsorized'].transform(rank_gaussian)
关键区别:Z-score保留因子值的大小差异;Rank标准化只保留排序信息,更适合Rank IC计算场景。

4.5 中性化处理

中性化旨在剔除因子值中与市场风格(市值、行业等)相关的部分,使因子暴露纯粹。这是Rank IC分层试验中最重要的步骤之一。

常见中性化维度:

  • 市值中性化:回归因子值对市值(或对数市值),取残差作为中性化后的因子值。
  • 行业中性化:将因子值减去行业均值(或回归行业哑变量取残差)。
  • 市值+行业双重中性化:同时回归市值和行业哑变量。
# 市值+行业双重中性化(按日期截面回归)
import statsmodels.api as sm

def neutralize_factor(factor_df, date):
    sub = factor_df[factor_df['date'] == date].copy()
    sub = sub.dropna(subset=['factor_zscore', 'log_market_cap', 'industry_code'])
    
    # 构造行业哑变量
    industry_dummies = pd.get_dummies(sub['industry_code'], prefix='ind')
    X = pd.concat([sub[['log_market_cap']], industry_dummies], axis=1)
    X = sm.add_constant(X)
    y = sub['factor_zscore']
    
    model = sm.OLS(y, X).fit()
    sub['factor_neutral'] = model.resid
    return sub[['stock', 'date', 'factor_neutral']]

neutralized_list = []
for date in factor_df['date'].unique():
    neutralized_list.append(neutralize_factor(factor_df, date))
neutralized_df = pd.concat(neutralized_list)
重要:中性化必须在每个时间截面独立进行,不能跨截面回归。否则会引入未来信息。
数据预处理流水线 原始行情数据 缺失值处理 去极值(MAD) 标准化 中性化 中性化回归模型 因子_neutral = 因子_raw − (β₁×log市值 + Σβᵢ×行业ᵢ) 残差 = 剔除市值和行业影响后的纯净因子 每个日期截面独立回归,避免未来信息泄漏

4.6 预处理效果验证

完成预处理后,建议通过以下指标验证数据质量:

验证项 检查方法 合格标准
缺失率 统计每个日期截面缺失比例 < 5%
极端值比例 检查去极值前后标准差变化 去极值后标准差降低 > 20%
中性化效果 计算中性化后因子与市值、行业的相关系数 相关系数绝对值 < 0.05
截面分布 绘制每个截面的因子分布直方图 近似正态或均匀分布
最佳实践:将预处理封装为可复用的Pipeline类,每次新增因子时只需调用 pipeline.fit_transform(factor_df),确保流程一致且可追溯。
中性化前后因子暴露对比 中性化前 正相关(市值影响) 中性化后 零均值 因子值与市值正相关,分层结果受风格干扰 残差与市值无关,分层结果反映因子真实选股能力

4.7 预处理Pipeline总结

将上述步骤整合为完整的预处理流程,确保每个因子在进入Rank IC分层试验前都经过统一处理:

  1. 数据获取:从数据源拉取日频行情,构建Panel结构。
  2. 缺失值处理:按日期+行业分组填充中位数,删除缺失比例过高的股票。
  3. 去极值:使用MAD法(n=3)按日期截面截断。
  4. 标准化:使用Rank标准化(映射到标准正态),保留排序信息。
  5. 中性化:对市值(对数)和行业哑变量回归,取残差。
  6. 质量验证:检查缺失率、极端值比例、中性化后相关系数。
最终产出:一个干净的、截面可比的、剔除风格干扰的因子值矩阵,可直接用于后续的Rank IC计算和分层组合构建。