HMA w/ SSE-Dynamic EWMA 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/
// © SegaRKO

//@version=5
indicator('HMA w/ SSE-Dynamic EWMA Volatility Bands [Loxx]', 
     shorttitle ="HMASSEDEWMAVB [Loxx]",
     overlay = true, max_bars_back = 4000, 
     precision = 3)

if not timeframe.isdaily
    runtime.error("Error: Invald timeframe. Indicator only works on daily timeframe.") 

logfast(x)=>
    logfast = -1.7417939 
         + (2.8212026 
         + (-1.4699568 
         + (0.44717955 - 0.056570851 * x) * x) * x) * x
    logfast

// constants
int MAX_COUNT = 100 // could be incrased for more refined lambda values
color darkGreenColor = #1B7E02 
color greencolor = #2DD204

float src = input.source(close, "Source")
int per = input.int(252, "Lookback Period for Mean of Squared Log-returns")
int lambper = input.int(252, "Lookback Period for Mean of Lambda")
int daysperyear = input.int(252, "Days per Year")
int hmaper = input.int(65, "HMA Period")
bool fastLog = input.bool(true, "Use fast Log2 approximation?")
int barsbark = input.int(1000, "Number of bars to calculate")

int bandsmult = input.int(1, "Bands Volatility Multiplier")

float logr = fastLog ? logfast(src/src[1]) : math.log(src/src[1])
float logr2 = math.pow(logr, 2)
float logr2mean = ta.sma(logr2, per)

float ewma = 0
float out = 0
float lambda = 0
float hma = 0

var testTable = table.new(position = position.middle_right, columns = 1, rows = 5, bgcolor = color.gray, border_width = 1)

if last_bar_index - bar_index < barsbark
    float[] lambdaarray = array.new_float(0)
    for i = 0 to MAX_COUNT - 1 
        float sseLambda = 0
        for j = 0 to per - 1
            sseLambda += math.pow(   
                 nz(i / MAX_COUNT) * nz(ewma[j + 1], logr2[j + 1])     
                 + nz(1 - i / MAX_COUNT) * nz(logr2[j + 1]) 
                 - logr2mean, 2)

        array.push(lambdaarray, sseLambda)
    opLambdaPos = array.min(lambdaarray)
    lambda := (array.indexof(lambdaarray, opLambdaPos) + 1) / MAX_COUNT
    ewma := lambda * nz(ewma[1], logr2[1])  + (1 - lambda) * logr2[1]
    out := math.sqrt(ewma) 

meanlambda = ta.sma(lambda, lambper)

if barstate.islast
    table.cell(table_id = testTable, column = 0, row = 0,  text = "Ouputs", bgcolor=darkGreenColor, text_color = color.white, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 0, row = 1,  text = "EWMA Volatility (daily): " + str.tostring(out * 100, format = format.percent), bgcolor=color.yellow, text_color = color.black, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 0, row = 2,  text = "EWMA Volatility (annulaized): " + str.tostring(out * math.sqrt(daysperyear) * 100, format = format.percent), bgcolor=color.yellow, text_color = color.black, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 0, row = 3,  text = "Mean of Lambda: " + str.tostring(meanlambda, "#.###"), bgcolor=color.yellow, text_color = color.black, text_halign = text.align_left)
    table.cell(table_id = testTable, column = 0, row = 4,  text = "The popular RiskMetrics developed by\nJ.P. Morgan uses an EWMA with Lambda = 0.94.", bgcolor=color.yellow, text_color = color.black, text_halign = text.align_left)

hma := ta.hma(src, hmaper)

plot(last_bar_index - bar_index < barsbark ? hma + out * src * bandsmult : na, 'Upper band', color=color.new(color.yellow, 0))
plot(last_bar_index - bar_index < barsbark ? hma : na, 'HMA', color=greencolor, linewidth = 2)
plot(last_bar_index - bar_index < barsbark ? hma - out * src * bandsmult : na, 'Lower band', color=color.new(color.yellow, 0))

plotchar(last_bar_index - bar_index < barsbark ? lambda : na, "Lambda", display = display.data_window)