Slow but limitless moving averages
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © f4r33x

//@version=5
indicator("Slow but limitless moving averages", overlay = true)
import PineCoders/LibraryStopwatch/1 as sw
import f4r33x/EncoderDecoder/4 as ed
import f4r33x/String_Encoder_Decoder/1 as sed
import f4r33x/fast_utils/4 as fu
import f4r33x/ReduceSecurityCalls/2 as rsc
nonRepaintingSecurity(sym, tf, expr) => request.security(sym, tf, expr[barstate.isrealtime ? 1 : 0])[barstate.isrealtime ? 0 : 1]
ifSecurity(sym, tf, expr) => request.security(sym, tf, expr)
resource_tf(string src, string tf, string inp_tf1, string inp_tf2=na, string inp_tf3=na, string inp_tf4=na, string inp_tf5=na, string inp_tf6=na, matrix<float> out_tf1=na, matrix<float> out_tf2=na, matrix<float> out_tf3=na, matrix<float> out_tf4=na, matrix<float> out_tf5=na, matrix<float> out_tf6=na) =>
    matrix<float> findmat = switch tf
        inp_tf1  => out_tf1
        inp_tf2  => out_tf2
        inp_tf3  => out_tf3
        inp_tf4  => out_tf4
        inp_tf5  => out_tf5
        inp_tf6  => out_tf6
    int row = switch src
        'open'   => 0
        'high'   => 1
        'low'    => 2
        'close'  => 3
        'custom' => 4
    out = matrix.row(findmat, row)
    out

change_tf(string tf, string inp_tf1, string inp_tf2=na, string inp_tf3=na, string inp_tf4=na, string inp_tf5=na, string inp_tf6=na, bool change_tf1, bool change_tf2 = na, bool change_tf3 = na, bool change_tf4 = na,bool change_tf5= na, bool change_tf6 = na) =>
    bool out = switch tf
        inp_tf1  => change_tf1
        inp_tf2  => change_tf2
        inp_tf3  => change_tf3
        inp_tf4  => change_tf4
        inp_tf5  => change_tf5
        inp_tf6  => change_tf6
    out


inp_l = input.int(defval = 22, title = 'length of htf')
inp_tf1 =input.timeframe(defval = '1D', title = 'TF1')
change_tf1 = timeframe.change(inp_tf1)
inp_tf2 =input.timeframe(defval = '30', title = 'TF2')
change_tf2 = timeframe.change(inp_tf2)
inp_tf3 = timeframe.period
inp_sym =input.symbol(defval = 'BYBIT:BTCUSDT.P',title = 'symbol')
inp_method =1
inp_refresh=input.int(defval=0, title = 'CLICK TO REFRESH')
inp_l1=input.int(defval=14, title = 'l1')
inp_l2=input.int(defval=21, title = 'l2')
inp_l3=input.int(defval=22, title = 'l3')
inp_type1=input.string(defval='EMA', title = 'type1', options = ['RMA','SMA','EMA','HMA_slow','WMA'])
inp_type2=input.string(defval='EMA', title = 'type2', options = ['RMA','SMA','EMA','HMA_slow','WMA'])
inp_type3=input.string(defval='EMA', title = 'type3', options = ['RMA','SMA','EMA','HMA_slow','WMA'])
inp_source1=input.string(defval='close', title = 'source1')
inp_source2=input.string(defval='high', title = 'source2')
inp_source3=input.string(defval='low', title = 'source3')
arr_l = array.from(inp_l1,inp_l2,inp_l3)
arr_t = array.from(inp_type1,inp_type2,inp_type3)
arr_s = array.from(inp_source1,inp_source2,inp_source3)
arr_tf =array.from(inp_tf1,inp_tf2,inp_tf3)
float a = na
float b = na
float c = na
//var mat_inouts = matrix.new<float>(100,0,na)
//var mat_inouts2 = matrix.new<string>(2,0,na)
var arr_in =array.new_string(0)
var arr_out=array.new_float(0)
var mat_out1= matrix.new<float>(0,0, na)
var mat_out2= matrix.new<float>(0,0, na)
var mat_out3= matrix.new<float>(0,0, na)
inp_o =input.string(defval = 'open', title = 'input first parsed value')
inp_o_l=input.int(defval = 14, title = 'input first parsed value')

//o =fu.resource('open')
o = switch inp_o 
    'open' => open
    'high' => high
    'low'  => low
    'close'=> close
    'ema'  => ta.ema(fu.resource(inp_source1), inp_o_l)

h =fu.resource('high')
l =fu.resource('low')
cl =fu.resource('close')
custom = volume/10000
if barstate.isfirst or timeframe.change(inp_tf1)
	mat_out1 := ifSecurity(inp_sym, inp_tf1, rsc.ParseSource(o=o, h=h, l=l, c=cl, custom =custom, length =inp_l)) //, custom =ta.tr(true)
if barstate.isfirst or timeframe.change(inp_tf2)
	mat_out2 := ifSecurity(inp_sym, inp_tf2, rsc.ParseSource(o=o, h=h, l=l, c=cl, custom =custom, length =inp_l)) //, custom =ta.tr(true)
if barstate.isfirst or timeframe.change(inp_tf3)
	mat_out3 := ifSecurity(inp_sym, inp_tf3, rsc.ParseSource(o=o, h=h, l=l, c=cl, custom =custom, length =inp_l)) //, custom =ta.tr(true)


wma(float[] source, series int length, float[] arr_o, int i) =>
    sum = 0.0
    for index = 0 to length - 1 
        sum := sum + array.get(source, index) * (length - index)

    lSum = length * (length + 1) / 2
    res = sum / lSum
    array.set(arr_o, i, res)
    res
wma(float[] source, series int length) =>
    sum = 0.0
    for index = 0 to length - 1 
        sum := sum + array.get(source, index) * (length - index)

    lSum = length * (length + 1) / 2
    res = sum / lSum
    res
wma(float[] source, series int length, float[] arr_o, int i, bool tf_changed) =>
    float res = na
    if tf_changed
        sum = 0.0
        for index = 0 to length - 1 
            sum := sum + array.get(source, index) * (length - i)

        lSum = length * (length + 1) / 2
        res := sum / lSum
        array.set(arr_o, i, res)
    res
wma(string source, float length, float[] arr_o, int i) =>
    sum = 0.0
    for index = 0 to length - 1 
        sum := sum + fu.resource(source, index) * (length - index)

    lSum = length * (length + 1) / 2
    res = sum / lSum
    array.set(arr_o, i, res)
    res
wma(string source, float length) =>
    sum = 0.0
    for i = 0 to length - 1 
        sum := sum + fu.resource(source, i) * (length - i)

    lSum = length * (length + 1) / 2
    sum / lSum
wma(string source, float length, int h) =>
    sum = 0.0
    for i = 0 to length - 1 
        sum := sum + fu.resource(source, i+h) * (length - i)

    lSum = length * (length + 1) / 2
    sum / lSum

ema(array<float> source, int length, array<float> arr_o, int i) =>
    float ema = na
    if i == -1
        runtime.error('push inputs to matrix')    
    k = 2 / (length + 1)

    currentEma = array.get(arr_o , i) 
    if na(currentEma)
        for index = 0 to length-1 

            src = array.get(source, length-1-index)
            ema := na(currentEma) ? src : src * k + (1 - k) * currentEma
            currentEma := ema

        array.set(arr_o , i, ema)
    else
        src = array.get(source, 0)    
        ema := src * k + (1 - k) * currentEma
        array.set(arr_o , i, ema)  

    ema
ema(array<float> source, int length, array<float> arr_o, int i, bool tf_changed) =>
    float ema = na
    
    k = 2 / (length + 1)
    if i == -1
        runtime.error('push inputs to matrix')
    currentEma = array.get(arr_o , i)
    if na(currentEma) 
        for index = 0 to length-1 

            src = array.get(source, length-1-index)
            ema := na(currentEma) ? src : src * k + (1 - k) * currentEma
            currentEma := ema
 
        array.set(arr_o , i, ema)


    if tf_changed
        src = array.get(source, 0)    
        ema := src * k + (1 - k) * currentEma
        array.set(arr_o , i, ema)

ema(float source, int length,  float[] arr_out, int i) =>
    if i == -1
        runtime.error('push inputs to matrix')  
    k = 2 / (length + 1)
    float ema = na
    currentEma = array.get(arr_out , i)  
    ema := na(currentEma) ? source : source * k + (1 - k) * currentEma
    array.set(arr_out, i, ema)  

    ema
rma(float source, int length,  float[] arr_out, int i) =>
    if i == -1
        runtime.error('push inputs to matrix')  
    
    float rma = na
    currentRma = array.get(arr_out , i)  
    rma := na(currentRma) ? source : (currentRma * (length-1) + source)/length
    array.set(arr_out, i, rma)  

    rma
//ta.rma()
rma(array<float> source, int length, array<float> arr_o, int i, bool tf_changed) =>
    float rma = na
    
     
    if i == -1
        runtime.error('push inputs to matrix')
    currentRma = array.get(arr_o , i)
    if na(currentRma) 
        for index = 0 to length-1 

            src = array.get(source, length-1-index)
            rma := na(currentRma) ? src : (currentRma * (length-1) + src)/length
            currentRma := rma
 
        array.set(arr_o , i, rma)


    if tf_changed
        src = array.get(source, 0)    
        rma := (currentRma * (length-1) + src)/length
        array.set(arr_o , i, rma)
rma(array<float> source, int length, array<float> arr_o, int i) =>
    float rma = na
    if i == -1
        runtime.error('push inputs to matrix')    
    

    currentRma = array.get(arr_o , i) 
    if na(currentRma)
        for index = 0 to length-1 

            src = array.get(source, length-1-index)
            rma := na(currentRma) ? src : (currentRma * (length-1) + src)/length
            currentRma := rma

        array.set(arr_o , i, rma)
    else
        src = array.get(source, 0)    
        rma := (currentRma * (length-1) + src)/length
        array.set(arr_o , i, rma)  

    rma
sma(string source, int length,  float[] arr_out, int i, bool tf_changed) => 
    float sma = 0
    float presma =array.get(arr_out, i)
    if na(presma) 
        for index = 0 to length-1
            sma := sma + fu.resource(source, index)/length
        array.set(arr_out, i, sma)  
    else if tf_changed
        sma:= presma + (fu.resource(source) - fu.resource(source, length))/length
        array.set(arr_out, i, sma)  
    sma
sma(string source, int length,  float[] arr_out, int i) => 
    float sma = 0
    float presma =array.get(arr_out, i)
    if na(presma) 
        for index = 0 to length-1
            sma := sma + fu.resource(source, index)/length
    else
        sma:= presma + (fu.resource(source) - fu.resource(source, length))/length
    array.set(arr_out, i, sma)  
    sma
sma(float[] source, int length,  float[] arr_out, int i) => 
    float sma = na
    sma := array.avg(array.slice(source, 0, length))
    array.set(arr_out, i, sma)  
    sma
// vwma(string source, int length,  float[] arr_out, int i) =>
//     ta.vwma()
hma(series string src, series int length, float[] arr_out, int i) =>  
    size = math.floor(math.sqrt(length))
    arr_temp = array.new<float>(size, na)
    for index=0 to  size -1 
        res = 2 * wma(src, math.floor(length / 2), h= index) - wma(src, length, h= index)
        array.set(arr_temp, index, res)
    hma = wma(arr_temp, size)
    array.set(arr_out, i, hma)
    hma


hma(series float[] source, series int length, float[] arr_out, int i) =>
    size = array.size(source)
    l_sqrt =math.floor(math.sqrt(length))
    
    if size < length+l_sqrt-1
        runtime.error('array size for hma should be at least (length + square_root of length -1)')
    hmaRange = array.new_float(0)

    for i=0 to (l_sqrt-1)
        arr_temp=array.slice(source, l_sqrt-1-i, size)
        wma1 = wma(arr_temp, math.floor(length / 2))
        wma2 = wma(arr_temp, length)
        array.unshift(hmaRange, 2 * wma1 - wma2)
    float hma = na
    if array.size(hmaRange) == l_sqrt
        hma := wma(hmaRange, l_sqrt)
    array.set(arr_out, i, hma)
    hma


if inp_method == 1
    
    if barstate.isfirst
        temp_arr = array.new_string(0)
        string val1 =na
        string val2 =na
        string val3 =na
        string val4 =na
        var str_arr_l=fu.stringify(arr_l)
        for i=0 to array.size(arr_l)-1
            val1 := array.get(str_arr_l, i)
            for i=0 to array.size(arr_t)-1
                val2 := array.get(arr_t, i)
                for i=0 to array.size(arr_s)-1
                    val3 := array.get(arr_s, i)
                    for i=0 to array.size(arr_tf)-1
                        val4 := array.get(arr_tf, i)
                        array.push(temp_arr, sed.encode(val1, val2, val3, val4))
        temp_arr := fu.arrdedup(temp_arr)            
        arr_in := temp_arr
        arr_out := array.new_float(array.size(temp_arr), na) 

    for [i, ident] in arr_in

        arr_temp = sed.decode(ident)
        length = int(str.tonumber(array.get(arr_temp, 0)))
        type   = array.get(arr_temp, 1)
        rawsrc = array.get(arr_temp, 2)
        tf = array.get(arr_temp, 3)
        if tf == timeframe.period
            source = fu.resource(rawsrc)
            switch type
                'EMA'                             => ema(source, length, arr_out, i)
                'SMA'                             => sma(rawsrc, length, arr_out, i)
                'RMA'                             => rma(source, length, arr_out, i)
                'WMA'                             => wma(rawsrc, length, arr_out, i)
                'HMA_slow'                             => hma(rawsrc, length, arr_out, i)
                //'EHMA'                             => hma(rawsrc, length, arr_out, i)
        if tf != timeframe.period  and  change_tf(tf, inp_tf1 = inp_tf1, inp_tf2 = inp_tf2, change_tf1 = change_tf1,change_tf2 = change_tf2)
            arr_src = resource_tf(src =rawsrc, tf= tf, inp_tf1 = inp_tf1, inp_tf2 = inp_tf2,inp_tf3 = inp_tf3, out_tf1 = mat_out1,out_tf2 = mat_out2, out_tf3 = mat_out3)
            switch type
                'EMA'                             => ema(arr_src, length, arr_out, i)
                'SMA'                             => sma(arr_src, length, arr_out, i)
                'RMA'                             => rma(arr_src, length, arr_out, i)
                'WMA'                             => wma(arr_src, length, arr_out, i)
                'HMA_slow'                             => hma(arr_src, length, arr_out, i)
                //'EHMA'                            => ehma(arr_src, length, arr_out, i)
    a := array.get(arr_out, array.indexof(arr_in,sed.encode(str.tostring(inp_l1), inp_type1, inp_source1, inp_tf1)))
    b := array.get(arr_out, array.indexof(arr_in,sed.encode(str.tostring(inp_l2), inp_type2, inp_source2, inp_tf2)))
    //c := array.get(arr_out, array.indexof(arr_in,sed.encode(str.tostring(inp_l3), inp_type3, inp_source3, inp_tf3)))
// plot(request.security(inp_sym, inp_tf1, ta.ema(fu.resource(inp_source1), inp_l1)), title = 'compare to my ma 1', color = color.yellow)
// plot(request.security(inp_sym, inp_tf2, ta.ema(fu.resource(inp_source2), inp_l2)), title = 'compare to my ma 2', color = color.purple)
// plot(request.security(inp_sym, inp_tf3, ta.ema(fu.resource(inp_source3), inp_l3)), title = 'compare to my ma 3', color = color.white)



float ta_ma_l1 = switch inp_type1
    'RMA'                             => ifSecurity(inp_sym, inp_tf1, ta.rma(fu.resource(inp_source1), inp_l1))
    'SMA'                             => ifSecurity(inp_sym, inp_tf1, ta.sma(fu.resource(inp_source1), inp_l1))
    'EMA'                             => ifSecurity(inp_sym, inp_tf1, ta.ema(fu.resource(inp_source1), inp_l1))
    'HMA'                             => ifSecurity(inp_sym, inp_tf1, ta.hma(fu.resource(inp_source1), inp_l1))
    //'VWMA'                            => ta.vwma(close,inp_l1)
    'WMA'                             => ifSecurity(inp_sym, inp_tf1, ta.wma(fu.resource(inp_source1), inp_l1))
float ta_ma_l2 = switch inp_type2
    'RMA'                             => ifSecurity(inp_sym, inp_tf2, ta.rma(fu.resource(inp_source2), inp_l2))
    'SMA'                             => ifSecurity(inp_sym, inp_tf2, ta.sma(fu.resource(inp_source2), inp_l2))
    'EMA'                             => ifSecurity(inp_sym, inp_tf2, ta.ema(fu.resource(inp_source2), inp_l2))
    'HMA'                             => ifSecurity(inp_sym, inp_tf2, ta.hma(fu.resource(inp_source2), inp_l2))
    //'VWMA'                            => ta.vwma(close,inp_l2)
    'WMA'                             => ifSecurity(inp_sym, inp_tf2, ta.wma(fu.resource(inp_source2), inp_l2))

plot(ta_ma_l1, title = 'compare to my ma 1', color = color.yellow)
plot(ta_ma_l2, title = 'compare to my ma 2', color = color.rgb(255, 0, 234))
plot(3*ta.wma(fu.resource(inp_source2),math.floor(inp_l2/2)) - 2*ta.ema(fu.resource(inp_source2),math.floor(inp_l2/2))) 


// a = matrix.get(mat_inouts, 1, array.indexof(matrix.row(mat_inouts,0), 140203))
// b = matrix.get(mat_inouts, 1, array.indexof(matrix.row(mat_inouts,0), 210203))
// c = matrix.get(mat_inouts, 1, array.indexof(matrix.row(mat_inouts,0), 510203))
plot(a, title='my_ma l1', color = color.green)
plot(b, title='my_ma l2', color = color.red)
//plot(c, title='my_ma l3', color = color.rgb(47, 0, 255)) 


res =  str.tostring(mat_out1)
// res2 =  inp_method == 1 ? str.tostring(matrix.row(mat_inouts, 1)) : str.tostring(arr_out)
res3 = str.tostring(arr_in)

[timePerBarInMs, totalTimeInMs, barsTimed, barsNotTimed] = sw.stopwatchStats()
msElapsed = sw.stopwatch()

if barstate.islast
    var table t = table.new(position.middle_right, 1, 1)
    var txt = str.tostring(timePerBarInMs, "ms/bar: #.######\n") +
              str.tostring(totalTimeInMs, "Total time (ms): #,###.######\n") + 
              str.tostring(barsTimed + barsNotTimed, "Bars analyzed: #\n") +
              str.tostring(a-ta_ma_l1, "Result: #.################\n")+
               res + '\n' +
               res3 + '\n' 
            //   res2
    table.cell(t, 0, 0, txt, bgcolor = color.yellow, text_halign = text.align_right)


// if barstate.islast
// 	labtext = 
// 	label=label.new(last_bar_index, high, labtext, textalign = text.align_left)
// 	label.delete(label[1])
