21、订单流与高频交易:Tick级订单流数据的处理与策略
说实话,很多做订单流的朋友都问过我一个问题:Tick级数据到底有什么用?
我个人的看法是,如果你只做日线级别的交易,那Tick数据确实有点大材小用。但如果你想把订单流做到极致,想捕捉那些机构资金在毫秒级别内的动作——那Tick级数据就是你的显微镜。
这一章,我们就来聊聊Tick级订单流数据的处理,以及怎么用它构建高频策略。
什么是Tick级数据?
先明确一个概念。Tick数据,就是每一笔成交的记录。每一笔交易,无论大小,都会生成一条Tick。
举个例子,某只股票在10:00:00.123这个时间点成交了100股,价格是50.01元。这就是一条Tick。
而订单流数据,是在Tick数据的基础上,进一步拆解了买卖方向。也就是我们常说的「逐笔成交」数据。
核心区别:
- Tick数据:记录每一笔成交的价格、数量、时间
- 订单流数据:在Tick基础上,标记每一笔是主动买还是主动卖
说白了,Tick数据告诉你「发生了什么」,订单流数据告诉你「谁在推动价格」。
Tick级数据的处理难点
我在项目中遇到过最头疼的问题,就是数据量太大。
一只活跃的股票,一天能产生几十万条Tick。如果是期货或者加密货币,这个数字能到几百万。你想想看,如果同时监控几十个品种,数据量直接爆炸。
处理Tick数据,有几个绕不开的坎:
- 时间对齐:不同交易所的时间戳精度不一样,有的精确到毫秒,有的精确到微秒
- 数据清洗:经常会有重复数据、错误数据、延迟数据
- 存储压力:一天的数据可能就几个G,长期存储是个大问题
- 计算效率:在Tick级别做计算,普通循环根本跑不动
避坑指南:我曾经因为没处理好时间对齐,导致策略在回测时表现完美,实盘却一塌糊涂。后来才发现,是不同数据源的时间戳差了2毫秒。2毫秒,在高频交易里就是天壤之别。
Tick级订单流的处理流程
我一般把处理流程分成三步:
第一步:数据清洗与标准化
原始数据拿过来,先做三件事:
- 去重:删除完全相同的重复Tick
- 排序:按时间戳严格排序
- 标记方向:根据成交价格与买卖盘口的对比,标记主动买/主动卖
import pandas as pd
import numpy as np
def clean_tick_data(df):
"""
清洗Tick级订单流数据
"""
# 去重
df = df.drop_duplicates(subset=['timestamp', 'price', 'volume'])
# 排序
df = df.sort_values('timestamp').reset_index(drop=True)
# 标记买卖方向
# 假设我们有买卖盘口数据
df['side'] = np.where(df['price'] >= df['ask_price'], 'buy',
np.where(df['price'] <= df['bid_price'], 'sell', 'mid'))
return df
第二步:聚合与降采样
原始Tick数据太密了,直接分析会疯掉。我习惯把它聚合到更粗的时间粒度上。
常用的聚合方式:
| 聚合粒度 | 适用场景 | 数据量 |
|---|---|---|
| 1秒 | 高频策略、做市 | 中等 |
| 100毫秒 | 超高频策略 | 较大 |
| 10毫秒 | 极高频、套利 | 非常大 |
def aggregate_tick(df, freq='1S'):
"""
将Tick数据聚合到指定频率
"""
# 按时间分组
grouped = df.groupby(pd.Grouper(key='timestamp', freq=freq))
# 计算聚合指标
result = grouped.agg({
'price': ['first', 'last', 'max', 'min', 'mean'],
'volume': 'sum',
'side': lambda x: (x == 'buy').sum() # 主动买次数
})
return result
第三步:特征工程
聚合完之后,就可以提取特征了。我常用的Tick级特征有:
- 买卖压力比:主动买成交量 / 主动卖成交量
- Tick成交量分布:大单占比、小单占比
- 价格冲击系数:每单位成交量引起的价格变化
- 订单流不平衡度:买卖订单的净差值
个人经验:买卖压力比这个指标,我用了好几年。当这个比值突然飙升到3以上,同时价格还在低位,那大概率是机构在吃货。这时候跟着进场,胜率很高。
基于Tick级订单流的高频策略
策略这块,我分享一个我自己实盘跑过的思路。
核心逻辑很简单:捕捉大单的痕迹。
机构资金进场,不可能悄无声息。他们会在Tick级数据上留下痕迹——比如连续几笔大额主动买单,或者买卖盘口的快速变化。
策略框架
class TickOrderFlowStrategy:
def __init__(self, window=100, threshold=2.5):
self.window = window # 观察窗口
self.threshold = threshold # 触发阈值
def on_tick(self, tick):
# 更新订单流数据
self.update_orderflow(tick)
# 计算买卖压力比
pressure_ratio = self.calc_pressure_ratio()
# 判断是否触发信号
if pressure_ratio > self.threshold:
# 检查价格位置
if self.is_at_support():
return 'buy'
return None
def calc_pressure_ratio(self):
"""计算最近window笔Tick的买卖压力比"""
recent_ticks = self.ticks[-self.window:]
buy_vol = sum(t['volume'] for t in recent_ticks if t['side'] == 'buy')
sell_vol = sum(t['volume'] for t in recent_ticks if t['side'] == 'sell')
if sell_vol == 0:
return float('inf')
return buy_vol / sell_vol
策略的注意事项
嗯,这里要注意几个点:
- 滑点控制:高频策略对滑点极其敏感,0.1个tick的滑点就能吃掉所有利润
- 延迟问题:从数据接收到策略执行,延迟必须控制在毫秒级
- 容量限制:Tick级策略通常容量有限,别想着用大资金去跑
血的教训:我曾经写过一个Tick级策略,回测年化收益200%。结果实盘第一天就亏了5%。后来排查发现,是回测时没考虑交易所的撮合延迟。真实市场里,你的订单根本抢不到那个价格。
知识体系总览
下面这张图,是我对Tick级订单流处理与策略的总结:
写在最后
Tick级订单流处理,说白了就是跟时间赛跑。数据量大、计算复杂、延迟敏感,每一个环节都可能成为瓶颈。
但我个人觉得,这恰恰是它的魅力所在。当你真正掌握了Tick级数据的处理能力,你会发现市场在你面前变得透明了——那些大资金的动向,就像黑夜里的萤火虫一样明显。
当然,这条路不好走。我见过太多人,数据还没处理好就急着上策略,结果亏得一塌糊涂。我的建议是:先把数据处理这关过了,再谈策略。