Order Block Retest Screener

Category: Screeners By: Iván González Created: July 2, 2026, 5:19 PM
July 2, 2026, 5:19 PM
Screeners
0 Comments

An order block is only useful while it is unmitigated — a demand or supply zone price has not yet traded through. The hard part is not drawing it, it is catching the moment price comes back to it. Flipping through hundreds of charts to find the one instrument currently reacting inside a live order block does not scale.

This ProScreener does it in a single pass. It returns the instruments whose latest candle is retesting a live order block — the demand zones that act as support and the supply zones that act as resistance — exactly the boxes drawn by the KN Support Resistance OB indicator. That indicator draws the two-tier (internal + swing) order-block map on the chart; this screener filters the whole universe and brings back only the names sitting inside a zone right now.

It is deliberately a screener of the live zone, not of the break that creates it. A break-of-structure scan would return the bar where the block is born — price already far from the level. This one filters what you actually see on the chart: price reacting inside a zone that is still alive.

Theory Behind the Screener

1. What counts as a “support” here

In this indicator, support and resistance are order blocks, not horizontal lines:

  • Bullish order block = demand zone = support. Price is expected to bounce up from it.
  • Bearish order block = supply zone = resistance. Price is expected to reject down from it.

A retest of demand is the natural place to look for longs; a retest of supply, for shorts. The mode input picks which side you want (or both).

2. What “retest of a live block” means

For each live block the screener checks whether the current candle has actually entered the zone:

  • Demand retest (bullish OB): the candle low enters the box [bot, top] and the close holds above the bottom (low <= top AND close >= bot) — price dipped into support and did not break it.
  • Supply retest (bearish OB): the candle high enters the box and the close holds below the top (high >= bot AND close <= top).

A block stays live until it is mitigated (price trades through it), with the same High/Low vs Close choice as the indicator.

3. How the zones are rebuilt inside a screener

The detection mirrors the indicator: LuxAlgo leg pivots at two lengths (internal 5, swing 50), and a block is born when the close breaks an uncrossed pivot. But ProScreener does not allow (a) defparam, (b) indexing user variables with [n], or (c) a loop-variable offset on native series (high[i] with i from a loop). So three things change versus the indicator:

  • Block geometry by incremental tracking, not a backward loop. While a pivot is unbroken, the screener keeps a running lowest parsedLow (with its parsedHigh) for the bullish candidate and a running highest parsedHigh for the bearish one. When the pivot breaks, that running extreme is the block. This closely approximates the indicator’s backward “best-candidate” loop, only differing on the length bars between a pivot forming and being confirmed.
  • Lags via prev variables instead of legSwing[1] / legInternal[1], and crosses over rewritten as close > level AND close[1] <= level (the only [1] is on close, a native series — allowed).
  • Live blocks stored in a circular array of MAXZ slots, swept for mitigation by fixed index, replacing the last-bar repaint of the indicator.
  • ATR-only volatility filter. The indicator’s Cumulative Mean Range option is dropped (it would need the recursive cumTR[1] accumulator); ATR is the indicator’s default anyway.

4. Output columns

  • Dist OB % — distance from the close to the centre of the nearest retested block, in % of price. Sort ascending to rank the tightest tests first.
  • R1 S-1 — -1 when the nearest zone is support (demand, below), 1 when it is resistance (supply, above).

How to Read It

  1. First pass — validate against the indicator. With proxATR = 100 (no proximity filter) the screener returns every instrument with an active order-block retest. Load the indicator on three or four of the results: price should sit inside a live box. If they line up, detection is aligned.
  2. Tighten by proximity. Lower proxATR to 0.51.0 to keep only the names genuinely pressed into the zone, and sort by Dist OB % ascending.
  3. Pick a side. mode = 1 for demand retests (longs at support), mode = 2 for supply retests (shorts at resistance), mode = 0 for both.
  4. Major structure only. useInternal = 0 keeps just the swing order blocks — the more significant zones, less noise.

The Screener Code

//----------------------------------------------
//SCR_KN Support Resistance OB - Screener SOPORTES
//Filtra valores en retest de un order block de demanda vivo
//version = 0
//Ivan Gonzalez @ www.prorealcode.com
//Sharing ProRealTime knowledge
//----------------------------------------------

// Geometria del OB por tracking incremental (running min/max).

// === INPUTS ===
internalLength = 5     // Internal Structure Length (>= 2)
swingLength    = 50    // Swing Structure Length (>= 10)
obMitigation   = 0     // 1 = Close, 0 = High/Low
useInternal    = 1     // 1 = internos + swing, 0 = solo swing
mode           = 1     // 1 = solo soportes (demanda). 2 = resistencias, 0 = ambos
proxATR        = 100   // retest a <= proxATR*ATR(14) (100 = sin filtro)
atrLen         = 200   // ATR del filtro de volatilidad (bajar en PRT Complete)

MAXZ = 20

// === ATR ===
atr   = averagetruerange[atrLen](close)
atr14 = averagetruerange[14](close)

// === parsed high/low (swap alta volatilidad LuxAlgo) ===
if (high - low) >= 2 * atr then
   pHigh = low
   pLow  = high
else
   pHigh = high
   pLow  = low
endif

// === STATE INIT ===
if barindex = 0 then
   prevLegSwing      = 0
   prevLegInternal   = 0
   swingHighLevel    = 0
   swingLowLevel     = 0
   internalHighLevel = 0
   internalLowLevel  = 0
   swingHighCrossed    = 1
   swingLowCrossed     = 1
   internalHighCrossed = 1
   internalLowCrossed  = 1
   sBullLow  = low
   sBullHigh = high
   sBearHigh = high
   sBearLow  = low
   iBullLow  = low
   iBullHigh = high
   iBearHigh = high
   iBearLow  = low
   obPtr = 0
endif

// === SWING LEG ===
if barindex <= swingLength then
   legSwing = 0
elsif high[swingLength] > highest[swingLength](high) then
   legSwing = 0
elsif low[swingLength] < lowest[swingLength](low) then
   legSwing = 1
else
   legSwing = prevLegSwing
endif

if legSwing <> prevLegSwing then
   if legSwing = 1 then
      swingLowLevel   = low[swingLength]
      swingLowCrossed = 0
      sBearHigh = pHigh
      sBearLow  = pLow
   else
      swingHighLevel   = high[swingLength]
      swingHighCrossed = 0
      sBullLow  = pLow
      sBullHigh = pHigh
   endif
endif

// === INTERNAL LEG ===
if barindex <= internalLength then
   legInternal = 0
elsif high[internalLength] > highest[internalLength](high) then
   legInternal = 0
elsif low[internalLength] < lowest[internalLength](low) then
   legInternal = 1
else
   legInternal = prevLegInternal
endif

if legInternal <> prevLegInternal then
   if legInternal = 1 then
      internalLowLevel   = low[internalLength]
      internalLowCrossed = 0
      iBearHigh = pHigh
      iBearLow  = pLow
   else
      internalHighLevel   = high[internalLength]
      internalHighCrossed = 0
      iBullLow  = pLow
      iBullHigh = pHigh
   endif
endif

// === TRACKING DE CANDIDATOS ===
if swingHighCrossed = 0 then
   if pLow < sBullLow then
      sBullLow  = pLow
      sBullHigh = pHigh
   endif
endif
if swingLowCrossed = 0 then
   if pHigh > sBearHigh then
      sBearHigh = pHigh
      sBearLow  = pLow
   endif
endif
if internalHighCrossed = 0 then
   if pLow < iBullLow then
      iBullLow  = pLow
      iBullHigh = pHigh
   endif
endif
if internalLowCrossed = 0 then
   if pHigh > iBearHigh then
      iBearHigh = pHigh
      iBearLow  = pLow
   endif
endif

// === ROTURAS -> PUSH OB (buffer circular) ===
if swingHighCrossed = 0 then
   if close > swingHighLevel and close[1] <= swingHighLevel then
      swingHighCrossed = 1
      $obTop[obPtr]  = sBullHigh
      $obBot[obPtr]  = sBullLow
      $obBias[obPtr] = 1
      $obAct[obPtr]  = 1
      obPtr = obPtr + 1
      if obPtr >= MAXZ then
         obPtr = 0
      endif
   endif
endif
if swingLowCrossed = 0 then
   if close < swingLowLevel and close[1] >= swingLowLevel then
      swingLowCrossed = 1
      $obTop[obPtr]  = sBearHigh
      $obBot[obPtr]  = sBearLow
      $obBias[obPtr] = -1
      $obAct[obPtr]  = 1
      obPtr = obPtr + 1
      if obPtr >= MAXZ then
         obPtr = 0
      endif
   endif
endif
if useInternal = 1 then
   if internalHighCrossed = 0 then
      if internalHighLevel <> swingHighLevel then
         if close > internalHighLevel and close[1] <= internalHighLevel then
            internalHighCrossed = 1
            $obTop[obPtr]  = iBullHigh
            $obBot[obPtr]  = iBullLow
            $obBias[obPtr] = 1
            $obAct[obPtr]  = 1
            obPtr = obPtr + 1
            if obPtr >= MAXZ then
               obPtr = 0
            endif
         endif
      endif
   endif
endif
if useInternal = 1 then
   if internalLowCrossed = 0 then
      if internalLowLevel <> swingLowLevel then
         if close < internalLowLevel and close[1] >= internalLowLevel then
            internalLowCrossed = 1
            $obTop[obPtr]  = iBearHigh
            $obBot[obPtr]  = iBearLow
            $obBias[obPtr] = -1
            $obAct[obPtr]  = 1
            obPtr = obPtr + 1
            if obPtr >= MAXZ then
               obPtr = 0
            endif
         endif
      endif
   endif
endif

// === MITIGACION ===
if obMitigation = 1 then
   mitBear = close
   mitBull = close
else
   mitBear = high
   mitBull = low
endif

for i = 0 to MAXZ - 1 do
   if $obAct[i] = 1 then
      if $obBias[i] = -1 then
         if mitBear > $obTop[i] then
            $obAct[i] = 0
         endif
      else
         if mitBull < $obBot[i] then
            $obAct[i] = 0
         endif
      endif
   endif
next

// === RETEST: la vela actual toca algun OB vivo ===
matchBull = 0
matchBear = 0
haveDist  = 0
minDist   = 0
nearBias  = 0
for i = 0 to MAXZ - 1 do
   if $obAct[i] = 1 then
      obT = $obTop[i]
      obB = $obBot[i]
      obMid = (obT + obB) / 2
      if $obBias[i] = 1 then
         if low <= obT and close >= obB then
            matchBull = matchBull + 1
            d = abs(close - obMid)
            if haveDist = 0 or d < minDist then
               minDist  = d
               nearBias = -1
               haveDist = 1
            endif
         endif
      else
         if high >= obB and close <= obT then
            matchBear = matchBear + 1
            d = abs(close - obMid)
            if haveDist = 0 or d < minDist then
               minDist  = d
               nearBias = 1
               haveDist = 1
            endif
         endif
      endif
   endif
next

// === MODE + PROXIMIDAD ===
matchAny = 0
if mode = 0 and (matchBull > 0 or matchBear > 0) then
   matchAny = 1
endif
if mode = 1 and matchBull > 0 then
   matchAny = 1
endif
if mode = 2 and matchBear > 0 then
   matchAny = 1
endif

if atr14 > 0 then
   distAtr = minDist / atr14
else
   distAtr = 0
endif
if close > 0 then
   minDistPct = minDist / close * 100
else
   minDistPct = 0
endif

prevLegSwing    = legSwing
prevLegInternal = legInternal

SCREENER[matchAny = 1 and haveDist = 1 and distAtr <= proxATR](minDistPct AS "Dist OB %", nearBias AS "R1 S-1")

Download
Filename: SCR_KN-Support-Resistance-OB.itf
Downloads: 1
Iván González Legend
Developer by day, aspiring writer by night. Still compiling my bio... Error 404: presentation not found.
Author’s Profile

Comments

Logo Logo
Loading...