Skip to content

Free TradingView indicator

RSI Divergence

RSI with shaded overbought and oversold zones and automatic regular and hidden divergence detection on confirmed RSI pivots.

Momentumv1.0TradingView (Pine Script v6) Non-repainting signals Alerts

What it does

RSI Divergence plots the Relative Strength Index with shaded overbought and oversold zones and automatically marks four kinds of divergence between price and RSI: regular bullish, regular bearish, hidden bullish and hidden bearish. The RSI line changes color while it is inside a zone, and there are alerts for each divergence type and for RSI leaving either zone.

How it works

RSI is the standard Wilder RSI over the chosen length, ranging from 0 to 100.

Pivots are detected on RSI itself. An RSI pivot low is lower than the "bars left" values before it and the "bars right" values after it; pivot highs mirror this. When a new pivot is confirmed, it is compared with the previous pivot of the same type, as long as the two are within the minimum and maximum bar distance.

  • Regular bullish: price makes a lower low while RSI makes a higher low.
  • Hidden bullish: price makes a higher low while RSI makes a lower low.
  • Regular bearish: price makes a higher high while RSI makes a lower high.
  • Hidden bearish: price makes a lower high while RSI makes a higher high.

Price is taken from the bar low or high at each RSI pivot. Regular divergences are drawn with solid lines, hidden ones with dashed lines. The zone-exit alerts fire when RSI crosses back above the oversold level or back below the overbought level on a closed bar.

How to use it

  • An overbought or oversold reading on its own is not a reversal signal; strong trends can keep RSI in a zone for a long time. Divergence adds evidence that momentum is fading.
  • Regular divergences that form inside or near a zone, at a meaningful support or resistance level, give more context than ones in the middle of the range.
  • Hidden divergences suit trend-continuation setups: in a downtrend, a hidden bearish divergence at a lower high can suggest the bounce is running out.
  • Wait for price confirmation, such as a break of the most recent swing, before treating any divergence as actionable. The RSI lesson covers the oscillator in detail.

Settings

InputDefaultWhat it changes
SourceclosePrice used for RSI.
RSI length14Smoothness of RSI.
Overbought / Oversold level70 / 30Zone boundaries and zone-exit alerts.
Pivot bars left / right5 / 5Pivot strictness and confirmation delay.
Min / Max bars between pivots5 / 60Ignores pivot pairs too close or too far apart.
Show regular / hidden divergenceOn / OnToggle each type (also affects alerts).
Draw divergence linesOnLines connecting the two RSI pivots.
Highlight RSI inside OB/OS zonesOnRecolors RSI while in a zone.

Limitations

  • Pivots confirm only after "bars right" bars, so every divergence appears late. Once drawn, it does not change.
  • In strong trends regular divergences can appear repeatedly while price keeps going.
  • Comparing pivots on RSI rather than on price means the price extreme used may not be the true swing low or high.
  • Fixed 70/30 levels do not suit every market. Trending instruments often respect 80/40 or 60/20 better, so adjust them to your chart.

Pine Script source

In TradingView open Pine Editor, paste the code, click Save and then Add to chart.

FXP_RsiDivergence.pine

99 lines

// © FXPIPS ONLINE — https://fxpips.online/tradingview-indicators/rsi-divergence/
// License: MIT. Educational tool — not financial advice. Trading involves substantial risk.
//@version=6
// RSI with overbought/oversold zones and regular + hidden divergence detection on confirmed
// RSI pivots. Divergences appear "Pivot bars right" bars after the pivot and are never redrawn.
indicator("FXP RSI Divergence", shorttitle = "FXP RSI Div", overlay = false, max_lines_count = 200, max_labels_count = 200)

// ── Inputs ─────────────────────────────────────────────────────────────
grpR    = "RSI"
src     = input.source(close, "Source", group = grpR)
rsiLen  = input.int(14, "RSI length", minval = 2, group = grpR)
obLevel = input.float(70.0, "Overbought level", minval = 50.0, maxval = 100.0, group = grpR)
osLevel = input.float(30.0, "Oversold level", minval = 0.0, maxval = 50.0, group = grpR)

grpD        = "Divergence"
lbL         = input.int(5, "Pivot bars left", minval = 1, group = grpD)
lbR         = input.int(5, "Pivot bars right", minval = 1, group = grpD)
minGap      = input.int(5, "Min bars between pivots", minval = 1, group = grpD)
maxGap      = input.int(60, "Max bars between pivots", minval = 2, group = grpD)
showRegular = input.bool(true, "Show regular divergence", group = grpD)
showHidden  = input.bool(true, "Show hidden divergence", group = grpD)
showLines   = input.bool(true, "Draw divergence lines", group = grpD)

grpV     = "Display"
colRsi   = input.color(#7e57c2, "RSI line", group = grpV)
colBull  = input.color(#26a69a, "Bullish color", group = grpV)
colBear  = input.color(#ef5350, "Bearish color", group = grpV)
showZone = input.bool(true, "Highlight RSI inside OB/OS zones", group = grpV)

// ── RSI ────────────────────────────────────────────────────────────────
rsi = ta.rsi(src, rsiLen)

// ── Pivots on RSI ──────────────────────────────────────────────────────
plFound = not na(ta.pivotlow(rsi, lbL, lbR))
phFound = not na(ta.pivothigh(rsi, lbL, lbR))

rsiPivot  = rsi[lbR]
lowPivot  = low[lbR]
highPivot = high[lbR]
pivotBar  = bar_index - lbR

prevRsiPl = ta.valuewhen(plFound, rsiPivot, 1)
prevLowPl = ta.valuewhen(plFound, lowPivot, 1)
prevPlBar = ta.valuewhen(plFound, pivotBar, 1)
prevRsiPh = ta.valuewhen(phFound, rsiPivot, 1)
prevHiPh  = ta.valuewhen(phFound, highPivot, 1)
prevPhBar = ta.valuewhen(phFound, pivotBar, 1)

plGap = pivotBar - prevPlBar
phGap = pivotBar - prevPhBar
plOk  = plFound and plGap >= minGap and plGap <= maxGap and barstate.isconfirmed
phOk  = phFound and phGap >= minGap and phGap <= maxGap and barstate.isconfirmed

regBull = showRegular and plOk and rsiPivot > prevRsiPl and lowPivot < prevLowPl
hidBull = showHidden and plOk and rsiPivot < prevRsiPl and lowPivot > prevLowPl
regBear = showRegular and phOk and rsiPivot < prevRsiPh and highPivot > prevHiPh
hidBear = showHidden and phOk and rsiPivot > prevRsiPh and highPivot < prevHiPh

// Zone exits (RSI leaves overbought / oversold on a closed bar)
leaveOs = ta.crossover(rsi, osLevel) and barstate.isconfirmed
leaveOb = ta.crossunder(rsi, obLevel) and barstate.isconfirmed

// ── Drawings ───────────────────────────────────────────────────────────
if regBull
    label.new(pivotBar, rsiPivot, "Bull", style = label.style_label_up, color = colBull, textcolor = color.white, size = size.small)
    if showLines
        line.new(prevPlBar, prevRsiPl, pivotBar, rsiPivot, color = colBull, width = 2)
if hidBull
    label.new(pivotBar, rsiPivot, "H Bull", style = label.style_label_up, color = color.new(colBull, 35), textcolor = color.white, size = size.small)
    if showLines
        line.new(prevPlBar, prevRsiPl, pivotBar, rsiPivot, color = colBull, width = 1, style = line.style_dashed)
if regBear
    label.new(pivotBar, rsiPivot, "Bear", style = label.style_label_down, color = colBear, textcolor = color.white, size = size.small)
    if showLines
        line.new(prevPhBar, prevRsiPh, pivotBar, rsiPivot, color = colBear, width = 2)
if hidBear
    label.new(pivotBar, rsiPivot, "H Bear", style = label.style_label_down, color = color.new(colBear, 35), textcolor = color.white, size = size.small)
    if showLines
        line.new(prevPhBar, prevRsiPh, pivotBar, rsiPivot, color = colBear, width = 1, style = line.style_dashed)

// ── Plots ──────────────────────────────────────────────────────────────
rsiColor = showZone and rsi >= obLevel ? colBear : showZone and rsi <= osLevel ? colBull : colRsi
plot(rsi, "RSI", color = rsiColor, linewidth = 2)
hOb  = hline(obLevel, "Overbought", color = color.new(colBear, 40), linestyle = hline.style_dashed)
hMid = hline(50, "Midline", color = color.new(color.gray, 60), linestyle = hline.style_dotted)
hOs  = hline(osLevel, "Oversold", color = color.new(colBull, 40), linestyle = hline.style_dashed)
hTop = hline(100, "Top", color = color.new(color.gray, 100))
hBot = hline(0, "Bottom", color = color.new(color.gray, 100))
fill(hOb, hTop, color = color.new(colBear, 90), title = "Overbought zone")
fill(hOs, hBot, color = color.new(colBull, 90), title = "Oversold zone")

// ── Alerts ─────────────────────────────────────────────────────────────
alertcondition(regBull, "Regular bullish divergence", "RSI regular bullish divergence confirmed on {{ticker}} ({{interval}})")
alertcondition(hidBull, "Hidden bullish divergence", "RSI hidden bullish divergence confirmed on {{ticker}} ({{interval}})")
alertcondition(regBear, "Regular bearish divergence", "RSI regular bearish divergence confirmed on {{ticker}} ({{interval}})")
alertcondition(hidBear, "Hidden bearish divergence", "RSI hidden bearish divergence confirmed on {{ticker}} ({{interval}})")
alertcondition(leaveOs, "RSI leaves oversold", "RSI crossed back above the oversold level on {{ticker}} ({{interval}})")
alertcondition(leaveOb, "RSI leaves overbought", "RSI crossed back below the overbought level on {{ticker}} ({{interval}})")

Key inputs

Key inputs of RSI Divergence
InputDefaultDescription
RSI length14Bars used for the Relative Strength Index.
Overbought level70Upper zone boundary.
Oversold level30Lower zone boundary.
Pivot bars left5Bars to the left an RSI pivot must exceed.
Pivot bars right5Bars to the right an RSI pivot must exceed; also the confirmation delay.
Min / max bars between pivots5 / 60Allowed distance between the two pivots being compared.

Educational tools, not advice. Indicators and Expert Advisors describe price; they do not predict it, and past behaviour does not guarantee future results. Test everything on a demo account first and never risk money you cannot afford to lose. See our disclaimer.

Keep exploring

Related tradingview indicators