Tick级数据与Order Book数据结构

做高频因子挖掘,第一步就是跟Tick数据和Order Book打交道。说实话,我刚入行那会儿,觉得这不就是一堆数字嘛,有什么好研究的?结果第一次实盘回测,策略在模拟环境跑得飞起,一上实盘就亏成狗。后来才发现,问题就出在数据理解上——你对Tick数据的字段含义理解不到位,Order Book的更新机制没吃透,策略再牛逼也是白搭。

今天我们就来聊聊这个基础但极其关键的话题。我会结合我自己的踩坑经历,把Tick数据的字段、Level2行情解析、Order Book的构建与更新,以及存储格式的选择,一次性讲清楚。

Tick数据的字段含义

Tick数据,说白了就是每一笔成交的“快照”。它记录的是市场在那一瞬间发生了什么。我个人习惯把Tick数据分成两类:成交Tick报价Tick。两者虽然都叫Tick,但含义完全不同。

成交Tick的典型字段包括:

  • 时间戳:精确到毫秒甚至微秒。注意,不同交易所的时间戳精度不一样,我遇到过因为时间戳精度不够导致回测结果偏差的情况。
  • 价格:成交价格。这里有个坑——有些交易所的成交价格是带小数点的,有些是整数,解析时一定要确认好精度。
  • 成交量:该笔成交的股数或合约数。
  • 成交额:价格×成交量,有些交易所直接提供,有些需要自己算。
  • 买卖方向:主动买还是主动卖。这个字段在Level2行情里通常叫“Side”或“Direction”。
  • 成交编号:唯一标识一笔成交的ID,用于去重。

报价Tick则记录的是买卖盘口的快照:

  • 买一价、买一量:最优买价及其挂单量。
  • 卖一价、卖一量:最优卖价及其挂单量。
  • 买二到买N、卖二到卖N:深度盘口数据,Level2行情通常提供10档或20档。
  • 总买量、总卖量:所有买盘和卖盘的挂单总量。

重点提醒:成交Tick和报价Tick的时间戳往往不是同步的。成交Tick记录的是成交发生的时间,报价Tick记录的是盘口变化的时间。两者混用时,一定要对齐时间轴,否则你的因子计算会出大问题。

Level2行情数据解析

Level2行情,说白了就是比普通行情更“深”的数据。普通行情只给你最优买卖盘口,Level2给你10档甚至20档的深度,还有逐笔成交、逐笔委托等详细信息。

我记得第一次解析Level2数据时,被它的数据格式搞晕了。不同交易所的Level2数据格式差异很大,但核心结构其实差不多:

// 一个典型的Level2行情数据包结构
{
  "timestamp": 1630000000000,  // 毫秒级时间戳
  "symbol": "000001.SZ",       // 股票代码
  "bid_prices": [10.01, 10.00, 9.99, ...],  // 买盘价格,从高到低
  "bid_volumes": [1000, 2000, 1500, ...],   // 对应买盘量
  "ask_prices": [10.02, 10.03, 10.04, ...], // 卖盘价格,从低到高
  "ask_volumes": [800, 1200, 1800, ...],    // 对应卖盘量
  "trades": [                                // 逐笔成交
    {"price": 10.01, "volume": 500, "side": "buy", "time": 1630000000001},
    ...
  ]
}

解析Level2数据时,有几个关键点要注意:

  • 数据对齐:盘口数据和成交数据的时间戳可能不同步,需要按时间戳排序后对齐。
  • 增量更新:很多交易所的Level2数据是增量推送的,不是全量快照。你需要维护一个本地Order Book,然后根据增量数据更新它。
  • 数据去重:同一笔成交可能被推送多次,需要根据成交编号去重。

我的经验:解析Level2数据时,建议先写一个数据校验模块。我曾经因为交易所改了数据格式没通知,导致解析出来的数据全是错的,回测结果完全不可用。加个校验模块,能帮你快速发现问题。

Order Book的构建与更新机制

Order Book,也就是订单簿,是高频因子挖掘的核心数据结构。它记录了当前市场上所有未成交的买卖订单。说白了,就是一张“挂单表”。

构建Order Book其实不复杂,但更新机制需要仔细设计。我常用的方法是:

  1. 初始化:用第一个全量快照数据初始化Order Book。
  2. 增量更新:后续每收到一条增量数据,就更新Order Book中对应的价格档位。
  3. 定时全量同步:每隔一段时间(比如1分钟),用全量快照重新同步一次,防止增量数据累积误差。

这里有个关键点:增量更新的顺序。如果增量数据到达的顺序乱了,Order Book就会出错。我遇到过因为网络延迟导致增量数据乱序,Order Book里的买卖盘口完全对不上。后来我加了一个序列号校验,确保增量数据按顺序处理。

// 一个简单的Order Book更新逻辑(伪代码)
class OrderBook:
    def __init__(self, snapshot):
        self.bids = snapshot['bids']  // 买盘,按价格降序
        self.asks = snapshot['asks']  // 卖盘,按价格升序
        self.seq_no = snapshot['seq_no']

    def update(self, delta):
        // 检查序列号,防止乱序
        if delta['seq_no'] <= self.seq_no:
            return  // 忽略旧数据
        self.seq_no = delta['seq_no']

        // 更新买盘
        for price, volume in delta['bid_changes']:
            if volume == 0:
                self.bids.pop(price, None)  // 撤单
            else:
                self.bids[price] = volume   // 新增或修改

        // 更新卖盘
        for price, volume in delta['ask_changes']:
            if volume == 0:
                self.asks.pop(price, None)
            else:
                self.asks[price] = volume

注意:Order Book的更新频率非常高,尤其是在开盘和收盘时段。如果处理速度跟不上,数据就会堆积。我建议用C++或Rust实现核心的Order Book逻辑,Python只做上层调用。或者用NumPy的向量化操作来加速。

数据存储格式选择:Parquet vs HDF5

Tick数据和Order Book数据量巨大,存储格式的选择直接影响后续的分析效率。我个人主要用两种格式:ParquetHDF5。两者各有优劣,我分别说说。

Parquet

  • 优点:列式存储,压缩率高,读取速度快。特别适合按列分析(比如只读取价格列)。
  • 缺点:写入速度相对较慢,不适合频繁追加数据。
  • 适用场景:批量处理、数据分析、机器学习特征工程。

HDF5

  • 优点:写入速度快,支持分层数据结构,适合存储大量小数据集。
  • 缺点:读取速度不如Parquet,压缩率一般。
  • 适用场景:实时数据存储、需要频繁追加数据的场景。
特性 Parquet HDF5
存储方式 列式 行式/分层
压缩率 高(通常50%-80%) 中(通常30%-50%)
读取速度 快(按列读取) 中(按行读取)
写入速度
适用场景 批量分析、特征工程 实时存储、频繁追加

我个人习惯是:原始Tick数据用HDF5存储,因为写入速度快,适合实时接收数据。等数据积累到一定量后,再转换成Parquet格式用于分析。这样既保证了实时性,又兼顾了分析效率。

避坑指南:我曾经把所有Tick数据都存成Parquet,结果每天写入数据要花好几个小时,根本来不及处理。后来改成先用HDF5缓存,再批量转Parquet,问题就解决了。你想想看,数据存储格式的选择,其实是在读写速度和存储效率之间做权衡。

知识体系结构图

下面这张图展示了本章的核心知识体系,从Tick数据到Order Book,再到存储格式,一目了然:

Tick级数据与Order Book知识体系 Tick数据 Level2行情 Order Book 字段含义 时间戳、价格、成交量 成交额、买卖方向 成交编号 数据解析 盘口深度(10档/20档) 逐笔成交、逐笔委托 增量更新、数据对齐 构建与更新 全量快照初始化 增量更新机制 序列号校验 数据存储格式选择 Parquet 列式存储、高压缩、适合分析 HDF5 行式存储、快写入、适合实时

这张图把本章的核心内容串起来了。从Tick数据出发,到Level2行情解析,再到Order Book的构建与更新,最后落到数据存储格式的选择。每个环节都环环相扣,缺一不可。

好了,关于Tick级数据与Order Book数据结构,我就讲这么多。记住,数据是高频因子挖掘的基石,这块搞不定,后面的因子计算、回测、实盘都是空中楼阁。希望我的经验能帮你少走一些弯路。