//@version=5
indicator("Order Block Indicator", overlay=true)
// Order Blocks
const color colup = #089981
const color coldn = #f23645
const string tm = "[Length] Use Length to adjust coordinate of the order blocks\
n[Full] Use whole candle body"
const string tn = "Mitigation method for when to trigger order blocks"
const string tj = "Order block Metrics text size"
const string ta = 'Display internal buy & sell activity'
const string tsorder = 'Show Last number of order blocks'
const string gv = "Volumetric Order Blocks"
obshow         = input.bool(true, "Show Last", group=gv)
oblast         = input.int(3, "", 0, 50, 1, group=gv)
obupcs         = input.color(color.new(colup, 90), "", group=gv)
obdncs         = input.color(color.new(coldn, 90), "", group=gv)
obshowactivity = input.bool(true, "Show Buy/Sell Activity", group=gv)
obactup        = input.color(color.new(colup, 50), "", group=gv)
obactdn        = input.color(color.new(coldn, 50), "", group=gv)
obmode         = input.string("Length", "Construction", ["Length", "Full"],
group=gv)
len            = input.int(5, "", 1, 20, 1, group=gv)
obmiti         = input.string("Close", "Mitigation Method", ["Close", "Wick",
"Avg"], group=gv)
obtxt          = input.string("Normal", "Metric Size", ["Tiny", "Small", "Normal",
"Large", "Huge"], group=gv)
showmetric     = input.bool(true, "Show Metrics", group=gv)
showline       = input.bool(true, "Show Mid-Line", group=gv)
overlap        = input.bool(true, "Hide Overlap", group=gv, tooltip="Most recent
order block will be preserved")
blcreated           =   input.bool(false,   "Bullish OB Formed", group="ANY ALERT")
brcreated           =   input.bool(false,   "Bearish OB Formed", group="ANY ALERT")
blmitigated         =   input.bool(false,   "Bullish OB Mitigated", group="ANY ALERT")
brmitigated         =   input.bool(false,   "Bearish OB Mitigated", group="ANY ALERT")
blinside            =   input.bool(false,   "Price Inside Bullish OB", group="ANY ALERT")
brinside            =   input.bool(false,   "Price Inside Bearish OB", group="ANY ALERT")
type bar
    float   o   =   open
    float   h   =   high
    float   l   =   low
    float   c   =   close
    float   v   =   volume
    int     i   =   bar_index
    int     t   =   time
type ob
    float   top
    float   btm
    float   avg
    int     loc
    color   css
    float   vol
    int     dir
    int     move
    int   blPOS
    int   brPOS
    int   xlocbl
    int   xlocbr
type alert
    bool created   = false
    bool inside    = false
    bool mitigated = false
type cross
    bool reset = false
bar          b = bar.new()
alert     blal = alert.new()
alert     bral = alert.new()
var cross blIS = cross.new()
var cross brIS = cross.new()
method txSz(string s) =>
    out = switch s
        "Tiny"   => size.tiny
        "Small" => size.small
        "Normal" => size.normal
        "Large" => size.large
        "Huge"   => size.huge
    out
method display(ob id, ob[] full, int i) =>
    box.new(top=id.top, bottom=id.btm, left=id.loc, right=b.t, border_color=na,
bgcolor=id.css, xloc=xloc.bar_time)
    box.new(top=id.top, bottom=id.btm, left=b.t, right=b.t + 1, border_color=na,
bgcolor=id.css, xloc=xloc.bar_time, extend=extend.right)
    if obshowactivity
        box.new(top=id.top, bottom=id.avg, left=id.loc, right=id.xlocbl,
border_color=na, bgcolor=obactup, xloc=xloc.bar_time)
        box.new(top=id.avg, bottom=id.btm, left=id.loc, right=id.xlocbr,
border_color=na, bgcolor=obactdn, xloc=xloc.bar_time)
    if showline
        line.new(x1=id.loc, x2=b.t, y1=id.avg, y2=id.avg, color=color.new(id.css,
0), xloc=xloc.bar_time, style=line.style_dashed)
    if showmetric
        if i == math.min(oblast - 1, full.size() - 1)
            float   tV = 0
            float[] dV = array.new<float>()
            seq = math.min(oblast - 1, full.size() - 1)
            for j = 0 to seq
                cV = full.get(j)
                tV += cV.vol
                if j == seq
                    for y = 0 to seq
                        dV.push(math.floor((full.get(y).vol / tV) * 100))
                        id = full.get(y)
                        label.new(b.i + 1, id.avg, textcolor=color.new(id.css, 0),
style=label.style_label_left, size=obtxt.txSz(), color=#ffffff00,
text=str.tostring(math.round(full.get(y).vol, 3), format=format.volume) + " (" +
str.tostring(dV.get(y)) + "%)")
method overlap(ob[] id) =>
    if id.size() > 1
        for i = id.size() - 1 to 1
            stuff    = id.get(i)
            current = id.get(0)
            switch
                stuff.btm > current.btm   and   stuff.btm   <   current.top   =>   id.remove(i)
                stuff.top < current.top   and   stuff.btm   >   current.btm   =>   id.remove(i)
                stuff.top > current.top   and   stuff.btm   <   current.btm   =>   id.remove(i)
                stuff.top < current.top   and   stuff.top   >   current.btm   =>   id.remove(i)
method umt(ob metric) =>
    switch metric.dir
        1 =>
             switch metric.move
                 1 => metric.blPOS := metric.blPOS + 1,      metric.move := 2
                 2 => metric.blPOS := metric.blPOS + 1,      metric.move := 3
                 3 => metric.brPOS := metric.brPOS + 1,      metric.move := 1
        -1 =>
             switch metric.move
                 1 => metric.brPOS := metric.brPOS + 1,      metric.move := 2
                 2 => metric.brPOS := metric.brPOS + 1,      metric.move := 3
                 3 => metric.blPOS := metric.blPOS + 1,      metric.move := 1
    if (b.t - b.t[1]) == (b.t[1] - b.t[2])
        metric.xlocbl := metric.loc + (b.t - b.t[1]) *       metric.blPOS
        metric.xlocbr := metric.loc + (b.t - b.t[1]) *       metric.brPOS
fnOB() =>
    var ob[] blob = array.new<ob>()
    var ob[] brob = array.new<ob>()
    var int dir = 0
    up = ta.highest(len)
    dn = ta.lowest(len)
    pv = ta.pivothigh(b.v, len, len)
    dir := b.h[len] > up ? -1 : b.l[len] < dn ? 1 : dir[1]
    atr = ta.atr(len)
    btmP = obmode == "Length" ? (b.h[len] - 1 * atr[len]) < b.l[len] ? b.l[len] :
(b.h[len] - 1 * atr[len]) : b.l[len]
    topP = obmode == "Length" ? (b.l[len] + 1 * atr[len]) > b.h[len] ? b.h[len] :
(b.l[len] + 1 * atr[len]) : b.h[len]
    if pv and dir == 1
        blob.unshift(ob.new(topP, b.l[len], math.avg(topP, b.l[len]), b.t[len],
obupcs, b.v[len], b.c[len] > b.o[len] ? 1 : -1, 1, 0, 0, b.t[len]))
        blal.created := true
        blIS.reset    := false
    if pv and dir == -1
        brob.unshift(ob.new(b.h[len], btmP, math.avg(btmP, b.h[len]), b.t[len],
obdncs, b.v[len], b.c[len] > b.o[len] ? 1 : -1, 1, 0, 0, b.t[len]))
        bral.created := true
        brIS.reset    := false
    if blob.size() > 0 and barstate.isconfirmed
        for [i, ob] in blob
            for j = 0 to len - 1
                if obmiti == "Close" ? math.min(b.c[j], b.o[j]) < ob.btm : obmiti
== "Wick" ? b.l < ob.btm : obmiti == "Avg" ? b.l < ob.avg : na
                    blob.remove(i)
                    blal.mitigated := true
                    break
    if brob.size() > 0 and barstate.isconfirmed
        for[i, ob] in brob
            for j = 0 to len - 1
                if obmiti == "Close" ? math.max(b.c[j], b.o[j]) > ob.top : obmiti
== "Wick" ? b.h > ob.top : obmiti == "Avg" ? b.h > ob.avg : na
                    brob.remove(i)
                    bral.mitigated := true
                    break
    if blob.size() > 0
        for [i, metric] in blob
            metric.umt()
    if brob.size() > 0
        for [i, metric] in brob
            metric.umt()
    if overlap
        blob.overlap()
        brob.overlap()
    if barstate.isconfirmed
        if blob.size() > 0
            ob = blob.get(0)
            if low < ob.top and blIS.reset == false
                blal.inside := true
                blIS.reset := true
        if brob.size() > 0
            ob = brob.get(0)
            if high > ob.btm and brIS.reset == false
                bral.inside := true
                brIS.reset := true
    if barstate.islast
        for bx in box.all
            bx.delete()
        for ln in line.all
            ln.delete()
        if blob.size() > 0
            for i = 0 to math.min(oblast - 1, blob.size() - 1)
                blob.get(i).display(blob, i)
        if brob.size() > 0
            for i = 0 to math.min(oblast - 1, brob.size() - 1)
                brob.get(i).display(brob, i)
if obshow
    fnOB()
if blinside and blal.inside
    alert("Price Inside Bullish OB")
if blcreated and blal.created
    alert("Bullish OB Formed")
if blmitigated and blal.mitigated
    alert("Bullish OB Mitigated")
if brinside and bral.inside
    alert("Price Inside Bearish OB")
if brcreated and bral.created
    alert("Bearish OB Formed")
if brmitigated and bral.mitigated
    alert("Bearish OB Mitigated")