Validation
PortfolioOptimisers.cross_val_predict Function
cross_val_predict(opt, rd::ReturnsResult, cv::CVER = KFold(); cols = :, ex = FLoops.ThreadedEx())Run cross-validated portfolio optimisation and return predictions over all folds.
Accepts either an optimisation estimator or an optimisation result. When cols is provided, restricts the optimisation to that subset of assets. Parallel fold execution is controlled by ex.
Arguments
opt: Optimisation estimator or result.rd::ReturnsResult: Returns data used for fitting and prediction.cv::CVER: Cross-validation scheme. Defaults toKFold().cols: Column selector. Defaults to:(all assets).ex: FLoops executor controlling parallelism. Defaults toFLoops.ThreadedEx().
Returns
- Cross-validation prediction result.
Related
sourcecross_val_predict(pipe::Pipeline, data::Prices_RR, cv::CombinatorialCrossValidation; ex = FLoops.ThreadedEx(), kwargs...) -> PopulationPredictionResultRun combinatorial cross-validation over a price- or returns-level Pipeline.
Each split fits the whole workflow on its (possibly non-contiguous) training rows and predicts each of the split's disjoint test groups; sort_predictions! then recombines the per-split test-group predictions into the scheme's paths, exactly like the plain-optimiser combinatorial loop. Time-dependent steps resolve per split against the fold's TimeDependentContext before fit.
At the returns level the training rows are order-independent for moment-style fitted steps, so this is exact. At the price level a split's training rows are non-contiguous — there are gaps where the held-out test groups sit — so the fold's rolling transform (PricesToReturns) produces one spurious return per gap boundary (a boundary return spanning a gap). That is the rolling-window approximation: combinatorial paths at the price level cost a few boundary returns in each fold's training window. Test groups are contiguous, so predictions are unaffected. Use MultipleRandomised if you need contiguous training rows at the price level.
Related
sourcecross_val_predict(pipe::Pipeline, data::Prices_RR, cv::MultipleRandomised; ex = FLoops.ThreadedEx(), kwargs...) -> PopulationPredictionResultRun asset-resampling (multiple-randomised) cross-validation over a price- or returns-level Pipeline.
Each resampled path is an inner walk-forward over a random asset subset; the subset is applied to the input data (an asset view via pipeline_asset_view), and the pipeline is fitted fresh on the sub-universe — so the pipeline never needs to sub-select its fitted universe. Paths are run by pipeline_path_fit_and_predict and returned as a PopulationPredictionResult. Asset resampling keeps every observation window contiguous (it draws over assets, not rows), so — unlike combinatorial cross-validation — multiple-randomised is admissible at the price level too: a price-starting pipeline fits fresh on the asset-subset prices per fold, with no rolling-window violation.
Related
sourcecross_val_predict(pipe::Pipeline, data::Prices_RR, cv::CVER = KFold(); ex = FLoops.ThreadedEx(), id = nothing)Run cross-validated prediction over an entire Pipeline workflow and return a MultiPeriodPredictionResult.
The input is split at its own level — price-level data by the prices-aware split methods (contiguous windows, so stateful preprocessing stays inside the fold), returns-level data as usual — and for each fold the whole workflow is fitted on the training window and predicts on the test window, exactly as fit/predict do for a holdout. This method covers the contiguous, single-path schemes (KFold and the walk-forwards). Combinatorial and asset-resampling schemes have their own methods for a returns-level pipeline (see cross_val_predict(pipe::Pipeline, data::AbstractReturnsResult, cv::CombinatorialCrossValidation) and cross_val_predict(pipe::Pipeline, data::AbstractReturnsResult, cv::MultipleRandomised)); for a price-starting pipeline they are rejected at split by the rolling-window rule.
This is the fold loop that consumes TimeDependent schedules in a pipeline (ADR 0030): when the pipeline is time-dependent, fold i builds a TimeDependentContext — with rd the raw, pre-preprocessing input data, so pipeline-level callables see the fold's data before any step has transformed it — and swaps every schedule for its fold-i value via update_time_dependent_estimator before fit runs. A schedule step may resolve to an estimator (the fold optimises) or a precomputed result (the fold predicts only); injection never sees a schedule. When the pipeline needs_previous_weights, run_folds runs sequentially and threads the previous fold's weights into the context's w_prev and, post-swap, into the optimisation steps via factory.
Arguments
pipe: The pipeline.data: Price- or returns-level input data (Prices_RR).cv::CVER: Cross-validation scheme with contiguous, non-combinatorial folds. Defaults toKFold().ex: FLoops executor controlling parallelism. Defaults toFLoops.ThreadedEx().id: Identifier stored on the result.
Returns
MultiPeriodPredictionResult: One prediction per fold, in fold order.
Related
source