2. 因子数据准备:数据源选择、清洗与预处理

因子数据是量化策略的“原材料”,数据质量直接决定因子效果评估的可靠性。本章系统讲解数据源选型、数据清洗、缺失值处理与异常值检测四大环节,帮助您构建干净、可复用的因子数据集。

2.1 数据源选择与对比

国内主流量化数据源包括 Wind(万得)、Tushare、聚宽(JoinQuant)、RiceQuant 等,各有侧重。下表从覆盖度、更新频率、成本与适用场景四个维度进行对比:

数据源 覆盖范围 更新频率 成本 适用场景
Wind A股、港股、期货、基金、宏观 实时/日频 高(机构年费) 机构投研、高频回测
Tushare A股、指数、基金、期货 日频/分钟频 免费/积分制 个人研究、学术实验
聚宽 A股、ETF、期货、期权 日频/分钟频 免费(平台内) 策略回测、因子平台
RiceQuant A股、期货、期权 日频/分钟频 免费(平台内) 策略回测、因子研究
选型建议:机构用户优先 Wind 保证数据权威性;个人研究者推荐 Tushare 或聚宽,兼顾成本与数据广度。多源交叉验证可提升数据可靠性。

2.2 数据清洗流程

数据清洗是因子准备的核心步骤,通常包含以下四个子环节:

  1. 格式统一:将不同数据源的日期格式、股票代码、字段命名标准化。
  2. 去重与对齐:剔除重复记录,按交易日对齐所有股票的时间序列。
  3. 行业分类映射:将股票代码映射到申万一级/二级行业,用于后续中性化处理。
  4. 复权处理:对价格类因子使用后复权或前复权,消除分红送股影响。
代码示例:使用 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)
因子数据准备全流程 数据源选择 数据清洗 缺失值处理 异常值处理 各环节关键操作: • 数据源:Wind(机构)、Tushare(个人)、聚宽(平台) • 清洗:格式统一 → 去重对齐 → 行业映射 → 复权处理 • 缺失:均值填充(<5%)/ 行业均值(5-20%)/ 前向填充(20-50%)/ 剔除(>50%) • 异常:MAD 法 / 百分位截断 / Z-score → 截尾或缩尾修正 干净因子数据集

2.5 实战案例:构建日频因子数据集

以下步骤演示从 Tushare 获取数据到生成可用因子数据集的完整流程:

  1. 获取股票列表:取沪深 300 成分股,避免小市值噪音。
  2. 批量拉取日线:循环获取每只股票近 3 年数据,合并为宽表。
  3. 计算基础因子:如市盈率、换手率、波动率等。
  4. 清洗与填充:按 2.2–2.4 节方法处理。
  5. 输出 parquet 文件:按日期分区存储,便于后续回测。
效率提示:使用 pandas.DataFrame.to_parquet() 替代 CSV,压缩率高且读写速度快 5–10 倍。建议按 year/month 分区存储。
异常值处理决策树 检测到异常值 近似正态分布 偏态 / 厚尾分布 Z-score 法 百分位截断法 MAD 法 箱线图法 截尾(Winsorize) 缩尾(Trim)

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
    }