Base Risk Measures

All risk measures are defined as their whole names, however this can be unwieldy, so we also provide convenience aliases defined in Public API → Aliases.

All concrete risk measures can be used as functors (callable structs) to compute their associated risk quantity, according to its risk_input_kind, or via expected_risk.

Quick-pick guide

Three legal-usage classes:

  • Optimisation: JuMP formulation; usable as objective/constraint.
  • Hierarchical: clustering proxy, no JuMP formulation.
  • Non-Optimisation: analysis only.

Table Key:

  • QP: Quadratic programming (generates a quadratic expression).
  • NOC: Norm one none.
  • SOC: Second order cone
  • RSOC: Rotated second order cone.
  • NIC: Norm infinity cone.
  • EC: Exponential cone.
  • PC: 3D Power cone.
  • Sym: Symmetric matrix space.
  • PSD: Positive semi-definite cone.
  • MIP: Mixed-integer variables.
  • *: Carries the requirements of its inner risk measures.
  • -: Not applicable.
TypeName (Alias)CompatibilityRequirementsRec. Solver
DispersionVarianceJuMP + HierarchicalQP, SOCClarabel
DispersionSDP graph / Risk contribution VarianceJuMP + HierarchicalSDPClarabel, SCS
DispersionStandardDeviation (SD)JuMP + HierarchicalSOCClarabel
DispersionBox UncertaintySetVariance (UcVariance)JuMP + HierarchicalSymClarabel
DispersionEllipse UncertaintySetVariance (UcVariance)JuMP + HierarchicalSym, PSD, SOCClarabel, SCS
DispersionLowOrderMoment (FLM)JuMP + Hierarchical-Clarabel
DispersionLowOrderMoment (MAD)JuMP + Hierarchical-Clarabel
DispersionDirect / Squared SOC LowOrderMoment (SCM, SLM)JuMP + HierarchicalQP, SOCClarabel
DispersionSum of Squares LowOrderMoment (SCM, SLM)JuMP + HierarchicalSOC, RSOCClarabel
DispersionSOC LowOrderMoment (SCM, SLM)JuMP + HierarchicalSOCClarabel
DispersionLowOrderMoment (ECM, ELM)JuMP + HierarchicalPCClarabel
DispersionHighOrderMoment (TLM, SSK, FTCM, FTLM, KT, SKT)Hierarchical--
DispersionDirect / Squared SOC Exact KurtosisJuMP + HierarchicalQP, SOC, PSDClarabel, SCS
DispersionSum of Squares Exact KurtosisJuMP + HierarchicalSOC, RSOC, PSDClarabel, SCS
DispersionSOC Exact KurtosisJuMP + HierarchicalSOC, PSDClarabel, SCS
DispersionDirect / Squared SOC Approx KurtosisJuMP + HierarchicalQP, SOCClarabel
DispersionSum of Squares Approx KurtosisJuMP + HierarchicalSOC, RSOCClarabel
DispersionSOC Exact Approx KurtosisJuMP + HierarchicalSOCClarabel
DispersionNegativeSkewnessJuMP + HierarchicalQP, SOCClarabel
DispersionSquare Root NegativeSkewnessJuMP + HierarchicalSOCClarabel
Tail lossExact ValueatRisk (VaR)JuMP + HierarchicalMIPPajarito (Clarabel + HiGHS)
Tail lossApprox ValueatRisk (VaR)JuMP + HierarchicalSOCClarabel
Tail drawdownDrawdownatRisk (DaR)JuMP + HierarchicalMIPPajarito (Clarabel + HiGHS)
Tail drawdownRelativeDrawdownatRisk (R_DaR)Hierarchical-Clarabel
DispersionExact ValueatRiskRange (VaR_RG)JuMP + HierarchicalMIPPajarito (Clarabel + HiGHS)
DispersionApprox ValueatRiskRange (VaR_RG)JuMP + HierarchicalSOCClarabel
Tail lossConditionalValueatRisk (CVaR)JuMP + Hierarchical-Clarabel
Tail lossDistributionallyRobustConditionalValueatRisk (DRCVaR)JuMP + HierarchicalNICClarabel
Tail drawdownConditionalDrawdownatRisk (CDaR)JuMP + Hierarchical-Clarabel
Tail drawdownDistributionallyRobustConditionalDrawdownatRisk (DRCDaR)JuMP + HierarchicalNICClarabel
Tail drawdownRelativeConditionalDrawdownatRisk (R_CDaR)Hierarchical-Clarabel
DispersionConditionalValueatRiskRange (CVaR_RG)JuMP + Hierarchical-Clarabel
DispersionDistributionallyRobustConditionalValueatRiskRange (DRCVaR_RG)JuMP + HierarchicalNICClarabel
Tail lossEntropicValueatRisk (EVaR)JuMP + HierarchicalECClarabel
Tail drawdownEntropicDrawdownatRisk (EDaR)JuMP + HierarchicalECClarabel
Tail drawdownRelativeEntropicDrawdownatRisk (R_EDaR)HierarchicalECClarabel
DispersionEntropicValueatRiskRange (EVaR_RG)JuMP + HierarchicalECClarabel
Tail lossRelativisticValueatRisk (RLVaR)JuMP + HierarchicalPCClarabel
Tail drawdownRelativisticDrawdownatRisk (RLDaR)JuMP + HierarchicalPCClarabel
Tail drawdownRelativeRelativisticDrawdownatRisk (R_RLDaR)HierarchicalPCClarabel
DispersionRelativisticValueatRiskRange (RLVaR_RG)JuMP + HierarchicalPCClarabel
Tail lossPowerNormValueatRisk (PNVaR)JuMP + HierarchicalPCClarabel
Tail drawdownPowerNormDrawdownatRisk (PNDaR)JuMP + HierarchicalPCClarabel
Tail drawdownRelativePowerNormDrawdownatRisk (R_PNDaR)HierarchicalPCClarabel
DispersionPowerNormValueatRiskRange (PNVaR_RG)JuMP + HierarchicalPCClarabel
DispersionExact OrderedWeightsArray (OWA_GMD, OWA_RG, OWA_CVaR_RG, OWA_TG_RG, OWA_LMoment)JuMP + Hierarchical-Clarabel
DispersionApprox OrderedWeightsArray (OWA_GMD, OWA_RG, OWA_CVaR_RG, OWA_TG_RG, OWA_LMoment)JuMP + HierarchicalPCClarabel
Tail lossExact OrderedWeightsArray (OWA_CVaR, OWA_TG, OWA_WR)JuMP + Hierarchical-Clarabel
Tail lossApprox OrderedWeightsArray (OWA_CVaR, OWA_TG, OWA_WR)JuMP + HierarchicalPCClarabel
DispersionExact OrderedWeightsArrayRangeJuMP + Hierarchical-Clarabel
DispersionApprox OrderedWeightsArrayRangeJuMP + HierarchicalPCClarabel
DrawdownAverageDrawdown (ADD)JuMP + Hierarchical-Clarabel
DrawdownRelativeAverageDrawdown (R_ADD)Hierarchical--
DrawdownUlcerIndex (UCI)JuMP + HierarchicalSOCClarabel
DrawdownRelativeUlcerIndex (R_UCI)Hierarchical--
Tail drawdownMaximumDrawdown (MDD)JuMP + Hierarchical-Clarabel
Tail drawdownRelativeMaximumDrawdown (R_MDD)Hierarchical--
DispersionDirect BrownianDistanceVariance (BDVariance)JuMP + HierarchicalQPClarabel
DispersionSum of Squares BrownianDistanceVariance (BDVariance)JuMP + HierarchicalQP, RSOCClarabel
Tail lossWorstRealisation (WR)JuMP + Hierarchical-Clarabel
Tail lossRange (RG)JuMP + Hierarchical-Clarabel
TurnoverTurnoverRiskMeasure (TnRM)JuMP + HierarchicalNOCClarabel
TrackingL1 Norm TrackingRiskMeasure (TrRM)JuMP + HierarchicalNOCClarabel
TrackingL2 Norm TrackingRiskMeasure (TrRM)JuMP + HierarchicalSOCClarabel
TrackingSquared L2 Norm TrackingRiskMeasure (TrRM)JuMP + HierarchicalQP, SOCClarabel
TrackingLp Norm TrackingRiskMeasure (TrRM)JuMP + HierarchicalPCClarabel
TrackingInfinity Norm TrackingRiskMeasure (TrRM)JuMP + HierarchicalNICClarabel
Risk trackingIndependent variable RiskTrackingRiskMeasure (RkTrRM)JuMP + Hierarchical*Clarabel
Risk trackingDependent variable RiskTrackingRiskMeasure (RkTrRM)JuMP + HierarchicalNOC, *Clarabel
DispersionVarianceSkewKurtosis (VSK)JuMP + HierarchicalSym, PSDSCS
DispersionGenericValueatRiskRange (GVaR_RG)JuMP + Hierarchical*Clarabel
RatioRiskRatioHierarchical**
RatioNonOptimisationRiskRatio (NonOptRkRatio)-**
FlatEqualRisk---
DispersionMedianAbsoluteDeviationHierarchical--
PerformanceMeanReturn---
PerformanceMeanReturnRiskRatio---
PerformanceExpectedReturn---
PerformanceExpectedReturnRiskRatio---
DispersionThirdCentralMoment---
DispersionSkewness---
PortfolioOptimisers.RiskMeasureType
abstract type RiskMeasure <: OptimisationRiskMeasure

Abstract supertype for standard risk measures used in portfolio optimisation.

Subtype RiskMeasure to implement concrete risk measures that quantify portfolio risk and can be used as objectives or constraints in optimisation problems. This type ensures compatibility with the optimisation framework and enables composability with other estimators and algorithms.

Interfaces

In order to implement a new risk measure that works seamlessly with the library, subtype RiskMeasure with a settings::RiskMeasureSettings field — set_risk_expression! reads its scale and rke, set_risk_upper_bound! its ub, and expected_risk its scale when several measures combine — and implement the following methods:

risk_input_kind

  • risk_input_kind(r::MyRiskMeasure) -> RiskInputKind: Declare which of the three functor shapes the measure exposes. There is no default: an undeclared measure throws rather than routing to the wrong input shape.

Arguments

  • r: The concrete subtype instance.

Returns

  • kind::RiskInputKind: One of NetReturnsInput(), WeightsReturnsFeesInput() or WeightsInput().

The functor

expected_risk evaluates the measure as a functor, in the shape its kind declares:

A WeightsReturnsFeesInput measure also declares supports_precomputed_returns, as supports_precomputed_returns(r::MyRiskMeasure) -> Bool, stating whether its risk is a function of the net return series alone; the fallback for that kind throws rather than guessing. The other two kinds answer through the kind itself.

The model builder

A JuMP optimiser builds the measure into its model through set_risk_constraints!, as set_risk_constraints!(model::JuMP.Model, i, r::MyRiskMeasure, opt::RiskJuMPOptimisationEstimator, pr::AbstractPriorResult, args...; kwargs...), which has no fallback. The method builds the risk expression from the model's weights, then registers it with set_risk_bounds_and_expression!, which reads the bound and the contribution to the aggregate risk expression off r.settings. Scale every constraint by get_constraint_scale, and multiply any constant bound by get_k, so the bound is compared against unrescaled weights under a ratio objective (ADR 0008).

Optional methods

Each has a fallback on AbstractBaseRiskMeasure:

  • needs_previous_weights: true for a measure that reads the previous weights; the fallback answers false.
  • bigger_is_better: true for a measure a caller maximises; the fallback answers false.
  • range_tails: the two point measures a range measure is the sum of; the fallback throws.
  • factory: rebuild the measure against a prior result before it is evaluated, for a measure whose slots are filled from the prior.

Related

source
PortfolioOptimisers.HierarchicalRiskMeasureType
abstract type HierarchicalRiskMeasure <: OptimisationRiskMeasure

Abstract supertype for hierarchical risk measures used in portfolio optimisation.

Subtype HierarchicalRiskMeasure to implement risk measures that operate on hierarchical or clustered portfolio structures. These measures are designed for use as objectives or constraints in optimisation problems that leverage asset clustering, hierarchical risk parity, or similar techniques.

Interfaces

A hierarchical measure is only ever evaluated at the value level, through expected_risk, so it builds no JuMP model and needs no model builder. In order to implement a new one that works seamlessly with the library, subtype HierarchicalRiskMeasure with a settings::HierarchicalRiskMeasureSettings field — expected_risk reads its scale when several measures combine — and implement the following methods:

risk_input_kind

  • risk_input_kind(r::MyRiskMeasure) -> RiskInputKind: Declare which of the three functor shapes the measure exposes. There is no default: an undeclared measure throws rather than routing to the wrong input shape.

Arguments

  • r: The concrete subtype instance.

Returns

  • kind::RiskInputKind: One of NetReturnsInput(), WeightsReturnsFeesInput() or WeightsInput().

The functor

expected_risk evaluates the measure as a functor, in the shape its kind declares:

A WeightsReturnsFeesInput measure also declares supports_precomputed_returns, as supports_precomputed_returns(r::MyRiskMeasure) -> Bool, stating whether its risk is a function of the net return series alone; the fallback for that kind throws rather than guessing. The other two kinds answer through the kind itself.

Optional methods

Each has a fallback on AbstractBaseRiskMeasure:

  • needs_previous_weights: true for a measure that reads the previous weights; the fallback answers false.
  • bigger_is_better: true for a measure a caller maximises; the fallback answers false.
  • factory: rebuild the measure against a prior result before it is evaluated, for a measure whose slots are filled from the prior.

Related

source
PortfolioOptimisers.FrontierType
struct Frontier{__T_N, __T_factor, __T_bound} <: AbstractAlgorithm

Sweeps the efficient frontier by solving the model once at each of N evenly spaced bound values.

Stand this in a risk measure's bound slot instead of a number. The optimisation then reads the span off two corner portfolios, divides it into N levels, and returns one portfolio per level. The corners are a minimum-risk solve and a maximum-return solve, so the span covers the whole attainable range of the bound.

The levels are evenly spaced in the units of the risk expression the bound is applied to, which bound names, and not in the units of the measure. See SquareRootBound for the case where the two differ.

Fields

  • N: Number of sweep points on the efficient frontier. The sweep solves the model N times, at N evenly spaced bound values.
  • factor: Multiplier applied to both ends of the sweep span after bound has transformed them. It carries a formulation's own correction factor, such as the inv(1 / (T - ddof)) of a second-moment bound.
  • bound: FrontierBoundEstimator that converts a bound value into the units of the risk expression the bound is applied to. The sweep points are evenly spaced in those units, not in the units of the measure.

Constructors

Frontier(;    N::Integer = 20,    bound::FrontierBoundEstimator = LinearBound()) -> Frontier

Creates a Frontier with N points, a scaling factor of 1, and the specified bound strategy. Used to set appropriate frontier bounds in variance_risk_bounds_val and second_moment_bound_val.

PortfolioOptimisers._Frontier(;    N::Integer = 20,    factor::Number,    bound::FrontierBoundEstimator) -> Frontier

Internal constructor. Keywords correspond to the struct's fields.

Validation

  • N > 0 and N <= RESOURCE_LIMITS[].max_frontier (each sweep point runs a full solve; see RESOURCE_LIMITS). This is the cheap early check on one bound. The sweep across several swept bounds is their product, capped separately at Model Assembly by assert_frontier_sweep_cap.
  • isfinite(factor) and factor > 0.

Examples

julia> Frontier(; N = 15)Frontier       N ┼ Int64: 15  factor ┼ Int64: 1   bound ┴ LinearBound()

Related

References

  • [5] D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025). Sections 8.2.1 and 8.2.2, Equations 8.7 and 8.10.
source
PortfolioOptimisers.AbstractRiskMeasureSettingsType
abstract type AbstractRiskMeasureSettings <: AbstractEstimator

Abstract supertype for all risk measure settings.

A settings object carries the three things a risk measure needs that are not part of the risk itself: the combination weight scale, whether the measure contributes to the model's risk expression (rke), and the bound the optimiser enforces on the measure's own expression. All concrete risk measure settings types should subtype AbstractRiskMeasureSettings.

Interfaces

In order to implement a new concrete settings type, subtype AbstractRiskMeasureSettings and give it these fields:

Required fields

  • scale::Number: Combination weight, read by set_risk_expression! and by the value-level readers.
  • rke::Bool: Whether the measure contributes to the model's aggregate risk expression.

Optional field

Related

source
PortfolioOptimisers.RiskMeasureSettingsType
struct RiskMeasureSettings{__T_scale, __T_ub, __T_rke} <: JuMPRiskMeasureSettings

Weights a risk measure inside an aggregate, and bounds its risk expression from above.

This is the settings type of every measure the optimiser minimises. ub is the maximum level of risk the solution may reach, the $\bar{\phi}$ of Equation 8.10 of [5]; sweeping it is how the book builds an efficient frontier, which is what a Frontier in that slot does. scale weights the measure against its siblings when several are given, and rke decides whether it reaches the aggregate at all.

Fields

  • scale: Weight of this risk measure in the aggregate risk expression formed from a vector of measures. It is a combination weight, so it is inert on a single measure: an optimiser given one measure drops it before the risk expression is built, and the value-level readers ignore it too. The upper bound in ub binds on the measure's own expression, before scale is applied.
  • ub: Upper bound(s) on the measure's own risk expression. A scalar bounds one model. A vector and a Frontier are sweep axes, one solve per entry, so the optimisation returns one portfolio per bound value.
  • rke: Whether to include the risk measure value in the JuMP risk expression.

Constructors

RiskMeasureSettings(;    scale::Number = 1.0,    ub::Option{<:RkRtBounds} = nothing,    rke::Bool = true,) -> RiskMeasureSettings

Creates a RiskMeasureSettings instance with the specified scale, upper bound, and risk evaluation flag.

Validation

Examples

julia> RiskMeasureSettings()RiskMeasureSettings  scale ┼ Float64: 1.0     ub ┼ nothing    rke ┴ Bool: true

Related

References

  • [5] D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025). Section 8.2.2, Equation 8.10.
source
PortfolioOptimisers.HierarchicalRiskMeasureSettingsType
struct HierarchicalRiskMeasureSettings{__T_scale} <: AbstractRiskMeasureSettings

Weights a hierarchical risk measure inside an aggregate, and carries no bound.

A clustering optimiser computes each cluster's risk as a number rather than building one model over the whole universe, so there is no risk expression for a bound to constrain and no ub field to hold one. scale is the only setting a HierarchicalRiskMeasure has, and it means what it means everywhere else: the measure's weight against its siblings when several are given.

Fields

  • scale: Weight of this risk measure in the aggregate risk expression formed from a vector of measures. It is a combination weight, so it is inert on a single measure: an optimiser given one measure drops it before the risk expression is built, and the value-level readers ignore it too. The upper bound in ub binds on the measure's own expression, before scale is applied.

Constructors

HierarchicalRiskMeasureSettings(;    scale::Number = 1.0) -> HierarchicalRiskMeasureSettings

Creates a HierarchicalRiskMeasureSettings instance with the specified scaling factor.

Validation

  • scale must be finite.

Examples

julia> HierarchicalRiskMeasureSettings()HierarchicalRiskMeasureSettings  scale ┴ Float64: 1.0

Related

source
PortfolioOptimisers.ScalariserType
abstract type Scalariser <: AbstractEstimator

Abstract supertype for scalarisation strategies used to combine multiple risk measures into a single scalar value for optimisation.

Subtype Scalariser to implement different methods for aggregating risk measures. These strategies are used in portfolio optimisation routines that require a single risk value from multiple risk measures.

Two consumers, and only one of them is restricted

A scalariser is read at two levels, and they are not equally permissive.

  • The model level. A JuMP optimiser builds the aggregation into the model as an expression, so the strategy must have a convex form. This is what NonHierarchicalScalariser and HierarchicalScalariser separate: the two subtypes name the consumers that can build a given strategy, not a property of the resulting number. JuMPOptimiser.sca is bounded NonHierarchicalScalariser and refuses the hierarchical half; the clustering optimisers compute each cluster's risk separately and accept either.
  • The value level. expected_risk and the readers around it evaluate the measures first and combine the resulting numbers, so nothing convex is being built and no strategy can be refused on those grounds. Every sca keyword and every sca result field at this level is bounded Scalariser, and all four scalarisers are admitted, MinScalariser included.

So a HierarchicalScalariser on a value-level call is not a misuse, and the word "hierarchical" in the subtype name describes which estimators accept it rather than where the number is meaningful.

The weights are the measures' own scale

Every strategy below is a weighted aggregation, and it reads its weights from one place: the scale field of each measure's settings. Both levels apply it before the strategy runs — the model in set_risk_expression!, which pushes scale * r_expr, and the value level in expected_risk, which multiplies each element by r.settings.scale. Stating it once here is why no strategy below carries a weight vector of its own.

Two of the three model forms are exact only while the objective pushes

SumScalariser builds an expression, so the model agrees with the value level whatever the objective is. The other two build a variable bounded below by the aggregation, and a minimising objective is what pulls it down onto the aggregation. Under MaximumReturn nothing pulls, so model[:risk] reports an upper bound rather than the aggregation; read the exact figure back with expected_risk.

The bound the model enforces is unaffected. An ub on the aggregate constrains the variable, and the variable stands above the aggregation, so the aggregation satisfies the bound too. It is the reported figure that stands above it, and reading the aggregate back from expected_risk gives the exact one.

Interfaces

In order to implement a new scalarisation strategy, subtype NonHierarchicalScalariser when the aggregation has a convex JuMP form and HierarchicalScalariser when it does not, then implement:

Required method name

  • scalarise(f, sca::MyScalariser, itr; by = nothing): Combine the per-measure values f returns into one.

Arguments

  • f: Per-element evaluation closure.
  • sca: The strategy instance.
  • itr: Iterable of risk measures.
  • by: Optional selection key, for a strategy that picks one element rather than combining all of them.

Returns

  • The combined value, with the same shape as the values f returns.

Required for a NonHierarchicalScalariser

  • scalarise_risk_expression!(model::JuMP.Model, sca::MyScalariser): Collapse the model's risk_vec array into the single risk expression. A HierarchicalScalariser declares no such method, which is what the split means.

Related

References

  • [95] S. Boyd and L. Vandenberghe. Convex Optimization (Cambridge University Press, Cambridge, UK, 2004). Vector optimisation and scalarisation.
  • [96] S. Diamond and S. Boyd. CVXPY: A Python-embedded modeling language for convex optimization. Journal of Machine Learning Research 17, 1–5 (2016). The cvxpy.transforms.scalarize module, which this family ports.
source
PortfolioOptimisers.scalariseFunction
scalarise(f, sca::Scalariser, itr; by = nothing)

Reduce per-risk-measure values into a single scalarised value.

Applies f to every element of itr (typically a vector of risk measures, or pairs thereof) and combines the results according to the scalariser. f must return a Number, an AbstractArray, or a Tuple of these, and must return freshly allocated values (no views into buffers reused across iterations), since results may be retained across iterations.

The combining rules are:

  • SumScalariser: slot-wise sum of all values. f is expected to include the measure's settings.scale weight in its result.

  • MaxScalariser/MinScalariser:

    • by === nothing: slot-wise (elementwise) maximum/minimum across values.
    • by given: winner-take-all — returns the single f result whose by(result) is largest/smallest (ties keep the earliest).
  • LogSumExpScalariser: slot-wise logsumexp of gamma-scaled values, divided by gamma.

Algorithm

  1. Start the accumulator acc at nothing, and the winning key k at nothing beside it. LogSumExpScalariser starts neither, and takes steps 5 and 6 instead.
  2. Apply f to each element of itr in turn, giving v.
  3. Fold v into acc through the branch that sca selects. SumScalariser takes scalarise_combine(+, acc, v). MaxScalariser and MinScalariser take scalarise_combine(max, acc, v) and scalarise_combine(min, acc, v) while by is nothing.
  4. Given a by, read the key ki = by(v) in place of step 3, and take v and ki as the new acc and k when ki beats the k already held. A tie keeps the earlier element.
  5. Under LogSumExpScalariser, apply f to every element of itr, scale each result by sca.gamma with scalarise_map, and collect them into vs.
  6. Reduce vs slot-wise with scalarise_logsumexp, and divide the reduction by sca.gamma with scalarise_map.
  7. Return acc, or the value step 6 built.

Arguments

  • f: Per-element evaluation closure, applied to each element of itr.
  • sca: Scalarisation strategy.
  • itr: Iterable of risk measures (or pairs of them, when f needs the index).
  • by: Optional selection key for MaxScalariser/MinScalariser; ignored by the other scalarisers.

Returns

  • The scalarised value, with the same shape as the values returned by f.

Related

source
PortfolioOptimisers.SumScalariserType
struct SumScalariser <: NonHierarchicalScalariser

Adds the scaled risk measures together.

This is the default everywhere a sca keyword appears, and the only strategy whose model form is an expression rather than a variable with constraints: scalarise_risk_expression! sums risk_vec into one affine or quadratic expression, so it is also the only one that takes a quadratic risk expression without a reformulation.

Mathematical definition

\[\begin{align} \phi &= \sum_{i=1}^{N} w_i \cdot r_i \,. \end{align}\]

Where:

  • $N$: Number of risk measures.
  • $i$: Subscript denoting the i-th risk measure.
  • $r_i$: i-th risk measure value.
  • $w_i$: Weight of the i-th risk measure.

Related

References

  • [95] S. Boyd and L. Vandenberghe. Convex Optimization (Cambridge University Press, Cambridge, UK, 2004). Vector optimisation and scalarisation.
  • [96] S. Diamond and S. Boyd. CVXPY: A Python-embedded modeling language for convex optimization. Journal of Machine Learning Research 17, 1–5 (2016). cvxpy.transforms.scalarize.weighted_sum.
source
PortfolioOptimisers.MaxScalariserType
struct MaxScalariser <: NonHierarchicalScalariser

Reports the largest of the scaled risk measures, so the aggregate is the worst of them.

The model form is a free variable held above every entry of risk_vec, which the objective then pushes down onto the largest. Under a minimum-risk objective the model agrees with expected_risk; under a maximum-return objective it reports an upper bound instead, which is the shared caveat in Scalariser. In clustering optimisations each cluster's risk is computed separately, so there is no coherence in which measure wins between clusters.

Mathematical definition

\[\begin{align} \phi &= \underset{i \in (1,\,N)}{\max} \left(w_i \cdot r_i \right)\,. \end{align}\]

Where:

  • $N$: Number of risk measures.
  • $i$: Subscript denoting the i-th risk measure.
  • $r_i$: i-th risk measure value.
  • $w_i$: Weight of the i-th risk measure.

JuMP formulation

Relaxation

The encoding is not exact: the entries below bound the quantity instead of reproducing it, and the bound is tight only under the condition stated here.

  • The bounded quantity is risk, the free variable scalarise_risk_expression! creates for this strategy. The row risk_ms holds it at or above every entry of risk_vec, so model[:risk] stands above the maximum rather than on it.
  • The bound is tight while the objective pulls risk down, which a minimum-risk objective does. Under MaximumReturn nothing pulls, and model[:risk] is then an upper bound.
  • An ub on the aggregate is unaffected. It constrains risk, and the aggregation stands below risk, so the aggregation meets the bound too. Read the exact figure back with expected_risk.

Related

References

  • [95] S. Boyd and L. Vandenberghe. Convex Optimization (Cambridge University Press, Cambridge, UK, 2004). Vector optimisation and scalarisation.
  • [96] S. Diamond and S. Boyd. CVXPY: A Python-embedded modeling language for convex optimization. Journal of Machine Learning Research 17, 1–5 (2016). cvxpy.transforms.scalarize.max.
source
PortfolioOptimisers.MinScalariserType
struct MinScalariser <: HierarchicalScalariser

Reports the smallest of the scaled risk measures, so the aggregate is the mildest of them.

It is this library's own extension: the source family carries a weighted sum, a maximum and a log-sum-exp, and no minimum. In clustering optimisations each cluster's risk is computed separately, so there is no coherence in which measure wins between clusters.

It is the one HierarchicalScalariser, because minimising a minimum is not convex and a JuMP optimiser cannot build it. That bound is the model's: at the value level the measures are already numbers, so MinScalariser is admitted by every sca keyword and every sca result field there, on equal footing with the other three.

Mathematical definition

\[\begin{align} \phi &= \underset{i \in (1,\,N)}{\min} \left( w_i \cdot r_i \right)\,. \end{align}\]

Where:

  • $N$: Number of risk measures.
  • $i$: Subscript denoting the i-th risk measure.
  • $r_i$: i-th risk measure value.
  • $w_i$: Weight of the i-th risk measure.

Related

source
PortfolioOptimisers.LogSumExpScalariserType
struct LogSumExpScalariser{__T_gamma} <: NonHierarchicalScalariser

Smooths the maximum of the scaled risk measures, so every measure keeps a share of the aggregate.

Mathematical definition

\[\begin{align} \phi &= \frac{1}{\gamma} \log \left( \sum_{i=1}^{N} \exp \left[ \gamma \cdot w_i \cdot r_i \right] \right)\,. \end{align}\]

Where:

  • $N$: Number of risk measures.
  • $i$: Subscript denoting the i-th risk measure.
  • $r_i$: i-th risk measure value.
  • $w_i$: Weight of the i-th risk measure.
  • $\gamma$: Positive parameter setting how close the aggregate runs to the maximum.

What gamma does

The aggregate is never below the maximum and never more than $\log N / \gamma$ above it:

\[\begin{align} \underset{i \in (1,\,N)}{\max} \left(w_i \cdot r_i\right) \leq \phi &\leq \underset{i \in (1,\,N)}{\max} \left(w_i \cdot r_i\right) + \frac{\log N}{\gamma}\,. \end{align}\]

So a large $\gamma$ gives the maximum. Under a minimum-risk objective the model agrees with expected_risk to within that bound, and under a maximum-return objective it reports an upper bound instead — the shared caveat in Scalariser.

Warning

A small $\gamma$ does not give the weighted sum. The bound above shows why: the aggregate never falls below the maximum, and $\log N / \gamma$ diverges. What survives is the shape — subtracting that divergent term recovers the weighted mean. An additive constant does not move a minimiser, so the portfolio a small $\gamma$ selects tends to the weighted sum's, while the number reported does not. The model degenerates first: $\gamma$ small enough to make the divergent term negligible can already fail to solve.

Warning

The model form is an exponential cone, which cannot hold a quadratic risk expression. Both Variance formulations produce one, so a Variance under this scalariser aborts the solve with MOI.UnsupportedConstraint{MOI.ScalarQuadraticFunction{Float64}, MOI.GreaterThan{Float64}}. Use StandardDeviation instead, or SumScalariser, which sums a quadratic expression directly. Nothing refuses the combination up front.

In clustering optimisations each cluster's risk is computed separately, so there is no coherence between clusters, and the value level has no cone to build: it evaluates the measures first and reduces the numbers, where a large $\gamma$ is safe because LogExpFunctions.logsumexp shifts by the maximum before it exponentiates.

JuMP formulation

Relaxation

The encoding is not exact: the entries below bound the quantity instead of reproducing it, and the bound is tight only under the condition stated here.

  • The bounded quantity is risk, the free variable scalarise_risk_expression! creates for this strategy. The rows u_risk_lse and risk_lse state the exponential-cone form of the log-sum-exp, which holds model[:risk] at or above the smoothed maximum.
  • The bound is tight while the objective pulls risk down, which a minimum-risk objective does. Under MaximumReturn nothing pulls, and model[:risk] is then an upper bound.
  • An ub on the aggregate is unaffected, on the terms MaxScalariser states. Read the exact figure back with expected_risk.

Fields

  • gamma: Log-sum-exp scalariser smoothing parameter.

Constructors

LogSumExpScalariser(;    gamma::Number = 1.0,) -> LogSumExpScalariser

Keywords correspond to the struct's fields.

Validation

  • gamma > 0.

Examples

julia> LogSumExpScalariser()LogSumExpScalariser  gamma ┴ Float64: 1.0

Related

References

  • [95] S. Boyd and L. Vandenberghe. Convex Optimization (Cambridge University Press, Cambridge, UK, 2004). The log-sum-exp function and its bound on the maximum.
  • [96] S. Diamond and S. Boyd. CVXPY: A Python-embedded modeling language for convex optimization. Journal of Machine Learning Research 17, 1–5 (2016). cvxpy.transforms.scalarize.log_sum_exp.
source
PortfolioOptimisers.FrontierBoundEstimatorType
abstract type FrontierBoundEstimator <: AbstractEstimator

Abstract supertype for bound-transformation strategies applied to efficient frontier bounds.

A bound and the risk expression it bounds are not always in the same units. A concrete subtype names the conversion between them, and it is used twice: once on a stated bound value, and once on each end of a Frontier sweep span. Because the span is transformed before it is divided, the sweep points are evenly spaced in the units of the expression, not in the units of the measure.

Interfaces

In order to implement a new bound transformation, subtype FrontierBoundEstimator and implement:

Required method name

  • variance_risk_bounds_val(bound::MyBound, ub::Num_VecNum): Convert a stated bound into the units of the risk expression.

Arguments

  • bound: The bound-transformation instance.
  • ub: The bound value the caller stated.

Returns

  • The converted bound, in the units of the risk expression.

The Frontier arm of variance_risk_bounds_val is generic and needs no method. The sweep applies the same transformation to both ends of its span in rebuild_risk_frontier, which reads bound off the Frontier and branches on it there.

Related

source
PortfolioOptimisers.LinearBoundType
struct LinearBound <: FrontierBoundEstimator

Passes bound values through unchanged (identity transformation).

The conversion is ub itself. The caller writes the bound against the measure, and the model enforces it against that same quantity. Used when the risk expression and the user-supplied bound are already in the same units (e.g. SDP variance formulation where both sides are in variance units). This is the default of Frontier, so a sweep that names no bound is evenly spaced in the units of the measure.

Related

source
PortfolioOptimisers.SquareRootBoundType
struct SquareRootBound <: FrontierBoundEstimator

Applies a square-root transformation to bound values before enforcing them.

The conversion is sqrt(ub). The caller writes the bound against the measure, and the model enforces it against the square root of that quantity, so a bound on a variance becomes a bound on a standard deviation. Used when the risk expression is in standard-deviation units but the user-supplied bound is in variance units (e.g. kurtosis and negative-skewness SOC formulations). This is also the transformation the default Variance formulation takes, so a Variance frontier is evenly spaced in standard-deviation units, and its variances are not evenly spaced.

Related

source
PortfolioOptimisers.SquaredBoundType
struct SquaredBound <: FrontierBoundEstimator

Applies a squaring transformation to bound values before enforcing them.

The conversion is ub^2. The caller writes the bound against the measure, and the model enforces it against the square of that quantity, so a bound on a standard deviation becomes a bound on a variance. Used when the risk expression is in squared units but the user-supplied bound is in linear units (e.g. kurtosis SDP formulation).

Related

source
PortfolioOptimisers.risk_input_kindFunction
risk_input_kind(r::AbstractBaseRiskMeasure) -> RiskInputKind

Return the RiskInputKind of risk measure r, declaring what its functor consumes when expected_risk evaluates it.

There is no default: every concrete AbstractBaseRiskMeasure (other than composite measures handled by explicit expected_risk methods) must declare its kind beside its type definition. Returning one of NetReturnsInput, WeightsReturnsFeesInput, or WeightsInput. An undeclared measure throws, rather than silently routing to the wrong input shape.

Validation

  • Throws an ArgumentError when r declares no method of its own. An undeclared measure would otherwise route to the wrong input shape in silence.

Returns

  • RiskInputKind: the declared input kind.

Related

source
PortfolioOptimisers.port_opt_viewMethod
port_opt_view(rs, i, X)

Get a view or subset of a risk measure for asset cluster index i.

Returns the risk measure sliced for the given cluster or asset index. Used internally in hierarchical optimisation to apply risk measures to each cluster.

A vector of risk measures (VecBaseRM) is handled by the generic vector methods of factory and port_opt_view, which rebuild and view each measure in turn.

Arguments

  • rs: Risk measure.
  • i: Cluster or asset index.
  • X: Data matrix (used for dimension-aware slicing).

Returns

  • Sliced risk measure or the original if no slicing is needed.

Related

source
PortfolioOptimisers.needs_previous_weightsMethod
needs_previous_weights(r::AbstractBaseRiskMeasure) -> Bool
needs_previous_weights(r::VecBaseRM) -> Bool

Return whether risk measure r requires previous portfolio weights as input.

The default returns false. Override to true for risk measures such as turnover or tracking constraints that depend on prior weights. The VecBaseRM overload returns true if any element returns true.

Returns

  • Bool: true if prior weights are required; false otherwise.

Related

source

References

[5]
D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025).
[95]
S. Boyd and L. Vandenberghe. Convex Optimization (Cambridge University Press, Cambridge, UK, 2004).
[96]
S. Diamond and S. Boyd. CVXPY: A Python-embedded modeling language for convex optimization. Journal of Machine Learning Research 17, 1–5 (2016).