Lab: Structured Chart Interpretation
Everything in this part so far has been a piece. This lab assembles them into a procedure: a fixed order of operations that takes an unfamiliar chart and produces a short written brief. The output is the point. If your chart reading lives only in your head, it cannot be compared with anyone else’s, checked against what happened next, or even compared with your own reading of the same chart a month later.
You will run the procedure three times, on three instruments, and finish with three briefs. Allow about 45 minutes; the first one takes twenty and the third takes five.
What you need
Section titled “What you need”- AmiBroker with the workspace from Part 3, and a database holding at least three years of daily data for the instruments you choose.
- Three instruments that differ from one another: pick one large, heavily traded company, one smaller or thinner listing, and one index or exchange-traded fund. The contrast is deliberate — a procedure that only works on well-behaved charts is not a procedure.
- The two formulas from earlier in this part, saved and applied:
market-structure.aflon the price pane, and the chart brief panel below. - Somewhere to write. A text file is fine. What matters is that the brief exists outside your head.
No part of this lab needs the Professional edition or a live data feed. End-of-day data obtained free is entirely sufficient.
The procedure
Section titled “The procedure”Five steps, always in this order
- 1. FrameWhat am I actually looking at? Symbol, interval, span, data condition
- 2. SwingsApply a stated swing definition; do not eyeball it
- 3. StructureClassify with the stated rule, and note the confirmation lag
- 4. ScaleMeasure volatility and position in range, so "big" means something
- 5. UnknownsWrite down what the chart cannot tell you
The order matters. Most bad chart reading happens because someone starts at step 3 — “what is it doing?” — before establishing what they are looking at or what counts as a large move on this particular instrument.
Step 1: Establish the frame
Section titled “Step 1: Establish the frame”Before any interpretation, write down five facts. They take thirty seconds and they prevent most of the errors that follow.
- Symbol and what it is. A ticker is not self-explanatory. An index, a leveraged fund and an ordinary share behave differently and the chart will not tell you which one you have.
- Interval. Daily, weekly, five-minute. Read it from the toolbar or the chart title rather than assuming.
- How many bars are on screen, and what date range they cover.
View → Zoom → Allshows the whole history available for the symbol;View → Zoom → Normalresets to the default number of bars from Preferences. Knowing which you are looking at is the difference between “a long consolidation” and “the six months I happened to load”. - Adjusted or unadjusted data. From Part 2: an unadjusted series shows a split as a crash that never happened, and an adjusted series has had its historical prices rewritten. Whichever you have, know which.
- Axis type. Linear or logarithmic. Over a long history this changes the shape you are about to describe.
Step 2: Mark the swings
Section titled “Step 2: Mark the swings”Apply the swing definition. Do not mark them by eye — that was the lesson two pages ago, and the reason is that eyeballed swings change with your zoom level.
State the strength you are using in the brief. For daily charts a strength of 5 is a reasonable starting point: it produces a readable number of points on a two-year window and confirms a week after the fact. If the chart shows fewer than four or five confirmed swings of each kind, the strength is too high for the history you have loaded; reduce it, or load more bars.
Note two numbers while you are here: the price of the most recent confirmed swing high, and the price of the most recent confirmed swing low. They are the boundaries of the most recent leg, and they are the levels Part 5 will spend its time on.
Step 3: Classify the structure
Section titled “Step 3: Classify the structure”Read the label from the ribbon and the title: higher highs and higher lows, lower highs and lower lows, or unclassified. Write down the label and the four swing prices it came from, so the brief contains its own evidence.
Then write down the lag. The label could not have been known until the swing strength had elapsed after the newer swing formed. On a daily chart at strength 5, your classification is describing a state of affairs that was established at least a week ago.
Step 4: Measure the scale
Section titled “Step 4: Measure the scale”This is the step that separates a description from an impression, and it is the one people leave out.
“That was a big move” is meaningless until you say big relative to what. Two numbers fix it, and the panel formula below reports both:
- Volatility as a percentage of price. ATR over 20 bars, divided by the close. An instrument whose ATR is 0.8% of price and one whose ATR is 4% of price will produce charts that look identical after auto-scaling, and a 3% move means completely different things on the two.
- Position in the long range. Where the last close sits between the highest high and the lowest low of the long lookback, expressed as 0 to 100. Near 100 means at the top of its own range; near 50 means in the middle of it. Add how long ago those extremes occurred, because a range high set three bars ago and one set 240 bars ago are different situations.
With those in hand, you can write “the last leg was about four times the 20-bar ATR” rather than “a strong move”, and the reader of your brief knows exactly what you meant.
Step 5: State what you do not know
Section titled “Step 5: State what you do not know”The final step is a written list of the things your reading cannot settle. It is the step most likely to be skipped and the one that most improves the brief.
At minimum, every brief should acknowledge:
- The parameter dependence. The structure label came from one swing strength. Say which, and say whether you checked another.
- The confirmation lag. Stated in bars, from step 3.
- What the chart cannot contain. Corporate actions not reflected in the data, index membership changes, anything that happened to the company or the market outside the price series. A chart contains prices, and prices are a summary of decisions whose reasons are not in the file.
- What would change your mind. One concrete, observable condition. “A confirmed swing low below 91.40 would make this a lower low and end the classification” is a falsifiable statement. “If it starts to look weak” is not.
The chart brief
Section titled “The chart brief”The output of the procedure is one paragraph with a fixed shape. Six sentences, in this order:
- Frame. What, at what interval, over what span, on which axis.
- Structure. The label, with the swing strength that produced it.
- Evidence. The four swing prices behind the label.
- Scale. ATR as a percentage of price, and position in the long range.
- Lag. How old this reading is, in bars.
- Falsifier. The one observable condition that would change the classification.
A worked example
Section titled “A worked example”The paragraph below is an example of the form. The numbers are invented to show what a completed brief looks like; they describe no real instrument and no real period.
Hypothetical worked example. ORNX, daily bars, 502 loaded, roughly two years, logarithmic axis, split-adjusted. Structure at swing strength 5 is higher highs and higher lows. The last two confirmed swing highs were 96.20 then 104.80; the last two confirmed swing lows were 88.10 then 93.60. ATR(20) is 2.35, which is 2.2% of the 106.40 close, and the close sits at 91% of the 252-bar range, whose high was made 6 bars ago and whose low was made 214 bars ago. The classification rests on a swing confirmed 5 bars after it formed, so this reading describes a state established at least 5 bars ago, and at swing strength 12 the same chart is unclassified. A confirmed swing low below 93.60 would break the higher-low sequence and end the classification.
Read that again and notice what it does not do. It does not say what will happen next. It does not use the words strong, weak, healthy or overextended. Every number in it can be checked by someone else with the same data, and the last sentence tells them what would prove the reading wrong.
The panel
Section titled “The panel”Steps 1, 3 and 4 all ask for numbers that are tedious to extract by hand and easy to misremember. This panel reports all of them at once, in one place, so that writing the brief is transcription rather than arithmetic.
Complete formula
Section titled “Complete formula”Complete runnable AFL
// chart-brief-panel.afl// Part 4 - Lab: Structured Chart Interpretation//// Reports the handful of numbers the five-step reading procedure asks for, so// that a written chart brief can be checked against something other than// memory and mood. It generates no signals and gives no opinion: it is a// measuring instrument.//// Put it in its own pane below the price chart. The line it draws is where the// last close sits inside the long lookback range, from 0 (the bottom of the// range) to 100 (the top).//// Assumptions:// - Any instrument and interval that has OHLC data.// - "Long lookback" is a number of bars you choose, not a calendar year.// On a daily chart 252 bars is the usual approximation of a trading year;// on a weekly chart it is roughly five years. The panel does not know// which, so it reports the bar count and leaves the reading to you.// - The structure label uses the same definition as market-structure.afl.
_SECTION_BEGIN("Chart brief panel");
Strength = Param( "Swing strength (bars either side)", 5, 1, 25, 1 );LongLookback = Param( "Long lookback (bars)", 252, 20, 2000, 1 );AtrPeriod = Param( "ATR period", 20, 2, 200, 1 );
Window = 2 * Strength + 1;BarNumber = Cum( 1 );HasWindow = BarNumber >= Window;
CandidateHigh = Ref( High, -Strength );CandidateLow = Ref( Low, -Strength );
SwingHigh = HasWindow AND CandidateHigh == HHV( High, Window );SwingLow = HasWindow AND CandidateLow == LLV( Low, Window );
HighNow = ValueWhen( SwingHigh, CandidateHigh, 1 );HighPrev = ValueWhen( SwingHigh, CandidateHigh, 2 );LowNow = ValueWhen( SwingLow, CandidateLow, 1 );LowPrev = ValueWhen( SwingLow, CandidateLow, 2 );
UpStructure = HighNow > HighPrev AND LowNow > LowPrev;DownStructure = HighNow < HighPrev AND LowNow < LowPrev;
// Step 2 of the procedure: where is price inside its own longer range?HasLongWindow = BarNumber >= LongLookback;
RawRangeHigh = HHV( High, LongLookback );RawRangeLow = LLV( Low, LongLookback );
// A dead instrument can print an identical high and low for the whole window,// which would divide by zero. Floor the denominator instead of hoping. The// floor has to sit inside the expression, not in a surrounding condition,// because IIf() evaluates both of its branches before choosing between them.RangeSize = Max( RawRangeHigh - RawRangeLow, 0.000001 );
// Same warm-up discipline as the swing block above. On the earliest bars the// lookback window is not full, so the "252-bar range" is really a 30-bar range// wearing a 252-bar label. Refuse to report it rather than invite the reader to// compare two figures that were measured over different amounts of history.RangeHigh = IIf( HasLongWindow, RawRangeHigh, Null );RangeLow = IIf( HasLongWindow, RawRangeLow, Null );PositionInRange = IIf( HasLongWindow, 100 * ( Close - RawRangeLow ) / RangeSize, Null );
// How long ago the extremes of that range happened. A high made three bars ago// and a high made three hundred bars ago mean very different things.BarsFromHigh = IIf( HasLongWindow, HHVBars( High, LongLookback ), Null );BarsFromLow = IIf( HasLongWindow, LLVBars( Low, LongLookback ), Null );
// Step 3: the scale everything else should be read against. Expressed as a// percentage of price so that two instruments quoted in different units and at// different price levels can be compared at all.Volatility = ATR( AtrPeriod );SafeClose = Max( Nz( Close ), 0.000001 );VolatilityPct = 100 * Volatility / SafeClose;
Plot( PositionInRange, "Position in the " + NumToStr( LongLookback, 1.0 ) + "-bar range (%)", colorBlueGrey, styleLine | styleThick );
PlotGrid( 50, colorLightGrey );PlotGrid( 0, colorLightGrey );PlotGrid( 100, colorLightGrey );
StructureText = WriteIf( UpStructure, "higher highs and higher lows", WriteIf( DownStructure, "lower highs and lower lows", "unclassified" ) );
// BarCount is how many bars this formula was actually given, which is not the// same as how much history exists. A brief written on 90 bars should say so._N( Title = Name() + " - " + Interval( 2 ) + " - chart brief\n" + StrFormat( "Bars available to this formula: %g\n", BarCount ) + "Structure at strength " + NumToStr( Strength, 1.0 ) + ": " + StructureText + "\n" + StrFormat( "Swing highs %g then %g; swing lows %g then %g\n", HighPrev, HighNow, LowPrev, LowNow ) + StrFormat( "Range high %g (%g bars ago), range low %g (%g bars ago)\n", RangeHigh, BarsFromHigh, RangeLow, BarsFromLow ) + StrFormat( "Close %g sits at %.0f%% of that range\n", Close, PositionInRange ) + StrFormat( "ATR(%g) is %g, which is %.2f%% of price", AtrPeriod, Volatility, VolatilityPct ) );
_SECTION_END();How it works
Section titled “How it works”Three groups of calculations feed one title.
The swing and structure block is the same code as the previous lesson, so the panel and
the ribbon can never disagree. The range block computes the highest high and lowest low
over the long lookback, converts the close into a 0-to-100 position inside that band,
and uses HHVBars and LLVBars to report how long ago each extreme occurred. The
volatility block takes ATR and expresses it as a percentage of the close so that the
number is comparable between instruments.
Both divisions are protected. A window in which the high and the low are identical would otherwise divide by zero, which is rarer than you think and not rare enough to ignore — halted instruments, single-print days and thin listings all produce it.
The single plotted line is the position in range, with grid lines at 0, 50 and 100 so the scale reads without a legend.
Key functions
Section titled “Key functions”HHVBars( array, periods )andLLVBars( array, periods )return the number of bars since the array reached its highest or lowest value in the window. They return a bar distance, not a price — confusing them withHHV/LLVproduces a chart scaled absurdly, which is a useful symptom to recognise.ATR( period )is average true range. AmiBroker implements the original formulation, which uses Wilder’s smoothing rather than a simple moving average, soATR(20)is not the same as a 20-bar simple average of true range. Part 6 goes into what it measures.PlotGrid( level, colour )draws a horizontal line at a constant level. The documentation recommends it overPlot()for constants because it performs better.NumToStr( number, format )converts a number to a string. The format1.0means no decimal places, which is what a bar count wants.BarCountis a reserved variable holding how many bars the formula was given. It reflects the delivered range and changes with zoom, so it is a fact about your view, not about the database — which is precisely why step 1 asks for it.
Expected result
Section titled “Expected result”Test it
Section titled “Test it”Verify the panel against the chart before you trust it in a brief:
- Read the range high from the title. Use
View → Zoom → All, find the highest bar in the lookback window by eye, select it with the Select tool and compare the high in the chart title. They should match. - Check that the reported bars-since figure lands you on that same bar when you count back.
- Confirm that when the close equals the range high, the position reads 100, and when it equals the range low, it reads 0.
Common errors
Section titled “Common errors”- Position in range pinned at 100 for long stretches. Correct behaviour on an instrument making successive new highs. Check the bars-since figure: if it is 0 or 1, the high is current.
- The structure label disagrees with the ribbon on the price pane. The two formulas have separate Parameters, and you changed the strength on only one of them.
- ATR percentage looks absurd. Check the instrument’s price. On a very low-priced listing, an ATR that is 15% of price is real, not a bug, and is itself the most important fact in the brief.
- Everything is empty on the left of the chart. The long lookback has not filled yet. Load more bars or shorten the lookback.
Extension
Section titled “Extension”Add a column to the panel reporting the structure label at a second swing strength. The brief’s fifth sentence then writes itself, because the sensitivity check is on screen rather than something you have to remember to do.
Now do it three times
Section titled “Now do it three times”Self-check
Section titled “Self-check”Work through this list against each brief you wrote. Every “no” is a specific thing to fix, not a general feeling that you could have done better.
- Does the brief name the interval and the number of bars, rather than assuming the reader knows?
- Does it state the swing strength?
- Does it contain the four swing prices, so a reader could reproduce the label?
- Does it express the last move in units of ATR or as a percentage, rather than as an adjective?
- Does it state the confirmation lag in bars?
- Does it contain exactly one falsifier, phrased as an observable price condition?
- Is it free of predictions? Search it for the words “will”, “should” and “expect”.
- Could someone with the same database reproduce every number in it?
- Does it say what you checked for data defects and what you found?
If a brief passes all nine, you have written something that can be argued with — which is the only kind of chart reading worth writing down.
What changed
Section titled “What changed”Chart reading started this part as a skill you either had or did not. It ends as a procedure with an order, a set of stated parameters, two formulas that do the arithmetic, and an output that survives contact with another person. You can now hand your reading of a chart to someone, and they can tell you specifically where they disagree.
Part 5 takes the swing points you have learned to identify and asks the obvious next question: do the levels they define actually do anything?
Check your understanding
Sources for this lesson
8 verified · checked 2026-08-31
- 01AmiBroker User's Guide — Beginners' charting guide§ Selecting a quote; Zoomingamibroker.com/guide/h_charting.html2026-08-31
- 02AmiBroker User's Guide — View menu§ Zoomamibroker.com/guide/m_view.html2026-08-31
- 03AFL Function Reference — HHVBarsamibroker.com/guide/afl/hhvbars.html2026-08-31
- 04AFL Function Reference — LLVBarsamibroker.com/guide/afl/llvbars.html2026-08-31
- 05AFL Function Reference — ATRamibroker.com/guide/afl/atr.html2026-08-31
- 06AFL Function Reference — PlotGridamibroker.com/guide/afl/plotgrid.html2026-08-31
- 07AFL Function Reference — NumToStramibroker.com/guide/afl/numtostr.html2026-08-31
- 08AmiBroker User's Guide — AFL Basics (predefined variables)§ BarCountamibroker.com/guide/a_language.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.