Power Order Blocks

Category: Indicators By: Iván González Created: July 20, 2026, 5:16 PM
July 20, 2026, 5:16 PM
Indicators
0 Comments

Introduction

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.

Theory Behind the Indicator

1. Displacement, not just “the last opposite candle”

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:

  • The previous candle was bearish (close[1] < open[1]).
  • The current candle is bullish and closes above the previous candle’s high — not just above its close, above its entire range.
  • The current candle’s body is larger than the previous candle’s full range, scaled by the displacement multiplier.
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.

2. The power score — the distinctive part

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.

3. Life and death of a block

Blocks are not permanent decorations. The engine manages them continuously:

  • Mitigation. A bullish block dies when price closes below its floor; a bearish block dies when price closes above its ceiling. Once consumed, the zone is gone.
  • Overlap replacement. When a new block is born overlapping an existing block of the same side, the old one is removed rather than stacked. No clusters of near-duplicate rectangles.
  • A hard cap. Only the 10 most recent blocks per side survive; the oldest drops off when an eleventh appears.

What remains on the chart is, by construction, the set of unmitigated zones still in play.

4. Retests and the touch counter

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:

  • For a bullish block: the bar’s low was at or below the block’s ceiling, and the next low is back above it. Price dipped into the zone and was pushed out.
  • For a bearish block: the mirror — the high reached down into the zone and then withdrew below it.

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.

How to Read the Indicator

  1. Opacity is priority. A vivid block was born from a strong displacement; a faint one was born from a modest one. Trade the ones you can see clearly.
  2. The block is the whole opposite candle. Its ceiling (bullish) or floor (bearish) is the reference edge for the retest; the equilibrium line splits it in two for those who prefer a mid-zone entry.
  3. Touch count is validation. More touches means the zone has been repeatedly defended. It also means it has been repeatedly consumed — a level tested many times is closer to failing than a fresh one.
  4. The arrow is the confirmation, not the entry. It prints when price has already been rejected out of the zone, so the reaction is confirmed rather than anticipated.
  5. A closed break is final. These blocks require a close beyond the far edge to die, so wicks through the zone do not invalidate it.

Practical Applications

  1. Filtering an existing SMC workflow. If you already trade order blocks, the power score gives you a ranking criterion that is not discretionary: only take blocks above a chosen power threshold.
  2. Retest entries with a defined stop. The block provides both the entry area and the invalidation level — the far edge, beyond which a close kills the zone.
  3. Confluence with higher timeframe structure. A high-power block on the higher timeframe, retested on the lower one, is a far better location than either signal alone.
  4. Reading exhaustion of a level. Watch the touch counter climb. Zones tend to hold the first tests and break the later ones; a block on its fourth touch is a different proposition from one on its first.

Indicator Configuration

  • 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.

Code

//----------------------------------------------------------------------//
// 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

Download
Filename: PRC_Power-Order-Blocks.itf
Downloads: 10
Iván González Legend
As an architect of digital worlds, my own description remains a mystery. Think of me as an undeclared variable, existing somewhere in the code.
Author’s Profile

Comments

Logo Logo
Loading...