Aroon Oscillator of Adaptive RSI [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("Aroon Oscillator of Adaptive RSI [Loxx]", shorttitle ="AO_ARSI [Loxx]", overlay = false, timeframe="", timeframe_gaps=true)

greencolor = #2DD204  
redcolor = #D2042D 

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

_rsx_rsi(src, len)=>
    f8 = 100 * src
    f10 = nz(f8[1])
    v8 = f8 - f10
    f18 = 3 / (len + 2)
    f20 = 1 - f18
    f28 = 0.0
    f28 := f20 * nz(f28[1]) + f18 * v8
    f30 = 0.0
    f30 := f18 * f28 + f20 * nz(f30[1])
    vC = f28 * 1.5 - f30 * 0.5
    f38 = 0.0
    f38 := f20 * nz(f38[1]) + f18 * vC
    f40 = 0.0
    f40 := f18 * f38 + f20 * nz(f40[1])
    v10 = f38 * 1.5 - f40 * 0.5
    f48 = 0.0
    f48 := f20 * nz(f48[1]) + f18 * v10
    f50 = 0.0
    f50 := f18 * f48 + f20 * nz(f50[1])
    v14 = f48 * 1.5 - f50 * 0.5
    f58 = 0.0
    f58 := f20 * nz(f58[1]) + f18 * math.abs(v8)
    f60 = 0.0
    f60 := f18 * f58 + f20 * nz(f60[1])
    v18 = f58 * 1.5 - f60 * 0.5
    f68 = 0.0
    f68 := f20 * nz(f68[1]) + f18 * v18
    f70 = 0.0
    f70 := f18 * f68 + f20 * nz(f70[1])
    v1C = f68 * 1.5 - f70 * 0.5
    f78 = 0.0
    f78 := f20 * nz(f78[1]) + f18 * v1C
    f80 = 0.0
    f80 := f18 * f78 + f20 * nz(f80[1])
    v20 = f78 * 1.5 - f80 * 0.5
    f88_ = 0.0
    f90_ = 0.0
    f88 = 0.0
    f90_ := nz(f90_[1]) == 0 ? 1 : nz(f88[1]) <= nz(f90_[1]) ? nz(f88[1]) + 1 : nz(f90_[1]) + 1
    f88 := nz(f90_[1]) == 0 and len - 1 >= 5 ? len - 1 : 5
    f0 = f88 >= f90_ and f8 != f10 ? 1 : 0
    f90 = f88 == f90_ and f0 == 0 ? 0 : f90_
    v4_ = f88 < f90 and v20 > 0 ? (v14 / v20 + 1) * 50 : 50
    rsx = v4_ > 100 ? 100 : v4_ < 0 ? 0 : v4_
    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
    
_bpDom(len, bpw, mult) =>
    HP = 0.0
    BP = 0.0
    Peak = 0.0
    Real = 0.0
    counter = 0.0
    DC = 0.0
    alpha2 = (math.cos(0.25 * bpw * 2 * math.pi / len) + math.sin(0.25 * bpw * 2 * math.pi  / len) - 1) / math.cos(0.25 * bpw * 2 * math.pi / len)
    HP := (1 + alpha2 / 2) * (close - nz(close[1])) + (1 - alpha2) * nz(HP[1])
    beta1 = math.cos(2 * math.pi / len)
    gamma1 = 1 / math.cos(2 *  math.pi * bpw / len)
    alpha1 = gamma1 - math.sqrt(gamma1 * gamma1 - 1)
    BP := 0.5 * (1 - alpha1) * (HP - nz(HP[2])) + beta1 * (1 + alpha1) * nz(BP[1]) - alpha1 * nz(BP[2])
    BP := bar_index == 1 or bar_index == 2  ? 0 : BP
    Peak := 0.991 * Peak
    Peak := math.abs(BP) > Peak ? math.abs(BP) : Peak
    Real := Peak != 0 ?  BP / Peak : Real
    DC := nz(DC[1])
    DC := DC < 6 ? 6 : DC
    counter := counter[1] + 1
    if ta.crossover(Real, 0) or ta.crossunder(Real, 0)
        DC := 2 * counter
        if 2 * counter > 1.25 * nz(DC[1])
            DC := 1.25 * DC[1]
        if 2 * counter < 0.8 * nz(DC[1])
            DC := 0.8 * nz(DC[1])
        counter := 0
    temp_out = mult * DC
    temp_out

rsiType = input.string("Wilders'", "RSI Type", options =["Wilders'", "RSX", "Rapid"], group = "Basic Settings")

adaptOn = input.string("VHF Adaptive", "RSI Calculation Type", options = ["Fixed", "VHF Adaptive", "Band-pass Adaptive"], group =  "Basic Settings")
src = input.source(close, "Source", group = "Basic Settings")
rsiPerIn = input.int(15, "RSI Period", group =  "Basic Settings")
AroonPeriod = input.int(15, "Aroon Period", group =  "Basic Settings")

bp_period = input.int(13, "Band-pass Period", minval = 1, group = "Band-pass")
bp_width = input.float(0.20, "Band-pass Width", step = 0.1, group = "Band-pass")
cyclelen = input.float(100, "Percent of Dominant Cycle (%)", step = 1.0, group = "Band-pass")/100

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

vmax = ta.highest(src, rsiPerIn)
vmin = ta.lowest(src, rsiPerIn)
noise = math.sum(math.abs(ta.change(src)), rsiPerIn)

vhf = math.abs(vmax - vmin) / noise
len_out = int(nz(_bpDom(bp_period, bp_width, cyclelen), 1)) < 1 ? 1 : int(nz(_bpDom(bp_period, bp_width, cyclelen), 1))

rsiPeriod = adaptOn == "Fixed" ? rsiPerIn  : adaptOn == "VHF Adaptive" ?  int(-math.log(vhf) * rsiPerIn) : len_out

rsiPeriod := nz(rsiPeriod) < 1 ? 1 : nz(rsiPeriod)

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

upper = 100 * (ta.highestbars(rsi, AroonPeriod + 1) + AroonPeriod)/AroonPeriod
lower = 100 * (ta.lowestbars(rsi, AroonPeriod + 1) + AroonPeriod)/AroonPeriod

plot(upper - lower, "Lower", color =  upper > lower ? greencolor : redcolor, linewidth = 2)

barcolor(colorbars ? upper > lower ? greencolor : redcolor : na)

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









