3. Tick数据预处理:缺失值处理、异常值检测、时间戳对齐与重采样
Tick 数据是逐笔成交或逐笔报价的原始记录,具有高频、非等间隔、数据量大的特点。在将 Tick 数据用于模型训练之前,必须进行严格的预处理,否则原始数据中的噪声、缺失和错位会直接污染标签,导致模型学到虚假规律。本章围绕四个核心环节展开:缺失值处理、异常值检测、时间戳对齐与重采样。
3.1 缺失值处理
Tick 数据缺失通常由网络延迟、交易所撮合间隙或记录系统故障引起。缺失值处理不当会破坏时间序列的连续性,影响后续特征计算(如收益率、波动率)。
关键原则: Tick 数据缺失不能简单删除或填充均值,必须根据缺失模式选择策略。
| 缺失类型 | 典型场景 | 推荐处理方式 |
|---|---|---|
| 单点缺失 | 某一毫秒的成交价/量缺失 | 前向填充(ffill)或线性插值 |
| 连续缺失(短) | 连续 3-5 笔缺失 | 线性插值或样条插值 |
| 连续缺失(长) | 超过 10 笔缺失 | 标记为缺失区间,不填充;或使用前一交易日同期数据 |
| 盘前/盘后缺失 | 非交易时段无数据 | 保留自然缺失,不填充 |
# 示例:前向填充与线性插值
import pandas as pd
# 假设 tick_df 包含 'timestamp', 'price', 'volume' 列
tick_df.set_index('timestamp', inplace=True)
# 前向填充(适用于单点缺失)
tick_df['price'].fillna(method='ffill', inplace=True)
# 线性插值(适用于短连续缺失)
tick_df['price'].interpolate(method='linear', inplace=True)
# 标记长连续缺失区间
missing_mask = tick_df['price'].isna()
long_gap = missing_mask.rolling(window=10).sum() >= 10
tick_df.loc[long_gap, 'price_missing_flag'] = 1
3.2 异常值检测
Tick 数据中的异常值通常由错误报价、胖手指交易或数据记录错误引起。异常值会严重扭曲统计特征和模型训练结果,必须在预处理阶段识别并处理。
注意: 不要将市场剧烈波动时的真实大单误判为异常值。异常值检测应结合市场微观结构特征。
常用检测方法
- 固定阈值法: 价格超出合理范围(如涨跌停板)直接剔除。
- 移动窗口 Z-score: 以当前点前后 N 笔数据计算均值和标准差,若 |Z| > 3 则标记为异常。
- 中位数绝对偏差(MAD): 对极端值更稳健,适合 Tick 数据。
- 价格反转检验: 若当前价格相对前一笔变化超过阈值(如 1%),且下一笔立即大幅回调,则当前笔可能为异常。
# 示例:移动窗口 Z-score 异常值检测
window = 20
tick_df['rolling_mean'] = tick_df['price'].rolling(window).mean()
tick_df['rolling_std'] = tick_df['price'].rolling(window).std()
tick_df['z_score'] = (tick_df['price'] - tick_df['rolling_mean']) / tick_df['rolling_std']
tick_df['is_outlier'] = tick_df['z_score'].abs() > 3
# 异常值处理:用窗口内中位数替换
tick_df.loc[tick_df['is_outlier'], 'price'] = tick_df['price'].rolling(window).median()
3.3 时间戳对齐
不同数据源(交易所、券商、第三方数据商)的时间戳精度和时区可能不一致。时间戳对齐是确保多源 Tick 数据在时间轴上正确匹配的前提。
对齐要点
- 统一精度: 将所有时间戳转换为同一精度(如毫秒或微秒),避免精度截断导致错位。
- 时区归一化: 统一转换为 UTC 或交易所本地时间,消除夏令时影响。
- 去重与排序: 同一毫秒内出现多条记录时,按接收顺序保留或聚合。
- 对齐到标准时钟: 将 Tick 时间戳对齐到最近的整毫秒或整微秒边界。
# 示例:时间戳对齐与去重
tick_df['timestamp'] = pd.to_datetime(tick_df['timestamp'], unit='ms')
tick_df.set_index('timestamp', inplace=True)
# 对齐到整毫秒(向下取整)
tick_df.index = tick_df.index.floor('1ms')
# 去重:同一毫秒内取最后一条记录
tick_df = tick_df[~tick_df.index.duplicated(keep='last')]
# 排序
tick_df.sort_index(inplace=True)
3.4 重采样
重采样是将非等间隔的 Tick 数据转换为等间隔时间序列的过程。这是连接 Tick 数据与下游模型(如 LSTM、Transformer)的关键步骤。
实践建议: 重采样频率的选择取决于交易频率和模型需求。高频策略常用 100ms、500ms、1s;中低频策略常用 1min、5min。
常用重采样方法
| 方法 | 描述 | 适用场景 |
|---|---|---|
| 最后价格法 | 取采样区间内最后一笔成交价 | 生成 OHLC 数据 |
| 成交量加权平均 | 以成交量为权重计算平均价格 | 反映真实成交成本 |
| 时间加权平均 | 以时间间隔为权重计算平均价格 | 消除成交量不均匀的影响 |
| Tick 计数 | 统计区间内 Tick 笔数 | 衡量市场活跃度 |
# 示例:1秒重采样生成 OHLC 数据
resampled = tick_df['price'].resample('1S').ohlc()
resampled['volume'] = tick_df['volume'].resample('1S').sum()
resampled['vwap'] = (tick_df['price'] * tick_df['volume']).resample('1S').sum() / resampled['volume']
3.5 预处理流程总结
完整的 Tick 数据预处理管线应按照以下顺序执行:
- 时间戳对齐与去重 — 统一精度、时区,去除重复记录。
- 缺失值处理 — 根据缺失长度选择填充或标记。
- 异常值检测与处理 — 多方法综合判定,避免误杀。
- 重采样 — 转换为等间隔时间序列,供模型使用。
核心要点: 预处理的质量直接决定标签校准的准确性。在 Tick 数据上,一个未被处理的异常值可能导致整个标签窗口的偏移。建议在预处理后增加数据质量报告(缺失率、异常率、时间戳连续性指标),作为模型训练前的最后一道检查。