This indicator brings the classic family of harmonic patterns to ProRealTime in a single, self-contained ProBuilder script. It detects six well-known XABCD figures — Bat, Gartley, Crab, Butterfly, AB=CD and Three Drives — directly on the chart, drawing each pattern as four connected segments labelled with its name and direction.
The original code was published in 2018 by Mario Jemic on FXCodeBase. This version cames with one important twist: the Carney ratios that drive the pattern classifier have been rebuilt from canonical references rather than copied verbatim, since the original Lua contains a few well-known bugs in the Bat detection block.
Harmonic patterns live on swings, not on individual bars. The first half of the indicator is therefore a complete style ZigZag built from scratch around three parameters:
Each new pivot is appended to a parallel-array structure ($pivIdx, $pivPrc, $pivTyp) and confirmed depth bars after its actual occurrence. When two pivots of the same type appear in sequence, the more extreme one replaces the previous; only true alternations (high → low → high → …) advance the pivot count.
Every time a new pivot is confirmed, the indicator takes the last five pivots (X-A-B-C-D) and computes the four canonical ratios used by Scott Carney’s Harmonic Trading:
rAB = AB / XA
rBC = BC / AB
rCD = CD / BC
rAD = AD / XA
Each pattern is then a set of bounded ranges over these ratios. For example:
| Pattern | rAB | rBC | rCD | rAD |
|---|---|---|---|---|
| **Bat** | 0.382-0.500 | 0.382-0.886 | 1.618-2.618 | 0.886 |
| **Gartley** | 0.618 | 0.382-0.886 | 1.272-1.618 | 0.786 |
| **Crab** | 0.382-0.618 | 0.382-0.886 | 2.618-3.618 | 1.618 |
| **Butterfly** | 0.786 | 0.382-0.886 | 1.618-2.618 | 1.272 |
| **AB=CD** | — | 0.618-0.786 | rCD/rAB ≈ 1 | — |
| **Three Drives** | — | — | XB/XA ≈ XD/XB ∈ 1.272-1.618 | — |
A single tolerance parameter — correction (in %) — widens every range symmetrically. The default of 25% is permissive enough to catch most real-market figures; lowering it to 10-15% produces stricter, less frequent matches.
The direction of the pattern is determined by the type of the D pivot: bullish if D is a low, bearish if D is a high. Detected patterns are stored in a separate parallel-array stack, so historical patterns remain visible as new ones appear.
Each detected pattern is drawn as four segments — green for bullish (long bias at D) and red for bearish (short bias at D) — with a text label at the X point indicating the pattern name and direction.
The indicator does not generate entry/exit signals: it is a structural detector. Typical workflows are:
| Parameter | Default | Description |
|---|---|---|
| `depth` | 12 | ZigZag pivot window (bars on each side) |
| `deviation` | 5 | Minimum % retracement between consecutive pivots |
| `backstep` | 3 | Minimum bars between consecutive pivots |
| `correction` | 25 | Tolerance (%) on the Fibonacci ratios |
| `showBat` / `showGartley` / `showCrab` / `showButterfly` / `showABCD` / `showThreeDrives` | 1 | Toggle each pattern individually (0/1) |
| `showZigzag` | 1 | Plot the underlying ZigZag for visual debugging |
| `showTargets` | 0 | Plot target lines from D at 0.886·XA and 1.272·AD |
A practical first session: load the indicator on the daily timeframe of any liquid instrument with showZigzag = 1 to validate that the pivots match what your eye sees, then disable individual patterns one by one to inspect the classifier in isolation.
A few decisions worth flagging for anyone reading the source:
// ==========================================================
// PRC_FXC Harmonic Pattern (Indicador)
// Traducción del original Lua/Indicore de Mario Jemic (FXCodeBase 2018)
// ZigZag MT-style + detector XABCD + clasificador 6 patrones armónicos
// version = 0
// 06.05.2026
// Iván González @ www.prorealcode.com
// Sharing ProRealTime knowledge
// ==========================================================
DEFPARAM DRAWONLASTBARONLY = TRUE
// ==========================================================
// === Inputs ===
// ==========================================================
depth = 12 // ZigZag: ventana simétrica del pivot (mín. barras a cada lado)
deviation = 5 // ZigZag: % mínimo de retracement entre pivots
backstep = 3 // ZigZag: barras mínimas entre pivots consecutivos
correction = 25 // Tolerancia (%) sobre los ratios Fibonacci
showBat = 1 // 0/1 — mostrar Bull/Bear Bat
showGartley = 1 // 0/1 — mostrar Bull/Bear Gartley
showCrab = 1 // 0/1 — mostrar Bull/Bear Crab
showButterfly = 1 // 0/1 — mostrar Bull/Bear Butterfly
showABCD = 1 // 0/1 — mostrar Bull/Bear AB=CD
showThreeDrives = 1 // 0/1 — mostrar Bull/Bear 3 Drives
showZigzag = 1 // 0/1 — dibujar la línea ZigZag entre pivots
showTargets = 0 // 0/1 — dibujar líneas de target (0.886·XA y 1.272·AD desde D)
// ==========================================================
// === 1. INICIALIZACIÓN ===
// ==========================================================
ONCE n = 0 // contador de pivots ZigZag
ONCE p = 0 // contador de patrones detectados
// Ventana del pivot (simétrica)
prd = depth
// Tolerancia: convertimos correction (%) a fracción
corrFrac = correction / 100
// ==========================================================
// === 2. DETECCIÓN DE PIVOTS (ZigZag MT-style) ===
// ==========================================================
// El pivot se confirma 'prd' barras después de su ocurrencia real.
// Filtros aplicados:
// - Symmetric depth: el pivot es máximo (o mínimo) en ventana 2*prd+1
// - Backstep: distancia mínima al pivot anterior
// - Deviation: % mínimo de retracement contra el pivot anterior del tipo opuesto
IF barindex >= 2*prd+1 THEN
pivBar = barindex - prd
isPivH = high[prd] >= highest[2*prd+1](high)
isPivL = low[prd] <= lowest[2*prd+1](low)
// === Procesar candidato HIGH ===
IF isPivH THEN
candPrc = high[prd]
// Cargar último pivot (escalares; no accedemos a array dentro de AND)
lastIdxH = 0
lastPrcH = 0
lastTypH = 0
IF n > 0 THEN
lastIdxH = $pivIdx[n-1]
lastPrcH = $pivPrc[n-1]
lastTypH = $pivTyp[n-1]
ENDIF
okH = 1
// Backstep
IF n > 0 THEN
IF (pivBar - lastIdxH) < backstep THEN
okH = 0
ENDIF
ENDIF
// Deviation (solo contra pivot opuesto)
IF okH = 1 THEN
IF n > 0 THEN
IF lastTypH = -1 THEN
devPctH = abs(candPrc - lastPrcH) / lastPrcH * 100
IF devPctH < deviation THEN
okH = 0
ENDIF
ENDIF
ENDIF
ENDIF
IF okH = 1 THEN
IF n = 0 THEN
$pivIdx[0] = pivBar
$pivPrc[0] = candPrc
$pivTyp[0] = 1
n = 1
ELSE
IF lastTypH = 1 THEN
// Mismo tipo consecutivo: nos quedamos con el más alto
IF candPrc > lastPrcH THEN
$pivIdx[n-1] = pivBar
$pivPrc[n-1] = candPrc
ENDIF
ELSE
// Alternancia válida: añadimos
$pivIdx[n] = pivBar
$pivPrc[n] = candPrc
$pivTyp[n] = 1
n = n + 1
ENDIF
ENDIF
ENDIF
ENDIF
// === Procesar candidato LOW ===
IF isPivL THEN
candPrc = low[prd]
lastIdxL = 0
lastPrcL = 0
lastTypL = 0
IF n > 0 THEN
lastIdxL = $pivIdx[n-1]
lastPrcL = $pivPrc[n-1]
lastTypL = $pivTyp[n-1]
ENDIF
okL = 1
IF n > 0 THEN
IF (pivBar - lastIdxL) < backstep THEN
okL = 0
ENDIF
ENDIF
IF okL = 1 THEN
IF n > 0 THEN
IF lastTypL = 1 THEN
devPctL = abs(candPrc - lastPrcL) / lastPrcL * 100
IF devPctL < deviation THEN
okL = 0
ENDIF
ENDIF
ENDIF
ENDIF
IF okL = 1 THEN
IF n = 0 THEN
$pivIdx[0] = pivBar
$pivPrc[0] = candPrc
$pivTyp[0] = -1
n = 1
ELSE
IF lastTypL = -1 THEN
// Mismo tipo consecutivo: nos quedamos con el más bajo
IF candPrc < lastPrcL THEN
$pivIdx[n-1] = pivBar
$pivPrc[n-1] = candPrc
ENDIF
ELSE
$pivIdx[n] = pivBar
$pivPrc[n] = candPrc
$pivTyp[n] = -1
n = n + 1
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
// ==========================================================
// === 3. EVALUACIÓN DE PATRÓN XABCD (sobre los 5 últimos pivots) ===
// ==========================================================
// Solo evaluamos cuando tenemos al menos 5 pivots Y el último cambió en esta barra.
evalPattern = 0
IF n >= 5 THEN
IF n <> n[1] THEN
evalPattern = 1
ENDIF
ENDIF
IF evalPattern = 1 THEN
xIdx = $pivIdx[n-5]
aIdx = $pivIdx[n-4]
bIdx = $pivIdx[n-3]
cIdx = $pivIdx[n-2]
dIdx = $pivIdx[n-1]
xPrc = $pivPrc[n-5]
aPrc = $pivPrc[n-4]
bPrc = $pivPrc[n-3]
cPrc = $pivPrc[n-2]
dPrc = $pivPrc[n-1]
dTyp = $pivTyp[n-1]
// Bull si D es low (-1), Bear si D es high (1)
isBull = 0
IF dTyp = -1 THEN
isBull = 1
ENDIF
// Magnitudes
xa = abs(xPrc - aPrc)
ab = abs(aPrc - bPrc)
bc = abs(bPrc - cPrc)
cd = abs(cPrc - dPrc)
ad = abs(aPrc - dPrc)
// Validar denominadores antes de dividir (learning 028: nada de array dentro de AND)
ratiosOk = 0
IF xa > 0 THEN
IF ab > 0 THEN
IF bc > 0 THEN
ratiosOk = 1
ENDIF
ENDIF
ENDIF
patternFound = 0
patternId = 0
// 1=Bat, 2=Gartley, 3=Crab, 4=Butterfly, 5=ABCD, 6=ThreeDrives
IF ratiosOk = 1 THEN
rAB = ab / xa
rBC = bc / ab
rCD = cd / bc
rAD = ad / xa
// ===== BAT =====
// AB=0.382-0.500 XA, BC=0.382-0.886 AB, CD=1.618-2.618 BC, AD=0.886 XA
abOk = 0
IF rAB >= 0.382 - corrFrac THEN
IF rAB <= 0.500 + corrFrac THEN
abOk = 1
ENDIF
ENDIF
bcOk = 0
IF rBC >= 0.382 - corrFrac THEN
IF rBC <= 0.886 + corrFrac THEN
bcOk = 1
ENDIF
ENDIF
cdOk = 0
IF rCD >= 1.618 - corrFrac THEN
IF rCD <= 2.618 + corrFrac THEN
cdOk = 1
ENDIF
ENDIF
adOk = 0
IF rAD >= 0.886 - corrFrac THEN
IF rAD <= 0.886 + corrFrac THEN
adOk = 1
ENDIF
ENDIF
IF abOk = 1 THEN
IF bcOk = 1 THEN
IF cdOk = 1 THEN
IF adOk = 1 THEN
IF showBat = 1 THEN
patternFound = 1
patternId = 1
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
// ===== GARTLEY =====
// AB=0.618 XA, BC=0.382-0.886 AB, CD=1.272-1.618 BC, AD=0.786 XA
IF patternFound = 0 THEN
abOk = 0
IF rAB >= 0.618 - corrFrac THEN
IF rAB <= 0.618 + corrFrac THEN
abOk = 1
ENDIF
ENDIF
bcOk = 0
IF rBC >= 0.382 - corrFrac THEN
IF rBC <= 0.886 + corrFrac THEN
bcOk = 1
ENDIF
ENDIF
cdOk = 0
IF rCD >= 1.272 - corrFrac THEN
IF rCD <= 1.618 + corrFrac THEN
cdOk = 1
ENDIF
ENDIF
adOk = 0
IF rAD >= 0.786 - corrFrac THEN
IF rAD <= 0.786 + corrFrac THEN
adOk = 1
ENDIF
ENDIF
IF abOk = 1 THEN
IF bcOk = 1 THEN
IF cdOk = 1 THEN
IF adOk = 1 THEN
IF showGartley = 1 THEN
patternFound = 1
patternId = 2
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
// ===== CRAB =====
// AB=0.382-0.618 XA, BC=0.382-0.886 AB, CD=2.618-3.618 BC, AD=1.618 XA
IF patternFound = 0 THEN
abOk = 0
IF rAB >= 0.382 - corrFrac THEN
IF rAB <= 0.618 + corrFrac THEN
abOk = 1
ENDIF
ENDIF
bcOk = 0
IF rBC >= 0.382 - corrFrac THEN
IF rBC <= 0.886 + corrFrac THEN
bcOk = 1
ENDIF
ENDIF
cdOk = 0
IF rCD >= 2.618 - corrFrac THEN
IF rCD <= 3.618 + corrFrac THEN
cdOk = 1
ENDIF
ENDIF
adOk = 0
IF rAD >= 1.618 - corrFrac THEN
IF rAD <= 1.618 + corrFrac THEN
adOk = 1
ENDIF
ENDIF
IF abOk = 1 THEN
IF bcOk = 1 THEN
IF cdOk = 1 THEN
IF adOk = 1 THEN
IF showCrab = 1 THEN
patternFound = 1
patternId = 3
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
// ===== BUTTERFLY =====
// AB=0.786 XA, BC=0.382-0.886 AB, CD=1.618-2.618 BC, AD=1.272 XA
IF patternFound = 0 THEN
abOk = 0
IF rAB >= 0.786 - corrFrac THEN
IF rAB <= 0.786 + corrFrac THEN
abOk = 1
ENDIF
ENDIF
bcOk = 0
IF rBC >= 0.382 - corrFrac THEN
IF rBC <= 0.886 + corrFrac THEN
bcOk = 1
ENDIF
ENDIF
cdOk = 0
IF rCD >= 1.618 - corrFrac THEN
IF rCD <= 2.618 + corrFrac THEN
cdOk = 1
ENDIF
ENDIF
adOk = 0
IF rAD >= 1.272 - corrFrac THEN
IF rAD <= 1.272 + corrFrac THEN
adOk = 1
ENDIF
ENDIF
IF abOk = 1 THEN
IF bcOk = 1 THEN
IF cdOk = 1 THEN
IF adOk = 1 THEN
IF showButterfly = 1 THEN
patternFound = 1
patternId = 4
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
// ===== AB=CD =====
// AB ≈ CD en magnitud, BC = 0.618-0.786 retrace de AB
IF patternFound = 0 THEN
// ratio AB:CD cercano a 1
rAcd = 1
IF ab > 0 THEN
rAcd = cd / ab
ENDIF
abcdOk = 0
IF rAcd >= 1 - corrFrac THEN
IF rAcd <= 1 + corrFrac THEN
abcdOk = 1
ENDIF
ENDIF
bcOk = 0
IF rBC >= 0.618 - corrFrac THEN
IF rBC <= 0.786 + corrFrac THEN
bcOk = 1
ENDIF
ENDIF
IF abcdOk = 1 THEN
IF bcOk = 1 THEN
IF showABCD = 1 THEN
patternFound = 1
patternId = 5
ENDIF
ENDIF
ENDIF
ENDIF
// ===== THREE DRIVES =====
// 3 extensiones progresivas: B = 1.272-1.618 XA, D = 1.272-1.618 BX (medido sobre XB)
IF patternFound = 0 THEN
xb = abs(xPrc - bPrc)
xd = abs(xPrc - dPrc)
ir1 = 0
ir2 = 0
IF xa > 0 THEN
ir1 = xb / xa
ENDIF
IF xb > 0 THEN
ir2 = xd / xb
ENDIF
ir1Ok = 0
IF ir1 >= 1.272 - corrFrac THEN
IF ir1 <= 1.618 + corrFrac THEN
ir1Ok = 1
ENDIF
ENDIF
ir2Ok = 0
IF ir2 >= 1.272 - corrFrac THEN
IF ir2 <= 1.618 + corrFrac THEN
ir2Ok = 1
ENDIF
ENDIF
IF ir1Ok = 1 THEN
IF ir2Ok = 1 THEN
IF showThreeDrives = 1 THEN
patternFound = 1
patternId = 6
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
// Guardar el patrón en arrays paralelos
IF patternFound = 1 THEN
$patId[p] = patternId
$patBull[p] = isBull
$patXIdx[p] = xIdx
$patAIdx[p] = aIdx
$patBIdx[p] = bIdx
$patCIdx[p] = cIdx
$patDIdx[p] = dIdx
$patXPrc[p] = xPrc
$patAPrc[p] = aPrc
$patBPrc[p] = bPrc
$patCPrc[p] = cPrc
$patDPrc[p] = dPrc
p = p + 1
ENDIF
ENDIF
// ==========================================================
// === 4. DIBUJADO ===
// ==========================================================
IF islastbarupdate THEN
// --- 4a. Línea ZigZag (segmentos pivot a pivot) ---
IF showZigzag = 1 THEN
IF n >= 2 THEN
FOR k = 1 TO n - 1 DO
bx0 = $pivIdx[k-1]
py0 = $pivPrc[k-1]
bx1 = $pivIdx[k]
py1 = $pivPrc[k]
DRAWSEGMENT(bx0, py0, bx1, py1) COLOURED(120, 120, 120) STYLE(line, 1)
NEXT
ENDIF
ENDIF
// --- 4b. Patrones detectados ---
IF p >= 1 THEN
FOR k = 0 TO p - 1 DO
pid = $patId[k]
pBull = $patBull[k]
xb0 = $patXIdx[k]
ab0 = $patAIdx[k]
bb0 = $patBIdx[k]
cb0 = $patCIdx[k]
db0 = $patDIdx[k]
xy0 = $patXPrc[k]
ay0 = $patAPrc[k]
by0 = $patBPrc[k]
cy0 = $patCPrc[k]
dy0 = $patDPrc[k]
// Color por dirección
IF pBull = 1 THEN
rC = 0
gC = 180
bC = 0
ELSE
rC = 200
gC = 0
bC = 0
ENDIF
// Las 4 patas del patrón XABCD
DRAWSEGMENT(xb0, xy0, ab0, ay0) COLOURED(rC, gC, bC) STYLE(line, 2)
DRAWSEGMENT(ab0, ay0, bb0, by0) COLOURED(rC, gC, bC) STYLE(line, 2)
DRAWSEGMENT(bb0, by0, cb0, cy0) COLOURED(rC, gC, bC) STYLE(line, 2)
DRAWSEGMENT(cb0, cy0, db0, dy0) COLOURED(rC, gC, bC) STYLE(line, 2)
// Posición de la etiqueta en X (offset según dirección)
offY = abs(ay0 - by0) * 0.05
IF pBull = 1 THEN
posY = xy0 - offY
ELSE
posY = xy0 + offY
ENDIF
// Etiqueta del patrón
IF pid = 1 THEN
IF pBull = 1 THEN
DRAWTEXT("Bull Bat", xb0, posY) COLOURED(rC, gC, bC)
ELSE
DRAWTEXT("Bear Bat", xb0, posY) COLOURED(rC, gC, bC)
ENDIF
ENDIF
IF pid = 2 THEN
IF pBull = 1 THEN
DRAWTEXT("Bull Gartley", xb0, posY) COLOURED(rC, gC, bC)
ELSE
DRAWTEXT("Bear Gartley", xb0, posY) COLOURED(rC, gC, bC)
ENDIF
ENDIF
IF pid = 3 THEN
IF pBull = 1 THEN
DRAWTEXT("Bull Crab", xb0, posY) COLOURED(rC, gC, bC)
ELSE
DRAWTEXT("Bear Crab", xb0, posY) COLOURED(rC, gC, bC)
ENDIF
ENDIF
IF pid = 4 THEN
IF pBull = 1 THEN
DRAWTEXT("Bull Butterfly", xb0, posY) COLOURED(rC, gC, bC)
ELSE
DRAWTEXT("Bear Butterfly", xb0, posY) COLOURED(rC, gC, bC)
ENDIF
ENDIF
IF pid = 5 THEN
IF pBull = 1 THEN
DRAWTEXT("Bull AB=CD", xb0, posY) COLOURED(rC, gC, bC)
ELSE
DRAWTEXT("Bear AB=CD", xb0, posY) COLOURED(rC, gC, bC)
ENDIF
ENDIF
IF pid = 6 THEN
IF pBull = 1 THEN
DRAWTEXT("Bull 3 Drives", xb0, posY) COLOURED(rC, gC, bC)
ELSE
DRAWTEXT("Bear 3 Drives", xb0, posY) COLOURED(rC, gC, bC)
ENDIF
ENDIF
// Targets opcionales
IF showTargets = 1 THEN
adAbs = abs(ay0 - dy0)
xaAbs = abs(xy0 - ay0)
IF pBull = 1 THEN
tgt1 = dy0 + xaAbs * 0.886
tgt2 = dy0 + adAbs * 1.272
ELSE
tgt1 = dy0 - xaAbs * 0.886
tgt2 = dy0 - adAbs * 1.272
ENDIF
DRAWSEGMENT(db0, tgt1, db0 + (db0 - ab0), tgt1) COLOURED(0, 0, 200) STYLE(dottedline, 1)
DRAWSEGMENT(db0, tgt2, db0 + (db0 - ab0), tgt2) COLOURED(0, 0, 200) STYLE(dottedline, 1)
ENDIF
NEXT
ENDIF
ENDIF
RETURN