Dynamo
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © sudoMode

//@version=5
//@release=0.0.3
indicator(title = 'Multi-Strength Indicator', shorttitle = 'Dynamo', max_bars_back = 100)


// --- constants ---
// supported indices
VOLUME        = 'Volume'
VOLUME_MA     = 'Volume MA'
RSI           = 'RSI'
RSI_MA        = 'RSI MA'
STOCHASTIC    = 'Stochastic'
STOCHASTIC_MA = 'Stochastic MA'
ADX           = 'ADX'
DMI_P         = 'DMI+'
DMI_N         = 'DMI-'


// --- user input ---
// volume
volume_toggle = input.bool(true, title = 'Show Volume', inline = '1', group = VOLUME)
volume_ma_toggle = input.bool(false, title = 'Show MA', inline = '2', group = VOLUME)
volume_ma_length = input.int(14, title = 'Length', inline = '2', group = VOLUME)

// rsi
rsi_toggle = input.bool(true, title = 'Show RSI', inline = '3', group = RSI)
rsi_length = input.int(14, title = 'Length', inline = '3', group = RSI)
rsi_ma_length = input.int(3, title = 'Smoothening', inline = '3', group = RSI)
rsi_ma_toggle= input.bool(false, title = 'Show MA', inline = '4', group = RSI)

// stochastic
stoch_toggle = input.bool(true, title = 'Show Stochastic', inline = '5', group = STOCHASTIC)
stoch_length = input.int(14, title = 'Length', inline = '5', group = STOCHASTIC)
stoch_ma_length = input.int(3, title = 'Smoothening', inline = '5', group = STOCHASTIC)
stoch_ma_toggle = input.bool(false, title = 'Show MA', inline = '6', group = STOCHASTIC)

// adx
adx_toggle = input.bool(true, title = 'Show ADX', inline = '7', group = ADX)
adx_length = input.int(14, title = 'Length', inline = '7', group = ADX)
adx_ma_length = input.int(14, title = 'Smoothening', inline = '7', group = ADX)
dmi_toggle = input.bool(false, title = 'Show DMI', inline = '8', group = ADX)


// --- utils ---
// base model
type Index
    float value = na
    color _color = na


// view control switches
show_everything = volume_toggle and volume_ma_toggle and
                  rsi_toggle and rsi_ma_toggle and
                  stoch_toggle and stoch_ma_toggle and
                  adx_toggle and dmi_toggle

mute_volume    = not show_everything and (rsi_ma_toggle or stoch_ma_toggle or dmi_toggle)
mute_volume_ma = rsi_ma_toggle or stoch_ma_toggle or dmi_toggle
show_volume    = volume_toggle or show_everything
show_volume_ma = (volume_toggle and volume_ma_toggle) and not(mute_volume_ma)

mute_rsi    = not show_everything and (volume_ma_toggle or stoch_ma_toggle or dmi_toggle)
mute_rsi_ma = volume_ma_toggle or stoch_ma_toggle or dmi_toggle
show_rsi    = rsi_toggle or show_everything
show_rsi_ma = (rsi_toggle and rsi_ma_toggle) and not(mute_rsi_ma)

mute_stoch    = not show_everything and (volume_ma_toggle or rsi_ma_toggle or dmi_toggle)
mute_stoch_ma = volume_ma_toggle or rsi_ma_toggle or dmi_toggle
show_stoch    = stoch_toggle or show_everything
show_stoch_ma = (stoch_toggle and stoch_ma_toggle) and not(mute_stoch_ma)

mute_adx = not show_everything and (volume_ma_toggle or rsi_ma_toggle or stoch_ma_toggle)
mute_dmi = volume_ma_toggle or rsi_ma_toggle or stoch_ma_toggle
show_adx = adx_toggle or show_everything
show_dmi = (adx_toggle and dmi_toggle) and not(mute_dmi)


// dynamic color generation
color_engine(component) =>
    switch component
        VOLUME        => show_volume ? color.new(color.purple, mute_volume ? 70 : 0) : na
        VOLUME_MA     => volume_ma_toggle ? color.new(color.lime, show_volume_ma ? 0 : mute_volume_ma ? 70 : 100) : na
        RSI           => show_rsi ? color.new(color.aqua, mute_rsi ? 70 : 0) : na
        RSI_MA        => rsi_ma_toggle ? color.new(color.blue, show_rsi_ma ? 0 : mute_rsi_ma ? 70 : 100) : na
        STOCHASTIC    => show_stoch ? color.new(color.gray, mute_stoch ? 70 : 40) : na
        STOCHASTIC_MA => stoch_ma_toggle ? color.new(color.yellow, show_stoch_ma ? 0 : mute_stoch_ma ? 70 : 100) : na
        ADX           => show_adx ? color.new(color.orange, mute_adx ? 70 : 0) : na
        DMI_P         => dmi_toggle ? color.new(color.green, show_dmi ? 0 : mute_dmi ? 70 : 100) : na
        DMI_N         => dmi_toggle ? color.new(color.red, show_dmi ? 0 : mute_dmi ? 70 : 100) : na


// return volume or volume ma
get_volume_component(component = 'Volume') =>
    min = ta.min(volume)
    max = ta.max(volume)
    _range  = max - min
    _volume = ((volume - min) / _range) * 100
    switch component
        VOLUME    => Index.new(_volume, color_engine(component))
        VOLUME_MA => Index.new(ta.sma(_volume, volume_ma_length), color_engine(component))


// return rsi or rsi ma
get_rsi_component(length, smoothening, _close, component = 'RSI') =>
    switch component
        RSI     => Index.new(ta.ema(ta.rsi(_close, length), smoothening), color_engine(component))
        RSI_MA  => Index.new(ta.sma(ta.ema(ta.rsi(_close, length), smoothening), length), color_engine(component))
    

// return stochastic or stochastic ma
get_stochastic_component(length, smoothening, _close, _high, _low, component = 'Stochastic') =>
    switch component
        STOCHASTIC    => Index.new(ta.sma(ta.stoch(_close, _high, _low, length), smoothening), color_engine(component))
        STOCHASTIC_MA => Index.new(ta.sma(ta.sma(ta.stoch(_close, _high, _low, length), smoothening), length), color_engine(component))


// return adx or dmi+/dmi-
get_adx_component(length, smoothening, component = 'ADX') =>
    [dmi_positive, dmi_negative, adx] = ta.dmi(length, smoothening)
    switch component
        ADX   => Index.new(adx, color_engine(component))
        DMI_P => Index.new(dmi_positive, color_engine(component))
        DMI_N => Index.new(dmi_negative, color_engine(component))


// each index has a dedicated worker
index_factory(index) =>
    switch index
        VOLUME        => get_volume_component(component = index)
        VOLUME_MA     => get_volume_component(component = index)
        RSI           => get_rsi_component(rsi_length, rsi_ma_length, close, component = index)
        RSI_MA        => get_rsi_component(rsi_length, rsi_ma_length, close, component = index)
        STOCHASTIC    => get_stochastic_component(stoch_length, stoch_ma_length, close, high, low, component = index)
        STOCHASTIC_MA => get_stochastic_component(stoch_length, stoch_ma_length, close, high, low, component = index)
        ADX           => get_adx_component(adx_length, adx_ma_length, component = index)
        DMI_P         => get_adx_component(adx_length, adx_ma_length, component = index)
        DMI_N         => get_adx_component(adx_length, adx_ma_length, component = index)
        =>               Index.new()  // generate an empty instance on invalid input


// --- driver ---
// --- plots ---
// draw background lines
__hline = color.rgb(66, 66, 66, 66)
top    = hline(75, title = 'Top Line', color = __hline, linewidth = 2)
bottom = hline(25, title = 'Bottom Line', color = __hline, linewidth = 2)
middle = hline(50, title = 'Middle Line', color = __hline)


// draw indices
_volume  = index_factory(VOLUME)
plot(_volume.value, title = VOLUME, color = _volume._color, style = plot.style_columns)

volume_ma  = index_factory(VOLUME_MA)
plot(volume_ma.value, title = VOLUME_MA, color = volume_ma._color)

rsi = index_factory(RSI)
plot(rsi.value, title = RSI, color = rsi._color, linewidth = 2)

rsi_ma = index_factory(RSI_MA)
plot(rsi_ma.value, title = RSI_MA, color = rsi_ma._color, linewidth = 1)

stoch   = index_factory(STOCHASTIC)
plot(stoch.value, title = STOCHASTIC, color = stoch._color, style = plot.style_histogram)

stoch_ma   = index_factory(STOCHASTIC_MA)
plot(stoch_ma.value, title = STOCHASTIC_MA, color = stoch_ma._color)

adx   = index_factory(ADX)
plot(adx.value, title = ADX, color = adx._color, linewidth = 2)

dmip   = index_factory(DMI_P)
plot(dmip.value, title = DMI_P, color = dmip._color)

dmin   = index_factory(DMI_N)
plot(dmin.value, title = DMI_N, color = dmin._color)


