Classic Chart Patterns
Candlestick patterns are hard to define. Chart patterns are harder, and they carry an extra hazard that candlesticks do not: the AFL functions almost everyone reaches for when detecting them are documented as possibly looking into the future. By the end of this lesson you will know what each classic pattern claims, roughly how many free parameters its definition hides, and how to see the look-ahead problem happening on your own chart rather than reading about it.
What a chart pattern is claiming
Section titled “What a chart pattern is claiming”A candlestick pattern is a statement about one, two or three bars. A chart pattern is a statement about a shape spanning many bars — typically twenty to a hundred — plus a claim about what tends to follow it and, usually, a target price.
That extra span brings a specific difficulty. Where a candlestick pattern is determined by eight or twelve numbers, a chart pattern is determined by which swing points you decided were the important ones out of the many the price made. That decision is where almost all the subjectivity lives, and it is made after you can see the whole shape.
Double top and double bottom
Section titled “Double top and double bottom”The double top is two peaks at roughly the same level with a trough between them, and the claim is that failure to exceed the first peak marks the end of the advance. The double bottom is the mirror.
Writing it down needs, at minimum:
- How a peak is identified. Highest high of a window of what size?
- How close is “roughly the same level”? Within 1% of each other? 2%? Half an ATR?
- How far apart must the two peaks be? Ten bars? Thirty? Both extremes are called double tops by somebody.
- How deep must the intervening trough be? A 3% retracement and a 30% retracement look nothing alike.
- What confirms the pattern? A close below the trough, usually — but on what bar, and by how much, and does volume have to do anything?
Five parameters, none of them determined by anything but taste. And that is before the target claim, which we come to below.
What the reader knows, and when
The tools most people use can see the future
Section titled “The tools most people use can see the future”AmiBroker provides Zig(), and the family built on it: Peak(), Trough(),
PeakBars() and TroughBars(). They are enormously convenient. Peak( Close, 5, 1 )
gives you the value of the most recent 5% swing peak, held forward, in one line.
Every one of those five reference pages carries the same caveat, in AmiBroker’s own
words: the function is based on the Zig Zag indicator and may look into the
future. The Zig() page goes further and says plainly that you can get unrealistic
results when backtesting a trading system that uses it, and that it is provided for
pattern and trend recognition rather than for systems.
The mechanism is easy to understand once you see it. A Zig Zag leg does not end until
price has reversed by the specified percentage. Until that happens, where the current
leg ends is undecided — so the line drawn over the most recent bars can change when
the next bar arrives. Ask “was this bar a peak?” at the time, and the honest answer is
“unknown”. Ask the same question of Peak() on a historical chart, and it answers
using bars that had not printed.
The alternative is the one from Part 4: define a swing point by a window, and accept that you learn about it late.
Fragment — not a complete formula
// A bar is a swing high if it is the highest High of the window reaching// Side bars either side of it. Knowable only Side bars later, which is why// this condition is true on the CONFIRMING bar rather than on the swing bar.SwingWindow = 2 * Side + 1;ConfirmedHigh = Ref( High, -Side ) == HHV( High, SwingWindow );That definition can never see the future, and the price you pay for it is a
confirmation lag of Side bars. There is no way to avoid paying it. A pattern
detector with no lag is a pattern detector that is cheating.
Head and shoulders
Section titled “Head and shoulders”Three peaks, the middle one highest, with a “neckline” drawn through the two troughs between them. The claim is that a close below the neckline marks a reversal.
Everything that had to be decided for the double top has to be decided again, and more:
- how the three peaks are identified, and the confirmation lag on each
- how much lower than the head the two shoulders must be, and by how much they may differ from each other
- how much separation is required between the peaks
- whether the neckline may slope, and how steeply before the pattern is disqualified
- whether volume must decline across the formation, as the traditional description requires, and how that is measured
- what constitutes a break of a sloping line
Eight or nine free parameters, several of them without any conventional value at all. It is genuinely difficult to write a head-and-shoulders detector that two people would agree implements the pattern they have in mind — which is a strong hint about how much weight the pattern’s reported historical performance can carry.
Triangles
Section titled “Triangles”Ascending, descending and symmetrical triangles are all the same claim: the range is narrowing, and the direction of the eventual break means something.
The narrowing part is measurable and the boundary lines are not. Drawing a triangle means choosing which highs and which lows the two lines touch, out of all the highs and lows available, and that choice is made looking at the finished shape. Two analysts will pick different touch points on the same chart, get different apex positions, and generate different break levels.
The measurable part needs no lines at all:
Fragment — not a complete formula
// Range contraction with no drawn lines and no chosen touch points.RecentRange = HHV( High, 20 ) - LLV( Low, 20 );EarlierRange = Ref( RecentRange, -20 );Contraction = SafeDivide( RecentRange, EarlierRange, Null );
// Scale-free version: the 20-bar range measured in units of recent volatility.RangeInATR = SafeDivide( RecentRange, ATR( 20 ), Null );That is a number, it is computed only from past bars, and two people running it get the same answer. It does not tell you whether the shape is ascending or symmetrical, because that distinction depends on the lines nobody can agree on. Part 5 develops this measurement properly; the point here is that the testable content of “triangle” is contraction, and the rest is drawing.
Flags and consolidations
Section titled “Flags and consolidations”A flag is a sharp directional move — the “pole” — followed by a shallow drift against it, and the claim is that the prior move tends to resume. The objective version:
Fragment — not a complete formula
PoleMove = ROC( Close, 10 ); // the sharp move, in percentDriftRange = HHV( High, 5 ) - LLV( Low, 5 ); // the shallow partPoleHeight = Ref( HHV( High, 10 ) - LLV( Low, 10 ), -5 );
IsFlagShape = PoleMove > 15 AND SafeDivide( DriftRange, PoleHeight, 1 ) < 0.4;Written this way, “flag” becomes three numbers: how big the pole must be, how long the drift may last, and how shallow it must stay. Change any of them and you get a different set of bars. That is the same lesson as the hammer, one scale up.
Measured-move claims
Section titled “Measured-move claims”Almost every chart pattern comes with a target. For the double top, the target is the height of the pattern projected down from the neckline. For head and shoulders, the distance from head to neckline. For a flag, the length of the pole.
The comparison in that last sentence is the part people skip, and skipping it makes the result meaningless. Pattern heights scale with volatility: a tall pattern forms in a volatile market, where large moves are common anyway. If you do not match the benchmark to the same distance and the same horizon, a high “target hit rate” tells you only that volatile instruments move a lot. The next lesson is entirely about getting that comparison right.
Why these are so hard to define objectively
Section titled “Why these are so hard to define objectively”Five distinct problems compound:
- The boundary problem. Where does the pattern start and end? Every answer is a choice, and the choice changes the height, which changes the target.
- The selection problem. Which swing points are the important ones? Chosen with the whole shape visible.
- The confirmation lag. Any honest swing definition is late. Patterns drawn without lag are drawn with information that was not available.
- The tooling problem. The convenient AFL functions are documented as possibly forward-looking, so a formula that appears to implement the pattern may not be implementable at all.
- The eye’s own filter. You remember the patterns that worked because they are the ones that produced a memorable move. The failures look like ordinary chart noise afterwards and are not stored as instances of the pattern at all.
None of these say the patterns are worthless. They say that a chart pattern is a hypothesis with an unusually large number of unstated parameters, and that testing one honestly is a bigger job than testing a candlestick pattern. This is why the reality check at the end of this part uses a candlestick pattern: it is the version of the question that can actually be answered in forty minutes.
Watching the look-ahead happen
Section titled “Watching the look-ahead happen”See the Zig Zag repainting problem on your own data rather than accepting it on authority, and see side by side what a swing definition that cannot cheat looks like.
Complete formula
Section titled “Complete formula”Complete runnable AFL
// zigzag-repaint-demo.afl// Part 7 - Classic Chart Patterns//// The AFL Function Reference states, for Zig(), Peak(), Trough(), PeakBars()// and TroughBars(), that they are based on the Zig Zag indicator and "may look// into the future". Most published double-top and head-and-shoulders detectors// are built on exactly those functions. This formula lets you see the problem// on your own data instead of taking it on trust.//// Three things are drawn on one pane:// 1. the Zig Zag line, whose last leg is redrawn as new bars arrive// 2. the most recent Zig Zag peak and trough, held forward as levels// 3. a swing definition that uses only bars that had already printed, marked// on the bar that CONFIRMED it and again on the swing bar itself//// Assumptions:// - Zig(), Peak() and Trough() are used here for illustration only. Nothing// built on them belongs in a backtest or a live rule.// - The past-only swing definition is one choice among many. Changing "bars// either side" changes both how many swings exist and how late you learn// about them.
_SECTION_BEGIN("Zig Zag versus a past-only swing");
// Cum() no longer forces AmiBroker to process every bar (that changed in// version 5.30), so a running count would otherwise depend on how much history// the chart happened to compute. -2 means "all bars", which makes the counts// below reproducible.SetBarsRequired( -2, -2 );
ChangePct = Param("Zig Zag minimum swing %", 5, 1, 25, 0.5 );Side = Param("Past-only swing: bars either side", 5, 2, 30, 1 );
Plot( Close, "Price", colorDefault, styleCandle );
// ------------------------------------------------------- 1. the Zig Zag line// Watch the right-hand end of this line. Add a bar and the last leg can move,// because where the current swing ends is not decided until price turns.ZigLine = Zig( Close, ChangePct );Plot( ZigLine, "Zig " + NumToStr( ChangePct, 1.1 ) + "%", colorOrange, styleLine | styleThick );
// -------------------------------------------- 2. last Zig peak and trough// Peak() and Trough() return the value of the most recent turning point, held// forward. Convenient, and unusable in a rule for the reason stated above.Plot( Peak( Close, ChangePct, 1 ), "Most recent Zig peak", colorRed, styleDashed | styleNoRescale );Plot( Trough( Close, ChangePct, 1 ), "Most recent Zig trough", colorBlue, styleDashed | styleNoRescale );
// ------------------------------------------------ 3. a past-only swing rule// This is the swing definition from Part 4, restated here so the two can be// compared side by side. A bar is a swing high when it is the highest High of// the window reaching Side bars either side of it. At the centre bar that is// not knowable; it becomes knowable Side bars later. ConfirmedHigh is therefore// true on the CONFIRMING bar, and every input it uses had already printed.SwingWindow = 2 * Side + 1;ConfirmedHigh = Ref( High, -Side ) == HHV( High, SwingWindow );ConfirmedLow = Ref( Low, -Side ) == LLV( Low, SwingWindow );
// Small circles: the bar on which you could first have known.PlotShapes( IIf( ConfirmedHigh, shapeSmallCircle, shapeNone ), colorRed, 0, High, 16 );PlotShapes( IIf( ConfirmedLow, shapeSmallCircle, shapeNone ), colorBlue, 0, Low, -16 );
// Stars: the swing bar itself. Ref() with a POSITIVE period reads bars that had// not printed yet. That is acceptable here because this formula only draws a// picture of the past. The same line inside a Buy statement would be a// look-ahead bug, and it is the single most common one in pattern code.PlotShapes( IIf( Ref( ConfirmedHigh, Side ), shapeStar, shapeNone ), colorRed, 0, High, 32 );PlotShapes( IIf( Ref( ConfirmedLow, Side ), shapeStar, shapeNone ), colorBlue, 0, Low, -32 );
Title = Name() + " " + Date() + "\nZig Zag " + NumToStr( ChangePct, 1.1 ) + "% | past-only swing window " + NumToStr( SwingWindow, 1.0 ) + " bars, confirmation lag " + NumToStr( Side, 1.0 ) + " bars" + "\nConfirmed swing highs: " + NumToStr( Cum( ConfirmedHigh ), 1.0 ) + " confirmed swing lows: " + NumToStr( Cum( ConfirmedLow ), 1.0 ) + "\nStars mark the swing bar. Circles mark the bar on which you could first have known.";
_SECTION_END();How it works
Section titled “How it works”Three layers are drawn on one price pane. The Zig Zag line comes from Zig(). The
most recent swing peak and trough come from Peak() and Trough(), plotted as dashed
levels held forward. The third layer is the past-only swing definition: small circles
mark the bar on which a swing could first have been confirmed, and stars mark the
swing bar itself.
The stars are drawn using Ref() with a positive period, which reads bars that had
not printed. That is deliberate and it is labelled as such in the formula. It is
acceptable because this formula only draws a picture of the past. The same expression
inside a Buy statement would be a look-ahead bug, and putting the two uses next to
each other is the fastest way to internalise the difference.
Key functions
Section titled “Key functions”Zig( array, change )returns the Zig Zag line for a minimum percentage swing.Peak( array, change, n )andTrough( array, change, n )return the value of the n-th most recent peak or trough, held forward.HHV()andLLV()return the highest and lowest value over a rolling window, ending at the current bar — so they never read forward.Ref( array, +n )reads n bars ahead. Used here only for drawing.
Expected result
Section titled “Expected result”Test it
Section titled “Test it”This is the important part, and it takes thirty seconds.
Set the chart to show the most recent bars, and note where the right-hand end of the orange line sits. Now use Bar Replay, or simply scroll the chart back a few bars and forward again, so that the formula is evaluated with a different final bar. The last leg of the Zig Zag can move. The circles and stars from the past-only definition cannot: once a swing is confirmed, no later bar changes the answer.
If the last Zig Zag leg never moves for your instrument and percentage setting, lower the percentage until swings become frequent enough that you catch one mid-formation.
Common errors
Section titled “Common errors”- The Zig Zag looks like a straight line. The percentage is too large for the instrument’s volatility. Reduce it.
Peak()andTrough()produce nothing. The reference notes that these functions work correctly for arrays containing data greater than zero; a series with zeros or negatives in it will misbehave.- Circles appear but stars do not. The forward
Ref()cannot resolve within the last few bars of the array, which is exactly the point being made. - Treating the dashed levels as support and resistance in a rule. They are computed with a function documented as possibly forward-looking.
Extension
Section titled “Extension”Add a second past-only swing definition with a much larger window and plot its markers in a third colour. Then count, in the title, how many swings each definition finds over the same history. You will have two defensible answers to “how many swing highs are on this chart”, differing by a factor of several — which is the honest state of affairs, and worth having seen before you read anyone’s pattern statistics.
What changed
Section titled “What changed”You now know what the four classic pattern families claim, and roughly how many free parameters each claim conceals: five for a double top, eight or nine for head and shoulders, and an unresolvable line-drawing choice for triangles. You also know the specific, documented reason that most published pattern-detection AFL cannot be used in a backtest, and you have seen the repainting happen.
The measured-move discussion left one question open: compared with what? That question is the whole of the next lesson.
Check your understanding
Sources for this lesson
9 verified · checked 2026-08-31
- 01AFL Function Reference — ZIGamibroker.com/guide/afl/zig.html2026-08-31
- 02AFL Function Reference — Peakamibroker.com/guide/afl/peak.html2026-08-31
- 03AFL Function Reference — Troughamibroker.com/guide/afl/trough.html2026-08-31
- 04AFL Function Reference — PeakBarsamibroker.com/guide/afl/peakbars.html2026-08-31
- 05AFL Function Reference — TroughBarsamibroker.com/guide/afl/troughbars.html2026-08-31
- 06AFL Function Reference — HHVamibroker.com/guide/afl/hhv.html2026-08-31
- 07AFL Function Reference — LLVamibroker.com/guide/afl/llv.html2026-08-31
- 08AFL Function Reference — ROCamibroker.com/guide/afl/roc.html2026-08-31
- 09AFL Function Reference — PlotShapesamibroker.com/guide/afl/plotshapes.html2026-08-31
Every technical claim on this page was checked against the official AmiBroker documentation on the date shown. Where the course disagrees with folklore, the source is how you can tell which one to trust.