Many posts have dealt with the issue of coding efficiency, and covered topics such as:
- Static arrays vs dynamic arrays vs vectors
- Function calls vs inline code (including methods)
- multiplication vs division
- choosing fast algorithm methods for standard calculations (average, standard deviation, standard error, sorting, etc.)
- “Tick Tamer” techniques of throttling the frequency of intrabar code execution to avoid CPU overload during periods of high market activity
The general concept that creates the greatest efficiency benefit is:
“only execute code when required, and not more frequently”
Examples of this are calculating functions once per bar when the value is not going to change significantly intrabar. Another example is throttling the frequency of intrabar calculations rather than allowing this code to run on each tick, a concept popularized by Goose when he published “Tick Tamer” code.
Some code has sections that only need to be run at the End of Bar, and other sections that need to be run intrabar. An example is an indicator that reports the difference between the current price and its 200-day moving average. The moving average needs to be calculated only once per bar, but the difference between the price and the moving average needs to be calculated intrabar. For the portion that is calculated intrabar, the question arises as to how frequently is must be calculated. Perhaps the indicator will be perfectly adequate running the intrabar portion once every 10 seconds, rather than with every tick. If that is the case, then throttling intrabar processing to once every 10 seconds may results in significant efficiency improvements and lower demand on the CPU.
When it comes to plotting the result, the frequency the plot statements have to issued varies depending upon whether the indicator is used on a chart or Radarsreen. Radarscreen requires that values displayed be plotted with each tick, otherwise the displayed value will disappear from the screen in between successive plot statements. For charts, however, the result could be plotted once every 10 seconds without detracting from the value of the indicator, because chart plots do not “disappear” in between plot statements as they do in Radarscreen. For charts, the plotting routine could be throttled intrabar to executing once every 10 seconds, or whatever frequency the user feels is necessary.
The ideal place to control the execution of code modules is from the main program of the indicator, function, or strategy. It is also a good practice to move most code performing calculations, plotting activities, and generating orders out of the main program and into code modules (functions or methods). This makes the logic of code execution easier to appreciate by both authors and interested readers.
Example of An Efficient Main Program Coding Structure
Indicator Main Program
In the above example, you will note the following features:
- The BS = BarStatus(1) statement (line 384) is used to determine whether the data is an End of Bar event or an Intrabar event.
- A switch BS begin statement (line 386) segregates the code that must be run intrabar from that which needs only to be run at the end of a bar.
- The once Recalc = true (line 395) statement will force force certain EOB code to execute once immediately if the LastBarOnChart has BarStatus(1) = 1. An example of when this will occur is if a weekly bar chart is loaded and the last bar on the chart has not yet closed because it is the middle of the week. This ensures the stop levels and triggered stops will be shown on the current bar while the market is open.
- Intrabar calculations are throttled to not occur more frequently than once every ReCalcSec, an input parameter specified for the indicator. A Time module sets ReCalc = true every ReCalcSec.
#region statements are used liberally in the above example, as this facilitates documenting the program structure in the Outline Window. It also allows the coder to select any item in the Outline Window, and the code window will highlight and jump to this section of code. This is a great benefit when coding projects become large and have multiple methods. The Outline Window allows you to spot the section of code you are searching for quickly, and selecting it makes that section of code visible in the coding window.
Outline Window Demonstrating Program Components
The ReCalc variable is set to be true once every ReCalcSec seconds by a Timer method, shown below:
Timer Method Determining Frequency of Intrabar Calculations
When are Efficient Coding Structures Essential?
Placing a few indicators on a few charts and monitoring their progress during the day does not generally overload the CPU, as there are not a large number of calculations occurring, even if the code is inefficient.
The following situations illustrate when efficient coding structures make the difference between a platform operating sluggishly during high market activity:
When multiple indicators are used in Radarscreen, and several hundred symbols have been loaded.
If portions of the indicator code are performing some intrabar calculations, it is important to separate the EOB calculations from the intrabar calculations and to throttle the intrabar calculation frequency. If this is not done, Radarscreen response will become very sluggish, especially at the market open and close, when trading activity is the greatest.
Many users will run dozens of open charts simultaneously that have multiple indicators loaded. Again, separating the code that must be run at the End of Bar from the code that runs intrabar is important. Throttling the intrabar frequency of calculations should also be done.
Function code is used in a strategy that is being backtested. The strategy will run thousands of times to determine optimum input parameters. In this case, using efficient coding and fast calculation algorithms is important, if backtesting is to be completed in a reasonable time.
When the strategy is run “live” during trading hours, portions of the strategy code must execute intrabar to determine if orders should be issued. Separating the code that must be executed End of Bar from the Intrabar code, and throttling the frequency of the intrabar calculations ensure the strategy will not overload the CPU during the busiest periods of the trading day, the open and closing portions of the day.
In my strategies, I often plot indicators on the chart from within the strategy using a custom function _TLPlot, that may be found in the forum. This is useful to show the levels at which stops will be hit or conditions where a trade will be executed. Plotting the values on the chart while the strategy is running allows the coder to verify the strategy has been programmed correctly and is a great benefit in debugging.
Since plotting is not desirable during backtesting, I detect when the strategy is backtesting, and automatically disable any plotting instructions in the strategy. In that way, I can use the identical strategy code for both backtesting, and running the strategy “live.”
General Recommendations for Efficient Software Development
- Make the core portions of code calculations as re-usable as possible rather than having multiple version of the same code in indicators, functions and strategies. Indicators that are plotting a function should place the calculations involved in a Function and call this Function from the indicator, leaving the indicator code to handle Plotting and Formatting of output.
- Convert functions with many input parameters to an equivalent method to avoid the overhead of passing many parameters with each call. These input parameters can be redefined to be variables in the main program, which will give them global status with respect to the methods.
This requires being more specific on naming the parameters so as not to conflict with other method parameters. For example, for a linear regression calculation method, a length variable should be named LRLength, rather than simply Length, as other methods will also probably have a similarly named parameter. Commonly used variables such as looping counters can be defined to be local variables of the method, to avoid conflicts with other methods’ counter variables.
- Store any methods in an Indicator container, and define all needed parameters as variables of the indicator. This allows the methods to be stored in an organized method, similar to other Indicators and Functions. It also allows the code to be verified, and groups the needed variables together so they can be “cut and pasted” to a larger program that needs to use the same method for its calculations. I suggest adding a suffix to the Indicator container so it can be easily identified for retrieval, such as LinReg.Method.