Skip to content
Level 3 · AFL DeveloperProjectPart 08 · page 2 of 930 min
30Minutes
5AFL functions
6Sources
StandardRequires
AFL functions taught here5

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.

Complete runnable AFL

first-formula.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();

Download first-formula.afl49 lines

Open Analysis -> Formula Editor, type it in, and read the next section before you press anything.

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.

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, %f or %e; %d does 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.

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.

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.

“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.

  1. Check the average against arithmetic you can do yourself. Set MaPeriod to 3. Apply. Now select a bar somewhere in the middle of the chart and read the Close value from the title; do the same for the two bars before it. Add the three closes, divide by three, and compare with the MA(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.
  2. Check the warm-up length. With MaPeriod at 3, 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.

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.

  • 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 indicator replaces 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 All re-reads the Formulas folder.
  • 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.

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

Question 1. With MaPeriod set to 20, how many bars at the start of the chart should have no moving-average line drawn?
Show the answer and why

Answer: Around nineteen or twenty

The average has nothing to average until enough bars have accumulated, so the first values in the array are empty and AmiBroker draws nothing where a value is empty. Checking that the warm-up is the length you expect is a genuine test: an indicator producing values it has no data for is either a bug or a padding setting.

Question 2. AmiBroker reports "Error 32. Syntax error, probably missing semicolon at the end of the previous line" and names line 24. Where should you look first?
Show the answer and why

Answer: Line 23

A statement may span several physical lines, so the parser cannot know a semicolon is missing until it meets something that cannot continue the statement. The error is reported where it was detected, which is normally the line after the mistake.

Question 3. Why is MaPeriod given a name at the top of the formula instead of being written directly as MA( Close, 50 )?
Show the answer and why

Answer: So the value can be changed in one obvious place and reused, including in the plot name

MA( Close, 50 ) is perfectly legal and produces the same picture. The named version is about maintenance: the number appears once, at the top, and the same name feeds the plot label so the chart cannot mislabel itself when you change it.

Question 4. Which of these would the Check syntax button NOT catch? Select all that apply.
Show the answer and why

Answer: Using a 200-bar average on a symbol that has 40 bars of history, Averaging the High when you meant to average the Close

The syntax check is a parser. It finds malformed code, not wrong ideas. Both of the last two produce a formula that parses cleanly, runs without complaint and gives you an answer to a question you did not ask — which is precisely why every project here includes a test procedure.

Sources for this lesson

6 verified · checked 2026-08-31

  1. 01AmiBroker User's Guide — Creating your own indicatorsamibroker.com/guide/h_indbuilder.html2026-08-31
  2. 02AFL Function Reference — Plotamibroker.com/guide/afl/plot.html2026-08-31
  3. 03AFL Function Reference — MAamibroker.com/guide/afl/ma.html2026-08-31
  4. 04AFL Function Reference — StrFormatamibroker.com/guide/afl/strformat.html2026-08-31
  5. 05AFL Function Reference — _SECTION_BEGINamibroker.com/guide/afl/_section_begin.html2026-08-31
  6. 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.