本地订单簿维护
通过 WebSocket 增量更新维护本地订单簿,实现低延迟的深度数据访问。
核心概念
| 字段 | 说明 |
|---|---|
U | 首个更新ID (first_update_id) - 本次推送包含的第一个更新 |
u | 末尾更新ID (last_update_id) - 本次推送包含的最后一个更新 |
id | REST API 快照返回的序列号(需要 with_id=true) |
同步流程
- 订阅增量更新:订阅
{symbol}@order_book_update - 缓存消息:在获取快照前,缓存收到的所有增量消息
- 获取快照:调用 REST API 获取订单簿快照(带序列号)
GET /api/v1/futures/open-api/orderbook/BTCUSDT?depth=100&with_id=true
- 处理缓存:
丢弃
u < id + 1的消息(已过期) 找到U <= id + 1 && u >= id + 1的消息开始同步 如果U > id + 1说明快照落后,需要重新获取 - 持续更新:检查每条消息
U <= lastProcessedId + 1,否则重建
更新规则
- 数量为
"0"表示删除该价格档位 - 数量非零表示更新或新增该价格档位
- 买单 (b) 按价格从高到低排序
- 卖单 (a) 按价格从低到高排序
伪代码示例
// 1. 建立 WebSocket 连接并订阅
ws.send(JSON.stringify({
action: 'subscribe',
streams: ['BTCUSDT@order_book_update']
}));
// 2. 缓存收到的增量消息
const buffer = [];
ws.onmessage = (msg) => {
const data = JSON.parse(msg.data);
if (data.action === 'order_book_update') {
buffer.push(data.result);
}
};
// 3. 获取 REST API 快照(带序列号)
const snapshot = await fetch('/api/v1/futures/open-api/orderbook/BTCUSDT?depth=100&with_id=true');
const { bids, asks, id: snapshotId } = snapshot.data;
// 4. 初始化本地订单簿
const orderbook = {
bids: new Map(bids.map(([p, q]) => [p, q])),
asks: new Map(asks.map(([p, q]) => [p, q])),
id: snapshotId
};
// 5. 处理缓存的消息
for (const update of buffer) {
if (update.u < snapshotId + 1) continue;
if (update.U > snapshotId + 1) {
rebuildOrderbook();
break;
}
applyUpdate(update);
}
// 6. 应用增量更新
function applyUpdate(update) {
for (const [price, qty] of update.b) {
if (qty === '0') orderbook.bids.delete(price);
else orderbook.bids.set(price, qty);
}
for (const [price, qty] of update.a) {
if (qty === '0') orderbook.asks.delete(price);
else orderbook.asks.set(price, qty);
}
orderbook.id = update.u;
}
错误处理
| 错误场景 | 处理方式 |
|---|---|
| WebSocket 断开 | 自动重连,重新订阅,重建订单簿 |
序列号不连续 (U > id + 1) | 重新获取快照,重建订单簿 |
| 快照落后 | 重新获取快照 |
注意事项
- 增量推送频率:100ms 聚合一次
- 价格和数量为字符串类型,避免浮点精度问题
- 建议定期校验本地订单簿与 REST API 快照的一致性