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:
- Replay the strategy.
- 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.
- Replay it again.
- 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.
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.
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:
- A decision taken at a bar's close reaches the venue at the next bar's open. A market order fills at the touch moved by the slippage against the account; an entry against the position held leaves it there first. A post-only order priced through the open's touch is rejected; otherwise it rests until the market trades through its price, or until it has waited its steps.
- An entry must fit the buying power when it trades and the largest position the market's margin schedule allows at the account's leverage; a declared size beyond the starting buying power is refused.
- Funding settles on the position held at a bar's open; a payment to the account is kept only if the bar ends with the same position.
- A bar is walked open → low → high → close and open → high → low → close. On each leg the price meets the liquidation price, the stop, the target and resting orders in the order it reaches them; a long leaves into the bid, a short into the ask. The walk that leaves the account worth less at the close is kept, and the drawdown counts the worse of the two.
- Exits pay slippage and the taker fee; a liquidation takes the whole wallet.
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.
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.