Draw order blocks on a chart for long enough and the same problem always appears: there are too many of them, and they all look equally important. A block left by a violent institutional push and a block left by a lazy afternoon candle get the same rectangle, the same colour, the same weight in your decision-making. They should not.
Power Order Blocks, by ChartPrime, addresses exactly that. It detects order blocks the strict way — through displacement, not through any candle that happens to precede a move — and then grades each one with a power score that answers a simple question: how big was the candle that created this block, compared with the biggest candle of the recent past? That score is painted directly into the block’s opacity. Strong blocks are vivid and demand attention. Weak blocks fade into the background where they belong.
On top of that grading, every block carries an equilibrium line, a live touch counter, and a retest arrow when price actually comes back and respects it.
The weak definition of an order block is “the last down candle before an up move”. It produces blocks everywhere. This indicator demands a genuine displacement candle — a bar that does not merely reverse, but overwhelms. Three conditions must hold at once for a bullish block:
close[1] < open[1]).prevBear = close[1] < open[1]
bullDisp = close > open AND close > high[1] AND (close - open) > (high[1] - low[1]) × dispThresh
bullOB = prevBear AND bullDisp
The third condition is what filters the noise. With dispThresh at its default 0.5, the engulfing body must exceed half the previous candle’s range; raise it towards 1.0 or beyond and only genuinely explosive moves qualify. The block itself is the full range of the bearish candle that got run over — high to low, the zone the buyers had to consume. The bearish case is the exact mirror.
Once a block is born, it gets measured:
power = ( block range / largest candle range of the last 100 bars ) × 100
This is a relative measurement, and that is the point. A 40-pip block means nothing in the abstract; a block whose range is 80% of the largest candle seen in the last hundred bars means the market was moving with force when it formed. The same instrument in a quiet session and in a news-driven session produces very different absolute numbers but comparable power scores.
That score then drives the opacity of the block. A block scoring near 100% is painted vividly; one scoring near 0% is barely visible. You are not reading a number — you are reading a chart where the important zones are simply the ones you can see.
Blocks are not permanent decorations. The engine manages them continuously:
What remains on the chart is, by construction, the set of unmitigated zones still in play.
The interesting moment is not when a block forms — it is when price returns to it. The indicator marks a retest when price enters the zone and leaves it from the side it came from:
Each retest increments that block’s touch counter, displayed on the block’s label alongside its power. An arrow is plotted, filtered so that two signals of the same side cannot fire within 10 bars of each other — clusters of arrows on a single choppy retest are collapsed into one.
The touch counter is worth watching on its own. A zone respected three or four times is a level the market is genuinely defending; a zone that has never been touched is untested.
dispThresh (default 0.5): displacement multiplier. Higher = fewer, more violent blocks.maxBlocks (default 10): simultaneous blocks kept per side.strLookback (default 100): window for the largest-candle reference used by the power score.extendRight (default 25): how far blocks project to the right of price.gapBars (default 10): minimum bars between two retest signals of the same side.maxArrows (default 50): how many historical retest arrows are kept.showEqLine / powerFade / showInfo / showRetests (default on): layer toggles.//----------------------------------------------------------------------//
// PRC_Power Order Blocks (by ChartPrime)
// version = 1
// 20.07.2026
// Ivan Gonzalez @ www.prorealcode.com
// Sharing ProRealTime knowledge
//----------------------------------------------------------------------//
DEFPARAM drawonlastbaronly = true
//-----Settings---------------------------------------------------------//
dispThresh = 0.5 // Displacement multiplier (tamano minimo del cuerpo)
maxBlocks = 10 // Bloques simultaneos por lado
strLookback = 100 // Ventana para la vela mayor (power)
extendRight = 25 // Barras de extension a la derecha
gapBars = 10 // Barras minimas entre dos senales de retest
maxArrows = 50 // Flechas de retest conservadas en el historico
showEqLine = 1 // Linea de equilibrio (1/0)
powerFade = 1 // Opacidad proporcional al power (1/0)
showInfo = 1 // Etiqueta power + toques (1/0)
showRetests = 1 // Flechas de retest (1/0)
//-----Colours----------------------------------------------------------//
bullR = 27
bullG = 211
bullB = 122
bearR = 188
bearG = 20
bearB = 230
//-----Series nativas (se evaluan en la barra cerrada mas abajo)--------//
barRange = high - low
maxCandleRaw = highest[strLookback](barRange)
atrRaw = averagetruerange[14](close)
//-----Estado persistente en ARRAYS-------------------------------------//
IF barindex = 0 THEN
$gState[0] = 0 - 1
$gState[1] = 0
$gState[2] = 0
$gState[3] = 0
$gState[4] = 0 - gapBars
$gState[5] = 0 - gapBars
ENDIF
//-----GUARD: una sola pasada por barra----------------------------------//
IF barindex > $gState[0] THEN
$gState[0] = barindex
// Barra que se procesa = la ultima CERRADA (p). Su anterior = q.
pIdx = barindex - 1
pO = open[1]
pH = high[1]
pL = low[1]
pC = close[1]
qO = open[2]
qH = high[2]
qL = low[2]
qC = close[2]
maxCandleSize = maxCandleRaw[1]
atrOff = atrRaw[1] * 0.2
// Cargar estado desde los arrays a escalares de trabajo
nBull = $gState[1]
nBear = $gState[2]
nArrow = $gState[3]
lastBullBar = $gState[4]
lastBearBar = $gState[5]
//-----Detection-----------------------------------------------------//
prevRange = qH - qL
prevBear = qC < qO
bullDisp = pC > pO AND pC > qH AND (pC - pO) > prevRange * dispThresh
bullOB = prevBear AND bullDisp
prevBull = qC > qO
bearDisp = pC < pO AND pC < qL AND (pO - pC) > prevRange * dispThresh
bearOB = prevBull AND bearDisp
//-----(1) Bullish blocks: mitigation + retest------------------------//
bullRetestRaw = 0
IF nBull > 0 THEN
FOR i = 1 TO nBull DO
IF pC < $obBullBtm[i] THEN
$obBullAlive[i] = 0
ELSE
IF qL <= $obBullTop[i] AND pL >= $obBullTop[i] AND pIdx > $obBullLeft[i] THEN
bullRetestRaw = 1
$obBullTouch[i] = $obBullTouch[i] + 1
ENDIF
ENDIF
NEXT
ENDIF
//-----(1b) Bearish blocks: mitigation + retest-----------------------//
bearRetestRaw = 0
IF nBear > 0 THEN
FOR i = 1 TO nBear DO
IF pC > $obBearTop[i] THEN
$obBearAlive[i] = 0
ELSE
IF qH >= $obBearBtm[i] AND pH <= $obBearBtm[i] AND pIdx > $obBearLeft[i] THEN
bearRetestRaw = 1
$obBearTouch[i] = $obBearTouch[i] + 1
ENDIF
ENDIF
NEXT
ENDIF
//-----(2) New block geometry + overlap kill--------------------------//
newTop = qH
newBtm = qL
newLeft = pIdx - 1
IF maxCandleSize > 0 THEN
newPct = (newTop - newBtm) / maxCandleSize * 100
ELSE
newPct = 0
ENDIF
makeBull = 0
IF bullOB AND pIdx > strLookback THEN
makeBull = 1
IF nBull > 0 THEN
FOR i = 1 TO nBull DO
IF $obBullAlive[i] = 1 AND newTop >= $obBullBtm[i] AND newBtm <= $obBullTop[i] THEN
$obBullAlive[i] = 0
ENDIF
NEXT
ENDIF
ENDIF
makeBear = 0
IF bearOB AND pIdx > strLookback THEN
makeBear = 1
IF nBear > 0 THEN
FOR i = 1 TO nBear DO
IF $obBearAlive[i] = 1 AND newTop >= $obBearBtm[i] AND newBtm <= $obBearTop[i] THEN
$obBearAlive[i] = 0
ENDIF
NEXT
ENDIF
ENDIF
//-----(3) Compact arrays (drop mitigated / overlapped)---------------//
wB = 0
IF nBull > 0 THEN
FOR i = 1 TO nBull DO
IF $obBullAlive[i] = 1 THEN
wB = wB + 1
$obBullTop[wB] = $obBullTop[i]
$obBullBtm[wB] = $obBullBtm[i]
$obBullLeft[wB] = $obBullLeft[i]
$obBullPct[wB] = $obBullPct[i]
$obBullTouch[wB] = $obBullTouch[i]
$obBullAlive[wB] = 1
ENDIF
NEXT
ENDIF
nBull = wB
wS = 0
IF nBear > 0 THEN
FOR i = 1 TO nBear DO
IF $obBearAlive[i] = 1 THEN
wS = wS + 1
$obBearTop[wS] = $obBearTop[i]
$obBearBtm[wS] = $obBearBtm[i]
$obBearLeft[wS] = $obBearLeft[i]
$obBearPct[wS] = $obBearPct[i]
$obBearTouch[wS] = $obBearTouch[i]
$obBearAlive[wS] = 1
ENDIF
NEXT
ENDIF
nBear = wS
//-----(4) FIFO cap + push new block----------------------------------//
IF makeBull = 1 THEN
WHILE nBull >= maxBlocks DO
FOR i = 1 TO nBull - 1 DO
$obBullTop[i] = $obBullTop[i+1]
$obBullBtm[i] = $obBullBtm[i+1]
$obBullLeft[i] = $obBullLeft[i+1]
$obBullPct[i] = $obBullPct[i+1]
$obBullTouch[i] = $obBullTouch[i+1]
NEXT
nBull = nBull - 1
WEND
nBull = nBull + 1
$obBullTop[nBull] = newTop
$obBullBtm[nBull] = newBtm
$obBullLeft[nBull] = newLeft
$obBullPct[nBull] = newPct
$obBullTouch[nBull] = 0
$obBullAlive[nBull] = 1
ENDIF
IF makeBear = 1 THEN
WHILE nBear >= maxBlocks DO
FOR i = 1 TO nBear - 1 DO
$obBearTop[i] = $obBearTop[i+1]
$obBearBtm[i] = $obBearBtm[i+1]
$obBearLeft[i] = $obBearLeft[i+1]
$obBearPct[i] = $obBearPct[i+1]
$obBearTouch[i] = $obBearTouch[i+1]
NEXT
nBear = nBear - 1
WEND
nBear = nBear + 1
$obBearTop[nBear] = newTop
$obBearBtm[nBear] = newBtm
$obBearLeft[nBear] = newLeft
$obBearPct[nBear] = newPct
$obBearTouch[nBear] = 0
$obBearAlive[nBear] = 1
ENDIF
//-----(5) Retest signals (gap filter) + arrow ring-------------------//
bullRetest = 0
IF bullRetestRaw = 1 AND pIdx - lastBullBar >= gapBars THEN
bullRetest = 1
lastBullBar = pIdx
ENDIF
bearRetest = 0
IF bearRetestRaw = 1 AND pIdx - lastBearBar >= gapBars THEN
bearRetest = 1
lastBearBar = pIdx
ENDIF
IF showRetests = 1 AND bullRetest = 1 THEN
WHILE nArrow >= maxArrows DO
FOR i = 1 TO nArrow - 1 DO
$rtX[i] = $rtX[i+1]
$rtY[i] = $rtY[i+1]
$rtBull[i] = $rtBull[i+1]
NEXT
nArrow = nArrow - 1
WEND
nArrow = nArrow + 1
$rtX[nArrow] = pIdx
$rtY[nArrow] = pL - atrOff
$rtBull[nArrow] = 1
ENDIF
IF showRetests = 1 AND bearRetest = 1 THEN
WHILE nArrow >= maxArrows DO
FOR i = 1 TO nArrow - 1 DO
$rtX[i] = $rtX[i+1]
$rtY[i] = $rtY[i+1]
$rtBull[i] = $rtBull[i+1]
NEXT
nArrow = nArrow - 1
WEND
nArrow = nArrow + 1
$rtX[nArrow] = pIdx
$rtY[nArrow] = pH + atrOff
$rtBull[nArrow] = 0
ENDIF
// Volcar el estado de vuelta a los arrays
$gState[1] = nBull
$gState[2] = nBear
$gState[3] = nArrow
$gState[4] = lastBullBar
$gState[5] = lastBearBar
ENDIF
//-----(6) Draw on last bar (cada tick, solo lee estado)-----------------//
IF islastbarupdate THEN
dBull = $gState[1]
dBear = $gState[2]
dArrow = $gState[3]
rightX = barindex + extendRight
infoX = barindex + round(extendRight / 2)
IF dBull > 0 THEN
FOR i = 1 TO dBull DO
bullTop = $obBullTop[i]
bullBtm = $obBullBtm[i]
bullMid = (bullTop + bullBtm) / 2
IF powerFade = 1 THEN
fillBull = round(20.4 + 0.816 * $obBullPct[i])
ELSE
fillBull = 51
ENDIF
DRAWRECTANGLE($obBullLeft[i], bullTop, rightX, bullBtm) COLOURED(bullR, bullG, bullB, 0) FILLCOLOR(bullR, bullG, bullB, fillBull)
IF showEqLine = 1 THEN
DRAWSEGMENT($obBullLeft[i], bullMid, barindex, bullMid) COLOURED(bullR, bullG, bullB, 153)
ENDIF
IF showInfo = 1 THEN
pwv = round($obBullPct[i])
tcv = $obBullTouch[i]
DRAWTEXT("Bullish OB | Power: #pwv#% | Touches: #tcv#", infoX, bullMid) COLOURED(0, 0, 0, 255)
ENDIF
NEXT
ENDIF
IF dBear > 0 THEN
FOR i = 1 TO dBear DO
bearTop = $obBearTop[i]
bearBtm = $obBearBtm[i]
bearMid = (bearTop + bearBtm) / 2
IF powerFade = 1 THEN
fillBear = round(20.4 + 0.816 * $obBearPct[i])
ELSE
fillBear = 51
ENDIF
DRAWRECTANGLE($obBearLeft[i], bearTop, rightX, bearBtm) COLOURED(bearR, bearG, bearB, 0) FILLCOLOR(bearR, bearG, bearB, fillBear)
IF showEqLine = 1 THEN
DRAWSEGMENT($obBearLeft[i], bearMid, barindex, bearMid) COLOURED(bearR, bearG, bearB, 153)
ENDIF
IF showInfo = 1 THEN
pwx = round($obBearPct[i])
tcx = $obBearTouch[i]
DRAWTEXT("Bearish OB | Power: #pwx#% | Touches: #tcx#", infoX, bearMid) COLOURED(0, 0, 0, 255)
ENDIF
NEXT
ENDIF
IF showRetests = 1 AND dArrow > 0 THEN
FOR i = 1 TO dArrow DO
IF $rtBull[i] = 1 THEN
DRAWTEXT("▲", $rtX[i], $rtY[i]) COLOURED(bullR, bullG, bullB, 255)
ELSE
DRAWTEXT("▼", $rtX[i], $rtY[i]) COLOURED(bearR, bearG, bearB, 255)
ENDIF
NEXT
ENDIF
ENDIF
RETURN