Articles ·
What a Metrand backtest accepts as market data
A backtest is only as true as the bars it is given. A single malformed candle can open a position at a price the market never traded, a timestamp off by a factor of a thousand can turn a year into a few hours, and bars sent newest-first can make a strategy decide on the future. So the engine checks every bar before it replays anything, and refuses the request with the first bar that fails and the reason.
The same checks apply to the bars of the market the strategy trades, to every other market it reads, and to the finer candles that can be sent under the bars.
Every bar
| Field | Rule |
|---|---|
o, h, l, c |
finite numbers above zero |
h, l |
the high at or above both the open and the close; the low at or below both |
v |
finite and not negative (it may be left out, and is then 0) |
t |
the bar's open time, in milliseconds since 1970-01-01 UTC, not negative |
The series
- Forward in time, once each. Every bar opens after the one before it. A duplicate or a bar out of order is refused.
- Milliseconds, not seconds. Bars less than a second apart are refused: their timestamps are almost certainly in seconds.
- Open times, not close times. A series stamped at each bar's close (
open + timeframe − 1 ms, as some APIs return next to the open) is refused: the engine would otherwise treat every bar as one bar later than it was. - One timeframe. The timeframe is the step most bars follow. Every other step must be a whole number of it: a 30-minute bar inside an hourly series is refused. Monthly bars, whose length varies, are checked for order only.
- Not more holes than bars. A series that misses more steps than it holds is not one timeframe, and is refused.
- Not empty. An empty list is refused. Only a request that leaves the bars out entirely gets the engine's
synthetic demo series, and its report says
"source": "synthetic".
Where a window ends
A window ends at the last bar that had closed by its end time. An end time in the middle of an hour ends an hourly window at the bar before, never at one running past the moment asked for, and never at the bar still forming now.
Only the traded market's own bars
A backtest replays the bars of the market a strategy trades: Binance USDⓈ-M futures or Bybit perpetuals. The 5- and 15-second bars the platform's charts fold from Binance spot seconds are a different market, and the futures market publishes no second bars, so a backtest refuses those timeframes instead of quietly replaying spot prices.
Other markets
A strategy that reads other markets gets their bars on the same timeframe as the market it trades. Bars are merged in the order they open, which is the order they close in only when they last as long: a 4-hour bar of another market sent under a 1-hour strategy would reach it hours before it closed, so the request is refused. When the bars of two markets open at the same moment, the traded market's comes first, as a live run receives them.
Bars the exchange does not have
Exchange histories have holes: maintenance, outages, gaps in the published archives. A hole is the market's history, not an error, so the engine replays across it the way the market resumed: a position held over a gap is marked at the next bar's open, and a stop the price gapped past fills there.
What changes is that the hole is no longer invisible. The report says how many bars were missing between the first
and the last bar, and the longest stretch without one (report.config.missing_bars, report.config.largest_gap_ms),
and the app shows it under the result.
Running these checks over five years of 15-minute Binance futures history for ten major coins found every bar sound, and one hole: three days in LTCUSDT, XLMUSDT and XRPUSDT, 480 missing bars each, which earlier reports had skipped over without a word.
Funding
Funding settlements must be in time order, each with a finite rate given as a fraction per settlement (0.0001 is 0.01%). A rate above 10% in absolute value is refused as a percentage sent by mistake.
A funding series must also be whole over the traded span: no stretch of more than 8½ hours — the longest interval any
venue settles on, and half an hour for the newest settlement to be published — without one. A series with a hole would charge part of the window and leave the rest free
without saying so. Sending no funding at all is allowed: the backtest then runs without it, and the report says
funding_applied: false. The platform's own funding history passed this check over five years of ten major coins,
including the three days their candles were missing.
In the API
A refused request answers with an error that starts with backtest_candles_invalid: or backtest_funding_invalid: and
names the series (the traded market, detail, or the other market's instrument), the bar's index and what is wrong with
it, so a client or an AI agent can fix the data and send it again.