Every harmonic pattern indicator has the same limitation baked in: somebody else decided which patterns exist. You get Gartley, Bat, Butterfly and Crab with the ratios the author chose, and if you want to test a variation, or a pattern that does not have a name yet, there is nothing to turn.
This indicator, built on the BYO Pattern concept by Trendoscope, inverts that. It ships with no patterns at all. Instead you describe the shape you are looking for as a set of leg ratios, and the scanner finds every structure that matches. Want a Gartley with a tighter D leg? Change one number. Want to know whether a 0.5 retracement works better than a 0.618 on your instrument? Change it and look.
On top of that, the search is not run on a single swing size. The indicator builds a recursive zigzag – level 0 is an ordinary zigzag, level 1 is the zigzag of level 0’s pivots, and so on – and looks for your pattern at every level at once. The same description finds the small structures and the large ones without touching a setting.
The default configuration reproduces the Three Drives pattern.
The recursive zigzag. Level 0 marks a pivot whenever the current bar holds the highest high or the lowest low of the last zzLen bars. Each higher level is distilled from the one below it: a pivot survives only if it is an extreme against its own neighbours of the same side, meaning a high must exceed both the previous high and the following high. Consecutive survivors of the same side are collapsed onto the most extreme one, so every level comes out properly alternating high-low-high-low.
The pattern description. A structure is read as five points (X, A, B, C, D) or six (X, A, B, C, D, E). Eight ratios can be measured on it, and each one has an on/off switch and a range:
Every measurement is the same calculation: the size of one leg divided by the size of the leg it is compared against, so a value below 1 is a retracement and a value above 1 is an extension.
Ranges and tolerance. If you set the start and end of a ratio to the same number, the indicator widens it by errPct on both sides. With the default 20%, asking for 0.618 actually accepts anything between 0.4944 and 0.7416. If you set start and end to different numbers, the range is taken literally and errPct is ignored. A structure is reported only when every enabled ratio falls inside its range.
The reversal zone. The ratios that describe where the last point should land are also projected forward as a price band, and the bands are intersected into a single PRZ – potential reversal zone. For six-point patterns the projecting ratios are CDE, ABE and ADE; for five-point ones they are BCD, XAD and XCD. The more of them you enable, the tighter the zone gets, because each one has to agree with the others.
The point of the indicator is section 4, so here are working recipes. Everything not listed stays switched off.
Three Drives (six points, the default). Three consecutive pushes, each retracing 0.618 of the previous one and extending 1.272 beyond it:
nPiv = 60.618, ABC 1.272, BCD 0.618, CDE 1.272Gartley (five points):
nPiv = 50.618, ABC 0.382 to 0.886, BCD 1.13 to 1.618, XAD 0.786Bat (five points):
nPiv = 50.382 to 0.5, ABC 0.382 to 0.886, BCD 1.618 to 2.618, XAD 0.886Butterfly (five points):
nPiv = 50.786, ABC 0.382 to 0.886, BCD 1.618 to 2.24, XAD 1.27 to 1.618Crab (five points):
nPiv = 50.382 to 0.618, ABC 0.382 to 0.886, BCD 2.24 to 3.618, XAD 1.618Note the difference in how the two forms are written. Setting a single value such as 0.786 means “0.786 give or take errPct“. Setting a span such as 0.382 to 0.886 means exactly that span, no tolerance added. Most classic harmonics mix the two: the defining leg is a single value, the loose legs are spans.
A practical warning: the defaults are strict. Four ratios at once, each within 20%, is a narrow filter. On the S&P 500 daily over the last eleven years the default Three Drives description matches once. That is not a fault, it is what asking for four simultaneous conditions costs. If you want to see the machinery working while you tune, raise errPct to 40 or 50 first, then tighten.
Zigzag
Pattern
Ratios
Eight blocks of three settings – useXAB / xabS / xabE and so on. The switch enables the ratio, the two values are the start and end of its range.
Display
dashX if the panel collides with your price scale.The zigzag does not wait for confirmation: a pivot is marked as soon as the current bar holds the extreme of its window, which means the most recent pivot can still move while its bar is forming. That is deliberate, because it is what lets a pattern be reported at the moment its last point lands in the reversal zone rather than several bars later. But it has a consequence worth stating plainly: the newest structure on the chart is provisional until the swing that defines it is complete.
Set useRT = 0 if you would rather trade confirmed structures only. The scan then runs one bar behind and nothing on the chart moves once drawn.
One further point on what the indicator does and does not claim. A match means the geometry you described is present. It says nothing about what price does next. The scanner identifies; it does not validate. If you want to know whether a description has an edge on your instrument, that is a separate exercise, and the ratio labels on the chart are there to make it possible.
//-----------------------------------------------------------//
//PRC_BYO Pattern V1 (by Trendoscope)
//version = 0
//31.08.26
//Ivan Gonzalez @ www.prorealcode.com
//Sharing ProRealTime knowledge
//-----------------------------------------------------------//
// Build your own harmonic pattern. Instead of hard coding a
// pattern, you describe it through the retracement ratios of its
// legs (XAB, ABC, BCD, CDE, XAD, XCD, ABE, ADE) and the scanner
// reports every 5 or 6 pivot structure that matches, on every
// level of a recursive zigzag.
// Default settings reproduce the "Three Drives" pattern.
// APPLY ON THE PRICE CHART (overlay).
//-----------------------------------------------------------//
defparam drawonlastbaronly = true
//-----Zigzag settings---------------------------------------//
zzLen = 5 //Zigzag length for level 0
zzDepth = 50 //Max number of level 0 pivots kept in memory
useRT = 1 //1 = use the running bar, 0 = confirmed bars only
maxLev = 5 //Max recursive zigzag level to scan
//-----Pattern settings--------------------------------------//
nPiv = 6 //Number of pivots in the pattern (5 or 6)
errPct = 20 //Error threshold when a ratio is absolute, in %
useLog = 0 //1 = compute the ratios on a log scale
//-----Ratios------------------------------------------------//
// Each ratio has an on/off switch and a range. When start = end
// the range is widened by errPct on both sides.
useXAB = 1 //XAB leg. Valid for 5 and 6 pivot patterns
xabS = 0.618
xabE = 0.618
useABC = 1 //ABC leg. Valid for 5 and 6 pivot patterns
abcS = 1.272
abcE = 1.272
useBCD = 1 //BCD leg. Valid for 5 and 6 pivot patterns
bcdS = 0.618
bcdE = 0.618
useCDE = 1 //CDE leg. 6 pivot patterns only
cdeS = 1.272
cdeE = 1.272
useXAD = 0 //XAD leg. Valid for 5 and 6 pivot patterns
xadS = 0.382
xadE = 0.618
useXCD = 0 //XCD leg. Valid for 5 and 6 pivot patterns
xcdS = 0.382
xcdE = 1.000
useABE = 0 //ABE leg. 6 pivot patterns only
abeS = 1.000
abeE = 2.618
useADE = 0 //ADE leg. 6 pivot patterns only
adeS = 1.000
adeE = 2.618
//-----Display-----------------------------------------------//
maxShow = 6 //Patterns kept on the chart (newest first)
showPiv = 1 //1 = draw the XABCD(E) pivot labels
showRat = 1 //1 = draw the ratio lines and their values
showPan = 1 //1 = draw the summary panel
showPrz = 1 //1 = draw the potential reversal zone
dashX = -440 //Panel: label column, px from the right edge
dashCol = 140 //Panel: gap to the value column, in px
//-----------------------------------------------------------//
//-----Guards on the settings--------------------------------//
// 5 pivot patterns have no E point: the three ratios that need it
// are switched off instead of raising an error.
IF nPiv <> 6 THEN
nPiv = 5
useCDE = 0
useABE = 0
useADE = 0
ENDIF
IF useRT = 1 THEN
zzOff = 0
ELSE
zzOff = 1
ENDIF
// Both stores are ring buffers with ONE SPARE SLOT. While a bar is
// still alive PRT rewinds the scalars on every tick but not the $
// arrays, so the slot a tick has just written can be left orphaned
// if the next tick takes another branch. The spare slot is always
// the next write position, and it is never part of the read window,
// so an orphan write can never be read back as valid data.
zzRing = zzDepth + 1
pRing = maxShow + 1
//-----------------------------------------------------------//
//-----Ratio ranges (computed once per bar)------------------//
IF xabS = xabE THEN
xabLo = xabS * (100 - errPct) / 100
xabHi = xabE * (100 + errPct) / 100
ELSE
xabLo = xabS
xabHi = xabE
ENDIF
IF abcS = abcE THEN
abcLo = abcS * (100 - errPct) / 100
abcHi = abcE * (100 + errPct) / 100
ELSE
abcLo = abcS
abcHi = abcE
ENDIF
IF bcdS = bcdE THEN
bcdLo = bcdS * (100 - errPct) / 100
bcdHi = bcdE * (100 + errPct) / 100
ELSE
bcdLo = bcdS
bcdHi = bcdE
ENDIF
IF cdeS = cdeE THEN
cdeLo = cdeS * (100 - errPct) / 100
cdeHi = cdeE * (100 + errPct) / 100
ELSE
cdeLo = cdeS
cdeHi = cdeE
ENDIF
IF xadS = xadE THEN
xadLo = xadS * (100 - errPct) / 100
xadHi = xadE * (100 + errPct) / 100
ELSE
xadLo = xadS
xadHi = xadE
ENDIF
IF xcdS = xcdE THEN
xcdLo = xcdS * (100 - errPct) / 100
xcdHi = xcdE * (100 + errPct) / 100
ELSE
xcdLo = xcdS
xcdHi = xcdE
ENDIF
IF abeS = abeE THEN
abeLo = abeS * (100 - errPct) / 100
abeHi = abeE * (100 + errPct) / 100
ELSE
abeLo = abeS
abeHi = abeE
ENDIF
IF adeS = adeE THEN
adeLo = adeS * (100 - errPct) / 100
adeHi = adeE * (100 + errPct) / 100
ELSE
adeLo = adeS
adeHi = adeE
ENDIF
//-----------------------------------------------------------//
//-----Persistent state--------------------------------------//
ONCE nzTot = 0 //Total level 0 pivots ever produced
ONCE nTot = 0 //Total patterns ever identified
ONCE prevDir = 1 //Direction of the last level 0 pivot
//-----------------------------------------------------------//
//-----LEVEL 0 ZIGZAG: pivot detection-----------------------//
// A pivot is confirmed when the window extreme sits exactly on the
// evaluated bar. highestbars / lowestbars return a positive
// distance in PRT, so the test is against zzOff, not against 0.
// The pivot store is a ring buffer: every write is a plain indexed
// assignment, which keeps it idempotent while a bar is still alive.
newPivot = 0
IF barindex >= zzLen + zzOff THEN
hb = highestbars[zzLen](high)
lb = lowestbars[zzLen](low)
IF hb = zzOff AND lb <> zzOff THEN
pvDir = 1
pvVal = high[zzOff]
ELSIF lb = zzOff AND hb <> zzOff THEN
pvDir = -1
pvVal = low[zzOff]
ELSIF hb = zzOff AND lb = zzOff THEN
pvDir = prevDir
IF pvDir = 1 THEN
pvVal = high[zzOff]
ELSE
pvVal = low[zzOff]
ENDIF
ELSE
pvDir = 0
pvVal = 0
ENDIF
IF pvDir <> 0 THEN
IF pvDir = 1 THEN
isHi = 1
ELSE
isHi = 0
ENDIF
pvBar = barindex - zzOff
IF nzTot = 0 THEN
// First pivot is seeded without any filter
$zI[0] = pvBar
$zP[0] = pvVal
$zH[0] = isHi
nzTot = 1
newPivot = 1
ELSE
lp = (nzTot - 1) MOD zzRing
IF $zH[lp] = isHi THEN
// Same side: stretch the running pivot if it improves
IF isHi = 1 THEN
IF pvVal > $zP[lp] THEN
$zI[lp] = pvBar
$zP[lp] = pvVal
newPivot = 1
ENDIF
ELSE
IF pvVal < $zP[lp] THEN
$zI[lp] = pvBar
$zP[lp] = pvVal
newPivot = 1
ENDIF
ENDIF
ELSE
// Side change: append a new pivot
np = nzTot MOD zzRing
$zI[np] = pvBar
$zP[np] = pvVal
$zH[np] = isHi
nzTot = nzTot + 1
newPivot = 1
ENDIF
ENDIF
prevDir = pvDir
ENDIF
ENDIF
//-----------------------------------------------------------//
//-----RECURSIVE SCAN (only when a pivot moved)--------------//
IF newPivot = 1 THEN
// Unroll the ring into the working window, oldest first
nCnt = min(nzTot, zzDepth)
stK = (nzTot - nCnt) MOD zzRing
nw = 0
FOR q = 0 TO nCnt - 1 DO
rk = stK + q
IF rk >= zzRing THEN
rk = rk - zzRing
ENDIF
$wI[nw] = $zI[rk]
$wP[nw] = $zP[rk]
$wH[nw] = $zH[rk]
nw = nw + 1
NEXT
FOR lev = 0 TO maxLev DO
IF nw >= nPiv THEN
//-----Candidate: the last nPiv pivots of this level-----//
// Slots are always X A B C D E. On a 5 pivot pattern the
// last pivot is D and E is aliased onto it, exactly as the
// original does.
kE = nw - 1
IF nPiv = 6 THEN
kD = nw - 2
kC = nw - 3
kB = nw - 4
kA = nw - 5
kX = nw - 6
ELSE
kD = nw - 1
kC = nw - 2
kB = nw - 3
kA = nw - 4
kX = nw - 5
ENDIF
pX = $wP[kX]
pA = $wP[kA]
pB = $wP[kB]
pC = $wP[kC]
pD = $wP[kD]
pE = $wP[kE]
iX = $wI[kX]
iA = $wI[kA]
iB = $wI[kB]
iC = $wI[kC]
iD = $wI[kD]
iE = $wI[kE]
// Log scale is only applied when every price is positive
useLg = 0
IF useLog = 1 THEN
IF min(min(pX, pA), min(min(pB, pC), min(pD, pE))) > 0 THEN
useLg = 1
ENDIF
ENDIF
IF useLg = 1 THEN
vX = log(pX)
vA = log(pA)
vB = log(pB)
vC = log(pC)
vD = log(pD)
vE = log(pE)
ELSE
vX = pX
vA = pA
vB = pB
vC = pC
vD = pD
vE = pE
ENDIF
//-----Leg ratios------------------------------------//
// retracementRatio(a,b,c) = |c-b| / |b-a|
dXA = abs(vA - vX)
dAB = abs(vB - vA)
dBC = abs(vC - vB)
dCD = abs(vD - vC)
dXC = abs(vC - vX)
dAD = abs(vD - vA)
rXAB = 0
IF dXA > 0 THEN
rXAB = dAB / dXA
ENDIF
rABC = 0
IF dAB > 0 THEN
rABC = dBC / dAB
ENDIF
rBCD = 0
IF dBC > 0 THEN
rBCD = dCD / dBC
ENDIF
rCDE = 0
IF dCD > 0 THEN
rCDE = abs(vE - vD) / dCD
ENDIF
rXAD = 0
IF dXA > 0 THEN
rXAD = abs(vD - vA) / dXA
ENDIF
rXCD = 0
IF dXC > 0 THEN
rXCD = abs(vD - vC) / dXC
ENDIF
rABE = 0
IF dAB > 0 THEN
rABE = abs(vE - vB) / dAB
ENDIF
rADE = 0
IF dAD > 0 THEN
rADE = abs(vE - vD) / dAD
ENDIF
//-----Validation------------------------------------//
okPat = 1
IF useXAB = 1 THEN
IF rXAB < xabLo THEN
okPat = 0
ENDIF
IF rXAB > xabHi THEN
okPat = 0
ENDIF
ENDIF
IF useABC = 1 THEN
IF rABC < abcLo THEN
okPat = 0
ENDIF
IF rABC > abcHi THEN
okPat = 0
ENDIF
ENDIF
IF useBCD = 1 THEN
IF rBCD < bcdLo THEN
okPat = 0
ENDIF
IF rBCD > bcdHi THEN
okPat = 0
ENDIF
ENDIF
IF useCDE = 1 THEN
IF rCDE < cdeLo THEN
okPat = 0
ENDIF
IF rCDE > cdeHi THEN
okPat = 0
ENDIF
ENDIF
IF useXAD = 1 THEN
IF rXAD < xadLo THEN
okPat = 0
ENDIF
IF rXAD > xadHi THEN
okPat = 0
ENDIF
ENDIF
IF useXCD = 1 THEN
IF rXCD < xcdLo THEN
okPat = 0
ENDIF
IF rXCD > xcdHi THEN
okPat = 0
ENDIF
ENDIF
IF useABE = 1 THEN
IF rABE < abeLo THEN
okPat = 0
ENDIF
IF rABE > abeHi THEN
okPat = 0
ENDIF
ENDIF
IF useADE = 1 THEN
IF rADE < adeLo THEN
okPat = 0
ENDIF
IF rADE > adeHi THEN
okPat = 0
ENDIF
ENDIF
IF okPat = 1 THEN
//-----Already registered?------------------------//
// Two patterns are the same when every pivot but the
// newest one matches, exactly as the original does.
// The scan walks the read window only, so the spare
// slot is left out: a tick by tick re-evaluation of the
// same candidate refreshes its slot instead of being
// rejected as a duplicate of itself.
nKeep = min(nTot, maxShow)
stP = (nTot - nKeep) MOD pRing
dupPat = 0
IF nKeep > 0 THEN
FOR q = 0 TO nKeep - 1 DO
sq = stP + q
IF sq >= pRing THEN
sq = sq - pRing
ENDIF
same = 1
bq = sq * 6
IF $pvI[bq] <> iX THEN
same = 0
ENDIF
IF $pvI[bq+1] <> iA THEN
same = 0
ENDIF
IF $pvI[bq+2] <> iB THEN
same = 0
ENDIF
IF $pvI[bq+3] <> iC THEN
same = 0
ENDIF
IF nPiv = 6 THEN
IF $pvI[bq+4] <> iD THEN
same = 0
ENDIF
ENDIF
IF same = 1 THEN
dupPat = 1
ENDIF
NEXT
ENDIF
IF dupPat = 0 THEN
//-----Potential reversal zone----------------//
// retracement(a,b,r) = b + (a-b)*r, intersected
// across every enabled projecting ratio.
hasZ = 0
z1 = 0
z2 = 0
IF nPiv = 6 THEN
IF useCDE = 1 THEN
s1 = vD + (vC - vD) * cdeLo
s2 = vD + (vC - vD) * cdeHi
IF useLg = 1 THEN
s1 = exp(s1)
s2 = exp(s2)
ENDIF
z1 = s1
z2 = s2
hasZ = 1
ENDIF
IF useABE = 1 THEN
s1 = vB + (vA - vB) * abeLo
s2 = vB + (vA - vB) * abeHi
IF useLg = 1 THEN
s1 = exp(s1)
s2 = exp(s2)
ENDIF
IF hasZ = 0 THEN
z1 = s1
z2 = s2
hasZ = 1
ELSE
IF z1 < z2 THEN
z1 = max(z1, min(s1, s2))
z2 = min(z2, max(s1, s2))
ELSE
z1 = min(z1, max(s1, s2))
z2 = max(z2, min(s1, s2))
ENDIF
ENDIF
ENDIF
IF useADE = 1 THEN
s1 = vD + (vA - vD) * adeLo
s2 = vD + (vA - vD) * adeHi
IF useLg = 1 THEN
s1 = exp(s1)
s2 = exp(s2)
ENDIF
IF hasZ = 0 THEN
z1 = s1
z2 = s2
hasZ = 1
ELSE
IF z1 < z2 THEN
z1 = max(z1, min(s1, s2))
z2 = min(z2, max(s1, s2))
ELSE
z1 = min(z1, max(s1, s2))
z2 = max(z2, min(s1, s2))
ENDIF
ENDIF
ENDIF
ELSE
IF useBCD = 1 THEN
s1 = vC + (vB - vC) * bcdLo
s2 = vC + (vB - vC) * bcdHi
IF useLg = 1 THEN
s1 = exp(s1)
s2 = exp(s2)
ENDIF
z1 = s1
z2 = s2
hasZ = 1
ENDIF
IF useXAD = 1 THEN
s1 = vA + (vX - vA) * xadLo
s2 = vA + (vX - vA) * xadHi
IF useLg = 1 THEN
s1 = exp(s1)
s2 = exp(s2)
ENDIF
IF hasZ = 0 THEN
z1 = s1
z2 = s2
hasZ = 1
ELSE
IF z1 < z2 THEN
z1 = max(z1, min(s1, s2))
z2 = min(z2, max(s1, s2))
ELSE
z1 = min(z1, max(s1, s2))
z2 = max(z2, min(s1, s2))
ENDIF
ENDIF
ENDIF
IF useXCD = 1 THEN
s1 = vC + (vX - vC) * xcdLo
s2 = vC + (vX - vC) * xcdHi
IF useLg = 1 THEN
s1 = exp(s1)
s2 = exp(s2)
ENDIF
IF hasZ = 0 THEN
z1 = s1
z2 = s2
hasZ = 1
ELSE
IF z1 < z2 THEN
z1 = max(z1, min(s1, s2))
z2 = min(z2, max(s1, s2))
ELSE
z1 = min(z1, max(s1, s2))
z2 = max(z2, min(s1, s2))
ENDIF
ENDIF
ENDIF
ENDIF
//-----Store it-------------------------------//
slot = nTot MOD pRing
bp = slot * 6
br = slot * 8
$pvI[bp] = iX
$pvI[bp+1] = iA
$pvI[bp+2] = iB
$pvI[bp+3] = iC
$pvI[bp+4] = iD
$pvI[bp+5] = iE
$pvP[bp] = pX
$pvP[bp+1] = pA
$pvP[bp+2] = pB
$pvP[bp+3] = pC
$pvP[bp+4] = pD
$pvP[bp+5] = pE
$pRa[br] = rXAB
$pRa[br+1] = rABC
$pRa[br+2] = rBCD
$pRa[br+3] = rCDE
$pRa[br+4] = rXAD
$pRa[br+5] = rXCD
$pRa[br+6] = rABE
$pRa[br+7] = rADE
IF pX > pA THEN
$pDirn[slot] = 1
ELSE
$pDirn[slot] = -1
ENDIF
$pCol[slot] = nTot MOD 6
$pZ1[slot] = z1
$pZ2[slot] = z2
$pHasZ[slot] = hasZ
nTot = nTot + 1
ENDIF
ENDIF
//-----Build the next zigzag level-------------------//
// Keep the pivots that are extreme against their previous
// and next same side neighbour. Consecutive survivors of
// the same side collapse onto the most extreme one.
IF lev < maxLev THEN
nv = 0
FOR k = 0 TO nw - 1 DO
keep = 1
IF k < nw - 1 THEN
IF k >= 2 THEN
prevSame = $wP[k-2]
IF $wH[k] = 1 THEN
IF $wP[k] < prevSame THEN
keep = 0
ENDIF
ELSE
IF $wP[k] > prevSame THEN
keep = 0
ENDIF
ENDIF
ENDIF
IF k + 2 <= nw - 1 THEN
nextSame = $wP[k+2]
IF $wH[k] = 1 THEN
IF $wP[k] < nextSame THEN
keep = 0
ENDIF
ELSE
IF $wP[k] > nextSame THEN
keep = 0
ENDIF
ENDIF
ENDIF
ENDIF
IF keep = 1 THEN
doPush = 1
IF nv > 0 THEN
IF $vH[nv-1] = $wH[k] THEN
doPush = 0
IF $wH[k] = 1 THEN
IF $wP[k] > $vP[nv-1] THEN
$vP[nv-1] = $wP[k]
$vI[nv-1] = $wI[k]
ENDIF
ELSE
IF $wP[k] < $vP[nv-1] THEN
$vP[nv-1] = $wP[k]
$vI[nv-1] = $wI[k]
ENDIF
ENDIF
ENDIF
ENDIF
IF doPush = 1 THEN
$vI[nv] = $wI[k]
$vP[nv] = $wP[k]
$vH[nv] = $wH[k]
nv = nv + 1
ENDIF
ENDIF
NEXT
FOR k = 0 TO nv - 1 DO
$wI[k] = $vI[k]
$wP[k] = $vP[k]
$wH[k] = $vH[k]
NEXT
nw = nv
ENDIF
ENDIF
NEXT
ENDIF
//-----------------------------------------------------------//
//-----DRAWING (last bar only)-------------------------------//
IF islastbarupdate THEN
atrv = averagetruerange[14]
off = atrv * 0.5
nDraw = min(nTot, maxShow)
stD = (nTot - nDraw) MOD pRing
IF nDraw > 0 THEN
FOR kk = 0 TO nDraw - 1 DO
ks = stD + kk
IF ks >= pRing THEN
ks = ks - pRing
ENDIF
bp = ks * 6
br = ks * 8
dirn = $pDirn[ks]
ci = $pCol[ks]
IF ci = 0 THEN
pr = 33
pg = 150
pb = 243
ELSIF ci = 1 THEN
pr = 76
pg = 175
pb = 80
ELSIF ci = 2 THEN
pr = 245
pg = 124
pb = 0
ELSIF ci = 3 THEN
pr = 156
pg = 39
pb = 176
ELSIF ci = 4 THEN
pr = 0
pg = 150
pb = 136
ELSE
pr = 216
pg = 27
pb = 96
ENDIF
//-----Pattern outline----------------------------//
FOR j = 0 TO nPiv - 2 DO
drawsegment($pvI[bp+j], $pvP[bp+j], $pvI[bp+j+1], $pvP[bp+j+1]) coloured(pr, pg, pb) style(line, 2)
NEXT
//-----Pivot labels-------------------------------//
// X B D sit on one side, A C E on the other. dirn tells
// which side X is on; drawtext centres on the point, so
// the label is pushed away by half an ATR.
IF showPiv = 1 THEN
FOR j = 0 TO nPiv - 1 DO
IF (j MOD 2) = 0 THEN
sideV = dirn
ELSE
sideV = 0 - dirn
ENDIF
xl = $pvI[bp+j]
yl = $pvP[bp+j] + sideV * off
IF j = 0 THEN
drawtext("X", xl, yl, sansserif, bold, 11) coloured(pr, pg, pb)
ELSIF j = 1 THEN
drawtext("A", xl, yl, sansserif, bold, 11) coloured(pr, pg, pb)
ELSIF j = 2 THEN
drawtext("B", xl, yl, sansserif, bold, 11) coloured(pr, pg, pb)
ELSIF j = 3 THEN
drawtext("C", xl, yl, sansserif, bold, 11) coloured(pr, pg, pb)
ELSIF j = 4 THEN
drawtext("D", xl, yl, sansserif, bold, 11) coloured(pr, pg, pb)
ELSE
drawtext("E", xl, yl, sansserif, bold, 11) coloured(pr, pg, pb)
ENDIF
NEXT
ENDIF
//-----Ratio guides and values--------------------//
IF showRat = 1 THEN
upV = dirn * off
dnV = 0 - dirn * off
IF useXAB = 1 THEN
drawsegment($pvI[bp], $pvP[bp], $pvI[bp+2], $pvP[bp+2]) coloured(pr, pg, pb) style(dottedline, 1)
mx = ($pvI[bp] + $pvI[bp+2]) / 2
my = ($pvP[bp] + $pvP[bp+2]) / 2 + upV
rv = round($pRa[br] * 1000) / 1000
drawtext("XAB : #rv#", mx, my) coloured(pr, pg, pb)
ENDIF
IF useABC = 1 THEN
drawsegment($pvI[bp+1], $pvP[bp+1], $pvI[bp+3], $pvP[bp+3]) coloured(pr, pg, pb) style(dottedline, 1)
mx = ($pvI[bp+1] + $pvI[bp+3]) / 2
my = ($pvP[bp+1] + $pvP[bp+3]) / 2 + dnV
rv = round($pRa[br+1] * 1000) / 1000
drawtext("ABC : #rv#", mx, my) coloured(pr, pg, pb)
ENDIF
IF useBCD = 1 THEN
drawsegment($pvI[bp+2], $pvP[bp+2], $pvI[bp+4], $pvP[bp+4]) coloured(pr, pg, pb) style(dottedline, 1)
mx = ($pvI[bp+2] + $pvI[bp+4]) / 2
my = ($pvP[bp+2] + $pvP[bp+4]) / 2 + upV
rv = round($pRa[br+2] * 1000) / 1000
drawtext("BCD : #rv#", mx, my) coloured(pr, pg, pb)
ENDIF
IF useCDE = 1 THEN
drawsegment($pvI[bp+3], $pvP[bp+3], $pvI[bp+5], $pvP[bp+5]) coloured(pr, pg, pb) style(dottedline, 1)
mx = ($pvI[bp+3] + $pvI[bp+5]) / 2
my = ($pvP[bp+3] + $pvP[bp+5]) / 2 + dnV
rv = round($pRa[br+3] * 1000) / 1000
drawtext("CDE : #rv#", mx, my) coloured(pr, pg, pb)
ENDIF
// XAD and XCD share the X-D guide: the second value is
// stacked half a step lower so they do not overlap.
IF useXAD = 1 OR useXCD = 1 THEN
drawsegment($pvI[bp], $pvP[bp], $pvI[bp+4], $pvP[bp+4]) coloured(pr, pg, pb) style(dottedline, 1)
mx = ($pvI[bp] + $pvI[bp+4]) / 2
my = ($pvP[bp] + $pvP[bp+4]) / 2 + upV
IF useXAD = 1 THEN
rv = round($pRa[br+4] * 1000) / 1000
drawtext("XAD : #rv#", mx, my) coloured(pr, pg, pb)
my = my + upV * 0.6
ENDIF
IF useXCD = 1 THEN
rv = round($pRa[br+5] * 1000) / 1000
drawtext("XCD : #rv#", mx, my) coloured(pr, pg, pb)
ENDIF
ENDIF
// ABE and ADE share the A-E guide
IF useABE = 1 OR useADE = 1 THEN
drawsegment($pvI[bp+1], $pvP[bp+1], $pvI[bp+5], $pvP[bp+5]) coloured(pr, pg, pb) style(dottedline, 1)
mx = ($pvI[bp+1] + $pvI[bp+5]) / 2
my = ($pvP[bp+1] + $pvP[bp+5]) / 2 + dnV
IF useABE = 1 THEN
rv = round($pRa[br+6] * 1000) / 1000
drawtext("ABE : #rv#", mx, my) coloured(pr, pg, pb)
my = my + dnV * 0.6
ENDIF
IF useADE = 1 THEN
rv = round($pRa[br+7] * 1000) / 1000
drawtext("ADE : #rv#", mx, my) coloured(pr, pg, pb)
ENDIF
ENDIF
ENDIF
//-----Potential reversal zone--------------------//
IF showPrz = 1 THEN
IF $pHasZ[ks] = 1 THEN
zx = $pvI[bp + nPiv - 1]
drawrectangle(zx, $pZ1[ks], zx + 3, $pZ2[ks]) coloured(pr, pg, pb) fillcolor(pr, pg, pb, 70)
ENDIF
ENDIF
NEXT
ENDIF
//-----Summary panel---------------------------------//
// The pattern name is a string literal. To use a different name,
// edit the drawtext below.
IF showPan = 1 THEN
nEn = useXAB + useABC + useBCD + useCDE + useXAD + useXCD + useABE + useADE
rowY = 20 + (nEn + 3) * 20
dashV = dashX + dashCol
drawtext("Pattern", dashX, rowY, sansserif, bold, 10) coloured(120, 120, 120) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
drawtext("Three Drives", dashV, rowY, sansserif, bold, 10) coloured(33, 150, 243) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
rowY = rowY - 20
drawtext("Pivots", dashX, rowY, sansserif, bold, 10) coloured(120, 120, 120) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
drawtext("#nPiv#", dashV, rowY, sansserif, standard, 10) coloured(33, 150, 243) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
rowY = rowY - 20
IF useXAB = 1 THEN
v1 = round(xabLo * 10000) / 10000
v2 = round(xabHi * 10000) / 10000
drawtext("XAB", dashX, rowY, sansserif, bold, 10) coloured(120, 120, 120) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
drawtext("#v1# - #v2#", dashV, rowY, sansserif, standard, 10) coloured(33, 150, 243) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
rowY = rowY - 20
ENDIF
IF useABC = 1 THEN
v1 = round(abcLo * 10000) / 10000
v2 = round(abcHi * 10000) / 10000
drawtext("ABC", dashX, rowY, sansserif, bold, 10) coloured(120, 120, 120) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
drawtext("#v1# - #v2#", dashV, rowY, sansserif, standard, 10) coloured(33, 150, 243) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
rowY = rowY - 20
ENDIF
IF useBCD = 1 THEN
v1 = round(bcdLo * 10000) / 10000
v2 = round(bcdHi * 10000) / 10000
drawtext("BCD", dashX, rowY, sansserif, bold, 10) coloured(120, 120, 120) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
drawtext("#v1# - #v2#", dashV, rowY, sansserif, standard, 10) coloured(33, 150, 243) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
rowY = rowY - 20
ENDIF
IF useCDE = 1 THEN
v1 = round(cdeLo * 10000) / 10000
v2 = round(cdeHi * 10000) / 10000
drawtext("CDE", dashX, rowY, sansserif, bold, 10) coloured(120, 120, 120) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
drawtext("#v1# - #v2#", dashV, rowY, sansserif, standard, 10) coloured(33, 150, 243) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
rowY = rowY - 20
ENDIF
IF useXAD = 1 THEN
v1 = round(xadLo * 10000) / 10000
v2 = round(xadHi * 10000) / 10000
drawtext("XAD", dashX, rowY, sansserif, bold, 10) coloured(120, 120, 120) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
drawtext("#v1# - #v2#", dashV, rowY, sansserif, standard, 10) coloured(33, 150, 243) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
rowY = rowY - 20
ENDIF
IF useXCD = 1 THEN
v1 = round(xcdLo * 10000) / 10000
v2 = round(xcdHi * 10000) / 10000
drawtext("XCD", dashX, rowY, sansserif, bold, 10) coloured(120, 120, 120) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
drawtext("#v1# - #v2#", dashV, rowY, sansserif, standard, 10) coloured(33, 150, 243) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
rowY = rowY - 20
ENDIF
IF useABE = 1 THEN
v1 = round(abeLo * 10000) / 10000
v2 = round(abeHi * 10000) / 10000
drawtext("ABE", dashX, rowY, sansserif, bold, 10) coloured(120, 120, 120) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
drawtext("#v1# - #v2#", dashV, rowY, sansserif, standard, 10) coloured(33, 150, 243) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
rowY = rowY - 20
ENDIF
IF useADE = 1 THEN
v1 = round(adeLo * 10000) / 10000
v2 = round(adeHi * 10000) / 10000
drawtext("ADE", dashX, rowY, sansserif, bold, 10) coloured(120, 120, 120) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
drawtext("#v1# - #v2#", dashV, rowY, sansserif, standard, 10) coloured(33, 150, 243) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
rowY = rowY - 20
ENDIF
drawtext("Identified", dashX, rowY, sansserif, bold, 10) coloured(120, 120, 120) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
drawtext("#nTot#", dashV, rowY, sansserif, bold, 10) coloured(33, 150, 243) ANCHOR(BOTTOMRIGHT, XSHIFT, YSHIFT)
ENDIF
ENDIF
//-----------------------------------------------------------//
//Drawing only overlay: close with a bare RETURN
return