Using Walkforward Safe Functions
There are various series functions in TradeStation that are not compatible with walkforward strategies. A “series” function is any function that operates on series data, such as open, high, low, close or volume data. These include all technical indicators, such as Average(), RSI(), ADX(), etc.
As of Jan 2024, I have found the following functions to have issues with walkforward strategies:
Analysis Functions
- ADX
- ConnorsRSI
- DirMovement
- MACD
- RSI
- SMI
- Stochastic
- TRIX
- XAverage
- XAverageOrig
Fast Calculation Functions
Candlestick Pattern Functions
- C_3WhSolds_3BlkCrows
- C_AbandonedBaby
- C_AdvanceBlock
- C_BullEng_BearEng
- C_BullHar_BearHar
- C_Hammer_HangingMan
- C_HaramiCross
- C_InvertedHammer
- C_Marubozu
- C_MeetingLines
- C_MornDoji_EveDoji
- C_MornStar_EveStar
- C_PierceLine_DkCloud
- C_ShootingStar
- C_TasukiGap
- C_ThreeMethods
- C_ThreeStarsInTheSouth
- C_TriStar
I have created drop-in function replacements that allow them to work with walkforward strategies. They are identical to their TradeStation counterparts, but have been modified to make them “walkforward safe”. They are named as WFSafe_ADX, WFSafe_RSI, etc.
These functions are included in MultiWalk and installed when you install MultiWalk. However, you will need to change your strategy code to reference them (i.e., use WFSafe_RSI instead of RSI).
So what, exactly, is happening?
Let’s say, for example, you have a walkforward strategy that uses RSI and varies the period length (RSILength) between 10-20, stepping 5 units each iteration.
This works fine in MultiOpt because it will calculate three different optimized iterations using fixed period lengths for each:
- Iteration 1 : RSILength = 10
- Iteration 2 : RSILength = 15
- Iteration 3 : RSILength = 20
For each iteration, RSILength is fixed at its designated value. The legnth parameter does not change throughout the entire iteration.
However, when we create the walkforward strategy that chooses the “best” iteration each walkforward period, RSILength will periodically change:
RSILength = 10; // initialize
if date >= 1160208 and date < 1170207 then
begin
RSILength = 15;
end;
if date >= 1170207 and date < 1180207 then
begin
RSILength = 20;
end;
if date >= 1180207 and date < 1190208 then
begin
RSILength = 10;
end;
When the strategy calls RSI:
RSIValue = RSI( RSILength );
You would expect it to compute RSIValue based on the given RSILength. However, the way some of TradeStation’s functions are written, it will only set this parameter on the first bar of your strategy.
In the above example, RSILength will never change — it will always remain at 10 since this was the value on the first bar of the strategy.
The reason for this is the way TradeStation is initializing the value in their function. Look at the RSI code in the TradeStation function:
The variable SF is initialized in the variable declaration. This means that it will only be set one time when the function is first called.
For a walkforward strategy, this needs to become:
Now the variable SF will be set on every bar when the RSI function is called and the RSI calculation will be correct.
Therefore, instead of calling TradeStation’s version of RSI(), you would instead use WFSafe_RSI():
RSIValue = WFSafe_RSI( RSILength );
TradeStation will not always treat your intializations in variable declarations as you might think. For this reason I have adopted the following rule-of-thumb:
Never rely on variable initializations in declarations. Always initialze variables in-code.
While this is less efficient, it is more reliable when writing strategies in TradeStation.