# 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.
