Lifecycle trace schema

Lifecycle trace schema

--lifecycle-json writes one JSON object (two-space indentation, one trailing newline, keys in the order given here) describing one run; see The order lifecycle trace for what it is for and a worked example. The schema is versioned by its first key; this is /1.

KeyValue
schema"algolang.lifecycle/1"
runrun_id; mode, plain for the bar loop or continuous for the served continuous loop; symbols, the run’s symbols (a continuous run names its composite’s canonical symbol); calendars, the run’s exchange-calendar provenance, only when the run asked for exchange calendars (see Engine side)
summarythe 25 counts in the summary
recordsthe record stream, always an array
capital, equity, realized, fillsthe fill blotter as --fills-json writes it: the simulator’s capital, equity and realized P&L, and its fill reports (BrokerOrderID, Fill, Anchor); fills is null when the run made none, as the blotter writes

Records and their order

A record is a JSON object whose first four keys are its header: seq, 1 for the first record and +1 per record; kind, one of submit, cancel_request, fill, cancel, roll, snapshot; bar, the index (0-based) of the loop bar current when the record was made, -1 before the first; and at, an instant in UTC as RFC 3339 with nanoseconds only when non-zero (2026-06-10T14:31:00Z), or "" for a zero instant.

The stream is the loop’s own order; seq is a record’s position and nothing is reordered. Within one loop bar the records come as the loop produces them: on a continuous run, the roll block when a boundary fires (its roll record, cancel requests, legs, suppressed events and re-submissions); then the events delivered at the top of the bar (fill and cancel, in the simulator’s order); then, once the strategy has seen the bar, its cancel requests (cancel_request, as it issued them) and its submissions (submit, as it returned them). After the last bar: one snapshot, then the shutdown cancels. A roll’s records carry the index of the first incoming bar, because the loop marks the bar before the roll block runs.

The at instant depends on the kind: a strategy submit or cancel_request carries the current bar’s close (the simulator’s clock at submission); a roll-sourced record carries the cut; a fill or cancel carries the event’s own timestamp (a roll’s suppressed cancel is stamped by the simulator at its own instant); the snapshot carries the last bar’s close.

submit — an order handed to the venue, or refused before it. Keys after the header: source, client_id, symbol, side, type, quantity, price, stop_price, trail_amount, tif, oco_group, venue_symbol, venue_price, venue_stop_price, venue_trail_amount, attached (only when the order carries attached children), status, broker_id, reason. source is strategy (the strategy’s own orders), roll_leg (an engine roll leg) or roll_resubmit (a standalone resting order re-submitted onto the incoming contract at a roll; a stop the cut price was already through keeps its type and stop_price but shows venue_stop_price: 0, because it went to the venue as a market order, or as its limit at venue_price for a stop-limit). status is accepted (the venue booked it: broker_id is the simulator’s record id, reason is empty), denied (the venue refused it: broker_id is the record id the simulator still assigns, reason its text verbatim) or rejected (the engine refused it before the venue saw it: broker_id is "", reason the loop’s text). attached lists the order’s bracket children in order, each with client_id, side, type, quantity, price, stop_price, trail_amount, tif and the venue-frame venue_price, venue_stop_price, venue_trail_amount. An accepted submit of any source is an attempt: one physical booking under its own broker id. A denied or rejected one is not.

cancel_request — a cancellation asked of the venue. Keys: source (strategy, or roll for a roll’s cancellation of a working order on the outgoing contract), client_id, broker_id (the latest attempt under that client id; "" for an unknown id or a bracket child, which is not an attempt), status (accepted when the venue cancelled, else rejected), reason (the venue’s text when rejected). An accepted request settles the attempt it addressed at once: the simulator’s cancel is synchronous, and the cancel event itself drains with the next advance.

fill — a fill the venue produced. Keys: delivered, origin, client_id, parent_client_id, symbol, venue_symbol, side, quantity, price, venue_price, commission. symbol and price are the strategy’s frame; venue_symbol and venue_price the venue’s; parent_client_id is set on a bracket child’s fill.

cancel — a cancel the venue produced. Keys: delivered, origin, client_id, reason, triggered_by (the sibling’s client id on an oco_triggered cancel). The engine’s own rejection cancels, which follow a denied or rejected submit before the next bar, carry reason: rejected and an empty origin.

roll — a continuous roll boundary, recorded once per fired boundary after the loop has classified the working orders on the outgoing contract and read its net position, before that roll’s cancel requests, legs and re-submissions. Keys: from, to, position (the signed net position rolled), retranslated (client ids of the standalone orders re-submitted onto to), reestablished (bracket children re-attached to the open leg), pinned (contract-addressed orders cancelled, and the strategy told). The three lists are always arrays.

snapshot — the working set at one instant; see the snapshot.

Frames and identity

Every submission and fill carries two frames. The strategy frame (symbol, price, stop_price, trail_amount; on a fill symbol, price) is what the strategy sent or received: the logical continuous symbol with adjusted levels, or the adjusted equity frame. The venue frame (venue_symbol, venue_price, venue_stop_price, venue_trail_amount; on a fill venue_symbol, venue_price) is what the simulator received or produced: the dated contract with raw levels, or the raw equity frame. On a series with no adjustment the two are equal. The venue frame is recorded as given. The continuous resolver translates only the levels an order type uses (a limit’s price, a stop’s trigger, both for a stop-limit), so a market or market-on-close order on the logical symbol reaches the contract with venue_price: 0 and venue_stop_price: 0.

Three identities run through the records:

  • client id (client_id): the strategy’s logical label. Translation onto a contract or a raw frame does not change it, a roll re-submission keeps it, and the strategy may re-use it once the previous order under it is terminal; the trace, like the simulator’s own cancel lookup, resolves a re-used client id to its newest attempt.
  • broker id (broker_id): the simulator’s attempt id, sim-N, one per physical booking. A re-translation at a roll is a new attempt under a new broker id; an activated bracket child has its own record id, which the trace first meets in the snapshot. A roll leg’s client id is roll/<symbol>/<cut in RFC 3339 UTC>/<close|open>.
  • origin (origin, on fill and cancel): the top-level broker id the event descends from — a standalone order’s own; the parent’s for a child’s fill or for an OCO or parent-cancelled cancel; after a roll, the open leg’s for the re-established children.

Rejections

Three different refusals are three different records:

  1. the engine refused an order before the venue saw it (on a continuous run, an order addressed to a symbol other than the logical symbol or the active contract): a submit with status: rejected, broker_id: "" and the loop’s reason; a cancel with reason rejected and an empty origin follows before the next bar;
  2. the venue refused it (a symbol outside the instrument set, a non-positive quantity, …): a submit with status: denied, the record id the simulator assigned and its reason verbatim; the same rejected cancel follows;
  3. the venue refused a cancel request (an unknown or already terminal id): a cancel_request with status: rejected and the venue’s reason; the strategy hears a cancel reject and the book is untouched.

The snapshot and its join

The loops take the snapshot exactly once, after the last bar’s submissions and before the end-of-data cancellation, so it shows the residue the shutdown then sweeps: every entry it lists is followed by a shutdown cancel of that client id, and working_at_end counts its entries. It lists every order the simulator still holds as working, in submission order. Each entry carries the venue’s facts, broker_id, client_id and venue_symbol, then what the trace could join to them, by this rule:

  1. the broker id is an attempt (an accepted submit of any source): kind: top, with source, symbol, side, type, quantity, price, stop_price, trail_amount, tif, venue_price, venue_stop_price and venue_trail_amount copied from that submit record, parent_client_id empty and submitted_seq that record’s seq;
  2. else some attempt declared an attached child under the entry’s client id: the latest such attempt wins (after a roll, the open leg, whose attached list carries the child re-translated onto the incoming contract), giving kind: child, that attempt’s source, symbol and client_id (as parent_client_id), the child’s own levels and tif, and submitted_seq the attempt’s seq;
  3. else kind: unknown, every other string empty and every number zero.

So an activated bracket child, which the simulator books under its own record id without an activation event, appears here joined to the bracket that declared it. After a roll a re-established child joins to the open leg: parent_client_id is the leg’s client id and symbol the incoming contract, the venue’s truth; the roll record’s reestablished list ties it back to the strategy’s bracket. A logical_parent_client_id is a candidate addition under a schema /2 if a consumer needs the strategy’s parent on the entry itself.

Labels

FieldValues
sidebuy, sell, else unspecified
typemarket, limit, stop, stop_limit, stop_trailing, moo, moc, loc, stop_touch, else order_type_<n>
tifday, gtc, ioc, fok; unspecified for none (a wire or an engine roll leg that carries no time in force); else tif_<n>
reason (on cancel)unspecified, oco_triggered, tif_expired, explicit, rejected, shutdown, parent_cancelled, gtc_expired (a GTC order cancelled at the venue’s quarter-end deadline under --gtc-expiry ibkr-quarter), else cancel_reason_<n>

Numbers are JSON’s defaults (50, not 50.0); booleans and zero numbers are always written; attached is the only omitted key.

The summary

Every value is an integer. A strategy attempt is an accepted submit with source: strategy. An attempt’s own terminal is the first of: a fill with its broker id as origin, its client id and an empty parent_client_id (a child’s fill is the child’s, not the parent’s); a cancel with its broker id as origin and its client id, delivered or not; or an accepted cancel_request addressed to it. A terminal is sticky. An moc attempt settles at submission and is left out of the two resting counts. The counts, in the file’s order:

KeyDefinition
barsloop bars observed (warm-up bars and a roll’s synthetic cut bars are not loop bars)
signalsdistinct bar values among strategy submit records (any status) and strategy cancel_request records
submittedstrategy submit records with status accepted or denied
accepted, denied, rejectedstrategy submit records, by status
attached_childrenchildren declared on accepted strategy submits
replacementsaccepted strategy submits whose client id appeared on an earlier strategy submit of any status
first_eligible_evaluationsstrategy attempts that reached their first eligible evaluation: a loop bar of the attempt’s venue symbol, observed while the attempt had no terminal, and closing strictly after the submission clock when the attempt was placed from another symbol’s bar (the simulator’s own rule). An order submitted on bar N of its symbol counts on bar N+1; one submitted on the last bar does not count; one cancelled from another symbol’s bar before its own symbol’s next bar does not count
unfilled_restingstrategy attempts, moc excluded, whose own terminal is not a fill: cancelled (delivered or suppressed, requested or venue-made) or still live
fills, suppressed_fillsfill records with delivered true, false
cancel_requestsstrategy cancel_request records; cancels_accepted and cancel_rejects split them by status
explicit_cancelsdelivered cancel records with reason explicit
unsolicited_cancelsdelivered cancel records with reason tif_expired, gtc_expired, oco_triggered or parent_cancelled
shutdown_cancelsdelivered cancel records with reason shutdown
rejection_cancelsdelivered cancel records with reason rejected
suppressed_cancelscancel records with delivered: false
rollsroll records
roll_resubmits, roll_legssubmit records with source roll_resubmit, roll_leg
pinned_cancelledtotal length of pinned over roll records
working_at_endentries on the last snapshot; 0 without one

A delivered cancel with reason unspecified or an unlabelled code falls in none of the four cancel classes. The eligibility count and each attempt’s terminal are kept as the loop runs, not derived from the records afterwards: a bar observation is not a record, and the “no terminal yet” test at each bar needs the terminal set as it stood then.

Coverage

The trace covers backtest strategy runs, plain and continuous (the one bar loop, with a continuous series’ bars through its frame), and nothing else: live mode and the TRADES schema refuse the flag before any spawn. Within a run it does not record:

  • warm-up bars (the loops do not observe them: no records, not counted in bars) or a roll’s two synthetic cut bars (their events are recorded under the first incoming bar’s index);
  • a bracket child’s activation. The simulator emits no such event; a child shows through its own events (origin the parent’s, or after a roll the open leg’s, broker id; parent_client_id on its fill) and through the snapshot, under its own record id, joined to its declaration;
  • the rewriting of resting orders at a split. The simulator rewrites them silently; a rewritten order still working at the end is listed in the snapshot under its broker id, joined to its pre-split declaration (the quantity and levels as submitted, not as rewritten). The programme’s I14 increment owns the rewrite and the record for it;
  • corporate-action and roll notices, account-state pushes, and the strategy’s own view of its orders.

The trace reads the venue’s answers and returns them unchanged; it drops, re-orders and decides nothing, and the loops hold no trace at all without the flag.

Exit behavior

algo run exits 0 on success; 1 on any run failure, with a single algo: <reason> line on stderr (a multi-run config fails fast: the first failing run aborts the rest); 2 on usage errors. A strategy process exits 0 after a clean shutdown, non-zero when algolang.Run returns an error (init failure, protocol violation, an OnBar error). At end of run the engine cancels all remaining working orders (your OnCancel hears Shutdown as the reason) before OnShutdown runs.

Everything a strategy prints to stderr passes through to your terminal, prefixed [<binary>]; stdout belongs to the wire protocol – never print to stdout in a strategy, use ctx.Log or stderr.