# 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

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.

> [!TIP] 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:

```text
// @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:

```sh
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](https://docs.ftw.trade/agents/cli).
