Skip to content
18

Base optimisation

All optimisers are defined as their whole names, however this can be unwieldy, so we also provide convenience aliases defined in API.

PortfolioOptimisers.AbstractOptimisationEstimator Type
julia
abstract type AbstractOptimisationEstimator <: AbstractEstimator

Abstract supertype for all portfolio optimisation estimators in PortfolioOptimisers.jl.

All optimisers and optimisation components should subtype AbstractOptimisationEstimator to participate in the optimisation dispatch system.

Related

source
PortfolioOptimisers.BaseOptimisationEstimator Type
julia
abstract type BaseOptimisationEstimator <: AbstractOptimisationEstimator

Abstract supertype for base portfolio optimisation estimators.

BaseOptimisationEstimator is the parent for all internal optimiser components that configure the optimisation problem but are not directly invokable as top-level optimisers.

Related

source
PortfolioOptimisers.OptimisationEstimator Type
julia
abstract type OptimisationEstimator <: AbstractOptimisationEstimator

Abstract supertype for portfolio optimisation estimators that produce portfolio weights.

Subtype OptimisationEstimator to implement concrete portfolio optimisers. All optimisers that can be invoked with optimise should subtype this.

Related

source
PortfolioOptimisers.NonFiniteAllocationOptimisationEstimator Type
julia
abstract type NonFiniteAllocationOptimisationEstimator <: OptimisationEstimator

Abstract supertype for portfolio optimisation estimators that produce continuous (non-integer) portfolio weights.

Related

source
PortfolioOptimisers.OptimisationAlgorithm Type
julia
abstract type OptimisationAlgorithm <: AbstractAlgorithm

Abstract supertype for optimisation algorithms used by portfolio optimisers.

Related

source
PortfolioOptimisers.OptimisationResult Type
julia
abstract type OptimisationResult <: AbstractResult

Abstract supertype for portfolio optimisation result types.

All concrete optimisation result types should subtype OptimisationResult.

Related

source
PortfolioOptimisers.NonFiniteAllocationOptimisationResult Type
julia
abstract type NonFiniteAllocationOptimisationResult <: OptimisationResult

Abstract supertype for continuous (non-integer allocation) optimisation results.

Related

source
PortfolioOptimisers.OptimisationReturnCode Type
julia
abstract type OptimisationReturnCode <: AbstractResult

Abstract supertype for optimisation return codes.

Concrete subtypes indicate whether an optimisation succeeded or failed.

Related

source
PortfolioOptimisers.VecOptRetCode Type
julia
const VecOptRetCode = AbstractVector{<:OptimisationReturnCode}

Alias for a vector of optimisation return codes.

Related

source
PortfolioOptimisers.OptRetCode_VecOptRetCode Type
julia
const OptRetCode_VecOptRetCode = Union{<:OptimisationReturnCode, <:VecOptRetCode}

Alias for either a single optimisation return code or a vector of return codes.

Related

source
PortfolioOptimisers.OptimisationModelResult Type
julia
abstract type OptimisationModelResult <: AbstractResult

Abstract supertype for intermediate optimisation model results.

Related

source
PortfolioOptimisers.OptimisationSuccess Type
julia
struct OptimisationSuccess{__T_res} <: OptimisationReturnCode

Indicates that a portfolio optimisation completed successfully.

Fields

  • res: Optional result or message from the solver.

Constructors

julia
OptimisationSuccess(; res = nothing) -> OptimisationSuccess

Keywords correspond to the struct's fields.

Examples

julia
julia> OptimisationSuccess()
OptimisationSuccess
  res ┴ nothing

Related

source
PortfolioOptimisers.OptimisationFailure Type
julia
struct OptimisationFailure{__T_res} <: OptimisationReturnCode

Indicates that a portfolio optimisation failed.

Fields

  • res: Optional result or message from the solver.

Constructors

julia
OptimisationFailure(; res = nothing) -> OptimisationFailure

Keywords correspond to the struct's fields.

Examples

julia
julia> OptimisationFailure()
OptimisationFailure
  res ┴ nothing

Related

source
PortfolioOptimisers.JuMPWeightFinaliserFormulation Type
julia
abstract type JuMPWeightFinaliserFormulation <: AbstractAlgorithm

Abstract supertype for JuMP-based weight finaliser formulations.

Defines the interface for norm types used when adjusting portfolio weights to satisfy bounds via a JuMP model.

Related

source
PortfolioOptimisers.RelativeErrorWeightFinaliser Type
julia
struct RelativeErrorWeightFinaliser <: JuMPWeightFinaliserFormulation

Minimises the L1 norm of relative weight deviations when enforcing weight bounds.

Constructors

julia
RelativeErrorWeightFinaliser() -> RelativeErrorWeightFinaliser

Examples

julia
julia> RelativeErrorWeightFinaliser()
RelativeErrorWeightFinaliser()

Related

source
PortfolioOptimisers.SquaredRelativeErrorWeightFinaliser Type
julia
struct SquaredRelativeErrorWeightFinaliser <: JuMPWeightFinaliserFormulation

Minimises the L2 norm (squared) of relative weight deviations when enforcing weight bounds.

Constructors

julia
SquaredRelativeErrorWeightFinaliser() -> SquaredRelativeErrorWeightFinaliser

Examples

julia
julia> SquaredRelativeErrorWeightFinaliser()
SquaredRelativeErrorWeightFinaliser()

Related

source
PortfolioOptimisers.AbsoluteErrorWeightFinaliser Type
julia
struct AbsoluteErrorWeightFinaliser <: JuMPWeightFinaliserFormulation

Minimises the L1 norm of absolute weight deviations when enforcing weight bounds.

Constructors

julia
AbsoluteErrorWeightFinaliser() -> AbsoluteErrorWeightFinaliser

Examples

julia
julia> AbsoluteErrorWeightFinaliser()
AbsoluteErrorWeightFinaliser()

Related

source
PortfolioOptimisers.SquaredAbsoluteErrorWeightFinaliser Type
julia
struct SquaredAbsoluteErrorWeightFinaliser <: JuMPWeightFinaliserFormulation

Minimises the L2 norm (squared) of absolute weight deviations when enforcing weight bounds.

Constructors

julia
SquaredAbsoluteErrorWeightFinaliser() -> SquaredAbsoluteErrorWeightFinaliser

Examples

julia
julia> SquaredAbsoluteErrorWeightFinaliser()
SquaredAbsoluteErrorWeightFinaliser()

Related

source
PortfolioOptimisers.WeightFinaliser Type
julia
abstract type WeightFinaliser <: AbstractAlgorithm

Abstract supertype for weight finaliser strategies.

A WeightFinaliser enforces weight bounds after the optimisation has produced unconstrained weights.

Related

source
PortfolioOptimisers.IterativeWeightFinaliser Type
julia
struct IterativeWeightFinaliser{__T_iter} <: WeightFinaliser

Iteratively projects weights into the feasible region defined by weight bounds.

IterativeWeightFinaliser repeatedly clips and redistributes portfolio weights until they satisfy the given lower and upper bounds, or the maximum number of iterations iter is reached.

Fields

  • iter: Maximum number of iterations.

Constructors

julia
IterativeWeightFinaliser(;
    iter::Integer = 100
) -> IterativeWeightFinaliser

Keywords correspond to the struct's fields.

Validation

  • iter > 0.

Examples

julia
julia> IterativeWeightFinaliser()
IterativeWeightFinaliser
  iter ┴ Int64: 100

Related

source
PortfolioOptimisers.JuMPWeightFinaliser Type
julia
struct JuMPWeightFinaliser{__T_slv, __T_sc, __T_so, __T_alg} <: WeightFinaliser

Uses a JuMP optimisation model to enforce weight bounds.

JuMPWeightFinaliser solves a small optimisation problem to find the closest feasible weights (in the sense of the chosen error formulation) that satisfy the given bounds. Falls back to IterativeWeightFinaliser if the JuMP model fails.

Fields

  • slv: Solver or vector of solvers.

  • sc: Constraint scale factor.

  • so: Objective scale factor.

  • alg: Weight finaliser error formulation algorithm.

Constructors

julia
JuMPWeightFinaliser(;
    slv::Slv_VecSlv,
    sc::Number = 1.0,
    so::Number = 1.0,
    alg::JuMPWeightFinaliserFormulation = RelativeErrorWeightFinaliser()
) -> JuMPWeightFinaliser

Keywords correspond to the struct's fields.

Validation

  • If slv is a VecSlv: !isempty(slv).

  • sc > 0, so > 0.

Examples

julia
julia> JuMPWeightFinaliser(; slv = Solver(; solver = nothing))
JuMPWeightFinaliser
  slv ┼ Solver
      │          name ┼ String: ""
      │        solver ┼ nothing
      │      settings ┼ nothing
      │     check_sol ┼ @NamedTuple{}: NamedTuple()
      │   add_bridges ┴ Bool: true
   sc ┼ Float64: 1.0
   so ┼ Float64: 1.0
  alg ┴ RelativeErrorWeightFinaliser()

Related

source
PortfolioOptimisers._optimise Function
julia
_optimise(opt, rd, args...; dims, str_names, save, kwargs...)

Internal dispatch function for portfolio optimisation.

Called by optimise to perform the actual optimisation. Each optimisation estimator type implements its own overload. Returns the estimator-specific result type.

Arguments

  • opt: Optimisation estimator (e.g. MeanRisk, RiskBudgeting, etc.).

  • rd::ReturnsResult: Returns data.

  • dims::Int: Observation dimension.

  • str_names::Bool: Whether to use string names in the JuMP model.

  • save::Bool: Whether to save the JuMP model in the result.

  • kwargs...: Additional keyword arguments.

Returns

  • Estimator-specific optimisation result.

Related

source
PortfolioOptimisers.optimise Method
julia
optimise(opt::OptimisationEstimator, args...; kwargs...) -> OptimisationResult
optimise(opt::OptimisationResult, args...; kwargs...) -> OptimisationResult

Run portfolio optimisation using the given estimator opt and return an OptimisationResult.

If opt returns an OptimisationFailure, the fallback estimator is tried automatically until either a successful result is obtained or all fallbacks are exhausted.

Passing an OptimisationResult directly returns it unchanged (pass-through method).

Arguments

  • opt: Optimisation estimator (e.g. a JuMPOptimisationEstimator subtype).

  • args: Additional positional arguments (ignored).

  • kwargs: Additional keyword arguments (ignored).

Returns

Related

source
PortfolioOptimisers.optimise Method
julia
optimise(
    opt::OptimisationEstimator,
    args...;
    kwargs...
) -> Union{NaiveOptimisationResult{__T_pr, __T_wb, OptimisationFailure{String}, __T_w, Nothing} where {__T_pr, __T_wb, __T_w}, NaiveOptimisationResult{__T_pr, __T_wb, OptimisationSuccess{Nothing}, __T_w, Nothing} where {__T_pr, __T_wb, __T_w}}

High level optimisation function that wraps around estimator-specific optimisation functions. This takes care of fallback methods if the primary optimisation fails. It returns the first successful optimisation result but stores all fallback results in the fb field of the result.

This is a fold-less entry point, so time-dependent schedules are inert here: the estimator is reset to its fold-less values (see reset_time_dependent_estimator) before the solve — in particular a scheduled fallback resets to its default, or to nothing (no fallback) when it has none, before the fallback chain is walked. Inside a fold loop this reset is a no-op, because the loop resolves every schedule before optimising.

Arguments

  • opt::OptimisationEstimator: The optimisation estimator to use.

  • args: Additional positional arguments passed to the optimisation function.

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

source
PortfolioOptimisers.calc_net_returns Function
julia
calc_net_returns(res::OptimisationResult, X::MatNum, fees = nothing)
calc_net_returns(res::OptimisationResult, pr::Pr_RR, fees = nothing)

Compute net returns for a OptimisationResult.

fees takes precedence over res.fees if both are provided. Delegates to calc_net_returns(w, X, fees).

When pr::Pr_RR is passed, extracts X from pr.X and delegates.

Related

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.assert_special_nco_requirements Method
julia
assert_special_nco_requirements(
    opt::AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:(Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}), var"#s2386"<:Union{TimeDependent{<:AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}}}
)

Assert special NCO requirements for each element of a vector of optimisation estimators or results.

Related

source
PortfolioOptimisers.needs_previous_weights Method
julia
needs_previous_weights(
    _::Union{Nothing, AbstractAlgorithm, AbstractEstimator, AbstractResult}
) -> Bool

Return false.

nothing never requires previous portfolio weights.

Related

source
PortfolioOptimisers.needs_previous_weights Method
julia
needs_previous_weights(opt)

Return true if the optimiser requires the previous period's weights.

The default returns false. Overridden for optimisers that contain turnover constraints, tracking error constraints, or other time-dependent components that require the previous optimisation's weights.

Arguments

  • opt: Optimisation estimator, result, risk measure, fee structure, or vector thereof.

Returns

  • Bool: true if previous weights are needed.

Related

source
PortfolioOptimisers.needs_previous_weights Method
julia
needs_previous_weights(
    opt::AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:(Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}), var"#s2386"<:Union{TimeDependent{<:AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}}}
) -> Any

Return true if any element of the vector of optimisation estimators or results requires previous portfolio weights.

Related

source
PortfolioOptimisers.needs_previous_weights Method
julia
needs_previous_weights(td::TimeDependent) -> Bool

Return true if a time-dependent constraint requires the previous optimisation's weights.

true for a PreviousWeightsFunction value; for vector values, delegates to needs_previous_weights on entries that support it (turnover, fees, tracking), descending into per-fold vector entries. Bare callables contribute false — their output cannot be inspected.

Related

source
PortfolioOptimisers.needs_previous_weights Method
julia
needs_previous_weights(
    opt::AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:(Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}), var"#s2386"<:Union{TimeDependent{<:AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}}}
) -> Any

Return true if any element of the vector of optimisation estimators or results requires previous portfolio weights.

Related

source
PortfolioOptimisers.TimeDependent Type
julia
struct TimeDependent{T1, T2} <: AbstractEstimator

Time-dependent constraint: an optimiser input whose value changes across the folds of a cross-validation scheme.

A TimeDependent is stored directly in the optimiser field it varies — e.g. JuMPOptimiser(; lt = TimeDependent([...])) — so the field's position names the target and a field holds either a static value or a per-fold schedule, never both. It is recognised at top-level optimiser fields only, never nested inside another input (e.g. inside a Fees or a risk measure).

val is either a vector of per-fold values — entry i is the complete field value for fold i of the consuming scheme's split enumeration — or a callable evaluated per fold: a bare function f(ctx::TimeDependentContext) (optionally wrapped in PreviousWeightsFunction) or a TimeDependentCallable functor struct.

For a field that itself accepts a vector of constraints statically, a per-fold entry is that whole vector, so a schedule of per-fold constraint vectors is a vector of vectors — TimeDependent([[c₁ᵃ, c₁ᵇ], [c₂ᵃ, c₂ᵇ], …]), entry i being fold i's complete constraint vector. There is no separate "vector of TimeDependent" facility and none is needed: TimeDependent is recognised only at a top-level field, so to vary individual constraints within a vector, build the fold's vector in a callable — TimeDependent(ctx -> [dynamic(ctx), static]) — which keeps the shared static parts in one place.

The machinery imposes no ordering of its own: fold i is whatever split(cv, rd) enumerates i-th, which is chronological for walk-forward and (unshuffled) KFold schemes. For schemes whose enumeration is not a timeline (combinatorial splits, randomised paths) it is the user's responsibility to key entries off the fold's indices — a callable sees its own fold's windows via ctx.train_idx[ctx.i]/ctx.test_idx[ctx.i] and may derive any ordering from them.

A time-dependent constraint participates only where folds exist and is inert everywhere else — a fold-less optimise replaces it with the field's fold-less value (see reset_time_dependent_estimator). Vector entries must have length equal to the number of folds of the consuming cross-validation scheme, validated at split time. Entries may be nothing, giving the field nothing for that fold.

The fold-less value is the field's static default, unless default overrides it. A field with no static default — the required, optimiser-valued fields — has nothing to reset to, so a schedule there must supply default; a fold-less solve of one that does not throws a TimeDependentDefaultError.

A vector whose entries are all optimisers or precomputed results (OptE_Opt) is stored as a Vector{OptE_Opt}, so a mixed schedule — fold i optimising or predicting depending on what entry i is — is admissible in an optimiser-valued field on its element type alone (see TD_OptE_Opt) rather than falling out to a Vector{Any} the field cannot accept.

Schedules do not nest: neither val, nor a vector entry of val, nor default may be a TimeDependent. Entry i is fold i's complete field value, and the fold-less value is by definition outside every fold loop, so nesting has no meaning. An estimator swapped in by a schedule may itself carry schedules — those resolve against the same fold context after the swap — but they live in its fields, not inside this wrapper.

Recovering which entry a fold ran needs no stored provenance, because a vector schedule is keyed by the fold index and nothing else. Entry i runs at fold i of the consuming scheme's split enumeration (time_dependent_value indexes val[ctx.i]), so val[i] is fold i's value — the same index you keyed the schedule by. Under the time-ordered schemes (walk-forward, unshuffled KFold, Pipeline) fold i is also the i-th entry of the returned MultiPeriodPredictionResult; under schemes that regroup for reporting (MultipleRandomised sorts by test index, combinatorial recombines each split's test groups into paths) the prediction order no longer tracks the fold order, so re-run split(cv, rd) and read the fold→path map off its path_ids — it is keyed by the very enumeration index the schedule was, so entry k still governs enumeration fold k. A callable schedule computes its value rather than selecting an entry, so there is no index to recover: what it returned is knowable only by re-running it on the fold's TimeDependentContext, or by having it record its own choice. Recording is a logging concern the caller owns, and the TimeDependentCallable struct interface is its natural home — a functor can stash the regime it picked per fold in a field of its own.

Fields

  • val: Vector of per-fold values (in the consuming scheme's split enumeration order), or a callable of the fold's TimeDependentContext: a bare function (optionally wrapped in PreviousWeightsFunction) or a TimeDependentCallable functor struct.

  • bind: Which fold loop consumes the schedule: :outermost (default) binds it to the outermost fold loop processing the estimator tree; :nearest binds it to the nearest enclosing fold loop — inside a meta-optimiser's inner estimators that is the meta's own cross-validation leg, which then consumes the schedule even when the meta is backtested under an outer fold loop.

  • default: Value the field takes outside every fold loop, overriding the host's static default (see time_dependent_field_defaults). NoDefault (the default) defers to the host's static default; a field that has none requires this to be set.

Constructors

julia
TimeDependent(val, bind::Symbol = :outermost; default = NoDefault())
TimeDependent(; val, bind::Symbol = :outermost, default = NoDefault())

Validation

  • If val is a vector: !isempty(val), and no entry is a TimeDependent.

  • val is not a TimeDependent.

  • default is not a TimeDependent.

  • bind in (:outermost, :nearest).

Examples

julia
julia> TimeDependent([Fees(; l = 0.001), Fees(; l = 0.002)])
TimeDependent
      val ┼ 2-element Vector{Fees}
          │ Fees 
          │ Fees 
     bind ┼ Symbol: :outermost
  default ┴ NoDefault()

Related

source
PortfolioOptimisers.factory Method
julia
factory(td::TimeDependent, args...) -> TimeDependent

Apply factory through a TimeDependent schedule: to each vector entry and to the default, rebuilding the schedule.

A schedule can survive a fold loop's resolution pass (a bind = :nearest element left for a meta's inner cross-validation), so the factory pass that follows resolution must see through it. Callable forms pass through unchanged — their per-fold values do not exist yet, and a callable receives the fold's context (including w_prev) when it runs.

Related

source
PortfolioOptimisers.TimeDependentContext Type
julia
struct TimeDependentContext{T1, T2, T3, T4, T5, T6, T7} <: AbstractResult

Per-fold context handed to time-dependent constraints when they are resolved.

Carries the fold's position in the consuming scheme's split enumeration and the data needed for a callable entry to compute its value. i indexes train_idx/test_idx, so ctx.train_idx[ctx.i]/ctx.test_idx[ctx.i] are always the fold's own windows; no ordering beyond the scheme's enumeration is implied. rd is the fold loop's (possibly asset-viewed) input data, so callables see the current universe and timestamps: the returns-level data at the optimiser fold loops, or the raw, pre-preprocessing price- or returns-level input at the Pipeline fold loop — a pipeline-level callable sees the fold's data before any pipeline step has transformed it. w_prev is populated only when the fold loop runs sequentially and a previous fold exists; path_id only under multi-path schemes.

Fields

  • i: Index of the fold within the scheme's split enumeration (1-based); indexes train_idx/test_idx.

  • n: Number of folds within the path.

  • rd: The fold loop's (possibly asset-viewed) returns data.

  • train_idx: Per-path training index vectors.

  • test_idx: Per-path test index vectors.

  • w_prev: Previous fold's portfolio weights, when threaded; nothing otherwise.

  • path_id: Path identifier under multi-path schemes; nothing otherwise.

Constructors

julia
TimeDependentContext(;
    i::Integer, n::Integer, rd::Prices_RR, train_idx, test_idx,
    w_prev::Option{<:VecNum} = nothing, path_id::Option{<:Integer} = nothing
)

Related

source
PortfolioOptimisers.TimeDependentCallable Type
julia
abstract type TimeDependentCallable <: AbstractAlgorithm

Abstract supertype for callable structs used as time-dependent constraint values.

A subtype is a data-carrying alternative to a bare function inside a TimeDependent: it must implement a functor (x::MySubtype)(ctx::TimeDependentContext) returning the fold's field value. Because it is a struct, it participates in a trait a bare function cannot: define needs_previous_weights(::MySubtype) = true to declare a previous-weights requirement directly (the default is false), instead of wrapping in PreviousWeightsFunction.

Being a struct also makes it the natural home for recording what a callable schedule chose: a bare ctx -> … selects nothing by index, so its per-fold decision is not otherwise recoverable (see the provenance note on TimeDependent). A functor can carry a mutable field — e.g. a vector it writes at ctx.i — and log the fold's resolved value as a side effect of computing it.

Related

source
PortfolioOptimisers.TimeDependentOptimiserCallable Type
julia
abstract type TimeDependentOptimiserCallable <: TimeDependentCallable

Abstract supertype for callable structs whose per-fold value is an optimiser.

A subtype implements a functor (x::MySubtype)(ctx::TimeDependentContext) returning the fold's optimiser (an OptE_Opt), so a TimeDependent holding it is admissible wherever an optimiser-valued field accepts a schedule (see TD_OptE_Opt). Declaring the functor's output kind in the type is what makes the schedule statically admissible: a bare ctx -> optimiser is admitted as a Base.Callable and checked only when the fold loop swaps its value in.

Related

source
PortfolioOptimisers.PreviousWeightsFunction Type
julia
struct PreviousWeightsFunction{T} <: AbstractAlgorithm

Wrapper marking a callable time-dependent constraint entry as requiring the previous optimisation's weights.

A bare callable inside a TimeDependent cannot be inspected for previous-weight requirements, so it contributes false to needs_previous_weights and its context's w_prev is only populated when something else makes the fold loop sequential. Wrapping the callable in PreviousWeightsFunction declares the requirement as data: it contributes true to needs_previous_weights, forcing sequential fold execution and a populated w_prev in the TimeDependentContext.

Fields

  • f: Callable evaluated per fold as f(ctx::TimeDependentContext), returning the fold's field value.

Constructors

julia
PreviousWeightsFunction(; f)

Related

source
PortfolioOptimisers.NoDefault Type
julia
struct NoDefault <: AbstractAlgorithm

Marker for "no default here", used in the two places a fold-less value may be missing.

Related

source
PortfolioOptimisers.TimeDependentDefaultError Type
julia
struct TimeDependentDefaultError{__T_msg} <: PortfolioOptimisersError

Exception thrown when a fold-less solve reaches a TimeDependent schedule that has no value to fall back to: the field has no static default and the schedule supplies no default.

A schedule is defined only over the folds of a cross-validation scheme. Fields with a static default reset to it silently; a required field (the optimiser-valued ones) has nothing to reset to, so the schedule must state the value a fold-less solve should use, via TimeDependent(val; default = x).

Fields

  • msg: Error message describing the condition that triggered the exception.

Constructors

julia
TimeDependentDefaultError(msg)

Related

source
PortfolioOptimisers.TD_Option Type
julia
const TD_Option{X} = Union{Nothing, <:TimeDependent, X}

Alias for an optimiser field that accepts nothing, a static value of type X, or a per-fold TimeDependent schedule.

The set of fields whose constructor signatures use this alias is the single source of truth for which optimiser inputs may vary over folds.

Related

source
PortfolioOptimisers.TD Type
julia
const TD{X} = Union{<:TimeDependent, X}

Alias for a required optimiser field that accepts a static value of type X or a per-fold TimeDependent schedule, but not nothing.

The problem-definition fields that always carry a value — the prior estimator, the returns model, the scalariser, the clustering estimator, the weight finaliser — are time-dependent through this alias rather than TD_Option, so nothing stays inadmissible where it was never a legal static value. Such a field still has a static default, so a schedule in one resets to that default on a fold-less solve, unlike the optimiser-valued fields (see TD_OptE_Opt).

Related

source
PortfolioOptimisers.TD_OptE_Opt Type
julia
const TD_OptE_Opt = Union{TimeDependent{<:AbstractVector{<:OptE_Opt}},
                          TimeDependent{<:TimeDependentOptimiserCallable},
                          TimeDependent{<:PreviousWeightsFunction},
                          TimeDependent{<:Base.Callable}}

The TimeDependent forms admissible in an optimiser-valued field — where the scheduled thing is the optimiser itself, not one of its inputs.

Two of the four are statically checked: a vector schedule whose entries are all OptE_Opt (an optimiser or a precomputed result — a mixed schedule is allowed, fold i optimising or predicting depending on what entry i is), and a TimeDependentOptimiserCallable, which declares its output kind in its type. The other two — a bare ctx -> optimiser and a PreviousWeightsFunction wrapping one — cannot be checked before they run, so their output is checked when the fold loop swaps it into the field, by the host's own keyword constructor.

Because an optimiser-valued field is required, a schedule in one has no static default to reset to on a fold-less solve and must supply default (see NoDefault, TimeDependentDefaultError).

Related

source
PortfolioOptimisers.TDO_Option Type
julia
const TDO_Option{X} = Union{Nothing, <:TDO_OptE_Opt, X}

Alias for an optional optimiser-valued field (e.g. a fallback) that accepts nothing, a static value of type X, or a per-fold schedule of optimisers whose entries may be nothing (see TDO_OptE_Opt).

A required optimiser-valued field spells its union out — Union{<:X, <:TD_OptE_Opt} — since nothing is not one of its values.

Related

source
PortfolioOptimisers.OptE_TD Type
julia
const OptE_TD = Union{<:NonFiniteAllocationOptimisationEstimator, <:TD_OptE_Opt}

Alias for an optimisation estimator, or a TimeDependent schedule standing in its place.

This is the entry-point type of the cross-validation fold loops that fit: a schedule handed straight to cross_val_predict is the optimiser, and fold i runs entry i. Precomputed results are excluded because a bare result takes the predict-only path, which has no fold loop to resolve a schedule against — but a schedule whose entries are results is admissible here, and each such entry takes the predict-only path per fold (see OptE_Opt_TD).

Related

source
PortfolioOptimisers.OptE_Opt_TD Type
julia
const OptE_Opt_TD = Union{<:OptE_Opt, <:TD_OptE_Opt}

Alias for an optimisation estimator or a precomputed result, or a TimeDependent schedule standing in their place.

The entry-point type of the fold loops that accept a precomputed result as well as an estimator. A schedule's entries are OptE_Opt, so a mixed schedule is admissible: fold i optimises when entry i is an estimator and predicts when it is a result, which the single-fold fit_and_predict methods already distinguish by dispatch.

Related

source
PortfolioOptimisers.VecOptE_Opt_TD Type
julia
const VecOptE_Opt_TD = AbstractVector{<:OptE_Opt_TD}

Alias for a vector of optimisation estimators or results in which individual elements may be TimeDependent schedules.

This is the element-level admission of schedules, needed where a vector-valued field's elements are themselves optimiser positions consumed by a fold loop one at a time — Stacking.opti, whose inner cross-validation is entered per candidate. It is a superset of VecOptE_Opt, so every method taking it continues to accept plain vectors.

Related

source
PortfolioOptimisers.factory Method
julia
factory(
    opt::AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:(Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}), var"#s2386"<:Union{TimeDependent{<:AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}}},
    args...
) -> Any

Apply factory element-wise to a vector of optimisation estimators or results, elements that are TimeDependent schedules included.

Related

source
PortfolioOptimisers.TD_VecOptE_Opt Type
julia
const TD_VecOptE_Opt = Union{TimeDependent{<:AbstractVector{<:VecOptE_Opt_TD}},
                             TimeDependent{<:TimeDependentOptimiserCallable},
                             TimeDependent{<:PreviousWeightsFunction},
                             TimeDependent{<:Base.Callable}}

The TimeDependent forms admissible in a vector-of-optimisers field (Stacking.opti): a vector schedule whose entries are per-fold optimiser vectors, or a callable returning the fold's vector.

Entry i is fold i's complete vector of candidates, so a field-level schedule varies the whole candidate set per fold; an entry's own elements may in turn be schedules (a VecOptE_Opt_TD), which the consuming host's inner fold loop resolves as usual. Only bind = :outermost is admissible at the field level — see the host's constructor for why.

Related

source
PortfolioOptimisers.TDO_OptE_Opt Type
julia
const TDO_OptE_Opt = Union{<:TD_OptE_Opt,
                           <:TimeDependent{<:AbstractVector{<:Option{<:OptE_Opt}}}}

The TimeDependent forms admissible in an optional optimiser-valued field (a fallback): every TD_OptE_Opt form, plus a vector schedule whose entries may be nothing.

nothing was always a legal static value of an optional field, and the TimeDependent contract says a vector entry may be nothing, giving the field nothing for that fold — so an optional optimiser field admits TimeDependent([mr, nothing]), a fallback switched off on some folds. A required optimiser position (the optimiser itself) never admits nothing, statically or per fold, so it stays on the strict TD_OptE_Opt bound.

Related

source
PortfolioOptimisers.assert_nearest_optimiser_schedule Function
julia
assert_nearest_optimiser_schedule(x, field::Symbol, cv, host::Symbol)

Validate a bind = :nearest TimeDependent schedule in an optimiser-valued position that a host's inner cross-validation does consume.

Two construction-time requirements, both consequences of the position's double consumer: the inner cross-validation leg resolves the schedule per fold, while the full-sample leg (the meta's wi fit, or the per-cluster optimise) always resolves it fold-lessly to its default.

  • An explicit default is required — without one, every solve would throw a TimeDependentDefaultError when the full-sample leg reaches the schedule, so the error is moved to construction. This deliberately departs from the rule that a defaultless schedule is legal at construction, for this position only.

  • cv !== nothing is required — without an inner cross-validation there is no inner fold loop, so the schedule could only ever be its default: silently inert.

No-op for anything that is not a bind = :nearest TimeDependent.

Related

source
PortfolioOptimisers.inner_fold_fields Function
julia
inner_fold_fields(opt)

Field names of opt that the host hands across a fold loop it opens itself, so the loop that merely reaches the host is never the nearest one for them.

The default is the empty tuple: an ordinary host opens no inner fold loop, so the loop reaching it is both outermost and nearest for every field. A meta-optimiser whose inner cross-validation consumes a field directly declares that field here (e.g. NestedClustered's opti, entered per cluster as cross_val_predict(opti, …; cols = cl)), and every generic pass — time_dependent_fields, and through it update, reset and the fold-count assertion — then leaves a bind = :nearest schedule in that field for the host's own inner loop. Without the reset leg of this rule, the fold-less reset at the top of _optimise would replace a :nearest optimiser schedule with its default before the inner cross-validation ever saw it.

Related

source
PortfolioOptimisers.time_dependent_value Function
julia
time_dependent_value(
    td::TimeDependent,
    ctx::TimeDependentContext
) -> Any

Resolve a time-dependent constraint to its value for the fold described by ctx.

Vector values index entry ctx.i; callables are invoked with ctx.

Related

source
PortfolioOptimisers.time_dependent_candidate_fields Function
julia
time_dependent_candidate_fields(opt)

Field names of opt whose type admits a TimeDependent value — the candidate set time_dependent_fields narrows by value.

Whether a field can hold a schedule is decidable from fieldtype alone: a host built through the widened constructor signatures (see TD_Option) records a schedule in the field's type parameter, so a field that holds no schedule cannot have a type intersecting TimeDependent. The tuple is therefore computed once per host type by a generated function, and a fold-invariant scan over a wide static host (JuMPOptimiser has 41 fields) folds to an empty tuple at compile time rather than walking every field dynamically on every split and _optimise.

This stays derived from the field types — no hand-maintained list — so the constructor signatures remain the single source of truth for which fields may vary over folds (ADR 0030).

Related

source
PortfolioOptimisers.time_dependent_fields Function
julia
time_dependent_fields(opt, all_binds::Bool = true)

Return the tuple of field names of opt whose values are TimeDependent.

The scan is generic over the host's fields, so the widened constructor signatures (see TD_Option) remain the single source of truth for which fields may vary over folds — there is no hand-maintained list. Only the fields whose type admits a schedule are visited (see time_dependent_candidate_fields); the rest are ruled out at compile time, so a static host returns an empty tuple without touching its fields.

The all_binds argument

all_binds encodes something the schedule's own bind field cannot: it is a property of the recursion position, not of the schedule. A TimeDependent's bind (:outermost / :nearest) says which fold loop the schedule wants; all_binds says whether the loop currently recursing is entitled to consume nearest-bound schedules at this depth. The second fact is not on the schedule.

Why position matters: under outer CV loop → meta → (meta's inner CV loop) → inner estimator with a :nearest field, the same :nearest field is visited by two loops. The outer loop recurses through the meta (mandatory — that recursion is how an inner estimator's :outermost field is resolved against the outer folds) and must skip the :nearest field, because it is not the nearest enclosing loop. The meta's inner CV loop drives the same estimator directly and must consume it, because it is. Same field, same bind, opposite actions — the difference is whether a nearer fold-loop boundary was crossed to reach it, which is exactly what all_binds carries.

So all_binds is true at every ordinary (outermost/standalone) fold loop — which is both outermost and nearest, and therefore takes everything remaining, including :nearest. It is forced to false only where a meta-optimiser recurses into the estimators its own inner CV owns, leaving their :nearest schedules for that inner loop. With all_binds = false, only fields with bind === :outermost are returned.

Entitlement is refined per field by inner_fold_fields: even at all_binds = true, a :nearest schedule in a field the host hands across its own inner fold loop is left alone — the host's inner loop, not the scanning one, is nearest for that field (see entitled).

Related

source
PortfolioOptimisers.time_dependent_entries Function
julia
time_dependent_entries(td::TimeDependent) -> Any

Return the statically inspectable entries of a TimeDependent schedule: the per-fold values of a vector schedule, plus its default when it has one. A callable schedule contributes nothing — its per-fold values cannot be inspected before it runs.

Related

source
PortfolioOptimisers.time_dependent_entry_needs_previous_weights Function
julia
time_dependent_entry_needs_previous_weights(x)

Return true if a per-fold entry value of a TimeDependent requires the previous optimisation's weights.

Delegates to needs_previous_weights for the value types that support the trait (turnover, fees, tracking); every other value contributes false.

Related

source
PortfolioOptimisers.assert_time_dependent_substitution Function
julia
assert_time_dependent_substitution(
    _::Type{T},
    args::NamedTuple,
    defaults::NamedTuple
)

Test-substitute every vector entry of the TimeDependent-valued fields in args through the keyword constructor of T.

args holds the host constructor's arguments; defaults the static defaults of the fields that may be time-dependent (see time_dependent_field_defaults). Each per-fold entry, and an explicit default, is substituted into its field — with every other time-dependent field standing at a value of its own (see time_dependent_stand_in) — and the constructor re-run, surfacing type and cross-field errors at construction time instead of mid-backtest. Substituted calls contain no TimeDependent values, so the recursion terminates.

Validation is skipped when a time-dependent field has no stand-in at all — a callable schedule in a required field, whose value only exists once a fold context does.

Related

source
PortfolioOptimisers.time_dependent_stand_in Function
julia
time_dependent_stand_in(
    td::TimeDependent,
    defaults::NamedTuple,
    field::Symbol
) -> Union{Nothing, Some}

Return a valid static value for a TimeDependent-valued field, wrapped in Some, or nothing if none exists.

Used by assert_time_dependent_substitution to stand the other time-dependent fields at a valid value while it test-substitutes one of them: the schedule's default, else the field's static default, else the schedule's first entry. A callable schedule in a field with no default of either kind has no stand-in — its value exists only inside a fold — so it returns nothing and validation is skipped.

Unlike time_dependent_reset_value this never throws: a schedule without a fold-less value is legitimate at construction time and only fails if it reaches a fold-less solve.

Related

source
PortfolioOptimisers.time_dependent_reset_value Function
julia
time_dependent_reset_value(
    td::TimeDependent,
    defaults::NamedTuple,
    field::Symbol,
    opt
) -> Any

Return the value a TimeDependent-valued field takes outside every fold loop.

The schedule's own default wins; absent one (NoDefault), the host's static default for field is used (time_dependent_field_defaults, nothing for fields it omits). Throws a TimeDependentDefaultError when neither exists — a schedule in a required field that never said what a fold-less solve should do.

Related

source
PortfolioOptimisers.assert_time_dependent_optimiser Function
julia
assert_time_dependent_optimiser(
    _::Union{NonFiniteAllocationOptimisationEstimator, NonFiniteAllocationOptimisationResult}
)

Assert that a TimeDependent schedule in an optimiser position resolved to something that can be optimised or predicted.

The vector and TimeDependentOptimiserCallable forms of a schedule declare their output kind in their type and are checked statically (see TD_OptE_Opt). The two callable forms — a bare ctx -> optimiser and a PreviousWeightsFunction wrapping one — cannot be, so their output is checked here, when the fold loop swaps it in.

Related

source
PortfolioOptimisers.assert_time_dependent_fold_count Function
julia
assert_time_dependent_fold_count(opt, n::Integer, all_binds::Bool = true)

Assert that every vector-valued time-dependent constraint in opt has exactly n entries.

Called by the cross-validation fold loops immediately after split, before any fold runs. The default is a no-op; hosts scan their time_dependent_fields and wrapper optimisers recurse. When all_binds is false, bind === :nearest schedules are skipped — they are validated by the nearest enclosing fold loop against its own fold count instead (see TimeDependent).

Related

source
PortfolioOptimisers.assert_time_dependent_fold_count Function
julia
assert_time_dependent_fold_count(
    td::Union{TimeDependent{<:AbstractVector{<:Union{Nothing, var"#s2380"} where var"#s2380"<:(Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult})}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}},
    n::Integer
)
assert_time_dependent_fold_count(
    td::Union{TimeDependent{<:AbstractVector{<:Union{Nothing, var"#s2380"} where var"#s2380"<:(Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult})}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}},
    n::Integer,
    all_binds::Bool
)

Assert that a TimeDependent schedule standing in for an optimiser has one entry per fold, and that the schedules within each entry are sized to the same fold loop.

Entry i runs at fold i of this loop, so its own :outermost schedules bind here too (see update_time_dependent_estimator) and are validated against this loop's fold count. The default is not — it runs only outside a fold loop, where its schedules reset instead.

Skipped when all_binds is false and the schedule is not :outermost-bound — the fold loop the host opens validates it against its own fold count instead.

Related

source
PortfolioOptimisers.assert_time_dependent_fold_count Function
julia
assert_time_dependent_fold_count(
    opt::AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:(Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}), var"#s2386"<:Union{TimeDependent{<:AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}}},
    n::Integer
)
assert_time_dependent_fold_count(
    opt::AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:(Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}), var"#s2386"<:Union{TimeDependent{<:AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}}},
    n::Integer,
    all_binds::Bool
)

Apply assert_time_dependent_fold_count element-wise to a vector of optimisation estimators or results.

Related

source
PortfolioOptimisers.assert_time_dependent_fields_fold_count Function
julia
assert_time_dependent_fields_fold_count(opt, n::Integer)
assert_time_dependent_fields_fold_count(
    opt,
    n::Integer,
    all_binds::Bool
)

Assert the fold count of every TimeDependent-valued field of a host optimiser.

Related

source
PortfolioOptimisers.rebuild_estimator Function
julia
rebuild_estimator(x, repl::NamedTuple) -> Any

Rebuild an estimator through its keyword constructor with the fields in repl replaced.

All remaining fields are carried through unchanged. Because the rebuild goes through the validated keyword constructor, every construction invariant re-runs.

source
PortfolioOptimisers.is_time_dependent Method
julia
is_time_dependent(opt)

Return true if the optimiser carries time-dependent constraints.

The default returns false. Hosts return true when any of their fields holds a TimeDependent (see time_dependent_fields); wrapper optimisers recurse into their inner optimiser and fallback.

Arguments

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

Returns

  • Bool: true if the estimator is time-dependent.

Related

source
PortfolioOptimisers.is_time_dependent Method
julia
is_time_dependent(
    opt::AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:(Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}), var"#s2386"<:Union{TimeDependent{<:AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}}}
) -> Any

Return true if any element of the vector of optimisation estimators or results is time-dependent.

Related

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

Return true if the base optimiser configuration carries time-dependent constraints.

Related

source
PortfolioOptimisers.is_time_dependent Method
julia
is_time_dependent(_::TimeDependent) -> Bool

A TimeDependent schedule is time-dependent by construction.

Related

source
PortfolioOptimisers.is_time_dependent Method
julia
is_time_dependent(
    opt::AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:(Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}), var"#s2386"<:Union{TimeDependent{<:AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}}}
) -> Any

Return true if any element of the vector of optimisation estimators or results is time-dependent.

Related

source
PortfolioOptimisers.update_time_dependent_estimator Function
julia
update_time_dependent_estimator(
    opt::BaseOptimisationEstimator,
    ctx::TimeDependentContext
) -> Any
update_time_dependent_estimator(
    opt::BaseOptimisationEstimator,
    ctx::TimeDependentContext,
    all_binds::Bool
) -> Any

Resolve the time-dependent constraints of a base optimiser configuration for the fold described by ctx.

Rebuilds the configuration through its validated keyword constructor with each TimeDependent-valued field replaced by its resolved per-fold value, so the result is an ordinary static configuration. When all_binds is false, bind === :nearest fields are left in place for the nearest enclosing fold loop to consume.

Related

source
julia
update_time_dependent_estimator(opt, ctx::TimeDependentContext, all_binds::Bool = true)

Resolve the time-dependent constraints of opt for the fold described by ctx.

The default returns the estimator unchanged. Hosts rebuild themselves through their validated keyword constructor with each TimeDependent-valued field replaced by its resolved per-fold value, so the result is an ordinary static estimator; wrapper optimisers recurse.

Arguments

  • opt: Optimisation estimator or result.

  • ctx::TimeDependentContext: The fold's context.

  • all_binds::Bool: When false, bind === :nearest schedules are skipped, leaving them for the nearest enclosing fold loop to consume. Meta-optimisers pass false when recursing into the estimators their internal fold loop processes; fold loops call with the default true.

Returns

  • Updated estimator.

Related

source
julia
update_time_dependent_estimator(
    td::Union{TimeDependent{<:AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}},
    ctx::TimeDependentContext
) -> Any
update_time_dependent_estimator(
    td::Union{TimeDependent{<:AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}},
    ctx::TimeDependentContext,
    all_binds::Bool
) -> Any

Resolve a TimeDependent schedule standing in for an optimiser to the optimiser of fold ctx.i.

Entry i may be an estimator or a precomputed result, so a mixed schedule optimises on some folds and predicts on others. After the swap the resolved estimator is recursed into with the same context, so its own :outermost schedules bind to this fold loop rather than going unresolved.

Returns the schedule unchanged when all_binds is false and it is not :outermost-bound — a :nearest schedule in an optimiser position is consumed by a fold loop the host itself opens, not by the loop that reached the host.

Related

source
julia
update_time_dependent_estimator(
    opt::AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:(Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}), var"#s2386"<:Union{TimeDependent{<:AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}}},
    ctx::TimeDependentContext
) -> Any
update_time_dependent_estimator(
    opt::AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:(Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}), var"#s2386"<:Union{TimeDependent{<:AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}}},
    ctx::TimeDependentContext,
    all_binds::Bool
) -> Any

Apply update_time_dependent_estimator element-wise to a vector of optimisation estimators or results.

Related

source
julia
update_time_dependent_estimator(
    opt::NaiveOptimisationEstimator,
    ctx::TimeDependentContext
) -> Any
update_time_dependent_estimator(
    opt::NaiveOptimisationEstimator,
    ctx::TimeDependentContext,
    all_binds::Bool
) -> Any

Resolve the time-dependent constraints of a naive optimiser for the fold described by ctx.

Rebuilds the optimiser through its validated keyword constructor with each TimeDependent-valued field replaced by its resolved per-fold value, recursing into the fallback, so the result is an ordinary static optimiser.

Related

source
julia
update_time_dependent_estimator(
    opt::ClusteringOptimisationEstimator,
    ctx::TimeDependentContext
) -> NestedClustered
update_time_dependent_estimator(
    opt::ClusteringOptimisationEstimator,
    ctx::TimeDependentContext,
    all_binds::Bool
) -> NestedClustered

Resolve time-dependent constraints for the fold described by ctx: the estimator's own scheduled fields (risk measures, scalarisers, fallback, …) are swapped for their per-fold values, then the inner optimiser and the (possibly just-swapped-in) fallback are recursed into with the same context.

NestedClustered overrides this with its own method resolving its own fields and inner estimators.

source
julia
update_time_dependent_estimator(
    opt::JuMPOptimisationEstimator,
    ctx::TimeDependentContext
) -> Any
update_time_dependent_estimator(
    opt::JuMPOptimisationEstimator,
    ctx::TimeDependentContext,
    all_binds::Bool
) -> Any

Resolve time-dependent constraints for the fold described by ctx: the estimator's own scheduled fields (risk measure, objective, warm start, fallback, …) are swapped for their per-fold values, then the inner JuMP optimiser and the (possibly just-swapped-in) fallback are recursed into with the same context.

source
julia
update_time_dependent_estimator(
    opt::NestedClustered,
    ctx::TimeDependentContext
) -> NestedClustered
update_time_dependent_estimator(
    opt::NestedClustered,
    ctx::TimeDependentContext,
    all_binds::Bool
) -> NestedClustered

Resolve time-dependent constraints for the fold described by ctx by recursing into the inner optimiser, outer optimiser, and fallback.

source
julia
update_time_dependent_estimator(
    opt::Stacking,
    ctx::TimeDependentContext
) -> Stacking
update_time_dependent_estimator(
    opt::Stacking,
    ctx::TimeDependentContext,
    all_binds::Bool
) -> Stacking

Resolve time-dependent constraints for the fold described by ctx by recursing into the inner optimisers, outer optimiser, and fallback.

source
julia
update_time_dependent_estimator(
    opt::SubsetResampling,
    ctx::TimeDependentContext
) -> SubsetResampling
update_time_dependent_estimator(
    opt::SubsetResampling,
    ctx::TimeDependentContext,
    all_binds::Bool
) -> SubsetResampling

Resolve time-dependent constraints for the fold described by ctx by recursing into the base optimiser and fallback.

source
julia
update_time_dependent_estimator(
    p::Pipeline,
    ctx::TimeDependentContext
) -> Pipeline
update_time_dependent_estimator(
    p::Pipeline,
    ctx::TimeDependentContext,
    all_binds::Bool
) -> Pipeline

Resolve the time-dependent steps of a Pipeline for the fold described by ctx — the swap of ADR 0030's pipeline integration.

The swap happens in the fold loop, outside fit entirely: it maps update_time_dependent_step over the steps (names preserved), so by the time fit runs on the fold's training window every schedule step is already a plain optimiser or precomputed result and injection (inject_context / maybe_inject_step) never sees a schedule — fit and run_step never learn about folds.

Related

source
PortfolioOptimisers.update_time_dependent_fields Function
julia
update_time_dependent_fields(
    opt,
    ctx::TimeDependentContext
) -> Any
update_time_dependent_fields(
    opt,
    ctx::TimeDependentContext,
    all_binds::Bool
) -> Any

Rebuild a host optimiser with each TimeDependent-valued field replaced by its per-fold value for ctx.

Shared implementation behind the hosts' update_time_dependent_estimator methods. Returns opt unchanged when no field is time-dependent.

Related

source
PortfolioOptimisers.time_dependent_field_defaults Function
julia
time_dependent_field_defaults(opt)

Return a NamedTuple of the static defaults of the optimiser fields that may hold a TimeDependent, for those whose default is not nothing.

Used by reset_time_dependent_estimator to replace per-fold schedules with their static defaults on fold-less solves; fields absent from the tuple default to nothing. A required field — one with no static default at all, i.e. the optimiser-valued fields — is listed with NoDefault, which is not a value it can take but a declaration that a schedule there must carry its own default. The fallback method returns an empty tuple.

Related

source
julia
time_dependent_field_defaults(
    _::HierarchicalOptimiser
) -> @NamedTuple{pe::EmpiricalPrior{PortfolioOptimisersCovariance{Covariance{SimpleExpectedReturns{Nothing}, GeneralCovariance{SimpleCovariance, Nothing}, FullMoment}, MatrixProcessing{Posdef{UnionAll, @NamedTuple{}}, Nothing, Nothing, Nothing, NTuple{4, Symbol}}}, SimpleExpectedReturns{Nothing}, Nothing}, cle::ClustersEstimator{PortfolioOptimisersCovariance{Covariance{SimpleExpectedReturns{Nothing}, GeneralCovariance{SimpleCovariance, Nothing}, FullMoment}, MatrixProcessing{Posdef{UnionAll, @NamedTuple{}}, Nothing, Nothing, Nothing, NTuple{4, Symbol}}}, Distance{Nothing, CanonicalDistance}, HClustAlgorithm{Symbol}, OptimalNumberClusters{Nothing, SecondOrderDifference{StandardisedValue{MeanValue{Nothing}, StdValue{Nothing, Bool}}}}}, wb::WeightBounds{Float64, Float64}, wf::IterativeWeightFinaliser{Int64}}

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

Related

source
PortfolioOptimisers.reset_time_dependent_estimator Method
julia
reset_time_dependent_estimator(opt)

Replace every TimeDependent-valued field of opt with its static default, recursing through wrapper optimisers.

A time-dependent constraint is defined only over the folds of a cross-validation scheme, so a fold-less solve runs with the affected fields at their static defaults (see time_dependent_field_defaults). Called at the top of the _optimise methods; per-fold estimators produced by update_time_dependent_estimator contain no TimeDependent values, so they pass through unchanged. The default returns the estimator unchanged; hosts rebuild themselves, wrapper optimisers recurse.

Related

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

Replace the time-dependent constraints of a base optimiser configuration with their static defaults (see time_dependent_field_defaults).

Related

source
PortfolioOptimisers.reset_time_dependent_estimator Method
julia
reset_time_dependent_estimator(
    td::Union{TimeDependent{<:AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}
) -> Any

Return the optimiser a TimeDependent schedule takes outside every fold loop.

An optimiser position is required — there is no static default to fall back to — so the schedule must supply its own default, and one that does not throws a TimeDependentDefaultError. The fold-less optimiser is itself reset, so its own schedules resolve to their defaults too.

Related

source
PortfolioOptimisers.reset_time_dependent_fields Function
julia
reset_time_dependent_fields(opt) -> Any

Rebuild a host optimiser with each TimeDependent-valued field replaced by its fold-less value (see time_dependent_reset_value).

Shared implementation behind the hosts' reset_time_dependent_estimator methods. Returns opt unchanged when no field is time-dependent.

Related

source
PortfolioOptimisers.optimise Method
julia
optimise(
    td::Union{TimeDependent{<:AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}},
    args...;
    kwargs...
) -> Any

Optimise with a TimeDependent schedule standing in for the optimiser, outside any fold loop.

There are no folds to index, so the schedule resolves to its default and that optimiser runs (see reset_time_dependent_estimator); a schedule with no default throws a TimeDependentDefaultError. Inside a fold loop this method is never reached — the loop resolves entry i first.

Related

source
PortfolioOptimisers.set_clustering_weight_finaliser_alg! Function
julia
set_clustering_weight_finaliser_alg!(model, ...)

Set the clustering weight finalisation algorithm on the JuMP model.

Configures how cluster-level weights are finalised in the hierarchical optimisation model, applying the specified weight finaliser.

Arguments

  • model: JuMP model.

  • Additional clustering and finaliser parameters.

Returns

  • nothing.

Related

source
PortfolioOptimisers.opt_weight_bounds Function
julia
opt_weight_bounds(wf, wb, w)

Compute optimised weight bounds from the finaliser, bounds, and current weights.

Adjusts the weight bounds based on the weight finaliser algorithm and the current weight allocation, used in hierarchical weight allocation.

Arguments

  • wf: Weight finaliser algorithm.

  • wb: Weight bounds.

  • w: Current portfolio weights.

Returns

  • Updated weight bounds.

Related

source
PortfolioOptimisers.finalise_weight_bounds Function
julia
finalise_weight_bounds(wf::WeightFinaliser, wb::WeightBounds, w::VecNum)

Apply weight finalisation to enforce bounds and determine the optimisation return code.

Runs opt_weight_bounds with the given finaliser and bounds, then returns a success or failure return code based on whether all weights are finite.

Arguments

  • wf::WeightFinaliser: Weight finaliser algorithm.

  • wb::WeightBounds: Weight bounds configuration.

  • w::VecNum: Portfolio weights to finalise.

Returns

  • (retcode, w): Tuple of return code and adjusted weights.

Related

source
PortfolioOptimisers.port_opt_view Method
julia
port_opt_view(opt, i, args...)

Return a view or subset of an optimisation estimator for a given cluster index i.

Default fallback returns the estimator unchanged. Overridden for composite estimators (e.g. JuMPOptimiser, HierarchicalRiskParity) to slice all sub-estimators for the i-th cluster.

Arguments

  • opt: Optimisation estimator or result.

  • i: Cluster or asset index.

  • args...: Additional arguments (e.g. asset returns matrix).

Returns

  • Sliced or unchanged optimisation estimator.

Related

source
PortfolioOptimisers.port_opt_view Method
julia
port_opt_view(x, i, args...; kwargs...) -> nothing_scalar_array_view(x, i)

Sub-select an estimator, result, or algorithm to the asset/observation index i.

port_opt_view is the index-selection counterpart of factory: where factory threads runtime values down a composed struct tree, port_opt_view threads an index selection — restricting every data-bearing field and composed child to the subset i. It is the mechanism that makes meta-optimisers (NestedClustered, SubsetResampling) and cross-validation variants operate on subproblems with identical struct shapes.

Callers do not normally call port_opt_view directly; it is driven by meta-optimisers and cross-validation internals. It is public (not exported) because extension authors who implement a new composed estimator may need to define a method. Use @vprop on data-bearing fields to have the method generated automatically.

This universal fallback handles leaf values: arrays are sliced via nothing_scalar_array_view; scalars, nothing, estimators without data fields, and algorithms pass through unchanged. Composed structs that recurse into children define their own (more specific) method — emitted by @vprop or hand-written.

The threaded tail args... (typically the returns matrix X for the JuMP families) and any kwargs are accepted and dropped here, so a macro-threaded port_opt_view(child, i, X) never MethodErrors on a leaf field.

Related

source
julia
port_opt_view(
    opt::Union{AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:(Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}), var"#s2386"<:Union{TimeDependent{<:AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}}}, AbstractVector{<:AbstractOptimisationEstimator}},
    i,
    args...
) -> Any

Apply port_opt_view element-wise to a vector of optimisation estimators.

Related

source
PortfolioOptimisers.port_opt_view Method
julia
port_opt_view(
    opt::Union{AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:(Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}), var"#s2386"<:Union{TimeDependent{<:AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}}}, AbstractVector{<:AbstractOptimisationEstimator}},
    i,
    args...
) -> Any

Apply port_opt_view element-wise to a vector of optimisation estimators.

Related

source
PortfolioOptimisers.port_opt_view Method
julia
port_opt_view(
    res::NonFiniteAllocationOptimisationResult,
    _::Colon,
    args...
) -> NonFiniteAllocationOptimisationResult

A precomputed optimisation result cannot be restricted to an asset subset.

Its weights were solved over the full universe and a sub-portfolio of them has no defined meaning, so an asset-subset view of a result throws. In particular, a TimeDependent schedule holding result entries is incompatible with asset-subsampling cross-validation (MultipleRandomised), whose fold loops view the optimiser to each fold's asset subset before the swap. The trivial all-assets view (Colon) passes the result through unchanged.

Related

source
PortfolioOptimisers.assert_internal_optimiser Method
julia
assert_internal_optimiser(
    _::NonFiniteAllocationOptimisationResult
)

Assert that res is a valid internal optimisation result.

Default no-op. Overridden for result types that must satisfy internal constraints before use.

Related

source
PortfolioOptimisers.assert_external_optimiser Method
julia
assert_external_optimiser(
    _::NonFiniteAllocationOptimisationResult
)

Assert that res is a valid external optimisation result.

Default no-op. Overridden for result types that must satisfy external interface constraints.

Related

source
PortfolioOptimisers.assert_special_nco_requirements Function
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
julia
assert_special_nco_requirements(
    opt::AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:(Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}), var"#s2386"<:Union{TimeDependent{<:AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}}}
)

Assert special NCO requirements for each element of a vector of optimisation estimators or results.

Related

source
PortfolioOptimisers.factory Method
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
PortfolioOptimisers.factory Method
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.factory Method
julia
factory(
    opt::AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:(Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}), var"#s2386"<:Union{TimeDependent{<:AbstractVector{<:Union{var"#s2387", var"#s2386"} where {var"#s2387"<:NonFiniteAllocationOptimisationEstimator, var"#s2386"<:NonFiniteAllocationOptimisationResult}}}, TimeDependent{<:TimeDependentOptimiserCallable}, TimeDependent{<:PreviousWeightsFunction}, TimeDependent{<:Union{Function, Type}}}}},
    args...
) -> Any

Apply factory element-wise to a vector of optimisation estimators or results, elements that are TimeDependent schedules included.

Related

source
PortfolioOptimisers.assert_no_nearest_bind_optimiser_schedule Method
julia
assert_no_nearest_bind_optimiser_schedule(x, field::Symbol, host::Symbol)

Reject a bind = :nearest TimeDependent schedule in an optimiser-valued position no inner fold loop consumes.

bind picks which fold loop supplies the schedule's index. :nearest therefore says something different from :outermost only where the host opens a fold loop of its own and hands the field across it — NestedClustered.opti (its inner cross-validation is entered per cluster) and Stacking.opti[k] (entered per candidate), the positions declared by inner_fold_fields. Everywhere else the loop that reaches the host is the nearest one, and the two binds would name the same loop.

The positions this guards have no such inner loop:

  • A fallback (fb), on every host. The fallback walk is a retry chain within a single fold's solve — it has no fold indices of its own — so :nearest there is either redundant with :outermost or, behind a meta's inner cross-validation, silently wrong: it would resolve against the inner loop's fold numbers (tuning folds) instead of the backtest's periods, changing meaning with nesting depth. A per-fold fallback is fully expressible with :outermost, including nothing entries to switch it off on some folds (see TDO_OptE_Opt).

  • The outer optimisers (opto). They consume the combined inner output, once per solve.

  • SubsetResampling.opt. Its internal loop is over randomly drawn asset subsets, not time folds.

So a :nearest schedule in any of them has no nearest fold loop to bind to, and is rejected at construction rather than resolving against a loop the caller did not mean. No-op for anything that is not a TimeDependent.

Related

source
PortfolioOptimisers.entitled Function
julia
entitled(opt, f::Symbol, all_binds::Bool)

Return true when the recursion position described by all_binds may consume a bind = :nearest schedule in field f of opt.

Entitlement is per-field, not per-host: a loop scanning with all_binds = true takes :nearest schedules everywhere except in the fields the host hands across its own inner fold loop (see inner_fold_fields) — for those, the host's inner loop is the nearest one, whatever loop is doing the scanning. A field is taken by a pass iff entitled(opt, f, all_binds) || bind === :outermost.

Related

source
PortfolioOptimisers.OptE_Opt Type
julia
const OptE_Opt = Union{<:NonFiniteAllocationOptimisationEstimator,
                       <:NonFiniteAllocationOptimisationResult}

Alias for a non-finite allocation optimisation estimator or result.

Matches either a NonFiniteAllocationOptimisationEstimator (specifying an optimiser configuration) or a NonFiniteAllocationOptimisationResult (a pre-computed result). Used for dispatch in cross-validation and optimisation workflows that accept either form.

Related

source
PortfolioOptimisers.VecOptE_Opt Type
julia
const VecOptE_Opt = AbstractVector{<:OptE_Opt}

Alias for a vector of optimisation estimators or results.

Represents a collection of OptE_Opt objects for batch processing.

Related

source
PortfolioOptimisers.VecOpt Type
julia
const VecOpt = AbstractVector{<:NonFiniteAllocationOptimisationResult}

Alias for a vector of non-finite allocation optimisation results.

Represents a collection of NonFiniteAllocationOptimisationResult objects.

Related

source
PortfolioOptimisers.VecOptE Type
julia
const VecOptE = AbstractVector{<:AbstractOptimisationEstimator}

Alias for a vector of portfolio optimisation estimators.

Represents a collection of AbstractOptimisationEstimator objects, used for dispatch in routines that process multiple optimisers simultaneously.

Related

source
PortfolioOptimisers.extract_fees Function
julia
extract_fees(res::OptimisationResult) -> Any
extract_fees(
    res::OptimisationResult,
    fees::Union{Nothing, Fees}
) -> Any

Obtains the fees to use for net return calculations from an optimisation result. If fees is provided; if not, it looks for a fees property in the result. Returns the fees or nothing if not found.

Arguments

  • res: Optimisation result, potentially containing a fees property.

  • fees: Optional fees to use, which take precedence over res.fees if provided.

Returns

  • Option{<:Fees}: The fees to use for net return calculations, or nothing if not found.

Related

source
PortfolioOptimisers.extract_pr Function
julia
extract_pr(res::OptimisationResult) -> Any
extract_pr(
    res::OptimisationResult,
    pr::Union{Nothing, AbstractPriorResult, ReturnsResult}
) -> Any

Extracts the prior result for risk calculation from an optimisation result. Checks for an explicitly provided pr, then looks for res.pr and res.pa.pr before throwing an error if none are found.

Arguments

  • res: Optimisation result, potentially containing a prior result in res.pr or res.pa.pr.

  • pr: Optional prior result to use for risk calculation, which takes precedence over any found in res.

Returns

  • Option{<:Pr_RR}: The prior result to use for risk calculation, or throws an error if none is found.
source