Lyapunov Hodrick-Prescott Oscillator w/ DSL [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("Lyapunov Hodrick-Prescott Oscillator w/ DSL [Loxx]", 
     shorttitle='LHPODSL [Loxx]', 
     overlay = false, 
     timeframe="", 
     timeframe_gaps = true)

import loxx/loxxexpandedsourcetypes/4
import loxx/loxxmas/1

greencolor = #2DD204
redcolor = #D2042D 

variant(type, src, len) =>
    sig = 0.0
    trig = 0.0
    special = false
    if type == "Exponential Moving Average - EMA"
        [t, s, b] = loxxmas.ema(src, len)
        sig := s
        trig := t
        special := b
    else if type == "Fast Exponential Moving Average - FEMA"
        [t, s, b] = loxxmas.fema(src, len)
        sig := s
        trig := t
        special := b
    trig

_HPFilter(src, lamb, per)=>
    H1 = 0., H2 = 0., H3 = 0., H4 = 0., H5 = 0., 
    HH1 = 0., HH2 = 0., HH3 = 0., HH5 = 0.
    HB= 0., HC= 0., Z= 0.
    
    a = array.new<float>(per, 0.)
    b = array.new<float>(per, 0.)
    c = array.new<float>(per, 0.)

    out = array.new<float>(per, 0.)
    for i = 0 to per - 1
        array.set(out, i, nz(src[i]))
        
    array.set(a, 0, 1.0 + lamb)
    array.set(b, 0, -2.0 * lamb)
    array.set(c, 0, lamb)
    
    for i = 1 to per - 3
        array.set(a, i, 6.0 * lamb + 1.0)
        array.set(b, i, -4.0 * lamb)
        array.set(c, i, lamb)
    
    array.set(a, 1, 5.0 * lamb + 1)
    array.set(a, per - 1, 1.0 + lamb)
    array.set(a, per - 2, 5.0 * lamb + 1.0)
    array.set(b, per - 2, -2.0 * lamb)
    array.set(b, per - 1, 0.)
    array.set(c, per - 2, 0.)
    array.set(c, per - 1, 0.)
    
    for i = 0 to per - 1 
        Z := array.get(a, i) - H4 * H1 - HH5 * HH2
        if (Z == 0) 
            break
            
        HB := array.get(b, i)
        HH1 := H1
        H1 := (HB - H4 * H2) / Z
        array.set(b, i, H1)
        
        HC := array.get(c, i)
        HH2 := H2
        H2 := HC / Z
        array.set(c, i, H2)
        
        array.set(a, i, (array.get(out, i) - HH3 * HH5 - H3 * H4) / Z)
        HH3 := H3
        H3 := array.get(a, i)
        H4 := HB - H5 * HH1
        HH5 := H5
        H5 := HC

    H2 := 0
    H1 := array.get(a, per - 1)
    array.set(out, per - 1, H1)
    
    for i = per - 2 to 0
        array.set(out, i, array.get(a, i) - array.get(b, i) * H1 - array.get(c, i) * H2)
        H2 := H1
        H1 := array.get(out, i)
        
    lhp = array.new<float>(per, 0.)

    for i = 0 to per - 2 
        array.set(lhp, i, math.log(math.abs(array.get(out, i) / array.get(out, i + 1))))
    lhp
  
smthtype = input.string("Kaufman", "Heikin-Ashi Better Caculation Type", options = ["AMA", "T3", "Kaufman"], group = "Source Settings")
srcin = 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)"])

fastper = input.int(50, 'Period', minval=4, group = "Basic Settings")

colorbars = input.bool(true, "Color bars?", group = "UI Options")
showSigs = input.bool(true, "Show signals?", group = "UI Options")

dzper = input.int(35, "Dynamic Zone Period", group = "Levels Settings") 
dzbuyprob  = input.float(0.9 , "Dynamic Zone Buy Probability", group = "Levels Settings", maxval = 1)
dzsellprob = input.float(0.9 , "Dynamic Zone Sell Probability", group = "Levels Settings", maxval = 1)

signal_length = input.int(9, "Signal Period", group = "Signal/DSL Settings")
sigmatype = input.string("Exponential Moving Average - EMA", "Signal/DSL Smoothing", options = ["Exponential Moving Average - EMA", "Fast Exponential Moving Average - FEMA"], group = "Signal/DSL Settings")

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)

src = 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
	
lambfast = 0.0625 / math.pow(math.sin(math.pi / fastper), 4) 
lhpfast = _HPFilter(src, lambfast, fastper)

out = array.get(lhpfast, 0) 

sig = out[1]
levelu = 0., leveld = 0., mid = 0.

levelu := (out > sig) ? variant(sigmatype, out, signal_length) : nz(levelu[1])
leveld := (out < sig) ? variant(sigmatype, out, signal_length) : nz(leveld[1])
        
colorout = out > levelu ? greencolor : out < leveld  ? redcolor : color.gray

plot(levelu, "Upper DSL", color = color.gray)
plot(leveld, "Lower DSL", color = color.gray)
plot(out, "LHPODSL", color = colorout, linewidth = 2)

barcolor(colorbars ? colorout : na)

goLong = ta.crossover(out, levelu)
goShort = ta.crossunder(out, leveld)

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="Lyapunov Hodrick-Prescott Oscillator w/ DSL [Loxx]: Long\nSymbol: {{ticker}}\nPrice: {{close}}")
alertcondition(goShort, title="Short", message="Lyapunov Hodrick-Prescott Oscillator w/ DSL [Loxx]: Short\nSymbol: {{ticker}}\nPrice: {{close}}")

