A backtest is the only way to learn whether a trading idea has ever made money without paying to find out. It is also the easiest way in trading to fool yourself, because every mistake makes the result look better. Here is how to run one, and the six places it quietly turns into fiction.
What a backtest is
You take a rule - "buy when the 20-day average crosses above the 50-day, stop 1.5 ATR below, target 3 ATR above" - and replay it bar by bar over past prices, exactly as if you had been there, recording each trade. At the end you have a list of results, and from the list, a verdict.
The rule has to be mechanical. "Buy when it looks strong" cannot be replayed. If a human judgement sits anywhere in the loop, the backtest is measuring the human, not the rule.
Step by step
- Write the rule down as code, or as something a machine could follow. Entry condition, exit condition, stop, target. All of it.
- Choose the horizon - 15-minute, hourly, 4-hour, daily bars. The same rule behaves differently on each, and the difference is usually larger than the difference between strategies.
- Get enough history. Years, not weeks. Five thousand daily bars is thirteen years; five thousand 15-minute bars is seven weeks.
- Replay it honestly - see the six traps below.
- Record every trade in R - the result divided by the risk taken - so the trades are comparable and the list can be averaged.
- Read the lower bound, not the average.
The six ways it goes wrong
1. Look-ahead
Your rule uses the close of bar i to decide, then enters at the close of bar i. You could not have done that. The close was not known until the bar closed, and by then the price was the next bar's open. Enter at the next bar's open or the backtest is trading on information from the future.
2. Optimistic tie-breaks
A single bar touches both your stop and your target. Which was hit first? From OHLC data you cannot know. Counting it as the target is how a losing strategy becomes a winning one. Count it as the stop.
3. No costs
A crypto taker fee is 0.1% on spot, 0.045% on futures, and slippage is about the same again. On a 15-minute bar the average move is not much larger than that. Every strategy NusaTerminal ships measures between −0.48R and −0.65R per trade on 15-minute crypto bars once costs are included, and positive on daily bars. A backtest without costs would show them all winning on every horizon.
4. Tuning per symbol
Fit the moving-average lengths to BTC and they will be wrong for SOL. Fit them to each symbol separately and every symbol will look good - because you have fitted the noise. Fixed parameters across every market give smaller numbers and true ones.
5. Picking the best row
Test ten strategies on five horizons on thirty symbols and sort the table. The top row is spectacular. It is also, almost always, the row with the fewest trades - because small samples have the widest spread, and the widest spread is where the extreme values live. Reporting that row as your result is the single most common way a backtest lies.
6. Trusting the average
Forty trades averaging +0.4R and two thousand trades averaging +0.1R: the second is the better strategy, and a table sorted by average puts it fifth. The number that ranks them correctly is the lower bound - the worst average the data is still consistent with at a chosen confidence. On forty trades it sits far below the average; on two thousand it sits close.
What that lower bound looks like on real data →
What a good result looks like
Small, and defended. A strategy on the daily horizon across the most liquid Binance perpetuals, measured over 2,810 trades with every cost included, averaging +0.10R with a 99% lower bound of +0.06R. That is a real edge. It is also a modest one, and a backtest that reports something much larger on a fraction of the sample is reporting one of the six mistakes above, not a better strategy.
Doing it in NusaTerminal
The terminal ships ten fixed strategies and replays them with next-bar entry, stop-first tie-breaks, the cost model for each market, and fixed parameters everywhere. The result reports win rate, average R, profit factor, drawdown, the trade count, and the lower bound. The same code produces the live signals, so the backtest and the trade it describes cannot drift apart.