Strategies

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:

SettingMeans
Series and intervalWhat it runs on, and the bar size
From, toThe window. The last 30 days by default
CashThe starting balance
Fee and slippage (bps)What each trade pays. A pool's own fee is always charged
PyramidingThe most entries one position may take
Stop, take, trail (%)Exits the broker places at a bar close, on top of the code's own
LeverageOn a perp

The backtest runs the same compiled program a live run would, against the same paper broker.

Read the report#

  1. Did every order fill? An order that couldn't (not enough cash, nothing to sell) reverts, and the report says why.
  2. Is the trade count what the idea implies? Under quiet, the report lists every when or while that never fired, and whether its condition was na throughout, never true, or true and held back.
  3. 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.
  4. What did it risk? Worst drawdown, win rate, average trade, exposure, and its round trips, each with entry, exit, P&L and time held.
  5. Why did it trade? Every metric() it publishes is returned bar by bar. Chart them against price to see each decision.
Publish what it decides on

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:

Terminal
ftw-trade local backtest ema.flow --series feed:btc-usd-chainlink --interval 1h
ftw-trade local backtest ema.flow --interval 1h --csv btc-1h.csv

See The ftw-trade CLI.