3、数据准备与清洗:获取价格、财务、宏观等数据,处理缺失值、异常值、复权因子,构建面板数据结构

多因子模型的实证效果高度依赖底层数据的质量与结构。若数据存在幸存者偏差、复权错误、缺失值未处理或面板结构错乱,后续的因子计算、回归检验与组合构建都将失去可信基础。本章围绕数据获取、清洗、复权处理与面板构建四个核心环节,建立一套可复用的工程化流程。

数据准备与清洗工程链路 数据源获取 • 行情数据(日/分钟) • 财务报表(三大表) • 宏观指标(利率/CPI) 数据清洗 • 缺失值处理 • 异常值检测 • 去重/对齐 复权因子处理 • 前复权/后复权 • 除权除息调整 • 因子计算基准 面板数据构建 • 股票×时间 • 多级索引 • 对齐与平衡 关键校验点: ① 时间戳对齐(交易日历) ② 股票代码一致性 ③ 复权因子单调性 ④ 财务数据滞后性(T-1季报) ⑤ 面板平衡性检查 数据质量维度 完整性(缺失率 < 5%) | 准确性(复权误差 < 0.1%) | 一致性(跨源校验) | 及时性(T+1更新) | 无偏性(剔除幸存者偏差) 输出:MultiIndex Panel DataFrame (date, asset) → features

3.1 数据源获取与基础校验

核心原理:多因子模型需要三类数据:价格/成交量数据(用于计算动量、波动率、换手率等量价因子)、财务报表数据(用于计算估值、盈利、成长、杠杆等基本面因子)、宏观数据(用于行业中性化、风格暴露或宏观敏感因子)。数据源的选择直接影响因子计算的精度与复现性。

数据类别 典型字段 常用频率 主要来源 关键注意事项
行情数据 OHLCV、成交额、涨跌停标志 日频/周频 Wind、聚宽、Tushare、RiceQuant 需包含已退市股票,避免幸存者偏差
财务数据 营收、净利润、总资产、净资产、现金流 季频(滞后发布) Wind、CSMAR、Choice 需使用实际披露日期而非报告期
宏观数据 无风险利率、CPI、PPI、M2、社融 月频/季频 国家统计局、央行、Wind 需与股票数据频率对齐,注意发布时滞

操作流程:

  1. 确定股票池范围:明确是否包含ST、次新股、停牌股、退市股。建议保留全市场A股(含已退市),在因子计算时再按需过滤。
  2. 获取交易日历:使用交易所官方日历,剔除周末与节假日,确保所有时间戳对齐。
  3. 批量拉取数据:按股票代码循环获取,注意接口限频,建议使用多线程或异步方式提高效率。
  4. 基础字段校验:检查价格是否为正、成交量是否非负、日期是否连续、代码是否匹配。
经验提醒:财务数据存在严重的“幸存者偏差”问题——如果只保留当前仍在交易的股票,会高估因子表现。务必在数据获取阶段就纳入历史退市股票,并在后续分析中记录退市原因(如亏损、违规、私有化)。

3.2 缺失值与异常值处理

核心原理:缺失值来源于停牌、新股上市初期、财务数据未披露或数据源本身不完整。异常值则可能由数据录入错误、极端市场事件(如乌龙指)或复权计算错误引起。处理策略需兼顾统计合理性与投资逻辑。

缺失值处理策略:

  • 行情数据缺失:停牌期间价格缺失,应保留NaN而非填充,因子计算时跳过该期;若需连续序列,可用前收盘价填充,但需标记。
  • 财务数据缺失:采用“最近可得值”填充(forward-fill),即使用最近一期已披露财报数据,避免未来函数。
  • 宏观数据缺失:线性插值或使用上月值填充,但需注意发布时滞。
  • 删除阈值:若某只股票在样本期内缺失率超过30%,建议剔除该股票;若某因子在横截面上缺失率超过20%,建议调整因子计算逻辑。

异常值检测方法:

  1. 3σ法则:计算因子值的均值与标准差,将超出±3σ的样本视为异常值。
  2. MAD(中位数偏差):更稳健,使用中位数替代均值,适合偏态分布。
  3. 分位数截尾(Winsorize):将上下1%或5%的极值压缩至分位数边界,保留样本量同时抑制极端值影响。
风险提示:异常值处理需区分“真实极端值”与“数据错误”。例如,某股票因重大资产重组复牌后单日涨幅500%,这是真实事件,不应直接截尾;而价格出现负数或成交量超过流通股本10倍,则属于数据错误,应追溯修正或删除。

3.3 复权因子处理

核心原理:股票在除权除息日(如送股、转增、配股、现金分红)后,价格会向下调整,若不进行复权处理,将导致收益率计算失真。复权因子用于将历史价格调整至可比基准,确保因子计算(尤其是动量、波动率类因子)不受分红送转的虚假影响。

复权方式对比:

复权方式 计算方法 适用场景 优缺点
前复权 以最新价格为基准,调整历史价格 技术分析、回测中观察当前价格水平 历史价格会随新除权事件变化,不适合长期回测
后复权 以上市首日价格为基准,调整后续价格 长期因子计算、收益归因 历史价格固定,但当前价格与真实市价差异大
不复权 使用原始价格 计算真实收益率(需自行处理除权日) 需额外计算除权调整,容易出错

推荐做法:在因子计算中统一使用后复权价格,因为其历史序列稳定,不会因后续分红事件而改变。计算收益率时,使用后复权价格的差分对数收益率,可自动包含分红再投资效应。

# 复权因子计算示例(Python伪代码)
# 假设原始数据包含 close(收盘价)、adj_factor(复权因子)
df['adj_close'] = df['close'] * df['adj_factor']
df['return'] = df['adj_close'].pct_change()

# 若数据源未提供复权因子,可自行计算:
# 除权日调整因子 = (除权前收盘价 - 现金红利 + 配股价×配股比例) / (除权前收盘价 + 送转股比例×除权前收盘价)
实战要点:复权因子应使用“累计复权因子”而非单次除权因子。累计复权因子是自上市以来所有除权事件的连乘结果,可直接用于价格调整。多数数据源(如Wind)直接提供该字段,但需验证其单调性——若出现非单调变化,说明数据存在错误。

3.4 面板数据结构构建

核心原理:面板数据(Panel Data)是同时包含横截面(多只股票)和时间序列(多个交易日)的二维结构。多因子模型的标准输入格式为“股票×时间”矩阵,每行代表一个股票-时间观测点,每列代表一个因子值或特征。

标准面板结构:

MultiIndex DataFrame:
- 第一层索引:日期(DatetimeIndex)
- 第二层索引:股票代码(str)
- 列:因子1, 因子2, ..., 因子N, 收益率, 行业, 市值等

示例:
                     momentum  value  size  return
date        asset
2023-01-03  000001.SZ  0.023  0.45  12.3  0.0012
            000002.SZ -0.011  0.38  8.9  -0.0008
            600000.SH  0.045  0.52  25.1  0.0021

构建流程:

  1. 数据对齐:将行情、财务、宏观数据统一到同一交易日历,使用 reindex 或 merge 按日期和代码对齐。
  2. 财务数据时滞处理:确保使用的财务数据为“当时可得”数据——例如,2023年4月30日只能使用2022年年报或2023年一季报,不能使用2023年中报。
  3. 生成多级索引:使用 pd.MultiIndex.from_product 或 set_index(['date', 'asset']) 构建面板。
  4. 平衡性检查:检查每个交易日是否包含相同数量的股票;若某日股票数量骤减,可能是停牌或数据缺失,需标记处理。
  5. 因子值对齐:确保所有因子在同一时间截面上计算,避免未来数据泄漏。
案例拆解:财务数据时滞处理
假设当前日期为2023年7月15日,某股票2023年中报(报告期2023-06-30)尚未披露,而2023年一季报(报告期2023-03-31)已于4月30日披露。此时计算估值因子(如PE),应使用一季报的净利润数据,而非中报数据。若错误使用中报数据,将引入未来函数,导致因子回测结果虚高。

3.5 常见误区与总结复盘

误区 后果 正确做法
未剔除已退市股票 幸存者偏差,因子收益虚高 纳入退市股票,记录退市原因
使用前复权价格做长期回测 历史价格随新除权事件变化,回测结果不可复现 使用后复权价格或累计复权因子
财务数据未做时滞处理 未来函数,因子IC虚高 使用实际披露日期,T-1季报
缺失值直接填充为0 扭曲因子分布,影响中性化 区分缺失原因,使用前向填充或标记
异常值直接删除 丢失真实极端事件信息 先判断异常原因,再决定截尾或保留

总结复盘:数据准备阶段的核心目标是构建一个无偏、完整、可复现的面板数据集。建议在正式建模前,完成以下质量检查清单:

  • ✔ 股票池是否包含退市股票?
  • ✔ 复权因子是否单调递增?
  • ✔ 财务数据是否使用实际披露日期?
  • ✔ 面板是否平衡?不平衡的原因是否已记录?
  • ✔ 因子值是否存在极端异常?是否已做Winsorize处理?
  • ✔ 所有数据源的时间戳是否对齐?
最终提醒:数据清洗的每一步都应保留操作日志(原始数据、清洗规则、处理前后对比),确保研究可复现。多因子模型的差异往往不来自模型本身,而来自数据处理的细节差异。一个严谨的数据管道,是因子研究最坚实的护城河。