Reality Check: Do High-Volume Breakouts Lead Anywhere?
You now have the tools to stop taking claims on trust. This lesson uses them on one of the most widely repeated claims in technical analysis, and the method matters far more than the answer you get.
The claim
Section titled “The claim”A breakout on unusually heavy volume is more significant than a breakout on ordinary volume. The volume confirms the move.
You will find this in books, courses and every trading forum. It is stated as though it were a property of markets rather than a hypothesis about them.
Notice what is wrong with it as written: every load-bearing word is undefined. Breakout above what, over what window? Unusually heavy compared to what? More significant in what measurable sense, over what horizon? As stated, the claim cannot be true or false, because it cannot be tested.
Our job is to replace it with something that can be.
Step 1: state the claim so it can be measured
Section titled “Step 1: state the claim so it can be measured”From a slogan to a testable question
- The slogan"Volume confirms a breakout." Not testable — no definitions, no horizon, no comparison.
- Define every term objectivelyBreakout = close above the highest high of the prior 50 bars. High volume = at least twice the 50-bar average volume computed to yesterday.
- Define the outcomePercentage change of the close over the following 20 bars. One number, computed the same way for every observation.
- Define the comparisonThe same measurement over every eligible bar, condition ignored. Without this the first number is uninterpretable.
- Decide in advance what would count as an answerWrite down, before running it, what difference would be interesting and what would not.
Here is the version we will actually test:
Among liquid shares priced above a floor, does the average 20-bar forward return following a close above the prior 50-bar high on volume at least twice the prior 50-bar average differ materially from the average 20-bar forward return following any eligible bar in the same universe and period?
Every term in that sentence is now a number in a formula. Somebody who disagrees with a choice can change one constant and re-run instead of arguing about words. That is the point.
Step 2: the two guards that make the measurement honest
Section titled “Step 2: the two guards that make the measurement honest”Nothing in the condition may use today’s own data to judge today
Section titled “Nothing in the condition may use today’s own data to judge today”Fragment — not a complete formula
PriorHigh = Ref( HHV( High, BreakLookback ), -1 );PriorAvgVol = Ref( MA( Volume, VolLookback ), -1 );Both windows are shifted back one bar. Without the shift, HHV( High, 50 ) includes today’s
high, so “close above the 50-bar high” becomes nearly impossible to satisfy — today’s high is
almost always at least today’s close. And MA( Volume, 50 ) including today means a huge volume
day inflates the very benchmark it is being compared against, which weakens the test in the
opposite direction.
Neither error announces itself. Both quietly change what you measured.
The outcome uses the future on purpose, and must never be copied
Section titled “The outcome uses the future on purpose, and must never be copied”Fragment — not a complete formula
FwdReturn = 100 * ( Ref( Close, Horizon ) / Close - 1 );Ref() with a positive period references bars in the future — the documentation says so
directly, and calls it “looking up the future”. In a formula that assigns Buy or Sell, that
is look-ahead bias and the results are worthless.
Here it is the measurement itself. The study asks “what followed?”, not “what should I do?”. Measuring history that has already happened is a legitimate and completely different activity from trading on information you did not have.
Note also that Ref( Close, 20 ) is Null within 20 bars of the end of the data — correctly, because
those bars have no outcome yet. Measurable drops them. Counting them as zero would drag every
average towards zero by an amount that depends on how recently you ran the study.
Step 3: choose the universe and the period, and write them down
Section titled “Step 3: choose the universe and the period, and write them down”Both are decisions, and both change the answer.
Universe. Liquid shares — a median turnover floor and a minimum price — because a “breakout” in an instrument that trades £4,000 a day is not a thing anyone could act on, and because penny stocks produce percentage returns that dominate any average they are included in.
Period. As long as your data allows, and long enough to contain more than one kind of market. A study that covers one sustained bull market has one observation of “a market”, not thousands. Part 30’s regime lesson develops this.
Data. Split- and dividend-adjusted daily bars. Unadjusted data manufactures both breakouts and volume spikes on corporate-action bars, which is precisely the event this study is looking for. This one is not a preference — an unadjusted database will produce a stronger-looking result for a purely mechanical reason.
Step 4: the formula
Section titled “Step 4: the formula”Complete runnable AFL
// breakout-forward-study.afl// Part 12 - Reality Check: Do High-Volume Breakouts Lead Anywhere?//// Measures what actually happened in the twenty bars after an objectively// defined high-volume breakout, and - on a second run with one line changed -// what happened in the twenty bars after EVERY eligible bar regardless of the// condition. The second number is the base rate. Without it the first number// means nothing at all, because a positive average after breakouts in a market// that rose over the period is not evidence about breakouts.//// THIS FORMULA READS FUTURE BARS ON PURPOSE.// Ref( Close, +20 ) looks twenty bars ahead. In a formula that produces Buy or// Sell that would be look-ahead bias and the results would be worthless. Here// we are measuring history that has already happened, which is a different// activity: the study asks "what followed?", not "what should I do?". Never// copy this line into a trading formula.//// DEFINITIONS - the whole point of the exercise is that these are arbitrary// but explicit, so that somebody who disagrees can change one number and// re-run rather than argue about words:// breakout : today's close above the highest HIGH of the previous 50// bars (the window is shifted back one bar, so today's own// high cannot make the test trivially impossible)// high volume : today's volume at least twice the 50-bar average volume// computed up to YESTERDAY, so today's volume does not inflate// its own benchmark// eligible : liquid and priced above a floor, measured to yesterday// outcome : simple percentage change of the close over the next 20 bars//// ASSUMPTIONS AND KNOWN WEAKNESSES, stated before the numbers are seen:// - Daily bars, split- and dividend-adjusted. Unadjusted data manufactures// both breakouts and volume spikes on corporate-action bars.// - The database contains the symbols that exist today. Instruments that// were delisted are absent, so the measured outcomes are biased upward by// survivorship. This cannot be fixed from inside the formula; it can only// be reported.// - Rows are not independent observations. One symbol can contribute many// overlapping 20-bar windows, and symbols move together, so the effective// sample size is far smaller than the row count.// - No costs, no slippage, no position sizing, no risk model.
// ---- The one line you change between the two runs -----------------------// False : measure only the bars where the condition was true// True : measure every eligible bar, which is the unconditional base rateMeasureEveryBar = False;
Horizon = 20;BreakLookback = 50;VolLookback = 50;VolMultiple = 2;MinTurnover = 2000000;MinPrice = 2;
// Everything used to define the condition is shifted back one bar, so the// condition is decidable from information available before today's close is// compared against it.PriorHigh = Ref( HHV( High, BreakLookback ), -1 );PriorAvgVol = Ref( MA( Volume, VolLookback ), -1 );PriorTurn = Ref( MA( Close * Volume, VolLookback ), -1 );
Eligible = Close > MinPrice AND PriorTurn > MinTurnover AND PriorAvgVol > 0;
Breakout = Close > PriorHigh;HighVolume = Volume > VolMultiple * PriorAvgVol;Event = Breakout AND HighVolume;
// The outcome. Ref() returns Null within Horizon bars of the end of the data,// which is correct: those bars have no outcome yet and must not be counted as// zero. Dropping them is what Measurable does.FwdReturn = 100 * ( Ref( Close, Horizon ) / Close - 1 );Measurable = NOT IsNull( FwdReturn ) AND NOT IsNull( PriorHigh ) AND NOT IsNull( PriorAvgVol );
if( MeasureEveryBar ) Population = Eligible;else Population = Eligible AND Event;
Filter = IsTrue( Population AND Measurable );
AddColumn( Close, "Close", 1.2 ); // column 3AddColumn( Volume / PriorAvgVol, "Vol / 50d avg", 1.2 ); // column 4AddColumn( 100 * ( Close / PriorHigh - 1 ), "% above 50d high", 1.2 );// column 5AddColumn( FwdReturn, "Fwd 20-bar %", 1.2 ); // column 6AddColumn( FwdReturn > 0, "Up in 20?", 1.0 ); // column 7AddColumn( DateTime(), "Bar (ISO)", formatDateTimeISO ); // column 8
// AVERAGE (2), MIN (4), MAX (8), STDEV (32) and COUNT (16) for the two// outcome columns only. The average of column 7 is the proportion of// observations that were positive; the count is the sample size; the standard// deviation of column 6 is what tells you how little the average means.// All of these rows appear at the TOP of the result list, not the bottom.AddSummaryRows( 2 + 4 + 8 + 16 + 32, 1.2, 6, 7 );
SetSortColumns( -6 );One line is the whole experiment:
Fragment — not a complete formula
// False : measure only the bars where the condition was true// True : measure every eligible bar, which is the unconditional base rateMeasureEveryBar = False;You run the exploration twice, changing that one value, and compare the two summary blocks.
AddSummaryRows( 2 + 4 + 8 + 16 + 32, 1.2, 6, 7 ) requests AVERAGE (2), MIN (4), MAX (8), COUNT
(16) and STANDARD DEVIATION (32) for columns 6 and 7 only. Those flags are documented values, and
summary rows are added at the top of the result list — so the numbers you came for are the
first thing on screen, not something you scroll to find.
Column 7 is FwdReturn > 0, a Boolean. Its average is therefore the proportion of observations
that were positive, which is the second number you want.
Step 5: run it and record both results
Section titled “Step 5: run it and record both results”Run it with MeasureEveryBar = False, record the summary. Change the line to True, run it
again, record that summary. Fill in this table — on paper, before you interpret anything:
| Condition true | Every eligible bar | |
|---|---|---|
| Count (sample size) | ||
| Average 20-bar forward return, % | ||
| Standard deviation of that return | ||
| Proportion positive | ||
| Min / Max |
Step 6: read it against the base rate
Section titled “Step 6: read it against the base rate”This is the step the original claim skips, and it is the reason this lesson exists.
Suppose the conditional average is +1.4%. That sounds like something. Now suppose the unconditional average — every eligible bar, condition ignored — is +1.2%. The condition contributed 0.2 percentage points, in a sample where the standard deviation of individual outcomes is perhaps 12%.
A positive average after breakouts, in a market that rose over the period, is not evidence about breakouts. It is evidence that the market rose. The only number that can speak to the claim is the difference, and the difference has to be judged against how noisy the individual observations are.
Three sanity questions to ask of the difference
Section titled “Three sanity questions to ask of the difference”How big is it compared with the standard deviation? If the gap between the two averages is a small fraction of the spread of individual outcomes, you cannot distinguish it from noise by looking at it.
How big is it compared with trading costs? A 0.2% edge over 20 bars does not survive commission and spread. An effect too small to trade is still interesting scientifically, but be clear which claim you are making.
How much did the sample actually contain? Which brings us to the honest part.
Step 7: what this exploration cannot establish
Section titled “Step 7: what this exploration cannot establish”Every one of these is a real limitation of the design, not a caveat added for politeness. They are also written into the formula’s own header, which is where limitations belong.
The rows are not independent observations. One symbol contributes many overlapping 20-bar windows — a breakout on Monday and another on Wednesday share 18 bars of outcome. And symbols move together, so a single market-wide surge can produce hundreds of “independent” rows that are really one event. The effective sample size is far smaller than the row count, and no standard statistical formula applied to the row count will tell you the truth about it.
Survivorship bias, and it cannot be fixed here. Your database contains the symbols that exist today. Companies that broke out on heavy volume and then went to zero were delisted and are absent. This biases the measured outcome upward, by an unknown amount, and the only honest response is to state it. Part 30 takes survivorship apart properly.
No costs, no slippage, no sizing, no risk model. This is a measurement of price change, not a simulation of trading. A positive average forward return is not a profitable strategy, and Part 28 is where the difference gets built.
One set of parameters was tested. If you now try 30 bars, and 100 bars, and 1.5×, and 3×, and report the best — you have performed data snooping, and the best of twenty tries on one dataset looks good for reasons that have nothing to do with markets.
Nothing here says anything about the future. The study measured what followed these events in this period in this universe. Whether the same relationship holds in a period you have not seen is a completely separate question that this design cannot address.
Step 8: what a stronger design would look like
Section titled “Step 8: what a stronger design would look like”If you want to push further, these are the changes that add real information, roughly in order of value:
- Split the sample by period. Run the same study over three or four disjoint multi-year spans. A relationship that appears in one span and not the others is telling you about that span.
- Compare against a matched control rather than everything. Instead of “every eligible bar”, use “every breakout without the volume condition”. That isolates the volume claim, which is what the original slogan is actually about.
- Look at the distribution, not the average. Export the forward returns and look at the shape. An average pulled up by three enormous outcomes is a different phenomenon from a consistently mild positive shift.
- Vary the horizon. 5, 10, 20, 60 bars. If an effect exists at 20 but nowhere near it, be suspicious of the 20.
- Record everything you tried, including what did not work. That log is the only defence against fooling yourself, and it is what the screen journal was for.
A claim that cannot be false cannot be tested, so the first job is always to replace undefined words with numbers. Every element of the condition must be computable from information available before the bar it judges. The outcome may legitimately read future bars, because measuring the past is not the same activity as trading — but that line must never migrate into a signal formula. And the conditional result on its own is meaningless: the base rate is the measurement. What you are left with is usually a small difference sitting inside a large spread, drawn from a sample far less informative than its row count, in a universe missing everything that failed. Saying that clearly is the finding.
Check your understanding
Sources for this lesson
5 verified · checked 2026-08-31
- 01AFL Function Reference — Ref§ A positive period references n periods in the futureamibroker.com/guide/afl/ref.html2026-08-31
- 02AFL Function Reference — AddSummaryRows§ Summary rows are added at the top of the listamibroker.com/guide/afl/addsummaryrows.html2026-08-31
- 03AFL Function Reference — SetSortColumnsamibroker.com/guide/afl/setsortcolumns.html2026-08-31
- 04AFL Function Reference — HHVamibroker.com/guide/afl/hhv.html2026-08-31
- 05AmiBroker User's Guide — Explorationamibroker.com/guide/h_exploration.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.