Lab: Indicator Behaviour Workbench
Reading about indicators and watching them behave are different activities, and only the second one changes what you believe. In this lab you will build a single chart sheet that shows trend, momentum, trend strength, volatility and participation for one instrument at the same time, then work through four named stretches of its history writing down what each measure actually did. At the end you will have a page of your own observations and — more valuable — a clear sense of how little any single one of them was telling you.
Budget about an hour. Most of it is looking, not typing.
What you will build
Section titled “What you will build”The finished workbench
- Price paneCandles, a fast and a slow exponential average, Bollinger Band edges, and a numeric readout of every other measure in the chart title.
- Momentum paneRSI with the 70, 50 and 30 lines.
- Trend-strength paneADX with +DI and -DI, and a reference line at 25.
- Volatility paneATR as a percentage of price, and Bollinger band width as a percentage of price.
- Participation paneVolume as a histogram with its 50-bar mean drawn across it.
Before you start
Section titled “Before you start”Choose the instrument carefully. You want a liquid, heavily traded symbol with at least ten years of daily history and genuine volume — a large-cap share or a broad-market ETF is ideal. Avoid index symbols and most FX symbols for this lab, because their volume is often zero or synthetic and the participation pane will have nothing to show.
Check the data first. Look at the raw chart before you put anything on top of it. Are there obvious price spikes to a single bar and back? Long flat stretches? Is the history split- and dividend-adjusted? Part 2 covered why this matters; the point here is that every indicator in this lab will faithfully transform whatever defects are in the data and present them to you as signals.
Decide where the usable history starts. Every recursive indicator here — RSI, ATR, ADX and
the exponential averages — carries its initialisation forward well past its first non-Null
bar. Scroll to the beginning of the symbol’s history and mentally discard the first year. Make
all your observations to the right of that line.
The workbench formula
Section titled “The workbench formula”Complete runnable AFL
// indicator-workbench.afl// Part 6 - Lab: Indicator Behaviour Workbench//// One price-pane formula that draws the trend and volatility context, and// reports momentum, trend strength, volatility and participation for the bar// you have selected, in the chart title.//// The point of the lab is comparison: every reading below is produced from the// same bars at the same moment, so you can see which of them agree and which// of them are saying completely different things about the same market.//// Assumptions declared up front:// - Daily bars, one instrument, at least five years of history.// - Volume is genuine. Index and many FX symbols carry zero or synthetic// volume; in that case the relative-volume readout says so rather than// printing a meaningless number.// - AmiBroker's Bollinger Band default period is 15, not the 20 most books// use, so the period is passed explicitly on every call.// - ATR has no documented default period at all, so it is always passed.
_SECTION_BEGIN( "Indicator Workbench" );
FastPeriod = Param( "Fast average", 20, 2, 200, 1 );SlowPeriod = Param( "Slow average", 100, 5, 400, 1 );BandPeriod = Param( "Bollinger period", 20, 5, 200, 1 );BandWidth = Param( "Bollinger width (std dev)", 2, 0.5, 4, 0.5 );RsiPeriod = Param( "RSI period", 14, 2, 50, 1 );AdxPeriod = Param( "ADX period", 14, 2, 50, 1 );AtrPeriod = Param( "ATR period", 14, 1, 100, 1 );VolPeriod = Param( "Volume average", 50, 5, 250, 1 );
// --- Trend context -------------------------------------------------------FastAvg = EMA( Close, FastPeriod );SlowAvg = EMA( Close, SlowPeriod );
// --- Volatility context --------------------------------------------------BandTop = BBandTop( Close, BandPeriod, BandWidth );BandBot = BBandBot( Close, BandPeriod, BandWidth );
// Band width as a percentage of price is the contraction / expansion readout.// Small values mean recent closes have clustered tightly around their average.BandPercent = 100 * ( BandTop - BandBot ) / Close;
// ATR is in price units, so it is not comparable between symbols or between// decades. Dividing by the close turns it into "typical move, in percent".AtrValue = ATR( AtrPeriod );AtrPercent = 100 * AtrValue / Close;
// --- Momentum and trend strength ----------------------------------------RsiLine = RSI( RsiPeriod );TrendStr = ADX( AdxPeriod );PlusLine = PDI( AdxPeriod );MinusLine = MDI( AdxPeriod );
// --- Participation -------------------------------------------------------// MA() is Null for the first VolPeriod-1 bars, and a Null propagates through// every comparison it touches, so the guard checks for Null explicitly rather// than relying on the division to fail quietly.AvgVolume = MA( Volume, VolPeriod );HasVolume = NOT IsNull( AvgVolume ) AND AvgVolume > 0;RelVolume = IIf( HasVolume, Volume / AvgVolume, 0 );
// --- Drawing -------------------------------------------------------------Plot( Close, "Close", colorDefault, styleCandle );Plot( FastAvg, "EMA fast", colorBlue, styleLine | styleThick );Plot( SlowAvg, "EMA slow", colorRed, styleLine | styleThick );Plot( BandTop, "BB top", colorGrey40, styleLine | styleDashed | styleNoTitle );Plot( BandBot, "BB bottom", colorGrey40, styleLine | styleDashed | styleNoTitle );
// --- Readout -------------------------------------------------------------// SelectedValue() reads each array at the bar marked by the chart's vertical// selection line, so the whole readout follows wherever you click.VolumeText = WriteIf( SelectedValue( HasVolume ), NumToStr( SelectedValue( RelVolume ), 1.2 ) + "x average", "no usable volume on this symbol" );
Title = Name() + " Indicator Workbench\n" + "RSI " + NumToStr( SelectedValue( RsiLine ), 1.1 ) + " ADX " + NumToStr( SelectedValue( TrendStr ), 1.1 ) + " +DI " + NumToStr( SelectedValue( PlusLine ), 1.1 ) + " -DI " + NumToStr( SelectedValue( MinusLine ), 1.1 ) + "\n" + "ATR " + NumToStr( SelectedValue( AtrPercent ), 1.2 ) + "% of close" + " Band width " + NumToStr( SelectedValue( BandPercent ), 1.2 ) + "%" + " Volume " + VolumeText;
_SECTION_END();Open Analysis -> Formula Editor, paste it in, type a name such as Indicator Workbench into
the Formula Name field, and press Apply Indicator. It replaces the formula in whichever
pane is currently selected, so click the price pane first.
How it works
Section titled “How it works”The formula has four sections and each one exists for a different reason.
The parameter block at the top puts every number in one place, exposed through Param() so
you can move them with sliders from Parameters (right-click the pane, or Ctrl+R) without
editing code. Two of the defaults are deliberate: the Bollinger period is passed explicitly as
20 because AmiBroker’s own default is 15, and the ATR period is passed explicitly because ATR
has no documented default at all.
The calculation block computes each measure and, where the measure is in price units,
normalises it. AtrPercent and BandPercent divide by the close so that a reading from 2014
is comparable with a reading from today even though the price level has changed. RelVolume
divides today’s volume by its own 50-bar mean, which is the only form in which volume means
anything.
The volume guard is worth reading closely:
Fragment — not a complete formula
AvgVolume = MA( Volume, VolPeriod );HasVolume = NOT IsNull( AvgVolume ) AND AvgVolume > 0;RelVolume = IIf( HasVolume, Volume / AvgVolume, 0 );MA returns Null for the first VolPeriod - 1 bars, and a Null propagates through every
comparison it touches — a comparison against Null gives Null, not false. Testing for it
explicitly with IsNull means the formula degrades into an honest “no usable volume” message
instead of producing a number that looks real.
The drawing and readout block plots the price context and then builds the title. Everything
in the title is wrapped in SelectedValue(), which reads an array at the bar marked by the
chart’s vertical selection line. That is what makes the workbench interactive: click any bar and
all five numbers jump to that bar.
Functions you may not have met in this form
Section titled “Functions you may not have met in this form”SelectedValue( array ) returns a single number, not an array — the value at the selected
bar. In a chart pane “selected” means the vertical selection line; in the Analysis window there
is no such line, and the documented behaviour there is the last bar of the analysis range
instead. That difference matters later; for now, just know that this readout is a charting
device.
NumToStr( number, format ) turns a number into text for the title. The format argument is
written as 1.2 for two decimal places, 1.0 for none.
WriteIf( condition, "text if true", "text if false" ) picks one of two strings. Here it is
what replaces the relative-volume figure with a plain-English message on symbols where volume
is unusable.
What you should see
Section titled “What you should see”Candles, a blue fast average and a red slow average, and two dashed grey lines above and below price marking the Bollinger edges. Two lines of text at the top: RSI, ADX, +DI and -DI on the first, and ATR as a percentage, band width as a percentage and relative volume on the second.
Click on any bar. Every number should change. Click on a bar in the first few weeks of the symbol’s history: several numbers should be blank or obviously nonsensical, which is the warm-up making itself visible.
Check that it is right
Section titled “Check that it is right”Three quick checks, in order:
- Set the ATR period to 1 in the Parameters dialog.
AtrPercentshould now report the true range of the selected bar as a percentage of its close. Pick an obvious gap bar and confirm the number is larger than( High - Low ) / Closewould give. - Set both averages to the same period. The blue and red lines should sit on top of each
other, because both are
EMA— if they do not, you have edited one of the two calls. - Select a bar where volume was visibly enormous. The relative-volume figure should be well
above 1. If it reads exactly
0x averageon every bar, your symbol has no usable volume and you should choose a different one for this lab.
When it goes wrong
Section titled “When it goes wrong”If the chart shows nothing but a flat line, you applied the formula to a pane that was already
scaled for something else, or you applied it to a fresh blank pane and the price is not being
plotted — re-apply it to the price pane. If the title is missing, check that the string
concatenation has not been broken by an edit; every + between pieces of text matters. If a
number reads as an empty value, you are looking at a warm-up bar. If the Bollinger lines hug
price far too tightly, check that the band period is 20 and not something small.
Building the rest of the sheet
Section titled “Building the rest of the sheet”Add four more panes below the price pane. The quickest route for each is Analysis -> Formula Editor, paste, name, Apply Indicator — but click on an empty pane first, or use the pane
context menu to insert a new one, so that you do not overwrite the workbench.
Fragment — not a complete formula
// Pane 2 — momentumPlot( RSI( 14 ), "RSI(14)", colorBlue, styleLine | styleThick );PlotGrid( 70, colorRed, 9, 1, True );PlotGrid( 50, colorGrey40, 8, 1, False );PlotGrid( 30, colorGreen, 9, 1, True );Fragment — not a complete formula
// Pane 3 — trend strength and directionPlot( ADX( 14 ), "ADX(14)", colorBlue, styleLine | styleThick );Plot( PDI( 14 ), "+DI", colorGreen, styleLine );Plot( MDI( 14 ), "-DI", colorRed, styleLine );PlotGrid( 25, colorGrey40, 9, 1, True );Fragment — not a complete formula
// Pane 4 — volatility, both measures normalised so they share one scalePlot( 100 * ATR( 14 ) / Close, "ATR(14) %", colorBlue, styleLine | styleThick );Plot( 100 * ( BBandTop( Close, 20, 2 ) - BBandBot( Close, 20, 2 ) ) / Close, "Band width %", colorRed, styleLine );PlotGrid( 0, colorBlack, 8, 1, False );Fragment — not a complete formula
// Pane 5 — participationVolumeMean = MA( Volume, 50 );BarTone = IIf( Close >= Ref( Close, -1 ), colorGreen, colorRed );Plot( Volume, "Volume", BarTone, styleHistogram | styleThick );Plot( VolumeMean, "50-bar mean", colorBlue, styleLine | styleThick );The observation protocol
Section titled “The observation protocol”Now the actual lab. Pick four stretches of your instrument’s history, each at least three months long, chosen to be as different from each other as possible:
- A sustained advance. A period where price rose substantially and mostly without interruption.
- A sideways range. A period where price ended roughly where it started, with no clear direction.
- A sharp decline. A fast, high-volatility fall. Many databases will have one in early 2020; any decade offers candidates.
- A recovery out of that decline. The stretch immediately after the low, before the next regime settled.
Write down the exact start and end dates of each. Use View -> Zoom -> Range after marking the
period, or simply note the dates — the point is that your observations should be repeatable by
someone else, and “the bit in the middle where it went up” is not repeatable.
What to record
Section titled “What to record”For each of the four episodes, record these eight things. A notebook, a spreadsheet or a text file is fine; consistency across the four rows matters more than the format.
| Column | What to write down |
|---|---|
| Episode and dates | A name you chose, plus exact start and end dates |
| Price change | Roughly how far price moved, in per cent, start to end |
| Fast vs slow average | Did the fast average stay on one side of the slow one, or cross repeatedly? Roughly how many crossings? |
| SMA vs EMA gap | Set the workbench to a single period and watch the grey gap line: how far did the exponential average lead the simple one, in per cent of price? |
| RSI range | The approximate band RSI occupied. Did it sit above 70 for extended stretches, oscillate through 50, or stay pinned low? |
| ADX and DI | Did ADX rise, fall or drift? Which DI line was on top, and did they cross often? |
| Volatility | ATR as a percentage at the start versus at the end. Did band width contract before the episode began or expand during it? |
| Volume | Was participation above or below its 50-bar mean during the episode? Were the largest volume bars at the beginning, the middle or the end? |
The five comparisons
Section titled “The five comparisons”With the table filled in, answer these in writing. Complete sentences, not bullet fragments — having to write a sentence exposes the places where you do not actually have an observation.
-
SMA against EMA. In which episode was the gap between them largest, and does that match what the weighting argument in the moving averages lesson predicted? Both are averages of the same closes, so the gap can only widen when recent bars differ from older ones in the window.
-
Momentum in trends against ranges. Compare the RSI row for the sustained advance with the RSI row for the sideways range. In which one did RSI spend more time beyond 70 or below 30? Now answer the question that matters: in the advance, would a rule that sold at 70 have sold once, or repeatedly, and how much of the advance would it have missed? You are not testing the rule here; you are looking at whether the behaviour is what the arithmetic said it must be.
-
Trend strength against direction. Find a stretch inside your sharp decline where ADX rose steeply. What was price doing? Now find a stretch inside your sustained advance where ADX also rose steeply. Write one sentence explaining why the same ADX behaviour accompanied opposite price outcomes.
-
Volatility expansion and contraction. Look at band width in the twenty bars before each episode began. Did it contract before all four, some of them, or none? Be honest about the ones where it did not — those are the observations that stop this becoming a demonstration of what you expected to find.
-
Price and volume. In which episode was volume most elevated relative to its mean? Was that the episode with the largest price move? Note where in each episode the heaviest bars occurred, and resist attaching a story to it for now.
Conclusions, and what they are worth
Section titled “Conclusions, and what they are worth”Write a short paragraph for each of the five comparisons, then a final paragraph answering one question: which pair of indicators most often disagreed, and what were they disagreeing about?
That last question is the real content of the lab. You will typically find that they were not disagreeing about the market at all — they were reporting different properties of it, at different speeds, and you had been reading them as though they were voting on the same proposition.
Then write down what this exercise cannot support, in your own words. Some of it:
- One instrument. Everything you observed could be a property of this symbol.
- Four episodes you chose, knowing the outcome. This is selection at its purest.
- One interval. Daily bars. The same indicators on weekly or 15-minute bars have different characteristics.
- One parameter set. Change RSI to 7 or 21 and several of your observations change with it.
- Description, not prediction. You recorded what indicators did during periods you had already identified. Nothing here says anything about identifying such periods in advance, which is a completely different and much harder problem.
Extension
Section titled “Extension”Two worthwhile ways to take it further, in increasing difficulty.
Repeat on a second instrument from a different market. Do not change the parameters. Fill in the same eight columns for four comparable episodes and compare the two sheets. Anything that holds on both is slightly more interesting than anything that holds on one; anything that reverses is a warning about how much of your first sheet was symbol-specific.
Change the interval, not the formula. Switch the chart to weekly and re-read the same four episodes. The formula does not change — AmiBroker time-compresses on the fly — but the number of bars in each episode collapses by a factor of five, and every indicator now has a different warm-up, a different saturation behaviour and a different crossing count. Write one paragraph on what stayed the same and what did not. It is the cheapest lesson in multi-timeframe thinking you will ever get, and Part 14 will build on it properly.
You now have a workbench that reports trend, momentum, trend strength, volatility and
participation for the same bars at the same moment, and a written record of what each of them
did across four different kinds of market. The chart formula normalises everything that is in
price units so the readings are comparable across the years, and it guards explicitly against
Null warm-up values and missing volume instead of quietly printing something wrong. Most
importantly you have a page of observations with an honest list of what they cannot support —
which is the shape every piece of research in this course takes from here on.
Check your understanding
Sources for this lesson
7 verified · checked 2026-08-31
- 01AFL Function Reference — Plotamibroker.com/guide/afl/plot.html2026-08-31
- 02AFL Function Reference — SelectedValueamibroker.com/guide/afl/selectedvalue.html2026-08-31
- 03AFL Function Reference — BBandTopamibroker.com/guide/afl/bbandtop.html2026-08-31
- 04AFL Function Reference — ATRamibroker.com/guide/afl/atr.html2026-08-31
- 05AmiBroker User's Guide — Chart sheetsamibroker.com/guide/h_sheets.html2026-08-31
- 06AmiBroker User's Guide — Creating indicators by drag-and-dropamibroker.com/guide/h_dragdrop.html2026-08-31
- 07AmiBroker User's Guide — Charting§ Selecting a quote, zoomingamibroker.com/guide/h_charting.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.