The source files can be found in examples/.

Regularisation

This example shows one of the simplest ways to improve the robustness of portfolios, regularisation penalties.

Section 2 states every coefficient as a number. Section 3 puts a Calibration Rule in the same slots, which computes the number from the sample in front of it, and it adds the three norm ceilings l2c, lpc and linfc that bound a norm rather than price one.

using PortfolioOptimisers, PrettyTables# Format for pretty tables.tsfmt = (v, i, j) -> begin    if j == 1        return Date(v)    else        return v    endend;resfmt = (v, i, j) -> begin    if j == 1        return v    else        return isa(v, Number) ? "$(round(v*100, digits=3)) %" : v    endend;mmtfmt = (v, i, j) -> begin    if i == j == 1        return v    else        return isa(v, Number) ? "$(round(v*100, digits=3)) %" : v    endend;summary_row = (data, j) -> begin    if j == 1        return "N/A"    else        return number_effective_assets(data[:, j])    endend
#11 (generic function with 1 method)

1. Setting up

We will use the same data as the previous example.

using CSV, TimeSeries, DataFrames, ClarabelX = TimeArray(CSV.File(joinpath(@__DIR__, "..", "SP500.csv.gz")); timestamp = :Date)[(end - 252):end]pretty_table(X[(end - 5):end]; formatters = [tsfmt])# Compute the returnsrd = prices_to_returns(X)pr = prior(EmpiricalPrior(), rd)slv = [Solver(; name = :clarabel1, solver = Clarabel.Optimizer,              settings = Dict("verbose" => false),              check_sol = (; allow_local = true, allow_almost = true)),       Solver(; name = :clarabel2, solver = Clarabel.Optimizer,              settings = Dict("verbose" => false, "max_step_fraction" => 0.95),              check_sol = (; allow_local = true, allow_almost = true)),       Solver(; name = :clarabel3, solver = Clarabel.Optimizer,              settings = Dict("verbose" => false, "max_step_fraction" => 0.9),              check_sol = (; allow_local = true, allow_almost = true)),       Solver(; name = :clarabel4, solver = Clarabel.Optimizer,              settings = Dict("verbose" => false, "max_step_fraction" => 0.85),              check_sol = (; allow_local = true, allow_almost = true)),       Solver(; name = :clarabel5, solver = Clarabel.Optimizer,              settings = Dict("verbose" => false, "max_step_fraction" => 0.8),              check_sol = (; allow_local = true, allow_almost = true)),       Solver(; name = :clarabel6, solver = Clarabel.Optimizer,              settings = Dict("verbose" => false, "max_step_fraction" => 0.75),              check_sol = (; allow_local = true, allow_almost = true)),       Solver(; name = :clarabel7, solver = Clarabel.Optimizer,              settings = Dict("verbose" => false, "max_step_fraction" => 0.70),              check_sol = (; allow_local = true, allow_almost = true))];
┌────────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐
│  timestamp     AAPL      AMD      BAC      BBY      CVX       GE       HD      JNJ      JPM       KO      LLY      MRK     MSFT      PEP      PFE       PG      RRC      UNH      WMT      XOM │
│       Date  Float64  Float64  Float64  Float64  Float64  Float64  Float64  Float64  Float64  Float64  Float64  Float64  Float64  Float64  Float64  Float64  Float64  Float64  Float64  Float64 │
├────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ 2022-12-20 │ 131.916 │   65.05 │  31.729 │  77.371 │ 169.497 │  62.604 │ 310.342 │ 173.109 │ 127.844 │  61.841 │  357.55 │ 108.229 │  240.67 │ 178.765 │  49.754 │ 147.661 │   25.65 │ 516.245 │ 142.919 │ 104.964 │
│ 2022-12-21 │ 135.057 │   67.68 │  32.212 │  78.729 │  171.49 │   64.67 │ 314.798 │  175.09 │ 129.282 │  62.836 │ 365.872 │ 109.611 │ 243.287 │ 180.017 │  50.084 │ 149.015 │  26.574 │ 523.519 │  144.04 │ 106.312 │
│ 2022-12-22 │ 131.846 │   63.86 │  31.927 │  78.563 │ 168.918 │  63.727 │ 311.604 │  174.45 │ 127.814 │  62.383 │ 363.187 │ 109.739 │ 237.077 │ 178.627 │  50.065 │ 149.359 │  25.232 │ 523.072 │ 142.354 │ 104.168 │
│ 2022-12-23 │ 131.477 │   64.52 │  32.005 │  79.432 │  174.14 │  63.742 │ 314.177 │ 174.893 │ 128.421 │  62.855 │ 365.762 │  110.35 │ 237.614 │ 179.781 │  50.249 │ 149.781 │  26.226 │  527.26 │ 142.641 │ 106.922 │
│ 2022-12-27 │ 129.652 │   63.27 │  32.065 │   79.93 │ 176.329 │  64.561 │ 314.985 │ 174.844 │ 128.871 │   63.24 │  362.76 │ 110.607 │ 235.852 │  180.58 │   49.57 │ 151.086 │  26.375 │ 527.935 │ 142.681 │ 108.408 │
│ 2022-12-28 │ 125.674 │   62.57 │  32.301 │  78.279 │ 173.728 │  63.883 │  311.22 │ 174.085 │ 129.575 │  62.609 │ 363.098 │ 109.581 │ 233.434 │ 179.278 │   49.25 │ 149.133 │  24.497 │ 524.422 │ 140.181 │ 106.627 │
└────────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘

2. Regularised portfolios

The optimal regularisation penalty value depends on the data, the investor preferences, and type of regularisation. The specific choice of penalty value is so volatile that it can only be estimated via grid search cross-validation or similar techniques, but the "optimal" (to some definition of optimal) value will also change over time as the market conditions change. Therefore, we will simply show how to set up and solve a regularised portfolio optimisation problem, without attempting to find the optimal penalty value.

We will use the same small penalty for all regularisations to illustrate how they differ.

  • L1 regularisation (also known as Lasso regularisation) adds a penalty proportional to the sum of the absolute values of the portfolio weights. This encourages sparsity in the portfolio, leading to fewer assets being selected.
  • L2 regularisation (also known as Ridge regularisation) adds a penalty proportional to the sum of the squares of the portfolio weights. This discourages large weights and promotes diversification.
  • Lp regularisation via LpRegularisation adds a penalty proportional to the p-norm of the portfolio weights, where p > 1 is a positive real number.
  • L-Inf regularisation adds a penalty proportional to the maximum absolute value of the portfolio weights. This limits the influence of any single asset in the portfolio.

2.1 Efficient frontier

opts = [JuMPOptimiser(; pe = pr, slv = slv, wb = WeightBounds(; lb = -1, ub = 1), sbgt = 1,                      bgt = 1,                      ret = ArithmeticReturn(;                                             settings = JuMPReturnsSettings(;                                                                            lb = Frontier(;                                                                                          N = 50)))),#        JuMPOptimiser(; pe = pr, slv = slv, wb = WeightBounds(; lb = -1, ub = 1), sbgt = 1,                      ret = ArithmeticReturn(;                                             settings = JuMPReturnsSettings(;                                                                            lb = Frontier(;                                                                                          N = 50))),                      bgt = 1, l1 = 4e-4),#        JuMPOptimiser(; pe = pr, slv = slv, wb = WeightBounds(; lb = -1, ub = 1), sbgt = 1,                      ret = ArithmeticReturn(;                                             settings = JuMPReturnsSettings(;                                                                            lb = Frontier(;                                                                                          N = 50))),                      bgt = 1, l2 = L2Regularisation(; val = 4e-4)),#        JuMPOptimiser(; pe = pr, slv = slv, wb = WeightBounds(; lb = -1, ub = 1), sbgt = 1,                      ret = ArithmeticReturn(;                                             settings = JuMPReturnsSettings(;                                                                            lb = Frontier(;                                                                                          N = 50))),                      bgt = 1, lp = LpRegularisation(; p = 5, val = 4e-4)),#        JuMPOptimiser(; pe = pr, slv = slv, wb = WeightBounds(; lb = -1, ub = 1), sbgt = 1,                      ret = ArithmeticReturn(;                                             settings = JuMPReturnsSettings(;                                                                            lb = Frontier(;                                                                                          N = 50))),                      bgt = 1, linf = 4e-4)]nocs = [MeanRisk(; opt = opt) for opt in opts]ress = optimise.(nocs)
5-element Vector{MeanRiskResult{JuMPOptimisationResult{ProcessedJuMPOptimiserAttributes{LowOrderPrior{Matrix{Float64}, Nothing, Vector{Float64}, Matrix{Float64}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing}, WeightBounds{StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}, StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, ArithmeticReturn{JuMPReturnsSettings{Float64, Frontier{Int64, Int64, LinearBound}, Bool, Bool, Bool}, Nothing, Vector{Float64}}, SumScalariser, Nothing}, Vector{OptimisationReturnCode}, Vector{JuMPOptimisationSolution}, Model}, Variance{RiskMeasureSettings{Float64, Nothing, Bool}, Matrix{Float64}, Nothing, Nothing, SquaredSOCRiskExpr}, Nothing}}:
 MeanRiskResult
  jr ┼ JuMPOptimisationResult
     │        pa ┼ ProcessedJuMPOptimiserAttributes
     │           │        pr ┼ LowOrderPrior
     │           │           │       X ┼ 252×20 Matrix{Float64}
     │           │           │     o_X ┼ nothing
     │           │           │      mu ┼ 20-element Vector{Float64}
     │           │           │   sigma ┼ 20×20 Matrix{Float64}
     │           │           │    chol ┼ nothing
     │           │           │       w ┼ nothing
     │           │           │     ens ┼ nothing
     │           │           │     kld ┼ nothing
     │           │           │      ow ┼ nothing
     │           │           │      rr ┼ nothing
     │           │           │     fpr ┴ nothing
     │           │        wb ┼ WeightBounds
     │           │           │   lb ┼ 20-element StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}
     │           │           │   ub ┴ 20-element StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}
     │           │        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
     │           │           │   settings ┼ JuMPReturnsSettings
     │           │           │            │   scale ┼ Float64: 1.0
     │           │           │            │      lb ┼ Frontier
     │           │           │            │         │        N ┼ Int64: 50
     │           │           │            │         │   factor ┼ Int64: 1
     │           │           │            │         │    bound ┴ LinearBound()
     │           │           │            │     rte ┼ Bool: true
     │           │           │            │     fee ┼ Bool: true
     │           │           │            │     mic ┴ Bool: true
     │           │           │        ucs ┼ nothing
     │           │           │         mu ┴ 20-element Vector{Float64}
     │           │       sca ┼ SumScalariser()
     │           │      imsk ┴ nothing
     │   retcode ┼ 50-element Vector{OptimisationSuccess}
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │       sol ┼ 50-element Vector{JuMPOptimisationSolution}
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │     model ┼ A JuMP Model
     │           │ ├ solver: Clarabel
     │           │ ├ objective_sense: MIN_SENSE
     │           │ │ └ objective_function_type: QuadExpr
     │           │ ├ num_variables: 62
     │           │ ├ num_constraints: 50
     │           │ │ ├ AffExpr in MOI.EqualTo{Float64}: 3
     │           │ │ ├ AffExpr in MOI.GreaterThan{Float64}: 1
     │           │ │ ├ Vector{AffExpr} in MOI.Nonnegatives: 2
     │           │ │ ├ Vector{AffExpr} in MOI.Nonpositives: 2
     │           │ │ ├ Vector{AffExpr} in MOI.SecondOrderCone: 1
     │           │ │ ├ VariableRef in MOI.GreaterThan{Float64}: 40
     │           │ │ └ VariableRef in MOI.Parameter{Float64}: 1
     │           │ └ Names registered in the model
     │           │   └ :G, :T, :bgt, :cdev_soc_1, :decomposition_contract, :dev_1, :k, :lbgt, :lw, :obj_expr, :ret, :ret_1, :ret_frontier, :ret_lb_1, :ret_lb_var_1, :ret_vec, :risk, :risk_vec, :sbgt, :sc, :so, :sw, :variance_flag, :variance_risk_1, :w, :w_lb, :w_lw, :w_sw, :w_ub
   r ┼ Variance
     │   settings ┼ RiskMeasureSettings
     │            │   scale ┼ Float64: 1.0
     │            │      ub ┼ nothing
     │            │     rke ┴ Bool: true
     │      sigma ┼ 20×20 Matrix{Float64}
     │       chol ┼ nothing
     │         rc ┼ nothing
     │        alg ┴ SquaredSOCRiskExpr()
  fb ┴ nothing

 MeanRiskResult
  jr ┼ JuMPOptimisationResult
     │        pa ┼ ProcessedJuMPOptimiserAttributes
     │           │        pr ┼ LowOrderPrior
     │           │           │       X ┼ 252×20 Matrix{Float64}
     │           │           │     o_X ┼ nothing
     │           │           │      mu ┼ 20-element Vector{Float64}
     │           │           │   sigma ┼ 20×20 Matrix{Float64}
     │           │           │    chol ┼ nothing
     │           │           │       w ┼ nothing
     │           │           │     ens ┼ nothing
     │           │           │     kld ┼ nothing
     │           │           │      ow ┼ nothing
     │           │           │      rr ┼ nothing
     │           │           │     fpr ┴ nothing
     │           │        wb ┼ WeightBounds
     │           │           │   lb ┼ 20-element StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}
     │           │           │   ub ┴ 20-element StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}
     │           │        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
     │           │           │   settings ┼ JuMPReturnsSettings
     │           │           │            │   scale ┼ Float64: 1.0
     │           │           │            │      lb ┼ Frontier
     │           │           │            │         │        N ┼ Int64: 50
     │           │           │            │         │   factor ┼ Int64: 1
     │           │           │            │         │    bound ┴ LinearBound()
     │           │           │            │     rte ┼ Bool: true
     │           │           │            │     fee ┼ Bool: true
     │           │           │            │     mic ┴ Bool: true
     │           │           │        ucs ┼ nothing
     │           │           │         mu ┴ 20-element Vector{Float64}
     │           │       sca ┼ SumScalariser()
     │           │      imsk ┴ nothing
     │   retcode ┼ 50-element Vector{OptimisationSuccess}
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │       sol ┼ 50-element Vector{JuMPOptimisationSolution}
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │     model ┼ A JuMP Model
     │           │ ├ solver: Clarabel
     │           │ ├ objective_sense: MIN_SENSE
     │           │ │ └ objective_function_type: QuadExpr
     │           │ ├ num_variables: 63
     │           │ ├ num_constraints: 51
     │           │ │ ├ AffExpr in MOI.EqualTo{Float64}: 3
     │           │ │ ├ AffExpr in MOI.GreaterThan{Float64}: 1
     │           │ │ ├ Vector{AffExpr} in MOI.Nonnegatives: 2
     │           │ │ ├ Vector{AffExpr} in MOI.Nonpositives: 2
     │           │ │ ├ Vector{AffExpr} in MOI.NormOneCone: 1
     │           │ │ ├ Vector{AffExpr} in MOI.SecondOrderCone: 1
     │           │ │ ├ VariableRef in MOI.GreaterThan{Float64}: 40
     │           │ │ └ VariableRef in MOI.Parameter{Float64}: 1
     │           │ └ Names registered in the model
     │           │   └ :G, :T, :bgt, :cdev_soc_1, :cl1_noc, :decomposition_contract, :dev_1, :k, :l1, :lbgt, :lw, :obj_expr, :op, :ret, :ret_1, :ret_frontier, :ret_lb_1, :ret_lb_var_1, :ret_vec, :risk, :risk_vec, :sbgt, :sc, :so, :sw, :t_l1, :variance_flag, :variance_risk_1, :w, :w_lb, :w_lw, :w_sw, :w_ub
   r ┼ Variance
     │   settings ┼ RiskMeasureSettings
     │            │   scale ┼ Float64: 1.0
     │            │      ub ┼ nothing
     │            │     rke ┴ Bool: true
     │      sigma ┼ 20×20 Matrix{Float64}
     │       chol ┼ nothing
     │         rc ┼ nothing
     │        alg ┴ SquaredSOCRiskExpr()
  fb ┴ nothing

 MeanRiskResult
  jr ┼ JuMPOptimisationResult
     │        pa ┼ ProcessedJuMPOptimiserAttributes
     │           │        pr ┼ LowOrderPrior
     │           │           │       X ┼ 252×20 Matrix{Float64}
     │           │           │     o_X ┼ nothing
     │           │           │      mu ┼ 20-element Vector{Float64}
     │           │           │   sigma ┼ 20×20 Matrix{Float64}
     │           │           │    chol ┼ nothing
     │           │           │       w ┼ nothing
     │           │           │     ens ┼ nothing
     │           │           │     kld ┼ nothing
     │           │           │      ow ┼ nothing
     │           │           │      rr ┼ nothing
     │           │           │     fpr ┴ nothing
     │           │        wb ┼ WeightBounds
     │           │           │   lb ┼ 20-element StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}
     │           │           │   ub ┴ 20-element StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}
     │           │        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
     │           │           │   settings ┼ JuMPReturnsSettings
     │           │           │            │   scale ┼ Float64: 1.0
     │           │           │            │      lb ┼ Frontier
     │           │           │            │         │        N ┼ Int64: 50
     │           │           │            │         │   factor ┼ Int64: 1
     │           │           │            │         │    bound ┴ LinearBound()
     │           │           │            │     rte ┼ Bool: true
     │           │           │            │     fee ┼ Bool: true
     │           │           │            │     mic ┴ Bool: true
     │           │           │        ucs ┼ nothing
     │           │           │         mu ┴ 20-element Vector{Float64}
     │           │       sca ┼ SumScalariser()
     │           │      imsk ┴ nothing
     │   retcode ┼ 50-element Vector{OptimisationSuccess}
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │       sol ┼ 50-element Vector{JuMPOptimisationSolution}
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │     model ┼ A JuMP Model
     │           │ ├ solver: Clarabel
     │           │ ├ objective_sense: MIN_SENSE
     │           │ │ └ objective_function_type: QuadExpr
     │           │ ├ num_variables: 63
     │           │ ├ num_constraints: 51
     │           │ │ ├ AffExpr in MOI.EqualTo{Float64}: 3
     │           │ │ ├ AffExpr in MOI.GreaterThan{Float64}: 1
     │           │ │ ├ Vector{AffExpr} in MOI.Nonnegatives: 2
     │           │ │ ├ Vector{AffExpr} in MOI.Nonpositives: 2
     │           │ │ ├ Vector{AffExpr} in MOI.SecondOrderCone: 2
     │           │ │ ├ VariableRef in MOI.GreaterThan{Float64}: 40
     │           │ │ └ VariableRef in MOI.Parameter{Float64}: 1
     │           │ └ Names registered in the model
     │           │   └ :G, :T, :bgt, :cdev_soc_1, :cl2_soc_1, :decomposition_contract, :dev_1, :k, :l2_1, :lbgt, :lw, :obj_expr, :op, :ret, :ret_1, :ret_frontier, :ret_lb_1, :ret_lb_var_1, :ret_vec, :risk, :risk_vec, :sbgt, :sc, :so, :sw, :t_l2_1, :variance_flag, :variance_risk_1, :w, :w_lb, :w_lw, :w_sw, :w_ub
   r ┼ Variance
     │   settings ┼ RiskMeasureSettings
     │            │   scale ┼ Float64: 1.0
     │            │      ub ┼ nothing
     │            │     rke ┴ Bool: true
     │      sigma ┼ 20×20 Matrix{Float64}
     │       chol ┼ nothing
     │         rc ┼ nothing
     │        alg ┴ SquaredSOCRiskExpr()
  fb ┴ nothing

 MeanRiskResult
  jr ┼ JuMPOptimisationResult
     │        pa ┼ ProcessedJuMPOptimiserAttributes
     │           │        pr ┼ LowOrderPrior
     │           │           │       X ┼ 252×20 Matrix{Float64}
     │           │           │     o_X ┼ nothing
     │           │           │      mu ┼ 20-element Vector{Float64}
     │           │           │   sigma ┼ 20×20 Matrix{Float64}
     │           │           │    chol ┼ nothing
     │           │           │       w ┼ nothing
     │           │           │     ens ┼ nothing
     │           │           │     kld ┼ nothing
     │           │           │      ow ┼ nothing
     │           │           │      rr ┼ nothing
     │           │           │     fpr ┴ nothing
     │           │        wb ┼ WeightBounds
     │           │           │   lb ┼ 20-element StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}
     │           │           │   ub ┴ 20-element StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}
     │           │        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
     │           │           │   settings ┼ JuMPReturnsSettings
     │           │           │            │   scale ┼ Float64: 1.0
     │           │           │            │      lb ┼ Frontier
     │           │           │            │         │        N ┼ Int64: 50
     │           │           │            │         │   factor ┼ Int64: 1
     │           │           │            │         │    bound ┴ LinearBound()
     │           │           │            │     rte ┼ Bool: true
     │           │           │            │     fee ┼ Bool: true
     │           │           │            │     mic ┴ Bool: true
     │           │           │        ucs ┼ nothing
     │           │           │         mu ┴ 20-element Vector{Float64}
     │           │       sca ┼ SumScalariser()
     │           │      imsk ┴ nothing
     │   retcode ┼ 50-element Vector{OptimisationSuccess}
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │       sol ┼ 50-element Vector{JuMPOptimisationSolution}
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │     model ┼ A JuMP Model
     │           │ ├ solver: Clarabel
     │           │ ├ objective_sense: MIN_SENSE
     │           │ │ └ objective_function_type: QuadExpr
     │           │ ├ num_variables: 83
     │           │ ├ num_constraints: 71
     │           │ │ ├ AffExpr in MOI.EqualTo{Float64}: 4
     │           │ │ ├ AffExpr in MOI.GreaterThan{Float64}: 1
     │           │ │ ├ Vector{AffExpr} in MOI.Nonnegatives: 2
     │           │ │ ├ Vector{AffExpr} in MOI.Nonpositives: 2
     │           │ │ ├ Vector{AffExpr} in MOI.SecondOrderCone: 1
     │           │ │ ├ Vector{AffExpr} in MOI.PowerCone{Float64}: 20
     │           │ │ ├ VariableRef in MOI.GreaterThan{Float64}: 40
     │           │ │ └ VariableRef in MOI.Parameter{Float64}: 1
     │           │ └ Names registered in the model
     │           │   └ :G, :T, :bgt, :cdev_soc_1, :clp_1, :cslp_1, :decomposition_contract, :dev_1, :k, :lbgt, :lp_1, :lw, :obj_expr, :op, :r_lp_1, :ret, :ret_1, :ret_frontier, :ret_lb_1, :ret_lb_var_1, :ret_vec, :risk, :risk_vec, :sbgt, :sc, :so, :sw, :t_lp_1, :variance_flag, :variance_risk_1, :w, :w_lb, :w_lw, :w_sw, :w_ub
   r ┼ Variance
     │   settings ┼ RiskMeasureSettings
     │            │   scale ┼ Float64: 1.0
     │            │      ub ┼ nothing
     │            │     rke ┴ Bool: true
     │      sigma ┼ 20×20 Matrix{Float64}
     │       chol ┼ nothing
     │         rc ┼ nothing
     │        alg ┴ SquaredSOCRiskExpr()
  fb ┴ nothing

 MeanRiskResult
  jr ┼ JuMPOptimisationResult
     │        pa ┼ ProcessedJuMPOptimiserAttributes
     │           │        pr ┼ LowOrderPrior
     │           │           │       X ┼ 252×20 Matrix{Float64}
     │           │           │     o_X ┼ nothing
     │           │           │      mu ┼ 20-element Vector{Float64}
     │           │           │   sigma ┼ 20×20 Matrix{Float64}
     │           │           │    chol ┼ nothing
     │           │           │       w ┼ nothing
     │           │           │     ens ┼ nothing
     │           │           │     kld ┼ nothing
     │           │           │      ow ┼ nothing
     │           │           │      rr ┼ nothing
     │           │           │     fpr ┴ nothing
     │           │        wb ┼ WeightBounds
     │           │           │   lb ┼ 20-element StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}
     │           │           │   ub ┴ 20-element StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}
     │           │        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
     │           │           │   settings ┼ JuMPReturnsSettings
     │           │           │            │   scale ┼ Float64: 1.0
     │           │           │            │      lb ┼ Frontier
     │           │           │            │         │        N ┼ Int64: 50
     │           │           │            │         │   factor ┼ Int64: 1
     │           │           │            │         │    bound ┴ LinearBound()
     │           │           │            │     rte ┼ Bool: true
     │           │           │            │     fee ┼ Bool: true
     │           │           │            │     mic ┴ Bool: true
     │           │           │        ucs ┼ nothing
     │           │           │         mu ┴ 20-element Vector{Float64}
     │           │       sca ┼ SumScalariser()
     │           │      imsk ┴ nothing
     │   retcode ┼ 50-element Vector{OptimisationSuccess}
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │           │ OptimisationSuccess ⋯
     │       sol ┼ 50-element Vector{JuMPOptimisationSolution}
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │           │ JuMPOptimisationSolution ⋯
     │     model ┼ A JuMP Model
     │           │ ├ solver: Clarabel
     │           │ ├ objective_sense: MIN_SENSE
     │           │ │ └ objective_function_type: QuadExpr
     │           │ ├ num_variables: 63
     │           │ ├ num_constraints: 51
     │           │ │ ├ AffExpr in MOI.EqualTo{Float64}: 3
     │           │ │ ├ AffExpr in MOI.GreaterThan{Float64}: 1
     │           │ │ ├ Vector{AffExpr} in MOI.Nonnegatives: 2
     │           │ │ ├ Vector{AffExpr} in MOI.Nonpositives: 2
     │           │ │ ├ Vector{AffExpr} in MOI.NormInfinityCone: 1
     │           │ │ ├ Vector{AffExpr} in MOI.SecondOrderCone: 1
     │           │ │ ├ VariableRef in MOI.GreaterThan{Float64}: 40
     │           │ │ └ VariableRef in MOI.Parameter{Float64}: 1
     │           │ └ Names registered in the model
     │           │   └ :G, :T, :bgt, :cdev_soc_1, :clinf_nic, :decomposition_contract, :dev_1, :k, :lbgt, :linf, :lw, :obj_expr, :op, :ret, :ret_1, :ret_frontier, :ret_lb_1, :ret_lb_var_1, :ret_vec, :risk, :risk_vec, :sbgt, :sc, :so, :sw, :t_linf, :variance_flag, :variance_risk_1, :w, :w_lb, :w_lw, :w_sw, :w_ub
   r ┼ Variance
     │   settings ┼ RiskMeasureSettings
     │            │   scale ┼ Float64: 1.0
     │            │      ub ┼ nothing
     │            │     rke ┴ Bool: true
     │      sigma ┼ 20×20 Matrix{Float64}
     │       chol ┼ nothing
     │         rc ┼ nothing
     │        alg ┴ SquaredSOCRiskExpr()
  fb ┴ nothing

Let's plot the efficient frontiers.

using StatsPlots, GraphRecipesr = Variance()
Variance
  settings ┼ RiskMeasureSettings
           │   scale ┼ Float64: 1.0
           │      ub ┼ nothing
           │     rke ┴ Bool: true
     sigma ┼ nothing
      chol ┼ nothing
        rc ┼ nothing
       alg ┴ SquaredSOCRiskExpr()

No regularisation portfolio weights.

plot_stacked_area_composition(ress[1].w, rd.nx;                              kwargs = (; xlabel = "Portfolios", ylabel = "Weight",                                        title = "No regularisation", legend = :outerright))
Example block output

No regularisation frontier.

plot_measures(ress[1].w, pr; x = r, y = ExpectedReturn(; rt = ress[1].ret),              c = ExpectedReturnRiskRatio(; rt = ress[1].ret, rk = r, rf = 4.2 / 100 / 252),              title = "No regularisation", xlabel = "Variance",              ylabel = "Arithmetic Return", colorbar_title = "\nRisk/Return Ratio",              right_margin = 6Plots.mm)
Example block output

L1 regularisation portfolio weights. As expected, the portfolio is sparsified, with fewer assets with non-zero weight.

plot_stacked_area_composition(ress[2].w, rd.nx;                              kwargs = (; xlabel = "Portfolios", ylabel = "Weight",                                        title = "L1 regularisation", legend = :outerright))
Example block output

L1 regularisation frontier. The sparsification makes the pareto front non-smooth.

plot_measures(ress[2].w, pr; x = r, y = ExpectedReturn(; rt = ress[2].ret),              c = ExpectedReturnRiskRatio(; rt = ress[1].ret, rk = r, rf = 4.2 / 100 / 252),              title = "L1 regularisation", xlabel = "Variance",              ylabel = "Arithmetic Return", colorbar_title = "\nRisk/Return Ratio",              right_margin = 6Plots.mm)
Example block output

L2 regularisation portfolio weights. Even values of p-norms smooth out the weights, leading to more diversified portfolios. The higher the value, the more highly penalised larger deviations from the mean weight become. This is similar to how moments of even order behave.

plot_stacked_area_composition(ress[3].w, rd.nx;                              kwargs = (; xlabel = "Portfolios", ylabel = "Weight",                                        title = "L2 regularisation", legend = :outerright))
Example block output

L2 regularisation frontier.

plot_measures(ress[3].w, pr; x = r, y = ExpectedReturn(; rt = ress[3].ret),              c = ExpectedReturnRiskRatio(; rt = ress[1].ret, rk = r, rf = 4.2 / 100 / 252),              title = "L2 regularisation", xlabel = "Variance",              ylabel = "Arithmetic Return", colorbar_title = "\nRisk/Return Ratio",              right_margin = 6Plots.mm)
Example block output

Lp regularisation portfolio weights. The higher the value of p, the closer the behaviour is to L-Inf regularisation, where the maximum absolute weight is penalised. This leads to portfolios where all weights are more similar in magnitude, but does not smear the negative weights into positive values like the L2 norm.

plot_stacked_area_composition(ress[4].w, rd.nx;                              kwargs = (; xlabel = "Portfolios", ylabel = "Weight",                                        title = "Lp (p = 5) regularisation",                                        legend = :outerright))
Example block output

Lp regularisation frontier.

plot_measures(ress[4].w, pr; x = r, y = ExpectedReturn(; rt = ress[4].ret),              c = ExpectedReturnRiskRatio(; rt = ress[1].ret, rk = r, rf = 4.2 / 100 / 252),              title = "Lp (p = 5) regularisation", xlabel = "Variance",              ylabel = "Arithmetic Return", colorbar_title = "\nRisk/Return Ratio",              right_margin = 6Plots.mm)
Example block output

L-Inf regularisation portfolio weights.

plot_stacked_area_composition(ress[5].w, rd.nx;                              kwargs = (; xlabel = "Portfolios", ylabel = "Weight",                                        title = "L-Inf regularisation",                                        legend = :outerright))
Example block output

L-Inf regularisation frontier.

plot_measures(ress[5].w, pr; x = r, y = ExpectedReturn(; rt = ress[5].ret),              c = ExpectedReturnRiskRatio(; rt = ress[1].ret, rk = r, rf = 4.2 / 100 / 252),              title = "L-Inf regularisation", xlabel = "Variance",              ylabel = "Arithmetic Return", colorbar_title = "\nRisk/Return Ratio",              right_margin = 6Plots.mm)
Example block output

2.2 Minimum risk portfolios

Lets view only the minimum risk portfolios for each regularisation to get more insight into what regularisation does.

opts = [JuMPOptimiser(; pe = pr, slv = slv, wb = WeightBounds(; lb = -1, ub = 1), sbgt = 1,                      bgt = 1),# no regularisation        JuMPOptimiser(; pe = pr, slv = slv, wb = WeightBounds(; lb = -1, ub = 1), sbgt = 1,                      bgt = 1, l1 = 4e-4),# L1 regularisation        JuMPOptimiser(; pe = pr, slv = slv, wb = WeightBounds(; lb = -1, ub = 1), sbgt = 1,                      bgt = 1, l2 = L2Regularisation(; val = 4e-4)),# L2 regularisation        JuMPOptimiser(; pe = pr, slv = slv, wb = WeightBounds(; lb = -1, ub = 1), sbgt = 1,                      bgt = 1, lp = LpRegularisation(; p = 5, val = 4e-4)),# Lp regularisation with p = 5        JuMPOptimiser(; pe = pr, slv = slv, wb = WeightBounds(; lb = -1, ub = 1), sbgt = 1,                      bgt = 1, linf = 4e-4)]# L-Inf regularisationnocs = [MeanRisk(; opt = opt) for opt in opts]ress = optimise.(nocs)pretty_table(DataFrame(:Assets => rd.nx, :No_Reg => ress[1].w, :L1 => ress[2].w,                       :L2 => ress[3].w, :L5 => ress[4].w, :LInf => ress[5].w);             formatters = [resfmt], summary_rows = [summary_row],             summary_row_labels = ["# Eff. Assets"])
┌───────────────┬────────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│                Assets     No_Reg        L1        L2        L5      LInf │
│                String    Float64   Float64   Float64   Float64   Float64 │
├───────────────┼────────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│               │   AAPL │ -11.866 % │    0.0 % │  1.136 % │ -3.026 % │ -5.751 % │
│               │    AMD │   -1.55 % │    0.0 % │ -2.963 % │ -6.029 % │ -7.437 % │
│               │    BAC │   -5.46 % │    0.0 % │  3.488 % │   4.28 % │  1.181 % │
│               │    BBY │  -3.234 % │    0.0 % │  1.035 % │ -1.015 % │ -1.935 % │
│               │    CVX │   9.487 % │  7.415 % │   6.57 % │  7.553 % │  7.879 % │
│               │     GE │   5.203 % │  0.823 % │  3.871 % │  5.501 % │  6.838 % │
│               │     HD │   3.621 % │    0.0 % │  4.194 % │  6.143 % │  7.879 % │
│               │    JNJ │  40.958 % │ 36.981 % │ 10.221 % │  9.019 % │  7.879 % │
│               │    JPM │   7.738 % │  0.762 % │  4.252 % │  5.446 % │  7.879 % │
│               │     KO │    12.0 % │ 11.098 % │  7.952 % │  8.217 % │  7.879 % │
│               │    LLY │  -7.707 % │    0.0 % │  5.655 % │  6.507 % │  7.878 % │
│               │    MRK │  19.792 % │ 17.462 % │  9.638 % │  8.719 % │  7.879 % │
│               │   MSFT │   8.105 % │    0.0 % │  2.536 % │  4.917 % │  7.071 % │
│               │    PEP │    13.7 % │  8.969 % │  7.992 % │  8.234 % │  7.879 % │
│               │    PFE │  -4.513 % │    0.0 % │  5.999 % │  6.673 % │  7.879 % │
│               │     PG │   1.787 % │  2.399 % │  7.873 % │  8.145 % │  7.879 % │
│               │    RRC │   -1.68 % │    0.0 % │  0.153 % │   -1.8 % │ -2.388 % │
│               │    UNH │  -1.188 % │    0.0 % │  5.939 % │  6.862 % │  7.879 % │
│               │    WMT │   9.161 % │  9.356 % │  8.205 % │   8.32 % │  7.879 % │
│               │    XOM │   5.649 % │  4.734 % │  6.254 % │  7.334 % │  7.879 % │
├───────────────┼────────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ # Eff. Assets │    N/A │   3.28601 │  4.88263 │  13.8946 │   11.588 │    9.971 │
└───────────────┴────────┴───────────┴──────────┴──────────┴──────────┴──────────┘

The effect of each regularisation depends on the relative values of the objective function with respect to the value of the relevant norm of the optimised portfolio weights multiplied by the penalty.

Generally, regularised portfolios tend to have more effective assets than unregularised ones. The number of effective assets is different to the sparsity in that it measures the concentration of weights as 1/(w ⋅ w), rather than counting the number of non-zero (or near zero) weights. Usually, the larger the number of effective assets, the more diversified the portfolio. Sparsity is a non-smooth measure, while the number of effective assets is smooth, so a portfolio can have higher sparsity and still have a larger number of effective assets.

It is possible to combine multiple regularisation penalties in the same optimisation problem by simultaneously specifying multiple regularisation keywords in the JuMPOptimiser. This can be useful to combine the benefits of different regularisations, such as sparsity and diversification, but can make the optimisation more difficult to solve and interpret.

3. A rule in place of a penalty value

Every coefficient section 2 states as a number is a calibration slot. A slot takes the number itself, and it takes a Calibration Rule, which computes the number from the prior result of the sample in front of it. The rule resolves where the model is built, so a cross-validation over folds of unequal length refits the coefficient on every fold and nothing else about the optimiser moves. The calibration example shows the slot on a risk measure. This section reads all seven slots this example owns.

The four penalty coefficients are ambiguity radii. A norm penalty is the support function of a ball in the dual of the penalised norm, so its coefficient is the radius of that ball and its slot takes a rule of the ambiguity-radius family.

The three norm ceilings l2c, lpc and linfc bound a norm rather than price one, so they are a different quantity and their slots take a family of their own, the norm-ceiling rules. A ceiling is a diversification statement: its reciprocal is a floor on the effective number of assets.

3.1 One rule reads the slot it stands in

DualNormRadius returns the sampling error of the mean vector, measured in the ground metric of the slot. The ground metric is the dual of the norm the slot penalises, so the rule reads the slot's own key and answers a different number for every key. Every other radius rule returns one number for every slot.

numfmt = (v, i, j) -> begin    return isa(v, AbstractFloat) ? round(v; sigdigits = 4) : vend;dnr = DualNormRadius(; confidence = 0.95)lp5 = CalibrationContext(; p = 5)radius_table = DataFrame(:slot => ["l1", "linf", "l2, val", "lp, val, p = 5"],                         :penalised_norm => ["1", "Inf", "2", "5"],                         :ground_metric => ["Inf", "1", "2", "1.25"],                         :radius => [dnr(:l1, pr, nothing, nothing, CalibrationContext()),                                     dnr(:linf, pr, nothing, nothing, CalibrationContext()),                                     dnr(:l2reg_val, pr, nothing, nothing, CalibrationContext()),                                     dnr(:lpreg_val, pr, nothing, nothing, lp5)])pretty_table(radius_table; formatters = [numfmt])
┌────────────────┬────────────────┬───────────────┬──────────┐
│           slot  penalised_norm  ground_metric    radius │
│         String          String         String   Float64 │
├────────────────┼────────────────┼───────────────┼──────────┤
│             l1 │              1 │           Inf │   0.0041 │
│           linf │            Inf │             1 │  0.04173 │
│        l2, val │              2 │             2 │ 0.009974 │
│ lp, val, p = 5 │              5 │          1.25 │   0.0233 │
└────────────────┴────────────────┴───────────────┴──────────┘

The l1 and linf rows are the reading to take away. One rule, one sample and one confidence level give two coefficients an order of magnitude apart, because an L1 penalty is priced in the ∞-norm of the error and an L-Inf penalty in its 1-norm. The lp row needs the penalty's own p, because no key can name the conjugate order. The rule holds no order of its own: the penalty site states it in a CalibrationContext, and the lp5 context above stands in for that site because the rule runs outside it here.

The slot takes the rule, and the coefficient appears when the model is built. The two runs below differ only in what stands in l1, and they hold the same weights.

l1_rule = JuMPOptimiser(; pe = pr, slv = slv, wb = WeightBounds(; lb = -1, ub = 1),                        sbgt = 1, bgt = 1, l1 = DualNormRadius())l1_num = JuMPOptimiser(; pe = pr, slv = slv, wb = WeightBounds(; lb = -1, ub = 1), sbgt = 1,                       bgt = 1, l1 = dnr(:l1, pr, nothing, nothing, CalibrationContext()))res_rule = optimise(MeanRisk(; opt = l1_rule))res_num = optimise(MeanRisk(; opt = l1_num))println("largest weight difference = $(maximum(abs, res_rule.w - res_num.w))")
largest weight difference = 0.0

3.2 Two rates, and the one the universe sets

RateRadius shrinks the ball as c / sqrt(T), which is the rate a sample mean's own error falls at. DimensionalRateRadius shrinks it at the rate the number of assets sets, which is far slower over a wide universe: the exponent is 1 / max(N, 2) rather than 1 / 2. The table reads both rules over three windows of the same record.

X_all = TimeArray(CSV.File(joinpath(@__DIR__, "..", "SP500.csv.gz")); timestamp = :Date)rate_table = DataFrame()for T in (252, 630, 1260)    pr_T = prior(EmpiricalPrior(), prices_to_returns(X_all[(end - T):end]))    push!(rate_table,          (; T = size(pr_T.X, 1), N = size(pr_T.X, 2),           rate = RateRadius(; c = 0.02)(:l1, pr_T, nothing, nothing, CalibrationContext()),           dimensional = DimensionalRateRadius(; confidence = 0.95)(:l1, pr_T, nothing,                                                                    nothing,                                                                    CalibrationContext())))endpretty_table(rate_table; formatters = [numfmt])
┌───────┬───────┬───────────┬─────────────┐
│     T      N       rate  dimensional │
│ Int64  Int64    Float64      Float64 │
├───────┼───────┼───────────┼─────────────┤
│   252 │    20 │   0.00126 │     0.01613 │
│   630 │    20 │ 0.0007968 │     0.01441 │
│  1260 │    20 │ 0.0005634 │     0.01537 │
└───────┴───────┴───────────┴─────────────┘

The rate radius falls by the square root of the ratio of the lengths. The dimensional radius barely moves, and it does not fall monotonically either: over this universe the exponent is 1 / 20, so a fivefold record buys almost nothing, and what movement the column holds comes from the scale the rule reads off each window rather than from the length of it.

3.3 The three norm ceilings

EffectiveAssetFloor is the one rule of the ceiling family. It states a fraction of the universe to hold effective, and returns the ceiling on the norm that meets it. The order-p effective number of assets is (sum(abs.(w) .^ p))^(1 / (1 - p)), and the ceiling is the number that holds it at or above fraction * N.

The order belongs to the constraint rather than to the rule, so each site states it in a CalibrationContext. A rule run outside a site needs a context that names p, exactly as the lp radius above did.

N = size(pr.X, 2)println("universe = $N assets, floor = $(0.5 * N) effective assets")eaf = EffectiveAssetFloor(; fraction = 0.5)ceiling_table = DataFrame(:slot => ["l2c", "lpc, p = 5", "linfc"],                          :norm_order => ["2", "5", "Inf"],                          :ceiling =>                              [eaf(:l2c, pr, nothing, nothing, CalibrationContext(; p = 2)),                               eaf(:lpc, pr, nothing, nothing, CalibrationContext(; p = 5)),                               eaf(:linfc, pr, nothing, nothing,                                   CalibrationContext(; p = Inf))])pretty_table(ceiling_table; formatters = [numfmt])
universe = 20 assets, floor = 10.0 effective assets
┌────────────┬────────────┬─────────┐
│       slot  norm_order  ceiling │
│     String      String  Float64 │
├────────────┼────────────┼─────────┤
│        l2c │          2 │  0.3162 │
│ lpc, p = 5 │          5 │  0.1585 │
│      linfc │        Inf │     0.1 │
└────────────┴────────────┴─────────┘

One rule serves the three slots, and each site reads it against its own norm order.

ceil_rule = EffectiveAssetFloor(; fraction = 0.5)copts = [JuMPOptimiser(; pe = pr, slv = slv, wb = WeightBounds(; lb = -1, ub = 1), sbgt = 1,                       bgt = 1),# no ceiling         JuMPOptimiser(; pe = pr, slv = slv, wb = WeightBounds(; lb = -1, ub = 1), sbgt = 1,                       bgt = 1, l2c = ceil_rule),# 2-norm ceiling         JuMPOptimiser(; pe = pr, slv = slv, wb = WeightBounds(; lb = -1, ub = 1), sbgt = 1,                       bgt = 1, lpc = LpRegularisation(; p = 5, val = ceil_rule)),# 5-norm ceiling         JuMPOptimiser(; pe = pr, slv = slv, wb = WeightBounds(; lb = -1, ub = 1), sbgt = 1,                       bgt = 1, linfc = ceil_rule)]# Inf-norm ceilingcress = [optimise(MeanRisk(; opt = opt)) for opt in copts]n_eff_p(w, p) = isinf(p) ? inv(maximum(abs, w)) : sum(abs.(w) .^ p)^inv(1 - p)ceiling_orders = [2, 2, 5, Inf]effective_table = DataFrame(:ceiling => ["none", "l2c", "lpc, p = 5", "linfc"],                            :p => ceiling_orders,                            :n_eff_2 => [number_effective_assets(r.w) for r in cress],                            :n_eff_p =>                                [n_eff_p(r.w, p) for (r, p) in zip(cress, ceiling_orders)])pretty_table(effective_table; formatters = [numfmt])
┌────────────┬─────────┬─────────┬─────────┐
│    ceiling        p  n_eff_2  n_eff_p │
│     String  Float64  Float64  Float64 │
├────────────┼─────────┼─────────┼─────────┤
│       none │     2.0 │   3.286 │   3.286 │
│        l2c │     2.0 │    10.0 │    10.0 │
│ lpc, p = 5 │     5.0 │   9.157 │    10.0 │
│      linfc │     Inf │   8.571 │    10.0 │
└────────────┴─────────┴─────────┴─────────┘

Every ceiling meets its own order's floor, which is the n_eff_p column. The n_eff_2 column is number_effective_assets, which is the order-2 reading alone, so only the l2c row is read against the order its ceiling was written for. The two columns are one number on that row and two numbers on the others, and that is a statement about which order the reading uses rather than about a ceiling that missed.

The rule refuses a resolution whose context names no order, and the message names the three slots that state one.

try    eaf(:l2c, pr, nothing, nothing, CalibrationContext())catch e    println(sprint(showerror, e))end
ArgumentError: `CalibrationContext.p` is `nothing` while the rule in `l2c` is being resolved. A ceiling is read against one norm order, the order belongs to the constraint, and each constraint site states it in the context. Place the rule in `l2c`, `lpc` or `linfc`, or state `p` in the context.

This page was generated using Literate.jl.