Relaxed risk budgeting

PortfolioOptimisers.RelaxedRiskBudgetingResultType
struct RelaxedRiskBudgetingResult{__T_jr, __T_prb, __T_fb} <: NonRiskJuMPOptimisationResult

Result type for Relaxed Risk Budgeting portfolio optimisation.

Fields

  • prb: Processed risk budgeting configuration.
  • fb: The fallback chain that answered this result: the (estimator, result) pair of every attempt optimise made before this one, in the order they ran, or nothing when the estimator it was asked of answered (see FbChain).

It carries no r. A RelaxedRiskBudgeting run builds its constraints straight from pr.sigma and never resolves a risk measure, so it sits on the NonRiskJuMPOptimisationResult half of the split and not beside RiskBudgetingResult, whose r is mandatory.

Property access delegates to the embedded JuMPOptimisationResult; unknown properties forward to prb first, then through jr (including the virtual :w and the pa fall-through).

Constructors

RelaxedRiskBudgetingResult(;    jr::JuMPOptimisationResult,    prb::Union{ProcessedAssetRiskBudgetingAttributes,               ProcessedFactorRiskBudgetingAttributes},    fb::Option{<:OptE_Opt_FbChain}) -> RelaxedRiskBudgetingResult

Keywords correspond to the struct's fields.

Related

source
PortfolioOptimisers.BasicRelaxedRiskBudgetingType
struct BasicRelaxedRiskBudgeting <: RelaxedRiskBudgetingAlgorithm

Bounds the risk variable by the portfolio standard deviation alone, which is the relaxation with no extra term.

Related

References

  • [139] V. Gambeta and R. Kwon. Risk return trade-off in relaxed risk parity portfolio optimization. Journal of Risk and Financial Management 13, 237 (2020).
  • [140] J.-C. Richard and T. Roncalli. Constrained Risk Budgeting Portfolios: Theory, Algorithms, Applications & Puzzles. arXiv preprint arXiv:1902.05710 (2019).
source
PortfolioOptimisers.RegularisedRelaxedRiskBudgetingType
struct RegularisedRelaxedRiskBudgeting <: RelaxedRiskBudgetingAlgorithm

Adds a second cone on an auxiliary scalar, which lifts the floor on the risk variable and improves numerical stability.

Related

References

  • [140] J.-C. Richard and T. Roncalli. Constrained Risk Budgeting Portfolios: Theory, Algorithms, Applications & Puzzles. arXiv preprint arXiv:1902.05710 (2019).
source
PortfolioOptimisers.RegularisedPenalisedRelaxedRiskBudgetingType
struct RegularisedPenalisedRelaxedRiskBudgeting{__T_p} <: RelaxedRiskBudgetingAlgorithm

Bounds the auxiliary scalar by the individual standard deviations rather than the portfolio one, weighted by p.

Fields

  • p: Power or order parameter.

Constructors

RegularisedPenalisedRelaxedRiskBudgeting(;    p::Number = 1.0) -> RegularisedPenalisedRelaxedRiskBudgeting

Keywords correspond to the struct's fields.

Validation

  • isfinite(p) and p > 0.

Related

References

  • [140] J.-C. Richard and T. Roncalli. Constrained Risk Budgeting Portfolios: Theory, Algorithms, Applications & Puzzles. arXiv preprint arXiv:1902.05710 (2019).
source
PortfolioOptimisers.RelaxedRiskBudgetingType
struct RelaxedRiskBudgeting{__T_opt, __T_rba, __T_wi, __T_alg, __T_fb} <: JuMPOptimisationEstimator

Relaxed Risk Budgeting (RRB) portfolio optimiser.

RelaxedRiskBudgeting implements a relaxed formulation of the risk budgeting problem using a Second Order Cone constraint on the portfolio variance. Unlike RiskBudgeting, it does not require a logarithmic or mixed-integer formulation, making it computationally more tractable.

Fields

  • opt: JuMP optimiser configuration.
  • rba: Risk budget algorithm.
  • wi: Initial portfolio weights for warm-starting the solver.
  • alg: Relaxed risk budgeting algorithm variant.
  • fb: Fallback result or estimator.

Constructors

RelaxedRiskBudgeting(;    opt::JuMPOptimiser,    rba::TD{<:RiskBudgetingAlgorithm} = AssetRiskBudgeting(),    wi::TD_Option{<:VecNum} = nothing,    alg::RelaxedRiskBudgetingAlgorithm = BasicRelaxedRiskBudgeting(),    fb::TDO_Option{<:OptE_Opt} = nothing) -> RelaxedRiskBudgeting

Keywords correspond to the struct's fields. Fields typed TD, TD_Option or TDO_Option may hold a TimeDependent per-fold schedule instead of a static value: the budgeting algorithm (and with it the risk budget), warm start and fallback are problem definition, so a cross-validation fold loop resolves them per fold, and a fold-less optimise runs with each at its static default (nothing for wi and fb). The relaxation variant alg is formulation control and stays static.

Validation

  • If wi is provided: !isempty(wi).
  • fb schedules: bind !== :nearest.

Mathematical definition

The Relaxed Risk Budgeting (RRB) formulation replaces the non-convex risk-parity constraint with a second-order cone (SOC) relaxation. Let $\mathbf{G}$ be the Cholesky factor of $\mathbf{\Sigma}$ (so $\mathbf{G}^\intercal\mathbf{G} = \mathbf{\Sigma}$). Introduce auxiliary variables $\boldsymbol{\zeta} = \mathbf{\Sigma}\boldsymbol{w}$, $\psi \geq 0$, $\gamma \geq 0$:

\[\begin{align} \underset{\boldsymbol{w},\psi,\gamma,\boldsymbol{\zeta}}{\min} \quad & \psi - \gamma\,, \\ \text{s.t.} \quad & \boldsymbol{\zeta} = \mathbf{\Sigma}\boldsymbol{w}\,, \\ & \begin{pmatrix} w_i + \zeta_i \\ 2\gamma\sqrt{b_i} \\ w_i - \zeta_i \end{pmatrix} \in \mathcal{K}_{\mathrm{SOC}}\,, \quad \forall i\,. \end{align}\]

The variant in alg decides the cone that bounds $\psi$. The three variants are versions A, B and C of the constrained risk budgeting model of Richard and Roncalli.

BasicRelaxedRiskBudgeting bounds it by the portfolio standard deviation alone:

\[\begin{align} \psi &\geq \lVert \mathbf{G}\boldsymbol{w} \rVert_2 = \sqrt{\boldsymbol{w}^\intercal\mathbf{\Sigma}\boldsymbol{w}}\,. \end{align}\]

RegularisedRelaxedRiskBudgeting adds a scalar $\rho \geq 0$ and a second cone:

\[\begin{align} \psi &\geq \sqrt{\boldsymbol{w}^\intercal\mathbf{\Sigma}\boldsymbol{w} + \rho^{2}}\,, \\ \rho &\geq \lVert \mathbf{G}\boldsymbol{w} \rVert_2\,. \end{align}\]

RegularisedPenalisedRelaxedRiskBudgeting keeps the first cone and replaces the second one, so that $\rho$ is bounded by the weighted individual standard deviations rather than by the portfolio one. p weights that term:

\[\begin{align} \rho &\geq \sqrt{p} \, \lVert \mathbf{\Theta}\boldsymbol{w} \rVert_2\,, \quad \mathbf{\Theta} = \mathrm{diag}\left(\sqrt{\mathrm{diag}(\mathbf{\Sigma})}\right)\,. \end{align}\]

Where:

  • $\boldsymbol{w}$: Portfolio weight vector.
  • $\psi$: Average risk of the portfolio.
  • $\gamma$: Lower bound of the risk contribution of every asset.
  • $\boldsymbol{\zeta}$: Auxiliary vector equal to $\mathbf{\Sigma}\boldsymbol{w}$.
  • $b_i$: Risk budget for asset $i$.
  • $\mathbf{G}$: Cholesky factor of $\mathbf{\Sigma}$ (so $\mathbf{G}^\intercal\mathbf{G} = \mathbf{\Sigma}$).
  • $\mathbf{\Sigma}$: Covariance matrix.
  • $\mathbf{\Theta}$: Diagonal matrix of the individual standard deviations.
  • $\rho$: Scalar auxiliary variable of the two regularised variants.
  • $p$: Penalty weight of RegularisedPenalisedRelaxedRiskBudgeting.
  • $\mathcal{K}_{\mathrm{SOC}}$: Second-order cone.

Details

  • The hyperbolic constraint is the substitution that makes the least squares risk parity problem disciplined convex, and the same device carries it here.
  • The hyperbolic constraint reads $\gamma \leq \sqrt{w_{i} \zeta_{i} / b_{i}}$ for every $i$, and $w_{i} \zeta_{i}$ is the risk contribution of asset $i$ under the variance. So maximising $\gamma$ drives the contributions towards the stated proportions, while minimising $\psi$ drives the total risk down. The single objective $\psi - \gamma$ does both.
  • The relaxation reads the covariance alone. This head resolves no risk measure, which is why its result carries no r.

Notes

Because this is a relaxation of the risk budgeting problem, the realised risk contributions will not adhere to the target risk budget as tightly as the exact logarithmic-barrier or mixed-integer formulations in RiskBudgeting. In well-behaved problems the deviation is negligible, but in pathological cases (e.g. ill-conditioned covariance matrices or extreme budget allocations) it can be noticeable. The trade-off is that the SOC formulation is convex and composes cleanly with additional constraints, making it the friendlier choice when the risk budget is one of several objectives rather than a hard requirement. Use RiskBudgeting when strict adherence to the risk budget is essential.

Propagated parameters

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

  • opt: Recursively updated via factory.
  • fb: Recursively updated via factory.

View parameters

RelaxedRiskBudgeting defines its own port_opt_view method rather than deriving one from field tags.

  • The method reads the returns matrix X as its third argument. When opt.pe already holds a prior result, the method replaces X with opt.pe.X, so the children are viewed against the prior's own observations rather than the caller's matrix.
  • opt recurses through port_opt_view with that matrix. rba recurses with the index alone.
  • wi is sliced to the selected assets.
  • alg and fb are carried through unchanged.

Related

References

  • [139] V. Gambeta and R. Kwon. Risk return trade-off in relaxed risk parity portfolio optimization. Journal of Risk and Financial Management 13, 237 (2020).
  • [140] J.-C. Richard and T. Roncalli. Constrained Risk Budgeting Portfolios: Theory, Algorithms, Applications & Puzzles. arXiv preprint arXiv:1902.05710 (2019).
  • [141] H. Mausser and O. Romanko. Computing equal risk contribution portfolios. IBM Journal of Research and Development 58, 5:1–5:12 (2014).
  • [5] D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025). Section 10.1.2, Equations 10.5-10.6.
source
PortfolioOptimisers.factoryMethod
factory(a::Union{Nothing, <:AbstractEstimator, <:AbstractAlgorithm,
                 <:AbstractResult}, args...; kwargs...) -> a
factory(a::AbstractVector{<:Union{Nothing, <:AbstractEstimator, <:AbstractAlgorithm,
                                  <:AbstractResult}}, args...; kwargs...) -> Vector

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

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

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

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

Algorithm

The scalar method:

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

The vector method:

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

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

Arguments

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

Returns

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

Examples

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

Related

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

Return opt unchanged.

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

Related

source
PortfolioOptimisers.port_opt_viewMethod
port_opt_view(
    rrb::RelaxedRiskBudgeting,
    i,
    X::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
    args...
) -> RelaxedRiskBudgeting{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}, _A, _B, <:RelaxedRiskBudgetingAlgorithm} where {__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, _A, _B}

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

source
PortfolioOptimisers.optimiseMethod
optimise(rrb::RelaxedRiskBudgeting{<:Any, <:Any, <:Any, <:Any, Nothing},
         rd::ReturnsResult; dims::Int = 1,
         str_names::Bool = false, save::Bool = true, kwargs...) -> RelaxedRiskBudgetingResult

Run the Relaxed Risk Budgeting portfolio optimisation.

Arguments

  • rrb: The relaxed risk budgeting optimiser to use.
  • rd: The returns result to use. If isa(rrb.opt.pe, AbstractPriorResult), rd is not necessary if doing a standalone optimisation, but may be required/desired by fallbacks and/or clusterisation.
  • dims: The dimension along which observations advance in time.
  • str_names: Whether to use string names for the assets in the optimisation.
  • save: Whether to save the JuMP model in the optimisation result.
  • kwargs: Additional keyword arguments passed to the optimisation function.

Validation

  • No field in the tree of rrb holds an Online. An ArgumentError naming the field is thrown otherwise, through assert_batch_entry: a plain optimise is a batch fit, and a wrapper resolves only at the warm-up of the fold loop's online arm.

Related

source

References

[5]
D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025).
[139]
V. Gambeta and R. Kwon. Risk Return Trade-Off in Relaxed Risk Parity Portfolio Optimization. Journal of Risk and Financial Management 13, 237 (2020).
[140]
J.-C. Richard and T. Roncalli. Constrained Risk Budgeting Portfolios: Theory, Algorithms, Applications & Puzzles. Technical Report 1902.05710 (arXiv, 2019).
[141]
H. Mausser and O. Romanko. Computing Equal Risk Contribution Portfolios. IBM Journal of Research and Development 58, 5:1–5:12 (2014).