Build Your Own Harmonic Pattern Scanner

Category: Indicators By: Iván González Created: August 31, 2026, 4:20 PM
August 31, 2026, 4:20 PM
Indicators
0 Comments

1. Introduction

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.

2. How It Works

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:

  • XAB – leg AB measured against leg XA
  • ABC – leg BC against leg AB
  • BCD – leg CD against leg BC
  • CDE – leg DE against leg CD (six-point patterns only)
  • XAD – leg AD against leg XA
  • XCD – leg CD against leg XC
  • ABE – leg BE against leg AB (six-point patterns only)
  • ADE – leg DE against leg AD (six-point patterns only)

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.

 

3. Reading the Indicator

  • Solid coloured line through the pivots: the pattern structure, X to A to B to C to D and, on six-point patterns, on to E. Each pattern gets its own colour so overlapping structures stay readable.
  • Letters at each pivot: X, A, B, C, D and E, offset half an ATR away from the bar so they do not sit on top of the candle.
  • Dotted guides with a number: one per enabled ratio, drawn between the two points the ratio spans, labelled with the value actually measured. This is the number to check when a structure looks wrong: it tells you which leg is carrying the match.
  • Shaded box at the last pivot: the PRZ, the intersection of the projected bands.
  • Bottom-right panel: the pattern name, how many points it has, the accepted range of every enabled ratio, and the running count of structures identified over the loaded history.

4. Building Your Own Pattern

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 = 6
  • XAB 0.618, ABC 1.272, BCD 0.618, CDE 1.272

Gartley (five points):

  • nPiv = 5
  • XAB 0.618, ABC 0.382 to 0.886, BCD 1.13 to 1.618, XAD 0.786

Bat (five points):

  • nPiv = 5
  • XAB 0.382 to 0.5, ABC 0.382 to 0.886, BCD 1.618 to 2.618, XAD 0.886

Butterfly (five points):

  • nPiv = 5
  • XAB 0.786, ABC 0.382 to 0.886, BCD 1.618 to 2.24, XAD 1.27 to 1.618

Crab (five points):

  • nPiv = 5
  • XAB 0.382 to 0.618, ABC 0.382 to 0.886, BCD 2.24 to 3.618, XAD 1.618

Note 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.

 

5. Parameters

Zigzag

  • zzLen (default 5): lookback for the base zigzag. Larger values give fewer, bigger swings.
  • zzDepth (default 50): how many base pivots feed the scan. This is the real performance lever, and it also decides how many levels can form: with 50 pivots the useful ceiling is level 2.
  • useRT (default 1): 1 uses the running bar, 0 waits for confirmed bars only. See section 6.
  • maxLev (default 5): safety ceiling on the recursion. The scan stops on its own when a level has fewer pivots than the pattern needs.

Pattern

  • nPiv (default 6): 6 for XABCDE patterns, 5 for XABCD. Anything other than 6 is read as 5, and the three ratios that need point E are switched off automatically.
  • errPct (default 20): tolerance applied when a ratio is given as a single value.
  • useLog (default 0): measure the ratios on a logarithmic scale, which matters on instruments that have travelled a long way in percentage terms.

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

  • maxShow (default 6): how many of the most recent structures stay drawn.
  • showPiv / showRat / showPrz / showPan: pivot letters, ratio guides, reversal zone, summary panel.
  • dashX / dashCol (-440 / 140): panel position and column width, in pixels from the right edge. Move dashX if the panel collides with your price scale.

6. A Note on Repainting

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.

 

7. BYO Pattern V1 Code

//-----------------------------------------------------------//
//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

Download
Filename: PRC_BYO-Pattern.itf
Downloads: 8
Iván González Legend
This author is like an anonymous function, present but not directly identifiable. More details on this code architect as soon as they exit 'incognito' mode.
Author’s Profile

Comments

Logo Logo
Loading...