Counting Elliott waves across a watchlist does not scale. Even if you are quick at it, going instrument by instrument to find the two or three that have just confirmed a third wave takes an evening, and by the time you finish, the first ones you looked at have moved.
This ProScreener does that pass for you. It returns the instruments where an impulse count is live and currently in wave 3: the structure changed character, the first leg printed, the pullback was accepted as point 2, and price has since broken above point 1.
What makes that moment worth screening for is not the label. It is that three separate things are defined at once:
So every name on the list arrives with an entry context, a stop and an objective already attached, rather than as a bare signal you still have to frame.
The screener runs the same engine as the Elliott Impulse Engine indicator (https://www.prorealcode.com/prorealtime-indicators/elliott-impulse-engine/), so anything it returns can be opened on a chart and inspected with the full count drawn out.
The condition is not a shape matched against past bars. It is the state of a machine that has walked through a specific sequence, in order:
Change of character. The engine tracks swing structure with a long pivot and keeps a standing bias — bullish once a swing high is closed through, bearish once a swing low is. A break in the same direction as that bias is just continuation and is ignored. Only a break against it opens a hypothesis. That is what seeds a count.
Point 0 and point 1. Point 0 is not the bar where the break happened: the engine has been tracking the running extreme of the swing in progress, and that extreme — a real turning point, with its real bar — becomes the origin. Point 1 is then the first confirmed pivot beyond the broken level.
Point 2. The pullback that follows. It is accepted using a second, shorter pivot length, which is what lets the engine catch turns inside a developing impulse without producing a change of character on every wiggle. A point 2 that lands outside its expected retracement band is still accepted — it is flagged, not discarded.
The break of point 1. This is the step that declares wave 3, and it is the screener’s condition. Until it happens, the count is a hypothesis about a pullback; after it, the market has confirmed the impulse is extending.
While all this runs, a single invalidation level ratchets forward with the count. During wave 3 it sits at point 2. A close beyond it and the whole reading is discarded — the instrument leaves the list on the next scan.
The same condition can be caught at three different moments of its life, set by sigMode:
Mode 0 — wave 3 just declared (default). Point 1 was broken maxBarsAge bars ago or less. This is the discovery mode: impulses that have just confirmed, with the move still ahead of them. It is the one to run daily.
Mode 1 — wave 3 in progress. Every live wave 3, regardless of when it started. Use it to see the full current inventory rather than only the fresh entries — useful the first time you run the screener on a new universe.
Mode 2 — price inside the target zone. Price has already reached the band between the -0.5 and -0.618 extensions of leg 0-1. This is not an entry mode: it is where an existing position starts thinking about the exit.
Two further filters narrow the list:
dirFilter restricts to longs or shorts, or leaves both.strictW2 keeps only textbook counts — those where point 2 landed inside its expected 0.5 to 0.705 retracement band. Off by default, because a wave 2 that only retraces 0.38 is common in strong impulses and is worth seeing; the results table reports the measured value so you can judge it yourself.Eight columns come back with each hit:
Sorting by % to target descending puts the names with the most theoretical room left on top. Sorting by W3 bars ascending puts the freshest first.
The two columns that earn their place beyond the obvious are W2 retr% and Invalidation. Together they tell you, before you open a single chart, whether a candidate is a tight setup with a close stop or a stretched one where being right still costs a lot to hold.
Swing Detection Length (swingLen, default 10) — the structural pivot. This is the main character dial: raise it for larger and rarer structure with fewer counts, lower it for faster structure and more of them. On a daily chart, 10 gives multi-week impulses.
Point Confirmation (accDepth, default 5) — the short pivot that accepts the wave points. Keep it at or below the swing length. Lower values catch the turns of a developing impulse sooner, at the cost of accepting noisier points.
Invalidation Mode (confClose, default 1) — whether a level break needs a confirmed close beyond it, or counts on any wick touch. Close mode produces fewer and slower resets.
Fib ranges — the expected windows for points 2 and 3. Range 2 feeds the strictW2 filter; range 3 defines the target zone used by mode 2 and the % to target column. Changing range 3 changes what the screener calls the objective.
Cooldown After Reset (default 0) — bars to wait after any count ends before a new one may be seeded. Worth raising on noisy instruments where structure breaks cluster together.
Timeout (default 0, off) — discards a hypothesis that has sat in the same state longer than k times the duration of leg 0-1. A count that stops developing is usually a count that was wrong.
scanBack (default 480) — how far back the engine looks when recovering the extreme of a retracement. This is the main cost driver of the scan. It is deliberately left at the same value the indicator uses, so that both produce the same count; lowering it makes the screener faster but can drift its reading away from what you see on the chart.
ProScreener works on a fixed window: 1024 bars on Premium, 255 on Complete. That matters more here than in a simple screener, because the engine needs to warm up and then walk a full sequence — change of character, point 1, point 2, break — inside that window.
Two practical consequences:
On Complete, expect few results. Instruments genuinely in wave 3 will be missed simply because their point 0 falls outside the loaded window. Lowering swingLen shortens the sequence and helps, at the cost of noisier structure.
A chart usually holds more history than the scan. If a chart starts further back, its engine may seed a count in a place the screener never saw, and the two can legitimately end up in different states. That is why Impulses is exposed as a column: it should match the indicator’s dashboard. When it does not, the two are looking at different windows, and the chart is the one to trust.
The honest way to use the screener is therefore as a shortlist generator, not as a verdict: run it, then open the handful of names it returns and confirm the count on the chart before doing anything with it.
// ----------------------------------------------------
// PRC Elliott Impulse Engine - Wave 3 screener
// version = 0
// 05.08.2026
// Ivan Gonzalez @ www.prorealcode.com
// Sharing ProRealTime knowledge
// ----------------------------------------------------
// Filters instruments with an Elliott wave 3 IN PROGRESS, using the engine of
// the PRC_Elliott-Impulse-Engine indicator: CHoCH against the
// trend -> point 1 -> point 2 accepted -> point 1 broken.
// ----------------------------------------------------
//=== 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 ===
// The engine accepts the point whether or not it is in range; here range 2 feeds
// strictW2 and range 3 defines the target zone of wave 3
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
//=== INPUTS: SCREENER FILTERING ===
sigMode = 0 // 0 = wave 3 just declared, 1 = wave 3 in progress, 2 = price inside target zone
maxBarsAge = 5 // maximum age of the point 1 break (mode 0 only)
dirFilter = 0 // 0 = both directions, 1 = longs only, 2 = shorts only
strictW2 = 0 // 1 = require point 2 to have landed inside its expected range
scanBack = 480 // cap of the backfill loops. 480 = SAME value as the
// indicator: lowering it speeds the screener up but can drift the
// count away from the one you see on the chart
//=== 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
//=== 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 has0 = 0
ONCE has1 = 0
ONCE has2 = 0
ONCE has4 = 0
ONCE has5 = 0
ONCE soft2 = 0
ONCE p2Lvl = 0
ONCE brk1Bar = 0
ONCE broke1 = 0
ONCE broke3 = 0
ONCE ote2 = 0
ONCE oteB4 = 0
ONCE hitM1 = 0
ONCE w2best = 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
//=== 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
doAcc2 = 0
acc2P = 0
acc2Soft = 0
acc2Lvl = 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, scanBack)
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, scanBack)
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
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
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
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
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
hasW2best = 0
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, scanBack)
bestV = low
IF wdir = -1 THEN
bestV = high
ENDIF
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
ENDIF
NEXT
ENDIF
w2best = bestV
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, scanBack)
bestV2 = low
IF wdir = -1 THEN
bestV2 = high
ENDIF
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
ENDIF
NEXT
ENDIF
w2best = bestV2
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
acc2Lvl = lvl2
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
p2Lvl = lvl2b
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
wp3 = wtV
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
wp3 = wtV
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
has4 = 1
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
wp4 = ctrV
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
wp5 = wtV
has5 = 1
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
wp5 = wtV
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
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
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
wpB = wtV
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
wpB = wtV
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
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
acc2Lvl = lvlB2
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
has2 = 1
soft2 = acc2Soft
p2Lvl = acc2Lvl
wState = 3
stateStart = barindex
IF acc2Brk = 1 THEN
broke1 = 1
brk1Bar = barindex
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
brk1Bar = barindex
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
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
has4 = 0
has5 = 0
soft2 = 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
ENDIF
//=== 7. INVALIDATION LEVEL FOR THE DASHBOARD AND THE DRAWING ===
dCritLvl = 0
IF wState = 1 OR wState = 2 THEN
dCritLvl = wp0
ELSIF wState = 3 THEN
IF broke1 = 1 THEN
dCritLvl = wp2
ELSE
dCritLvl = wp0
ENDIF
ELSIF wState = 4 THEN
dCritLvl = wp2
ELSIF wState = 5 THEN
IF broke3 = 1 THEN
dCritLvl = wp4
ELSE
dCritLvl = wp2
ENDIF
ELSIF wState >= 6 THEN
dCritLvl = wp2
ENDIF
//=== 8. SCREENER CONDITION ===
// Wave 3 declared: point 2 accepted and point 1 broken
inW3 = 0
IF wState = 3 AND broke1 = 1 AND has2 = 1 THEN
inW3 = 1
ENDIF
// Wave 3 progress on the 0-1 grid (negative = beyond point 1)
denW = wp1 - wp0
w3Lvl = 0
IF inW3 = 1 AND denW <> 0 THEN
w3Lvl = (wp1 - close) / denW
ENDIF
// Wave 3 target and percentage distance in favour of the count
tgtPct = 0
inZone = 0
IF inW3 = 1 THEN
tgt3 = wp1 - rng3Max * denW
tgtPct = (tgt3 - close) / close * 100 * wdir
z3a = wp1 - rng3Min * denW
IF close >= min(z3a, tgt3) AND close <= max(z3a, tgt3) THEN
inZone = 1
ENDIF
ENDIF
ageW3 = barindex - brk1Bar
modeOk = 0
IF inW3 = 1 THEN
IF sigMode = 0 AND ageW3 <= maxBarsAge THEN
modeOk = 1
ELSIF sigMode = 1 THEN
modeOk = 1
ELSIF sigMode = 2 AND inZone = 1 THEN
modeOk = 1
ENDIF
ENDIF
dirOk = 0
IF dirFilter = 0 THEN
dirOk = 1
ELSIF dirFilter = 1 AND wdir = 1 THEN
dirOk = 1
ELSIF dirFilter = 2 AND wdir = -1 THEN
dirOk = 1
ENDIF
// Textbook count: point 2 landed inside its expected range
w2Ok = 1
IF strictW2 = 1 AND soft2 = 1 THEN
w2Ok = 0
ENDIF
p2Pct = round(p2Lvl * 1000) / 10
w3Pct = round(w3Lvl * 1000) / 10
tgtShow = round(tgtPct * 10) / 10
critShow = round(dCritLvl * 100) / 100
SCREENER[modeOk = 1 AND dirOk = 1 AND w2Ok = 1](wdir AS "Dir", w3Pct AS "W3 fib%", tgtShow AS "% to target", critShow AS "Invalidation", p2Pct AS "W2 retr%", ageW3 AS "W3 bars", impulses AS "Impulses", lastResetCode AS "Reset code")