Trend/Retracement - ZigZag - New way
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Sharad_Gaikwad
//import Sharad_Gaikwad/SSGLibrary/1 as lib
//@version=5 
indicator("Trend/Retracement - ZigZag - New way", shorttitle = 'ZigZag - Trend/Retracement', overlay = true, max_lines_count = 500) 

//bt_start = input.time(timestamp("01 Jan 2022"))
bt_start = timestamp("01 Jan 1900")
_g1                                 = '==========  Zigzag Parameters   =========='
lb_predefined                       = input.bool(title          = 'Use predefined Lookback (See "TF based Lookback period config" section below for defaults)',                                    defval = true,               group = _g1)
lb_manual                           = input.int(title           = 'Trend lookback candles (used if predefined is unchecked)',   defval = 50,                  group = _g1)
manual_retracement_percent          = input.float(title         = 'Retracement % of look back candles',                         defval = 25,                  group = _g1)/100
mark_retracements                   = input.bool(title          = 'Mark retracements',                          defval = true,               group = _g1)
plot_zigzag                         = input.bool(title          = 'Plot zigzag',                                                defval = true,                group = _g1, inline = 'g1')
trend_color                         = input.color(title         = 'Zigzag line color',                                          defval = color.blue,        group = _g1, inline = 'g1')
retrace_color                       = trend_color
//retrace_color = input.color(title = 'Retracement line color', defval = color.yellow, group = _g1, inline = 'g1')

plot_sr                             = input.bool(title          = 'Plot support/resistance',                                    defval = true,                   group = _g1, inline = 'l2')
support_line_color                  = input.color(title         = 'Color - Support',                                            defval = color.green,          group = _g1, inline = 'l2')
resistance_line_color               = input.color(title         = 'Color - Resistance',                                         defval = color.red,            group = _g1, inline = 'l2')
keep_tested                         = input.bool(title          = 'Show support/resistance tested lines',                       defval = true,                   group = _g1)
high_src                            = input.source(title        = 'Data source - High',                                          defval = high,                   group = _g1)
low_src                             = input.source(title        = 'Data source - Low',                                           defval = low,                    group = _g1)
_g2                                 = '==========  TF based Lookback period config  =========='
tf_min_1                            = input.int(title           = 'Timeframe in minute',                                        defval = 5, minval = 1, maxval = 1440, group = _g2, inline = 'min1')
tf_min_1_lb                         = input.int(title           = 'Trend lookback candles',                                     defval = 375, group = _g2, inline = 'min1')

tf_min_2                            = input.int(title           = 'Timeframe in minute',                                        defval = 15, minval = 1, maxval = 1440, group = _g2, inline ='min2')
tf_min_2_lb                         = input.int(title           = 'Trend lookback candles',                                     defval = 250, group = _g2, inline = 'min2')

tf_min_3                            = input.int(title           = 'Timeframe in minute',                                        defval = 60, minval = 1, maxval = 1440, group = _g2, inline = 'min3')
tf_min_3_lb                         = input.int(title           = 'Trend lookback candles',                                     defval = 90, group = _g2, inline = 'min3')

tf_day_lb                           = input.int(title           = "Trend lookback candles for timeframe 'D'",                   defval = 30, group = _g2 )
tf_week_lb                          = input.int(title           = "Trend lookback candles for timeframe 'W'",                   defval = 21, group = _g2 )
tf_month_lb                         = input.int(title           = "Trend lookback candles for timeframe 'M'",                   defval = 12, group = _g2 )
predefined_retracement_percent      = input.float(title         = 'Retracement % of look back candles',                         defval = 25, group = _g2)/100
//=========================
// tf based lb defaults		
// tf	~days	LB candles
//      data
//========================
// 5m	5	    375
// 15m	10	    250
// 60m	15	    90
// 1d	30	    30
// 1w	105	    21
// 1m	250	    12
//========================
//switch_sr = input.bool(title = 'Switch SR lines on 1st cross', defval = true, tooltip = 'Supprt will turn resistance and resistance will turn support only on 1st cross. \n The lines would be removed on 2nd cross')
var tabd = table.new(position=position.bottom_right, columns=1, rows=1)
disp(msg) => table.cell(table_id=tabd, column=0, row=0, text=msg, text_color=color.yellow, text_size = size.small)

tab = table.new(position=position.top_right, columns=10, rows=200,frame_color = color.yellow, frame_width = 1)
msg(int row, int col, string msg_str, clr=color.blue) =>
    table.cell(table_id=tab, column=col, row=row, text=msg_str, text_color=clr)
t(val) => str.tostring(val)

remove_lines(_arr , top_bottom = 'Top') =>
    size = array.size(_arr)
    __arr = array.new<int>()
    if(size > 1)
        for i = 0 to size - 1
            if(top_bottom == 'Top'
                 and ((not keep_tested and high > line.get_price(array.get(_arr, i), bar_index))
                 or (keep_tested and close > line.get_price(array.get(_arr, i), bar_index)
                 or keep_tested and open > line.get_price(array.get(_arr, i), bar_index))))
                line.delete(array.get(_arr, i))
                array.unshift(__arr, i)
            if(top_bottom == 'Bottom' 
                 and ((not keep_tested and low < line.get_price(array.get(_arr, i), bar_index))
                 or (keep_tested and close < line.get_price(array.get(_arr, i), bar_index)
                 or keep_tested and open < line.get_price(array.get(_arr, i), bar_index))))
                line.delete(array.get(_arr, i))
                array.unshift(__arr, i)
                                
    _size = array.size(__arr)
    if(_size > 1)
        for i = 0 to _size - 1
            element = array.get(__arr, i)
            array.remove(_arr, element)

var top_arr = array.new<line>()
var bottom_arr = array.new<line>()

remove_lines(top_arr, 'Top')
remove_lines(bottom_arr, 'Bottom')
                          
draw(x1, y1, x2, y2, state_) =>
    clr = state_ == 'HM' or state_ == 'LM' ? trend_color : retrace_color
    ln = line.new(x1, y1, x2, y2, color = clr)
    ln
extend(ln, x2, y2, state_) => 
    clr = state_ == 'HM' or state_ == 'LM' ? trend_color : retrace_color
    line.set_xy2(ln, x2, y2)
    line.set_color(ln, clr)
    
process(_state, _price, _bar, dir, is_pivot) =>
    var ln = line.new(na, na, na, na)
    var x1 = int(na), var y1 = float(na)
    var x2 = int(na), var y2 = float(na)
    var is_pending_extend = bool(na)
    var pending_x2 = int(na)
    var pending_y2 = float(na)
    var pending_state = string(na)
    // var dow_hip1 = float(na), var dow_lop1 = float(na)
    // var dow_hip2 = float(na), var dow_lop2 = float(na)
    // var dow_hib1 = int(na), var dow_lob1 = int(na)
    // var dow_hib2 = int(na), var dow_lob2 = int(na)
    if(plot_zigzag)
        new_line = na(ln) ? 1 : 0
        wip_line = not na(ln) ? 1 : 0
        if(is_pivot)
            if((_state != 'NA' and _state[1] == 'NA') or (new_line and _state == _state[1]))
                x1 := _bar, y1 := _price
            if(new_line and _state != _state[1])
                // if(is_pending_extend)
                //     extend(ln, pending_x2, pending_y2, pending_state)
                // is_pending_extend := false
                x2 := _bar, y2 := _price
                ln := draw(x1, y1, x2, y2, _state)
            if(wip_line and dir == dir[1])
                x2 := _bar, y2 := _price
                // pending_x2 := _bar
                // pending_y2 := _price
                // pending_state := _state
                // is_pending_extend := true
                extend(ln, x2, y2, _state)
            if(wip_line and dir != dir[1])
                // if(is_pending_extend)
                //     extend(ln, pending_x2, pending_y2, 'HM')
                // is_pending_extend := false            
                x1 := x2, y1 := y2
                x2 := _bar, y2 := _price
                ln := draw(x1, y1, x2, y2, _state)          
    [x1, y1, x2, y2]

var wip_state = string('NA'), var wip_price = float(na), var wip_bar = int(na)
var direction = int(na)

var lb_candles = not lb_predefined ? lb_manual
     : timeframe.multiplier == tf_min_1 ? tf_min_1_lb
     : timeframe.multiplier == tf_min_2 ? tf_min_2_lb 
     : timeframe.multiplier == tf_min_3 ? tf_min_3_lb 
     : timeframe.isdaily ? tf_day_lb
     : timeframe.isweekly ? tf_week_lb
     : timeframe.ismonthly ? tf_month_lb
     : 50

var lb_retrace = lb_predefined ? math.max(math.floor(lb_candles * predefined_retracement_percent), 2) 
     : math.max(math.floor(lb_candles * manual_retracement_percent), 2) 
if(barstate.islast)
    var trend_msg = str.tostring(lb_candles) +' Candles'
    var retrace_msg = not mark_retracements ? 'NA' : str.tostring(lb_retrace) + ' Candles'
    disp('Parameters: Trend:'+ trend_msg + ' Retracement:'+ retrace_msg)

phm = time >= bt_start  and ta.highestbars(high_src, lb_candles) == 0 ? high_src : na
plm = time >= bt_start  and  ta.lowestbars(low_src, lb_candles) == 0 ? low_src : na

phr = time >= bt_start  and  ta.highestbars(high_src, lb_retrace) == 0 ? mark_retracements ? high_src : na : na
plr = time >= bt_start  and  ta.lowestbars(low_src, lb_retrace) == 0 ? mark_retracements ? low_src : na  : na

wip_state := phm ? 'HM' : phr ? 'HR' : plm  ? 'LM' : plr ? 'LR' : wip_state
wip_price := phm ? phm : phr ? phr : plm ? plm : plr  ? plr : float(na)
wip_bar := phm ? bar_index : phr ? bar_index : plm  ? bar_index : plr  ? bar_index : int(na)
direction := phm or phr ? 1 : plm or plr ? -1 : direction

if(phm)
    if(plot_sr)
        lnt = line.new(bar_index, high_src, bar_index+30, high_src, color = resistance_line_color, extend = extend.right)
        array.unshift(top_arr, lnt)

if(plm)
    if(plot_sr)
        lnb = line.new(bar_index, low_src, bar_index+20, low_src, color = support_line_color, extend = extend.right)
        array.unshift(bottom_arr, lnb)
if(phr)
    if(plot_sr)
        lnt = line.new(bar_index, high_src, bar_index+30, high_src, color = resistance_line_color, extend = extend.right)
        array.unshift(top_arr, lnt)
if(plr)
    if(plot_sr)
        lnb = line.new(bar_index, low_src, bar_index+20, low_src, color = support_line_color, extend = extend.right)
        array.unshift(bottom_arr, lnb)

[x1, y1, x2, y2] = process(wip_state, wip_price, wip_bar, direction, phm or plm or phr or plr)
// msg(0, 0, "Data")
// msg(1, 0, "wip state = "+t(wip_state))
// msg(2, 0, "wip bar = "+t(wip_bar))
// msg(3, 0, "wip price = "+t(wip_price))
// msg(4, 0, "wip state[1] = "+t(wip_state[1]))
// msg(5, 0, "y1 = "+t(y1))
// msg(6, 0, "y2 = "+t(y2))
// msg(7, 0 ,'Dir / dir[1]= '+t(direction)+ ' / '+t(direction[1]))
// msg(8, 0, 'phm/plm = '+t(phm) + '/ '+t(plm))
// msg(9, 0, 'phr/plr = '+t(phr) + '/ '+t(plr))