Articles ·

How the Metrand backtest is tested

Every rule the backtest settles by has tests of its own: a stop gapped through, a bar that reaches the stop and the target, a resting order the price never trades through. Two more checks hold the engine as a whole to two properties. Both run with the engine's other tests, on every change to it.

1. The past does not depend on the future

The check

For a strategy and a market:

  1. Replay the strategy.
  2. Pick a moment T. Replace every candle that opens at T or later with a different random continuation, and the finer candles inside them, the candles of any other market the strategy reads and the funding rates from T on.
  3. Replay it again.
  4. Compare everything before T. It must be identical, to the last digit:
    • every decision the strategy took on a bar that opened before T, compared as the whole plan it emitted — side, size, stop, target and the prices it saw;
    • the lowest, highest and closing equity of every bar that opened before T;
    • every trade that closed before T, every field of it.

A bar that opens before T has closed by T, so all of it may be known before T; nothing that opens at T or later may be.

Two replays of one strategy: identical up to the moment T, a different future after it. Before T every decision, every bar's equity and every closed trade must be identical
The check: change the future, keep the past.

Why decisions, and not only trades

A decision taken on a bar's close trades at the next bar's open. If a strategy decided at the close of bar k−1, at T, using bar k's close, the replay with a different bar k would change that decision — but the trade it makes starts at bar k's open, after T, and every trade before T would still match. Only the decision shows the peek, so the check compares decisions.

Candles k−3 to k+1. A decision at the close of bar k−1, at T, that reads bar k's close; the trade it makes starts at bar k's open, after T
A one-bar peek is visible in the decision, not in the trades.

What it covers

Strategies What they do
Every indicator in the catalog, 32 long while the indicator rises, short while it falls, at market
The five templates EMA crossover, Momentum, Mean reversion, Volatility breakout, Trend momentum basket, as published
A grid and a martingale a buy grid under an oversold market; a short martingale over an overbought one
Managed exits and a session enters post-only on momentum and volume between 07:00 and 16:00 UTC; leaves after six bars or a bar wider than the average
Another market long BTC while ETH outpaces it, short while it lags

Each runs in eight ways of asking for a replay: plain; warmed up on 100 hours before the window; with funding; at 20× under a three-step margin schedule; on range bars sized in percent of the day's close and in price steps; deciding on hours while trading on their quarter-hour candles; and under the earlier signal-close model. The market is 360 hours of quarter-hour candles with calm, busy and violent spells, gaps between candles and a jump now and then, so stops are gapped through, stops and targets share candles and leveraged accounts are liquidated. In all: 500 replays, holding 83,751 decisions, 57,681 bars and 11,587 trades equal before their cuts.

Does it catch a peek?

A check that never fails proves nothing, so it was run once against an engine with a peek planted in it: every bar's volume replaced by the next bar's. It failed on Relative Volume, on the decision taken on the bar just before the cut.

2. A second simulator

The check

The rules are written a second time, apart from the engine and as plainly as they can be: one position, one resting order, one ladder, a decision on its way to the next open, and on each leg of a bar a list of what the price can meet there. This second simulator knows the engine only through its public calls. The rules it implements:

The details of each rule are in How a Metrand backtest fills orders inside a bar and How a Metrand backtest prices liquidation and the largest position.

4,000 random scenarios go through both simulators: accounts with fees from 0 to 10 bps, slippage up to 10 bps, leverage from 1× to 50×, a flat or three-step margin schedule and expiring resting orders; markets with gaps, flat bars and long wicks, funding at bar opens and inside bars; and random decisions — market and post-only entries both ways, turns, managed exits, grids and martingales. Both must agree to the last digit on every field of every trade, every bar's lowest, highest and closing equity, the drawdown and every count.

What the 4,000 scenarios covered: 39,529 stop-losses, 18,366 maker entries, 16,088 take-profits, 13,690 managed exits, 10,917 ladder positions, 10,351 bars decided by the worse way, 8,661 expired orders, 8,090 trades with funding, 6,469 post-only rejections, 5,122 entries over the position limit, 665 liquidations and 598 positions closed at the end of the data
The check also fails if the scenarios stop covering any of these.

Does it catch a mistake?

Planted mistake Caught on
A post-only buy priced exactly at the ask accepted instead of rejected the 3rd scenario
A funding payment to the account kept on a bar that closed the position the 3rd scenario
A tie between a bar's two ways resolved the other way the 15th scenario
A ladder's remaining orders left resting after its position closed the 3rd scenario

What the checks do not cover

They show that the engine follows its rules and never reads the future. Whether the rules are an exchange's is a different question, answered rule by rule against the exchanges' documentation in the audit, and it leaves stated approximations: stops trigger on a bid or ask built half a spread from the bar's prices rather than on the last trade, liquidation is priced on the mid rather than the mark price, and a candle has no order book, so a market order fills at its touch plus the run's slippage whatever its size.

Run the first check yourself

You can repeat the look-ahead check on your own strategy without any code. Backtest it twice on one market over two windows that start on the same day — for example, hourly bars from 1 January to 31 March, then from 1 January to 30 June. Every trade the shorter backtest closed before its end must be the same trade in the longer one, to the cent; only a position still open at the end of March differs, because the shorter backtest closes it there. Both windows warm up on the same bars before 1 January, as long as neither comes near 50,000 bars.

In the engine's API, POST /backtest with "marks": true also returns every bar's lowest, highest and closing equity, so the same comparison can be made bar by bar.