Stablecoin Dominance
// 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
//    ______   __              __       __                            __                 _______                        __                                              
//   /      \ /  |            /  |     /  |                          /  |               /       \                      /  |                                             
//  /$$$$$$  _$$ |_    ______ $$ |____ $$ | ______   _______  ______ $$/ _______        $$$$$$$  | ______  _____  ____ $$/ _______   ______  _______   _______  ______  
//  $$ \__$$/ $$   |  /      \$$      \$$ |/      \ /       |/      \/  /       \       $$ |  $$ |/      \/     \/    \/  /       \ /      \/       \ /       |/      \ 
//  $$      $$$$$$/   $$$$$$  $$$$$$$  $$ /$$$$$$  /$$$$$$$//$$$$$$  $$ $$$$$$$  |      $$ |  $$ /$$$$$$  $$$$$$ $$$$  $$ $$$$$$$  |$$$$$$  $$$$$$$  /$$$$$$$//$$$$$$  |
//   $$$$$$  |$$ | __ /    $$ $$ |  $$ $$ $$    $$ $$ |     $$ |  $$ $$ $$ |  $$ |      $$ |  $$ $$ |  $$ $$ | $$ | $$ $$ $$ |  $$ |/    $$ $$ |  $$ $$ |     $$    $$ |
//  /  \__$$ |$$ |/  /$$$$$$$ $$ |__$$ $$ $$$$$$$$/$$ \_____$$ \__$$ $$ $$ |  $$ |      $$ |__$$ $$ \__$$ $$ | $$ | $$ $$ $$ |  $$ /$$$$$$$ $$ |  $$ $$ \_____$$$$$$$$/ 
//  $$    $$/ $$  $$/$$    $$ $$    $$/$$ $$       $$       $$    $$/$$ $$ |  $$ |      $$    $$/$$    $$/$$ | $$ | $$ $$ $$ |  $$ $$    $$ $$ |  $$ $$       $$       |
//   $$$$$$/   $$$$/  $$$$$$$/$$$$$$$/ $$/ $$$$$$$/ $$$$$$$/ $$$$$$/ $$/$$/   $$/       $$$$$$$/  $$$$$$/ $$/  $$/  $$/$$/$$/   $$/ $$$$$$$/$$/   $$/ $$$$$$$/ $$$$$$$/ 
//                                                                                                                                                                      

version=1.5
indicator(title="Aggregated Stablecoin Dominance",
     shorttitle="AggrStableDom", format=format.percent, precision=3)


//-- Data Sources --\\
usdt = request.security('USDT.D', timeframe.period, close)
usdc = request.security('USDC.D', timeframe.period, close)
busd = request.security('(BUSD_MARKETCAP*100)/CRYPTOCAP:TOTAL', timeframe.period, close)
dai = request.security('DAI.D', timeframe.period, close)

usdt_o = request.security('USDT.D', timeframe.period, open)
usdc_o = request.security('USDC.D', timeframe.period, open)
busd_o = request.security('(BUSD_MARKETCAP*100)/CRYPTOCAP:TOTAL', timeframe.period, open)
dai_o = request.security('DAI.D', timeframe.period, open)

usdt_h = request.security('USDT.D', timeframe.period, high)
usdc_h = request.security('USDC.D', timeframe.period, high)
busd_h = request.security('(BUSD_MARKETCAP*100)/CRYPTOCAP:TOTAL', timeframe.period, high)
dai_h = request.security('DAI.D', timeframe.period, high)

usdt_l = request.security('USDT.D', timeframe.period, low)
usdc_l = request.security('USDC.D', timeframe.period, low)
busd_l = request.security('(BUSD_MARKETCAP*100)/CRYPTOCAP:TOTAL', timeframe.period, low)
dai_l = request.security('DAI.D', timeframe.period, low)


//-- User Inputs --\\
include_usdt = input.bool(true, 'Include USDT', group='Total Dominance')
include_usdc = input.bool(true, 'Include USDC', group='Total Dominance')
include_busd = input.bool(true, 'Include BUSD', group='Total Dominance')
include_dai = input.bool(true, 'Include DAI', group='Total Dominance')

darkMode = input.bool(true, 'Dark Mode', group='Swag')
miniTable = input.bool(false, 'Mini-Table', group='Swag')
col = input.bool(true, 'MA Color Fill', group='Swag')
colInvert = input.bool(false, 'Invert MA Coloring', group='Swag')
use_candles = input.bool(false, 'Show Candles', group='Swag')
heikin = input.bool(true, 'Heikin Candles', group='Swag')

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

f_length = input.int(21, 'Fast MA Length', minval=2, group='Moving Averages')
s_length = input.int(50, 'Slow MA Length', minval=2, group='Moving Averages')
ema = input.bool(true, 'Use EMA instead of SMA', group='Moving Averages')

bb = input.bool(false, 'Use Bollinger Bands', group='Bollinger Bands')
length = input.int(21, minval=1, group='Bollinger Bands')
mult = input.float(2.618, minval=0.001, maxval=50, group='Bollinger Bands')

// Base Colors
text_col = darkMode ? color.white : color.black
back_col = darkMode ? color.black : color.white
faded_col = darkMode ? color.gray : color.rgb(73, 75, 82)

//-- Pure Functions --\\
allFalse(_array) =>
    out = true
    for _bool in _array
        if _bool
            out := false

isBelow(_v1, _v2) =>
    out =_v1 < _v2

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

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


//-- Math --\\
include = array.new_bool(4,na)
array.insert(include, 0, include_usdt)
array.insert(include, 1, include_usdc)
array.insert(include, 2, include_busd)
array.insert(include, 3, include_dai)

totalDom = 0.00
totalDom_o = 0.00
totalDom_h = 0.00
totalDom_l = 0.00

i=0
for boolean in include
    if boolean
        switch i
            0 =>
                totalDom += usdt
                totalDom_o += usdt_o
                totalDom_h += usdt_h
                totalDom_l += usdt_l
            1 =>
                totalDom += usdc
                totalDom_o += usdc_o
                totalDom_h += usdc_h
                totalDom_l += usdc_l
            2 =>
                totalDom += busd
                totalDom_o += busd_o
                totalDom_h += busd_h
                totalDom_l += busd_l
            3 =>
                totalDom += dai
                totalDom_o += dai_o
                totalDom_h += dai_h
                totalDom_l += dai_l
    i += 1

// Candles
src(_src)  => 
    Close  = not heikin ? totalDom : math.avg(totalDom_o, totalDom_h, totalDom_l, totalDom)
    Open   = float(na)
    Open  := not heikin ? totalDom_o  : na(Open[1]) ? (totalDom_o + totalDom) / 2 : (nz(Open[1]) + nz(Close[1])) / 2
    High   = not heikin ? totalDom_h  : math.max(totalDom_h, math.max(Open, Close))
    Low    = not heikin ? totalDom_l   : math.min(totalDom_l,  math.min(Open, Close))
    HL2    = not heikin ? math.avg(totalDom_l, totalDom_h)   : math.avg(High, Low) 
    HLC3   = not heikin ? math.avg(totalDom_l, totalDom_h, totalDom)  : math.avg(High, Low, Close) 
    OHLC4  = not heikin ? math.avg(totalDom_o, totalDom_h, totalDom_l, totalDom) : 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  
    Source = math.round(Price / syminfo.mintick) * syminfo.mintick  // PineCoders method for aligning Pine prices with chart instrument prices

// Bollinger-Bands
src = math.avg(totalDom_h,totalDom_l,totalDom)
basis = ta.vwma(src, length)
dev = mult * ta.stdev(src, length)
upper_1= basis + (0.236*dev)
upper_2= basis + (0.382*dev)
upper_3= basis + (0.5*dev)
upper_4= basis + (0.618*dev)
upper_5= basis + (0.768*dev)
upper_6= basis + (1*dev)
lower_1= basis - (0.236*dev)
lower_2= basis - (0.382*dev)
lower_3= basis - (0.5*dev)
lower_4= basis - (0.618*dev)
lower_5= basis - (0.768*dev)
lower_6= basis - (1*dev)

fMA = 0.00
sMA = 0.00
string predict_type = "EMA"

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

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

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


//-- Logic & Color-- \\
bool totalBull = ta.change(totalDom) > 0
bool usdtBull = ta.change(usdt) > 0
bool usdcBull = ta.change(usdc) > 0
bool busdBull = ta.change(busd) > 0
bool daiBull = ta.change(dai) > 0

bool slowIsBelow = sMA < fMA
bool isBullish = totalDom <= fMA

bool allDisabled = allFalse(include)
bool isProbCrypto = syminfo.basecurrency != ''

string total_disp = not allDisabled ? str.format('{0}%', totalDom) : '-'
string usdt_disp = include_usdt ? str.format('{0}%', usdt) : '-'
string usdc_disp = include_usdc ? str.format('{0}%', usdc) : '-'
string busd_disp = include_busd ? str.format('{0}%', busd) : '-'
string dai_disp = include_dai ? str.format('{0}%', dai) : '-'

color fastFill_col =
      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 slowFill_col =
     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)

if not isProbCrypto
    fastFill_col := color.gray
    slowFill_col := color.gray


//-- 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=text_col, linewidth=2)

dom_plot = plot(totalDom, 'Combined Dominance', color=use_candles ? na : text_col, trackprice=true)

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

fill(dom_plot, f_plot, color=fastFill_col)
fill(f_plot, s_plot, color=slowFill_col)

plot(bb ? basis : na, color=text_col, linewidth=2, style=plot.style_stepline)
p1 = plot(bb ? upper_1 : na, color=text_col, linewidth=1, title="0.236", display=display.none)
p2 = plot(bb ? upper_2 : na, color=color.aqua, linewidth=1, title="0.382", display=display.none)
p3 = plot(bb ? upper_3 : na, color=text_col, linewidth=1, title="0.5", display=display.none)
p4 = plot(bb ? upper_4 : na, color=color.orange, linewidth=1, title="0.618")
p5 = plot(bb ? upper_5 : na, color=text_col, linewidth=1, title="0.768", display=display.none)
p6 = plot(bb ? upper_6 : na, color=faded_col, linewidth=2, title="1")
p13 = plot(bb ? lower_1 : na, color=text_col, linewidth=1, title="0.236", display=display.none)
p14 = plot(bb ? lower_2 : na, color=color.aqua, linewidth=1, title="0.382", display=display.none)
p15 = plot(bb ? lower_3 : na, color=text_col, linewidth=1, title="0.5", display=display.none)
p16 = plot(bb ? lower_4 : na, color=color.orange, linewidth=1, title="0.618")
p17 = plot(bb ? lower_5 : na, color=text_col, linewidth=1, title="0.768", display=display.none)
p18 = plot(bb ? lower_6 : na, color=faded_col, linewidth=2, title="1")

plot(predict_ema and use_sma ? longemapredictor_1 : na, color=isBelow(shortemapredictor_1, longemapredictor_1) ? text_col : faded_col,
     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) ? text_col : faded_col,
     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) ? text_col : faded_col,
     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) ? text_col : faded_col,
     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) ? text_col : faded_col,
     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)

// Barstate-Triggered Tasks
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=totalDom, x2=bar_index, y2=totalDom, width=1, extend=extend.both)
    line.set_color(id=realLine, color=totalDom > totalDom_o ? color.purple : totalDom < totalDom_o ? color.teal : text_col)
    line.set_style(id=realLine, style=line.style_dashed)
    
if barstate.isconfirmed
    line.delete(id=realLine)
    lineVis := false

// Table & Warning Label
var label L1 = na
var table TA_Display = table.new(position.bottom_right, 14, 5)

if barstate.islast
    label.delete(L1)

    if not isProbCrypto
        L1 := label.new(bar_index, totalDom, '', color=color.yellow, style=label.style_label_lower_left, textcolor=color.black)
        label.set_text(L1, 'Accuracy Warning')
        label.set_tooltip(L1,
             'On markets that do not have 24h continuous data, historical data displayed on this indicator is likely inaccurate.')
    
    if allDisabled or miniTable
        table.cell(TA_Display, 0, 0, 'TOTAL', text_color=text_col, text_size=size.auto, bgcolor=back_col)
        table.cell(TA_Display, 1, 0, total_disp, text_color=totalBull ? color.red : color.green, text_size=size.auto, bgcolor=back_col)
    else
        table.cell(TA_Display, 0, 0, 'TOTAL', text_color=text_col, text_size=size.auto, bgcolor=back_col)
        table.cell(TA_Display, 1, 0, total_disp, text_color=totalBull ? color.red : color.green, text_size=size.auto, bgcolor=back_col)
        table.cell(TA_Display, 0, 1, 'USDT', text_color=text_col, text_size=size.auto, bgcolor=back_col)
        table.cell(TA_Display, 1, 1, usdt_disp, text_color=usdtBull ? color.red : color.green, text_size=size.auto, bgcolor=back_col)
        table.cell(TA_Display, 0, 2, 'USDC', text_color=text_col, text_size=size.auto, bgcolor=back_col)
        table.cell(TA_Display, 1, 2, usdc_disp, text_color=usdcBull ? color.red : color.green, text_size=size.auto, bgcolor=back_col)
        table.cell(TA_Display, 0, 3, 'BUSD', text_color=text_col, text_size=size.auto, bgcolor=back_col)
        table.cell(TA_Display, 1, 3, busd_disp, text_color=busdBull ? color.red : color.green, text_size=size.auto, bgcolor=back_col)
        table.cell(TA_Display, 0, 4, 'DAI', text_color=text_col, text_size=size.auto, bgcolor=back_col)
        table.cell(TA_Display, 1, 4, dai_disp, text_color=daiBull ? color.red : color.green, text_size=size.auto, bgcolor=back_col)
