Base Cross Validation

PortfolioOptimisers.PredictionReturnsResultType
struct PredictionReturnsResult{__T_nx, __T_X, __T_nf, __T_F, __T_nb, __T_B, __T_ts, __T_iv, __T_ivpa} <: AbstractReturnsResult

Stores the portfolio returns data associated with a cross-validation prediction. Packages asset returns, factor returns, benchmark returns, timestamps, implied volatilities, and the implied volatility risk premium adjustment for use in prediction result types.

Fields

  • nx: Asset name vector.
  • X: Data matrix observations × assets if the dims keyword does not exist or dims = 1, assets × observations when dims = 2.
  • nf: Factor name vector.
  • F: Data matrix observations × factors if the dims keyword does not exist or dims = 1, factors × observations when dims = 2.
  • nb: Benchmark name vector.
  • B: Benchmark returns.
  • ts: Timestamp vector.
  • iv: Implied volatilities.
  • ivpa: Implied volatility risk premium adjustment.

Constructors

PredictionReturnsResult(;    nx::Option{<:VecStr} = nothing,    X::Option{<:VecNum_VecVecNum} = nothing,    nf::Option{<:VecStr} = nothing,    F::Option{<:MatNum} = nothing,    nb::Option{<:VecStr} = nothing,    B::Option{<:VecNum_VecVecNum} = nothing,    ts::Option{<:VecDate} = nothing,    iv::Option{<:VecNum_VecVecNum} = nothing,    ivpa::Option{<:Num_VecNum} = nothing) -> PredictionReturnsResult

Keywords correspond to the struct's fields.

No feature matrix

This carrier used to transport a per-fold collapsed feature matrix, so that rebuild_returns_result could stack the folds into the outer problem's. It no longer does: the outer collapse is recomputed at the assembly seam from the original, unsliced rd.pnl and the fold's weights, which is the same call the non-cross-validated path makes (see rebuild_returns_result). Nothing is lost — the fold's weights are on PredictionResult's res, and ts here is the fold's slice of the original clock, which is everything the seam needs to reconstruct any fold's view.

Validation

  • nf and F must be consistent (both nothing, or F has length(nf) columns).
  • If X and F provided: row count of F matches length of each X vector.
  • If B and X provided: same type (VecNum/VecVecNum) and matching lengths.
  • If ts provided: !isempty(ts); at least one of X, F is not nothing; lengths of ts match X, F, and B where applicable.
  • If iv is a VecNum: ivpa is scalar or nothing; iv is non-empty, non-negative, and finite; length(iv) == length(X).
  • If iv is a VecVecNum: ivpa is VecNum or nothing; length(iv) == length(X) == length(ivpa); each sub-vector non-empty, non-negative, finite, and same length as corresponding X.

Related

source
PortfolioOptimisers.PredictionResultType
struct PredictionResult{__T_res, __T_rd, __T_hw} <: AbstractPredictionResult

Stores the result of a single cross-validation fold prediction. Pairs an optimisation result with the returns data from the test period.

Fold provenance

The fold's rows are not stored here. They do not need to be: rd.ts is the fold's slice of the original clock — port_opt_view slices it with the very test_idx the fold was built from — so feature_row_indices recovers them by matching timestamps whenever a consumer needs absolute rows. rebuild_returns_result is the one that does, and recovering rather than storing is what keeps that recovery correct on the combinatorial path, where a path's folds are assembled in split order rather than chronologically.

Fields

  • res: Optimisation result from the training fold.
  • rd: The returns result to use.
  • hw: Held-weights record of the fold, or nothing when the fold held its target weights on every observation.

Constructors

PredictionResult(;    res::NonFiniteAllocationOptimisationResult,    rd::PredictionReturnsResult,    hw::Option{<:HeldWeightsResult} = nothing) -> PredictionResult

Keywords correspond to the struct's fields. res and rd are required, because a fold prediction is meaningless without either half. hw defaults to nothing, which is the fold that held its target weights on every observation.

The held-weights record

hw is present only when a Weight Drift ran over the fold, so a reader dispatches on its absence rather than testing for it. It carries the asset returns of the fold, the weights held after the last observation and the form that made them, and weight_path rebuilds the weight path from it.

Related

source
PortfolioOptimisers.MultiPeriodPredictionResultType
struct MultiPeriodPredictionResult{__T_pred, __T_mrd, __T_id, __T_opt} <: AbstractPredictionResult

Stores predictions from multiple cross-validation folds as a single combined result. Concatenates the test-period returns from all folds into an aggregated PredictionReturnsResult.

Per-observation quantities (X, F, B, ts, iv) stack across folds. ivpa is per-asset, not per-observation, and each fold's reconstruct_rd has already collapsed it to one value per synthetic asset using that fold's weights, so it cannot stack — it is reduced to the last fold's value. This matches predict_realised_vols, which reads the last row of the stacked iv: the premium divisor is paired with the implied volatility it divides.

A feature matrix is not among them. It is not carried through the folds at all — rebuild_returns_result recomputes the outer collapse from the original rd.pnl, reaching each fold through pred[f].res.w and pred[f].rd.ts, so pred is what this result has to retain for it.

Fields

  • pred: Collection of fold predictions.
  • mrd: Aggregated multi-period returns result.
  • id: Path or fold identifier.
  • opt: The estimator the online arm of the fold loop threaded, folded through the last training end, or nothing on a batch run. Resume re-enters the loop from it.

An online run's Result also carries the estimator the fold loop threaded, in opt, folded through the last training end last(train_idx[end]). A batch run writes nothing. Resume hands the Result back to the loop over a longer history, and the loop continues from the fold after the last one held; a hand step in the value form, partial_fit(res.opt, rows), deploys the state from the last training end and leaves the Result resumable. The bang form, partial_fit!(res.opt, rows), writes the state's arrays in place — the held timestamps among them — so the Result it came from then names a row no fold ends at, and Resume refuses it; that is the seam's contract for a kept estimator, stated at partial_fit!.

Constructors

MultiPeriodPredictionResult(;    pred::VecPredRes,    id::Any = nothing,    opt::Option{<:AbstractEstimator} = nothing) -> MultiPeriodPredictionResult

Keywords correspond to the struct's fields. pred is required: the constructor stacks the folds' returns data into mrd, and the stack of no folds has no columns, no names, and no clock.

Validation

  • !isempty(pred).

Related

source
PortfolioOptimisers.PopulationPredictionResultType
struct PopulationPredictionResult{__T_pred} <: AbstractPredictionResult

Stores a collection of multi-period prediction results produced by a population-based cross-validation scheme (e.g. MultipleRandomised). Each element of pred represents one random asset-subset path.

Fields

  • pred: Collection of fold predictions.

Constructors

PopulationPredictionResult(;    pred::VecPredRes_MultiPredRes = Vector{PredRes_MultiPredRes}(undef, 0)) -> PopulationPredictionResult

Keywords correspond to the struct's fields. An empty pred is admitted: a population from which every path was dropped is a valid, if empty, answer.

Related

source
StatsAPI.predictMethod
predict(res::NonFiniteAllocationOptimisationResult, rd::ReturnsResult)
predict(res, rd, test_idx, cols = :)
predict(res, rd, test_idxs::VecVecInt, cols = :)

Apply an optimisation result res to returns data rd to produce a PredictionResult or a vector of prediction results.

When test_idx is provided, only the rows (observations) indexed by test_idx (and optionally columns cols) of rd are used for the prediction.

The fee needs no horizon stamped onto it. charge_fees hands in the length of the series it charges, so a fold spreads a one-off cost over its own observations and the whole-sample method spreads it over the whole sample, each without a number stored on the fee. fees.fa names the clock alone: a nothing or FirstObservationFees charges the two fixed terms on the first observation of the series, and an AmortisedFees spreads them evenly over it.

The fa keyword overrides that clock for the series this method builds, and it reaches the result not at all. nothing inherits the clock the fee itself states, which is the library's original behaviour. This is what lets a report charge a fixed fee the way a fund saw it while the optimiser prices the same fee the way its own objective must. override_fee_amortisation is the verb, and the cross-validation schemes state the keyword in a field of the same name.

The Investable Mask, then the Held Gaps

A test window over a point-in-time universe holds two kinds of gap, and predict takes them in order.

  1. The column of a non-investable asset. The fit found it, its weight is 0, and the fold views the window and the weights at res.imsk through investable_fold_view before anything reads them, so the column is never read. The fees are not viewed, because the result carries them on the investable universe already.
  2. A Held Gap, an (observation, asset) pair at which the weight is non-zero and the return is missing. It is what an asset that delists inside the test window makes, and the mask is a per-fit fact that cannot see it. filter_held_gaps zeroes every non-finite entry of the reduced window once, before the series is formed and before a Weight Drift compounds on it, and names the held pairs through strict_diagnostic: a warning by default, an ArgumentError under strict. Nothing is renormalised, so the missing weight sits in cash on that observation.

The filtered window feeds calc_net_returns and held_weights_result alike, so the drift compounds on the matrix the series was formed from. The Held Weights record expands back to the caller's universe through expand_held_weights, because the next fold's turnover reads it. The identity a fold's series satisfies, with the fee taken over the whole weight vector:

returns[t] == sum_i w_i * (isfinite(X[t, i]) ? X[t, i] : 0) - fee

A failed fold holds

A fold whose solve failed carries NaN weights, and its series is NaN: no fee is charged and the identity above holds of NaN. Under a drift the fold still held something, and held_start_weights says what: the previous weights it was handed through w_prev, member by member under a population, so the Held Weights record drifts them through the window and the next fold reads the book the fund carried. With no w_prev — fold 1, or a scheme whose folds are not a timeline — the record is NaN and nothing throws. res.w and rd.X stay NaN either way, so a scorer still sees the failure.

Arguments

  • res::NonFiniteAllocationOptimisationResult: Fitted optimisation result.
  • rd::ReturnsResult: Returns data for the prediction period.
  • test_idx: Observation index or vector of observation indices for the test fold.
  • cols: Column selector. Defaults to : (all assets).

Keyword Arguments

  • fa::Option{<:AbstractFeeAmortisation} = nothing: The clock the series charges the two fixed fee terms on, or nothing to inherit the clock the fee itself states.
  • strict::Bool = false: Whether a Held Gap raises an ArgumentError rather than warning.
  • w_prev::Option{<:VecNum_VecVecNum} = nothing: The previous weights the fold was handed, which a failed fold holds under a drift, or nothing.

Returns

Related

source
PortfolioOptimisers.sort_by_measureFunction
sort_by_measure(ppred::PopulationPredictionResult, r::BaseRM_VecBaseRM; kwargs...)

Sort the successful paths in a PopulationPredictionResult by their expected risk under r. Paths where any fold returned a non-success retcode are excluded.

Arguments

  • ppred::PopulationPredictionResult: Population prediction to sort.
  • r::BaseRM_VecBaseRM: Risk measure used for ranking, or a vector of them. A vector is scalarised by kwargs.sca, defaulting to SumScalariser.
  • kwargs...: Keyword arguments forwarded to expected_risk.

Returns

  • Vector{MultiPeriodPredictionResult}: Sorted vector of successful path predictions.

A non-finite measure is last, whatever rev is

A path whose measure is not finite is placed after every finite one, under both directions of the ranking. A sort that put it first under one direction would make it the answer of a first, and a non-finite number is not a best. The finite members are sorted among themselves, and the non-finite ones keep their own order at the tail.

A mixed vector is refused here, and accepted by its sibling

The ranking direction comes from bigger_is_better, which throws on a vector whose elements disagree on polarity, because the flag decides which tail of the ranking is best and neither answer would be right. quantile_by_measure takes an explicit sign instead, so it admits a mixed vector.

Related

source
PortfolioOptimisers.previous_weightsFunction
previous_weights(pws::Any, prev::Nothing)
previous_weights(pws::Nothing, prev::PredictionResult)
previous_weights(pws::AbstractPreviousWeightsSource, prev::PredictionResult)

Read the weights a fold threads into the fold that follows it.

This is the one seam of the Previous-Weights Source. The first fold of a run has no fold behind it, so it threads nothing whatever the source is. A later fold threads the target weights of the previous fold by default, and the weights that fold held after its last observation when the source asks for them.

prev is the last fold whose weights this seam can thread, not always the fold before: the sequential loops advance it only when threads_weights holds of a fold, so a fold whose solve failed is skipped over by the target read and the fold before it is read instead. The weights this seam gives are therefore finite whenever it gives any.

Algorithm

  1. With no previous fold, give nothing.
  2. With no source, give the target weights of the previous fold, prev.res.w.
  3. With a source, give the held weights of the previous fold, prev.hw.w.

Arguments

  • pws: Previous-weights source of the scheme, or nothing.
  • prev: Prediction result of the last threadable fold, or nothing.

Returns

  • Option{<:VecNum_VecVecNum}: The weights the next fold reads through factory.

Related

source