Bar Move Probability (BMP)
// 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("Bar Move Probability", "BMP", precision = 3, overlay = true)

percent(a, b)=>
    ((a-b)/b) * 100

logReturn(a, b)=>
    math.log(a / b) * 100

addPercentage(number, percentage) =>
    result = number * (1 + percentage / 100)
    result

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

Price = input.price(0.0, "Price", confirm = true)
useLogReturns = input(true, title="Use Log Returns")
arb_price = useLogReturns ? math.round(logReturn(Price, open), 3) : math.round(percent(Price, open), 3)
length = input.int(100, "Length", 10, 4000)


var movement_size_green = array.new<float>(length)
var count_green = array.new<int>(length)
var movement_size_red = array.new<float>(length)
var count_red = array.new<int>(length)
change = useLogReturns ? math.round(logReturn(close, close[1]), 3) : math.round(percent(close, close[1]), 3)

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)
    else
        array.push(movement_size_green, change)
        array.push(count_green, 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)
    else
        array.push(movement_size_red, change)
        array.push(count_red, 1)

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

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

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)

green_index = array.indexof(movement_size_green, arb_price)
red_index = array.indexof(movement_size_red, arb_price)

probability = arb_price > 0 and green_index != -1 ? array.get(probability_green, green_index) : arb_price < 0 and red_index != -1 ? array.get(probability_red, red_index) : 0

lines = line.new(bar_index + 3, (useLogReturns ? addLogReturn(open, arb_price) : addPercentage(open, arb_price)), bar_index + 4, (useLogReturns ? addLogReturn(open, arb_price) : addPercentage(open, arb_price)), color = (useLogReturns ? addLogReturn(open, arb_price) : addPercentage(open, arb_price)) > open ? color.green : color.red)
price = label.new(bar_index + 3, (useLogReturns ? addLogReturn(open, arb_price) : addPercentage(open, arb_price)), text=str.tostring(math.round(probability, 2))+"%", style=label.style_label_down, textcolor = (useLogReturns ? addLogReturn(open, arb_price) : addPercentage(open, arb_price)) > open ? color.green : color.red, color=color.new(color.white, 100))

if barstate.isconfirmed
    label.delete(price)
    line.delete(lines)
