vike
Market making

Inventory skew

The pure size-shaping multipliers that lean the maker toward a target inventory, and the round-trip netting behind the per-side fill-rate breaker that pulls a side being picked off.

crates/vike-mm/src/skew.rs holds the two pure risk helpers of the spread maker: skew_multipliers, which shapes the two quote sizes from inventory, and net_signed_fills, the round-trip netting the fill-rate circuit breaker trips on. Both were moved out of the crate root verbatim and are isolated so they unit-test without a runtime. Neither touches a price: prices come from the style or the Avellaneda–Stoikov layer, the skew decides sizes, and the breaker decides which sides quote at all.

Size shaping

skew_multipliers(position, target, max_inventory, skew) returns a (bid_mult, ask_mult) pair applied to the base quote size. With imbalance = clamp((position − target) / max_inventory, −1, 1):

bid_mult = max(1 − skew · imbalance, 0)
ask_mult = max(1 + skew · imbalance, 0)

Its doc lists the five properties the skew_sizing tests cover. Neutral: skew <= 0, or a degenerate max_inventory <= 0, yields exactly (1.0, 1.0) for any position, reproducing the fixed-size path bit-for-bit — and guarding the division. Directional: long (position > target) shrinks the bid and grows the ask, leaning to sell the position down; short mirrors. Symmetric about the target. Monotonic: the bid is non-increasing and the ask non-decreasing as position rises. Clamped: beyond ±max_inventory the multipliers saturate at 1 ∓ skew, floored at zero so an over-unit skew can never request a negative size. The crate header adds where the position comes from: the maker reads its own signed position off the broker through HftBroker::position, and the default target of 0 means stay flat.

SpreadMaker::with_skew(target_inventory, max_inventory, skew) is the builder; the TOML keys of the same names default to 0.0 / 1.0 / 0.0, which SpreadMaker::from_params notes are exactly the constructor's neutral values, so a keyless table is byte-identical with skew off. The shaped size is the base every ladder rung multiplies, and the reward min_size floor is applied after it.

Round-trip netting

net_signed_fills(fills, now, window_ms) sums the signed fill size within [now − window_ms, now]: +size per bid (buy) fill, −size per ask (sell) fill. The sign is the round-trip cancellation — a bid fill later offset by an equal ask fill sums back toward zero, which is a completed, inventory-neutral round trip, the desired market-making outcome, and must not trip the guard. Only net one-directional accumulation survives the sum: a result at or above +threshold means bids are being repeatedly hit without offsetting asks — adverse selection on the bid, a falling market lifting it — and suppresses the bid; at or below −threshold, the mirror suppresses the ask. Every timestamp in the window is a fill or quote event stamp (FillRec::ts); wall-clock is never read, matching the runtime's event-clock determinism.

The breaker

SpreadMaker::with_fill_breaker(window_ms, net_fill_threshold, cooldown_ms) arms it, and SpreadMaker::breaker_enabled is the single gate every breaker path checks: all three knobs must be positive, and any non-positive value leaves it off, so the strategy reduces exactly to the skew-only maker with no fill tracking and no suppression. Strategy::on_fill in crates/vike-mm/src/strategy_impl.rs records each fill at its event timestamp, evicts everything older than the window — the window rides the latest fill — nets, and arms the over-hit side's cooldown from the fill's timestamp. The threshold it compares against is net_fill_threshold / pull_accel_mult(fill.ts), the settlement acceleration, which is exactly 1.0 unless a resolution is known, so the default comparison is bit-for-bit the unchanged threshold; the pure netting is untouched.

The arm is acted on by requote, from either lane: a side is suppressed while the triggering event's timestamp is still before its suppressed_until deadline, its resting quote is pulled through HftBroker::cancel_tagged, and it is not re-quoted until the cooldown expires, while the other side keeps quoting and re-skewing normally. The crate header calls this the adverse-selection guard the plain maker lacked.

A pull is never tolerance-gated

The refresh tolerance is consulted only on the re-price path, so it can never suppress the breaker's suppression cancel — a quote that must come off the book always comes off. A laddered side pulls every rung.

In the cross-exchange maker

The xEMM reuses both helpers with two deliberate differences. XemmMaker::with_skew shapes the maker leg's sizes off the owned hedge ledger's maker_pos, never a broker read, for the reasons the hedge ledger page carries. And XemmMaker::record_maker_fill folds maker-leg fills only into the breaker window: hedge-leg fills are the strategy's own mechanical offset, not market flow, and netting them would cancel every real one-sided signal to exactly zero. Its module doc ranks the breaker as the third of four independent safety layers — the one that fires on the symptom regardless of whether any pricing model is right, so a wrong assumption costs bounded one-sided flow.

The end-to-end suite is crates/vike-mm/src/tests/breaker.rs: round-trip netting, per-side suppression and cooldown expiry, the accelerated pull near a binary resolution, the OFI-to-toxicity synthesis that feeds the same guard, and the breaker-off reduction.

On this page