Order Book Local Maintenance
Complete guide on how to maintain a local order book using incremental updates.
Core Synchronization Flow
1. Subscribe to Incremental Updates
{
"action": "subscribe",
"streams": ["{symbol}@order_book_update"]
}
2. Cache Incremental Messages
Cache all received incremental update messages before fetching snapshot.
3. Get Initial Snapshot
Call REST API to get order book snapshot:
GET /api/v1/stock/open-api/depth?symbol=XSM&limit=100
The lastUpdateId in the response is used for subsequent synchronization.
4. Process Cached Messages
Discard all incremental messages where u < lastUpdateId + 1.
5. Continuously Apply Increments
Apply incremental updates sequentially:
- If price level exists, update quantity
- If quantity is
"0", delete that price level - If price level doesn't exist and quantity is not
"0", add new level
Example Code
class OrderBookManager {
constructor(symbol) {
this.symbol = symbol;
this.bids = new Map(); // price -> quantity
this.asks = new Map();
this.lastUpdateId = 0;
this.buffer = []; // Cache incremental messages
}
// 1. Subscribe to incremental
subscribe() {
this.ws.send(JSON.stringify({
action: 'subscribe',
streams: [`${this.symbol}@order_book_update`]
}));
}
// 2. Receive increment, cache first
onMessage(data) {
if (data.action === 'order_book_update') {
if (!this.lastUpdateId) {
this.buffer.push(data.result);
} else {
this.processUpdate(data.result);
}
}
}
// 3. Get snapshot and initialize
async initSnapshot() {
const snapshot = await fetch(
`/api/v1/stock/open-api/depth?symbol=${this.symbol}&limit=100`
).then(r => r.json());
this.lastUpdateId = snapshot.data.lastUpdateId;
// Initialize order book
snapshot.data.bids.forEach(([price, qty]) => {
this.bids.set(price, qty);
});
snapshot.data.asks.forEach(([price, qty]) => {
this.asks.set(price, qty);
});
// 4. Process cached messages
this.buffer = this.buffer.filter(msg => msg.u >= this.lastUpdateId + 1);
this.buffer.forEach(msg => this.processUpdate(msg));
this.buffer = [];
}
// 5. Apply incremental updates
processUpdate(update) {
// Verify continuity
if (update.U !== this.lastUpdateId + 1) {
console.error('Sequence gap, need re-sync');
return;
}
// Update bids
update.b.forEach(([price, qty]) => {
if (qty === '0') {
this.bids.delete(price);
} else {
this.bids.set(price, qty);
}
});
// Update asks
update.a.forEach(([price, qty]) => {
if (qty === '0') {
this.asks.delete(price);
} else {
this.asks.set(price, qty);
}
});
this.lastUpdateId = update.u;
}
}
Important Notes
- Sequence Continuity: Ensure
U = lastUpdateId + 1, otherwise need re-sync - Quantity is 0: Indicates deletion of that price level
- Concurrency Control: Use queue to ensure incremental messages are processed in order
- Reconnection: After reconnection, need to execute complete sync process again