However, there can be many benefits to keeping your code in functions that may be called from indicators or other functions. It may be useful to summarize some of these advantages here:
- Code can be re-used, rather than re-written each time. An in-line code cannot usually be cut and pasted into a routine without some tweaking of the variable names used.
- Updating, maintaining and improving your functions code is infinitely simpler when they are located in a single place.Can you imagine how many of your indicators or strategies use a common function like LastBarOnChart, or StandardDev? On my system, I have over 35 indicators or functions that use LastBarOnchart. If someone improves these functions, do you really want to recode dozens of indicators that use them?
Despite there being a global search feature to locate (but not replace) all occurances of a particular function in your routines, many times you have had to change the variable names in them to avoid conflicts with other variables in the indicator or strategy code. How many of these indicators or strategies will become “broken” after modifying the code of a widely used function? Each broken routine will then have to be tweaked back to health and debugged.
- Less memory is used when code is reused, rather than replicated throughout indicators. Consider how often you might place an indicator on multiple charts. With in-line coding, if you run the same code snippet within 10 different indicators you are using 10 times the memory to run these indicators simultaneously.
- SuperFunctions that return the values of multiple other functions and SuperIndicators that display a wide variety of indicators from a single indicator are very useful when testing trading strategies because the optimization features built into Tradestation can not only iterate through the values of specific functions but can also change the type of function being tested by iterating through the FunctionID parameters supplied to the SuperFunction.This saves considerable time when testing a class of trading systems.
Methods of Passing Parameters
Function parameters may be passed by value or by reference.
When parameters are passed by value, a copy of the parameter is made for the function. The original parameters is not manipulated directly by the function. With Tradestation, only input parameters may be passed by value.
When parameters are passed by reference, only the memory address of the parameter is passed to the function. The function then uses this address to directly manipulate the memory location that represented the stored value of the variable. This can be more efficient than passing a parameter by value, but with large numbers of parameters correspondingly large numbers of variable addresses have to be passed to the function. With Tradestation, both input and output parameters may be passed by reference.
Some Functions Require Many Parameters
Some functions are complex and utilize calls to many other functions to complete their task. If calls to many functions are made from within a function, then the parameters for all of these sub-functions called must also be passed to the main function. The number of parameters passed can grow quite large, and this increases the overhead of passing data to and from the function with each call made.
When looking at a function with a large number of parameters, the efficiency of calling this function must be considered.
Is there a way to pass a large number of static input parameters without causing significant processor overhead from the function call?
The Method
Packing Parameters into a Scaler Array
One method to improve the efficiency of function calls is to pack the parameters into a scaler array and then pass the array as a single parameter by reference. Instead of passing many values or addresses, only a single memory address must be passed to the function. The overhead associated with a function call passing only a single value or memory address is trivial and therefore need not concern us.
But what about the overhead of packing and unpacking the parameters from the array? Fortunately, this needs to be done only once rather than with each call to the function. Therefore, the function could be called thousands of times without having to pass many parameter values and addresses each time. How is this done?
An indicator with many static input parameters, MyIndicator, is used to illustrate the technique.
MyIndicator (Price, AMA_Length1, AMA_Length2, StdDevLength, StdDevMult, LinRegLength1, … etc.)
arrays: StaticParams[20](0); { dimensioned to handle up to 20 parameters }
{ pack the static input parameters into the array } once begin
StaticParams[1] = AMALength1;
StaticParams[2] = AMALength2;
StaticParams[3] = StdDevLength;
StaticParams[5] = LinRegLength;
//etc.
end;
value1 = MyFunction(Price, StaticParams);
plot1(value1, “Func Result”);
————————————————————————————
MyFunction(Price, NumericArrayRef)
vars: AMALength1(0), AMALength2(0), StdDevLength(0), LinRegLength(0), …. etc.)
vars: Result(0);
arrays: StaticParams[20](0);
{ unpack the static input variables }
once begin
AMALength1 = StaticParams[1];
AMALength2 = StaticParams[2];
StdDevLength = StaticParams[3];
LinRegLength = StaticParams[4];
// etc.
end;
{ put calculations and subfunction calls here }
_MyFunction = Result;
The parameters are packed into the array defined in MyIndicator when the indicator is initially applied to the chart. This code executes only one time when the chart is loaded or refreshed.
In MyFunction, the parameters are unpacked into variables. This unpacking occurs only once when the function is called for the first time. There is no further unpacking overhead, even if the function is called thousands of times.
The only events that trigger parameter packing and unpacking are the loading of chart data, the refreshing of chart data or the optimizer starting a new strategy optimization run.
Applications Suitable for This Technique
Suppose you had a SuperTrialingStop function that calculates any of several possible types of stops based on the input parameters supplied to it. For example, it might calculate trailing stop based on percentage, points, ATR multiples, Juric ATR multiples, standard deviation, standard error, or some combination of these. There may be a dozen input parameters needed to fully characterize the output that was desired.
Further suppose you now want to test a trading system using many different types of trailing stops as well as many different parameters applied to these stops and optimize not only the type of stop but the specific parameter values that produce the best results.
You can use this SuperTrailingStop function in a strategy to handle just about every possible type of trailing stop, and test the effect of all of them on the trading system’s performance. This is accomplished with a single strategy calling this function, and having the optimization feature of Tradestation iterate through all of the possible combinations you wish to test.
Would not this be less programming effort than adding or subtracting all the different types of trailing stops to the strategy manually and retesting each subset?
Similarly, additional input parameters would determine how the ribbons would be defined by calculating a vertical offset from the centerline based on standard deviation (Bollinger Bands), standard error (Anderson Ribbons), ATR units (Keltner Bands), Jurik ATR, percentage or points.
Such a SuperRibbonIndicator would be ideal to test many variations of a “reversion to the mean” type of trading system and could be done using a single strategy. The optimization runs would be selecting not only the values of the parameters but also choosing the type of ribbon bands creating by varying the CenterLineID and DeviationID variable value.
A completed SuperRibbonIndicator (renamed simply RibbonPlotter) has been posted in the indicator section.