3. 数据清洗与预处理:缺失值处理、异常值检测与时间对齐
高频行情数据在采集、传输与存储过程中,不可避免地会引入噪声、缺失与异常。本章聚焦于将原始Tick/快照数据转化为可用于特征工程的标准时间序列,核心步骤包括:缺失值处理、异常值检测(跳空、涨跌停)以及时间对齐与重采样。
核心原则:高频数据清洗必须兼顾“速度”与“保真度”。过度平滑会抹去微观结构信号,清洗不足则会导致特征标签失真。建议在清洗前后保留数据质量统计日志。
3.1 缺失值处理
高频数据缺失通常表现为:某笔Tick缺失、某秒无成交、或某字段(如卖一量)为空。处理策略需根据缺失模式与业务场景选择。
| 缺失类型 | 典型场景 | 推荐处理方法 | 注意事项 |
|---|---|---|---|
| 完全随机缺失 | 网络丢包导致单笔Tick丢失 | 前向填充(ffill) | 适用于短时间间隔(< 100ms) |
| 连续缺失 | 交易所断流、系统宕机 | 标记为缺失区间,不填充 | 避免引入虚假价格序列 |
| 字段级缺失 | 卖五价为空(流动性不足) | 用卖一价 + 最小价差估算 | 需记录估算标记 |
| 盘口快照缺失 | 某时刻无买卖盘数据 | 线性插值(仅限价格) | 量数据不建议插值 |
代码示例:高频缺失值处理(Python伪代码)
import pandas as pd
import numpy as np
# 假设df为包含'timestamp', 'price', 'bid1', 'ask1'的DataFrame
# 1. 前向填充:适用于毫秒级缺失
df['price_ffill'] = df['price'].fillna(method='ffill', limit=5)
# 2. 标记连续缺失区间(超过10个Tick视为无效区间)
df['missing_flag'] = df['price'].isna().astype(int)
df['missing_group'] = df['missing_flag'].ne(df['missing_flag'].shift()).cumsum()
df['missing_count'] = df.groupby('missing_group')['missing_flag'].transform('sum')
df.loc[df['missing_count'] > 10, 'price'] = np.nan # 整段置为NaN
# 3. 盘口价格线性插值(仅限价格字段)
df['bid1_interp'] = df['bid1'].interpolate(method='linear', limit=3)
3.2 异常值检测:跳空与涨跌停
高频数据中的异常值会严重扭曲特征分布,尤其是价格跳空和涨跌停两种场景。跳空通常由隔夜信息、重大事件或数据错误引起;涨跌停则是交易所规则导致的特殊价格状态。
3.2.1 价格跳空检测
跳空定义为相邻两笔Tick价格变化超过阈值。检测方法需区分“正常波动”与“异常跳空”。
- 绝对阈值法:价格变化超过N个最小变动价位(Tick Size)。例如,沪深300股指期货最小变动0.2点,若相邻Tick价格变化超过10个Tick(2点),标记为跳空。
- 相对阈值法:价格变化超过前N笔滚动均值的M倍标准差。适用于波动率突变场景。
- 隔夜跳空:当日第一笔价格与昨日最后一笔价格之差,需单独标记,不参与日内波动统计。
注意:跳空不一定是错误数据。隔夜跳空是有效信息,应保留但单独标记;日内跳空若由撮合异常引起(如价格从10.00直接跳到10.50又跳回),应剔除或修正。
3.2.2 涨跌停状态处理
涨跌停期间,价格被锁定在涨停价或跌停价,成交量急剧萎缩,盘口挂单严重失衡。此时特征计算需特殊处理。
| 状态 | 价格特征 | 成交量特征 | 特征工程处理 |
|---|---|---|---|
| 涨停 | 最新价 = 涨停价,卖一为空 | 成交量极低或为0 | 标记涨停状态位;不计算收益率;盘口失衡指标置为极端值 |
| 跌停 | 最新价 = 跌停价,买一为空 | 成交量极低或为0 | 标记跌停状态位;不计算收益率;盘口失衡指标置为极端值 |
| 盘中临时停牌 | 价格不变,无成交 | 成交量为0 | 剔除该区间或标记为缺失 |
3.3 时间对齐与重采样
高频数据源(交易所、券商、第三方)的时间戳精度和基准可能不一致。时间对齐是将多源数据统一到同一时间轴的过程;重采样则是将不规则Tick数据转换为固定频率的时间序列。
3.3.1 时间戳标准化
- 精度统一:将所有时间戳统一到纳秒或微秒级。Python中建议使用
pd.Timestamp或np.datetime64。 - 时区处理:交易所数据通常为本地时间(如北京时间),需显式指定时区,避免夏令时或跨市场对齐错误。
- 时钟漂移校正:对比交易所官方撮合时间与本地接收时间,若偏差超过1ms,需进行线性校正。
3.3.2 重采样策略
高频特征工程中,常用重采样频率包括:1ms、10ms、100ms、1s、5s。选择取决于策略持仓周期和信号衰减速度。
| 重采样频率 | 适用策略 | OHLCV计算方式 | 注意事项 |
|---|---|---|---|
| 1ms | 高频做市、逐笔统计 | 首笔为Open,末笔为Close | 数据量极大,需压缩存储 |
| 100ms | 统计套利、订单流分析 | 加权平均价(VWAP) | 适合盘口快照对齐 |
| 1s | 日内趋势、动量策略 | Last + Volume累计 | 最常用频率,平衡精度与计算 |
| 5s | 低频因子计算 | 标准OHLCV | 适合分钟级因子回测 |
代码示例:1秒重采样(Python)
# 假设df包含'timestamp'(datetime), 'price', 'volume'
df.set_index('timestamp', inplace=True)
# 1秒重采样:计算OHLCV
resampled = df.resample('1S').agg({
'price': 'ohlc', # 生成Open, High, Low, Close
'volume': 'sum'
})
# 展开多级列索引
resampled.columns = ['_'.join(col).strip() for col in resampled.columns.values]
# 处理空区间:无成交的秒用前向填充
resampled.fillna(method='ffill', inplace=True)
3.3.3 多源数据对齐
当同时使用逐笔成交、盘口快照和订单簿数据时,需按时间戳对齐到同一时间轴。
- 左对齐:以成交数据时间轴为主,盘口快照取最近时刻的快照。
- 右对齐:以盘口快照时间轴为主,成交数据聚合到快照时间点。
- 最近邻对齐:寻找时间差最小的匹配点,适用于毫秒级精度要求。
工程建议:在分布式流处理框架(如Flink、Kafka Streams)中,建议使用事件时间(Event Time)而非处理时间(Processing Time)进行对齐,并设置合理的watermark延迟(通常为100ms-500ms)以处理乱序数据。
完成数据清洗与预处理后,高频行情数据将形成干净、连续、对齐的时间序列,为后续特征计算和标签生成奠定坚实基础。下一阶段将进入特征构造环节。