Backtesting guide: costs, slippage and out-of-sample tests done right
A practical backtesting guide: set real commission and slippage, avoid look-ahead, keep an out-of-sample period and read results honestly in SuperCharting.
A good backtest answers one question honestly: if you had followed these exact rules, with real costs, on data the rules were not tuned on, what would have happened? Most misleading backtests fail on one of four points: costs left at zero, fills that could not happen, information from the future, or settings tuned on the same data they are judged on. This guide covers each one and shows how to set it up in SuperCharting's Strategy Tester.
Backtests are a study tool. Past results do not guarantee future results, and nothing on this page is a recommendation to trade.
The five checks of an honest backtest
Step 1: write rules a person could follow
A backtest can only test rules that are complete. Before writing code, write the rules in a sentence each: when to enter, how much to buy or sell, where the stop is, where the target is, and when to give up on a trade. If a rule needs judgement ("only take good-looking setups"), it cannot be tested.
Then put those rules in a strategy() script. If you are new to that, follow write your first strategy, which builds an EMA crossover with an ATR stop and target.
Step 2: put real costs in
Open the strategy's settings and go to the Properties tab, or write the values in the strategy() line:
- Commission: as a percent of trade value, a cash amount per contract, or a cash amount per order. Use what your broker and exchange actually charge on both entry and exit, including taxes and fees that apply per trade in your market.
- Slippage: a number of ticks added to market and stop order prices. On liquid instruments such as NSE:NIFTY futures or EURUSD during main hours a tick or two is common; thin stocks and fast markets need more.
- Default order size: contracts, cash, or a percent of equity. Percent of equity lets the position grow and shrink with the account.
- Initial capital and currency: set them to an amount you would really trade.
Intraday strategies on 1-minute and 5-minute charts trade often, so costs decide most of the result. Run the test once with costs and once without: if the profit disappears when costs are switched on, the strategy has no edge left for you.
Step 3: make sure fills could have happened
SuperCharting's broker emulator follows the usual conventions:
- A market order sent when a bar closes fills at the next bar's open. That is realistic: you only know the close once the bar has finished.
- Limit and stop orders fill when the bar's range reaches their price.
- When a stop and a target could both be hit inside the same bar, the emulator has to guess the path inside the bar. Bar detalization in the tester toolbar sets how finely it walks each bar: Default (4 ticks per bar) or High (about 24 ticks per bar). If switching to High changes your results a lot, your stops and targets are too close for the timeframe.
Setting Order execution delay to None, or process_orders_on_close=true, fills on the signal bar's close. It is useful for some end-of-day systems but easy to abuse, so use it only when you could really trade at that close.
Step 4: keep the future out of the past
Look-ahead bias creeps in quietly:
- Higher timeframe values. On an intraday chart,
request.security(syminfo.tickerid, "D", close)can show the day's final close before the day has ended. Use the completed bar instead:request.security(syminfo.tickerid, "D", close[1], lookahead=barmerge.lookahead_on). - Signals on the bar that has not closed. A condition that flickers true and false inside a live bar behaves differently from one checked on closed bars. Base entries on closed-bar values.
- Survivorship. Testing only on today's index members leaves out the companies that failed. Keep that in mind when you judge a stock-picking idea.
Bar Replay is a good way to see what your indicator showed at each moment, bar by bar, before you trust a backtest of it.
Step 5: hold back an out-of-sample period
Tune the rules on one stretch of history and judge them on another. The example below adds two date inputs so the strategy only enters inside a chosen window:
//@version=6
strategy("RSI Pullback", overlay=false, initial_capital=100000, default_qty_type=strategy.percent_of_equity, default_qty_value=10, commission_type=strategy.commission.percent, commission_value=0.03, slippage=1)
rsiLen = input.int(14, "RSI length", minval=2)
buyLvl = input.int(30, "Buy below", minval=1, maxval=99)
exitLvl = input.int(55, "Exit above", minval=1, maxval=99)
fromDay = input.time(timestamp("2023-01-01"), "Test from")
toDay = input.time(timestamp("2025-12-31"), "Test to")
inWindow = time >= fromDay and time <= toDay
r = ta.rsi(close, rsiLen)
plot(r, "RSI", color=color.purple)
hline(buyLvl, "Buy level")
hline(exitLvl, "Exit level")
trendUp = close > ta.sma(close, 200)
if inWindow and trendUp and ta.crossover(r, buyLvl)
strategy.entry("Long", strategy.long)
if ta.crossover(r, exitLvl)
strategy.close("Long")
You can get the same split without code: the tester's Testing period button has a Custom date range with a start and end date.
How to backtest in SuperCharting, start to finish
- Open the chart you will trade, for example NSE:RELIANCE daily, and add the strategy from the Script Editor.
- Set commission, slippage, order size and capital on the Properties tab.
- In the Strategy Tester, choose Testing period > Custom date range and pick the older two-thirds of the history as the in-sample period.
- Tune the inputs there, by hand or with the strategy optimiser, with a filter that demands enough trades.
- Write the chosen settings down. Set the custom range to the newer third and run once, without changing anything.
- Compare the two periods: win rate, profit factor and max drawdown should be in the same region. Then try a related symbol, such as NSE:TCS, with the same settings.
Reading results honestly
- Sample size: thirty trades is a minimum to say anything; a few hundred is better.
- Drawdown first: could you sit through the worst drawdown in the report without abandoning the rules?
- Against doing nothing: the Benchmarking tab compares the strategy with buy and hold over the same period.
- Dependence on a few trades: if removing the two largest winners turns the result negative, the strategy depends on luck you cannot plan for.
- History length: a chart holds up to 40,000 bars. On a 1-minute chart that is only a few months of trading, so intraday ideas see fewer market phases than daily ones.
SuperCharting is currently free to use, and the Strategy Tester and optimiser are part of every account; free backtesting software compares the options.
Frequently asked questions
What is an out-of-sample test?
A test on data the rules were not built or tuned on. It is the closest a backtest gets to showing how the rules behave on new data.
How much slippage should I use?
Enough to cover the gap between the price you see and the price you get. Start with one or two ticks on liquid markets and more on thin ones, then see how sensitive the result is.
Why does my result change on a different timeframe?
Different bars give different signals and different fill prices. A rule that only works on one exact timeframe is often fitted to that data.
Can I trust a backtest with no losing trades?
Be suspicious. Check for look-ahead, zero costs, very few trades or a stop that never triggers.