Zigzag Matrix
// 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("Zigzag Matrix", overlay=true, max_lines_count=500, max_labels_count=500, max_boxes_count=500)

import HeWhoMustNotBeNamed/mZigzag/3 as zigzag
import HeWhoMustNotBeNamed/enhanced_ta/14 as eta

length = input.int(8, 'Length', group='Zigzag', tooltip='Zigzag Length')
supertrendLength = input.int(5, 'Zupertrend Length', group='Zigzag', tooltip='Number of past pivots to calculate zigzag supertrend')
showIndicators = input.bool(false, 'Show Indicators', group='Zigzag', inline='b')
showSupertrend = input.bool(false, 'Show Supertrend', group='Zigzag', inline='b')
useClosePrices = input.bool(false, 'Use Close Prices', group='Zigzag', inline='b', tooltip='Show Indicators - Display Indicator values along with pivots.\n\n'+
                                             'Show Supertrend - Show supertrend derived from zigzag. This trend is also used for calculating divergence.\n\n'+
                                             'Use Close Prices - Use close prices for calculating pivots instead of high/low. This also means indicator values are calculated on close instead of high/low')

includeMa = input.bool(true, 'Moving Average', group="Indicators", inline='ma')
matype = input.string("sma", title="", group="Indicators", options=["sma", "ema", "hma", "rma", "wma", "vwma", "swma", "median"], inline="ma")
malength = input.int(60, title="", group="Indicators", inline="ma", tooltip='Moving Average Type and Length')

includeOscillator = input.bool(true, "Oscillator      ", inline="osc", group="Indicators")
oscillatorType = input.string("rsi", title="", group="Indicators", inline="osc", options=["cci", "cmo", "cog", "mfi", "roc", "rsi"])
oscLength = input.int(14, title="", group="Indicators", inline="osc", tooltip='Oscillator Type and Length')

includeVolume = input.bool(true, "Volume      ", inline="vol", group="Indicators")
volType = input.string("obv", title="", group="Indicators", inline="vol", options=["obv", "pvi", "nvi", "pvt", "vwap"], tooltip='Volume Indicator Type')

showStats = input.bool(true, 'Show Stats Table', group='Stats', inline='s')
tblPosition = input.string(position.top_right, '', group='Stats', options=[position.top_right, position.bottom_right, position.top_left, position.bottom_left], inline='s')
textSize = input.string(size.small, '', group='Stats', options=[size.tiny, size.small, size.normal, size.large, size.huge], inline='s', tooltip='Option to display pivot details in stats table')


indicatorHigh = array.new_float()
indicatorLow = array.new_float()
indicatorLabels = array.new_string()

getSentimentDetails(sentiment) =>
    sentimentSymbol = sentiment == 4 ? '⬆' : 
                         sentiment == -4 ? '⬇' : 
                         sentiment == 3 ? '↗' : 
                         sentiment == -3 ? '↘' : 
                         sentiment == 2 ? '⤴' : 
                         sentiment == -2 ? '⤵' : 
                         sentiment == 1 ? '⤒' :
                         sentiment == -1 ? '⤓' : '▣'
    sentimentColor = sentiment == 4 ? color.green :
                         sentiment == -4 ? color.red : 
                         sentiment == 3 ? color.lime : 
                         sentiment == -3 ? color.orange : 
                         sentiment == 2 ? color.rgb(202, 224, 13, 0) : 
                         sentiment == -2 ? color.rgb(250, 128, 114, 0) :
                         sentiment == 1? color.lime:
                         sentiment == -1? color.orange: color.silver
    sentimentLabel = math.abs(sentiment) == 4 ? 'C' : math.abs(sentiment) == 3 ? 'H' : math.abs(sentiment) == 2 ? 'D' : 'I'
    [sentimentSymbol, sentimentLabel, sentimentColor]
    

getDirectionDetails(directionMatrix, i, j)=>
    value = matrix.get(directionMatrix,i,j)
    direction = value == 2? "⇈" :
                 value == 1? "↑" :
                 value == -1? "↓" :
                 value == -2? "⇊" : "⬍"
    directionColor = value == 2? color.green:
                         value == 1? color.orange:
                         value == -1? color.lime:
                         value == -2? color.red: color.silver
    [direction, directionColor]

highSource = useClosePrices? close : high
lowSource = useClosePrices? close : low

if(includeMa)
    maHigh = eta.ma(highSource, matype, malength)
    maLow = eta.ma(lowSource, matype, malength)
    array.push(indicatorHigh, maHigh)
    array.push(indicatorLow, maLow)
    array.push(indicatorLabels, str.upper(matype+str.tostring(malength)))

if(includeOscillator)
    [oscHigh, _, _] = eta.oscillator(oscillatorType, oscLength, oscLength, oscLength, highSource)
    [oscLow, _, _] = eta.oscillator(oscillatorType, oscLength, oscLength, oscLength, lowSource)
    array.push(indicatorHigh, oscHigh)
    array.push(indicatorLow, oscLow)
    array.push(indicatorLabels, str.upper(oscillatorType+str.tostring(oscLength)))

if(includeVolume)
    volHigh = volType == "obv"? ta.obv:
             volType == "pvi"? ta.pvi:
             volType == "nvi"? ta.nvi:
             volType == "pvt"? ta.pvt: ta.vwap(highSource)
    volLow = volType == "obv"? ta.obv:
             volType == "pvi"? ta.pvi:
             volType == "nvi"? ta.nvi:
             volType == "pvt"? ta.pvt: ta.vwap(lowSource)
    array.push(indicatorHigh, volHigh)
    array.push(indicatorLow, volLow)
    array.push(indicatorLabels, str.upper(volType))
    
    
ohlc = array.from(highSource, lowSource)

[valueMatrix, directionMatrix, ratioMatrix, divergenceMatrix, doubleDivergenceMatrix, barArray, supertrendDir, supertrend, newZG, doubleZG] = 
                                                         zigzag.calculate(length, ohlc, indicatorHigh, indicatorLow, supertrendLength = supertrendLength)
zigzag.draw(valueMatrix, directionMatrix, ratioMatrix,  divergenceMatrix, doubleDivergenceMatrix, barArray, newZG, doubleZG, indicatorLabels, showIndicators=showIndicators)

var lastD = 0
lastD := array.size(barArray)>=2? array.get(barArray, 1) : lastD

plot(showSupertrend? supertrend:na, color=supertrendDir>0? color.green:color.red, style=plot.style_linebr)

if(lastD > lastD[1])
    alert('New pivot Alert')

if(barstate.islast and showStats)
    numberOfSubColumns = 4
    stats = table.new(tblPosition, (matrix.columns(valueMatrix)+1)*numberOfSubColumns, matrix.rows(valueMatrix)+2, border_color=color.black, border_width=2)
    
    rows = matrix.rows(valueMatrix)
    columns = matrix.columns(valueMatrix)
    headers = array.from('DISTANCE', 'PRICE')
    array.concat(headers, indicatorLabels)
    
    for [index, lbl] in headers
        table.cell(stats, index*numberOfSubColumns, 0, lbl, text_color=color.white, text_size=textSize, bgcolor=color.maroon)
        if(index!=0)
            table.merge_cells(stats, index*numberOfSubColumns, 0, (index+1)*numberOfSubColumns-(index==1? 2 : 1), 0)
    for i=0 to rows-1
        table.cell(stats, 0, rows-i, str.tostring(bar_index-array.get(barArray, i)), text_color=color.black, text_size=textSize, bgcolor=color.aqua)
        for j=0 to columns-1
            [direction, directionColor] = getDirectionDetails(directionMatrix, i, j)
            table.cell(stats, numberOfSubColumns+(j*numberOfSubColumns), rows-i, str.tostring(math.round_to_mintick(matrix.get(valueMatrix, i, j))), text_color=color.black, text_size=textSize, bgcolor=color.new(directionColor, 20), tooltip='Value')
            table.cell(stats, numberOfSubColumns+(j*numberOfSubColumns)+1, rows-i, direction, text_color=color.black, text_size=textSize, bgcolor=color.new(directionColor, 20), tooltip='Direction')
            if(j==0)
                table.cell(stats, numberOfSubColumns+(j*numberOfSubColumns)+2, rows-i, str.tostring(math.round(matrix.get(ratioMatrix, i, j), 3)), text_color=color.black, text_size=textSize, bgcolor=color.new(directionColor, 20), tooltip='Retracement')
            else
                divergence = matrix.get(divergenceMatrix, i, j)
                doubleDivergence = matrix.get(doubleDivergenceMatrix, i, j)
                [dSymbol, dLabel, dColor] = getSentimentDetails(divergence)
                [ddSymbol, ddLabel, ddColor] = getSentimentDetails(doubleDivergence)
                table.cell(stats, numberOfSubColumns+(j*numberOfSubColumns)+2, rows-i, dSymbol, text_color=color.black, text_size=textSize, bgcolor=color.new(dColor, 20), tooltip='Divergence')
                table.cell(stats, numberOfSubColumns+(j*numberOfSubColumns)+3, rows-i, ddSymbol, text_color=color.black, text_size=textSize, bgcolor=color.new(ddColor, 20), tooltip='Double Divergence')