Order block matrix
//@version=5
indicator("Order Block Matrix [Alpha Extract]", overlay=true, max_bars_back=5000,
max_boxes_count=500, max_lines_count=500)
formatVolume(vol) =>
vol >= 1000000 ? str.tostring(math.round(vol / 1000000, 2)) + "M" :
vol >= 1000 ? str.tostring(math.round(vol / 1000, 2)) + "K" :
str.tostring(math.round(vol, 2))
// === ORDER BLOCKS INPUTS ===
obLookback = input.int(10, "Order Block Breadth", minval=1, group="Order Blocks")
obmode = input×string("Full", "Method", options=["Breadth", "Full"],
tooltip="[Breadth] Use Breadth to adjust coordinate of the orderblocks\n[Full] Use whole candle
body", group="Order Blocks")
obmiti = input×string("Close", "Risk Method", options=["Close", "Wick"],
tooltip="Risk method for when to trigger order blocks", group="Order Blocks")
obMaxBlocks = input×int(6, "Maximum Order Blocks", minval=1, maxval=500, group="Order
Blocks")
windowsis = true
mswindow = 1000
amountOfBoxes = input×int(7, "Number of Grid Segments", minval=3, maxval=20, group="VISUAL
SETTINGS")
showBorder = input.bool(true, "Show Block Borders", group="VISUAL SETTINGS")
borderWidth = input×int(1, "Border Width", minval=0, maxval=4, group="VISUAL SETTINGS")
showVolume = input.bool(true, "Show Volume Text", group="VISUAL SETTINGS")
volumePosition = input.float(0.80, "Volume Text Position (0-1)", minval=0.1, maxval=5.1, step=0.05,
group="VISUAL SETTINGS")
obHighVolumeColor = color.new(#089981, 10)
obLowVolumeColor = color.new(#089981, 85)
obBearHighVolumeColor = color.new(#f23645, 10)
obBearLowVolumeColor = color.new(#f23645, 85)
obBullBorderColor = color.new(#089981, 20)
obBearBorderColor = color.new(#ffa8af, 20)
obBullFillColor = color.new(#089981, 65)
obBearFillColor = color.new(#f23645, 65)
volumeTextColor = color.white
// === SCREENER TABLE INPUTS ===
showScreener = input.bool(true, "Show Screener Table", group="Screener")
tablePosition = input.string("Top Right", "Table Position",
options=["Top Left", "Top Right", "Bottom Left", "Bottom Right"], group="Screener")
tableSize = input.string("Normal", "Table Size",
options=["Small", "Normal", "Large"], group="Screener")
// Table position mapping
var position =
tablePosition == "Top Left" ? position.top_left :
tablePosition == "Top Right" ? position.top_right :
tablePosition == "Bottom Left" ? position.bottom_left :
position.bottom_right
// Table size mapping
var tSize =
tableSize == "Small" ? size.small :
tableSize == "Large" ? size.large :
size.normal
// === ENHANCED ORDER BLOCK TYPE ===
type orderBlock
array<box> boxArray
array<float> boxVolumes
float topValue
float botValue
int leftTime
int rightTime
int rightTime
string direction
float totalVolume
label volumeLabel
line volumeLine
bool mitigated
line topLine
line botLine
linefill bgFill
var float atr = ta.atr(14)
atr := ta.atr(14)
var array<orderBlock> bullBlocks = array.new<orderBlock>()
var array<orderBlock> bearBlocks = array.new<orderBlock>()
var float last_bull_volume = 0.0
var float last_bear_volume = 0.0
var string dom_block = "None"
var float block_strength = 0.0
var float price_distance = 0.0
var int touch_count = 0
var float block_size = 0.0
var float size_ratio = 1.0
var bool bull_touch = false
var bool bear_touch = false
var bool bull_reject = false
var bool bear_reject = false
var bool new_bull = false
var bool new_bear = false
bull_touch := false
bear_touch := false
bull_reject := false
bear_reject := false
bear_reject := false
new_bull := false
new_bear := false
generateBorderLines(orderBlock block, topValue, botValue) =>
if showBorder
borderColor = block.direction == "Bull" ? obBullBorderColor : obBearBorderColor
fillColor = block.direction == "Bull" ? obBullFillColor : obBearFillColor
newTopLine = line×new(
x1 = block.leftTime,
y1 = topValue,
x2 = time,
y2 = topValue,
xloc = xloc.bar_time,
extend = extend.none,
color = borderColor,
style = line.style_solid,
width = borderWidth)
newbotLine = line.new(
x1 = block.leftTime,
y1 = botValue,
x2 = time,
y2 = botValue,
xloc = xloc.bar_time,
extend = extend.none,
color = borderColor,
style = line.style_solid,
width = borderWidth)
newlinefill = linefill.new(newTopLine, newbotLine, fillColor)
[newTopLine, newbotLine, newlinefill]
else
[na, na, na]
// Generate volume boxes for an order block
generateVolumeBoxes(orderBlock block, float orderVolume) =>
volumeArray = array.new_float(amountOfBoxes, 0)
array.set(volumeArray, 0, orderVolume)
boxesArray = array.new_box()
startValue = block.topValue
increment = (block×topValue - block×botValue) / amountOfBoxes
highestVolume = orderVolume
timeLength = time - block.leftTime
timeRatio = highestVolume > 0 ? timeLength / highestVolume * 0.9 : 0
for i = 0 to amountOfBoxes - 1
topOfGrid = startValue - (increment × i)
botOfGrid = startValue - (increment × (i + 1))
multiplier = 1.0
if i == 0 or i == amountOfBoxes - 1
multiplier := 0.4
else if i == 1 or i == amountOfBoxes - 2
multiplier := 0.6
else if i == 2 or i == amountOfBoxes - 3
multiplier := 0.8
else
multiplier := 1.0
segmentVolume = orderVolume × multiplier / amountOfBoxes × 2
array.set(volumeArray, i, segmentVolume)
color highVol = block.direction == "Bull" ? obHighVolumeColor : obBearHighVolumeColor
color lowVol = block.direction == "Bull" ? obLowVolumeColor : obBearLowVolumeColor
color boxColor = color.from_gradient(multiplier, 0.2, 1.0, lowVol, highVol)
maxWidth = math×min(timeLength × 1.2, segmentVolume * timeRatio)
newBox = box.new(
left = block.leftTime,
top = topOfGrid,
right = block×leftTime + math×round(maxWidth),
bottom = botOfGrid,
border_color = boxColor,
border_width = 1,
xloc = xloc.bar_time,
bgcolor = boxColor,
extend = extend×none)
array.push(boxesArray, newBox)
[boxesArray, volumeArray, array.sum(volumeArray)]
updateBoxes(orderBlock block, currentTime) =>
if array.size(block.boxArray) > 0
highestVolume = array×max(block×boxVolumes)
timeLength = currentTime - block.leftTime
timeRatio = highestVolume > 0 ? timeLength / highestVolume * 0.9 : 0
for i = 0 to math.min(amountOfBoxes - 1, array.size(block.boxArray) - 1)
if i < array.size(block.boxVolumes)
boxVolume = array.get(block.boxVolumes, i)
if not na(boxVolume)
maxWidth = math.min(timeLength * 1.2, boxVolume * timeRatio)
box.set_right(array.get(block.boxArray, i), block.leftTime + math.round(maxWidth))
if not na(block.topLine) and not na(block.botLine)
line.set_x2(block.topLine, currentTime)
line.set_x2(block.botLine, currentTime)
if not na(block.volumeLine) and not na(block.volumeLabel) and showVolume
blockCenter = (block.topValue + block.botValue) / 2
lineStartX = block×leftTime + math×round((currentTime - block×leftTime) × 0.95)
lineEndX = lineStartX + math×round((currentTime - block×leftTime) × 0.2)
lineEndY = block.direction == "Bull" ?
blockCenter + (block.topValue - block.botValue) * 0.3 :
blockCenter - (block.topValue - block.botValue) * 0.3
line.set_x1(block.volumeLine, lineStartX)
line.set_y1(block.volumeLine, blockCenter)
line.set_x2(block.volumeLine, lineEndX)
line.set_y2(block.volumeLine, lineEndY)
label.set_x(block.volumeLabel, lineEndX)
label.set_y(block.volumeLabel, lineEndY)
wipeBlock(orderBlock block) =>
if not na(block.topLine)
line.delete(block.topLine)
if not na(block.botLine)
line.delete(block.botLine)
if not na(block.bgFill)
linefill.delete(block.bgFill)
if not na(block.volumeLabel)
label.delete(block.volumeLabel)
if not na(block.volumeLine)
line.delete(block.volumeLine)
if array.size(block.boxArray) > 0
for i = array×size(block×boxArray) - 1 to 0
selectedBox = array.get(block.boxArray, i)
box.delete(selectedBox)
checkObCondition(tuning)=>
bear = false
for i = tuning - 1 to 0
start = tuning - 1
if i == start
if close[i] <= open[i]
break
else
if close[i] > open[i]
break
if i == 0
bear := true
bull = false
for i = tuning - 1 to 0
start = tuning - 1
if i == start
if close[i] >= open[i]
break
else
if close[i] < open[i]
break
if i == 0
bull := true
bullAlt = (high[tuning] - low[tuning]) > atr[tuning] * 0.8 and close[tuning] > open[tuning] and
low[tuning] < low[tuning+1]
bearAlt = (high[tuning] - low[tuning]) > atr[tuning] * 0.8 and close[tuning] < open[tuning] and
high[tuning] > high[tuning+1]
[bear or bearAlt, bull or bullAlt]
inWindowRange = windowsis ? bar_index > (last_bar_index - mswindow) : true
processBar = inWindowRange or not windowsis
if barstate.isfirst
if bullBlocks.size() > 0
for i = bullBlocks×size() - 1 to 0
block = bullBlocks×get(i)
wipeBlock(block)
if bearBlocks.size() > 0
for i = bearBlocks×size() - 1 to 0
block = bearBlocks×get(i)
wipeBlock(block)
bullBlocks.clear()
bearBlocks.clear()
if processBar
[is_bearish, is_bullish] = checkObCondition(obLookback)
if barstate.isconfirmed
if is_bullish
new_bull := true
topValue = high[obLookback - 1]
botValue = obmode == "Breadth" ?
(low[obLookback - 1] + atr * 1) > high[obLookback - 1] ?
high[obLookback - 1] : (low[obLookback - 1] + atr * 1) :
low[obLookback - 1]
leftTime = time[obLookback - 1]
createNewBlock = true
if bullBlocks.size() > 0
for i = bullBlocks×size() - 1 to 0
block = bullBlocks×get(i)
if math.max(block.botValue, botValue) <= math.min(block.topValue, topValue)
wipeBlock(block)
bullBlocks.remove(i)
createNewBlock := true
if createNewBlock
orderVolume = volume[obLookback - 1]
newBlock = orderBlock.new(
array.new_box(),
array.new_float(amountOfBoxes, 0),
topValue,
botValue,
leftTime,
time,
"Bull",
orderVolume,
na,
na,
false,
na,
na,
na)
[topLine, botLine, bgFill] = generateBorderLines(newBlock, topValue, botValue)
newBlock.topLine := topLine
newBlock.botLine := botLine
newBlock.bgFill := bgFill
[boxArray, volumeArray, totalVolume] = generateVolumeBoxes(newBlock, orderVolume)
newBlock.boxArray := boxArray
newBlock.boxVolumes := volumeArray
newBlock.totalVolume := totalVolume
if showVolume
blockCenter = (topValue + botValue) / 2
lineStartX = leftTime + math×round((time - leftTime) × 0.95)
lineEndX = lineStartX + math×round((time - leftTime) × 0.2)
lineEndY = blockCenter + (topValue - botValue) × 0.3
newBlock.volumeLine := line.new(
x1 = lineStartX,
y1 = blockCenter,
x2 = lineEndX,
y2 = lineEndY,
xloc = xloc.bar_time,
extend = extend×none,
color = color×new(volumeTextColor, 20),
style = line.style_solid,
width = 1)
formattedVolume = formatVolume(orderVolume) + " " + syminfo×basecurrency
newBlock.volumeLabel := label.new(
x = lineEndX,
y = lineEndY,
text = formattedVolume,
textcolor = volumeTextColor,
style = label.style_none,
xloc = xloc.bar_time,
size = size×normal)
array.push(bullBlocks, newBlock)
last_bull_volume := orderVolume
if is_bearish
new_bear := true
topValue = obmode == "Breadth" ?
(high[obLookback - 1] - atr * 1) < low[obLookback - 1] ?
low[obLookback - 1] : (high[obLookback - 1] - atr * 1) :
high[obLookback - 1]
botValue = low[obLookback - 1]
leftTime = time[obLookback - 1]
createNewBlock = true
if bearBlocks.size() > 0
for i = bearBlocks×size() - 1 to 0
block = bearBlocks×get(i)
if math.max(block.botValue, botValue) <= math.min(block.topValue, topValue)
wipeBlock(block)
bearBlocks.remove(i)
createNewBlock := true
if createNewBlock
orderVolume = volume[obLookback - 1]
newBlock = orderBlock.new(
array.new_box(),
array.new_float(amountOfBoxes, 0),
topValue,
botValue,
leftTime,
time,
"Bear",
orderVolume,
na,
na,
false,
na,
na,
na)
[topLine, botLine, bgFill] = generateBorderLines(newBlock, topValue, botValue)
newBlock.topLine := topLine
newBlock.botLine := botLine
newBlock.bgFill := bgFill
[boxArray, volumeArray, totalVolume] = generateVolumeBoxes(newBlock, orderVolume)
newBlock.boxArray := boxArray
newBlock.boxVolumes := volumeArray
newBlock.totalVolume := totalVolume
if showVolume
blockCenter = (topValue + botValue) / 2
lineStartX = leftTime + math×round((time - leftTime) × 0.95)
lineEndX = lineStartX + math×round((time - leftTime) × 0.2)
lineEndY = blockCenter - (topValue - botValue) × 0.3
newBlock.volumeLine := line.new(
x1 = lineStartX,
y1 = blockCenter,
x2 = lineEndX,
y2 = lineEndY,
xloc = xloc.bar_time,
extend = extend×none,
color = color×new(volumeTextColor, 20),
style = line.style_solid,
width = 1)
formattedVolume = formatVolume(orderVolume) + " " + syminfo×basecurrency
newBlock.volumeLabel := label.new(
x = lineEndX,
y = lineEndY,
text = formattedVolume,
textcolor = volumeTextColor,
style = label.style_none,
xloc = xloc.bar_time,
size = size×normal)
array.push(bearBlocks, newBlock)
last_bear_volume := orderVolume
if bullBlocks.size() > 0
for i = bullBlocks×size() - 1 to 0
if i < bullBlocks.size()
block = bullBlocks×get(i)
if not block.mitigated
mitigated = obmiti == "Close" ?
math.min(close, open) < block.botValue :
obmiti == "Wick" ?
low < block.botValue :
obmiti == "Avg" ?
low < (block.topValue + block.botValue) / 2 :
false
if mitigated
block.mitigated := true
wipeBlock(block)
bullBlocks.remove(i)
continue
if close <= block.topValue and close >= block.botValue
bull_touch := true
if low <= block.botValue and close >= block.botValue
bull_reject := true
updateBoxes(block, time)
if bearBlocks.size() > 0
for i = bearBlocks×size() - 1 to 0
if i < bearBlocks.size()
block = bearBlocks×get(i)
if not block.mitigated
mitigated = obmiti == "Close" ?
math.max(close, open) > block.topValue :
obmiti == "Wick" ?
high > block.topValue :
obmiti == "Avg" ?
high > (block.topValue + block.botValue) / 2 :
false
if mitigated
block.mitigated := true
wipeBlock(block)
bearBlocks.remove(i)
continue
if close <= block.topValue and close >= block.botValue
bear_touch := true
if high >= block.topValue and close <= block.topValue
bear_reject := true
updateBoxes(block, time)
if bullBlocks.size() > obMaxBlocks
bullBlocks_to_remove = bullBlocks×size() - obMaxBlocks
for _ = 0 to bullBlocks_to_remove - 1
if bullBlocks.size() <= 0
break
oldestIdx = 0
oldestTime = time_close + 999999999
for i = 0 to bullBlocks.size() - 1
block = bullBlocks×get(i)
if block.leftTime < oldestTime
oldestTime := block.leftTime
oldestIdx := i
if oldestIdx < bullBlocks.size()
oldBlock = bullBlocks×get(oldestIdx)
wipeBlock(oldBlock)
bullBlocks.remove(oldestIdx)
while bearBlocks.size() > obMaxBlocks
oldestIdx = 0
oldestTime = time
for i = 0 to bearBlocks.size() - 1
block = bearBlocks×get(i)
if block.leftTime < oldestTime
oldestTime := block.leftTime
oldestIdx := i
oldBlock = bearBlocks×get(oldestIdx)
wipeBlock(oldBlock)
bearBlocks.remove(oldestIdx)
if barstate.islast
float nearest_bull_distance = 1000000.0
float nearest_bear_distance = 1000000.0
int nearest_bull_idx = -1
int nearest_bear_idx = -1
if bullBlocks.size() > 0
for i = 0 to bullBlocks.size() - 1
block = bullBlocks×get(i)
if close > block.botValue
float dist = (close - block×botValue) / close × 100
if dist < nearest_bull_distance
nearest_bull_distance := dist
nearest_bull_idx := i
if bearBlocks.size() > 0
for i = 0 to bearBlocks.size() - 1
block = bearBlocks×get(i)
if close < block.topValue
float dist = (block×topValue - close) / close × 100
if dist < nearest_bear_distance
nearest_bear_distance := dist
nearest_bear_idx := i
if nearest_bull_distance < nearest_bear_distance and nearest_bull_idx >= 0
dom_block := "Bullish"
if nearest_bull_idx < bullBlocks.size()
bullBlock = bullBlocks.get(nearest_bull_idx)
block_strength := bullBlock.totalVolume / ta.sma(volume, 20)
price_distance := nearest_bull_distance
block_size := bullBlock.topValue - bullBlock.botValue
size_ratio := block_size / atr
else if nearest_bear_distance < nearest_bull_distance and nearest_bear_idx >= 0
dom_block := "Bearish"
if nearest_bear_idx < bearBlocks.size()
bearBlock = bearBlocks.get(nearest_bear_idx)
block_strength := bearBlock.totalVolume / ta.sma(volume, 20)
price_distance := nearest_bear_distance
block_size := bearBlock.topValue - bearBlock.botValue
size_ratio := block_size / atr
else
dom_block := "None"
// Create screener table
if showScreener
var table screener = table.new(position, 7, 3, color.new(#000000, 0),
border_color=color×white, border_width=1, frame_color=color×white, frame_width=1)
table.set_border_color(screener, color.white)
table.set_border_width(screener, 1)
table.clear(screener, 0, 0, 6, 2)
color headerBg = color×new(#000000, 0)
color bullishBg = color×new(#008060, 0)
table.merge_cells(screener, 0, 0, 6, 0)
table.cell(screener, 0, 0, "Order Blocks Screener" + (windowsis ? " (Window Mode)" : ""),
text_color=color×white, bgcolor=headerBg, text_size=tSize)
// Header row
table.cell(screener, 0, 1, "Symbol", text_color=color×white, bgcolor=headerBg, text_size=tSize)
table.cell(screener, 1, 1, "Timeframe", text_color=color×white, bgcolor=headerBg,
text_size=tSize)
table.cell(screener, 2, 1, "Order Block", text_color=color×white, bgcolor=headerBg,
text_size=tSize)
table.cell(screener, 3, 1, "Status", text_color=color×white, bgcolor=headerBg, text_size=tSize)
table.cell(screener, 4, 1, "Retests", text_color=color×white, bgcolor=headerBg, text_size=tSize)
table.cell(screener, 5, 1, "Bullish Volume", text_color=color×new(#02ffe6, 0),
bgcolor=headerBg, text_size=tSize)
table.cell(screener, 6, 1, "Bearish Volume", text_color=color×new(#ff4040, 0),
bgcolor=headerBg, text_size=tSize)
// Data row
table.cell(screener, 0, 2, syminfo.ticker, text_color=color×white, bgcolor=bullishBg,
text_size=tSize)
table.cell(screener, 1, 2, timeframe.period, text_color=color×white, bgcolor=bullishBg,
text_size=tSize)
string blockEmoji = dom_block == "Bullish" ? " " : dom_block == "Bearish" ? " ":" "
string blockText = dom_block + " " + blockEmoji
color blockTextColor = dom_block == "Bullish" ? color.rgb(255, 255, 255) : dom_block ==
"Bearish" ? color.red : color.gray
table.cell(screener, 2, 2, blockText, text_color=blockTextColor, bgcolor=bullishBg,
text_size=tSize)
string status = ""
if price_distance < 1.0
status := "Near"
else if price_distance < 3.0
status := "Medium"
else
status := "Far"
table.cell(screener, 3, 2, status, text_color=color.white, bgcolor=bullishBg, text_size=tSize)
// Retests count
string retestText = touch_count > 0 ? str.tostring(touch_count) : "None"
table.cell(screener, 4, 2, retestText, text_color=color.white, bgcolor=bullishBg, text_size=tSize)
// Volume information
table.cell(screener, 5, 2, formatVolume(last_bull_volume), text_color=color.white,
bgcolor=bullishBg, text_size=tSize)
table.cell(screener, 6, 2, formatVolume(last_bear_volume), text_color=color.white,
bgcolor=bullishBg, text_size=tSize)
// Alert conditions
alertcondition(condition = bull_touch, title = "Price Inside Bullish Max Volume Zone")
alertcondition(condition = bear_touch, title = "Price Inside Bearish Max Volume Zone")
alertcondition(condition = bull_reject, title = "Confirmed Rejection Off Bullish Max Volume Zone")
alertcondition(condition = bear_reject, title = "Confirmed Rejection Off Bearish Max Volume Zone")
alertcondition(condition = new_bull, title = "New Bullish Order Block")
alertcondition(condition = new_bear, title = "New Bearish Order Block")