Flowscript

Tests and risk limits

Write tests that prove a strategy buys the move it should and holds still when it should, and an invariants block that refuses entries once it loses too much.

Tests#

A test feeds a strategy bars you write and checks what it does with them, on the backtest's paper broker. Saved tests run on every save, and a strategy with a failing test can't be started.

JSON
{ "name": "buys the step up once, never sells", "bars": 80,
  "ohlc": { "steps": [[0, 100], [30, 130]] },
  "expect": [
    { "fires": "paper.buy", "at": 30, "args": { "quote": 100 } },
    { "fills": "paper.buy", "at": 30 },
    { "fires": "paper.sell", "count": 0 } ] }
  • Bars are lists (null is na) or generators: constant, ramp, sine, steps and seeded walk, each with bars knocked out as na.
  • Expectations: fires, fills, reverts, paused, a metric that equals a value or is na, a check over the program's values (at a bar, or ever, always, never, last) and warns.
  • runs: N repeats a case over N random walks: the way to state a rule that must hold in any market, such as "never more than a fifth of equity in".

Good cases to write: a step up, a crash, a gap, a flat market, and a missing bar.

Risk limits: invariants#

An invariants block is a cage around a strategy. While a bound is breached (or can't be read), every entry is refused and the refusal recorded. A sell is never refused.

Flowscript
const close = bars.close(market, interval);
when (crossover(close, ema(close, 20)) && !inPosition()) { paper.buy({ quote: 100 }); }
when (inPosition() && crossunder(close, ema(close, 20))) { paper.sell({}); }
invariants {
  drawdown() <= pct(5);
  dailyLoss() <= 50;
  trades.perDay() <= 4;
  on_breach: pause;
}
  • on_breach: pause refuses new entries; flatten_and_pause also sells everything when a drawdown or daily-loss bound breaks.
  • Backtests, tests and live runs enforce the same cage. A backtest lists refused entries under paused; a test can expect one with { "paused": "paper.buy", "by": "drawdown" }.

Platform limits#

ftw.trade adds limits of its own, so a program can't overrun the servers or your account: a step budget per bar, caps on series length, fold state and list sizes, and on how many orders one bar may place. A program that hits one is refused with the limit it hit.