Jelle RSI DIV
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © K-zax

//@version=5
indicator('Jelle RSI DIV')

//  INDICATOR INPUT
//--------------------------------------------------------------------------------------------------------------------------
rsiLen = input.int(14, title='RSI Length', minval=1, group='RSI')

//  OVERSELL / OVERBUY LEVEL INPUT
//--------------------------------------------------------------------------------------------------------------------------
overbuy = input.float(70, title='Overbuy', minval=0, maxval=100, group='Overbuy - Oversell')
oversell = input.float(30, title='Oversell', minval=0, maxval=100, group='Overbuy - Oversell')
useFillObOs = input.bool(true, 'Fill overbuy / oversell area', group='Overbuy - Oversell')
useBgObOs = input.bool(true, 'Plot background on overbuy / oversell', group='Overbuy - Oversell')

//  DIVERGENCE INPUT
//--------------------------------------------------------------------------------------------------------------------------
priceTopBotSource = input.string('close', options=['High / Low', 'close'], title='Price top / bot source', group='Input')
loopback = input.int(defval=2, minval=1, title='Loopback', group='Divergence')
confirmation = input.int(defval=2, minval=1, title='Confirmation', group='Divergence')
topLimit = input.float(60, title='Top Detection Limit', minval=0, maxval=100, group='Divergence')
botLimit = input.float(40, title='Bottom Detection Limit', minval=0, maxval=100, group='Divergence')
maxLoopback = input.int(defval=50, minval=1, title='Max Divergence Loopback', group='Divergence')
showToBotDiv = input.bool(true, 'Highlight Div Top / Bot', group='Divergence')

//  COLOR
//--------------------------------------------------------------------------------------------------------------------------
rsiLineColor = input(title='RSI line', defval=#0b9ebf)
obOsLineColor = input(title='Overbuy / oversell line', defval=color.gray)
fillObColor = input(title='Fill overbuy', defval=#2caa83)
fillOsColor = input(title='Fill oversell', defval=#cc141e)
bgObColor = input(title='Background overbuy', defval=#2caa83)
bgOsColor = input(title='Background oversell', defval=#cc141e)
divLineColor = input(title='Divergence line', defval=color.yellow)
topColor = input(title='Divergence top', defval=#2caa83)
botColor = input(title='Divergence bot', defval=#cc141e)

//  INDICATEUR
//--------------------------------------------------------------------------------------------------------------------------
_rsi = ta.rsi(close, rsiLen)

//  PLOT INDICATOR
//--------------------------------------------------------------------------------------------------------------------------
hline(overbuy, linestyle=hline.style_dotted, color=obOsLineColor)
hline(oversell, linestyle=hline.style_dotted, color=obOsLineColor)

rsiPlot = plot(_rsi, color=rsiLineColor)

upperLine = plot(overbuy, color=color.new(color.black, 100))
lowerLine = plot(oversell, color=color.new(color.black, 100))
fill(rsiPlot, upperLine, color=useFillObOs and _rsi > overbuy ? color.new(fillObColor, 50) : na, transp=90)
fill(rsiPlot, lowerLine, color=useFillObOs and _rsi < oversell ? color.new(fillOsColor, 50) : na, transp=90)


bgcolor(useBgObOs and _rsi > overbuy ? color.new(bgObColor, 70) : na, transp=90)
bgcolor(useBgObOs and _rsi < oversell ? color.new(bgOsColor, 70) : na, transp=90)

//	TOP / BOT FINDER
//-----------------------------
current = nz(_rsi[confirmation])


//	FIND TOP / BOT
//-----------------------------
isTop = true
isBot = true
for i = 0 to loopback + confirmation by 1
    if _rsi[i] > current or current < 50
        isTop := false
        isTop
    if _rsi[i] < current or current > 50
        isBot := false
        isBot

//	IS TOP
//-----------------------------
var top1 = 0.0
var top2 = 0.0
var top3 = 0.0

var priceTop1 = 0.0
var priceTop2 = 0.0
var priceTop3 = 0.0

var top1Time = 0
var top2Time = 0
var top3Time = 0

var top1BarIndex = 0
var top2BarIndex = 0
var top3BarIndex = 0

var masterTopTime = 0
var masterTopBarIndex = 0
var masterTopRsiValue = 0.0
var masterTopPriceValue = 0.0

if isTop
    top3 := top2
    top2 := top1
    top1 := current
    priceTop3 := priceTop2
    priceTop2 := priceTop1
    priceTop1 := priceTopBotSource == 'close' ? close[confirmation] : high[confirmation]
    top3Time := top2Time
    top2Time := top1Time
    top1Time := time[confirmation]
    top3BarIndex := top2BarIndex
    top2BarIndex := top1BarIndex
    top1BarIndex := bar_index[confirmation]
    top1BarIndex

isMasterTop = top3 < top2 and top2 > top1 and top2 > overbuy

if isMasterTop
    masterTopTime := top2Time
    masterTopBarIndex := top2BarIndex
    masterTopRsiValue := top2
    masterTopPriceValue := priceTop2
    masterTopPriceValue

if top1BarIndex - masterTopBarIndex > maxLoopback
    masterTopTime := 0
    masterTopBarIndex := 0
    masterTopRsiValue := 0.0
    masterTopPriceValue := 0.0
    masterTopPriceValue

isBearDiv = isTop and not isMasterTop and top1 > topLimit and top1 < masterTopRsiValue and priceTop1 > masterTopPriceValue and top1BarIndex - masterTopBarIndex <= maxLoopback

var line bearDivLine = na
if isBearDiv
    bearDivLine := line.new(x1=masterTopTime, y1=masterTopRsiValue, x2=top1Time, y2=top1, color=color.new(divLineColor, 0), xloc=xloc.bar_time, style=line.style_solid, width=1)
    bearDivLine

plotshape(isBearDiv and showToBotDiv ? current + 10 : na, style=shape.diamond, location=location.absolute, color=topColor, offset=-confirmation, size=size.tiny)

//	IS BOT
//-----------------------------
var bot1 = 0.0
var bot2 = 0.0
var bot3 = 0.0

var priceBot1 = 0.0
var priceBot2 = 0.0
var priceBot3 = 0.0

var bot1Time = 0
var bot2Time = 0
var bot3Time = 0

var bot1BarIndex = 0
var bot2BarIndex = 0
var bot3BarIndex = 0

var masterBotTime = 0
var masterBotBarIndex = 0
var masterBotRsiValue = 0.0
var masterBotPriceValue = 0.0

if isBot
    bot3 := bot2
    bot2 := bot1
    bot1 := current
    priceBot3 := priceBot2
    priceBot2 := priceBot1
    priceBot1 := priceTopBotSource == 'close' ? close[confirmation] : low[confirmation]
    bot3Time := bot2Time
    bot2Time := bot1Time
    bot1Time := time[confirmation]
    bot3BarIndex := bot2BarIndex
    bot2BarIndex := bot1BarIndex
    bot1BarIndex := bar_index[confirmation]
    bot1BarIndex

isMasterBot = bot3 > bot2 and bot2 < bot1 and bot2 < oversell

if isMasterBot
    masterBotTime := bot2Time
    masterBotBarIndex := bot2BarIndex
    masterBotRsiValue := bot2
    masterBotPriceValue := priceBot2
    masterBotPriceValue

if bot1BarIndex - masterBotBarIndex > maxLoopback
    masterBotTime := 0
    masterBotBarIndex := 0
    masterBotRsiValue := 0.0
    masterBotPriceValue := 0.0
    masterBotPriceValue

isBullDiv = isBot and not isMasterBot and bot1 < botLimit and bot1 > masterBotRsiValue and priceBot1 < masterBotPriceValue and bot1BarIndex - masterBotBarIndex <= maxLoopback

var line bullDivLine = na
if isBullDiv
    bullDivLine := line.new(x1=masterBotTime, y1=masterBotRsiValue, x2=bot1Time, y2=bot1, color=color.new(divLineColor, 0), xloc=xloc.bar_time, style=line.style_solid, width=1)
    bullDivLine

alertcondition(isBullDiv,title="Buy",message="Buy")
alertcondition(isBearDiv,title="Sell",message="Sell")

plotshape(isBullDiv and showToBotDiv ? current - 10 : na, style=shape.diamond, location=location.absolute, color=botColor, offset=-confirmation, size=size.tiny)

