Pine Script Breakout Strategy: Test the Win-Rate Claim
A breakout and retest strategy waits for a close beyond a defined level, then a later pullback that closes on the breakout side. The Pine Script v6 example below uses confirmed pivots, volume and ATR filters, a retest deadline, and stop orders. No win rate or profitability has been measured for this example. Evaluate average win, average loss, trade count and costs alongside win rate; a high win rate alone does not establish an edge.
Define the breakout, retest and failure rules
This example tests a specific hypothesis: after a filtered break, a later retest may provide an entry near the broken level. A retest can still fail, and waiting for one can miss moves that never pull back. These are educational rules, not investment advice or a claim of profitable performance.
Use levels available at the time
The script uses the latest confirmed pivot high and low. With a lookback of 20, a pivot becomes available 20 bars after its candidate turning point. Levels are plotted when confirmed, with no backward offset. Do not treat the earlier pivot candle as a signal that was available then.
Require a fresh break
For a long setup, the previous close must be at or below resistance and the current close must exceed it by 0.3 ATR. Volume must reach 1.5 times its 20-bar SMA, including the current bar. Short rules are mirrored. These defaults are test inputs; neither filter identifies who traded nor guarantees that a breakout will hold. Missing volume blocks setups in this example.
Freeze the level and wait for a retest
Once a setup starts, the script stores its level and bar index. It ignores new breakouts while waiting. On one of the next 15 bars, a long signal requires the low to land within 0.25% on either side of that stored level and the close to finish above it. A short signal requires the high in the same band and a close below. A deep wick outside the band does not qualify. A close more than 1.5 ATR against the setup, or expiry after bar 15, cancels it.
Separate the signal from the fill
The initial stop is 1.5 ATR beyond the stored level. The target is twice the distance from the signal close to that stop. Default market entries fill on the next available tick, typically the next bar open. Gaps and slippage therefore change the realized risk/reward. A trailing stop updates at bar close using that bar's high minus 2 ATR for longs, or low plus 2 ATR for shorts, and never loosens. A fixed profit target remains active, so this version does not keep unlimited trend runners.
How a channel breakout strategy differs
A channel breakout compares price with the highest high or lowest low of a completed lookback window, instead of waiting for pivot confirmation. Exclude the current bar from the channel boundary when testing a close above its previous high. The full script here implements pivot retests, not a channel strategy. Compare the two definitions on the same market, dates, exits and cost assumptions before attributing a difference to the entry rule.
Documentation and related strategies
TradingView documents simulated orders, fill timing and trading costs and the risks of plotting confirmed pivots in the past. Explore the TradingView strategies hub or compare a Bollinger Bands breakout, which defines its boundary using volatility bands.
| Component | Description | Role |
|---|---|---|
| Confirmed pivots | Latest pivot high and low, known only after the right-hand confirmation bars. | Level definition |
| Volume and ATR thresholds | Default breakout filters: 1.5 times volume SMA and a close 0.3 ATR beyond the level. | Setup qualification |
| Retest state | Frozen level, a symmetric 0.25% extreme-price band and a 15-bar deadline. | Entry signal |
| Stop and target | An initial 1.5 ATR stop beyond the level, a 2R signal-based target and a ratcheting 2 ATR stop. | Simulated exits |
Pine Script v6 Implementation
//@version=6
strategy("TradingView Breakout and Retest Strategy [Pineify]",
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,
slippage = 1)
// =============================================================================
// 1. INPUT PARAMETERS
// =============================================================================
pivot_lookback = input.int(20, "Pivot Lookback (Bars)", minval = 5, group = "Level Detection",
tooltip = "Number of bars to the left and right to confirm structural high/low pivots.")
vol_length = input.int(20, "Volume MA Length", minval = 1, group = "Breakout Filters",
tooltip = "Lookback period for the benchmark volume moving average.")
vol_multiplier = input.float(1.5, "Volume Expansion Multiplier", minval = 1.0, step = 0.1, group = "Breakout Filters",
tooltip = "Breakout bar volume must exceed average volume by this factor to pass the configured volume filter.")
atr_length = input.int(14, "ATR Length", minval = 1, group = "Breakout Filters",
tooltip = "Period for Average True Range volatility calculation.")
atr_buffer_mult = input.float(0.3, "Breakout ATR Buffer Multiplier", minval = 0.0, step = 0.05, group = "Breakout Filters",
tooltip = "Minimum distance candle close must pierce beyond the level (in ATR units) for the configured breakout threshold.")
retest_tolerance_pct = input.float(0.25, "Retest Tolerance Zone (%)", minval = 0.01, step = 0.05, group = "Retest Parameters",
tooltip = "Percentage envelope around broken level where pullbacks qualify as a structural retest.")
max_retest_bars = input.int(15, "Max Bars Allowed for Retest", minval = 1, maxval = 50, group = "Retest Parameters",
tooltip = "Maximum bars after breakout to wait for a retest before the setup expires.")
risk_reward_ratio = input.float(2.0, "Risk/Reward Ratio (TP)", minval = 1.0, step = 0.5, group = "Risk Management",
tooltip = "Take profit multiplier based on initial distance to stop loss.")
trail_atr_mult = input.float(2.0, "Trailing Stop ATR Multiplier", minval = 0.5, step = 0.1, group = "Risk Management",
tooltip = "ATR distance for ratcheting dynamic trailing stop as position advances.")
// =============================================================================
// 2. STRUCTURAL LEVEL DETECTION
// =============================================================================
ph = ta.pivothigh(high, pivot_lookback, pivot_lookback)
pl = ta.pivotlow(low, pivot_lookback, pivot_lookback)
var float resistance_level = na
var float support_level = na
if not na(ph)
resistance_level := ph
if not na(pl)
support_level := pl
plot(resistance_level, "Resistance Level", color = color.red, style = plot.style_linebr, linewidth = 1)
plot(support_level, "Support Level", color = color.green, style = plot.style_linebr, linewidth = 1)
// =============================================================================
// 3. VOLATILITY & VOLUME BREAKOUT VERIFICATION
// =============================================================================
atr_val = ta.atr(atr_length)
vol_ma = ta.sma(volume, vol_length)
volume_confirmed = volume >= (vol_ma * vol_multiplier)
// Require a fresh close through the level, plus an ATR buffer and volume filter
bullish_breakout = barstate.isconfirmed and not na(resistance_level) and close[1] <= resistance_level and (close > resistance_level + (atr_val * atr_buffer_mult)) and volume_confirmed
bearish_breakout = barstate.isconfirmed and not na(support_level) and close[1] >= support_level and (close < support_level - (atr_val * atr_buffer_mult)) and volume_confirmed
// =============================================================================
// 4. RETEST STATE MACHINE
// =============================================================================
var bool pending_long_retest = false
var bool pending_short_retest = false
var int breakout_bar_index = 0
var float breakout_ref_level = na
// Activate state machine upon confirmed breakout (only when flat)
if bullish_breakout and not pending_long_retest and not pending_short_retest and strategy.position_size == 0
pending_long_retest := true
pending_short_retest := false
breakout_bar_index := bar_index
breakout_ref_level := resistance_level
else if bearish_breakout and not pending_long_retest and not pending_short_retest and strategy.position_size == 0
pending_short_retest := true
pending_long_retest := false
breakout_bar_index := bar_index
breakout_ref_level := support_level
// Timeout invalidation: cancel setup if retest does not happen within max_retest_bars
if bar_index - breakout_bar_index > max_retest_bars
pending_long_retest := false
pending_short_retest := false
// Invalidate if price breaches counter-trend invalidation boundary before retest
if pending_long_retest and (close < breakout_ref_level - (atr_val * 1.5))
pending_long_retest := false
if pending_short_retest and (close > breakout_ref_level + (atr_val * 1.5))
pending_short_retest := false
// =============================================================================
// 5. RETEST CONFIRMATION LOGIC
// =============================================================================
// Long Retest: Price dips into the resistance-turned-support envelope and closes above the level
long_retest_zone = breakout_ref_level * (1 + retest_tolerance_pct / 100)
long_retest_confirmed = barstate.isconfirmed and pending_long_retest and (low <= long_retest_zone) and (low >= breakout_ref_level * (1 - retest_tolerance_pct / 100)) and (close > breakout_ref_level) and (bar_index > breakout_bar_index)
// Short Retest: Price bounces into the support-turned-resistance envelope and closes below the level
short_retest_zone = breakout_ref_level * (1 - retest_tolerance_pct / 100)
short_retest_confirmed = barstate.isconfirmed and pending_short_retest and (high >= short_retest_zone) and (high <= breakout_ref_level * (1 + retest_tolerance_pct / 100)) and (close < breakout_ref_level) and (bar_index > breakout_bar_index)
// =============================================================================
// 6. ORDER EXECUTION & DYNAMIC TRAILING STOP
// =============================================================================
var float long_trailing_stop = na
var float short_trailing_stop = na
var float long_take_profit = na
var float short_take_profit = na
// Clear old levels before processing a new signal, including same-bar round trips.
if strategy.closedtrades > strategy.closedtrades[1]
long_trailing_stop := na
short_trailing_stop := na
long_take_profit := na
short_take_profit := na
// Long Entry
if long_retest_confirmed and strategy.position_size == 0
initial_sl = breakout_ref_level - (atr_val * 1.5)
risk_per_share = close - initial_sl
long_take_profit := close + (risk_per_share * risk_reward_ratio)
long_trailing_stop := initial_sl
strategy.entry("Long", strategy.long, comment = "Breakout Retest Long")
strategy.exit("Exit Long", "Long", stop = long_trailing_stop, limit = long_take_profit)
pending_long_retest := false
// Short Entry
if short_retest_confirmed and strategy.position_size == 0
initial_sl = breakout_ref_level + (atr_val * 1.5)
risk_per_share = initial_sl - close
short_take_profit := close - (risk_per_share * risk_reward_ratio)
short_trailing_stop := initial_sl
strategy.entry("Short", strategy.short, comment = "Breakout Retest Short")
strategy.exit("Exit Short", "Short", stop = short_trailing_stop, limit = short_take_profit)
pending_short_retest := false
// Dynamic Trailing Stop Adjustment on Subsequent Bars
if strategy.position_size > 0
dynamic_stop = high - (atr_val * trail_atr_mult)
long_trailing_stop := na(long_trailing_stop) ? dynamic_stop : math.max(long_trailing_stop, dynamic_stop)
strategy.exit("Exit Long", "Long", stop = long_trailing_stop, limit = long_take_profit)
if strategy.position_size < 0
dynamic_stop = low + (atr_val * trail_atr_mult)
short_trailing_stop := na(short_trailing_stop) ? dynamic_stop : math.min(short_trailing_stop, dynamic_stop)
strategy.exit("Exit Short", "Short", stop = short_trailing_stop, limit = short_take_profit)
// =============================================================================
// 7. VISUAL CUES & INDICATOR PLOTS
// =============================================================================
plot(strategy.position_size > 0 ? long_trailing_stop : na, "Long Dynamic Trail SL",
color = color.orange, style = plot.style_linebr, linewidth = 2)
plot(strategy.position_size < 0 ? short_trailing_stop : na, "Short Dynamic Trail SL",
color = color.orange, style = plot.style_linebr, linewidth = 2)
plotshape(long_retest_confirmed and strategy.position_size == 0, "Long Retest Entry",
style = shape.triangleup, location = location.belowbar, color = color.teal, size = size.small)
plotshape(short_retest_confirmed and strategy.position_size == 0, "Short Retest Entry",
style = shape.triangledown, location = location.abovebar, color = color.maroon, size = size.small)
plotchar(bullish_breakout and strategy.position_size == 0, "Breakout Candle", "B",
location = location.abovebar, color = color.yellow, size = size.tiny)
plotchar(bearish_breakout and strategy.position_size == 0, "Breakdown Candle", "B",
location = location.belowbar, color = color.yellow, size = size.tiny)What the script does and what it assumes
- 1
Pivots are stored on their confirmation bar. The code does not move plots or entries back to the turning-point candle.
- 2
A fresh breakout requires the previous close on the unbroken side of the same level. A late volume surge several bars beyond that level does not start a setup.
- 3
The pending level remains fixed even if a new pivot confirms. Entry requires a later bar whose extreme is inside the tolerance band and whose close respects the level.
- 4
Stops and targets persist while a market entry awaits its fill. Closed-trade detection clears old values before processing another entry signal.
- 5
The 2R target uses the signal close, not the eventual fill price. The trailing stop updates after each bar closes; it does not retroactively execute at that bar's earlier prices.
- 6
Position size is 10% of equity, not 10% risk. Commission is an illustrative 0.05% per fill and slippage is one tick. Replace these with assumptions for the instrument being tested.
Backtest Assumptions and Metrics
No historical performance test has been run for this example. The table is a reporting checklist, not a set of results. Use standard candles, record the exchange-qualified symbol, timeframe, session, exact date range, inputs and execution settings, and reserve a later period that is not used to select parameters.
| Metric | Value | Interpretation |
|---|---|---|
| Closed trades and win rate | Not measured | Report wins divided by all closed trades, plus the trade count and breakeven count. Split long and short results. |
| Average win / average loss | Not measured | Use realized payoffs from the same sample. A configured 2R target does not establish a realized 2:1 ratio. |
| Net expectancy and profit factor | Not measured | Report average net profit per trade and gross profit divided by absolute gross loss. State how costs are accounted for. |
| Drawdown and losing streak | Not measured | Record maximum equity drawdown and consecutive losses, including the period in which they occurred. |
| Cost and holdout sensitivity | Not measured | Repeat with higher costs and unchanged inputs on the reserved period. Report deterioration as well as improvement. |
Win rate must be evaluated with payoff size. In a hypothetical model with every winner equal to 2R and every loser equal to 1R, the break-even win rate before costs is one third. Actual trailing stops, gaps and costs change both payoffs. For measured trades, expectancy is win fraction times average win minus loss fraction times average absolute loss; subtract costs only if they are not already included. This arithmetic example is not a backtest result. Reject a high-win-rate claim when its trade list, dates, costs or selection method cannot be checked.
Pineify Backtest Deep Report: Beyond Basic Metrics
Use the Pine Script AI Coding Agent to adapt these rules, then inspect the generated source and test it in TradingView. Ask for one change at a time, such as a channel boundary or an optional volume filter.
The Strategy Optimizer can help compare parameter settings. Keep a separate holdout period: selecting the best historical settings is not evidence of future profitability. Use Backtest Deep Report to review supported trade exports and investigate the trade distribution behind headline metrics.
Strategy Optimization Suggestions
Test the contribution of each filter
Compare the same entry and exit rules with volume filtering enabled and disabled, then with and without the ATR buffer. Record excluded winners as well as excluded losers.
Compare direct entries with retests
Keep market, dates, sizing and exits fixed. Compare entries at the breakout with entries after retests, including how many moves never retest.
Separate target and trailing-stop effects
Compare a fixed target, a trailing stop, and their combination. Change only the exit rule and report the realized payoff distribution.
Test nearby inputs on unseen data
Choose a small parameter range before testing. Check whether nearby settings behave similarly, then evaluate unchanged settings on the reserved later period.
Frequently Asked Questions
Build and review your breakout rules
Adapt the Pine Script example with the AI Coding Agent, test the strategy in TradingView, and inspect the trade distribution with Backtest Deep Report. Historical simulations do not guarantee future results.
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.
Related Strategy Tutorials
Bollinger Bands Breakout Strategy Tutorial
Master the Bollinger Bands Squeeze and Breakout strategy on TradingView. Learn the Pine Script v5 code and professional analysis with Pineify.
SuperTrend Strategy Tutorial
Learn how to build, backtest, and optimize the SuperTrend strategy on TradingView using Pine Script v5 and Pineify Deep Report.
MACD Crossover Strategy Tutorial
Learn how to build, backtest, and optimize a MACD crossover strategy on TradingView using Pine Script v5 and Pineify Deep Report.
Candlestick Pattern Strategy Tutorial
Learn how to automate classic candlestick patterns like Hammer, Engulfing, and Doji with Pine Script v5 on TradingView.