Adaptive-Lookback Stochastic [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("Adaptive-Lookback Stochastic [Loxx]", 
     overlay = false, 
     shorttitle='ALBSTOCH [Loxx]',
     timeframe="",
     timeframe_gaps=true)
     
import loxx/loxxexpandedsourcetypes/4

greencolor = #2DD204
redcolor = #D2042D 

SM02 = 'Slope'
SM03 = 'Middle Crosses'
SM04 = 'Levels Crosses'

_albper(swingCount, speed)=>
    swing = 0.
    if bar_index > 3
        if (high > nz(high[1]) and 
             nz(high[1]) > nz(high[2]) and
             nz(low[2]) < nz(low[3]) and 
             nz(low[3]) < nz(low[4]))
            swing := -1
            
        if (low < nz(low[1]) and 
             nz(low[1]) < nz(low[2]) and 
             nz(high[2]) > nz(high[3]) and 
             nz(high[3]) > nz(high[4]))
            swing :=  1
    
    swingBuffer = swing
    k = 0, n = 0
    while (k < bar_index) and (n < swingCount)
        if(swingBuffer[k] != 0) 
            n += 1
        k += 1
    albPeriod = math.max(math.round((speed != 0 and swingCount != 0) ? k/swingCount/speed : k/swingCount), 1)
    albPeriod

smthtype = input.string("Kaufman", "Heiken-Ashi Better Smoothing", options = ["AMA", "T3", "Kaufman"], group=  "Source Settings")
srcoption = input.string("Close", "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)"])

inpSlowing = input.int(3, "Slowing Period", group = "Basic Settings")
swingCount = input.int(5, "ALB Swing Count", group = "Adaptive Lookback Settings")
speed = input.float(.1, "ALB Speed", minval = 0., step = 0.01, group = "Adaptive Lookback Settings")

lvlup = input.int(80, "Upper Level", group = "Levels Settings")
lvldn = input.int(20, "Bottom Level", group = "Levels Settings")

sigtype = input.string(SM03, "Signal type", options = [SM02, SM03, SM04], group = "Signal Settings")

colorbars = input.bool(true, "Color bars?", group= "UI Options")
showSigs = input.bool(true, "Show signals?", 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")

haclose = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, close)
haopen = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, open)
hahigh = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, high)
halow = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, low)
hamedian = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, hl2)
hatypical = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, hlc3)
haweighted = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, hlcc4)
haaverage = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, ohlc4)

float src = switch srcoption
	"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
	
albper = _albper(swingCount, speed)

mi = ta.lowest(src, albper)
ma = ta.highest(src, albper)

sumlow  = 0.
sumhigh = 0.

for k = 0 to inpSlowing - 1
    sumlow  += nz(src[k]) - nz(mi[k])
    sumhigh += nz(ma[k]) - nz(mi[k])

stoch = 100.0 * sumlow/sumhigh
sigval = stoch[1]
mid = 50

state = 0.
if sigtype == SM02
    if (stoch < sigval) 
        state :=-1
    if (stoch > sigval) 
        state := 1
else if sigtype == SM03
    if (stoch < mid) 
        state :=-1
    if (stoch > mid)
        state := 1
else if sigtype == SM04
    if (stoch < lvldn) 
        state := -1
    if (stoch > lvlup)
        state := 1
        
colorout = state == 1 ? greencolor : state == -1 ? redcolor : color.gray

plot(stoch, "Stochastic", color = colorout, linewidth = 2)
plot(lvlup, "Overbought", color = bar_index % 2 ? color.gray : na)
plot(lvldn, "Oversold", color = bar_index % 2 ? color.gray : na)
plot(mid, "Middle", color = bar_index % 2 ? color.gray : na)

barcolor(colorbars ?  colorout: na)

goLong = sigtype == SM02 ? ta.crossover(stoch, sigval) : sigtype == SM03 ? ta.crossover(stoch, mid) :  ta.crossover(stoch, lvlup)
goShort = sigtype == SM02 ? ta.crossunder(stoch, sigval) : sigtype == SM03 ? ta.crossunder(stoch, mid) : ta.crossunder(stoch, lvldn)

plotshape(showSigs and goLong, title = "Long", color = color.yellow, textcolor = color.yellow, text = "L", style = shape.triangleup, location = location.bottom, size = size.auto)
plotshape(showSigs and goShort, title = "Short", color = color.fuchsia, textcolor = color.fuchsia, text = "S", style = shape.triangledown, location = location.top, size = size.auto)

alertcondition(goLong, title="Long", message="Adaptive-Lookback Stochastic [Loxx]: Long\nSymbol: {{ticker}}\nPrice: {{close}}")
alertcondition(goShort, title="Short", message="Adaptive-Lookback Stochastic [Loxx]: Short\nSymbol: {{ticker}}\nPrice: {{close}}")

