Walk Forward Optimization
Walk Forward Optimization (WFO in short) is a variant of cross-validation for
time series and was first published by Robert Pardo as a backtest method for
trading strategies. It trains and tests the strategy in several cycles using a
data frame that "walks" over the simulation period. This allows an out-of-sample
backtest that still uses most of the historical data. WFO is not restricted to
backtests, but can be continued In live trading and regularly adapt the strategy
parameters to the current market situation. In this way, a WFO trained strategy
is essentially a 'parameter-less' system.
For greatly reducing the
backtest time with WFO, Zorro separates test and training and stores all trained
parameters separately for any WFO cycle. This way a backtest needs not perform
all optimizations again. To activate WFO in a strategy script, only the
following variable needs to be set:
NumWFOCycles
Number of cycles in a Walk Forward Optimization / Analysis (default = 0 = no Walk Forward Optimization). If NumWFOCycles is set to a positive number
at or above 2, rolling walk forward optimization is enabled
with the given number of cycles; if it is set to a negative number at or below
-2, anchored walk forward optimization is enabled. In Walk Forward Optimization, a data frame consisting of a training and test period is shifted over the simulation period in the given number of cycles (see image
for ).
WFOCycle |
Simulation period |
1 |
LookBack |
Training |
Test1 |
|
|
|
2 |
|
LookBack |
Training |
Test2 |
|
|
3 |
|
|
LookBack |
Training |
Test3 |
|
4 |
|
|
|
LookBack |
Training |
Test4 |
5 |
|
|
|
|
LookBack |
Training |
OOS Test |
LookBack |
Test5 |
|
|
|
WFO Test |
|
|
|
Look |
Back |
Test1 |
Test2 |
Test3 |
Test4 |
Rolling Walk Forward Optimization (NumWFOCycles
= 5)
WFOCycle |
Simulation period |
1 |
LookBack |
Training |
Test1 |
|
|
|
2 |
LookBack |
Training |
Test2 |
|
|
3 |
LookBack |
Training |
Test3 |
|
4 |
LookBack |
Training |
Test4 |
5 |
LookBack |
Training |
WFO Test |
|
|
|
Look |
Back |
Test1 |
Test2 |
Test3 |
Test4 |
Anchored Walk Forward Optimization (NumWFOCycles
= -5)
Strategy parameters and trade rules are generated separately for every cycle in [Train] mode, and are separately loaded for every test segment in [Test] mode. This way, the strategy test is guaranteed to be out of sample - it uses only parameters and rules that were generated in the preceding training cycle from data that does not occur in the test. This simulates the behavior of real trading where parameters and rules are generated from past price data. A Walk Forward Test gives the best prediction possible of the strategy performance over time.
WFOPeriod
Alternative to NumWFOCycles; number of bars of one WFO cycle (training + test), f.i. 5*24 for a one-week WFO cycle with 1-hour bars. For getting a fixed test period of N bars, set WFOPeriod to N*100/(100-DataSplit).
Since the number of WFO cycles now depends on the number of bars in the history,
asset must be called before. The WFO period is automatically adjusted so that the simulation period covers an integer number of WFO cycles.
SelectWFO
Set this to a certain WFO cycle number for selecting only this cycle in a training or test; otherwise the process runs over all cycles.
Setting it to -1 selects the last cycle that's normally used
for live trading.
Type:
int
WFOCycle
The number of the current WFO cycle, from 1 to NumWFOCycles.
Automatically set during WFO test and training.
WFOBar
The bar number inside the current WFO cycle, starting with 0 at the begin of the cycle. Can be used to determine when the cycle starts: if(WFOBar == 0).
Automatically set during a WFO test.
Type:
int, read/only
Remarks:
- WFO theory is explained in the Training chapter and in Workshop 5.
- If DataSplit is not set otherwise, WFO uses a default training period of 85% and a default test period of 15%.
- WFO can strongly reduce the overall test period as compared to no WFO (see
diagrams above). For a longer test period, increase the number of WFO cycles
and/or reduce DataSplit.
- DataSlope can improve the parameter quality with anchored WFO cycles.
- Use DataHorizon to prevent test bias when
training is based on future prices, as for some machine learning algorithms.
- WFO training time can be strongly reduced by using
several CPU cores.
- For executing code at the begin of every WFO cycle, check
if(WFOBar == 0) ... .
- The optimal number of WFO cycles depends mainly on the strategy, and to a lesser extent on the simulation period and the time frame. Large time frames or infrequent trading require a small number of WFO cycles for getting enough trades per cycle. Very market-dependent strategies with fast expiring parameters require a high number of WFO cycles. If the strategy performance highly varies with small changes of
NumWFOCycles, a periodic seasonal effect is likely the reason. Try to determine and eliminate seasonal effects before optimizing a strategy.
- Parameters, rules, and factors are stored in files in the Data folder. The number of the cycle is added to the file name, except for the last WFO cycle. F.i. a parameter WFO of a script "Trade.c" and 4 cycles will generate the following parameter files in the
Data folder: Trade_1.par, Trade_2.par,
Trade_3.par, Trade.par, Trade.fac. [Test] mode reloads the parameters and rules for every segment during the test cycle. [Trade] mode uses the parameters and rules from the last WFO cycle, without an attached number.
- Normally the LookBack period precedes the first WFO cycle in [Test] mode. If the
RECALCULATE flag is set, a dedicated lookback period precedes every WFO cycle. The content of all
series is discarded and recalculated from the parameters and rules of the new cycle. This increases the test time, but produces a slightly more realistic test by simulating the start of a new trade session at every WFO cycle.
- While live trading a walk-forward optimized strategy, it is recommended to
re-train the last cycle perodically for making
the strategy independent on parameter settings. The best time period
between retraining is the time of a WFO test cycle.
- The last WFO cycle (cycle 5 in the figure above) has no test period.
It can be a few bars longer than the other cycles to ensure training until
the very EndDate. If
SelectWFO is set to the last cycle in [Test] mode, the out-of-sample period before the training is tested instead (OOS Test in the figure; rolling WFO only). The OOS test often generates low profit, but can inform about the long-term performance of the strategy when it is not re-trained.
- Anchored WFO can be used to test the lifetime of parameters or rules. If anchored WFO generates a better result than rolling WFO, the strategy is longlived and does not need to be retrained often. Normally, rolling WFO produces better results, meaning that the market changes and that parameters / rules must be adapted from time to time.
- WFO parameters and rules are only valid for the simulation period with which they were created. If the period is changed - for instance, when new price data is downloaded - the strategy must be trained anew. For avoiding unintended changes of the simulation period, set a fixed
StartDate and EndDate for the test.
- In training, every WFO cycle is a separate simulation run including
INITRUN and EXITRUN. In testing all WFO cycles are tested in a single simulation run.
- Use the plotWFOCycle and plotWFOProfit functions for analyzing the profit curve over one or several WFO cycles.
Examples:
// anchored WFO, 10 cycles
function run()
{
NumWFOCycles = -10; DataSplit = 80; // 20% test cycle
DataSlope = 2; // more weight to more recent data
set(PARAMETERS+TESTNOW); // run a test immediately after WFO
...
}
// WFO with 120 bars training and 30 bars test
function run()
{
WFOPeriod = 120+30;
DataSplit = 100*120/WFOPeriod;
..
}
// calculate NumWFOCycles from the test period in days
// DataSplit and LookBack must be set before
function setWFO2(int daysTest)
{
asset(Asset); // requires NumBars, so asset must be set
int barsTest = daysTest * 1440/BarPeriod;
int barsTrain = barsTest * DataSplit/(100-DataSplit);
int barsTotal = NumBars-LookBack;
NumWFOCycles = (barsTotal-barsTrain)/barsTest + 1;
}
See also:
Training, Tutorial, DataSplit, NumSampleCycles, NumTotalCycles, NumOptCycles
► latest version online