MTF - Zigzag + Tech Indicators
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Sharad_Gaikwad

//#region General functions
var tab = table.new(position=position.top_right, columns=7, rows=30,frame_color = color.yellow, frame_width = 1)
msg(int row, int col, string msg_str, clr=color.blue) =>
    if(barstate.islast)
        table.cell(table_id=tab, column=col, row=row, text=msg_str, text_color=clr)

t(val) => str.tostring(val)
method f(float f) => str.tostring(f)
method f(int i) => str.tostring(i)
method f(bool b) => str.tostring(b)
method f(string s) => str.tostring(s)
method fd(int t, string format = "dd-MM-yy HH:mm") => str.format_time(t, format, syminfo.timezone)

dt(val, format = "dd-MM-yy HH:mm") => str.format_time(val, format, syminfo.timezone)
var debug = array.new<string>(1, "")
method clear_log(string [] s) =>
    s.clear()
    s.unshift("")
method log(string [] s, string msg, bool on_last_bar = false) =>
    if((not on_last_bar) or (on_last_bar and barstate.islast))
        if(str.length(s.get(0)) + str.length(msg) < 4095)
            s.set(0, s.get(0) + msg + "\n")
method display(string [] s, where = 'B') =>
    if(not na(s.get(0)))
        if(where == 'B')
            label.new(bar_index, low, yloc = yloc.belowbar, style = label.style_diamond, tooltip = s.get(0), size = size.tiny)
        else
            label.new(bar_index, high, yloc = yloc.abovebar, style = label.style_diamond, tooltip = s.get(0), size = size.tiny)
        debug.clear_log()
//#endregion General functions
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////    

//@version=5
indicator('MTF - Zigzag + Tech Indicators', overlay=true, max_lines_count = 500, max_labels_count = 500)
//#region Input Parameters
start_time = input.time(timestamp("01 Jan 1900"))

g1 = 'High high, Lower low Setup'
lb = input.int(title = 'Left bars', defval = 10, group = g1, inline = 'g11')
rb = input.int(title = 'Right bars', defval = 10, group = g1, inline = 'g11')

g2 = 'Chart timeframe Zigzag Setup'
color_ctf = input.color(title = 'Zigzag color', defval = color.blue, group = g2, inline = 'g21')
width_ctf = input.int(title = 'Width', defval = 1, group = g2, inline = 'g21')
plot_ctf_zigzag = input.bool(title = 'Plot Zigzag', defval = true, group = g2, inline = 'g22')
plot_ctf_hhll_labels = input.bool(title = 'Plot HHLL labels', defval = true, group = g2, inline = 'g22')


g3 = 'Higher timeframe Zigzag Setup'
htf = input.timeframe(title = 'HTF', defval = 'D', group = g3, inline = 'g31')
htf_offset = input.int(title = 'Offset', defval = 1, minval = 0, maxval = 1, group = g3, inline = 'g31')
color_htf = input.color(title = 'Zigzag color', defval = color.yellow, group = g3, inline = 'g32')
width_htf = input.int(title = 'Width', defval = 1, group = g3, inline = 'g32')
plot_htf_zigzag = input.bool(title = 'Plot Zigzag', defval = true, group = g3, inline = 'g33')
plot_htf_hhll_labels = input.bool(title = 'Plot HHLL labels', defval = true, group = g3, inline = 'g33')

g4 = 'Higher timeframe Zigzag Setup 1'
htf1 = input.timeframe(title = 'HTF', defval = 'W', group = g4, inline = 'g41')
htf1_offset = input.int(title = 'Offset', defval = 1, minval = 0, maxval = 1, group = g4, inline = 'g41')
color_htf1 = input.color(title = 'Zigzag color', defval = color.purple, group = g4, inline = 'g42')
width_htf1 = input.int(title = 'Width', defval = 1, group = g4, inline = 'g42')
plot_htf1_zigzag = input.bool(title = 'Plot Zigzag', defval = false, group = g4, inline = 'g43')
plot_htf1_hhll_labels = input.bool(title = 'Plot HHLL labels', defval = false, group = g4, inline = 'g43')
//#endregion Input Parameters

//#region Definitions and functios
var tf_info = table.new(position=position.bottom_right, columns=1, rows=1,frame_color = na, frame_width = 1)
show_tf(int row = 0, int col = 0, string msg_str, clr=color.blue) =>
    if(barstate.islast)
        table.cell(table_id=tf_info, column=col, row=row, text=msg_str, text_color=clr, text_size = size.tiny)
var ctf = t(timeframe.period)
show_tf(0, 0, "TF: "+t(ctf)+"/"+t(htf)+"/"+t(htf1))

type class_visuals
    bool plot_line = false
    bool plot_label = false
    color clr
    int width
var visuals  = map.new<string, class_visuals>()
if(bar_index == 0)
    visuals.put('CTF', class_visuals.new(plot_ctf_zigzag, plot_ctf_hhll_labels, color_ctf, width_ctf))
    visuals.put('HTF', class_visuals.new(plot_htf_zigzag, plot_htf_hhll_labels, color_htf, width_htf))
    visuals.put('HTF1', class_visuals.new(plot_htf1_zigzag, plot_htf1_hhll_labels, color_htf1, width_htf1))
    
//objects 
type class_hhll
    string group //H or l
    string sub_group // HH, LL, LH, HL
    int bar_id 
    int bar_time
    float pivot_pp 
    line ln
    label lbl
    float rsi
    float stoc
    float adx
    float sma20
    float sma50
    float sma100
    float sma200
    float price_move
    float price_change
    float cum_volume
    float swing_volume
    string tf
    bool is_new = false

var array<class_hhll> hhll_ctf = array.new<class_hhll>()
var array<class_hhll> hhll_htf = array.new<class_hhll>()
var array<class_hhll> hhll_htf1 = array.new<class_hhll>()
type class_ohlc
    bool pivot
    float pp
    int bar_id
    int bar_time
    float rsi
    float stoc
    float adx
    float sma20
    float sma50
    float sma100
    float sma200
    float cum_volume

//
rsi = math.round(ta.rsi(close, 14), 2)
stoc = math.round(ta.stoch(close, high, low, 14), 2)
[diplus, diminus, _adx] = ta.dmi(14, 14)
adx = math.round(_adx, 2)
sma20 = math.round(ta.sma(close, 20), 2)
sma50 = math.round(ta.sma(close, 50), 2)
sma100 = math.round(ta.sma(close, 100), 2)
sma200 = math.round(ta.sma(close, 200), 2)
vol_sum = ta.cum(volume)
//
get_ph_pl(i) =>
    ph = class_ohlc.new(pivot = false)
    pl = class_ohlc.new(pivot = false)
    _ph = ta.pivothigh(high[i], lb, rb) 
    _pl = ta.pivotlow(low[i], lb, rb) 
    // __ph = ta.pivothigh(high[i], lb, rb)
    // __pl = ta.pivotlow(low[i], lb, rb)
    // _ph = __ph != 0 ? true : false
    // _pl = __pl != 0 ? true : false
    
    if((_ph > 0  or _pl > 0) and time >= start_time)
        rec = class_ohlc.new(
             pivot = true,
             pp = _ph > 0 ? high[rb+i] : low[rb+i],
             bar_id = bar_index[rb+i],
             bar_time = time[rb+i],
             rsi = rsi[rb+i],
             stoc = stoc[rb+i],
             adx = adx[rb+i],
             sma20 = sma20[rb+i],
             sma50 = sma50[rb+i],
             sma100 = sma100[rb+i],
             sma200 = sma200[rb+i],
             cum_volume = vol_sum[rb+i])
        ph := not na(_ph) ? rec : ph
        pl := not na(_pl) ? rec : pl
        
    [ph, pl]
//
method get_tt(class_hhll hhll) =>
    tt = "Tag = "+ hhll.sub_group + " (" + dt(hhll.bar_time) +")\n" +
         "Timeframe = "+ hhll.tf + "\n" +
         "Price point = "+ t(hhll.pivot_pp) + "\n" +
         "Price change  = "+ t(hhll.price_change) + " ("+t(hhll.price_move) + " %)\n" +
         "Swing volume  = "+ t(math.round(hhll.swing_volume/1000000, 2)) + " M\n" +
         "---- Technical Indicators ----" +"\n"+
         "RSI = "+ t(hhll.rsi) + "\n" +
         "Stochastic = "+ t(hhll.stoc) + "\n" +
         "ADX = "+ t(hhll.adx) + "\n" +
         "SMA20 = "+ t(hhll.sma20) + "\n" +
         "SMA50 = "+ t(hhll.sma50) + "\n" +
         "SMA100 = "+ t(hhll.sma100) + "\n" +
         "SMA200 = "+ t(hhll.sma200) 
//
method get(array<class_hhll> hhll, id) => hhll.size() > id ? hhll.get(id)  : class_hhll.new()
//
method add_ph(array<class_hhll> hhll, class_ohlc ph, class_visuals v, tf) =>
    if(ph.pivot)
        pp = ph.pp 
        bar_id = ph.bar_id 
        bar_time = ph.bar_time
        vol = ph.cum_volume
        class_hhll prev = hhll.get(0)
        rec_deleted = false
        if(prev.group == 'H')
            if(pp > prev.pivot_pp)
                rec_deleted := true
                prev.ln.delete()
                prev.lbl.delete()
                hhll.shift()
                prev := hhll.get(0)
        if(prev.group != 'H')
            move = math.round((pp - nz(prev.pivot_pp, 0)) / pp * 100, 2)
            change = math.round_to_mintick(pp - nz(prev.pivot_pp))
            swing_vol = vol - prev.cum_volume
            new_rec = class_hhll.new(group = 'H', bar_id = bar_id, bar_time = bar_time, pivot_pp = pp, rsi = ph.rsi,
                 stoc = ph.stoc, adx = ph.adx, sma20 = ph.sma20, sma50 = ph.sma50, sma100 = ph.sma100, sma200 = ph.sma200, price_move = move,  price_change = change,
                 cum_volume = ph.cum_volume, swing_volume = swing_vol, tf = t(tf), is_new = true)
            if(na(prev.group))
                new_rec.sub_group := 'HH'
                if(v.plot_line)
                    new_rec.ln := line.new(bar_time, pp, bar_time, pp, xloc = xloc.bar_time, color = v.clr, width = v.width)
                if(v.plot_label)
                    tt = new_rec.get_tt()
                    new_rec.lbl := label.new(bar_time, pp, text = new_rec.sub_group, xloc = xloc.bar_time, style = label.style_label_down, size = size.tiny,
                         color = v.clr, tooltip = tt)
            else
                class_hhll prev_ph = hhll.get(1)
                new_rec.sub_group := pp > prev_ph.pivot_pp ? 'HH' : 'LH'
                if(v.plot_line)
                    new_rec.ln := line.new(prev.bar_time, prev.pivot_pp, bar_time, pp, xloc = xloc.bar_time, color = v.clr, width = v.width)
                if(v.plot_label)
                    tt = new_rec.get_tt()
                    new_rec.lbl := label.new(bar_time, pp, text = new_rec.sub_group, xloc = xloc.bar_time, style = label.style_label_down, size = size.tiny, color = v.clr, tooltip = tt)
            hhll.unshift(new_rec)
    true
//
method add_pl(array<class_hhll> hhll, class_ohlc pl, class_visuals v, tf) =>
    if(pl.pivot)
        pp = pl.pp 
        bar_id = pl.bar_id 
        bar_time = pl.bar_time
        vol = pl.cum_volume
        class_hhll prev = hhll.get(0)
        rec_deleted = false
        if(prev.group == 'L')
            if(pp < prev.pivot_pp)
                rec_deleted := true
                prev.ln.delete()
                prev.lbl.delete()
                hhll.shift()
                prev := hhll.get(0)
        if(prev.group != 'L')
            move = math.round((pp - nz(prev.pivot_pp, 0)) / pp * 100, 2)
            change = math.round_to_mintick(pp - nz(prev.pivot_pp))
            swing_vol = vol - prev.cum_volume
            new_rec = class_hhll.new(group = 'L', bar_id = bar_id, bar_time = bar_time, pivot_pp = pp, rsi = pl.rsi,
                 stoc = pl.stoc, adx = pl.adx, sma20 = pl.sma20, sma50 = pl.sma50, sma100 = pl.sma100, sma200 = pl.sma200, price_move = move, price_change = change,
                 cum_volume = pl.cum_volume, swing_volume = swing_vol, tf = t(tf), is_new = true)
            if(na(prev.group))
                new_rec.sub_group := 'LL'
                if(v.plot_line)
                    new_rec.ln := line.new(bar_time, pp, bar_time, pp, xloc = xloc.bar_time, color = v.clr, width = v.width)
                if(v.plot_label)
                    tt = new_rec.get_tt()
                    new_rec.lbl := label.new(bar_time, pp, text = new_rec.sub_group, xloc = xloc.bar_time, style = label.style_label_up, size = size.tiny, color = v.clr, tooltip = tt)
            else
                class_hhll prev_pl = hhll.get(1)
                new_rec.sub_group := pp < prev_pl.pivot_pp ? 'LL' : 'HL'
                if(v.plot_line)
                    new_rec.ln := line.new(prev.bar_time, prev.pivot_pp, bar_time, pp, xloc = xloc.bar_time, color = v.clr, width = v.width)
                if(v.plot_label)
                    tt = new_rec.get_tt()
                    new_rec.lbl := label.new(bar_time, pp, text = new_rec.sub_group, xloc = xloc.bar_time, style = label.style_label_up, size = size.tiny, color = v.clr, tooltip = tt)


            hhll.unshift(new_rec)
    true    
//#endregion Definitions and functios

//#region Main 
//main code
[ph_ctf, pl_ctf] = get_ph_pl(0)
hhll_ctf.add_ph(ph_ctf, visuals.get('CTF'), ctf)
hhll_ctf.add_pl(pl_ctf, visuals.get('CTF'), ctf)

//HTF code
new_htf = timeframe.change(htf)
[ph_htf, pl_htf] = request.security('', htf, get_ph_pl(htf_offset))
if(na(ph_htf) or not new_htf)
    ph_htf := class_ohlc.new(false)
if(na(pl_htf) or not new_htf)
    pl_htf := class_ohlc.new(false)


hhll_htf.add_ph(ph_htf, visuals.get('HTF'), htf)
hhll_htf.add_pl(pl_htf, visuals.get('HTF'), htf)

//HTF 1 code
new_htf1 = timeframe.change(htf1)
[ph_htf1, pl_htf1] = request.security('', htf1, get_ph_pl(htf1_offset))
if(na(ph_htf1) or not new_htf1)
    ph_htf1 := class_ohlc.new(false)
if(na(pl_htf1) or not new_htf1)    
    pl_htf1 := class_ohlc.new(false)

hhll_htf1.add_ph(ph_htf1, visuals.get('HTF1'), htf1)
hhll_htf1.add_pl(pl_htf1, visuals.get('HTF1'), htf1)
//#endregion Main 
