Skip to content
Level 1 · Chart ReaderLessonPart 05 · page 3 of 624 min
24Minutes
11AFL functions
8Sources
StandardRequires
AFL functions taught here11

Breakouts, Failed Breakouts and Role Reversal

Two analysts look at the same daily chart. One says the breakout happened on Tuesday, the other says it has not happened yet. Neither is being difficult: they are using different definitions of a word that sounds as though it has only one meaning. This lesson makes the definitions explicit, because a definition you have not written down is a definition you cannot test.

All six of these are in common use. Every one of them is defensible. They identify different days.

# Definition Fires when
1 Intraday penetration The bar’s high exceeds the prior N-bar high
2 Closing breakout The close exceeds the prior N-bar high
3 Margin breakout The close exceeds the level by at least k × ATR, or by x per cent
4 Multi-bar confirmation Two or more consecutive closes above the level
5 Volume-confirmed breakout A closing breakout on volume above m times its own average
6 Range breakout The close leaves a defined consolidation band, either side

Definition 1 fires earliest and most often; definition 4 fires latest and least often. On a typical liquid stock over five years the counts can differ by a factor of three or more. If someone tells you that breakouts do or do not work, the first useful question is: which of these six did you measure?

Before any of this can be measured, one piece of arithmetic has to be right.

The natural way to write “the close is above the highest high of the last twenty bars” is this, and it produces an array that is essentially never true:

Fragment — not a complete formula

// WRONG. HHV's window includes the current bar, so today's high is one of the
// twenty candidates and the close cannot exceed a maximum that contains it.
BreakoutBar = Close > HHV( High, 20 );

The official HHV page states that the period includes the current day. The window has to be pushed back one bar to mean “before today”, and Ref with a negative argument does exactly that:

Fragment — not a complete formula

// RIGHT. The level is the highest high of the twenty bars before this one.
Level = Ref( HHV( High, 20 ), -1 );
BreakoutBar = Cross( Close, Level );

Note the second change as well. Close > Level is a state: it stays true for as long as price remains above the level, which on a strong trend can be months. Cross(Close, Level) is an event: it is true only on the bar where the relationship changed. A breakout is an event. Part 9 devotes a whole lesson to this distinction because it breaks more formulas than any other single mistake.

State versus event on the same level

The state is true on five bars. The breakout happened on one of them.
Bar1234567
Close20.020.421.622.121.922.822.4
Level21.021.021.021.021.021.021.0
Close > Level (state)0011111
Cross(Close, Level) (event)0010000
The state is true on five bars. The breakout happened on one of them. Prices in this diagram are invented for the illustration. They are not market data and nothing should be inferred from them.

Confirmation filters exist to reduce the number of breakouts that immediately reverse. They work, in the narrow sense that they do reduce that number. They are not free.

Every filter buys you the same three things, in the same proportions:

  • Fewer immediate reversals, because the weakest breakouts are excluded.
  • A worse entry price, because you are acting later and further from the level.
  • Fewer observations, which makes whatever you conclude less reliable and, in the limit, makes the test uninformative.

And every filter adds a parameter. A closing breakout has one number in it: the lookback. Add an ATR margin and you have two. Add a volume multiple and its averaging length and you have four. Four parameters, each tried at five plausible values, is 625 versions of “the breakout system” — and one of them will look excellent on any dataset you own. Part 30 puts numbers on how badly that misleads.

A failed breakout is a breakout that reverses. To count them, two more numbers are needed: how far back price has to come, and within how long.

Without the time limit, the concept is empty. A breakout followed by a return below the level after eighteen months is not a failed breakout in any useful sense; it is a breakout and then a subsequent decline. Without the distance rule, a single tick back below the level counts as failure. So:

A breakout at level L on bar t has failed if the close is below L on any bar between t+1 and t+N.

That is testable. It is also, notice, impossible to know at the time. On bar t+3, a breakout with N = 10 is neither a success nor a failure; it is undetermined. Any commentary that labels breakouts as failed in real time is either using a much shorter N than it admits, or is describing the past.

The claim, in its usual form:

Once resistance is broken, it becomes support. Once support is broken, it becomes resistance.

This is a compound claim, and taking it apart is more useful than arguing about it. It asserts, first, that the level continues to matter after it has been crossed — that participants remember it. And second, that the direction of its effect flips.

The first half is at least mechanically plausible: buyers who missed the move may place limit orders at the old high, and the sellers who were absorbed at that price are gone. The second half does not follow from the first. A level could keep mattering without reversing role, and the memory could simply fade.

Making it measurable requires you to be specific about the sequence:

  1. A level L exists by some stated definition.
  2. Price closes above L by some stated margin (an upward break).
  3. Some bars later, price returns to the zone around L from above — the first touch after the break.
  4. Question: over the next N bars, does price hold above Lw more often than a comparable price with no history would?

Step 4 is the one people skip, and it is the only one that turns the story into evidence. Without a comparison, “it held 6 times out of 10” is a number without a scale. The reality check at the end of this part builds that comparison properly.

AmiBroker gives you the strict definitions directly. GapUp() returns true on a bar where yesterday’s high is less than today’s low; GapDown() is true where yesterday’s low is greater than today’s high.

Notice that this is stricter than the definition many people carry in their heads, which is “today’s open is above yesterday’s close”. Under AmiBroker’s definition the entire bar must sit clear of the previous bar’s range, so a day that opens higher and then trades back into yesterday’s range is not a gap up. Both definitions are legitimate; they are not the same thing, and the counts differ substantially. When you report gap statistics, say which one you used.

Gaps matter to this part for a specific reason. A gap through a level means no trading took place at the level at all. Whatever mechanism you believed in — resting limit orders, absorbed supply, remembered prices — did not have the opportunity to operate. So a gap through resistance is a different event from a grind through resistance, even though a horizontal line on a chart makes them look identical.

There is a practical consequence too, which Part 30 develops: if your rule places a stop at a level, a gap does not fill you at the level. It fills you wherever the market reopened, which may be a long way below.

Two related built-ins are worth knowing while we are here. Inside() is true when today’s high is below yesterday’s high and today’s low is above yesterday’s low; Outside() is true on an outside bar. Both are used in the next lesson as consolidation and expansion markers.

This is the general procedure, and it applies to everything in this part.

From something you noticed to something a computer can find

  1. Name the object"The prior 20-bar high", not "the resistance up there"
  2. Give it a numberEvery fuzzy word becomes a parameter with a stated value
  3. Give it a time windowWithin how many bars must the thing happen?
  4. Make it an event, not a stateCross, not >, unless you genuinely mean the whole span
  5. Decide the outcome measure in advanceAnd what result would count as nothing happening
Steps three and five are the ones people skip, and they are the ones that make the difference between a rule and an opinion.

The formula below applies the first four steps to three of the six definitions, so that you can see them disagreeing on your own data.

Complete runnable AFL

breakout-definitions.afl
// breakout-definitions.afl
// Part 5 - Breakouts, Failed Breakouts and Role Reversal
//
// One chart, three definitions of "breakout", three different sets of dates.
// Nothing here says which definition is the right one. The point is that the
// word on its own does not identify an event, so a rule has to name one and
// live with the consequences.
//
// A (circle) - the close rises through the highest HIGH of the previous
// Lookback bars.
// B (square) - as A, but the level must be exceeded by AtrMult x ATR(20),
// so a one-tick poke does not qualify.
// C (star) - as A, but the day's volume must also exceed VolMult times its
// own moving average.
//
// A "failed breakout" is marked with a cross: a breakout by definition A whose
// close falls back below the level within FailBars bars.
//
// Assumptions:
// - daily bars; Volume is present and non-zero for definition C;
// - counts in the title cover the bars currently loaded on the chart, so they
// change when you zoom. That is deliberate: it forces the range to be
// explicit rather than assumed;
// - the failure marker looks FORWARD by FailBars bars. It is legitimate here
// only because this array is drawn and never traded.
_SECTION_BEGIN("Three breakout definitions");
Lookback = Param( "Breakout lookback (bars)", 20, 5, 250, 1 );
AtrMult = Param( "Definition B: excess (x ATR)", 0.25, 0.05, 2, 0.05 );
VolMult = Param( "Definition C: volume multiple", 1.5, 1, 5, 0.1 );
VolAvgLen = Param( "Definition C: volume average length", 50, 5, 200, 5 );
FailBars = Param( "Failure window (bars)", 5, 1, 40, 1 );
// The level. HHV includes the current bar, so without Ref( ..., -1 ) the test
// would be comparing today's close with a window that already contains today's
// high - and Close > HHV( High, n ) can then never be true.
Level = Ref( HHV( High, Lookback ), -1 );
Excess = AtrMult * ATR( 20 );
VolumeAvg = MA( Volume, VolAvgLen );
BreakA = Cross( Close, Level );
BreakB = Cross( Close, Level + Excess );
BreakC = BreakA AND Volume > VolMult * VolumeAvg;
// Failure: at any point in the next FailBars bars the close is back below the
// level that was broken. LLV of the forward window does the "at any point".
FellBack = Ref( LLV( Close, FailBars ), FailBars ) < Level;
FailedA = BreakA AND FellBack;
Plot( Close, "Close", colorDefault, styleCandle );
Plot( Level, "Prior " + Lookback + "-bar high", colorRed, styleStaircase );
Plot( Level + Excess, "... plus " + WriteVal( AtrMult, 1.2 ) + " x ATR",
colorLightOrange, styleStaircase | styleDashed );
PlotShapes( IIf( BreakA, shapeSmallCircle, shapeNone ), colorBlue, 0, High, 12 );
PlotShapes( IIf( BreakB, shapeSmallSquare, shapeNone ), colorOrange, 0, High, 26 );
PlotShapes( IIf( BreakC, shapeStar, shapeNone ), colorGreen, 0, High, 40 );
PlotShapes( IIf( FailedA, shapeDownArrow, shapeNone ), colorRed, 0, High, 54 );
CountA = LastValue( Cum( BreakA ) );
CountB = LastValue( Cum( BreakB ) );
CountC = LastValue( Cum( BreakC ) );
CountF = LastValue( Cum( FailedA ) );
Title = Name() + " " + Interval( 2 ) + " breakouts on the loaded bars: " +
"A circle " + NumToStr( CountA, 1.0 ) + " " +
"B square " + NumToStr( CountB, 1.0 ) + " " +
"C star " + NumToStr( CountC, 1.0 ) + " " +
"of which A failed within " + FailBars + " bars: " +
NumToStr( CountF, 1.0 );
_SECTION_END();

Download breakout-definitions.afl74 lines

One level, three tests, one failure marker. The level is Ref(HHV(High, Lookback), -1), drawn as a staircase because it only changes when a new high enters or an old one leaves the window.

Definition A is Cross(Close, Level). Definition B is the same cross against Level + AtrMult * ATR(20), so a one-tick poke above the old high does not qualify. Definition C takes A and additionally requires volume above VolMult times its own moving average.

The failure marker looks forward: it asks whether the lowest close of the next FailBars bars is below the level. That is a deliberate forward reference, legitimate only because the array is drawn and never traded — the formula’s header comment says so, and you should form the habit of writing that comment yourself.

  • Cross(a, b) — true on the bar a crosses above b, and only that bar.
  • HHV/LLV with Ref(..., -1) — the prior-window extreme, excluding today.
  • ATR(period) — volatility scale, used to express the margin in the instrument’s own units.
  • MA(Volume, n) — the volume baseline for definition C.
  • PlotShapes(shape, colour, layer, yposition, offset) — marks bars. A positive offset moves the shape up the pane, which is how three markers stack without overlapping.

Set the lookback to 20 and record the three counts. Now set it to 50 without changing anything else. Every count falls, and they do not fall proportionally — the volume-confirmed count usually falls fastest, because longer-lookback breakouts are rarer events and the volume condition prunes what is left. Then set the ATR margin to its maximum and watch the B count collapse. Nothing about the market changed during this experiment. Only your definitions did.

Add definition 4 from the table: two consecutive closes above the level. The pattern is the same one used in the trendline formula, Sum(condition, 2) == 2 AND Sum(condition, 3) == 2, which is true on the second qualifying bar and not afterwards. Then count how many of the A signals survive to become 4 signals, and how many bars later they arrive.

“Breakout” names at least six different events, and the choice between them changes both how many you find and which days they land on. State the definition or the conversation is not about anything.

The mechanics matter: HHV includes the current bar, so the level needs a Ref(..., -1) shift, and a breakout is an event, so it needs Cross rather than a comparison. Confirmation filters trade false starts for worse prices, fewer observations and more parameters.

A failed breakout needs both a distance and a time limit before it means anything, and it cannot be identified at the time. Role reversal is two claims stacked, only the first of which has an obvious mechanism, and it becomes measurable only when you specify the sequence and supply a comparison. Gaps deserve separate treatment because a gap through a level means the level was never actually traded.

Check your understanding

Question 1. Why does this fragment fire on far more bars than intended?
Level = Ref( HHV( High, 20 ), -1 );
Signal = Close > Level;
Show the answer and why

Answer: It is a state, not an event: it stays true for every bar price remains above the level

The level itself is correct here — the Ref shift is present. The problem is the comparison: Close > Level is true on every bar of the move, not just the bar it began. Cross( Close, Level ) is the event.

Question 2. AmiBroker’s GapUp() is true when which condition holds?
Show the answer and why

Answer: Yesterday’s high is less than today’s low

The official page defines a gap up as yesterday’s high being less than today’s low, so the whole of today’s range sits clear of yesterday’s. The looser "open above previous close" definition is common elsewhere and produces a much larger count.

Question 3. A study claims that 70% of breakouts fail. Which single missing specification most undermines the number?
Show the answer and why

Answer: The window within which the reversal must occur

Interval matters and sector might, but without a time window "failure" simply means "price traded back below the level at some point ever", which nearly everything eventually does. The statistic then measures the length of the history rather than the behaviour of breakouts.

Question 4. Which of these are true consequences of adding a volume-confirmation filter to a breakout rule? Select all that apply.
Show the answer and why

Answer: The number of signals falls, The number of free parameters increases, The evidence base for any conclusion gets smaller

A filter removes signals and adds at least two parameters (the multiple and the averaging length), which shrinks the sample any conclusion rests on. Whether the survivors behave better is the open question the filter was supposed to answer, and cannot be assumed.

Sources for this lesson

8 verified · checked 2026-08-31

  1. 01AFL Function Reference — HHVamibroker.com/guide/afl/hhv.html2026-08-31
  2. 02AFL Function Reference — Crossamibroker.com/guide/afl/cross.html2026-08-31
  3. 03AFL Function Reference — Refamibroker.com/guide/afl/ref.html2026-08-31
  4. 04AFL Function Reference — GapUpamibroker.com/guide/afl/gapup.html2026-08-31
  5. 05AFL Function Reference — GapDownamibroker.com/guide/afl/gapdown.html2026-08-31
  6. 06AFL Function Reference — Insideamibroker.com/guide/afl/inside.html2026-08-31
  7. 07AFL Function Reference — Outsideamibroker.com/guide/afl/outside.html2026-08-31
  8. 08AFL 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.