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

// Update
//   - update screenshot
//   - add alerts
//   - fix deletion of triggered zone
//   - update screenshot
//   - add mid lines to imbalanced boxes
//   - add color option for each box type

// Update 6-5-2023
//  - add timeframe selection

//@version=5
indicator(title='Imbalanced zone', overlay=true)

tf = input.timeframe(defval = '', title = 'TimeFrame')

penetrationRatio = input.float(defval=0.2, minval=0, maxval=1, step=0.1, title="Penetration Ratio")
reduce = input.bool(defval=true, title="Reduce boxes")
extendBoxes = input.bool(defval=true, title="Extend boxes")
imbalancedDownColor = input.color(color.rgb(120,120,120,80), title="Box color", group="Imbalanced down")
imbalancedDownMidLineColor = input.color(color.rgb(120,120,120), title="Mid line color", group="Imbalanced down")
imbalancedUpColor = input.color(color.rgb(120,120,120,80), title="Box color", group="Imbalanced up")
imbalancedUpMidLineColor = input.color(color.rgb(120,120,120), title="Mid line color", group="Imbalanced up")

var box[] imbalancedDownBoxes = array.new_box()
var box[] imbalancedUpBoxes = array.new_box()
var line[] imbalancedDownMidLines = array.new_line()
var line[] imbalancedUpMidLines = array.new_line()

maxBarHistory = input.int(500, title="Max IPA age")

extension = extend.none
if extendBoxes
    extension := extend.right

findImbalance(h1, h3, l1, l3) =>
    if h3 < l1 and l3 != l3[1]
        array.push(imbalancedDownBoxes,box.new(left=bar_index - 2,bottom=h3,right=bar_index + 20,top=l1,bgcolor=imbalancedDownColor,border_color=imbalancedDownColor,extend=extension))
        midPoint = (h3-l1)/2+l1
        array.push(imbalancedDownMidLines, line.new(bar_index - 2, midPoint, bar_index+20, midPoint, style=line.style_dotted, extend=extension, color=imbalancedDownMidLineColor))

    if l3 > h1 and h3 != h3[1]
        array.push(imbalancedUpBoxes, box.new(left=bar_index - 2, top=l3, right=bar_index + 20, bottom=h1, bgcolor=imbalancedUpColor, border_color=imbalancedUpColor, extend=extension))
        midPoint = (h1-l3)/2+l3
        array.push(imbalancedUpMidLines, line.new(bar_index - 2, midPoint, bar_index+20, midPoint, style=line.style_dotted, extend=extension, color=imbalancedUpMidLineColor))

[h1, h3, l1, l3] = request.security(syminfo.tickerid, tf, [high[1], high[3], low[1], low[3]],lookahead = barmerge.lookahead_on)
findImbalance(h1, h3, l1, l3)

if array.size(imbalancedUpBoxes) > 0
    for i = array.size(imbalancedUpBoxes) - 1 to 0 by 1
        imbalancedBox = array.get(imbalancedUpBoxes, i)
        top = box.get_top(imbalancedBox)
        bottom = box.get_bottom(imbalancedBox)
        invalidationLimit = (top - bottom) * penetrationRatio
        box.set_right(imbalancedBox, bar_index + 20)

        midLine = array.get(imbalancedUpMidLines, i)
        line.set_x2(midLine, bar_index + 20)

        age = box.get_left(imbalancedBox)
        if bar_index - age > maxBarHistory
            box.delete(imbalancedBox)
            array.remove(imbalancedUpBoxes, i)
            line.delete(midLine)
            array.remove(imbalancedUpMidLines, i)
        else
            if reduce and high > bottom
                box.set_bottom(imbalancedBox, high)
            if high >= bottom + invalidationLimit
                box.delete(imbalancedBox)
                array.remove(imbalancedUpBoxes, i)
                line.delete(midLine)
                array.remove(imbalancedUpMidLines, i)

if array.size(imbalancedDownBoxes) > 0
    for i = array.size(imbalancedDownBoxes) - 1 to 0 by 1
        imbalancedBox = array.get(imbalancedDownBoxes, i)
        top = box.get_top(imbalancedBox)
        bottom = box.get_bottom(imbalancedBox)
        invalidationLimit = (top - bottom) * penetrationRatio
        box.set_right(imbalancedBox, bar_index + 20)

        midLine = array.get(imbalancedDownMidLines, i)
        line.set_x2(midLine, bar_index + 20)

        age = box.get_left(imbalancedBox)
        if bar_index - age > maxBarHistory
            box.delete(imbalancedBox)
            array.remove(imbalancedDownBoxes, i)
            line.delete(midLine)
            array.remove(imbalancedDownMidLines, i)
        else
            if reduce and low < top
                box.set_top(imbalancedBox, low)
            if low <= top - invalidationLimit
                box.delete(imbalancedBox)
                array.remove(imbalancedDownBoxes, i)
                line.delete(midLine)
                array.remove(imbalancedDownMidLines, i)
