第一章:Tick数据清洗——缺失值处理、异常值检测、时间对齐、数据重采样
做超短线交易的朋友都知道,Tick数据就是我们的命根子。但说实话,交易所给出来的原始Tick数据,那叫一个脏。我刚开始做高频策略那会儿,拿到的数据里什么妖魔鬼怪都有——缺的、错的、时间戳乱跳的。今天咱们就把这四块硬骨头啃下来。
1.1 缺失值处理:别让空值毁了你的策略
Tick数据缺失的原因很多。网络抖动、交易所撮合引擎的瞬时故障、甚至行情网关的缓存策略,都会导致某一条或某几条数据凭空消失。嗯,这里要注意:缺失值不是简单地删掉就完事了。
核心原则:超短线策略对时序连续性要求极高。缺失值处理不当,会导致后续计算出的买卖信号出现偏移。
我个人习惯分三步走:
- 先定位缺失位置——检查时间戳是否连续。比如1秒内应该有4笔Tick,结果只有3笔,那中间就缺了。
- 判断缺失类型——是单点缺失还是连续缺失?单点缺失可以用前一笔填充;连续缺失超过3笔,我建议直接标记为无效区间。
- 选择填充方法——超短线场景下,我推荐用前向填充(ffill)。为什么?因为Tick数据是事件驱动的,最新一笔成交价就是当前市场认可的价格。
# 伪代码示例:Tick数据前向填充
import pandas as pd
def clean_tick_missing(df):
# 检查时间戳是否连续
df['time_diff'] = df['timestamp'].diff()
missing_mask = df['time_diff'] > pd.Timedelta(milliseconds=250)
# 前向填充价格和成交量
df['price'].fillna(method='ffill', inplace=True)
df['volume'].fillna(0, inplace=True) # 成交量缺失填0
return df
我的经验:曾经有个策略在回测时表现完美,实盘却一直亏。查了两天发现是数据填充方式不对——用了线性插值,结果把Tick数据平滑成了K线级别的走势,完全失真。从那以后,我所有Tick级别的填充只用ffill。
1.2 异常值检测:揪出那些离谱的报价
你想想看,一只股票平时在10块钱附近波动,突然冒出一笔成交价是100块。这明显是异常。为什么会这样?可能是交易所的错单,也可能是数据采集环节的bug。
我常用的检测方法有三种:
| 方法 | 适用场景 | 阈值设置 |
|---|---|---|
| 固定阈值法 | 价格波动范围已知 | ±5% 或 ±10% |
| 移动窗口法 | 日内波动剧烈 | 中位数 ± 3倍MAD |
| Z-score法 | 数据分布较稳定 | |Z| > 3 |
我个人最常用的是移动窗口法。因为Tick数据在开盘、收盘、盘中不同时段波动率差异很大,固定阈值容易误杀。
# 基于移动中位数的异常检测
def detect_outliers(df, window=20, threshold=3):
df['rolling_median'] = df['price'].rolling(window).median()
df['rolling_mad'] = (df['price'] - df['rolling_median']).abs().rolling(window).median()
# 标记异常
df['is_outlier'] = (
(df['price'] - df['rolling_median']).abs()
> threshold * df['rolling_mad']
)
return df[df['is_outlier'] == False]
避坑指南:我曾经在检测异常值时把阈值设得太宽松,结果把一些真实的闪崩数据当成了异常给剔除掉。后来复盘才发现,那些"异常"其实是市场情绪的极端反应。所以建议你:异常值先标记,不要直接删除。等做完策略回测,再决定哪些是真的异常。
1.3 时间对齐:让不同数据源步调一致
做超短线交易,你往往需要同时处理多个数据源——行情Tick、订单簿快照、甚至Level2数据。这些数据的时间戳可能来自不同的时钟源,精度也不一样。说白了,就是它们的时间轴对不上。
时间对齐的核心思路就一个:以最精确的时间戳为基准,其他数据向它靠拢。
- 精确到毫秒级:直接用时间戳做左连接,取最近的前一笔数据
- 精确到微秒级:需要做时间窗口匹配,比如±100微秒内的数据视为同一时刻
- 跨交易所数据:先统一时区,再对齐到同一个时钟源(比如用NTP时间)
# 时间对齐示例:将订单簿数据对齐到Tick时间戳
def align_to_tick(tick_df, orderbook_df):
# 将订单簿时间戳向上取整到最近的Tick时间
aligned = pd.merge_asof(
tick_df.sort_values('timestamp'),
orderbook_df.sort_values('timestamp'),
on='timestamp',
direction='backward', # 取Tick时刻之前最近的订单簿快照
tolerance=pd.Timedelta(milliseconds=1)
)
return aligned
我的习惯:时间对齐这一步,我建议你在数据入库时就做好。别等到策略运行时再对齐,那样延迟会很高。我一般会在ETL流程里加一个对齐模块,把不同源的数据统一成同一个时间轴。
1.4 数据重采样:从Tick到更高时间粒度
有时候我们不需要那么精细的Tick数据。比如做日内趋势策略,用1秒或5秒的K线就够了。这时候就需要重采样——把不规则的事件流,转换成规则的时间序列。
重采样的关键参数有三个:
- 时间窗口:1秒、5秒、10秒?取决于你的策略频率
- 聚合函数:价格用OHLC(开盘、最高、最低、收盘),成交量用求和
- 边界处理:窗口从整秒开始还是从第一个Tick开始?
# Tick数据重采样为1秒K线
def resample_to_second(df):
df.set_index('timestamp', inplace=True)
# 1秒重采样
ohlc = df['price'].resample('1S').ohlc()
volume = df['volume'].resample('1S').sum()
result = pd.concat([ohlc, volume], axis=1)
result.columns = ['open', 'high', 'low', 'close', 'volume']
return result
注意:重采样会丢失信息。你想想看,1秒内可能有10笔Tick,也可能只有1笔。重采样成1秒K线后,这10笔的微观结构就看不到了。所以重采样粒度越粗,信息损失越大。做超短线策略,我建议最多重采样到1秒或3秒,再粗就没意义了。
知识体系总览
下面这张图把Tick数据清洗的四个步骤串起来了。你可以看到,数据从原始状态到最终可用,每一步都有明确的输入输出。
这张图里,我特意把重采样做成了分支决策。因为不是所有策略都需要重采样。比如做高频做市商策略,你就要用原始Tick数据;做日内趋势策略,重采样到1秒K线就够了。这个选择,取决于你的策略逻辑。
公众号:蓝海资料掘金营,微信deep3321