Articles ·

Backtest audit, part 3: how the results are shown

Backtest audit, part 3: the ledger, baskets on one account and the optimizer's held-out check

Part 1 fixed how the engine trades, part 2 what it is given. This part is about the figures it hands back: a correct simulation is still misread if a total adds text, a drawdown skips the worst bar or a percentage rounds 249 of 250 up to all.

The ledger adds numbers, not text

The engine sends money as exact decimal strings. The ledger of saved backtests added a basket's markets as text, so "10.5" and "-2.25" became "010.5-2.25" and every basket line showed NaN for its result and return. It also grouped any backtests of one version on one window as a basket, so a backtest run twice with different fees read as "2 markets", summed.

Now a basket is recorded as one: its markets carry its id, and its line in the ledger shows what its account did as a whole — result, return and drawdown — next to how many markets made money.

A basket is one account, bar by bar

A basket's account used to be added up from each market's chart line: 200 points per market, at bar closes only. That missed falls inside a bar and, because each market kept different moments, could add two falls that never happened together. Now it is reckoned on every bar of every market — its lowest, highest and closing equity and the notional it held — and markets that fall in the same bar are taken to reach their lows together.

A basket of two markets on 1,000 USDT: one holds a 1,000 USDT long through a bar that falls to 88 and closes at 99. At closes the account fell 10 USDT; inside the bar it fell 120
The same bar: 1% at the close, 12% inside it, as the market alone reports.

How it all adds up: How a Metrand basket backtest adds markets into one account.

The optimizer's held-out check stays held out

The optimizer ranks combinations on the first part of the window and checks them on the rest. Its map coloured the cells by the held-out result, which invites picking there and turns the check into another fit. The map now shows the in-sample result, where a pick is made; the held-out map is a switch, labelled as a check. The results also say how many combinations would look positive on both sides by chance — the share positive in-sample times the share positive out-of-sample — and each result shows its return in percent beside the USDT.

One meaning for each figure

In the API

A basket answer carries basket_id, and its portfolio now has per_year_pct (instead of cagr_pct), ruined_at, over_margin and years[].partial. POST /backtest takes "marks": true and returns every bar's low, high, close and held, so a caller can add backtests into one account the same way. Reports carry avg_trade_return_pct.

Next

The last part, part 4, checks the engine against an independent reference simulation, and that no decision can read a price from after it was made.