Strategies

How strategies work

A strategy is Flowscript run once per closed bar. The same code backtests over history and runs live, so what you tested is what trades. What a strategy can trade, how a run behaves, and the rules every run follows.

One program, two uses#

A strategy is a short program in Flowscript. It reads a market's bars, computes values, and fires orders when its conditions turn true.

  • In a backtest it steps through history, bar by bar, against a paper broker with fees and slippage.
  • In a run (a deployment) it steps through each new bar as it closes, on ftw.trade's servers, from the next bar after you start it.

Flowscript has no loops and no hidden state, so the same bars always give the same decisions. That's why a backtest and a live run agree, and why ftw.trade can check that they keep agreeing.

What a strategy runs on#

MarketSeriesBars
A pool on any indexed EVM chain or Solanapool:<chain>:<address>5s, 15s, 1m, 5m, 15m, 1h, 4h, 1d
A Hyperliquid or Lighter perp, builder perp or spot pairperp:<venue>:<symbol>1m and up (5s and 15s from its recorded book)
A shared price feed (crypto, stocks, commodities)feed:<name>1m and up
A Polymarket outcomepoly:<token id>5m and up
One of your own sourcessource:<name>its events made into bars

What a strategy can do#

  • Spot: buy and sell, by amount or by percent of equity, with resting limit and stop orders.
  • Perps: go long, short or flat, rest maker bids and asks on the book, and cap an order to what the book holds near the price.
  • Liquidity: hold a range on a pool and move it by your rules.
  • Hedges: short what a liquidity range holds on a perp, and keep the perp account topped up.
  • DeFi: supply, borrow and repay on lending markets, deposit in vaults, hold fixed rates on Pendle, trade options on Derive, or let earn find the best rate.

Strategies over more than one market (a range hedged on a perp, a lending loop, options hedged with perps) are covered in Hedged and multi-market.

How a run behaves#

  • It decides on closed bars. Each bar decides once, once its indicators have enough history (it replays warm-up bars before its start). On a pool, 5s and 15s bars decide within seconds of the swaps that make them.
  • It runs the code it started with. Saving a new version marks the run outdated but changes nothing it does. Start a new run for the new code.
  • It holds still on stale data. If a market's data stops arriving, the run waits rather than trading on old prices, and says so.
  • It guards against drift. If replaying its bars ever stops matching the orders it already sent, it stops itself with the reason, so a live run never quietly diverges from its backtest.
  • It explains itself. Each run keeps its metrics bar by bar, every decision, order and fill, and every error, in the Log.

The rules every strategy should follow#

  1. Entries go in when, not while. A when fires once as its condition turns true; a while fires every bar it holds and can spend all your cash.
  2. Every entry has an exit: a when that sells, a stop from the entry price, or the run's stopPct, takePct and trailPct.
  3. Sizes are numbers or settings as written. Use paper.buyPct({ pct }) to size by equity.
  4. Compare with the previous window: close > highest(close, n)[1], not highest(close, n), which includes the bar itself.
  5. na means unknown, not false. A trigger never fires on na.
  6. Add risk limits to anything that runs unattended with real money: a drawdown bound, a daily loss bound and a trade count, in an invariants block.

ftw.trade's compiler warns about most of these (a while that buys with no guard, a condition that is never true, a divide by zero) by line, before you save.

Next#