Most Elliott Wave indicators work backwards. They scan the finished chart, find the labelling that best fits the rules, and print it. The count looks impressive precisely because it was chosen after the fact — and it quietly re-labels itself as new bars arrive.
The Elliott Impulse Engine by WillyAlgoTrader takes the opposite approach. It maintains one live hypothesis at a time and commits to it. A count is seeded by a change of character in market structure, then each wave point is either accepted or the whole hypothesis is destroyed and the engine goes back to scanning. There is always a single price on the chart — the invalidation level — that tells you exactly where the current reading dies.
That design choice is what makes it useful. You are not looking at the prettiest count in hindsight; you are looking at a falsifiable claim with a stop attached to it.
This article explains the engine module by module, how to read what it draws, how to configure it, and ships the full ProBuilder code.
The engine runs two independent pivot detectors instead of one, and this separation is the backbone of the whole thing.
The long pivot (swingLen, default 10 bars each side) builds the swing structure: the running swing high, the running swing low, and whether each has been broken. This is the slow layer, and it decides when a count is allowed to begin.
The short pivot (accDepth, default 5 bars each side) does nothing but accept wave points once a count is running. This is the fast layer, and it decides where each point lands.
Using one depth for both would force an impossible compromise: a depth long enough to give meaningful structure is too slow to catch the turns inside a developing impulse, and a depth short enough to catch those turns produces a change of character on every wiggle. Splitting them resolves the conflict cleanly. accDepth should always be kept at or below swingLen.
The engine keeps a structural bias — bullish after a swing high is closed through, bearish after a swing low is closed through. A break in the same direction as the current bias is simply continuation and is ignored for seeding purposes.
A count is seeded only on a break against the standing bias. That is the change of character (CHoCH): the market had been making lower highs, and now it has closed above one. The engine marks the broken level, flips the bias, and opens a hypothesis in the new direction.
Two guards sit on this gate:
Broken structure levels stay on the chart as a history layer. They survive count resets, and each one switches to a thin dotted line at the bar where price closed back through it — the moment that level stopped mattering.
When the change of character fires, the engine needs a point 0 — the origin of the impulse. It does not go looking for one; it already has it.
While waiting, the engine continuously tracks the extreme of the swing in progress: the lowest low since the last confirmed swing high, and the highest high since the last confirmed swing low. When the seed fires, that running extreme becomes point 0, with its actual bar index. Point 0 therefore sits at a real turning point, not at the bar where the break happened.
Point 0 is still allowed to move deeper while the engine waits for point 1, which handles the case where price makes one more marginal low before the impulse truly starts.
Each wave point has an expected window measured on a Fibonacci grid:
Here is the part worth paying attention to: a point that lands outside its window is not rejected. It is accepted and labelled with a tilde — 3~ instead of 3. The count continues.
This is the right call, and it is what separates this engine from the textbook implementations. Real impulses routinely produce a wave 3 that runs to -1.2 or a wave 2 that only retraces 0.38. A tool that discards those is not measuring the market, it is measuring its own parameters. The tilde keeps the information visible without throwing the count away: you can see at a glance whether you are looking at a textbook structure or a stretched one.
The 0.705 default deserves a note. It is not a classical Fibonacci number — it is the midpoint between 0.618 and 0.786, the band commonly traded as the optimal entry zone. The default point 2 and point 4 windows are that zone, not the whole retracement range.
A wave point is provisional until the next event closes it.
The rule underneath all of these is consistent: a point stays editable until the structure that follows it has been confirmed, and then it is frozen. This is what allows the engine to hold a single hypothesis through a developing move rather than resetting on every new extreme.
There is one detail worth knowing. Because point acceptance runs on confirmed pivots, there is a lag between the true turn and its confirmation. If price breaks point 1 during that lag window, the engine sweeps back over the bars since point 1 and takes the actual extreme as point 2, rather than losing the point entirely. The count keeps its geometry.
At any moment there is exactly one price that kills the current hypothesis, drawn on the chart and shown in the panel as INVALID. It ratchets forward as the count matures:
The last one is the interesting case. During the correction the engine is not defending the impulse, it is defending the claim that what follows is a correction and not a reversal. A move that carries below point 2 means the whole structure was misread, so the count is discarded rather than relabelled.
Breaks can be confirmed two ways, set by the invalidation mode. In close mode a bar must close beyond the level; in wick mode any touch counts. Close mode produces fewer and slower resets, wick mode produces faster and more of them.
When point 5 is about to be accepted, the engine measures the three impulsive legs. If wave 3 turns out to be the shortest of the three, the count is thrown away with the reason W3 SHORTEST.
Of all the Elliott guidelines, this is the one that is genuinely a rule rather than a tendency, and it is the only one enforced as a hard reset. Everything else — retracement depth, extension targets, alternation — is expressed through the soft tilde marking instead. That is a defensible division: enforce what is structural, flag what is statistical.
After point 5 the engine tracks an A-B-C correction with the same accept-or-die logic. Point C closes the count as complete.
But there is a second, more informative ending. If point B pushes beyond point 5, the premise was wrong: this was never a correction, the impulse simply continued. The engine closes the count as complete rather than invalid, and starts looking for a fresh structure. Distinguishing “my correction hypothesis was wrong” from “my impulse hypothesis was wrong” is a small touch that says the state machine was thought through.
From the last accepted point, the engine draws a dashed path through every point still to come — 2? 3? 4? 5? A? B? C? — placing each one inside its Fibonacci window and stepping forward in time by a multiple of how long leg 0-1 took.
It is a sketch, not a forecast, and it should be read as one: it is simply the current parameters drawn forward. Its real value is spatial. It shows you where the next point is expected and roughly when, so you can see immediately whether price is tracking the hypothesis or drifting away from it. You can anchor each ghost point at the middle, the near edge or the far edge of its window.
The numbered path is the accepted count: 0-1-2-3-4-5 for the impulse, A-B for the correction in progress. It is drawn in green for long counts and red for short ones. A tilde next to a number means that point landed outside its expected window.
The yellow boxes are the target zones for points 2, 3 and 4, each with a dashed centre line. A box stays open to the right while the engine is hunting for that point, and freezes at the bar where the point is accepted.
The dotted grid is the Fibonacci scale of leg 0-1, extended to the right so it lines up with the projection. The 0.705 level is drawn in teal, since it is the one the default point 2 and point 4 windows are built around.
The dashed grey path with question marks is the projection of what has not happened yet.
The red dotted line labelled INVALID is the level that kills the count.
The CHoCH lines are the history of structural breaks. A solid dashed line is a level still in force; a thin dotted line ending mid-chart is one that price has already closed back through.
The panel in the top right reports the state machine position, the direction, the live invalidation price, the reason the previous count ended, and how many complete five-wave impulses have been counted on the loaded history.
A practical reading sequence: check the panel state first to know what the engine is looking for, then the INVALID level to know what it costs to be wrong, then the ghost path to know where it expects price to go.
Swing Detection Length (default 10) — the structural pivot. Raise it for larger, rarer structure and fewer seeds; lower it for faster structure and more of them. This is the main character dial.
Point Confirmation (default 5) — the fast pivot that accepts wave points. Keep it at or below the swing length. Lower values catch turns sooner at the cost of noise.
Breaks: Invalidation Mode (default close) — close mode needs a confirmed close beyond the level; wick mode fires on any touch.
Point ranges — the six Fibonacci windows. Widening them produces fewer tildes but says less; tightening them marks more points as unusual. The defaults reflect the optimal-entry convention rather than the full classical ranges.
Show Projection, Point Inside Range, Time Step Coef — control the ghost path: whether to draw it, where inside each window the points sit, and how far apart they are spaced in time.
Cooldown After Reset (default 0) — bars to wait after any count ends before seeding a new one. Useful on noisy instruments where structure breaks cluster.
Timeout (default 0, off) — kills a hypothesis that has been sitting in the same state for longer than k times the duration of leg 0-1. A count that stops developing is usually a count that was wrong.
Historical CHoCH, max (default 20) — how many structure lines to keep on screen. This is also the first dial to lower if the chart feels heavy, since it is the largest consumer of drawing objects.
The visual toggles (grid, boxes, CHoCH history, panel) can each be switched off independently. Turning off the grid and the projection leaves a very clean chart with just the numbered path and the invalidation level, which is arguably the best way to trade it once you trust the engine.
This engine draws a count with numbers on it, and numbered counts are persuasive in a way that raw price is not. Two things are worth keeping in mind.
First, the count is a hypothesis, not a measurement. The tool is honest about this — that is the whole point of the invalidation level and the tilde marks — but it is easy to read a clean 1-2-3-4-5 on the screen and forget that the engine chose it from one specific structural break with one specific pivot depth. Change the swing length and you will often get a different, equally clean count.
Second, wave counting is not a system by itself. The genuinely useful outputs here are the two that are objective: where the hypothesis dies (the invalidation level) and whether the current structure is behaving normally or unusually (the tilde marks). Those combine well with an existing method. The wave numbers themselves are context, not signals.
Used that way — as a structural map with a built-in stop, rather than as an oracle — it is a genuinely well-built tool.
//-------------------------------------------
// PRC_Elliott Impulse Engine (by WillyAlgoTrader)
// version = 0
// 05.08.2026
// Ivan Gonzalez @ www.prorealcode.com
// Sharing ProRealTime knowledge
//-------------------------------------------
// OVERLAY INDICATOR: set "on price" in the editor, not "new panel"
//-------------------------------------------
DEFPARAM DRAWONLASTBARONLY = TRUE
//=== INPUTS: ENGINE ===
swingLen = 10 // structural pivot, drives structure and CHoCH
accDepth = 5 // short pivot, accepts points 1-5/A-C (<= swingLen)
confClose = 1 // 1 = confirm breaks on close, 0 = on the wick
//=== INPUTS: FIB RANGE OF EACH POINT ===
// Outside its range the point is still accepted, marked with "~"
rng2Min = 0.5 // point 2: retracement of leg 0-1
rng2Max = 0.705
rng3Min = -0.5 // point 3: extension beyond point 1
rng3Max = -0.618
rng4Min = 0.5 // point 4: retracement of leg 2-3
rng4Max = 0.705
rng5Min = -0.618 // point 5: extension of leg 0-1
rng5Max = -1.0
rngBMin = -0.5 // point B
rngBMax = -0.618
rngCMin = 0.0 // point C
rngCMax = 0.236
//=== INPUTS: GHOST PROJECTION ===
showGhost = 1
ghMode = 0 // 0 = range midpoint, 1 = edge nearest to 0, 2 = far edge
ghCoef = 1.0 // horizontal step as a multiple of the leg 0-1 duration
g5Off = 0.05 // ghost point 5? sits at extension -1.0 plus this offset
//=== INPUTS: RESET ===
cooldownBars = 0 // bars to wait after a count ends before a CHoCH may seed
timeoutK = 0.0 // reset if the state lasts longer than k x (leg 0-1 duration). 0 = off
//=== INPUTS: VISUAL ===
showGrid = 1 // fib grid anchored to leg 0-1
showBoxes = 1 // target boxes for points 2, 3 and 4
showChoch = 1 // CHoCH line history
showDash = 1 // status dashboard
dashX = -340 // dashboard margin from the right edge, in pixels
dashY = -20 // dashboard margin from the top edge, in pixels
dashCol = 140 // gap between the label column and the value column
boxExt = 20 // initial box length, in bars
maxChoch = 20 // maximum CHoCH lines kept on screen
ghMaxFwd = 200 // forward projection cap, in bars
//=== INPUTS: GRID LEVELS ===
on0236 = 1
on0382 = 1
on0500 = 1
on0618 = 1
on0705 = 1
on0786 = 1
on0886 = 1
on1000 = 1
onM500 = 1
onM618 = 1
onM100 = 1
//=== PALETTE (mid tones: readable on white AND dark backgrounds) ===
pathR = 22
pathG = 163
pathB = 74
shortR = 211
shortG = 47
shortB = 47
oteR = 176
oteG = 110
oteB = 0
ghR = 113
ghG = 128
ghB = 150
bullR = 8
bullG = 145
bullB = 120
bearR = 211
bearG = 47
bearB = 47
critR = 211
critG = 47
critB = 47
mutedR = 113
mutedG = 128
mutedB = 150
//=== FIB GRID SEEDING ===
ONCE $fibLv[0] = 0.236
ONCE $fibLv[1] = 0.382
ONCE $fibLv[2] = 0.5
ONCE $fibLv[3] = 0.618
ONCE $fibLv[4] = 0.705
ONCE $fibLv[5] = 0.786
ONCE $fibLv[6] = 0.886
ONCE $fibLv[7] = 1.0
ONCE $fibLv[8] = -0.5
ONCE $fibLv[9] = -0.618
ONCE $fibLv[10] = -1.0
$fibOn[0] = on0236
$fibOn[1] = on0382
$fibOn[2] = on0500
$fibOn[3] = on0618
$fibOn[4] = on0705
$fibOn[5] = on0786
$fibOn[6] = on0886
$fibOn[7] = on1000
$fibOn[8] = onM500
$fibOn[9] = onM618
$fibOn[10] = onM100
//=== STATE (scalars: PRT rewinds them on every tick of the live bar) ===
// wState 0 = scanning 1 = CHoCH, waiting for 1 2..5 = waves 2 to 5 6/7/8 = A/B/C
ONCE wState = 0
ONCE wdir = 0
ONCE chLvl = 0
ONCE hasChLvl = 0
ONCE wp0 = 0
ONCE wp1 = 0
ONCE wp2 = 0
ONCE wp3 = 0
ONCE wp4 = 0
ONCE wp5 = 0
ONCE wpA = 0
ONCE wpB = 0
ONCE wb0 = 0
ONCE wb1 = 0
ONCE wb2 = 0
ONCE wb3 = 0
ONCE wb4 = 0
ONCE wb5 = 0
ONCE wbA = 0
ONCE wbB = 0
ONCE has0 = 0
ONCE has1 = 0
ONCE has2 = 0
ONCE has3 = 0
ONCE has4 = 0
ONCE has5 = 0
ONCE hasA = 0
ONCE hasB = 0
ONCE soft2 = 0
ONCE soft3 = 0
ONCE soft4 = 0
ONCE soft5 = 0
ONCE softB = 0
ONCE broke1 = 0
ONCE broke3 = 0
ONCE ote2 = 0
ONCE oteB4 = 0
ONCE hitM1 = 0
ONCE w2best = 0
ONCE w2bestBar = 0
ONCE hasW2best = 0
ONCE lockedLvl = 0
ONCE hasLocked = 0
ONCE lastResetBar = 0 - 100000
ONCE stateStart = 0
ONCE impulses = 0
ONCE lastResetCode = 0
ONCE ewTrend = 0
ONCE swHi = 0
ONCE swLo = 0
ONCE swHiBar = 0
ONCE swLoBar = 0
ONCE hasSwHi = 0
ONCE hasSwLo = 0
ONCE swHiBroken = 0
ONCE swLoBroken = 0
ONCE curSHbar = 0
ONCE curSLbar = 0
ONCE hasCurSH = 0
ONCE hasCurSL = 0
ONCE runLo = 0
ONCE runLoBar = 0
ONCE runHi = 0
ONCE runHiBar = 0
ONCE hasRunLo = 0
ONCE hasRunHi = 0
ONCE hasXmark = 0
ONCE xMarkBar = 0
ONCE xMarkPrice = 0
ONCE nCh = 0
myatr = averagetruerange[14](close)
lblOff = 0.5 * myatr
//=== EVENT FLAGS FOR THIS BAR ===
pendingReset = 0 // 1 = invalidated, 2 = completed
resetCode = 0 // 1 TIMEOUT 2 FALSE_CHOCH 3 BELOW_0 4 BELOW_W2 5 BELOW_W4 6 DEEP_C 7 W3_SHORTEST 8 B_ABOVE_5 9 DONE
seedNow = 0
seedDefer = 0
seedDir = 0
seedLvl = 0
seedP0 = 0
seedB0 = 0
seedSwBar = 0
doAcc2 = 0
acc2P = 0
acc2B = 0
acc2Soft = 0
acc2Brk = 0
//=== PIVOTS (two lengths: structure and point acceptance) ===
swPHok = 0
swPLok = 0
swPH = 0
swPL = 0
IF barindex > 2 * swingLen THEN
IF high[swingLen] >= highest[2 * swingLen + 1](high) THEN
swPH = high[swingLen]
swPHok = 1
ENDIF
IF low[swingLen] <= lowest[2 * swingLen + 1](low) THEN
swPL = low[swingLen]
swPLok = 1
ENDIF
ENDIF
swBarP = barindex - swingLen
phAok = 0
plAok = 0
phA = 0
plA = 0
IF barindex > 2 * accDepth THEN
IF high[accDepth] >= highest[2 * accDepth + 1](high) THEN
phA = high[accDepth]
phAok = 1
ENDIF
IF low[accDepth] <= lowest[2 * accDepth + 1](low) THEN
plA = low[accDepth]
plAok = 1
ENDIF
ENDIF
pivBA = barindex - accDepth
//=== CURRENT INVALIDATION LEVEL (evaluated BEFORE touching the pivots) ===
critOk = 0
critLvl = 0
critCode = 0
IF wState = 1 THEN
critLvl = wp0
critOk = has0
critCode = 2
ELSIF wState = 2 THEN
critLvl = wp0
critOk = has0
critCode = 3
ELSIF wState = 3 THEN
IF broke1 = 1 THEN
critLvl = wp2
critOk = has2
critCode = 4
ELSE
critLvl = wp0
critOk = has0
critCode = 3
ENDIF
ELSIF wState = 4 THEN
critLvl = wp2
critOk = has2
critCode = 4
ELSIF wState = 5 THEN
IF broke3 = 1 THEN
critLvl = wp4
critOk = has4
critCode = 5
ELSE
critLvl = wp2
critOk = has2
critCode = 4
ENDIF
ELSIF wState >= 6 THEN
critLvl = wp2
critOk = has2
critCode = 6
ENDIF
//=== 1. INVALIDATION CHECK ===
IF wState <> 0 THEN
IF timeoutK > 0 AND has1 = 1 THEN
IF barindex - stateStart > timeoutK * (wb1 - wb0) THEN
pendingReset = 1
resetCode = 1
ENDIF
ENDIF
IF timeoutK > 0 AND wState = 1 THEN
IF barindex - stateStart > timeoutK * swingLen THEN
pendingReset = 1
resetCode = 1
ENDIF
ENDIF
IF pendingReset = 0 AND critOk = 1 THEN
adverse = 0
IF wdir = 1 THEN
IF confClose = 1 THEN
IF close < critLvl THEN
adverse = 1
ENDIF
ELSE
IF low < critLvl THEN
adverse = 1
ENDIF
ENDIF
ELSE
IF confClose = 1 THEN
IF close > critLvl THEN
adverse = 1
ENDIF
ELSE
IF high > critLvl THEN
adverse = 1
ENDIF
ENDIF
ENDIF
IF adverse = 1 THEN
pendingReset = 1
resetCode = critCode
ENDIF
ENDIF
ENDIF
//=== 2. SWING STRUCTURE AND CHoCH ===
isWarmed = 0
IF barindex >= max(swingLen * 3, 50) THEN
isWarmed = 1
ENDIF
IF swPHok = 1 THEN
swHi = swPH
swHiBar = swBarP
hasSwHi = 1
swHiBroken = 0
ENDIF
IF swPLok = 1 THEN
swLo = swPL
swLoBar = swBarP
hasSwLo = 1
swLoBroken = 0
ENDIF
// Running extreme of the current swing: it becomes point 0 of the next count
IF hasSwHi = 1 THEN
newSH = 0
IF hasCurSH = 0 THEN
newSH = 1
ELSIF swHiBar <> curSHbar THEN
newSH = 1
ENDIF
IF newSH = 1 THEN
curSHbar = swHiBar
hasCurSH = 1
offSH = min(barindex - swHiBar, 480)
mLo = low
mLoBar = barindex
IF offSH > 0 THEN
FOR k = 0 TO offSH DO
IF low[k] < mLo THEN
mLo = low[k]
mLoBar = barindex - k
ENDIF
NEXT
ENDIF
runLo = mLo
runLoBar = mLoBar
hasRunLo = 1
ELSIF low < runLo THEN
runLo = low
runLoBar = barindex
ENDIF
ENDIF
IF hasSwLo = 1 THEN
newSL = 0
IF hasCurSL = 0 THEN
newSL = 1
ELSIF swLoBar <> curSLbar THEN
newSL = 1
ENDIF
IF newSL = 1 THEN
curSLbar = swLoBar
hasCurSL = 1
offSL = min(barindex - swLoBar, 480)
mHi = high
mHiBar = barindex
IF offSL > 0 THEN
FOR k = 0 TO offSL DO
IF high[k] > mHi THEN
mHi = high[k]
mHiBar = barindex - k
ENDIF
NEXT
ENDIF
runHi = mHi
runHiBar = mHiBar
hasRunHi = 1
ELSIF high > runHi THEN
runHi = high
runHiBar = barindex
ENDIF
ENDIF
// Structure break on close. CHoCH = break against the standing trend
bullBreak = 0
IF hasSwHi = 1 AND swHiBroken = 0 AND isWarmed = 1 AND close > swHi THEN
bullBreak = 1
ENDIF
bearBreak = 0
IF hasSwLo = 1 AND swLoBroken = 0 AND isWarmed = 1 AND close < swLo THEN
bearBreak = 1
ENDIF
IF bullBreak = 1 THEN
swHiBroken = 1
wasBear = 0
IF ewTrend = -1 THEN
wasBear = 1
ENDIF
ewTrend = 1
cooled = 0
IF barindex >= lastResetBar + cooldownBars THEN
cooled = 1
ENDIF
freeUp = 0
IF hasLocked = 0 THEN
freeUp = 1
ELSIF abs(swHi - lockedLvl) > pipsize / 2 THEN
freeUp = 1
ENDIF
IF wasBear = 1 AND wState = 0 AND pendingReset = 0 AND cooled = 1 AND freeUp = 1 AND hasRunLo = 1 THEN
seedNow = 1
seedDefer = 0
seedDir = 1
seedLvl = swHi
seedP0 = runLo
seedB0 = runLoBar
seedSwBar = swHiBar
ELSIF wasBear = 1 AND pendingReset = 1 AND cooldownBars = 0 AND freeUp = 1 AND hasRunLo = 1 THEN
// Bull break on the same bar as a reset: deferred seed
seedNow = 1
seedDefer = 1
seedDir = 1
seedLvl = swHi
seedP0 = runLo
seedB0 = runLoBar
seedSwBar = swHiBar
ENDIF
ENDIF
IF bearBreak = 1 THEN
swLoBroken = 1
wasBull = 0
IF ewTrend = 1 THEN
wasBull = 1
ENDIF
ewTrend = -1
cooledD = 0
IF barindex >= lastResetBar + cooldownBars THEN
cooledD = 1
ENDIF
freeDn = 0
IF hasLocked = 0 THEN
freeDn = 1
ELSIF abs(swLo - lockedLvl) > pipsize / 2 THEN
freeDn = 1
ENDIF
IF wasBull = 1 AND wState = 0 AND pendingReset = 0 AND cooledD = 1 AND freeDn = 1 AND hasRunHi = 1 THEN
seedNow = 1
seedDefer = 0
seedDir = -1
seedLvl = swLo
seedP0 = runHi
seedB0 = runHiBar
seedSwBar = swLoBar
ELSIF wasBull = 1 AND pendingReset = 1 AND cooldownBars = 0 AND freeDn = 1 AND hasRunHi = 1 THEN
seedNow = 1
seedDefer = 1
seedDir = -1
seedLvl = swLo
seedP0 = runHi
seedB0 = runHiBar
seedSwBar = swLoBar
ENDIF
ENDIF
// Immediate seed: the count starts on this very bar, so the pivot logic
// below can already accept point 1 today
IF seedNow = 1 AND seedDefer = 0 THEN
wdir = seedDir
chLvl = seedLvl
hasChLvl = 1
wp0 = seedP0
wb0 = seedB0
has0 = 1
wState = 1
stateStart = barindex
hasXmark = 0
hasW2best = 0
IF showChoch = 1 THEN
slotA = nCh - floor(nCh / maxChoch) * maxChoch
$chX1[slotA] = seedSwBar
$chX2[slotA] = barindex
$chLv[slotA] = chLvl
$chDir[slotA] = seedDir
$chMit[slotA] = 0
nCh = nCh + 1
ENDIF
ENDIF
// CHoCH history mitigation: the close crosses back through the level
nActive = min(nCh, maxChoch)
IF nActive > 0 THEN
FOR k = 0 TO nActive - 1 DO
IF $chMit[k] = 0 THEN
mitNow = 0
IF $chDir[k] = 1 AND close < $chLv[k] THEN
mitNow = 1
ENDIF
IF $chDir[k] = -1 AND close > $chLv[k] THEN
mitNow = 1
ENDIF
IF mitNow = 1 THEN
$chMit[k] = 1
$chX2[k] = barindex
ENDIF
ENDIF
NEXT
ENDIF
//=== 3. STATE LOGIC ON PIVOTS ===
// With-trend and counter-trend pivots: resolved with the ALREADY seeded direction
ctrOk = 0
ctrV = 0
wtOk = 0
wtV = 0
IF wdir = 1 THEN
ctrOk = plAok
ctrV = plA
wtOk = phAok
wtV = phA
ELSE
ctrOk = phAok
ctrV = phA
wtOk = plAok
wtV = plA
ENDIF
IF pendingReset = 0 AND wState = 1 THEN
// Waiting for point 1: point 0 keeps running with counter-trend pivots
IF ctrOk = 1 THEN
deeper0 = 0
IF wdir = 1 AND ctrV < wp0 THEN
deeper0 = 1
ENDIF
IF wdir = -1 AND ctrV > wp0 THEN
deeper0 = 1
ENDIF
IF deeper0 = 1 THEN
wp0 = ctrV
wb0 = pivBA
ENDIF
ENDIF
IF wtOk = 1 THEN
beyondCh = 0
IF wdir = 1 AND wtV > chLvl THEN
beyondCh = 1
ENDIF
IF wdir = -1 AND wtV < chLvl THEN
beyondCh = 1
ENDIF
IF beyondCh = 1 THEN
wp1 = wtV
wb1 = pivBA
has1 = 1
wState = 2
stateStart = barindex
// Backfill the retracement extreme over the pivot lag window
spanB = min(barindex - wb1 - 1, 479)
bestV = low
IF wdir = -1 THEN
bestV = high
ENDIF
bestB = barindex
IF spanB > 0 THEN
FOR k = 1 TO spanB DO
vv = low[k]
IF wdir = -1 THEN
vv = high[k]
ENDIF
betterV = 0
IF wdir = 1 AND vv < bestV THEN
betterV = 1
ENDIF
IF wdir = -1 AND vv > bestV THEN
betterV = 1
ENDIF
IF betterV = 1 THEN
bestV = vv
bestB = barindex - k
ENDIF
NEXT
ENDIF
w2best = bestV
w2bestBar = bestB
hasW2best = 1
ENDIF
ENDIF
ELSIF pendingReset = 0 AND wState = 2 THEN
// Relocate point 1 if a more extreme with-trend pivot shows up
IF wtOk = 1 THEN
higher1 = 0
IF wdir = 1 AND wtV > wp1 THEN
higher1 = 1
ENDIF
IF wdir = -1 AND wtV < wp1 THEN
higher1 = 1
ENDIF
IF higher1 = 1 THEN
wp1 = wtV
wb1 = pivBA
spanC = min(barindex - wb1 - 1, 479)
bestV2 = low
IF wdir = -1 THEN
bestV2 = high
ENDIF
bestB2 = barindex
IF spanC > 0 THEN
FOR k = 1 TO spanC DO
vv2 = low[k]
IF wdir = -1 THEN
vv2 = high[k]
ENDIF
better2 = 0
IF wdir = 1 AND vv2 < bestV2 THEN
better2 = 1
ENDIF
IF wdir = -1 AND vv2 > bestV2 THEN
better2 = 1
ENDIF
IF better2 = 1 THEN
bestV2 = vv2
bestB2 = barindex - k
ENDIF
NEXT
ENDIF
w2best = bestV2
w2bestBar = bestB2
hasW2best = 1
ENDIF
ENDIF
IF ctrOk = 1 THEN
den01 = wp1 - wp0
lvl2 = 0
IF den01 <> 0 THEN
lvl2 = (wp1 - ctrV) / den01
ENDIF
IF lvl2 >= 1.0 THEN
pendingReset = 1
resetCode = 3
ELSIF lvl2 > 0.0 THEN
doAcc2 = 1
acc2P = ctrV
acc2B = pivBA
acc2Soft = 1
IF lvl2 >= min(rng2Min, rng2Max) AND lvl2 <= max(rng2Min, rng2Max) THEN
acc2Soft = 0
ENDIF
ENDIF
ENDIF
ELSIF pendingReset = 0 AND wState = 3 THEN
// Point 2 can only be relocated while point 1 has not been broken
IF ctrOk = 1 THEN
deeper2 = 0
IF wdir = 1 AND ctrV < wp2 THEN
deeper2 = 1
ENDIF
IF wdir = -1 AND ctrV > wp2 THEN
deeper2 = 1
ENDIF
IF deeper2 = 1 AND broke1 = 1 THEN
pendingReset = 1
resetCode = 4
ELSIF deeper2 = 1 THEN
den02 = wp1 - wp0
lvl2b = 0
IF den02 <> 0 THEN
lvl2b = (wp1 - ctrV) / den02
ENDIF
IF lvl2b >= 1.0 THEN
pendingReset = 1
resetCode = 3
ELSE
wp2 = ctrV
wb2 = pivBA
soft2 = 1
IF lvl2b >= min(rng2Min, rng2Max) AND lvl2b <= max(rng2Min, rng2Max) THEN
soft2 = 0
ENDIF
ENDIF
ENDIF
ENDIF
IF pendingReset = 0 AND wtOk = 1 AND broke1 = 1 THEN
beyond3 = 0
IF wdir = 1 AND wtV > wp1 THEN
beyond3 = 1
ENDIF
IF wdir = -1 AND wtV < wp1 THEN
beyond3 = 1
ENDIF
IF beyond3 = 1 THEN
den03 = wp1 - wp0
lvl3 = 0
IF den03 <> 0 THEN
lvl3 = (wp1 - wtV) / den03
ENDIF
wp3 = wtV
wb3 = pivBA
has3 = 1
soft3 = 1
IF lvl3 >= min(rng3Min, rng3Max) AND lvl3 <= max(rng3Min, rng3Max) THEN
soft3 = 0
ENDIF
wState = 4
stateStart = barindex
oteB4 = 0
ENDIF
ENDIF
ELSIF pendingReset = 0 AND wState = 4 THEN
// Relocate point 3 if the impulse extends
IF wtOk = 1 THEN
higher3 = 0
IF wdir = 1 AND wtV > wp3 THEN
higher3 = 1
ENDIF
IF wdir = -1 AND wtV < wp3 THEN
higher3 = 1
ENDIF
IF higher3 = 1 THEN
den04 = wp1 - wp0
lvl3b = 0
IF den04 <> 0 THEN
lvl3b = (wp1 - wtV) / den04
ENDIF
wp3 = wtV
wb3 = pivBA
soft3 = 1
IF lvl3b >= min(rng3Min, rng3Max) AND lvl3b <= max(rng3Min, rng3Max) THEN
soft3 = 0
ENDIF
ENDIF
ENDIF
IF pendingReset = 0 AND ctrOk = 1 THEN
below2 = 0
IF wdir = 1 AND ctrV < wp2 THEN
below2 = 1
ENDIF
IF wdir = -1 AND ctrV > wp2 THEN
below2 = 1
ENDIF
den23 = wp3 - wp2
fib4 = 0
IF den23 <> 0 THEN
fib4 = (wp3 - ctrV) / den23
ENDIF
IF below2 = 1 THEN
pendingReset = 1
resetCode = 4
ELSIF fib4 > 0.0 THEN
wp4 = ctrV
wb4 = pivBA
has4 = 1
soft4 = 1
IF fib4 >= min(rng4Min, rng4Max) AND fib4 <= max(rng4Min, rng4Max) THEN
soft4 = 0
ENDIF
wState = 5
stateStart = barindex
hitM1 = 0
ENDIF
ENDIF
ELSIF pendingReset = 0 AND wState = 5 THEN
// Point 4 can deepen while point 3 has not been broken
IF ctrOk = 1 AND broke3 = 0 THEN
deeper4 = 0
IF wdir = 1 AND ctrV < wp4 THEN
deeper4 = 1
ENDIF
IF wdir = -1 AND ctrV > wp4 THEN
deeper4 = 1
ENDIF
below2r = 0
IF wdir = 1 AND ctrV < wp2 THEN
below2r = 1
ENDIF
IF wdir = -1 AND ctrV > wp2 THEN
below2r = 1
ENDIF
IF deeper4 = 1 AND below2r = 1 THEN
pendingReset = 1
resetCode = 4
ELSIF deeper4 = 1 THEN
den23b = wp3 - wp2
fib4b = 0
IF den23b <> 0 THEN
fib4b = (wp3 - ctrV) / den23b
ENDIF
wp4 = ctrV
wb4 = pivBA
soft4 = 1
IF fib4b >= min(rng4Min, rng4Max) AND fib4b <= max(rng4Min, rng4Max) THEN
soft4 = 0
ENDIF
ENDIF
ENDIF
// Once point 3 is broken, a pivot beyond point 4 invalidates
IF pendingReset = 0 AND ctrOk = 1 AND broke3 = 1 THEN
beyond4 = 0
IF wdir = 1 AND ctrV < wp4 THEN
beyond4 = 1
ENDIF
IF wdir = -1 AND ctrV > wp4 THEN
beyond4 = 1
ENDIF
IF beyond4 = 1 THEN
pendingReset = 1
resetCode = 5
ENDIF
ENDIF
IF pendingReset = 0 AND wtOk = 1 AND broke3 = 1 THEN
beyond5 = 0
IF wdir = 1 AND wtV > wp3 THEN
beyond5 = 1
ENDIF
IF wdir = -1 AND wtV < wp3 THEN
beyond5 = 1
ENDIF
IF beyond5 = 1 THEN
len1 = abs(wp1 - wp0)
len3 = abs(wp3 - wp2)
len5 = abs(wtV - wp4)
IF len3 < len1 AND len3 < len5 THEN
// Wave 3 cannot be the shortest of the impulse
pendingReset = 1
resetCode = 7
ELSE
den05 = wp1 - wp0
lvl5 = 0
IF den05 <> 0 THEN
lvl5 = (wp1 - wtV) / den05
ENDIF
wp5 = wtV
wb5 = pivBA
has5 = 1
soft5 = 1
IF lvl5 >= min(rng5Min, rng5Max) AND lvl5 <= max(rng5Min, rng5Max) THEN
soft5 = 0
ENDIF
wState = 6
stateStart = barindex
impulses = impulses + 1
ENDIF
ENDIF
ENDIF
ELSIF pendingReset = 0 AND wState = 6 THEN
// Wave 5 can still extend
IF wtOk = 1 THEN
higher5 = 0
IF wdir = 1 AND wtV > wp5 THEN
higher5 = 1
ENDIF
IF wdir = -1 AND wtV < wp5 THEN
higher5 = 1
ENDIF
IF higher5 = 1 THEN
den06 = wp1 - wp0
lvl5b = 0
IF den06 <> 0 THEN
lvl5b = (wp1 - wtV) / den06
ENDIF
wp5 = wtV
wb5 = pivBA
soft5 = 1
IF lvl5b >= min(rng5Min, rng5Max) AND lvl5b <= max(rng5Min, rng5Max) THEN
soft5 = 0
ENDIF
ENDIF
ENDIF
IF pendingReset = 0 AND ctrOk = 1 THEN
deepA = 0
IF wdir = 1 AND ctrV < wp2 THEN
deepA = 1
ENDIF
IF wdir = -1 AND ctrV > wp2 THEN
deepA = 1
ENDIF
IF deepA = 1 THEN
pendingReset = 1
resetCode = 6
ELSE
wpA = ctrV
wbA = pivBA
hasA = 1
wState = 7
stateStart = barindex
ENDIF
ENDIF
ELSIF pendingReset = 0 AND wState = 7 THEN
// Point A can deepen until B appears
IF ctrOk = 1 THEN
deeperA = 0
IF wdir = 1 AND ctrV < wpA THEN
deeperA = 1
ENDIF
IF wdir = -1 AND ctrV > wpA THEN
deeperA = 1
ENDIF
deepHard = 0
IF wdir = 1 AND ctrV < wp2 THEN
deepHard = 1
ENDIF
IF wdir = -1 AND ctrV > wp2 THEN
deepHard = 1
ENDIF
IF deeperA = 1 AND deepHard = 1 THEN
pendingReset = 1
resetCode = 6
ELSIF deeperA = 1 THEN
wpA = ctrV
wbA = pivBA
ENDIF
ENDIF
IF pendingReset = 0 AND wtOk = 1 THEN
above5 = 0
IF wdir = 1 AND wtV >= wp5 THEN
above5 = 1
ENDIF
IF wdir = -1 AND wtV <= wp5 THEN
above5 = 1
ENDIF
IF above5 = 1 THEN
pendingReset = 2
resetCode = 8
ELSE
den07 = wp1 - wp0
lvlB = 0
IF den07 <> 0 THEN
lvlB = (wp1 - wtV) / den07
ENDIF
wpB = wtV
wbB = pivBA
hasB = 1
softB = 1
IF lvlB >= min(rngBMin, rngBMax) AND lvlB <= max(rngBMin, rngBMax) THEN
softB = 0
ENDIF
wState = 8
stateStart = barindex
ENDIF
ENDIF
ELSIF pendingReset = 0 AND wState = 8 THEN
// Point B can extend until C appears
IF wtOk = 1 THEN
above5b = 0
IF wdir = 1 AND wtV >= wp5 THEN
above5b = 1
ENDIF
IF wdir = -1 AND wtV <= wp5 THEN
above5b = 1
ENDIF
higherB = 0
IF wdir = 1 AND wtV > wpB THEN
higherB = 1
ENDIF
IF wdir = -1 AND wtV < wpB THEN
higherB = 1
ENDIF
IF above5b = 1 THEN
pendingReset = 2
resetCode = 8
ELSIF higherB = 1 THEN
den08 = wp1 - wp0
lvlBb = 0
IF den08 <> 0 THEN
lvlBb = (wp1 - wtV) / den08
ENDIF
wpB = wtV
wbB = pivBA
softB = 1
IF lvlBb >= min(rngBMin, rngBMax) AND lvlBb <= max(rngBMin, rngBMax) THEN
softB = 0
ENDIF
ENDIF
ENDIF
IF pendingReset = 0 AND ctrOk = 1 THEN
deepC = 0
IF wdir = 1 AND ctrV < wp2 THEN
deepC = 1
ENDIF
IF wdir = -1 AND ctrV > wp2 THEN
deepC = 1
ENDIF
IF deepC = 1 THEN
pendingReset = 1
resetCode = 6
ELSE
// Point C closes the correction on this very bar
pendingReset = 2
resetCode = 9
ENDIF
ENDIF
ENDIF
//=== 4. INTRABAR PROGRESS: ZONES AND BREAKS ===
IF pendingReset = 0 AND wState = 2 THEN
ext2 = low
IF wdir = -1 THEN
ext2 = high
ENDIF
trackBest = 0
IF hasW2best = 0 THEN
trackBest = 1
ELSIF wdir = 1 AND ext2 < w2best THEN
trackBest = 1
ELSIF wdir = -1 AND ext2 > w2best THEN
trackBest = 1
ENDIF
IF trackBest = 1 THEN
w2best = ext2
w2bestBar = barindex
hasW2best = 1
ENDIF
den09 = wp1 - wp0
eLvl = 0
IF den09 <> 0 THEN
eLvl = (wp1 - ext2) / den09
ENDIF
IF ote2 = 0 AND eLvl >= min(rng2Min, rng2Max) THEN
ote2 = 1
ENDIF
// Point 1 broken before point 2 is accepted: point 2 is the best extreme
brk1now = 0
IF wdir = 1 THEN
IF confClose = 1 THEN
IF close > wp1 THEN
brk1now = 1
ENDIF
ELSE
IF high > wp1 THEN
brk1now = 1
ENDIF
ENDIF
ELSE
IF confClose = 1 THEN
IF close < wp1 THEN
brk1now = 1
ENDIF
ELSE
IF low < wp1 THEN
brk1now = 1
ENDIF
ENDIF
ENDIF
IF doAcc2 = 0 AND brk1now = 1 THEN
lvlB2 = 0
IF den09 <> 0 THEN
lvlB2 = (wp1 - w2best) / den09
ENDIF
doAcc2 = 1
acc2P = w2best
acc2B = w2bestBar
acc2Brk = 1
acc2Soft = 1
IF lvlB2 >= min(rng2Min, rng2Max) AND lvlB2 <= max(rng2Min, rng2Max) THEN
acc2Soft = 0
ENDIF
ENDIF
ENDIF
// Unified acceptance of point 2
IF pendingReset = 0 AND doAcc2 = 1 THEN
wp2 = acc2P
wb2 = acc2B
has2 = 1
soft2 = acc2Soft
wState = 3
stateStart = barindex
IF acc2Brk = 1 THEN
broke1 = 1
ENDIF
ENDIF
IF pendingReset = 0 AND wState = 3 AND broke1 = 0 THEN
brk1b = 0
IF wdir = 1 THEN
IF confClose = 1 THEN
IF close > wp1 THEN
brk1b = 1
ENDIF
ELSE
IF high > wp1 THEN
brk1b = 1
ENDIF
ENDIF
ELSE
IF confClose = 1 THEN
IF close < wp1 THEN
brk1b = 1
ENDIF
ELSE
IF low < wp1 THEN
brk1b = 1
ENDIF
ENDIF
ENDIF
IF brk1b = 1 THEN
broke1 = 1
ENDIF
ENDIF
IF pendingReset = 0 AND wState = 4 AND oteB4 = 0 THEN
ext4 = low
IF wdir = -1 THEN
ext4 = high
ENDIF
den10 = wp3 - wp2
extF = 0
IF den10 <> 0 THEN
extF = (wp3 - ext4) / den10
ENDIF
IF extF >= min(rng4Min, rng4Max) THEN
oteB4 = 1
ENDIF
ENDIF
IF pendingReset = 0 AND wState = 5 THEN
IF broke3 = 0 THEN
brk3now = 0
IF wdir = 1 THEN
IF confClose = 1 THEN
IF close > wp3 THEN
brk3now = 1
ENDIF
ELSE
IF high > wp3 THEN
brk3now = 1
ENDIF
ENDIF
ELSE
IF confClose = 1 THEN
IF close < wp3 THEN
brk3now = 1
ENDIF
ELSE
IF low < wp3 THEN
brk3now = 1
ENDIF
ENDIF
ENDIF
IF brk3now = 1 THEN
broke3 = 1
ENDIF
ENDIF
IF hitM1 = 0 THEN
tgtM1 = wp1 + 1.0 * (wp1 - wp0)
hitNow = 0
IF wdir = 1 AND high >= tgtM1 THEN
hitNow = 1
ENDIF
IF wdir = -1 AND low <= tgtM1 THEN
hitNow = 1
ENDIF
IF hitNow = 1 THEN
hitM1 = 1
ENDIF
ENDIF
ENDIF
IF pendingReset = 0 AND wState >= 7 AND has5 = 1 THEN
brk5now = 0
IF wdir = 1 THEN
IF confClose = 1 THEN
IF close > wp5 THEN
brk5now = 1
ENDIF
ELSE
IF high > wp5 THEN
brk5now = 1
ENDIF
ENDIF
ELSE
IF confClose = 1 THEN
IF close < wp5 THEN
brk5now = 1
ENDIF
ELSE
IF low < wp5 THEN
brk5now = 1
ENDIF
ENDIF
ENDIF
IF brk5now = 1 THEN
pendingReset = 2
resetCode = 8
ENDIF
ENDIF
//=== 5. RESET EXECUTION ===
IF pendingReset = 1 THEN
hasXmark = 1
xMarkBar = barindex
xMarkPrice = close
IF hasChLvl = 1 THEN
lockedLvl = chLvl
hasLocked = 1
ENDIF
ENDIF
IF pendingReset > 0 THEN
lastResetBar = barindex
lastResetCode = resetCode
wState = 0
wdir = 0
hasChLvl = 0
has0 = 0
has1 = 0
has2 = 0
has3 = 0
has4 = 0
has5 = 0
hasA = 0
hasB = 0
soft2 = 0
soft3 = 0
soft4 = 0
soft5 = 0
softB = 0
broke1 = 0
broke3 = 0
ote2 = 0
oteB4 = 0
hitM1 = 0
hasW2best = 0
ENDIF
//=== 6. DEFERRED SEED (CHoCH on the same bar as the reset) ===
// The invalidation marker of this bar is kept until the next seed
IF seedNow = 1 AND seedDefer = 1 THEN
wdir = seedDir
chLvl = seedLvl
hasChLvl = 1
wp0 = seedP0
wb0 = seedB0
has0 = 1
wState = 1
stateStart = barindex
hasW2best = 0
IF showChoch = 1 THEN
slotB = nCh - floor(nCh / maxChoch) * maxChoch
$chX1[slotB] = seedSwBar
$chX2[slotB] = barindex
$chLv[slotB] = chLvl
$chDir[slotB] = seedDir
$chMit[slotB] = 0
nCh = nCh + 1
ENDIF
ENDIF
//=== 7. INVALIDATION LEVEL FOR THE DASHBOARD AND THE DRAWING ===
dCritOk = 0
dCritLvl = 0
IF wState = 1 OR wState = 2 THEN
dCritLvl = wp0
dCritOk = has0
ELSIF wState = 3 THEN
IF broke1 = 1 THEN
dCritLvl = wp2
dCritOk = has2
ELSE
dCritLvl = wp0
dCritOk = has0
ENDIF
ELSIF wState = 4 THEN
dCritLvl = wp2
dCritOk = has2
ELSIF wState = 5 THEN
IF broke3 = 1 THEN
dCritLvl = wp4
dCritOk = has4
ELSE
dCritLvl = wp2
dCritOk = has2
ENDIF
ELSIF wState >= 6 THEN
dCritLvl = wp2
dCritOk = has2
ENDIF
//=== 8. DRAWING ===
IF islastbarupdate THEN
// --- 8.1 CHoCH history ---
IF showChoch = 1 THEN
nDraw = min(nCh, maxChoch)
IF nDraw > 0 THEN
FOR k = 0 TO nDraw - 1 DO
IF $chDir[k] = 1 THEN
cr = bullR
cg = bullG
cb = bullB
ELSE
cr = bearR
cg = bearG
cb = bearB
ENDIF
xEnd = barindex + 20
IF $chMit[k] = 1 THEN
xEnd = $chX2[k]
ENDIF
IF $chMit[k] = 1 THEN
DRAWSEGMENT($chX1[k], $chLv[k], xEnd, $chLv[k]) COLOURED(cr, cg, cb) STYLE(dottedline, 1)
ELSE
DRAWSEGMENT($chX1[k], $chLv[k], xEnd, $chLv[k]) COLOURED(cr, cg, cb) STYLE(dottedline, 2)
ENDIF
xMid = round(($chX1[k] + xEnd) / 2)
IF $chDir[k] = 1 THEN
DRAWTEXT("CHoCH", xMid, $chLv[k] + lblOff * 0.5) COLOURED(cr, cg, cb)
ELSE
DRAWTEXT("CHoCH", xMid, $chLv[k] - lblOff * 0.5) COLOURED(cr, cg, cb)
ENDIF
NEXT
ENDIF
ENDIF
// --- 8.2 Invalidated hypothesis marker ---
IF hasXmark = 1 THEN
DRAWTEXT("✖", xMarkBar, xMarkPrice) COLOURED(critR, critG, critB)
ENDIF
IF wState >= 2 AND has1 = 1 THEN
// --- 8.3a Projection anchor and step (used by both the grid and the ghost) ---
gstep = max(1, round((wb1 - wb0) * ghCoef))
aPx = wp1
aBx = wb1
IF wState = 3 THEN
aPx = wp2
aBx = wb2
ELSIF wState = 4 THEN
aPx = wp3
aBx = wb3
ELSIF wState = 5 THEN
aPx = wp4
aBx = wb4
ELSIF wState = 6 THEN
aPx = wp5
aBx = wb5
ELSIF wState = 7 THEN
aPx = wpA
aBx = wbA
ELSIF wState = 8 THEN
aPx = wpB
aBx = wbB
ENDIF
ngEst = 9 - wState
ghEndX = min(aBx + gstep * ngEst, barindex + ghMaxFwd)
// --- 8.3 Fib grid anchored to leg 0-1 ---
gridEnd = barindex + 2
IF showGhost = 1 THEN
gridEnd = max(gridEnd, ghEndX + 5)
ENDIF
IF showGrid = 1 THEN
FOR k = 0 TO 10 DO
IF $fibOn[k] = 1 THEN
lvv = $fibLv[k]
yv = wp1 - lvv * (wp1 - wp0)
IF k = 0 THEN
gr = 211
gg = 47
gb = 47
ELSIF k = 4 THEN
gr = 13
gg = 148
gb = 136
ELSIF k = 2 OR k = 7 OR k = 8 OR k = 10 THEN
gr = 113
gg = 128
gb = 150
ELSIF k = 5 THEN
gr = 59
gg = 130
gb = 246
ELSE
gr = 37
gg = 99
gb = 235
ENDIF
DRAWSEGMENT(wb0, yv, gridEnd, yv) COLOURED(gr, gg, gb) STYLE(dottedline, 1)
IF k = 0 THEN
DRAWTEXT("0.236", gridEnd, yv) COLOURED(gr, gg, gb)
ELSIF k = 1 THEN
DRAWTEXT("0.382", gridEnd, yv) COLOURED(gr, gg, gb)
ELSIF k = 2 THEN
DRAWTEXT("0.5", gridEnd, yv) COLOURED(gr, gg, gb)
ELSIF k = 3 THEN
DRAWTEXT("0.618", gridEnd, yv) COLOURED(gr, gg, gb)
ELSIF k = 4 THEN
DRAWTEXT("0.705", gridEnd, yv) COLOURED(gr, gg, gb)
ELSIF k = 5 THEN
DRAWTEXT("0.786", gridEnd, yv) COLOURED(gr, gg, gb)
ELSIF k = 6 THEN
DRAWTEXT("0.886", gridEnd, yv) COLOURED(gr, gg, gb)
ELSIF k = 7 THEN
DRAWTEXT("1.0", gridEnd, yv) COLOURED(gr, gg, gb)
ELSIF k = 8 THEN
DRAWTEXT("-0.5", gridEnd, yv) COLOURED(gr, gg, gb)
ELSIF k = 9 THEN
DRAWTEXT("-0.618", gridEnd, yv) COLOURED(gr, gg, gb)
ELSE
DRAWTEXT("-1.0", gridEnd, yv) COLOURED(gr, gg, gb)
ENDIF
ENDIF
NEXT
ENDIF
// --- 8.4 Target boxes (they coexist: each box freezes at its accepted point) ---
IF showBoxes = 1 THEN
e2a = wp1 - rng2Min * (wp1 - wp0)
e2b = wp1 - rng2Max * (wp1 - wp0)
x2r = wb1 + boxExt
IF has2 = 1 THEN
x2r = wb2
ENDIF
DRAWRECTANGLE(wb1, max(e2a, e2b), x2r, min(e2a, e2b)) COLOURED(oteR, oteG, oteB) FILLCOLOR(oteR, oteG, oteB, 80)
mid2 = (e2a + e2b) / 2
DRAWSEGMENT(wb1, mid2, x2r, mid2) COLOURED(oteR, oteG, oteB) STYLE(dottedline, 1)
IF has2 = 1 THEN
e3a = wp1 - rng3Min * (wp1 - wp0)
e3b = wp1 - rng3Max * (wp1 - wp0)
x3r = wb2 + boxExt
IF has3 = 1 THEN
x3r = wb3
ENDIF
DRAWRECTANGLE(wb2, max(e3a, e3b), x3r, min(e3a, e3b)) COLOURED(oteR, oteG, oteB) FILLCOLOR(oteR, oteG, oteB, 80)
mid3 = (e3a + e3b) / 2
DRAWSEGMENT(wb2, mid3, x3r, mid3) COLOURED(oteR, oteG, oteB) STYLE(dottedline, 1)
ENDIF
IF has3 = 1 THEN
e4a = wp3 - rng4Min * (wp3 - wp2)
e4b = wp3 - rng4Max * (wp3 - wp2)
x4r = wb3 + boxExt
IF has4 = 1 THEN
x4r = wb4
ENDIF
DRAWRECTANGLE(wb3, max(e4a, e4b), x4r, min(e4a, e4b)) COLOURED(oteR, oteG, oteB) FILLCOLOR(oteR, oteG, oteB, 80)
mid4 = (e4a + e4b) / 2
DRAWSEGMENT(wb3, mid4, x4r, mid4) COLOURED(oteR, oteG, oteB) STYLE(dottedline, 1)
ENDIF
ENDIF
// --- 8.5 Accepted wave path ---
IF wdir = 1 THEN
lr = pathR
lg = pathG
lb = pathB
ELSE
lr = shortR
lg = shortG
lb = shortB
ENDIF
DRAWSEGMENT(wb0, wp0, wb1, wp1) COLOURED(lr, lg, lb) STYLE(line, 2)
IF wdir = 1 THEN
DRAWTEXT("0", wb0, wp0 - lblOff) COLOURED(lr, lg, lb)
DRAWTEXT("1", wb1, wp1 + lblOff) COLOURED(lr, lg, lb)
ELSE
DRAWTEXT("0", wb0, wp0 + lblOff) COLOURED(lr, lg, lb)
DRAWTEXT("1", wb1, wp1 - lblOff) COLOURED(lr, lg, lb)
ENDIF
IF has2 = 1 THEN
DRAWSEGMENT(wb1, wp1, wb2, wp2) COLOURED(lr, lg, lb) STYLE(line, 2)
y2 = wp2 - lblOff
IF wdir = -1 THEN
y2 = wp2 + lblOff
ENDIF
IF soft2 = 1 THEN
DRAWTEXT("2~", wb2, y2) COLOURED(lr, lg, lb)
ELSE
DRAWTEXT("2", wb2, y2) COLOURED(lr, lg, lb)
ENDIF
ENDIF
IF has3 = 1 THEN
DRAWSEGMENT(wb2, wp2, wb3, wp3) COLOURED(lr, lg, lb) STYLE(line, 2)
y3 = wp3 + lblOff
IF wdir = -1 THEN
y3 = wp3 - lblOff
ENDIF
IF soft3 = 1 THEN
DRAWTEXT("3~", wb3, y3) COLOURED(lr, lg, lb)
ELSE
DRAWTEXT("3", wb3, y3) COLOURED(lr, lg, lb)
ENDIF
ENDIF
IF has4 = 1 THEN
DRAWSEGMENT(wb3, wp3, wb4, wp4) COLOURED(lr, lg, lb) STYLE(line, 2)
y4 = wp4 - lblOff
IF wdir = -1 THEN
y4 = wp4 + lblOff
ENDIF
IF soft4 = 1 THEN
DRAWTEXT("4~", wb4, y4) COLOURED(lr, lg, lb)
ELSE
DRAWTEXT("4", wb4, y4) COLOURED(lr, lg, lb)
ENDIF
ENDIF
IF has5 = 1 THEN
DRAWSEGMENT(wb4, wp4, wb5, wp5) COLOURED(lr, lg, lb) STYLE(line, 2)
y5 = wp5 + lblOff
IF wdir = -1 THEN
y5 = wp5 - lblOff
ENDIF
IF soft5 = 1 THEN
DRAWTEXT("5~", wb5, y5) COLOURED(lr, lg, lb)
ELSE
DRAWTEXT("5", wb5, y5) COLOURED(lr, lg, lb)
ENDIF
ENDIF
IF hasA = 1 THEN
DRAWSEGMENT(wb5, wp5, wbA, wpA) COLOURED(lr, lg, lb) STYLE(line, 2)
yA = wpA - lblOff
IF wdir = -1 THEN
yA = wpA + lblOff
ENDIF
DRAWTEXT("A", wbA, yA) COLOURED(lr, lg, lb)
ENDIF
IF hasB = 1 THEN
DRAWSEGMENT(wbA, wpA, wbB, wpB) COLOURED(lr, lg, lb) STYLE(line, 2)
yB = wpB + lblOff
IF wdir = -1 THEN
yB = wpB - lblOff
ENDIF
IF softB = 1 THEN
DRAWTEXT("B~", wbB, yB) COLOURED(lr, lg, lb)
ELSE
DRAWTEXT("B", wbB, yB) COLOURED(lr, lg, lb)
ENDIF
ENDIF
// --- 8.6 Ghost projection ---
IF showGhost = 1 THEN
den11 = wp1 - wp0
// Point chosen inside each range according to ghMode
s2 = (rng2Min + rng2Max) / 2
IF ghMode = 1 THEN
s2 = rng2Min
IF abs(rng2Max) < abs(rng2Min) THEN
s2 = rng2Max
ENDIF
ELSIF ghMode = 2 THEN
s2 = rng2Max
IF abs(rng2Max) < abs(rng2Min) THEN
s2 = rng2Min
ENDIF
ENDIF
s4 = (rng4Min + rng4Max) / 2
IF ghMode = 1 THEN
s4 = rng4Min
IF abs(rng4Max) < abs(rng4Min) THEN
s4 = rng4Max
ENDIF
ELSIF ghMode = 2 THEN
s4 = rng4Max
IF abs(rng4Max) < abs(rng4Min) THEN
s4 = rng4Min
ENDIF
ENDIF
sB = (rngBMin + rngBMax) / 2
IF ghMode = 1 THEN
sB = rngBMin
IF abs(rngBMax) < abs(rngBMin) THEN
sB = rngBMax
ENDIF
ELSIF ghMode = 2 THEN
sB = rngBMax
IF abs(rngBMax) < abs(rngBMin) THEN
sB = rngBMin
ENDIF
ENDIF
sC = (rngCMin + rngCMax) / 2
IF ghMode = 1 THEN
sC = rngCMin
IF abs(rngCMax) < abs(rngCMin) THEN
sC = rngCMax
ENDIF
ELSIF ghMode = 2 THEN
sC = rngCMax
IF abs(rngCMax) < abs(rngCMin) THEN
sC = rngCMin
ENDIF
ENDIF
gh2 = wp1 - s2 * den11
gh3 = wp1 - ((rng3Min + rng3Max) / 2) * den11
ref2 = gh2
IF wState >= 3 THEN
ref2 = wp2
ENDIF
ref3 = gh3
IF wState >= 4 THEN
ref3 = wp3
ENDIF
gh4 = ref3 - s4 * (ref3 - ref2)
gh5 = wp1 - (0 - 1.0 + g5Off) * den11
ghA = gh4
IF wState >= 5 THEN
ghA = wp4
ENDIF
ghB = wp1 - sB * den11
ghC = wp1 - sC * den11
// The anchor (aPx, aBx) and the step (gstep) are computed in 8.3a
ng = 0
IF wState <= 2 THEN
$ghP[ng] = gh2
$ghId[ng] = 2
ng = ng + 1
ENDIF
IF wState <= 3 THEN
$ghP[ng] = gh3
$ghId[ng] = 3
ng = ng + 1
ENDIF
IF wState <= 4 THEN
$ghP[ng] = gh4
$ghId[ng] = 4
ng = ng + 1
ENDIF
IF wState <= 5 THEN
$ghP[ng] = gh5
$ghId[ng] = 5
ng = ng + 1
ENDIF
IF wState <= 6 THEN
$ghP[ng] = ghA
$ghId[ng] = 6
ng = ng + 1
ENDIF
IF wState <= 7 THEN
$ghP[ng] = ghB
$ghId[ng] = 7
ng = ng + 1
ENDIF
$ghP[ng] = ghC
$ghId[ng] = 8
ng = ng + 1
pPrev = aPx
bPrev = aBx
FOR k = 0 TO ng - 1 DO
ptv = $ghP[k]
gbx = min(aBx + gstep * (k + 1), barindex + ghMaxFwd)
DRAWSEGMENT(bPrev, pPrev, gbx, ptv) COLOURED(ghR, ghG, ghB) STYLE(dottedline, 1)
yg = ptv - lblOff * 0.6
IF ptv >= pPrev THEN
yg = ptv + lblOff * 0.6
ENDIF
IF $ghId[k] = 2 THEN
DRAWTEXT("2?", gbx, yg) COLOURED(ghR, ghG, ghB)
ELSIF $ghId[k] = 3 THEN
DRAWTEXT("3?", gbx, yg) COLOURED(ghR, ghG, ghB)
ELSIF $ghId[k] = 4 THEN
DRAWTEXT("4?", gbx, yg) COLOURED(ghR, ghG, ghB)
ELSIF $ghId[k] = 5 THEN
DRAWTEXT("5?", gbx, yg) COLOURED(ghR, ghG, ghB)
ELSIF $ghId[k] = 6 THEN
DRAWTEXT("A?", gbx, yg) COLOURED(ghR, ghG, ghB)
ELSIF $ghId[k] = 7 THEN
DRAWTEXT("B?", gbx, yg) COLOURED(ghR, ghG, ghB)
ELSE
DRAWTEXT("C?", gbx, yg) COLOURED(ghR, ghG, ghB)
ENDIF
pPrev = ptv
bPrev = gbx
NEXT
ENDIF
ENDIF
// --- 8.7 Current invalidation level ---
IF dCritOk = 1 AND wState <> 0 THEN
DRAWSEGMENT(barindex - 1, dCritLvl, barindex + 2, dCritLvl) COLOURED(critR, critG, critB) STYLE(dottedline, 1)
critShow = round(dCritLvl * 100) / 100
DRAWTEXT("INVALID #critShow#", barindex + 12, dCritLvl) COLOURED(critR, critG, critB)
ENDIF
// --- 8.8 Status dashboard ---
IF showDash = 1 THEN
px = dashX
py = dashY
DRAWTEXT("EIE - Elliott Impulse Engine", px, py, sansserif, bold, 11) COLOURED(37, 99, 235) ANCHOR(topright, xshift, yshift)
DRAWTEXT("State", px, py - 22, sansserif, standard, 10) COLOURED(mutedR, mutedG, mutedB) ANCHOR(topright, xshift, yshift)
IF wState = 0 THEN
DRAWTEXT("Scanning", px + dashCol, py - 22, sansserif, bold, 10) COLOURED(mutedR, mutedG, mutedB) ANCHOR(topright, xshift, yshift)
ELSIF wState = 1 THEN
DRAWTEXT("CHoCH wait 1", px + dashCol, py - 22, sansserif, bold, 10) COLOURED(oteR, oteG, oteB) ANCHOR(topright, xshift, yshift)
ELSIF wState = 2 THEN
DRAWTEXT("Wave 2", px + dashCol, py - 22, sansserif, bold, 10) COLOURED(oteR, oteG, oteB) ANCHOR(topright, xshift, yshift)
ELSIF wState = 3 THEN
DRAWTEXT("Wave 3", px + dashCol, py - 22, sansserif, bold, 10) COLOURED(oteR, oteG, oteB) ANCHOR(topright, xshift, yshift)
ELSIF wState = 4 THEN
DRAWTEXT("Wave 4", px + dashCol, py - 22, sansserif, bold, 10) COLOURED(oteR, oteG, oteB) ANCHOR(topright, xshift, yshift)
ELSIF wState = 5 THEN
DRAWTEXT("Wave 5", px + dashCol, py - 22, sansserif, bold, 10) COLOURED(oteR, oteG, oteB) ANCHOR(topright, xshift, yshift)
ELSIF wState = 6 THEN
DRAWTEXT("Corr - A", px + dashCol, py - 22, sansserif, bold, 10) COLOURED(oteR, oteG, oteB) ANCHOR(topright, xshift, yshift)
ELSIF wState = 7 THEN
DRAWTEXT("Corr - B", px + dashCol, py - 22, sansserif, bold, 10) COLOURED(oteR, oteG, oteB) ANCHOR(topright, xshift, yshift)
ELSE
DRAWTEXT("Corr - C", px + dashCol, py - 22, sansserif, bold, 10) COLOURED(oteR, oteG, oteB) ANCHOR(topright, xshift, yshift)
ENDIF
DRAWTEXT("Direction", px, py - 42, sansserif, standard, 10) COLOURED(mutedR, mutedG, mutedB) ANCHOR(topright, xshift, yshift)
IF wdir = 1 THEN
DRAWTEXT("Long", px + dashCol, py - 42, sansserif, bold, 10) COLOURED(pathR, pathG, pathB) ANCHOR(topright, xshift, yshift)
ELSIF wdir = -1 THEN
DRAWTEXT("Short", px + dashCol, py - 42, sansserif, bold, 10) COLOURED(shortR, shortG, shortB) ANCHOR(topright, xshift, yshift)
ELSE
DRAWTEXT("-", px + dashCol, py - 42, sansserif, bold, 10) COLOURED(mutedR, mutedG, mutedB) ANCHOR(topright, xshift, yshift)
ENDIF
DRAWTEXT("Invalidation", px, py - 62, sansserif, standard, 10) COLOURED(mutedR, mutedG, mutedB) ANCHOR(topright, xshift, yshift)
IF dCritOk = 1 AND wState <> 0 THEN
dcShow = round(dCritLvl * 100) / 100
DRAWTEXT("#dcShow#", px + dashCol, py - 62, sansserif, bold, 10) COLOURED(critR, critG, critB) ANCHOR(topright, xshift, yshift)
ELSE
DRAWTEXT("-", px + dashCol, py - 62, sansserif, bold, 10) COLOURED(mutedR, mutedG, mutedB) ANCHOR(topright, xshift, yshift)
ENDIF
DRAWTEXT("Last reset", px, py - 82, sansserif, standard, 10) COLOURED(mutedR, mutedG, mutedB) ANCHOR(topright, xshift, yshift)
IF lastResetCode = 1 THEN
DRAWTEXT("TIMEOUT", px + dashCol, py - 82, sansserif, bold, 10) COLOURED(mutedR, mutedG, mutedB) ANCHOR(topright, xshift, yshift)
ELSIF lastResetCode = 2 THEN
DRAWTEXT("FALSE CHoCH", px + dashCol, py - 82, sansserif, bold, 10) COLOURED(mutedR, mutedG, mutedB) ANCHOR(topright, xshift, yshift)
ELSIF lastResetCode = 3 THEN
DRAWTEXT("BELOW 0", px + dashCol, py - 82, sansserif, bold, 10) COLOURED(mutedR, mutedG, mutedB) ANCHOR(topright, xshift, yshift)
ELSIF lastResetCode = 4 THEN
DRAWTEXT("BELOW W2", px + dashCol, py - 82, sansserif, bold, 10) COLOURED(mutedR, mutedG, mutedB) ANCHOR(topright, xshift, yshift)
ELSIF lastResetCode = 5 THEN
DRAWTEXT("BELOW W4", px + dashCol, py - 82, sansserif, bold, 10) COLOURED(mutedR, mutedG, mutedB) ANCHOR(topright, xshift, yshift)
ELSIF lastResetCode = 6 THEN
DRAWTEXT("DEEP C", px + dashCol, py - 82, sansserif, bold, 10) COLOURED(mutedR, mutedG, mutedB) ANCHOR(topright, xshift, yshift)
ELSIF lastResetCode = 7 THEN
DRAWTEXT("W3 SHORTEST", px + dashCol, py - 82, sansserif, bold, 10) COLOURED(mutedR, mutedG, mutedB) ANCHOR(topright, xshift, yshift)
ELSIF lastResetCode = 8 THEN
DRAWTEXT("B ABOVE 5", px + dashCol, py - 82, sansserif, bold, 10) COLOURED(mutedR, mutedG, mutedB) ANCHOR(topright, xshift, yshift)
ELSIF lastResetCode = 9 THEN
DRAWTEXT("DONE", px + dashCol, py - 82, sansserif, bold, 10) COLOURED(pathR, pathG, pathB) ANCHOR(topright, xshift, yshift)
ELSE
DRAWTEXT("-", px + dashCol, py - 82, sansserif, bold, 10) COLOURED(mutedR, mutedG, mutedB) ANCHOR(topright, xshift, yshift)
ENDIF
DRAWTEXT("Impulses", px, py - 102, sansserif, standard, 10) COLOURED(mutedR, mutedG, mutedB) ANCHOR(topright, xshift, yshift)
DRAWTEXT("#impulses#", px + dashCol, py - 102, sansserif, bold, 10) COLOURED(pathR, pathG, pathB) ANCHOR(topright, xshift, yshift)
ENDIF
ENDIF
RETURN