3. 订单簿数据结构:订单簿的构成、买卖盘口、深度图、Level1/Level2数据
好,咱们今天聊聊订单簿。这东西,说白了就是交易所的「记账本」。
我刚开始做交易系统那会儿,以为订单簿就是个简单的价格列表。后来被坑过一次——某次行情爆发,我的订单簿更新慢了,导致撮合结果全错。嗯,从那以后,我对订单簿的数据结构就格外上心。
3.1 订单簿的构成
订单簿,英文叫 Order Book。它记录着所有未成交的买卖委托。
我个人习惯把它拆成三部分:
- 买盘(Bid Side):所有想买入的人,按价格从高到低排列
- 卖盘(Ask Side):所有想卖出的人,按价格从低到高排列
- 盘口(Order Book Depth):买卖双方在每个价位上的挂单量
你想想看,交易所每秒要处理几万笔订单。如果每次都用数组遍历,那性能肯定崩。所以,订单簿的底层数据结构很关键。
核心要点:订单簿必须支持高频的增、删、改、查操作。我建议用「价格-数量」的映射结构,比如红黑树或跳表。
3.2 买卖盘口
盘口,就是当前市场上最活跃的买卖价位。
举个例子:
| 卖盘(Ask) | 价格 | 买盘(Bid) |
|---|---|---|
| 200 手 | 10.05 | |
| 150 手 | 10.04 | |
| 10.03 | 100 手 | |
| 10.02 | 300 手 | |
| 10.01 | 250 手 |
这里有个关键概念——最优买卖价:
- 最优买价(Best Bid):10.03,100 手
- 最优卖价(Best Ask):10.04,150 手
- 买卖价差(Spread):10.04 - 10.03 = 0.01
我曾经在开发中遇到过一个坑:盘口数据更新时,如果只更新价格不更新数量,会导致撮合逻辑误判。所以,每次盘口变动,必须同时校验价格和数量。
避坑指南:我曾经因为盘口数据延迟,导致一个高频策略在价差为0时误判为「无套利机会」。后来加了时间戳校验,才解决这个问题。
3.3 深度图
深度图,就是把盘口数据可视化。横轴是价格,纵轴是累计挂单量。
我个人觉得,深度图是交易员最直观的工具。它能告诉你:
- 在某个价位上,有多少流动性
- 大单砸下去,价格会滑多少
- 市场深度够不够支撑你的交易
下面我用 SVG 画一个简单的深度图结构:
为什么会这样画?因为深度图本质上就是「价格-累计量」的阶梯函数。买盘从左到右价格递增,卖盘从右到左价格递减,中间的交汇点就是当前市价。
实战技巧:我建议你在做深度图时,不要只显示前10档。有些交易所提供全量深度,但全量数据量太大。我一般取前50档,既能看清趋势,又不会太卡。
3.4 Level1 / Level2 数据
这两个概念,很多新手容易搞混。我简单说清楚:
| 数据级别 | 包含内容 | 更新频率 | 典型用途 |
|---|---|---|---|
| Level 1 | 最优买卖价、最新成交价、成交量 | 秒级 | 散户看盘、简单策略 |
| Level 2 | 多档盘口(通常10-50档)、逐笔成交 | 毫秒级 | 高频交易、做市商 |
说白了,Level 1 就是「精简版」,Level 2 是「完整版」。
我记得有一次,一个客户问我:「为什么我的 Level 1 数据和 Level 2 数据对不上?」
原因很简单:Level 1 是快照数据,Level 2 是增量数据。快照和增量之间有时间差,所以会有短暂的不一致。这是正常现象。
核心要点:如果你做高频策略,必须用 Level 2 数据。Level 1 的延迟太高,等你看到最优价时,可能已经成交了。
3.5 代码示例:订单簿的简单实现
下面我用 Python 写一个极简的订单簿。注意,这只是教学用途,生产环境要复杂得多。
class OrderBook:
def __init__(self):
# 买盘:价格从高到低
self.bids = {} # {price: volume}
# 卖盘:价格从低到高
self.asks = {} # {price: volume}
def add_order(self, side, price, volume):
if side == 'buy':
self.bids[price] = self.bids.get(price, 0) + volume
else:
self.asks[price] = self.asks.get(price, 0) + volume
def get_best_bid(self):
if not self.bids:
return None
return max(self.bids.keys())
def get_best_ask(self):
if not self.asks:
return None
return min(self.asks.keys())
def get_spread(self):
best_bid = self.get_best_bid()
best_ask = self.get_best_ask()
if best_bid and best_ask:
return best_ask - best_bid
return None
这段代码虽然简单,但已经包含了订单簿的核心逻辑。你想想看,实际生产环境里,我们还要处理:
- 订单的撤销和修改
- 多线程并发访问
- 内存和性能优化
- 数据持久化
避坑指南:我曾经在生产环境中直接用 Python 字典做订单簿,结果在高并发下出现了哈希冲突,导致数据丢失。后来改用 C++ 的跳表实现,才解决问题。
3.6 总结
订单簿数据结构,是交易系统的核心。我个人的经验是:
- 理解买卖盘口的构成,是基础中的基础
- 深度图能帮你快速判断市场流动性
- Level 1 和 Level 2 数据各有用途,别混用
- 代码实现时,注意性能和并发安全
嗯,今天就聊到这儿。记住,订单簿不是死的,它是市场情绪的实时反映。你读懂了它,就读懂了市场的一半。