Overview
Retail order flow tends to be reactive and dispersed across price. Institutional order flow is not — large participants must fill size before a market can move directionally, and that fill activity leaves a structural signature: a period of accumulation or distribution, followed by a decisive break of structure.
Automated Institutional Order Blocks is built to isolate that exact signature. The script identifies structure breaks in real time, filters them against an imbalance threshold to exclude low-conviction moves, then maps the origin candle — the last opposing candle before the displacement — as a live institutional zone directly on your chart.
Features of This Indicator
-
Real-Time Structural Verification Order blocks are only plotted after a confirmed break of market structure, filtering out premature or invalidated shifts before they reach your chart.
-
Imbalance-Filtered Detection Every block is cross-checked against a 20-period average range multiplier, so only candles with genuine institutional displacement qualify.
-
Automatic Mitigation Tracking Blocks are removed the moment price closes back through them, keeping the chart limited to zones that remain structurally unmitigated.
-
Multi-Timeframe Compatibility Runs cleanly from the 1-minute to the 4-hour chart with no repainting logic, so intraday and swing traders work off the same engine.
How to Use
- Scroll down to the source code section and copy the entire script.
- Open your TradingView chart and click the Pine Editor tab at the bottom.
- Delete any existing code, paste the Crypto Indicator Pro script, and click Add to Chart.
- Watch for price to retrace back into a highlighted zone before reading it as a potential reaction level.
The Pine Script V5 Code
//@version=5
indicator("Automated Order Blocks — Crypto Indicator Pro", shorttitle="Auto OB [CIP]", overlay=true, max_boxes_count=500)
// ============================================================================
// AUTOMATED INSTITUTIONAL ORDER BLOCKS
// Identifies institutional accumulation/distribution zones using:
// 1. Market structure breaks (Break of Structure / BOS)
// 2. Imbalance (impulse) candle confirmation
// 3. Origin candle mapping (the last opposing candle before the impulse)
// No repainting logic is used — all detections confirm on bar close.
// ============================================================================
// ---------------------------- INPUTS ----------------------------
swingLength = input.int(10, "Swing Detection Length", minval=2, group="Market Structure",
tooltip="Bars used on each side to confirm a valid swing high/low.")
imbalanceMult = input.float(1.5, "Imbalance Multiplier", minval=1.0, step=0.1, group="Imbalance Filter",
tooltip="Candle range must exceed the 20-bar average range by this factor to qualify as an impulse candle.")
lookback = input.int(20, "Origin Candle Search Range", minval=5, maxval=50, group="Order Block",
tooltip="Max bars to scan backward for the origin (opposing) candle.")
maxBoxAge = input.int(200, "Auto-Delete Unmitigated Blocks After (bars)", minval=10, group="Order Block")
extendBoxes = input.bool(true, "Extend Active Blocks to Current Bar", group="Display")
bullBg = input.color(color.new(color.teal, 80), "Bullish OB Color", group="Display")
bearBg = input.color(color.new(color.red, 80), "Bearish OB Color", group="Display")
// ---------------------------- SWING STRUCTURE ----------------------------
swingHigh = ta.pivothigh(high, swingLength, swingLength)
swingLow = ta.pivotlow(low, swingLength, swingLength)
var float lastSwingHigh = na
var float lastSwingLow = na
if not na(swingHigh)
lastSwingHigh := swingHigh
if not na(swingLow)
lastSwingLow := swingLow
// ---------------------------- IMBALANCE FILTER ----------------------------
avgRange = ta.sma(high - low, 20)
candleRange = high - low
isImpulseCandle = candleRange > (avgRange * imbalanceMult)
isBullishCandle = close > open
isBearishCandle = close < open
// ---------------------------- STRUCTURE BREAKS (BOS) ----------------------------
bullishBOS = not na(lastSwingHigh) and close > lastSwingHigh and close[1] <= lastSwingHigh
bearishBOS = not na(lastSwingLow) and close < lastSwingLow and close[1] >= lastSwingLow
// ---------------------------- STORAGE ----------------------------
var array<box> bullBoxes = array.new<box>()
var array<box> bearBoxes = array.new<box>()
var array<int> bullBoxBar = array.new<int>() // creation bar index, for age-based cleanup
var array<int> bearBoxBar = array.new<int>()
// ---------------------------- ORDER BLOCK CREATION ----------------------------
// Bullish OB: last bearish candle preceding a bullish BOS confirmed by an impulse candle
if bullishBOS and isImpulseCandle and isBullishCandle
int offset = na
for i = 1 to lookback
if close[i] < open[i]
offset := i
break
if not na(offset)
obBarIndex = bar_index - offset
obHigh = high[offset]
obLow = low[offset]
newBox = box.new(left=obBarIndex, top=obHigh, right=bar_index, bottom=obLow,
border_color=color.teal, bgcolor=bullBg,
extend=extendBoxes ? extend.right : extend.none)
array.push(bullBoxes, newBox)
array.push(bullBoxBar, obBarIndex)
// Bearish OB: last bullish candle preceding a bearish BOS confirmed by an impulse candle
if bearishBOS and isImpulseCandle and isBearishCandle
int offset = na
for i = 1 to lookback
if close[i] > open[i]
offset := i
break
if not na(offset)
obBarIndex = bar_index - offset
obHigh = high[offset]
obLow = low[offset]
newBox = box.new(left=obBarIndex, top=obHigh, right=bar_index, bottom=obLow,
border_color=color.red, bgcolor=bearBg,
extend=extendBoxes ? extend.right : extend.none)
array.push(bearBoxes, newBox)
array.push(bearBoxBar, obBarIndex)
// ---------------------------- MITIGATION (price closes back through the block) ----------------------------
if array.size(bullBoxes) > 0
for i = array.size(bullBoxes) - 1 to 0
b = array.get(bullBoxes, i)
if close < box.get_bottom(b)
box.delete(b)
array.remove(bullBoxes, i)
array.remove(bullBoxBar, i)
if array.size(bearBoxes) > 0
for i = array.size(bearBoxes) - 1 to 0
b = array.get(bearBoxes, i)
if close > box.get_top(b)
box.delete(b)
array.remove(bearBoxes, i)
array.remove(bearBoxBar, i)
// ---------------------------- AGE-BASED CLEANUP ----------------------------
if array.size(bullBoxes) > 0
for i = array.size(bullBoxes) - 1 to 0
if bar_index - array.get(bullBoxBar, i) > maxBoxAge
box.delete(array.get(bullBoxes, i))
array.remove(bullBoxes, i)
array.remove(bullBoxBar, i)
if array.size(bearBoxes) > 0
for i = array.size(bearBoxes) - 1 to 0
if bar_index - array.get(bearBoxBar, i) > maxBoxAge
box.delete(array.get(bearBoxes, i))
array.remove(bearBoxes, i)
array.remove(bearBoxBar, i)
// ---------------------------- ALERTS ----------------------------
alertcondition(bullishBOS and isImpulseCandle and isBullishCandle,
title="Bullish Order Block Formed", message="Bullish institutional order block detected.")
alertcondition(bearishBOS and isImpulseCandle and isBearishCandle,
title="Bearish Order Block Formed", message="Bearish institutional order block detected.")