Possible RSI [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("Possible RSI [Loxx]", 
     shorttitle='PRSI [Loxx]', 
     overlay = false, 
     timeframe="", 
     timeframe_gaps = true)

import loxx/loxxdynamiczone/3
import loxx/loxxexpandedsourcetypes/4
import loxx/loxxvarietyrsi/1

greencolor = #2DD204
redcolor = #D2042D

darkGreenColor =  #1B7E02 
darkRedColor = #93021F

gauss = "Gaussian (Fisher)"
soft = "Softmax"
regnorm = "Regular Norm"

SM02 = 'Slope'
SM03 = 'Dynamic Middle Crossover'
SM04 = 'Levels Crossover'
SM05 = 'Zeroline Crossover'

highpass(float src, int period) => 
    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
    
softmax(float src, int per)=>
    float mean = ta.sma(src, per)
    float dev = ta.stdev(src, per)
    float zmean = (src - mean) / dev
    float val = (1.0 - math.exp(-zmean)) / (1.0 + math.exp(-zmean))
    val
    
regnorm(float src, int per)=>
    float mean = ta.sma(src, per)
    float dev = ta.stdev(src, per)
    float zmean = (src - mean) / (dev * 3)
    zmean

round_(float val) => 
    val > .99 ? .999 : val < -.99 ? -.999 : val

fisherTransform(float src, len)=>
    float high_ = ta.highest(src, len)
    float low_ = ta.lowest(src, len)
    float value = 0.0
    value := round_(.66 * ((src - low_) / (high_ - low_) - .5) + .67 * nz(value[1]))
    float fish1 = 0.0
    fish1 := .5 * math.log((1 + value) / (1 - value)) + .5 * nz(fish1[1])
    fish1
    
nonlagma(float src, int len)=>
    float cycle = 4.0
    float coeff = 3.0 * math.pi
    float phase = len - 1.0
    int _len = int(len * cycle + phase)
    float weight = 0., float alfa = 0., float out = 0.
    float[] alphas = array.new_float(_len, 0.)
    for k = 0 to _len - 1
        float t = 0.
        t := k <= phase - 1 ?  1.0 * k / (phase - 1) : 1.0 + (k - phase + 1) * (2.0 * cycle - 1.0) / (cycle * len -1.0)
        float beta = math.cos(math.pi * t)
        float g = 1.0/(coeff * t + 1)
        g := t <= 0.5  ? 1 : g
        array.set(alphas, k, g * beta)
        weight += array.get(alphas, k)
    if (weight > 0)  
        float sum = 0.
        for k = 0 to _len - 1
            sum += array.get(alphas, k) * nz(src[k])
        out := (sum / weight)
    out
    
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)"])

runhp = input.bool(false, "Filter Price w/ High-Pass Filter?", group = "Source Settings") 
hper = input.int(15, "High-Pass Period", group = "Source Settings") 

per = input.int(32, "RSI Period", group = "Basic Settings") 
rsitype = input.string("Regular", "RSI Type", options = ["RSX", "Regular", "Slow", "Rapid", "Harris", "Cuttler", "Ehlers Smoothed"], group = "Basic Settings")

norm = input.int(100, "Min/Max Norm Period", group = "Basic Settings") 
ntype = input.string(gauss, "Normalizatino Function", options = [gauss, soft, regnorm], group = "Basic Settings") 

norm1 = input.int(15, "Fisher/Softmax Normalization Period", group = "Basic Settings") 
nonlag = input.int(15, "Non-Lag Smoother Period", group = "Basic Settings") 

dzper = input.int(20, "Dynamic Zone Period", group = "Levels Settings") 
buy1  = input.float(0.2 , "Dynamic Zone Buy Probability Level 1", group = "Levels Settings", maxval = 0.5)
sell1 = input.float(0.2 , "Dynamic Zone Sell Probability Level 1", group = "Levels Settings", maxval = 0.5)

sigtype = input.string(SM05, "Signal type", options = [SM02, SM03, SM04, SM05], 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)

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
	
rsimode = switch rsitype
	"RSX" => "rsi_rsx"
	"Regular" => "rsi_rsi"
	"Slow" => "rsi_slo"
	"Rapid" => "rsi_rap"
	"Harris" => "rsi_har"
	"Cuttler" => "rsi_cut"
	"Ehlers Smoothed" => "rsi_ehl"
	=> "rsi_rsi"

src := runhp ? highpass(src, hper) : src

rsi = loxxvarietyrsi.rsiVariety(rsimode, src, per)

float fmax = ta.highest(rsi, norm)
float fmin = ta.lowest(rsi, norm)
float rng = (fmax - fmin) / 100.0

val = (rsi - fmin) / rng
val := ntype == gauss ? fisherTransform(val, norm1) : ntype == soft ? softmax(val, norm1) : regnorm(val, norm1)
val := nonlagma(val, nonlag)

sig = val[1]
mid = 0

bl1 = loxxdynamiczone.dZone("buy", val, buy1,  dzper)
sl1 = loxxdynamiczone.dZone("sell", val, sell1, dzper)
zli = loxxdynamiczone.dZone("sell", val, 0.5 , dzper)

state = 0.
if sigtype == SM02
    if (val<sig) 
        state :=-1
    if (val>sig) 
        state := 1
else if sigtype == SM03
    if (val<zli) 
        state :=-1
    if (val>zli)
        state := 1
else if sigtype == SM04
    if (val<bl1) 
        state :=-1
    if (val>sl1)
        state := 1
else if sigtype == SM05
    if (val < mid) 
        state :=-1
    if (val > mid)
        state := 1  

colorout = state == -1 ? redcolor : state == 1 ? greencolor : color.gray
plot(val, color = colorout, linewidth = 2)
plot(bl1, color =  darkGreenColor)
plot(sl1, color =  darkRedColor)
plot(zli, color =  color.white)

plot(mid, color =  bar_index % 2 ? color.white : na)

barcolor(colorbars ? colorout : na)
hline(0)

goLong =  
     sigtype == SM02 ? ta.crossover(val, sig) : 
     sigtype == SM03 ? ta.crossover(val, zli) : 
     sigtype == SM04 ? ta.crossover(val, sl1) : 
     ta.crossover(val, mid)
     
goShort = 
     sigtype == SM02 ? ta.crossunder(val, sig) : 
     sigtype == SM03 ? ta.crossunder(val, zli) : 
     sigtype == SM04 ? ta.crossunder(val, bl1) : 
     ta.crossunder(val, mid) 

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="Possible RSI [Loxx]: Long\nSymbol: {{ticker}}\nPrice: {{close}}")
alertcondition(goShort, title="Short", message="Possible RSI [Loxx]: Short\nSymbol: {{ticker}}\nPrice: {{close}}")
