How to backtest a Pine Script strategy with costs and an out-of-sample check
Write the rules as a Pine strategy, charge commission and slippage on every fill, let orders fill at the next candle's open, run years of history, then judge the result on a later stretch you did not tune on. An RSI(2) pullback on SPY made 42.8% from January 2016 to October 2026 before costs, and 24.8% after them.
The steps
- Write the rules downWhat opens a trade, what closes it, and which side. “Long when the 2-day RSI is under 10 and the close is above its 200-day average, out on a close above the 5-day average” can be tested. “Trade the pullbacks” cannot.
- Make it a strategy, not an indicatorIn TradingView only a script declared with strategy() places test orders and gets a strategy report. An indicator can draw the same rule, but it trades nothing.
- Charge costs on every fillSet commission_type, commission_value and slippage in the strategy() line. Left out, both costs are zero and every result is too kind.
- Fill at the next openAct on candles that have closed and let the order fill at the next candle's open. That is TradingView's default; turning on process_orders_on_close fills at the same close the rule just read.
- Test years, not weeksUse as much history as the rules allow, through rises and falls. Under 20 trades, or under a year, our tester calls the result too few trades to judge.
- Keep the last stretch for the verdictChoose settings on the earlier part only, then run the last third or so once, unchanged. If the result falls apart there, the settings were fitted to the past.
- Compare it with holding, then with other stocksJudge the result against holding the same stock over the same dates, then run it on other symbols. One chart can flatter any rule; TradingView's manual calls that selection bias.
Worked example: RSI(2) pullback on SPY
The copilot's library build for “RSI 2 pullback strategy, longs only”: long when the 2-day RSI is under 10 with the close above its 200-day average, out on a close above the 5-day average. Run on SPY's daily candles from 4 January 2016 to 19 October 2026: 2,714 sessions, 10.8 years, 90 trades, in the market 10.5% of the time.
| Measure | Before costs | After costs |
|---|---|---|
| Net return | +42.8% | +24.8% |
| Profit factor | 2.10 | 1.62 |
| Win rate | 75.6% | 68.9% |
| Average trade | +0.41% | +0.26% |
| Sharpe | 0.60 | 0.38 |
| Worst drawdown | 11.3% | 13.7% |
The costs are our tester's defaults: 0.05% commission and 0.025% slippage on each side, 0.15% a round trip. They took 18 of the 42.8 points, and they alone moved the tester's verdict from “real edge so far” to “made money, but could be luck”. Holding SPY over the same dates made 281.1%; the script held a position about a tenth of the time.
Then the last three years were kept apart, as the tester does by default, and the script ran on them unchanged:
| Period | Dates | Net | Sharpe | Trades |
|---|---|---|---|---|
| In sample | 4 January 2016 → 18 October 2023 | +8.2% | 0.20 | 56 |
| Out of sample | 19 October 2023 → 19 October 2026 | +15.4% | 0.96 | 34 |
Out of sample the Sharpe was higher than in sample, so the tester's word is survived. One pass is not proof; it says the rule was not simply fitted to the years it was built on. Last, the same script, unchanged and after costs, on the tester's other large US stocks:
| Stock | Trades | Net after costs | Tester's verdict |
|---|---|---|---|
| NVDA | 92 | +302.9% | Real edge so far |
| AAPL | 88 | +29.8% | Could be luck |
| MSFT | 76 | +67.6% | Real edge so far |
| AMZN | 80 | +36.0% | Could be luck |
| META | 73 | +115.5% | Real edge so far |
| TSLA | 62 | +48.1% | Could be luck |
| AMD | 83 | +223.0% | Real edge so far |
| GOOGL | 87 | +65.1% | Real edge so far |
It made money on all eight. Holding each stock over the same dates made more on every one of them, which is what a script that is in the market a tenth of the time should be weighed against.
The same steps in TradingView
Costs go in the declaration, and a date window keeps the tuning inside the in-sample years. Here the tester's 0.05% commission and 0.025% slippage are charged together as a 0.075% commission on every fill, with one tick of slippage on top, because Pine's own slippage counts ticks, and a fixed number of ticks costs a different share of the price as the price moves: commission and slippage, explained. Instead of the window, you can leave the script alone and set the testing period at the top of the strategy report: first the in-sample dates, then the later ones.
//@version=6
strategy("RSI(2) pullback", overlay = true,
commission_type = strategy.commission.percent,
commission_value = 0.075, // 0.05% commission and 0.025% slippage, as a percent of every fill
slippage = 1) // and one tick on the fill price itself
// tune with the window on the in-sample dates, then move it to the later ones once
fromDate = input.time(timestamp("2016-01-04"), "Test from")
toDate = input.time(timestamp("2023-10-18"), "Test to")
inWindow = time >= fromDate and time <= toDate
rsi = ta.rsi(close, 2)
trendMa = ta.sma(close, 200)
exitMa = ta.sma(close, 5)
if inWindow and barstate.isconfirmed and strategy.position_size == 0 and close > trendMa and rsi < 10
strategy.entry("Long", strategy.long)
if barstate.isconfirmed and strategy.position_size > 0 and close > exitMa
strategy.close("Long")Or let the tester do it
Alpha Charts' Strategy Tester does steps 3 to 7 by default: 0.05% commission and 0.025% slippage each side, fills at the next open, the last stretch nearest a third held out and judged in one word, holding the stock drawn beside the curve, and the same script on seven other large US stocks. Paste a Pine strategy into the free tester to see it on your own script, and read out-of-sample and walk-forward testing for what the held-out word means.
Questions
Write the rules so a script can follow them, charge commission and slippage on every fill, fill orders at the next candle's open, run years of history, then judge the result on a later stretch you did not tune on. Last, compare it with simply holding the stock and run it on other symbols.
As accurate as its settings. By default a Pine strategy pays no commission and no slippage, and the report covers one testing period you choose. Add costs in the strategy() declaration or the Properties tab, and hold out a later period yourself, and the numbers get much closer to what trading the script would have done.
Our tester calls anything under 20 trades, or under a year of history, too few to judge, and it wants at least 5 trades in the held-out stretch before giving that a verdict. Even past those floors, an average trade that is small next to its spread could still be luck.
Charge something. A commission-free broker still fills you at the spread, and a fast market moves before the order lands. On the RSI(2) example below, the difference between no costs and 0.15% a round trip was 18 points of return.
Sources, read on 20 October 2026: TradingView's Pine Script manual, “Strategies” (commission, slippage, fills, the testing period, selection bias), and the Pine Script v6 reference for strategy(). The numbers are our engine's, run on 20 October 2026 on our daily data from 4 January 2016 to 19 October 2026. A backtest describes the past; it is not a forecast or advice. TradingView belongs to its owner, and Alpha Charts is not affiliated with it.