JuMP Optimiser

PortfolioOptimisers.ProcessedJuMPOptimiserAttributesType
struct ProcessedJuMPOptimiserAttributes{__T_pr, __T_wb, __T_lt, __T_st, __T_lcsr, __T_ctr, __T_gcardr, __T_sgcardr, __T_smtx, __T_sgmtx, __T_slt, __T_sst, __T_sglt, __T_sgst, __T_tn, __T_fees, __T_plr, __T_ret, __T_sca, __T_imsk} <: ProcessedAttributes

Flat bundle of all processed constraint and prior results consumed by assemble_jump_model!.

Produced once per optimise call by processed_jump_optimiser_attributes and passed directly to the model-assembly pipeline, so every builder reads already-resolved results rather than re-processing estimators.

Fields

  • pr: Prior result.
  • wb: Weight bounds.
  • lt: Long-side minimum holding threshold.
  • st: Short-side minimum holding threshold.
  • lcsr: Processed linear constraint set result.
  • ctr: Centrality constraint result. The LinearConstraint the centrality constraints resolve to.
  • gcardr: Processed grouped cardinality constraint result.
  • sgcardr: Processed sub-grouped cardinality constraint result.
  • smtx: Sub-group selection matrix or estimator.
  • sgmtx: Sub-grouped selection matrix or estimator.
  • slt: Sub-group long threshold.
  • sst: Sub-group short threshold.
  • sglt: Sub-grouped long threshold.
  • sgst: Sub-grouped short threshold.
  • tn: Turnover result.
  • fees: Fees result.
  • plr: Phylogeny result.
  • ret: Return term, or vector of return terms, for the JuMP model. Several terms are weighted-summed into the model's single scalar return expression, in the same way MeanRisk's r takes several risk measures.
  • sca: The scalariser the optimisation ran under, taken from opt.sca. Pass it back as expected_risk(res.r, res.w, res.pr; sca = res.sca) so the reported figure matches the optimised one.
  • 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.

Constructors

ProcessedJuMPOptimiserAttributes(;    pr::AbstractPriorResult, wb::Option{<:WeightBounds}, lt::Option{<:Threshold},    st::Option{<:Threshold}, lcsr::Option{<:Lc_VecLc}, ctr::Option{<:Lc_CC_VecCC},    gcardr::Option{<:LinearConstraint}, sgcardr::Option{<:Lc_VecLc},    smtx::Option{<:MatNum_VecMatNum}, sgmtx::Option{<:MatNum_VecMatNum},    slt::Option{<:Bt_VecOptBt}, sst::Option{<:Bt_VecOptBt}, sglt::Option{<:Bt_VecOptBt},    sgst::Option{<:Bt_VecOptBt}, tn::Option{<:Tn_VecTn}, fees::Option{<:Fees},    plr::Option{<:Union{<:AbstractPhylogenyConstraintResult,                        <:AbstractVector{<:AbstractPhylogenyConstraintResult}}},    ret::JRE_VecJRE, sca::Scalariser, imsk::Option{<:BitVector} = nothing) -> ProcessedJuMPOptimiserAttributes

Keywords correspond to the struct's fields. The field types are the result side of the matching JuMPOptimiser estimator slots: this bundle holds the constraint and prior results produced by processed_jump_optimiser_attributes, not the raw estimators (ret aside — a returns estimator has no separate result form). In practice, construct via processed_jump_optimiser_attributes rather than directly.

Examples

julia> pr = prior(EmpiricalPrior(),                  ReturnsResult(; nx = ["a", "b"], X = [0.1 -0.2; -0.1 0.2; 0.05 0.1]));julia> ProcessedJuMPOptimiserAttributes(; pr = pr, wb = nothing, lt = nothing, st = nothing,                                        lcsr = nothing, ctr = nothing, gcardr = nothing,                                        sgcardr = nothing, smtx = nothing, sgmtx = nothing,                                        slt = nothing, sst = nothing, sglt = nothing,                                        sgst = nothing, tn = nothing, fees = nothing,                                        plr = nothing, ret = ArithmeticReturn(),                                        sca = SumScalariser()) isa ProcessedJuMPOptimiserAttributestrue

Related

source
PortfolioOptimisers.JuMPOptimisationResultType
struct JuMPOptimisationResult{__T_pa, __T_retcode, __T_sol, __T_model} <: BaseJuMPOptimisationResult

Shared field core for JuMP-based optimisation results.

Holds the fields common to every JuMP optimisation result. Embedded as the first field (jr) of each concrete JuMP result, analogous to how JuMPOptimiser is embedded as opt in each JuMP optimiser. The concrete result keeps only its unique fields plus the trailing fb.

Defined here (rather than in 01_Base_JuMPOptimisation.jl, where its BaseJuMPOptimisationResult supertype lives) so its typed constructor can bind pa::ProcessedJuMPOptimiserAttributes and sol::JuMPOptimisationSolution, both in scope at this point in load order.

Fields

  • pa: Processed optimisation attributes.
  • retcode: Optimisation return code.
  • sol: Optimisation solution.
  • model: :JuMP.Model`: The JuMP optimisation model.

Constructors

JuMPOptimisationResult(;    pa::ProcessedJuMPOptimiserAttributes,    retcode::OptRetCode_VecOptRetCode,    sol::JuMPOptSol_VecJuMPOptSol,    model::Option{<:JuMP.Model}) -> JuMPOptimisationResult

Keywords correspond to the struct's fields.

Related

source
PortfolioOptimisers.JuMPOptimiserType
struct JuMPOptimiser{__T_pe, __T_slv, __T_wb, __T_bgt, __T_sbgt, __T_gbgt, __T_xbgt, __T_lt, __T_st, __T_lcse, __T_cte, __T_gcarde, __T_sgcarde, __T_smtx, __T_sgmtx, __T_slt, __T_sst, __T_sglt, __T_sgst, __T_tn, __T_fees, __T_sets, __T_tr, __T_ple, __T_ret, __T_sca, __T_ccnt, __T_cobj, __T_sc, __T_so, __T_ss, __T_card, __T_scard, __T_l2c, __T_lpc, __T_linfc, __T_l1, __T_l2, __T_lp, __T_linf, __T_brt, __T_x_src, __T_strict, __T_cache} <: BaseJuMPOptimisationEstimator

Main JuMP-based portfolio optimiser configuration.

JuMPOptimiser collects all the inputs needed to formulate and solve a JuMP-based portfolio optimisation problem: prior estimator, solver, constraints, bounds, fees, tracking, regularisation, and more. It is intended to be passed to a higher-level optimiser such as MeanRisk or RiskBudgeting.

Fields

  • pe: Prior estimator.
  • slv: Solver or vector of solvers.
  • wb: Weight bounds estimator or weight bounds.
  • bgt: Net budget, 1ᵀw. A number pins it, a BudgetRange bounds it. By default budgets bound the realised exposure rather than pinning it (see xbgt). Together with sbgt this fixes the net and gross exposures only jointly; to constrain the gross exposure on its own see gbgt.
  • sbgt: Short-side budget, sum(sw). A number pins it, a BudgetRange bounds it; by default it bounds, so sbgt = 0.3 means at most 30% short unless xbgt pins the long/short decomposition. Together with bgt this fixes the net and gross exposures only jointly; to constrain the gross exposure on its own see gbgt.
  • gbgt: Gross budget (leverage) constraint, sum(lw) + sum(sw). A number pins the gross exposure; a BudgetRange bounds it, e.g. BudgetRange(; lb = nothing, ub = 2.0) caps leverage at 2x. Unlike bgt and sbgt — which pin the net and gross exposures only together — this constrains the gross exposure on its own, leaving the net free. Requires weight bounds that admit short positions, and is bounded rather than pinned unless xbgt is set.
  • xbgt: Whether to pin the long/short decomposition exactly. When false (the default), lw and sw are upper bounds on the positive and negative parts of w, so bgt, sbgt and gbgt bound the realised exposures rather than pinning them — a short budget of 0.3 means at most 30% short. When true, the long/short binary indicators force lw == max(w, 0) and sw == max(-w, 0), so the budgets hold exactly, at the cost of turning the problem into a mixed-integer program. It reuses the indicators the cardinality, threshold and fee builders already create (see short_mip_threshold_constraints) rather than adding its own, and is ignored when the weight bounds admit no shorts.
  • lt: Long-side minimum holding threshold.
  • st: Short-side minimum holding threshold.
  • lcse: Linear constraint set estimator(s). This is the one constraint slot that also admits an ExposureConstraintEstimator, so a row may be written in the names of another basis — factor names, say — and re-based through the prior's loadings at generation time. What reaches the model is an ordinary asset-space LinearConstraint either way.
  • cte: Centrality constraint(s). A CentralityConstraint, a vector of them, or an already-generated LinearConstraint. Resolved by centrality_constraints into the ctr slot of ProcessedJuMPOptimiserAttributes.
  • gcarde: Grouped cardinality constraint estimator.
  • sgcarde: Sub-grouped cardinality constraint estimator(s).
  • smtx: Sub-group selection matrix or estimator.
  • sgmtx: Sub-grouped selection matrix or estimator.
  • slt: Sub-group long threshold.
  • sst: Sub-group short threshold.
  • sglt: Sub-grouped long threshold.
  • sgst: Sub-grouped short threshold.
  • tn: Turnover constraint estimator(s).
  • fees: Fee estimator or fee structure.
  • sets: Sets used to map estimator values to assets.
  • tr: Tracking error constraint(s).
  • ple: Phylogeny constraint estimator(s).
  • ret: Return term, or vector of return terms, for the JuMP model. Several terms are weighted-summed into the model's single scalar return expression, in the same way MeanRisk's r takes several risk measures.
  • sca: Scalariser for combining multiple risk measures.
  • ccnt: Custom JuMP constraint.
  • cobj: Custom JuMP objective.
  • sc: Constraint scale factor.
  • so: Objective scale factor.
  • ss: Optional scalar shrinkage parameter.
  • card: Global cardinality constraint.
  • scard: Sub-group cardinality constraint(s).
  • l2c: 2-norm ceiling on the weights — bounds norm(w, 2) <= l2c * k (k is the budget, 1 for a fully invested portfolio). Smaller l2c forces a more evenly spread portfolio. Used as a diversification floor via the reciprocal: l2c = 1 / sqrt(m) requires at least m effective assets (inv(norm(w, 2)^2) >= m). Norm-constraint family with lpc and linfc. The bound is Num_NormCeilCal under the time-dependent wrapper, so the slot takes the ceiling itself, an AbstractNormCeilingCalibrationAlgorithm that computes it from the prior result, or a plain function of the same five arguments.
  • lpc: p-norm ceiling(s) on the weights at an arbitrary norm order. Each LpRegularisation supplies a norm order p and a bound val, enforcing norm(w, p) <= val * k. Smaller val forces a more evenly spread portfolio. Used as a diversification floor via the reciprocal: val = m^(1/p - 1) requires at least m order-p effective assets (sum(abs.(w) .^ p)^inv(1 - p) >= m), which is number_effective_assets taken to an arbitrary order. Norm-constraint family with l2c and linfc.
  • linfc: ∞-norm ceiling on the weights — a cap on the largest absolute weight: norm(w, Inf) <= linfc * k. So linfc = 0.2 caps the largest weight at 20% of a fully invested portfolio. Used as a diversification floor via the reciprocal: linfc = 1 / m spreads the portfolio across at least m assets. Norm-constraint family with l2c and lpc. The bound is Num_NormCeilCal under the time-dependent wrapper, so the slot takes the ceiling itself, an AbstractNormCeilingCalibrationAlgorithm that computes it from the prior result, or a plain function of the same five arguments.
  • l1: L1 regularisation coefficient. It is the ambiguity radius of a type-$\infty$ Wasserstein ground metric, whose dual norm is the 1-norm, so the bound is Num_AmbRadCal under the time-dependent wrapper and the slot takes an AbstractAmbiguityRadiusCalibrationAlgorithm that computes it from the prior result, or a plain function of the same five arguments.
  • l2: L2 regularisation term(s).
  • lp: Lp regularisation specification(s).
  • linf: L∞ regularisation coefficient. It is the ambiguity radius of a type-1 Wasserstein ground metric, whose dual norm is the $\infty$-norm, so the bound is Num_AmbRadCal under the time-dependent wrapper and the slot takes an AbstractAmbiguityRadiusCalibrationAlgorithm that computes it from the prior result, or a plain function of the same five arguments.
  • brt: Whether to use bootstrap returns.
  • x_src: Which returns matrix the clustering, phylogeny and centrality estimators read: :prior takes the prior result's X, :data takes the raw returns result's X. Ignored when no returns result is available, in which case the prior result's X is used.
  • 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

JuMPOptimiser(;    pe::Onl{<:TD{<:PrE_Pr}} = EmpiricalPrior(),    slv::Slv_VecSlv,    wb::TD_Option{<:WbE_Wb} = WeightBounds(),    bgt::TD_Option{<:Num_BgtCE} = 1.0,    sbgt::TD_Option{<:Num_BgtRg} = nothing,    gbgt::TD_Option{<:Num_BgtRg} = nothing,    xbgt::Bool = false,    lt::TD_Option{<:BtE_Bt} = nothing,    st::TD_Option{<:BtE_Bt} = nothing,    lcse::TD_Option{<:EcE_LcE_Lc_VecEcE_LcE_Lc} = nothing,    cte::TD_Option{<:Lc_CC_VecCC} = nothing,    gcarde::TD_Option{<:LcE_Lc} = nothing,    sgcarde::TD_Option{<:LcE_Lc_VecLcE_Lc} = nothing,    smtx::TD_Option{<:MatNum_ASetMatE_VecMatNum_ASetMatE} = nothing,    sgmtx::TD_Option{<:MatNum_ASetMatE_VecMatNum_ASetMatE} = nothing,    slt::TD_Option{<:BtE_Bt_VecOptBtE_Bt} = nothing,    sst::TD_Option{<:BtE_Bt_VecOptBtE_Bt} = nothing,    sglt::TD_Option{<:BtE_Bt_VecOptBtE_Bt} = nothing,    sgst::TD_Option{<:BtE_Bt_VecOptBtE_Bt} = nothing,    tn::TD_Option{<:TnE_Tn_VecTnE_Tn} = nothing,    fees::TD_Option{<:FeesE_Fees} = nothing,    sets::TD_Option{<:UniverseSets} = nothing,    tr::TD_Option{<:Tr_VecTr} = nothing,    ple::TD_Option{<:PlCE_PlC_VecPlCE_PlC} = nothing,    ret::TD{<:JRE_VecJRE} = ArithmeticReturn(),    sca::TD{<:NonHierarchicalScalariser} = SumScalariser(),    ccnt::TD_Option{<:JuMPConstr_VecJuMPConstr} = nothing,    cobj::TD_Option{<:JuMPObj_VecJuMPObj} = nothing,    sc::Number = 1,    so::Number = 1,    ss::TD_Option{<:Number} = nothing,    card::TD_Option{<:Integer} = nothing,    scard::TD_Option{<:Int_VecInt} = nothing,    l2c::TD_Option{<:Num_NormCeilCal} = nothing,    lpc::TD_Option{<:LpReg_VecLpReg} = nothing,    linfc::TD_Option{<:Num_NormCeilCal} = nothing,    l1::TD_Option{<:Num_AmbRadCal} = nothing,    l2::TD_Option{<:L2Reg_VecL2Reg} = nothing,    lp::TD_Option{<:LpReg_VecLpReg} = nothing,    linf::TD_Option{<:Num_AmbRadCal} = nothing,    brt::Bool = false,    x_src::Symbol = :prior,    strict::Bool = false,    cache::Option{<:ReturnsBufferState} = nothing,) -> JuMPOptimiser

Keywords correspond to the struct's fields. Fields typed TD_Option or TD may hold a TimeDependent per-fold schedule instead of a static value; a cross-validation fold loop resolves it per fold, and a fold-less optimise runs with the field at its static default. The problem definition — the prior estimator, returns model, scalariser and asset sets as much as the constraints — may therefore vary over folds; execution control (slv, sc, so, brt, x_src, strict) stays static.

Validation

  • x_src in (:prior, :data).
  • If slv is a vector: !isempty(slv).
  • If bgt is a number: isfinite(bgt).
  • If bgt is a BudgetCostEstimator: isnothing(sbgt).
  • If sbgt is a number: isfinite(sbgt) and sbgt >= 0.
  • If gbgt is a number: isfinite(gbgt) and gbgt >= 0.
  • If gbgt is provided: wb must admit short positions, and bgt and sbgt must not both be pinned numbers (which already determine the gross exposure as bgt + 2 * sbgt).
  • If cte is a vector: !isempty(cte).
  • If card is provided: card > 0 and finite.
  • If tn or tr is a vector: each must be non-empty.
  • If l2c, linfc, l1, or linf is provided as a number: each must be > 0 and finite. l1 and linf also take an ambiguity-radius rule, and l2c and linfc a norm-ceiling rule. A rule states no number here, so the check runs on the number the rule returns, in assemble_jump_model!.
  • The rule in each LpRegularisation is checked against the field that holds it: lp is a penalty, so it refuses a norm-ceiling rule, and lpc is a constraint, so it refuses an ambiguity-radius rule. The term itself carries one bound for both readings, so this is the point at which the reading is known.
  • If l2, lp or lpc is a vector: each must be non-empty. An empty vector builds no term, which is what nothing already spells.
  • l2, lp and lpc are validated by their own estimator constructors (L2Regularisation, LpRegularisation).
  • If scard is provided: compatible smtx, slt, sst sizes required.
  • If sgcarde is provided: compatible sgmtx, sglt, sgst sizes required.
  • If any estimator-type field (wb, lt, fees, etc.) is provided: !isnothing(sets).
  • If any field holds a TimeDependent: every vector entry is test-substituted through this constructor (with the other time-dependent fields at their static defaults) so type and cross-field compatibility errors surface immediately. Validation coupling a time-dependent field to static fields is deferred to the per-fold rebuild.

Related

source
PortfolioOptimisers.factoryMethod
factory(
    opt::JuMPOptimiser,
    w::AbstractVector
) -> JuMPOptimiser{_A, _B, _C, _D, _E, _F, Bool, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, _Z1, var"#s185", var"#s1851", _Z2, _Z3, _Z4, _Z5, _Z6, _Z7, _Z8, _Z9, _Z10, _Z11, Bool, Symbol, Bool} where {_A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, _Z1, var"#s185"<:Number, var"#s1851"<:Number, _Z2, _Z3, _Z4, _Z5, _Z6, _Z7, _Z8, _Z9, _Z10, _Z11}

Return a copy of opt with all weight-tracking estimator fields updated via factory for the new weights w.

Updates turnover, fees, tracking error, custom constraint, and custom objective fields; all other fields are carried through unchanged.

Arguments

  • opt::JuMPOptimiser: JuMP optimiser configuration.
  • w::AbstractVector: New portfolio weights.

Returns

  • JuMPOptimiser: Updated optimiser with weight-tracking fields refreshed.

Examples

julia> opt = JuMPOptimiser(; slv = Solver(; solver = nothing));julia> PortfolioOptimisers.factory(opt, fill(0.1, 10)) isa JuMPOptimisertrue

Related

source
PortfolioOptimisers.port_opt_viewMethod
port_opt_view(
    opt::JuMPOptimiser,
    i,
    X::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
    args...
) -> JuMPOptimiser{_A, _B, _C, _D, _E, _F, Bool, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, _Z1, var"#s185", var"#s1851", _Z2, _Z3, _Z4, _Z5, _Z6, _Z7, _Z8, _Z9, _Z10, _Z11, Bool, Symbol, Bool} where {_A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, _Z1, var"#s185"<:Number, var"#s1851"<:Number, _Z2, _Z3, _Z4, _Z5, _Z6, _Z7, _Z8, _Z9, _Z10, _Z11}

Return a cluster-sliced copy of opt restricted to asset indices i.

Slices all per-asset estimator fields (prior, weight bounds, thresholds, turnover, fees, tracking, custom constraint/objective) to the cluster; solver and scalar parameters are carried through unchanged.

Arguments

  • opt::JuMPOptimiser: JuMP optimiser configuration.
  • i: Asset index or index set for the cluster.
  • X::MatNum: Asset returns matrix (observations x assets) used to slice tracking estimators. A precomputed prior in opt.pe supplies its own X instead.

Returns

  • JuMPOptimiser: Cluster-restricted optimiser.

Examples

julia> opt = JuMPOptimiser(; slv = Solver(; solver = nothing));julia> X = rand(50, 5);julia> PortfolioOptimisers.port_opt_view(opt, 1:3, X) isa JuMPOptimisertrue

Related

source
PortfolioOptimisers.needs_previous_weightsMethod
needs_previous_weights(opt::JuMPOptimiser) -> Any

Return true if any sub-estimator of opt requires previous portfolio weights.

Checks turnover, fees, tracking error, custom constraint, and custom objective fields.

Arguments

  • opt::JuMPOptimiser: JuMP optimiser configuration.

Returns

  • Bool: true if any sub-estimator needs previous weights; false otherwise.

Examples

julia> PortfolioOptimisers.needs_previous_weights(JuMPOptimiser(;                                                                slv = Solver(; solver = nothing)))false

Related

source