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
| Input | Default | What it changes |
|---|---|---|
| Source | close | Price used for RSI. |
| RSI length | 14 | Smoothness of RSI. |
| Overbought / Oversold level | 70 / 30 | Zone boundaries and zone-exit alerts. |
| Pivot bars left / right | 5 / 5 | Pivot strictness and confirmation delay. |
| Min / Max bars between pivots | 5 / 60 | Ignores pivot pairs too close or too far apart. |
| Show regular / hidden divergence | On / On | Toggle each type (also affects alerts). |
| Draw divergence lines | On | Lines connecting the two RSI pivots. |
| Highlight RSI inside OB/OS zones | On | Recolors 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}})")

