AFL Reference Manual

Revision 2.50

Document history:

(Revision 2.4 - December 11th, 2003 - added AFL 2.5 new features)
(Revision 2.4 - July 21st, 2003 - added AFL 2.4 new features)
(Revision 2.4 - July 21st, 2003 - added AFL 2.4 new features)
(Revision 2.3 - March 8th, 2003 - added AFL 2.3 functions)
(Revision 2.2 - October 18th, 2002 - added AFL 2.2 functions)
(Revision 2.1 - August 18th, 2002 - added AFL 2.1 functions)
(Revision 2.0 - April 24th, 2002 - added AFL 2.0 functions)
(Revision 1.8 - November 11th, 2001 - added AFL 1.8 functions)
(Revision 1.7 - August 31th, 2001 - added AFL 1.7 functions)
(Revision 1.65 - August 6th, 2001 - added AFL 1.65 functions)
(Revision 1.6 - June 2nd, 2001 - added AFL 1.6 functions)
(Revision 1.5 - February 24th, 2001 - added AFL 1.5 functions)
(Revision 1.4 - January 4th, 2001 - added AFL 1.4 functions)
(Revision 1.3 - October 30th, 2000 - added AFL 1.3 functions)
(Revision 1.2 - June 23rd, 2000 - added AFL 1.2 functions)
(Revision 1.1A - January 17th, 2000 - reedited )
(Revision 1.1 - February 28th, 1999 - added AFL 1.1 functions)
(Revision 1.0 Jan, 1999 - created)

Introduction

AFL is a special programming language used to define and create custom indicators, scans, explorations, back-tests and guru commentaries.

Basics

Lexical elements

This chapter describes the different categories of word-like units (tokens) recognized by the AFL language interpreter.

Whitespace

Whitespace is the collective name given to spaces (blanks), tabs, new line characters and comments. Whitespace can serve to indicate where tokens start and end, but beyond this function, any surplus whitespace is discarded.

Comments

Comments are pieces of text used to annotate a program. Comments are for the programmer's use only; they are stripped from the source code before parsing. The are two ways to delineate comments: C-like comments and C++ like comments. A C-like comment is any sequence of characters placed after the symbol pair /*. The comment terminates at the first occurrence of the pair */ following the initial /*. The entire sequence, including the four comment-delimiter symbols, is replaced by one space. A C++ like comments are single-line comments that start by using two adjacent slashes (//) in any position within the line and extend until the next new line.

AFL does not allow nested comments.

Tokens

AFL recognizes five classes of tokens:

Identifiers are arbitrary names of any length given to functions and variables. Identifiers can contain the letters (a-z, A-Z), the underscore character ("_"), and the digits (0-9). The first character must be a letter.
AFL identifiers are NOT case sensitive.

Constants are tokens representing fixed numeric or character values. Numeric constants consist of decimal integer and optionally: decimal point and decimal fraction part. Negative numeric constants have unary minus (-) prefixed.
String constants, also known as string literals, form a special category of constants used to handle fixed sequences of characters and are written as a sequence of any number of characters surrounded by double quotes:

"This is literally a string"

The null (empty) string is written "". The characters inside the double quotes can include escape sequences ("\n" - a new line escape sequence).

A Constant expression is an expression that always evaluates to a constant. They are evaluated just as regular expressions are.

Punctuator (also known as separator) in AFL is one of the following characters:
( ) , ; = .

Parentheses (open ( and close ) ) group expressions, isolate conditional expressions and indicate function calls and function parameters:
d = c * ( a + b ) /* override normal precedence */
a= (b AND c) OR (d AND e) /* conditional expression */
func() /* function call no arguments */

The comma (,) separates the elements of a function argument list

The semicolon (;) is a statement terminator. Any legal AFL expression followed by a semicolon is interpreted as a statement, known as expression statement. The expression is evaluated and its value
is discarded (except Guru Commentaries where string values are written to output window)

The dot (.) is a member access operator. It is used to call COM object methods. If myobj variable holds the object, using dot operator we can call the methods (functions) of myobj object:

myobj.Method();

The equal sign (=) separates variable declarations from initialization lists:
x = 5;
It also indicates the default value for a parameter (see built-in function description):
macd( fast = 12; slow = 26 ) /* default values for fast and slow arguments)

Language structure

Each formula in AFL contains of one or more expression statements. Each statement MUST be terminated by semicolon (;). In this way you are able to break long expressions into several physical lines (in order to gain clarity) and AmiBroker will still treat it like a single statement until terminating semicolon. Examples:

x = ( y + 3 );          /* x is assigned the value of y + 3  */

x = y =
0;              /* Both x and y are initialized to 0 */
proc( arg1, arg2 );     
/* Function call, return value discarded */
y = z = ( f( x ) +
3 ); /* A function-call expression  */

my_indicator =    
IIf( MACD() > 0,                Close - MA(Close,9),                MA( Close, 9 ) - Close );                 /* one statement in several lines */
Identifiers

Identifiers in AFL are used to identify variables and functions.
There are some predefined identifiers referencing built-in arrays and functions.
The most important are price array identifiers. They identify specific price fields that the formula should operate on. The valid price array identifiers are open, high, low, close, volume, openint, average. Price array identifiers can be abbreviated as shown in the following table. Note that these are not case-specific.

Long name Abbreviation Comment
Open O  
High H  
Low L  
Close C  
Volume V  
OpenInt OI  
Avg <none available> (High+Low+Close)/3 - so called "typical price"

Examples of the use of price array identifiers in formulas are shown below.

MA( Close, 10 ); IIf( H > Ref(H,-1), MA(H,20), MA(C,20) );

Operators

Formulas can contain the following mathematical operators:

Symbol Meaning
+ Addition
- Subtraction (or negative value)
* Multiplication
/ Division
% Modulus (or remainder) (AFL 1.7+)
^ Exponentiation (raising to a power)
| Bit-wise "Or" (AFL 2.1+)
& Bit-wise "And" (AFL 2.1+)

The following formulas illustrate the use of operators in a formula:

var1 = ( H + L ) / 2; 

var2 =
MA(C,10)-MA(C,20) / (H + L + C);
var3 =
Close + ((1.02 * High)-High);    
Logical operators

If a formula requires multiple conditions, you can combine the conditions with AND and OR operators. For example, maybe you'd like to plot a +1 when the MACD is greater than zero and the RSI is greater than 70:

Condition = MACD() > 0 AND RSI(14) > 70; 

You can add as many conditions within a formula as you like.

Operator precedence and the parentheses

AFL supports parentheses in formulas.

Parentheses can be used to control the operation precedence (the order in which the operators are calculated). AmiBroker always does operations within the innermost parentheses first. When parentheses are not used, the precedence is as follows (higher precedence listed first):

No Symbol Meaning
1 ++ Post-increment/pre-increment
(i++ works like i = i + 1)
2 --

Post-decrement/pre-decrement
(i-- works like i = i - 1 )

3 [ ] Array element (subscript) operator
4 ^ Exponentiation
5 - Negation - Unary minus
6 * Multiplication
7 / Division
8 % Reminder (Modulo operator)
9 + Addition
10 - Subtraction
11 < Less than
12 > Greater than
13 <= Less than or equal to
14 >= Greater than or equal to
15 == Equal to
16 != Not equal to
17 & Bit-wise "And" (AFL 2.1+)
18 | Bit-wise "Or" (AFL 2.1+)
19 NOT Logical "Not"
20 AND Logical "And"
21 OR Logical "Or"
22 = Variable assignment operator

The expression

H + L / 2; 

(without parenthesis) would be calculated by AmiBroker as "L / 2" plus "H", since division has a higher precedence. This would result in a much different value than

(H + L)/2; 

A few words about increment/decrement operators. There are two kinds of them: postfix and prefix.

The unary operators (++ and --) are called “prefix” increment or decrement operators when the increment or decrement operators appear before the operand. Postfix increment and decrement has higher precedence than prefix increment and decrement operators. When the operator appears before its operand, the operand is incremented or decremented and its new value is the result of the expression.

i = 5;

j = ++i;
// i will be incremented first and result (number 6) will be assigned to j.

The result of the postfix increment or decrement operation is the value of the postfix-expression before the increment or decrement operator is applied. The type of the result is the same as that of the postfix-expression but is no longer an l-value. After the result is obtained, the value of the operand is incremented (or decremented).

i = 5;

j = i++;
// j will be assigned the value of 5 (before incrementation) and then i will be incremented to 6.

Accessing array elements: [ ] - subscript operator

An array identifier followed by an expression in square brackets ([ ]) is a subscripted representation of an element of an array object.

arrayidentifier [ expression ]

It represents the value of expression-th element of array.

BarCount constant gives the number of bars in array (such as Close, High, Low, Open, Volume, etc). Array elements are numbered from 0 (zero) to BarCount-1.

To get the first bar you can use array[ 0 ], to get the last bar of array you can use array[ BarCount - 1 ];

For example:

Close[ 5 ];
Represents the sixth element (bar) of the close array.

Close[ 0 ];
Represents the very first available bar of the close array.

High[ BarCount - 1 ];
Represents the last bar of High array.

Compound statements (Blocks)

A compound statement consists of zero or more statements enclosed in curly braces ({ }). A compound statement can be used anywhere a statement is expected. Compound statements are commonly called “blocks.”

{

statement1;

....

statementN;

}

(this is 'borrowed' from C language, users of other programming languages are used to use BEGIN for { and END for } )

if( Amount > 100 )
{
   
_TRACE("Amount above 100");
    Balance += Amount;
}
else
    Balance -= Amount;

Built-in Functions

In addition to mathematical operators, AmiBroker contains over 70 built-in functions that perform mathematical operations.

The following formula consists of a single function that gives the square roots of the closing prices:

sqrt( Close ); 

The following formula consists of a single function that gives a 14-period RSI indicator:

Graph0 = RSI(14);

The following formula consists of two functions. The result is the difference between the MACD indicator and a 9-period exponential moving average of the MACD:

Graph0 = MACD() - EMA(MACD(),9); 

All function calls must consist of function identifier (name) followed by a pair of parentheses.

As has been eluded to in earlier examples, a function can be "nested" within a function. The nested function can serve as the main function's data array parameter. The following examples show functions nested within functions:

MA( RSI(15), 10 ); 
MA( EMA( RSI(15), 20), 10 ); 

The first example calculates a 10-period simple moving average of a 15-period Relative Strength Index (RSI). The second example calculates a 20-period exponential moving average of a 15-period RSI, and then calculates a 10-period simple moving average of this moving average.

Conditional function IIF()

The iif() function is used to create conditional assignments. It contains three parameters as shown in the following example.

dynamicrsi = IIf( Close > MA(C,10), RSI(9), RSI(14) ); 

The above "iif" statement reads (in English) as follows: If today's close is greater than today's 10-day simple moving average of the close, then assign a 9-day RSI to the dynamicrsi variable, otherwise, assign a 14-day RSI. The next formula assigns “positive volume” to volresult variable if the close is greater than the median price. Otherwise, "negative volume" is assigned.

volresult = IIf( Close > (High+Low)/2, Volume, -Volume ); 

If you simply want an expression to be evaluated as either true or false, it can be done without the use of the iif() function. The following formula will result in either a 1 (true) or a 0 (false):

result = RSI(14) > 70; 

The same done with iif() gives the same results, but the formula is longer.

result = IIf(RSI(14) > 70, 1, 0 ); 

Please note that IIF is a function - so the result of evaluation is returned by that function and should be assigned to some variable.

IIf always evaluates both TRUE_PART and FALSE_PART, even though it returns only one of them. Because of this, you should watch for undesirable side effects. IIF function is NOT a flow-control statement. If you need flow control (conditional execution of some code parts) you should look for if-else conditional statement described later in this document.

The following example shows one common error made with IIF function:

IIf( condition, result = 7, result = 9 ); // THIS IS WRONG

Correct usage is:

result = IIf( condition, 7, 9 );

/* 7 or 9 is *returned* and assigned to result variable depending on condition */

 

Variables

In order to shorten, simplify, enhance, and make the maintenance of complex formulas easier, you may want to use variables. In fact using variables you can significantly improve formula calculation speed. So it is strongly recommended to use variables and there is no limit on number of variables you can define.

A variable is an identifier that is assigned to an expression or a constant. The number of variables used in a formula is not limited. Variables must be assigned before the variable is used in the formula. Variables cannot be assigned within a function call.

User-defined variable names (identifiers) cannot duplicate names already used by functions (e.g., ma, rsi, cci, iif, etc.) or predefined array identifiers (e.g., open, high, low, close, simple, o, c, l, h, s, a).

Reserved variables

AmiBroker uses some reserved variable names in its formulas, for example in Auto-Analysis window you have to assign values to 2 variables named 'buy' or 'sell' to specify the conditions where "buy" and "sell" conditions occur. For example (system that buys when MACD rises above 0 line, and sells when MACD falls below 0 line)

Buy  = Cross( MACD(), 0 );
Sell = Cross( 0, MACD() ); 

AmiBroker uses the following reserved variable names:

Variable Usage Applies to
buy defines "buy" (enter long position) trading rule Automatic Analysis, Commentary
sell

defines "sell" (close long position) trading rule

Automatic Analysis, Commentary
short defines "short" (enter short position - short sell) trading rule Automatic Analysis
cover defines "cover" (close short position - buy to cover) trading rule Automatic Analysis
buyprice defines buying price array (this array is filled in with the default values according to the Automatic Analyser settings) Automatic Analysis
sellprice defines selling price array (this array is filled in with the default values according to the Automatic Analyser settings) Automatic Analysis
shortprice defines short selling price array (this array is filled in with the default values according to the Automatic Analyser settings) Automatic Analysis
coverprice defines buy to cover price array (this array is filled in with the default values according to the Automatic Analyser settings) Automatic Analysis
title defines title text (overrides any graphNname) Indicator Builder
maxgraph specifies maximum number of graphs to be drawn in custom indicator window (default=3) Indicator Builder
graphN defines the formula for the graph number N (where N is a number 0,1,2,..., maxgraph-1) Indicator Builder
graphNname defines the name of Nth graph line. This will appear in the title of the chart pane Indicator Builder
graphNcolor

defines the color index of Nth graph line (color indexes are related to the current palette - see Preferences/Color)

colorCustom1 = 0
colorCustom2 = 1
colorCustom3 = 2
colorCustom4 = 3
colorCustom5 = 4
colorCustom6 = 5
colorCustom7 = 6
colorCustom8 = 7
colorCustom9 = 8
colorCustom10 = 9
colorCustom11 = 10
colorCustom12 = 11
colorCustom13 = 12
colorCustom14 = 13
colorCustom15 = 14
colorCustom16 = 15

colorBlack = 16
colorBrown = 17
colorDarkOliveGreen = 18
colorDarkGreen = 19
colorDarkTeal = 20
colorDarkBlue = 21
colorIndigo = 22
colorDarkGrey = 23

colorDarkRed = 24
colorOrange = 25
colorDarkYellow = 26
colorGreen = 27
colorTeal = 28
colorBlue = 29
colorBlueGrey = 30
colorGrey40 = 31

colorRed = 32
colorLightOrange = 33
colorLime = 34
colorSeaGreen = 35
colorAqua = 35
colorLightBlue = 37
colorViolet = 38
colorGrey50 = 39

colorPink = 40
colorGold = 41
colorYellow = 42
colorBrightGreen = 43
colorTurquoise = 44
colorSkyblue = 45
colorPlum = 46
colorLightGrey = 47

colorRose = 48
colorTan = 49
colorLightYellow = 50
colorPaleGreen = 51
colorPaleTurquoise = 52
colorPaleBlue = 53
colorLavender = 54
colorWhite = 55

Indicator Builder
graphNbarcolor defines the array that holds palette indexes for each bar drawn Indicator Builder
graphNstyle

defines the style of Nth graph. Style is defined as a combination (sum) of one or more following flags:

styleLine = 1 - normal (line) chart (default)
styleHistogram = 2 - histogram chart
styleThick =4 - fat (thick)
styleDots = 8 - include dots
styleNoLine = 16 - no line
styleLog = 32 - semi-logarithmic scale
styleCandle = 64 - candlestick chart
styleBar = 128 - traditional bar chart
styleNoDraw = 256 - no draw (perform axis scaling only)
styleStaircase = 512 - staircase (square) chart
styleSwingDots = 1024 - middle dots for staircase chart
styleNoRescale = 2048 - no rescale
styleNoLabel = 4096 - no value label
stylePointAndFigure = 8192 - point and figure
(new in 4.20):
styleArea = 16384 - area chart (extra wide histogram)
styleOwnScale = 32768 - plot is using independent scaling
styleLeftAxisScale = 65536 - plot is using left axis scale (independent from right axis)

Not all flag combinations make sense, for example (64+1) (candlestick + line) will result in candlestick chart (style=64)

Note on candlestick/bar charts: these styles use indirectly O, H, L arrays in addition to graphN. So ordinary candlestick price chart formula is graph0=close; graph0style=64;.
But if you want to draw something else than close price you have to assign new values to predefined O,H,L arrays.

Indicator Builder
graphNbarcolor defines the array of color indexes for the bars and candlesticks in Nth graph ine (color indexes are related to the current palette - see Preferences/Color) Indicator Builder
graphxspace defines percentage extra space added at the top and the bottom of the chart Indicator Builder
graphzorder GraphZOrder variable allows to change the order of plotting indicator lines. When GraphZOrder is not defined or is zero (false) - old ordering (last to first) is used, when GraphZOrder is 1 (true) - reverse ordering is applied. Indicator Builder
exclude If defined, a true (or 1) value of this variable excludes current symbol from scan/exploration/back test. They are also not considered in buy and hold calculations. Useful when you want to narrow your analysis to certain set of symbols. Automatic Analysis
roundlotsize defines round lot sizes used by backtester (see explanations below) Automatic Analysis (new in 4.10)
ticksize defines tick size used to align prices generated by built-in stops (see explanations below) (note: it does not affect entry/exit prices specified by buyprice/sellprice/shortprice/coverprice) Automatic Analysis (new in 4.10)
pointvalue allows to read and modify future contract point value (see backtesting futures) Automatic Analysis (new in 4.10)
margindeposit allows to read and modify future contract margin (see backtesting futures) Automatic Analysis (new in 4.10)
positionsize

Allows control dollar amount or percentage of portfolio that is invested into the trade (more information available in the "Tutorial: Backtesting your trading ideas")

Automatic Analysis (new in 3.9)
positionscore Defines the score of the position. More details: "Tutorial: Portfolio Backtesting") Automatic analysis
numcolumns Exploration only: defines the number of your own columns (excluding predefined ticker and date columns) and assign the column value to the variable Automatic Analysis
filter Exploration only: controls which symbols/quotes are accepted. If "true" (or 1) is assigned to that variable for given symbol/quote it will be displayed in the report.

So, for example, the following formula will accept all symbols with closing prices greater than 50 :

filter = close > 50;

Automatic Analysis
columnN

Exploration only: defines Nth column value. Example:

column0 = Close;

Automatic Analysis
columnNformat

Exploration only: allows you to define the formatting applied to numbers. By default all variables are displayed with 2 decimal digits, but you can change this by assigning a different value to this variable: 1.5 gives 5 decimal digits, 1.0 gives no decimal digits. So, in our example, typing:

column0format = 1.4;

will give closing prices displayed with 4 decimal digits.
(Note for advanced users: the integer part of this number can be used to pad formatted number with spaces - 6.0 will give no decimal digits but a number space-padded upto 6 characters.)

Automatic Analysis
columnNname

Exploration only: allows you to define the header name. Assigning

column0name = "Close";

will change the name of the first custom column from the default "Column 0" to more appropriate "Close".

Automatic Analysis

FLOW CONTROL AND LOOPING

do-while Statement

The do-while statement lets you repeat a statement or compound statement until a specified expression becomes false.

Syntax

do statement while ( expression ) ;

The expression in a do-while statement is evaluated after the body of the loop is executed. Therefore, the body of the loop is always executed at least once.

The expression must have numeric or boolean type. Execution proceeds as follows:

  1. The statement body is executed.

  2. Next, expression is evaluated. If expression is false, the do-while statement terminates and control passes to the next statement in the program. If expression is true (nonzero), the process is repeated, beginning with step 1.

This is an example of the do-while statement:

x=100;
do 
{
    y = sin( x );
    x--;
} while ( x > 0 );

In this do-while statement, the two statements y = sin( x ); and x--; are executed, regardless of the initial value of x. Then x > 0 is evaluated. If x is greater than 0, the statement body is executed again and x > 0 is reevaluated. The statement body is executed repeatedly as long as x remains greater than 0. Execution of the do-while statement terminates when x becomes 0 or negative. The body of the loop is executed at least once.


while Statement

The while statement lets you repeat a statement until a specified expression becomes false.

Syntax

while ( expression ) statement

The expression must have arithmetic (numeric/boolean) type. Execution proceeds as follows:

  1. The expression is evaluated.

  2. If expression is initially false, the body of the while statement is never executed, and control passes from the while statement to the next statement in the program.

    If expression is true (nonzero), the body of the statement is executed and the process is repeated beginning at step 1.

This is an example of the while statement:

i = 10;
while( i < 20 )
{
  Plot( MA( Close, i ), "MA" + WriteVal( i, 0 ), colorBlack + i );
  i = i + 1;
}

The example plots 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 - bar moving averages.

 

for Statement

The for statement lets you repeat a statement or compound statement a specified number of times. The body of a for statement is executed zero or more times until an optional condition becomes false. 

Syntax

for ( init-expression ; cond-expression ; loop-expression ) statement

Execution of a for statement proceeds as follows:

  1. The init-expression, is evaluated. This specifies the initialization for the loop. There is no restriction on the type of init-expression.

  2. The cond-expression,  is evaluated. This expression must have arithmetic type. It is evaluated before each iteration. Three results are possible:
    • If cond-expression is true (nonzero), statement is executed; then loop-expression, if any, is evaluated. The loop-expression is evaluated after each iteration. There is no restriction on its type. Side effects will execute in order. The process then begins again with the evaluation of cond-expression.

    • If cond-expression is false (0), execution of the for statement terminates and control passes to the next statement in the program.

This example illustrates the for statement:

myema[ 0 ] = Close[ 0 ];
for( i = 1; i < BarCount; i++ )
{
    myema[ i ] =
0.1 * Close[ i ] + 0.9 * myema[ i - 1 ];
}

This example iterates all bars of close array to calculate exponential moving average.

For loop is extremely flexible.

loop-expression can be ANY kind of expression you wish. You can produce not only regular series like this:

for( i = 0; i < BarCount; i = i + 3 ) // increment by 3 every iteration

but you can produce exponential series too:

for( i = 1; i < BarCount; i = i * 2 ) // produces series of 1, 2, 4, 8, 16, 32, ...

if, else Statements

if( expression )
statement1
[else
statement2]

The if keyword executes statement1 if expression is true (nonzero); if else is present and expression is false (zero), it executes statement2. After executing statement1 or statement2, control passes to the next statement.

Example 1

if ( i > 0 )
    y = x / i;
else 
{
    x = i;
    y = abs( x );
}

In this example, the statement y = x/i; is executed if i is greater than 0. If i is less than or equal to 0, i is assigned to x and abs( x ) is assigned to y. Note that the statement forming the if clause ends with a semicolon.

When nesting if statements and else clauses, use braces to group the statements and clauses into compound statements that clarify your intent. If no braces are present, the compiler resolves ambiguities by associating each else with the closest if that lacks an else.

Example 2

if ( i > 0 )           /* Without braces */
    if ( j > i )
        x = j;
    else
        x = i;

The else clause is associated with the inner if statement in this example. If i is less than or equal to 0, no value is assigned to x.

Example 3

if ( i > 0 ) 
{             /* With braces */
    if ( j > i )
        x = j;
}
else
    x = i;

The braces surrounding the inner if statement in this example make the else clause part of the outer if statement. If i is less than or equal to 0, i is assigned to x.

USER-DEFINABLE FUNCTIONS, PROCEDURES, LOCAL/GLOBAL VARIABLES

Here is a sample code showing user-defined function:

// the following function is 2nd order smoother

function IIR2( input, f0, f1, f2 )
{
    result[
0 ] = input[ 0 ];
    result[
1 ] = input[ 1 ];

   
for( i = 2; i < BarCount; i++ )
    {
       result[ i ] = f0 * input[ i ] +
                     f1 * result[ i -
1 ] +
                     f2 * result[ i -
2 ];
    }

   
return result;
}

Plot( Close, "Price", colorBlack, styleCandle );
Plot( IIR2( Close, 0.2, 1.4, -0.6 ), "function example", colorRed );


In this code IIR2 is a user-defined function. input, f0, f1, f2 are formal parameters of the functions.
At the time of function call the values of arguments are passed in these variables. Formal parameters behave like local variables.
Later we have result and i which are local variables. Local variables are visible inside function only. If any other function uses the same variable name they won't interfere between each other.

Due to the fact that AFL does not require to declare variables the decision whenever given variable is treated as local or global is taken depends on where it is FIRST USED.

If given identifier appears first INSIDE function definition - then it is treated as LOCAL variable.
If given identifier appears first OUTSIDE function definition - then it is treated as GLOBAL variable.

This default behaviour can be however overriden using global and local keywords (introduced in 4.36) - see example 2.

Example (commentary):

k = 4; // this is GLOBAL variable

function f( x )
{
   z =
3; // this is LOCAL variable
   return z * x * k; // 'k' here references global variable k (first used above outside function)
}

z =
5; // this is GLOBAL variable with the same name as local variable in function f

"The value of z before function call :" + WriteVal( z );

// Now even if we call function
// the value of our global variable z
// is not affected by function call because
// global variable z and local variable z are separate and
// arguments are passed by value (not by reference)

"The result of f( z ) = " + WriteVal( f( z ) );

"The value of z after function call is unchanged : " + WriteVal( z );

Example 2: Using local and global keywords to override default visibility rules:

VariableA = 5; // implict global variable

function Test()
{
   local VariableA;  // explicit local variable with the same identifier as global
   global VariableB; // explicit global variable not defined earlier
                     // may be used to return more than one value from the function

   VariableA =
99;
   VariableB =
333;
}

VariableB =
1; // global variable

"Before function call";
"VariableA = " + VariableA;
"VariableB = " + VariableB;

Test();

"After function call";
"VariableA = " + VariableA + " (not affected by function call )";
"VariableB = " + VariableB + " (affected by the function call )"

At the end of the function we can see 'return' statement that is used to return the result to the caller. Note that currently return statement must be placed at the very end of the function.

It is also possible to write a procedure (a function that returns nothing (void))

procedure SinePlotter( Freq, ColorIndex )
{
   pname =
"Line"+WriteVal(ColorIndex,1.0);
   array =
sin( Cum( Freq * 0.01 ) );
   
Plot( array, pname , colorRed + ColorIndex, styleThick );
}

for( n = 1; n < 10; n++ )
{
    SinePlotter( n/
2+Cum(0.01), n );
}

Note that although there are two separate keywords 'function' and 'procedure' AmiBroker currently treats them the same (they both accept return values but not require them), but in the future the rules maight get enforced to use
return statement ONLY in conjunction with function keyword. So it is advised to use function keyword in case when your function returns any value and procedure keyword otherwise.

Note also that recursion (having a function call itself from within itself) is NOT supported as for now.

More information

Please read also Understanding how AFL works article to learn more.

Common misunderstandings

"New if-else problem"

Question:

Why I get the syntax error when I write: if( H > Ref(H,-1) )

Answer:

if-else statement changes flow of execution (opposite to IIF function that evaluates all arguments and works on arrays) and you can not really write

if ( H >Ref(H,-1) )

because it has no meaning. It would translate to " If high array is higher than high array shifted one bar" (see tutorial below). Flow control statement (such as if-else) has to get SINGLE boolean value to make decision which execution path should be taken. If you write H (or High) it means ARRAY (entire array).
if you write H[ i ] - it means i-th element of the array. The subscript operator [ ] allows you to access individual array elements.

Instead you should write:

for( i = 1; i < BarCount; i++ )
{
   
if ( High[ i ] > High[ i - 1 ] )
   {
       x[ i ] =
High[ i ];
   }
   
else
   {
       x[ i ] =
Low[ i ];
   }
}



this will translate to correct one "for EVERY BAR 'i' assign i-th element of high array to the i-th element of x array if i-th element of high array is higher than the previous element, otherwise assign i-th of low array to the i-th element of x array". The rule is: new if-else and while statements need single boolean value (not array) to decide which execution path should be taken. If you want to use them with arrays you have to iterate through bars using for loop (as shown above).

On the other hand this can be implemented in single line using old-style array operations and IIF function:

x = IIf( High > Ref( High, -1 ), High, Low );

This works because IIF operates on ARRAYS as described in the tutorial.

As you can see in many cases old-style AFL provides much more compact form. I always tried to explain this advantage of AFL but only a few realised that. New control statements should be used where it is better to use them. As I tried to explain during last years in 80% of cases 'old-style' AFL provides the shortest formula. Only remaining 20% of cases needed script. Those 'script-only' cases now can be coded in native AFL thanks to new for/while/if-else statements. And this is correct usage of them - to replace script parts.