Order Blocks
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © pmk07
//
// Acknowledgements/Reference:
//
// @rumpypumpydumpy - Higher Order Pivots
// https://www.tradingview.com/v/x2LlRvBe/
//
// @MarkMiddleton2020 - Order Blocks
// https://www.tradingview.com/v/GecN34Qq/
//
//@version=5

indicator(title='Order Blocks', overlay=true)

string      bos_type        = input.string      ("High and Low",    title='MSB trigger',                            options=["High and Low", "Close and Open"])
bool        pv2_sv          = input.bool        (true,              title='Display 2nd order pivots')
bool        bos_sv          = input.bool        (true,              title='Highlight candles that broke structure')
bool        msb_sv          = input.bool        (true,              title='Plot market structure broke lines')
bool        box_sv          = input.bool        (true,              title='Plot demand boxes')
int         box_test_delay  = input.int         (3,                 title='Delay to count test of demand box')
int         box_fill_delay  = input.int         (3,                 title='Delay to count fill of demand box')
bool        box_test_sv     = input.bool        (true,              title='Dim tested demand boxes')
bool        box_stop_sv     = input.bool        (true,              title='Stop plotting filled demand boxes')

var float[] pvh1_price      = array.new_float   (1000, na)          // high
var int[]   pvh1_time       = array.new_int     (1000, na)
var float[] pvl1_price      = array.new_float   (1000, na)          // low
var int[]   pvl1_time       = array.new_int     (1000, na)

var float[] pvh2_price      = array.new_float   (1000, na)          // higher high
var int[]   pvh2_time       = array.new_int     (1000, na)
var float[] pvl2_price      = array.new_float   (1000, na)          // lower low
var int[]   pvl2_time       = array.new_int     (1000, na)

var float   htcmrll_price   = na                                    // high that created most recent ll
var int     htcmrll_time    = na
var float   ltcmrhh_price   = na                                    // low that created most recent hh
var int     ltcmrhh_time    = na

var box[]   long_boxes      = array.new_box()
var box[]   short_boxes     = array.new_box()
var box[]   oldlong_boxes   = array.new_box()
var box[]   oldshort_boxes  = array.new_box()

var line[]  bull_bos_lines  = array.new_line()
var line[]  bear_bos_lines  = array.new_line()

var label[] la_ph2          = array.new_label   (1000, na)
var label[] la_pl2          = array.new_label   (1000, na)

var float   temp_pv_0       = na
var float   temp_pv_1       = na
var float   temp_pv_2       = na

bool        pvh             = high < high[1] and high[1] > high[2]
bool        pvl             = low > low[1] and low[1] < low[2]

int         pv1_time        = bar_index[1]
float       pv1_high        = high[1]
float       pv1_low         = low[1]
float       trigger_high    = bos_type=="High and Low" ? high : math.max(open, close)
float       trigger_low     = bos_type=="High and Low" ? low : math.min(open, close)

bool        bos_candle      = false
bool        new_ph_2nd      = false
bool        new_pl_2nd      = false

if barstate.isconfirmed

    if pvh
        array.pop(pvh1_price)
        array.pop(pvh1_time)
        array.unshift(pvh1_price, pv1_high)
        array.unshift(pvh1_time, pv1_time)
        if array.size(pvh1_price) > 2
            temp_pv_0 := array.get(pvh1_price, 0)
            temp_pv_1 := array.get(pvh1_price, 1)
            temp_pv_2 := array.get(pvh1_price, 2)
            if temp_pv_0 > temp_pv_1
                for i = 0 to array.size(pvl1_time) - 1 by 1
                    temp_ltcmrhh_time = array.get(pvl1_time, i)
                    if temp_ltcmrhh_time < array.get(pvh1_time, 0)
                        ltcmrhh_price := array.get(pvl1_price, i)
                        ltcmrhh_time := temp_ltcmrhh_time
                        break
            if temp_pv_0 < temp_pv_1 and temp_pv_1 > temp_pv_2
                array.pop(pvh2_price)
                array.pop(pvh2_time)
                array.unshift(pvh2_price, temp_pv_1)
                array.unshift(pvh2_time, array.get(pvh1_time, 1))
                new_ph_2nd := true

    if pvl
        array.pop(pvl1_price)
        array.pop(pvl1_time)
        array.unshift(pvl1_price, pv1_low)
        array.unshift(pvl1_time, pv1_time)
        if array.size(pvl1_price) > 2
            temp_pv_0 := array.get(pvl1_price, 0)
            temp_pv_1 := array.get(pvl1_price, 1)
            temp_pv_2 := array.get(pvl1_price, 2)
            if temp_pv_0 < temp_pv_1
                for i = 0 to array.size(pvh1_time) - 1 by 1
                    temp_htcmrll_time = array.get(pvh1_time, i)
                    if temp_htcmrll_time < array.get(pvl1_time, 0)
                        htcmrll_price := array.get(pvh1_price, i)
                        htcmrll_time := temp_htcmrll_time
                        break
            if temp_pv_0 > temp_pv_1 and temp_pv_1 < temp_pv_2
                array.pop(pvl2_price)
                array.pop(pvl2_time)
                array.unshift(pvl2_price, temp_pv_1)
                array.unshift(pvl2_time, array.get(pvl1_time, 1))
                new_pl_2nd := true

    if trigger_high > htcmrll_price
        if msb_sv
            array.push(bull_bos_lines, line.new(x1=htcmrll_time, y1=htcmrll_price, x2=bar_index, y2=htcmrll_price, color=color.red, width=2))
        if box_sv
            array.push(long_boxes, box.new(left=array.get(pvl1_time, 0), top=math.min(high[bar_index - array.get(pvl1_time, 0)], high[bar_index - array.get(pvl1_time, 0) + 1]), right=bar_index, bottom=array.get(pvl1_price, 0), bgcolor=color.rgb(0, 255, 0, 80), border_color=color.rgb(0, 255, 0, 80), extend=extend.right))
        bos_candle := true
        htcmrll_price := na
        htcmrll_price

    if trigger_low < ltcmrhh_price
        if msb_sv
            array.push(bear_bos_lines, line.new(x1=ltcmrhh_time, y1=ltcmrhh_price, x2=bar_index, y2=ltcmrhh_price, color=color.green, width=2))
        if box_sv
            array.push(short_boxes, box.new(left=array.get(pvh1_time, 0), top=array.get(pvh1_price, 0), right=bar_index, bottom=math.max(low[bar_index - array.get(pvh1_time, 0)], low[bar_index - array.get(pvh1_time, 0) + 1]), bgcolor=color.rgb(255, 0, 0, 80), border_color=color.rgb(255, 0, 0, 80), extend=extend.right))
        bos_candle := true
        ltcmrhh_price := na
        ltcmrhh_price

    if array.size(short_boxes) > 0
        for i = array.size(short_boxes) - 1 to 0 by 1
            tbox = array.get(short_boxes, i)
            top = box.get_top(tbox)
            bottom = box.get_bottom(tbox)
            if trigger_high > bottom and box.get_left(tbox) + box_test_delay < bar_index and box_test_sv
                box.set_bgcolor(tbox, color.rgb(192, 192, 192, 80))
                box.set_border_color(tbox, color.rgb(192, 192, 192, 80))
            if trigger_high > top and box.get_left(tbox) + box_fill_delay < bar_index
                if box_stop_sv
                    box.set_right(tbox, bar_index)
                    box.set_extend(tbox, extend.none)
                    array.unshift(oldshort_boxes, tbox)
                    array.remove(short_boxes, i)

    if array.size(long_boxes) > 0
        for i = array.size(long_boxes) - 1 to 0 by 1
            lbox = array.get(long_boxes, i)
            top = box.get_top(lbox)
            bottom = box.get_bottom(lbox)
            if trigger_low < top and box.get_left(lbox) + box_test_delay < bar_index and box_test_sv
                box.set_bgcolor(lbox, color.rgb(192, 192, 192, 80))
                box.set_border_color(lbox, color.rgb(192, 192, 192, 80))
            if trigger_low < bottom and box.get_left(lbox) + box_fill_delay < bar_index
                if box_stop_sv
                    box.set_right(lbox, bar_index)
                    box.set_extend(lbox, extend.none)
                    array.unshift(oldlong_boxes, lbox)
                    array.remove(long_boxes, i)
    if pv2_sv
        if new_ph_2nd
            array.pop(la_ph2)
            array.unshift(la_ph2, label.new(x = array.get(pvh2_time, 0), y = array.get(pvh2_price, 0), xloc = xloc.bar_index, style = label.style_label_down,    color = #770000FF, size = size.tiny))
        if new_pl_2nd
            array.pop(la_pl2)
            array.unshift(la_pl2, label.new(x = array.get(pvl2_time, 0), y = array.get(pvl2_price, 0), xloc = xloc.bar_index, style = label.style_label_up,      color = #007700FF, size = size.tiny))

barcolor(bos_candle and bos_sv ? color.yellow : na)
