April 22, 2011
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_AFL( condition, inputA, inputB ) { result = Null; for( bar = 0; bar < BarCount; bar++ ) { if( condition[ bar ] ) result[ bar ] = inputA[ bar ]; else result[ bar ] = inputB[ bar ]; } 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 = IIf( DayOfWeek()==1, colorWhite, colorBlack); Plot( C, "Close", color, styleBar );
IIF()s Can be nested. This example colors Monday bars White, Wednesday bars Blue and Friday bars Yellow:
D = DayOfWeek(); Color = IIf(D==1, colorWhite, IIf(D==3, colorBlue, IIf(D==5, colorYellow, colorBlack))); Plot( C, "Close", color, styleBar );
The IF() Statement
One of the most common applications for the if() is to select what you want to see on your chart:
Plot( C, "Close", colorBlack, styleBar ); ShowMA10 = ParamToggle( "Moving Average", "MA|EMA", 0 ); if ( ShowMA10 ) { Plot( MA( C, 10 ), "MA10", colorWhite, styleLine ); } else { Plot( EMA( C, 10 ), "MA10", colorWhite, styleLine ); }
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", 1 ); if ( SelectedIndicator == "MA10" ) { Plot( MA( C, 10 ), "MA10", colorBlue, styleLine ); } else if ( SelectedIndicator == "MA10" ) { Plot( EMA( C, 50 ), "MA10", colorRed, styleLine ); } else if ( SelectedIndicator == "MA100" ) { Plot( EMA( C, 100 ), "MA100", colorYellow, styleLine ); }
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", 1 ); switch ( SelectedIndicator ) { case "MA10": Plot( MA( C, 10 ), "MA10", colorBlue, styleLine ); break; case "MA50": Plot( MA( C, 50 ), "MA50", colorBlue, styleLine ); break; case "MA100": Plot( MA( C, 100 ), "MA100", colorBlue, styleLine ); 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 SayOnce( Text ) { if ( StaticVarGetText( "Lastsay" ) != Text ) { Say( Text, False ); 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( 1 ) { 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 SayOnce( Text ) { if ( StaticVarGetText( "Lastsay" ) != Text ) { Say( Text, False ); 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; }
Filed by Herman at 10:20 am under AFL - The Basics
Comments Off on Using IIF(), IF() and Switch() functions