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

//@version=5
indicator(title = "Change of Volatility", shorttitle = "CHGVOLT", timeframe = "", timeframe_gaps = false)

//==============================================================================
// Function: Calculate a given type of moving average
//==============================================================================
get_ma_out(type, src, len, alma_offset, alma_sigma, kama_fastLength, kama_slowLength, vama_vol_len, vama_do_smth, vama_smth, mf_beta, mf_feedback, mf_z, eit_alpha, ssma_pow, ssma_smooth, rsrma_pw, svama_method, svama_vol_or_volatility, wrma_smooth) =>
    float baseline = 0.0
    
    if type == 'SMA  | Simple MA'
        baseline := ta.sma(src, len)

    else if type == 'EMA  | Exponential MA'
        baseline := ta.ema(src, len)

    else if type == 'DEMA | Double Exponential MA'
        e = ta.ema(src, len)
        baseline := 2 * e - ta.ema(e, len)

    else if type == 'TEMA | Triple Exponential MA'
        e = ta.ema(src, len)
        baseline := 3 * (e - ta.ema(e, len)) + ta.ema(ta.ema(e, len), len)

	else if type == 'TMA  | Triangular MA' // by everget
        baseline := ta.sma(ta.sma(src, math.ceil(len / 2)), math.floor(len / 2) + 1)

    else if type == 'WMA  | Weighted MA'
        baseline := ta.wma(src, len)

    else if type == 'VWMA | Volume-Weighted MA'
        baseline := ta.vwma(src, len)

    else if type == 'SMMA | Smoothed MA'
        w = ta.wma(src, len)
        baseline := na(w[1]) ? ta.sma(src, len) : (w[1] * (len - 1) + src) / len

    else if type == 'RMA  | Rolling MA'
        baseline := ta.rma(src, len)

    else if type == 'HMA  | Hull MA'
        baseline := ta.wma(2 * ta.wma(src, len / 2) - ta.wma(src, len), math.round(math.sqrt(len)))

    else if type == 'LSMA | Least Squares MA'
        baseline := ta.linreg(src, len, 0)

    else if type == 'Kijun'  //Kijun-sen
        kijun = math.avg(ta.lowest(len), ta.highest(len))
        baseline := kijun

    else if type == 'MD   | McGinley Dynamic'
        mg = 0.0
        mg := na(mg[1]) ? ta.ema(src, len) : mg[1] + (src - mg[1]) / (len * math.pow(src / mg[1], 4))
        baseline := mg

	else if type == 'JMA  | Jurik MA' // by everget
		DEMAe1 = ta.ema(src, len)
		DEMAe2 = ta.ema(DEMAe1, len)
		baseline := 2 * DEMAe1 - DEMAe2
		
    else if type == 'ALMA | Arnaud Legoux MA'
        baseline := ta.alma(src, len, alma_offset, alma_sigma)

    else if type == 'VAR  | Vector Autoregression MA'
        valpha = 2 / (len+1)
        vud1   = (src > src[1]) ? (src - src[1]) : 0
        vdd1   = (src < src[1]) ? (src[1] - src) : 0
        vUD    = math.sum(vud1, 9)
        vDD    = math.sum(vdd1, 9)
        vCMO   = nz( (vUD - vDD) / (vUD + vDD) )
        baseline := nz(valpha * math.abs(vCMO) * src) + (1 - valpha * math.abs(vCMO)) * nz(baseline[1])
        
    else if type == 'ZLEMA | Zero-Lag Exponential MA' // by HPotter
        xLag     = (len) / 2
        xEMAData = (src + (src - src[xLag]))
        baseline := ta.ema(xEMAData, len)
        
    else if type == 'AHMA | Ahrens Moving Average' // by everget
        baseline := nz(baseline[1]) + (src - (nz(baseline[1]) + nz(baseline[len])) / 2) / len 
        
    else if type == 'EVWMA | Elastic Volume Weighted MA'
        volumeSum = math.sum(volume, len)
        baseline := ( (volumeSum - volume) * nz(baseline[1]) + volume * src ) / volumeSum
    
    else if type == 'SWMA | Sine Weighted MA' // by everget
        sum = 0.0
        weightSum = 0.0
        for i = 0 to len - 1
            weight = math.sin(i * math.pi / (len + 1))
            sum := sum + nz(src[i]) * weight
            weightSum := weightSum + weight
        baseline := sum / weightSum
        
    else if type == 'LMA  | Leo MA'
        baseline := 2 * ta.wma(src, len) - ta.sma(src, len)
        
    else if type == 'VIDYA | Variable Index Dynamic Average' // by KivancOzbilgic
        mom = ta.change(src)
        upSum = math.sum(math.max(mom, 0), len)
        downSum = math.sum(-math.min(mom, 0), len)
        out = (upSum - downSum) / (upSum + downSum)

        cmo = math.abs(out)
        
        alpha = 2 / (len + 1)
        
        baseline := src * alpha * cmo + nz(baseline[1]) * (1 - alpha * cmo)
    
    else if type == 'FRAMA | Fractal Adaptive MA'
        length2 = math.floor(len / 2)
        hh2 = ta.highest(length2)
        ll2 = ta.lowest(length2)
        
        N1 = (hh2 - ll2) / length2
        
        N2 = (hh2[length2] - ll2[length2]) / length2
        
        N3 = (ta.highest(len) - ta.lowest(len)) / len
        
        D = (math.log(N1 + N2) - math.log(N3)) / math.log(2)
        
        factor = math.exp(-4.6 * (D - 1))
        
        baseline := factor * src + (1 - factor) * nz(baseline[1])
    
    else if type == 'VMA  | Variable MA' // by LazyBear
        k = 1.0/len
        pdm = math.max((src - src[1]), 0)
        mdm = math.max((src[1] - src), 0)
        
        pdmS  = float(0.0)
        mdmS  = float(0.0)
        pdmS := ((1 - k)*nz(pdmS[1]) + k*pdm)
        mdmS := ((1 - k)*nz(mdmS[1]) + k*mdm)
        
        s = pdmS + mdmS
        pdi = pdmS/s
        mdi = mdmS/s
        
        pdiS  = float(0.0)
        mdiS  = float(0.0)
        pdiS := ((1 - k)*nz(pdiS[1]) + k*pdi)
        mdiS := ((1 - k)*nz(mdiS[1]) + k*mdi)
        
        d = math.abs(pdiS - mdiS)
        s1 = pdiS + mdiS
        
        iS  = float(0.0)
        iS := ((1 - k)*nz(iS[1]) + k*d/s1)
        
        hhv = ta.highest(iS, len) 
        llv = ta.lowest(iS, len) 
        d1 = hhv - llv
        vI = (iS - llv)/d1
        baseline := (1 - k*vI)*nz(baseline[1]) + k*vI*src
    
    else if type == 'GMMA | Geometric Mean MA'
        lmean = math.log(src)
        smean = math.sum(lmean, len)
        baseline := math.exp(smean / len)
        
    else if type == 'CMA  | Corrective MA' // by everget
        sma = ta.sma(src, len)
        baseline := sma
        v1 = ta.variance(src, len)
        v2 = math.pow(nz(baseline[1], baseline) - baseline, 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
                
        baseline := nz(baseline[1], src) + k * (sma - nz(baseline[1], src))
        
    else if type == 'MM   | Moving Median' // by everget
        baseline := ta.percentile_nearest_rank(src, len, 50)
        
    else if type == 'QMA  | Quick MA' // by everget
        peak = len / 3
        num = 0.0
        denom = 0.0
         
        for i = 1 to len + 1
            mult = 0.0
            if i <= peak
                mult := i / peak
            else
                mult := (len + 1 - i) / (len + 1 - peak)
            num := num + src[i - 1] * mult
            denom := denom + mult
         
        baseline := (denom != 0.0) ? (num / denom) : src
        
    else if type == 'KAMA | Kaufman Adaptive MA' // by everget
        mom = math.abs(ta.change(src, len))
        volatility = math.sum(math.abs(ta.change(src)), len)
        
        // Efficiency Ratio
        er = volatility != 0 ? mom / volatility : 0
        
        fastAlpha = 2 / (kama_fastLength + 1)
        slowAlpha = 2 / (kama_slowLength + 1)
        
        alpha = math.pow(er * (fastAlpha - slowAlpha) + slowAlpha, 2)
        
        baseline := alpha * src + (1 - alpha) * nz(baseline[1], src)
    
    else if type == 'VAMA | Volatility Adjusted MA' // by Joris Duyck (JD)
        mid = ta.ema(src, len)
        dev = src - mid
        
        vol_up   = ta.highest(dev, vama_vol_len)
        vol_down = ta.lowest(dev, vama_vol_len)
        
        vama = mid + math.avg(vol_up, vol_down)
        baseline := vama_do_smth ? ta.wma(vama, vama_smth) : vama
    
    else if type == 'Modular Filter' // by alexgrover
        //----
        b = 0.0, c = 0.0, os = 0.0, ts = 0.0
        //----
        alpha = 2/(len+1)
        a = mf_feedback ? mf_z*src + (1-mf_z)*nz(baseline[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 = mf_beta*b+(1-mf_beta)*c
        lower = mf_beta*c+(1-mf_beta)*b 
        baseline := os*upper+(1-os)*lower
    
    else if type == 'EIT  | Ehlers Instantaneous Trendline' // by Franklin Moormann (cheatcountry)
        baseline := bar_index < 7 ? (src + (2 * nz(src[1])) + nz(src[2])) / 4 : ((eit_alpha - (math.pow(eit_alpha, 2) / 4)) * src) + (0.5 * math.pow(eit_alpha, 2) * nz(src[1])) - 
         ((eit_alpha - (0.75 * math.pow(eit_alpha, 2))) * nz(src[2])) + (2 * (1 - eit_alpha) * nz(baseline[1])) - (math.pow(1 - eit_alpha, 2) * nz(baseline[2]))
    
    else if type == 'ESD  | Ehlers Simple Decycler' // by everget
        // High-pass Filter
        alphaArg = 2 * math.pi / (len * math.sqrt(2))
        
        alpha = 0.0
        alpha := math.cos(alphaArg) != 0
             ? (math.cos(alphaArg) + math.sin(alphaArg) - 1) / math.cos(alphaArg)
             : nz(alpha[1])
        
        hp = 0.0
        hp := math.pow(1 - (alpha / 2), 2) * (src - 2 * nz(src[1]) + nz(src[2])) + 2 * (1 - alpha) * nz(hp[1]) - math.pow(1 - alpha, 2) * nz(hp[2])
    
        baseline := src - hp
    
    else if type == 'SSMA  | Shapeshifting MA' // by alexgrover
        //----
        ssma_sum = 0.0
        ssma_sumw = 0.0
        alpha = ssma_smooth ? 2 : 1
        power = ssma_smooth ? ssma_pow - ssma_pow % 2 : ssma_pow
        //----
        for i = 0 to len-1
            x = i/(len-1)
            n = ssma_smooth ? -1 + x*2 : x
            w = 1 - 2*math.pow(n,alpha)/(math.pow(n,power) + 1) 
            ssma_sumw := ssma_sumw + w
            ssma_sum := ssma_sum + src[i] * w
        baseline := ssma_sum/ssma_sumw
        //----
    
    else if type == 'RSRMA  | Right Sided Ricker MA' // by alexgrover
        //----
        rsrma_sum = 0.0
        rsrma_sumw = 0.0
        rsrma_width = rsrma_pw/100*len
        for i = 0 to len-1
            w = (1 - math.pow(i/rsrma_width,2))*math.exp(-(i*i/(2*math.pow(rsrma_width,2))))
            rsrma_sumw := rsrma_sumw + w
            rsrma_sum := rsrma_sum + src[i] * w
        baseline := rsrma_sum/rsrma_sumw
        //----

    else if type == 'DSWF  | Damped Sine Wave Weighted Filter' // by alexgrover
        //----
        dswf_sum = 0.0
        dswf_sumw = 0.0
        for i = 1 to len
            w = math.sin(2.0*math.pi*i/len)/i
            dswf_sumw := dswf_sumw + w
            dswf_sum := dswf_sum + w*src[i-1]
        //----
        baseline := dswf_sum/dswf_sumw
  
    else if type == 'BMF  | Blackman Filter' // by alexgrover
        //----
        bmf_sum = 0.0
        bmf_sumw = 0.0
        for i = 0 to len-1
            k = i/len
            w = 0.42 - 0.5 * math.cos(2 * math.pi * k) + 0.08 * math.cos(4 * math.pi * k)
            bmf_sumw := bmf_sumw + w
            bmf_sum := bmf_sum + w*src[i]
        //----
        baseline := bmf_sum/bmf_sumw

    else if type == 'HCF  | Hybrid Convolution Filter' // by alexgrover
        //----
        sum = 0.
        for i = 1 to len
            sgn = .5*(1 - math.cos((i/len)*math.pi))
            sum := sum + (sgn*(nz(baseline[1],src)) + (1 - sgn)*src[i-1]) * ( (.5*(1 - math.cos((i/len)*math.pi))) - (.5*(1 - math.cos(((i-1)/len)*math.pi))) )
        baseline := sum
        //----
    
    else if type == 'FIR  | Finite Response Filter' // by alexgrover
        //----
        var b = array.new_float(0)
        if barstate.isfirst
            for i = 0 to len-1
                w = len-i
                array.push(b,w)
        den = array.sum(b)
        //----
        sum = 0.0
        for i = 0 to len-1
            sum := sum + src[i]*array.get(b,i)
        baseline := sum/den
        
    else if type == 'FLSMA  | Fisher Least Squares MA' // by alexgrover
        //----
        b = 0.0
        //----
        e = ta.sma(math.abs(src - nz(b[1])),len)
        z = ta.sma(src - nz(b[1],src),len)/e 
        r = (math.exp(2*z) - 1)/(math.exp(2*z) + 1) 
        a = (bar_index - ta.sma(bar_index,len))/ta.stdev(bar_index,len) * r
        b := ta.sma(src,len) + a*ta.stdev(src,len)
        baseline := b
    
    else if type == 'SVAMA  | Non-Parametric Volume Adjusted MA' // by alexgrover and bjr117
        //----
        h = 0.0
        l = 0.0
        c = 0.0
        //----
        a = svama_vol_or_volatility == 'Volume' ? volume : ta.tr
        h := a > nz(h[1], a) ? a : nz(h[1], a)
        l := a < nz(l[1], a) ? a : nz(l[1], a)
        //----
        b = svama_method == 'Max' ? a / h : l / a
        c := b * close + (1 - b) * nz(c[1], close)
        baseline := c

    else if type == 'RPMA  | Repulsion MA' // by alexgrover
        baseline := ta.sma(close, len*3) + ta.sma(close, len*2) - ta.sma(close, len)

    else if type == 'WRMA  | Well Rounded MA' // by alexgrover
        //----
        alpha = 2/(len+1)
        p1 = wrma_smooth ? len/4 : 1
        p2 = wrma_smooth ? len/4 : len/2
        //----
        a = float(0.0)
        b = float(0.0)
        y = ta.ema(a + b,p1)
        A = src - y
        B = src - ta.ema(y,p2)
        a := nz(a[1]) + alpha*nz(A[1]) 
        b := nz(b[1]) + alpha*nz(B[1])
        baseline := y

    baseline
//==============================================================================


//==============================================================================
// Inputs
//==============================================================================
// Histogram calculation inputs
chgvolt_short_len = input.int(title = "CHGVOLT Short Length", defval = 6,   minval = 1, group = "Main Calculation Settings")
chgvolt_long_len  = input.int(title = "CHGVOLT Long Length",  defval = 100, minval = 1, group = "Main Calculation Settings")


// Histogram momentum calculation inputs
chgvolt_mom_len   = input.int(title = "CHGVOLT Momentum Calculation Length",    defval = 1, minval = 1, group = "Main Calculation Settings")
chgvolt_mom_src   = input.source(title = "CHGVOLT Momentum Calculation Source", defval = close,         group = "Main Calculation Settings")


// Histogram deadzone calculation inputs
chgvolt_dz_ma_type = input.string("EMA  | Exponential MA", "CHGVOLT Deadzone MA Type", options=[ "EMA  | Exponential MA",                  "SMA  | Simple MA", 
                                                                     "WMA  | Weighted MA",                     "DEMA | Double Exponential MA", 
                                                                     "TEMA | Triple Exponential MA",           "TMA  | Triangular MA", 
                                                                     "VWMA | Volume-Weighted MA",              "SMMA | Smoothed MA", 
                                                                     "HMA  | Hull MA",                         "LSMA | Least Squares MA", 
                                                                     "Kijun",                                  "MD   | McGinley Dynamic", 
                                                                     "RMA  | Rolling MA",                      "JMA  | Jurik MA", 
                                                                     "ALMA | Arnaud Legoux MA",                "VAR  | Vector Autoregression MA", 
                                                                     "ZLEMA | Zero-Lag Exponential MA",        
                                                                     "AHMA | Ahrens Moving Average",           "EVWMA | Elastic Volume Weighted MA",
                                                                     "SWMA | Sine Weighted MA",                "LMA  | Leo MA",
                                                                     "VIDYA | Variable Index Dynamic Average", "FRAMA | Fractal Adaptive MA",
                                                                     "VMA  | Variable MA",                     "GMMA | Geometric Mean MA",
                                                                     "CMA  | Corrective MA",                   "MM   | Moving Median",
                                                                     "QMA  | Quick MA",                        "KAMA | Kaufman Adaptive MA",
                                                                     "VAMA | Volatility Adjusted MA",          "Modular Filter",
                                                                     "EIT  | Ehlers Instantaneous Trendline",  "ESD  | Ehlers Simple Decycler",
                                                                     "SSMA  | Shapeshifting MA",               "RSRMA  | Right Sided Ricker MA",
                                                                     "DSWF  | Damped Sine Wave Weighted Filter", "BMF  | Blackman Filter",
                                                                     "HCF  | Hybrid Convolution Filter",       "FIR  | Finite Response Filter",
                                                                     "FLSMA  | Fisher Least Squares MA",       "SVAMA  | Non-Parametric Volume Adjusted MA",
                                                                     "RPMA  | Repulsion MA",                   "WRMA  | Well Rounded MA"
                                                                     ], group = "Main Calculation Settings")
chgvolt_dz_len      = input.int(title = "CHGVOLT Deadzone Length", defval = 55, minval = 1, group = "Main Calculation Settings")
chgvolt_do_barcolor = input.bool(title = "CHGVOLT Show Bar Color", defval = false)

// Specific ma settings
chgvolt_dz_alma_offset = input.float(title = "Offset",    defval = 0.85, step = 0.05, group = "Deadzone ALMA Settings")
chgvolt_dz_alma_sigma  = input.int(title   = "Sigma", defval = 6,                     group = "Deadzone ALMA Settings")

chgvolt_dz_kama_fastLength = input(title = "Fast EMA Length", defval=2,  group = "Deadzone KAMA Settings")
chgvolt_dz_kama_slowLength = input(title = "Slow EMA Length", defval=30, group = "Deadzone KAMA Settings")

chgvolt_dz_vama_vol_len = input.int(title  = "Volatality Length", defval = 51,            group = "Deadzone VAMA Settings")
chgvolt_dz_vama_do_smth = input.bool(title = "Do Smoothing?",     defval = false,         group = "Deadzone VAMA Settings")
chgvolt_dz_vama_smth    = input.int(title  = "Smoothing length",  defval = 5, minval = 1, group = "Deadzone VAMA Settings")

chgvolt_dz_mf_beta     = input.float(title = "Beta",               defval = 0.8, step = 0.1, minval = 0, maxval=1,   group = "Deadzone Modular Filter Settings")
chgvolt_dz_mf_feedback = input.bool(title  = "Feedback?",          defval = false,                                   group = "Deadzone Modular Filter Settings")
chgvolt_dz_mf_z        = input.float(title = "Feedback Weighting", defval = 0.5, step = 0.1, minval = 0, maxval = 1, group = "Deadzone Modular Filter Settings")

chgvolt_dz_eit_alpha = input.float(title = "Alpha", defval = 0.07, step = 0.01, minval = 0.0, group = "Deadzone Ehlers Instantaneous Trendline Settings")

chgvolt_dz_ssma_pow = input.float(title = "Power", defval = 4.0, step = 0.5, minval = 0.0, group = "Deadzone SSMA Settings")
chgvolt_dz_ssma_smooth = input.bool(title = "Smooth", defval = false, group = "Deadzone SSMA Settings")

chgvolt_dz_rsrma_pw = input.float(title = "Percent Width", defval = 60.0, step = 10.0, minval = 0.0, maxval = 100.0, group = "Deadzone RSRMA Settings")

chgvolt_dz_svama_method            = input.string(title = "Max", options=["Max", "Min"], defval = "Max", group = "Deadzone SVAMA Settings")
chgvolt_dz_svama_vol_or_volatility = input.string(title = "Use Volume or Volatility?", options = ["Volume", "Volatility"], defval = "Volatility", group = "Deadzone SVAMA Settings")

chgvolt_dz_wrma_smooth = input.bool(title = "Extra Smoothing?", defval = false, group = "Deadzone WRMA Settings")
//==============================================================================


//==============================================================================
// Function: Default MT4/MT5 Momentum calculation method
//==============================================================================
get_mom_mt(mom_length, mom_src) =>
    mom_src * 100 / mom_src[mom_length]
//==============================================================================


//==============================================================================
// Calculating the change of volatility histogram
//==============================================================================
chgvolt_momentum = get_mom_mt(chgvolt_mom_len, chgvolt_mom_src)
chgvolt = ta.stdev(chgvolt_momentum, chgvolt_short_len, true) / ta.stdev(chgvolt_momentum, chgvolt_long_len, true)
//==============================================================================


//==============================================================================
// Calculating the deadzone (long term ma of the change of volatility histogram)
//==============================================================================
chgvolt_dz = get_ma_out(chgvolt_dz_ma_type, chgvolt, chgvolt_dz_len, chgvolt_dz_alma_offset, chgvolt_dz_alma_sigma, chgvolt_dz_kama_fastLength, chgvolt_dz_kama_slowLength, chgvolt_dz_vama_vol_len, chgvolt_dz_vama_do_smth, chgvolt_dz_vama_smth, chgvolt_dz_mf_beta, chgvolt_dz_mf_feedback, chgvolt_dz_mf_z, chgvolt_dz_eit_alpha, chgvolt_dz_ssma_pow, chgvolt_dz_ssma_smooth, chgvolt_dz_rsrma_pw, chgvolt_dz_svama_method, chgvolt_dz_svama_vol_or_volatility, chgvolt_dz_wrma_smooth)
//==============================================================================


//==============================================================================
// Coloring and plotting the change of volatility line and the deadzone
//==============================================================================
chgvolt_plot_color = chgvolt > chgvolt_dz ? color.green : color.gray

plot(chgvolt, title = "Change of Volatility Histogram", color = chgvolt_plot_color, style = plot.style_columns)
plot(chgvolt_dz, title = "Change of Volatility Deadzone Line", color = color.black)
//==============================================================================


//==============================================================================
// Calculating and plotting the change of volatility bar coloring
//==============================================================================
barcolor(chgvolt_do_barcolor ? chgvolt_plot_color : na, title = "Change of Volatility Bar Color")
//==============================================================================