Normalized Velocity [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("Normalized Velocity [Loxx]", 
     shorttitle='NV [Loxx]', 
     overlay=false,  
     timeframe="", 
     timeframe_gaps=true)

import loxx/loxxexpandedsourcetypes/4

greencolor = #2DD204
redcolor = #D2042D 

darkGreenColor =  #1B7E02 
darkRedColor = #93021F

SM02 = 'Slope'
SM03 = 'Middle Crosses'
SM04 = 'Levels Crosses'

moms(float src, float per, float powSlow, float powFast)=>
    float suma = 0.0
    float sumwa=0
    float sumb = 0.0
    float sumwb=0
    out = 0.
    for k = 0 to per - 1 
        float weight = per - k
        suma  += nz(src[k]) * math.pow(weight, powSlow)
        sumb  += nz(src[k]) * math.pow(weight, powFast)
        sumwa += math.pow(weight, powSlow)
        sumwb += math.pow(weight, powFast)
    out := sumb / sumwb - suma / sumwa
    out

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

per = input.int(32, "Period", minval=1, group = "Basic Settings")
minMaxPeriod = input.int(50, "Min/Max Period", minval=1, group = "Basic Settings")

LevelUp = input.int(80, "Upper Level", group = "Levels Settings")
LevelDown = input.int(20, "Bottom Level", group = "Levels Settings")

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

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

outmaxminper = (minMaxPeriod > 0) ? minMaxPeriod : per

div = ta.atr(15)
out = src
out := moms(out, per, 1, 2) / div
sig = out[1]

float fmin = ta.lowest(out, outmaxminper)
float fmax = ta.highest(out, outmaxminper)

float rng = fmax - fmin
lvlup = fmin + LevelUp  * rng / 100.0
lvldn = fmin + LevelDown * rng / 100.0
mid = fmin + 0.5 * rng

state = 0.
if sigtype == SM02
    if (out < sig) 
        state :=-1
    if (out > sig) 
        state := 1
else if sigtype == SM03
    if (out < mid) 
        state :=-1
    if (out > mid)
        state := 1
else if sigtype == SM04
    if (out < lvldn) 
        state := -1
    if (out > lvlup)
        state := 1
        
colorout = state == 1 ? greencolor : state == -1 ? redcolor : color.gray

plot(out, "Normalized Velocity", color = colorout, linewidth = 2)
plot(lvlup, "Up level", color = bar_index % 2 ? color.gray : na)
plot(lvldn, "Down level", color = bar_index % 2 ? color.gray : na)
plot(mid, "Mid", color = bar_index % 2 ? color.gray : na)

barcolor(colorbars ?  colorout: na)

goLong = sigtype == SM02 ? ta.crossover(out, sig) : sigtype == SM03 ? ta.crossover(out, mid) :  ta.crossover(out, lvlup)
goShort = sigtype == SM02 ? ta.crossunder(out, sig) : sigtype == SM03 ? ta.crossunder(out, mid) : ta.crossunder(out, lvldn)

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