Jurik CFB Adaptive QQE [Loxx]
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © loxx

//@version=5
indicator("Jurik CFB Adaptive QQE [Loxx]", 
     shorttitle = "JCFBAQQE [Loxx]", 
     overlay = false, 
     timeframe="", 
     timeframe_gaps = true, 
     max_bars_back = 2000)

greencolor = #2DD204
redcolor = #D2042D 

RMA(x, t) =>
    _rma = x
    _rma := na(_rma[1]) ? x : (x - nz(_rma[1])) * (1/t) + nz(_rma[1])
    _rma

EMA(x, t, a) =>
    _ema = x
    _ema := na(_ema[1]) ? x : (x - nz(_ema[1])) * a + nz(_ema[1])
    _ema

_a_jurik_filt(src, len, phase) =>
    //static variales
    volty = 0.0, avolty = 0.0, vsum = 0.0, bsmax = src, bsmin = src
    len1 = math.max(math.log(math.sqrt(0.5 * (len-1))) / math.log(2.0) + 2.0, 0)
    len2 = math.sqrt(0.5 * (len - 1)) * len1
    pow1 = math.max(len1 - 2.0, 0.5)
    beta = 0.45 * (len - 1) / (0.45 * (len - 1) + 2)
    div = 1.0 / (10.0 + 10.0 * (math.min(math.max(len-10, 0), 100)) / 100)
    phaseRatio = phase < -100 ? 0.5 : phase > 100 ? 2.5 : 1.5 + phase * 0.01
    bet = len2 / (len2 + 1)

    //Price volatility
    del1 = src - nz(bsmax[1])
    del2 = src - nz(bsmin[1])
    volty := math.abs(del1) > math.abs(del2) ? math.abs(del1) : math.abs(del2)

    //Relative price volatility factor
    vsum := nz(vsum[1]) + div * (volty - nz(volty[10]))
    avolty := nz(avolty[1]) + (2.0 / (math.max(4.0 * len, 30) + 1.0)) * (vsum - nz(avolty[1]))
    dVolty = avolty > 0 ? volty / avolty : 0
    dVolty := math.max(1, math.min(math.pow(len1, 1.0/pow1), dVolty))
    
    //Jurik volatility bands
    pow2 = math.pow(dVolty, pow1)
    Kv = math.pow(bet, math.sqrt(pow2))
    bsmax := del1 > 0 ? src : src - Kv * del1
    bsmin := del2 < 0 ? src : src - Kv * del2
    
    //Jurik Dynamic Factor
    alpha = math.pow(beta, pow2)

    //1st stage - prelimimary smoothing by adaptive EMA
    jma = 0.0, ma1 = 0.0, det0 = 0.0, e2 = 0.0
    ma1 := (1 - alpha) * src + alpha * nz(ma1[1])
    
    //2nd stage - one more prelimimary smoothing by Kalman filter
    det0 := (src - ma1) * (1 - beta) + beta * nz(det0[1])
    ma2 = ma1 + phaseRatio * det0

    //3rd stage - final smoothing by unique Jurik adaptive filter
    e2 := (ma2 - nz(jma[1])) * math.pow(1 - alpha, 2) + math.pow(alpha, 2) * nz(e2[1])
    jma := e2 + nz(jma[1])
    jma

_jcfbaux(src, depth)=>
    jrc04 = 0.0, jrc05 = 0.0, jrc06 = 0.0, jrc13 = 0.0, jrc03 = 0.0, jrc08 = 0.0
    if (bar_index >= bar_index - depth * 2) 
        for k = depth - 1 to 0
            jrc04 += math.abs(nz(src[k]) - nz(src[k+1]))
            jrc05 += (depth + k) * math.abs(nz(src[k]) - nz(src[k+1]))
            jrc06 += nz(src[k+1])
    if(bar_index < bar_index - depth * 2)
        jrc03 := math.abs(src - nz(src[1]))
        jrc13 := math.abs(nz(src[depth]) - nz(src[depth+1]))
        jrc04 := jrc04 - jrc13 + jrc03
        jrc05 := jrc05 - jrc04 + jrc03 * depth
        jrc06 := jrc06 - nz(src[depth+1]) + nz(src[1])
    jrc08 := math.abs(depth * src - jrc06)
    jcfbaux = jrc05 == 0.0 ? 0.0 : jrc08/jrc05
    jcfbaux

_jcfb(src, len, smth) =>
    a1 = array.new<float>(0, 0)
    c1 = array.new<float>(0, 0)
    d1 = array.new<float>(0, 0)
    cumsum = 2.0, result = 0.0

    //crete an array of 2^index, pushed onto itself
    //max values with injest of depth = 10: [1, 1, 2, 2, 4, 4, 8, 8, 16, 16, 32, 32, 64, 64, 128, 128, 256, 256, 512, 512]
    for i = 0 to len -1
        array.push(a1, math.pow(2, i))
        array.push(a1, math.pow(2, i))
        
    //cumulative sum of 2^index array
    //max values with injest of depth = 10: [2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536]
    for i = 0 to array.size(a1) - 1 
        array.push(c1, cumsum)
        cumsum += array.get(a1, i)
        
    //add values from jcfbaux calculation using the cumumulative sum arrary above
    for i = 0 to array.size(c1) - 1 
        array.push(d1, _jcfbaux(src, array.get(c1, i)))
    
    baux = array.copy(d1)
    cnt = array.size(baux)
    arrE = array.new<float>(cnt, 0)
    arrX = array.new<float>(cnt, 0)
    
    if bar_index <= smth
        for j = 0 to bar_index - 1
            for k = 0 to cnt - 1
                eget = array.get(arrE, k)
                array.set(arrE, k, eget + nz(array.get(baux, k)[bar_index - j]))
        for k = 0 to cnt - 1
            eget = nz(array.get(arrE, k))
            array.set(arrE, k, eget / bar_index)
    else
        for k = 0 to cnt - 1
            eget = nz(array.get(arrE, k))
            array.set(arrE, k, eget + (nz(array.get(baux, k)) - nz(array.get(baux, k)[smth])) / smth)
    if bar_index > 5
        a = 1.0, b = 1.0
        for k = 0 to cnt - 1
            if (k % 2 == 0)
                array.set(arrX, k, b * nz(array.get(arrE, k)))
                b := b * (1 - nz(array.get(arrX, k)))
            else
                array.set(arrX, k, a * nz(array.get(arrE, k)))
                a := a * (1 - nz(array.get(arrX, k)))
        sq = 0.0, sqw = 0.0 
        for i = 0 to cnt - 1
            sqw += nz(array.get(arrX, i)) * nz(array.get(arrX, i)) * nz(array.get(c1, i))
        for i = 0 to array.size(arrX) - 1
            sq += math.pow(nz(array.get(arrX, i)), 2)
        result := sq == 0.0 ? 0.0 : sqw / sq
        result

_rsx_rsi(src, len)=>
    src_out = 100 * src
    mom0 = ta.change(src_out)
    moa0 = math.abs(mom0)
    Kg = 3 / (len + 2)
    Hg = 1 - Kg
 
    //mom
    f28 = 0.0, f30 = 0.0
    f28 := Kg * mom0 + Hg * nz(f28[1])
    f30 := Hg * nz(f30[1]) + Kg * f28
    mom1 = f28 * 1.5 - f30 * 0.5
 
    f38 = 0.0, f40 = 0.0
    f38 := Hg * nz(f38[1]) + Kg * mom1
    f40 := Kg * f38 + Hg * nz(f40[1])
    mom2 = f38 * 1.5 - f40 * 0.5
 
    f48 = 0.0, f50 = 0.0
    f48 := Hg * nz(f48[1]) + Kg * mom2
    f50 := Kg * f48 + Hg * nz(f50[1])
    mom_out = f48 * 1.5 - f50 * 0.5
 
    //moa
    f58 = 0.0, f60 = 0.0
    f58 := Hg * nz(f58[1]) + Kg * moa0
    f60 := Kg * f58 + Hg * nz(f60[1])
    moa1 = f58 * 1.5 - f60 * 0.5
 
    f68 = 0.0, f70 = 0.0
    f68 := Hg * nz(f68[1]) + Kg * moa1
    f70 := Kg * f68 + Hg * nz(f70[1])
    moa2 = f68 * 1.5 - f70 * 0.5
 
    f78 = 0.0, f80 = 0.0
    f78 := Hg * nz(f78[1]) + Kg * moa2
    f80 := Kg * f78 + Hg * nz(f80[1])
    moa_out = f78 * 1.5 - f80 * 0.5
 
    rsx = math.max(math.min((mom_out / moa_out + 1.0) * 50.0, 100.00), 0.00)
 
    rsx

_wilders_rsi(x, y) => 
    u = math.max(x - x[1], 0) 
    d = math.max(x[1] - x, 0)  
    rs = RMA(u, y) / RMA(d, y)
    res = 100 - 100 / (1 + rs)
    res
    
_rapid_rsi(src, len)=>
    upSum = math.sum(math.max(ta.change(src), 0), len)
    dnSum = math.sum(math.max(-ta.change(src), 0), len)
    rrsi = dnSum == 0 ? 100 : upSum == 0 ? 0 : 100 - 100 / (1 + upSum / dnSum)
    rrsi 
    
rsiType = input.string("RSX", "RSI Type", options =["Wilders", "RSX", "Rapid"], group = "Basic Settings")
adaptOn = input.string("CFB Adaptive", "RSI Calculation Type", options = ["Fixed", "CFB Adaptive"], group =  "Basic Settings")


src = input.source(close, "Source", group = "Basic Settings")
rsiPeriodIn = input.int(14, title='RSI Period', minval = 1, group = "Basic Settings")
RsiSmoothingFactor = input.int(5, title='Smoothing Factor', group = "Basic Settings")

RsiPriceSmoothing  = input.int(1, "Source Smoothing Period", minval = 1, group = "Basic Settings")

WPFast = input.float(2.618, "WP Fast Coeffient", group = "Basic Settings")
WPSlow = input.float(4.236, "WP Slow Coeffient", group = "Basic Settings")

overbought = input.int(80, "Overbought Level", group =  "Basic Settings")
oversold = input.int(20, "Oversold Level", group =  "Basic Settings")


cfb_src = input.source(hlcc4, "CFB Source", group = "CFB Ingest Settings")

nlen = input.int(50, "CFB Normal Length", minval = 1, group = "CFB Ingest Settings")
cfb_len = input.int(10, "CFB Depth", maxval = 10, group = "CFB Ingest Settings")

smth = input.int(8, "CFB Smooth Length", minval = 1, group = "CFB Ingest Settings")
slim = input.int(10, "CFB Short Limit", minval = 1, group = "CFB Ingest Settings")
llim = input.int(20, "CFB Long Limit", minval = 1, group = "CFB Ingest Settings")

jcfbsmlen = input.int(10, "CFB Jurik Smooth Length", minval = 1, group = "CFB Ingest Settings")
jcfbsmph = input.float(0, "CFB Jurik Smooth Phase", group = "CFB Ingest Settings")

colorbars = input.bool(false, "Color bars?", group = "UI Options")

cfb_draft = _jcfb(cfb_src, cfb_len, smth)
cfb_pre = _a_jurik_filt(_a_jurik_filt(cfb_draft, jcfbsmlen, jcfbsmph), jcfbsmlen, jcfbsmph)

max = ta.highest(cfb_pre, nlen)
min = ta.lowest(cfb_pre, nlen)
denom = max - min
ratio = (denom > 0) ? (cfb_pre - min) / denom : 0.5
len_out_cfb = math.ceil(slim + ratio * (llim - slim))

len_out_cfb := math.ceil(len_out_cfb) < 1 ? 1 : math.ceil(len_out_cfb)

rsiPeriod = adaptOn == "Fixed" ? rsiPeriodIn  : len_out_cfb
rsiPeriod := nz(rsiPeriod) < 1 ? 1 : nz(rsiPeriod)

closer = ta.sma(src, RsiPriceSmoothing)

rsi = rsiType == "Wilders" ? _wilders_rsi(closer, rsiPeriod) : rsiType == "Rapid" ? _rapid_rsi(closer, rsiPeriod) :  _rsx_rsi(closer, rsiPeriod) 

alpha1 = 2.0/(RsiSmoothingFactor+1.0)
alpha2 = 1.0/(math.max(rsiPeriod, 1))

rsiMA = EMA(rsi, RsiSmoothingFactor, alpha1)

MaAtrRsi = EMA(math.abs(rsiMA[1] - rsiMA), rsiPeriodIn, alpha2)
outer = EMA(MaAtrRsi, rsiPeriodIn, alpha2)

emaf = outer * WPFast
emas = outer * WPSlow

TrendSlow = 0.0
tr = nz(TrendSlow[1])
dv = tr

if (rsiMA < tr) 
    tr := rsiMA + emas
    if ((nz(rsiMA[1]) < dv and tr > dv)) 
        tr := dv
       
if (rsiMA > tr) 
    tr := rsiMA - emas
    if ((nz(rsiMA[1]) > dv) and (tr < dv))
        tr := dv

TrendSlow := tr

TrendFast = 0.0
tr := nz(TrendFast[1])
dv := tr

if (rsiMA < tr) 
    tr := rsiMA + emaf
    if ((nz(rsiMA[1]) < dv and tr > dv)) 
        tr := dv
       
if (rsiMA > tr) 
    tr := rsiMA - emaf
    if ((nz(rsiMA[1]) > dv) and (tr < dv))
        tr := dv

TrendFast := tr

plot(TrendSlow, "Slow Trend", color=color.new(color.yellow, 0),linewidth = 1, style=plot.style_circles)
plot(TrendFast, "Fast Trend", color=color.new(color.white, 0), linewidth = 1)

colout =  
     rsiMA > TrendSlow and rsiMA > TrendFast ? greencolor : 
     rsiMA < TrendSlow and rsiMA < TrendFast ? redcolor : 
     color.gray

plot(rsiMA,"RSIMA", color = colout, linewidth = 3)


plot(50, color=color.new(color.gray, 30), linewidth=1, style=plot.style_circles, title = "Zero line")


tl = plot(overbought, color=color.rgb(0, 255, 255, 100), style=plot.style_line, title = "Overbought Level 2")
tl1 = plot(overbought-5, color=color.rgb(0, 255, 255, 100), style=plot.style_line, title = "Overbought Level 1")

bl1 = plot(oversold, color=color.rgb(255, 255, 255, 100), style=plot.style_line, title = "Overbought Level 1")
bl = plot(oversold+5, color=color.rgb(255, 255, 255, 100), style=plot.style_line, title = "Overbought Level 2")

fill(tl, tl1, color=color.new(color.gray, 85), title = "Top Boundary Fill Color")
fill(bl, bl1, color=color.new(color.gray, 85), title = "Bottom Boundary Fill Color")

barcolor(colorbars ? colout : na)

































