Staleness
The opt-in stale-price wait discipline — a market order that would fill against a forward-filled or otherwise stale price waits for a fresh print instead, with three rules, an event-time clock, and a counter that says when it bit.
A repeated last price is not a tradeable price. crates/vike-fills/src/staleness.rs opens with
the case: on sparse feeds — Polymarket especially, where an outcome token can go minutes without
a trade — a resampled series emits flat, forward-filled bars, and a market order that fills
against one manufactures edge that never existed. The discipline this module supplies makes such
an order wait for a fresh print.
It is enabled only by EngineParams::max_price_staleness_ms. None, the default, means the
module is never consulted and every fill lane is byte-identical to the pre-feature engine; the
header lists the parity and golden suites that all run with None. A
run profile's [engine] table carries no key for it — the
knob is set on EngineParams by a caller building the engine directly.
The three rules
The header states them once, and each is one function.
- What counts as a fresh print —
is_fresh_print. An event carrying genuine current price evidence:volume > 0.0(a real transaction happened in this event or period) or a live two-sided quote (bidandaskboth present — resting liquidity you can actually cross). A zero-volume, quote-less bar is precisely the fill-forward shape, and is the thing this discipline exists to refuse. A one-sided quote is not crossable and is not a print. - Which orders defer —
defers.MarketandMarketCloseonly. Those resolve at "whatever the current price is", so a stale price silently becomes their fill price.Limit,LimitClose,Stop,Trailingand the protective-stop lane are price-conditional: a repeated stale price cannot spuriously satisfy a condition it did not already satisfy, and gating a protective stop would strand live risk. They pass through untouched. - When a price is stale —
is_stale. The age of the symbol's last fresh print, measured against the event clock, strictly exceeds the bound. Age0— this very event is a print — therefore always fills. A symbol that has not printed at all yet is stale by definition: there is no evidence of a tradeable price, so its market orders keep waiting.
Because the comparison is strict, Some(0) is the tightest useful setting — fill only on an
event that is itself a fresh print. A negative bound is clamped to 0 rather than taken
literally: read literally, age > −1 would make even age 0 stale and nothing could ever fill,
a silent total freeze from a plausible typo. A negative age — an out-of-order event stamped
before the last print — is never stale.
What a deferral is
A deferred order is not cancelled and not rejected, and it is never lost. It stays in pending
exactly as it was and is retried on the very next event for that symbol, so it fills on the next
fresh print, at that print's price. The only way a deferred order stops resting is the way any
resting order does: the strategy cancels it, or an opt-in resolution or expiry lane retires it.
SimBroker::stale_deferrals counts how often a deferral happened, and it is mirrored onto
BacktestResult::stale_deferrals by both run and run_ticks, so callers that drop the
engine — the harness and the backtest bin — can read it too. The
result carries it for one reason: "zero trades because the tape never
printed" must stay distinguishable from "zero trades because the strategy never traded". The
zero-trade analyzer turns a non-zero count into a
named cause, stale-price, whose detail says the tape never printed fresh data within the bound
and may be forward-filled or zero-volume across the window.
A strategy can also ask before it sends. SimBroker::price_is_stale(symbol) is the read-only
surface — a maker can check it before sending a taker order it knows would be deferred. It
answers false when the discipline is off and on an unknown symbol, and on the bar path it
measures against that symbol's own current bar timestamp rather than the engine clock, so
that on a ragged multi-symbol tape the predicate agrees with the engine's actual deferral
decision.
The clock is event time
Every age is event_ts − last_print_ts, both drawn from the tape. Nothing reads a system clock,
so a replay of the same tape yields the same deferrals on any machine at any speed — the
determinism the golden and parity suites depend on.
Which tier it actually bites
The target is the bar tier
Forward-filled and no-trade bars are real on StrategyEngine::run, including the cash-gated
lane: a resampled sparse series emits flat zero-volume bars, and without this knob a market order
fills against them. Freshness is recorded once per symbol per step from that step's coarse bar,
immediately before the fill phase, so a bar that is itself a print has age 0 and fills on the
spot.
On the granular sub-bar lane each sub-bar is a first-class print: it is recorded before its own fill pass and the age is measured at the sub-bar's timestamp, because recording only the coarse bar while measuring at sub-bar time would make every later sub-bar of a fully fresh step spuriously stale. A fully printing granular step never defers; a quiet stretch within a step does, which is the point of the finer tier.
The tick tier is inert by construction, and the header says that is correct: each fill event
there is the symbol's own just-arrived tick, recorded as a print immediately before the fill
phase, so the age is always 0. Freshness there is decided from the tick kind — a Tick::Trade
counts as a print regardless of its reported size, since some venues emit zero-size index prints
that would project to zero volume with no quote. The tick path already waits for fresh data
structurally, because an order rests until the symbol's next tick arrives; the gate is still
wired into it so the policy has one definition rather than two. Book events never reach the
price/fill path at all, so there is nothing to gate.
The knob composes with the other bar-lane knobs. crates/vike-backtest/tests/fills/knob_composition.rs
arms impact, staleness and variation-margin settlement on
one run and proves each stays individually observable while the others are on — staleness there
pushes a fill one bar out, onto a bar whose open is a different price.
The cost model
The three canonical cost scalars the backtest engines charge through — adverse fill price, fee and funding, the first two shared with the paper exchange — with the one deliberate deviation from the port, a floor that keeps an adverse move from inverting a price.
Market impact
The opt-in size-dependent slippage model for bar-mode backtests — the Almgren–Chriss cost function, its published constants and units, what it charges and what it deliberately does not, and where its output is bounded.