Order lifecycle trace
--lifecycle-json PATH writes a second machine-readable artefact beside the fill blotter: the order lifecycle trace, schema algolang.lifecycle/1. --fills-json shows what filled. The trace shows everything the venue was asked and everything it answered, in the order the bar loop saw it:
- every submission, in the strategy’s frame and in the venue’s (on a continuous run the logical symbol with adjusted levels against the dated contract with raw levels; on an adjusted equity series the adjusted levels against the raw ones), with the venue’s answer:
acceptedunder a broker id,deniedwith the simulator’s reason, orrejectedby the engine before the venue saw it; - every cancel request the strategy made, and whether the venue honoured it;
- every fill and every cancel the venue produced, with the cancel’s reason (
explicit,tif_expired,gtc_expired,oco_triggered,parent_cancelled,rejected,shutdown) and whether the strategy was shown it — a continuous roll’s own legs and its cancels on the outgoing contract are suppressed from the strategy but present in the trace; - each continuous roll: the net position rolled, the standalone orders re-translated onto the incoming contract, the bracket children re-established on the open leg, and the contract-addressed orders cancelled;
- one snapshot of the working set, taken after the last bar’s submissions and before the end-of-data cancellation sweeps it;
- a summary of 25 counts (bars, signals, submissions by outcome, first eligible evaluations, unfilled resting orders, fills, cancels by class, rolls, the working set at the end), then the same
capital,equity,realizedandfillsthe fill blotter carries.
Use it when the question is an order’s lifetime rather than its P&L: why an order rested unfilled, and whether the simulator evaluated it at all (first_eligible_evaluations against unfilled_resting); what the shutdown cancellation swept away (working_at_end and the snapshot, each entry of which is followed by a shutdown cancel); whether a cancel request reached the book, and which attempt it addressed; which level and which contract a roll re-translated a resting order to, and under which new broker id; which orders the venue denied and why. It is the evidence base of the order-lifetime programme (GLE-366 is the increment that added it): the trace observes and decides nothing.
Trace on or off, a run’s fill blotter, printed report and BENCH line are byte-identical (timings aside), and the trace’s capital, equity, realized and fills decode equal to the --fills-json file of the same run. Two runs of the same input write the same trace.
It is a command-line option only, like --fills-json: no run-config key sets it (a config’s report.fills writes the blotter alone). It covers backtest strategy runs, plain and continuous: since GLE-421 both run through the one bar loop, a continuous series’ bars through its frame. Two cases refuse it before any adapter or strategy is spawned, so the run fails at once:
algo: engine: --lifecycle-json is a backtest-only export (live orders belong to the venue ledger)
algo: engine: --lifecycle-json needs a strategy run (schema BARS, not TRADES)
A write failure (a missing directory, say) fails the run, as the blotter’s does. The file’s layout is in The lifecycle trace schema.
A plain-loop example. The e9equity demo strategy buys 100 shares at market on its first bar and rests a GTC sell-stop at half that bar’s close. Run it over eight flat daily bars of a file-csv fixture (flat at 100, so the stop at 50 cannot trigger):
bin/algo run \
--adapter bin/file-csv-adapter --adapter-inputs Path=aapl-1d.csv,Interval=1d \
--strategy bin/e9equity --symbols AAPL --interval 1d \
--start 2026-03-10T00:00:00Z --end 2026-03-20T00:00:00Z \
--simulation-resolution bar --run-id lc-demo \
--fills-json fills.json --lifecycle-json trace.jsontrace.json holds five records. The file writes one key per line; it is re-flowed here, and the blotter section is elided:
{
"schema": "algolang.lifecycle/1",
"run": {"run_id": "lc-demo", "mode": "plain", "symbols": ["AAPL"]},
"summary": {
"bars": 8, "signals": 1, "submitted": 2, "accepted": 2, "denied": 0, "rejected": 0,
"attached_children": 0, "replacements": 0,
"first_eligible_evaluations": 2, "unfilled_resting": 1,
"fills": 1, "suppressed_fills": 0,
"cancel_requests": 0, "cancels_accepted": 0, "cancel_rejects": 0,
"explicit_cancels": 0, "unsolicited_cancels": 0, "shutdown_cancels": 1,
"rejection_cancels": 0, "suppressed_cancels": 0,
"rolls": 0, "roll_resubmits": 0, "roll_legs": 0, "pinned_cancelled": 0,
"working_at_end": 1
},
"records": [
{"seq": 1, "kind": "submit", "bar": 0, "at": "2026-03-10T00:00:00Z",
"source": "strategy", "client_id": "e9_entry_20260310T000000",
"symbol": "AAPL", "side": "buy", "type": "market", "quantity": 100,
"price": 0, "stop_price": 0, "trail_amount": 0, "tif": "day", "oco_group": "",
"venue_symbol": "AAPL", "venue_price": 0, "venue_stop_price": 0, "venue_trail_amount": 0,
"status": "accepted", "broker_id": "sim-1", "reason": ""},
{"seq": 2, "kind": "submit", "bar": 0, "at": "2026-03-10T00:00:00Z",
"source": "strategy", "client_id": "e9_stop_20260310T000000",
"symbol": "AAPL", "side": "sell", "type": "stop", "quantity": 100,
"price": 0, "stop_price": 50, "trail_amount": 0, "tif": "gtc", "oco_group": "",
"venue_symbol": "AAPL", "venue_price": 0, "venue_stop_price": 50, "venue_trail_amount": 0,
"status": "accepted", "broker_id": "sim-2", "reason": ""},
{"seq": 3, "kind": "fill", "bar": 1, "at": "2026-03-11T00:00:00Z",
"delivered": true, "origin": "sim-1", "client_id": "e9_entry_20260310T000000",
"parent_client_id": "", "symbol": "AAPL", "venue_symbol": "AAPL",
"side": "buy", "quantity": 100, "price": 100, "venue_price": 100, "commission": 0},
{"seq": 4, "kind": "snapshot", "bar": 7, "at": "2026-03-19T00:00:00Z",
"working": [
{"broker_id": "sim-2", "client_id": "e9_stop_20260310T000000", "venue_symbol": "AAPL",
"kind": "top", "source": "strategy", "parent_client_id": "",
"symbol": "AAPL", "side": "sell", "type": "stop", "quantity": 100,
"price": 0, "stop_price": 50, "trail_amount": 0, "tif": "gtc",
"venue_price": 0, "venue_stop_price": 50, "venue_trail_amount": 0,
"submitted_seq": 2}]},
{"seq": 5, "kind": "cancel", "bar": 7, "at": "2026-03-19T00:00:00Z",
"delivered": true, "origin": "sim-2", "client_id": "e9_stop_20260310T000000",
"reason": "shutdown", "triggered_by": ""}
],
"capital": 100000, "equity": 100000, "realized": 0,
"fills": [ ... ]
}Reading it:
- Records 1 and 2 are the two orders bar 0 returned, stamped at that bar’s close (
aton a submission is the simulator’s clock) and accepted as attemptssim-1andsim-2. On this unadjusted series the two frames coincide (symbolandvenue_symbol,stop_priceandvenue_stop_price); on an adjusted equity series or a continuous run they differ. - Record 3: the market buy filled on bar 1, the next bar of its symbol (an order decided on bar N first evaluates on bar N+1).
originnames the attempt the fill descends from, anddeliveredsays the strategy received it. - Record 4: the snapshot after the last bar (bar 7) lists the stop, still working, joined (
kind: top) to the record that submitted it (submitted_seq: 2). - Record 5: the shutdown cancel that swept it, after the snapshot, so
working_at_end: 1andshutdown_cancels: 1describe the same order. - The summary:
first_eligible_evaluations: 2— both attempts reached a bar on which the simulator evaluated them;unfilled_resting: 1— the stop’s own terminal was a cancel, not a fill;signals: 1— one bar carried strategy activity.