2. 因子数据准备:数据源选择、清洗与预处理
因子数据是量化策略的“原材料”,数据质量直接决定因子效果评估的可靠性。本章系统讲解数据源选型、数据清洗、缺失值处理与异常值检测四大环节,帮助您构建干净、可复用的因子数据集。
2.1 数据源选择与对比
国内主流量化数据源包括 Wind(万得)、Tushare、聚宽(JoinQuant)、RiceQuant 等,各有侧重。下表从覆盖度、更新频率、成本与适用场景四个维度进行对比:
| 数据源 | 覆盖范围 | 更新频率 | 成本 | 适用场景 |
|---|---|---|---|---|
| Wind | A股、港股、期货、基金、宏观 | 实时/日频 | 高(机构年费) | 机构投研、高频回测 |
| Tushare | A股、指数、基金、期货 | 日频/分钟频 | 免费/积分制 | 个人研究、学术实验 |
| 聚宽 | A股、ETF、期货、期权 | 日频/分钟频 | 免费(平台内) | 策略回测、因子平台 |
| RiceQuant | A股、期货、期权 | 日频/分钟频 | 免费(平台内) | 策略回测、因子研究 |
选型建议:机构用户优先 Wind 保证数据权威性;个人研究者推荐 Tushare 或聚宽,兼顾成本与数据广度。多源交叉验证可提升数据可靠性。
2.2 数据清洗流程
数据清洗是因子准备的核心步骤,通常包含以下四个子环节:
- 格式统一:将不同数据源的日期格式、股票代码、字段命名标准化。
- 去重与对齐:剔除重复记录,按交易日对齐所有股票的时间序列。
- 行业分类映射:将股票代码映射到申万一级/二级行业,用于后续中性化处理。
- 复权处理:对价格类因子使用后复权或前复权,消除分红送股影响。
代码示例:使用 Python 进行基础清洗(以 Tushare 日线数据为例)
import pandas as pd
import tushare as ts
# 获取原始数据
df = ts.get_k_data('000001', start='2023-01-01', end='2023-12-31')
# 格式统一:日期转为 datetime,代码转为字符串
df['date'] = pd.to_datetime(df['date'])
df['code'] = df['code'].astype(str).str.zfill(6)
# 去重:按日期+代码去重
df = df.drop_duplicates(subset=['date', 'code'])
# 排序:按日期升序
df = df.sort_values('date').reset_index(drop=True)
print(df.head())
2.3 缺失值处理
因子数据中常见缺失值,原因包括停牌、新股上市、数据源缺失等。处理策略需结合因子类型与缺失比例:
| 缺失比例 | 推荐方法 | 适用因子类型 | 注意事项 |
|---|---|---|---|
| < 5% | 截面均值/中位数填充 | 估值、动量、质量 | 避免引入未来信息 |
| 5% – 20% | 行业均值填充 | 财务类、分析师预期 | 按申万一级行业分组 |
| 20% – 50% | 时间序列前向填充 | 流动性、波动率 | 最多前向 5 个交易日 |
| > 50% | 剔除该因子或股票 | 所有类型 | 记录剔除原因 |
关键原则:填充时严禁使用未来数据(如用 t+1 期值填充 t 期缺失)。建议在回测框架内按时间窗口滚动填充。
2.4 异常值处理
异常值会严重扭曲因子排序与收益统计,需通过检测与修正两步处理。
2.4.1 异常值检测方法
- MAD 法(中位数绝对偏差):稳健性强,适合偏态分布。阈值通常设为 3 倍 MAD。
- 百分位截断法:直接剔除上下 0.5% 或 1% 极值,简单高效。
- Z-score 法:适用于近似正态分布,阈值设为 3 或 4。
2.4.2 异常值修正策略
| 策略 | 操作方式 | 优点 | 缺点 |
|---|---|---|---|
| 截尾(Winsorize) | 将超出阈值的值替换为阈值 | 保留样本量 | 可能扭曲分布 |
| 缩尾(Trim) | 直接删除异常值 | 分布更干净 | 损失样本 |
| 缺失化 | 将异常值设为缺失,再填充 | 结合缺失处理 | 流程复杂 |
代码示例:MAD 法检测与截尾修正
import numpy as np
def mad_outlier_cap(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['pe_ttm'] = mad_outlier_cap(factor_df['pe_ttm'], n=3)
2.5 实战案例:构建日频因子数据集
以下步骤演示从 Tushare 获取数据到生成可用因子数据集的完整流程:
- 获取股票列表:取沪深 300 成分股,避免小市值噪音。
- 批量拉取日线:循环获取每只股票近 3 年数据,合并为宽表。
- 计算基础因子:如市盈率、换手率、波动率等。
- 清洗与填充:按 2.2–2.4 节方法处理。
- 输出 parquet 文件:按日期分区存储,便于后续回测。
效率提示:使用
pandas.DataFrame.to_parquet() 替代 CSV,压缩率高且读写速度快 5–10 倍。建议按 year/month 分区存储。
2.6 数据质量检查清单
在因子入库前,建议逐项核对以下检查点:
- 时间跨度完整性:每个股票的交易日期是否连续?停牌日是否标记?
- 截面覆盖度:每日股票数量是否稳定?是否存在大量退市或新股跳变?
- 因子分布合理性:均值、标准差、偏度、峰度是否在预期范围内?
- 行业分布均匀性:因子值是否在某些行业集中偏离?
- 前后向一致性:复权因子是否与除权除息日匹配?
自动化检查脚本片段:
def quality_check(factor_df, date_col='date', code_col='code'):
# 1. 时间跨度
date_range = factor_df[date_col].min(), factor_df[date_col].max()
# 2. 每日股票数
daily_count = factor_df.groupby(date_col)[code_col].nunique()
# 3. 因子统计
stats = factor_df.describe()
# 4. 缺失比例
missing_ratio = factor_df.isnull().mean()
return {
'date_range': date_range,
'daily_count_mean': daily_count.mean(),
'stats': stats,
'missing_ratio': missing_ratio
}