4. 数据清洗与预处理:缺失值处理、异常值检测、重复值去除与数据对齐合并

金融时间序列数据在采集、传输与存储过程中,往往因系统故障、网络延迟、交易停牌、数据源切换或人工录入错误而产生缺失、异常、重复及时间戳错位等问题。若未经清洗直接进入因子计算或模型训练,轻则导致指标失真,重则引发策略回测的“未来函数”或“幸存者偏差”。本章围绕金融时序数据清洗的四大核心任务——缺失值处理、异常值检测与处理、重复值去除、数据对齐与合并——展开系统讲解,帮助学习者建立一套可复用的数据质量保障流程。

金融时间序列数据清洗与预处理链路 原始时序数据 行情/财务/另类数据 ① 缺失值处理 前向填充 / 插值 / 删除 需区分缺失机制 MCAR/MAR/MNAR ② 异常值检测 3σ / IQR / MAD / 分位数 金融数据厚尾分布 需结合业务逻辑 ③ 重复值去除 完全重复 / 部分重复 时间戳+代码去重 保留最新或首条 ④ 数据对齐与合并 inner/outer/left/right 时间戳对齐 / 频率转换 避免未来函数 清洗后标准数据集 因子计算 / 模型训练 数据质量检查清单 • 缺失比例与分布模式 • 异常值是否业务合理 • 重复数据来源追溯 • 时间戳对齐精度验证

4.1 缺失值处理:从缺失机制到策略选择

4.1.1 核心原理:理解缺失机制是决策前提

金融时序数据的缺失并非随机事件,其背后往往对应特定的市场状态或系统行为。统计上,缺失机制分为三类:完全随机缺失(MCAR),缺失与任何变量无关,如偶发的网络抖动;随机缺失(MAR),缺失与其他可观测变量相关,如某只股票因停牌导致当日无交易数据;非随机缺失(MNAR),缺失与缺失值本身相关,如价格跌破某阈值后数据源停止上报。在金融场景中,MNAR 尤为危险——例如 ST 股票连续跌停时可能因涨跌停限制产生“有价无量”的缺失,若简单填充会严重扭曲流动性因子。

缺失机制 典型金融场景 推荐处理策略 风险提示
MCAR 偶发网络中断、交易所临时故障 删除或插值均可 删除比例不宜超过 5%
MAR 停牌、低流动性时段无成交 前向填充 + 交易量置零 需同步处理成交量字段
MNAR 涨跌停板导致的价格缺失 业务规则填充 + 标记 不可用统计插值,需人工规则

4.1.2 操作流程:三种核心方法的适用边界

前向填充(Forward Fill)是金融时序最常用的方法,其逻辑是“用最近的有效观测值填补后续缺失”。该方法适用于价格、指数等具有强路径依赖的变量,但必须注意:若连续缺失超过 5 个交易日,前向填充会引入“陈旧价格”偏差。实现时建议同时生成一个 is_filled 标记列,便于后续因子计算时识别填充数据。

# Python 示例:前向填充 + 填充标记
import pandas as pd
df['price_ffill'] = df['close'].ffill()
df['is_filled'] = df['close'].isna().astype(int)
df.loc[df['close'].notna(), 'is_filled'] = 0

插值法适用于收益率、波动率等相对平滑的序列。线性插值假设相邻两点间呈线性关系,而时间加权插值则考虑时间间隔——对于日频数据,若周五与下周一之间缺失,时间加权插值会赋予周五数据更高权重。高阶样条插值在金融中需谨慎使用,因其可能产生“过冲”现象,导致插值价格超出合理区间。

删除法仅适用于缺失比例极低(<2%)且缺失机制为 MCAR 的场景。在面板数据中,删除某只股票的全部记录可能引入“幸存者偏差”——例如删除已退市股票,会使回测结果高估策略收益。因此,删除前必须确认缺失原因与业务无关。

风险警示:在金融时序中,切勿对“成交量”使用插值法。成交量为零与缺失有本质区别——零代表无交易发生,缺失代表数据未采集。若将缺失成交量插值为非零值,将直接导致流动性因子失真。

4.1.3 案例拆解:停牌股票的数据修复

某量化团队在构建中证 500 成分股因子库时,发现某股票因重大资产重组停牌 20 个交易日。初始数据中,该股票的价格、成交量均为 NaN。团队的处理流程如下:

  1. 识别缺失机制:通过公告确认停牌原因,判定为 MAR(与公司事件相关,但可观测)。
  2. 价格处理:采用前向填充,将停牌前最后收盘价作为停牌期间价格,同时生成 is_suspended 标记。
  3. 成交量处理:将停牌期间成交量置为 0,而非填充或插值。
  4. 复牌处理:复牌首日价格与停牌前价格可能存在跳空,需在因子计算中排除停牌期间的数据点。

复盘发现,若直接删除该股票 20 日数据,会导致因子面板不平衡;若使用线性插值,则插值价格与真实复牌价偏差达 15%,严重扭曲动量因子。最终方案在回测中保留了该股票,并通过标记列在因子计算时动态调整权重。

4.2 异常值检测与处理:识别“真异常”与“假信号”

4.2.1 核心原理:金融数据的厚尾分布挑战

金融收益率呈现典型的“尖峰厚尾”分布,极端值(如 2008 年雷曼破产日、2020 年原油期货负价格)并非“错误数据”,而是真实的市场状态。因此,异常值检测必须区分两类情况:数据错误(如价格录入为 0、小数点错位)和极端真实值(如闪崩、涨跌停)。前者需要修正或删除,后者需要保留但需在模型中特殊处理。

4.2.2 判断方法:从统计阈值到业务规则

方法 计算公式 适用场景 局限性
3σ 法则 |x - μ| > 3σ 近似正态分布的指标 金融数据厚尾,σ 被极端值拉大
IQR 法 x < Q1 - 1.5×IQR 或 x > Q3 + 1.5×IQR 偏态分布,稳健性较好 对样本量敏感,小样本不稳定
MAD 法 |x - median| > 3×1.4826×MAD 含极端值的厚尾数据 对对称分布效果最佳
业务规则 价格 ≤ 0、涨跌幅 > 20% 交易所规则约束 需人工维护规则库

实际操作中,建议采用“统计初筛 + 业务复核”的两阶段策略。首先用 MAD 或 IQR 方法标记潜在异常点,然后由业务人员逐一确认。对于确认的数据错误,若为单点错误且前后值合理,可采用前后均值修正;若为连续错误,则需回溯数据源。

经验提示:在日频数据中,若某日收益率超过 10%(A 股主板涨跌停限制),不应直接判定为异常。需检查是否为新股上市首日、ST 股 5% 限制或科创板 20% 限制。务必结合交易规则进行判断。

4.2.3 案例拆解:负油价事件的异常值处理

2020 年 4 月 20 日,WTI 原油 5 月期货结算价收于 -37.63 美元/桶,这是历史上首次出现负价格。某能源基金的数据团队在清洗历史数据时,该值被统计模型标记为“极端异常值”。若直接删除,将导致后续波动率计算严重低估尾部风险;若保留,则传统的对数收益率计算(log(P_t/P_{t-1}))因负价格而无法计算。

团队的解决方案是:

  1. 保留原始价格数据,不做删除或修正。
  2. 在收益率计算中,改用“简单百分比变化”(P_t/P_{t-1} - 1),避免对数变换的数学限制。
  3. 在风险模型中,为该日单独设置“极端事件标记”,并在 VaR 计算中采用历史模拟法而非参数法。

复盘结论:异常值处理的核心不是“消除异常”,而是“理解异常并选择正确的处理路径”。对于真实极端事件,删除或修正反而会破坏数据的真实性。

4.3 重复值去除:识别“真重复”与“假重复”

4.3.1 核心原理:重复数据的来源与危害

金融数据重复通常源于三个渠道:数据源重复推送(同一 tick 数据被发送两次)、多源拼接未去重(Wind 与 Bloomberg 数据合并时重叠)、以及除权除息调整时生成的历史数据覆盖。重复值若不处理,会导致:① 收益率计算出现连续相同值,扭曲波动率估计;② 因子值被重复计算,在排序时产生虚假的集中度;③ 回测中同一信号被触发多次,高估交易频率。

4.3.2 操作流程:多级去重策略

第一级:完全重复去重。基于所有字段的完全匹配,直接删除重复行。使用 df.drop_duplicates() 即可实现,但需注意保留顺序——默认保留第一条,若数据源有版本更新,应保留最后一条。

第二级:关键字段去重。金融时序中,通常以“时间戳 + 证券代码”作为唯一键。若同一时间戳下出现多条记录,需检查是否为不同数据源拼接导致。此时应制定合并规则:优先保留主数据源,或对重复字段取均值(仅限数值型)。

第三级:业务逻辑去重。例如,日频数据中同一交易日出现两条不同收盘价,需通过成交量验证——成交量大的记录通常更可靠。

# 多级去重示例
# 第一级:完全去重
df = df.drop_duplicates()
# 第二级:按时间戳+代码去重,保留最后一条
df = df.drop_duplicates(subset=['datetime', 'symbol'], keep='last')
# 第三级:业务规则——若仍有重复,保留成交量较大的记录
df = df.sort_values('volume', ascending=False).drop_duplicates(
    subset=['datetime', 'symbol'], keep='first')
关键提醒:去重操作必须在数据对齐之前完成。若先合并再去重,可能因合并产生的 NaN 干扰去重逻辑,导致误删有效数据。

4.4 数据对齐与合并:构建统一的时间轴

4.4.1 核心原理:时间戳对齐的精度与方向

金融数据合并的核心挑战在于时间戳的对齐。不同数据源的采样频率可能不同——日频行情、分钟频交易、季度财报、不定期公告。合并时必须明确“主时间轴”,通常选择最高频率的数据作为基准,其他数据通过“前向填充”或“重采样”对齐到该时间轴。同时需警惕“未来函数”:合并时只能使用当前时点已发生的数据,不能将未来数据填充到当前时点。

4.4.2 合并方式对比与选择

合并方式 逻辑 金融应用场景 风险
inner join 仅保留两边都有的时间点 严格对齐的因子计算 丢失停牌期间数据
left join 保留左侧全部时间点 以行情为主轴,匹配财务数据 财务数据缺失时产生 NaN
outer join 保留所有时间点 多源数据完整性检查 产生大量 NaN,需后续填充
asof merge 按时间点向前匹配最近值 公告数据与行情对齐 需指定方向,避免未来数据

4.4.3 案例拆解:财务数据与行情数据的对齐

某多因子模型需要将季度财务数据(如 ROE、营收增速)与日频行情数据合并。财务数据每季度发布一次,且存在“报告期”与“公告日”的区别——报告期是财务数据覆盖的期间,公告日才是数据真正可用的时间点。若按报告期对齐,会导致未来函数:Q1 财报在 4 月底公告,但报告期是 3 月 31 日,若在 4 月 1 日就使用该数据,则使用了未来信息。

正确做法是使用 merge_asof 按公告日对齐:

# 按公告日向前匹配,确保不使用未来数据
merged = pd.merge_asof(
    price_df,           # 日频行情
    financial_df,       # 季度财务数据
    left_on='date',
    right_on='announce_date',
    direction='backward'  # 只使用已公告的数据
)

该案例的复盘要点:数据对齐不仅是技术操作,更是业务逻辑的体现。对齐方向(向前/向后)、对齐键(报告期/公告日)的选择直接影响因子是否包含未来信息,进而决定回测结果的可靠性。

严重警告:在因子计算中,未来函数是回测“作弊”的主要来源。常见错误包括:① 使用当日收盘后才知道的数据计算当日开盘信号;② 财务数据按报告期而非公告日对齐;③ 前向填充时跨越了停牌边界。务必在数据清洗阶段就建立防未来函数的检查机制。

4.5 本章总结与质量验证清单

数据清洗与预处理是量化研究的基础工程,其质量直接决定上层模型的可靠性。本章的核心要点可归纳为:

  • 缺失值处理:先判断缺失机制(MCAR/MAR/MNAR),再选择前向填充、插值或删除;金融场景中前向填充最常用,但需配合标记列。
  • 异常值检测:统计方法(MAD/IQR)初筛 + 业务规则复核;真实极端事件应保留,数据错误才需修正。
  • 重复值去除:三级去重策略——完全去重、关键字段去重、业务逻辑去重;去重必须在合并前完成。
  • 数据对齐合并:明确主时间轴,选择正确的合并方式;财务数据必须按公告日对齐,防止未来函数。

建议在每次数据清洗后执行以下验证流程:

  1. 检查缺失值比例是否在可接受范围(<5%),且填充逻辑合理。
  2. 抽样 10 个时间点,人工核对异常值处理结果。
  3. 验证去重后数据量是否符合预期(如交易日历 × 证券数量)。
  4. 用“滞后一天”的收益率相关性检验是否存在未来函数。
实践建议:将数据清洗流程封装为可复用的 Python 类或函数,并记录每次清洗的参数与日志。当策略表现异常时,可快速回溯数据清洗环节是否存在问题。数据清洗不是一次性工作,而是需要随市场规则变化持续迭代的工程模块。