高频交易基础:Tick数据与订单簿结构解析
做高频交易这些年,我最大的感触就是——数据是一切策略的根基。你想想看,如果连最原始的Tick数据和订单簿都搞不明白,后面那些花里胡哨的信号处理、订单流分析,说白了就是空中楼阁。
今天咱们就来啃这块硬骨头。我会把我在实战中踩过的坑、总结的经验,一股脑儿倒出来。
一、Tick数据:市场的呼吸
什么是Tick数据?每一次成交,就是一个Tick。它记录了市场最微观的变动。
一个标准的Tick数据长什么样?我直接贴个结构体:
// 我个人习惯用这个结构
struct TickData {
uint64_t timestamp; // 纳秒级时间戳
double price; // 成交价
uint64_t volume; // 成交量
char side; // 'B' 主动买,'S' 主动卖
uint64_t trade_id; // 成交编号
};
嗯,这里要注意——时间戳的精度。我在项目中遇到过,有些交易所给的是毫秒级,有些是微秒级,甚至纳秒级。如果你混着用,回测结果会偏差很大。
核心要点:Tick数据是高频交易的「原子单位」。所有高阶信号,都是从Tick里一层层提取出来的。
二、订单簿:市场的骨架
订单簿,说白了就是所有未成交订单的集合。它分为买盘(Bid)和卖盘(Ask),每一档都有价格和数量。
我习惯用「快照+增量」的方式来理解订单簿:
- 快照:某一时刻订单簿的全貌
- 增量:每次订单簿变化的事件(新增、删除、修改)
举个例子,一个典型的订单簿快照:
| 档位 | 买价 (Bid) | 买量 | 卖价 (Ask) | 卖量 |
|---|---|---|---|---|
| 1 | 100.50 | 1,200 | 100.55 | 800 |
| 2 | 100.45 | 2,300 | 100.60 | 1,500 |
| 3 | 100.40 | 1,800 | 100.65 | 2,100 |
看到没?买卖价差(Spread)就是100.55 - 100.50 = 0.05。这个值越小,说明市场流动性越好。
实战技巧:我曾经在回测时忽略了订单簿的「深度衰减」问题。就是当大单吃掉某一档后,下一档的价格会跳变。这个细节,直接影响了我的滑点模型。
三、Tick与订单簿的联动
Tick数据和订单簿不是孤立的。每一次Tick成交,都会导致订单簿发生变化。
举个例子:
- 一个主动买单(吃单)进来,成交了卖一档的800手
- 卖一档被吃光,卖二档变成新的卖一档
- 同时,这个Tick被记录:价格100.55,量800,方向'B'
为什么会这样?因为订单簿是「因」,Tick是「果」。订单簿的状态决定了成交的可能性,而Tick是成交后的结果。
四、核心知识体系
我把这一章的核心逻辑画了张图,方便你理解:
五、实战中的避坑指南
我直接说几个我踩过的坑:
坑1:时间戳对齐
我曾经在回测时,把不同交易所的Tick数据直接拼接。结果发现,A交易所的毫秒级时间戳和B交易所的微秒级时间戳,差了整整一个数量级。回测结果看起来很美,实盘一跑就崩。
解决方案:统一将所有时间戳转为纳秒级,并且做时钟同步校正。
坑2:订单簿的「幽灵订单」
有些做市商会挂单后立刻撤单,制造虚假深度。如果你直接拿订单簿数据做信号,很容易被误导。
解决方案:我习惯用「订单簿稳定性指标」来过滤,比如统计某一档位挂单的存活时间。
小技巧:处理Tick数据时,建议用内存映射文件(mmap)来加速读取。我试过,比普通文件读取快3-5倍。
六、代码实战:解析Tick数据
光说不练假把式。我写个简单的Python示例,演示如何解析Tick数据并更新订单簿:
class OrderBook:
def __init__(self):
self.bids = {} # 买盘 {价格: 数量}
self.asks = {} # 卖盘 {价格: 数量}
def update_by_tick(self, tick):
"""根据Tick更新订单簿"""
if tick.side == 'B': # 主动买单,吃掉卖盘
price = tick.price
vol = tick.volume
# 从卖盘中扣除成交量
if price in self.asks:
self.asks[price] -= vol
if self.asks[price] <= 0:
del self.asks[price]
elif tick.side == 'S': # 主动卖单,吃掉买盘
price = tick.price
vol = tick.volume
if price in self.bids:
self.bids[price] -= vol
if self.bids[price] <= 0:
del self.bids[price]
def get_spread(self):
"""计算买卖价差"""
if not self.bids or not self.asks:
return None
best_bid = max(self.bids.keys())
best_ask = min(self.asks.keys())
return best_ask - best_bid
这段代码虽然简单,但核心逻辑都在里面了。你想想看,每次Tick进来,订单簿都在动态变化。这就是高频交易最底层的「心跳」。
七、总结
这一章我们聊了:
- Tick数据的结构:时间戳、价格、成交量、方向
- 订单簿的结构:买盘、卖盘、快照、增量
- 两者的联动关系:Tick是果,订单簿是因
- 实战中的坑:时间戳对齐、幽灵订单
嗯,这些基础打牢了,后面讲高频统计信号、订单流分析,你才能跟得上。我个人觉得,理解数据比理解策略更重要。数据对了,策略才有意义。
公众号:蓝海资料掘金营,微信deep3321