Using IIF(), IF() and Switch() functions

Many newcomers to AFL are confused by the IF(), IIF() and Switch(). This post gives a few simple examples of their use. The IF() and Switch() are program flow control statements, the IIF() is a function that acts on all elements of an input array and returns an output array.

In all but the simplest applications the Switch() is the preferred method to the IF() to change program flow. It can be used to code complex decision trees and state machines, for example as these are often needed in automated trading systems.

For more detailed explanations click IF(), IIF(), or Switch(). A search of the afl library will also get you many more examples.

The IIF() function

It is possible to use if()s to individually test and modify each bar in an array for a condition. An example on how this would be done is shown in the function below (copied from the AmiBroker help). This function is an AFL equivalent for the IIF() function.

function IIF_AFLconditioninputAinputB )
{
    result Null;
    for( bar 0bar BarCountbar++ )
    {
       if( conditionbar ] )
           resultbar ] = inputAbar ];
       else
           resultbar ] = inputBbar ];
     }
    return result;
}

While the above approach works, using the IIF() function always provides a better and faster solution. The IIF() is very powerful and should be used whenever possible. Below are a few simple examples to get started. btw, It is highly unlikely that you will be able to improve on execution time by using a loop or writing a DLL.

To color all bars that fall on a Monday White:

Color IIfDayOfWeek()==1colorWhitecolorBlack);
PlotC"Close"colorstyleBar );

IIF()s Can be nested. This example colors Monday bars White, Wednesday bars Blue and Friday bars Yellow:

DayOfWeek();
Color IIf(D==1colorWhiteIIf(D==3colorBlueIIf(D==5colorYellowcolorBlack)));
PlotC"Close"colorstyleBar );

The IF() Statement

One of the most common applications for the if() is to select what you want to see on your chart:


PlotC"Close"colorBlackstyleBar );
ShowMA10 ParamToggle"Moving Average""MA|EMA");

if ( ShowMA10 )
{
PlotMAC10 ), "MA10"colorWhitestyleLine );
}
else
{
PlotEMAC10 ), "MA10"colorWhitestyleLine );
}

In the above example the IF() basically selects one of two sections of code.  To select one of many options you could the use the else-if extension:


SelectedIndicator ParamList"Show""MA10,MA50,MA100");

if ( SelectedIndicator == "MA10" )
{
PlotMAC10 ), "MA10"colorBluestyleLine );
}
else
if ( SelectedIndicator == "MA10" )
{
PlotEMAC50 ), "MA10"colorRedstyleLine );
}
else
if ( SelectedIndicator == "MA100" )
{
PlotEMAC100 ), "MA100"colorYellowstyleLine );
}

The Switch() Statement

When there are many conditions, the lengthy If() expressions can become confusing, difficult to compose, and difficult to modify. In such cases it is often better to use the Switch() statement. Using a simple Switch() the last example looks much cleaner:


SelectedIndicator ParamList"Show""MA10,MA50,MA100");
switch ( SelectedIndicator )
{

case "MA10":
PlotMAC10 ), "MA10"colorBluestyleLine );
break;

case "MA50":
PlotMAC50 ), "MA50"colorBluestyleLine );
break;

case "MA100":
PlotMAC100 ), "MA100"colorBluestyleLine );
break;
}

There are times that you will have many individually named variables that you would like to process in a Switch() statement. Even though the Switch() can only accept a single variable name as argument you can use the method below to work around this limitation:

function SayOnceText )
{
    if ( StaticVarGetText"Lastsay" ) != Text )
    {
        SayTextFalse );
        StaticVarSetText"LastSay"Text );
    }
}

RequestTimedRefresh(1);
Trigger1 ParamTrigger"Trigger 1""TRIGGER1" );
Trigger2 ParamTrigger"Trigger 2""TRIGGER2" );
Trigger3 ParamTrigger"Trigger 3""TRIGGER3" );
Trigger4 ParamTrigger"Trigger 4""TRIGGER4" );

switch( )
{
    case Trigger1:
    SayOnce"One" );
    break;

    case Trigger2:
    case Trigger4:
    SayOnce"2 or 4" );
    break;

    case Trigger3:
    SayOnce"Three" );
    break;

    default:
    SayOnce"Default" );
    // Here you can place code that will execute
    // repeatedly while no other case is true
}

The Switch() argument can be a string or number. Using strings makes code easier to read. Another advantage of using the Switch() is that they format nicely using Edit->Prettify Selection in you editor window, using too many else-if statements tends to run the if()s of the page. As shown above you can stack case statements to have multiple conditions trigger the same task.

To implement a simple State Machine you pass the system “state” to the Switch(). This way you can have any event trigger any sequence of tasks, and do so in any desired order. In a real application the SayOnce() functions in the example code below would be replaced by the task you want to be performed in the state. The next state would usually be conditionally set inside each state, for example you only want to proceed to the next state after an order is filled, or a price is crossed. You can use multilevel Switch()s or if()s inside each case section. This use of Switch() statements is very useful in Automated Trading systems. For example to process order status (Pending, Filled, Error, etc) and parsing TWS error messages.

Since states are saved in a Static Variables they remain valid over multiple AFL executions, and can last indefinitely. You can also save states in Persistent Variables.

States are processed in sequential afl executions, i.e., if you change the state in a case statement this next state will be processed in the next AFL refresh. In some applications this delay can cause problems. To ensure responsive code you might want to use a 0.1 second refresh rate. You could remove the delay by using the Switch() inside a loop/while statement, anf loop until a stable state is reached.

function SayOnceText )
{
    if ( StaticVarGetText"Lastsay" ) != Text )
    {
        SayTextFalse );
        StaticVarSetText"LastSay"Text );
    }
}

RequestTimedRefresh(1);
if( ParamTrigger"Reset System""RESET" ) ) StaticVarSetText"State""RESET" );
if( ParamTrigger"Task 1""TASK 1" ) ) StaticVarSetText"State""TASK1" );
if( ParamTrigger"Task 2""TASK 2" ) ) StaticVarSetText"State""TASK2" );
if( ParamTrigger"Task 3""TASK 3" ) ) StaticVarSetText"State""TASK3" );

State StaticVarGetText"State" );
switch( State )
{
    case "RESET":
    SayOnce"Reset" );
    StaticVarSetText"State""READY" );
    break;

    case "READY":
    SayOnce"Ready" );
    StaticVarSetText"State""IDLE" );
    break;

    case "TASK1":
    SayOnce"Task 1" );
    StaticVarSetText"State""IDLE" );
    break;

    case "TASK2":
    SayOnce"Task 2" );
    StaticVarSetText"State""TASK1" );
    break;

    case "TASK3":
    SayOnce"Task 3" );
    StaticVarSetText"State""TASK2" );
    break;

    case "IDLE":
    SayOnce"Idle" );
    break;
}

AFL Shapes Cheat Sheet

The plotshapes() can be used to plot shapes on your chart to indicate signals, stops, and other events and conditions. Figure 1 below gives a quick overview of the shapes that are available and includes a few undocumented ones. A PDF version suitable for printing is here: AFL Shapes Cheat Sheet

afl-shapes.png

Figure 1

Figure 2 shows the small AFL program that was used to explore all the built-in shapes and their numerical values.

for ( 0&ltBarCounti++ )
{
    O[i] = C[i] = i;
    H[i] = 5;
    L[i] = 5;

    if ( == )  {PlotTextNumToStri1.0False ), iL[i]-2colorDarkGrey );}
    else  {PlotTextNumToStri1.0False ), iH[i]+.5colorDarkGrey );}
}
PlotShapesCcolorRed0C, -10 );
PlotC""colorLightGreystyleBar );

Title "Hollow = " NumToStrshapeHollowCircle shapeCircle0) + "\n" + 
    "Small = " NumToStrshapeSmallCircle shapeCircle0);

aflshapesf2.png

Figure 2

With additions by Herman

Weekly High or Low Days

Selected Date Range

Abbreviated Identifiers v2

In AFL the identifiers Open, High, Low, Close, Volume, OpenInt and Avg are reserved for price field arrays. Of the small number of variables that are reserved the price identifiers are the only ones that can be abbreviated (OHLCVOI can be used instead of the longer form).

They are not case sensitive and when entered into a formula, in the Formula Editor, they will default to upper case and bold (as shown in the figure below).

image

This is very nice for speeding up formula writing but there is a ‘Catch 22’.

If abbreviated identifiers are used it makes the task of finding and replacing price arrays, using Formula Editor >> Edit >> Replace very tedious e.g. if the ‘writer’ wants to replace all “C’s” with a variable = = ParamField, for example, the Replace tool will pick up every “C” in the code and ask the user to confirm the replacement.

image

Checking Match whole word only in the Text Search Tool will change the criteria so that where “C” is part of a word it will be passed over while “C”, on it’s own, will be treated as a word and highlighted in the search report .

image

Note: The font format for reserved variables can be customized in Tools >> Preferences.

image

A handy tip for searching, with the Text Search Tool, is to position the cursor at the top of the code so that the search will begin from there. If the cursor is lower down the code the search will start from there and it will only traverse to the end before reporting that the search is complete.

Plotting Functions

BarCount versus BarIndex

Introduction to AFL

The AmiBroker Programming Language (AFL) is a very unique and powerful programming language but to use it effectively you have to understand how it works and how to properly use the AFL functions. For the newcomer to programming, this may represent a steep learning curve and it may take a little persistence to find the answers to all your questions. To write documentation on any topic that leaves no questions unanswered is an impossible task, all help documentation assumes a minimum level of familiarity with the topic studied. The problem is that this prerequisite minimum level of understanding is set by the subjective judgment of the author. The results is that, for the individual user, some topics are covered excessively while others are skimmed over because the author assumed that everybody is basically familiar with the topic. Users on the other hand often assume that their lack of knowledge is shared by all beginners and, if the Help file inadequately explains something, claim that the documentation is badly written. Of course their view is just as relative and subjective as those of the author.

This situation exist in various degrees in all documentation and cannot be prevented. The way for you to cope with this is to stay calm (there have been some heated posts on the lists) and do your own research research. If you still can’t understand something and/or can’t find the answer to your specific question you can email AmiBroker Technical Support for help or post your question on one of the AmiBroker forums:

Amibroker Users’ Group
AmiBroker Automated-Trading
AmiBroker Trading Systems
AmiBroker AFL

There are some other Yahoo forums you may want to look at, especially if you are multilingual. For a more general search targeting groups in any language click here.

If you believe your question is of general interest you can ask your question using the comment field below. But please be specific; questions like ‘How do I use AFL’ would require a book to answer and is way beyond the scope of what volunteers can contribute.

Of course we welcome your solutions to specific AFL problems, either as an author with a post (requires registration) in this category, or in the comment field below.

How Many Trading Days In A Year?

Sometimes it is useful to know the number of trading days in a year e.g. to annualize returns from a system that trades on a daily basis.

On other occasions, index data can contain errors, and the number of daily bars, in a year, can be compared to the exchange calendar, for the relevant index, to check on this failing.