3. 数据准备与清洗

在量化策略组合回测中,数据准备与清洗往往占据整个项目60%以上的工作量。一个看似微小的复权错误、一处未处理的停牌缺失值,或一次频率转换中的时间戳错位,都可能导致回测收益虚高、回撤失真,甚至让策略在实盘中完全失效。本节将系统讲解金融数据来源选择、复权处理、缺失值与异常值处理、数据对齐与频率转换四大核心环节,帮助你建立可复现、可审计的数据管道。

一、核心原理:数据质量决定回测上限

回测的本质是用历史数据模拟策略在未来可能的表现。如果输入数据本身存在偏差,那么无论策略逻辑多么精妙,输出结果都只是“垃圾进、垃圾出”。数据清洗的核心目标可以归纳为三个一致性:

  • 价格一致性:确保用于计算收益的价格序列经过正确复权,避免除权除息造成的虚假跳空。
  • 时间一致性:确保不同标的、不同频率的数据在同一时间轴上对齐,避免未来函数或信息泄露。
  • 统计一致性:确保缺失值与异常值被合理识别和处理,不引入系统性偏差。

关键认知:数据清洗不是一次性任务,而是需要版本化管理的工程流程。每一次清洗规则的变化,都应当记录在案,并重新运行回测以评估影响。

二、金融数据来源与选择

常见的数据来源可分为三类:交易所官方数据、商业数据供应商、开源数据接口。不同来源在覆盖范围、更新频率、字段完整性和成本上差异显著。

数据来源类型 典型代表 优势 局限
交易所官方 上交所、深交所、中金所 权威、准确、字段规范 获取门槛高、历史数据需付费
商业供应商 Wind、聚源、Bloomberg 覆盖广、清洗较完善、支持API 成本高、接口有配额限制
开源接口 AkShare、Tushare、Yahoo Finance 免费、易上手、社区活跃 稳定性参差、字段缺失较多

选择数据源时,应优先确认以下字段是否完整:交易日期、开盘价、最高价、最低价、收盘价、成交量、成交额、复权因子、停牌标记。对于股票策略,还需关注成分股调整、ST标记、退市日期等元数据。

三、复权处理:前复权、后复权与不复权

股票在除权除息日会出现价格跳空,若不处理,直接计算收益率会产生虚假信号。复权处理的核心是调整历史价格,使其连续可比。

  • 前复权:以当前价格为基准,向前调整历史价格。优点是当前价格真实,适合可视化;缺点是历史价格会随新的除权事件变化,不适合长期回测。
  • 后复权:以上市首日为基准,向后调整价格。优点是历史价格稳定,适合回测;缺点是当前价格与市场报价不一致。
  • 不复权:保留原始价格,仅在需要计算真实交易成本或涨跌停判断时使用。

实战建议:回测中统一使用后复权价格计算收益率,用不复权价格判断涨跌停和交易可行性,用复权因子进行两者之间的转换。

# 示例:使用后复权因子调整价格
import pandas as pd

# 假设 df 包含 raw_close 和 adj_factor
df['adj_close'] = df['raw_close'] * df['adj_factor']

# 计算日收益率
df['ret'] = df['adj_close'].pct_change()

# 检查除权日附近是否存在异常跳空
print(df.loc[df['adj_factor'].diff() != 0, ['date', 'raw_close', 'adj_factor', 'adj_close', 'ret']])

四、缺失值与异常值处理

缺失值在金融数据中极为常见,来源包括停牌、未上市、数据源遗漏等。异常值则可能来自错误报价、拆股未调整、涨跌停板上的极端成交等。

  1. 缺失值识别:区分“真实缺失”(如停牌)与“数据缺失”(如接口漏传)。停牌期间应保留缺失标记,而非简单填充。
  2. 缺失值处理:对于价格序列,可采用前向填充(ffill)并配合停牌标记;对于财务数据,应根据公告日期进行点对点填充,避免未来函数。
  3. 异常值检测:常用方法包括MAD(中位数绝对偏差)、分位数截断、Z-Score。对于收益率序列,可设定阈值如±20%进行初步筛查。
  4. 异常值修正:确认是数据错误后,可用相邻交易日插值或直接剔除;若为真实极端行情,应保留但考虑使用稳健统计量。

常见误区:直接用均值或中位数填充停牌期间的价格。这会导致停牌期间收益率被平滑,回测中产生虚假的低波动和超额收益。正确做法是标记停牌,并在组合构建时排除不可交易标的。

五、数据对齐与频率转换

多标的、多频率数据在组合回测中必须严格对齐。常见场景包括:日频策略使用周频财务数据、分钟频数据聚合为日频、不同市场交易时间不一致等。

  • 时间戳对齐:统一使用UTC或交易所本地时间,避免时区混淆。对于日频数据,明确使用收盘时间戳还是交易日日期。
  • 频率转换:降频(如分钟转日)需明确OHLC聚合规则:开盘取首、最高取最大、最低取最小、收盘取末、成交量求和。
  • 升频(如日转周):通常不推荐,因为会引入插值假设。若必须升频,应使用前向填充并严格避免未来信息。
  • 对齐策略:使用reindex或merge_asof进行时间对齐,确保每个时间点只使用当时可获得的信息。
# 示例:分钟数据聚合为日频
daily = df.resample('1D').agg({
    'open': 'first',
    'high': 'max',
    'low': 'min',
    'close': 'last',
    'volume': 'sum'
}).dropna(how='all')

# 示例:使用 merge_asof 对齐财务数据与日频价格
aligned = pd.merge_asof(
    price_df.sort_values('date'),
    financial_df.sort_values('announce_date'),
    left_on='date',
    right_on='announce_date',
    direction='backward'
)
数据准备与清洗核心流程 数据源接入 交易所/供应商/开源 复权处理 前复权/后复权/因子 缺失与异常处理 停牌标记/MAD/插值 对齐与频率转换 reindex/merge_asof 清洗后数据管道 可复现 · 可审计 · 无未来函数 质量检查清单 复权因子连续性 · 停牌标记 · 时间戳对齐 · 收益率分布

六、案例复盘:一次复权错误导致的回测偏差

某多因子策略在回测中显示年化收益28%,但实盘三个月后收益仅为3%。排查发现,数据管道中使用了前复权价格计算动量因子,而前复权价格会随新的除权事件不断变化,导致回测中使用的历史价格与实盘时不一致。更严重的是,部分股票在除权日附近产生了虚假的动量信号,策略频繁交易并承担了额外成本。

修正方案:统一改用后复权价格计算因子,用不复权价格判断交易可行性,并在每次除权后重新生成复权因子。修正后回测年化收益降至15%,与实盘表现基本一致。

复盘要点:数据清洗规则必须与实盘数据管道保持一致。回测中使用的每一个字段、每一个转换步骤,都应当在实盘中有对应的实现。任何“回测专用”的数据处理,都是潜在的风险点。

七、工程化建议与检查清单

  • 建立数据版本管理,每次清洗规则变更后重新运行回测并记录差异。
  • 对每个标的维护停牌日历、涨跌停日历、除权除息日历。
  • 使用单元测试验证复权因子、频率转换、时间对齐的正确性。
  • 在回测报告中加入数据质量指标:缺失率、异常值比例、对齐偏差天数。
  • 避免在清洗阶段引入未来信息,所有填充和插值必须基于历史可得数据。

数据准备与清洗是量化策略组合回测中最基础也最容易被忽视的环节。只有建立起严格、可复现、可审计的数据管道,后续的组合构建、回测执行与归因分析才有意义。建议在每次策略迭代前,先花时间检查数据质量,而不是急于调参。