ER-Adaptive ATR Limit Channels w/ States [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("ER-Adaptive ATR Limit Channels w/ States [Loxx]", 
     overlay = true, 
     timeframe="", 
     timeframe_gaps = true)

import loxx/loxxexpandedsourcetypes/4

greencolor = #2DD204
redcolor = #D2042D 
bluecolor = #042dd2

lightgreencolor = #96E881 
lightredcolor =  #DF4F6C
lightbluecolor =  #4f6cdf

darkGreenColor =  #1B7E02 
darkRedColor = #93021F
darkBlueColor = #021f93

smthtype = input.string("Kaufman", "Heikin-Ashi Better Caculation Type", options = ["AMA", "T3", "Kaufman"], group = "Source Settings")
srcin = input.string("Median", "Source", group = "Source Settings", 
     options = 
     ["Close", "Open", "High", "Low", "Median", "Typical", "Weighted", "Average", "Average Median Body", "Trend Biased", "Trend Biased (Extreme)", 
     "HA Close", "HA Open", "HA High", "HA Low", "HA Median", "HA Typical", "HA Weighted", "HA Average", "HA Average Median Body", "HA Trend Biased", "HA Trend Biased (Extreme)",
     "HAB Close", "HAB Open", "HAB High", "HAB Low", "HAB Median", "HAB Typical", "HAB Weighted", "HAB Average", "HAB Average Median Body", "HAB Trend Biased", "HAB Trend Biased (Extreme)"])

inpAtrPeriod = input.int(14, "ATR Period", group = "Basic Settings")
inpAtrMultiplier = input.float(5, "Multiplier", group = "Basic Settings")
bandlevel = input.int(3, "States", minval = 1, maxval = 3, group = "Basic Settings")
adaptit = input.bool(true, "Make it Adaptive?", group = "Basic Settings")
colorbars = input.bool(false, "Color bars?", group = "UI Options")

kfl=input.float(0.666, title="* Kaufman's Adaptive MA (KAMA) Only - Fast End", group = "Moving Average Inputs")
ksl=input.float(0.0645, title="* Kaufman's Adaptive MA (KAMA) Only - Slow End", group = "Moving Average Inputs")
amafl = input.int(2, title="* Adaptive Moving Average (AMA) Only - Fast", group = "Moving Average Inputs")
amasl = input.int(30, title="* Adaptive Moving Average (AMA) Only - Slow", group = "Moving Average Inputs")
t3hot = input.float(.7, "* T3 Only - T3 Hot", group = "Moving Average Inputs") 
t3swt = input.string("T3 New", "* T3 Only - T3 Type", options = ["T3 New", "T3 Original"], group = "Moving Average Inputs")

mper = (inpAtrPeriod > 1) ? inpAtrPeriod : 1
mfast = math.max(mper / 2.0, 1)
mslow = mper * 5
mperDiff = mslow - mfast
noise = 0., aatr = 0.


haopen = (nz(open[1], open) + nz(close[1], close)) / 2
haclose = (open + close + high + low) / 4
hahigh = math.max(high, open, close) 
halow = math.min(low, open, close)  
hamedian = (hahigh + halow) / 2
hatypical = (hahigh + halow + haclose) / 3
haweighted =  (hahigh + halow + haclose + haclose)/4 
haaverage =  (haopen + hahigh + halow + haclose)/4

srcout(srcin, smthtype)=>
    outer = switch srcin
        "Close" => loxxexpandedsourcetypes.rclose()
        "Open" => loxxexpandedsourcetypes.ropen()
        "High" => loxxexpandedsourcetypes.rhigh()
        "Low" => loxxexpandedsourcetypes.rlow()
        "Median" => loxxexpandedsourcetypes.rmedian()
        "Typical" => loxxexpandedsourcetypes.rtypical()
        "Weighted" => loxxexpandedsourcetypes.rweighted()
        "Average" => loxxexpandedsourcetypes.raverage()
        "Average Median Body" => loxxexpandedsourcetypes.ravemedbody()
        "Trend Biased" => loxxexpandedsourcetypes.rtrendb()
        "Trend Biased (Extreme)" => loxxexpandedsourcetypes.rtrendbext()
        "HA Close" => loxxexpandedsourcetypes.haclose(haclose)
        "HA Open" => loxxexpandedsourcetypes.haopen(haopen)
        "HA High" => loxxexpandedsourcetypes.hahigh(hahigh)
        "HA Low" => loxxexpandedsourcetypes.halow(halow)
        "HA Median" => loxxexpandedsourcetypes.hamedian(hamedian)
        "HA Typical" => loxxexpandedsourcetypes.hatypical(hatypical)
        "HA Weighted" => loxxexpandedsourcetypes.haweighted(haweighted)
        "HA Average" => loxxexpandedsourcetypes.haaverage(haaverage)
        "HA Average Median Body" => loxxexpandedsourcetypes.haavemedbody(haclose, haopen)
        "HA Trend Biased" => loxxexpandedsourcetypes.hatrendb(haclose, haopen, hahigh, halow)
        "HA Trend Biased (Extreme)" => loxxexpandedsourcetypes.hatrendbext(haclose, haopen, hahigh, halow)
        "HAB Close" => loxxexpandedsourcetypes.habclose(smthtype, amafl, amasl, kfl, ksl)
        "HAB Open" => loxxexpandedsourcetypes.habopen(smthtype, amafl, amasl, kfl, ksl)
        "HAB High" => loxxexpandedsourcetypes.habhigh(smthtype, amafl, amasl, kfl, ksl)
        "HAB Low" => loxxexpandedsourcetypes.hablow(smthtype, amafl, amasl, kfl, ksl)
        "HAB Median" => loxxexpandedsourcetypes.habmedian(smthtype, amafl, amasl, kfl, ksl)
        "HAB Typical" => loxxexpandedsourcetypes.habtypical(smthtype, amafl, amasl, kfl, ksl)
        "HAB Weighted" => loxxexpandedsourcetypes.habweighted(smthtype, amafl, amasl, kfl, ksl)
        "HAB Average" => loxxexpandedsourcetypes.habaverage(smthtype, amafl, amasl, kfl, ksl)
        "HAB Average Median Body" => loxxexpandedsourcetypes.habavemedbody(smthtype, amafl, amasl, kfl, ksl)
        "HAB Trend Biased" => loxxexpandedsourcetypes.habtrendb(smthtype, amafl, amasl, kfl, ksl)
        "HAB Trend Biased (Extreme)" => loxxexpandedsourcetypes.habtrendbext(smthtype, amafl, amasl, kfl, ksl)
	    => haclose
    outer

src = srcout(srcin, smthtype)
diff = math.abs(src - nz(src[1]))

signal = math.abs(src - nz(src[mper]))
noise := nz(noise[1]) + diff - nz(diff[mper])
avgper = (noise != 0) ? (signal / noise) * mperDiff + mfast : mper
aatr := nz(aatr[1]) + (2.0 / (1.0 + avgper)) * ((high - low) - nz(aatr[1]))  

up1 =  high
up2 = high
up3 = high
down1 = low 
down2 = low
down3 = low

_atr = adaptit ? aatr * inpAtrMultiplier : ta.atr(inpAtrPeriod) * inpAtrMultiplier

if high > nz(up3[1]) 
    up3 := high
else if high < nz(up3[1])
    up3 := math.min(high + _atr * 1.0, nz(up3[1]))
else 
    up3 := nz(up3[1])

if high > nz(up2[1])
    up2 := high
else if high < nz(up2[1])
    up2 := math.min(high + _atr * 0.5, nz(up2[1]))
else 
    up2 := nz(up2[1])

if high > nz(up1[1])
    up1 := high
else if high < nz(up1[1])
    up1 := math.min(high + _atr * 0.1, nz(up1[1]))
else 
    up1 := nz(up1[1])


if low < nz(down1[1])
    down1 := low
else if (low > nz(down1[1])) 
    down1 := math.max(low - _atr * 0.1, nz(down1[1]))
else 
    down1 := nz(down1[1])

if low < nz(down2[1])
    down2 := low
else if low > nz(down2[1])
    down2 := math.max(low - _atr * 0.5, nz(down2[1]))
else 
    down2 := nz(down2[1])

if low < nz(down3[1])
    down3 := low
else if low > nz(down3[1])
    down3 := math.max(low - _atr * 1.0, nz(down3[1]))
else 
    down3 := nz(down3[1])

mid = bandlevel == 1 ? (up1 + down1) / 2 : bandlevel == 2 ? (up2 + down2) / 2 : (up3 + down3) / 2

histoup = 0.
histodn = 0.
histoc = 0

histoc := (mid > mid[1]) ? 1 : (mid < mid[1]) ? 2 : histoc[1]

colorout = histoc == 1 ? greencolor : histoc == 2 ? redcolor : color.gray

plot(mid, "Mid", color = colorout, linewidth = 3)

plot(up1, "Level 1 Up", color = color.new(color.gray, 0), linewidth = 1)
plot(up2, "Level 2 Up",color = color.new(darkGreenColor, 0), linewidth = 1)
plot(up3, "Level 3 Up",color = color.new(greencolor, 0), linewidth = 2)

plot(down1, "Level 1 Down",color = color.new(color.gray, 0), linewidth = 1)
plot(down2, "Level 2 Down",color = color.new(darkRedColor, 0), linewidth = 1)
plot(down3, "Level 3 Down",color = color.new(redcolor, 0), linewidth = 2)

barcolor(colorbars ? colorout : na)