Equity Bond Currency Dashboard
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © iravan

//@version=5
indicator("Equity, Bond, Currency Dashboard", overlay=false, max_boxes_count=500, max_lines_count=100, max_labels_count=100)

grp_us = "Currency, yield and equity indices for the US"
grp_country = "Currency, yield and equity indices for countries"

sym01 = input.symbol("TVC:DXY", title="US", inline="country0", group=grp_us)
sym02 = input.symbol("TVC:US02Y", title="", inline="country0", group=grp_us)
sym03 = input.symbol("CURRENCYCOM:US500", title="", inline="country0", group=grp_us)

sym11 = input.symbol("FX_IDC:USDINR", title="C1", inline="country1", group=grp_country)
sym12 = input.symbol("TVC:IN02Y", title="", inline="country1", group=grp_country)
sym13 = input.symbol("NSE:NIFTY", title="", inline="country1", group=grp_country)

sym21 = input.symbol("FX_IDC:USDEUR", title="C2", inline="country2", group=grp_country)
sym22 = input.symbol("TVC:DE02Y", title="", inline="country2", group=grp_country)
sym23 = input.symbol("CURRENCYCOM:DE40", title="", inline="country2", group=grp_country)

sym31 = input.symbol("FX_IDC:USDGBP", title="C3", inline="country3", group=grp_country)
sym32 = input.symbol("TVC:GB02Y", title="", inline="country3", group=grp_country)
sym33 = input.symbol("FXOPEN:UK100", title="", inline="country3", group=grp_country)

sym41 = input.symbol("FX_IDC:USDCNY", title="C4", inline="country4", group=grp_country)
sym42 = input.symbol("TVC:CN02Y", title="", inline="country4", group=grp_country)
sym43 = input.symbol("OANDA:CN50USD", title="", inline="country4", group=grp_country)

sym51 = input.symbol("FX_IDC:USDJPY", title="C5", inline="country5", group=grp_country)
sym52 = input.symbol("TVC:JP02Y", title="", inline="country5", group=grp_country)
sym53 = input.symbol("OANDA:JP225USD", title="", inline="country5", group=grp_country)

custom_bar = input.bool(false, "", inline="begin", group="Run on a historical bar")
bar = input.time(timestamp("23 Mar 2020"), "Render On", inline="begin", confirm=false, group="Run on a historical bar", tooltip="Enable and set this option to use a custom date. If this option is unchecked, current date will be used")

[o01, h01, l01, c01] = request.security(sym01, timeframe.period, [open, high, low, close])
[o02, h02, l02, c02] = request.security(sym02, timeframe.period, [open, high, low, close])
[o03, h03, l03, c03] = request.security(sym03, timeframe.period, [open, high, low, close])

[o11, h11, l11, c11] = request.security(sym11, timeframe.period, [open, high, low, close])
[o12, h12, l12, c12] = request.security(sym12, timeframe.period, [open, high, low, close])
[o13, h13, l13, c13] = request.security(sym13, timeframe.period, [open, high, low, close])

[o21, h21, l21, c21] = request.security(sym21, timeframe.period, [open, high, low, close])
[o22, h22, l22, c22] = request.security(sym22, timeframe.period, [open, high, low, close])
[o23, h23, l23, c23] = request.security(sym23, timeframe.period, [open, high, low, close])

[o31, h31, l31, c31] = request.security(sym31, timeframe.period, [open, high, low, close])
[o32, h32, l32, c32] = request.security(sym32, timeframe.period, [open, high, low, close])
[o33, h33, l33, c33] = request.security(sym33, timeframe.period, [open, high, low, close])

[o41, h41, l41, c41] = request.security(sym41, timeframe.period, [open, high, low, close])
[o42, h42, l42, c42] = request.security(sym42, timeframe.period, [open, high, low, close])
[o43, h43, l43, c43] = request.security(sym43, timeframe.period, [open, high, low, close])

[o51, h51, l51, c51] = request.security(sym51, timeframe.period, [open, high, low, close])
[o52, h52, l52, c52] = request.security(sym52, timeframe.period, [open, high, low, close])
[o53, h53, l53, c53] = request.security(sym53, timeframe.period, [open, high, low, close])

var o_0 = array.new<float>()
var h_0 = array.new<float>()
var l_0 = array.new<float>()
var c_0 = array.new<float>()
var c_1 = array.new<float>()
var bar_set = false
if not bar_set and ((custom_bar and time_close > bar) or (not custom_bar and barstate.islast))
    bar_set := true
    o_0 := array.from(o01, o02, o03, o11, o12, o13, o21, o22, o23, o31, o32, o33, o41, o42, o43, o51, o52, o53)
    h_0 := array.from(h01, h02, h03, h11, h12, h13, h21, h22, h23, h31, h32, h33, h41, h42, h43, h51, h52, h53)
    l_0 := array.from(l01, l02, l03, l11, l12, l13, l21, l22, l23, l31, l32, l33, l41, l42, l43, l51, l52, l53)
    c_0 := array.from(c01, c02, c03, c11, c12, c13, c21, c22, c23, c31, c32, c33, c41, c42, c43, c51, c52, c53)
    c_1 := array.from(c01[1], c02[1], c03[1], c11[1], c12[1], c13[1], c21[1], c22[1], c23[1], c31[1], c32[1], c33[1], c41[1], c42[1], c43[1], c51[1], c52[1], c53[1])

var box_space = timeframe.isdwm ? 5 : 5
var box_width = 20
var cell_width = 10

getCords(int i, int j) =>
    [last_bar_index - i * cell_width, j * cell_width, last_bar_index - i * cell_width + box_width, j * cell_width - box_width]

getAnchor(b, side) =>
    if side == 0
        [box.get_left(b), (box.get_bottom(b) + box.get_top(b)) / 2]
    else if side == 1
        [(box.get_left(b) + box.get_right(b)) / 2, box.get_top(b)]
    else if side == 2
        [box.get_right(b), (box.get_bottom(b) + box.get_top(b)) / 2]
    else if side == 3
        [(box.get_left(b) + box.get_right(b)) / 2, box.get_bottom(b)]

var symbols = array.from(sym01, sym02, sym03, sym11, sym12, sym13, sym21, sym22, sym23, sym31, sym32, sym33, sym41, sym42, sym43, sym51, sym52, sym53)
var locs = array.from(10, 8, 12, 0, 8, 0, 10, 16, 7, 20, 13, 20, 15, 13, 18, 16, 18, 10, 15, 3, 18, 5, 18, 0, 5, 13, 2, 16, 2, 10, 5, 3, 2, 5, 2, 0)
var arrows = array.from(0, 3, 1, 1, 0, 3, 2, 1, 3, 2, 4, 3, 3, 0, 5, 3, 6, 0, 7, 2, 6, 0, 8, 2, 9, 0, 10, 2, 9, 0, 11, 2, 12, 2, 13, 0, 12, 2, 14, 0, 15, 2, 16, 0, 15, 2, 17, 0, 0, 1, 3, 3, 0, 0, 6, 2, 0, 0, 9, 2, 0, 2, 12, 0, 0, 2, 15, 0) 

var box_legends = array.new<box>(array.size(symbols))
var box_legend_borders = array.new<box>(array.size(symbols))
var box_names = array.new<box>(array.size(symbols))
var box_values = array.new<box>(array.size(symbols))
var box_changes = array.new<box>(array.size(symbols))
var lines = array.new<line>(array.size(arrows) / 4)
var changes = array.new<float>(array.size(symbols))

var display = true
var render = false
if last_bar_index < cell_width * array.max(locs) and barstate.isfirst
    display := false
    label.new(last_bar_index, 0, "Oops! Not enough bars are available for display!\nPlease chose another symbol or timeframe.", color=color.yellow)

if barstate.islast and (barstate.isrealtime or custom_bar)
    render := true
    
if barstate.islast and not barstate.isrealtime and display and not render
    label.new(last_bar_index - 7, cell_width * 20, "Market closed!", size=size.small, color=color.yellow, style=label.style_label_center)

if barstate.islast and custom_bar
    label.new(last_bar_index - 6, cell_width * 20, "Historical Bar", size=size.small, color=color.yellow, style=label.style_label_center)    

if barstate.isfirst and display
    for i = 0 to array.size(symbols) - 1
        name = syminfo.ticker(array.get(symbols, i))

        [l, t, r, b] = getCords(array.get(locs, i * 2), array.get(locs, i * 2 + 1))
        box.new(l, t, r, b, text="",  border_width=0, bgcolor=color.black )
        box_legend = box.new(l, t, l+1, b, text="",  border_width=0)
        box_legend_border = box.new(l, t, l+1, b, text="",  border_width=1, bgcolor=color.rgb(0,0,0,100))        
        box_value = box.new(l, t, r, b, text="",  text_size=size.normal, border_width=0, bgcolor=color.rgb(0,0,0,100), text_color=color.white)
        box_name = box.new(l+1, t, l + cell_width, t - cell_width, text=name, text_size=size.tiny, text_halign=text.align_left, bgcolor=color.rgb(0,0,0,100), border_width=0, text_color=color.white)
        box_change = box.new(r - cell_width, b + cell_width, r, b, text="", text_size=size.small, text_halign=text.align_right, bgcolor=color.rgb(0,0,0,100), border_width=0, text_color=color.white)
        array.set(box_legends, i, box_legend)
        array.set(box_legend_borders, i, box_legend_border)
        array.set(box_names, i, box_name)
        array.set(box_values, i, box_value)
        array.set(box_changes, i, box_change)

    for i = 0 to array.size(arrows) - 4 by 4
        src_box = array.get(box_values, array.get(arrows, i))
        tgt_box = array.get(box_values, array.get(arrows, i + 2))

        [x1, y1] = getAnchor(src_box, array.get(arrows, i + 1))
        [x2, y2] = getAnchor(tgt_box, array.get(arrows, i + 3))

        _x1 = x1 > x2 ? x1 - 1 : x1 + 1
        _y1 = y1 > y2 ? y1 - 2: y1 + 2
        _x2 = x2 > x1 ? x2 - 1 : x2 + 1
        _y2 = y2 > y1 ? y2 - 2: y2 + 2
        array.set(lines, i / 4, line.new(_x1, _y1, _x2, _y2))

if barstate.islast and display
    for i = 0 to array.size(symbols) - 1
        o = array.get(o_0, i)
        h = array.get(h_0, i)
        l = array.get(l_0, i)
        c = array.get(c_0, i)
        c1 = array.get(c_1, i)

        box_legend = array.get(box_legends, i)
        box_legend_border = array.get(box_legend_borders, i)
        box_name = array.get(box_names, i)
        box_value = array.get(box_values, i)
        box_change = array.get(box_changes, i)

        chg = math.round((c - c1) / math.abs(c1) * 100, 2)
        array.set(changes, i, chg)

        box.set_text(box_value, str.tostring(c))
        box.set_text(box_change, str.tostring(chg)+"%")

        bg_color = c >= o ? color.teal: color.red
        if not render or (o == c and h == l) or (c == c1)
            bg_color := color.gray

        [_l, _t, _r, _b] = getCords(array.get(locs, i * 2), array.get(locs, i * 2 + 1))
        box.set_bottom(box_legend, _b + (_t - _b) / (h - l) * (o - l))
        box.set_top(box_legend, _t - (_t - _b) / (h - l) * (h - c))
        box.set_bgcolor(box_legend, bg_color)

        border_color = c >= c1 ? color.teal: color.red
        if not render or (o == c and h == l) or (c == c1)
            border_color := color.gray

        box.set_border_color(box_legend, border_color)        
        box.set_border_color(box_legend_border, border_color)        

    for i = 0 to array.size(arrows) - 4 by 4
        src_idx = array.get(arrows, i)
        tgt_idx = array.get(arrows, i + 2)
        src_chg = array.get(changes, src_idx)
        tgt_chg = array.get(changes, tgt_idx)

        lin = array.get(lines, i / 4)
        usdpair = str.startswith(syminfo.ticker(array.get(symbols, tgt_idx)), "USD")

        if src_idx == 0 and tgt_idx % 3 == 0 and usdpair and render
            line.set_style(lin, tgt_chg < 0 ? line.style_arrow_right : line.style_arrow_left)
        else if src_idx == 0 and tgt_idx % 3 == 0 and render
            line.set_style(lin, tgt_chg > 0 ? line.style_arrow_right : line.style_arrow_left)
        else if src_idx % 3 == 0 and tgt_idx % 3 == 1 and render
            line.set_style(lin, tgt_chg < 0 ? line.style_arrow_right : line.style_arrow_left)    
        else if src_idx % 3 == 0 and tgt_idx % 3 == 2 and render
            line.set_style(lin, tgt_chg > 0 ? line.style_arrow_right : line.style_arrow_left)    

        line.set_color(lin, color.aqua)
        if(src_idx % 3 == 0 and tgt_chg == 0 and render)
            line.set_style(lin, line.style_dashed)    
            line.set_color(lin, color.gray)                            
        
// if barstate.isfirst
//     for i = 0 to 20
//         line.new(last_bar_index - i * cell_width, 1, last_bar_index - i * cell_width, 0, extend=extend.both, color=color.yellow)
//         line.new(0, i * cell_width, 1, i * cell_width, extend=extend.both, color=color.yellow)