Regularized-Moving-Average Oscillator Suite
// This work is licensed under a Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) https://creativecommons.org/licenses/by-nc-sa/4.0/
// © EliCobra

//@version=5
indicator("Regularized-MA Oscillator Suite", "{Ʌ} - MA Osc. Suite", false)

f_kama(src, len, kamaf, kamas) =>
    white = math.abs(src - src[1])
    ama = 0.0
    nsignal = math.abs(src - src[len])
    nnoise = math.sum(white, len)
    nefratio = nnoise != 0 ? nsignal / nnoise : 0
    nsmooth = math.pow(nefratio * (kamaf - kamas) + kamas, 2) 
    ama := nz(ama[1]) + nsmooth * (src - nz(ama[1]))

    ama

f_t3(src, len) =>
    x1 = ta.ema(src, len)
    x2 = ta.ema(x1, len)
    x3 = ta.ema(x2, len)
    x4 = ta.ema(x3, len)
    x5 = ta.ema(x4, len)
    x6 = ta.ema(x5, len)
    b = 0.7
    c1 = - math.pow(b, 3)
    c2 = 3 * math.pow(b, 2) + 3 * math.pow(b, 3)
    c3 = -6 * math.pow(b, 2) - 3 * b - 3 * math.pow(b, 3)
    c4 = 1 + 3 * b + math.pow(b, 3) + 3 * math.pow(b, 2)
    
    c1 * x6 + c2 * x5 + c3 * x4 + c4 * x3

f_ehma(src, length) =>  
    ta.ema(2 * ta.ema(src, length / 2) - ta.ema(src, length), math.round(math.sqrt(length)))

f_thma(src, length) =>  
    ta.wma(ta.wma(src, length / 3) * 3 - ta.wma(src, length / 2) - ta.wma(src, length), length)

f_tema(src, len) =>
    x = ta.ema(src, len)
    y = ta.ema(x, len)
    z = ta.ema(y, len)
    
    3 * x - 3 * y + z

f_dema(src, len) =>
    x = ta.ema(src, len)
    y = ta.ema(x, len)

    2 * x - y

f_ma(src, len, type, kamaf, kamas, offset, sigma) =>
    x = switch type
        "SMA" => ta.sma(src, len)
        "EMA" => ta.ema(src, len)
        "HMA" => ta.hma(src, len)
        "RMA" => ta.rma(src, len)
        "WMA" => ta.wma(src, len)
        "VWMA" => ta.vwma(src, len)
        "ALMA" => ta.alma(src, len, offset, sigma)
        "DEMA" => f_dema(src, len)
        "TEMA" => f_tema(src, len)
        "EHMA" => f_ehma(src, len)
        "THMA" => f_thma(src, len)
        "T3" => f_t3(src, len)
        "KAMA" => f_kama(src, len, kamaf, kamas)
        "LSMA" => ta.linreg(src, len, 0)
    
    x

matype = input.string("EMA", "Type", ["SMA", "EMA", "DEMA", "TEMA", "HMA", "EHMA", "THMA", "RMA", "WMA", "VWMA", "T3", "KAMA", "ALMA", "LSMA"], group = "MA Settings")
src = input.source(close, "Source", inline = "1", group = "MA Settings")
len = input.int(14, "Length", inline = "1", group = "MA Settings")
kamaf = input.float(0.666, "Kaufman Fast", group = "MA Settings")
kamas = input.float(0.0645, "Kaufman Slow", group = "MA Settings")
offset = input.float(0.85, "ALMA Offset", group = "MA Settings")
sigma = input.int(6, "ALMA Sigma", group = "MA Settings")
norm = input.int(30, "Regularize Length", group = "Oscillator Settings")
revt = input.int(3, "Reversion Threshold", options = [1, 2, 3], group = "Oscillator Settings")
revshow = input.bool(true, "Show Reversal Signals", group = "UI Options")
colbar = input.string("None", "Bar Coloring", ["None", "Trend", "Extremities", "Reversions", "Slope"], group = "UI Options")

ma = f_ma(src, len, matype, kamaf, kamas, offset, sigma)

mean = ta.sma(ma, norm)
dev = ta.stdev(ma, norm)
zmean = (ma - mean) / dev

hline(0, "Mid Line", #ffffff80, hline.style_solid)

max = hline(4, display = display.none)
hh = hline(3, display = display.none)
lh = hline(2, display = display.none)

fill(lh, hh, color = #bb001028)
fill(hh, max, color = #bb00104d)

min = hline(-4, display = display.none)
ll = hline(-3, display = display.none)
hl = hline(-2, display = display.none)

fill(ll, hl, color = #00b35128)
fill(ll, min, color = #00b35144)

z = plot(zmean, "Z", zmean > 0 ? #00b350 : #bb0010)
mid = plot(0, display = display.none, editable = false)

fill(z, mid, zmean > 0 ? zmean : 0, zmean > 0 ? 0 : zmean, zmean > 0 ? #00b351a1 : #00000000, zmean > 0 ? #00000000 : #bb0010b9)

plotchar(revshow ? zmean > revt and zmean < zmean[1] and not (zmean[1] < zmean[2]) ? zmean + 0.5 : na : na, "OB", "⚬", location.absolute, #bb0010, size = size.tiny)
plotchar(revshow ? zmean < -revt and zmean > zmean[1] and not (zmean[1] > zmean[2]) ? zmean - 0.5 : na : na, "OS", "⚬", location.absolute, #00b350, size = size.tiny)

color col = switch colbar
    "None" => na
    "Trend" => zmean > 0 ? #00b350 : #bb0010
    "Extremities" => zmean > 2 ? #00b350 : zmean < -2 ? #bb0010 : #b3b3b3c2
    "Reversions" => zmean > revt and zmean < zmean[1] and not (zmean[1] < zmean[2]) ? #bb0010 : zmean < -revt and zmean > zmean[1] and not (zmean[1] > zmean[2]) ? #00b350 : #b3b3b3c2
    "Slope" => zmean > zmean[1] ? #00b350 : #bb0010

barcolor(col)

if zmean < -revt and zmean > zmean[1] and not (zmean[1] > zmean[2])
    alert("OverSold")

if zmean > revt and zmean < zmean[1] and not (zmean[1] < zmean[2])
    alert("OverBought")