4. 数据预处理实战

在金融时序预测项目中,原始数据往往来自多个渠道:行情接口、交易所文件、第三方数据库、宏观指标发布页。它们的时间戳格式、交易时段、频率和缺失模式各不相同。如果直接把原始数据喂给模型,模型学到的可能不是市场规律,而是数据采集过程中的噪声。数据预处理不是“清洗一下”这么简单,它决定了后续特征工程和模型训练的上限。本节围绕缺失值处理、异常值检测、时间对齐、重采样与频率转换五个核心环节,给出可落地的工程方法。

一、核心原理:金融时序的四种“脏数据”

金融时序的脏数据通常表现为四类。第一类是缺失值,包括非交易时段缺失、停牌缺失、接口丢包和字段未披露。第二类是异常值,如价格瞬时跳变、成交量错误放大、涨跌幅超出制度限制。第三类是时间不对齐,例如日频因子在周末有值而行情没有,或不同市场因时区差异导致同一“日期”实际错位。第四类是频率不一致,分钟线、日线、周线和月度宏观数据混用。

处理这四类问题的基本原则是:先理解业务时间语义,再做数学变换。金融时间不是均匀的连续轴,而是由交易日历、交易时段和结算规则共同定义的离散事件序列。忽略这一点,任何插值和对齐都可能引入前视偏差。

关键判断:缺失值要区分“真实无交易”和“数据未采集”。前者应保留为无观测,后者才考虑填补。异常值要区分“错误数据”和“真实极端行情”,后者往往是模型最需要学习的尾部信息。

二、方法流程:从原始表到建模宽表

一个稳健的预处理流水线通常按以下顺序执行:

  1. 统一时间索引:将所有数据的时间戳转换为带时区的标准时间,并映射到统一的交易日历。
  2. 缺失值诊断:统计缺失率、连续缺失长度和缺失发生的时间分布。
  3. 异常值检测:结合业务规则与统计方法标记可疑点。
  4. 对齐:以目标频率的时间轴为基准,将多源数据对齐到同一索引。
  5. 重采样与频率转换:按目标频率聚合或升采样,并明确每个字段的聚合函数。
  6. 输出建模宽表:保留原始值、处理后值和缺失/异常标记列。

下面是一个使用 pandas 的典型预处理骨架:

import pandas as pd
import numpy as np

# 1. 统一时间索引
df["timestamp"] = pd.to_datetime(df["timestamp"], utc=True)
df = df.set_index("timestamp").sort_index()

# 2. 缺失值诊断
missing_rate = df.isna().mean()
continuous_gap = df["close"].isna().astype(int).groupby(
    (df["close"].notna()).cumsum()
).sum()

# 3. 异常值检测:基于滚动中位数绝对偏差
window = 20
median = df["close"].rolling(window).median()
mad = (df["close"] - median).abs().rolling(window).median()
df["is_outlier"] = (df["close"] - median).abs() > 5 * mad

# 4. 对齐到交易日历
trading_days = pd.date_range("2020-01-01", "2024-12-31", freq="B")
df = df.reindex(trading_days)

# 5. 重采样:分钟线转日线
daily = df.resample("1D").agg({
    "open": "first",
    "high": "max",
    "low": "min",
    "close": "last",
    "volume": "sum"
})

三、案例场景:多源数据对齐与频率转换

假设我们要构建一个日频预测模型,输入包括:A股某股票的日行情、分钟级资金流、以及月度发布的宏观经济指标。三者的时间语义完全不同。日行情只在交易日有值;分钟资金流需要先聚合到日频;宏观指标通常在月初发布,但实际可用时间可能滞后一周。

正确的做法是:以交易日历为主轴,将分钟资金流按交易日聚合为日频;对宏观指标,使用发布日而非统计月份进行对齐,避免未来信息泄露。对于停牌日,行情字段保留缺失,但可以增加“是否停牌”的标记列,让模型自行学习。

数据类型原始频率目标频率聚合/转换方式注意事项
股票行情分钟日OHLC:first/max/min/last,成交量求和停牌日不填补价格
资金流分钟日净流入求和,大单占比取均值注意交易时段过滤
宏观指标月度日按发布日前向填充禁止使用统计月份直接对齐
财务数据季度日按公告日前向填充区分报告期与公告期
实用技巧:在重采样时,始终显式指定每个字段的聚合函数。默认的 mean() 对价格和成交量都不合适。价格用 OHLC 逻辑,成交量用求和,比率类字段用加权平均或最后值。

四、误区与复盘:预处理中最容易犯的错

误区一:用未来数据填补过去。例如用全样本均值填补缺失值,或用后向填充处理宏观数据。这会造成严重的前视偏差,模型在回测中表现优异,实盘却失效。

误区二:把停牌日当作缺失值填补。停牌期间没有交易,价格不应被插值。正确做法是保留缺失并增加停牌标记,或直接从训练样本中排除。

误区三:异常值一律删除。金融市场的极端行情往往包含重要信息。更稳妥的做法是:先标记,再在特征工程中增加“是否异常”的哑变量,或使用稳健缩放(如 RobustScaler)降低影响。

误区四:忽略时区与夏令时。跨市场数据对齐时,纽约、伦敦、东京的交易日和交易时段不同。必须统一到 UTC 或交易所本地时间,并处理夏令时切换。

复盘清单:每次预处理后,检查以下问题——时间索引是否单调递增?缺失率是否在可接受范围?异常标记是否与业务事件对应?对齐后是否引入了未来信息?重采样后的字段语义是否仍然正确?
金融时序预处理流水线 原始数据源 行情 / 资金流 / 宏观 统一时间索引 UTC + 交易日历 缺失值诊断 缺失率 / 连续长度 异常值检测 MAD / 业务规则 多源对齐 发布日 / 交易日 重采样与频率转换 OHLC / 求和 / 加权 建模宽表 原始值 + 处理后值 + 缺失标记 + 异常标记 + 停牌标记 检查清单 索引单调? 缺失率合理? 异常有标记? 无前视偏差? 语义正确? 时区统一? 停牌已标记? 聚合函数明确?

数据预处理的质量直接决定了模型能否学到真实的市场结构。把缺失、异常、对齐和频率转换当作一个整体流水线来设计,而不是零散地调用几个函数,才能在后续的特征工程和模型训练中少走弯路。建议在每次预处理后保留完整的处理日志和标记列,这样当模型表现异常时,可以快速回溯到数据层面的原因。