Moving Averages 3D
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © LonesomeTheBlue

//@version=5
indicator("Moving Averages 3D", max_lines_count = 500)
matype = input.string(defval = 'SMA', title = 'Moving Average Type', options = ['EMA', 'SMA'])
len1 = input.int(defval = 10, title = 'Starting Length', minval = 5, inline = 'len')
step = input.int(defval = 5, title = 'Step', minval = 1, inline = 'len')
colorup = input.string(defval = 'Green', title = 'Colors', options = ['Green', 'Red', 'Blue'], inline = 'colors')
colordown = input.string(defval = 'Red', title = '', options = ['Green', 'Red', 'Blue'], inline = 'colors')
width = input.int(defval = 2, title = 'Width', minval = 1, maxval = 5)

var lines = array.new_line(500)
for x = 0 to array.size(lines) - 1
    line.delete(array.get(lines, x))

get_ma(length)=>
    (matype == 'EMA' ? ta.ema(close, length) : ta.sma(close, length))

var sma1 = array.new_float(50, na),     array.unshift(sma1, get_ma(len1 + 9 * step)), array.pop(sma1)
var sma2 = array.new_float(50, na),     array.unshift(sma2, get_ma(len1 + 8 * step)),  array.pop(sma2)
var sma3 = array.new_float(50, na),     array.unshift(sma3, get_ma(len1 + 7 * step)),  array.pop(sma3)
var sma4 = array.new_float(50, na),     array.unshift(sma4, get_ma(len1 + 6 * step)),  array.pop(sma4)
var sma5 = array.new_float(50, na),     array.unshift(sma5, get_ma(len1 + 5 * step)),  array.pop(sma5)
var sma6 = array.new_float(50, na),     array.unshift(sma6, get_ma(len1 + 4 * step)),  array.pop(sma6)
var sma7 = array.new_float(50, na),     array.unshift(sma7, get_ma(len1 + 3 * step)),  array.pop(sma7)
var sma8 = array.new_float(50, na),     array.unshift(sma8, get_ma(len1 + 2 * step)),  array.pop(sma8)
var sma9 = array.new_float(50, na),     array.unshift(sma9, get_ma(len1 + 1 * step)),  array.pop(sma9)
var sma10 = array.new_float(50, na),    array.unshift(sma10, get_ma(len1 + 0 * step)), array.pop(sma10)

get_array(array, element)=>
    array == 1 ? array.get(sma1, element) : 
     array == 2 ? array.get(sma2, element) : 
     array == 3 ? array.get(sma3, element) :
     array == 4 ? array.get(sma4, element) :
     array == 5 ? array.get(sma5, element) :
     array == 6 ? array.get(sma6, element) :
     array == 7 ? array.get(sma7, element) :
     array == 8 ? array.get(sma8, element) :
     array == 9 ? array.get(sma9, element) :
     array.get(sma10, element)

cwidth = (ta.highest(200) - ta.lowest(200)) / 200
cwidthcolor = (ta.highest(200) - ta.lowest(200)) / 4000
get_min_max(value)=>
    math.min(math.max(value, 0), 255)
    
get_color(value1, value2)=>
    diff = math.round((value2 - value1) / cwidthcolor)
    red = colorup == 'Red' ? get_min_max(128 + diff) : 
          colordown == 'Red' ? get_min_max(128 - diff) : 0
    green = colorup == 'Green' ? get_min_max(128 + diff) : 
          colordown == 'Green' ? get_min_max(128 - diff) : 0
    blue = colorup == 'Blue' ? get_min_max(128 + diff) : 
          colordown == 'Blue' ? get_min_max(128 - diff) : 0
    color.rgb(red, green, blue, 0)
    
base = array.get(sma1, 0)
if barstate.islast
    for x = 39 to 0
        b1 = base + x * cwidth
        b2 = base + (x + 1) * cwidth
        for y = 10 to 1
            array.set(lines, x + 40 * y, line.new(x1 = bar_index - 5 * (10 - y) - x * width, 
                                                  y1 = b1 + cwidth * y * 10 + get_array(y, x) - array.get(sma1, 0), 
                                                  x2 = bar_index - 5 * (10 - y) - x * width - width, 
                                                  y2 = b2 + cwidth * y * 10 + get_array(y, x + 1) - array.get(sma1, 0),
                                                  color = color.new(color.black, 50), style = line.style_dotted))
            
            if y < 10
                linefill.new(array.get(lines, x + 40 * y), array.get(lines, x + 40 * (y + 1)), color = get_color(get_array(y, x + 1), get_array(y, x)))
                