BinanceOrderBook
Maintains a live, local Binance SPOT order book for a configurable set
of currency pairs, per Binance's documented local-order-book-
maintenance algorithm (not linked here -- its status page returns
inconsistent HTTP status codes to automated checks; search Binance's
spot API docs for "How to manage a local order book correctly"):
bootstrap from a REST depth snapshot, then apply WS update-deltas
whose U (first update id) brackets the book's current
lastUpdateId, resyncing on any gap.
| Property | Value |
|---|---|
| Project | Virtufin.Worker.BinanceOrderBook |
| Base class | Virtufin.Worker.Host.WorkerBase<JsonNode, JsonObject> |
| Engine | application/x-dotnet-dll (Managed only) |
One instance, many pairs
One worker instance tracks a configurable set of pairs (default: every
pair it sees) rather than one instance per pair -- one-worker-per-pair
was judged operationally heavy for Binance spot's 50+ actively-traded
pairs. Internally, a ConcurrentDictionary<string, L2OrderBook<DecimalAmount,
DecimalAmount>> (from Virtufin.Base.OrderBooks) holds one book per pair,
with Level2OrderBookUpdater applying snapshots and diffs.
The worker subscribes to three flat, un-suffixed topics --
act.exchange.binance.orderbook.update, .snapshot, and the consolidated
act.exchange.binance.instrument.snapshot -- rather than the pubsub-topics
spec's per-pair pattern for these input topics.
The pair for an update-delta event is parsed from Binance's own
combined-stream envelope (payload.stream); the pair for a snapshot
event is read from the CloudEvent's currencypair extension
attribute (Binance's raw REST response has no symbol field of its
own). Prices and sizes are normalized to the instrument's tick/lot
precision from instrument.info; a pair's book events are skipped until
that metadata arrives. The worker's output -- state key,
changed/gap topics -- stays genuinely per-pair, matching the spec.
Snapshot delivery
This worker doesn't call Binance's REST snapshot endpoint itself.
HttpGetWorker (workers/HttpGetWorker) is deployed once
per tracked pair (Binance's depth endpoint is single-symbol), with
config.currencypair=<PAIR>, config.publishtopic=act.exchange.binance.orderbook.snapshot
and config.venue=binance (HttpGetWorker 0.4.0+ stamps the market-data
envelope from venue), and a
platform Trigger (Dapr Jobs, the same primitive the TUI's Triggers
page exposes) fires periodically to invoke it. Post-gap recovery
latency is therefore bounded by the polling interval (default @every
30s), not instant -- a deliberate tradeoff paired with dropping (not
buffering) updates received while the book isn't Live.
State
| Key | act\|hyp.<name>.exchange.<venue>.orderbook.<pair> |
| Value | { bids, asks, last_update_id, updated_at } |
Events published
| Action | Topic | Payload |
|---|---|---|
| Book updated | ...orderbook.changed.<PAIR> |
the book as a FlatBuffers image (application/x-virtufin-orderbook+flatbuffers, dataschema urn:virtufin:marketdata:orderbook:v1), up to depthlevels levels per side, best first -- the source of truth for that update |
| Gap detected | ...orderbook.gap.<PAIR> |
{ pair, expected, got } |
Configuration
| Key | Default | Purpose |
|---|---|---|
currencypairs |
required | Comma-separated canonical pairs, e.g. BTC_USDT,ETH_USDT (Binance's BTCUSDT cannot be split on its own). |
saveintervalms |
500 |
Minimum ms between two saves of one pair's book, newest book written once it has passed; 0 writes as fast as saves complete. |
publishintervalms |
0 |
Least ms between two changed events of one pair; 0 announces every applied diff. Latest wins: updates inside the interval are applied but not announced, and the next event carries the newest book. Snapshots are always announced. |
depthlevels |
20 |
Levels per side in the changed image and the saved book (both come from the same image); 0 keeps every level. |
pairconfig |
-- | Per-pair overrides of the two keys above as a JSON object, e.g. {"BTC_USDT":{"depthlevels":50}}; a pair's entry wins, omitted keys fall back to the worker-wide value. |
venue |
binance |
Multi-venue future-proofing. |
world |
act |
act or hyp.<name>. |
stateservice |
workmanager |
SaveStateRequest.service. |
apihost |
host process's gateway | Gateway host:port for SaveState; unset uses VIRTUFIN_API_HOST/VIRTUFIN_API_GRPC_PORT. |
marketuniverse |
ALL |
marketuniverse envelope extension. |
clocktype |
wall for act, else historical |
clocktype envelope extension. |
See the repo README for the full deploy sequence and known-gaps list.
All topics, keys and subjects come from the naming library: the virtufin.venue.binance fragment of this repo on top of virtufin.market. See Naming; the pair in every payload, topic and key is canonical (BTC_USDT).