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.
{ "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 (
nullisna) or generators:constant,ramp,sine,stepsand seededwalk, each with bars knocked out asna. - Expectations:
fires,fills,reverts,paused, ametricthat equals a value or isna, acheckover the program's values (at a bar, or ever, always, never, last) andwarns. runs: Nrepeats 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.
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: pauserefuses new entries;flatten_and_pausealso 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.