Skip to main content
Version: Next

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

  1. Sequence Continuity: Ensure U = lastUpdateId + 1, otherwise need re-sync
  2. Quantity is 0: Indicates deletion of that price level
  3. Concurrency Control: Use queue to ensure incremental messages are processed in order
  4. Reconnection: After reconnection, need to execute complete sync process again