Adaptive Parabolic SAR (PSAR) [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 Parabolic SAR (PSAR) [Loxx]",
     shorttitle = "APSAR [Loxx]", 
     overlay = true, 
     timeframe="", 
     timeframe_gaps = true)

//thanks to @Bjorgum
calcBaseUnit() =>
    bool  isForexSymbol = syminfo.type     == "forex"
    bool  isYenPair     = syminfo.currency == "JPY"
    float result = isForexSymbol ? isYenPair ? 0.01 : 0.0001 : syminfo.mintick

greencolor = #2DD204
redcolor = #D2042D 

_afact(mode,input, per, smooth) =>
    eff = 0., seff = 0.
    len = 0, sum = 0., max = 0., min = 1000000000.
    len := mode == "Kaufman" ? math.ceil(per) : math.ceil(math.max(20, 5 * per))
    for i = 0 to len 
        if (mode == "Kaufman") 
            sum += math.abs(input[i] - input[i + 1])
        else
            max := input[i] > max ? input[i] : max
            min := input[i] < min ? input[i] : min
    if (mode == "Kaufman" and sum != 0) 
        eff := math.abs(input - input[len]) / sum
    else
        if (mode == "Ehlers" and (max - min) > 0) 
            eff := (input - min) / (max - min)
    seff := ta.ema(eff, smooth)
    seff
    
src = input.source(close, "Sourse", group = "Basic Settings")
startAFactor = input.float(0.02, "Starting Acceleration Factor", step = 0.001, group = "Basic Settings")
minStep = input.float(0.0, "Min Step", step = 0.001, group = "Basic Settings")
maxStep = input.float(0.02, "Max Step", step = 0.001, group = "Basic Settings")
maxAFactor = input.float(0.2, "Max Acceleration Factor", step = 0.001, group = "Basic Settings")  

hiloMode = input.string("Off", "HiLo Mode", options = ["Off", "On"], group = "Advanced Settings")
adaptMode = input.string("Off", "Adaptive Mode", options = ["Off", "Kaufman", "Ehlers"], group = "Advanced Settings")
adaptSmth = input.int(5, "Adaptive Smoothing Period", minval = 1, group = "Advanced Settings")
filt = input.float(0.0, "Filter in Pips", group = "Advanced Settings", minval = 0)
minChng = input.float(0.0, "Min Change in Pips", group = "Advanced Settings", minval = 0)
SignalMode = input.string("Only Stops", "Signal Mode", options = ["Only Stops", "Signals & Stops"], group = "Advanced Settings")

colorbars = input.bool(false, "Color bars?", group = "UI Options")

hVal2 = nz(high[2]), hVal1 = nz(high[1]), hVal0 = high
lowVal2 = nz(low[2]), lowVal1 = nz(low[1]), lowVal0 = low
hiprice2 = nz(high[2]), hiprice1 = nz(high[1]), hiprice0 = high
loprice2 = nz(low[2]), loprice1 = nz(low[1]), loprice0 = low

upSig = 0., dnSig = 0.
aFactor = 0., step = 0., trend = 0.
upTrndSAR = 0., dnTrndSAR = 0.
length = (2 / maxAFactor - 1)

if (hiloMode == "On") 
    hiprice0 := high
    loprice0 := low
else
    hiprice0 := src
    loprice0 := hiprice0

if bar_index == 1
    trend := 1
    hVal1 := hiprice1
    hVal0 := math.max(hiprice0, hVal1)
    lowVal1 := loprice1
    lowVal0 := math.min(loprice0, lowVal1)
    aFactor := startAFactor
    upTrndSAR := lowVal0
    dnTrndSAR := 0.
else
    hVal0 := hVal1
    lowVal0 := lowVal1
    trend := nz(trend[1])
    aFactor := nz(aFactor[1])
    inputs = 0.
    inprice = src
    if (adaptMode != "Off")
        if (hiloMode == "On") 
            inprice := src
        else 
            inprice := hiprice0
        if (adaptMode == "Kaufman") 
            inputs := inprice
        else
            if (adaptMode == "Ehlers") 
                if (nz(upTrndSAR[1]) != 0.)
                    inputs := math.abs(inprice - nz(upTrndSAR[1]))
                else
                    if (nz(dnTrndSAR[1]) != 0.) 
                        inputs := math.abs(inprice - nz(dnTrndSAR[1]))
        step := minStep + _afact(adaptMode, inputs, length, adaptSmth) * (maxStep - minStep)
    else 
        step := maxStep
        
    upTrndSAR := 0., dnTrndSAR := 0., upSig := 0., dnSig := 0.
    
    if (nz(trend[1]) > 0) 
        if (nz(trend[1]) == nz(trend[2]))
            aFactor := hVal1 > hVal2 ? nz(aFactor[1]) + step : aFactor
            aFactor := aFactor > maxAFactor ? maxAFactor : aFactor
            aFactor := hVal1 < hVal2 ? startAFactor : aFactor
        else 
            aFactor := nz(aFactor[1])
            
        upTrndSAR := nz(upTrndSAR[1]) + aFactor * (hVal1 - nz(upTrndSAR[1]))
        upTrndSAR := upTrndSAR > loprice1 ? loprice1 : upTrndSAR
        upTrndSAR := upTrndSAR > loprice2 ? loprice2 : upTrndSAR
    else
        if (nz(trend[1]) == nz(trend[2])) 
            aFactor := lowVal1 < lowVal2 ? nz(aFactor[1]) + step : aFactor
            aFactor := aFactor > maxAFactor ? maxAFactor : aFactor
            aFactor := lowVal1 > lowVal2 ? startAFactor : aFactor
        else
            aFactor := nz(aFactor[1])
            
        dnTrndSAR := nz(dnTrndSAR[1]) + aFactor * (lowVal1 - nz(dnTrndSAR[1]))
        dnTrndSAR := dnTrndSAR < hiprice1 ? hiprice1 : dnTrndSAR
        dnTrndSAR := dnTrndSAR < hiprice2 ? hiprice2 : dnTrndSAR
    
    hVal0 := hiprice0 > hVal0 ? hiprice0 : hVal0
    lowVal0 := loprice0 < lowVal0 ? loprice0 : lowVal0
        
    if (minChng > 0) 
        if (upTrndSAR - nz(upTrndSAR[1]) < minChng * calcBaseUnit() and upTrndSAR != 0. and nz(upTrndSAR[1]) != 0.)
            upTrndSAR := nz(upTrndSAR[1])
        if (nz(dnTrndSAR[1]) - dnTrndSAR < minChng * calcBaseUnit() and dnTrndSAR != 0. and nz(dnTrndSAR[1]) != 0.)
            dnTrndSAR := nz(dnTrndSAR[1])

    dnTrndSAR := trend < 0 and dnTrndSAR > nz(dnTrndSAR[1]) ? nz(dnTrndSAR[1]) : dnTrndSAR
    upTrndSAR := trend > 0 and upTrndSAR < nz(upTrndSAR[1]) ? nz(upTrndSAR[1]) : upTrndSAR
    
    if (trend < 0 and hiprice0 >= dnTrndSAR + filt * calcBaseUnit())
        trend := 1
        upTrndSAR := lowVal0
        upSig := SignalMode == "Signals & Stops" ? lowVal0 : upSig
        dnTrndSAR := 0.
        aFactor := startAFactor
        lowVal0 := loprice0
        hVal0 := hiprice0
    else if (trend > 0 and loprice0 <= upTrndSAR - filt * calcBaseUnit())
        trend := -1
        dnTrndSAR := hVal0
        dnSig := SignalMode == "Signals & Stops" ? hVal0 : dnSig
        upTrndSAR := 0.
        aFactor := startAFactor
        lowVal0 := loprice0
        hVal0 := hiprice0
    
outer = upTrndSAR > 0 ? upTrndSAR : dnTrndSAR
colorout = outer >= src ? redcolor : greencolor
plot(upTrndSAR > 0 ? upTrndSAR : dnTrndSAR, color = colorout, style = plot.style_cross, linewidth = 2)  

barcolor(colorbars ? colorout : na)

plotshape(SignalMode == "Signals & Stops" ? dnSig : na, color = redcolor, textcolor = redcolor, style = shape.triangledown, location = location.abovebar, size = size.small, title='Short', text='S')
plotshape(SignalMode == "Signals & Stops" ? upSig : na, color = greencolor, textcolor = greencolor, style = shape.triangleup, location = location.belowbar, size = size.small, title='Long', text='L')



















