4. 订单簿管理模块:订单簿深度维护、买卖盘口价差计算、订单簿不平衡度指标、订单簿快照与增量更新
做市商系统里,订单簿管理模块可以说是最核心的「心脏」了。我刚开始做量化交易那会儿,总觉得订单簿不就是一堆买单卖单排排队嘛,有什么难的?直到有一次实盘环境里,因为订单簿更新延迟了 200 毫秒,导致我们做市策略报了一堆废单……嗯,从那以后,我对这个模块的敬畏心就上来了。
说白了,订单簿管理模块要干四件事:维护深度、算价差、算不平衡度、处理快照与增量。咱们一个一个来拆解。
4.1 订单簿深度维护
订单簿深度,就是看某个价格水平上到底有多少挂单。我习惯用一个价格-数量映射表来维护,买盘和卖盘各一张表。
举个例子,BTC/USDT 的买盘可能是这样的:
| 价格 (USDT) | 数量 (BTC) | 累计深度 |
|---|---|---|
| 30000 | 1.5 | 1.5 |
| 29990 | 2.0 | 3.5 |
| 29980 | 0.8 | 4.3 |
这里有个坑:价格精度问题。不同交易所的价格精度不一样,有的支持小数点后两位,有的支持八位。我曾经因为精度没对齐,导致订单簿里同一个价格出现了两条记录,直接让价差计算崩了。
4.2 买卖盘口价差计算
价差(Spread)就是最优卖价和最优买价的差值。公式很简单:
spread = ask_price_1 - bid_price_1
但实际项目中,我一般会算三个指标:
- 绝对价差:就是上面那个差值,单位是 USDT 或 BTC
- 相对价差:绝对价差 / 中间价,用百分比表示
- 加权价差:考虑深度后的价差,比如前 5 档的平均价差
为什么要算加权价差?你想想看,如果最优档只有 0.001 个 BTC,那这个价差其实没什么参考价值。真正能成交的,往往是前几档的加权结果。
4.3 订单簿不平衡度指标
这个指标很有意思,它能告诉你当前市场的买卖力量对比。我常用的公式是:
imbalance = (bid_volume - ask_volume) / (bid_volume + ask_volume)
结果范围在 -1 到 1 之间:
- 接近 1:买盘远大于卖盘,市场看涨情绪浓
- 接近 -1:卖盘远大于买盘,市场看跌情绪浓
- 接近 0:买卖力量均衡,市场处于震荡
但这里要注意:不平衡度要看深度档位。只看最优档容易误判。我记得有一次,最优买盘只有 0.5 BTC,但第二档到第五档加起来有 50 BTC,这时候只看最优档就会得出「买盘弱」的错误结论。
所以我一般会算三个维度的不平衡度:
- 第 1 档不平衡度:反应最直接的买卖意愿
- 前 5 档不平衡度:反应短期流动性分布
- 全深度不平衡度:反应整体市场情绪
4.4 订单簿快照与增量更新
这是订单簿管理里最考验性能的部分。交易所通常提供两种数据:
- 快照(Snapshot):某一时刻的完整订单簿
- 增量(Incremental):订单簿的变化事件(新增、删除、修改)
我建议的架构是:以快照为基准,用增量做实时更新。具体流程如下:
1. 首次连接:获取全量快照,建立初始订单簿
2. 持续接收:增量事件流,逐条更新订单簿
3. 定期校验:每隔 N 秒或 N 条增量后,重新获取快照做对比
4. 异常恢复:发现不一致时,丢弃当前订单簿,重新拉取快照
这里有个关键点:增量事件的顺序不能乱。如果先处理了「删除」事件,再处理「新增」事件,但实际顺序是反的,那订单簿就乱套了。我一般会用队列加时间戳来保证顺序。
4.5 整体架构图
下面这张图是我自己项目里用的订单簿管理模块架构,你可以参考一下:
从架构图可以看出,数据从交易所进来后,先做预处理,然后交给核心引擎维护订单簿状态。三个计算模块并行工作,最后统一输出到监控、告警和策略系统。
4.6 代码示例:订单簿核心类
下面是一个简化版的订单簿管理类,包含了核心功能:
class OrderBook:
def __init__(self, symbol):
self.symbol = symbol
self.bids = {} # 买盘: {price: volume}
self.asks = {} # 卖盘: {price: volume}
self.last_snapshot_id = 0
def apply_snapshot(self, snapshot):
"""应用全量快照"""
self.bids = {item[0]: item[1] for item in snapshot['bids']}
self.asks = {item[0]: item[1] for item in snapshot['asks']}
self.last_snapshot_id = snapshot['id']
def apply_incremental(self, events):
"""应用增量事件"""
for event in events:
price = event['price']
volume = event['volume']
side = event['side'] # 'bid' or 'ask'
if volume == 0:
# 删除该价格档位
if side == 'bid':
self.bids.pop(price, None)
else:
self.asks.pop(price, None)
else:
# 新增或更新
if side == 'bid':
self.bids[price] = volume
else:
self.asks[price] = volume
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
def get_imbalance(self, levels=5):
"""计算不平衡度"""
bid_vol = sum(sorted(self.bids.values(), reverse=True)[:levels])
ask_vol = sum(sorted(self.asks.values())[:levels])
total = bid_vol + ask_vol
if total == 0:
return 0
return (bid_vol - ask_vol) / total
好了,订单簿管理模块的核心内容就这些。记住一句话:订单簿是市场的镜子,维护好这面镜子,你的做市策略才能看清路。