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

// @version=5
// @description Contains utilities for generating moving average values including getting a moving average by name and a function for generating a Volume-Adjusted WMA.
library('MovingAverages', true)

isLenInvalid(simple int len, simple string name = 'len') =>
    if na(len)
        runtime.error("The '"+name+"' param cannot be NA.")
        true
    else if len < 0
        runtime.error("The '"+name+"' param cannot be negative.") 
        true       
    else if len == 0
        true
    else
        false

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// VAWMA = VWMA and WMA combined.
// Simply put, this attempts to determine the average price per share over time weighted heavier for recent values.
// Uses triangular algorithm to taper off values in the past (same as WMA does).
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////
// @function Simple Moving Avereage
// @param _D The series to measure from.
// @param _len The number of bars to measure with.
export sma(
  series float _D,
  simple int _len) =>
    if isLenInvalid(_len, 'n')
        na
    else 
        _sma=ta.sma(_D, _len)
        _sma
///////////////////////////////////////////////////

///////////////////////////////////////////////////
// @function Exponential Moving Avereage
// @param _D The series to measure from.
// @param _len The number of bars to measure with.
export ema(
  series float _D,
  simple int _len) =>
    if isLenInvalid(_len, 'n')
        na
    else 
        _ema=ta.ema(_D, _len)
        _ema
///////////////////////////////////////////////////

///////////////////////////////////////////////////
// @function RSI Moving Avereage
// @param _D The series to measure from.
// @param _len The number of bars to measure with.
export rma(
  series float _D,
  simple int _len) =>
    if isLenInvalid(_len, 'n')
        na
    else 
        _rma=ta.rma(_D,_len)
        _rma
///////////////////////////////////////////////////


///////////////////////////////////////////////////
// @function Weighted Moving Avereage
// @param _D The series to measure from.
// @param _len The number of bars to measure with.
export wma(
  series float _D,
  simple int _len) =>
    if isLenInvalid(_len, 'n')
        na
    else 
        _wma=ta.wma(_D,_len)
        _wma
///////////////////////////////////////////////////

///////////////////////////////////////////////////
// @function volume-weighted Moving Avereage 
// @param _D The series to measure from.  Default is 'close'.
// @param _len The number of bars to measure with.
export vwma(
  series float _D,
  simple int _len) =>
    if isLenInvalid(_len, 'n')
        na
    else 
        _vwma=ta.vwma(_D,_len)
        _vwma
///////////////////////////////////////////////////

///////////////////////////////////////////////////
// @function Arnaud Legoux Moving Avereage
// @param _D The series to measure from.  Default is 'close'.
// @param _len The number of bars to measure with.
export alma(
  series float _D,
  simple int _len) =>
    if isLenInvalid(_len, 'n')
        na
    else 
        _alma=ta.alma(_D,_len, 0.8, 6)
        _alma
///////////////////////////////////////////////////

///////////////////////////////////////////////////
// @function Coefficient Moving Avereage (CMA) is a variation of a moving average that can simulate SMA or WMA with the advantage of previous data.
// @param _D The series to measure from.  Default is 'close'.
// @param _len The number of bars to measure with.
// @param C The coefficient to use when averaging. 0 behaves like SMA, 1 behaves like WMA.
// @param compound When true (default is false) will use a compounding method for weighting the average.
export cma(
  series float _D,
  simple int _len,
  simple float C = 1,
  simple bool compound = false) =>
    var float notAvailable = na
    if isLenInvalid(_len, 'n')
        notAvailable
    else 
        var float result = na
        sum = 0.0
        weight = 1.0
        weightTotal = 0.0
        for m = 1 to _len
            s = _D[_len - m]
            if not na(s)
                sum := sum + s * weight
                weightTotal += weight
                if compound
                    weight *= 1 + C
                else
                    weight += C

        prev = result[_len]
        if na(prev)
            result := sum / weightTotal
        else
            result := (prev + sum) / (weightTotal + 1)
///////////////////////////////////////////////////

///////////////////////////////////////////////////
// @function Double Exponential Moving Avereage
// @param _D The series to measure from.  Default is 'close'.
// @param _len The number of bars to measure with.
export dema(
  series float _D,
  simple int _len) =>
    if isLenInvalid(_len, 'n')
        na
    else 
        e1 = ta.ema(_D, _len)
        e2 = ta.ema(e1, _len)
        _dema=2 * e1 - e2
        _dema
///////////////////////////////////////////////////

///////////////////////////////////////////////////
// @function Arnaud Legoux Moving Avereage
// @param _D The series to measure from.  Default is 'close'.
// @param _len The number of bars to measure with.
export zlsma(
  series float _D,
  simple int _len) =>
    if isLenInvalid(_len, 'n')
        na
    else 
        _zlsma1     = ta.linreg(_D, math.round(_len/2), 0)
        _zlsma2     = ta.linreg(_zlsma1, math.round(_len/2), 0)
        _zlsma=(2*_zlsma1)-_zlsma2
        _zlsma
///////////////////////////////////////////////////

///////////////////////////////////////////////////
// @function Arnaud Legoux Moving Avereage
// @param _D The series to measure from.  Default is 'close'.
// @param _len The number of bars to measure with.
export zlema(
  series float _D,
  simple int _len) =>
    if isLenInvalid(_len, 'n')
        na
    else 
        _quadLength     = math.round((_len -1)/4)
        _sensitivePrice = (_D + (_D - _D[_quadLength]))
        _zlema=ta.ema(_sensitivePrice, _len)
        _zlema
///////////////////////////////////////////////////

///////////////////////////////////////////////////
// @function Generates a moving average based upon a 'type'.
// @param type The type of moving average to generate.  Values allowed are: SMA, EMA, WMA, VWMA and VAWMA.
// @param len The number of bars to measure with.
// @param src The series to measure from.  Default is 'close'.
// @returns The moving average series requested.
export get(
  simple string type='SMA',
  series float src = close,
  simple int len=20) =>
    if isLenInvalid(len, 'n')
        na
    else 
        switch type
            'SMA'   => ta.sma(src, len)
            'EMA'   => ta.ema(src, len)
            'RMA'   => ta.rma(src, len)
            'WMA'   => ta.wma(src, len)
            'VWMA'  => ta.vwma(src,len)
            'ALMA'  => ta.alma(src, len, 0.8,6)
            'CMA'   => cma(src, len)
            'DMEA'  => dema(src, len)
            'ZLSMA' => zlsma(src, len)
            'ZLEMA' => zlema(src, len)
            =>
                runtime.error("No matching MA type found.")
                ta.sma(src, len)
///////////////////////////////////////////////////


// Demo
plot(ema(hlc3,30), 'ema(20)', color.red)
plot(wma(hlc3,30), 'wma(20)', color.yellow)
plot(vwma(hlc3,30), 'vwma(20)', color.blue)