Auto AB=CD 1 to 1 Ratio Experimental
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © RozaniGhani-RG


//@version=5
indicator('Auto AB=CD 1 to 1 Ratio Experimental', overlay=true, max_bars_back=500, max_lines_count=500, max_labels_count=500, precision=2)


// 0. Inputs
// 1. Variables
// 2. zig zag core (Credits to LonesomeTheBlue)
// 3. Ternary conditional operator
// 4. Custom functions
// 5. Construct


// 0. Inputs
G1 = 'AB=CD Line & Label'
G2 = 'AB=CD Table'
I1 = '1'
I2 = '2'
I3 = '3'

// group = G1
i_prd   = input.int(           8, 'Period   ', inline = I1, group = G1, minval = 2)
c_up    = input.color(color.lime,          '', inline = I1, group = G1)
c_dn    = input.color( color.red,          '', inline = I1, group = G1)
i_table = input.bool(       true,     'Table', inline = I2, group = G2)

// group = G2
i_font     = input.string('normal',         '', inline = I2, group = G2, options=['tiny', 'small', 'normal', 'large', 'huge'])
i_Y        = input.string('bottom',         '', inline = I2, group = G2, options=['top', 'middle', 'bottom'])
i_X        = input.string( 'right',         '', inline = I2, group = G2, options=['left', 'center', 'right'])
i_currency = input.bool(      true, 'Currency', inline = I3, group = G2)


// 1. Variables
// Common colors
cnb = color.new(color.blue, 100)
ck = color.black
cb = color.blue
cr = color.red

// Common variables
var max_array_size = 10
var zigzag         = array.new_float(0)
var float ratio_AB = 0
var float ratio_CD = 0
var abcd_table     = table.new(i_Y + '_' + i_X, 4, 6, bgcolor = cnb, border_color = cnb, border_width = 1)

// Show currency into cell
// Dependency :      i_currency
ter_currency = switch i_currency
    true => syminfo.currency + ' '



// 2. zig zag core (Credits to LonesomeTheBlue)
get_ph_pl_dir(len) =>
    float ph = ta.highestbars(high, len) == 0 ? high : na
    float pl = ta.lowestbars(  low, len) == 0 ?  low : na
    var dir = 0
    iff  = pl and na(ph) ? -1 : dir
    dir := ph and na(pl) ?  1 : iff
    [ph, pl, dir]

[ph, pl, dir] = get_ph_pl_dir(i_prd)

add_to_zigzag(value, bindex) =>
    array.unshift(zigzag, bindex)
    array.unshift(zigzag, value)
    if array.size(zigzag) > max_array_size
        array.pop(zigzag)
        array.pop(zigzag)

update_zigzag(value, bindex) =>
    if array.size(zigzag) == 0
        add_to_zigzag(value, bindex)
    else
        if dir == 1 and value > array.get(zigzag, 0) or dir == -1 and value < array.get(zigzag, 0)
            array.set(zigzag, 0, value)
            array.set(zigzag, 1, bindex)
        0.

dir_changed = ta.change(dir)
if ph or pl
    if dir_changed
        add_to_zigzag(dir == 1 ? ph : pl, time)
    else
        update_zigzag(dir == 1 ? ph : pl, time)


// 3. Switch
// Dependency : dir
[pos, style1, style2] = switch dir
    1 => [ 1, label.style_label_down, label.style_label_up  ]
    =>   [-1, label.style_label_up,   label.style_label_down]

// 4. Custom functions
// ABC custom functions
f_line_solid_array(_value) =>
    var line id = na, line.delete(id)
    // Dependency : zigzag
    id := line.new(  x1   = math.round(array.get(zigzag, _value + 3)), y1 = array.get(zigzag, _value + 2),
                     x2   = math.round(array.get(zigzag, _value + 1)), y2 = array.get(zigzag, _value),
                     xloc = xloc.bar_time)
    id

f_line_dot_get(_id1, _id2) =>
    var line id = na, line.delete(id)
    id := line.new(  x1    = line.get_x1(_id1), y1 = line.get_y1(_id1),
                     x2    = line.get_x2(_id2), y2 = line.get_y2(_id2),
                     xloc  = xloc.bar_time,
                     style = line.style_dotted)
    id

f_label1(_id, _text, _style) =>
    var label id = na
    label.delete(id)
    id := label.new(x=line.get_x1(_id), y=line.get_y1(_id), xloc=xloc.bar_time, text=_text, color=cnb, style=_style)
    id

f_label2(_id, _text, _style) =>
    var label id = na
    label.delete(id)
    id := label.new(x=line.get_x2(_id), y=line.get_y2(_id), xloc=xloc.bar_time, text=_text, color=cnb, style=_style)
    id

// Shared custom functions
f_dif(_id) =>
    var int      dx = 0,    dx := int(math.abs(line.get_x1(_id) - line.get_x2(_id)))
    var float    dy = 0,    dy := math.abs(line.get_y1(_id) - line.get_y2(_id))
    var int   avg_x = 0, avg_x := int(math.avg(line.get_x1(_id), line.get_x2(_id)))
    var float avg_y = 0, avg_y := math.avg(line.get_y1(_id), line.get_y2(_id))
    [dx, dy, avg_x, avg_y]

f_label_avg(_x, _y, _text) =>
    var label id = na
    label.delete(id)
    id := label.new(x=_x, y=_y, xloc=xloc.bar_time, text=str.tostring(_text, '0.000'), textcolor=ck, style=label.style_label_center)
    id

// BCD custom function
f_line_solid_future(_id1, _id2, _x, _y) =>
    var line id = na
    line.delete(id)
    id := line.new(x1=label.get_x(_id1), y1=label.get_y(_id1), x2=label.get_x(_id2) + _x, y2=label.get_y(_id2) + _y, xloc=xloc.bar_time)
    id

f_line_dot_future(_id1, _id2, _x, _y) =>
    var line id = na
    line.delete(id)
    id := line.new(x1=label.get_x(_id1), y1=label.get_y(_id1), x2=label.get_x(_id2) + _x, y2=label.get_y(_id2) + _y, xloc=xloc.bar_time, style=line.style_dotted)
    id

// Cell custom function
f_cell(_column, _row, _text, _color) =>
    table.cell(abcd_table, _column, _row, _text, bgcolor=_color, text_color=ck, text_size=i_font)

// Ternary conditional operator custom functions
f_cond1(_id1, _id2, _id3) =>
    label.get_y(_id1) < label.get_y(_id2) and label.get_y(_id2) < label.get_y(_id3)
f_cond2(_id1, _id2, _id3) =>
    label.get_y(_id1) > label.get_y(_id2) and label.get_y(_id2) > label.get_y(_id3)


// 5. Construct
if barstate.islast and array.size(zigzag) > 6
    // ABC lines
    line_BC = f_line_solid_array(0)
    line_AB = f_line_solid_array(2)
    // Dependencies : line_AB, line_BC  
    line_AC = f_line_dot_get(line_AB, line_BC)

    // ABC labels recall from ABC lines
    // Dependency : line_AB
    label_A = f_label1(line_AB, 'A', style1)

    // Dependency : line_BC    
    label_B = f_label1(line_BC, 'B', style2)
    label_C = f_label2(line_BC, 'C', style1)

    // Tuple values recall from ABC lines
    // Dependencies : line_AB, line_BC, line_AC
    [dx_AB, dy_AB, avg_x_AB, avg_y_AB] = f_dif(line_AB)
    [dx_BC, dy_BC, avg_x_BC, avg_y_BC] = f_dif(line_BC)
    [dx_AC, dy_AC, avg_x_AC, avg_y_AC] = f_dif(line_AC)

    // Retracement value
    // Dependencies : dy_BC, dy_AB
    ratio_AB := math.abs(dy_BC / dy_AB)

    // Conditional Retracement
    // Dependency : dir, ratio_AB  
    txt_dir = dir == 1 and ratio_AB < 1 ? 'Bullish' : dir == -1 and ratio_AB < 1 ? 'Bearish' : dir == 1 and ratio_AB > 1 ? 'Bullish' : dir == -1 and ratio_AB > 1 ? 'Bearish' : ''
    txt_type = ratio_AB > 1 ? 'Reciprocal AB=CD' : ratio_AB < 1 ? 'AB=CD' : ''
    combo = dir == 1 and ratio_AB < 1 ? -1 : dir == -1 and ratio_AB < 1 ? -1 : dir == 1 and ratio_AB > 1 ? 1 : dir == -1 and ratio_AB > 1 ? 1 : 0
    col = dir == 1 and ratio_AB < 1 ? c_up : dir == -1 and ratio_AB < 1 ? c_dn : dir == 1 and ratio_AB > 1 ? c_up : dir == -1 and ratio_AB > 1 ? c_dn : cb

    // BCD future line and label
    line_CD = f_line_solid_future(label_C, label_B, dx_AC, dy_AC * pos * combo)
    line_BD = f_line_dot_future(label_B, label_B, dx_AC, dy_AC * pos * combo)
    label_D = f_label2(line_CD, 'D', style2)

    // Tuple values recall from BCD lines
    // Dependencies : line_CD, line_BD
    [dx_CD, dy_CD, avg_x_CD, avg_y_CD] = f_dif(line_CD)
    [dx_BD, dy_BD, avg_x_BD, avg_y_BD] = f_dif(line_BD)

    // Projection value
    // Dependencies : dy_CD, dy_BC
    ratio_CD := math.abs(dy_CD / dy_BC)

    // Label Position for AB=CD value
    // Dependencies :      avg_x_AC, avg_y_AC, ratio_AB
    label_AC = f_label_avg(avg_x_AC, avg_y_AC, ratio_AB)
    // Dependencies :      avg_x_BD, avg_y_BD, ratio_CD
    label_BD = f_label_avg(avg_x_BD, avg_y_BD, ratio_CD)

    // Set color
    label.set_textcolor(label_A, col)
    label.set_textcolor(label_B, col)
    label.set_textcolor(label_C, col)
    label.set_textcolor(label_D, col)
    label.set_color(label_AC, col)
    label.set_color(label_BD, col)
    line.set_color(line_AB, col)
    line.set_color(line_BC, col)
    line.set_color(line_CD, col)
    line.set_color(line_AC, col)
    line.set_color(line_BD, col)

    // Show Table
    if i_table
        f_cell(0, 0, txt_dir, col)
        f_cell(1, 0, '', col)
        f_cell(2, 0, txt_type, col)
        f_cell(0, 1, 'Retracement', cr)
        f_cell(1, 1, 'A', cr)
        f_cell(2, 1, str.tostring(label.get_y(label_A), ter_currency + '#.#'), cr)
        f_cell(0, 2, str.tostring(ratio_AB, '0.000'), cr)
        f_cell(1, 2, 'B', cr)
        f_cell(2, 2, str.tostring(label.get_y(label_B), ter_currency + '#.#'), cr)
        f_cell(0, 3, 'Projection', cb)
        f_cell(1, 3, 'C', cb)
        f_cell(2, 3, str.tostring(label.get_y(label_C), ter_currency + '#.#'), cb)
        f_cell(0, 4, str.tostring(ratio_CD, '0.000'), cb)
        f_cell(1, 4, 'D', cb)
        f_cell(2, 4, str.tostring(label.get_y(label_D), ter_currency + '#.#'), cb)

