Simple Moving Average Extrapolation via Monte Carlo (SMAE)
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © peacefulLizard50262

//@version=5
indicator("SMAE", overlay = true, max_lines_count = 500)
logReturn(a, b)=>
    math.log(a / b) * 100

addLogReturn(number, logReturn) =>
    result = number * math.exp(logReturn / 100)
    result

movement_probability(source, accuracy)=>
    var movement_size_green = array.new<float>(1)
    var count_green = array.new<int>(1)
    var movement_size_red = array.new<float>(1)
    var count_red = array.new<int>(1)
    var candle = array.new<int>(2, 0)
    change = math.round(logReturn(source, source[1]), accuracy)

    if close > open
        if array.includes(movement_size_green, change)
            index = array.indexof(movement_size_green, change)
            array.set(count_green, index, array.get(count_green, index) + 1)
            array.set(candle, 0, array.get(candle, 0) + 1)
        else
            array.push(movement_size_green, change)
            array.push(count_green, 1)
            array.set(candle, 0, array.get(candle, 0) + 1)

    if close < open
        if array.includes(movement_size_red, change)
            index = array.indexof(movement_size_red, change)
            array.set(count_red, index, array.get(count_red, index) + 1)
            array.set(candle, 1, array.get(candle, 1) + 1)
        else
            array.push(movement_size_red, change)
            array.push(count_red, 1)
            array.set(candle, 1, array.get(candle, 1) + 1)

    probability_green = array.new<float>(array.size(count_green))
    probability_red   = array.new<float>(array.size(count_red))
    probability_can   = array.new<float>(2)

    count_green_sum = array.sum(count_green)
    count_red_sum = array.sum(count_red)
    count_can_sum = array.sum(candle)

    for i = 0 to array.size(count_green) - 1
        probability = array.get(count_green, i) / count_green_sum * 100
        array.set(probability_green, i, probability)

    for i = 0 to array.size(count_red) - 1
        probability = array.get(count_red, i) / count_red_sum * 100
        array.set(probability_red, i, probability)

    for i = 0 to 1
        probability = array.get(candle, i) / count_can_sum * 100
        array.set(probability_can, i, probability)
    [movement_size_green, movement_size_red, probability_green, probability_red, probability_can]


monte(steps, movement_size_green, movement_size_red, probability_green, probability_red, probability_can)=>
    // Initialize the array to store the Monte Carlo simulation results
    var monte_array = array.new<float>(steps)

    // Loop through the number of steps
    for i = 0 to steps - 1
        // Generate a random number to determine if it's a green or red move
        random_num_1 = math.random(0, 100, timenow)

        // Check if the random number is less than or equal to the green candle probability
        if random_num_1 <= array.get(probability_can, 0)
            // Pick a green move size based on its probability
            random_num = math.random(0, 100, timenow)
            var cumulative_probability = 0.0
            for j = 0 to array.size(probability_green) - 1
                cumulative_probability := nz(cumulative_probability[1]) + array.get(probability_green, j)
                if random_num <= cumulative_probability
                    // Get the corresponding green move size
                    move_size = array.get(movement_size_green, j)
                    // Add the move size to the monte_array
                    array.set(monte_array, i, move_size)

        else
            // Pick a red move size based on its probability
            random_num = math.random(0, 100, timenow)
            var cumulative_probability = 0.0
            for j = 0 to array.size(probability_red) - 1
                cumulative_probability := nz(cumulative_probability[1]) + array.get(probability_red, j)
                if random_num <= cumulative_probability
                    // Get the corresponding red move size
                    move_size = array.get(movement_size_red, j)
                    // Add the move size to the monte_array
                    array.set(monte_array, i, move_size)
    monte_array

sim(source, steps, movement_size_green, movement_size_red, probability_green, probability_red, probability_can) =>
    var simulation = array.new<float>(steps)
    movements = monte(steps, movement_size_green, movement_size_red, probability_green, probability_red, probability_can)
    for i = 1 to steps - 1
        array.set(simulation, 0, source)
        array.set(simulation, i, addLogReturn(array.get(simulation, i - 1), array.get(movements, i)))
    simulation


draw_lines(source, values, length, extrapolate)=>
    time_quanta = -(time[1] - time)
    delay = length/2 * time_quanta
    size = extrapolate ? array.size(values) - 1 : length/2 - 1
    array.insert(values, 0, source)
    lines = array.new<line>(array.size(values) - 1)
    if barstate.islast
        for i = 0 to size
            array.push(lines, line.new(time + time_quanta * i - delay, array.get(values, i), time + time_quanta * (i + 1) - delay, array.get(values, i + 1), xloc.bar_time, color = color.orange))

    if barstate.isconfirmed
        for i = 0 to array.size(lines) - 1
            line.delete(array.get(lines, i))

extrapolate(source, length, accuracy) =>
    ma = ta.sma(source, length)
    delay = int(length/10)
    [movement_size_green, movement_size_red, probability_green, probability_red, probability_can] = movement_probability(ma, accuracy)
    sim = sim(ma, delay, movement_size_green, movement_size_red, probability_green, probability_red, probability_can)

source = input.source(close, "Source")
number = input.int(20800, "Bar Start")
length = input.int(30, "Length", 20)
extrapolate = input.bool(false, "Extrapolate")
show_bar_index = input.bool(true, "Show Bar Index")

avg = ta.sma(source, length)

if bar_index > number
    draw_lines(avg, extrapolate(close, length, 10), length, extrapolate)

var label barindex = na

if show_bar_index
    barindex := label.new(bar_index + 1, close, text = str.tostring(bar_index), style=label.style_label_down, textcolor = close > open ? color.green : color.red, color=color.new(color.white, 100))

if barstate.isconfirmed
    label.delete(barindex)

plot(avg, offset = -length/2)