Divergence-Support/Resistance - Widget [HeWhoMustNotBeNamed]
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © HeWhoMustNotBeNamed

//   __    __            __       __  __                  __       __                        __      __    __              __      _______             __    __                                          __ 
//  /  |  /  |          /  |  _  /  |/  |                /  \     /  |                      /  |    /  \  /  |            /  |    /       \           /  \  /  |                                        /  |
//  $$ |  $$ |  ______  $$ | / \ $$ |$$ |____    ______  $$  \   /$$ | __    __   _______  _$$ |_   $$  \ $$ |  ______   _$$ |_   $$$$$$$  |  ______  $$  \ $$ |  ______   _____  ____    ______    ____$$ |
//  $$ |__$$ | /      \ $$ |/$  \$$ |$$      \  /      \ $$$  \ /$$$ |/  |  /  | /       |/ $$   |  $$$  \$$ | /      \ / $$   |  $$ |__$$ | /      \ $$$  \$$ | /      \ /     \/    \  /      \  /    $$ |
//  $$    $$ |/$$$$$$  |$$ /$$$  $$ |$$$$$$$  |/$$$$$$  |$$$$  /$$$$ |$$ |  $$ |/$$$$$$$/ $$$$$$/   $$$$  $$ |/$$$$$$  |$$$$$$/   $$    $$< /$$$$$$  |$$$$  $$ | $$$$$$  |$$$$$$ $$$$  |/$$$$$$  |/$$$$$$$ |
//  $$$$$$$$ |$$    $$ |$$ $$/$$ $$ |$$ |  $$ |$$ |  $$ |$$ $$ $$/$$ |$$ |  $$ |$$      \   $$ | __ $$ $$ $$ |$$ |  $$ |  $$ | __ $$$$$$$  |$$    $$ |$$ $$ $$ | /    $$ |$$ | $$ | $$ |$$    $$ |$$ |  $$ |
//  $$ |  $$ |$$$$$$$$/ $$$$/  $$$$ |$$ |  $$ |$$ \__$$ |$$ |$$$/ $$ |$$ \__$$ | $$$$$$  |  $$ |/  |$$ |$$$$ |$$ \__$$ |  $$ |/  |$$ |__$$ |$$$$$$$$/ $$ |$$$$ |/$$$$$$$ |$$ | $$ | $$ |$$$$$$$$/ $$ \__$$ |
//  $$ |  $$ |$$       |$$$/    $$$ |$$ |  $$ |$$    $$/ $$ | $/  $$ |$$    $$/ /     $$/   $$  $$/ $$ | $$$ |$$    $$/   $$  $$/ $$    $$/ $$       |$$ | $$$ |$$    $$ |$$ | $$ | $$ |$$       |$$    $$ |
//  $$/   $$/  $$$$$$$/ $$/      $$/ $$/   $$/  $$$$$$/  $$/      $$/  $$$$$$/  $$$$$$$/     $$$$/  $$/   $$/  $$$$$$/     $$$$/  $$$$$$$/   $$$$$$$/ $$/   $$/  $$$$$$$/ $$/  $$/  $$/  $$$$$$$/  $$$$$$$/ 
//                                                                                                                                                                                                          
//                                                                                                                                                                                                          
//
//@version=5
indicator("Divergence-Support/Resistance - Widget [HeWhoMustNotBeNamed]", shorttitle="DSR - Widget [HeWhoMustNotBeNamed]", overlay=true, max_lines_count=500, max_labels_count=500, max_bars_back=500)
import HeWhoMustNotBeNamed/zigzag/6 as zg
import HeWhoMustNotBeNamed/enhanced_ta/10 as eta
import HeWhoMustNotBeNamed/supertrend/4 as st
import HeWhoMustNotBeNamed/customcandles/2 as ca

ohlcSource = input.string("regular", "OHLC Source", options=["regular", "ha", "ma"], group="Candles")
matype = input.string("hma", title="Moving Average", group="Candles", options=["sma", "ema", "hma", "rma", "wma", "vwma", "swma", "linreg", "median"], inline="m")
malength = input.int(10, step=5, title="", group="Candles", inline="m")

highSource = high
lowSource = low
closeSource = close
openSource = open
if(ohlcSource == "ma")
    [maOpen, maHigh, maLow, maClose] = ca.macandles(matype, malength)
    openSource := maOpen
    highSource := maHigh
    lowSource := maLow
    closeSource := maClose
    
if(ohlcSource == "ha")
    [haOpen, haHigh, haLow, haClose] = ca.hacandles()
    openSource := haOpen
    highSource := math.max(haOpen, haClose)
    lowSource := math.min(haOpen, haClose)
    closeSource := haClose
    
showZigzag1 = input.bool(true, "", group="Zigzag", inline="z1")
zigzag1Length = input.int(5, "", group="Zigzag", inline="z1")
lineStyle1 = input.string(line.style_solid, "", options = [line.style_dotted, line.style_dashed, line.style_solid], group="Zigzag", inline="z1")

showZigzag2 = input.bool(true, "", group="Zigzag", inline="z2")
zigzag2Length = input.int(8, "", group="Zigzag", inline="z2")
lineStyle2 = input.string(line.style_dashed, "", options = [line.style_dotted, line.style_dashed, line.style_solid], group="Zigzag", inline="z2")

showZigzag3 = input.bool(true, "", group="Zigzag", inline="z3")
zigzag3Length = input.int(13, "", group="Zigzag", inline="z3")
lineStyle3 = input.string(line.style_dotted, "", options = [line.style_dotted, line.style_dashed, line.style_solid], group="Zigzag", inline="z3")

showZigzag4 = input.bool(true, "", group="Zigzag", inline="z4")
zigzag4Length = input.int(21, "", group="Zigzag", inline="z4")
lineStyle4 = input.string(line.style_dotted, "", options = [line.style_dotted, line.style_dashed, line.style_solid], group="Zigzag", inline="z4")

var useAlternativeSource = true
source = close

var srArray1 = array.new_float()
var lnArray1 = array.new_line()
var lblArray1 = array.new_label()

var srArray2 = array.new_float()
var lnArray2 = array.new_line()
var lblArray2 = array.new_label()

var srArray3 = array.new_float()
var lnArray3 = array.new_line()
var lblArray3 = array.new_label()

var srArray4 = array.new_float()
var lnArray4 = array.new_line()
var lblArray4 = array.new_label()

oscillatorType = input.string('rsi', title='Oscillator Source', options=["cci", "cmo", "cog", "mfi", "roc", "rsi", "stoch", "tsi", "wpr"], group='Oscillator', inline='osc')
useExternalSource = input.bool(false, title='External Source', group='Oscillator', inline="osce")
externalSource = input.source(close, title='', group='Oscillator', inline="osce")

maxItems = input.int(10, step=5, title="Max S/R Per Zigzag", group="Misc")
maxSupportResistanceForLive = input.int(3, "Max S/R for Stats", group="Misc")
maxSupportResistanceForAlerts = input.int(3, "Max S/R for Alerts", group="Misc")

showNewSRAlert = input.bool(true, "New S/R Alert", group="Misc", inline="s")
showSRBreakAlert = input.bool(true, "S/R Break Alert", group="Misc", inline="s")

var history = 1
var waitForClose = true
var atrMaType = "rma"
var atrMultiplier = 1

add_to_array(arr, val, maxItems)=>
    array.push(arr, val)
    if(array.size(arr) > maxItems)
        array.remove(arr,0)

add_to_line_array(arr, val, maxItems)=>
    array.push(arr, val)
    if(array.size(arr) > maxItems)
        line.delete(array.remove(arr,0))

add_to_label_array(arr, val, maxItems)=>
    array.push(arr, val)
    if(array.size(arr) > maxItems)
        label.delete(array.remove(arr,0))
        
getSentimentDetails(sentiment) =>
    [sentimentSymbol, sentimentColor, type] = switch sentiment
        4 => ['⬆', color.green, "Continuation"]
        -4 => ['⬇', color.red, "Continuation"]
        3 => ['↗', color.lime, "Hidden Divergence"]
        -3 => ['↘', color.orange, "Hidden Divergence"]
        2 => ['⤴',color.rgb(202, 224, 13, 0), "Divergence"]
        -2 => ['⤵',color.rgb(250, 128, 114, 0), "Divergence"] 
        => ['▣', color.silver, "Indecision"]
    [sentimentSymbol, sentimentColor, type]

f_get_arrays(zigzagLength)=>
    switch zigzagLength
        zigzag1Length => [srArray1, lnArray1, lblArray1]
        zigzag2Length => [srArray2, lnArray2, lblArray2]
        zigzag3Length => [srArray3, lnArray3, lblArray3]
        zigzag4Length => [srArray4, lnArray4, lblArray4]
        
f_get_zigzag_properties(zigzagLength)=>
    switch zigzagLength
        zigzag1Length => [showZigzag1, lineStyle1, 1]
        zigzag2Length => [showZigzag2, lineStyle2, 1]
        zigzag3Length => [showZigzag3, lineStyle3, 1]
        zigzag4Length => [showZigzag4, lineStyle4, 1]
        
f_get_support_resistance_break_status(srArray, brokenSRArray, nextSupportArray, nextResistanceArray)=>
    count = 0
    for i = 0 to array.size(srArray) > 0? array.size(srArray)-1 : na
        sr = array.get(srArray, i)
        currentSR = (sr < close[2] and sr > close[1]? -1 : sr > close[2] and sr < close[1] ? 1 : 0)
        if(currentSR!=0)
            count += currentSR
            array.unshift(brokenSRArray, sr)
        if(close[1] > sr)
            array.unshift(nextSupportArray, sr)
        if(close[1] < sr)
            array.unshift(nextResistanceArray, sr)
    count

get_alert_stats(brokenSRArray, nextSupportArray, nextResistanceArray)=>
    count1 = f_get_support_resistance_break_status(srArray1, brokenSRArray, nextSupportArray, nextResistanceArray)
    count2 = f_get_support_resistance_break_status(srArray2, brokenSRArray, nextSupportArray, nextResistanceArray)
    count3 = f_get_support_resistance_break_status(srArray3, brokenSRArray, nextSupportArray, nextResistanceArray)
    count4 = f_get_support_resistance_break_status(srArray4, brokenSRArray, nextSupportArray, nextResistanceArray)
    array.sort(nextSupportArray, order.descending)
    array.sort(nextResistanceArray, order.ascending)
    totalCount = count1 + count2 + count3 + count4
    totalCount

f_get_live_support_resistance(srArray, liveSupportArray, liveResistanceArray)=>
    for i = 0 to array.size(srArray) > 0? array.size(srArray)-1 : na
        sr = array.get(srArray, i)
        if(close < sr)
            array.unshift(liveResistanceArray, sr)
        if(close > sr)
            array.unshift(liveSupportArray, sr)
            
f_get_complete_support_resistance_heirarchy(maxSupportResistanceForLive)=>
    liveSupportArray = array.new_float()
    liveResistanceArray = array.new_float()
    f_get_live_support_resistance(srArray1, liveSupportArray, liveResistanceArray)
    f_get_live_support_resistance(srArray2, liveSupportArray, liveResistanceArray)
    f_get_live_support_resistance(srArray3, liveSupportArray, liveResistanceArray)
    f_get_live_support_resistance(srArray4, liveSupportArray, liveResistanceArray)
    array.sort(liveSupportArray, order.descending)
    array.sort(liveResistanceArray, order.ascending)
    supportLevels = array.size(liveSupportArray)
    resistanceLevels = array.size(liveResistanceArray)
    shortSupportArray = supportLevels > maxSupportResistanceForLive? array.slice(liveSupportArray, 0, maxSupportResistanceForLive):liveSupportArray
    shortResistanceArray = resistanceLevels > maxSupportResistanceForLive? array.slice(liveResistanceArray, 0, maxSupportResistanceForLive):liveResistanceArray
    [supportLevels, resistanceLevels, shortSupportArray, shortResistanceArray]
    
f_get_price_and_divergence(zigzagLength)=>
    [srArray, lnArray, lblArray] = f_get_arrays(zigzagLength)
    [showZigzag, lineStyle, lineWidth] = f_get_zigzag_properties(zigzagLength)
    if(showZigzag)
        length = zigzagLength * 3
        longLength = zigzagLength*5
        atrLength = zigzagLength*5
        [oscillator, overbought, oversold] = eta.oscillator(oscillatorType, length, length, longLength)
        [oscillatorHigh, overboughtHigh, oversoldHigh] = eta.oscillator(oscillatorType, length, length, longLength, highSource)
        [oscillatorLow, overboughtLow, oversoldLow] = eta.oscillator(oscillatorType, length, length, longLength, lowSource)
        var dir_zigzag = 1
        [zigzagpivots, zigzagpivotbars, zigzagpivotdirs, zigzagpivotratios, zigzagoscillators,
                                             zigzagoscillatordirs, zigzagtrendbias, zigzagdivergence, 
                                             newPivot, doublePivot] = 
                                             zg.czigzag(zigzagLength, 20, highSource, lowSource, oscillatorHigh, oscillatorLow, dir_zigzag)
        [tmp_dir_zigzag, supertrend] = st.zsupertrend(zigzagpivots, history = history, source = closeSource, highSource = highSource, lowSource = lowSource, 
                     waitForClose = waitForClose, atrMaType = atrMaType, atrlength = atrLength, multiplier = atrMultiplier)
        dir_zigzag := tmp_dir_zigzag
        size = array.size(zigzagpivots)
        if(size>1)
            index = 1
            price = array.get(zigzagpivots, index)
            divergence = array.get(zigzagdivergence, index)
            bar = array.get(zigzagpivotbars, index)
            
            if(math.abs(divergence) == 2 or math.abs(divergence) == 3)
                if(array.lastindexof(srArray, price) == -1)
                    add_to_array(srArray, price, maxItems)
                    [sentimentSymbol, sentimentColor, type] = getSentimentDetails(divergence)
                    if showNewSRAlert
                        alertMessage = '{'+
                                         '\n\t"alert" : "new support resistance",'+
                                         '\n\t"type" : "'+type+'",'+
                                         '\n\t"bias" : "'+(divergence >0 ? "Bullish" : "Bearish")+'",'+
                                         '\n\t"zigzag" : '+str.tostring(zigzagLength)+','+
                                         '\n\t"price" : '+str.tostring(price)+
                                         '\n}'
                        alert(alertMessage)


f_get_price_and_divergence(zigzag1Length)
f_get_price_and_divergence(zigzag2Length)
f_get_price_and_divergence(zigzag3Length)
f_get_price_and_divergence(zigzag4Length)


if(showSRBreakAlert)
    brokenSRArray = array.new_float()
    nextSupportArray = array.new_float()
    nextResistanceArray = array.new_float()
    
    totalCount = get_alert_stats(brokenSRArray, nextSupportArray, nextResistanceArray)
    if(totalCount!=0)
        alertType = totalCount > 0? "Broken Resistance" : totalCount < 0? "Broken Support" : " Invalid"
    
        shortenedSupportArray = array.size(nextSupportArray)<=maxSupportResistanceForAlerts? nextSupportArray : array.slice(nextSupportArray, 0, maxSupportResistanceForAlerts)
        shortenedResistanceArray = array.size(nextResistanceArray)<=maxSupportResistanceForAlerts? nextResistanceArray : array.slice(nextResistanceArray, 0, maxSupportResistanceForAlerts)
        
        alertMessage = '{'+
                         '\n\t"alert" : "'+alertType+'",'+
                         '\n\t"broken_levels" : ['+array.join(brokenSRArray,',')+']'+
                         '\n\t"next_support_levels" : ['+array.join(shortenedSupportArray,',')+']'+
                         '\n\t"next_resistance_levels" : ['+array.join(shortenedResistanceArray,',')+']'+
                         '\n\t"support_count" : ' +str.tostring(array.size(nextSupportArray))+
                         '\n\t"resistance_count" : '+str.tostring(array.size(nextResistanceArray))+
                         '\n}'
        alert(alertMessage)

[supportLevels, resistanceLevels, shortSupportArray, shortResistanceArray] = f_get_complete_support_resistance_heirarchy(maxSupportResistanceForLive)

stats = table.new(position=position.top_right, columns=5, rows=maxSupportResistanceForLive + 1, border_width=1)
if(array.size(shortResistanceArray)>0)
    table.cell(table_id=stats, column=1, row=0, text="Resistance ("+str.tostring(resistanceLevels)+")", bgcolor=color.teal, text_color=color.white, text_size=size.small)
    for i=0 to array.size(shortResistanceArray)-1
        table.cell(table_id=stats, column=0, row=i+1, text=str.tostring(math.round_to_mintick(array.get(shortResistanceArray, i))-math.round_to_mintick(close)), bgcolor=color.blue, text_color=color.black, text_size=size.tiny)
        table.cell(table_id=stats, column=1, row=i+1, text=str.tostring(math.round_to_mintick(array.get(shortResistanceArray, i))), bgcolor=color.orange, text_color=color.black, text_size=size.small)

if(array.size(shortSupportArray)>0)
    table.cell(table_id=stats, column=2, row=0, text="Support ("+str.tostring(supportLevels)+")", bgcolor=color.teal, text_color=color.white, text_size=size.small)
    for i=0 to array.size(shortSupportArray)-1
        table.cell(table_id=stats, column=2, row=i+1, text=str.tostring(math.round_to_mintick(array.get(shortSupportArray, i))), bgcolor=color.lime, text_color=color.black, text_size=size.small)
        table.cell(table_id=stats, column=3, row=i+1, text=str.tostring(math.round_to_mintick(close)-math.round_to_mintick(array.get(shortSupportArray, i))), bgcolor=color.blue, text_color=color.black, text_size=size.tiny)
