▲ Work in progress. This site is built and researched in the open: pages, numbers and findings may change or be wrong, and the record has gaps. Not advice. More
The Lab
Ideas traders use, put to the record
← the Lab · models explained · write-up
#60 How our backtester works, step by step
Past prices; no real orders were placed. Every figure is a share of the money, a price, or a count.
Last checked: 20 October 2026.
Where the idea comes from
- Backtesting means replaying a trading rule over past prices to see what it would have done. It is as old as trading rules, with no single originator.
- Its best-known trap is a rule tuned until it fits the past and then fails on new prices: David Bailey, Jonathan Borwein, Marcos López de Prado and Qiji Jim Zhu, "Pseudo-Mathematics and Financial Charlatanism" (2014). Our page "The best of many tries" measures it on our own results.
- Before building ours we studied how two open-source trading projects backtest: freqtrade and passivbot. Our rule list credits them for its core rules: fill only when the price trades through, charge funding, and count order sizes in the exchange's whole steps.
- Ours is not a separate program. It lives inside our trading engine and replays the bots' own code. No AI is involved: it is ordinary code that anyone can run.
The question
What does our backtester do, step by step? And how far can its answers be trusted?
What it is
- We call it a rehearsal. The bot's real decision-making code runs unchanged, against a pretend exchange made of past price candles.
- A candle is one stretch of time, one minute to four hours, given as four prices: where it opened, its high, its low, and where it closed. The exchanges publish these for free.
- Why the real code: a separate backtesting program would have to copy the bot's rules, and copies drift. Ours can't drift, because there is nothing copied.
- There are four rehearsals:
- grid bots, long or short, futures or spot;
- buy-the-dips (DCA) bots: here the whole real bot runs, stops and targets included;
- comparing rung counts for one grid;
- the hedged basket.
- You can run them from our control panel, by filling in a bot and a number of days, or from the command line.
The loop, one candle at a time (grid bots)
1. Plan at the open. At each candle's opening price, the bot's own planner says which orders it would have resting on the exchange: buys under the price, sells over what it holds.
2. Keep only what the live bot would rest. The live bot keeps only orders near the price resting (within 5%, or two rung gaps when that is wider). The rehearsal keeps the same.
3. Fill only what the price trades through. A buy at 99.92 fills only if the candle's low goes under 99.92. Just touching it is not enough, because at that price there could be a queue of other orders ahead of ours.
4. A lot can't be sold in the candle it was bought in. The planner only looks at each candle's open, so a lot bought during a candle gets its sell order at the next one. This is the cautious side: live, it could have sold sooner.
5. Pay the costs. The exchange's fee on every fill (0.02% for a resting order on Bybit). The real funding at each 8-hour payment, on what the bot holds, read from the exchange's history.
6. Stop where the bot would. If the bot has a stop-loss or a loss limit, the rehearsal checks it on every candle, at the bot's own level:
- the price it watches: a bot's stop watches the mark price, the price the futures exchange uses to value positions and decide liquidations. It is built from several exchanges' prices, so it can differ from the last trade, most of all in a crash.
- For Bybit bots the rehearsal reads Bybit's own mark-price candles for the stop, matched to the trade candles by time.
- Hyperliquid publishes no mark-price candles, so its rehearsals judge the stop on the trade prices, and say so.
- Fills still use the trade prices, where orders really fill.
- a plain stop fires as soon as a candle reaches the level, at the level itself (or at the candle's open, when it opened past it);
- a stop with a waiting time fires only after that many candles in a row have closed past it;
- a stop judged on closes looks only at candle closes;
- when it fires, the bot sells what it holds at the market-order fee, or keeps it if it was set to leave the position. A loss limit always sells. Nothing trades after that.
7. Keep score. Profit from finished buy-and-sell trips, the value of what is still held, and the deepest drop. The first moment the bot, on its own money, would have reached the exchange's liquidation line. And, given the account's latest state, how close the whole account would have come to it.
A worked example
A long grid from 96 to 104, five rungs, 1,000 USDT, no leverage, with a stop at 95. The rungs sit 2% apart: 96, 97.94, 99.92, 101.94 and 104. Each buy is 200 USDT, and each sells one rung up. Seven made-up hourly candles, run through the engine's own code:
| candle | open | high | low | close | what happened | trips | profit, after fees |
|---|---|---|---|---|---|---|---|
| 1 | 101 | 101.5 | 99.5 | 100.2 | low passes 99.92: buy 2.0016 coins | 0 | −0.04 |
| 2 | 100.2 | 100.4 | 97.9 | 98.1 | low passes 97.94: buy; the high (100.4) passes this lot's sell (99.92), but that sell is only placed next candle | 0 | −0.08 |
| 3 | 98.1 | 100.3 | 97.5 | 100.1 | sell the 97.94 lot at 99.92 | 1 | +1.92 |
| 4 | 100.1 | 102.4 | 100 | 102.2 | sell the 99.92 lot at 101.94: nothing held | 2 | +7.92 |
| 5 | 102.2 | 102.3 | 101.94 | 102 | low only touches 101.94: no fill | 2 | +7.92 |
| 6 | 102 | 102.1 | 101.5 | 101.6 | low passes 101.94: buy | 2 | +7.88 |
| 7 | 101.6 | 101.7 | 94.5 | 95.2 | low passes 99.92 and 97.94: buy, buy; then 95: the stop sells everything | 2 | −21.98 |
Profit in USDT.
- Each trip earned one rung gap on 200 USDT: about 2%, or 4.04 USDT. Two trips made 8.09, less 0.16 in fees: +7.92.
- After candle 6 one lot is held, worth a little less than paid: counting it, the run is up 7.22.
- Candle 7 falls through the stop. First the two rungs on the way down buy, as they would live. The bottom rung, 96, never buys: at the candle's open (101.6) it was more than 5% away, so it was not resting (step 2). Then the price reaches 95, and the stop sells all three lots there, about 6 coins, with the market-order fee (0.055%).
- What the stop cost: the three lots were bought at 101.94, 99.92 and 97.94 and sold at 95, a loss of 29.47, plus 0.31 in fees. The two trips' profit is gone, and the run ends down 21.98, holding nothing. Nothing trades after a stop.
- This is the best case for a stop (see "What it assumes"): the sale is at exactly 95, with no slippage.
- These candles are made up and have no mark price, so here the stop reads the candles' own prices, as a Hyperliquid rehearsal would.
- How profit is counted. The engine counts each sale's profit against the average price of everything held, as the exchange does. So candle 3 shows +2.04 rather than +4.04. The total is the same once everything is sold.
What it assumes, plainly
| assumption | which way it leans |
|---|---|
| fills need the price to trade through, not touch | cautious |
| a lot can't sell in the candle it was bought in | cautious |
| on long candles, every rung a fast move passed fills | hopeful (live, a fast move can skip rungs) |
| resting orders fill at exactly their price | fair: a resting order can't slip |
| a small, fixed gap between buying and selling prices | hopeful in thin markets |
| real funding for Bybit futures; otherwise "not modelled", said | fair |
| one bot at a time | can't see bots sharing one account |
| a stop is judged on the mark price the bot watches, from Bybit's mark-price candles | fair, on Bybit |
| on Hyperliquid, which publishes no mark-price candles, a stop is judged on the trade prices | hopeful in a crash, when the two can be far apart (see below) |
| a stop that fires sells at its level, with no slippage | hopeful (see below) |
| DCA bots: when one candle hits both the stop and the target, the stop comes first | cautious |
Measured 1: how much the candle size matters
The same Bitcoin grid, replayed on 1-minute candles, and on bigger candles built from the same minutes. The setup: 12 months (October 2025 to September 2026); each month a fresh long grid from 15% under the start to the same distance over, 20 rungs, 10,000 USDT, no leverage, with Bybit's real order steps, fees and funding.
| candle | trips a month | of the 1-minute trips | net a month | gap to 1-minute: average | gap: worst month |
|---|---|---|---|---|---|
| 1 minute | 23.4 | 100% | −0.56% | ||
| 5 minutes | 22.4 | 96% | −0.58% | −0.02% | 0.16% |
| 15 minutes | 22.0 | 95% | −0.61% | −0.06% | 0.50% |
| 1 hour | 20.7 | 89% | −0.71% | −0.15% | 0.87% |
| 4 hours | 18.2 | 80% | −0.87% | −0.31% | 1.18% |
Net: a share of the money, after fees and funding, with what is held at each month's end sold. It was a falling year for Bitcoin, so a long grid lost.
The bigger the candle, the fewer round trips the replay could see, because a lot cannot sell in the candle it was bought in.
- Bigger candles saw fewer trips: step 4 hides the trips that started and ended inside one candle.
- So the cautious rules outweighed the hopeful one on Bitcoin this year. An hourly replay came out about 0.15% a month worse than the minute one.
- It has gone the other way before. In a review of 48 days of live trading, an hourly replay of one bot held three times the short position the live bot really held: on hourly candles, every rung a fast move passed had filled. So we replay on candles no longer than the moves in question.
- Even 1-minute candles miss a little. On a week of real trades rebuilt into 1-second candles, grids with 50 to 120 rungs made 4–10% more trips (our page "Grid bots: more rungs or fewer?").
Measured 2: slippage
A resting order can't slip: it fills at its own price or not at all. A market order can: it takes whatever is offered, from the best price down. Our bots send market orders to stop or close. The rehearsal fills those with no slippage.
What a market order would have cost beyond the middle price, from Bybit's public order book: 10 readings, 30 seconds apart, on 20 October 2026 at about 10:41 UTC.
| coin | 1,000 USDT | 10,000 | 100,000 | 1,000,000 |
|---|---|---|---|---|
| Bitcoin | 0.0001% | 0.0001% | 0.0001% (worst 0.005%) | 0.006% (worst 0.013%) |
| Ether | 0.0002% | 0.0002% (worst 0.009%) | 0.001% (worst 0.016%) | 0.014% (worst 0.024%) |
| Solana | 0.005% | 0.005% (worst 0.010%) | 0.009% (worst 0.016%) | 0.041% (worst 0.052%) |
The middle of 10 readings, the worst in brackets where it differs. The market-order fee itself is 0.055%.
- On an ordinary day, slippage is small next to the fee at any size we trade.
- It bites in a crash, which is exactly when stops fire. The book empties and prices jump. A crash also pulls apart the two prices a stop could be judged on: the trade price the candles show, and the mark price the exchange uses. On 10 October 2025 Solana's futures mark price fell 18.7% under its spot price within minutes (our page "The carry trade in one account: does it survive the crashes?"). Our Bybit rehearsals now read the mark for the stop; Hyperliquid's can't. And a calm book can't measure a crash's slippage. So a rehearsed stop's price is a best case.
How we use it
- Before a bot goes live: rehearse it on recent candles, against the same money just held.
- "Compare grid counts": the same range with many rung counts. The count picked on one week is checked on the next, so a lucky pick shows up as one.
- For years of research: the engine plans afresh at every candle, which is slow. So research studies rewrite the grid rules in a faster form and check it against the engine's backtest first. One such check agreed within 0.05% of the money on average.
- Forward tests are different. Paper bots on demo accounts, and our paper momentum book, trade on new prices as they arrive. No backtest can stand in for that.
What it means
- A rehearsal answers "what would my bot have done on these prices", with the bot's own code. It does not answer "what will it do next".
- Its rules mostly lean cautious, and we measured by how much: about 0.15% a month on hourly candles for this Bitcoin grid.
- Two gaps are known and named: market orders don't slip, and on Hyperliquid a stop is judged on trade prices rather than the mark. Both lean hopeful in a crash, so a rehearsed stop is a best case.
Limits
- The candle-size test is one coin, one year, one grid shape. Other shapes and wilder markets will differ.
- The slippage readings cover about 5 minutes of one day, not a crash.
- The worked example's candles are made up. The rules are the engine's.
Credits
- David H. Bailey, Jonathan M. Borwein, Marcos López de Prado and Qiji Jim Zhu, "Pseudo-Mathematics and Financial Charlatanism: The Effects of Backtest Overfitting on Out-of-Sample Performance", Notices of the American Mathematical Society, 2014: the overfitting trap.
- freqtrade and passivbot, open-source trading projects, whose backtesters we studied before building ours: credited in our rule list for the trade-through fill, the funding charge and whole-step order sizes.
- Our own: the rehearsal's design, the worked example, and both measurements.
- Data: Binance's public 1-minute futures trade prices; Bybit's public funding history, order book, order steps and fees.
- PerpGremlin and Claude, How our backtester works, step by step, TerminalGremlin, 2026: the tests, the code and the words.
← Hidden order: the BDS testSkill or luck →
Educational research from public market data; any trading discussed is paper, never real. Nothing here is advice. The full notice.