Naive optimisation

PortfolioOptimisers.NaiveOptimisationResultType
struct NaiveOptimisationResult{__T_pr, __T_wb, __T_retcode, __T_w, __T_imsk, __T_fb} <: NonJuMPOptimisationResult

Result type for naive portfolio optimisation estimators.

Fields

  • pr: Prior result.
  • wb: Weight bounds.
  • retcode: Optimisation return code.
  • w: Portfolio weights vector assets × 1.
  • imsk: The Investable Mask the optimisation reduced on: true at every asset whose prior moments were finite. It is nothing when every asset was investable, and that sentinel is what skips both the reduction and the expansion. investable_mask derives it once from the full-universe prior result, and the result carries it, because the reduced prior can no longer yield it.
  • fb: The fallback chain that answered this result: the (estimator, result) pair of every attempt optimise made before this one, in the order they ran, or nothing when the estimator it was asked of answered (see FbChain).

Constructors

NaiveOptimisationResult(;    pr::Option{<:Pr_RR},    wb::Option{<:WeightBounds}, retcode::OptimisationReturnCode, w::Option{<:VecNum},    imsk::Option{<:BitVector} = nothing, fb::Option{<:OptE_Opt_FbChain}) -> NaiveOptimisationResult

Keywords correspond to the struct's fields. The keyword constructor expands w onto the full asset universe through expand_investable_weights, which is the one door _optimise exits through. The positional constructor never expands.

Examples

julia> NaiveOptimisationResult(; pr = nothing, wb = nothing, retcode = OptimisationSuccess(),                               w = [0.5, 0.5], fb = nothing)NaiveOptimisationResult       pr ┼ nothing       wb ┼ nothing  retcode ┼ OptimisationSuccess          │   res ┴ nothing        w ┼ Vector{Float64}: [0.5, 0.5]     imsk ┼ nothing       fb ┴ nothing

Related

source
PortfolioOptimisers.InverseVolatilityType
struct InverseVolatility{__T_pe, __T_wb, __T_sets, __T_wf, __T_fb, __T_sq, __T_brt, __T_strict, __T_cache} <: NaiveOptimisationEstimator

Allocates each asset a weight inversely proportional to its volatility, or to its variance when sq = true.

The volatilities come from the diagonal of the covariance matrix the prior estimator pe returns, so every choice inside pe reaches the result. This is the naive risk parity allocation that HierarchicalRiskParity applies inside a cluster.

Mathematical definition

\[\begin{align} w_i &= \frac{\sigma_i^{-1}}{\sum_{j=1}^N \sigma_j^{-1}} \quad \textrm{when } \texttt{sq = false}\,,\\ w_i &= \frac{\sigma_i^{-2}}{\sum_{j=1}^N \sigma_j^{-2}} \quad \textrm{when } \texttt{sq = true}\,. \end{align}\]

Where:

  • $w_i$: Portfolio weight of asset $i$ before the weight bounds are applied.
  • $\sigma_i^2$: Variance of asset $i$, the $i$-th diagonal entry of the prior covariance matrix.
  • $\sigma_i$: Standard deviation of asset $i$.
  • $N$: Number of assets.

The weight finaliser wf then imposes the resolved weight bounds on $\boldsymbol{w}$, so the returned weights equal the expression above only when no bound binds.

Fields

  • pe: Prior estimator.
  • wb: Weight bounds.
  • sets: Sets used to map estimator values to assets.
  • wf: Weight finaliser.
  • fb: Fallback result or estimator.
  • sq: Whether to use variance instead of volatility in the inverse weighting.
  • brt: Whether to use bootstrap returns.
  • strict: Whether to strictly enforce weight bounds.
  • cache: Optional ReturnsBufferState, the fold context of the online step. It is nothing until partial_fit! writes one, and optimise(opt) with no returns reads it. The returns themselves are carried by the prior, which owns the rows once; this holds every other column of the carrier and the context pinned at the first step. factory carries it unchanged and port_opt_view slices it to the selected assets.

Constructors

InverseVolatility(;    pe::Onl{<:TD{<:PrE_Pr}} = EmpiricalPrior(),    wb::TD_Option{<:WbE_Wb} = WeightBounds(),    sets::TD_Option{<:UniverseSets} = nothing,    wf::TD{<:WeightFinaliser} = IterativeWeightFinaliser(),    fb::TDO_Option{<:OptE_Opt} = nothing,    sq::Bool = false,    brt::Bool = false,    strict::Bool = false,    cache::Option{<:ReturnsBufferState} = nothing) -> InverseVolatility

Keywords correspond to the struct's fields. Fields typed TD, TD_Option or TDO_Option may hold a TimeDependent per-fold schedule instead of a static value: the prior estimator, weight bounds, asset sets, weight finaliser and fallback are problem definition, so a cross-validation fold loop resolves them per fold, and a fold-less optimise runs with each at its static default. sq, brt and strict are execution control and stay static.

Validation

Propagated parameters

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

  • fb: Recursively updated via factory.

View parameters

When port_opt_view is called on this type, the following @vprop-tagged fields are automatically subset to the selected indices:

Examples

julia> InverseVolatility()InverseVolatility      pe ┼ EmpiricalPrior         │           ce ┼ PortfolioOptimisersCovariance         │              │   ce ┼ Covariance         │              │      │    me ┼ SimpleExpectedReturns         │              │      │       │   w ┴ nothing         │              │      │    ce ┼ GeneralCovariance         │              │      │       │   ce ┼ StatsBase.SimpleCovariance: StatsBase.SimpleCovariance(true)         │              │      │       │    w ┴ nothing         │              │      │   alg ┼ FullMoment()         │              │      │     w ┴ nothing         │              │   mp ┼ MatrixProcessing         │              │      │     pdm ┼ Posdef         │              │      │         │      alg ┼ UnionAll: NearestCorrelationMatrix.Newton         │              │      │         │   kwargs ┴ @NamedTuple{}: NamedTuple()         │              │      │      dn ┼ nothing         │              │      │      dt ┼ nothing         │              │      │     alg ┼ nothing         │              │      │   order ┴ NTuple{4, Symbol}: (:pdm, :dn, :dt, :alg)         │           me ┼ SimpleExpectedReturns         │              │   w ┴ nothing         │      horizon ┼ nothing         │   fill_limit ┴ nothing      wb ┼ WeightBounds         │   lb ┼ Float64: 0.0         │   ub ┴ Float64: 1.0    sets ┼ nothing      wf ┼ IterativeWeightFinaliser         │   iter ┴ Int64: 100      fb ┼ nothing      sq ┼ Bool: false     brt ┼ Bool: false  strict ┴ Bool: false

Related

References

  • [5] D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025). Section 12.1.3, footnote 5.
source
PortfolioOptimisers.EqualWeightedType
struct EqualWeighted{__T_wb, __T_sets, __T_wf, __T_fb, __T_strict, __T_cache} <: NaiveOptimisationEstimator

Allocates the same weight to every asset in the universe.

The asset count comes from rd.X, so this optimiser reads no prior and no covariance matrix.

$N$ is the size of the Coverage Universe of the window, not of the full asset universe. An asset is in it when its return is finite and the active mask of the AssetPanel is true at every row of the window, so an asset that is not yet listed, is delisted, or carries a stale finite price during an inactive spell, weights nothing and holds a zero. The result carries that universe as its imsk. An all-dead window throws an IsEmptyError.

Mathematical definition

\[\begin{align} w_i &= \frac{1}{N} \quad \forall i\,. \end{align}\]

Where:

  • $w_i$: Portfolio weight of asset $i$ before the weight bounds are applied.
  • $N$: Number of assets.

The weight finaliser wf then imposes the resolved weight bounds on $\boldsymbol{w}$, so the returned weights equal $1/N$ only when no bound binds.

Fields

  • wb: Weight bounds.
  • sets: Sets used to map estimator values to assets.
  • wf: Weight finaliser.
  • fb: Fallback result or estimator.
  • strict: Whether to strictly enforce weight bounds.
  • cache: Optional ReturnsBufferState, the fold context of the online step. It is nothing until partial_fit! writes one, and optimise(opt) with no returns reads it. This head holds no prior, so it is the bottom of the chain and its state carries the returns themselves, beside every other column of the carrier and the context pinned at the first step. factory carries it unchanged and port_opt_view slices it to the selected assets.

Constructors

EqualWeighted(;    wb::TD_Option{<:WbE_Wb} = WeightBounds(),    sets::TD_Option{<:UniverseSets} = nothing,    wf::TD{<:WeightFinaliser} = IterativeWeightFinaliser(),    fb::TDO_Option{<:OptE_Opt} = nothing,    strict::Bool = false,    cache::Option{<:ReturnsBufferState} = nothing) -> EqualWeighted

Keywords correspond to the struct's fields. Fields typed TD, TD_Option or TDO_Option may hold a TimeDependent per-fold schedule instead of a static value: the weight bounds, asset sets, weight finaliser and fallback are problem definition, so a cross-validation fold loop resolves them per fold, and a fold-less optimise runs with each at its static default. strict is execution control and stays static.

Validation

Propagated parameters

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

  • fb: Recursively updated via factory.

View parameters

When port_opt_view is called on this type, the following @vprop-tagged fields are automatically subset to the selected indices:

Examples

julia> EqualWeighted()EqualWeighted      wb ┼ WeightBounds         │   lb ┼ Float64: 0.0         │   ub ┴ Float64: 1.0    sets ┼ nothing      wf ┼ IterativeWeightFinaliser         │   iter ┴ Int64: 100      fb ┼ nothing  strict ┴ Bool: false

Related

source
PortfolioOptimisers.RandomWeightedType
struct RandomWeighted{__T_alpha, __T_rng, __T_seed, __T_wb, __T_sets, __T_wf, __T_fb, __T_strict, __T_cache} <: NaiveOptimisationEstimator

Draws portfolio weights at random from a Dirichlet distribution with concentration parameter alpha.

Use it for simulation, benchmarking, or stress testing. A scalar alpha draws from the symmetric Dirichlet distribution over $N$ assets; a vector alpha must be one entry per asset.

$N$ is the size of the Coverage Universe of the window, not of the full asset universe. An asset is in it when its return is finite and the active mask of the AssetPanel is true at every row of the window, so an asset that is not yet listed, is delisted, or carries a stale finite price during an inactive spell, weights nothing and holds a zero. A vector alpha is still stated over the full universe, and is sliced to the Coverage Universe. The result carries that universe as its imsk. An all-dead window throws an IsEmptyError.

Mathematical definition

\[\begin{align} \boldsymbol{w} &\sim \mathrm{Dirichlet}(\boldsymbol{\alpha})\,. \end{align}\]

Where:

  • $\boldsymbol{w}$: Portfolio weight vector before the weight bounds are applied. A Dirichlet draw is non-negative and sums to one.
  • $\boldsymbol{\alpha}$: Concentration parameter, one entry per asset. A larger value concentrates the distribution near equal weights.

The weight finaliser wf then imposes the resolved weight bounds on $\boldsymbol{w}$, which by default are absent for this optimiser.

Fields

  • alpha: Dirichlet concentration parameter.
  • rng: Random number generator.
  • seed: Seed for the random number generator.
  • wb: Weight bounds.
  • sets: Sets used to map estimator values to assets.
  • wf: Weight finaliser.
  • fb: Fallback result or estimator.
  • strict: Whether to strictly enforce weight bounds.
  • cache: Optional ReturnsBufferState, the fold context of the online step. It is nothing until partial_fit! writes one, and optimise(opt) with no returns reads it. This head holds no prior, so it is the bottom of the chain and its state carries the returns themselves, beside every other column of the carrier and the context pinned at the first step. factory carries it unchanged and port_opt_view slices it to the selected assets.

Constructors

RandomWeighted(;    alpha::Num_VecNum = 1,    rng::Random.AbstractRNG = Random.default_rng(),    seed::Option{<:Integer} = nothing,    wb::TD_Option{<:WbE_Wb} = nothing,    sets::TD_Option{<:UniverseSets} = nothing,    wf::TD{<:WeightFinaliser} = IterativeWeightFinaliser(),    fb::TDO_Option{<:OptE_Opt} = nothing,    strict::Bool = false,    cache::Option{<:ReturnsBufferState} = nothing) -> RandomWeighted

Keywords correspond to the struct's fields. Fields typed TD, TD_Option or TDO_Option may hold a TimeDependent per-fold schedule instead of a static value: the weight bounds, asset sets, weight finaliser and fallback are problem definition, so a cross-validation fold loop resolves them per fold, and a fold-less optimise runs with each at its static default (nothing for wb, sets and fb). rng, seed and strict are execution control and stay static.

Validation

  • alpha: non-empty, and every element is positive and finite.
  • If wb is a WeightBoundsEstimator: !isnothing(sets).
  • fb schedules: bind !== :nearest.

Propagated parameters

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

  • fb: Recursively updated via factory.

View parameters

When port_opt_view is called on this type, the following @vprop-tagged fields are automatically subset to the selected indices:

Examples

julia> RandomWeighted()RandomWeighted   alpha ┼ Int64: 1     rng ┼ Random.TaskLocalRNG: Random.TaskLocalRNG()    seed ┼ nothing      wb ┼ nothing    sets ┼ nothing      wf ┼ IterativeWeightFinaliser         │   iter ┴ Int64: 100      fb ┼ nothing  strict ┴ Bool: false

Related

source
PortfolioOptimisers.PreviousWeightsType
struct PreviousWeights{__T_w, __T_fb} <: NaiveOptimisationEstimator

Holds the weights it was handed, and solves nothing.

The hold-only head. Its weights are the previous fold's, threaded into w by the fold loop through factory exactly as they reach a TurnoverEstimator, and it returns them verbatim on the full asset universe: no prior, no Coverage Universe, no weight bounds, so a hold is never rewritten. Its one use is as the fallback fb of an optimiser inside a walk-forward, where a failed solve then holds the book instead of writing NaN weights and losing the fold; a weight on an asset that left the panel is still held, and its returns are zeroed as a Held Gap. It is also a primary: PreviousWeights(; w = w) is a walk-forward that holds w on every fold. It refuses nothing at construction and fails at solve time when w is nothing, which is what fold 1 of a walk-forward, and a fold-less optimise with no w, hand it.

needs_previous_weights is true, so an optimiser that carries it as a fallback runs sequentially, and the loop threads the previous weights into it.

Fields

  • w: Weights to hold, or nothing before the fold loop threads any.
  • fb: Fallback result or estimator.

Constructors

PreviousWeights(; w::Option{<:VecNum} = nothing, fb::TDO_Option{<:OptE_Opt} = nothing) -> PreviousWeights

Keywords correspond to the struct's fields. fb may hold a TimeDependent per-fold schedule.

Validation

  • w: all(isfinite, w), else a DomainError is raised.
  • fb schedules: bind !== :nearest.

Propagated parameters

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

  • fb: Recursively updated via factory.

Examples

julia> PreviousWeights()PreviousWeights   w ┼ nothing  fb ┴ nothing

Related

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

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.

The vector method is the one forwarding contract for every vector-valued propagation field: it applies factory to each element and forwards args... and kwargs... unchanged, so a family that admits a vector of estimators, algorithms, or results needs no method of its own. A family that needs more than the forward, such as a concrete element type (concrete_typed_array_if_abstract), defines its own more specific method.

Algorithm

The scalar method:

  1. Return a unchanged, and drop args... and kwargs.... This method is the leaf of the recursion, and it is what makes an untagged type safe to call the verb on.

The vector method:

  1. For each element ai of a, call factory on ai, and forward args... and kwargs... unchanged.
  2. Collect the results into a new vector, in the order of a, and return it.

A @propagatable struct with at least one @fprop- or @wprop-tagged field carries a generated method that dominates the scalar method. That method rebuilds the struct with its keyword constructor, sending each @fprop field through factory_child and each @wprop field through _wprop.

Arguments

  • a: Indicates no object should be constructed, or a vector whose elements are rebuilt one by one.
  • args...: Arbitrary positional arguments (ignored by the scalar method, forwarded by the vector method).
  • kwargs...: Arbitrary keyword arguments (ignored by the scalar method, forwarded by the vector method).

Returns

  • a: The input unchanged.
  • v::Vector: The element-wise rebuilds, for the vector method.

Examples

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

Related

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

Rebuild a continuous optimisation result with an updated fallback record 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. optimise is the one caller, and it hands in the FbChain it walked.

Related

source
factory(
    opt::Union{NonFiniteAllocationOptimisationEstimator, NonFiniteAllocationOptimisationResult},
    _
) -> RandomWeighted{_A, var"#s185", _B, _C, _D, _E, _F, Bool} where {_A, var"#s185"<:AbstractRNG, _B, _C, _D, _E, _F}

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.optimiseMethod
optimise(iv::InverseVolatility{<:Any, <:Any, <:Any, <:Any, Nothing},
         rd::ReturnsResult; dims::Int = 1, kwargs...) -> NaiveOptimisationResult

Run the inverse volatility portfolio optimisation.

Arguments

  • iv: The inverse volatility optimiser to use.
  • rd: The returns result to use. If isa(iv.pe, AbstractPriorResult), rd is not necessary.
  • dims: The dimension along which observations advance in time.
  • kwargs: Additional keyword arguments passed to the optimisation function.

Validation

  • No field in the tree of iv holds an Online. An ArgumentError naming the field is thrown otherwise, through assert_batch_entry: a plain optimise is a batch fit, and a wrapper resolves only at the warm-up of the fold loop's online arm.
source
PortfolioOptimisers.optimiseMethod
optimise(ew::EqualWeighted{<:Any, <:Any, <:Any, Nothing},
         rd::ReturnsResult; dims::Int = 1, kwargs...) -> NaiveOptimisationResult

Run the equal-weighted portfolio optimisation.

Arguments

  • ew: The equal-weighted optimiser to use.
  • rd: The returns result to use. Its returns matrix and its Asset Panel give the Coverage Universe of the window, which is the universe the weights are spread over.
  • dims: Must be 1. A ReturnsResult is always observations × assets, so dims == 2 throws ConflictingArgumentError; build one in this layout with prices_to_returns.
  • kwargs: Additional keyword arguments passed to the optimisation function.
source
PortfolioOptimisers.optimiseMethod
optimise(rw::RandomWeighted{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, Nothing},
         rd::ReturnsResult; dims::Int = 1, kwargs...) -> NaiveOptimisationResult

Run the random-weighted portfolio optimisation.

Arguments

  • rw: The random-weighted optimiser to use.
  • rd: The returns result to use. Its returns matrix and its Asset Panel give the Coverage Universe of the window, which is the universe the draw is taken over.
  • dims: Must be 1. A ReturnsResult is always observations × assets, so dims == 2 throws ConflictingArgumentError; build one in this layout with prices_to_returns.
  • kwargs: Additional keyword arguments passed to the optimisation function.
source
PortfolioOptimisers.factoryMethod
factory(pw::PreviousWeights, w::VecNum) -> PreviousWeights

Thread the previous fold's weights into the hold-only head, and on into its fallback.

Arguments

  • pw: The head.
  • w: The weights the fold loop threads.

Returns

  • PreviousWeights: The head holding w, with fb propagated through factory.

Examples

julia> PortfolioOptimisers.factory(PreviousWeights(), [0.25, 0.75])PreviousWeights   w ┼ Vector{Float64}: [0.25, 0.75]  fb ┴ nothing

Related

source
PortfolioOptimisers._optimiseMethod
_optimise(
    iv::InverseVolatility;
    ...
) -> NaiveOptimisationResult{__T_pr, __T_wb, __T_retcode, __T_w, __T_imsk, Nothing} where {__T_pr, __T_wb, __T_retcode, __T_w, __T_imsk}
_optimise(
    iv::InverseVolatility,
    rd::ReturnsResult;
    dims,
    kwargs...
) -> NaiveOptimisationResult{__T_pr, __T_wb, __T_retcode, __T_w, __T_imsk, Nothing} where {__T_pr, __T_wb, __T_retcode, __T_w, __T_imsk}

Run the inverse volatility portfolio optimisation.

Internal dispatch called by optimise. Computes covariance via the prior estimator, reduces the prior, the optimiser and the returns data to the Investable Mask with investable_reduction, assigns weights inversely proportional to volatility (or variance when iv.sq = true), then applies weight bounds. NaiveOptimisationResult expands the weights back onto the full asset universe.

Related

source
PortfolioOptimisers._optimiseMethod
_optimise(
    ew::EqualWeighted,
    rd::ReturnsResult;
    dims,
    kwargs...
) -> NaiveOptimisationResult{__T_pr, __T_wb, __T_retcode, __T_w, __T_imsk, Nothing} where {__T_pr, __T_wb, __T_retcode, __T_w, __T_imsk}

Run the equal-weighted portfolio optimisation.

Internal dispatch called by optimise. Reduces the optimiser and the returns data to the Coverage Universe of the window with coverage_reduction, assigns equal weights to the assets it keeps, then applies weight bounds. NaiveOptimisationResult expands the weights back onto the full asset universe.

This head fits no prior, so no Prior Result yields an Investable Mask for it. The Coverage Universe is the mask it derives instead: an asset is kept when its return is finite and the active mask of the AssetPanel is true at every row of the window. A stale finite price during an inactive spell weights nothing, and an asset outside the mask holds a zero. The result carries the mask as imsk, so a reader of a walk-forward has the same idiom here as in every other family. An all-dead window throws an IsEmptyError.

Related

source
PortfolioOptimisers._optimiseMethod
_optimise(
    rw::RandomWeighted,
    rd::ReturnsResult;
    dims,
    kwargs...
) -> NaiveOptimisationResult{__T_pr, __T_wb, __T_retcode, __T_w, __T_imsk, Nothing} where {__T_pr, __T_wb, __T_retcode, __T_w, __T_imsk}

Run the random-weighted portfolio optimisation.

Internal dispatch called by optimise. Reduces the optimiser and the returns data to the Coverage Universe of the window with coverage_reduction, draws weights over the assets it keeps from a Dirichlet distribution parameterised by rw.alpha, then applies weight bounds. NaiveOptimisationResult expands the weights back onto the full asset universe.

This head fits no prior, so no Prior Result yields an Investable Mask for it. The Coverage Universe is the mask it derives instead: an asset is kept when its return is finite and the active mask of the AssetPanel is true at every row of the window. A stale finite price during an inactive spell weights nothing, and an asset outside the mask holds a zero. The result carries the mask as imsk, so a reader of a walk-forward has the same idiom here as in every other family. An all-dead window throws an IsEmptyError.

A vector alpha is one concentration per asset of the full universe, because that is the universe the caller states it over. Its length is therefore checked against the full width, before the reduction, and port_opt_view then slices it to the Coverage Universe with the rest of the estimator.

Related

source
PortfolioOptimisers._optimiseFunction
_optimise(
    pw::PreviousWeights;
    ...
) -> NaiveOptimisationResult{ReturnsResult{Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing}, Nothing, __T_retcode, __T_w, Nothing, Nothing} where {__T_retcode, __T_w}
_optimise(
    pw::PreviousWeights,
    rd::ReturnsResult;
    kwargs...
) -> NaiveOptimisationResult{__T_pr, Nothing, __T_retcode, __T_w, Nothing, Nothing} where {__T_pr, __T_retcode, __T_w}

Return the held weights as a result, or a failure when there are none.

Internal dispatch called by optimise. The head reads nothing off rd but its width: the weights are returned verbatim, on the universe they were threaded on, with imsk = nothing and no weight bounds. A head whose w is nothing answers an OptimisationFailure naming the missing weights, so a fallback chain that reaches it walks on, and its weights are a NaN vector of the carrier's width — what every failed solve carries, so a fold reads the failure as it reads any other — or nothing when the carrier has no returns to take a width from.

Algorithm

  1. With w set, give a NaiveOptimisationResult carrying w and an OptimisationSuccess.
  2. With w unset, give one carrying an OptimisationFailure and NaN weights, one per column of rd.X, or nothing when rd.X is nothing.

Related

source

References

[5]
D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025).