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 coneRSOC: 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.
| Type | Name (Alias) | Compatibility | Requirements | Rec. Solver |
|---|---|---|---|---|
| Dispersion | Variance | JuMP + Hierarchical | QP, SOC | Clarabel |
| Dispersion | SDP graph / Risk contribution Variance | JuMP + Hierarchical | SDP | Clarabel, SCS |
| Dispersion | StandardDeviation (SD) | JuMP + Hierarchical | SOC | Clarabel |
| Dispersion | Box UncertaintySetVariance (UcVariance) | JuMP + Hierarchical | Sym | Clarabel |
| Dispersion | Ellipse UncertaintySetVariance (UcVariance) | JuMP + Hierarchical | Sym, PSD, SOC | Clarabel, SCS |
| Dispersion | LowOrderMoment (FLM) | JuMP + Hierarchical | - | Clarabel |
| Dispersion | LowOrderMoment (MAD) | JuMP + Hierarchical | - | Clarabel |
| Dispersion | Direct / Squared SOC LowOrderMoment (SCM, SLM) | JuMP + Hierarchical | QP, SOC | Clarabel |
| Dispersion | Sum of Squares LowOrderMoment (SCM, SLM) | JuMP + Hierarchical | SOC, RSOC | Clarabel |
| Dispersion | SOC LowOrderMoment (SCM, SLM) | JuMP + Hierarchical | SOC | Clarabel |
| Dispersion | LowOrderMoment (ECM, ELM) | JuMP + Hierarchical | PC | Clarabel |
| Dispersion | HighOrderMoment (TLM, SSK, FTCM, FTLM, KT, SKT) | Hierarchical | - | - |
| Dispersion | Direct / Squared SOC Exact Kurtosis | JuMP + Hierarchical | QP, SOC, PSD | Clarabel, SCS |
| Dispersion | Sum of Squares Exact Kurtosis | JuMP + Hierarchical | SOC, RSOC, PSD | Clarabel, SCS |
| Dispersion | SOC Exact Kurtosis | JuMP + Hierarchical | SOC, PSD | Clarabel, SCS |
| Dispersion | Direct / Squared SOC Approx Kurtosis | JuMP + Hierarchical | QP, SOC | Clarabel |
| Dispersion | Sum of Squares Approx Kurtosis | JuMP + Hierarchical | SOC, RSOC | Clarabel |
| Dispersion | SOC Exact Approx Kurtosis | JuMP + Hierarchical | SOC | Clarabel |
| Dispersion | NegativeSkewness | JuMP + Hierarchical | QP, SOC | Clarabel |
| Dispersion | Square Root NegativeSkewness | JuMP + Hierarchical | SOC | Clarabel |
| Tail loss | Exact ValueatRisk (VaR) | JuMP + Hierarchical | MIP | Pajarito (Clarabel + HiGHS) |
| Tail loss | Approx ValueatRisk (VaR) | JuMP + Hierarchical | SOC | Clarabel |
| Tail drawdown | DrawdownatRisk (DaR) | JuMP + Hierarchical | MIP | Pajarito (Clarabel + HiGHS) |
| Tail drawdown | RelativeDrawdownatRisk (R_DaR) | Hierarchical | - | Clarabel |
| Dispersion | Exact ValueatRiskRange (VaR_RG) | JuMP + Hierarchical | MIP | Pajarito (Clarabel + HiGHS) |
| Dispersion | Approx ValueatRiskRange (VaR_RG) | JuMP + Hierarchical | SOC | Clarabel |
| Tail loss | ConditionalValueatRisk (CVaR) | JuMP + Hierarchical | - | Clarabel |
| Tail loss | DistributionallyRobustConditionalValueatRisk (DRCVaR) | JuMP + Hierarchical | NIC | Clarabel |
| Tail drawdown | ConditionalDrawdownatRisk (CDaR) | JuMP + Hierarchical | - | Clarabel |
| Tail drawdown | DistributionallyRobustConditionalDrawdownatRisk (DRCDaR) | JuMP + Hierarchical | NIC | Clarabel |
| Tail drawdown | RelativeConditionalDrawdownatRisk (R_CDaR) | Hierarchical | - | Clarabel |
| Dispersion | ConditionalValueatRiskRange (CVaR_RG) | JuMP + Hierarchical | - | Clarabel |
| Dispersion | DistributionallyRobustConditionalValueatRiskRange (DRCVaR_RG) | JuMP + Hierarchical | NIC | Clarabel |
| Tail loss | EntropicValueatRisk (EVaR) | JuMP + Hierarchical | EC | Clarabel |
| Tail drawdown | EntropicDrawdownatRisk (EDaR) | JuMP + Hierarchical | EC | Clarabel |
| Tail drawdown | RelativeEntropicDrawdownatRisk (R_EDaR) | Hierarchical | EC | Clarabel |
| Dispersion | EntropicValueatRiskRange (EVaR_RG) | JuMP + Hierarchical | EC | Clarabel |
| Tail loss | RelativisticValueatRisk (RLVaR) | JuMP + Hierarchical | PC | Clarabel |
| Tail drawdown | RelativisticDrawdownatRisk (RLDaR) | JuMP + Hierarchical | PC | Clarabel |
| Tail drawdown | RelativeRelativisticDrawdownatRisk (R_RLDaR) | Hierarchical | PC | Clarabel |
| Dispersion | RelativisticValueatRiskRange (RLVaR_RG) | JuMP + Hierarchical | PC | Clarabel |
| Tail loss | PowerNormValueatRisk (PNVaR) | JuMP + Hierarchical | PC | Clarabel |
| Tail drawdown | PowerNormDrawdownatRisk (PNDaR) | JuMP + Hierarchical | PC | Clarabel |
| Tail drawdown | RelativePowerNormDrawdownatRisk (R_PNDaR) | Hierarchical | PC | Clarabel |
| Dispersion | PowerNormValueatRiskRange (PNVaR_RG) | JuMP + Hierarchical | PC | Clarabel |
| Dispersion | Exact OrderedWeightsArray (OWA_GMD, OWA_RG, OWA_CVaR_RG, OWA_TG_RG, OWA_LMoment) | JuMP + Hierarchical | - | Clarabel |
| Dispersion | Approx OrderedWeightsArray (OWA_GMD, OWA_RG, OWA_CVaR_RG, OWA_TG_RG, OWA_LMoment) | JuMP + Hierarchical | PC | Clarabel |
| Tail loss | Exact OrderedWeightsArray (OWA_CVaR, OWA_TG, OWA_WR) | JuMP + Hierarchical | - | Clarabel |
| Tail loss | Approx OrderedWeightsArray (OWA_CVaR, OWA_TG, OWA_WR) | JuMP + Hierarchical | PC | Clarabel |
| Dispersion | Exact OrderedWeightsArrayRange | JuMP + Hierarchical | - | Clarabel |
| Dispersion | Approx OrderedWeightsArrayRange | JuMP + Hierarchical | PC | Clarabel |
| Drawdown | AverageDrawdown (ADD) | JuMP + Hierarchical | - | Clarabel |
| Drawdown | RelativeAverageDrawdown (R_ADD) | Hierarchical | - | - |
| Drawdown | UlcerIndex (UCI) | JuMP + Hierarchical | SOC | Clarabel |
| Drawdown | RelativeUlcerIndex (R_UCI) | Hierarchical | - | - |
| Tail drawdown | MaximumDrawdown (MDD) | JuMP + Hierarchical | - | Clarabel |
| Tail drawdown | RelativeMaximumDrawdown (R_MDD) | Hierarchical | - | - |
| Dispersion | Direct BrownianDistanceVariance (BDVariance) | JuMP + Hierarchical | QP | Clarabel |
| Dispersion | Sum of Squares BrownianDistanceVariance (BDVariance) | JuMP + Hierarchical | QP, RSOC | Clarabel |
| Tail loss | WorstRealisation (WR) | JuMP + Hierarchical | - | Clarabel |
| Tail loss | Range (RG) | JuMP + Hierarchical | - | Clarabel |
| Turnover | TurnoverRiskMeasure (TnRM) | JuMP + Hierarchical | NOC | Clarabel |
| Tracking | L1 Norm TrackingRiskMeasure (TrRM) | JuMP + Hierarchical | NOC | Clarabel |
| Tracking | L2 Norm TrackingRiskMeasure (TrRM) | JuMP + Hierarchical | SOC | Clarabel |
| Tracking | Squared L2 Norm TrackingRiskMeasure (TrRM) | JuMP + Hierarchical | QP, SOC | Clarabel |
| Tracking | Lp Norm TrackingRiskMeasure (TrRM) | JuMP + Hierarchical | PC | Clarabel |
| Tracking | Infinity Norm TrackingRiskMeasure (TrRM) | JuMP + Hierarchical | NIC | Clarabel |
| Risk tracking | Independent variable RiskTrackingRiskMeasure (RkTrRM) | JuMP + Hierarchical | * | Clarabel |
| Risk tracking | Dependent variable RiskTrackingRiskMeasure (RkTrRM) | JuMP + Hierarchical | NOC, * | Clarabel |
| Dispersion | VarianceSkewKurtosis (VSK) | JuMP + Hierarchical | Sym, PSD | SCS |
| Dispersion | GenericValueatRiskRange (GVaR_RG) | JuMP + Hierarchical | * | Clarabel |
| Ratio | RiskRatio | Hierarchical | * | * |
| Ratio | NonOptimisationRiskRatio (NonOptRkRatio) | - | * | * |
| Flat | EqualRisk | - | - | - |
| Dispersion | MedianAbsoluteDeviation | Hierarchical | - | - |
| Performance | MeanReturn | - | - | - |
| Performance | MeanReturnRiskRatio | - | - | - |
| Performance | ExpectedReturn | - | - | - |
| Performance | ExpectedReturnRiskRatio | - | - | - |
| Dispersion | ThirdCentralMoment | - | - | - |
| Dispersion | Skewness | - | - | - |
PortfolioOptimisers.RiskMeasure — Type
abstract type RiskMeasure <: OptimisationRiskMeasureAbstract 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 ofNetReturnsInput(),WeightsReturnsFeesInput()orWeightsInput().
The functor
expected_risk evaluates the measure as a functor, in the shape its kind declares:
(r::MyRiskMeasure)(x::VecNum) -> NumberunderNetReturnsInput, wherexis the net portfolio return seriescalc_net_returnsbuilds.(r::MyRiskMeasure)(w::VecNum, X::MatNum, fees::Option{<:Fees}) -> NumberunderWeightsReturnsFeesInput.(r::MyRiskMeasure)(w::VecNum) -> NumberunderWeightsInput.
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:truefor a measure that reads the previous weights; the fallback answersfalse.bigger_is_better:truefor a measure a caller maximises; the fallback answersfalse.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
PortfolioOptimisers.HierarchicalRiskMeasure — Type
abstract type HierarchicalRiskMeasure <: OptimisationRiskMeasureAbstract 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 ofNetReturnsInput(),WeightsReturnsFeesInput()orWeightsInput().
The functor
expected_risk evaluates the measure as a functor, in the shape its kind declares:
(r::MyRiskMeasure)(x::VecNum) -> NumberunderNetReturnsInput, wherexis the net portfolio return seriescalc_net_returnsbuilds.(r::MyRiskMeasure)(w::VecNum, X::MatNum, fees::Option{<:Fees}) -> NumberunderWeightsReturnsFeesInput.(r::MyRiskMeasure)(w::VecNum) -> NumberunderWeightsInput.
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:truefor a measure that reads the previous weights; the fallback answersfalse.bigger_is_better:truefor a measure a caller maximises; the fallback answersfalse.factory: rebuild the measure against a prior result before it is evaluated, for a measure whose slots are filled from the prior.
Related
PortfolioOptimisers.Frontier — Type
struct Frontier{__T_N, __T_factor, __T_bound} <: AbstractAlgorithmSweeps 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 modelNtimes, atNevenly spaced bound values.
factor: Multiplier applied to both ends of the sweep span afterboundhas transformed them. It carries a formulation's own correction factor, such as theinv(1 / (T - ddof))of a second-moment bound.
bound:FrontierBoundEstimatorthat 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()) -> FrontierCreates 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) -> FrontierInternal constructor. Keywords correspond to the struct's fields.
Validation
N > 0andN <= RESOURCE_LIMITS[].max_frontier(each sweep point runs a full solve; seeRESOURCE_LIMITS). This is the cheap early check on one bound. The sweep across several swept bounds is their product, capped separately at Model Assembly byassert_frontier_sweep_cap.isfinite(factor)andfactor > 0.
Examples
julia> Frontier(; N = 15)Frontier N ┼ Int64: 15 factor ┼ Int64: 1 bound ┴ LinearBound()Related
FrontierBoundEstimatorLinearBoundSquareRootBoundSquaredBoundRiskMeasureSettingsrebuild_risk_frontier
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.
PortfolioOptimisers.AbstractRiskMeasureSettings — Type
abstract type AbstractRiskMeasureSettings <: AbstractEstimatorAbstract 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 byset_risk_expression!and by the value-level readers.rke::Bool: Whether the measure contributes to the model's aggregate risk expression.
Optional field
- A bound field.
RiskMeasureSettingsnames ituband bounds the expression from above;MaxRiskMeasureSettingsnames itlband bounds it from below.HierarchicalRiskMeasureSettingshas none, because a clustering optimiser builds no model to bound.
Related
PortfolioOptimisers.RiskMeasureSettings — Type
struct RiskMeasureSettings{__T_scale, __T_ub, __T_rke} <: JuMPRiskMeasureSettingsWeights 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 inubbinds on the measure's own expression, beforescaleis applied.
ub: Upper bound(s) on the measure's own risk expression. A scalar bounds one model. A vector and aFrontierare sweep axes, one solve per entry, so the optimisation returns one portfolio per bound value.
rke: Whether to include the risk measure value in theJuMPrisk expression.
Constructors
RiskMeasureSettings(; scale::Number = 1.0, ub::Option{<:RkRtBounds} = nothing, rke::Bool = true,) -> RiskMeasureSettingsCreates a RiskMeasureSettings instance with the specified scale, upper bound, and risk evaluation flag.
Validation
isfinite(scale).ubis validated withassert_nonempty_nonneg_finite_val.
Examples
julia> RiskMeasureSettings()RiskMeasureSettings scale ┼ Float64: 1.0 ub ┼ nothing rke ┴ Bool: trueRelated
JuMPRiskMeasureSettingsRiskMeasureFrontierHierarchicalRiskMeasureSettingsMaxRiskMeasureSettingsset_risk_bounds_and_expression!
References
- [5] D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025). Section 8.2.2, Equation 8.10.
PortfolioOptimisers.HierarchicalRiskMeasureSettings — Type
struct HierarchicalRiskMeasureSettings{__T_scale} <: AbstractRiskMeasureSettingsWeights 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 inubbinds on the measure's own expression, beforescaleis applied.
Constructors
HierarchicalRiskMeasureSettings(; scale::Number = 1.0) -> HierarchicalRiskMeasureSettingsCreates a HierarchicalRiskMeasureSettings instance with the specified scaling factor.
Validation
scalemust be finite.
Examples
julia> HierarchicalRiskMeasureSettings()HierarchicalRiskMeasureSettings scale ┴ Float64: 1.0Related
PortfolioOptimisers.Scalariser — Type
abstract type Scalariser <: AbstractEstimatorAbstract 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
JuMPoptimiser builds the aggregation into the model as an expression, so the strategy must have a convex form. This is whatNonHierarchicalScalariserandHierarchicalScalariserseparate: the two subtypes name the consumers that can build a given strategy, not a property of the resulting number.JuMPOptimiser.scais boundedNonHierarchicalScalariserand refuses the hierarchical half; the clustering optimisers compute each cluster's risk separately and accept either. - The value level.
expected_riskand 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. Everyscakeyword and everyscaresult field at this level is boundedScalariser, and all four scalarisers are admitted,MinScalariserincluded.
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 valuesfreturns 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
freturns.
Required for a NonHierarchicalScalariser
scalarise_risk_expression!(model::JuMP.Model, sca::MyScalariser): Collapse the model'srisk_vecarray into the singleriskexpression. AHierarchicalScalariserdeclares 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.scalarizemodule, which this family ports.
PortfolioOptimisers.scalarise — Function
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.fis expected to include the measure'ssettings.scaleweight in its result.by === nothing: slot-wise (elementwise) maximum/minimum across values.bygiven: winner-take-all — returns the singlefresult whoseby(result)is largest/smallest (ties keep the earliest).
LogSumExpScalariser: slot-wiselogsumexpofgamma-scaled values, divided bygamma.
Algorithm
- Start the accumulator
accatnothing, and the winning keykatnothingbeside it.LogSumExpScalariserstarts neither, and takes steps 5 and 6 instead. - Apply
fto each element ofitrin turn, givingv. - Fold
vintoaccthrough the branch thatscaselects.SumScalarisertakesscalarise_combine(+, acc, v).MaxScalariserandMinScalarisertakescalarise_combine(max, acc, v)andscalarise_combine(min, acc, v)whilebyisnothing. - Given a
by, read the keyki = by(v)in place of step 3, and takevandkias the newaccandkwhenkibeats thekalready held. A tie keeps the earlier element. - Under
LogSumExpScalariser, applyfto every element ofitr, scale each result bysca.gammawithscalarise_map, and collect them intovs. - Reduce
vsslot-wise withscalarise_logsumexp, and divide the reduction bysca.gammawithscalarise_map. - Return
acc, or the value step 6 built.
Arguments
f: Per-element evaluation closure, applied to each element ofitr.sca: Scalarisation strategy.itr: Iterable of risk measures (orpairsof them, whenfneeds the index).by: Optional selection key forMaxScalariser/MinScalariser; ignored by the other scalarisers.
Returns
- The scalarised value, with the same shape as the values returned by
f.
Related
PortfolioOptimisers.SumScalariser — Type
struct SumScalariser <: NonHierarchicalScalariserAdds 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
ScalariserMaxScalariserMinScalariserLogSumExpScalariserRiskMeasureSettingsHierarchicalRiskMeasureSettingsscalarise_risk_expression!
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.
PortfolioOptimisers.MaxScalariser — Type
struct MaxScalariser <: NonHierarchicalScalariserReports 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 variablescalarise_risk_expression!creates for this strategy. The rowrisk_msholds it at or above every entry ofrisk_vec, somodel[:risk]stands above the maximum rather than on it. - The bound is tight while the objective pulls
riskdown, which a minimum-risk objective does. UnderMaximumReturnnothing pulls, andmodel[:risk]is then an upper bound. - An
ubon the aggregate is unaffected. It constrainsrisk, and the aggregation stands belowrisk, so the aggregation meets the bound too. Read the exact figure back withexpected_risk.
Related
ScalariserSumScalariserMinScalariserLogSumExpScalariserRiskMeasureSettingsHierarchicalRiskMeasureSettingsscalarise_risk_expression!
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.
PortfolioOptimisers.MinScalariser — Type
struct MinScalariser <: HierarchicalScalariserReports 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
PortfolioOptimisers.LogSumExpScalariser — Type
struct LogSumExpScalariser{__T_gamma} <: NonHierarchicalScalariserSmooths 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.
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.
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 variablescalarise_risk_expression!creates for this strategy. The rowsu_risk_lseandrisk_lsestate the exponential-cone form of the log-sum-exp, which holdsmodel[:risk]at or above the smoothed maximum. - The bound is tight while the objective pulls
riskdown, which a minimum-risk objective does. UnderMaximumReturnnothing pulls, andmodel[:risk]is then an upper bound. - An
ubon the aggregate is unaffected, on the termsMaxScalariserstates. Read the exact figure back withexpected_risk.
Fields
gamma: Log-sum-exp scalariser smoothing parameter.
Constructors
LogSumExpScalariser(; gamma::Number = 1.0,) -> LogSumExpScalariserKeywords correspond to the struct's fields.
Validation
gamma > 0.
Examples
julia> LogSumExpScalariser()LogSumExpScalariser gamma ┴ Float64: 1.0Related
ScalariserSumScalariserMaxScalariserMinScalariserRiskMeasureSettingsHierarchicalRiskMeasureSettingsscalarise_risk_expression!
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.
PortfolioOptimisers.FrontierBoundEstimator — Type
abstract type FrontierBoundEstimator <: AbstractEstimatorAbstract 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
PortfolioOptimisers.LinearBound — Type
struct LinearBound <: FrontierBoundEstimatorPasses 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
PortfolioOptimisers.SquareRootBound — Type
struct SquareRootBound <: FrontierBoundEstimatorApplies 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
PortfolioOptimisers.SquaredBound — Type
struct SquaredBound <: FrontierBoundEstimatorApplies 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
PortfolioOptimisers.risk_input_kind — Function
risk_input_kind(r::AbstractBaseRiskMeasure) -> RiskInputKindReturn 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
ArgumentErrorwhenrdeclares 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
PortfolioOptimisers.factory — Method
factory(
rs::AbstractBaseRiskMeasure,
args...;
kwargs...
) -> AverageDrawdown{RiskMeasureSettings{__T_scale, __T_ub, __T_rke}} where {__T_scale, __T_ub, __T_rke}
Return the risk measure rs unchanged.
Identity pass-through used when a risk measure is provided in a context that calls factory.
Related
PortfolioOptimisers.port_opt_view — Method
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
PortfolioOptimisers.needs_previous_weights — Method
needs_previous_weights(r::AbstractBaseRiskMeasure) -> Bool
needs_previous_weights(r::VecBaseRM) -> BoolReturn 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:trueif prior weights are required;falseotherwise.
Related
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).