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)
重要:中性化必须在每个时间截面独立进行,不能跨截面回归。否则会引入未来信息。
4.6 预处理效果验证
完成预处理后,建议通过以下指标验证数据质量:
| 验证项 | 检查方法 | 合格标准 |
|---|---|---|
| 缺失率 | 统计每个日期截面缺失比例 | < 5% |
| 极端值比例 | 检查去极值前后标准差变化 | 去极值后标准差降低 > 20% |
| 中性化效果 | 计算中性化后因子与市值、行业的相关系数 | 相关系数绝对值 < 0.05 |
| 截面分布 | 绘制每个截面的因子分布直方图 | 近似正态或均匀分布 |
最佳实践:将预处理封装为可复用的Pipeline类,每次新增因子时只需调用
pipeline.fit_transform(factor_df),确保流程一致且可追溯。
4.7 预处理Pipeline总结
将上述步骤整合为完整的预处理流程,确保每个因子在进入Rank IC分层试验前都经过统一处理:
- 数据获取:从数据源拉取日频行情,构建Panel结构。
- 缺失值处理:按日期+行业分组填充中位数,删除缺失比例过高的股票。
- 去极值:使用MAD法(n=3)按日期截面截断。
- 标准化:使用Rank标准化(映射到标准正态),保留排序信息。
- 中性化:对市值(对数)和行业哑变量回归,取残差。
- 质量验证:检查缺失率、极端值比例、中性化后相关系数。
最终产出:一个干净的、截面可比的、剔除风格干扰的因子值矩阵,可直接用于后续的Rank IC计算和分层组合构建。