Local Order Book Sync
This system provides two depth channels that can be used together for efficient local order book maintenance:
depth@{symbol},{level}: Snapshot stream (200 ms throttled)depth_update@{symbol}: Incremental stream (100 ms throttled)GET /fapi/v1/depth?symbol=X&with_id=true: REST depth snapshot
Option 1: Snapshot Only (Simple Mode)
- Subscribe to
depth@{symbol},20 - Replace the local order book every time a snapshot is received
- No incremental synchronization logic is required
Limitation: 200 ms throttling and limited depth levels mean intermediate changes may be missed.
Option 2: Snapshot + Delta (Accurate Mode)
Initialization Phase
Step 1: Subscribe to depth_update@{symbol} and start buffering delta events (do not apply yet)
Step 2: Obtain a snapshot using either of the following:
a) Subscribe to depth@{symbol},{level} and wait for the first push
b) Call REST GET /fapi/v1/depth?symbol={symbol}&with_id=true
Step 3: Record the snapshot lastUpdateId (field `u` or `id`)
Step 4: Initialize local ordered bids / asks with the snapshot data
Synchronization Phase
Step 5: Find the first buffered event satisfying U <= lastUpdateId+1 && u >= lastUpdateId+1
Discard all buffered events before it
Step 6: Apply this event and every subsequent delta event:
- quantity > 0 -> insert or update
- quantity = 0 -> delete
- update local lastUpdateId = event.u
Step 7: Validate continuity:
- U > local lastUpdateId+1 -> gap detected, reinitialize
- u <= local lastUpdateId -> stale event, discard
- otherwise apply normally
Exception Handling
| Scenario | Handling |
|---|---|
| WS reconnect | Re-run full initialization (Steps 1-4) |
Gap in delta events (U > lastUpdateId + 1) | Fetch a new snapshot and reinitialize |
| No delta push for a long time | Periodically verify with REST snapshot |
| Snapshot fetch failure | Retry, or downgrade to Option 1 |
Sequence Diagram
Client Server
| |
|-- subscribe depth_update@X --->| Step 1
|-- GET /fapi/v1/depth?with_id ->| Step 2
|<-------- snapshot (id=100) ----| Step 3: lastUpdateId=100
|<-- depth_update U=99,u=101 ----| first valid event, apply
|<-- depth_update U=101,u=103 ---| apply
|<-- depth_update U=200,u=201 ---| gap detected, reinitialize
Complete Example
class OrderBook {
constructor(symbol) {
this.symbol = symbol;
this.bids = new Map();
this.asks = new Map();
this.lastUpdateId = 0;
this.buffer = [];
this.ready = false;
}
async init(ws) {
this.ready = false;
this.buffer = [];
ws.subscribe(`depth_update@${this.symbol}`, (data) => {
if (!this.ready) { this.buffer.push(data); return; }
this.applyUpdate(data);
});
const resp = await fetch(`/fapi/v1/depth?symbol=${this.symbol}&with_id=true`);
const snapshot = (await resp.json()).data;
this.bids.clear(); this.asks.clear();
for (const [p, q] of snapshot.bids) this.bids.set(p, q);
for (const [p, q] of snapshot.asks) this.asks.set(p, q);
this.lastUpdateId = snapshot.id;
for (const event of this.buffer) {
if (event.u <= this.lastUpdateId) continue;
if (event.U > this.lastUpdateId + 1) return this.init(ws);
this.applyUpdate(event);
}
this.buffer = [];
this.ready = true;
}
applyUpdate(event) {
if (event.u <= this.lastUpdateId) return;
if (event.U > this.lastUpdateId + 1) {
this.ready = false;
return;
}
for (const [p, q] of event.b) {
if (q === '0') this.bids.delete(p); else this.bids.set(p, q);
}
for (const [p, q] of event.a) {
if (q === '0') this.asks.delete(p); else this.asks.set(p, q);
}
this.lastUpdateId = event.u;
}
}