vike
Order flow

Order-to-trade ratio

Quoting intensity inferred from consecutive book snapshots — the add/update/cancel proxy over the visible top-N, the per-window formula, and the event-anchored windows it is reported in.

crates/vike-orderflow/src/otr.rs is the order-to-trade ratio, "a quoting-intensity study inferred from consecutive book snapshots". Its header is candid about what it can and cannot see, and that candour is the contract: without an order-by-order feed, messaging activity is approximated by diffing the visible top-N level lists of consecutive snapshots.

The proxy

For each side, two consecutive snapshots of the top depth levels are compared price by price:

a price that is…counts as
present only in the new listan add
present in both with a different quantityan update
present only in the old lista cancel

Prices are matched on exact bits, which is safe because "both lists come off the SAME integer-tick grid (L2Book::top_n prices are tick · tick_size), so equal price ⇒ equal bits". The first push has nothing to diff against and contributes no messages; it is the baseline.

A level scrolling out of the window is a cancel — by construction

A level pushed off the top-N by a better price "counts as a cancel by construction — this is a visible-window proxy, not exchange message truth; document-level contract, not a bug". The tests pin it: a new best bid arriving on a depth-2 window reads as one add and one cancel. Read the ratio as activity at the visible edge of the book, not as a message count.

The formula

Per fixed event-time window — default 100 ms, configurable:

OTR = (adds + 2·updates + cancels) / max(trades, 1) − 1

An update is one cancel-plus-replace, hence two messages. The −1 sets the baseline at parity: "0 means one message per trade above baseline parity". The max(trades, 1) floor keeps a window with no prints finite — the tests show five messages over zero trades reporting 4.0.

The tracker

pub struct OtrConfig { pub depth: usize, pub window_ms: i64 }   // Default: depth 10, 100 ms

pub struct OtrSample {
    pub window_start: i64, pub window_end: i64,   // end = start + window_ms
    pub adds: u64, pub updates: u64, pub cancels: u64, pub trades: u64,
    pub otr: f64,
}

impl OtrTracker {
    pub fn new(cfg: OtrConfig) -> Self;                    // asserts depth >= 1, window_ms >= 1
    pub fn push(&mut self, ts: i64, book: &L2Book, trade_count: u32) -> Option<OtrSample>;
    pub fn forming(&self) -> Option<OtrSample>;
    pub fn last(&self) -> Option<OtrSample>;
}

push folds "one observation: ts (event time, ms), the book to snapshot, and how many trades printed since the previous push". It returns the committed sample only when this push crossed the window boundary, and then "the push's own diff lands in the NEXT window". forming() is the open window's counts with an interim ratio, None before any push; last() is the last committed window. The header says the pair "mirror the crate's forming/committed convention" — the same reading the flow bars offer.

Windows are event-anchored

Two rules from the header decide where windows begin and end:

  • "The first push after a close starts the next window at its own ts (idle gaps don't emit empty windows)." A push at 730 ms into a 100 ms window closes exactly one window, [0, 100), and opens the next at 730 — however long the gap.
  • "A push whose ts reaches start + window_ms FIRST closes the window (its own diff opens the next one)." The boundary is half-open: a push at 99 ms is still in the first window; a push at 100 ms closes it.

The module's scripted window, from its tests: baseline at t=0; at t=50 one bid add and one ask update with three trades; the push at t=100 closes the window with adds 1, updates 1, cancels 0, trades 3 and OTR = (1 + 2·1 + 0) / 3 − 1 = 0. The t=100 diff — a bid pulled — opens window two with one cancel.

How it is gated

Like depth imbalance, this study has no trade-slice batch twin, so the crate header lists it among the studies that are "pure functions/scripted-snapshot gated": the diff counts (identical lists, from-empty, to-empty), the window arithmetic, the half-open boundary, the event anchoring and the trade floor are each pinned against hand-computed values. The book it reads is crates/vike-marketdata/src/orderbook.rs's L2Book, on the data model page.

On this page