Parkinson's Historical Volatility Bands [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("Parkinson's Historical Volatility Bands [Loxx]",
     shorttitle = "PHVB [Loxx]", 
     overlay = true, 
     timeframe="", 
     timeframe_gaps = true)

import loxx/loxxexpandedsourcetypes/4

greencolor = #2DD204
redcolor = #D2042D 

ema(float src, float per)=>
    float alpha = 2.0 / (1.0 + per)
    float out = src
    out := nz(out[1]) + alpha * (src - nz(out[1]))
    out 

sma(float src, float per)=>
    float sum = 0
    float out = src
    for k = 0 to per - 1
        sum += nz(src[k])
    out := sum / per 
    out

smma(float src, float per)=>
    float alpha = 1.0 / per
    float out = src
    out := nz(out[1]) + alpha * (src - nz(out[1]))
    out 
    
lwma(float src, float per)=>
    float sumw = 0
    float sum = 0
    float out = 0
    for i = 0 to per - 1
        float weight = (per - i) * per
        sumw += weight
        sum  += nz(src[i]) * weight
    out := sum / sumw
    out
    
pwma(float src, float per, float pow)=>
    float sum = 0
    float sumw = 0
    float out = 0
    for k = 0 to per - 1 
        float weight = math.pow((per - k), pow)
        sumw += weight
        sum  += nz(src[k]) * weight
    out := sum / sumw
    out
    
lsma(float src, float per)=>
    float out = 0
    out :=  3.0 * lwma(src, per) - 2.0 * sma(src, per)
    out 
    
hma(float src, float per)=>
    int HalfPeriod = math.floor(per / 2)
    int HullPeriod = math.floor(math.sqrt(per))
    float out = 0
    float price1 = 2.0 * lwma(src, HalfPeriod) - lwma(src, per)
    out := lwma(price1, HullPeriod)
    out
    
tma(float src, float per)=>
    float half = (per + 1.0) / 2.0
    float sum  = 0
    float sumw = 0
    float out = 0
    for k = 0 to per - 1 
        float weight = k + 1
        if weight > half
            weight := per - k
        sumw += weight
        sum  += weight * nz(src[k]) 
    out := sum / sumw
    out
    
swma(float src, float per)=>
    float sum = 0
    float sumw = 0
    float out = 0
    for k = 0 to per - 1  
        weight = math.sin((k + 1) * math.pi / (per + 1))
        sumw += weight
        sum  += weight * nz(src[k])
    out := sum / sumw
    out
    
vwma(float src, float per)=>
    float sum  = 0
    float sumw = 0
    float out = 0
    for k = 0 to per - 1
        float weight = nz(volume[k])
        sumw += weight
        sum  += weight * nz(src[k])
    out := sum / sumw
    out

nonlagma(float src, float 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)
        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
    
variant(type, src, len) =>
    out = 0.0
    switch type
        "Exponential Moving Average - EMA" => out := ema(src, len)
        "Hull Moving Average - HMA" => out := hma(src, len)
        "Linear Regression Value - LSMA (Least Squares Moving Average)" => out := lsma(src, len)
        "Linear Weighted Moving Average - LWMA" => out := lwma(src, len)
        "Non-Lag Moving Average" => out := nonlagma(src, len)
        "Parabolic Weighted Moving Average" => out := pwma(src, len, 2)
        "Simple Moving Average - SMA" => out := sma(src, len)
        "Sine Weighted Moving Average" => out := swma(src, len)
        "Smoothed Moving Average - SMMA" => out := smma(src, len)
        "Triangular Moving Average - TMA" => out := tma(src, len)
        "Volume Weighted Moving Average - VWMA" => out := vwma(src, len)
        => out := sma(src, len)
    out
    
hvbands(string type, float src, int per, float dev)=>
    volConst = 1.0 / (4.0 * per * math.log(2))

    sum = volConst * math.sum(math.pow(math.log(high / low), 2), per)

    float deviation  = math.sqrt(sum) * src
    
    bufferMe = variant(type, src, per)
    bufferUp = bufferMe + deviation * dev
    bufferDn = bufferMe - deviation * dev
    
    bufferUpc = 0
    bufferDnc = 0
    bufferUpc := nz(bufferUpc[1])
    bufferDnc := nz(bufferDnc[1])
    
    if bufferUp > nz(bufferUp[1])
        bufferUpc := 0
    if bufferUp < nz(bufferUp[1])
        bufferUpc := 1
    if bufferDn> nz(bufferDn[1])
        bufferDnc := 0
    if bufferDn< nz(bufferDn[1])
        bufferDnc := 1
    bufferMec = (bufferUpc == 0 and bufferDnc == 0) ? 0 : (bufferUpc == 1 and bufferDnc == 1) ? 1 : 2
    [bufferUp, bufferDn, bufferMe, bufferMec, bufferUpc, bufferDnc]
    
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(40, "Period", group = "Basic Settings")
type = input.string("Hull Moving Average - HMA", "Double Smoothing MA Type", options = ["Exponential Moving Average - EMA"
     , "Hull Moving Average - HMA"
     , "Linear Regression Value - LSMA (Least Squares Moving Average)"
     , "Linear Weighted Moving Average - LWMA"
     , "Non-Lag Moving Average"
     , "Parabolic Weighted Moving Average"
     , "Simple Moving Average - SMA"
     , "Sine Weighted Moving Average"
     , "Smoothed Moving Average - SMMA"
     , "Triangular Moving Average - TMA"
     , "Volume Weighted Moving Average - VWMA"],
     group = "Bands Settings")

dev = input.float(1.0, "Deviations", group = "Basic 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 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
    
[bufferUp, bufferDn, bufferMe, bufferMec, bufferUpc, bufferDnc] = hvbands(type, src, per, dev)

pregoLong = bufferMec == 0
pregoShort = bufferMec == 1

contsw = 0
contsw := nz(contsw[1])
contsw := pregoLong ? 1 : pregoShort ? -1 : nz(contsw[1])

goLong = pregoLong and nz(contsw[1]) == -1
goShort = pregoShort and nz(contsw[1]) == 1 

colorout = bufferMec == 0 ? greencolor : bufferMec == 1 ? redcolor : color.white
coloroutu = bufferUpc == 0 ? greencolor : bufferUpc == 1 ? redcolor : color.white
coloroutd = bufferDnc == 0 ? greencolor : bufferDnc == 1 ? redcolor : color.white

plot(bufferMe, "Middle", color = colorout, linewidth = 3)
plot(bufferUp, "Up band", color = coloroutu)
plot(bufferDn, "Down band", color = coloroutd)

barcolor(colorbars ? colorout : na)

plotshape(showSigs and goLong, title = "Long", color = color.yellow, textcolor = color.yellow, text = "L", style = shape.triangleup, location = location.belowbar, size = size.tiny)
plotshape(showSigs and goShort, title = "Short", color = color.fuchsia, textcolor = color.fuchsia, text = "S", style = shape.triangledown, location = location.abovebar, size = size.tiny)

alertcondition(goLong, title = "Long", message = "Parkinson's Historical Volatility Bands [Loxx]: Long\nSymbol: {{ticker}}\nPrice: {{close}}")
alertcondition(goShort, title = "Short", message = "Parkinson's Historical Volatility Bands [Loxx]: Short\nSymbol: {{ticker}}\nPrice: {{close}}")
