Consolidation and Volatility Contraction
“The market is consolidating” is a description dressed as an observation. By the end of this lesson you should be able to replace it with two numbers that another person could compute from the same data and get the same answer — and to say precisely what those numbers licence you to conclude, which is less than most people assume.
What people mean by consolidation
Section titled “What people mean by consolidation”Four different things travel under the same word, and they do not always occur together.
Sideways drift. Price is going neither up nor down in any sustained way: the trend component is small relative to the noise.
A narrowing range. The distance between recent highs and recent lows is shrinking, so successive bars occupy less vertical space.
Falling volatility. The size of a typical bar is decreasing, whether or not the overall range has narrowed.
Falling participation. Volume is declining, which is often bundled into the idea but is a separate measurement about a separate quantity.
You can have a narrowing range in a market that is still moving decisively — a wedge, in the usual vocabulary. You can have falling bar-by-bar volatility while price grinds steadily higher. Since these come apart, a lesson that treats them as one thing will produce confusion later; so pick the ones you care about and measure them separately.
The two most useful, because they are the easiest to make objective, are range width and volatility contraction.
Range width as a number
Section titled “Range width as a number”The raw measurement is simple: over the last N bars, how far apart are the highest high and the lowest low?
Fragment — not a complete formula
RangeHigh = HHV( High, 20 );RangeLow = LLV( Low, 20 );Width = RangeHigh - RangeLow;That number is in price units, which makes it useless for comparison. A 4-unit range means one thing on a 20-unit stock and another on a 400-unit one. Dividing by price fixes the scale problem:
Fragment — not a complete formula
// SafeDivide returns the third argument instead of Null when the divisor is zero,// so one bad bar with a zero close does not empty the whole array.WidthPct = 100 * SafeDivide( RangeHigh - RangeLow, Close, 0 );Now WidthPct is a percentage, comparable across instruments. It still is not comparable
across regimes: 6% is a tight twenty-day range for a small-cap technology share and a wide
one for a large utility. The fix for that is to compare each instrument with itself.
PercentRank(array, range) reports, on a 0 to 100 scale, where the current value of an array
sits within its own last range values. A reading of 0 means this is the lowest value in the
lookback; 100 means the highest.
Fragment — not a complete formula
// "Tighter than 90% of the last year of readings" is a statement that means the// same thing on any instrument.WidthRank = PercentRank( WidthPct, 250 );IsTight = WidthRank < 10;From a price range to a comparable reading
| Bar | t-3 | t-2 | t-1 | t |
|---|---|---|---|---|
HHV(High,20) - LLV(Low,20) | 8.40 | 7.90 | 6.20 | 5.10 |
Close | 104.0 | 103.1 | 102.6 | 102.9 |
WidthPct | 8.08 | 7.66 | 6.04 | 4.96 |
PercentRank(WidthPct, 250) | 46 | 38 | 19 | 8 |
IsTight (rank below 10) | 0 | 0 | 0 | 1 |
Volatility contraction
Section titled “Volatility contraction”Range width asks how much ground price has covered. Contraction asks whether the typical bar is getting smaller, which is a slightly different question and often moves first.
The standard measurement is a ratio of two volatility estimates over different windows:
Fragment — not a complete formula
// Below 1 means recent bars are smaller than the longer-run norm.Compression = SafeDivide( ATR( 10 ), ATR( 50 ), 1 );ATR — average true range — is covered properly in Part 6, including why true range rather
than high-minus-low is the right base and why AmiBroker uses Wilder’s smoothing. For now,
read ATR(10) as “the size of a typical bar over the last ten bars”.
Two alternatives you will meet, both legitimate:
- Standard deviation of returns over the window, using
StDev. Closer to the statistical definition of volatility, blind to gaps and intraday range. - Bollinger band width: the distance between the upper and lower bands as a fraction of
the middle band. Since the bands are built from
StDev, this is a repackaging of the previous option rather than an independent measurement, which is worth knowing before you present them as two pieces of agreeing evidence.
Expansion, and the single-bar markers
Section titled “Expansion, and the single-bar markers”Expansion is the mirror image: Compression rising above 1, or WidthRank climbing back
towards 100. Nothing new is needed to measure it.
Two built-in single-bar markers are conventionally associated with the two states, and both have exact documented definitions:
Inside()is true when today’s high is below yesterday’s high and today’s low is above yesterday’s low. A run of inside bars is the smallest visible form of contraction.Outside()is true on an outside bar, whose range engulfs the previous bar’s.
They are cheap, precise and available with no parameters at all, which makes them useful as a cross-check on the parametrised measurements above. If your contraction meter says the market is tight and there has not been an inside bar in six weeks, one of the two is describing something other than what you think.
Measuring contraction objectively
Section titled “Measuring contraction objectively”The formula below computes both measurements, converts both to percentiles of their own history, and requires the two to agree before a bar is called tight.
Complete runnable AFL
// consolidation-meter.afl// Part 5 - Consolidation and Volatility Contraction//// Turns "the market is consolidating" into two numbers you could sort a whole// watchlist on:// Width - the N-bar high-to-low range as a percentage of price;// Compression - a short-window ATR divided by a long-window ATR.// Both are then reported as a PERCENTILE of their own recent history, because a// 4% range is tight for one instrument and wide for another, and neither number// means anything until it is compared with that instrument's own past.//// Apply this in its OWN pane. It is a ratio, not a price.//// Assumptions:// - daily bars;// - at least SlowAtr + RankLen bars of history, otherwise the early bars are// warm-up and the percentiles are computed from too little data;// - a tight reading says the recent range is small relative to this// instrument's own history. It says nothing at all about direction.
_SECTION_BEGIN("Consolidation meter");
RangeLen = Param( "Range window (bars)", 20, 5, 120, 1 );FastAtr = Param( "Fast ATR period", 10, 2, 50, 1 );SlowAtr = Param( "Slow ATR period", 50, 10, 250, 5 );RankLen = Param( "Percentile lookback (bars)", 250, 50, 1000, 10 );TightPct = Param( "Tight threshold (percentile)", 20, 1, 50, 1 );
RangeHigh = HHV( High, RangeLen );RangeLow = LLV( Low, RangeLen );
// SafeDivide stops a zero or missing close from turning the whole array Null// and silently emptying the pane.Width = 100 * SafeDivide( RangeHigh - RangeLow, Close, 0 );
Compression = SafeDivide( ATR( FastAtr ), ATR( SlowAtr ), 1 );
// PercentRank reports where the current value sits within its own last RankLen// values, on a 0-100 scale. 0 means the lowest in that window, 100 the highest.WidthRank = PercentRank( Width, RankLen );CompressionRank = PercentRank( Compression, RankLen );
// Both measures have to agree before a bar counts as tight. Requiring agreement// between two different measurements of the same idea is cheap insurance// against one of them being an artefact of its own window length.IsTight = WidthRank < TightPct AND CompressionRank < TightPct;
// Colour carries no information that the histogram height does not already// carry, so a reader who cannot distinguish the two colours loses nothing.MeterColor = IIf( IsTight, colorOrange, colorBlueGrey );
Plot( WidthRank, "Range width percentile", MeterColor, styleHistogram | styleThick );Plot( CompressionRank, "ATR compression percentile", colorDarkBlue, styleLine | styleThick );PlotGrid( TightPct, colorRed, 9, 1, True );
TightRun = BarsSince( NOT IsTight );
Title = Name() + " " + RangeLen + "-bar range " + WriteVal( Width, 1.2 ) + "% of price (percentile " + WriteVal( WidthRank, 1.0 ) + ")" + " | ATR" + FastAtr + " / ATR" + SlowAtr + " = " + WriteVal( Compression, 1.2 ) + " (percentile " + WriteVal( CompressionRank, 1.0 ) + ")" + " | bars tight in a row: " + WriteVal( TightRun, 1.0 );
_SECTION_END();How it works
Section titled “How it works”Three stages. Measure: WidthPct from the N-bar high-low range divided by price, and
Compression from the fast ATR over the slow ATR. Normalise: PercentRank converts both
into a 0 to 100 position within their own recent history, so a reading means the same thing
on any instrument. Classify: a bar is tight only when both percentiles are below the
threshold, and BarsSince(NOT IsTight) counts how long that has been true.
The plot puts the width percentile as a histogram and the compression percentile as a line in the same pane, with a grid line at the threshold. The histogram changes colour when the bar qualifies as tight — but the colour is redundant, since the bar height already crosses the grid line, so nothing is lost by a reader who cannot distinguish the two colours.
Key functions
Section titled “Key functions”PercentRank(array, range)— the current value’s position within its own lastrangevalues, on a 0 to 100 scale.SafeDivide(x, y, valueifzerodiv)— division that returns a stated value instead ofNullwhen the divisor is zero.ATR(period)— average true range.BarsSince(array)— how many bars have passed since the condition was last true.PlotGrid(level, colour, pattern, width, label)— a constant horizontal line, cheaper than plotting a flat array.
Expected result
Section titled “Expected result”Test it
Section titled “Test it”Find a chart period you would describe by eye as “a base” and check that the meter agrees. It usually will. Then do the harder test: scroll to a period where the meter says tight and you would not have said so by eye. Work out which of the two measurements drove it. Most of the time it is the compression ratio picking up a sequence of small bars inside a range that is still wide because of one old spike — which tells you something true about the market and something true about the limitations of a twenty-bar high-minus-low.
Common errors
Section titled “Common errors”Extension
Section titled “Extension”Add a third condition: volume percentile also below the threshold. Then compare how often all three agree with how often two of them do. The count of three-way agreements will be much smaller, and you will have to decide whether you have built a better filter or simply a rarer one. That decision — precision against sample size — recurs throughout the course.
What contraction does and does not imply
Section titled “What contraction does and does not imply”Now the part that matters.
What the evidence supports. Volatility clustering is among the better-documented statistical regularities in financial series, and Part 1 introduced it: large moves tend to be followed by large moves, small by small, more than would be expected if bar sizes were independent. So a reading of “volatility is low now” carries genuine information about volatility over the next few bars. That is a real, useful and modest conclusion — and it is about volatility, not about price.
What follows almost tautologically. Contraction is bounded below by zero and mean-reverts in the sense that a period of unusually low readings is, by construction of the percentile, unusual and therefore temporary. “Contraction is eventually followed by expansion” is close to a definition. It is not a discovery, and it certainly is not a timing tool: “eventually” carries all the weight and has no length attached to it.
What does not follow at all. Direction. This is where the popular version overreaches:
The market is coiling like a spring. When it breaks out, the move will be explosive — and upward.
Nothing in a range-width or ATR-ratio calculation contains directional information. Both are computed from absolute distances. A tight range that resolves downward produces exactly the same contraction reading beforehand as one that resolves upward. Any directional expectation has to come from somewhere else, and be tested separately.
And the selection problem again. Every dramatic breakout in market history was preceded by a quieter period, because every period is preceded by a quieter or a noisier one and history remembers the dramatic ones. Scrolling back to find contractions that preceded big moves will find them, in the same way that drawing support lines under old lows finds bounces. The count that matters is the other one: of all contractions meeting your definition, what fraction were followed by a move of the size you care about, and how does that compare with the same fraction measured on ordinary bars?
That question has the shape of a reality check, and the last lesson of this part builds the machinery for exactly that shape of question.
Consolidation is four ideas sharing a word. Two of them — range width and volatility contraction — become numbers easily: measure the range as a percentage of price, measure the short-window ATR against the long-window one, and express both as percentiles of each instrument’s own history so that the readings are comparable.
Requiring two genuinely different measurements to agree is worth more than stacking two measurements built from the same quantity. Inside and outside bars provide a parameter-free cross-check.
Low volatility tending to persist is a defensible claim with evidence behind it. Contraction being followed by expansion is close to a tautology once you have defined contraction as an extreme. Contraction implying a direction is not supported by anything in the calculation, and the vivid coiled-spring language quietly attaches the third claim to the first two.
Check your understanding
Sources for this lesson
8 verified · checked 2026-08-31
- 01AFL Function Reference — ATRamibroker.com/guide/afl/atr.html2026-08-31
- 02AFL Function Reference — StDevamibroker.com/guide/afl/stdev.html2026-08-31
- 03AFL Function Reference — PercentRankamibroker.com/guide/afl/percentrank.html2026-08-31
- 04AFL Function Reference — SafeDivideamibroker.com/guide/afl/safedivide.html2026-08-31
- 05AFL Function Reference — Insideamibroker.com/guide/afl/inside.html2026-08-31
- 06AFL Function Reference — Outsideamibroker.com/guide/afl/outside.html2026-08-31
- 07AFL Function Reference — BarsSinceamibroker.com/guide/afl/barssince.html2026-08-31
- 08AFL Function Reference — HHVamibroker.com/guide/afl/hhv.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.