Navigation: Backtest Engine Details >

Number of Bars Required for Functions and Indicators





A question that any backtesting framework must answer is: what to do when there are not enough bars of data to evaluate a formula term such as a moving average function. One solution is to place limitations on the syntax, such as requiring that every bar count argument is a constant. In RealTest, there is no need to ever think about how many bars of data are available. What happens automatically is as follows:

Any formula which contains any term which cannot be evaluated with available data simply returns 0. For example, say there’s a stock within your data which was only listed 2 weeks ago and you’re scanning for C > Avg(C, 20). This expression just returns 0 (FALSE), and the scanner moves on to the next symbol.

If you want, you can use BarNum to allow shorter average lengths to be used in specific formulas. For example, to scan for "above the 20-day average or the longest average currently available", you could say C > AVG(C, Min(20,BarNum)). There is also an option, described below, to do this automatically.

It’s easy to see how equating "can’t evaluate" with 0 works intuitively for conditional formulas like EntrySetup or the Scan filter. For formulas that return a dollar amount, like MaxInvested, or a price, like ExitStop, the test engine does the right thing as well. You do not have to worry, for example, that if your long entry stop price formula evaluates to 0, all candidates will trigger an immediate stop. In this and every similar case, the 0 is interpreted as "can never be hit".

All of the above was a long-winded way to say don’t worry about how many bars of data are available.

An exception to the "number of bars required" rule are the functions SinceTrue, CountTrue, and TrueInRow. These functions treat a can't be evaluated term as FALSE but do not abort the entire formula, since doing so would defeat their purpose.

For example, a liquidity filter which I like to use in data import is:

Data import ExcludeIf formulas are evaluated once per symbol, after all the bars have been processed, to decide whether to keep or discard that symbol’s data.

In this example, stocks will be excluded that have never traded above $10/share and had average volume of at least 100K shares.

This function returns -1 if a condition was NEVER true within the specified bar count (in this case the default of all available bars).

As mentioned above, there is also an option to automatically shorten moving average and indicator lengths when there are not enough bars available. Most of the time, the default setting (off), as described above, is recommended.

If you know that you want to always use shorter lengths when not enough bars are available (or, in the case of exponential smoothing, permit decreased precision), add UseAvailableBars: True to your Settings definitions.

You can see this option both ways by plotting a long moving average on a chart and scrolling back near the start of the data. Change the setting and then select Refresh from the Chart's menu to see the effect.

With the default setting (off):

With the option to use whatever bars are available:

When using this option, you can also use BarNum to explicitly force a specific formula to require enough bars, e.g., iif(BarNum > 20, Avg(C, 20), NaN).





Copyright © 2020-2024 Systematic Solutions, LLC