vike
Backtesting

Fill resolution

What happens when several resting orders trigger inside one bar — adverse-first ordering, the stop/take-profit bracket cap, and the both-hit count the result carries so the ambiguity is reported rather than hidden.

OHLC cannot reveal the sequence inside a bar. When several resting orders trigger on the same bar, the engine has to choose an order to apply them in, and the choice changes the result: a stop and a take-profit that both lie inside the bar's range cannot both have filled against one position. crates/vike-fills/src/fill_resolution.rs is the one shared answer — an exact port of the Python oracle's resolve_intrabar_fills — and its header states the two rules.

Adverse first

Apply adverse fills (stop and trailing) before favourable fills (limit). That is the pessimistic ordering: when the data cannot say which side of the range the price reached first, the engine assumes the side that hurts. resolve_intrabar_fills takes the (order, fill_price) pairs that triggered this bar together with the position size before any of them, and returns the pairs sorted adverse-first.

The bracket cap

When more than one order reduces the current position in the same bar — an SL + TP bracket — the total reduction is capped to the position size, adverse-first, so the profit target cannot also fill after the stop has already flattened the position. The returned sizes are the bracket-capped ones.

The ambiguous bar is not silently resolved. The function returns both_hit — 1 when the cap applied this bar — and the header says why: it is surfaced on the result for honesty. BacktestResult::intrabar_both_hit in crates/vike-analytics/src/result.rs is the counter, described there as event-engine only; the vectorized kernel leaves it at 0. A run whose stops and targets kept landing in the same bar is a run whose bar interval is too coarse for its brackets, and the count is how you find that out from the result.

Where it runs

The step is part of every event-engine fill pass. fill_pending_tick_queued's doc gives the shape of the tick lanes as trigger → resolve → dispatch → tagged lane, the resolve being this function; the queue lane inherits it unchanged from the frozen path it mirrors. The paper exchange is built from the same vike-fills primitives, which is the point of the function living there: one definition of what happens on an ambiguous bar, on both sides of the backtest-equals-paper law.

Frozen arithmetic

vike-fills was extracted verbatim from the backtester — a pure relocation with no behaviour change — and the crate header's parity rules apply: f64 end to end, the same expression order as the oracle, no fused multiply-add or fast-math. See the parity gate for why none of that is relaxed to make a test pass.

On this page