DocsBacktestsThe backtest lifecycle
BacktestsGuide

The backtest lifecycle

What actually happens when a backtest runs: the per-minute spine the engine walks, the fixed phase order inside each minute, and the session edges that bound a trading day.

Updated Jul 20265 min read

The minute spine

A run materializes a per-minute spine over the NYSE trading sessions inside its [start, end] range, one row per backtest minute, and steps every phase along it. The spine is the loop driver. It is built from the raw NYSE calendar and trimmed by the session offsets described under session edges, so on a normal day the first row is 09:35 and the last is 15:55.

Half-days are handled from the calendar itself. A 13:00 early close yields a 12:55 last minute with no configuration.

Everything you attach to the backtest is read against this spine, one value per minute: the entry and exit signals, each adjustment trigger, and each leg's contract selection. This spine is the backtest engine's own clock. It is distinct from the signal spine you write indicators against (see the execution model); each per-minute signal value is aligned onto the backtest spine before the engine reads it.

A run covers one symbol. See data coverage for which symbols carry backtest history.

The per-minute phase order

Each backtest minute runs the same five phases, in this order:

  1. Splits (see below)
  2. Force-close (expiration)
  3. Exit
  4. Adjustments
  5. Entry

The order is deliberate. A position that should close closes before anything new opens, so force-close and exit run first. Entry runs last so it opens against the book after that minute's exits and adjustments have settled.

Each phase reads the book at its own point in the minute:

  • Exit sees the pre-exit book.
  • Adjustments see the post-exit book.
  • Entry sees the post-adjustment book.

This is why capital an exit frees at minute T is available to an entry at the same minute T (through the margin gate), and why a position cannot both be adjusted and force-closed for expiration in an inconsistent order: expiration force-close has already run by the time the adjustment phase looks at the book.

The account's risk checks also run inside each minute, alongside these five phases: a margin and deficit check can force positions closed before the strategy's own exit phase. Those mechanics live on margin and auto-decomposition.

Splits

Split adjustment is gated to the first session-start minute of each date. When a split lands, every open leg's strike and multiplier are adjusted exactly (the transform uses exact integer arithmetic, so the strike times multiplier notional is preserved to the cent), and the leg is re-quoted on the post-split contract.

Dividends, special dividends, and spinoffs are not modeled in the backtest. A non-standard adjusted contract has no standard-chain forward quotes, so the run fails loud and stops rather than guessing at a synthetic contract. See stock price adjustments for how the underlying series itself is adjusted.

Session edges

Two offsets trim each session, and both default to 5 minutes:

  • Open offset adds minutes to the raw NYSE open. At the default of 5, the first eligible minute is 09:35.
  • Close-before-expiration subtracts minutes from the raw NYSE close. At the default of 5, the last eligible minute is 15:55 on a normal day and 12:55 on a 13:00 half-day.

Positions are force-closed at the session-close minute of their earliest leg expiration. For a multi-leg position, that is the short-side expiration on a calendar or diagonal, not the long side. Options are never carried into in-book expiration settlement.

These 5-minute bounds are engine session offsets. They are not a data-accessor window, so they do not change how a signal's rolling windows count observations.

How results stream

Results stream in batches while the run progresses. The engine flushes a batch at each calendar-month boundary of session days, and each batch carries:

  • the equity and margin points for that span,
  • the rows and closing summaries for every position that closed in that span, and
  • that span's action-ledger events.

After the final session, a terminal end summary carries the authoritative final NLV (net liquidation value) and every position still open at the end date. A position whose earliest leg expires past the end date is never force-closed, so it stays open and its mark is reflected in the final NLV.

A mid-stream failure surfaces as an error and closes the stream. The run does not silently truncate to a shorter-than-requested result.

You read the assembled output on reading backtest results.

The action ledger

The action ledger is the per-minute event stream of everything the engine did: entries, exits, adjustments accepted and rejected, deficit and margin-warning events, splits, and the halt. It is the audit trail behind the equity and margin curves. When a curve moves and you want to know which action moved it, the ledger is where the answer is.

Saving and extending a run

A run is persisted only when the request supplies both a name and a slug and the run reaches its end date cleanly. A halted run never persists, and a run missing either field persists nothing.

Extending a saved run to a later end date restores the run's captured state and steps only the new sessions, as long as every version and fingerprint gate matches: the same symbol, the same original start date, the same request, and the same engine version. On any mismatch the extend re-runs from the original start rather than resuming from a state that no longer applies. A resumed extend produces the same result as a single cold run over the full range.

A run that originated from a backfill and carries no stored request cannot be extended, because there is nothing to reconstruct it from.

Halts

A run halts when NLV reaches zero or below at any minute. equity_below_zero is the only halt reason.

On halt the engine:

  1. force-closes every open position at marketable bid/ask fills, regardless of the run's fill mode,
  2. skips the rest of the spine,
  3. pads the equity and margin curves flat at the halt NLV through the end date, and
  4. produces no resume snapshot.

Because a halted run produces no snapshot, it cannot be saved or extended. The final NLV in the results is the post-liquidation figure at the halt minute, which the marketable closing fills can push further negative.

Was this page helpful?