4. 数据清洗基础:缺失值处理、异常值检测(价格跳变、零成交量)、时间戳对齐与重采样
Tick 数据在采集、传输与存储过程中极易受到市场噪声、系统延迟及交易所规则的影响,产生缺失值、异常记录与时间戳错位。本章将系统讲解三类核心清洗操作,确保后续标签校准建立在干净、连续、可对齐的数据基础之上。
4.1 缺失值处理
Tick 数据中的缺失值通常表现为某一行或连续多行的时间戳、价格、成交量字段为空。缺失原因包括:交易所撮合引擎在无成交时跳过推送、网络丢包、数据库写入失败等。
处理原则: 对于 Tick 级别的金融时间序列,禁止直接删除缺失行,因为删除会破坏时间轴的连续性,导致后续重采样与标签计算出现偏差。应优先采用前向填充(Forward Fill)或线性插值。
4.1.1 缺失值分类与处理策略
| 缺失类型 | 典型场景 | 推荐处理方式 |
|---|---|---|
| 单点价格缺失 | 某笔成交记录缺少价格字段 | 前向填充(Last Observation Carried Forward) |
| 连续多笔缺失 | 网络中断导致 3-5 秒无数据 | 线性插值(Linear Interpolation) |
| 成交量缺失 | 价格存在但成交量为 0 或空 | 填充为 0(表示无成交) |
| 时间戳缺失 | 整行记录的时间戳为空 | 删除该行(时间戳是索引基础) |
4.1.2 前向填充与线性插值代码示例
import pandas as pd
import numpy as np
# 示例:模拟 Tick 数据
tick_data = pd.DataFrame({
'timestamp': pd.to_datetime(['2024-01-10 09:30:00.000',
'2024-01-10 09:30:00.100',
'2024-01-10 09:30:00.200',
'2024-01-10 09:30:00.300']),
'price': [100.50, np.nan, 100.55, np.nan],
'volume': [100, 50, np.nan, 80]
})
# 前向填充价格
tick_data['price_ffill'] = tick_data['price'].ffill()
# 线性插值价格(适用于连续缺失)
tick_data['price_interp'] = tick_data['price'].interpolate(method='linear')
# 成交量缺失填充为 0
tick_data['volume_filled'] = tick_data['volume'].fillna(0)
print(tick_data)
4.2 异常值检测:价格跳变与零成交量
异常值会严重扭曲标签校准结果。Tick 数据中最常见的两类异常是价格跳变(Price Spike)和零成交量(Zero Volume)。
4.2.1 价格跳变检测
价格跳变指相邻 Tick 之间的价格变化幅度远超正常市场波动范围,通常由数据录入错误或撮合异常引起。检测方法包括固定阈值法和滚动统计法。
滚动 Z-Score 法: 计算过去 N 个 Tick 的价格变化均值和标准差,若当前价格变化超过 3 倍标准差,则标记为跳变。
def detect_price_spike(series, window=20, threshold=3):
"""基于滚动 Z-Score 检测价格跳变"""
returns = series.pct_change().dropna()
rolling_mean = returns.rolling(window).mean()
rolling_std = returns.rolling(window).std()
z_scores = (returns - rolling_mean) / rolling_std
return z_scores.abs() > threshold
# 标记异常
tick_data['is_spike'] = detect_price_spike(tick_data['price_ffill'])
4.2.2 零成交量处理
零成交量记录表示该 Tick 时间点没有实际成交。在标签校准中,零成交量记录不应参与价格变化计算,但保留时间戳用于对齐。
| 场景 | 处理方式 |
|---|---|
| 零成交量且价格与前一笔相同 | 保留,视为无成交的报价更新 |
| 零成交量但价格发生跳变 | 标记为可疑记录,需人工复核 |
| 连续多笔零成交量 | 保留时间戳,但计算标签时跳过 |
4.3 时间戳对齐与重采样
不同数据源(交易所、券商、第三方数据商)的时间戳精度和基准可能不一致。时间戳对齐是构建统一标签样本的前提。
4.3.1 时间戳对齐策略
- 最近邻对齐: 将每个 Tick 的时间戳对齐到最近的整数毫秒或微秒。
- 向前对齐: 将时间戳向下取整到指定精度(如 100ms 桶)。
- 向后对齐: 将时间戳向上取整到指定精度。
4.3.2 重采样方法
重采样将不规则 Tick 数据转换为固定频率的时间序列,是标签校准的标准前置步骤。
# 设置时间戳为索引
tick_data.set_index('timestamp', inplace=True)
# 重采样为 100ms 频率,使用前向填充
resampled = tick_data.resample('100ms').ffill()
# 重采样为 1s 频率,使用 OHLC 聚合
ohlc = tick_data['price_ffill'].resample('1s').ohlc()
volume_sum = tick_data['volume_filled'].resample('1s').sum()
注意: 重采样频率的选择需与交易品种的流动性匹配。对于高频合约(如股指期货),推荐 100ms-500ms;对于低频合约(如部分债券),推荐 1s-5s。过高的重采样频率会引入大量空值,过低则会丢失微观结构信息。
4.4 数据清洗流程总览
4.5 清洗效果评估指标
完成清洗后,建议使用以下指标评估清洗质量:
| 指标 | 计算公式 | 合格标准 |
|---|---|---|
| 缺失率 | 缺失行数 / 总行数 × 100% | < 0.1% |
| 异常率 | 标记为异常的行数 / 总行数 × 100% | < 0.05% |
| 时间戳连续性 | 相邻 Tick 时间差的标准差 | 标准差 < 重采样间隔的 50% |
| 数据保留率 | 清洗后行数 / 原始行数 × 100% | > 99.5% |
最佳实践: 建议将清洗逻辑封装为可复用的 Pipeline 函数,每次处理新数据时自动生成清洗报告,包含缺失率、异常率、时间戳连续性等关键指标,便于快速定位数据质量问题。