Divergence and Pivot - Detector For Any Indicator
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © HALDRO

//@version=5
indicator('Divergence and Pivot - Detecter For Any Indicator', shorttitle = 'Detector')
//------------------------------------------------------------------------------
// Inputs
groupdiv       = 'Divergence Detect'
grouppiv       = 'Pivot Detect'
groupvis       = 'Visual'
src            = input     (close,'                                   Oscillator Source')
pivothl        = input.bool(true, 'Pivot Show ?'                      , group = grouppiv, inline = ' ')
pivR           = input.int (1,    'Pivot Lookback - Left / Right     ', group = grouppiv, inline = '2')
pivL           = input.int (21,   ' '                                 , group = grouppiv, inline = '2')
lbR            = input.int (1,    'Divergence Lookback - Left / Right', group = groupdiv, inline = '1')
lbL            = input.int (16,   ' '                                 , group = groupdiv, inline = '1')
rangeUpper     = input.int (100,  'Lookback Range - MAX / MIN        ', group = groupdiv, inline = '0')
rangeLower     = input.int (6,    ' '                                 , group = groupdiv, inline = '0')

plotBull       = input.bool(true, 'Bullish'                           , group = groupdiv, inline = 'd')
plotBear       = input.bool(true, 'Bearish'                           , group = groupdiv, inline = 'd')
plotHiddenBull = input.bool(true, 'Hidden Bullish'                    , group = groupdiv, inline = 'd')
plotHiddenBear = input.bool(true, 'Hidden Bearish'                    , group = groupdiv, inline = 'd')
plot(src == close ? na : src,     'Oscillator Source')

// Visual
bullColor = input.color(#00ff6d,'Bull'                            , group = groupvis, inline = 'c')
bearColor = input.color(#ff0056,'Bear'                            , group = groupvis, inline = 'c')
char1     = input('•', 'Char Div',   group = groupvis, inline = 'c')
char2     = input('▪', 'Char Pivot', group = groupvis, inline = 'c')

// Pivot Detect
PHCond = ta.pivothigh(src, pivL, pivR), h = +1.1
PLCond = ta.pivotlow (src, pivL, pivR), l = -0.9

// Check Panel
var table panel = table.new(position = position.middle_center, columns = 2, rows = 1, bgcolor = #363A45, border_width = 1)
if barstate.islast 
    table.cell(panel, 1, 0, text=src == close ? str.tostring('Choose a source!') : na, text_color = #ffa726, bgcolor = #363A45)
if src != close
    table.delete(panel)

// Plot Pivot
plotchar(pivothl and src != close ? PHCond * +h : na, title='Pivot High', char=char2, size=size.tiny, location=location.absolute, color=bearColor)
plotchar(pivothl and src != close ? PLCond * -l : na, title='Pivot Low' , char=char2, size=size.tiny, location=location.absolute, color=bullColor)


// Divergence Detect
osc    = src 
//------------------------------------------------------------------------------
plFound = na(ta.pivotlow(src, lbL, lbR)) ? false : true
phFound = na(ta.pivothigh(src, lbL, lbR)) ? false : true
_inRange(cond) =>
    bars = ta.barssince(cond == true)
    rangeLower <= bars and bars <= rangeUpper

//------------------------------------------------------------------------------
// Regular Bullish
// Osc: Higher Low
oscHL = osc[lbR] > ta.valuewhen(plFound, osc[lbR], 1) and _inRange(plFound[1]) and src != close
// Price: Lower Low
priceLL = low[lbR] < ta.valuewhen(plFound, low[lbR], 1)
bullCond = plotBull and priceLL and oscHL and plFound
plot(plFound ? osc[lbR] : na, offset=-lbR, title='Regular Bullish', linewidth=1, color=bullCond ? #089981 : na)
plotchar(bullCond ? osc[lbR] : na, offset=-lbR, title='Regular Bullish Label', char=char1, size=size.tiny, location=location.absolute, color=bullColor)

//------------------------------------------------------------------------------
// Hidden Bullish
// Osc: Lower Low
oscLL = osc[lbR] < ta.valuewhen(plFound, osc[lbR], 1) and _inRange(plFound[1]) and src != close
// Price: Higher Low
priceHL = low[lbR] > ta.valuewhen(plFound, low[lbR], 1)
hiddenBullCond = plotHiddenBull and priceHL and oscLL and plFound
plot(plFound ? osc[lbR] : na, offset=-lbR, title='Hidden Bullish', linewidth=1, color=hiddenBullCond ? #089981 : na)
plotchar(hiddenBullCond ? osc[lbR] : na, offset=-lbR, title='Hidden Bullish Label', char=char1, size=size.tiny, location=location.absolute, color=bullColor)

//------------------------------------------------------------------------------
// Regular Bearish
// Osc: Lower High
oscLH = osc[lbR] < ta.valuewhen(phFound, osc[lbR], 1) and _inRange(phFound[1]) and src != close
// Price: Higher High
priceHH = high[lbR] > ta.valuewhen(phFound, high[lbR], 1)
bearCond = plotBear and priceHH and oscLH and phFound
plot(phFound ? osc[lbR] : na, offset=-lbR, title='Regular Bearish', linewidth=1, color=bearCond ? #673ab7 : na)
plotchar(bearCond ? osc[lbR] : na, offset=-lbR, title='Regular Bearish Label', char=char1, size=size.tiny, location=location.absolute, color=bearColor)

//------------------------------------------------------------------------------
// Hidden Bearish
// Osc: Higher High
oscHH = osc[lbR] > ta.valuewhen(phFound, osc[lbR], 1) and _inRange(phFound[1]) and src != close
// Price: Lower High
priceLH = high[lbR] < ta.valuewhen(phFound, high[lbR], 1)
hiddenBearCond = plotHiddenBear and priceLH and oscHH and phFound
plot(phFound ? osc[lbR] : na, offset=-lbR, title='Hidden Bearish', linewidth=1, color=hiddenBearCond ? #673ab7 : na)
plotchar(hiddenBearCond ? osc[lbR] : na, offset=-lbR, title='Hidden Bearish Label', char=char1, size=size.tiny, location=location.absolute, color=bearColor)
//------------------------------------------------------------------------------

alertcondition (bullCond,       title = '~Alert Bull Divergence',        message = 'Bull Diver')
alertcondition (bearCond,       title = '~Alert Bear Divergence',        message = 'Bear Diver')
alertcondition (hiddenBullCond, title = '~Alert Hidden Bull Divergence', message = 'Hidden Bull Diver')
alertcondition (hiddenBearCond, title = '~Alert Hidden Bear Divergence', message = 'Hidden Bear Diver')
alertcondition (PLCond,         title = '~Alert Bull Pivot',             message = 'Bull Pivot')
alertcondition (PHCond,         title = '~Alert Bear Pivot',             message = 'Bear Pivot')