Backtest and optimize
Replay a strategy over history with fees, slippage and funding, read the report honestly, search its settings without fitting noise, and check it on data it never saw.
Run a backtest#
Open a strategy and press Backtest, or call backtest_strategy from your AI or ftw-trade strategies backtest from the CLI. You choose:
| Setting | Means |
|---|---|
| Series and interval | What it runs on, and the bar size |
| From, to | The window. The last 30 days by default |
| Cash | The starting balance |
| Fee and slippage (bps) | What each trade pays. A pool's own fee is always charged |
| Pyramiding | The most entries one position may take |
| Stop, take, trail (%) | Exits the broker places at a bar close, on top of the code's own |
| Leverage | On a perp |
The backtest runs the same compiled program a live run would, against the same paper broker.
Read the report#
- Did every order fill? An order that couldn't (not enough cash, nothing to sell) reverts, and the report says why.
- Is the trade count what the idea implies? Under quiet, the report lists every
whenorwhilethat never fired, and whether its condition wasnathroughout, never true, or true and held back. - Does it beat holding the market? Compare its return with buy-and-hold, and over more than one window. A strategy that made money in one month only may have been that month.
- What did it risk? Worst drawdown, win rate, average trade, exposure, and its round trips, each with entry, exit, P&L and time held.
- Why did it trade? Every
metric()it publishes is returned bar by bar. Chart them against price to see each decision.
A strategy that calls metric("fast", fast) and metric("slow", slow) can be read bar by bar. Without metrics, a quiet backtest is a guess.
Optimize, honestly#
Optimize searches a strategy's settings: every combination when they fit in the trial budget, otherwise a genetic search. It ranks them on the training span only, then checks the best on a holdout, the end of the window no choice ever saw.
- Choose the objective (return, Sharpe and others), the trials, the holdout share and whether to walk forward: re-fit on a rolling window and test each fit on the stretch after it.
- The answer comes with a verdict, the holdout return, the best setting against the one you use now, and a deflated Sharpe ratio: the odds it has an edge after counting every setting tried.
- Apply the best as the strategy's defaults, or turn a running copy's knobs to it.
Read the landscape, not just the peak: a broad plateau of good settings is an edge; a lone peak the holdout doesn't share is luck. Strategy dashboards can draw it as a surface over two knobs.
Sweeps#
A sweep backtests a strategy over a grid of settings and windows in one call (backtest_sweep), with walk-forward, so you can see how stable a result is across time before you trust any one number.
Put the settings in the code#
A strategy can carry how it's tested and run, one comment line each, so anyone (or any AI) can rerun it exactly:
// @backtest { series: "perp:hyperliquid:BTC", interval: "5m", days: 90, cash: 1000 }
// @optimize { objective: "sharpe", trials: 200, holdout: 30, walkForward: true }
// @deploy { cash: 500 }Backtest on your machine#
The CLI ships the Flowscript compiler and ftw.trade's host as WebAssembly, so a local backtest makes the same trades the server does, on ftw.trade's data or on your own CSV:
ftw-trade local backtest ema.flow --series feed:btc-usd-chainlink --interval 1h
ftw-trade local backtest ema.flow --interval 1h --csv btc-1h.csvSee The ftw-trade CLI.