Most trend-following stops use a tolerance chosen in advance, usually a multiple of the ATR. This indicator lets the trend set its own tolerance. Every correction the trend absorbs, from one high to the next high (or from one low to the next low in a downtrend), is stored as a box with two dimensions: its depth and its duration. The deepest and the longest boxes set the template, which is hung from the latest extreme.
A pullback that leaves the template through the bottom is deeper than anything the trend has absorbed so far: the indicator calls a reversal. A pullback that leaves it through the right side lasts longer than any previous one: the trend goes on hold until price makes a new extreme. Time becomes a criterion of its own, alongside price.
After a large correction has been absorbed, the template is wide and the exit comes late. At the start of a trend the template is inherited from the previous trend, so the first exits are approximate. The ATR is only used to seed the template and as a floor. The idea that a correction larger than the previous ones signals a reversal goes back to Gann; storing it in two dimensions and drawing it as boxes is the new part. This is a visual tool and has not been tested as a trading system.
The code uses arrays and draws its objects on the last bar only, so the whole history is redrawn at each update. It contains no loop over past bars.
//----------------------------------------------------------------
// PRC_Correction Template | indicator
// Author: Nicolas
// Date: 12.11.2026
// https://www.prorealcode.com
// License: CC BY-NC 4.0 - free to use, share and modify for
// non-commercial purposes, provided the author and the source
// above are credited.
// https://creativecommons.org/licenses/by-nc/4.0/
//----------------------------------------------------------------
// CORRECTION TEMPLATE - the largest correction already absorbed
// Every absorbed correction (from one high to the next high, or from one low to
// the next low in a downtrend) is stored: its depth and its duration. The deepest
// and the longest set the template, hung from the latest extreme.
// Exit through price: reversal. Exit through time: trend on hold.
DEFPARAM DrawOnLastBarOnly = true
// ---------- Settings
margin = 1.25 // tolerance = template x margin
atrPeriod = 20
seedATR = 2.5 // depth of the very first template, in ATR
seedBars = 12 // duration of the very first template, in bars
minTplATR = 1.2 // minimum template depth, in ATR
minTplBars = 5 // minimum template duration, in bars
minCorrATR = 0.5 // a smaller correction is not stored
inheritCount = 3 // the inherited template counts until this many corrections
maxBoxes = 300 // number of past boxes redrawn
atr = AverageTrueRange[atrPeriod](close)
ONCE ready = 0
ONCE nB = 0
ONCE nE = 0
ONCE nCorr = 0
ONCE maxDepth = 0
ONCE maxDur = 0
ONCE idDepth = -1
ONCE idDur = -1
ONCE side = 0
ONCE tplLimit = close
IF ready = 0 THEN
IF barindex > atrPeriod + 10 AND atr > 0 THEN
// seed: direction from the last ten bars, default template
side = -1
refPrice = low
IF close >= close[10] THEN
side = 1
refPrice = high
ENDIF
regime = side
refBar = barindex
inPullback = 0
extremeP = refPrice
extremeBar = barindex
inhDepth = seedATR * atr
inhDur = seedBars
tDepth = inhDepth
tDur = inhDur
tplLimit = refPrice - side * margin * tDepth
tplRight = refBar + CEIL(margin * tDur)
ready = 1
ENDIF
ELSE
fav = low
adv = high
IF side = 1 THEN
fav = high
adv = low
ENDIF
IF side * (fav - refPrice) > 0 THEN
// new extreme: the preceding correction is absorbed
IF inPullback = 1 THEN
depth = side * (refPrice - extremeP)
dur = barindex - refBar
IF depth >= minCorrATR * atr OR regime = 0 THEN
$bX1[nB] = refBar
$bY1[nB] = refPrice
$bX2[nB] = barindex
$bY2[nB] = extremeP
$bS[nB] = side
IF depth > maxDepth THEN
maxDepth = depth
idDepth = nB
ENDIF
IF dur > maxDur THEN
maxDur = dur
idDur = nB
ENDIF
nB = nB + 1
nCorr = nCorr + 1
ENDIF
ENDIF
refPrice = fav
refBar = barindex
inPullback = 0
extremeP = fav
extremeBar = barindex
regime = side
ELSE
IF inPullback = 0 THEN
inPullback = 1
extremeP = adv
extremeBar = barindex
ELSIF side * (adv - extremeP) < 0 THEN
extremeP = adv
extremeBar = barindex
ENDIF
tplLimit = refPrice - side * margin * tDepth
tplRight = refBar + CEIL(margin * tDur)
IF side * (close - tplLimit) < 0 THEN
// exit through price: reversal, the template is handed to the next trend
$eX[nE] = barindex
$eY[nE] = tplLimit
$eT[nE] = -side
nE = nE + 1
inhDepth = tDepth
inhDur = tDur
side = -side
regime = side
refPrice = extremeP
refBar = extremeBar
inPullback = 0
nCorr = 0
maxDepth = 0
maxDur = 0
idDepth = -1
idDur = -1
ELSIF regime <> 0 AND barindex > tplRight THEN
// exit through time: on hold until the next extreme
regime = 0
$eX[nE] = barindex
$eY[nE] = refPrice + side * 0.6 * atr
$eT[nE] = 0
nE = nE + 1
ENDIF
ENDIF
// template in force after this bar
tDepth = minTplATR * atr
tDur = minTplBars
IF nCorr < inheritCount THEN
tDepth = MAX(tDepth, inhDepth)
tDur = MAX(tDur, inhDur)
ENDIF
tDepth = MAX(tDepth, maxDepth)
tDur = MAX(tDur, maxDur)
tplLimit = refPrice - side * margin * tDepth
tplRight = refBar + CEIL(margin * tDur)
ENDIF
// ---------- colour by regime: rise, fall, on hold
rG = 130
gG = 140
bG = 150
aLim = 0
IF ready = 1 THEN
IF regime = 1 THEN
rG = 0
gG = 153
bG = 153
ELSIF regime = -1 THEN
rG = 226
gG = 106
bG = 44
ENDIF
// the line breaks on the reversal bar
IF side = side[1] THEN
aLim = 255
ENDIF
ENDIF
// ---------- drawings, rebuilt on the last bar
IF IsLastBarUpdate AND ready = 1 THEN
// absorbed corrections; thick border for those that set the template
IF nB > 0 THEN
FOR k = MAX(0, nB - maxBoxes) TO nB - 1 DO
cr = 226
cg = 106
cb = 44
IF $bS[k] = 1 THEN
cr = 0
cg = 153
cb = 153
ENDIF
IF k = idDepth OR k = idDur THEN
DRAWRECTANGLE($bX1[k], $bY1[k], $bX2[k], $bY2[k]) COLOURED(cr, cg, cb, 70) BORDERCOLOR(cr, cg, cb, 255) STYLE(line, 3)
ELSE
DRAWRECTANGLE($bX1[k], $bY1[k], $bX2[k], $bY2[k]) COLOURED(cr, cg, cb, 26) BORDERCOLOR(cr, cg, cb, 140)
ENDIF
NEXT
ENDIF
// X: exit through price. "time": exit through time.
IF nE > 0 THEN
FOR k = MAX(0, nE - maxBoxes) TO nE - 1 DO
IF $eT[k] = 0 THEN
DRAWTEXT("time", $eX[k], $eY[k]) COLOURED(130, 140, 150)
ELSIF $eT[k] = 1 THEN
DRAWTEXT("X", $eX[k], $eY[k]) COLOURED(0, 153, 153)
ELSE
DRAWTEXT("X", $eX[k], $eY[k]) COLOURED(226, 106, 44)
ENDIF
NEXT
ENDIF
// current template, hung from the latest extreme; what sticks out on the right is the time left
DRAWRECTANGLE(refBar, refPrice, tplRight, tplLimit) COLOURED(rG, gG, bG, 22) BORDERCOLOR(rG, gG, bG, 230) STYLE(dottedline, 2)
// label placed beyond the latest extreme, where there are no candles
depthATR = ROUND(tDepth / atr * 10) / 10
xT = barindex
yT = refPrice + side * 0.7 * atr
IF regime = 0 THEN
DRAWTEXT("on hold", xT, yT) COLOURED(rG, gG, bG)
ELSE
DRAWTEXT("template #depthATR# ATR x #tDur# bars", xT, yT) COLOURED(rG, gG, bG)
ENDIF
ENDIF
RETURN tplLimit COLOURED(rG, gG, bG, aLim) STYLE(line, 2) AS "Template limit"