SPY SPX ES Price Converter [Pt]
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © PtGambler

//@version=5
indicator("SPY SPX ES Price Converter [Pt]", shorttitle = 'SPY SPX ES Price[Pt]', overlay = true, max_labels_count = 500, max_lines_count = 500)

group_table = 'Price Table'

// inputs
spy     = input.bool(true, 'SPY', inline = 'spy')
multi1  = input.float(1, '| Multiplier',minval = 0.1, step = 0.5, inline = 'spy')
col1    = input.color(color.fuchsia, '', inline = 'spy')
spybase = input.int(1, 'Base', minval = 1, inline = 'spy' )
offset1 = input.int(50, '    └ Offset', step = 10, inline = 'spy2')
line_style1 = input.string('Dashed', '| Line style', options = ['Solid', 'Dashed', 'Dotted'], inline = 'spy2')

spx     = input.bool(false, 'SPX', inline = 'spx')
multi2  = input.float(10, '| Multiplier', minval = 0.1, step = 5, inline = 'spx')
col2    = input.color(color.yellow, '', inline = 'spx')
offset2 = input.int(100, '    └ Offset', step = 10, inline = 'spx2')
line_style2 = input.string('Dashed', '| Line style', options = ['Solid', 'Dashed', 'Dotted'], inline = 'spx2')

es      = input.bool(false, 'ES', inline = 'es')
multi3  = input.float(10, '| Multiplier', minval = 0.25, step = 5, inline = 'es')
col3    = input.color(color.orange, '', inline = 'es')
offset3 = input.int(150, '    └ Offset', step = 10, inline = 'es2')
line_style3 = input.string('Dashed', '| Line style', options = ['Solid', 'Dashed', 'Dotted'], inline = 'es2')

spxusd  = input.bool(false, '', inline = 'spxusd')
ticker_input = input.symbol('SPX500USD', '', inline = 'spxusd')
multi4  = input.float(10, '| Multiplier', minval = 0.1, step = 5, inline = 'spxusd')
col4    = input.color(color.aqua, '', inline = 'spxusd')
offset4 = input.int(200, '    └ Offset', step = 10, inline = 'spxusd2')
line_style4 = input.string('Dashed', '| Line style', options = ['Solid', 'Dashed', 'Dotted'], inline = 'spxusd2')

levels  = input.int(10, '# of levels +/-') * 2 + 1

in_text_size = input.string('Normal', 'Text size', options = ['Auto', 'Large', 'Normal', 'Small', 'Tiny'])

text_size = switch in_text_size
    'Auto'      => size.auto
    'Large'     => size.large
    'Normal'    => size.normal
    'Small'     => size.small
    'Tiny'      => size.tiny

show_last = input.bool(true, 'Show last closing price', inline = 'showlast')

show_table      = input.bool(true,"Show Prices", group= group_table, inline="Table")
tableLocation   = input.string(defval='Top right', options=['Top left', 'Top center', 'Top right', 'Bottom left', 'Bottom center', 'Bottom right'], title='| Location', group= group_table, inline="Table")
tablePosition   = tableLocation == 'Top left' ? position.top_left : tableLocation == 'Top center' ? position.top_center : tableLocation == 'Top right' ? position.top_right : tableLocation == 'Bottom left' ? position.bottom_left : tableLocation == 'Bottom center' ? position.bottom_center : position.bottom_right
table_font_size       = input.string('Normal', "    └ Font Size", options = ['Auto', 'Tiny', 'Small', 'Normal', 'Large'], group= group_table, inline = 'font')
show_chg        = input.bool(true, 'Show Price Change', group = group_table)

// linestyles
f_lineStyle(_styleStr) =>
    _out = switch _styleStr
        'Solid' => line.style_solid
        'Dashed' => line.style_dashed
        'Dotted' => line.style_dotted

line_style1 := f_lineStyle(line_style1)
line_style2 := f_lineStyle(line_style2)
line_style3 := f_lineStyle(line_style3)
line_style4 := f_lineStyle(line_style4)

// Variables and data 
t1 = time(timeframe.period, "0930-1600:23456", "America/New_York")

ticker = syminfo.ticker

day = dayofweek

SPY_price  = request.security('SPY', timeframe.period, math.round_to_mintick(close))
SPX_price = request.security('SPX', timeframe.period, math.round_to_mintick(close))
[ES_price, ES_last_close]    = request.security('ES1!', timeframe.period, [math.round_to_mintick(close), ta.valuewhen(t1[1] and not t1,open,0)])
ticker_price = request.security(ticker_input, timeframe.period, math.round_to_mintick(close))
ticker_last_close = request.security(ticker_input, '1D', math.round_to_mintick(close[1]))

var spy_levels   = array.new_line()
var spx_levels   = array.new_line()
var es_levels    = array.new_line()
var spxusd_levels = array.new_line()
var spy_lbls        = array.new_label()
var spx_lbls        = array.new_label()
var es_lbls         = array.new_label()
var spxusd_lbls     = array.new_label()

// closest base price
SPYp0   = math.floor(SPY_price/spybase)*spybase
SPXp0   = math.floor(SPX_price/10)*10
ESp0    = math.floor(ES_price/10)*10
SPXUSDp0 = math.floor(ticker_price)

// closing price during regular hours
close_price = close
regular_close = ta.valuewhen(t1, close, 0)

// Price change / change %
ES_chg = ES_price - ES_last_close
ES_chg_pct = ES_chg / ES_last_close * 100

ticker_chg = ticker_price - ticker_last_close
ticker_chg_pct = ticker_chg / ticker_last_close * 100

// Functions
f_setLevels(_alt, _lineArray, _lblArray, _basePrice, _altPrice, _multiplier, _offset, _color, _linestyle) =>
    _price = close_price
    if _alt == 'SPY' or _alt == 'SPX'
        _price := regular_close
    base =  _basePrice * (_price / _altPrice)
    array.push(_lineArray, line.new(bar_index, base, bar_index + 1, base, xloc.bar_index, extend.both, _color, _linestyle, 1))
    array.push(_lblArray, label.new(bar_index+_offset, base, _alt + ' ' + str.tostring(_basePrice), xloc = xloc.bar_index, color = color.new(chart.bg_color,50), style = label.style_label_left, textcolor = _color, size = text_size, textalign = text.align_left))

    for x = 1 to (levels-1)/2
        price = (_basePrice + x*_multiplier) * (_price / _altPrice)
        alt_price = (_basePrice + x*_multiplier)
        array.push(_lineArray, line.new(bar_index, price, bar_index + 1, price, xloc.bar_index, extend.both, _color, _linestyle, 1))
        array.push(_lblArray, label.new(bar_index+_offset, price, _alt + ' ' + str.tostring(alt_price), xloc = xloc.bar_index, color = color.new(chart.bg_color,50), style = label.style_label_left, textcolor = _color, size = text_size, textalign = text.align_left))
    for x = (levels-1)/2 + 1 to levels - 1
        y = (levels-1)/2 - x
        price = (_basePrice + y*_multiplier) * (_price / _altPrice)
        alt_price = (_basePrice + y*_multiplier)
        array.push(_lineArray, line.new(bar_index, price, bar_index + 1, price, xloc.bar_index, extend.both, _color, _linestyle, 1))
        array.push(_lblArray, label.new(bar_index+_offset, price, _alt + ' ' + str.tostring(alt_price), xloc = xloc.bar_index, color = color.new(chart.bg_color,50), style = label.style_label_left, textcolor = _color, size = text_size, textalign = text.align_left))


f_drawLabelX(_x, _y, _text, _xloc, _yloc, _color, _style, _textcolor, _size, _textalign, _tooltip) =>
    var id = label.new(_x, _y, _text, _xloc, _yloc, _color, _style, _textcolor, _size, _textalign, _tooltip)
    label.set_xy(id, _x, _y)
    label.set_text(id, _text)
    label.set_tooltip(id, _tooltip)
    label.set_textcolor(id, _textcolor)
    id

f_clearLines(_array) =>
    if array.size(_array) > 0
        for i = 0 to array.size(_array) - 1
            line.delete(array.shift(_array))

f_clearLabels(_array) =>
    if array.size(_array) > 0
        for i = 0 to array.size(_array) - 1
            label.delete(array.shift(_array))

// Last price display texts
text_spy = spy or ticker == 'SPY' ? 'SPY ' + str.tostring(SPY_price) + '\n' : ''
text_spx = spx or ticker == 'SPX' ? 'SPX ' + str.tostring(SPX_price) + '\n' : ''
text_es = es or ticker == 'ES1!' ? 'ES1! ' + str.tostring(ES_price) + '\n' : ''
text_spxusd = spxusd or ticker == ticker_input ? array.get(str.split(ticker_input, ':'), 1) + ' ' + str.tostring(ticker_price) + '\n' : ''

price_text = ''

if ticker == 'SPY'
    price_text := text_spy + text_spx + text_es + text_spxusd
else if ticker == 'SPX'
    price_text := text_spx + text_spy + text_es + text_spxusd
else if ticker == 'ES1!' or ticker == 'MES1!'
    price_text := text_es + text_spy + text_spx +  text_spxusd
else if ticker == ticker_input
    price_text := text_spxusd + text_spy + text_spy + text_es

// Main
if barstate.islast

    if spy
        f_clearLines(spy_levels)
        f_clearLabels(spy_lbls)
    if spx
        f_clearLines(spx_levels)
        f_clearLabels(spx_lbls)
    if es
        f_clearLines(es_levels)
        f_clearLabels(es_lbls)
    if spxusd
        f_clearLines(spxusd_levels)
        f_clearLabels(spxusd_lbls)

    if show_last
        f_drawLabelX(bar_index, close, price_text, xloc.bar_index, yloc.price, color.new(chart.bg_color,70), label.style_label_left, chart.fg_color, size.normal, text.align_left, '' )
    
    if spy
        f_setLevels('SPY', spy_levels, spy_lbls, SPYp0, SPY_price, multi1, offset1, col1, line_style1)
    if spx
        f_setLevels('SPX', spx_levels, spx_lbls, SPXp0, SPX_price, multi2, offset2, col2, line_style2)
    if es
        f_setLevels('ES1!', es_levels, es_lbls, ESp0, ES_price, multi3, offset3, col3 ,line_style3)
    if spxusd
        f_setLevels(array.get(str.split(ticker_input, ':'), 1), spxusd_levels, spxusd_lbls, SPXUSDp0, ticker_price, multi4, offset4, col4, line_style4)

// Table --------------------------------------------------------------------------------------------------------------

var displayTable = table.new(tablePosition, 4, 4, border_width=1)

f_fillCell(_row, _column,series string _cellText) =>
    switch table_font_size
        "Auto" => table.cell(displayTable, _column, _row, _cellText, bgcolor=color.new(chart.bg_color,50), text_color=color.white, text_halign = text.align_left, text_valign = text.align_center, text_size = size.auto)
        "Tiny" => table.cell(displayTable, _column, _row, _cellText, bgcolor=color.new(chart.bg_color,50), text_color=color.white, text_halign = text.align_left, text_valign = text.align_center, text_size = size.tiny)
        "Small" => table.cell(displayTable, _column, _row, _cellText, bgcolor=color.new(chart.bg_color,50), text_color=color.white, text_halign = text.align_left, text_valign = text.align_center, text_size = size.small)
        "Normal" => table.cell(displayTable, _column, _row, _cellText, bgcolor=color.new(chart.bg_color,50), text_color=color.white, text_halign = text.align_left, text_valign = text.align_center, text_size = size.normal)
        "Large" => table.cell(displayTable, _column, _row, _cellText, bgcolor=color.new(chart.bg_color,50), text_color=color.white, text_halign = text.align_left, text_valign = text.align_center, text_size = size.large)

if barstate.islast and show_table
    f_fillCell(0, 0, 'SPY')
    f_fillCell(1, 0, 'SPX')
    f_fillCell(2, 0, 'ES1!')
    f_fillCell(3, 0, array.get(str.split(ticker_input, ':'), 1))

    f_fillCell(0, 1, str.tostring(SPY_price))
    f_fillCell(1, 1, str.tostring(SPX_price))
    f_fillCell(2, 1, str.tostring(ES_price))
    f_fillCell(3, 1, str.tostring(ticker_price))

    if show_chg
        f_fillCell(0, 2, math.sign(ES_chg) >= 0 ? "+" + str.tostring(ES_chg_pct,'#.##') + "%" : str.tostring(ES_chg_pct,'#.##') + "%")
        table.cell_set_text_color(displayTable, 2, 0, math.sign(ES_chg) >= 0 ? color.green : color.red )
        table.merge_cells(displayTable,2, 0, 2, 2)

        f_fillCell(3, 2, math.sign(ticker_chg) >= 0 ? "+" + str.tostring(ticker_chg_pct,'#.##') + "%" : str.tostring(ticker_chg_pct,'#.##') + "%")
        table.cell_set_text_color(displayTable, 2, 3, math.sign(ticker_chg) >= 0 ? color.green : color.red )