probability_of_touch
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © voided

//@version=5
indicator("probability_of_touch", overlay = true)


// background coloring

start = input.time(timestamp("1 Jan 1950"), "start")
end = input.time(timestamp("1 Jan 2300"), "end")

in_clr = color.new(color.gray, 95)
out_clr = color.new(color.white, 100)

in_range = time >= start and time < end
bgcolor(in_range ? in_clr : out_clr)

// main

level = input.float(0, "level")
type = input.string("price", "type", options = [ "price", "percentage", "stdev" ])
mark = input.string("close", "mark", options = [ "open", "high", "low", "close" ])
length = input.int(1, "length")
window = input.int(20, "window")
debug = input.bool(false, "debug")

hv20 = math.sqrt(ta.ema(math.pow(math.log(close / close[1]), 2), window) * 252)

var up = array.new_float()
var dn = array.new_float()

mark_ = mark == "open" ? open : mark == "high" ? high : mark == "low" ? low : mark == "close" ? close : na

pos = level >= 0
touched = 0
y = mark_[length]

for i = 0 to length - 1 by 1

    y_ = type == "price" ? y + level : type == "percentage" ? y * (1 + level / 100) : y * (1 + level * hv20[length] * math.sqrt((length - i) / 252))
    
    if pos and high[i] >= y_
    
        touched := 1
        
        if in_range and debug
        
            label.new(bar_index, na, "x", yloc = yloc.abovebar, style = label.style_none, textcolor = color.fuchsia)
        
        break
        
    else if not pos and low[i] <= y_
    
        touched := 1
        
        if in_range and debug
        
            label.new(bar_index, na, "x", yloc = yloc.abovebar, style = label.style_none, textcolor = color.fuchsia)
        
        break

if in_range

    array.push(level >= 0 ? up : dn, touched)


// display

if barstate.islast
    
    lbl_clr = pos ? color.new(color.green, 70) : color.new(color.red, 70)
    data_clr = color.new(color.gray, 95)
    fmt = "#.##"
    
    y_ = type == "price" ? mark_ + level : type == "percentage" ? mark_ * (1 + level / 100) : mark_ * (1 + level * hv20 * math.sqrt(length / 252))
    
    one_day = 24 * 60 * 60 * 1000
    line.new(time, y_, time + one_day * length, y_, color = color.fuchsia, xloc = xloc.bar_time)
    
    pct = pos ? array.avg(up) : array.avg(dn)
    count = pos ? array.sum(up) : array.sum(dn)
    total = pos ? array.size(up) : array.size(dn)

    t = table.new(position.top_right, 3, 3, bgcolor = data_clr)

    table.cell(t, 0, 0, "level type", bgcolor = lbl_clr)
    table.cell(t, 1, 0, type)

    table.cell(t, 0, 1, "current level (price)", bgcolor = lbl_clr)
    table.cell(t, 1, 1, str.tostring(y_, fmt))
    
    table.cell(t, 0, 2, "probability of touch", bgcolor = lbl_clr)
    table.cell(t, 1, 2, str.tostring(pct, fmt) + " (" + str.tostring(count) + "/" + str.tostring(total) + ")")
    
    