Skip to content
Level 3 · AFL DeveloperLessonPart 08 · page 4 of 924 min
24Minutes
5AFL functions
6Sources
StandardRequires
AFL functions taught here5

Variables, Numbers and Expressions

A formula is mostly expressions, and an expression that computes something other than what you meant is the most expensive kind of bug, because it produces a number rather than an error. This lesson covers what a variable is in AFL, what kinds of number exist, which operators you have, and the one precedence rule that AmiBroker’s own documentation lists among the most common mistakes its users make.

A variable in AFL is a name that holds whatever was last assigned to it.

Fragment — not a complete formula

MaPeriod = 50;
Average = MA( Close, MaPeriod );

There are no declarations, no types to announce, and no initialisation ceremony. The first assignment brings the name into existence. Reading a name that has never been assigned is Error 29, “Variable used without having been initialized” — a clear message that usually means a typo, since a misspelled name is by definition a name never assigned.

Assignment is itself an expression that has a value, which makes chained assignment work:

Fragment — not a complete formula

Wins = Losses = 0;

Both names end up holding zero. This is occasionally useful and rarely necessary. Note the names: Highest and Lowest would have been the natural words here and are both built-in functions, so assigning to them raises Error 33, “Identifier already in use”.

Since version 5.00 there is also compound assignment, where op is one of + - * / % & |:

Fragment — not a complete formula

Total = 0;
Total += 1; // exactly the same as Total = Total + 1;

The documentation describes the compound form as doing the same thing “a little faster”. Use it where it reads better and ignore it where it does not.

This is item one on AmiBroker’s official Common Coding Mistakes list, and it earns its place. = assigns. == compares. They are not interchangeable, and the wrong one usually compiles.

Fragment — not a complete formula

// WRONG - assigns 10 to Variable, then uses the result as a condition
Result = IIf( Variable = 10, High, Low );
// CORRECT
Result = IIf( Variable == 10, High, Low );

AmiBroker helps here. Whenever it sees an assignment inside the condition of an if, while or for, or inside IIf(), it emits Warning 501: “Assignment within conditional. Did you mean == instead of = ?”. Read your warnings. That warning exists because this mistake is common enough to have earned its own diagnostic.

This surprises people, and it is stated flatly in the official documentation for printf: “%d or %x will not work because there are no integers in AFL.”

Every number in AFL is floating point. 5 and 5.0 are the same value. There is no integer division: 7 / 2 is 3.5, not 3. When you need a whole number you have to ask for one with Int() or Round(), and when you format a number for display you use %g, %f or %e, never %d.

Two practical consequences follow. Counting works exactly as you would hope — a running count is just a sum of ones, and nothing overflows or truncates. But equality between computed values is fragile in the way it is fragile in every floating-point language: 0.1 + 0.2 is not reliably equal to 0.3. Compare computed values with > or <, or compare a difference against a small tolerance, rather than testing two calculated quantities for exact equality.

The complete set, with the AFL version each arrived in where the documentation states one:

Operator Meaning
+ addition, and string concatenation
- subtraction, and unary minus
* multiplication
/ division
% remainder / modulus (AFL 1.7 and later)
^ exponentiation
& bit-wise And (AFL 2.1 and later)
| bit-wise Or (AFL 2.1 and later)

Note carefully what & and | are and are not. They are bit-wise operators, not logical ones. Their everyday use in AFL is combining flag constants — styleLine | styleThick merges two drawing styles, and every combination of AmiBroker’s style, stop and composite flags is built the same way. The logical operators are AND, OR and NOT, and they are the subject of lesson seven.

Dividing by zero does not raise an error in AFL. It produces an infinite value, which then travels through every subsequent calculation and quietly ruins it. Three tools deal with it:

Fragment — not a complete formula

// Guard the result: Nz() turns Null, NaN and Infinity into 0 (or a value you choose).
Ratio = Nz( ( High - Low ) / ( Close - Low ) );
// Or guard the division itself, which says what you meant more clearly.
Ratio = SafeDivide( High - Low, Close - Low, 0 );
// Or test explicitly, when the right answer is "do not use this bar".
Usable = IsFinite( ( High - Low ) / ( Close - Low ) );

SafeDivide( x, y, valueifzerodiv ) arrived in AmiBroker 6.40 and returns the third argument whenever the divisor is zero. It is the most readable of the three, because the reader can see what happens in the bad case without working backwards from a repair.

When there are no parentheses, AFL applies operators in a fixed order. The official table lists twenty-three ranks; here are the ones that decide the shape of ordinary expressions, highest binding first:

  1. [ ] array element
  2. ^ exponentiation
  3. - unary minus (negation)
  4. * / %
  5. + -
  6. < > <= >=
  7. == !=
  8. & bit-wise And
  9. | bit-wise Or
  10. NOT
  11. AND
  12. OR
  13. = and the compound assignments

Three of those rankings routinely catch people.

Multiplication before addition. The official guide gives the example itself: H + L / 2 means H + ( L / 2 ), not ( H + L ) / 2. If you want the midpoint of the bar, the parentheses are not optional.

Fragment — not a complete formula

MidPrice = ( High + Low ) / 2; // the midpoint
NotMidPrice = High + Low / 2; // High plus half the Low. A different number entirely.

Exponentiation before unary minus. ^ sits above negation, so -2^2 parses as -( 2^2 ), which is -4. If you want 4, write ( -2 )^2.

Comparisons before logic. AND, OR and NOT all bind looser than the comparison operators, which is exactly what you want: a > b AND c > d groups as ( a > b ) AND ( c > d ) without any help. But AND binds tighter than OR, and that one is a trap big enough to have its own place on the official mistakes list. Lesson seven takes it apart.

AmiBroker evaluates the innermost parentheses first, always. There is no performance penalty for redundant ones, and there is no prize for remembering rank 8 versus rank 9. The house rule for this course is simple: if an expression mixes two kinds of operator, parenthesise the grouping you mean, even when the default happens to be right. A reader should not have to consult a precedence table to know what your rule says.

A name holds whatever was last assigned to it, and reassignment is normal and useful:

Fragment — not a complete formula

Signal = Close > MA( Close, 200 );
Signal = Signal AND Volume > MA( Volume, 50 ); // narrow the same idea

But two forms of reassignment deserve care.

Reassigning a built-in price array. Open, High, Low, Close, Volume, OpenInt and Avg are ordinary variables. Assigning to one changes what every later line sees, including the inside of built-in functions:

Fragment — not a complete formula

Close = MA( Close, 5 ); // legal, and rarely what anyone meant
Average = MA( Close, 50 ); // now a 50-bar average of a 5-bar average

There are legitimate uses — the obsolete graphNstyle documentation notes that to draw something other than the close price you have to assign new values to the predefined arrays — but it should always be deliberate and commented. The same applies, on a much larger scale, to TimeFrameSet(), which replaces all of them at once with time-compressed bars until you call TimeFrameRestore(). Part 14 covers that.

Reassigning inside a block. Because braces do not create scope in AFL, a variable first assigned inside an if block still exists afterwards. What it holds depends on whether the block ran, which on a formula of any size is a genuinely hard question to answer by reading. Assign a default before the block and let the block overwrite it:

Fragment — not a complete formula

WarmUpBars = 0; // a definite starting value
if( BarCount > 200 )
{
WarmUpBars = 200;
}

Everything above collapses into three habits.

Name the intermediate steps. AFL costs you nothing for extra variables, and a named intermediate is a place to put a comment and a thing you can plot when the answer is wrong.

Fragment — not a complete formula

// Hard to read, hard to debug, and the precedence needs checking.
Signal = Close > MA( Close, 50 ) AND 100 * ATR( 20 ) / Close < 3;
// Same rule. Each step can be named, commented, plotted and checked.
Average = MA( Close, 50 );
AtrPercent = 100 * ATR( 20 ) / Close; // volatility as a percentage of price
AboveTrend = Close > Average;
CalmEnough = AtrPercent < 3;
Signal = AboveTrend AND CalmEnough;

The second version is five lines instead of one and is better in every way that matters. It also foreshadows the debugging method in lesson nine: when a signal is wrong, you plot the intermediates and find out which one lied.

Break long expressions across lines, aligning the operators so the structure is visible.

Put the settings at the top. Any number a reader might want to change belongs in a named variable at the head of the formula, not buried at the end of an expression twenty lines down.

A variable is a name holding whatever was last assigned; reading one that was never assigned is Error 29. Assignment is =, comparison is ==, and AmiBroker warns you when it suspects you meant the other one. Every number is floating point — there are no integers, %d does not work, and exact equality between computed values is unreliable. & and | are bit-wise flag operators, not logic. Precedence follows a published table in which * beats +, ^ beats unary minus, and NOT binds looser than every comparison; parentheses cost nothing and remove the question. And the built-in price arrays are assignable, so reassigning one silently rewrites the rest of the formula.

The next lesson is the one this part exists for.

Check your understanding

Question 1. What does MidPrice hold after this line?
MidPrice = High + Low / 2;
Show the answer and why

Answer: The High plus half the Low

Division binds tighter than addition, so this is High + ( Low / 2 ). The guide uses this exact expression as its precedence example. The midpoint needs ( High + Low ) / 2 — and this is why the house rule is to parenthesise any expression that mixes operator kinds.

Question 2. Why will printf( "Bars: %d", BarCount ); not work?
Show the answer and why

Answer: There are no integers in AFL, so %d and %x are not supported; use %g, %f or %e

Every AFL number is floating point. The printf documentation states the restriction explicitly. Since version 6.10 a mismatch between format specifiers and arguments also raises Error 61, so the failure is at least loud.

Question 3. A formula computes Ratio = ( High - Low ) / ( Close - Low ); on a symbol that had one untraded day where High, Low and Close were all identical. What happens on that bar?
Show the answer and why

Answer: The division produces an infinite value which then travels through every calculation that uses Ratio

Division by zero is not an error in AFL; it produces infinity, which propagates silently. SafeDivide( x, y, valueifzerodiv ) states the intended fallback in the line itself, and Nz() converts Null, NaN and Infinity to a value of your choosing. Untraded and suspended symbols make this a routine event, not an edge case.

Question 4. Which of these are true of AFL variables? Select all that apply.
Show the answer and why

Answer: A name must be assigned before it can be read, Assigning to Close changes what later built-in functions see, Assignment can be chained, as in a = b = 0;

Braces group statements but do not create scope in AFL — only a function definition does. That is why a variable assigned only inside a conditional block is a hazard: whether it holds anything sensible afterwards depends on whether the block ran.

Sources for this lesson

6 verified · checked 2026-08-31

  1. 01AmiBroker User's Guide — AFL Reference Manual§ Operators and precedenceamibroker.com/guide/a_language.html2026-08-31
  2. 02AmiBroker User's Guide — Common Coding Mistakesamibroker.com/guide/a_mistakes.html2026-08-31
  3. 03AFL Function Reference — printfamibroker.com/guide/afl/printf.html2026-08-31
  4. 04AFL Function Reference — SafeDivideamibroker.com/guide/afl/safedivide.html2026-08-31
  5. 05AFL Function Reference — Nzamibroker.com/guide/afl/nz.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.