第四章 订单簿数据结构:市场脉搏的数字化映射
做市引擎的核心,说白了就是跟订单簿打交道。你想想看,整个市场的买卖意愿,都浓缩在这张「价格-数量」的表格里。我个人习惯把订单簿比作市场的「心电图」——每一笔订单的进入、撤单、成交,都是市场的一次心跳。
4.1 订单簿的构成:bids 与 asks
订单簿分两边:买方和卖方。买方出价叫 bid,卖方要价叫 ask。这俩词儿你得刻在脑子里,因为后面所有的策略逻辑都绕不开它们。
举个例子,比特币当前市场是这样的:
asks (卖单):
价格: 50002.0 数量: 1.5
价格: 50001.5 数量: 2.0
价格: 50001.0 数量: 0.8
bids (买单):
价格: 50000.5 数量: 3.2
价格: 50000.0 数量: 1.0
价格: 49999.5 数量: 2.5
看到没?asks 从低到高排列,bids 从高到低排列。中间那个 gap 就是「买卖价差」,做市商赚的就是这个差价。我在项目中遇到过新手把 bids 和 asks 搞反,结果策略直接反向交易,亏了不少钱。嗯,这里要注意:bids 是别人愿意出的最高价,asks 是别人愿意卖的最低价。
4.2 价格-数量阶梯:市场的深度画像
光看最优报价是不够的。你得知道每个价位上有多少「弹药」。这就是价格-数量阶梯,也叫市场深度。
我习惯用这样的数据结构来表示:
class OrderBookLevel:
def __init__(self, price: float, size: float, order_count: int):
self.price = price # 价格
self.size = size # 该价位总数量
self.count = order_count # 该价位订单数
class OrderBook:
def __init__(self):
self.bids = [] # 按价格降序排列
self.asks = [] # 按价格升序排列
self.update_time = 0
为什么要有 order_count?我在做流动性分析时发现,同样 100 个比特币,如果分散在 50 个订单里,说明这个价位是散户堆出来的,容易被击穿;如果只有 1 个大单,那可能是机构在护盘。这个细节,很多教材不会讲。
核心指标:
- 价差 (spread) = ask[0].price - bid[0].price
- 中间价 (mid price) = (ask[0].price + bid[0].price) / 2
- 深度 (depth) = 某个价格区间内的累计数量
4.3 增量更新 vs 全量快照
这是订单簿数据流里最关键的决策点。你想想看,如果每次变化都把整个订单簿发过来,那网络带宽和 CPU 都得炸。所以交易所通常提供两种模式:
| 特性 | 全量快照 | 增量更新 |
|---|---|---|
| 数据量 | 大(整个订单簿) | 小(仅变化部分) |
| 实时性 | 低(通常几秒一次) | 高(每笔变化都推送) |
| 可靠性 | 高(完整状态) | 低(可能丢包导致状态不一致) |
| 使用场景 | 初始化、状态恢复 | 日常实时更新 |
实际项目中,我通常的做法是:
- 启动时先拉一次全量快照,建立基准状态
- 之后订阅增量更新流,逐笔应用变化
- 每隔 N 秒或 N 笔更新后,再拉一次快照做校验
我曾经踩过的坑:有一次增量更新丢了一笔,导致订单簿里凭空多出来一个价位。做市策略以为出现了套利机会,疯狂下单,结果全被吃掉。从那以后,我强制要求每 100 毫秒做一次快照校验,发现不一致立即重建。
增量更新的数据格式通常长这样:
{
"type": "snapshot", // 或 "update"
"bids": [["50000.5", "3.2"], ["50000.0", "1.0"]],
"asks": [["50001.0", "0.8"], ["50001.5", "2.0"]]
}
注意:增量更新里,如果某个价位的数量为 0,表示这个价位被删除了。这个约定很重要,我见过有人忘记处理这个逻辑,导致订单簿越积越大。
4.4 订单簿的本地维护
在内存里维护订单簿,我推荐用红黑树或者跳表。Python 里可以用 sortedcontainers 库,但做高频交易时建议自己实现一个简单的跳表,因为第三方库的 GC 开销不可控。
核心操作就三个:
- 添加/更新:某个价位上增加或减少数量
- 删除:某个价位数量归零时移除
- 查询:获取最优报价、某个价位的深度等
我自己的实现里,还会维护一个「累计深度」的缓存,这样计算某个价格区间的总数量时,时间复杂度能从 O(n) 降到 O(log n)。
小技巧:做市策略里,别每次都遍历整个订单簿。维护一个「最近活跃价位」的缓存,大部分时候市场只在最优报价附近波动,这样能省不少 CPU。
4.5 知识体系总览
下面这张图,是我对订单簿数据结构的理解框架。你可以把它当作本章的「思维导图」:
嗯,看到这张图你应该能感受到,订单簿不只是简单的价格列表。它背后涉及数据结构的选型、网络协议的取舍、以及状态一致性的保障。我个人觉得,能把订单簿维护好,做市引擎就成功了一半。
最后说一句:别小看订单簿的本地维护。我见过不少团队,策略逻辑写得花里胡哨,结果订单簿更新慢了 1 毫秒,整个策略就废了。记住,做市是毫秒级的博弈,数据结构的效率直接决定你的竞争力。
公众号:蓝海资料掘金营,微信deep3321