Trading StrategyPine Script v5

Test a Bollinger Bands and RSI Mean Reversion Strategy

This example buys a close crossing below the lower Bollinger Band when RSI is below its threshold, and sells short on the opposite setup. It tests a reversal hypothesis; price can keep trending away from the moving average. No audited performance results are supplied for this script.

A band crossing is a testable rule, not a probability

Bollinger Bands place an SMA between bands based on recent price standard deviation. RSI measures the balance of recent gains and losses. Neither proves that price is stationary, that a reversal is due, or that an extreme will recover.

John Bollinger’s published rules distinguish a band touch from a trade signal and warn against normal-distribution assumptions. This example deliberately tests a countertrend interpretation of a closing cross; it does not enter merely because an intrabar low touched a band.

Before running the example

  1. Select a standard candlestick chart, exact exchange-qualified symbol, interval, and test dates. The code itself has no date filter; record the tester’s actual available range.
  2. Paste the script into TradingView’s Pine Editor, add it to the chart, and inspect the trade list alongside the band and RSI conditions.
  3. Record commission, slippage, order timing, and position size before comparing results. Preserve an untouched later period for evaluation.

TradingView’s strategy documentation describes simulated orders and broker-emulator assumptions. Syntax validation is not a profitability or execution test.

ComponentDescriptionRole
EntryClosing cross outside a band plus RSI thresholdCountertrend hypothesis
ExitBasis cross or percentage bracket after a position is observedSimulated exit conditions; timing matters

Pine Script v5 Implementation

Pine Script v5
//@version=5
// This strategy is designed for mean reversion trading using Bollinger Bands and RSI.
// It tests band crosses with an RSI threshold; neither establishes exhaustion.
strategy("Mean Reversion Strategy [BB + RSI]", 
     overlay=true, 
     initial_capital=10000, 
     default_qty_type=strategy.percent_of_equity, 
     default_qty_value=10,
     commission_type=strategy.commission.percent,
     commission_value=0.05)

// --- Inputs ---
// Example defaults; validate parameters on the chosen market and sample.
bbLength = input.int(20, "BB Length", minval=1, group="Bollinger Bands", 
     tooltip="The number of periods used to calculate the SMA and Standard Deviation.")
bbMult   = input.float(2.0, "BB StdDev", minval=0.1, step=0.1, group="Bollinger Bands", 
     tooltip="Band width multiplier; it does not imply a price-containment or reversal probability.")

// RSI Settings: 14 periods is standard, but can be shortened for faster signals.
rsiLength = input.int(14, "RSI Length", minval=1, group="RSI")
rsiOverbought = input.int(70, "RSI Overbought Level", minval=50, maxval=100, group="RSI")
rsiOversold   = input.int(30, "RSI Oversold Level", minval=0, maxval=50, group="RSI")

// Hypothetical exit levels, not a guaranteed limit on losses.
tpPercent = input.float(2.0, "Take Profit (%)", minval=0.1, step=0.1, group="Risk Management")
slPercent = input.float(1.0, "Stop Loss (%)", minval=0.1, step=0.1, group="Risk Management")

// --- Calculations ---
// Bollinger Bands Calculation
// [basis, upper, lower] = ta.bb(close, bbLength, bbMult)
basis = ta.sma(close, bbLength)
dev = bbMult * ta.stdev(close, bbLength)
upper = basis + dev
lower = basis - dev

// Plotting the Bands for visual reference
plot(basis, "Basis (Mean)", color=color.gray, linewidth=1)
p1 = plot(upper, "Upper Band", color=color.red, linewidth=1)
p2 = plot(lower, "Lower Band", color=color.green, linewidth=1)
fill(p1, p2, color=color.new(color.blue, 90), title="Volatility Zone")

// RSI Calculation
rsiValue = ta.rsi(close, rsiLength)

// --- Strategy Logic ---
// Entry Conditions: Price must pierce the band AND RSI must be in the extreme zone.
longCondition  = ta.crossunder(close, lower) and rsiValue < rsiOversold
shortCondition = ta.crossover(close, upper) and rsiValue > rsiOverbought

// Exit Conditions: The primary goal is to return to the mean (the basis line).
exitLongCondition  = ta.crossover(close, basis)
exitShortCondition = ta.crossunder(close, basis)

// --- Execution ---
// Long Entry Logic
if (longCondition)
    strategy.entry("Long", strategy.long, comment="BB Lower + RSI Oversold")

// Short Entry Logic
if (shortCondition)
    strategy.entry("Short", strategy.short, comment="BB Upper + RSI Overbought")

// Brackets are submitted only after the strategy observes an open position.
if (strategy.position_size > 0)
    // Exit at the mean or at the Take Profit/Stop Loss levels
    strategy.exit("Exit Long", "Long", 
         limit=strategy.position_avg_price * (1 + tpPercent/100), 
         stop=strategy.position_avg_price * (1 - slPercent/100), 
         comment="TP/SL Exit")
    if (exitLongCondition)
        strategy.close("Long", comment="Mean Reached")

if (strategy.position_size < 0)
    // Exit at the mean or at the Take Profit/Stop Loss levels
    strategy.exit("Exit Short", "Short", 
         limit=strategy.position_avg_price * (1 - tpPercent/100), 
         stop=strategy.position_avg_price * (1 + slPercent/100), 
         comment="TP/SL Exit")
    if (exitShortCondition)
        strategy.close("Short", comment="Mean Reached")

// --- Visual Signals ---
plotshape(longCondition, title="Long Signal", location=location.belowbar, color=color.green, style=shape.triangleup, size=size.small)
plotshape(shortCondition, title="Short Signal", location=location.abovebar, color=color.red, style=shape.triangledown, size=size.small)

Read the rules and execution limits

  1. 1

    Initial capital is 10,000 and order size is 10% of equity. Position allocation is not the percentage of account equity at risk. Commission is 0.05% per executed order; slippage is not modeled by the declaration.

  2. 2

    The 20-bar SMA and standard deviation define the bands; RSI uses 14 bars by default. These are example parameters, not fitted optimal values.

  3. 3

    Long entries require ta.crossunder(close, lower) and RSI below 30; short entries require the opposite upper-band cross with RSI above 70. Signals plotted on the chart are conditions, not fill confirmations.

  4. 4

    With default strategy calculation settings, a market order normally fills on the next available tick, often the next bar open. An opposite strategy.entry can reverse the position.

  5. 5

    The bracket is issued only when position_size is already nonzero. Because there is no recalculation on fills, it may not exist during the entry bar. This is a limitation of the example, not protection at the moment of entry.

  6. 6

    Stops can fill beyond their requested price after a gap. Basis-cross closes and bracket orders are simulated according to the broker emulator; inspect the trade list and chart before drawing conclusions.

Backtest Assumptions and Metrics

No verifiable trade export or settings report supports a return or win-rate claim for this example. Use the following checklist to produce a reproducible test; the values below are configuration facts, not performance results.

MetricValue
Starting equity10,000 (example)
Order allocation10% of equity
Commission0.05% per order
SlippageNot set in code
TP / SL inputs2% / 1% of entry price
Return, win rate, drawdownNot established

Record symbol, interval, dates, script revision, settings, and exported trades together. Review net results after realistic costs, losing streaks, drawdown, exposure, and sensitivity to neighboring parameters. Compare ranging and trending periods without selecting them after seeing the result. Evaluate the frozen rule on later data; no profitable outcome is assumed.

Review the resulting trade export

Backtest Deep Report analyzes an exported TradingView trade list and can summarize trade-based equity, drawdown, monthly results, and risk metrics. Confirm starting capital and included trades before comparing reports.

The CSV alone cannot verify this script’s bar-by-bar execution, repainting, or lookahead. Its trade-based equity cannot reconstruct all intrabar losses. Keep the TradingView settings and source alongside the export.

Strategy Optimization Suggestions

1

Test one regime filter

Compare the original rule with a predeclared trend filter on the same data and costs. A filter may remove both losing and winning trades; do not assume it improves returns.

2

Check exit timing before changing distances

Inspect when the bracket first appears and when market closes fill. Changing TP/SL percentages does not repair the initial unprotected-bar limitation.

3

Vary parameters without selecting one lucky peak

Use neighboring band lengths, widths, and RSI thresholds. Report failed variants and reserve later observations for evaluation.

4

Compare costs and liquidity

Repeat with plausible fees and slippage for the instrument. Frequent reversals can be sensitive to execution costs; a liquid chart alone does not establish feasible fills.

Frequently Asked Questions

No. A moving average can follow a continuing trend, and price levels need not be stationary. The strategy tests a hypothesis rather than a mathematical guarantee.

No. Band width is a descriptive calculation from recent prices. Neither containment nor future reversal probability follows from that multiplier.

Not in this example under its default calculation settings. The bracket is submitted after an open position is observed, which can leave the entry bar without it. Gaps can also produce worse stop fills.

No comparison here establishes a best market or interval. Fix the rule and realistic costs, test multiple regimes, and retain an untouched evaluation period.

Inspect the Trade List Behind a Backtest

Export your own test and compare its trade-based metrics with the original TradingView report. Keep the source, settings, and dates so differences can be investigated.

Disclaimer: Trading involves significant risk. The strategies and code provided are for educational purposes only and do not constitute financial advice. Past performance is not indicative of future results.