Most trend tools answer a single question: is the market going up or down? Trend Exhaustion & Continuation Map by BullByte asks a series of smaller ones, and keeps the answers apart. Has a directional leg actually started? Has it made real progress, measured with closes and not with wicks? Is the current pullback a normal giveback or the start of a failure? Did price reclaim a local reference after a meaningful pullback? Is the move running out of steam? Has the structure that protected the leg been lost?
A strong bar is not automatically a mature trend, an exhaustion warning is not a reversal, and a continuation event is not an instruction to buy. The indicator draws each of those events separately: boxes and markers on the price chart, and an oscillator panel with accepted progress, giveback and the current phase of the leg.
A movement scale instead of points. Every threshold is measured in units of typical movement: by default the median of the last 20 true ranges (on intraday charts, a bar that opens after a time gap only counts its own high-low range, so an overnight jump does not distort the scale). The alternative is the median of the absolute close-to-close moves. When a leg is born, the scale is frozen for the whole leg, so its thresholds do not drift in the middle of the move. The same settings work on a stock, an index or a currency pair.
Birth of a leg (DRIVE). A bullish leg starts only when four tests agree on the same bar:
The bearish case is the mirror image. The lowest low of the 4 bars before the birth becomes the protected structure of the leg, and it does not move while the leg is alive.
Phases: DRIVE, FORMING, TREND. Progress is counted with closes only: a new accepted close is a close beyond the best close of the leg. The leg becomes FORMING when the best accepted close is 0.5 scale units beyond the birth close, and TREND at 2 scale units with at least 3 bars. Time alone never promotes a leg.
The pullback cycle. When the close falls 0.35 scale units below the best accepted close the leg enters GIVEBACK; a close back in the direction of the leg starts RECOVERY, and a new accepted close resumes the drive.
Continuation. After a pullback of at least 0.25 scale units from the best accepted close, a close above the high of the 2 previous bars is a long continuation trigger (the mirror for short). It does not need the TREND phase.
Exhaustion. A latched warning that needs two things: a giveback of 0.40 scale units in DRIVE (0.70 in FORMING or TREND), and either a failed extension (a wick beyond the best close that closes back inside) or 2 bars without new accepted progress. Once fired it stays on until the leg makes 0.75 scale units of new progress beyond the close where the warning appeared.
Structure failure. A close beyond the protected structure ends the leg. In the default Structure Confirmed mode, an opposite leg can replace it only if its drive also clears an extra 0.30 scale units of displacement, and after a failure only the opposite direction may start the next leg. Losing the structure ends the old direction; it does not promise a move the other way.
Balance. A range of the last 12 bars that is at least 2 scale units wide, inefficient (net move no more than 0.24 of the path) and with the close near its centre, for 4 bars in a row, is accepted as BALANCE. It is released by a close 0.10 scale units beyond the stored range, or by a new drive.
Session gaps. On intraday charts, when more than 3 bars of time pass between one close and the next open (an overnight gap on a stock, a weekend on a currency pair), the analysis starts a fresh segment: no level from the previous session is reused.
A ProBuilder indicator lives either on the price chart or in its own panel. This one needs both, so it comes as two indicators that run exactly the same engine:
Both read the same settings block, so if you change a setting in one, change it in the other too.
On the price chart:
In the panel:
100 x (1 - e^-x), where x is the new progress in scale units: about 63 for one unit, close to 100 for a big push. It is a bounded intensity, not a probability.
None of the events is an order: they describe what price did, not what it must do next.
All thresholds are in units of the movement scale. Both indicators share these settings:
Segment boundaries and scale:
Leg detection:
Phases, exhaustion and continuation:
Balance:
Colours:
Only in the panel: showPaneEvents (default 1) and showGiveback (default 1). Only on the price chart: keepSegments (default 100: about 4 boxes per unit, oldest removed first), showPriceEvents (default 1) and showDashboard (default 1).
Alerts. The panel returns six invisible series, all at 0 when nothing happens: Drive Birth Event (+1 bullish, -1 bearish), Phase Change Event (+2 / -2 forming, +3 / -3 trend), Continuation Event (+1 long, -1 short), Exhaustion Event (+1 in a bullish leg, -1 in a bearish leg), Structure Failure Event (+1 bullish structure lost, -1 bearish) and Balance Event (+1 formed, -1 released). Create a ProRealTime alert on the one you need.
The live bar. The last bar is recalculated on every tick, so its events are provisional until it closes: a birth circle, a phase change or a new box can appear and disappear while the bar is open. Every closed bar is final.
Part 1: the panel (apply it in a new panel)
//---------------------------------------------------------------
//PRC_Trend Exhaustion & Continuation Map (panel)
//version = 0
//22.10.2026
//Iván González @ www.prorealcode.com
//Author: BullByte
//Sharing ProRealTime knowledge
//---------------------------------------------------------------
//PART 1 of 2 - THE OSCILLATOR. Apply this one in its own panel.
//The companion indicator, PRC_Trend Exhaustion & Continuation Map (price),
//draws the leg boxes, the event markers and the scanner on the price chart.
//Both run the same state engine, so they always tell the same story.
//---------------------------------------------------------------
// --- Panel display
showPaneEvents = 1 // 1 = milestone, continuation and exhaustion diamonds in the panel
showGiveback = 1 // 1 = grey giveback field below zero
// ===================== INPUTS =====================
// Thresholds are measured in units of typical movement (the movement scale),
// so the same settings work on instruments with very different prices.
// --- Segment boundaries
resetOnGap = 1 // 1 = reset the active leg on a material intraday time gap (intraday charts only)
gapResetMultiple = 3.0 // gap threshold: interval between the prior close and the next open, in chart bars (1.5 to 20)
// --- Movement scale
scaleMode = 0 // 0 = Median True Range (gap-filtered), 1 = Median Close Travel
scaleLength = 20 // number of samples of the median (5 to 100)
// --- Leg detection
driveWindow = 4 // displacement window (3 to 15)
breakWindow = 4 // breakout reference window: prior bars whose high/low the close must clear (3 to 30)
birthMoveUnits = 0.85 // minimum net displacement to start a leg, in scale units
birthEfficiency = 0.32 // minimum directional efficiency (net move / close-to-close path)
protectedWindow = 4 // bars that set the protected structure anchored at the leg birth (2 to 12)
breakClearanceUnits = 0.12 // the close must clear the breakout level by this fraction of the scale
reversalMode = 0 // 0 = Structure Confirmed, 1 = Early Displacement
reversalBuffer = 0.30 // extra displacement required for an opposite leg in Structure Confirmed mode
// --- Phase transitions
formingUnits = 0.50 // accepted progress beyond the birth close required for FORMING
trendUnits = 2.0 // accepted progress beyond the birth close required for TREND
minTrendBars = 3 // minimum leg bars before TREND (2 to 12)
// --- Exhaustion
driveExhaustionUnits = 0.40 // giveback that flags a stall during DRIVE
trendExhaustionUnits = 0.70 // giveback that flags exhaustion during FORMING and TREND
failedAttemptBuffer = 0.05 // excursion beyond the best accepted close for the failed-extension route
exhaustionStallBars = 2 // alternative route: bars without new accepted progress (1 to 8)
exhaustionRearmUnits = 0.75 // new progress beyond the warning close required to re-arm the warning
// --- Continuation scan
triggerWindow = 2 // local reclaim reference: preceding bars whose high/low the close must clear (1 to 5)
minPullbackUnits = 0.25 // minimum pullback from the best accepted close
givebackPhaseUnits = 0.35 // distance from the best accepted close that starts GIVEBACK
progressMilestoneUnits = 1.0 // extra favourable progress between two milestone markers
// --- Balance
balanceWindow = 12 // balance observation window (5 to 30)
balanceEfficiency = 0.24 // maximum efficiency of a balance
balanceWidthUnits = 2.0 // minimum balance width, in scale units
balancePersistence = 4 // consecutive bars meeting the conditions before balance is accepted (2 to 10)
balanceBreakUnits = 0.10 // close beyond the stored balance boundary needed to release it
balanceMaturity = 8 // bars in balance before its fill gets darker (4 to 20)
// --- Colours
darkChart = 0 // 0 = colours tuned for the white ProRealTime background, 1 = original palette for dark charts
// ===================== STATE ENGINE (identical in both indicators) =====================
// Integer inputs kept inside the original ranges
gapMult = max(1.5, min(20, gapResetMultiple))
scaleLen = max(5, min(100, round(scaleLength)))
driveWin = max(3, min(15, round(driveWindow)))
breakWin = max(3, min(30, round(breakWindow)))
protWin = max(2, min(12, round(protectedWindow)))
trigWin = max(1, min(5, round(triggerWindow)))
balWin = max(5, min(30, round(balanceWindow)))
balPersist = max(2, min(10, round(balancePersistence)))
balMature = max(4, min(20, round(balanceMaturity)))
trendBarsMin = max(2, min(12, round(minTrendBars)))
stallBars = max(1, min(8, round(exhaustionStallBars)))
// TREND always needs a little more progress than FORMING
effTrendUnits = max(trendUnits, formingUnits + 0.1)
// Exact ties on a threshold (an extension of exactly 2.00 units, a giveback of exactly 0.35)
// count the way the rule is written (>=, <=), not the way floating-point rounding falls
epsU = 0.000000001 // tolerance in scale units
epsP = ticksize * 0.000001 // tolerance in price
// State that persists from bar to bar
ONCE regime = 0 // +1 bullish leg, -1 bearish leg, 0 scanning or transition, 2 balance
ONCE transActive = 0
ONCE transBars = 0
ONCE transDir = 0 // after a structure failure, the only direction allowed to replace the leg
ONCE trendPhase = 0 // 1 DRIVE, 2 FORMING, 3 TREND
ONCE legPhase = 0 // 0 drive, 1 giveback, 2 recovery
ONCE legBars = 0
ONCE birthClose = 0
ONCE scaleAtBirth = 0
ONCE bestClose = 0
ONCE protLow = 0
ONCE protHigh = 0
ONCE pbActive = 0
ONCE pbExtreme = 0
ONCE pbRefClose = 0
ONCE barsSinceBest = 0
ONCE lastMilestone = 0
ONCE exLatched = 0
ONCE exRef = 0
ONCE exRefOk = 0
ONCE exStatus = 0 // 0 -, 1 CLEAR, 2 CLEARED, 3 DRIVE STALL, 4 EXHAUSTION WATCH, 5 STRUCTURE FAILED
ONCE lockBalHigh = 0
ONCE lockBalLow = 0
ONCE lastEvent = 0
ONCE balanceBars = 0
ONCE balanceAge = 0
ONCE barsInSeg = 0
// --- Gap handling: on intraday charts a material time gap starts a new segment
tfSec = GetTimeFrame
rawGap = 0
gapSec = 0
IF barindex > 0 THEN
gapSec = max(0, opentimestamp - timestamp[1])
IF tfSec < 86400 AND opentimestamp > timestamp[1] THEN
rawGap = 1
ENDIF
ENDIF
materialGap = 0
IF rawGap = 1 AND gapSec > tfSec * gapMult THEN
materialGap = 1
ENDIF
segStart = 0
IF barindex = 0 OR (resetOnGap = 1 AND materialGap = 1) THEN
segStart = 1
ENDIF
// --- Movement scale: median of the true range (a gap bar only counts its own high-low)
// or of the close-to-close travel (samples that span a gap are left out)
IF barindex = 0 OR rawGap = 1 THEN
rangeS = high - low
ELSE
rangeS = max(high - low, max(abs(high - close[1]), abs(low - close[1])))
ENDIF
rangeS = max(rangeS, ticksize)
travelOk = 0
travelS = 0
IF barindex > 0 AND rawGap = 0 THEN
travelOk = 1
travelS = max(abs(close - close[1]), ticksize)
ENDIF
medOk = 0
medV = 0
IF barindex >= scaleLen - 1 THEN
nW = 0
FOR jw = 0 TO scaleLen - 1 DO
IF scaleMode = 1 THEN
IF travelOk[jw] = 1 THEN
$wq[nW] = travelS[jw]
nW = nW + 1
ENDIF
ELSE
$wq[nW] = rangeS[jw]
nW = nW + 1
ENDIF
NEXT
IF nW > 0 THEN
// nearest-rank median: the ceil(N/2)-th smallest value (Wirth's selection)
kSel = floor((nW + 1) / 2) - 1
loQ = 0
hiQ = nW - 1
WHILE loQ < hiQ DO
pvQ = $wq[kSel]
iQ = loQ
jQ = hiQ
WHILE iQ <= jQ DO
WHILE $wq[iQ] < pvQ DO
iQ = iQ + 1
WEND
WHILE pvQ < $wq[jQ] DO
jQ = jQ - 1
WEND
IF iQ <= jQ THEN
tmpQ = $wq[iQ]
$wq[iQ] = $wq[jQ]
$wq[jQ] = tmpQ
iQ = iQ + 1
jQ = jQ - 1
ENDIF
WEND
IF jQ < kSel THEN
loQ = iQ
ENDIF
IF kSel < iQ THEN
hiQ = jQ
ENDIF
WEND
medV = $wq[kSel]
medOk = 1
ENDIF
ENDIF
// while the median warms up, the current bar range is used
IF medOk = 1 AND medV > ticksize + epsP THEN
safeScale = medV
ELSE
safeScale = rangeS
ENDIF
safeScale = max(safeScale, ticksize)
// --- Local references: completed bars only, never across the segment boundary
IF segStart = 1 THEN
avail = 0
ELSE
avail = barsInSeg
ENDIF
nBrk = min(breakWin, avail)
nPrt = min(protWin, avail)
nTrg = min(trigWin, avail)
nBal = min(balWin, avail)
nRef = max(max(nBrk, nPrt), max(nTrg, nBal))
runHi = 0
runLo = 0
brkHi = 0
brkLo = 0
prtHi = 0
prtLo = 0
trgHi = 0
trgLo = 0
balHi = 0
balLo = 0
IF nRef > 0 THEN
FOR jr = 1 TO nRef DO
IF jr = 1 THEN
runHi = high[1]
runLo = low[1]
ELSE
runHi = max(runHi, high[jr])
runLo = min(runLo, low[jr])
ENDIF
IF jr = nBrk THEN
brkHi = runHi
brkLo = runLo
ENDIF
IF jr = nPrt THEN
prtHi = runHi
prtLo = runLo
ENDIF
IF jr = nTrg THEN
trgHi = runHi
trgLo = runLo
ENDIF
IF jr = nBal THEN
balHi = runHi
balLo = runLo
ENDIF
NEXT
ENDIF
// close-to-close path of the drive and balance windows
nDrvP = min(driveWin, avail)
nBalP = min(balWin, avail)
nPath = max(nDrvP, nBalP)
runPath = 0
drivePath = 0
balPath = 0
IF nPath > 0 THEN
FOR jp = 0 TO nPath - 1 DO
runPath = runPath + abs(close[jp] - close[jp + 1])
IF jp + 1 = nDrvP THEN
drivePath = runPath
ENDIF
IF jp + 1 = nBalP THEN
balPath = runPath
ENDIF
NEXT
ENDIF
// --- Drive birth: displacement, a clear break of prior structure and efficiency must agree
driveNetOk = 0
driveNet = 0
IF avail >= driveWin THEN
driveNetOk = 1
driveNet = close - close[driveWin]
ENDIF
dirEff = 0
IF driveNetOk = 1 AND drivePath > ticksize + epsP THEN
dirEff = driveNet / drivePath
ENDIF
birthNeed = max(driveWin, max(breakWin, protWin))
birthReady = 0
IF avail >= birthNeed AND driveNetOk = 1 AND drivePath > ticksize + epsP THEN
birthReady = 1
ENDIF
clearance = safeScale * breakClearanceUnits
bullBirthRaw = 0
IF segStart = 0 AND birthReady = 1 AND nBrk > 0 AND close > brkHi + clearance + epsP AND driveNet >= safeScale * birthMoveUnits - epsP AND dirEff >= birthEfficiency - epsU AND close > open THEN
bullBirthRaw = 1
ENDIF
bearBirthRaw = 0
IF segStart = 0 AND birthReady = 1 AND nBrk > 0 AND close < brkLo - clearance - epsP AND driveNet <= epsP - safeScale * birthMoveUnits AND dirEff <= epsU - birthEfficiency AND close < open THEN
bearBirthRaw = 1
ENDIF
// opposite legs need a little more displacement in Structure Confirmed mode
bullRevRaw = 0
IF bullBirthRaw = 1 AND driveNet >= safeScale * (birthMoveUnits + reversalBuffer) - epsP THEN
bullRevRaw = 1
ENDIF
bearRevRaw = 0
IF bearBirthRaw = 1 AND driveNet <= epsP - safeScale * (birthMoveUnits + reversalBuffer) THEN
bearRevRaw = 1
ENDIF
// --- Balance: wide enough, inefficient, centred, for several bars in a row
balWidth = balHi - balLo
balMid = (balHi + balLo) * 0.5
balNetOk = 0
balNet = 0
IF avail >= balWin THEN
balNetOk = 1
balNet = abs(close - close[balWin])
ENDIF
balEff = 1.0
IF balNetOk = 1 AND balPath > ticksize + epsP THEN
balEff = balNet / balPath
ENDIF
balanceRaw = 0
IF segStart = 0 AND avail >= balWin AND nBal > 0 AND balWidth >= safeScale * balanceWidthUnits - epsP AND balPath > ticksize + epsP AND balWidth > epsP AND abs(close - balMid) <= balWidth * 0.35 + epsP AND balEff <= balanceEfficiency + epsU THEN
balanceRaw = 1
ENDIF
IF balanceRaw = 1 THEN
curBalRun = balanceBars + 1
ELSE
curBalRun = 0
ENDIF
balConfirmed = 0
IF curBalRun >= balPersist THEN
balConfirmed = 1
ENDIF
// --- One-bar events
bullBirthEv = 0
bearBirthEv = 0
bullFailEv = 0
bearFailEv = 0
balBirthEv = 0
balReleaseEv = 0
bullFormEv = 0
bearFormEv = 0
bullTrendEv = 0
bearTrendEv = 0
bullContEv = 0
bearContEv = 0
exhaustEv = 0
milestoneEv = 0
bullFailPrice = 0
bearFailPrice = 0
exhaustPrice = 0
impulseOk = 0
impulse = 0
bullLost = 0
IF regime = 1 AND close < protLow THEN
bullLost = 1
ENDIF
bearLost = 0
IF regime = -1 AND close > protHigh THEN
bearLost = 1
ENDIF
IF reversalMode = 1 THEN
bullOpp = bullBirthRaw
bearOpp = bearBirthRaw
ELSE
bullOpp = bullRevRaw
bearOpp = bearRevRaw
ENDIF
IF segStart = 1 THEN
regime = 0
trendPhase = 0
legPhase = 0
legBars = 0
pbActive = 0
barsSinceBest = 0
exLatched = 0
exRefOk = 0
exStatus = 0
balanceBars = 0
balanceAge = 0
barsInSeg = 1
transActive = 0
transBars = 0
transDir = 0
IF materialGap = 1 AND resetOnGap = 1 THEN
lastEvent = 1
ENDIF
ELSE
barsInSeg = barsInSeg + 1
balanceBars = curBalRun
oldRegime = regime
nextRegime = oldRegime
IF oldRegime = 0 THEN
// after a structure failure only the opposite direction may replace the failed leg
IF transActive = 1 AND transDir <> 0 THEN
IF transDir = 1 AND bullOpp = 1 THEN
nextRegime = 1
ELSIF transDir = -1 AND bearOpp = 1 THEN
nextRegime = -1
ELSIF balConfirmed = 1 THEN
nextRegime = 2
ENDIF
ELSE
IF bullBirthRaw = 1 THEN
nextRegime = 1
ELSIF bearBirthRaw = 1 THEN
nextRegime = -1
ELSIF balConfirmed = 1 THEN
nextRegime = 2
ENDIF
ENDIF
ELSIF oldRegime = 1 THEN
IF bearOpp = 1 AND (reversalMode = 1 OR bullLost = 1) THEN
nextRegime = -1
IF bullLost = 1 THEN
bullFailEv = 1
bullFailPrice = protLow
ENDIF
ELSIF bullLost = 1 THEN
nextRegime = 0
bullFailEv = 1
bullFailPrice = protLow
ELSIF balConfirmed = 1 THEN
nextRegime = 2
ENDIF
ELSIF oldRegime = -1 THEN
IF bullOpp = 1 AND (reversalMode = 1 OR bearLost = 1) THEN
nextRegime = 1
IF bearLost = 1 THEN
bearFailEv = 1
bearFailPrice = protHigh
ENDIF
ELSIF bearLost = 1 THEN
nextRegime = 0
bearFailEv = 1
bearFailPrice = protHigh
ELSIF balConfirmed = 1 THEN
nextRegime = 2
ENDIF
ELSIF oldRegime = 2 THEN
balBreakUp = close > lockBalHigh + safeScale * balanceBreakUnits + epsP
balBreakDn = close < lockBalLow - safeScale * balanceBreakUnits - epsP
IF bullBirthRaw = 1 THEN
nextRegime = 1
ELSIF bearBirthRaw = 1 THEN
nextRegime = -1
ELSIF balBreakUp OR balBreakDn THEN
nextRegime = 0
balReleaseEv = 1
ENDIF
ENDIF
IF nextRegime <> oldRegime THEN
regime = nextRegime
IF nextRegime = 1 THEN
transActive = 0
transBars = 0
transDir = 0
birthClose = close
scaleAtBirth = safeScale
bestClose = close
protLow = prtLo
pbActive = 0
trendPhase = 1
legPhase = 0
legBars = 1
barsSinceBest = 0
lastMilestone = close
exLatched = 0
exRefOk = 0
exStatus = 1
bullBirthEv = 1
impulse = 100 * (1 - exp(0 - max(0, (close - close[1]) / scaleAtBirth)))
impulseOk = 1
IF oldRegime = -1 THEN
lastEvent = 2
ELSIF oldRegime = 2 THEN
lastEvent = 3
ELSE
lastEvent = 4
ENDIF
IF oldRegime = 2 THEN
balReleaseEv = 1
ENDIF
ELSIF nextRegime = -1 THEN
transActive = 0
transBars = 0
transDir = 0
birthClose = close
scaleAtBirth = safeScale
bestClose = close
protHigh = prtHi
pbActive = 0
trendPhase = 1
legPhase = 0
legBars = 1
barsSinceBest = 0
lastMilestone = close
exLatched = 0
exRefOk = 0
exStatus = 1
bearBirthEv = 1
impulse = 100 * (1 - exp(0 - max(0, (close[1] - close) / scaleAtBirth)))
impulseOk = 1
IF oldRegime = 1 THEN
lastEvent = 5
ELSIF oldRegime = 2 THEN
lastEvent = 6
ELSE
lastEvent = 7
ENDIF
IF oldRegime = 2 THEN
balReleaseEv = 1
ENDIF
ELSIF nextRegime = 2 THEN
transActive = 0
transBars = 0
transDir = 0
lockBalHigh = balHi
lockBalLow = balLo
pbActive = 0
trendPhase = 0
legPhase = 0
exLatched = 0
exRefOk = 0
exStatus = 0
balBirthEv = 1
IF oldRegime = 0 THEN
lastEvent = 8
ELSE
lastEvent = 9
ENDIF
ELSE
trendPhase = 0
legPhase = 0
pbActive = 0
IF bullFailEv = 1 THEN
exLatched = 1
exStatus = 5
lastEvent = 10
transActive = 1
transBars = 1
transDir = -1
ELSIF bearFailEv = 1 THEN
exLatched = 1
exStatus = 5
lastEvent = 11
transActive = 1
transBars = 1
transDir = 1
ELSE
exStatus = 0
IF oldRegime = 2 THEN
lastEvent = 12
transActive = 1
transBars = 1
transDir = 0
ENDIF
ENDIF
ENDIF
ELSE
regime = nextRegime
IF regime = 0 AND transActive = 1 THEN
transBars = transBars + 1
ENDIF
IF regime = 1 OR regime = -1 THEN
legBars = legBars + 1
phaseStart = trendPhase
priorBest = bestClose
madeNew = 0
IF regime = 1 AND close > priorBest THEN
madeNew = 1
ENDIF
IF regime = -1 AND close < priorBest THEN
madeNew = 1
ENDIF
contTrig = 0
// continuation: a meaningful pullback, then a close through a completed local reference
IF pbActive = 1 THEN
IF regime = 1 THEN
candExt = min(pbExtreme, low)
ELSE
candExt = max(pbExtreme, high)
ENDIF
pbDepth = regime * (pbRefClose - candExt) / scaleAtBirth
reclaim = 0
IF regime = 1 AND nTrg > 0 AND close > trgHi THEN
reclaim = 1
ENDIF
IF regime = -1 AND nTrg > 0 AND close < trgLo THEN
reclaim = 1
ENDIF
IF pbDepth >= minPullbackUnits - epsU AND reclaim = 1 THEN
contTrig = 1
IF regime = 1 THEN
bullContEv = 1
lastEvent = 13
ELSE
bearContEv = 1
lastEvent = 14
ENDIF
pbActive = 0
legPhase = 0
ELSE
pbExtreme = candExt
toward = 0
IF regime = 1 AND close > close[1] THEN
toward = 1
ENDIF
IF regime = -1 AND close < close[1] THEN
toward = 1
ENDIF
IF legPhase = 1 AND toward = 1 THEN
legPhase = 2
IF madeNew = 0 THEN
lastEvent = 15
ENDIF
ENDIF
ENDIF
ELSIF madeNew = 0 THEN
adverse = 0
IF regime = 1 AND close < close[1] THEN
adverse = 1
ENDIF
IF regime = -1 AND close > close[1] THEN
adverse = 1
ENDIF
IF adverse = 1 THEN
pbActive = 1
IF regime = 1 THEN
pbExtreme = low
ELSE
pbExtreme = high
ENDIF
pbRefClose = bestClose
ENDIF
ENDIF
// accepted progress uses closes, not wick-only extremes
IF madeNew = 1 THEN
IF pbActive = 1 AND contTrig = 0 THEN
pbActive = 0
ENDIF
newExt = max(0, regime * (close - priorBest) / scaleAtBirth)
impulse = 100 * (1 - exp(0 - newExt))
impulseOk = 1
IF regime * (close - lastMilestone) >= scaleAtBirth * progressMilestoneUnits - epsP THEN
milestoneEv = 1
lastMilestone = close
ENDIF
IF exLatched = 1 AND exRefOk = 1 THEN
IF regime * (close - exRef) / scaleAtBirth >= exhaustionRearmUnits - epsU THEN
exLatched = 0
exRefOk = 0
exStatus = 2
lastEvent = 16
ENDIF
ENDIF
bestClose = close
barsSinceBest = 0
IF legPhase <> 0 THEN
legPhase = 0
IF bullContEv = 0 AND bearContEv = 0 THEN
lastEvent = 17
ENDIF
ENDIF
ELSE
barsSinceBest = barsSinceBest + 1
gbLocal = max(0, regime * (priorBest - close) / scaleAtBirth)
IF contTrig = 0 AND legPhase = 0 AND gbLocal >= givebackPhaseUnits - epsU THEN
legPhase = 1
lastEvent = 18
ENDIF
ENDIF
// FORMING and TREND are progress thresholds, not time-based labels
extFromBirth = regime * (bestClose - birthClose) / scaleAtBirth
IF trendPhase = 1 AND extFromBirth >= formingUnits - epsU THEN
trendPhase = 2
IF regime = 1 THEN
bullFormEv = 1
IF bullContEv = 0 AND bearContEv = 0 THEN
lastEvent = 19
ENDIF
ELSE
bearFormEv = 1
IF bullContEv = 0 AND bearContEv = 0 THEN
lastEvent = 20
ENDIF
ENDIF
ELSIF trendPhase = 2 AND legBars >= trendBarsMin AND extFromBirth >= effTrendUnits - epsU THEN
trendPhase = 3
IF regime = 1 THEN
bullTrendEv = 1
IF bullContEv = 0 AND bearContEv = 0 THEN
lastEvent = 21
ENDIF
ELSE
bearTrendEv = 1
IF bullContEv = 0 AND bearContEv = 0 THEN
lastEvent = 22
ENDIF
ENDIF
ENDIF
// exhaustion is a latched warning: giveback plus a failed extension or a stall
IF madeNew = 0 AND exLatched = 0 AND contTrig = 0 THEN
curGb = max(0, regime * (priorBest - close) / scaleAtBirth)
failedExt = 0
IF regime = 1 AND high > priorBest + scaleAtBirth * failedAttemptBuffer + epsP AND close <= priorBest THEN
failedExt = 1
ENDIF
IF regime = -1 AND low < priorBest - scaleAtBirth * failedAttemptBuffer - epsP AND close >= priorBest THEN
failedExt = 1
ENDIF
IF phaseStart = 1 THEN
thrGb = driveExhaustionUnits
ELSE
thrGb = trendExhaustionUnits
ENDIF
IF curGb >= thrGb - epsU AND (failedExt = 1 OR barsSinceBest >= stallBars) THEN
exhaustEv = 1
exLatched = 1
exRef = priorBest
exRefOk = 1
IF phaseStart = 1 THEN
exStatus = 3
lastEvent = 23
ELSE
exStatus = 4
lastEvent = 24
ENDIF
IF regime = 1 THEN
exhaustPrice = high
ELSE
exhaustPrice = low
ENDIF
ENDIF
ENDIF
IF exStatus = 2 AND exhaustEv = 0 THEN
exStatus = 1
ENDIF
ENDIF
ENDIF
IF regime = 2 THEN
IF oldRegime = 2 THEN
balanceAge = balanceAge + 1
ELSE
balanceAge = 1
ENDIF
ELSE
balanceAge = 0
ENDIF
ENDIF
// ===================== END OF STATE ENGINE =====================
// ===================== OSCILLATOR: ACCEPTED PROGRESS, GIVEBACK AND PHASE =====================
// Phase colours on the chart (light or dark background)
IF darkChart = 1 THEN
bdR = 165
bdG = 255
bdB = 207
bfR = 0
bfG = 205
bfB = 118
sdR = 255
sdG = 190
sdB = 198
yeR = 255
yeG = 214
yeB = 64
ELSE
bdR = 36
bdG = 168
bdB = 118
bfR = 0
bfG = 150
bfB = 86
sdR = 236
sdG = 104
sdB = 124
yeR = 196
yeG = 128
yeB = 0
ENDIF
// colour of the current direction and phase (grey outside a leg)
pcR = 132
pcG = 145
pcB = 160
IF regime = 1 THEN
IF trendPhase = 1 THEN
pcR = bdR
pcG = bdG
pcB = bdB
ELSIF trendPhase = 2 THEN
pcR = bfR
pcG = bfG
pcB = bfB
ELSE
pcR = 0
pcG = 118
pcB = 72
ENDIF
ELSIF regime = -1 THEN
IF trendPhase = 1 THEN
pcR = sdR
pcG = sdG
pcB = sdB
ELSIF trendPhase = 2 THEN
pcR = 245
pcG = 42
pcB = 70
ELSE
pcR = 170
pcG = 8
pcB = 39
ENDIF
ENDIF
// Accepted close extension: one column per birth or new accepted close
impulseV = 0
impulseA = 0
IF impulseOk = 1 THEN
impulseV = impulse
impulseA = 255
ENDIF
// Giveback: how far the close has given back from the best accepted close
gbOk = 0
gbInt = 0
IF regime = 1 AND scaleAtBirth > 0 THEN
gbOk = 1
gbInt = 0 - 100 * (1 - exp(0 - max(0, (max(bestClose, close) - close) / scaleAtBirth)))
ELSIF regime = -1 AND scaleAtBirth > 0 THEN
gbOk = 1
gbInt = 0 - 100 * (1 - exp(0 - max(0, (close - min(bestClose, close)) / scaleAtBirth)))
ENDIF
// the line and the field only join two bars that both have a value
gbA = 0
gbFillA = 0
IF showGiveback = 1 AND gbOk = 1 AND gbOk[1] = 1 THEN
gbA = 255
gbFillA = 56
ENDIF
zeroLine = 0
COLORBETWEEN(gbInt, zeroLine, 132, 145, 160, gbFillA)
// Phase track: DRIVE, FORMING, TREND or BALANCE, drawn as a step line
trackOk = 0
trackV = -150
IF regime = 1 OR regime = -1 THEN
trackOk = 1
IF trendPhase = 1 THEN
trackV = -112
ELSIF trendPhase = 2 THEN
trackV = -124
ELSE
trackV = -136
ENDIF
ELSIF regime = 2 THEN
trackOk = 1
trackV = -148
ENDIF
trackA = 0
IF trackOk = 1 AND trackOk[1] = 1 THEN
trackA = 255
// a change of level is drawn as a step: horizontal at the old level, then vertical
IF trackV <> trackV[1] THEN
trackA = 0
DRAWSEGMENT(barindex - 1, trackV[1], barindex, trackV[1]) COLOURED(pcR, pcG, pcB, 255) STYLE(line, 5)
DRAWSEGMENT(barindex, trackV[1], barindex, trackV) COLOURED(pcR, pcG, pcB, 255) STYLE(line, 5)
ENDIF
ENDIF
// Pane event markers
IF showPaneEvents = 1 THEN
IF milestoneEv = 1 THEN
DRAWTEXT("◆", barindex, impulse) COLOURED(132, 145, 160, 255)
ENDIF
IF bullContEv = 1 THEN
DRAWTEXT("◆", barindex, 0) COLOURED(bfR, bfG, bfB, 255)
ENDIF
IF bearContEv = 1 THEN
DRAWTEXT("◆", barindex, 0) COLOURED(245, 42, 70, 255)
ENDIF
IF exhaustEv = 1 THEN
DRAWTEXT("◆", barindex, gbInt) COLOURED(yeR, yeG, yeB, 255)
ENDIF
ENDIF
// Balance shades the pane background
IF regime = 2 THEN
BACKGROUNDCOLOR(132, 145, 160, 13)
ENDIF
// ===================== CONFIRMED EVENTS FOR ALERTS =====================
// Invisible series: create a ProRealTime alert on any of them (value at bar close).
evBirth = bullBirthEv - bearBirthEv // +1 bullish drive birth, -1 bearish
evPhase = 2 * bullFormEv + 3 * bullTrendEv - 2 * bearFormEv - 3 * bearTrendEv // +-2 FORMING, +-3 TREND
evCont = bullContEv - bearContEv // +1 long trigger, -1 short trigger
evExhaust = exhaustEv * regime // +1 in a bullish leg, -1 in a bearish leg
evFailure = bullFailEv - bearFailEv // +1 bullish structure failed, -1 bearish
evBalance = balBirthEv - balReleaseEv // +1 balance formed, -1 balance released
RETURN impulseV COLOURED(pcR, pcG, pcB, impulseA) STYLE(histogram) AS "Accepted Close Extension", gbInt COLOURED(132, 145, 160, gbA) STYLE(line, 1) AS "Giveback Boundary", trackV COLOURED(pcR, pcG, pcB, trackA) STYLE(line, 5) AS "Leg Phase Track", 100 COLOURED(132, 145, 160, 33) STYLE(line, 1) AS "Drive Intensity Ceiling", 50 COLOURED(132, 145, 160, 26) STYLE(line, 1) AS "Drive Reference", 0 COLOURED(132, 145, 160, 158) STYLE(line, 1) AS "Drive Baseline", -50 COLOURED(132, 145, 160, 26) STYLE(line, 1) AS "Giveback Reference", -100 COLOURED(132, 145, 160, 33) STYLE(line, 1) AS "Giveback Intensity Ceiling", -150 COLOURED(132, 145, 160, 26) STYLE(line, 1) AS "Phase Track Base", evBirth COLOURED(0, 0, 0, 0) AS "Drive Birth Event", evPhase COLOURED(0, 0, 0, 0) AS "Phase Change Event", evCont COLOURED(0, 0, 0, 0) AS "Continuation Event", evExhaust COLOURED(0, 0, 0, 0) AS "Exhaustion Event", evFailure COLOURED(0, 0, 0, 0) AS "Structure Failure Event", evBalance COLOURED(0, 0, 0, 0) AS "Balance Event"
Part 2: the price chart (apply it on the price)
//---------------------------------------------------------------
//PRC_Trend Exhaustion & Continuation Map (price)
//version = 0
//22.10.2026
//Iván González @ www.prorealcode.com
//Author: BullByte
//Sharing ProRealTime knowledge
//---------------------------------------------------------------
//PART 2 of 2 - THE PRICE CHART. Apply this one ON the price.
//The companion indicator, PRC_Trend Exhaustion & Continuation Map (panel),
//plots the accepted progress, the giveback and the phase track in its own panel.
//Both run the same state engine, so they always tell the same story.
//---------------------------------------------------------------
defparam drawonlastbaronly = true
// --- Price chart display
keepSegments = 100 // historical drawing capacity: about 4 boxes per unit (10 to 180)
showPriceEvents = 1 // 1 = drive births, structure failures, balance and exhaustion markers
showDashboard = 1 // 1 = scanner in the top-right corner
// ===================== INPUTS =====================
// Thresholds are measured in units of typical movement (the movement scale),
// so the same settings work on instruments with very different prices.
// --- Segment boundaries
resetOnGap = 1 // 1 = reset the active leg on a material intraday time gap (intraday charts only)
gapResetMultiple = 3.0 // gap threshold: interval between the prior close and the next open, in chart bars (1.5 to 20)
// --- Movement scale
scaleMode = 0 // 0 = Median True Range (gap-filtered), 1 = Median Close Travel
scaleLength = 20 // number of samples of the median (5 to 100)
// --- Leg detection
driveWindow = 4 // displacement window (3 to 15)
breakWindow = 4 // breakout reference window: prior bars whose high/low the close must clear (3 to 30)
birthMoveUnits = 0.85 // minimum net displacement to start a leg, in scale units
birthEfficiency = 0.32 // minimum directional efficiency (net move / close-to-close path)
protectedWindow = 4 // bars that set the protected structure anchored at the leg birth (2 to 12)
breakClearanceUnits = 0.12 // the close must clear the breakout level by this fraction of the scale
reversalMode = 0 // 0 = Structure Confirmed, 1 = Early Displacement
reversalBuffer = 0.30 // extra displacement required for an opposite leg in Structure Confirmed mode
// --- Phase transitions
formingUnits = 0.50 // accepted progress beyond the birth close required for FORMING
trendUnits = 2.0 // accepted progress beyond the birth close required for TREND
minTrendBars = 3 // minimum leg bars before TREND (2 to 12)
// --- Exhaustion
driveExhaustionUnits = 0.40 // giveback that flags a stall during DRIVE
trendExhaustionUnits = 0.70 // giveback that flags exhaustion during FORMING and TREND
failedAttemptBuffer = 0.05 // excursion beyond the best accepted close for the failed-extension route
exhaustionStallBars = 2 // alternative route: bars without new accepted progress (1 to 8)
exhaustionRearmUnits = 0.75 // new progress beyond the warning close required to re-arm the warning
// --- Continuation scan
triggerWindow = 2 // local reclaim reference: preceding bars whose high/low the close must clear (1 to 5)
minPullbackUnits = 0.25 // minimum pullback from the best accepted close
givebackPhaseUnits = 0.35 // distance from the best accepted close that starts GIVEBACK
progressMilestoneUnits = 1.0 // extra favourable progress between two milestone markers
// --- Balance
balanceWindow = 12 // balance observation window (5 to 30)
balanceEfficiency = 0.24 // maximum efficiency of a balance
balanceWidthUnits = 2.0 // minimum balance width, in scale units
balancePersistence = 4 // consecutive bars meeting the conditions before balance is accepted (2 to 10)
balanceBreakUnits = 0.10 // close beyond the stored balance boundary needed to release it
balanceMaturity = 8 // bars in balance before its fill gets darker (4 to 20)
// --- Colours
darkChart = 0 // 0 = colours tuned for the white ProRealTime background, 1 = original palette for dark charts
// ===================== STATE ENGINE (identical in both indicators) =====================
// Integer inputs kept inside the original ranges
gapMult = max(1.5, min(20, gapResetMultiple))
scaleLen = max(5, min(100, round(scaleLength)))
driveWin = max(3, min(15, round(driveWindow)))
breakWin = max(3, min(30, round(breakWindow)))
protWin = max(2, min(12, round(protectedWindow)))
trigWin = max(1, min(5, round(triggerWindow)))
balWin = max(5, min(30, round(balanceWindow)))
balPersist = max(2, min(10, round(balancePersistence)))
balMature = max(4, min(20, round(balanceMaturity)))
trendBarsMin = max(2, min(12, round(minTrendBars)))
stallBars = max(1, min(8, round(exhaustionStallBars)))
// TREND always needs a little more progress than FORMING
effTrendUnits = max(trendUnits, formingUnits + 0.1)
// Exact ties on a threshold (an extension of exactly 2.00 units, a giveback of exactly 0.35)
// count the way the rule is written (>=, <=), not the way floating-point rounding falls
epsU = 0.000000001 // tolerance in scale units
epsP = ticksize * 0.000001 // tolerance in price
// State that persists from bar to bar
ONCE regime = 0 // +1 bullish leg, -1 bearish leg, 0 scanning or transition, 2 balance
ONCE transActive = 0
ONCE transBars = 0
ONCE transDir = 0 // after a structure failure, the only direction allowed to replace the leg
ONCE trendPhase = 0 // 1 DRIVE, 2 FORMING, 3 TREND
ONCE legPhase = 0 // 0 drive, 1 giveback, 2 recovery
ONCE legBars = 0
ONCE birthClose = 0
ONCE scaleAtBirth = 0
ONCE bestClose = 0
ONCE protLow = 0
ONCE protHigh = 0
ONCE pbActive = 0
ONCE pbExtreme = 0
ONCE pbRefClose = 0
ONCE barsSinceBest = 0
ONCE lastMilestone = 0
ONCE exLatched = 0
ONCE exRef = 0
ONCE exRefOk = 0
ONCE exStatus = 0 // 0 -, 1 CLEAR, 2 CLEARED, 3 DRIVE STALL, 4 EXHAUSTION WATCH, 5 STRUCTURE FAILED
ONCE lockBalHigh = 0
ONCE lockBalLow = 0
ONCE lastEvent = 0
ONCE balanceBars = 0
ONCE balanceAge = 0
ONCE barsInSeg = 0
// --- Gap handling: on intraday charts a material time gap starts a new segment
tfSec = GetTimeFrame
rawGap = 0
gapSec = 0
IF barindex > 0 THEN
gapSec = max(0, opentimestamp - timestamp[1])
IF tfSec < 86400 AND opentimestamp > timestamp[1] THEN
rawGap = 1
ENDIF
ENDIF
materialGap = 0
IF rawGap = 1 AND gapSec > tfSec * gapMult THEN
materialGap = 1
ENDIF
segStart = 0
IF barindex = 0 OR (resetOnGap = 1 AND materialGap = 1) THEN
segStart = 1
ENDIF
// --- Movement scale: median of the true range (a gap bar only counts its own high-low)
// or of the close-to-close travel (samples that span a gap are left out)
IF barindex = 0 OR rawGap = 1 THEN
rangeS = high - low
ELSE
rangeS = max(high - low, max(abs(high - close[1]), abs(low - close[1])))
ENDIF
rangeS = max(rangeS, ticksize)
travelOk = 0
travelS = 0
IF barindex > 0 AND rawGap = 0 THEN
travelOk = 1
travelS = max(abs(close - close[1]), ticksize)
ENDIF
medOk = 0
medV = 0
IF barindex >= scaleLen - 1 THEN
nW = 0
FOR jw = 0 TO scaleLen - 1 DO
IF scaleMode = 1 THEN
IF travelOk[jw] = 1 THEN
$wq[nW] = travelS[jw]
nW = nW + 1
ENDIF
ELSE
$wq[nW] = rangeS[jw]
nW = nW + 1
ENDIF
NEXT
IF nW > 0 THEN
// nearest-rank median: the ceil(N/2)-th smallest value (Wirth's selection)
kSel = floor((nW + 1) / 2) - 1
loQ = 0
hiQ = nW - 1
WHILE loQ < hiQ DO
pvQ = $wq[kSel]
iQ = loQ
jQ = hiQ
WHILE iQ <= jQ DO
WHILE $wq[iQ] < pvQ DO
iQ = iQ + 1
WEND
WHILE pvQ < $wq[jQ] DO
jQ = jQ - 1
WEND
IF iQ <= jQ THEN
tmpQ = $wq[iQ]
$wq[iQ] = $wq[jQ]
$wq[jQ] = tmpQ
iQ = iQ + 1
jQ = jQ - 1
ENDIF
WEND
IF jQ < kSel THEN
loQ = iQ
ENDIF
IF kSel < iQ THEN
hiQ = jQ
ENDIF
WEND
medV = $wq[kSel]
medOk = 1
ENDIF
ENDIF
// while the median warms up, the current bar range is used
IF medOk = 1 AND medV > ticksize + epsP THEN
safeScale = medV
ELSE
safeScale = rangeS
ENDIF
safeScale = max(safeScale, ticksize)
// --- Local references: completed bars only, never across the segment boundary
IF segStart = 1 THEN
avail = 0
ELSE
avail = barsInSeg
ENDIF
nBrk = min(breakWin, avail)
nPrt = min(protWin, avail)
nTrg = min(trigWin, avail)
nBal = min(balWin, avail)
nRef = max(max(nBrk, nPrt), max(nTrg, nBal))
runHi = 0
runLo = 0
brkHi = 0
brkLo = 0
prtHi = 0
prtLo = 0
trgHi = 0
trgLo = 0
balHi = 0
balLo = 0
IF nRef > 0 THEN
FOR jr = 1 TO nRef DO
IF jr = 1 THEN
runHi = high[1]
runLo = low[1]
ELSE
runHi = max(runHi, high[jr])
runLo = min(runLo, low[jr])
ENDIF
IF jr = nBrk THEN
brkHi = runHi
brkLo = runLo
ENDIF
IF jr = nPrt THEN
prtHi = runHi
prtLo = runLo
ENDIF
IF jr = nTrg THEN
trgHi = runHi
trgLo = runLo
ENDIF
IF jr = nBal THEN
balHi = runHi
balLo = runLo
ENDIF
NEXT
ENDIF
// close-to-close path of the drive and balance windows
nDrvP = min(driveWin, avail)
nBalP = min(balWin, avail)
nPath = max(nDrvP, nBalP)
runPath = 0
drivePath = 0
balPath = 0
IF nPath > 0 THEN
FOR jp = 0 TO nPath - 1 DO
runPath = runPath + abs(close[jp] - close[jp + 1])
IF jp + 1 = nDrvP THEN
drivePath = runPath
ENDIF
IF jp + 1 = nBalP THEN
balPath = runPath
ENDIF
NEXT
ENDIF
// --- Drive birth: displacement, a clear break of prior structure and efficiency must agree
driveNetOk = 0
driveNet = 0
IF avail >= driveWin THEN
driveNetOk = 1
driveNet = close - close[driveWin]
ENDIF
dirEff = 0
IF driveNetOk = 1 AND drivePath > ticksize + epsP THEN
dirEff = driveNet / drivePath
ENDIF
birthNeed = max(driveWin, max(breakWin, protWin))
birthReady = 0
IF avail >= birthNeed AND driveNetOk = 1 AND drivePath > ticksize + epsP THEN
birthReady = 1
ENDIF
clearance = safeScale * breakClearanceUnits
bullBirthRaw = 0
IF segStart = 0 AND birthReady = 1 AND nBrk > 0 AND close > brkHi + clearance + epsP AND driveNet >= safeScale * birthMoveUnits - epsP AND dirEff >= birthEfficiency - epsU AND close > open THEN
bullBirthRaw = 1
ENDIF
bearBirthRaw = 0
IF segStart = 0 AND birthReady = 1 AND nBrk > 0 AND close < brkLo - clearance - epsP AND driveNet <= epsP - safeScale * birthMoveUnits AND dirEff <= epsU - birthEfficiency AND close < open THEN
bearBirthRaw = 1
ENDIF
// opposite legs need a little more displacement in Structure Confirmed mode
bullRevRaw = 0
IF bullBirthRaw = 1 AND driveNet >= safeScale * (birthMoveUnits + reversalBuffer) - epsP THEN
bullRevRaw = 1
ENDIF
bearRevRaw = 0
IF bearBirthRaw = 1 AND driveNet <= epsP - safeScale * (birthMoveUnits + reversalBuffer) THEN
bearRevRaw = 1
ENDIF
// --- Balance: wide enough, inefficient, centred, for several bars in a row
balWidth = balHi - balLo
balMid = (balHi + balLo) * 0.5
balNetOk = 0
balNet = 0
IF avail >= balWin THEN
balNetOk = 1
balNet = abs(close - close[balWin])
ENDIF
balEff = 1.0
IF balNetOk = 1 AND balPath > ticksize + epsP THEN
balEff = balNet / balPath
ENDIF
balanceRaw = 0
IF segStart = 0 AND avail >= balWin AND nBal > 0 AND balWidth >= safeScale * balanceWidthUnits - epsP AND balPath > ticksize + epsP AND balWidth > epsP AND abs(close - balMid) <= balWidth * 0.35 + epsP AND balEff <= balanceEfficiency + epsU THEN
balanceRaw = 1
ENDIF
IF balanceRaw = 1 THEN
curBalRun = balanceBars + 1
ELSE
curBalRun = 0
ENDIF
balConfirmed = 0
IF curBalRun >= balPersist THEN
balConfirmed = 1
ENDIF
// --- One-bar events
bullBirthEv = 0
bearBirthEv = 0
bullFailEv = 0
bearFailEv = 0
balBirthEv = 0
balReleaseEv = 0
bullFormEv = 0
bearFormEv = 0
bullTrendEv = 0
bearTrendEv = 0
bullContEv = 0
bearContEv = 0
exhaustEv = 0
milestoneEv = 0
bullFailPrice = 0
bearFailPrice = 0
exhaustPrice = 0
impulseOk = 0
impulse = 0
bullLost = 0
IF regime = 1 AND close < protLow THEN
bullLost = 1
ENDIF
bearLost = 0
IF regime = -1 AND close > protHigh THEN
bearLost = 1
ENDIF
IF reversalMode = 1 THEN
bullOpp = bullBirthRaw
bearOpp = bearBirthRaw
ELSE
bullOpp = bullRevRaw
bearOpp = bearRevRaw
ENDIF
IF segStart = 1 THEN
regime = 0
trendPhase = 0
legPhase = 0
legBars = 0
pbActive = 0
barsSinceBest = 0
exLatched = 0
exRefOk = 0
exStatus = 0
balanceBars = 0
balanceAge = 0
barsInSeg = 1
transActive = 0
transBars = 0
transDir = 0
IF materialGap = 1 AND resetOnGap = 1 THEN
lastEvent = 1
ENDIF
ELSE
barsInSeg = barsInSeg + 1
balanceBars = curBalRun
oldRegime = regime
nextRegime = oldRegime
IF oldRegime = 0 THEN
// after a structure failure only the opposite direction may replace the failed leg
IF transActive = 1 AND transDir <> 0 THEN
IF transDir = 1 AND bullOpp = 1 THEN
nextRegime = 1
ELSIF transDir = -1 AND bearOpp = 1 THEN
nextRegime = -1
ELSIF balConfirmed = 1 THEN
nextRegime = 2
ENDIF
ELSE
IF bullBirthRaw = 1 THEN
nextRegime = 1
ELSIF bearBirthRaw = 1 THEN
nextRegime = -1
ELSIF balConfirmed = 1 THEN
nextRegime = 2
ENDIF
ENDIF
ELSIF oldRegime = 1 THEN
IF bearOpp = 1 AND (reversalMode = 1 OR bullLost = 1) THEN
nextRegime = -1
IF bullLost = 1 THEN
bullFailEv = 1
bullFailPrice = protLow
ENDIF
ELSIF bullLost = 1 THEN
nextRegime = 0
bullFailEv = 1
bullFailPrice = protLow
ELSIF balConfirmed = 1 THEN
nextRegime = 2
ENDIF
ELSIF oldRegime = -1 THEN
IF bullOpp = 1 AND (reversalMode = 1 OR bearLost = 1) THEN
nextRegime = 1
IF bearLost = 1 THEN
bearFailEv = 1
bearFailPrice = protHigh
ENDIF
ELSIF bearLost = 1 THEN
nextRegime = 0
bearFailEv = 1
bearFailPrice = protHigh
ELSIF balConfirmed = 1 THEN
nextRegime = 2
ENDIF
ELSIF oldRegime = 2 THEN
balBreakUp = close > lockBalHigh + safeScale * balanceBreakUnits + epsP
balBreakDn = close < lockBalLow - safeScale * balanceBreakUnits - epsP
IF bullBirthRaw = 1 THEN
nextRegime = 1
ELSIF bearBirthRaw = 1 THEN
nextRegime = -1
ELSIF balBreakUp OR balBreakDn THEN
nextRegime = 0
balReleaseEv = 1
ENDIF
ENDIF
IF nextRegime <> oldRegime THEN
regime = nextRegime
IF nextRegime = 1 THEN
transActive = 0
transBars = 0
transDir = 0
birthClose = close
scaleAtBirth = safeScale
bestClose = close
protLow = prtLo
pbActive = 0
trendPhase = 1
legPhase = 0
legBars = 1
barsSinceBest = 0
lastMilestone = close
exLatched = 0
exRefOk = 0
exStatus = 1
bullBirthEv = 1
impulse = 100 * (1 - exp(0 - max(0, (close - close[1]) / scaleAtBirth)))
impulseOk = 1
IF oldRegime = -1 THEN
lastEvent = 2
ELSIF oldRegime = 2 THEN
lastEvent = 3
ELSE
lastEvent = 4
ENDIF
IF oldRegime = 2 THEN
balReleaseEv = 1
ENDIF
ELSIF nextRegime = -1 THEN
transActive = 0
transBars = 0
transDir = 0
birthClose = close
scaleAtBirth = safeScale
bestClose = close
protHigh = prtHi
pbActive = 0
trendPhase = 1
legPhase = 0
legBars = 1
barsSinceBest = 0
lastMilestone = close
exLatched = 0
exRefOk = 0
exStatus = 1
bearBirthEv = 1
impulse = 100 * (1 - exp(0 - max(0, (close[1] - close) / scaleAtBirth)))
impulseOk = 1
IF oldRegime = 1 THEN
lastEvent = 5
ELSIF oldRegime = 2 THEN
lastEvent = 6
ELSE
lastEvent = 7
ENDIF
IF oldRegime = 2 THEN
balReleaseEv = 1
ENDIF
ELSIF nextRegime = 2 THEN
transActive = 0
transBars = 0
transDir = 0
lockBalHigh = balHi
lockBalLow = balLo
pbActive = 0
trendPhase = 0
legPhase = 0
exLatched = 0
exRefOk = 0
exStatus = 0
balBirthEv = 1
IF oldRegime = 0 THEN
lastEvent = 8
ELSE
lastEvent = 9
ENDIF
ELSE
trendPhase = 0
legPhase = 0
pbActive = 0
IF bullFailEv = 1 THEN
exLatched = 1
exStatus = 5
lastEvent = 10
transActive = 1
transBars = 1
transDir = -1
ELSIF bearFailEv = 1 THEN
exLatched = 1
exStatus = 5
lastEvent = 11
transActive = 1
transBars = 1
transDir = 1
ELSE
exStatus = 0
IF oldRegime = 2 THEN
lastEvent = 12
transActive = 1
transBars = 1
transDir = 0
ENDIF
ENDIF
ENDIF
ELSE
regime = nextRegime
IF regime = 0 AND transActive = 1 THEN
transBars = transBars + 1
ENDIF
IF regime = 1 OR regime = -1 THEN
legBars = legBars + 1
phaseStart = trendPhase
priorBest = bestClose
madeNew = 0
IF regime = 1 AND close > priorBest THEN
madeNew = 1
ENDIF
IF regime = -1 AND close < priorBest THEN
madeNew = 1
ENDIF
contTrig = 0
// continuation: a meaningful pullback, then a close through a completed local reference
IF pbActive = 1 THEN
IF regime = 1 THEN
candExt = min(pbExtreme, low)
ELSE
candExt = max(pbExtreme, high)
ENDIF
pbDepth = regime * (pbRefClose - candExt) / scaleAtBirth
reclaim = 0
IF regime = 1 AND nTrg > 0 AND close > trgHi THEN
reclaim = 1
ENDIF
IF regime = -1 AND nTrg > 0 AND close < trgLo THEN
reclaim = 1
ENDIF
IF pbDepth >= minPullbackUnits - epsU AND reclaim = 1 THEN
contTrig = 1
IF regime = 1 THEN
bullContEv = 1
lastEvent = 13
ELSE
bearContEv = 1
lastEvent = 14
ENDIF
pbActive = 0
legPhase = 0
ELSE
pbExtreme = candExt
toward = 0
IF regime = 1 AND close > close[1] THEN
toward = 1
ENDIF
IF regime = -1 AND close < close[1] THEN
toward = 1
ENDIF
IF legPhase = 1 AND toward = 1 THEN
legPhase = 2
IF madeNew = 0 THEN
lastEvent = 15
ENDIF
ENDIF
ENDIF
ELSIF madeNew = 0 THEN
adverse = 0
IF regime = 1 AND close < close[1] THEN
adverse = 1
ENDIF
IF regime = -1 AND close > close[1] THEN
adverse = 1
ENDIF
IF adverse = 1 THEN
pbActive = 1
IF regime = 1 THEN
pbExtreme = low
ELSE
pbExtreme = high
ENDIF
pbRefClose = bestClose
ENDIF
ENDIF
// accepted progress uses closes, not wick-only extremes
IF madeNew = 1 THEN
IF pbActive = 1 AND contTrig = 0 THEN
pbActive = 0
ENDIF
newExt = max(0, regime * (close - priorBest) / scaleAtBirth)
impulse = 100 * (1 - exp(0 - newExt))
impulseOk = 1
IF regime * (close - lastMilestone) >= scaleAtBirth * progressMilestoneUnits - epsP THEN
milestoneEv = 1
lastMilestone = close
ENDIF
IF exLatched = 1 AND exRefOk = 1 THEN
IF regime * (close - exRef) / scaleAtBirth >= exhaustionRearmUnits - epsU THEN
exLatched = 0
exRefOk = 0
exStatus = 2
lastEvent = 16
ENDIF
ENDIF
bestClose = close
barsSinceBest = 0
IF legPhase <> 0 THEN
legPhase = 0
IF bullContEv = 0 AND bearContEv = 0 THEN
lastEvent = 17
ENDIF
ENDIF
ELSE
barsSinceBest = barsSinceBest + 1
gbLocal = max(0, regime * (priorBest - close) / scaleAtBirth)
IF contTrig = 0 AND legPhase = 0 AND gbLocal >= givebackPhaseUnits - epsU THEN
legPhase = 1
lastEvent = 18
ENDIF
ENDIF
// FORMING and TREND are progress thresholds, not time-based labels
extFromBirth = regime * (bestClose - birthClose) / scaleAtBirth
IF trendPhase = 1 AND extFromBirth >= formingUnits - epsU THEN
trendPhase = 2
IF regime = 1 THEN
bullFormEv = 1
IF bullContEv = 0 AND bearContEv = 0 THEN
lastEvent = 19
ENDIF
ELSE
bearFormEv = 1
IF bullContEv = 0 AND bearContEv = 0 THEN
lastEvent = 20
ENDIF
ENDIF
ELSIF trendPhase = 2 AND legBars >= trendBarsMin AND extFromBirth >= effTrendUnits - epsU THEN
trendPhase = 3
IF regime = 1 THEN
bullTrendEv = 1
IF bullContEv = 0 AND bearContEv = 0 THEN
lastEvent = 21
ENDIF
ELSE
bearTrendEv = 1
IF bullContEv = 0 AND bearContEv = 0 THEN
lastEvent = 22
ENDIF
ENDIF
ENDIF
// exhaustion is a latched warning: giveback plus a failed extension or a stall
IF madeNew = 0 AND exLatched = 0 AND contTrig = 0 THEN
curGb = max(0, regime * (priorBest - close) / scaleAtBirth)
failedExt = 0
IF regime = 1 AND high > priorBest + scaleAtBirth * failedAttemptBuffer + epsP AND close <= priorBest THEN
failedExt = 1
ENDIF
IF regime = -1 AND low < priorBest - scaleAtBirth * failedAttemptBuffer - epsP AND close >= priorBest THEN
failedExt = 1
ENDIF
IF phaseStart = 1 THEN
thrGb = driveExhaustionUnits
ELSE
thrGb = trendExhaustionUnits
ENDIF
IF curGb >= thrGb - epsU AND (failedExt = 1 OR barsSinceBest >= stallBars) THEN
exhaustEv = 1
exLatched = 1
exRef = priorBest
exRefOk = 1
IF phaseStart = 1 THEN
exStatus = 3
lastEvent = 23
ELSE
exStatus = 4
lastEvent = 24
ENDIF
IF regime = 1 THEN
exhaustPrice = high
ELSE
exhaustPrice = low
ENDIF
ENDIF
ENDIF
IF exStatus = 2 AND exhaustEv = 0 THEN
exStatus = 1
ENDIF
ENDIF
ENDIF
IF regime = 2 THEN
IF oldRegime = 2 THEN
balanceAge = balanceAge + 1
ELSE
balanceAge = 1
ENDIF
ELSE
balanceAge = 0
ENDIF
ENDIF
// ===================== END OF STATE ENGINE =====================
// ===================== PRICE CONTEXT ENVELOPES AND PHASE BANDS =====================
// Every box lives in arrays (left, right, top, bottom, type, direction, phase). The
// boxes are only written by plain assignment at an index held in a scalar counter, so
// the open candle can be recalculated on every tick without piling up copies.
ONCE actKind = 0
ONCE actKey = -999
ONCE hasCont = 0
ONCE hasFill = 0
ONCE contIdx = 0
ONCE fillIdx = 0
ONCE grpFirst = 0
ONCE nBox = 0
ONCE nGrp = 0
ONCE legTop = 0
ONCE legBot = 0
ONCE phTop = 0
ONCE phBot = 0
ONCE nMk = 0
balVisPh = 0
IF regime = 2 THEN
IF balanceAge >= balMature THEN
balVisPh = 3
ELSE
balVisPh = 2
ENDIF
ENDIF
// 3 = dashed transition frame, drawn after two confirmed neutral bars
IF regime = 0 AND transActive = 1 AND transBars >= 2 THEN
visKind = 3
ELSE
visKind = regime
ENDIF
visPhase = 0
IF regime = 1 OR regime = -1 THEN
visPhase = trendPhase
ELSIF regime = 2 THEN
visPhase = balVisPh
ENDIF
visKey = 0
IF visKind = 1 THEN
visKey = 10 + visPhase
ELSIF visKind = -1 THEN
visKey = -10 - visPhase
ELSIF visKind = 2 THEN
visKey = 2
ELSIF visKind = 3 THEN
visKey = 3
ENDIF
closeCtx = 0
IF segStart = 1 OR (hasCont = 1 AND visKind <> actKind) THEN
closeCtx = 1
ENDIF
// a finished leg, balance or transition: freeze its outer frame and its bands as one group
IF closeCtx = 1 AND hasCont = 1 THEN
$bR[contIdx] = barindex
IF hasFill = 1 THEN
$bR[fillIdx] = barindex
ENDIF
$gFirst[nGrp] = grpFirst
$gSize[nGrp] = nBox - grpFirst
nGrp = nGrp + 1
hasCont = 0
hasFill = 0
actKind = 0
actKey = -999
ENDIF
IF hasCont = 1 AND visKind = actKind AND visKind <> 0 THEN
// the outer frame keeps the full high-low range of the leg or balance
legTop = max(legTop, high)
legBot = min(legBot, low)
$bR[contIdx] = barindex + 1
$bT[contIdx] = legTop
$bB[contIdx] = legBot
IF visKind = 1 OR visKind = -1 THEN
IF visKey <> actKey THEN
// new phase: freeze the previous band and start a new one
IF hasFill = 1 THEN
$bR[fillIdx] = barindex
ENDIF
phTop = high
phBot = low
inset = min(ticksize, max((phTop - phBot) * 0.005, 0))
$bL[nBox] = barindex
$bR[nBox] = barindex + 1
$bT[nBox] = phTop - inset
$bB[nBox] = phBot + inset
$bTy[nBox] = 1
$bK[nBox] = visKind
$bP[nBox] = visPhase
fillIdx = nBox
hasFill = 1
nBox = nBox + 1
actKey = visKey
ELSIF hasFill = 1 THEN
phTop = max(phTop, high)
phBot = min(phBot, low)
inset = min(ticksize, max((phTop - phBot) * 0.005, 0))
$bR[fillIdx] = barindex + 1
$bT[fillIdx] = phTop - inset
$bB[fillIdx] = phBot + inset
ENDIF
ELSIF visKind = 2 AND hasFill = 1 THEN
phTop = legTop
phBot = legBot
inset = min(ticksize, max((legTop - legBot) * 0.005, 0))
$bR[fillIdx] = barindex + 1
$bT[fillIdx] = legTop - inset
$bB[fillIdx] = legBot + inset
$bP[fillIdx] = balVisPh
ENDIF
ELSIF visKind <> 0 AND segStart = 0 THEN
// a new context starts on this bar
legTop = high
legBot = low
phTop = high
phBot = low
$bL[nBox] = barindex
$bR[nBox] = barindex + 1
$bT[nBox] = high
$bB[nBox] = low
$bTy[nBox] = 0
$bK[nBox] = visKind
$bP[nBox] = 0
contIdx = nBox
grpFirst = nBox
hasCont = 1
nBox = nBox + 1
IF visKind = 3 THEN
hasFill = 0
ELSE
inset = min(ticksize, max((phTop - phBot) * 0.005, 0))
$bL[nBox] = barindex
$bR[nBox] = barindex + 1
$bT[nBox] = phTop - inset
$bB[nBox] = phBot + inset
$bTy[nBox] = 1
$bK[nBox] = visKind
IF visKind = 2 THEN
$bP[nBox] = balVisPh
ELSE
$bP[nBox] = visPhase
ENDIF
fillIdx = nBox
hasFill = 1
nBox = nBox + 1
ENDIF
actKind = visKind
actKey = visKey
ELSE
actKind = visKind
actKey = -999
ENDIF
// ===================== PRICE-CHART EVENTS =====================
// 1 bullish drive birth, 2 bearish drive birth, 3 bullish structure failure,
// 4 bearish structure failure, 5 balance formed, 6 exhaustion watch
IF bullBirthEv = 1 THEN
$mkB[nMk] = barindex
$mkT[nMk] = 1
$mkY[nMk] = low
nMk = nMk + 1
ENDIF
IF bearBirthEv = 1 THEN
$mkB[nMk] = barindex
$mkT[nMk] = 2
$mkY[nMk] = high
nMk = nMk + 1
ENDIF
IF bullFailEv = 1 THEN
$mkB[nMk] = barindex
$mkT[nMk] = 3
$mkY[nMk] = bullFailPrice
nMk = nMk + 1
ENDIF
IF bearFailEv = 1 THEN
$mkB[nMk] = barindex
$mkT[nMk] = 4
$mkY[nMk] = bearFailPrice
nMk = nMk + 1
ENDIF
IF balBirthEv = 1 THEN
$mkB[nMk] = barindex
$mkT[nMk] = 5
$mkY[nMk] = high
nMk = nMk + 1
ENDIF
IF exhaustEv = 1 THEN
$mkB[nMk] = barindex
$mkT[nMk] = 6
$mkY[nMk] = exhaustPrice
nMk = nMk + 1
ENDIF
// ===================== DRAWING (last bar) =====================
IF islastbarupdate THEN
// chart colours (light or dark background)
IF darkChart = 1 THEN
bdR = 165
bdG = 255
bdB = 207
bfR = 0
bfG = 205
bfB = 118
sdR = 255
sdG = 190
sdB = 198
yeR = 255
yeG = 214
yeB = 64
fbR = 66
fbG = 220
fbB = 151
ELSE
bdR = 36
bdG = 168
bdB = 118
bfR = 0
bfG = 150
bfB = 86
sdR = 236
sdG = 104
sdB = 124
yeR = 196
yeG = 128
yeB = 0
fbR = 20
fbG = 166
fbB = 104
ENDIF
// History budget: the newest finished groups that fit, oldest removed first
maxObj = min(460, max(40, round(keepSegments) * 4))
firstBox = 0
IF nGrp > 0 THEN
usedObj = 0
firstGrp = nGrp
FOR gq = nGrp - 1 DOWNTO 0 DO
IF usedObj + $gSize[gq] <= maxObj THEN
usedObj = usedObj + $gSize[gq]
firstGrp = gq
ELSE
BREAK
ENDIF
NEXT
IF firstGrp < nGrp THEN
firstBox = $gFirst[firstGrp]
ELSE
lastG = nGrp - 1
firstBox = $gFirst[lastG] + $gSize[lastG]
ENDIF
ENDIF
IF nBox > firstBox THEN
FOR bq = firstBox TO nBox - 1 DO
bxL = $bL[bq]
bxR = $bR[bq]
bxT = $bT[bq]
bxB = $bB[bq]
bxK = $bK[bq]
bxP = $bP[bq]
IF $bTy[bq] = 0 THEN
// outer frame: solid for a leg or a balance, dashed for a transition
IF bxK = 1 THEN
DRAWRECTANGLE(bxL, bxT, bxR, bxB) COLOURED(fbR, fbG, fbB, 255) FILLCOLOR(16, 23, 32, 0)
ELSIF bxK = -1 THEN
DRAWRECTANGLE(bxL, bxT, bxR, bxB) COLOURED(255, 77, 99, 255) FILLCOLOR(16, 23, 32, 0)
ELSIF bxK = 2 THEN
DRAWRECTANGLE(bxL, bxT, bxR, bxB) COLOURED(132, 145, 160, 255) FILLCOLOR(16, 23, 32, 0)
ELSE
DRAWRECTANGLE(bxL, bxT, bxR, bxB) COLOURED(132, 145, 160, 255) STYLE(dottedline2, 1) FILLCOLOR(16, 23, 32, 0)
ENDIF
ELSE
// inner band: one per DRIVE, FORMING or TREND phase; grey for a balance
IF bxK = 2 THEN
fR = 132
fG = 145
fB = 160
IF bxP = 3 THEN
fA = 61
ELSE
fA = 36
ENDIF
ELSIF bxK = 1 THEN
IF bxP = 1 THEN
fR = bdR
fG = bdG
fB = bdB
fA = 54
ELSIF bxP = 2 THEN
fR = bfR
fG = bfG
fB = bfB
fA = 59
ELSE
fR = 0
fG = 118
fB = 72
fA = 66
ENDIF
ELSE
IF bxP = 1 THEN
fR = sdR
fG = sdG
fB = sdB
fA = 54
ELSIF bxP = 2 THEN
fR = 245
fG = 42
fB = 70
fA = 59
ELSE
fR = 170
fG = 8
fB = 39
fA = 66
ENDIF
ENDIF
DRAWRECTANGLE(bxL, bxT, bxR, bxB) COLOURED(fR, fG, fB, 0) FILLCOLOR(fR, fG, fB, fA)
ENDIF
NEXT
ENDIF
// Event markers: circle = drive birth, cross = protected structure lost,
// square = balance formed, yellow diamond with halo = exhaustion watch
IF showPriceEvents = 1 AND nMk > 0 THEN
FOR mq = 0 TO nMk - 1 DO
mkX = $mkB[mq]
mkY = $mkY[mq]
mkT = $mkT[mq]
IF mkT = 1 THEN
DRAWPOINT(mkX, mkY, 2) COLOURED(bdR, bdG, bdB, 255)
ELSIF mkT = 2 THEN
DRAWPOINT(mkX, mkY, 2) COLOURED(sdR, sdG, sdB, 255)
ELSIF mkT = 3 THEN
DRAWTEXT("✕", mkX, mkY) COLOURED(170, 8, 39, 255)
ELSIF mkT = 4 THEN
DRAWTEXT("✕", mkX, mkY) COLOURED(0, 118, 72, 255)
ELSIF mkT = 5 THEN
DRAWTEXT("■", mkX, mkY) COLOURED(132, 145, 160, 255)
ELSE
DRAWPOINT(mkX, mkY, 3) COLOURED(yeR, yeG, yeB, 31)
DRAWPOINT(mkX, mkY, 3) COLOURED(yeR, yeG, yeB, 97)
DRAWTEXT("◆", mkX, mkY) COLOURED(yeR, yeG, yeB, 255)
ENDIF
NEXT
ENDIF
// ===================== SCANNER =====================
// Fixed in the top-right corner (the corner of ANCHOR cannot be a variable).
// The original shows the latest event as a tooltip of SCAN: here it is the last row.
IF showDashboard = 1 THEN
xR = -90
x3 = xR - 70
x2 = x3 - 85
x1 = x2 - 65
x0 = x1 - 90
c0X = (x0 + x1) / 2
c1X = (x1 + x2) / 2
c2X = (x2 + x3) / 2
c3X = (x3 + xR) / 2
mX = (x1 + xR) / 2
hX = (x0 + xR) / 2
// frame and header
DRAWRECTANGLE(x0, -10, xR, -130) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(132, 145, 160, 115) FILLCOLOR(16, 23, 32, 255)
DRAWRECTANGLE(x0, -10, xR, -30) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(132, 145, 160, 41) FILLCOLOR(29, 40, 52, 255)
DRAWTEXT("SCANNER", hX, -20, sansserif, bold, 10) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
// row 1: PHASE (confirmed direction)
IF regime = 1 THEN
vR = 0
vG = 205
vB = 118
ELSIF regime = -1 THEN
vR = 245
vG = 42
vB = 70
ELSE
vR = 132
vG = 145
vB = 160
ENDIF
DRAWRECTANGLE(x0, -30, x1, -50) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(132, 145, 160, 41) FILLCOLOR(16, 23, 32, 0)
DRAWRECTANGLE(x1, -30, xR, -50) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(132, 145, 160, 41) FILLCOLOR(vR, vG, vB, 51)
DRAWTEXT("PHASE", c0X, -40, sansserif, standard, 9) COLOURED(151, 165, 181, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
IF regime = 1 THEN
DRAWTEXT("BULLISH", mX, -40, sansserif, standard, 9) COLOURED(vR, vG, vB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF regime = -1 THEN
DRAWTEXT("BEARISH", mX, -40, sansserif, standard, 9) COLOURED(vR, vG, vB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSE
DRAWTEXT("NEUTRAL", mX, -40, sansserif, standard, 9) COLOURED(vR, vG, vB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ENDIF
// row 2: SCAN (direction and phase, or balance / transition)
sR = 132
sG = 145
sB = 160
IF regime = 1 THEN
IF trendPhase = 1 THEN
sR = 165
sG = 255
sB = 207
ELSIF trendPhase = 2 THEN
sR = 0
sG = 205
sB = 118
ELSE
sR = 0
sG = 118
sB = 72
ENDIF
ELSIF regime = -1 THEN
IF trendPhase = 1 THEN
sR = 255
sG = 190
sB = 198
ELSIF trendPhase = 2 THEN
sR = 245
sG = 42
sB = 70
ELSE
sR = 170
sG = 8
sB = 39
ENDIF
ENDIF
DRAWRECTANGLE(x0, -50, x1, -70) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(132, 145, 160, 41) FILLCOLOR(16, 23, 32, 0)
DRAWRECTANGLE(x1, -50, xR, -70) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(132, 145, 160, 41) FILLCOLOR(sR, sG, sB, 46)
DRAWTEXT("SCAN", c0X, -60, sansserif, standard, 9) COLOURED(151, 165, 181, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
IF regime = 1 THEN
IF trendPhase = 1 THEN
DRAWTEXT("BULLISH DRIVE", mX, -60, sansserif, standard, 9) COLOURED(sR, sG, sB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF trendPhase = 2 THEN
DRAWTEXT("BULLISH FORMING", mX, -60, sansserif, standard, 9) COLOURED(sR, sG, sB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSE
DRAWTEXT("BULLISH TREND", mX, -60, sansserif, standard, 9) COLOURED(sR, sG, sB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ENDIF
ELSIF regime = -1 THEN
IF trendPhase = 1 THEN
DRAWTEXT("BEARISH DRIVE", mX, -60, sansserif, standard, 9) COLOURED(sR, sG, sB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF trendPhase = 2 THEN
DRAWTEXT("BEARISH FORMING", mX, -60, sansserif, standard, 9) COLOURED(sR, sG, sB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSE
DRAWTEXT("BEARISH TREND", mX, -60, sansserif, standard, 9) COLOURED(sR, sG, sB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ENDIF
ELSIF regime = 2 THEN
DRAWTEXT("BALANCE", mX, -60, sansserif, standard, 9) COLOURED(sR, sG, sB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF transActive = 1 THEN
DRAWTEXT("TRANSITION · SCANNING", mX, -60, sansserif, standard, 9) COLOURED(sR, sG, sB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSE
DRAWTEXT("SCANNING", mX, -60, sansserif, standard, 9) COLOURED(sR, sG, sB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ENDIF
// row 3: EXHAUSTION (latched warning, or the state of the pullback cycle)
// eTx: 0 -, 1 status, 2 PULLBACK ACTIVE, 3 GIVEBACK, 4 RECOVERY, 5 NONE, 6 BALANCE
eTx = 0
eR = 151
eG = 165
eB = 181
IF regime = 1 OR regime = -1 THEN
IF exLatched = 1 THEN
eTx = 1
IF exStatus = 5 THEN
eR = 170
eG = 8
eB = 39
ELSE
eR = 255
eG = 214
eB = 64
ENDIF
ELSIF pbActive = 1 THEN
eTx = 2
eR = 132
eG = 145
eB = 160
ELSIF legPhase = 1 THEN
eTx = 3
eR = 132
eG = 145
eB = 160
ELSIF legPhase = 2 THEN
eTx = 4
eR = sR
eG = sG
eB = sB
ELSE
eTx = 5
ENDIF
ELSIF exStatus = 5 THEN
eTx = 1
eR = 170
eG = 8
eB = 39
ELSIF regime = 2 THEN
eTx = 6
eR = 132
eG = 145
eB = 160
ENDIF
DRAWRECTANGLE(x0, -70, x1, -90) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(132, 145, 160, 41) FILLCOLOR(16, 23, 32, 0)
DRAWRECTANGLE(x1, -70, xR, -90) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(132, 145, 160, 41) FILLCOLOR(eR, eG, eB, 41)
DRAWTEXT("EXHAUSTION", c0X, -80, sansserif, standard, 9) COLOURED(151, 165, 181, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
IF eTx = 1 THEN
IF exStatus = 1 THEN
DRAWTEXT("CLEAR", mX, -80, sansserif, standard, 9) COLOURED(eR, eG, eB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF exStatus = 2 THEN
DRAWTEXT("CLEARED", mX, -80, sansserif, standard, 9) COLOURED(eR, eG, eB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF exStatus = 3 THEN
DRAWTEXT("DRIVE STALL", mX, -80, sansserif, standard, 9) COLOURED(eR, eG, eB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF exStatus = 4 THEN
DRAWTEXT("EXHAUSTION WATCH", mX, -80, sansserif, standard, 9) COLOURED(eR, eG, eB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF exStatus = 5 THEN
DRAWTEXT("STRUCTURE FAILED", mX, -80, sansserif, standard, 9) COLOURED(eR, eG, eB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSE
DRAWTEXT("-", mX, -80, sansserif, standard, 9) COLOURED(eR, eG, eB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ENDIF
ELSIF eTx = 2 THEN
DRAWTEXT("PULLBACK ACTIVE", mX, -80, sansserif, standard, 9) COLOURED(eR, eG, eB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF eTx = 3 THEN
DRAWTEXT("GIVEBACK", mX, -80, sansserif, standard, 9) COLOURED(eR, eG, eB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF eTx = 4 THEN
DRAWTEXT("RECOVERY", mX, -80, sansserif, standard, 9) COLOURED(eR, eG, eB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF eTx = 5 THEN
DRAWTEXT("NONE", mX, -80, sansserif, standard, 9) COLOURED(eR, eG, eB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF eTx = 6 THEN
DRAWTEXT("BALANCE", mX, -80, sansserif, standard, 9) COLOURED(eR, eG, eB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSE
DRAWTEXT("-", mX, -80, sansserif, standard, 9) COLOURED(eR, eG, eB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ENDIF
// row 4: EXTENSION from the birth close and GIVEBACK from the best accepted close,
// both in units of the movement scale frozen at the leg birth
DRAWRECTANGLE(x0, -90, x1, -110) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(132, 145, 160, 41) FILLCOLOR(16, 23, 32, 0)
DRAWRECTANGLE(x1, -90, x2, -110) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(132, 145, 160, 41) FILLCOLOR(16, 23, 32, 0)
DRAWRECTANGLE(x2, -90, x3, -110) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(132, 145, 160, 41) FILLCOLOR(16, 23, 32, 0)
DRAWRECTANGLE(x3, -90, xR, -110) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(132, 145, 160, 41) FILLCOLOR(16, 23, 32, 0)
DRAWTEXT("EXTENSION", c0X, -100, sansserif, standard, 9) COLOURED(151, 165, 181, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
DRAWTEXT("GIVEBACK", c2X, -100, sansserif, standard, 9) COLOURED(151, 165, 181, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
IF regime = 1 OR regime = -1 THEN
extU = regime * (close - birthClose) / scaleAtBirth
gbU = max(0, regime * (bestClose - close) / scaleAtBirth)
extV = round(extU * 100) / 100
gbV = round(gbU * 100) / 100
IF extU < 0 THEN
xtR = 170
xtG = 8
xtB = 39
ELSE
xtR = sR
xtG = sG
xtB = sB
ENDIF
DRAWTEXT("#extV#×", c1X, -100, sansserif, standard, 9) COLOURED(xtR, xtG, xtB, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
IF gbU > 0 THEN
DRAWTEXT("#gbV#×", c3X, -100, sansserif, standard, 9) COLOURED(132, 145, 160, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSE
DRAWTEXT("#gbV#×", c3X, -100, sansserif, standard, 9) COLOURED(151, 165, 181, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ENDIF
ELSE
DRAWTEXT("-", c1X, -100, sansserif, standard, 9) COLOURED(132, 145, 160, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
DRAWTEXT("-", c3X, -100, sansserif, standard, 9) COLOURED(151, 165, 181, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ENDIF
// row 5: latest confirmed event
DRAWRECTANGLE(x0, -110, x1, -130) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(132, 145, 160, 41) FILLCOLOR(16, 23, 32, 0)
DRAWRECTANGLE(x1, -110, xR, -130) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(132, 145, 160, 41) FILLCOLOR(16, 23, 32, 0)
DRAWTEXT("LAST EVENT", c0X, -120, sansserif, standard, 9) COLOURED(151, 165, 181, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
IF lastEvent = 1 THEN
DRAWTEXT("INTRADAY GAP RESET", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 2 THEN
DRAWTEXT("BULLISH REVERSAL", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 3 THEN
DRAWTEXT("BALANCE BREAK UP", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 4 THEN
DRAWTEXT("BULLISH DRIVE", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 5 THEN
DRAWTEXT("BEARISH REVERSAL", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 6 THEN
DRAWTEXT("BALANCE BREAK DOWN", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 7 THEN
DRAWTEXT("BEARISH DRIVE", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 8 THEN
DRAWTEXT("BALANCE FORMED", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 9 THEN
DRAWTEXT("LEG ENTERED BALANCE", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 10 THEN
DRAWTEXT("BULL STRUCTURE FAILED", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 11 THEN
DRAWTEXT("BEAR STRUCTURE FAILED", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 12 THEN
DRAWTEXT("BALANCE RELEASED", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 13 THEN
DRAWTEXT("LONG CONTINUATION TRIGGER", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 14 THEN
DRAWTEXT("SHORT CONTINUATION TRIGGER", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 15 THEN
DRAWTEXT("RECOVERY DEVELOPING", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 16 THEN
DRAWTEXT("EXHAUSTION CLEARED BY PROGRESS", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 17 THEN
DRAWTEXT("ACCEPTED PROGRESS RESUMED", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 18 THEN
DRAWTEXT("GIVEBACK STARTED", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 19 THEN
DRAWTEXT("BULLISH FORMING", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 20 THEN
DRAWTEXT("BEARISH FORMING", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 21 THEN
DRAWTEXT("BULLISH TREND", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 22 THEN
DRAWTEXT("BEARISH TREND", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 23 THEN
DRAWTEXT("DRIVE STALL", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSIF lastEvent = 24 THEN
DRAWTEXT("EXHAUSTION WATCH", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ELSE
DRAWTEXT("Waiting for valid history", mX, -120, sansserif, standard, 9) COLOURED(235, 241, 247, 255) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT)
ENDIF
ENDIF
ENDIF
RETURN