Keltner Channel Volatility Filter
// 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 = "Keltner Channel Volatility Filter", shorttitle = "KCVF", overlay = true)

//==============================================================================
// 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
//==============================================================================
// Calculation Settins
kc_length = input.int(title = "Keltner Channel Length", defval = 20, minval = 1, group = "Keltner Channel Volatility Filter Settings")
kc_mult = input.float(title = "Keltner Channel Multiplier", defval = 1.0, minval = 0.00001, step = 0.25, group = "Keltner Channel Volatility Filter Settings")
kc_src = input.source(title = "Keltner Channel Calculation Source", defval = close, group = "Keltner Channel Volatility Filter Settings")
kcvf_src = input.source(title = "Keltner Channel Price Filter Source", defval = hl2, group = "Keltner Channel Volatility Filter Settings")
kc_ma_type = input.string('EMA  | Exponential MA', '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 = "Keltner Channel Volatility Filter Settings")
kc_band_style = input.string( title="Bands Style", defval = "Average True Range", options = ["Average True Range", "True Range", "Range"], group = "Keltner Channel Volatility Filter Settings")
kc_atr_length = input(title = "ATR Length", defval = 10, group = "Keltner Channel Volatility Filter Settings")


// Display Settings
kcvf_do_box      = input.bool(title = "Display Grey Box Around Low Volatility Candles", defval = true, group = "Keltner Channel Volatility Filter Settings")
kcvf_do_barcolor = input.bool(title = "Color Low Volatility Candles Gray", defval = false, group = "Keltner Channel Volatility Filter Settings")


// Specific MA settings
kc_alma_offset = input.float(title = "Offset",    defval = 0.85, step = 0.05, group = 'KCVF ALMA Settings')
kc_alma_sigma  = input.int(title   = 'Sigma', defval = 6,                     group = 'KCVF ALMA Settings')

kc_kama_fastLength = input(title = 'Fast EMA Length', defval=2,  group = 'KCVF KAMA Settings')
kc_kama_slowLength = input(title = 'Slow EMA Length', defval=30, group = 'KCVF KAMA Settings')

kc_vama_vol_len = input.int(title  = 'Volatality Length', defval = 51,            group = 'KCVF VAMA Settings')
kc_vama_do_smth = input.bool(title = 'Do Smoothing?',     defval = false,         group = 'KCVF VAMA Settings')
kc_vama_smth    = input.int(title  = 'Smoothing length',  defval = 5, minval = 1, group = 'KCVF VAMA Settings')

kc_mf_beta     = input.float(title = 'Beta',               defval = 0.8, step = 0.1, minval = 0, maxval=1,   group = 'KCVF Modular Filter Settings')
kc_mf_feedback = input.bool(title  = 'Feedback?',          defval = false,                                   group = 'KCVF Modular Filter Settings')
kc_mf_z        = input.float(title = 'Feedback Weighting', defval = 0.5, step = 0.1, minval = 0, maxval = 1, group = 'KCVF Modular Filter Settings')

kc_eit_alpha = input.float(title = 'Alpha', defval = 0.07, step = 0.01, minval = 0.0, group = 'KCVF Ehlers Instantaneous Trendline Settings')

kc_ssma_pow = input.float(title = 'Power', defval = 4.0, step = 0.5, minval = 0.0, group = 'KCVF SSMA Settings')
kc_ssma_smooth = input.bool(title = 'Smooth', defval = false, group = 'KCVF SSMA Settings')

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

kc_svama_method            = input.string(title = 'Max', options=['Max', 'Min'], defval = 'Max', group = 'KCVF SVAMA Settings')
kc_svama_vol_or_volatility = input.string(title = 'Use Volume or Volatility?', options = ['Volume', 'Volatility'], defval = 'Volatility', group = 'KCVF SVAMA Settings')

kc_wrma_smooth = input.bool(title = "Extra Smoothing?", defval = false, group = 'KCVF WRMA Settings')
//==============================================================================


//==============================================================================
// Keltner Channels Indicator
//==============================================================================
kc_ma = get_ma_out(kc_ma_type, kc_src, kc_length, kc_alma_offset, kc_alma_sigma, kc_kama_fastLength, kc_kama_slowLength, kc_vama_vol_len, kc_vama_do_smth, kc_vama_smth, kc_mf_beta, kc_mf_feedback, kc_mf_z, kc_eit_alpha, kc_ssma_pow, kc_ssma_smooth, kc_rsrma_pw, kc_svama_method, kc_svama_vol_or_volatility, kc_wrma_smooth)
kc_rangema = kc_band_style == "True Range" ? ta.tr(true) : kc_band_style == "Average True Range" ? ta.atr(kc_atr_length) : ta.rma(high - low, kc_length)
kc_upper = kc_ma + kc_rangema * kc_mult
kc_lower = kc_ma - kc_rangema * kc_mult
//==============================================================================


//==============================================================================
// Determining if price is within the keltner channels
//==============================================================================
price_within_kc = kcvf_src <= kc_upper and kcvf_src >= kc_lower ? true : false
//==============================================================================


//==============================================================================
// Plotting the grey box/changing bar color if price is within the KC
//==============================================================================
// Plotting grey box
kc_upper_plot = plot(kcvf_do_box ? kc_upper : na, title = "KCVF Upper", color = color.new(#000000, 100))
kc_lower_plot = plot(kcvf_do_box ? kc_lower : na, title = "KCVF Lower", color = color.new(#000000, 100))
fill(kc_upper_plot, kc_lower_plot, title = "KCVF Background", color = kcvf_do_box and price_within_kc ? color.new(#000000, 70) : color.new(#000000, 100))

// Changing bar color
kcvf_barcolor = kcvf_do_barcolor and price_within_kc ? color.new(color.gray, 0) : na
barcolor(kcvf_barcolor, title = "KCVF Barcolor")
//==============================================================================