RSI Divergence Scanner by zdmre
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © zdmre

//@version=5
indicator("RSI Divergence Scanner by zdmre", overlay=false, max_bars_back = 500)

////INPUTs
inputgroup = "**********INPUTs**********"
shdiv = input.bool(defval = true, title = "Show Divergences", group=inputgroup)
shpoints = input.bool(defval = true, title = "Show Points", group=inputgroup)
shdate = input.bool(defval = true, title = "Show Dates", group=inputgroup)
shtline = input.bool(defval = true, title = "Show Trend Lines", group=inputgroup)
shbg = input.bool(defval = false, title = "Show StdDev Background", group=inputgroup)

////ALERTs
alertgroup = "**********ALERTs**********"
i_detectLongs  = input(true, "Detect Longs", group=alertgroup)
i_detectShorts = input(true, "Detect Shorts", group=alertgroup)

////RSI
rsigroup = "**********RSI**********"
len = input.int(14, minval=1, title='RSI Length', group = rsigroup)
src = input(close, 'RSI Source', group = rsigroup)
up = ta.rma(math.max(ta.change(src), 0), len)
down = ta.rma(-math.min(ta.change(src), 0), len)
rsi = down == 0 ? 100 : up == 0 ? 0 : 100 - 100 / (1 + up / down)
plot(rsi, 'RSI', color=color.new(#7E57C2, 0))

band1 = hline(70, "Upper Band", color=#787B86)
bandm = hline(50, "Middle Band", color=color.new(#787B86, 50))
band0 = hline(30, "Lower Band", color=#787B86)
fill(band1, band0, color=color.rgb(126, 87, 194, 90), title="Background")

ob= ta.cross(rsi, 70) == 1 and rsi >= 70
os = ta.cross(rsi, 30) == 1 and rsi <= 30

plot(ob and shpoints ? rsi : na ,title='Overbought', style=plot.style_circles, color=color.new(color.red, 0), linewidth=5)
plot(os and shpoints ? rsi : na ,title='Oversold ', style=plot.style_circles, color=color.new(color.green, 0), linewidth=5)

////STDDEV
drawLine(X1, Y1, X2, Y2, ExtendType, Color, LineStyle) =>
    var line Line = na
    Line := line.new(X1, Y1, X2, Y2, xloc.bar_index, ExtendType, Color, LineStyle, 1)
    line.delete(Line[1])

rsdcr2(PeriodMinusOne, Deviations, Estimate) =>
    var period = PeriodMinusOne + 1
    var devDenominator = Estimate == 'Unbiased' ? PeriodMinusOne : period
    Ex = 0.0
    Ex2 = 0.0
    Exy = 0.0
    Ey = 0.0
    for i = 0 to PeriodMinusOne by 1
        closeI = nz(rsi[i])
        Ex := Ex + i
        Ex2 := Ex2 + i * i
        Exy := Exy + closeI * i
        Ey := Ey + closeI
        Ey
    ExEx = Ex * Ex
    slope = Ex2 == ExEx ? 0.0 : (period * Exy - Ex * Ey) / (period * Ex2 - ExEx)
    linearRegression = (Ey - slope * Ex) / period
    intercept = linearRegression + bar_index * slope
    deviation = 0.0
    for i = 0 to PeriodMinusOne by 1
        deviation := deviation + math.pow(nz(rsi[i]) - (intercept - bar_index[i] * slope), 2.0)
        deviation
    deviation := Deviations * math.sqrt(deviation / devDenominator)
    correlate = ta.correlation(rsi, bar_index, period)
    r2 = math.pow(correlate, 2.0)
    [linearRegression, slope, deviation, correlate, r2]
periodTrend = 140
deviationsAmnt = 1.1
estimatorType = 'Unbiased'
var extendType = 'Right'
periodMinusOne = periodTrend - 1
[linReg, slope, deviation, correlate, r2] = rsdcr2(periodMinusOne, deviationsAmnt, estimatorType)
endPointBar = bar_index - periodTrend + 1
endPointY = linReg + slope * periodMinusOne

////TRENDLINEs
trendgroup = "**********TREND LINE**********"
length = input(21, title='Zigzag Length')

r = rsi[length]
l = rsi[14]

ph = ta.pivothigh(rsi,length,length)
pl = ta.pivotlow(rsi,length,length)
valH = ta.valuewhen(ph,r,0)
valL = ta.valuewhen(pl,r,0)
valpH = ta.valuewhen(ph,r,1)
valpL = ta.valuewhen(pl,r,1)

d = math.abs(r)
n = bar_index
label lbl = na
HIH = valH > valpH ? "HH" : na 
HIL = valH < valpH ? "LH" : na
LOL = valL < valpL ? "LL" : na
LOH = valL > valpL ? "HL" : na

if ph and valH > valpH and shtline
    lbl := label.new(n[length],math.max(r,l),HIH,color=#ff1100  ,
      style=label.style_label_down,textcolor=color.white,size=size.tiny)
    label.delete(lbl[1])
    linehh = line.new(n[length],math.max(r,l), bar_index, math.max(r,l), extend=extend.right, color=color.red)
    line.delete(linehh[1])
else if ph and valH < valpH and shtline
    lbl := label.new(n[length],math.max(r,l),HIL,color=#e79700 ,
      style=label.style_label_down,textcolor=color.white,size=size.tiny)
    label.delete(lbl[1])
    linehl = line.new(n[length],math.max(r,l), bar_index, math.max(r,l), extend=extend.right, color=color.orange)
    line.delete(linehl[1])
else if pl and valL < valpL and shtline
    lbl := label.new(n[length],math.min(r,l),LOL,color=#009c0c   ,  
      style=label.style_label_up,textcolor=color.white,size=size.tiny)
    label.delete(lbl[1]) 
    linell = line.new(n[length],math.min(r,l), bar_index, math.min(r,l), extend=extend.right, color=color.green)
    line.delete(linell[1])
else if pl and valL > valpL and shtline
    lbl := label.new(n[length],math.min(r,l),LOH,color= #0073ee ,
      style=label.style_label_up,textcolor=color.white,size=size.tiny)
    label.delete(lbl[1])
    linelh = line.new(n[length],math.min(r,l), bar_index, math.min(r,l), extend=extend.right, color=color.blue)
    line.delete(linelh[1])
label.delete(lbl[200])  
barcolor(rsi > linReg + deviation and ta.pivothigh(rsi, length, 0) and rsi >= 65 and shbg ? color.white : rsi < linReg - deviation and ta.pivotlow(rsi, length, 1) and rsi <= 35 and shbg ? color.yellow : na)
bgcolor(rsi > linReg + deviation and ta.pivothigh(rsi, length, 0) and rsi >= 65 and shbg ? color.new(color.red,50) : rsi < linReg - deviation and ta.pivotlow(rsi, length, 1) and rsi <= 35 and shbg ? color.new(color.green, 50) : na)


////WEDGE
wedgegroup = "**********WEDGE**********"
//Inputs
lengthdiv = input.int(21, 'Leftbars Length',minval=0, group=wedgegroup)
lengthright = input.int(0, 'Rightbars Length', tooltip = "0 for Instant",minval=0, group=wedgegroup)
extendOptionUp = input.string("none", title="Upper Line Extension", 
     options=["none", "left", "right", "both"],group=wedgegroup)
extendOptionLow = input.string("none", title="Lower Line Extension", 
     options=["none", "left", "right", "both"])

astart = input.int(1, 'Draw Upper Line From Pivot',minval=1, inline='pivot')
aend = input.int(0, 'To Pivot #',minval=0, inline='pivot')
bstart = input.int(1, 'Draw Lower Line From Pivot',minval=1, inline='pivot2')
bend = input.int(0, 'To Pivot #', minval=0,inline='pivot2')
stylewg = input.string('Dot', 'Line Style:', options=['Dot', 'Dash', 'Solid'], inline='custom2')
colorwg = input.string('Red/Green', 'Line Color:', options=['Red/Green', 'White', 'Yellow', 'Blue'], inline='custom2')
lwidth = input.int(2,'Line Width:', minval=0,maxval=4, inline='custom2')

lengthwg = lengthright
length2wg = lengthright


//Conditions
upwg = ta.pivothigh(rsi, lengthdiv,  lengthright)
dnwg = ta.pivotlow(rsi, lengthdiv,  lengthright)
upchart = ta.pivothigh(close, lengthdiv, lengthright)
dnchart = ta.pivotlow(close, lengthdiv, lengthright)

nw = bar_index
a1 = ta.valuewhen(not na(upwg), nw, astart)
b1 = ta.valuewhen(not na(dnwg), nw, bstart)
a2 = ta.valuewhen(not na(upwg), nw, aend)
b2 = ta.valuewhen(not na(dnwg), nw, bend)

ach1 = ta.valuewhen(not na(upchart), nw, astart)
bch1 = ta.valuewhen(not na(dnchart), nw, bstart)
ach2 = ta.valuewhen(not na(upchart), nw, aend)
bch2 = ta.valuewhen(not na(dnchart), nw, bend)

//Styles
style = line.style_dotted
if stylewg == 'Dash'
    style := line.style_dashed
if stylewg == 'Solid'
    style := line.style_solid
if stylewg == 'Dot'
    style := line.style_dotted

//Colors
color1 = color.red
color2 = color.green
if colorwg == 'White'
    color1 := color.white
    color2 := color.white
    color2
if colorwg == 'Yellow'
    color1 := color.yellow
    color2 := color.yellow
    color2
if colorwg == 'Blue'
    color1 := color.blue
    color2 := color.blue
    color2
if colorwg == 'Red/Green'
    color1 := color.red
    color2 := color.green
    color2

//Lines
lineExtendUp = extendOptionUp == "left" ? extend.left :
     extendOptionUp == "right" ? extend.right :
     extendOptionUp == "both" ? extend.both :
         extend.none
lineExtendLow = extendOptionLow == "left" ? extend.left :
     extendOptionLow == "right" ? extend.right :
     extendOptionLow == "both" ? extend.both :
         extend.none

//DIVERGENCE
div1 = upwg[nw - a2] < upwg[nw - a1] and upchart[nw - ach2] > upchart[nw - ach1] and upchart > high[nw - ach1]
div2 = dnwg[nw - b2] > dnwg[nw - b1] and dnchart[nw - bch2] < dnchart[nw - bch1] and dnchart < low[nw - bch1]

if div1 and shdiv and i_detectShorts
    line.new(nw[nw - a1 + lengthwg], upwg[nw - a1], nw[nw - a2 + lengthwg], upwg[nw - a2], extend=lineExtendUp,color=color1, width=lwidth,style=style)
    label1 = label.new(nw[nw - a2 + lengthwg], 70 , text="Divergence\n|", style=label.style_label_down, color=color.new(color.red,100))
    label.set_size(label1, size.small)
    label.set_textcolor(label1, color.red) 
if div2 and shdiv and i_detectLongs
    line.new(nw[nw - b1 + length2wg], dnwg[nw - b1], nw[nw - b2 + length2wg], dnwg[nw - b2], extend=lineExtendLow, color=color2, width=lwidth, style=style)
    label2 = label.new(nw[nw - b2 + length2wg], 30, text="|\nDivergence", style=label.style_label_up, color=color.new(color.green,100))
    label.set_size(label2, size.small)
    label.set_textcolor(label2, color.green) 
if div1 and shdate and i_detectShorts
    label1 = label.new(nw[nw - a2 + lengthwg], 80 , text = str.format("{0,date, y\nMMM-d\n}", time[nw - a2 + lengthwg]), style=label.style_label_down, color=color.new(color.red,100))
    label.set_size(label1, size.small)
    label.set_textcolor(label1, color.red)
if div2 and shdate and i_detectLongs
    label2 = label.new(nw[nw - b2 + length2wg], 20 , text = str.format("{0,date, \ny\nMMM-d}", time[nw - b2 + length2wg]), style=label.style_label_up, color=color.new(color.green,100))
    label.set_size(label2, size.small)
    label.set_textcolor(label2, color.green)

////ALERT
// Trigger an alert on crosses.
enterShort = i_detectShorts and div1
enterLong  = i_detectLongs  and div2
enterPos = i_detectLongs and i_detectShorts and (div1 or div2)
//Conditions
alertcondition(enterLong, "Divergence Alert (Long)", "Go Long ▲\n\nTicker: {{ticker}}\nTime: {{time}}\nPrice: {{close}}")
alertcondition(enterShort, "Divergence Alert (Short)", "Go Short ▼\n\nTicker: {{ticker}}\nTime: {{time}}\nPrice: {{close}}")
alertcondition(enterPos, "Divergences Alert", "Ticker: {{ticker}}\nTime: {{time}}\nPrice: {{close}}")