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)
AFL is a special programming language used to define and create custom indicators, scans, explorations, back-tests and guru commentaries.
This chapter describes the different categories of word-like units (tokens) recognized by the AFL language interpreter.
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 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.
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)
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 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)
);
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);
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.
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 |
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.
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.
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;
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.
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 */
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).
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 colorBlack = 16 colorDarkRed = 24 colorRed = 32 colorPink = 40 colorRose = 48 |
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) Not all flag combinations make sense, for example (64+1) (candlestick
+ line) will result in candlestick chart (style=64) |
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 :
|
Automatic Analysis |
columnN |
Exploration only: defines Nth column value. Example:
|
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:
will give closing prices displayed with 4 decimal digits. |
Automatic Analysis |
columnNname |
Exploration only: allows you to define the header name. Assigning
will change the name of the first custom column from the default "Column 0" to more appropriate "Close". |
Automatic Analysis |
FLOW CONTROL AND LOOPING
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:
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.
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:
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.
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:
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( 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.
Please read also Understanding how AFL works article to learn more.
"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.