WalkForward
PortfolioOptimisers.WalkForwardResult — Type
struct WalkForwardResult{__T_train_idx, __T_test_idx} <: SequentialCrossValidationResultResult type produced by WalkForwardEstimator subtypes after splitting time series data.
Stores the train and test index vectors for each fold of the walk-forward cross-validation.
Fields
train_idx: Training set indices.
test_idx: Test set indices.
Constructors
WalkForwardResult(; train_idx::VecVecInt, test_idx::VecVecInt) -> WalkForwardResultKeywords correspond to the struct's fields.
Validation
!isempty(train_idx)(sufficient data to cover training + testing periods).!isempty(test_idx)(sufficient data to cover training + testing periods).length(train_idx) == length(test_idx).
Related
PortfolioOptimisers.OnlineStep — Type
struct OnlineStep <: AbstractFoldFitFit each fold of a walk-forward by the online step: warm up once, then fold the new observations in and read the estimator out.
Under this Fold Fit the loop takes its online arm, online_folds. It resolves every Online wrapper through update_online_estimator, folds the first training window into the estimator with partial_fit!, and then, per fold, folds only the rows the training window has gained since the last fold and reads the estimator out through optimise(opt). The estimator is threaded from fold to fold, so fold i never re-reads the rows fold i - 1 read, and the run reaches the weights of the batch expanding-window walk-forward fold for fold. An online run is expanding by construction — a fold cannot un-fold an observation — so the scheme derives expand_train = true from this selector, and a rolling window computed online is the estimator's to declare, through Online(pe; max_history = train_size - purged_size).
The loop starts cold: an estimator carrying a partial-fit state at entry is refused by name, and a TimeDependent schedule on a field that carries a state — the prior, or the optimiser itself — is refused at warm-up, because a schedule replaces the value a state is threaded through. A schedule on any other field composes with no rule.
Examples
julia> OnlineStep()OnlineStep()Related
PortfolioOptimisers.IndexWalkForward — Type
struct IndexWalkForward{__T_train_size, __T_test_size, __T_purged_size, __T_expand_train, __T_reduce_test, __T_wd, __T_pws, __T_fa, __T_store_weight_path, __T_strict, __T_ff} <: WalkForwardEstimatorImplements index-based walk-forward cross-validation for time series, supporting purging and flexible train/test windowing.
purged_size drops the last purged_size rows of each training window. This opens a gap of that many observations before the test window, and removes the training rows whose labels reach into the test period. The test windows do not move, so a purge costs training rows rather than test coverage.
Fields
train_size: Training window size.
test_size: Test window size.
purged_size: Number of observations to purge between train and test sets.
expand_train: Whether to expand the training window over time. The constructor keyword takesnothing, which derives the field from the Fold Fit:trueunder anOnlineStep, because an online run is expanding by construction, andfalseotherwise. An explicitfalsebeside anOnlineStepis refused.
reduce_test: Whether to allow the last test window to be smaller.
wd: Weight drift the fold's return series is read under, ornothingto read it at the target weights of the fold.
pws: Previous-weights source the fold loop threads into the next fold, ornothingto thread the target weights of the previous fold.
fa: Fee amortisation algorithm the fold's realised series charges the two fixed fee terms on, ornothingto inherit the clock the fee itself states. It overridesFees.fafor that series alone, and it reaches the fit not at all.
store_weight_path: Iftrue, the fold stores the weight path it computed; iffalse, a reader rebuilds it on demand.
strict: Iftrue, a Held Gap raises anArgumentError; iffalse, it warns and the pair contributes zero. A Held Gap is an (observation, asset) pair at which the fold's weight is non-zero and the asset's return is missing, which is what a delisting inside a test window makes.
ff: Fold Fit of the scheme, ornothingto refit every fold from its training window. AnOnlineStepmakes the fold loop warm up once on the first training window and then fold each fold's new observations into one estimator threaded from fold to fold, reading it out where a refit would have run.
Constructors
IndexWalkForward( train_size::Integer, test_size::Integer; purged_size::Integer = 0, expand_train::Option{Bool} = nothing, reduce_test::Bool = false, wd::Option{<:AbstractWeightDrift} = nothing, pws::Option{<:AbstractPreviousWeightsSource} = nothing, fa::Option{<:AbstractFeeAmortisation} = nothing, store_weight_path::Bool = false, strict::Bool = false, ff::Option{<:AbstractFoldFit} = nothing,) -> IndexWalkForwardPositional and keyword arguments correspond to the struct's fields.
Fold Fit
ff is the Fold Fit of the scheme, the switch that says how the fold loop fits each fold. nothing refits every fold from its training window, which is the library's original behaviour. An OnlineStep makes the loop warm up once on the first training window and then fold each fold's new observations into one estimator threaded from fold to fold, reading it out where a refit would have run; the run reaches the weights of the expanding batch scheme fold for fold. expand_train derives from it: nothing, the default, resolves to true under an OnlineStep and to false otherwise, because an online run is expanding by construction. An explicit expand_train = false beside an OnlineStep is refused; a rolling window computed online is the estimator's to declare, through Online(pe; max_history = train_size - purged_size).
Weight drift and previous weights
The two switches are independent, and each one is nothing by default, which is the library's original behaviour.
wd is the Weight Drift of the scheme. nothing reads a fold's return series as X * w net of fees, at the target weights of that fold. A SelfFinancingDrift reads the series as the wealth ratio of the drifted holdings instead. store_weight_path makes the fold store the weight path it computed, which a reader otherwise rebuilds on demand. strict decides what a Held Gap does: an asset that delists inside a test window carries a non-zero weight and a missing return, and the fold zeroes that pair and warns, or refuses with an ArgumentError under strict.
pws is the Previous-Weights Source. nothing threads the target weights of the previous fold into the next one. A DriftedWeights threads the weights held after the last observation of the previous fold instead, so a turnover, a tracking or a fee estimator measures the trades a fund places rather than the change in the decision. A fold enumeration of this scheme is a timeline, so the source has a previous fold to read.
Fee clock
fa is the clock the fold's realised series charges the two fixed fee terms on, and it overrides the fa of the fee itself. nothing inherits that fee's clock, which is the library's original behaviour. A FirstObservationFees charges the two terms on the first observation of the fold, and an AmortisedFees spreads them over the fold. The field reaches the fit not at all, so the optimiser keeps pricing the fee the way its own objective must.
Validation
train_sizeandpurged_sizemust be non-empty, non-negative, and finite.test_sizemust be non-empty, greater than zero, and finite.purged_size < train_size, because the purge is taken out of the training window.expand_trainistruewhenffis set, because a Fold Fit cannot un-fold an observation.
The rule train_size < T, where T is the number of observations, belongs to the data rather than to the estimator, so Base.split checks it.
Examples
julia> IndexWalkForward(100, 20; purged_size = 5, expand_train = true, reduce_test = false)IndexWalkForward train_size ┼ Int64: 100 test_size ┼ Int64: 20 purged_size ┼ Int64: 5 expand_train ┼ Bool: true reduce_test ┼ Bool: false wd ┼ nothing pws ┼ nothing fa ┼ nothing store_weight_path ┼ Bool: false strict ┼ Bool: false ff ┴ nothingRelated
cross_val_predictsearch_cross_validationWalkForwardEstimatorWalkForwardResultOnlineStepfold_fitn_splits
References
PortfolioOptimisers.DateWalkForward — Type
struct DateWalkForward{__T_train_size, __T_test_size, __T_period, __T_period_offset, __T_purged_size, __T_adjuster, __T_previous, __T_expand_train, __T_reduce_test, __T_wd, __T_pws, __T_fa, __T_store_weight_path, __T_strict, __T_ff} <: WalkForwardEstimatorImplements date-based walk-forward cross-validation for time series, supporting flexible windowing, purging, and custom date adjustment.
purged_size drops the last purged_size rows of each training window. This opens a gap of that many observations before the test window, and removes the training rows whose labels reach into the test period. The test window itself is not shortened, so purged_size must be smaller than the training window.
Fields
train_size: Training window size.
test_size: Test window size.
period: Time period for date-based walk-forward cross-validation.
period_offset: Offset applied to the walk-forward period.
purged_size: Number of observations to purge between train and test sets.
adjuster: Function for adjusting walk-forward dates.
previous: Whether to include the previous period in the training window.
expand_train: Whether to expand the training window over time. The constructor keyword takesnothing, which derives the field from the Fold Fit:trueunder anOnlineStep, because an online run is expanding by construction, andfalseotherwise. An explicitfalsebeside anOnlineStepis refused.
reduce_test: Whether to allow the last test window to be smaller.
wd: Weight drift the fold's return series is read under, ornothingto read it at the target weights of the fold.
pws: Previous-weights source the fold loop threads into the next fold, ornothingto thread the target weights of the previous fold.
fa: Fee amortisation algorithm the fold's realised series charges the two fixed fee terms on, ornothingto inherit the clock the fee itself states. It overridesFees.fafor that series alone, and it reaches the fit not at all.
store_weight_path: Iftrue, the fold stores the weight path it computed; iffalse, a reader rebuilds it on demand.
strict: Iftrue, a Held Gap raises anArgumentError; iffalse, it warns and the pair contributes zero. A Held Gap is an (observation, asset) pair at which the fold's weight is non-zero and the asset's return is missing, which is what a delisting inside a test window makes.
ff: Fold Fit of the scheme, ornothingto refit every fold from its training window. AnOnlineStepmakes the fold loop warm up once on the first training window and then fold each fold's new observations into one estimator threaded from fold to fold, reading it out where a refit would have run.
Constructors
DateWalkForward( train_size::IntPeriodDateRange, test_size::Integer; period::DatesUnionPeriod = Dates.Day(1), period_offset::Option{<:DatesUnionPeriod} = nothing, purged_size::Integer = 0, adjuster::DateAdjType = identity, previous::Bool = false, expand_train::Option{Bool} = nothing, reduce_test::Bool = false, wd::Option{<:AbstractWeightDrift} = nothing, pws::Option{<:AbstractPreviousWeightsSource} = nothing, fa::Option{<:AbstractFeeAmortisation} = nothing, store_weight_path::Bool = false, strict::Bool = false, ff::Option{<:AbstractFoldFit} = nothing,) -> DateWalkForwardPositional and keyword arguments correspond to the struct's fields.
Fold Fit
ff is the Fold Fit of the scheme, the switch that says how the fold loop fits each fold. nothing refits every fold from its training window, which is the library's original behaviour. An OnlineStep makes the loop warm up once on the first training window and then fold each fold's new observations into one estimator threaded from fold to fold, reading it out where a refit would have run; the run reaches the weights of the expanding batch scheme fold for fold. expand_train derives from it: nothing, the default, resolves to true under an OnlineStep and to false otherwise, because an online run is expanding by construction. An explicit expand_train = false beside an OnlineStep is refused; a rolling window computed online is the estimator's to declare, through Online(pe; max_history = …) with the cap the window's rows count to.
Weight drift and previous weights
The two switches are independent, and each one is nothing by default, which is the library's original behaviour.
wd is the Weight Drift of the scheme. nothing reads a fold's return series as X * w net of fees, at the target weights of that fold. A SelfFinancingDrift reads the series as the wealth ratio of the drifted holdings instead. store_weight_path makes the fold store the weight path it computed, which a reader otherwise rebuilds on demand. strict decides what a Held Gap does: an asset that delists inside a test window carries a non-zero weight and a missing return, and the fold zeroes that pair and warns, or refuses with an ArgumentError under strict.
pws is the Previous-Weights Source. nothing threads the target weights of the previous fold into the next one. A DriftedWeights threads the weights held after the last observation of the previous fold instead, so a turnover, a tracking or a fee estimator measures the trades a fund places rather than the change in the decision. A fold enumeration of this scheme is a timeline, so the source has a previous fold to read.
Fee clock
fa is the clock the fold's realised series charges the two fixed fee terms on, and it overrides the fa of the fee itself. nothing inherits that fee's clock, which is the library's original behaviour. A FirstObservationFees charges the two terms on the first observation of the fold, and an AmortisedFees spreads them over the fold. The field reaches the fit not at all, so the optimiser keeps pricing the fee the way its own objective must.
Validation
test_sizemust be non-empty, greater than zero, and finite.purged_sizemust be non-empty, non-negative, and finite.- If
train_sizeis an integer, it must be non-empty, non-negative, and finite. expand_trainistruewhenffis set, because a Fold Fit cannot un-fold an observation.
Examples
julia> DateWalkForward(252, 21; period = Dates.Day(1), purged_size = 5, expand_train = true)DateWalkForward train_size ┼ Int64: 252 test_size ┼ Int64: 21 period ┼ Dates.Day: Dates.Day(1) period_offset ┼ nothing purged_size ┼ Int64: 5 adjuster ┼ typeof(identity): identity previous ┼ Bool: false expand_train ┼ Bool: true reduce_test ┼ Bool: false wd ┼ nothing pws ┼ nothing fa ┼ nothing store_weight_path ┼ Bool: false strict ┼ Bool: false ff ┴ nothingRelated
cross_val_predictsearch_cross_validationWalkForwardEstimatorWalkForwardResultOnlineStepfold_fitn_splits
References
Base.split — Method
Base.split(iwf::IndexWalkForward, rd::Prices_RR) -> WalkForwardResultSplit the returns data rd into sequential walk-forward folds using integer observation indices. Each fold advances the test window by test_size observations.
Arguments
iwf::IndexWalkForward: Index-based walk-forward cross-validation estimator.rd: Returns-level or price-level data to split (Prices_RR).
Validation
train_size < T, whereTis the number of observations inrd.
Returns
WalkForwardResult: Result containing train and test index ranges for each fold.
Related
Base.split — Method
Base.split(dwf::DateWalkForward{<:Integer}, rd::Prices_RR) -> WalkForwardResultSplit the returns data rd into sequential walk-forward folds using date-aligned indices, where train_size is specified as an integer number of date-range steps.
The timestamp vector (cv_timestamps) must not be nothing. Training and test windows are aligned to the period date range and advanced by test_size steps at a time.
Arguments
dwf::DateWalkForward{<:Integer}: Date-based walk-forward estimator with an integertrain_size.rd: Returns-level or price-level data with timestamps (Prices_RR).
Returns
WalkForwardResult: Result containing train and test index ranges for each fold.
Related
Base.split — Method
Base.split(dwf::DateWalkForward{<:Any}, rd::Prices_RR) -> WalkForwardResultSplit the returns data rd into sequential walk-forward folds using date-aligned indices, where train_size is specified as a date Period (e.g., Dates.Month(6)).
The timestamp vector (cv_timestamps) must not be nothing. Training windows are defined by subtracting train_size from the split date, allowing calendar-based window lengths.
Arguments
dwf::DateWalkForward{<:Any}: Date-based walk-forward estimator with aPeriodtrain_size.rd: Returns-level or price-level data with timestamps (Prices_RR).
Returns
WalkForwardResult: Result containing train and test index ranges for each fold.
Related
PortfolioOptimisers.n_splits — Function
n_splits(cv, rd::Prices_RR)
n_splits(cv)Return the number of cross-validation splits (folds) that would be produced by cv for the given returns data rd.
Arguments
cv: A cross-validation estimator or result (e.g.KFold,IndexWalkForward,DateWalkForward,CombinatorialCrossValidation,MultipleRandomised, or their corresponding result types).rd: Returns-level or price-level data used to determine the number of splits (Prices_RR).
Returns
Integer: The number of folds.
Related
n_splits(dwf::DateWalkForward{<:Integer}, rd::Prices_RR) -> IntegerReturn the number of walk-forward folds that would be produced by dwf for the given returns data rd when the training window size is specified as an integer number of date-range steps.
Arguments
dwf::DateWalkForward{<:Integer}: Date-based walk-forward estimator with an integertrain_size.rd: Returns-level or price-level data with timestamps (Prices_RR).
Returns
Integer: The number of folds.
Related
n_splits(dwf::DateWalkForward{<:Any}, rd::Prices_RR) -> IntegerReturn the number of walk-forward folds that would be produced by dwf for the given returns data rd when the training window size is specified as a date Period.
Arguments
dwf::DateWalkForward{<:Any}: Date-based walk-forward estimator with aPeriodtrain_size.rd: Returns-level or price-level data with timestamps (Prices_RR).
Returns
Integer: The number of folds.
Related
PortfolioOptimisers.n_splits — Method
n_splits(dwf::DateWalkForward{<:Integer}, rd::Prices_RR) -> IntegerReturn the number of walk-forward folds that would be produced by dwf for the given returns data rd when the training window size is specified as an integer number of date-range steps.
Arguments
dwf::DateWalkForward{<:Integer}: Date-based walk-forward estimator with an integertrain_size.rd: Returns-level or price-level data with timestamps (Prices_RR).
Returns
Integer: The number of folds.
Related
n_splits(dwf::DateWalkForward{<:Any}, rd::Prices_RR) -> IntegerReturn the number of walk-forward folds that would be produced by dwf for the given returns data rd when the training window size is specified as a date Period.
Arguments
dwf::DateWalkForward{<:Any}: Date-based walk-forward estimator with aPeriodtrain_size.rd: Returns-level or price-level data with timestamps (Prices_RR).
Returns
Integer: The number of folds.
Related
PortfolioOptimisers.n_splits — Method
n_splits(dwf::DateWalkForward{<:Any}, rd::Prices_RR) -> IntegerReturn the number of walk-forward folds that would be produced by dwf for the given returns data rd when the training window size is specified as a date Period.
Arguments
dwf::DateWalkForward{<:Any}: Date-based walk-forward estimator with aPeriodtrain_size.rd: Returns-level or price-level data with timestamps (Prices_RR).
Returns
Integer: The number of folds.
Related
References
- [5]
- D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025).
- [118]
- M. López de Prado. Advances in Financial Machine Learning (John Wiley & Sons, Hoboken, NJ, 2018).