//@version=6
indicator("RSI_RD", format=format.price, timeframe="", timeframe_gaps=true)
// RSI_RD - RSI Divergence Detector (Ryan DeBraal)
// This script plots a standard RSI along with advanced automatic divergence detection.
// It identifies four types of divergences using pivot logic and configurable
// lookback windows. Signals appear directly on the RSI line as plotted marks and labels.
//
// FEATURES
// - Standard RSI with user-defined length and source.
// - Midline (50), overbought (70), and oversold (30) levels with shaded background.
// - Automatic detection of:
// • Regular Bullish Divergence
// • Regular Bearish Divergence
// • Hidden Bullish Divergence
// • Hidden Bearish Divergence
// - Each divergence type can be toggled on/off individually.
// - Pivot-based detection using left/right lookback lengths.
// - Range filter (bars since pivot) to avoid stale or invalid divergences.
// - Colored markers and labels placed exactly on pivot points.
// - Alerts for all four divergence conditions.
//
// PURPOSE
// This indicator makes RSI divergence trading systematic and visual.
// It highlights when price action disagrees with RSI momentum — often signaling
// exhaustion, reversal setups, or continuation opportunities depending on the divergence type.
// Ideal for combining with trend filters, VWAP, or ORB structures.
// Inputs
lineThickness = input.int(2)
len = input.int(title="RSI Period", minval=1, defval=14)
src = input(title="RSI Source", defval=close)
lbR = input(title="Pivot Lookback Right", defval=5, display = display.data_window)
lbL = input(title="Pivot Lookback Left", defval=5, display = display.data_window)
rangeUpper = input(title="Max of Lookback Range", defval=60, display = display.data_window)
rangeLower = input(title="Min of Lookback Range", defval=5, display = display.data_window)
plotBull = input(title="Plot Bullish", defval=true, display = display.data_window)
plotHiddenBull = input(title="Plot Hidden Bullish", defval=false, display = display.data_window)
plotBear = input(title="Plot Bearish", defval=true, display = display.data_window)
plotHiddenBear = input(title="Plot Hidden Bearish", defval=false, display = display.data_window)
bearColor = color.red
bullColor = color.green
hiddenBullColor = color.new(color.green, 80)
hiddenBearColor = color.new(color.red, 80)
textColor = color.white
noneColor = color.new(color.white, 100)
aboveColor = color.blue
belowColor = color.blue
// RSI
osc = ta.rsi(src, len)
// green above 50, red below 50
rsiColor = osc >= 50 ? aboveColor : belowColor
plot(osc, "RSI", color=rsiColor, linewidth=lineThickness)
hline(50, "Middle Line", #787B86, hline.style_dotted)
obLevel = hline(70, "Overbought", #787B86, hline.style_dotted)
osLevel = hline(30, "Oversold", #787B86, hline.style_dotted)
fill(obLevel, osLevel, color=color.rgb(33, 150, 243, 90))
// Pivots
plFound = not na(ta.pivotlow(osc, lbL, lbR))
phFound = not na(ta.pivothigh(osc, lbL, lbR))
_inRange(cond) =>
bars = ta.barssince(cond)
rangeLower <= bars and bars <= rangeUpper
//------------------------------------------------------------------------------
// Regular Bullish
inRangePl = _inRange(plFound[1])
oscHL = osc[lbR] > ta.valuewhen(plFound, osc[lbR], 1) and inRangePl
priceLL = low[lbR] < ta.valuewhen(plFound, low[lbR], 1)
bullCondAlert = priceLL and oscHL and plFound
bullCond = plotBull and bullCondAlert
plot(plFound ? osc[lbR] : na, "Regular Bullish", color=bullCond ? bullColor : noneColor, offset=-lbR, display=display.pane, editable=plotBull)
//------------------------------------------------------------------------------
// Hidden Bullish
oscLL = osc[lbR] < ta.valuewhen(plFound, osc[lbR], 1) and inRangePl
priceHL = low[lbR] > ta.valuewhen(plFound, low[lbR], 1)
hiddenBullCondAlert = priceHL and oscLL and plFound
hiddenBullCond = plotHiddenBull and hiddenBullCondAlert
plot(plFound ? osc[lbR] : na, "Hidden Bullish", color=hiddenBullCond ? hiddenBullColor : noneColor, offset=-lbR, display=display.pane, editable=plotHiddenBull)
//------------------------------------------------------------------------------
// Regular Bearish
inRangePh = _inRange(phFound[1])
oscLH = osc[lbR] < ta.valuewhen(phFound, osc[lbR], 1) and inRangePh
priceHH = high[lbR] > ta.valuewhen(phFound, high[lbR], 1)
bearCondAlert = priceHH and oscLH and phFound
bearCond = plotBear and bearCondAlert
plot(phFound ? osc[lbR] : na, "Regular Bearish", color=bearCond ? bearColor : noneColor, offset=-lbR, display=display.pane, editable=plotBear)
//------------------------------------------------------------------------------
// Hidden Bearish
oscHH = osc[lbR] > ta.valuewhen(phFound, osc[lbR], 1) and inRangePh
priceLH = high[lbR] < ta.valuewhen(phFound, high[lbR], 1)
hiddenBearCondAlert = priceLH and oscHH and phFound
hiddenBearCond = plotHiddenBear and hiddenBearCondAlert
plot(phFound ? osc[lbR] : na, "Hidden Bearish", color=hiddenBearCond ? hiddenBearColor : noneColor, linewidth=2, offset=-lbR, display=display.pane, editable=plotHiddenBear)
// Alerts
alertcondition(bullCondAlert, "Regular Bullish Divergence", "Regular Bullish Divergence")
alertcondition(hiddenBullCondAlert, "Hidden Bullish Divergence", "Hidden Bullish Divergence")
alertcondition(bearCondAlert, "Regular Bearish Divergence", "Regular Bearish Divergence")
alertcondition(hiddenBearCondAlert, "Hidden Bearish Divergence", "Hidden Bearish Divergence")