vike
Concepts

Contingent orders

One-triggers-other arming and one-cancels-other cancellation are decided by a single resolver that every fill lane calls, so the paper book and the backtest broker can no longer disagree about what a bracket does.

A bracket is three orders with two relationships between them: the protective stop and the take-profit are held until the entry fills (one-triggers-other), and once armed, whichever exit fills cancels the other (one-cancels-other). crates/vike-exec/src/contingency.rs is the one place those two relationships are resolved, and its header says why it had to be one place.

The defect that made it one resolver

Before this module the law lived lane-dependently. The paper book ran full one-cancels-other on every fill, while the backtest SimBroker cancelled a fired protective stop's exit siblings only on its granular sub-bar lane — its coarse and tick lanes left the take-profit limit resting on a flat book, so one wide bar could fire the stop and the take-profit and open a spurious reversed position. The paper book's own apply_contingency was the semantic template the module lifts.

The module owns only the decision — which children arm, which siblings cancel. Both callers keep their own book mechanics: resting-order storage, event emission, the actual cancels. That split is what makes the two lanes structurally unable to disagree again, and the paper exchange's r7 gate has a bracket row that proves it — the backtest broker's entry-with-protective-stop plus resting take-profit fills bit-identically to the live submit_bracket's triple resting in the paper book, with both stops firing through the one trigger oracle and both sides cancelling the surviving exit.

Two linkage shapes, one law

Coid-linked — ContingencyBook. Explicit parent and linked client-order-ids ride on the order request, which is what the bracket lowering vike_model::build_bracket emits: an entry with no parent, linked to both exits; a stop whose parent is the entry, linked to the take-profit; a take-profit whose parent is the entry, linked to the stop. Live, OrderIntent::Bracket is the verb — the runtime mints the three ids and wires the links — and the paper book keys its own book by those ids.

Kind-linked — is_protective_exit_sibling. The backtest SimBroker arms an id-less implicit bracket through WorkingOrder::stop, so after a protective-stop fill flattens the position there is no id to follow. Its siblings are identified by kind: a resting order is that stop's one-cancels-other partner, and must cancel, if and only if it is a closing-side Limit or LimitClose exit. Entries and adds on the opposite side never match, and neither do stop or trailing kinds — a second protective layer is not a take-profit. The test pins both directions: a long stopped out cancels sell limits and keeps a re-entry buy limit and a second stop; a short stopped out mirrors it.

What the book does on a fill

ContingencyBook::insert records a leg's linkage. A held exit — one with a parent — starts inactive; an entry starts active immediately. is_held is the question a fill pass asks before letting an order fill: a held order must not, and every plain order, which is never inserted at all, is never held. That absence is the byte-identical path for non-bracket runs — a book with no brackets stays empty and every resolver call is a cheap no-op.

on_fill resolves one fill in two steps: arm every held child of the filled id, then return its one-cancels-other siblings — its linked ids that are not its own children — in linked order. The caller cancels whichever of those still rest in its book and calls remove for each one it actually cancelled; a sibling already gone cancels nothing. The bracket test shows the shape: the entry's fill arms both exits and cancels nothing, because the entry's linked ids are its own children; the stop's fill then returns exactly the take-profit. A plain, unrecorded fill arms nothing and returns empty.

siblings_of answers the same sibling question read-only, so a dying protective exit — cancelled or expired rather than filled — resolves its surviving partner off the same linkage a fill would, and the terminal-cancel and fill-cancel paths cannot disagree about who the sibling is. children_of is the walk step of the expire/cancel cascade: a child whose parent terminated without filling can never arm. clear is the mass-cancel twin.

Restarting with an armed bracket in flight

insert derives active from "has no parent", which is wrong for a child that was already armed by its parent's fill before a crash: re-inserting it would silently re-hold an armed exit. So the book exposes snapshot — every entry as (coid, parent, linked, active) in insertion order — for the journal's Snap record, and insert_active takes the flag explicitly for the restart re-seed. Neither is used on the live submit path; both exist so replay reproduces the same arm and cancel iteration order the live book had, which is also why the book is an IndexMap: the arm sweep and children_of iterate the map, and insertion order keeps the event stream reproducible.

Live, the drive is gated on the fold's verdict

On the venue ingest lane the runtime drives this resolver only when the event bus reports that the engine actually applied the fill. The Fold verdict was added for exactly this consumer: a fabricated OrderFilled on an unknown client-order-id used to cancel its sibling and arm its held children even though the engine had dropped the event — a naked position on the strength of a fill that never happened. crates/vike-core/src/runtime/mod.rs's pump_client records that the in-process client seam is deliberately not gated the same way.

One boundary of the coid-linked shape is declared where it bites. The paper client's combo book does not support contingency links on a multi-leg combo — a combo request carrying a parent or linked ids is terminally rejected, because the hold and cascade machinery enforce against the single-symbol resting book only — while a plain order may link a resting combo as a sibling, and a combo's fill arms plain held children as any fill does. What each order kind means, and which venues carry it natively, is under orders; a conditional the core emulates for a venue without one is emulated orders.

On this page