RSI Divergence Method
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © RozaniGhani-RG

//@version=5
indicator('RSI Divergence Method', 'RDM')

// 0. Inputs
// 1. Custom Functions
// 2. Variables
// 3. Plots

//#region ———————————————————— 0. Inputs
src   = input.source(close, 'Source / Length', inline = '0', group = 'RSI')
len   = input.int(      14,                '', inline = '0', group = 'RSI', minval = 1)
left  = input.int(       5,    'Left / Right', inline = '1', group = 'Pivot Lookback')
right = input.int(       5,                '', inline = '1', group = 'Pivot Lookback')
min   = input.int(       5,       'Min / Max', inline = '2', group = 'Lookback Range')
max   = input.int(      60,                '', inline = '2', group = 'Lookback Range')
boolBigBull = input.bool(true,  'Big | Small', inline = '3', group = 'Bull')
boolKidBull = input.bool(true,             '', inline = '3', group = 'Bull')
boolBigBear = input.bool(true,  'Big | Small', inline = '4', group = 'Bear')
boolKidBear = input.bool(true,             '', inline = '4', group = 'Bear')
//#endregion

//#region ———————————————————— 1. Custom Functions
// @type        Used for divergence setup
// @field osc   oscillator used such as rsi
// @field src   source such as high or low
// @field right offset value for pivot
// @field min   minimum bars for barssince
// @field max   maximum bars for barssince
type setup
	float osc   = na
	float right = na
	int   min   = na
	int   max   = na

// @function          range between bars
// @param   condition bars since
// @param   min       minimum bars
// @param   max       maximum bars
method rangeBetween(setup startUp = na, bool condition = true) =>
	bars = ta.barssince(condition)
	startUp.min <= bars and bars <= startUp.max

// @function          divergence
// @param   higher    higher or lower condition
// @param   rsi       rsi
// @param   src       source close
// @param   condition bars since
// @param   right     nbars since
// @param   min       minimum bars
// @param   max       maximum bars
// @param   enable    bool condition
// @returns osc       rsiHL / rsiLL / rsiLH / rsiHH
// @returns price     priceLL / priceHL / priceHH / priceLH
// @returns output    bigBull / kidBull / bigBear / kidBear
method divergence(setup startUp = na, bool higher = true, float src = na,bool condition = true, bool enable = true) =>
	vw0 = ta.valuewhen(condition, startUp.osc[startUp.right], 1) 
	vw1 = ta.valuewhen(condition,         src[startUp.right], 1) 
	rng = startUp.rangeBetween(condition[1])
	ref = startUp.osc[startUp.right]
	osc = switch higher
		true  => ref > vw0 and rng
		false => ref < vw0 and rng
	price  = switch higher
		true  => src[right] < vw1
		false => src[right] > vw1
	output   = enable and price and osc and condition
	[osc, price, output]

// @function          validate
// @param   this      refer to formula or float
// @returns           true / false
method validate(float this = na) => na(this) ? false : true
//#endregion

//#region ———————————————————— 2. Variables
rsi     = ta.rsi(src, len)
PL      = ta.pivotlow( rsi, left, right).validate()
PH      = ta.pivothigh(rsi, left, right).validate()
startUp = setup.new(rsi, right, min, max)

//																				// RSI	Price	Bul / Bear
[rsiHL, priceLL, bigBull] = startUp.divergence(true,  low,  PL, boolBigBull)	// HL	LL		Big Bull
[rsiLL, priceHL, kidBull] = startUp.divergence(false, low,  PL, boolKidBull)	// LL	HL		Kid Bull
[rsiLH, priceHH, bigBear] = startUp.divergence(false, high, PH, boolBigBear)	// LH	HH		Big Bear
[rsiHH, priceLH, kidBear] = startUp.divergence(true,  high, PH, boolKidBear)	// HH	LH		Kid Bear
//#endregion

//#region ———————————————————— 3. Plots
plot(rsi, 'RSI', chart.fg_color, 2)

hline(50, "Middle Line", color.silver, hline.style_dashed, 2, false)

OB100 = hline(100, 'OVERBOUGHT', color.red, hline.style_solid, 1, false, display.none)
OB070 = hline( 70, 'OVERBOUGHT', color.red, hline.style_solid, 1, false, display.none)
OS030 = hline( 30, 'OVERSOLD',   color.red, hline.style_solid, 1, false, display.none)
OS000 = hline(  0, 'OVERSOLD',   color.red, hline.style_solid, 1, false, display.none)

fill(OB070, OB100, color.new(color.red,  90), 'OVERBOUGHT')
fill(OS000, OS030, color.new(color.lime, 90), 'OVERSOLD')

plot(PL ? rsi[right] : na,   'BIG BULL', bigBull ? color.lime : color.new(color.lime, 100), 2, offset = -right, editable = false)
plot(PL ? rsi[right] : na, 'SMALL BULL', kidBull ? color.lime : color.new(color.lime, 100), 2, offset = -right, editable = false)
plot(PH ? rsi[right] : na,   'BIG BEAR', bigBear ? color.red  : color.new(color.red,  100), 2, offset = -right, editable = false)
plot(PH ? rsi[right] : na, 'SMALL BEAR', kidBear ? color.red  : color.new(color.red,  100), 2, offset = -right, editable = false)

plotshape(bigBull ? rsi[right] : na,   'BIG BULL LABEL', shape.labelup,   location.bottom,   color.new(color.lime, 100), -right, 'BIG\nBULL', color.lime,     display = display.pane, editable = false)
plotshape(kidBull ? rsi[right] : na, 'SMALL BULL LABEL', shape.labelup,   location.absolute, color.new(color.lime, 100), -right, 'SMALL\nBULL', chart.fg_color, display = display.pane, editable = false)
plotshape(bigBear ? rsi[right] : na,   'BIG BEAR LABEL', shape.labeldown, location.top,      color.new(color.red,  100), -right, 'BIG\nBEAR', color.red,      display = display.pane, editable = false)
plotshape(kidBear ? rsi[right] : na, 'SMALL BEAR LABEL', shape.labeldown, location.absolute, color.new(color.red,  100), -right, 'SMALL\nBEAR', chart.fg_color, display = display.pane, editable = false)
//#endregion