Quote styles
The four pure placement styles, the book view they price from, and the two own-order filtrations that stop a maker joining or leaning on its own quote.
Where the two quote prices land on the book, when the Avellaneda–Stoikov layer is not pricing, is
a QuoteStyle. crates/vike-model/src/strategy/maker_params.rs holds the enum — "a small registry
of PURE styles" that lives in the bottom layer "so it can ride the typed live-params payload" — and
crates/vike-mm/src/book.rs holds the arithmetic, because "it needs this crate's private book
view."
The book view
BookView is "a normalized, best-first view of one book that the QuoteStyle registry + own-order
filtration are PURE functions of. bids are highest-price first, asks lowest-price first; each
level is (price, size)." tick_size is the venue grid: an L2 book carries its own
(BookView::from_l2, the n best levels), and the L1 lane supplies "the venue tick LEARNED from
the L2 book lane (learned_book_tick) when one has been seen, else the configured tick_size
param (pure-L1 fallback)" (BookView::from_quote, exactly one level per side) — so one
*_half_spread_ticks can't resolve to two different spreads across the lanes. A 0.0 grid means
"Top can't step and filtration is inert."
The four styles
BookView::priced(style, half_spread, depth_levels) returns None when the book lacks a side.
Otherwise:
| style | bid | ask | note |
|---|---|---|---|
Mid (default) | mid − half_spread | mid + half_spread | mid = 0.5·(best_bid + best_ask) — "the ORIGINAL SpreadMaker pricing, reproduced bit-for-bit" |
Join | best_bid | best_ask | "quote at the touch" |
Top | best_bid + tick | best_ask − tick | "one tick in front; … a 0.0 grid collapses Top to the touch" |
Depth | bids[n].price | asks[n].price | n = depth_levels, "clamped to the deepest available level (so a 1-level L1 view collapses to the touch)" |
half_spread is read by Mid only and depth_levels by Depth only. The builder is
SpreadMaker::with_quote_style(style, depth_levels, tick_size); from a profile the keys are style
(top | join | mid | depth) and depth_levels, which from_params exposes so "a backtest profile
can sweep the fill-rate-driving placement, not just the pricing model."
The style prices only when A-S does not
read_pricing_chains_as on SpreadMaker::from_params: pricing = "as" — and absent — chains the
A-S layer, "which makes the style key INERT (requote bypasses the QuoteStyle placement
whenever A-S state is Some)". pricing = "style" skips A-S "so the selected placement
(style/depth_levels) actually prices — the registry path where the style key finally does
something (audit F8)." A style line under the default pricing changes nothing.
Single-snapshot filtration
filter_own in book.rs subtracts "our OWN resting orders from the public book so the maker never
joins or leans on its own quote — the correctness fix for quoting on the same feed it consumes."
For each own (price, size) it finds the level at the same tick on that side and subtracts the
size; "a level whose remaining size is <= 0 is REMOVED, so the derived best shifts to the next
genuine market level (exactly what stops a Top/Join maker from stepping in front of — or resting
on — itself)." Matching is by tick index through subtract_own, which "mirrors L2Book's own
quantization"; with no grid it is a no-op.
It is enabled by with_own_order_filtration and is OFF by default, which crates/vike-mm/src/lib.rs
lists among the opt-in refinements that COMPOSE with the rest. Turning it on changes pricing
bit-for-bit. The subtracted pair is the
per-side SideState::own snapshot — "the maker's INTENDED quote, not the venue-side remainder".
The own-order ladder
crates/vike-mm/src/own_book.rs exists because the single snapshot "is wrong in two ways the moment
either assumption breaks": "Multiple own orders per level" — a ladder or an in-flight replace has N
orders at a price, and "the single snapshot can only subtract the last one it remembered" — and
"The accepted→public race": a venue acks "microseconds before the public market-data feed shows it.
Subtracting it in that window removes depth the feed never added — inventing a phantom-empty level
and pushing a Top/Join maker to quote THROUGH real resting depth."
OwnOrderBook is, per side, "a BTreeMap<price_ticks, level> (price-ordered …), where a level is
an insertion-ordered IndexMap<coid, OwnOrder> — insertion order IS venue queue order, and it also
makes the per-level f64 qty fold deterministic." Removals use shift_remove.
An OwnOrder has "deliberately only three states": Submitted ("NOT in the public book, so it
must never be subtracted — enforced structurally: a Submitted order has no ts_accepted"),
Accepted, and PendingCancel ("still resting at the venue … until the cancel lands, so by default
it still counts"); terminal outcomes remove the order. OwnOrder::is_visible is the race gate: true
"only when the venue has accepted it AND ts_accepted + accepted_buffer_ns <= now_ns", and "a 0
buffer means 'trust the accept immediately' — the behavior of the pre-existing single-snapshot
filtration." StatusMask::RESTING (accepted + pending-cancel) is the default; ACCEPTED_ONLY is
"for a maker that wants to lean on the depth it is about to reclaim."
on_modify is chosen "so the book never OVER-subtracts": a price move re-enters the new level "at
the BACK (a venue re-price forfeits queue priority)" as Submitted with the stamp cleared; a size
increase re-arms the stamp; a reduction keeps it, "since subtracting the smaller new size can only
under-subtract, which is the safe direction." subtract_levels is "byte-identical to
book::subtract_own's arithmetic for the single-own-order case".
Who drives it
SpreadMaker::with_own_order_book(tick_size, accepted_buffer) is "the whole opt-in" — it implies
filter_own. The maker drives the ladder through OwnBookFiltration::place "with an optimistic
ack": a Strategy never sees client-order-ids or venue accepts, "so the maker keys the book by
its own tag ("bid"/"ask") and stamps ts_accepted itself at the placing event ts", the buffer
"measured from send instead of ack." A runtime that knows the tag↔coid mapping drives the real
lifecycle verbs through SpreadMaker::own_book_mut, "after which the buffer covers market-data
propagation alone."
Units, and what is not saved
"The _ns suffixes follow the HFT nanosecond convention; the code never converts or divides a
timestamp, so any single consistent unit is valid." The live lanes feed epoch-millis, "so a
maker-side accepted_buffer_ns is configured in MILLIS." The ladder is mount-structural — not in
SpreadMakerParams, untouched by a re-tune — and "nothing here is persisted": the cache is
"rediscovered from the venue by the runtime's reconcile pass." It keeps its own fixed grid rather
than adopting the L2 book's, "so on the L2 lane pass that venue's grid here explicitly."
The place in the tick where either filtration runs is stage (1) of quote emission.
Quote emission
The one shared tick step both lanes drive — its stages in order, what each one consumes, the two gates only a re-price consults, and the hold that must never be silent.
Ladder quoting
The pure per-side rung builder behind the maker's N-orders-per-side mode — rung 0 is today's quote bit-for-bit, deeper rungs step outward, and the whole side is diffed to the minimal verb set each tick.