Skip to content
18

Stacking

PortfolioOptimisers.BaseStackingOptimisationEstimator Type
julia
abstract type BaseStackingOptimisationEstimator <: NonFiniteAllocationOptimisationEstimator

Abstract supertype for stacking-based portfolio optimisation estimators.

Related Types

source
PortfolioOptimisers.StackingResult Type
julia
struct StackingResult{__T_pr, __T_wb, __T_fees, __T_resi, __T_reso, __T_cv, __T_retcode, __T_w, __T_fb} <: NonJuMPOptimisationResult

Result type for Stacking portfolio optimisation.

Fields

  • pr: Prior result.

  • wb: Weight bounds.

  • fees: Fees estimator or result.

  • resi: Inner optimisation results.

  • reso: Outer optimisation results.

  • cv: Cross-validation estimator.

  • retcode: Optimisation return code.

  • w: Final aggregated portfolio weights.

  • fb: Fallback result or estimator.

Related

source
PortfolioOptimisers.factory Method
julia
factory(a::Union{Nothing, <:AbstractEstimator, <:AbstractAlgorithm,
                 <:AbstractResult}, args...; kwargs...) -> a

No-op factory function for constructing objects with a uniform interface.

Defining methods which dispatch on the first argument allows for a consistent factory interface across different types.

factory and port_opt_view are the two propagation mechanisms in this library. They are duals: factory threads runtime values (prior moments, observation weights, previous portfolio weights) down through a composed struct tree; port_opt_view threads an index selection (a subset of assets or observations) down through the same tree.

Arguments

  • a: Indicates no object should be constructed.

  • args...: Arbitrary positional arguments (ignored).

  • kwargs...: Arbitrary keyword arguments (ignored).

Returns

  • a: The input unchanged.

Examples

julia
julia> factory(nothing, 1, 2; x = 3)

julia> factory(MeanValue())
MeanValue
  w ┴ nothing

Related

source
julia
factory(res::NonFiniteAllocationOptimisationResult, fb::Option{<:OptE_Opt})

Rebuild a continuous optimisation result with an updated fallback optimiser fb.

Every optimisation result carries fb as its last field, so the generic rebuild copies all fields unchanged except the trailing fb. Concrete result types may override this method when rebuilding requires more than swapping fb.

Related

source
julia
factory(
    opt::Union{NonFiniteAllocationOptimisationEstimator, NonFiniteAllocationOptimisationResult},
    _
) -> SubsetResamplingResult

Return opt unchanged.

Default pass-through factory for optimisation estimators and results. Overridden for estimators that carry parameters requiring update at each optimisation step.

Related

source
PortfolioOptimisers.stacking_td_defaults Function
julia
stacking_td_defaults(

) -> @NamedTuple{pe::EmpiricalPrior{PortfolioOptimisersCovariance{Covariance{SimpleExpectedReturns{Nothing}, GeneralCovariance{SimpleCovariance, Nothing}, FullMoment}, MatrixProcessing{Posdef{UnionAll, @NamedTuple{}}, Nothing, Nothing, Nothing, NTuple{4, Symbol}}}, SimpleExpectedReturns{Nothing}, Nothing}, opti::NoDefault, opto::NoDefault, wf::IterativeWeightFinaliser{Int64}}

Return the static defaults of the Stacking fields that may hold a TimeDependent.

Shared by the constructor's test-substitution pass and time_dependent_field_defaults. The optimiser-valued fields opti and opto are required and have no static default, so they are marked NoDefault: a schedule there must carry its own default to be usable outside a fold loop. pe and wf reset to their keyword defaults; fields whose static default is nothing (wb, fees, sets, scale, fb) are omitted.

Related

source
PortfolioOptimisers.Stacking Type
julia
struct Stacking{__T_pe, __T_wb, __T_fees, __T_sets, __T_scale, __T_opti, __T_opto, __T_cv, __T_wf, __T_ex, __T_fb, __T_brt, __T_strict} <: BaseStackingOptimisationEstimator

Stacking portfolio optimiser.

Stacking implements a stacking (model combination) approach to portfolio optimisation. It applies multiple inner optimisers (opti) to the data, then combines their outputs with a single outer optimiser (opto) to produce a final portfolio. Optionally, cross-validation can be used to weight the inner optimisers' contributions.

Fields

  • pe: Prior estimator.

  • wb: Weight bounds estimator or weight bounds.

  • fees: Fees estimator.

  • sets: Sets used to map estimator values to features.

  • scale: Optional scaling vector for inner optimiser weights (length must match opti).

  • opti: Inner optimiser.

  • opto: Outer optimiser.

  • cv: Cross-validation estimator.

  • wf: Weight finaliser.

  • ex: Parallel execution strategy.

  • fb: Fallback result or estimator.

  • brt: Whether to use bootstrap returns.

  • strict: Whether to strictly enforce weight bounds.

Constructors

julia
Stacking(;
    pe::TD{<:PrE_Pr} = EmpiricalPrior(),
    wb::TD_Option{<:WbE_Wb} = nothing,
    fees::TD_Option{<:FeesE_Fees} = nothing,
    sets::TD_Option{<:AssetSets} = nothing,
    scale::TD_Option{<:VecNum} = nothing,
    opti::Union{<:AbstractVector, <:TD_VecOptE_Opt},
    opto::OptE_TD,
    cv::Option{<:OptimisationCrossValidation} = nothing,
    wf::TD{<:WeightFinaliser} = IterativeWeightFinaliser(),
    ex::FLoops.Transducers.Executor = FLoops.ThreadedEx(),
    fb::TDO_Option{<:OptE_Opt} = nothing,
    brt::Bool = false,
    strict::Bool = false
) -> Stacking

Keywords correspond to the struct's fields.

Time-dependent fields

pe, wb, fees, sets, scale, wf, opto and fb may hold a TimeDependent per-fold schedule — no inner fold loop of Stacking consumes them, so the fold loop that reaches the Stacking resolves them; the optimiser positions opto and fb are bind = :outermost only. opti admits schedules at two levels:

  • Element (opti = [static, TimeDependent(…)]): the element is an optimiser position of the inner cross-validation (entered per candidate), so bind = :nearest is legal there — with a mandatory explicit default and cv !== nothing, because the full-sample wi fit always resolves the element fold-lessly to its default (see assert_nearest_optimiser_schedule).

  • Field (opti = TimeDependent([[…], […]]), a per-fold vector of candidate vectors, see TD_VecOptE_Opt): bind = :outermost only. A :nearest field-level schedule is rejected — the inner cross-validation is handed the elements, never the field, and a per-fold candidate vector would change the number and identity of the returns-proxy columns opto sees.

Validation

  • If opti is a vector: !isempty(opti), every element is an OptE_Opt_TD, and any bind = :nearest element schedule has an explicit default and cv !== nothing.

  • If opti is a TimeDependent: bind !== :nearest.

  • If scale is provided and static: all elements are finite, and length(scale) == length(opti) when opti is a vector.

  • opto and fb schedules: bind !== :nearest.

Mathematical definition

Let K inner optimisers produce weight vectors w1,,wK. Stack them as rows of a returns proxy matrix and pass to outer optimiser opto:

w=opto(k=1KskWk).

Where:

  • w: Final stacked portfolio weights.

  • K: Number of inner optimisers.

  • sk: Optional scale factor for inner optimiser k.

  • Wk: Returns proxy matrix weighted by inner-optimiser weights wk.

  • opto: Outer optimiser applied to the aggregated returns proxy.

Propagated parameters

When factory is called on this type, the following @fprop-tagged fields are automatically propagated:

  • fees: Recursively updated via factory.

  • opti: Recursively updated via factory.

  • opto: Recursively updated via factory.

  • fb: Recursively updated via factory.

Related

source
PortfolioOptimisers.needs_previous_weights Method
julia
needs_previous_weights(opt::Stacking) -> Any

Return true if any sub-estimator of opt requires previous portfolio weights (fees, inner optimiser, outer optimiser, or fallback).

source
PortfolioOptimisers.is_time_dependent Method
julia
is_time_dependent(opt::Stacking) -> Any

Return true if any inner optimiser, the outer optimiser, or the fallback carries time-dependent constraints.

source
PortfolioOptimisers.reset_time_dependent_estimator Method
julia
reset_time_dependent_estimator(opt::Stacking) -> Stacking

Replace this meta-optimiser's own time-dependent fields with their static defaults.

Deliberately does not recurse into the wrapped optimisers: a standalone meta solve consumes inner per-fold schedules through its inner cross-validation leg, and its fold-less full-window inner solves reset themselves at their own _optimise seam. Only the meta's own fields (applied to the combined weights, resolved by an outer fold loop when one exists) are inert here. A bind = :nearest schedule in a field the meta hands across its own inner fold loop (see inner_fold_fields) is likewise left in place — resetting it here would replace it with its default before the inner cross-validation ever saw it.

source
PortfolioOptimisers.factory Method
julia
factory(a::Union{Nothing, <:AbstractEstimator, <:AbstractAlgorithm,
                 <:AbstractResult}, args...; kwargs...) -> a

No-op factory function for constructing objects with a uniform interface.

Defining methods which dispatch on the first argument allows for a consistent factory interface across different types.

factory and port_opt_view are the two propagation mechanisms in this library. They are duals: factory threads runtime values (prior moments, observation weights, previous portfolio weights) down through a composed struct tree; port_opt_view threads an index selection (a subset of assets or observations) down through the same tree.

Arguments

  • a: Indicates no object should be constructed.

  • args...: Arbitrary positional arguments (ignored).

  • kwargs...: Arbitrary keyword arguments (ignored).

Returns

  • a: The input unchanged.

Examples

julia
julia> factory(nothing, 1, 2; x = 3)

julia> factory(MeanValue())
MeanValue
  w ┴ nothing

Related

source
julia
factory(
    opt::Union{NonFiniteAllocationOptimisationEstimator, NonFiniteAllocationOptimisationResult},
    _
) -> SubsetResamplingResult

Return opt unchanged.

Default pass-through factory for optimisation estimators and results. Overridden for estimators that carry parameters requiring update at each optimisation step.

Related

source
PortfolioOptimisers.port_opt_view Method
julia
port_opt_view(
    st::Stacking,
    i,
    X::AbstractMatrix{<:Union{var"#s29", var"#s28"} where {var"#s29"<:Number, var"#s28"<:AbstractJuMPScalar}},
    args...
) -> Stacking{_A, _B, _C, _D, _E, _F, _G, _H, _I, var"#s179", _J, Bool, Bool} where {_A, _B, _C, _D, _E, _F, _G, _H, _I, var"#s179"<:Transducers.Executor, _J}

Return a cluster-sliced copy of Stacking for asset index set i and returns matrix X.

source
PortfolioOptimisers.predict_outer_st_estimator_returns Function
julia
predict_outer_st_estimator_returns(
    st::Option{<:Stacking},
    rd::ReturnsResult,
    pr::AbstractPriorResult,
    fees::Option{<:Fees},
    wi::MatNum,
    resi::VecOpt
)

Predict outer portfolio returns for Stacking optimisation. Overload this using st.cv for custom cross-validation prediction.

source
PortfolioOptimisers.assert_special_nco_requirements Method
julia
assert_special_nco_requirements(opt)

Assert that the optimiser meets special requirements for Nested Clustered Optimisation (NCO).

The default implementation does nothing. Overridden for estimators (e.g. Stacking) that have requirements which must be validated before NCO can proceed.

Arguments

  • opt: Optimisation estimator, result, or vector thereof.

Returns

  • nothing.

Related

source
PortfolioOptimisers.optimise Method
julia
optimise(st::Stacking{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any,
                 <:Any, <:Any, Nothing
             }, rd::ReturnsResult;
         dims::Int = 1, branchorder::Symbol = :optimal, str_names::Bool = false,
         save::Bool = true, kwargs...) -> StackingResult

Run the Stacking portfolio optimisation.

Arguments

  • st: The stacking optimiser to use.

  • rd: The returns result to use.

  • dims: The dimension along which observations advance in time.

  • branchorder: Passed to the inner and outer optimisers. The branch order to use for the clusterisation.

  • str_names: Passed to the inner and outer optimisers. Whether to use string names for the assets in the optimisation.

  • save: Passed to the inner and outer optimisers. Whether to save the JuMP model in the optimisation result.

  • kwargs: Additional keyword arguments passed to the optimisation function.

Related

source
PortfolioOptimisers.assert_special_nco_requirements_stacking_opti Function
julia
assert_special_nco_requirements_stacking_opti(
    opti::AbstractVector
)

Validate that a Stacking candidate vector contains no precomputed results.

The candidates in opti are refit on each inner cross-validation fold to produce the columns the outer optimiser stacks. A NonFiniteAllocationOptimisationResult is already solved, so it cannot be refit per fold and has no candidate semantics here; it is rejected at construction rather than silently producing a column that ignores the fold.

Arguments

  • opti::AbstractVector: Candidate optimisers to validate.

Validation

  • !any(x -> isa(x, NonFiniteAllocationOptimisationResult), opti).

Returns

  • nothing.

Related

source