Your First Formula
Thirty minutes from now you will have a working custom indicator on a chart, saved under a name you chose, and you will have made it fail on purpose twice so that the first two AmiBroker error messages you ever read are ones you understand completely.
Type the formula rather than downloading it. The typing is the point: it is where the mistakes come from, and the mistakes are half the lesson.
Draw the price as candles, with one simple moving average of the closing price on top of it, and a title line that shows the numbers behind the picture. That is deliberately modest. It is also the skeleton of perhaps a third of everything you will write in this course — take some data, derive something from it, draw both, and label them.
The complete formula
Section titled “The complete formula”Complete runnable AFL
// first-formula.afl// Part 8 - Your First Formula//// Purpose: see one complete, working AFL formula end to end, before the// language is taken apart lesson by lesson.// Draws: the price as candles, plus one simple moving average of the close.// Assumes: any bar interval and any symbol. Fewer bars than MaPeriod is not// an error - the average is simply empty until it has enough data.// Apply: Formula Editor -> type a name in the Formula Name field -> Save ->// Apply indicator. The formula replaces the chart in the pane that// is currently selected.
_SECTION_BEGIN( "First Formula" );
// ---------------------------------------------------------------------------// Setting// ---------------------------------------------------------------------------
// How many bars the average covers. Change this one number, press Apply// indicator again, and watch the line become smoother (larger number) or// twitchier (smaller number). Nothing else in the formula has to change,// which is the whole reason the number lives here and not inside the call.MaPeriod = 50;
// ---------------------------------------------------------------------------// Calculation// ---------------------------------------------------------------------------
// Close is a built-in array: one closing price for every bar on the chart.// MA() returns another array of the same length - the average as it stood at// each bar. This single line performs one average per bar, however many// thousands of bars the chart happens to hold.Average = MA( Close, MaPeriod );
// ---------------------------------------------------------------------------// Drawing// ---------------------------------------------------------------------------
// Plot() draws an array. The second argument is the name shown in the chart// title bar, the third is the colour, the fourth is the drawing style.Plot( Close, "Close", colorDefault, styleCandle );Plot( Average, "MA(" + MaPeriod + ")", colorBlue, styleLine | styleThick );
// The title puts the numbers behind the picture on screen. StrFormat replaces// each %g with the value of the matching argument at the selected bar.Title = StrFormat( "{{NAME}} {{DATE}} Close %g MA(%g) %g", Close, MaPeriod, Average );
_SECTION_END();Open Analysis -> Formula Editor, type it in, and read the next section before you press
anything.
How it works
Section titled “How it works”The formula has four parts, and they are separated by comment banners for exactly that reason.
The section marker. _SECTION_BEGIN( "First Formula" ) and _SECTION_END() mark the
beginning and end of a drag-and-drop section. AmiBroker’s own indicator formulas use them,
which is reason enough to adopt the habit now, and from Part 10 they start to matter: they
are what lets a chart carry several independent plots each with their own parameters. The
section name must be a literal string in quotes — a variable there is an error.
The setting. MaPeriod = 50; puts the one number a reader might want to change on its
own line, at the top, with a name. It could have been written directly into the call as
MA( Close, 50 ), and beginners’ formulas usually are. The habit of naming it costs one
line and pays back every time you revisit the formula, because the alternative is hunting
for magic numbers scattered through an expression.
The calculation. Average = MA( Close, MaPeriod ); is the line that matters, and it is
doing far more than it looks like. Close is not a number — it is a built-in array holding
one closing price for every bar on the chart. MA() reads that entire array and returns
another array of exactly the same length, holding the average as it stood at each bar. If
the chart has 5,000 bars, that one line performed 5,000 averages. Lesson five takes this
apart properly; for now, notice only that nothing in the formula mentions a bar, a loop or
a starting point.
The drawing. Two Plot() calls and a Title assignment. Each Plot() takes an array
and draws it; the second argument is the name that appears in the chart title bar, the
third is the colour, the fourth is the style. styleLine | styleThick combines two style
flags with the | operator, which is how every combination of AmiBroker style constants is
built. The Title line uses StrFormat() to substitute values into a template; {{NAME}}
and {{DATE}} are placeholders AmiBroker fills in with the symbol and the date of the bar
you have selected.
Key functions
Section titled “Key functions”You have met five things you had not seen before. Two of them are functions you will use in almost every formula from here on.
MA( ARRAY, periods )— simple moving average. It takes an array and a period and returns an array. Note the argument order: the data first, the length second. It is one of the functions whose period may itself vary bar by bar, which Part 9 uses.Plot( array, name, color, style )— draws an array on the current pane. The full signature has nine parameters; the four used here are enough for now, and Part 10 covers the rest.StrFormat( formatstr, ... )— builds a string by substituting values into a template. For numbers use%g,%for%e;%ddoes not work, because there are no integers in AFL at all — every number is floating point._SECTION_BEGIN( "name" )/_SECTION_END()— drag-and-drop section markers.Title— not a function but a reserved variable. Assigning to it replaces the text in the chart’s title bar.
Expected result
Section titled “Expected result”Type a name — My First Formula will do — into the Formula Name field in the editor
toolbar, press Save, then press Apply indicator.
The missing line at the left-hand edge is not a defect and not something to fix. A 50-bar average has nothing to average until 50 bars have gone by, so the first stretch of the array holds empty values, and AmiBroker draws nothing where a value is empty. Lesson eight is entirely about that behaviour and the damage it does when you fail to notice it.
Changing the period
Section titled “Changing the period”Change MaPeriod to 20, press Apply indicator again, and watch. The line hugs the
price more closely, turns sooner, and turns more often. Change it to 200 and the opposite
happens: the line is smoother, the empty stretch at the left is much longer, and on a short
chart the line may vanish entirely.
Nothing about the market changed. You changed a definition. This is worth sitting with for a moment, because it is the same point Part 4 made about chart scaling and the same point Part 6 makes about every indicator: the picture is a function of your choices, and a choice you did not consciously make is still a choice.
Test it
Section titled “Test it”“It drew something” is not evidence that it drew the right thing. Two checks take a minute between them and would catch a genuinely wrong formula.
- Check the average against arithmetic you can do yourself. Set
MaPeriodto3. Apply. Now select a bar somewhere in the middle of the chart and read theClosevalue from the title; do the same for the two bars before it. Add the three closes, divide by three, and compare with theMA(3)value the title reports for that same bar. They should agree to the precision shown. If they do not, either you selected the wrong bars or the formula is not doing what you think. - Check the warm-up length. With
MaPeriodat3, scroll to the very first bar in the history. There should be exactly two bars with no blue line, and the line should begin on the third. A 20-period average should leave nineteen. If the line starts on bar 0, something is filling in values that should not exist.
The second check is a useful reflex in general. Any time an indicator produces a value it has no data to produce, you have found either a bug or a padding setting, and both matter.
Reading an error message
Section titled “Reading an error message”Now break it deliberately. In the editor, delete the semicolon at the end of the
MaPeriod = 50; line, and press Check syntax.
Error 32. Syntax error, probably missing semicolon at the end of the previous line.Read the line number it reports, then look at the line above it. This is the single most useful piece of knowledge about AmiBroker’s error messages: because a statement can span several physical lines, the parser only discovers that a semicolon was missing when it reaches something that cannot possibly continue the statement — which is on the next line. The error is reported where it was detected, not where it was made.
Put the semicolon back. Now break it a different way: change MA to MovAvg and check
syntax again. This time the parser reports an unknown identifier, because there is no
function by that name. AmiBroker’s function list is fixed and documented; if a name is not
in the AFL Function Reference, it does not exist, however plausible it sounds.
Common errors
Section titled “Common errors”- The line is drawn but is a flat straight line at the bottom of the pane. You plotted
something whose scale is nothing like the price — volume, for instance — on the price
pane. Part 10 covers
styleOwnScale, which gives a plot its own axis. - Nothing appears at all after Apply indicator. Check which pane was selected.
Apply indicatorreplaces the chart in the currently selected pane, and if the pane you were watching was not the selected one, your formula went somewhere else on the sheet. - The formula vanishes from the Charts tree. You saved it with an external editor, or
into a different folder.
View -> Refresh Allre-reads theFormulasfolder. - Error 30 or 31, “syntax error”, pointing at a line that looks fine. Check for a missing closing parenthesis or quote earlier in the formula. The editor’s brace matching will show you: put the caret next to a bracket and its partner highlights.
- The title shows the same numbers whichever bar you click. You are looking at a
formula that used a function which collapses an array to one number. Not possible with
the formula as written, but it becomes a real trap once you meet
LastValue()in lesson five.
Extension
Section titled “Extension”Add a second moving average of a different length and plot it in a different colour, so the
chart shows a fast and a slow line. You need one extra setting, one extra calculation and
one extra Plot() call, all copied from the pattern already in the formula.
Fragment — not a complete formula
SlowPeriod = 200;SlowAverage = MA( Close, SlowPeriod );Plot( SlowAverage, "MA(" + SlowPeriod + ")", colorRed, styleLine | styleThick );Then resist the obvious next step. It is tempting to write “buy when the fast line crosses the slow line”, and you now have almost enough AFL to express it. The reason to wait is not difficulty. It is that a crossing is an event and a line being above another is a state, these are not the same thing, and the difference is the subject of a whole lesson in Part 9. Formulas written before that distinction is clear tend to generate either a signal on every single bar or no signals at all.
You have written, saved and applied a complete AFL formula. You know that Close is an
array rather than a number, that MA() returns another array of the same length, and that
Plot() draws one. You have changed a parameter and watched the picture change without the
data changing. You have checked the output against arithmetic you did yourself, and you have
read two error messages — including the one that points at the line after the mistake.
The next three lessons slow down and cover the mechanics: what a statement is, what a name
is, and what an expression is. Then lesson five explains what really happened when you
wrote MA( Close, MaPeriod ).
Check your understanding
Sources for this lesson
6 verified · checked 2026-08-31
- 01AmiBroker User's Guide — Creating your own indicatorsamibroker.com/guide/h_indbuilder.html2026-08-31
- 02AFL Function Reference — Plotamibroker.com/guide/afl/plot.html2026-08-31
- 03AFL Function Reference — MAamibroker.com/guide/afl/ma.html2026-08-31
- 04AFL Function Reference — StrFormatamibroker.com/guide/afl/strformat.html2026-08-31
- 05AFL Function Reference — _SECTION_BEGINamibroker.com/guide/afl/_section_begin.html2026-08-31
- 06AmiBroker User's Guide — AFL error listamibroker.com/guide/errors2026-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.