Skip to content

Latest commit

 

History

History
149 lines (119 loc) · 4.96 KB

order_books.md

File metadata and controls

149 lines (119 loc) · 4.96 KB

Order Books

MoonProto keeps an applied current order book for every subscribed market. The application subscribes by market name; the library handles server indexes, out-of-order diffs, full-book recovery, reconnect replay, and the retained current-book state.

Subscribe

client.streams().subscribe_orderbook("BTCUSDT")?;
client.streams().subscribe_orderbooks(["ETHUSDT", "SOLUSDT"])?;
client.streams().unsubscribe_orderbook("ETHUSDT")?;
client.streams().unsubscribe_orderbooks(["SOLUSDT"])?;
client.streams().unsubscribe_all_orderbooks()?;

Subscriptions are stored in the reconnect registry. Before Init, these calls only update the registry. After Init, changed subscriptions also queue the server request. Reconnect replays the registry automatically after fresh market indexes are known.

The server subscription is per market name. OrderBookKind is not part of the subscribe request; it identifies incoming futures/spot book packets and current snapshot reads.

Events

use moonproto::Event;
use moonproto::state::OrderBookEvent;

for event in client.drain_events() {
    if let Event::OrderBook(OrderBookEvent::Apply {
        market_name,
        kind,
        is_full,
        top,
        ..
    }) = event {
        redraw_top(
            market_name.as_deref().unwrap_or("<unknown>"),
            kind,
            top.bid,
            top.ask,
            is_full,
        );
    }
}

Apply is emitted after the retained current book has already been updated. For UI code, use the resolved market_name, typed kind, and top fields. For the full book of the selected chart, keep MarketHandle and read the retained OrderBookSnapshot through the handle-based snapshot helper. The returned value is a short-lived read guard over the current per-book object; copy or draw the needed levels inside that scope.

Recovery control and ignored-packet notifications are hidden diagnostic telemetry. MoonClient consumes them internally and requests fresh full books automatically. Application code observes the maintained book snapshot and applied top-of-book events.

Reading Current Book

For rendering the full current book, keep the selected MarketHandle and read from the latest snapshot through that handle:

use moonproto::state::OrderBookKind;

let Some(state) = client.snapshot() else { return; };
let Some(market) = state.markets().get("BTCUSDT") else { return; };

if let Some(book) = state.order_book_for(&market, OrderBookKind::Futures) {
    if last_book_revision != Some(book.revision()) {
        last_book_revision = Some(book.revision());
        rebuild_orderbook_layer(&book.buys, &book.sells);
    }
}

if let Some(top) = state.top_of_book_for(&market, OrderBookKind::Futures) {
    draw_top(top.bid, top.ask);
}

Use OrderBookKind::as_str() for stable UI/log labels.

UI-relevant fields on the applied current book:

pub struct OrderBookLevel {
    pub rate: f64,
    pub quantity: f64,
}

pub struct OrderBookSnapshot {
    pub buys: Vec<OrderBookLevel>,
    pub sells: Vec<OrderBookLevel>,
}

impl OrderBookSnapshot {
    pub fn revision(&self) -> u64;
}

pub struct TopOfBook {
    pub bid: Option<OrderBookLevel>,
    pub ask: Option<OrderBookLevel>,
}

Incoming wire rows are compact f32, but the retained current-book snapshot stores levels as f64.

OrderBookSnapshot::revision() is the UI-facing freshness gate. It advances once per full or diff packet applied to that retained book. Keep the last seen revision in your view model and skip expensive orderbook buffer rebuilds while it stays unchanged. It is local to one MoonClient runtime and is not the raw wire sequence number.

Recovery

The orderbook state keeps one reorder cache per market/kind pair:

  • a full snapshot resets sequence state;
  • an in-order diff applies immediately;
  • out-of-order diffs are cached;
  • stale diffs are ignored;
  • long-lived gaps switch the cache to corrupted mode and request a full book no more often than every 5 seconds;
  • while corrupted, incoming diffs are still applied as a degraded live view, so the visible book keeps moving (and its revision keeps advancing) until the full snapshot restores exact state;
  • the first diff can apply without a prior full snapshot when the local sequence is zero, matching the server behavior.

After a hard reconnect, orderbook packets are ignored until the library has synchronized fresh market indexes and the server has confirmed the replayed orderbook subscription batch for the new session. Both steps are owned by the runtime; the application does not resubscribe. This prevents a new server market_index map from racing old local indexes and keeps stale-session book packets out of the retained state.

Protocol Data

The internal orderbook parser, OrderBookUpdate, raw packet rows, raw market_index, and raw orderbook kind bytes are for protocol tests and replay tools. Regular applications should subscribe by name, react to OrderBookEvent::Apply, and read current books from MoonClient snapshots.