PhantomFlow DynamicLevels
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// Credit to LonesomeTheBlue for the calculate/get volume function:
// https://www.tradingview.com/script/kIY0znXs-Volume-Profile-Fixed-Range/
// © mxrshxl_trxde

//@version=5
indicator(title = "PhantomFlow DynamicLevels", shorttitle = "PhantomFlow DynamicLevels", overlay = true)

tooltip_period  = 'If the indicator does not show anything, but only an error about the execution time, try to reduce the period, or the period multiplier. For free TradingView - it is desirable that the Period * Multiplier does not exceed 1000'

period = input.int(defval = 40, title = "Period", minval = 40, maxval = 100, step = 10, tooltip = tooltip_period)
period_multiplier = input.int(defval = 10, title = "Period Multiplier", minval = 10, maxval = 100, step = 1)
cnum = input.int(defval = 20, title = "Accuracy", minval = 20, maxval = 100, step = 10)
wide_channel = input.bool(true, title='Wide Channel')

poc_color = input.color(title='POC', defval = color.rgb(0, 255, 255), group="Visual Settings", inline = 'ltf_color_liquidity')
val_color = input.color(title='VAL', defval = color.rgb(0, 42, 255), group="Visual Settings", inline = 'ltf_color_liquidity')
vah_color = input.color(title='VAH', defval = color.rgb(255, 0, 238), group="Visual Settings", inline = 'ltf_color_liquidity')

up_color = input.color(title='VAL Area', defval = color.rgb(17, 0, 255, 90), group="Visual Settings", inline = 'ltf_color_liquidity_area')
down_color = input.color(title='VAH Area', defval = color.rgb(255, 0, 221, 90), group="Visual Settings", inline = 'ltf_color_liquidity_area')

rangema = ta.atr(10)
va_percent = 70.

var float _lastVah = 0
var float _lastVal = 0
var float _lastPoc = 0

get_vol(y11, y12, y21, y22, height, vol) =>
    nz(math.max(math.min(math.max(y11, y12), math.max(y21, y22)) - math.max(math.min(y11, y12), math.min(y21, y22)), 0) * vol / height)

levels = array.new_float(cnum + 1)
totalvols = array.new_float(cnum, 0.)

d_switch = bar_index > period

top = ta.highest(high, period)
bot = ta.lowest(low, period)

float poc_level = na
float val_level = na
float vah_level = na

bool plotLines = false

calculateLevels(int bar_end, int bar_start) =>
    step = (top - bot) / cnum
    for x = 0 to cnum by 1
        array.set(levels, x, bot + step * x)

if bar_index >= last_bar_index - period * period_multiplier
    bar_end = bar_index
    bar_start = bar_index - period
    volumes = array.new_float(cnum * 2, 0.)
    all_bars = bar_end - bar_start
    calculateLevels(bar_end, bar_start)
    for bars = 0 to period by 1
        body_top = math.max(close[bars], open[bars])
        body_bot = math.min(close[bars], open[bars])
        itsgreen = close[bars] >= open[bars]

        topwick = high[bars] - body_top
        bottomwick = body_bot - low[bars]
        body = body_top - body_bot

        bodyvol = body * volume[bars] / (2 * topwick + 2 * bottomwick + body)
        topwickvol = 2 * topwick * volume[bars] / (2 * topwick + 2 * bottomwick + body)
        bottomwickvol = 2 * bottomwick * volume[bars] / (2 * topwick + 2 * bottomwick + body)
        for x = 0 to cnum - 1 by 1
            array.set(volumes, x, array.get(volumes, x) + (itsgreen ? get_vol(array.get(levels, x), array.get(levels, x + 1), body_bot, body_top, body, bodyvol) : 0) + get_vol(array.get(levels, x), array.get(levels, x + 1), body_top, high[bars], topwick, topwickvol) / 2 + get_vol(array.get(levels, x), array.get(levels, x + 1), body_bot, low[bars], bottomwick, bottomwickvol) / 2)
            array.set(volumes, x + cnum, array.get(volumes, x + cnum) + (itsgreen ? 0 : get_vol(array.get(levels, x), array.get(levels, x + 1), body_bot, body_top, body, bodyvol)) + get_vol(array.get(levels, x), array.get(levels, x + 1), body_top, high[bars], topwick, topwickvol) / 2 + get_vol(array.get(levels, x), array.get(levels, x + 1), body_bot, low[bars], bottomwick, bottomwickvol) / 2)

    for x = 0 to cnum - 1 by 1
        array.set(totalvols, x, array.get(volumes, x) + array.get(volumes, x + cnum))

    int poc = array.indexof(totalvols, array.max(totalvols))

    totalmax = array.sum(totalvols) * va_percent / 100.
    va_total = array.get(totalvols, poc)
    int up = poc
    int down = poc
    for x = 0 to cnum - 1 by 1
        above = 0.0
        if up + 1 < array.size(totalvols) - 1
            above += nz(array.get(totalvols, up + 1), 0.0)
            above
        if up + 2 < array.size(totalvols) - 1
            above += nz(array.get(totalvols, up + 2), 0.0)
            above

        below = 0.0
        if down - 1 > 0
            below += nz(array.get(totalvols, down - 1), 0.0)
            below
        if down - 2 > 0
            below += nz(array.get(totalvols, down - 2), 0.0)
            below

        if above > below
            up := math.min(up + 2, array.size(totalvols) - 1)
            va_total += above
            va_total
        else
            down := math.max(down - 2, 0)
            va_total += below
            va_total

        if va_total >= totalmax or down <= 0 and up >= array.size(totalvols) - 1
            break

    maxvol = array.max(totalvols)
    for x = 0 to cnum * 2 - 1 by 1
        array.set(volumes, x, array.get(volumes, x) * all_bars / (3 * maxvol))
    
    poc_level := (array.get(levels, poc) + array.get(levels, poc + 1)) / 2
    val_level := (array.get(levels, down) + array.get(levels, down + 1)) / 2
    vah_level := (array.get(levels, up) + array.get(levels, up + 1)) / 2

    bot := low
    top := high
    plotLines := true
    
if wide_channel
    vah_level += rangema * 2
    val_level -= rangema * 2

vah = plot(plotLines ? vah_level : na, "Up", vah_color)
val = plot(plotLines ? val_level : na, "Down", val_color)
poc = plot(plotLines ? poc_level : na, "Medium", poc_color)

fill(poc, vah, color=down_color, title="Background Down")
fill(poc, val, color=up_color, title="Background Up")