Altcoin Analyzer [Lysergik]
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © lysergik

//@version=5
indicator("Altcoin Analyzer [Lysergik]", shorttitle='AltAnal', precision=8)

// ------------
// Inputs
int f_length = input.int(21, 'Fast MA Length', minval=2, group='Configs')
int s_length = input.int(50, 'Slow MA Length', minval=2, group='Configs')

bool use_fma = input.bool(true, 'Show Fast Moving Average', group='Settings')
bool use_sma = input.bool(true, 'Show Slow Moving Average', group='Settings')
bool predict_ema = input.bool(true, 'Show MA Prediction', group='Settings')

bool ema = input.bool(true, 'Use EMA instead of SMA', group='Settings')
bool use_candles = input.bool(true, 'Use Candles', group='Settings')
bool heikin = input.bool(true, 'Heikin Candles', group='Settings')

bool dark_mode = input.bool(true, 'Dark Mode', group='Swag')
bool mini_display = input.bool(false, 'Mini-Display', 'Show less information in the table', group="Swag")
bool col = input.bool(true, 'Color MAs to Reflect the Trend', group='Swag')

// ------------
// Data
float btc_open = request.security("INDEX:BTCUSD",
     timeframe.period, open, barmerge.gaps_off, barmerge.lookahead_off)

float btc_high = request.security("INDEX:BTCUSD",
     timeframe.period, high, barmerge.gaps_off, barmerge.lookahead_off)

float btc_low = request.security("INDEX:BTCUSD",
     timeframe.period, low, barmerge.gaps_off, barmerge.lookahead_off)

float btc_close = request.security("INDEX:BTCUSD",
     timeframe.period, close, barmerge.gaps_off, barmerge.lookahead_off)

float o = open/btc_open
float h = high/btc_high
float l = low/btc_low
float c = close/btc_close

float btcusd_change = ( ta.highest(btc_high, 2) ) - ( ta.lowest(btc_low, 2) )
float altusd_change = ( ta.highest(high, 2) ) - ( ta.lowest(low, 2) )

color neutral_col = dark_mode ? color.white : color.black
color back_col = dark_mode ? color.black : color.white

float fMA = 0.00
float sMA = 0.00
string predict_type = "EMA"
bool colInvert = true

string id = syminfo.basecurrency

// ------------
// Pure Functions
ma(_type, _src, _len) =>
    if _type == "EMA"
        ta.ema(_src, _len)
    else if _type == "SMA"
        ta.sma(_src, _len)

ma_prediction(_type, _src, _period, _offset) => (ma(_type, _src, _period - _offset) * (_period - _offset) + _src * _offset) / _period

isBelow(_v2, _v1) =>
    out = false
    if _v1 > _v2
        out := true

volatility(_change, _length, _close) =>
    lowest_roc = ta.lowest(_change, _length)
    highest_roc = ta.highest(_change, _length)
    
    volatility = ta.ema((highest_roc - lowest_roc), 2)
    percent = volatility/_close*100

// ------------
// Math & Logic
btc_volatility = volatility(btcusd_change, 21, btc_close)
alt_volatility = volatility(altusd_change, 21, close)
relative_volatility = alt_volatility - btc_volatility

src(_src)  => 
    Close  = not heikin ? c : math.avg(o, h, l, c)
    Open   = float(na)
    Open  := not heikin ? o  : na(Open[1]) ? (o + c) / 2 : (nz(Open[1]) + nz(Close[1])) / 2
    High   = not heikin ? h  : math.max(h, math.max(Open, Close))
    Low    = not heikin ? l   : math.min(l,  math.min(Open, Close))
    HL2    = not heikin ? math.avg(l, h)   : math.avg(High, Low) 
    HLC3   = not heikin ? math.avg(l, h, c)  : math.avg(High, Low, Close) 
    OHLC4  = not heikin ? math.avg(o, h, l, c) : math.avg(Open, High, Low, Close)
    Price  = _src == 'close' ? Close : _src == 'open' ? Open : _src == 'high' ? High : _src == 'low' ? Low : _src == 'hl2' ? HL2 : _src == 'hlc3' ? HLC3 : OHLC4  
    // PineCoders method for aligning Pine prices with chart instrument prices
    //Source = math.round(Price / syminfo.mintick) * syminfo.mintick

if id == ''
    id := 'invalid'

if ema
    fMA := ta.ema(c, f_length)
    sMA := ta.ema(c, s_length)
    predict_type := "EMA"
else
    fMA := ta.sma(c, f_length)
    sMA := ta.sma(c, s_length)
    predict_type := "SMA"

longemapredictor_1 = ma_prediction(predict_type, c, s_length, 1)
longemapredictor_2 = ma_prediction(predict_type, c, s_length, 2)
longemapredictor_3 = ma_prediction(predict_type, c, s_length, 3)
longemapredictor_4 = ma_prediction(predict_type, c, s_length, 4)
longemapredictor_5 = ma_prediction(predict_type, c, s_length, 5)

shortemapredictor_1 = ma_prediction(predict_type, c, f_length, 1)
shortemapredictor_2 = ma_prediction(predict_type, c, f_length, 2)
shortemapredictor_3 = ma_prediction(predict_type, c, f_length, 3)
shortemapredictor_4 = ma_prediction(predict_type, c, f_length, 4)
shortemapredictor_5 = ma_prediction(predict_type, c, f_length, 5)

isBullish = c <= fMA ? true : false
slowIsBelow = sMA < fMA ? true : false

color fastCol =
     not colInvert ? (isBullish and col ? color.rgb(0,255,0,70) : not isBullish and col ? color.rgb(255,0,0,70) : na) :
     (isBullish and col ? color.rgb(255,0,0,70) : not isBullish and col ? color.rgb(0,255,0,70) : na)
     
color slowCol =
     not colInvert ? (slowIsBelow and col ? color.new(color.purple,75) : not slowIsBelow and col ? color.new(color.aqua,75) : na) :
     (slowIsBelow and col ? color.new(color.aqua,75) : not slowIsBelow and col ? color.new(color.purple,75) : na)

// ------------
// Front-End
f_plot = plot(use_fma ? fMA : na, 'Fast Moving Average', color=col ? color.black : color.blue, linewidth=2)
s_plot = plot(use_sma ? sMA : na, 'Slow Moving Average', color=color.white, linewidth=2)

main = plot(c, 'Price Line', color=use_candles ? na : neutral_col, trackprice=true)

fill(main, f_plot, color=fastCol)
fill(f_plot, s_plot, color=slowCol)

barColor = not use_candles ? na : src('close') >= src('open') ? color.teal : color.purple
plotcandle(src('open'), src('high'), src('low'), src('close'),
     'Candles', color=barColor, wickcolor=not use_candles ? na : color.new(neutral_col, 50), bordercolor=na)

plot(predict_ema and use_sma ? longemapredictor_1 : na, color=isBelow(shortemapredictor_1, longemapredictor_1) ? color.white : color.gray,
     linewidth=2, style=plot.style_cross, offset=1, show_last=1, editable=false)
plot(predict_ema and use_sma ? longemapredictor_2 : na, color=isBelow(shortemapredictor_2, longemapredictor_2) ? color.white : color.gray,
     linewidth=2, style=plot.style_cross, offset=2, show_last=1, editable=false)
plot(predict_ema and use_sma ? longemapredictor_3 : na, color=isBelow(shortemapredictor_3, longemapredictor_3) ? color.white : color.gray,
     linewidth=2, style=plot.style_cross, offset=3, show_last=1, editable=false)
plot(predict_ema and use_sma ? longemapredictor_4 : na, color=isBelow(shortemapredictor_4, longemapredictor_4) ? color.white : color.gray,
     linewidth=2, style=plot.style_cross, offset=4, show_last=1, editable=false)
plot(predict_ema and use_sma ? longemapredictor_5 : na, color=isBelow(shortemapredictor_5, longemapredictor_5) ? color.white : color.gray,
     linewidth=2, style=plot.style_cross, offset=5, show_last=1, editable=false)

plot(predict_ema and use_fma ? shortemapredictor_1 : na, color=isBelow(shortemapredictor_1, longemapredictor_1) ? color.aqua : color.purple,
     linewidth=2, style=plot.style_cross, offset=1, show_last=1, editable=false)
plot(predict_ema and use_fma ? shortemapredictor_2 : na, color=isBelow(shortemapredictor_2, longemapredictor_2) ? color.aqua : color.purple,
     linewidth=2, style=plot.style_cross, offset=2, show_last=1, editable=false)
plot(predict_ema and use_fma ? shortemapredictor_3 : na, color=isBelow(shortemapredictor_3, longemapredictor_3) ? color.aqua : color.purple,
     linewidth=2, style=plot.style_cross, offset=3, show_last=1, editable=false)
plot(predict_ema and use_fma ? shortemapredictor_4 : na, color=isBelow(shortemapredictor_4, longemapredictor_4) ? color.aqua : color.purple,
     linewidth=2, style=plot.style_cross, offset=4, show_last=1, editable=false)
plot(predict_ema and use_fma ? shortemapredictor_5 : na, color=isBelow(shortemapredictor_5, longemapredictor_5) ? color.aqua : color.purple,
     linewidth=2, style=plot.style_cross, offset=5, show_last=1, editable=false)

var line realLine = na
varip lineVis = false

if use_candles and
     ((lineVis == false and not barstate.isconfirmed) or
     (barstate.isrealtime and barstate.islast and not barstate.isconfirmed))
    
    line.delete(id=realLine)
    realLine := line.new(x1=bar_index[1], y1=c, x2=bar_index, y2=c, width=1, extend=extend.both)
    line.set_color(id=realLine, color=c > o ? color.purple : c < o ? color.teal : neutral_col)
    line.set_style(id=realLine, style=line.style_dashed)
    
if barstate.isconfirmed
    line.delete(id=realLine)
    lineVis := false

var table disp = table.new(position.bottom_right, 710, 2)

if barstate.islast
    if not mini_display
        table.cell(disp, 0, 0, str.format('{0}/BTC', id), text_color=neutral_col, text_size=size.auto, bgcolor=back_col)
        table.cell(disp, 1, 0, str.tostring(c, '0.00000000'), text_color=neutral_col, text_size=size.auto, bgcolor=back_col)
        
        table.cell(disp, 0, 1, str.format('{0} - BTC Volatility', id), text_color=neutral_col, text_size=size.auto, bgcolor=back_col)
        table.cell(disp, 1, 1, str.format('{0}%', math.round(relative_volatility, 2)), text_color=neutral_col,
             text_size=size.auto, bgcolor=back_col)
    
    else if mini_display
        table.cell(disp, 0, 0, str.tostring(c, '0.00000000'), text_color=neutral_col, text_size=size.auto, bgcolor=back_col)
