The queue lane
The opt-in queue-position fill lane of the tick replay — the queue-gated twins of the frozen per-tick fill pass, entered only when a queue model is configured, where a price touch no longer fills a resting limit on its own.
The frozen tick-replay fill pass fills a resting limit the instant price touches it. The queue
lane is the opt-in alternative: the same pass, with every resting limit — a tagged maker quote
included — additionally gated by an estimate of how much size sits ahead of it at its price.
crates/vike-backtest/src/engine/queued.rs is the lane; the estimators it consults are
queue models, and this page is about the lane rather than
the arithmetic.
When it runs
The lane is reached only when EngineParams::queue_model is Some. Its module header
states the contract the rest of this page rests on: a default run never enters the module at
all, and the frozen fill_pending_tick / fill_tagged lanes it mirrors stay byte-identical.
From a run profile that is queue_model = "…" in [engine],
which EngineCfg::queue_model's doc marks tick mode only — the lane is consulted exclusively
by run_ticks, so a bar-mode profile that sets it is rejected by BacktestProfile::validate
rather than silently doing nothing.
The module is a separate file for a reason its header gives: the queued twins used to sit some 700 lines from the code they mirror, in a 2 200-line file — a hand-synced duplication no reader could check without scrolling between two ends of the file.
What is shared and what is genuinely different
The per-order gate sequence is not duplicated here. Session → staleness → emulator stop-release
→ trigger price lives in one function on the parent module, StrategyEngine::gate_pending,
which this lane's taker branch calls like every other lane; the maker-lane session gate lives
in StrategyEngine::defer_tagged_session. What the header lists as staying in queued.rs is
only what makes the twin a twin: queue-state identity and sync (WorkingOrder::qid), the
queue_gate verdict layered on top of the frozen price law, partial-fill remainder bookkeeping,
and the maker straddle and min-hold guards. The one gate the lane still runs by hand is
StrategyEngine::defer_session, on its price-conditional branch.
fill_pending_tick_queued's doc gives the shape: the same structure as the frozen path —
trigger → resolve → dispatch → tagged lane — with resting Limit / LimitClose orders
additionally gated by their queue-position estimate. Non-limit kinds (market, market-close,
stop, trailing) are takers and pass through unchanged: a taker crosses the book, it never rests
in it.
The verdict
queue_gate judges one resting limit against the raw tick, because the derived Bar shape
cannot distinguish a trade from a quote. It is computed only after the existing fill model has
already said the price condition holds — the gate composes on top of the frozen price logic and
never replaces it. Three outcomes, QueueVerdict::{Rest, Fill, Partial}:
- a strict price cross — traded or quoted through the level — fills in full, because the whole level, queue ahead included, was consumed;
- a trade at the price consumes the front first, and only its excess beyond the front fills — in full when that excess covers the whole order, partially otherwise, the remainder resting;
- a quote touch fills only once the front is already cleared — there is no trade proof otherwise.
Book events never reach the price/fill path, so a Tick::Book always rests. Two simplifications
are declared on the function rather than hidden: trades are not filtered by aggressor side, and
simulated orders never consume each other's queue — each order is gated independently, the
standard shadow-order convention.
Identity, not fingerprints
Queue state is keyed by the order's engine-local identity, WorkingOrder::qid, stamped the
first time the lane sees an order — never by a (side, price) fingerprint. The doc gives the
case that rule exists for: the maker re-quote pattern cancel_all + re-submit at the same price
correctly re-seeds at the back of the level instead of inheriting the cancelled order's
advanced front. States whose order vanished since the last pass — cancel_all, an OCO
sibling-cancel, a fill — are dropped by that pass's rebuild.
The tagged (HFT maker) lane, fill_tagged_queued, is the queue-gated twin of fill_tagged and
keys differently, because a tag names a quote slot, not a resting order: its sync keys on the
tag plus the slot's current side, price and qid. Its doc lists what forfeits priority — a
re-priced tag, a re-submit over a live tag (submit_limit_tagged replaces the resting order), a
cancel_tagged + re-submit, and a modify_tagged quantity increase all re-seed at the back
of the level, each being a new order on a real venue. An amend-down keeps its place, as venues
do. Gone tags drop their state; partial fills shrink the resting tag in place instead of retiring
it.
The maker guards
The min-hold guard is EngineParams::queue_min_hold_ms (queue_min_hold_ms in the profile,
read only when queue_model is set): on the tagged maker lane, a fill that would reduce
the current position — the closing side of a maker round-trip — is deferred until at least that
long after the position was opened, so a maker cannot book a physically impossible
same-millisecond or sub-second round-trip. 0, the default, disables it, and the untagged
queued-limit lane runs no such check at any setting. The field's doc says where to calibrate it
from — real market-maker behaviour — and quotes Polymarket BTC-5m market-maker wallets flipping
at roughly a 2 s p10 and an 11 s median. The straddle guard is the other guard the header names;
its mechanics are documented at its call site in the lane.
The session gate reaches this lane too. EngineParams::session_gate's doc is explicit that a
closed market crosses no resting limit and no maker quote, queued or not: resting orders, the
tagged maker lane and protective stops are gated on the frozen fill lanes and on their opt-in
queue_model tick twins alike.
What to feed it
EngineCfg::queue_model's doc closes with the data requirement: for a real maker backtest, feed
the trade tape, and — for true size-ahead seeding — the book. Without a book the seed
falls back to the last L1 quote and then to queue_seed_depth; queue
models carries that chain. The natural partner is a
latency model: the queue removes the "always first in
line" optimism, latency removes the "reacts on the same tick" optimism, and
crates/vike-backtest/src/latency.rs's header names the two as partners.
The sim broker
The backtest fill engine and the context every strategy handler receives — its per-symbol state, the order verbs that all but one cross the live risk gate, the one fill-application path, and the diagnostics a run leaves on it.
Queue models
The position-in-queue estimators for resting maker limits on the tick/book replay path — what they observe, how each attributes a depth change, how a new order is seeded, and why the probabilistic arm's transcendentals come from one libm.