Most pattern tools look only at price: they find five swing points that fit a shape and call it a reversal. The weak point is obvious. A head and shoulders or a harmonic shape tells you where price has been, not whether the move behind it is running out of energy.
Vdubus Pattern Gen V2 checks both. It builds the last five swing points of the market as an X-A-B-C-D structure and only marks a reversal when the momentum behind those swings agrees: the MACD histogram has to show the right sequence of peaks or troughs. It runs on two scales at once, a fast structure for entry geometry and a slow one for the macro context, and it classifies every confirmed setup into one of four families: standard reversal, climax reversal, rounded top or bottom, and the hidden “predator” pattern.
Swing highs and lows are confirmed pivots: a bar whose high (or low) is the extreme of the bars on both sides. The fast scale uses 9 bars on each side, the slow scale 24. Each scale keeps its own sequence of pivots, highs and lows in the order they were confirmed. Every time a new pivot appears, the last five of that scale become X, A, B, C and D, with D the newest.
Momentum is measured with the MACD histogram (21, 34, 5 by default, smoother than the classic 12, 26, 9). The indicator records the pivots of the histogram itself, 3 bars on each side, and reads the last three or four of them on the side that matters: histogram peaks for bearish setups, troughs for bullish ones.
Standard reversals also have to pass a shape filter. The indicator measures the AB leg as a fraction of XA and tags the structure:
By default only head and shoulders shapes are shown for the standard family; each harmonic name can be switched on separately. Climax, rounded and predator setups do not depend on the shape filter.
Fast structure:
Slow structure:
Targets and momentum:
Pattern filters:
Dashboard:
//---------------------------------------------------------------
// PRC_Vdubus Pattern Gen
// version = 0
// 08.10.2026
// Iván González @ www.prorealcode.com
// Author: Bespoke_Analysis_Engine
// Sharing ProRealTime knowledge
//--------------------------------------------------------------------//
defparam drawonlastbaronly = true
//---------------------------------------------------------------
// SETTINGS
//---------------------------------------------------------------
// Fast structure (entry geometry)
fastDepth = 9 // fast pattern scale (pivot length)
showFastLines = 0
showFastFills = 1
showFastLbl = 0
// Slow structure (macro context)
slowDepth = 24 // slow pattern scale (pivot length)
showSlowLines = 1
showSlowFills = 1
showSlowLbl = 1
// Target projections
showTgtBox = 0 // box from the pattern end (D) to the previous structural high/low (C)
// Momentum physics (MACD histogram)
fastLen = 21
slowLen = 34
sigLen = 5
momLookback = 3 // momentum pivot lookback
// Pattern filters
showStandard = 1 // standard reversals
showClimax = 1 // climax reversals (trap)
showRounded = 1 // rounded tops / bottoms
showPred = 1 // predator (hidden)
// Harmonic shape selectors
showGartley = 0
showBat = 0
showButterfly = 0
showCrab = 0
showDeep = 0 // deep / shark
showHS = 1 // head and shoulders
errTol = 0.15 // harmonic tolerance (0.01 to 0.5)
// Confluence dashboard
showDash = 0
textSize = 1 // 1 = Small, 2 = Normal, 3 = Large
// Object budget: like the original, only the most recent 500 lines,
// 500 labels and 500 boxes stay on the chart (fill edges count as lines)
maxObjects = 500
//---------------------------------------------------------------
// MOMENTUM: MACD HISTOGRAM AND ITS PIVOTS
//---------------------------------------------------------------
emaFast = exponentialaverage[fastLen](close)
emaSlow = exponentialaverage[slowLen](close)
macdL = emaFast - emaSlow
sigLine = exponentialaverage[sigLen](macdL)
histL = macdL - sigLine
// Momentum pivots are stored with a counter that is a plain variable:
// it is rewound on every tick, so a pivot that appears and disappears
// inside the live bar is simply overwritten by the next one.
once nWH = 0
once nWL = 0
lb = momLookback
momWarm = slowLen + sigLen + 2 * lb
IF barindex >= momWarm THEN
IF histL[lb] > highest[lb](histL)[lb + 1] AND histL[lb] >= highest[lb](histL) THEN
nWH = nWH + 1
$wHv[nWH] = histL[lb]
ENDIF
IF lowest[lb](histL)[lb + 1] > histL[lb] AND lowest[lb](histL) >= histL[lb] THEN
nWL = nWL + 1
$wLv[nWL] = histL[lb]
ENDIF
ENDIF
// Momentum validation (bearish side reads the histogram highs)
stdBear = 0
climBear = 0
rndBear = 0
predBear = 0
IF nWH >= 2 THEN
IF $wHv[nWH] > $wHv[nWH - 1] THEN
predBear = 1
ENDIF
ENDIF
IF nWH >= 3 THEN
h1 = $wHv[nWH - 2]
h2 = $wHv[nWH - 1]
h3 = $wHv[nWH]
IF h1 > h2 AND h2 >= h3 THEN
stdBear = 1
ENDIF
IF h2 >= h1 AND h2 > h3 THEN
climBear = 1
ENDIF
ENDIF
IF nWH >= 4 THEN
IF $wHv[nWH - 3] > $wHv[nWH - 2] AND $wHv[nWH - 2] > $wHv[nWH - 1] AND $wHv[nWH - 1] > $wHv[nWH] THEN
rndBear = 1
ENDIF
ENDIF
// Momentum validation (bullish side reads the histogram lows)
stdBull = 0
climBull = 0
rndBull = 0
predBull = 0
IF nWL >= 2 THEN
IF $wLv[nWL - 1] > $wLv[nWL] THEN
predBull = 1
ENDIF
ENDIF
IF nWL >= 3 THEN
l1 = $wLv[nWL - 2]
l2 = $wLv[nWL - 1]
l3 = $wLv[nWL]
IF l2 > l1 AND l3 >= l2 THEN
stdBull = 1
ENDIF
IF l1 >= l2 AND l3 > l2 THEN
climBull = 1
ENDIF
ENDIF
IF nWL >= 4 THEN
IF $wLv[nWL - 2] > $wLv[nWL - 3] AND $wLv[nWL - 1] > $wLv[nWL - 2] AND $wLv[nWL] > $wLv[nWL - 1] THEN
rndBull = 1
ENDIF
ENDIF
//---------------------------------------------------------------
// PRICE STRUCTURE PIVOTS (fast and slow)
//---------------------------------------------------------------
fd = fastDepth
sd = slowDepth
fPH = 0
fPL = 0
sPH = 0
sPL = 0
IF barindex >= 2 * fd THEN
IF high[fd] > highest[fd](high)[fd + 1] AND high[fd] >= highest[fd](high) THEN
fPH = 1
ENDIF
IF lowest[fd](low)[fd + 1] > low[fd] AND lowest[fd](low) >= low[fd] THEN
fPL = 1
ENDIF
ENDIF
IF barindex >= 2 * sd THEN
IF high[sd] > highest[sd](high)[sd + 1] AND high[sd] >= highest[sd](high) THEN
sPH = 1
ENDIF
IF lowest[sd](low)[sd + 1] > low[sd] AND lowest[sd](low) >= low[sd] THEN
sPL = 1
ENDIF
ENDIF
// label offset from the pattern end
IF barindex >= 14 THEN
atrOff = averagetruerange[14](close) * 0.5
ELSE
atrOff = (high - low) * 0.5
ENDIF
//---------------------------------------------------------------
// PATTERN ENGINE
// engK = 1 fast structure, engK = 2 slow structure
// sideK = 1 pivot high (bearish), sideK = 2 pivot low (bullish)
// Pivots of both engines share one array: slot 2*n+engK.
//---------------------------------------------------------------
once nF = 0
once nS = 0
once nK = 0
FOR engK = 1 TO 2 DO
IF engK = 1 THEN
nZ = nF
dep = fd
eLbl = showFastLbl
ELSE
nZ = nS
dep = sd
eLbl = showSlowLbl
ENDIF
FOR sideK = 1 TO 2 DO
isPiv = 0
pv = 0
IF engK = 1 AND sideK = 1 AND fPH THEN
isPiv = 1
pv = high[fd]
ELSIF engK = 1 AND sideK = 2 AND fPL THEN
isPiv = 1
pv = low[fd]
ELSIF engK = 2 AND sideK = 1 AND sPH THEN
isPiv = 1
pv = high[sd]
ELSIF engK = 2 AND sideK = 2 AND sPL THEN
isPiv = 1
pv = low[sd]
ENDIF
IF isPiv THEN
nZ = nZ + 1
$zP[2 * nZ + engK] = pv
$zL[2 * nZ + engK] = barindex - dep
IF nZ >= 5 THEN
// D = newest pivot, X = fifth newest
yD = $zP[2 * nZ + engK]
yC = $zP[2 * (nZ - 1) + engK]
yB = $zP[2 * (nZ - 2) + engK]
yA = $zP[2 * (nZ - 3) + engK]
yX = $zP[2 * (nZ - 4) + engK]
xD = $zL[2 * nZ + engK]
xC = $zL[2 * (nZ - 1) + engK]
xB = $zL[2 * (nZ - 2) + engK]
xA = $zL[2 * (nZ - 3) + engK]
xX = $zL[2 * (nZ - 4) + engK]
IF sideK = 1 THEN
sg = 1
mStd = stdBear
mClim = climBear
mRnd = rndBear
mPred = predBear
ELSE
sg = -1
mStd = stdBull
mClim = climBull
mRnd = rndBull
mPred = predBull
ENDIF
// harmonic ratios (the original always evaluates XD at 1.27)
xaLen = abs(yA - yX)
abLen = abs(yB - yA)
IF xaLen = 0 THEN
xbR = 0
ELSE
xbR = abLen / xaLen
ENDIF
xdR = 1.27
IF errTol > abs(xbR - 0.618) AND errTol > abs(xdR - 0.786) THEN
hName = 1
ELSIF xbR >= 0.382 - errTol AND 0.5 + errTol >= xbR AND errTol > abs(xdR - 0.886) THEN
hName = 2
ELSIF errTol > abs(xbR - 0.786) AND xdR >= 1.27 - errTol AND 1.618 + errTol >= xdR THEN
hName = 3
ELSIF xbR >= 0.382 - errTol AND 0.618 + errTol >= xbR AND errTol > abs(xdR - 1.618) THEN
hName = 4
ELSIF xdR > 1.0 THEN
hName = 5
ELSE
hName = 6
ENDIF
// head and shoulders: B is the head, beyond X and D
isHS = 0
IF showHS AND sg * (yB - yX) > 0 AND sg * (yB - yD) > 0 THEN
isHS = 1
ENDIF
shouldDraw = 0
IF isHS THEN
shouldDraw = 1
ELSIF hName = 1 AND showGartley THEN
shouldDraw = 1
ELSIF hName = 2 AND showBat THEN
shouldDraw = 1
ELSIF hName = 3 AND showButterfly THEN
shouldDraw = 1
ELSIF hName = 4 AND showCrab THEN
shouldDraw = 1
ELSIF hName = 5 AND showDeep THEN
shouldDraw = 1
ELSIF hName = 6 THEN
shouldDraw = 1
ENDIF
// record types, in the original order:
// 1 standard, 2 climax, 3 rounded (label only), 4 predator
FOR recK = 1 TO 4 DO
doRec = 0
nmCode = 0
IF recK = 1 AND showStandard AND mStd AND shouldDraw THEN
doRec = 1
IF isHS THEN
nmCode = 7
ELSE
nmCode = hName
ENDIF
ELSIF recK = 2 AND showClimax AND mClim THEN
doRec = 1
ELSIF recK = 3 AND showRounded AND mRnd AND eLbl THEN
doRec = 1
ELSIF recK = 4 AND showPred AND mStd = 0 AND sg * (yX - yD) > 0 AND mPred THEN
doRec = 1
ENDIF
IF doRec THEN
nK = nK + 1
$kT[nK] = recK
$kN[nK] = nmCode
$kB[nK] = sideK - 1
$kE[nK] = engK
$kO[nK] = atrOff
$kX0[nK] = xX
$kX1[nK] = xA
$kX2[nK] = xB
$kX3[nK] = xC
$kX4[nK] = xD
$kY0[nK] = yX
$kY1[nK] = yA
$kY2[nK] = yB
$kY3[nK] = yC
$kY4[nK] = yD
ENDIF
NEXT
ENDIF
ENDIF
NEXT
IF engK = 1 THEN
nF = nZ
ELSE
nS = nZ
ENDIF
NEXT
//---------------------------------------------------------------
// DRAWING (newest first, within the object budget)
//---------------------------------------------------------------
IF islastbarupdate THEN
lineCnt = 0
lblCnt = 0
boxCnt = 0
lineFull = 0
lblFull = 0
boxFull = 0
IF nK >= 1 THEN
FOR k = nK DOWNTO 1 DO
kT = $kT[k]
kN = $kN[k]
kBull = $kB[k]
IF $kE[k] = 1 THEN
doLn = showFastLines
doFl = showFastFills
doLb = showFastLbl
ELSE
doLn = showSlowLines
doFl = showSlowFills
doLb = showSlowLbl
ENDIF
// colours: standard red / green, climax orange, rounded navy, predator purple
IF kT = 1 AND kBull THEN
cR = 8
cG = 153
cB = 129
ELSIF kT = 1 THEN
cR = 242
cG = 54
cB = 69
ELSIF kT = 2 THEN
cR = 255
cG = 152
cB = 0
ELSIF kT = 3 THEN
cR = 49
cG = 27
cB = 146
ELSE
cR = 156
cG = 39
cB = 176
ENDIF
x0 = $kX0[k]
x1 = $kX1[k]
x2 = $kX2[k]
x3 = $kX3[k]
x4 = $kX4[k]
y0 = $kY0[k]
y1 = $kY1[k]
y2 = $kY2[k]
y3 = $kY3[k]
y4 = $kY4[k]
isStruct = 1
IF kT = 3 THEN
isStruct = 0
ENDIF
// lines and fills (fill edges are invisible lines in the original)
IF isStruct THEN
need = 4 * doLn + 4 * doFl
IF need > 0 AND lineFull = 0 THEN
IF lineCnt + need > maxObjects THEN
lineFull = 1
ELSE
lineCnt = lineCnt + need
IF doLn AND kT = 4 THEN
DRAWSEGMENT(x0, y0, x1, y1) COLOURED(cR, cG, cB, 255) STYLE(line, 2)
DRAWSEGMENT(x1, y1, x2, y2) COLOURED(cR, cG, cB, 255) STYLE(line, 2)
DRAWSEGMENT(x2, y2, x3, y3) COLOURED(cR, cG, cB, 255) STYLE(line, 2)
DRAWSEGMENT(x3, y3, x4, y4) COLOURED(cR, cG, cB, 255) STYLE(line, 2)
ELSIF doLn THEN
DRAWSEGMENT(x0, y0, x1, y1) COLOURED(cR, cG, cB, 255) STYLE(line, 1)
DRAWSEGMENT(x1, y1, x2, y2) COLOURED(cR, cG, cB, 255) STYLE(line, 1)
DRAWSEGMENT(x2, y2, x3, y3) COLOURED(cR, cG, cB, 255) STYLE(line, 1)
DRAWSEGMENT(x3, y3, x4, y4) COLOURED(cR, cG, cB, 255) STYLE(line, 1)
ENDIF
IF doFl THEN
DRAWTRIANGLE(x0, y0, x1, y1, x2, y2) COLOURED(cR, cG, cB, 0) FILLCOLOR(cR, cG, cB, 20)
DRAWTRIANGLE(x2, y2, x3, y3, x4, y4) COLOURED(cR, cG, cB, 0) FILLCOLOR(cR, cG, cB, 20)
ENDIF
ENDIF
ENDIF
// target box: from D to the previous structural high / low (C)
IF showTgtBox AND boxFull = 0 THEN
IF boxCnt >= maxObjects THEN
boxFull = 1
ELSE
boxCnt = boxCnt + 1
DRAWRECTANGLE(x4, y3, x4 + 20, y4) COLOURED(cR, cG, cB, 51) FILLCOLOR(cR, cG, cB, 13)
ENDIF
ENDIF
ENDIF
// labels (rounded patterns are only recorded when labels are on)
IF (kT = 3 OR doLb) AND lblFull = 0 THEN
IF lblCnt >= maxObjects THEN
lblFull = 1
ELSE
lblCnt = lblCnt + 1
IF kBull THEN
ly = y4 - $kO[k]
ELSE
ly = y4 + $kO[k]
ENDIF
IF kT = 1 AND kBull THEN
IF kN = 7 THEN
DRAWTEXT("Bullish Inv H&S", x4, ly, SansSerif, Bold, 9) COLOURED(cR, cG, cB)
ELSIF kN = 1 THEN
DRAWTEXT("Bullish Gartley", x4, ly, SansSerif, Bold, 9) COLOURED(cR, cG, cB)
ELSIF kN = 2 THEN
DRAWTEXT("Bullish Bat", x4, ly, SansSerif, Bold, 9) COLOURED(cR, cG, cB)
ELSIF kN = 3 THEN
DRAWTEXT("Bullish Butterfly", x4, ly, SansSerif, Bold, 9) COLOURED(cR, cG, cB)
ELSIF kN = 4 THEN
DRAWTEXT("Bullish Crab", x4, ly, SansSerif, Bold, 9) COLOURED(cR, cG, cB)
ELSIF kN = 5 THEN
DRAWTEXT("Bullish Deep Pattern", x4, ly, SansSerif, Bold, 9) COLOURED(cR, cG, cB)
ELSE
DRAWTEXT("Bullish Retracement", x4, ly, SansSerif, Bold, 9) COLOURED(cR, cG, cB)
ENDIF
ELSIF kT = 1 THEN
IF kN = 7 THEN
DRAWTEXT("Bearish H&S", x4, ly, SansSerif, Bold, 9) COLOURED(cR, cG, cB)
ELSIF kN = 1 THEN
DRAWTEXT("Bearish Gartley", x4, ly, SansSerif, Bold, 9) COLOURED(cR, cG, cB)
ELSIF kN = 2 THEN
DRAWTEXT("Bearish Bat", x4, ly, SansSerif, Bold, 9) COLOURED(cR, cG, cB)
ELSIF kN = 3 THEN
DRAWTEXT("Bearish Butterfly", x4, ly, SansSerif, Bold, 9) COLOURED(cR, cG, cB)
ELSIF kN = 4 THEN
DRAWTEXT("Bearish Crab", x4, ly, SansSerif, Bold, 9) COLOURED(cR, cG, cB)
ELSIF kN = 5 THEN
DRAWTEXT("Bearish Deep Pattern", x4, ly, SansSerif, Bold, 9) COLOURED(cR, cG, cB)
ELSE
DRAWTEXT("Bearish Retracement", x4, ly, SansSerif, Bold, 9) COLOURED(cR, cG, cB)
ENDIF
ELSIF kT = 2 THEN
DRAWTEXT("CLIMAX REVERSAL", x4, ly, SansSerif, Bold, 9) COLOURED(cR, cG, cB)
ELSIF kT = 3 AND kBull THEN
DRAWTEXT("ROUNDED BOTTOM", x4, ly, SansSerif, Bold, 9) COLOURED(cR, cG, cB)
ELSIF kT = 3 THEN
DRAWTEXT("ROUNDED TOP", x4, ly, SansSerif, Bold, 9) COLOURED(cR, cG, cB)
ELSE
DRAWTEXT("PREDATOR", x4, ly, SansSerif, Bold, 9) COLOURED(cR, cG, cB)
ENDIF
ENDIF
ENDIF
IF lineFull AND lblFull AND (boxFull OR showTgtBox = 0) THEN
BREAK
ENDIF
NEXT
ENDIF
//------------------------------------------------------------
// CONFLUENCE DASHBOARD: which side carries the stronger last
// momentum pivot
//------------------------------------------------------------
IF showDash THEN
lastBull = 0
lastBear = 0
IF nWL >= 1 THEN
lastBull = $wLv[nWL]
ENDIF
IF nWH >= 1 THEN
lastBear = $wHv[nWH]
ENDIF
IF abs(lastBull) > abs(lastBear) THEN
bullStrong = 1
sR = 8
sG = 153
sB = 129
ELSE
bullStrong = 0
sR = 242
sG = 54
sB = 69
ENDIF
IF textSize = 3 THEN
sc = 1.4
ELSIF textSize = 2 THEN
sc = 1.2
ELSE
sc = 1
ENDIF
w0 = round(110 * sc)
w1 = round(95 * sc)
rowH = round(22 * sc)
// fixed in the top right corner, measured in pixels from that corner and
// shifted left so the price scale does not cover the right cell
xIn = 0 - 100
xMid = xIn - w1
xOut = xMid - w0
yA0 = 0 - 15
yA1 = yA0 - rowH
cx0 = round((xOut + xMid) / 2)
cx1 = round((xMid + xIn) / 2)
cy = round((yA0 + yA1) / 2)
DRAWRECTANGLE(xOut, yA0, xMid, yA1) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(255, 255, 255, 255) FILLCOLOR(0, 0, 0, 127)
DRAWRECTANGLE(xMid, yA0, xIn, yA1) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(255, 255, 255, 255) FILLCOLOR(sR, sG, sB, 255)
// the font size has to be a literal, hence one branch per size
IF textSize = 3 THEN
DRAWTEXT("Opposing Force", cx0, cy, SansSerif, Standard, 13) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(255, 255, 255)
IF bullStrong THEN
DRAWTEXT("Bulls Strong", cx1, cy, SansSerif, Standard, 13) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(255, 255, 255)
ELSE
DRAWTEXT("Bears Strong", cx1, cy, SansSerif, Standard, 13) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(255, 255, 255)
ENDIF
ELSIF textSize = 2 THEN
DRAWTEXT("Opposing Force", cx0, cy, SansSerif, Standard, 11) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(255, 255, 255)
IF bullStrong THEN
DRAWTEXT("Bulls Strong", cx1, cy, SansSerif, Standard, 11) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(255, 255, 255)
ELSE
DRAWTEXT("Bears Strong", cx1, cy, SansSerif, Standard, 11) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(255, 255, 255)
ENDIF
ELSE
DRAWTEXT("Opposing Force", cx0, cy, SansSerif, Standard, 9) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(255, 255, 255)
IF bullStrong THEN
DRAWTEXT("Bulls Strong", cx1, cy, SansSerif, Standard, 9) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(255, 255, 255)
ELSE
DRAWTEXT("Bears Strong", cx1, cy, SansSerif, Standard, 9) ANCHOR(TOPRIGHT, XSHIFT, YSHIFT) COLOURED(255, 255, 255)
ENDIF
ENDIF
ENDIF
ENDIF
RETURN