Ehlers Linear Extrapolation Predictor [Loxx]
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © loxx
// "Predict Extrapolation" (C) 1978-2021 John F. Ehlers
//@version=5
indicator("Ehlers Linear Extrapolation Predictor [Loxx]", 
     overlay = false, 
     timeframe="", 
     timeframe_gaps = true)

//+------------------------------------------------------------------+
//| Extrenal library exports
//+------------------------------------------------------------------+
import loxx/loxxexpandedsourcetypes/4

//+------------------------------------------------------------------+
//| Declare constants
//+------------------------------------------------------------------+
SM02 = 'Predict/Filter Crosses'
SM03 = 'Predict Middle Crosses'
SM04 = 'Filter Middle Crosses'
redcolor = #D2042D 
greencolor = #2DD204

//+------------------------------------------------------------------+
//| Fucntions
//+------------------------------------------------------------------+
highpass(float src, int period) => // High-Pass Filter
    float a1 = math.exp(-1.414 * math.pi / period)
    float b1 = 2 * a1 * math.cos(1.414 * math.pi / period)
    float c2 = b1
    float c3 = -a1 * a1
    float c1 = (1 + c2 - c3) / 4
    float hp = 0.0
    hp := bar_index < 4 ? 0 : c1 * (src - 2 * nz(src[1]) + nz(src[2])) + (c2 * nz(hp[1])) + (c3 * nz(hp[2]))
    hp

hann(float src, int period) => // Hann Low-Pass FIR Filter
    var PIx2 = 2.0 * math.pi / (period + 1)
    float sum4Hann = 0.0, sumCoefs = 0.0
    for count = 1 to period
        float coef = 1.0 - math.cos(count * PIx2)
        sum4Hann += coef * src[count - 1]
        sumCoefs += coef
    float filt = nz(sum4Hann / sumCoefs)
    filt
    
//+------------------------------------------------------------------+
//| Inputs
//+------------------------------------------------------------------+
srctype = input.string("Kaufman", "Heiken-Ashi Better Smoothing", 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)"])

HPLength = input.int(125, "High-Pass Filter Period", group = "Preliminary Filtering Settings")
LPLength = input.int(12, "Low-Pass Hann Filter Period", group = "Preliminary Filtering Settings")
Gain = input.float(0.7, "Gain Reduction", group =  "Output Shaping Settings")
BarsFwd = input.int(5, "Bars Forward", group =  "Output Shaping Settings")

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

predictcol = input.color(color.yellow, "Prediction Color", group = "UI Settings")
filtcol = input.color(redcolor, "Filter Color", group =  "UI Settings")
upcol = input.color(greencolor, "Uptrend Color", group = "UI Settings")
dncol = input.color(redcolor, "Downtrend Color", group =  "UI Settings")
colorbars = input.bool(true, "Color bars?", group = "UI Options")
showSigs = input.bool(true, "Show signals?", group = "UI Options")

//+------------------------------------------------------------------+
//| Prepare source data
//+------------------------------------------------------------------+

//Inputs for Loxx's Expanded Source Types
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(srctype, amafl, amasl, kfl, ksl)
	"HAB Open" => loxxexpandedsourcetypes.habopen(srctype, amafl, amasl, kfl, ksl)
	"HAB High" => loxxexpandedsourcetypes.habhigh(srctype, amafl, amasl, kfl, ksl)
	"HAB Low" => loxxexpandedsourcetypes.hablow(srctype, amafl, amasl, kfl, ksl)
	"HAB Median" => loxxexpandedsourcetypes.habmedian(srctype, amafl, amasl, kfl, ksl)
	"HAB Typical" => loxxexpandedsourcetypes.habtypical(srctype, amafl, amasl, kfl, ksl)
	"HAB Weighted" => loxxexpandedsourcetypes.habweighted(srctype, amafl, amasl, kfl, ksl)
	"HAB Average" => loxxexpandedsourcetypes.habaverage(srctype, amafl, amasl, kfl, ksl)
    "HAB Average Median Body" => loxxexpandedsourcetypes.habavemedbody(srctype, amafl, amasl, kfl, ksl)
	"HAB Trend Biased" => loxxexpandedsourcetypes.habtrendb(srctype, amafl, amasl, kfl, ksl)
	"HAB Trend Biased (Extreme)" => loxxexpandedsourcetypes.habtrendbext(srctype, amafl, amasl, kfl, ksl)
	=> haclose
	
//+------------------------------------------------------------------+
//| Private Varible declarations 
//+------------------------------------------------------------------+

float HP = 0.
float SumSq = 0.
float Filt = 0.
float coef = 0.
int Length = 10
float Predict = 0.

XX = array.new<float>(21, 0.) 

//+------------------------------------------------------------------+
//| Calculations 
//+------------------------------------------------------------------+

//High-Pass Filter
HP := highpass(src, HPLength)

//Hann Low-Pass Filter
Filt := hann(HP, LPLength) 

//Convert the Filt variable to a data array
for count = 1 to Length 
	array.set(XX, count, nz(Filt[Length - count]))

//Linear Exptrapolation 
for count = 0 to 9 
	array.set(XX, Length + count + 1, array.get(XX, Length + count) + (array.get(XX, Length + count) - array.get(XX, Length + count - 1)))

//Convert desired forward look in the array to the variable Predict
Predict := array.get(XX, Length + BarsFwd) * Gain

//+------------------------------------------------------------------+
//| Plot output 
//+------------------------------------------------------------------+

mid = 0.
plot(Filt, "Filter", color = filtcol, linewidth = 2)
plot(mid, "Zero line", color = bar_index % 2 ? color.silver : na)
plot(Predict, "Prediction", color = predictcol, linewidth = 1)

state = 0.
if sigtype == SM02
    if (Predict < Filt) 
        state :=-1
    if (Predict > Filt)
        state := 1
else if sigtype == SM03
    if (Predict < mid) 
        state :=-1
    if (Predict > mid)
        state := 1
else if sigtype == SM04
    if (Filt < mid) 
        state :=-1
    if (Filt > mid)
        state := 1
        
colorout = state == 1 ? upcol : state == -1 ? dncol : color.gray

barcolor(colorbars ? colorout : na)

goLong =  sigtype == SM02 ? ta.crossover(Predict, Filt) : sigtype == SM03 ? ta.crossover(Predict, mid) : ta.crossover(Filt, mid) 
goShort = sigtype == SM02 ? ta.crossunder(Predict, Filt) : sigtype == SM03 ? ta.crossunder(Predict, mid) : ta.crossunder(Filt, mid) 

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

alertcondition(goLong, title="Long", message="Ehlers Linear Extrapolation Predictor [Loxx]: Long\nSymbol: {{ticker}}\nPrice: {{close}}")
alertcondition(goShort, title="Short", message="Ehlers Linear Extrapolation Predictor [Loxx]: Short\nSymbol: {{ticker}}\nPrice: {{close}}")

