vike
Market makingCross-exchange

Guards

The xEMM's pure safety verdicts — the freshness bounds, the soft and hard naked-inventory bands, and the halt vocabulary every stop is named in.

Between a maker fill on venue A and the hedge fill on venue B a cross-exchange maker is directionally exposed, on a leg it cannot see through any broker read — a risk a single-venue maker does not carry. crates/vike-mm/src/xemm/guards.rs's module doc says every verdict in it exists because some way of losing sight of that exposure has a specific, nameable shape: the reference feed silently freezes; the maker venue's own feed freezes, so passive_clamp is anchored on a touch that no longer exists; both freeze, so neither age bound can fire from a lane that never runs; the hedge does not fill; the pair decouples. Each is a pure function, testable without a runtime or a broker, so the fault path is one readable list rather than a chain of inline conditions.

Built, tested, and mounted by nothing

XemmMaker is reached by no binary — crates/vike-run/src/xemm.rs's build_live_xemm_core has no caller, and crates/vike-strategy/src/registry.rs's strategy_by_name has no xemm arm. Keeping the code rather than deleting it was a deliberate verdict, not an oversight. These guards are design constraints a future mount must not break, not a description of anything running today; see the xEMM overview.

The halt vocabulary

HaltReason is public and serialisable, and every variant is an operator-facing fact — a specific thing that is wrong, not a generic error, because the correct response differs:

reasonwhat it names
ReferenceStalethe reference venue's touch is older than max_ref_age_ms, or has never arrived
OwnTouchStalethe maker venue's own touch is older than max_own_touch_age_ms — the passive clamp's anchor is gone
FeedImpairedneither venue has ticked within max_emission_gap_ms, or the maker venue's feed reported a non-Live status
HedgeUnfilledthe hedge residual survived its full retry budget — an open, unhedged position the taker venue has refused to close
NakedHardBandthe unhedged exposure breached naked_hard_band
BasisOutOfBandthe observed maker-vs-reference basis breached max_basis_bps — a decoupled pair, a wrong-instrument mount, or a lying feed
UnknownFillSymbola fill arrived for a symbol that is neither leg; the maker can no longer know its own inventory, so it fails closed

The doc's triage: a stale feed is transient, an out-of-band basis means the mount is probably wrong, and an unfilled hedge means there is a real open position someone has to close by hand.

Halt is Running or Halted { reason, since }. A halt latches with the event timestamp it began at, and crates/vike-mm/src/xemm/mod.rs's enter_halt keeps the first reason if one is already latched — the first fault is the diagnostic one, and a cascade of consequences would otherwise overwrite it. resume_after_halt_ms is event time like every window in the crate; its default 0 means manual resume only, which maybe_resume's doc calls correct for faults that all mean "something I cannot see is wrong". The latch is persisted in XemmStateV1, so a halted maker cannot un-halt merely by being restarted — load_state's doc says a restore can only make the maker more cautious, never less. XemmMaker::halt_reason is the operator-facing observable.

Freshness

stale(last_ts, now, max_age_ms) is true when the timestamp is absent, or older than max_age_ms of event time. Three edges are deliberate and pinned: max_age_ms <= 0 disables the bound for a present timestamp, but an absent one is still stale, because no configuration may declare a never-seen feed fresh; a negative age (an out-of-order or clock-skewed tick) is fresh, since it is evidence the feed is alive and halting on it would fire on healthy feeds; and the bound is inclusive — exactly at it is fresh, one millisecond past is stale.

detect_fault consults it three times, freshness first as the most common and most recoverable fault: the reference touch against max_ref_age_ms, the own touch against max_own_touch_age_ms, then last_tick_ts — the last touch from either venue — against max_emission_gap_ms. That third clock exists because the per-venue ages each catch one dead feed, and when nothing at all is arriving no per-venue lane can fire. A cold start, either touch not yet seen, is deliberately not a fault; requote pulls on it anyway. XemmParams::default sets both ages to 2 s and the gap to 5 s, and the_defaults_ship_every_guard_armed pins that every guard ships armed — the one place this workspace inverts its neutral-default rule, because a strategy signing orders on two venues must not be one forgotten field away from quoting into a frozen feed. Under with_freshness any <= 0 disables that bound, which for the two ages means "quote off a dead feed forever".

Three lanes back the pure check. on_feed_status receives the maker venue's status only — FeedStatus carries neither venue nor symbol — and any non-Live value halts as FeedImpaired and pulls at once; the reference venue is covered by max_ref_age_ms, which is strictly stronger than a status subscription because it also catches a feed that freezes without ever reporting a disconnect. on_schedule is the all-feeds-dead sweep: every other lane fires off a market message, so when both venues go silent none of them run and a stale quote would rest forever; the wall-clock schedule fires regardless and re-runs the whole funnel. on_trade_tick carries no touch but advances the clock, which lets a bound fire on a venue printing trades while its quote feed has silently frozen.

The naked bands

Both bands read HedgeLedger::naked — maker_pos + hedge_pos, the exposure actually carried — off the hedge ledger, never a broker.

soft_band_suppression(naked, band) returns (suppress_bid, suppress_ask). While |naked| > band the side that would grow the exposure is withheld and the reducing side keeps quoting, so the maker trades its way back toward flat instead of stopping dead: net long suppresses the bid, net short the ask. The doc calls it the mirror-image of the fill-rate breaker — that one reacts to one-sided flow, this one to the resulting position. A negative band is clamped to 0, a non-finite exposure suppresses both sides, and exactly at the band holds. In requote it is one of three suppression terms, beside the breaker's cooldown and a non-positive size.

over_hard_band(naked, hard) is strictly over (>), matching taker_flatten's "exactly at the band is not over" convention so the halt and the impulse leg engage on the same inventories — the_hard_band_matches_taker_flattens_convention asserts they agree. hard <= 0 disables the band (there is no meaningful "halt on any exposure"; that is the soft band's job), and a non-finite exposure trips it. On a breach detect_fault raises NakedHardBand, requote pulls both sides, and drive_flatten crosses the taker venue for the excess over the soft band — the flatten-the-excess law. The impulse fires at most once per halt (flatten_fired, cleared on resume) and only while the ledger owes nothing, since an in-flight hedge is already closing the exposure; at hedge_ratio == 1.0 the residual and the naked exposure are the same quantity, so the leg is unreachable by construction and exists for the partial-ratio case.

XemmMaker::new sets the bands to qty (soft) and 3·qty (hard): one full quote size of unhedged exposure stops growing it, three sizes is a fault. with_naked_bands tunes both, and from_params keeps the derived pair unless the profile names naked_band/naked_hard_band.

A bare XemmParams::default arms no hard band

XemmParams::default sets both bands to 0.0, and its doc reads that as suppress on any exposure and halt on any exposure. The soft half is right; the hard half is not: over_hard_band treats hard <= 0 as disabled, and its test pins !over_hard_band(1e9, 0.0). A maker configured from a bare default through apply_params has no hard halt at all; the builder (new) or the TOML reader (from_params) is what gives you one.

The remaining verdicts, and what a halt does

HedgeUnfilled is HedgeLedger::attempts_exhausted — owed, budget spent, and timed out, all three. BasisOutOfBand is BasisEwma::out_of_band: the estimate has warmed and |basis| · 10 000 > max_bps; max_bps <= 0 (the default) disables it, and a cold estimator never trips, because an absent estimate is not evidence of a wide basis — the freshness bounds cover "I cannot see anything" (basis). UnknownFillSymbol latches from on_fill with no order verb, because the dispatch series of an unattributable fill is unknown and a tagged cancel from the wrong series is a silent no-op; the next maker-venue lane pulls both sides. detect_fault's full order is reference age, own age, emission gap, hedge exhaustion, hard band, basis — freshness, then exposure, then the model.

requote's stage 0 runs maybe_resume, then detect_fault, and a halted or not-yet-ready maker calls pull_all and stops. pull_all is two cancel_taggeds, never mass_cancel, which in the live runtime is scoped to the dispatching series and from a hedge-fill dispatch would cancel nothing on the maker venue — a pull that silently does not pull. A pull is never gated by the refresh tolerance. The one-sided-fill breaker (record_maker_fill, maker-leg fills only, off by default) is the symptom guard beside these — see inventory skew — and crates/vike-mm/src/tests/xemm_guards.rs opens with the poisoned-broker test that pins the one read the maker must never make, and drives the freshness, hard-band and basis verdicts to their halts; HedgeUnfilled and UnknownFillSymbol are driven in crates/vike-mm/src/tests/xemm_hedge.rs instead.

On this page