backtest_run (or the REST API receives POST /api/runs with kind: "backtest"), Ithaca loads the referenced StrategySpec, fetches an aligned price panel, and runs a multi-asset accounting kernel. The result is a deterministic equity curve, a stats block, and a risk attestation — all reproducible to the bit.
Pipeline
1
Load spec
The engine loads the immutable
StrategySpec by id + version. The spec is never mutated.2
Fetch price panel
An aligned multi-asset price panel is fetched from the configured data source (e.g. Polygon). The panel is a single numpy array of shape
(T, N, K) — time × names × fields (open, high, low, close, volume). All names are aligned to the same trading calendar; missing bars are forward-filled then flagged.3
Run accounting kernel
The kernel computes signal → weights → returns with no lookahead: weights computed at time
t-1 are applied to returns at time t. This is the single most important invariant in the engine.4
Compute stats
Performance and risk statistics are computed from the equity curve.
5
Emit artifacts
The 8-file artifact bundle is written and content-hashed. See Artifacts.
No-lookback invariant
weights[t-1] are applied to return[t]. The kernel never reads a bar at time t to decide the weight at time t. Violating this would be a critical bug and is covered by the test suite.t cannot influence the weight applied to day t’s return.
Return values
A successfulbacktest_run returns:
stats block
riskAttestation
violations lists the bars and the breach. A non-empty violations array does not fail the run, but it is surfaced to the human reviewer.
Job lifecycle
A backtest run moves through a state machine:draft | Spec is attached; run not yet enqueued. |
| queued | Run is in the work queue waiting for a worker lease. |
| running | A worker has leased the run and is executing the kernel. |
| succeeded | Kernel completed; artifacts written. |
| failed | Kernel raised an error. Error captured in the trace. |
| timeout | Run exceeded its tier’s wall-clock budget. |
| cancelled | A human or the system cancelled the run. |
Transitions are emitted as run_transition trace events and streamed over SSE. See Run endpoints for the REST API and Trace events for the event contract.
Determinism
A backtest is reproducible if and only if these three things are held constant:seed— the RNG seed from the spec (default42).- Pure numpy — the kernel uses only deterministic numpy operations. No
random.random(), no GPU non-determinism, no floating-point reordering across runs. - Fixed window — the same price panel (same symbols, same calendar, same bar range).
environment_lock.json that pins the numpy version and panel hash so a future re-run can be verified.
Related
- StrategySpec — the declarative input
- Artifacts — the 8-file output bundle
- Approval workflow — promoting a backtest to paper trading
- Run endpoints — REST API for creating and inspecting runs