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

//@version=5

// @description This library contains all majority important moving averages' funtions with int series support.

library("Moving_Averages")

// @function            Exponential Moving Average (EMA)
// @param src           Source
// @param len           Period
// @returns             Exponential Moving Average with Series Int Support (EMA)
export ema(float src, int len) =>        // ─── Arnaud Legoux Moving Average (ALMA) 
    float result = 0.0
    result := (src - nz(result[1])) * (2.0 / (len + 1)) + nz(result[1])
    result


// @function            Arnaud Legoux Moving Average (ALMA) 
// @param src           Source
// @param len           Period
// @param a_offset      Arnaud Legoux offset
// @param a_sigma       Arnaud Legoux sigma
// @returns             Arnaud Legoux Moving Average (ALMA) 
export alma(float src, int len, float a_offset, float a_sigma) =>        // ─── Arnaud Legoux Moving Average (ALMA) 
    var float result = 0.0
    result := ta.alma(src, len, a_offset, a_sigma)
    result
    
// @function            Coefficient of Variation Weighted Exponential Moving Average (COVWEMA)
// @param src           Source
// @param len           Period
// @returns             Coefficient of Variation Weighted Exponential Moving Average (COVWEMA)
export covwema(float src, int len) =>       // ─── Coefficient of Variation Weighted Exponential Moving Average (COVWEMA)
    var float result = 0.0
    _dev = ta.stdev(src, len)
    _ema = ema(src, len)
    _cov = _dev / _ema
    _cWg = src * _cov
    result := math.sum(_cWg, len) / math.sum(_cov, len)
    result
    
    
// @function            Coefficient of Variation Weighted Moving Average (COVWMA) 
// @param src           Source
// @param len           Period
// @returns             Coefficient of Variation Weighted Moving Average (COVWMA) 
export covwma(float src, int len) =>       //      Coefficient of Variation Weighted Moving Average (COVWMA) 
    var float result = 0.0
    _dev = ta.stdev(src, len)
    _sma = ta.sma(src, len)
    _cov = _dev / _sma
    _cWg = src * _cov
    result := math.sum(_cWg, len) / math.sum(_cov, len)
    result
    
// @function            DEMA - Double Exponential Moving Average
// @param src           Source
// @param len           Period
// @returns             DEMA - Double Exponential Moving Average
export dema(float src, int len) =>       //      DEMA - Double Exponential Moving Average
    var float result = 0.0
    e = ema(src, len)
    result := 2 * e - ema(e, len)
    result
  
//    EDSMA   Pre-requisits functions
get2PoleSSF(src, length) =>
    PI = 2 * math.asin(1)
    arg = math.sqrt(2) * PI / length
    a1 = math.exp(-arg)
    b1 = 2 * a1 * math.cos(arg)
    c2 = b1
    c3 = -math.pow(a1, 2)
    c1 = 1 - c2 - c3

    ssf = 0.0
    ssf := c1 * src + c2 * nz(ssf[1]) + c3 * nz(ssf[2])
    ssf

get3PoleSSF(src, length) =>
    PI = 2 * math.asin(1)

    arg = PI / length
    a1 = math.exp(-arg)
    b1 = 2 * a1 * math.cos(1.738 * arg)
    c1 = math.pow(a1, 2)

    coef2 = b1 + c1
    coef3 = -(c1 + b1 * c1)
    coef4 = math.pow(c1, 2)
    coef1 = 1 - coef2 - coef3 - coef4

    ssf = 0.0
    ssf := coef1 * src + coef2 * nz(ssf[1]) + coef3 * nz(ssf[2]) + coef4 * nz(ssf[3])
    ssf


// @function            EDSMA - Ehlers Deviation Scaled Moving Average
// @param src           Source
// @param len           Period
// @param ssfLength     EDSMA - Super Smoother Filter Length
// @param ssfPoles      EDSMA - Super Smoother Filter Poles
// @returns             Ehlers Deviation Scaled Moving Average (EDSMA)
export edsma(float src, int len, int ssfLength, int ssfPoles) =>       //      EDSMA - Ehlers Deviation Scaled Moving Average
    var float result = 0.0
    zeros = src - nz(src[2])
    avgZeros = (zeros + zeros[1]) / 2
    poles2 = get2PoleSSF(avgZeros, ssfLength)
    poles3 = get3PoleSSF(avgZeros, ssfLength)
    
    // Ehlers Super Smoother Filter 
    ssf = ssfPoles == 2 ? poles2 : poles3

    // Rescale filter in terms of Standard Deviations
    stdev = ta.stdev(ssf, len)
    scaledFilter = stdev != 0 ? ssf / stdev : 0

    alpha = 5 * math.abs(scaledFilter) / len

    edsma = 0.0
    edsma := alpha * src + (1 - alpha) * nz(edsma[1])
    result := edsma
    result


// @function            Ehlrs Modified Fractal Adaptive Moving Average (EFRAMA) 
// @param src           Source
// @param len           Period
// @param FC            Lower Shift Limit for Ehlrs Modified Fractal Adaptive Moving Average
// @param SC            Upper Shift Limit for Ehlrs Modified Fractal Adaptive Moving Average
// @returns             Ehlrs Modified Fractal Adaptive Moving Average (EFRAMA) 
export eframa(float src, int len, int FC, int SC) =>   // Ehlrs Modified Fractal Adaptive Moving Average (EFRAMA) 
    var float result = 0.0
    len1 = len / 2
    e = 2.7182818284590452353602874713527
    w = math.log(2 / (SC + 1)) / math.log(e)  // Natural logarithm (ln(2/(SC+1))) workaround
    H1 = ta.highest(high, len1)
    L1 = ta.lowest(low, len1)
    N1 = (H1 - L1) / len1
    H2 = ta.highest(high, len1)[len1]
    L2 = ta.lowest(low, len1)[len1]
    N2 = (H2 - L2) / len1
    H3 = ta.highest(high, len)
    L3 = ta.lowest(low, len)
    N3 = (H3 - L3) / len
    dimen1 = (math.log(N1 + N2) - math.log(N3)) / math.log(2)
    dimen = N1 > 0 and N2 > 0 and N3 > 0 ? dimen1 : nz(dimen1[1])
    alpha1 = math.exp(w * (dimen - 1))
    oldalpha = alpha1 > 1 ? 1 : alpha1 < 0.01 ? 0.01 : alpha1
    oldN = (2 - oldalpha) / oldalpha
    N = (SC - FC) * (oldN - 1) / (SC - 1) + FC
    alpha_ = 2 / (N + 1)
    alpha = alpha_ < 2 / (SC + 1) ? 2 / (SC + 1) : alpha_ > 1 ? 1 : alpha_
    result := (1 - alpha) * nz(result[1]) + alpha * src
    result
    
    
// @function            EHMA - Exponential Hull Moving Average
// @param src           Source
// @param len           Period
// @returns             Exponential Hull Moving Average (EHMA)
export ehma(float src, int len) =>      // EHMA - Exponential Hull Moving Average
    var float result = 0.0
    result := ema(2 * ema(src, len / 2) - ema(src, len), math.round(math.sqrt(len)))
    result
    
    
// @function            Exponential Triangular Moving Average (ETMA)
// @param src           Source
// @param len           Period
// @returns             Exponential Triangular Moving Average (ETMA)
export etma(float src, int len) =>      // ─── Exponential Triangular Moving Average (ETMA)
    var float result = 0.0
    result := ema(ema(src, len), len)
    result
    
// @function            Fractal Adaptive Moving Average (FRAMA) 
// @param src           Source
// @param len           Period
// @returns             Fractal Adaptive Moving Average (FRAMA) 
export frama(float src, int len) =>    // ─── Fractal Adaptive Moving Average (FRAMA) 
    var float result = 0.0
    _coef = -4.6
    _len0 = len
    _len2 = len / 2
    _hl0 = (ta.highest(high, _len0) - ta.lowest(low, _len0)) / _len0
    _hl1 = (ta.highest(high, _len2) - ta.lowest(low, _len2)) / _len2
    _hl2 = (ta.highest(high, _len2)[_len2] - ta.lowest(low, _len2)[_len2]) / _len2
    _dim = _hl0 > 0 and _hl1 > 0 and _hl2 > 0 ? (math.log(_hl2 + _hl1) - math.log(_hl0)) / math.log(2) : 0
    _alpha = math.exp(_coef * (_dim - 1))
    _sc = _alpha < 0.01 ? 0.01 : _alpha > 1 ? 1 : _alpha
    var _frama = src
    _frama := bar_index <= _len0 + 1 ? src : src * _sc + nz(_frama[1]) * (1 - _sc)
    result := _frama
    result
    
    
// @function            HMA - Hull Moving Average
// @param src           Source
// @param len           Period
// @returns             Hull Moving Average (HMA)
export hma(float src, int len) =>    // HMA - Hull Moving Average
    var float result = 0.0
    result := ta.wma(2 * ta.wma(src, len / 2) - ta.wma(src, len), math.round(math.sqrt(len)))
    result
    

// @function            Jurik Moving Average - JMA
// @param src           Source
// @param len           Period
// @param jurik_phase   Jurik (JMA) Only - Phase
// @param jurik_power   Jurik (JMA) Only - Power
// @returns             Jurik Moving Average (JMA)
export jma(float src, int len, int jurik_phase, int jurik_power) =>    // Jurik Moving Average - JMA
    /// Copyright © 2018 Alex Orekhov (everget)
    /// Copyright © 2017 Jurik Research and Consulting.
    var float result = 0.0
    phaseRatio = jurik_phase < -100 ? 0.5 : jurik_phase > 100 ? 2.5 : jurik_phase / 100 + 1.5
    _beta = 0.45 * (len - 1) / (0.45 * (len - 1) + 2)
    _alpha = math.pow(_beta, jurik_power)
    jma = 0.0
    e0 = 0.0
    e0 := (1 - _alpha) * src + _alpha * nz(e0[1])
    e1 = 0.0
    e1 := (src - e0) * (1 - _beta) + _beta * nz(e1[1])
    e2 = 0.0
    e2 := (e0 + phaseRatio * e1 - nz(jma[1])) * math.pow(1 - _alpha, 2) + math.pow(_alpha, 2) * nz(e2[1])
    jma := e2 + nz(jma[1])
    result := jma
    result
    
    
// @function            Kaufman's Adaptive Moving Average (KAMA)
// @param src           Source
// @param len           Period
// @param k_fastLength  Number of periods for the fastest exponential moving average
// @param k_slowLength  Number of periods for the slowest exponential moving average
// @returns             Kaufman's Adaptive Moving Average (KAMA)
export kama(float src, int len, float k_fastLength, float k_slowLength) =>    // ─── Kaufman's Adaptive Moving Average (KAMA)
    var float result = 0.0    
    var float _constant1 = 2 / (k_fastLength + 1) - 2 / (k_slowLength + 1)
    var float _constant2 = 2 / (k_slowLength + 1)
    _efficiencyRatio = nz(math.abs(ta.change(src, len)) / math.sum(math.abs(ta.change(src)), len))
    _smoothingConstant = math.pow(math.abs(_efficiencyRatio * _constant1 + _constant2), 2)
    result := nz(result[1]) + _smoothingConstant * (src - nz(result[1]))
    result
    
    
// @function            Kijun v2
// @param _high         High value of bar
// @param _low          Low value of bar
// @param len           Period
// @param kidiv         Kijun MOD Divider
// @returns             Kijun v2
export kijun(float _high, float _low, int len, int kidiv) =>     //  Kijun v2
    var float result = 0.0     
    kijun = math.avg(ta.lowest(_high, len), ta.highest(_low, len))  //, (open + close)/2)
    conversionLine = math.avg(ta.lowest(_low, len / kidiv), ta.highest(_high, len / kidiv))
    delta = (kijun + conversionLine) / 2
    result := delta
    result
    

// @function            LSMA/LRC - Least Squares Moving Average / Linear Regression Curve
// @param src           Source
// @param len           Period
// @param offset        Offset
// @returns             Least Squares Moving Average (LSMA)/ Linear Regression Curve (LRC)
export lsma(float src, int len, int offset) =>    // LSMA/LRC - Least Squares Moving Average / Linear Regression Curve
    var float result = 0.0   
    result := ta.linreg(src, len, offset)
    result
    


// @function            MF - Modular Filter
// @param src           Source
// @param len           Period
// @param beta          Modular Filter, General Filter Only - Beta
// @param feedback      Modular Filter Only - Feedback
// @param z             Modular Filter Only - Feedback Weighting
// @returns             Modular Filter (MF)
export mf(float src, int len, float beta, bool feedback, float z) =>    // MF - Modular Filter
    var float result = 0.0      
    ts = 0.
    b = 0.
    c = 0.
    os = 0.
    //----
    alpha = 2 / (len + 1)
    a = feedback ? z * src + (1 - z) * nz(ts[1], src) : src
    //----
    b := a > alpha * a + (1 - alpha) * nz(b[1], a) ? a : alpha * a + (1 - alpha) * nz(b[1], a)
    c := a < alpha * a + (1 - alpha) * nz(c[1], a) ? a : alpha * a + (1 - alpha) * nz(c[1], a)
    os := a == b ? 1 : a == c ? 0 : os[1]
    //----
    upper = beta * b + (1 - beta) * c
    lower = beta * c + (1 - beta) * b
    ts := os * upper + (1 - os) * lower
    result := ts
    result
    
    
// @function            RMA - RSI Moving average
// @param src           Source
// @param len           Period
// @returns             RSI Moving average (RMA)
export rma(float src, int len) =>    //  RMA - RSI Moving average
    var float result = 0.0 
    alpha = 1 / len
    result := alpha * src + (1 - alpha) * nz(result[1])
    result
    
    
// @function            SMA - Simple Moving Average
// @param src           Source
// @param len           Period
// @returns             Simple Moving Average (SMA)
export sma(float src, int len) =>    // SMA - Simple Moving Average
    var float result = 0.0 
    result := ta.sma(src, len)
    result
    
// @function            Smoothed Moving Average (SMMA)
// @param src           Source
// @param len           Period
// @returns             Smoothed Moving Average (SMMA)
export smma(float src, int len) =>    //  Smoothed Moving Average (SMMA)
    var float result = 0.0 
    _sma = ta.sma(src, len)
    result := na(result[1]) ? _sma : (result[1] * (len - 1) + src) / len
    result
    
    
// @function            Simple Triangular Moving Average (STMA) 
// @param src           Source
// @param len           Period
// @returns             Simple Triangular Moving Average (STMA) 
export stma(float src, int len) =>    //  Simple Triangular Moving Average (STMA) 
    var float result = 0.0 
    result := ta.sma(ta.sma(src, math.ceil(len / 2)), math.floor(len / 2) + 1)
    result
    
    
// @function            TEMA - Triple Exponential Moving Average
// @param src           Source
// @param len           Period
// @returns             Triple Exponential Moving Average (TEMA)
export tema(float src, int len) =>    // TEMA - Triple Exponential Moving Average
    var float result = 0.0 
    e = ema(src, len)
    result := 3 * (e - ema(e, len)) + ema(ema(e, len), len)
    result
    
    
// @function            THMA - Triple Hull Moving Average
// @param src           Source
// @param len           Period
// @returns             Triple Hull Moving Average (THMA)
export thma(float src, int len) =>    // THMA - Triple Hull Moving Average
    var float result = 0.0
    result := ta.wma(ta.wma(src, len / 3) * 3 - ta.wma(src, len / 2) - ta.wma(src, len), len)
    result
    

// @function                            VAMA - Volatility Adjusted Moving Average
// @param src                           Source
// @param len                           Period
// @param volatility_lookback           Volatility lookback length
// @returns                             Volatility Adjusted Moving Average (VAMA)
export vama(float src, int len, int volatility_lookback) =>    // VAMA - Volatility Adjusted Moving Average
    var float result = 0.0    
    mid = ema(src, len)
    dev = src - mid
    vol_up = ta.highest(dev, volatility_lookback)
    vol_down = ta.lowest(dev, volatility_lookback)
    result := mid + math.avg(vol_up, vol_down)
    result
    

// @function            Variable Index Dynamic Average (VIDYA)
// @param src           Source
// @param len           Period
// @returns             Variable Index Dynamic Average (VIDYA)
export vidya(float src, int len) =>    // ─── Variable Index Dynamic Average (VIDYA)
    var float result = 0.0  
    _cmo = ta.cmo(src, len) / 100  //Chande Momentum Oscillator
    var _factor = 2 / (len + 1)
    result := src * _factor * math.abs(_cmo) + nz(result[1]) * (1 - _factor * math.abs(_cmo))
    result
    

// @function            Volume-Weighted Moving Average (VWMA)
// @param src           Source
// @param len           Period
// @returns             Volume-Weighted Moving Average (VWMA)
export vwma(float src, int len) =>    //    Volume-Weighted Moving Average (VWMA)
    var float result = 0.0      
    result := ta.vwma(src, len)
    result
    
    
// @function            WMA - Weighted Moving Average
// @param src           Source
// @param len           Period
// @returns             Weighted Moving Average (WMA)
export wma(float src, int len) =>    // WMA - Weighted Moving Average
    var float result = 0.0  
    result := ta.wma(src, len)
    result
    
    
// @function            Zero-Lag Exponential Moving Average (ZEMA)
// @param src           Source
// @param len           Period
// @returns             Zero-Lag Exponential Moving Average (ZEMA)
export zema(float src, int len) =>    // ─── Zero-Lag Exponential Moving Average (ZEMA)
    var float result = 0.0  
    var _l = (len + 1) / 2
    _srcL = src + nz(ta.mom(src, _l))
    result := ema(_srcL, len)
    result
    
    
// @function            Zero-Lag Simple Moving Average (ZSMA)
// @param src           Source
// @param len           Period
// @returns             Zero-Lag Simple Moving Average (ZSMA)
export zsma(float src, int len) =>    // ─── Zero-Lag Simple Moving Average (ZSMA)
    var float result = 0.0  
    var _l = (len + 1) / 2
    _srcL = src + nz(ta.mom(src, _l))
    result := ta.sma(_srcL, len)
    result
    
    
// @function            EVWMA - Elastic Volume Weighted Moving Average
// @param src           Source
// @param len           Period
// @returns             Elastic Volume Weighted Moving Average (EVWMA)
export evwma(float src, int len) =>    // EVWMA - Elastic Volume Weighted Moving Average
    // Copyright © LazyBear
    var float result = 0.0     
    nbfs = math.sum(volume, len)
    result := nz(result[1]) * (nbfs - volume) / nbfs + volume * src / nbfs
    result
    
    
// @function            Tillson T3
// @param src           Source
// @param len           Period
// @param a1_t3         Tillson T3 Volume Factor
// @returns             Tillson T3
export tt3(float src, int len, float a1_t3) =>    // Tillson T3
    var float result = 0.0      
    result := -a1_t3 * a1_t3 * a1_t3 * ema(ema(ema(ema(ema(ema(src, len), len), len), len), len), len) + (3 * a1_t3 * a1_t3 + 3 * a1_t3 * a1_t3 * a1_t3) * ema(ema(ema(ema(ema(src, len), len), len), len), len) + (-6 * a1_t3 * a1_t3 - 3 * a1_t3 - 3 * a1_t3 * a1_t3 * a1_t3) * ema(ema(ema(ema(src, len), len), len), len) + (1 + 3 * a1_t3 + a1_t3 * a1_t3 * a1_t3 + 3 * a1_t3 * a1_t3) * ema(ema(ema(src, len), len), len)
    result
    
    
// @function            GMA - Geometric Moving Average
// @param src           Source
// @param len           Period
// @returns             Geometric Moving Average (GMA)
export gma(float src, int len) =>    //  GMA - Geometric Moving Average
    var float result = 0.0      
    sum = src
    for i = 1 to len - 1 by 1
        sum *= src[i]
        sum
    result := math.pow(sum, 1 / len)
    result
    
    
// @function            WWMA - Welles Wilder Moving Average
// @param src           Source
// @param len           Period
// @returns             Welles Wilder Moving Average (WWMA)
export wwma(float src, int len) =>    //  WWMA - Welles Wilder Moving Average
    var float result = 0.0       
    result := (nz(result[1]) * (len - 1) + src) / len
    result
    
    
// @function            AMA - Adjusted Moving Average
// @param src           Source
// @param _high         High value of bar
// @param _low          Low value of bar
// @param len           Period
// @param ama_f_length  Fast EMA Length
// @param ama_s_length  Slow EMA Length
// @returns             Adjusted Moving Average (AMA)
export ama(float src, float _high, float _low, int len, int ama_f_length, int ama_s_length) =>    //  AMA - Adjusted Moving Average
    var float result = 0.0
    fastAlpha = 2 / (ama_f_length + 1)
    slowAlpha = 2 / (ama_s_length + 1)
    hh = ta.highest(_high, len + 1)
    ll = ta.lowest(_low, len + 1)
    mltp = hh - ll != 0 ? math.abs(2 * src - ll - hh) / (hh - ll) : 0
    ssc_ = mltp * (fastAlpha - slowAlpha) + slowAlpha
    result := nz(result[1]) + math.pow(ssc_, 2) * (src - nz(result[1]))
    result
    
    
// @function            Corrective Moving average (CMA)
// @param src           Source
// @param len           Period
// @returns             Corrective Moving average (CMA)
export cma(float src, int len) =>    //   Corrective Moving average (CMA)
    var float result = 0.0    
    sma_ = ta.sma(src, len)
    _cma = sma_
    v1 = ta.variance(src, len)
    v2 = math.pow(nz(_cma[1], _cma) - sma_, 2)
    v3 = v1 == 0 or v2 == 0 ? 1 : v2 / (v1 + v2)
    var tolerance = math.pow(10, -5)
    float err = 1
    // Gain Factor
    float kPrev = 1
    float k = 1
    for i = 0 to 5000 by 1
        if err > tolerance
            k := v3 * kPrev * (2 - kPrev)
            err := kPrev - k
            kPrev := k
            kPrev
    _cma := nz(_cma[1], src) + k * (sma_ - nz(_cma[1], src))
    result := _cma
    result
    
    
// @function            Geometric Mean Moving Average (GMMA)
// @param src           Source
// @param len           Period
// @returns             Geometric Mean Moving Average (GMMA)
export gmma(float src, int len) =>    //  Geometric Mean Moving Average (GMMA)
    var float result = 0.0       
    lmean = math.log(src)
    smean = math.sum(lmean, len)
    geoMA = math.exp(smean / len)
    result := geoMA
    result
    
    
// @function            Ehler's Adaptive Laguerre filter (EALF)
// @param src           Source
// @param len           Period
// @param LAPercLen_    Median Length 
// @param FPerc_        Median Percentage
// @returns             Ehler's Adaptive Laguerre filter (EALF)
export ealf(float src, int len, int LAPercLen_, simple float FPerc_) =>    // Ehler's Adaptive Laguerre filter
    var float result = 0.0      
    y = float(na)
    alpha = float(na)
    error = math.abs(src - nz(y[1]))
    le_ = ta.lowest(error, len)
    he_ = ta.highest(error, len)
    range_2 = he_ - le_
    perc = range_2 != 0 ? (error - le_) / range_2 : nz(alpha[1])
    alpha := ta.percentile_nearest_rank(perc, LAPercLen_, FPerc_)
    L0 = 0.0
    L0 := alpha * src + (1 - alpha) * nz(L0[1])
    L1 = 0.0
    L1 := -(1 - alpha) * L0 + nz(L0[1]) + (1 - alpha) * nz(L1[1])
    L2 = 0.0
    L2 := -(1 - alpha) * L1 + nz(L1[1]) + (1 - alpha) * nz(L2[1])
    L3 = 0.0
    L3 := -(1 - alpha) * L2 + nz(L2[1]) + (1 - alpha) * nz(L3[1])
    y := (L0 + 2 * L1 + 2 * L2 + L3) / 6
    result := y
    result
    

// @function            ELF - Ehler's Laguerre filter
// @param src           Source
// @param len           Period
// @param LAPercLen_    Median Length 
// @param FPerc_        Median Percentage
// @returns             Ehler's Laguerre Filter (ELF)
export elf(float src, int len) =>    //   ELF - Ehler's Laguerre filter
    var float result = 0.0     
    y = float(na)
    alpha = math.exp((1 - len) / 20)  // map length to alpha
    L0 = 0.0
    L0 := alpha * src + (1 - alpha) * nz(L0[1])
    L1 = 0.0
    L1 := -(1 - alpha) * L0 + nz(L0[1]) + (1 - alpha) * nz(L1[1])
    L2 = 0.0
    L2 := -(1 - alpha) * L1 + nz(L1[1]) + (1 - alpha) * nz(L2[1])
    L3 = 0.0
    L3 := -(1 - alpha) * L2 + nz(L2[1]) + (1 - alpha) * nz(L3[1])
    y := (L0 + 2 * L1 + 2 * L2 + L3) / 6
    result := y
    result
    
    
// @function            Exponentially Deviating Moving Average (MZ EDMA)
// @param src           Source
// @param len           Period
// @returns             Exponentially Deviating Moving Average (MZ EDMA)
export edma(float src, int len) =>    //   Exponentially Deviating Moving Average (MZ EDMA)
    var hexp = float(na)
    var lexp = float(na)
    var _hma = float(na)
    var _edma = float(na)
    float smoothness = 1.0  // Increasing smoothness will result in MA same as EMA
    h_len = int(len/1.5)    // Length to be used in final calculation of Hull Moving Average
    // Defining Exponentially Expanding Moving Line
    hexp := na(hexp[1]) ? src : src >= hexp[1] ? src : hexp[1] + (src - hexp[1]) * (smoothness / (len + 1))
    // Defining Exponentially Contracting Moving Line
    lexp := na(lexp[1]) ? hexp : src <= lexp[1] ? hexp : lexp[1] + (src - lexp[1]) * (smoothness / (len + 1))
    // Calculating Hull Moving Average of resulted Exponential Moving Line with 3/2 of total length
    _hma := ta.wma(2 * ta.wma(lexp, h_len / 2) - ta.wma(lexp, h_len), math.round(math.sqrt(h_len)))
    _edma := _hma   // EDMA will be equal to resulted smoothened Hull Moving Average
    _edma
    
    
// @function                PNR - percentile nearest rank
// @param src               Source
// @param len               Period
// @param rank_inter_Perc_  Rank and Interpolation Percentage
// @returns                 Percentile Nearest Rank (PNR)
export pnr(float src, int len, simple float rank_inter_Perc_) =>    //  PNR - percentile nearest rank. Calculates percentile using method of Nearest Rank.
    var float result = 0.0   
    result := ta.percentile_nearest_rank(src, len, rank_inter_Perc_)
    result
    
// @function                PLI - Percentile Linear Interpolation
// @param src               Source
// @param len               Period
// @param rank_inter_Perc_  Rank and Interpolation Percentage
// @returns                 Percentile Linear Interpolation (PLI)
export pli(float src, int len, simple float rank_inter_Perc_) =>    // PLI - Percentile Linear Interpolation. Calculates percentile using method of linear interpolation between the two nearest ranks.
    var float result = 0.0     
    result := ta.percentile_linear_interpolation(src, len, rank_inter_Perc_)
    result
    
    
// @function            Range EMA (REMA)
// @param src           Source
// @param len           Period
// @returns             Range EMA (REMA)
export rema(float src, int len) =>    // Range EMA (REMA)  
    var float result = 0.0  
    alpha = 2 / (1 + len)
    weight = high - low
    weight := weight == 0 ? syminfo.pointvalue : weight
    num = 0.0
    den = 0.0
    num := na(num[1]) ? weight * src : num[1] + alpha * (weight * src - num[1])
    den := na(den[1]) ? weight : den[1] + alpha * (weight - den[1])
    result := num / den
    result
    
    
// @function            Sine-Weighted Moving Average (SW-MA)
// @param src           Source
// @param len           Period
// @returns             Sine-Weighted Moving Average (SW-MA)
export sw_ma(float src, int len) =>    // Sine-Weighted Moving Average (SW-MA)
    var float result = 0.0
    PI_ = 2 * math.asin(1)
    sum = 0.0
    weightSum = 0.0
    for i = 0 to len - 1 by 1
        weight = math.sin((i + 1) * PI_ / (len + 1))
        sum += nz(src[i]) * weight
        weightSum += weight
        weightSum
    result := sum / weightSum
    result
    
    
// @function            Volume Weighted Average Price (VWAP)
// @param src           Source
// @param len           Period
// @returns             Volume Weighted Average Price (VWAP)
export vwap(float src) =>    //  Volume Weighted Average Price (VWAP)
    var float result = 0.0
    var float sumSrcVol = na
    var float sumVol = na
    var float sumSrcSrcVol = na
    isNewPeriod = ta.change(time('D'))
    sumSrcVol := isNewPeriod ? src * volume : src * volume + sumSrcVol[1]
    sumVol := isNewPeriod ? volume : volume + sumVol[1]
    result := sumSrcVol / sumVol
    result
    
//{ MAMA & FAMA & HKAMA Pre-requisits 
// Original Hilbert by everget : https://www.tradingview.com/script/aaWzn9bK-Ehlers-MESA-Adaptive-Moving-Averages-MAMA-FAMA/
PI = 2 * math.asin(1)
hilbertTransform(src) =>
    0.0962 * src + 0.5769 * nz(src[2]) - 0.5769 * nz(src[4]) - 0.0962 * nz(src[6])

computeComponent(src, mesaPeriodMult) =>
    hilbertTransform(src) * mesaPeriodMult

computeAlpha(src, fastLimit, slowLimit) =>
    mesaPeriod = 0.0
    mesaPeriodMult = 0.075 * nz(mesaPeriod[1]) + 0.54

    smooth = 0.0
    smooth := (4 * src + 3 * nz(src[1]) + 2 * nz(src[2]) + nz(src[3])) / 10

    detrender = 0.0
    detrender := computeComponent(smooth, mesaPeriodMult)

    // Compute InPhase and Quadrature components
    I1 = nz(detrender[3])
    Q1 = computeComponent(detrender, mesaPeriodMult)

    // Advance the phase of I1 and Q1 by 90 degrees
    jI = computeComponent(I1, mesaPeriodMult)
    jQ = computeComponent(Q1, mesaPeriodMult)

    I2 = 0.0
    Q2 = 0.0

    // Phasor addition for 3 bar averaging
    I2 := I1 - jQ
    Q2 := Q1 + jI

    // Smooth the I and Q components before applying the discriminator
    I2 := 0.2 * I2 + 0.8 * nz(I2[1])
    Q2 := 0.2 * Q2 + 0.8 * nz(Q2[1])

    // Homodyne Discriminator
    Re = I2 * nz(I2[1]) + Q2 * nz(Q2[1])
    Im = I2 * nz(Q2[1]) - Q2 * nz(I2[1])

    Re := 0.2 * Re + 0.8 * nz(Re[1])
    Im := 0.2 * Im + 0.8 * nz(Im[1])

    if Re != 0 and Im != 0
        mesaPeriod := 2 * PI / math.atan(Im / Re)
        mesaPeriod

    if mesaPeriod > 1.5 * nz(mesaPeriod[1])
        mesaPeriod := 1.5 * nz(mesaPeriod[1])
        mesaPeriod

    if mesaPeriod < 0.67 * nz(mesaPeriod[1])
        mesaPeriod := 0.67 * nz(mesaPeriod[1])
        mesaPeriod

    if mesaPeriod < 6
        mesaPeriod := 6
        mesaPeriod

    if mesaPeriod > 50
        mesaPeriod := 50
        mesaPeriod

    mesaPeriod := 0.2 * mesaPeriod + 0.8 * nz(mesaPeriod[1])

    phase = 0.0

    if I1 != 0
        phase := 180 / PI * math.atan(Q1 / I1)
        phase

    deltaPhase = nz(phase[1]) - phase

    if deltaPhase < 1
        deltaPhase := 1
        deltaPhase

    alpha = fastLimit / deltaPhase

    if alpha < slowLimit
        alpha := slowLimit
        alpha
    [alpha, alpha / 2.0]
// }

    
// @function            MAMA - MESA Adaptive Moving Average
// @param src           Source
// @param len           Period
// @returns             MESA Adaptive Moving Average (MAMA)
export mama(float src, int len) =>    // MAMA - MESA Adaptive Moving Average
    var float result = 0.0
    er = math.abs(ta.change(src, len)) / math.sum(math.abs(ta.change(src)), len)
    [a, b] = computeAlpha(src, er, er * 0.1)
    result := a * src + (1 - a) * nz(result[1])
    result
    
    
// @function            FAMA - Following Adaptive Moving Average
// @param src           Source
// @param len           Period
// @returns             Following Adaptive Moving Average (FAMA)
export fama(float src, int len) =>    // FAMA - Following Adaptive Moving Average
    var float result = 0.0
    var float mama = 0.0
    var float fama = 0.0
    er = math.abs(ta.change(src, len)) / math.sum(math.abs(ta.change(src)), len)
    [a, b] = computeAlpha(src, er, er * 0.1)
    mama := a * src + (1 - a) * nz(mama[1])
    fama := b * mama + (1 - b) * nz(fama[1])
    result := fama
    result
    
    

// @function            HKAMA - Hilbert based Kaufman's Adaptive Moving Average
// @param src           Source
// @param len           Period
// @returns             Hilbert based Kaufman's Adaptive Moving Average (HKAMA)
export hkama(float src, int len) =>    // HKAMA - Hilbert based Kaufman's Adaptive Moving Average
    var float result = 0.0    
    er = math.abs(ta.change(src, len)) / math.sum(math.abs(ta.change(src)), len)
    [a, b] = computeAlpha(src, er, er * 0.1)
    alpha = math.pow(er * (b - a) + a, 2)
    result := alpha * src + (1 - alpha) * nz(result[1])
    result
    
    
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/////                       Plotting                           //////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
string MA01 = 'ALMA - Arnaud Legoux Moving Average'
string MA02 = 'COVWEMA - Coefficient of Variation Weighted\tExponential Moving Average'
string MA03 = 'COVWMA - Coefficient of Variation Weighted\tMoving Average'
string MA04 = 'DEMA - Double Exponential Moving Average'
string MA05 = 'EDSMA - Ehlers Deviation Scaled Moving Average'
string MA06 = 'EFRAMA - Ehlrs Modified Fractal Adaptive Moving Average'
string MA07 = 'EHMA - Exponential Hull Moving Average'
string MA08 = 'EMA - Exponential Moving Average'
string MA09 = 'ETMA - Exponential Triangular Moving Average'
string MA10 = 'FRAMA - Fractal Adaptive Moving Average'
string MA11 = 'HMA - Hull Moving Average'
string MA12 = 'JMA - Jurik Moving Average'
string MA13 = 'KAMA - Kaufman\'s Adaptive Moving Average'
string MA14 = 'Kijun v2'
string MA15 = 'LSMA/LRC - Least Squares Moving Average'
string MA16 = 'MF - Modular Filter'
string MA17 = 'RMA - RSI Moving average'
string MA18 = 'SMA - Simple Moving Average'
string MA19 = 'SMMA - Smoothed Moving Average'
string MA20 = 'STMA - Simple Triangular Moving Average'
string MA21 = 'TEMA - Triple Exponential Moving Average'
string MA22 = 'THMA - Triple Hull Moving Average'
string MA23 = 'VAMA - Volatility Adjusted Moving Average'
string MA24 = 'VIDYA - Variable Index Dynamic Average'
string MA25 = 'VWMA - Volume Weighted Moving Average'
string MA26 = 'WMA - Weighted Moving Average'
string MA27 = 'ZEMA - Zero-Lag Exponential Moving Average'
string MA28 = 'ZSMA - Zero-Lag Simple Moving Average'
string MA29 = 'EVWMA - Elastic Volume Weighted Moving Average'
string MA30 = 'Tillson T3'
string MA31 = 'GMA - Geometric Moving Average'
string MA32 = 'WWMA - Welles Wilder Moving Average'
string MA33 = 'AMA - Adjusted Moving Average'
string MA34 = 'CMA - Corrective Moving Average'
string MA35 = 'GMMA - Geometric Mean Moving Average'
string MA36 = 'EALF - Ehler\'s Adaptive Laguerre filter'
string MA37 = 'ELF - Ehler\'s Laguerre Filter'
string MA38 = 'PNR - Percentile Nearest Rank'
string MA39 = 'PLI - Percentile Linear Interpolation'
string MA40 = 'REMA - Range EMA'
string MA41 = 'SWMA - Sine-Weighted Moving Average'
string MA42 = 'VWAP - Volume Weighted Average Price'
string MA43 = 'MAMA - MESA Adaptive Moving Average'
string MA44 = 'FAMA - Following Adaptive Moving Average'
string MA45 = 'HKAMA - Hilbert based Kaufman\'s Adaptive Moving Average'
string MA46 = 'EDMA - MZ Exponentially Deviating Moving Average'
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////


// Slow MA Parameters
src = input.source(hlc3, 'Source')
grp1 = 'Slow MA Parameters'
grp2 = 'Fast MA Parameters'

slowMAtype = input.string(title='Slow MA Type', group=grp1, defval=MA05, options=['Source', MA01, MA02, MA03, MA04, MA05, MA06, MA07, MA08, MA09, MA10,
     MA11, MA12, MA13, MA14, MA15, MA16, MA17, MA18, MA19, MA20,
     MA21, MA22, MA23, MA24, MA25, MA26, MA27, MA28, MA29, MA30,
     MA31, MA32, MA33, MA34, MA35, MA36, MA37, MA38, MA39, MA40,
     MA41, MA42, MA43, MA44, MA45, MA46])
slowMAlen = input.int(100, title='Slow MA Length:', group=grp1)

fastMAtype = input.string(title='Fast MA Type', group=grp2, defval=MA11, options=['Source', MA01, MA02, MA03, MA04, MA05, MA06, MA07, MA08, MA09, MA10,
     MA11, MA12, MA13, MA14, MA15, MA16, MA17, MA18, MA19, MA20,
     MA21, MA22, MA23, MA24, MA25, MA26, MA27, MA28, MA29, MA30,
     MA31, MA32, MA33, MA34, MA35, MA36, MA37, MA38, MA39, MA40,
     MA41, MA42, MA43, MA44, MA45, MA46])
fastMAlen = input.int(50, title='Fast MA Length:', group=grp2)

/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/////           MA Functions' Additional Inputs                //////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////

//           Pre-reqs
//
// Kijun V2
kidiv = input.int(defval=1, maxval=4, title='Kijun MOD Divider')
// JMA
jurik_phase = input(title='* Jurik (JMA) Only - Phase', defval=3)
jurik_power = input(title='* Jurik (JMA) Only - Power', defval=1)
// VAMA
volatility_lookback = input(10, title='* Volatility Adjusted (VAMA) Only - Volatility lookback length')
// MF
beta = input.float(0.8, minval=0, maxval=1, step=0.1, title='Modular Filter, General Filter Only - Beta')
feedback = input(false, title='Modular Filter Only - Feedback')
z = input.float(0.5, title='Modular Filter Only - Feedback Weighting', step=0.1, minval=0, maxval=1)
// EDSMA
ssfLength = input.int(title='EDSMA - Super Smoother Filter Length', minval=1, defval=20)
ssfPoles = input.int(title='EDSMA - Super Smoother Filter Poles', defval=2, options=[2, 3])
// KAMA
k_fastLength = input.float(2.0, 'Number of periods for the fastest exponential moving average', minval=1)
k_slowLength = input.float(30.0, 'Number of periods for the slowest exponential moving average', minval=1)
// ─── Ehlrs Modified Fractal Adaptive Moving Average (EFRAMA) 
FC = input.int(defval=1, title='Lower Shift Limit for Ehlrs Modified Fractal Adaptive Moving Average', minval=1)
SC = input.int(defval=200, title='Upper Shift Limit for Ehlrs Modified Fractal Adaptive Moving Average', minval=1)
// ─── Arnaud Legoux Moving Average (ALMA) 
a_offset = input.float(0.85, 'Arnaud Legoux offset', minval=0.01, step=0.05)
a_sigma = input.float(6.0, 'Arnaud Legoux sigma', minval=1, step=0.1)
// Tillson T3
a1_t3 = input.float(title='Tillson T3 Volume Factor', defval=0.7, inline='5', step=0.001)
//  AMA
ama_f_length = input(title='Fast Length (Adjusted Moving Average)', defval=14)
ama_s_length = input(title='Slow Length (Adjusted Moving Average)', defval=100)
//  EALF Ehler's Adaptive Laguerre filter
LAPercLen_ = input(title='Median Length (Ehler\'s Adaptive Laguerre filter)', defval=5)
FPerc_ = input(title='Median Percentage (Ehler\'s Adaptive Laguerre filter)', defval=50)
// PNR  &   PLI
rank_inter_Perc_ = input(title='Rank and Interpolation Percentage (PNR & PLI)', defval=50)

/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/////                Selections & Calculations                 //////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////

//          MA Selection and Output function
ma(type, src, len) =>
    var float result = 0.0 // initiating
    if type == MA01 
        result := alma(src, len, a_offset, a_sigma)
    if type == MA02
        result := covwema(src, len)
    if type == MA03
        result := covwma(src, len)
    if type == MA04
        result := dema(src, len)
    if type == MA05         
        result := edsma(src, len, ssfLength, ssfPoles) 
    if type == MA06         
        result := eframa(src, len, FC, SC) 
    if type == MA07         
        result := ehma(src, len) 
    if type == MA08         
        result := ema(src, len) 
    if type == MA09         
        result := etma(src, len) 
    if type == MA10         
        result := frama(src, len) 
    if type == MA11         
        result := hma(src, len) 
    if type == MA12         
        result := jma(src, len, jurik_phase, jurik_power) 
    if type == MA13         
        result := kama(src, len, k_fastLength, k_slowLength) 
    if type == MA14         
        result := kijun(high, low, len, kidiv) 
    if type == MA15         
        result := lsma(src, len, 0) 
    if type == MA16         
        result := mf(src, len, beta, feedback, z) 
    if type == MA17         
        result := rma(src, len) 
    if type == MA18         
        result := sma(src, len) 
    if type == MA19         
        result := smma(src, len)
    if type == MA20         
        result := stma(src, len) 
    if type == MA21         
        result := tema(src, len) 
    if type == MA22         
        result := thma(src, len) 
    if type == MA23         
        result := vama(src, len, volatility_lookback) 
    if type == MA24         
        result := vidya(src, len) 
    if type == MA25         
        result := vwma(src, len) 
    if type == MA26         
        result := wma(src, len) 
    if type == MA27         
        result := zema(src, len) 
    if type == MA28         
        result := zsma(src, len) 
    if type == MA29         
        result := evwma(src, len) 
    if type == MA30         
        result := tt3(src, len, a1_t3) 
    if type == MA31         
        result := gma(src, len) 
    if type == MA32         
        result := wwma(src, len) 
    if type == MA33         
        result := ama(src, high, low, len, ama_f_length, ama_s_length) 
    if type == MA34         
        result := cma(src, len) 
    if type == MA35         
        result := gmma(src, len) 
    if type == MA36         
        result := ealf(src, len, LAPercLen_, FPerc_) 
    if type == MA37         
        result := elf(src, len) 
    if type == MA38         
        result := pnr(src, len, rank_inter_Perc_) 
    if type == MA39         
        result := pli(src, len, rank_inter_Perc_) 
    if type == MA40         
        result := rema(src, len) 
    if type == MA41         
        result := sw_ma(src, len) 
    if type == MA42         
        result := vwap(src) 
    if type == MA43         
        result := mama(src, len) 
    if type == MA44         
        result := fama(src, len) 
    if type == MA45         
        result := hkama(src, len) 
    if type == MA46         
        result := edma(src, len) 

    result

/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////

slowMA = ma(slowMAtype, src, slowMAlen)
fastMA = ma(fastMAtype, src, fastMAlen)

//Plot
plot(slowMA, 'Slow MA', color.blue, 4)
plot(fastMA, 'Fast MA', color.green, 2)
