Skip to content

Backtesting

A backtest is one historical simulation of a strategy on an instrument over a date range. It is the unit everything else builds on — the gauntlet stages are all variations of it, and the metrics it produces feed the research ladder.

  • Backtests read only from the local data catalog, never a live API.
  • Strategies act on closed bars, and market orders fill on the next bar — so there is no look-ahead, and the same inputs always produce the same outputs.
  • Costs are modeled: IB-style commissions, and (for intraday) the measured spread per fill.

A backtest is an asynchronous job — the API never runs the engine itself (see architecture):

  1. Submit — from the Backtests tab (or POST /api/backtests). A BacktestRun row is written with status QUEUED and a job is enqueued on Redis.
  2. Run — the worker picks up the job, resolves the strategy from the registry, and runs it on a NautilusTrader BacktestEngine fed by the catalog. Progress is published to Redis.
  3. Persist — artifacts (equity curve, fills, positions, and per-kind JSON) are written to ./artifacts/<run-id>/, and the summary metrics to the run row; status becomes DONE (or FAILED with the error).
  4. Review — the UI renders the result: full metrics, the candlestick chart with fills, the mark-to-market equity curve overlaid with buy-and-hold, a drawdown subplot, a calendar-year vs buy-and-hold table, the round-trip trades, and a Monte Carlo panel.

Pick a strategy, set the symbol, date range, starting cash, and commission model, then choose a Mode. Each mode is a run kind:

  • Single backtest — one run (above).
  • Walk-forward / WFO, Optimize, Rule test — the gauntlet stages; see the gauntlet.
  • Cross-sectional / Pairs / Portfolio / Portfolio-search / Book — the multi-instrument builders; see multi-instrument engines.

Tick up to 3 runs in the log to compare them side by side. The runs log is paginated and filterable, and every run has a deep-linkable detail view.

For intraday (1-minute) strategies, fills are charged the measured spread from the ingested data’s sidecar (--slippage-bps auto), so the whole ladder runs net of honest costs. Metrics annualize with the right factor for the cadence automatically — see metrics.

python -m ats.research --strategy <key> --symbols SPY,TLT,GLD runs the full ladder for a strategy in one command (the registry imports fresh each invocation, so a brand-new strategy file works without a worker restart). See command-line research.