Tick级数据结构:Level1与Level2行情数据、Order Book的构建、Tick数据的存储格式
各位同学,咱们今天聊点实在的。
做高频交易,第一关就是搞懂Tick数据。说白了,Tick就是市场最原始的脉搏。你连心跳都摸不准,还谈什么交易策略?
我个人习惯把Tick数据比作「市场的DNA」。Level1是简化版,Level2是完整版。咱们一个一个拆开看。
Level1行情数据:市场的「快照」
Level1,也叫Top of Book。它只告诉你当前最优的买一和卖一价格和数量。
举个例子,你在交易软件上看到的「最新价:100.50,买一:100.48,卖一:100.52」,这就是Level1。
我在项目中遇到过不少新手,拿着Level1数据做高频策略,结果回测漂亮,实盘一跑就崩。为什么?因为Level1信息量太少了。它看不到市场的深度,看不到大单在哪个价位埋伏。
Level1的核心字段:
- 时间戳(精确到微秒或纳秒)
- 最新成交价
- 买一价 & 买一量
- 卖一价 & 卖一量
- 当日开盘价、最高价、最低价、收盘价
- 成交量、成交额
嗯,这里要注意。Level1的更新频率一般是每笔成交或每500毫秒一次。对于高频交易来说,这个粒度太粗了。你想想看,500毫秒够市场发生多少次变化?
Level2行情数据:市场的「CT扫描」
Level2就不一样了。它给你的是整个订单簿的深度信息。能看到买一到买N档,卖一到卖N档。国内A股一般是10档,美股可以到几十档。
Level2数据里,我最看重的是「逐笔成交」和「逐笔委托」。这两个东西,才是真正还原市场博弈过程的利器。
我的经验:Level2数据里有一个容易被忽略的字段——「委托序号」。它能帮你还原订单的先后顺序。我曾经靠这个字段,发现了一个机构拆单的规律,做出了一个不错的统计套利策略。
Level2的数据量有多大?我给你们算笔账:
| 数据类型 | 单条数据大小 | 日均条数(A股) | 日均数据量 |
|---|---|---|---|
| Level1快照 | ~200字节 | ~10万条 | ~20MB |
| Level2快照 | ~2KB | ~50万条 | ~1GB |
| Level2逐笔成交 | ~100字节 | ~500万条 | ~500MB |
| Level2逐笔委托 | ~80字节 | ~2000万条 | ~1.6GB |
看到没?Level2的数据量是Level1的几十倍。这就是为什么存储格式变得至关重要。
Order Book(订单簿)的构建:从数据流到实时状态
订单簿,说白了就是一个「价格-数量」的映射表。买盘按价格从高到低排,卖盘从低到高排。
构建订单簿的核心逻辑其实不复杂:
# 伪代码示意
class OrderBook:
def __init__(self):
self.bids = {} # 买盘:价格 -> 数量
self.asks = {} # 卖盘:价格 -> 数量
def update(self, price, quantity, side):
if quantity == 0:
# 撤单或成交完毕,删除该价位
del self.book[side][price]
else:
# 新增或修改
self.book[side][price] = quantity
def get_top(self, n=10):
# 获取前n档买卖盘
sorted_bids = sorted(self.bids.items(), reverse=True)[:n]
sorted_asks = sorted(self.asks.items())[:n]
return sorted_bids, sorted_asks
但实际项目中,坑比你想的多。我曾经踩过一个坑:不同交易所的订单簿更新机制不一样。有的是「增量更新」,只发变化的部分;有的是「全量快照」,每次发全部数据。
避坑指南:我曾经因为没处理好增量更新的时序问题,导致订单簿状态错乱,回测结果完全失真。后来我加了一个「快照校验」机制——每收到1000条增量后,强制拉一次全量快照做对比。这个习惯救了我很多次。
构建订单簿时,还有几个关键点:
- 时间同步:不同数据源的时间戳可能不同步,需要做对齐处理
- 乱序处理:网络延迟会导致数据包乱序到达,需要缓存排序
- 内存管理:高频场景下,订单簿对象需要复用,避免频繁GC
Tick数据的存储格式:Parquet vs Arrow
说到存储,这是很多人的盲区。觉得随便存个CSV就行。我告诉你,高频数据用CSV存,你连回测都跑不动。
目前业界主流就两个选择:Parquet和Arrow。我两个都用过,说说我的感受。
Parquet:列式存储的王者
Parquet是Hadoop生态出来的,专为分析型查询设计。它的核心优势是:
- 列式压缩:同一列的数据类型相同,压缩率极高。我实测过,Tick数据用Parquet存,体积只有CSV的1/5到1/10
- 谓词下推:查询时只读取需要的列和行,IO开销小
- Schema自描述:文件里自带字段定义,不用额外维护元数据
但Parquet也有缺点:写入速度慢,不适合实时写入。而且读取时需要反序列化,延迟在毫秒级。
Arrow:内存中的利器
Arrow是专门为「零拷贝」设计的。数据在内存里就是列式排布的,CPU可以直接访问,不需要序列化/反序列化。
Arrow的核心优势:
- 读取延迟在微秒级,比Parquet快两个数量级
- 支持向量化计算,配合NumPy/Pandas效率极高
- 跨语言共享内存,C++写的策略可以直接读Python写的数据
但Arrow是内存格式,持久化到磁盘时,其实还是需要转成Parquet或Feather。
我的建议是:
- 历史数据存储:用Parquet,压缩率高,查询方便
- 实时计算管道:用Arrow,低延迟,零拷贝
- 两者结合:磁盘存Parquet,加载到内存后转Arrow做计算
知识体系总览
下面这张图,是我自己梳理的Tick数据结构知识体系。你们可以保存下来,当个地图用。
这张图把今天讲的内容串起来了。从Level1到Level2,再到订单簿构建和存储格式,每一步都有坑,也都有技巧。
最后分享一个我的习惯:每次接入新的数据源,我都会先花一周时间做数据质量检查。检查时间戳是否连续、价格是否有跳变、买卖盘是否平衡。这些脏数据,才是策略最大的敌人。
好了,Tick数据结构这块,咱们今天就聊到这儿。记住一句话:数据是高频交易的基石,地基不牢,楼盖得再高也得塌。