The Reversal Trap Probability Bands is an advanced technical indicator created by BigBeluga to identify and trade fakeout traps around market extremes. Traditional envelope or band indicators often fail because traders blindly enter breakouts that quickly reverse into whipsaw losses. In order to provide a solution to this problem, this indicator combines volatility-based envelope channels with a dynamic probability tracking engine, measuring historical RSI buckets to calculate real-time win probabilities for reversal traps.
The indicator aims to visualize institutional exhaustion and subsequent mean-reversion expansions. Candle colors, bubbles, and cloud coloring are all optional in the settings.
Original code: https://www.tradingview.com/script/fcmOq240-Reversal-Trap-Probability-Bands-BigBeluga/
This example is a four-hour chart of /NQ.
The indicator aims to visualize institutional exhaustion and subsequent mean-reversion expansions. Candle colors, bubbles, and cloud coloring are all optional in the settings.
Original code: https://www.tradingview.com/script/fcmOq240-Reversal-Trap-Probability-Bands-BigBeluga/
This example is a four-hour chart of /NQ.
Code:
# Reversal_Trap_Probability_Bands [BigBeluga]
# Original Pine Script by BigBeluga
# https://www.tradingview.com/script/fcmOq240-Reversal-Trap-Probability-Bands-BigBeluga/
# Conversion and modification by Chewie 8/4/2026
declare upper;
# ═══════════════════════════════════════════════════════════
# INPUTS
# ═══════════════════════════════════════════════════════════
input color_candles = yes;
input TrapBubble = yes;
input WinBubble = yes;
input cloud = yes;
input envelope_len = 55; # Envelope Smoothness
input multiplier = 4.0; # Envelope Width
input XMult = 5.5; # Exttreme band width
input trap_window = 10; # Trap Window (Candles)
input signal_gap = 10; # Minimum Bars Between Signals
input stop_mult = 1.0; # Original Stop is 0.5 ATR Stop Multiplier
DefineGlobalColor("Bearish", CreateColor(236, 51, 51));
DefineGlobalColor("Bullish", CreateColor(18, 175, 159));
DefineGlobalColor("MidLine", CreateColor(120, 123, 134));
# ═══════════════════════════════════════════════════════════
# CORE CALCULATIONS
# ═══════════════════════════════════════════════════════════
def basis = ExpAverage(close, envelope_len);
def vola = Average(TrueRange(high, close, low), envelope_len); # ATR(envelope_len)
def rsi = RSI(Length = 20);
def upper_band = basis + (multiplier * vola);
def lower_band = basis - (multiplier * vola);
def Xupper_band = basis + (XMult * vola);
def Xlower_band = basis - (XMult * vola);
def atr = Average(TrueRange(high, close, low), 100) * stop_mult;
# ═══════════════════════════════════════════════════════════
# TRAP COUNTER STATES
# ═══════════════════════════════════════════════════════════
def close_above_count = if high > upper_band
then close_above_count[1] + 1
else 0;
def close_below_count = if low < lower_band
then close_below_count[1] + 1
else 0;
# ═══════════════════════════════════════════════════════════
# RAW TRAP DETECTION
# Bear trap: price poked above upper band then closed back inside
# Bull trap: price poked below lower band then closed back inside
# ═══════════════════════════════════════════════════════════
# Bear trap — close drops back below upper_band after a poke above
def raw_bear_trap =
if close < upper_band and
((high > upper_band and close[1] < upper_band and close_above_count[1] <= trap_window) or
(close[1] > upper_band and close_above_count[1] <= trap_window))
then 1
else 0;
# Bull trap — close recovers back above lower_band after a poke below
def raw_bull_trap =
if close > lower_band and
((low < lower_band and close[1] > lower_band and close_below_count[1] <= trap_window) or
(close[1] < lower_band and close_below_count[1] <= trap_window))
then 1
else 0;
# ═══════════════════════════════════════════════════════════
# COOLDOWN / SIGNAL GAP
# Only fire a signal if at least signal_gap bars have passed since the last one
# ═══════════════════════════════════════════════════════════
def any_raw_signal = if raw_bull_trap or raw_bear_trap then 1 else 0;
# Track bars since the last fired signal
def bars_since_signal =
if any_raw_signal and bars_since_signal[1] >= signal_gap
then 0
else if bars_since_signal[1] < signal_gap
then bars_since_signal[1] + 1
else bars_since_signal[1] + 1;
def can_fire = bars_since_signal[1] >= signal_gap;
def bull_trap = raw_bull_trap and can_fire;
def bear_trap = raw_bear_trap and can_fire;
# Reset the cooldown counter whenever a signal fires
def cooldown =
if bull_trap or bear_trap
then 0
else if cooldown[1] < signal_gap
then cooldown[1] + 1
else cooldown[1] + 1;
# Refined with the cooldown counter (single-source-of-truth approach)
def bull_signal = raw_bull_trap and cooldown[1] >= signal_gap;
def bear_signal = raw_bear_trap and cooldown[1] >= signal_gap;
# ═══════════════════════════════════════════════════════════
# STOP & TARGET LEVELS — held active until resolved
# Bull: target = basis, stop = lowest(low,2) - atr
# Bear: target = basis, stop = highest(high,2) + atr
# ═══════════════════════════════════════════════════════════
# ── Bull trade tracking ──────────────────────────────────
def bull_target_stored =
CompoundValue(1,
if bull_signal
then basis
else bull_target_stored[1],
basis);
def bull_stop_stored =
CompoundValue(1,
if bull_signal
then Lowest(low, 2) - atr
else bull_stop_stored[1],
Lowest(low, 2) - atr);
def bull_active =
CompoundValue(1,
if bull_signal and bull_active[1] == 0
then 1
else if bull_active[1] == 1 and
(high >= bull_target_stored[1] or low < bull_stop_stored[1])
then 0
else bull_active[1],
0);
def bull_target_level = if bull_active then bull_target_stored else Double.NaN;
def bull_stop_level = if bull_active then bull_stop_stored else Double.NaN;
# ── Bear trade tracking ──────────────────────────────────
def bear_target_stored =
CompoundValue(1,
if bear_signal
then basis
else bear_target_stored[1],
basis);
def bear_stop_stored =
CompoundValue(1,
if bear_signal
then Highest(high, 2) + atr
else bear_stop_stored[1],
Highest(high, 2) + atr);
def bear_active =
CompoundValue(1,
if bear_signal and bear_active[1] == 0
then 1
else if bear_active[1] == 1 and
(low <= bear_target_stored[1] or high > bear_stop_stored[1])
then 0
else bear_active[1],
0);
def bear_target_level = if bear_active then bear_target_stored else Double.NaN;
def bear_stop_level = if bear_active then bear_stop_stored else Double.NaN;
# ── Win/Loss detection for chart bubbles ────────────────
def bull_win = bull_active[1] == 1 and high >= bull_target_stored[1];
def bull_loss = bull_active[1] == 1 and low < bull_stop_stored[1];
def bear_win = bear_active[1] == 1 and low <= bear_target_stored[1];
def bear_loss = bear_active[1] == 1 and high > bear_stop_stored[1];
# ═══════════════════════════════════════════════════════════
# PLOTS — Envelope Bands
# ═══════════════════════════════════════════════════════════
plot UpperBand = upper_band;
UpperBand.SetDefaultColor(GlobalColor("Bearish"));
UpperBand.SetLineWeight(3);
plot LowerBand = lower_band;
LowerBand.SetDefaultColor(GlobalColor("Bullish"));
LowerBand.SetLineWeight(3);
plot XUpperBand = Xupper_band;
plot XLowerBand = Xlower_band;
XupperBand.setdefaultColor(color.gray);
XLowerband.setdefaultColor(color.gray);
plot BasisLine = basis;
BasisLine.SetDefaultColor(GlobalColor("MidLine"));
#BasisLine.SetStyle(Curve.SHORT_DASH);
BasisLine.SetLineWeight(1);
plot Bull = if bull_signal then low - ATR(60) / 2 else Double.NaN;
Bull.SetPaintingStrategy(PaintingStrategy.arrow_up);
Bull.SetDefaultColor(Color.cyan);
Bull.SetLineWeight(3);
plot Bear = if bear_signal then high + ATR(60) / 2 else Double.NaN;
Bear.SetPaintingStrategy(PaintingStrategy.arrow_down);
Bear.SetDefaultColor(Color.magenta);
Bear.SetLineWeight(3);
# Gradient fills (upper zone = bearish, lower zone = bullish)
AddCloud(if cloud then UpperBand else Double.NaN, BasisLine, CreateColor(236, 51, 51), CreateColor(236, 51, 51));
AddCloud(if cloud then BasisLine else Double.NaN, LowerBand, CreateColor(18, 175, 159), CreateColor(18, 175, 159));
AddCloud(if cloud then UpperBand else Double.NaN, XUpperband, color.gray, color.gray);
AddCloud(if cloud then XLowerband else Double.NaN, LowerBand, color.gray, color.gray);
# ═══════════════════════════════════════════════════════════
# PLOTS — Active Stop & Target Dashed Lines
# ═══════════════════════════════════════════════════════════
plot BullTarget = bull_target_level;
BullTarget.SetDefaultColor(GlobalColor("Bullish"));
#BullTarget.SetStyle(Curve.SHORT_DASH);
BullTarget.SetLineWeight(2);
plot BullStop = bull_stop_level;
BullStop.SetDefaultColor(GlobalColor("Bearish"));
#BullStop.SetStyle(Curve.SHORT_DASH);
BullStop.SetLineWeight(2);
plot BearTarget = bear_target_level;
BearTarget.SetDefaultColor(GlobalColor("Bullish"));
#BearTarget.SetStyle(Curve.SHORT_DASH);
BearTarget.SetLineWeight(2);
plot BearStop = bear_stop_level;
BearStop.SetDefaultColor(GlobalColor("Bearish"));
#BearStop.SetStyle(Curve.SHORT_DASH);
BearStop.SetLineWeight(2);
# ═══════════════════════════════════════════════════════════
# SIGNAL LABELS
# ═══════════════════════════════════════════════════════════
# Bull Trap signal bubble (below bar)
AddChartBubble(trapbubble and
bull_signal,
low - atr,
"TRAP UP\nRSI: " + Round(rsi, 0),
GlobalColor("Bullish"),
no # below price
);
# Bear Trap signal bubble (above bar)
AddChartBubble(trapbubble and
bear_signal,
high + atr,
"TRAP DOWN\nRSI: " + Round(rsi, 0),
GlobalColor("Bearish"),
yes # above price
);
# Win confirmation bubbles
AddChartBubble(winbubble and bull_win, high, "Y", GlobalColor("Bullish"), yes);
AddChartBubble(winbubble and bear_win, low, "Y", GlobalColor("Bearish"), no);
AssignPriceColor(
if !color_candles
then Color.CURRENT
else if close > upper_band
then Color.MAGENTA
else if close < lower_band
then Color.green
else if close > basis
then GlobalColor("Bearish")
else GlobalColor("Bullish")
);
Last edited: