Backtest runner
@qtrader/backtest-runner
Backtest a trading idea on an OHLCV CSV with honest assumptions: next-bar fills, slippage, commission, no look-ahead. Built-in SMA crossover, RSI mean reversion, and buy-and-hold, plus a one-file plug-in format for your own strategies. Writes equity curve and trades CSVs and metrics (CAGR, volatility, Sharpe, Sortino, max drawdown, win rate, exposure). Not financial advice.
- Activity
- New: be the first to try it
- Installs
- 0 installs
- Likes
- 0 likes
- Clones
- 0 clones
- 1# backtest-runner
- 3Test a trading idea on historical prices before you risk anything. Give it an OHLCV CSV and a strategy (SMA crossover, RSI mean reversion, buy-and-hold, or your own one-file plug-in) and it writes an equity curve, a trade list, and the metrics people actually compare: CAGR, volatility, Sharpe, Sortino, max drawdown, win rate, and exposure.
- 5**Who it is for:** people learning systematic trading who want honest numbers, not a curve that only works because it peeked at tomorrow.
- 7Not financial advice. Backtests describe the past under simplified assumptions.
- 9## How it stays honest
- 11- Decisions use bars up to today only. Strategies get a read-only view where tomorrow reads as `undefined`.
- 12- Orders fill at the **next bar's open**, never at the close that produced the signal.
- 13- Slippage (default 5 bps per side) and commission (per share and/or bps) are charged on every fill.
- 14- Integer shares by default, long only unless you pass `--allow-short`.
- 15- A signal on the last bar is never filled. Open trades at the end are reported as open, not counted as wins.
- 17## Example
- 19```bash
- 20node scripts/backtest.mjs --data examples/prices.csv --strategy sma-cross --param fast=10 --param slow=30
- 21```
- 23```text
- 24sma-cross {"fast":10,"slow":30} on examples/prices.csv, 2024-01-02 to 2025-03-24 (320 bars)
- 25Total 0.37% | CAGR 0.29% | Vol 11.18% | Sharpe 0.08 | Sortino 0.11 | Max DD -16.60%
- 26Trades 5 (+0 open) | Win rate 40.00% | Exposure 42.50% | Buy and hold 9.38%
- 27```
- 29`examples/prices.csv` is a made-up series (a trend plus cycles plus seeded noise), not real market data. Files written: `out/backtest-equity.csv`, `out/backtest-trades.csv`, `out/backtest-metrics.json`.
- 31Your own strategy is one file. See `examples/strategies/donchian-breakout.mjs`:
- 33```bash
- 34node scripts/backtest.mjs --data data/SPY-1Day.csv --strategy-file strategies/my-idea.mjs --param lookback=50
- 35```
- 37## Hand check (verify it yourself)
- 39**Costs and fills.** Bars (open, close): d0 (100, 100), d1 (101, 104), d2 (105, 103), d3 (102, 106), d4 (107, 108). Capital 10,000, slippage 10 bps, commission $0.01 per share. Signal long on d0's close, flat on d2's close.
- 41- Buy on d1 open: 101 x 1.001 = 101.101. Shares = floor(10,000 / (101.101 + 0.01)) = 98. Cash = 10,000 - 98 x 101.101 - 0.98 = 91.122.
- 42- Equity d1 = 91.122 + 98 x 104 = 10,283.122. Equity d2 = 91.122 + 98 x 103 = 10,185.122.
- 43- Sell on d3 open: 102 x 0.999 = 101.898. Cash = 91.122 + 98 x 101.898 - 0.98 = 10,076.146.
- 44- Trade pnl = +76.146 after costs. Exposure = 2 of 5 bars = 40%.
- 46**Metrics.** Buy and hold on closes 100, 100, 110, 121, 108.9, 119.79 (open = close), 10,000 capital, fractional, no costs. The signal on bar 0 fills at bar 1's open (100), so equity is 10,000, 10,000, 11,000, 12,100, 10,890, 11,979.
- 48- Returns: 0, 0.10, 0.10, -0.10, 0.10. Mean 0.04. Sample variance 0.032 / 4 = 0.008.
- 49- Sharpe = 0.04 / sqrt(0.008) x sqrt(252) = 7.0993. Sortino = 0.04 / sqrt(0.01 / 5) x sqrt(252) = 14.1986.
- 50- Max drawdown = 10,890 / 12,100 - 1 = -10%. Total return 19.79%. Exposure 5 of 6 bars.
- 52**SMA cross.** fast 2, slow 3 on closes 10, 9, 8, 9, 10, 11, 10, 9, 8. On bar 4, SMA2 = 9.5 > SMA3 = 9, so buy on bar 5's open (11). On bar 7, SMA2 = 9.5 < SMA3 = 10, so sell on bar 8's open (8).
- 54`node scripts/selftest.mjs` checks all of these, plus Wilder RSI by hand, a short sale, a long-to-short flip, and that strategies cannot see or change future bars.
- 56## What it can access
- 58- Reads your price CSV from `data/` and strategy files from `strategies/`. Writes results to `out/` and new strategy files to `strategies/`.
- 59- Runs only its own scripts with Node. No network, no API keys.
- 61## Setup
- 63Node 22 or newer. Nothing to install. Get prices from any source as CSV, or use the `alpaca-connector` kit: `node scripts/alpaca.mjs bars --symbol SPY --start 2021-01-01 --out data/SPY-1Day.csv`.
