So if the first three values of each array are using a different example to above:HighClose = IIfClose > Open, True, False; The array HighClose would then have a True value of one for ev
Trang 1Looping in AmiBroker AFL
Introduction
AmiBroker AFL uses arrays of numbers in very much the same way as it uses singlenumbers Arrays can be used in mathematical expressions, passed to functions, andreturned from functions, where the operations performed on the array are typicallyequivalent to the same operation performed on every individual number in the array.Much can be done without ever having to reference a particular number in an array.However, more complicated operations and systems may require access to the
individual numbers in arrays, perhaps because there's no built-in function that
performs the required task, or because it's more efficient to implement it that way.While AFL already provides a few functions that return individual values from arrays,like LastValue, BeginValue, and EndValue, for much more than that you need toaccess the elements yourself and use loops if you want to perform special functions onall the elements of an array
Arrays
Before you can really understand array indexing and looping, you need to understandwhat an array is and how it's organised Most programming languages support arrays,with similar accessing methods, but AFL arrays have some limitations
An AFL array can be thought of as a list of numbers, with one number in the list foreach bar on the current chart What the numbers mean depends on the array Forexample, in the Close array shown above, each number is the closing price for thecorresponding bar, while in an ATR array it is the value of the ATR function at thatbar The first number in the list (array) is for the first (oldest) bar on the chart, whilethe last number in the list is for the most-recent bar on the chart On days when the
Trang 2stock didn't trade, and thus there's no bar, there won't be a number in the list Thearray index is the position of each number in the list relative to the start, with the firstnumber being at index zero.
Where AFL arrays differ from generic arrays in other programming languages is thatAFL arrays always match the bars on the current chart, so the size of the array (ie thenumber of values in the array) is the same as the number of bars on the chart In otherprogramming languages it's usually possible to specify the array size yourself, andstore anything you like in the array
Array Indexing
As mentioned above, an array index is just the position of a number in the arrayrelative to the start, with the first number being at index zero Hence the last closingprice shown above of $9.35 is at array index eleven
The syntax for referencing a number in an array in AFL, and in most other
programming languages, is with the opening and closing square bracket symbols '['and ']' in the manner ArrayName[ArrayIndex] So with the closing price array above:
Close[0] has the value $8.91
Close[1] has the value $8.86
Close[6] has the value $9.06
Close[11] has the value $9.35
Note that the values shown above are individual numbers, not other arrays So while
in AFL you can write something like:
Avg = (High + Low) / 2;
and Avg will be an array covering all bars of the chart, you can also write somethinglike:
FirstAvg = (High[0] + Low[0]) / 2;
where FirstAvg is now a single number, not an array, as High[0] and Low[0] are alsosingle numbers
Using Arrays And Numbers In Expressions
As arrays can be used in AFL in almost the same manner as individual numbers, itcan sometimes lead to confusion, particularly where the two are mixed in the sameexpression For example, the statement:
Avg = (High + Low[0]) / 2;
is completely different to either of the two examples above, yet is still valid AFL.Now, however, each value in Avg (ie the value at each bar) will be the average of theHigh value at the same bar and the first value of Low at index zero
Trang 3So if the first three values of each array are (using a different example to above):
HighClose = IIf(Close > Open, True, False);
The array HighClose would then have a True (value of one) for every bar where theclose was higher than the open, and a False (value of zero) for every bar where theclose was lower than or equal to the open Note though that this statement could bemore simply written as just:
HighClose = Close > Open;
since the result of any comparison is always True (one) or False (zero), ignoring nullvalues for now Likewise for individual numbers in the arrays:
HighClose[10] = Close[10] > Open[10];
Trang 4However, if the resulting array is assigned any value other than True or False, then theIIf statement is required:
HighOpenClose = IIf(Close > Open, Close, Open);
In this example, each value in HighOpenClose is the higher of the open or closingprices at each bar This is equivalent to writing for each bar:
Naturally this could be written more readily using a loop, but we'll get to that
Null Values In Arrays
The null value is used to indicate that no data exists for a bar in an array While thiswouldn't normally occur in price arrays like Open and Close, it does in things likemoving average arrays where no value is defined for the first "period" bars So if afive day exponential moving average is obtained:
e5 = EMA(Close, 5);
then the first five numbers in array e5 are all null (the number -1e10)
Trang 5When plotted on a chart, no data will be displayed for these bars To simplify arraymathematics, any expression involving a null value will give a null result, so:
if (e5[4] == Null) // This won't work!
As e5[4] == Null is Null, this is the same as if (Null) which is never true (despite thefact that the null value -1e10 is non-zero) To test for Null, the IsNull() function can
be used:
if (IsNull(e5[4]))
Note that IsNull() will return an array if passed an array, in which case IIf would berequired rather than If:
e5x = IIf(IsNull(e5), Close, e5);
In this example, numbers in e5x have the same value as in e5 provided they are notnull, otherwise they have the same value as in Close
And as indicated earlier, if the assigned values are just True and False, then IIf is notrequired:
as a standard function already built into AmiBroker, then it's easier to use the built-infunction For example, to take the square root of all numbers in an array, the built-inSQRT function can just be used on the array:
array2 = SQRT(array1);
However, if the operations are not the same as a built-in function, or different
operations are required on different numbers in the array, then looping may be
required In some cases it may be possible to achieve the same result using multiplebuilt-in array functions, but looping may be more efficient
Trang 6There are three constructs available for looping:
For clarity of reading, it is standard practice to indent code inside a loop or an Ifstatement (typically four spaces or one tab - although I personally prefer spaces tohard tabs) This makes it easier to ensure each opening brace has a matching closingbrace, something that is critical to avoid language runtime errors (or compiler errors
in C and C++) For placement of braces, one favoured method is as shown above,which I will continue to use in this document Another, which I actually prefer andnormally use myself, is:
x=3;
is the same as:
x = 3;
Trang 7which is the same as:
x
=
3
;
Before starting to look at the three loop constructs, one final thing needs to be
mentioned in relation to array indexing In all the earlier examples, the array indiceswere constant values: Close[0], Open[10], and e5[6] This is not generally very usefulwith looping, as the loop typically wants to access all the values in an array and therecould be thousands of them With loops, the array index is more commonly a variablethat changes with each pass of the loop (don't worry about the while construct yet):
i = 0; // 'i' is an index variable
while (i < BarCount) // 'i' used in loop termination condition{
Avg[i] = (High[i] + Low[i]) / 2; // 'i' used to index into arrays in loop i++; // 'i' incremented at end of each loop}
In this example, 'i' is the loop index variable The reason 'i' is typically used as thefirst loop variable comes from the days of Fortran, where variables starting with 'i' and
a number of letters after that were integer values (ie whole numbers), while variablesstarting with other letters were floating point (fractional) values Integers are best forloop index variables, as floating point values are subject to rounding errors which canprevent loop termination conditions from being met Despite that, all numbers in AFLare floating point, so care needs to be taken with mathematical operations on loopindex variables to ensure rounding errors don't prevent loop termination Simpleoperations like addition and subtraction rarely cause rounding errors, but other morecomplex operations (like division) can
The variable BarCount is a built-in variable in AmiBroker equal to the total number ofbars in the current chart
The construct i++ is called post-incrementing and is detailed in the AmiBroker help It
is essentially shorthand for i = i + 1 While incrementing the loop index by one is byfar the most common scenario, any integer operation on the index could be used, forexample, i = i + 30
For Loops
For loops are probably the most commonly-used of all loop constructs Three
expressions involving the loop index variable must be specified:
• The initial value
• The termination value as a continuation statement
• The change after each pass of the loop
Trang 8A common for loop in AFL looks like this:
for (i = 0; i < BarCount; i++)
{
}
where the statements inside "for ( ; ; )" are:
i = 0 Initialises 'i' to zero at the start of the loop
i < BarCount Loop continues while 'i' is less than BarCount
i++ Increments 'i' by one after each loop pass
So to run a loop from array index ten to the last bar (BarCount-1) covering all bars inbetween, with the average of the high and low being calculated, the statements wouldbe:
Avg = Null; // Fill result array with null valuesfor (i = 10; i < BarCount; i++) // Run loop from 10 to BarCount-1 Avg[i] = (High[i] + Low[i]) / 2; // Calculate average at each bar
Note that braces are not required here because there is only one line of code inside thefor loop, but the line is still indented for clarity Also note that the first initialisationstatement Avg = Null sets all array values to null, as Avg is an array.
Another for loop example that performs a similar operation to the ExRem function onthe Buy and Sell arrays:
OpenPos = False; // No open position to start with
for (i = 0; i < BarCount; i++) // Loop over all bars in the chart
{
if (OpenPos) // If have an open position
{
Buy[i] = False; // Remove any surplus buy signals
if (Sell[i]) // If have sell signal on this bar
OpenPos = False; // No longer have open position
}
else // Else if don't have open position
{
Sell[i] = False; // Remove any surplus sell signals
if (Buy[i]) // If have a buy signal on this bar
OpenPos = True; // Now have an open position
}
}
In this example, the variable OpenPos is known as a state variable, meaning it
maintains the state of whether we currently have an open position in the stock or not
It is a single number, not an array, and in this example only ever holds the values True(one) or False (zero) While we do have an open position, we aren't interested in anymore buy signals Similarly, while we don't have an open position, we're not
interested in any sell signals
Trang 9The statement if (OpenPos), where it's not compared to anything, just means if
OpenPos is non-zero The If function only ever tests for a zero or non-zero
expression, where all comparison operations like i > 3 and i < BarCount are non-zero(one) if they are true and zero if they are false So the statement if (OpenPos) isequivalent to if (OpenPos == True), although it would also be true if OpenPos wasany other non-zero value (ie not just one) For example:
e1 = EMA(Close, 30);
e2 = EMA(Close, 60);
ediff = e1 - e2; // Difference between e1 and e2
for (i = 0; i < BarCount; i++)
i=0; // Initialise loop index
while (i < BarCount) // Loop continuation condition
{
i++; // Increment loop index
}
In fact, any for loop can be rewritten this way, putting the initial condition as a
separate statement before the while loop, including the continuation condition in thewhile statement, and putting the index change function as the last operation before theend of the loop (although use of the Break and Continue control words can upset thiscomparison) So rewriting our simple averaging function as a while loop would give:
Avg = Null; // Fill result array with null values
i = 10; // Initialise loop index to 10
while (i < BarCount) // Loop until index reaches BarCount{
Avg[i] = (High[i] + Low[i]) / 2; // Calculate average at each bar
i++; // Increment loop index
}
Trang 10Note that as we now have two lines of code in the while loop, the braces becomenecessary.
And rewriting our ExRem equivalent using a while loop:
OpenPos = False; // No open position to start with
i = 0; // Initialise loop index
while (i < BarCount) // Loop over all bars in the chart
{
if (OpenPos) // If have an open position
{
Buy[i] = False; // Remove any surplus buy signals
if (Sell[i]) // If have sell signal on this bar
OpenPos = False; // No longer have open position
}
else // Else if don't have open position {
Sell[i] = False; // Remove any surplus sell signals
if (Buy[i]) // If have a buy signal on this bar OpenPos = True; // Now have an open position
Our common for loop now as a do loop:
i=0; // Initialise loop index
do // Start loop - no condition specified yet{
i++; // Increment loop index
}
while (i < BarCount); // Test continuation condition
The only difference to a while loop is that the initial while statement has been
replaced with the word "do" and moved to the end of the loop instead Since thecontinuation condition is not tested until the end of the loop, the loop will alwaysexecute at least once, even if the continuation condition is false at the start
Trang 11So in the following situations, if Close[0] equals $10.00:
Other than the difference outlined above, do loops are identical to while loops Forcompletion though, our ExRem equivalent rewritten with a do loop:
OpenPos = False; // No open position to start with
i = 0; // Initialise loop index
{
if (OpenPos) // If have an open position
{
Buy[i] = False; // Remove any surplus buy signals
if (Sell[i]) // If have sell signal on this bar
OpenPos = False; // No longer have open position
}
else // Else if don't have open position {
Sell[i] = False; // Remove any surplus sell signals
if (Buy[i]) // If have a buy signal on this bar
OpenPos = True; // Now have an open position
Trang 12More Complex Loop Control Expressions
The examples given above use common but simple loop control expressions It ispossible however to have more complicated expressions with multiple statements foreach expression Initialisation and loop index change expressions can have multipleindependant statements separated by commas, while continuation statements caninclude the && (AND) and || (OR) operators to combine multiple conditions:
for (i = 0, j = 10; i < BarCount && j < 1000; i++, j = j + 10)
This example initialises 'i' to zero and 'j' to 10, continues while 'i' is less than
BarCount AND 'j' is less than 1000 (either becoming false will terminate the loop),and at the end of each loop pass, increments 'i' by one and 'j' by 10 While this
provides quite a lot of flexibility with loop control, care should be taken not to make it
so complicated that it becomes unreadable Remember the KISS principle
Switch And Case
Starting from AmiBroker version 4.91.0 Beta, AFL supports what is generally known
as a case statement It also supports the Break and Continue key words, which arecommonly used with loops but break is also used with case statements
A case statement is a more elegant way of implementing a complex If statement of thetype: if (A) then do something, else if (B) then do something different, else if (C) then
do something different again, etc In a case statement, the variable is tested in a
Switch statement, and then code for each desired value of the variable is placed under
a series of Case statements, with an optional default case for all unlisted values of thevariable It takes the form: