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

//@version=5

//  -----------------------------------------------------------------------------
//
//  Authors:  @EltAlt
//  Revision: v1.00
//  Date:     28-Apr-2022
//
//  Description
//  =============================================================================
//  This indicator displays a ribbon of two individually configured Fast and Slow and Moving Averages for a fixed time frame. It also 
//  displays the last close price of the configured time frame, colored green when above the band, red below and blue when interacting.
//  A label shows the percentage distance of the current price from the band, (again red below, green above, blue interacting), when 
//  the price is within the band it will show the percentage distance from median of the band.
//
//  The Fast and Slow Moving Averages can be set to:
//  Simple Moving Average (SMA)
//  Exponential Moving Average (EMA)
//  Weighted Moving Average (WMA)
//  Volume-Weighted Moving Average (VWMA)
//  Hull Moving Average (HMA) 
//  Exponentially Weighted Moving Average (RMA) (SMMA)
//  Linear regression curve Moving Average (LSMA)
//  Double EMA (DEMA)
//  Double SMA (DSMA)
//  Double WMA (DWMA)
//  Double RMA (DRMA)
//  Triple EMA (TEMA)
//  Triple SMA (TSMA)
//  Triple WMA (TWMA)
//  Triple RMA (TRMA)
//  Symmetrically Weighted Moving Average (SWMA) ** length does not apply **
//  Arnaud Legoux Moving Average (ALMA)
//  Variable Index Dynamic Average (VIDYA)
//  Fractal Adaptive Moving Average (FRAMA)
//
//  I wrote this script after identifying some interesting moving average bands with my AMACD indicator and wanting to see them on the
//  price chart. As an example look at the interactions between ETHBUSD 4hr and the band of VIDYA 32 Open and VIDYA 39 Open. Or start 
//  from the good old BTC Bull market support band, Weekly EMA 21 and SMA 20 and see if you can get a better fit. I find the Double 
//  RMA 22 a better fast option than the standard EMA 21.
//
//
//  =============================================================================
//
//  I would gladly accept any suggestions to improve the script.
//  If you encounter any problems please share them with me.
//
//  Thanks to Smartcryptotrade for "MACD Alert [All MA in one] [Smart Crypto Trade (SCT)]" which gave me the initial starting point.
//
//  Changlog
//  =============================================================================
//
//  1.00 Initial release.
//
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒

indicator(title='Any Ribbon',
          shorttitle = 'Any Ribbon',
          overlay = true)


// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// ============ Inputs
timeframe = input.timeframe('W', "Timeframe")

FastType = input.string(title='Fast Moving Average Type', defval='EMA', options=['EMA', 'SMA', 'WMA', 'VWMA', 'HMA', 'RMA', 'LSMA', 'Double EMA', 'Double SMA', 'Double WMA', 'Double RMA', 'Triple EMA', 'Triple SMA', 'Triple WMA', 'Triple RMA', 'SWMA', 'ALMA', 'VIDYA', 'FRAMA'], group='Moving Averages')
FastSource = input(title='Fast Source', defval=close, inline='Fast', group='Moving Averages')
FastLength = input.int(title='Fast Length', minval=1, maxval=50, defval=21, inline='Fast', group='Moving Averages')

SlowType = input.string(title='Slow Moving Average Type', defval='SMA', options=['EMA', 'SMA', 'WMA', 'VWMA', 'HMA', 'RMA', 'LSMA', 'Double EMA', 'Double SMA', 'Double WMA', 'Double RMA', 'Triple EMA', 'Triple SMA', 'Triple WMA', 'Triple RMA', 'SWMA', 'ALMA', 'VIDYA', 'FRAMA'], group='Moving Averages')
SlowSource = input(title='Slow Source', defval=close, inline='Slow', group='Moving Averages')
SlowLength = input.int(title='Slow Length', minval=1, maxval=50, defval=20, inline='Slow', group='Moving Averages')

offset_alma = input(title='ALMA Offset', defval=0.85, inline='1', group='Alma')
sigma_alma = input.float(title='ALMA Sigma', defval=6, inline='1', group='Alma')

FC = input.int(1, minval=1, title='FRAMA lower shift limit (FC)', inline='1', group='Frama')
SC = input.int(198, minval=1, title='FRAMA upper shift limit (SC)', inline='1', group='Frama')

showBand = input.bool (true, "Show MA Band", inline = '1', group='Display Options')
showPrice = input.bool (true, "Show Close Price", inline = '1', group='Display Options')
showLabel = input.bool (true, "Show Label", inline = '1', group='Display Options')


// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// ============ Functions

getCMO(src, length) =>
    mom = ta.change(src)
    upSum = math.sum(math.max(mom, 0), length)
    downSum = math.sum(-math.min(mom, 0), length)
    out = (upSum - downSum) / (upSum + downSum)
    out

vidya(src, length) =>
    alpha = 2 / (length + 1)
    cmo = math.abs(getCMO(src, length))
    vidya = 0.0
    vidya := src * alpha * cmo + nz(vidya[1]) * (1 - alpha * cmo)
    vidya

frama(x, y, z, v) =>
    // x = source , y = length , z = FC , v = SC
    HL = (ta.highest(high, y) - ta.lowest(low, y)) / y
    HL1 = (ta.highest(high, y / 2) - ta.lowest(low, y / 2)) / (y / 2)
    HL2 = (ta.highest(high, y / 2)[y / 2] - ta.lowest(low, y / 2)[y / 2]) / (y / 2)
    D = (math.log(HL1 + HL2) - math.log(HL)) / math.log(2)
    dim = HL1 > 0 and HL2 > 0 and HL > 0 ? D : nz(D[1])
    w = math.log(2 / (v + 1))
    alpha = math.exp(w * (dim - 1))
    alpha1 = alpha > 1 ? 1 : alpha < 0.01 ? 0.01 : alpha
    oldN = (2 - alpha1) / alpha1
    newN = (v - z) * (oldN - 1) / (v - 1) + z
    newalpha = 2 / (newN + 1)
    newalpha1 = newalpha < 2 / (v + 1) ? 2 / (v + 1) : newalpha > 1 ? 1 : newalpha
    frama = 0.0
    frama := (1 - newalpha1) * nz(frama[1]) + newalpha1 * x
    frama

calcMA(_type, _src, _length) =>
    switch _type 
        'EMA'        => ta.ema(_src, _length)
        'SMA'        => ta.sma(_src, _length)
        'WMA'        => ta.wma(_src, _length)
        'VWMA'       => ta.vwma(_src, _length)
        'HMA'        => ta.hma(_src, _length)
        'RMA'        => ta.rma(_src, _length)
        'LSMA'       => ta.linreg(_src, _length, 0)
        'Double EMA' => 2 * ta.ema(_src, _length) - ta.ema(ta.ema(_src, _length), _length)
        'Double SMA' => 2 * ta.sma(_src, _length) - ta.sma(ta.sma(_src, _length), _length)
        'Double WMA' => 2 * ta.wma(_src, _length) - ta.wma(ta.wma(_src, _length), _length)
        'Double RMA' => 2 * ta.rma(_src, _length) - ta.rma(ta.rma(_src, _length), _length)
        'Triple EMA' => 3 * (ta.ema(_src, _length) - ta.ema(ta.ema(_src, _length), _length)) + ta.ema(ta.ema(ta.ema(_src, _length), _length), _length)
        'Triple SMA' => 3 * (ta.sma(_src, _length) - ta.sma(ta.sma(_src, _length), _length)) + ta.sma(ta.sma(ta.sma(_src, _length), _length), _length)
        'Triple WMA' => 3 * (ta.wma(_src, _length) - ta.wma(ta.wma(_src, _length), _length)) + ta.wma(ta.wma(ta.wma(_src, _length), _length), _length)
        'Triple RMA' => 3 * (ta.rma(_src, _length) - ta.rma(ta.rma(_src, _length), _length)) + ta.rma(ta.rma(ta.rma(_src, _length), _length), _length)
        'SWMA'       => ta.swma(_src)        // No Length for SWMA
        'ALMA'       => ta.alma(_src, _length, offset_alma, sigma_alma)
        'VIDYA'      => vidya(_src, _length)
        'FRAMA'      => frama(_src, _length, FC, SC)


// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// ============ Calculations
FastCalc = calcMA(FastType, FastSource, FastLength)
SlowCalc = calcMA(SlowType, SlowSource, SlowLength)

MA_fast = request.security(syminfo.tickerid, timeframe, FastCalc)
MA_slow = request.security(syminfo.tickerid, timeframe, SlowCalc)
TFclose = request.security(syminfo.tickerid, timeframe, close)
TFhigh  = request.security(syminfo.tickerid, timeframe, high)
TFlow   = request.security(syminfo.tickerid, timeframe, low)

float delta = na
float deltaPct = na

stateColor = color.blue
string state = ''
if low > math.max (MA_fast, MA_slow)
    delta := close - math.max (MA_fast, MA_slow)
    deltaPct := delta / math.max (MA_fast, MA_slow) * 100
    state := ' Above the \nMoving Average band'
    stateColor := color.lime
else
    if high < math.min (MA_fast, MA_slow)
        delta := math.min (MA_fast, MA_slow) - close
        deltaPct := delta / math.min (MA_fast, MA_slow) * 100
        state := ' Below the \nMoving Average band'
        stateColor := color.red
    else
        if close > math.avg (MA_fast, MA_slow)
            delta := close - math.avg (MA_fast, MA_slow)
            deltaPct := delta / math.avg (MA_fast, MA_slow) * 100
            state := ' Above the \nBand Median'
            stateColor := color.blue
        else 
            delta := math.avg (MA_fast, MA_slow) - close
            deltaPct := delta / math.avg (MA_fast, MA_slow) * 100
            state := ' Below the \nBand Median'
            stateColor := color.blue


// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// ============ Plots

plot(showPrice ? TFclose: na, title='Price', linewidth=2, color = stateColor)

fastPlot = plot(showBand ? MA_fast: na, color=color.green, title="Fast Moving Average")
slowPlot = plot(showBand ? MA_slow: na, color=color.red, title="Slow Moving Average")
fill(fastPlot, slowPlot, color=color.new(color.orange, 75), fillgaps=true)

string stateText = "Price is currently \n" + str.tostring (deltaPct, format=format.percent) + state

currentState = showLabel ? label.new ( time +2, close, stateText, xloc.bar_time, yloc.price, stateColor, label.style_label_left, color.white, textalign = text.align_left ) : na
label.delete ( currentState[1])
