STD/Clutter Filtered, One-Sided, N-Sinc-Kernel, EFIR Filt [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("STD/Clutter Filtered, One-Sided, N-Sinc-Kernel, EFIR Filt [Loxx]",
     shorttitle = "STDCFOSNSKEFIRF [Loxx]",
     overlay = true, 
     timeframe="", 
     timeframe_gaps = true)

import loxx/loxxexpandedsourcetypes/4

greencolor = #2DD204
redcolor = #D2042D 

duelElementLagReducer(float[] coeff, int LagReductionFactor)=>
    if LagReductionFactor > 0
        per = array.size(coeff)
        for i = per - 1 to 0
            if i >= LagReductionFactor
                array.set(coeff, i, 2 * array.get(coeff, i) - array.get(coeff, i - LagReductionFactor))
            else
                array.set(coeff, i, 2 * array.get(coeff, i))
    coeff
    
clutterFilt(float src, float threshold)=>
    bool out = math.abs(ta.roc(src, 1)) > threshold
    out
    
stdFilter(float src, int len, float filter)=>
    float price = src
    float filtdev = filter * ta.stdev(src, len)
    price := math.abs(price - nz(price[1])) < filtdev ? nz(price[1]) : price
    price
    
sincKernel(int dpth, float cutoff)=>
    float[] kernel = array.new<float>(dpth, 0.)
    for i = 0 to dpth - 1
        // Center of sinc kernel.
        if i - (dpth / 2) == 0
            array.set(kernel, i, 2.0 * math.pi * (1 / cutoff))
        //Left or right lobe of sinc kernel
        if i - (dpth / 2) != 0
            array.set(kernel, i, math.sin(2.0 * math.pi * (1 / cutoff) * (i - dpth / 2)) / (i - dpth / 2))
        //blackman window
        array.set(kernel, i, array.get(kernel, i) * (0.54 - 0.46 * math.cos(2.0 * math.pi * i - dpth / 2)))

    // Normalize filter kernel for unity gain at DC.
    float sumh = 0
	for i = 0 to dpth - 1 
		sumh += array.get(kernel, i)
	for i = 0 to dpth - 1 
		array.set(kernel, i, array.get(kernel, i) / sumh)
    // Slice to right-sided
    float[] coeffout = array.slice(kernel, int(dpth / 2) - 1, dpth - 1)
    coeffout
    
ehlerFilter(float src, float[] coeff)=>
    float num = 0
    float sumCoeff = 0
    float out = 0
    for k = 0 to array.size(coeff) - 1
        num += array.get(coeff, k) * nz(src[k])
        sumCoeff += array.get(coeff, k)
    out := num/sumCoeff
    out

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


per = input.int(100, "Period", group = "Basic Settings")
lagr = input.int(0, "Lag Reduction Factor", group = "Basic Settings")
cutoff = input.float(10, "Kernel Noise Cut-Off", group = "Basic Settings")
sth = input.float(0.1, "Clutter Filter Threshold", group = "Basic Settings", step = 0.001)

filterop = input.string("Both", "Filter Options", options = ["Price", "STDCFOSNSKEFIRF", "Both", "None"], group=  "Filter Settings")
filter = input.float(0, "Filter Devaitions", minval = 0, group= "Filter Settings")
filterperiod = input.int(15, "Filter Period", minval = 0, group= "Filter Settings")

colorbars = input.bool(true, "Color bars?", group = "UI Options")
showSigs = input.bool(true, "Show signals?", group= "UI Options")
showdeadzones = input.bool(false, "Show dead zones?", 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
	
var array<float> coeffout = array.new<float>(per, 0)

if barstate.isfirst
    coeffout := sincKernel(per, cutoff)
    duelElementLagReducer(coeffout, lagr)

src := filterop == "Both" or filterop == "Price" and filter > 0 ? stdFilter(src, filterperiod, filter) : src
out = ehlerFilter(src, coeffout)
out := filterop == "Both" or filterop == "STDCFOSNSKEFIRF" and filter > 0 ? stdFilter(out, filterperiod, filter) : out
sig = nz(out[1])

filtTrend = clutterFilt(out, sth)
state = filtTrend ? (out > sig ? 1 : out < sig ? -1 : 0) : 0

pregoLong = state == 1
pregoShort =state == -1

contsw = 0
contsw := nz(contsw[1])
contsw := pregoLong ? 1 : pregoShort ? -1 : nz(contsw[1])

goLong = pregoLong and nz(contsw[1]) == -1
goShort = pregoShort and nz(contsw[1]) == 1 

color colorout = na
colorout := filtTrend ? (state == 1 ? greencolor : state == -1 ? redcolor : showdeadzones ? color.gray : colorout[1]) : showdeadzones ? color.gray : colorout[1]

plot(out, "STDCFOSNSKEFIRF MA", color = colorout, linewidth = 3)

barcolor(colorbars ? colorout : na)

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

alertcondition(goLong, title = "Long", message = "STD/Clutter Filtered, One-Sided, N-Sinc-Kernel, EFIR Filt [Loxx]: Long\nSymbol: {{ticker}}\nPrice: {{close}}")
alertcondition(goShort, title = "Short", message = "STD/Clutter Filtered, One-Sided, N-Sinc-Kernel, EFIR Filt [Loxx]: Short\nSymbol: {{ticker}}\nPrice: {{close}}")

