Skip to content
18

ℓ1 Uncertainty Sets

PortfolioOptimisers.L1UncertaintySet Type
julia
struct L1UncertaintySet{__T_eps, __T_sd} <: AbstractUncertaintySetResult

1 (cross-polytope) uncertainty set on the characteristic vector.

The set is S={μ^+e:eσ1ϵ}, with a single error budget shared across every asset and both signs. Produced by CharacteristicUncertaintySet and consumed by ArithmeticReturn.

Fields

  • eps: Radius of the ell1 uncertainty set on the characteristic vector. Larger values admit more estimation error, and therefore activate more assets.

  • sd: Per-asset scaling vector for the ell1 uncertainty set (the estimated standard deviations). nothing leaves the set unscaled, so every element of the characteristic vector is assumed to suffer the same estimation error.

Constructors

julia
L1UncertaintySet(;
    eps::Number,
    sd::Option{<:VecNum} = nothing
) -> L1UncertaintySet

Keywords correspond to the struct's fields.

Validation

  • isfinite(eps) and eps >= 0.

  • If sd is provided: !isempty(sd) and all(sd .> 0).

Mathematical definition

The worst case of a linear characteristic over the set collapses to a scaled infinity norm (Lemmas 1 and 8 of [2]):

minμSμw=μ^wϵσw.

Where:

  • μ^: Estimated characteristic vector.

  • ϵ: Radius of the set.

  • σ: Per-asset scaling (sd); 1 when sd is nothing.

  • , : Element-wise product and division.

Because the right-hand side is concave and positively homogeneous, this is an LP once the infinity norm is epigraphed — no conic solver is needed.

Notes

This set bounds a mean/characteristic vector. It has no covariance analogue, so sigma_ucs is not defined for the estimator that produces it.

Related

source
PortfolioOptimisers.SignedL1UncertaintySet Type
julia
struct SignedL1UncertaintySet{__T_ep, __T_en, __T_sd} <: AbstractUncertaintySetResult

Signed 1 uncertainty set on the characteristic vector, with a separate error budget per sign.

The set is A2={μ^+e:1[eσ]+ϵ+,1[eσ]ϵ}.

Fields

  • ep: Radius of the positive-error side of the signed ell1 uncertainty set.

  • en: Radius of the negative-error side of the signed ell1 uncertainty set.

  • sd: Per-asset scaling vector for the ell1 uncertainty set (the estimated standard deviations). nothing leaves the set unscaled, so every element of the characteristic vector is assumed to suffer the same estimation error.

Constructors

julia
SignedL1UncertaintySet(;
    ep::Number,
    en::Number,
    sd::Option{<:VecNum} = nothing
) -> SignedL1UncertaintySet

Keywords correspond to the struct's fields.

Validation

  • isfinite(ep) and ep >= 0.

  • isfinite(en) and en >= 0.

  • If sd is provided: !isempty(sd) and all(sd .> 0).

Mathematical definition

minμA2μw=μ^wϵ+[maxi(σiwi)]+ϵ[maxi(σiwi)]+.

Where:

  • ϵ+, ϵ: Radii of the positive- and negative-error sides (ep, en).

  • []+, []: Element-wise positive and negative parts.

Still concave and LP-representable, with one epigraph variable per sign.

Notes

This is not L1UncertaintySet with ep == en: the joint set shares one budget across both signs, giving max(t+,t), whereas this one spends a budget per sign, giving ϵ+t++ϵt. The two agree only when w is single-signed — as it is under a long-only budget, where the joint set is the simpler choice.

[2] introduces this set in order to decouple the long-short problem into two independent problems (its equations 27 and 28), which its Remark 12 then recombines only when the two legs happen to have complementary support. Modelling the worst case above directly keeps the problem coupled, so that caveat does not arise.

Related

source
PortfolioOptimisers.L1UncertaintySetAlgorithm Type
julia
struct L1UncertaintySetAlgorithm{__T_method, __T_scaled, __T_paired} <: AbstractUncertaintySetAlgorithm

Shape algorithm selecting a joint 1 uncertainty set.

Fields

  • method: Radius of the ell1 uncertainty set. A number is the radius itself; an AbstractUncertaintyEpsAlgorithm computes it from the data.

  • scaled: Whether to scale the uncertainty set by the estimated standard deviations. false assumes every characteristic suffers the same estimation error; true assumes assets with larger variance suffer larger estimation error, which yields inverse-volatility weights.

  • paired: paired: Whether to calibrate the radius against the paired (dollar-neutral) ladder rather than the long-only one. Inert when method is a number.

Constructors

julia
L1UncertaintySetAlgorithm(;
    method::Num_UcSEps = ActiveAssetsUncertaintyAlgorithm(),
    scaled::Bool = false,
    paired::Bool = false
) -> L1UncertaintySetAlgorithm

Keywords correspond to the struct's fields.

Validation

  • If method is a Number: isfinite(method) and method >= 0.

Details

scaled selects between the two uncertainty sets of [2]: false gives S (its equation 5), which assumes every characteristic suffers the same estimation error and yields equally-weighted active assets; true gives A1 (its equation 18), which assumes assets with larger volatility suffer larger estimation error and yields inverse-volatility weights.

paired selects which closed form calibrates the radius when method is an AbstractUncertaintyEpsAlgorithm, and is inert when method is a number. The number of assets a radius activates depends on the sign structure of the problem it is used in, which an uncertainty set cannot observe, so the caller must say:

  • false: the long-only ladder, for a problem with w >= 0 and bgt = 1 (Corollaries 4 and 11 of [2]).

  • true: the paired ladder, for a dollar-neutral problem with bgt = 0 and sbgt = 1/2, where assets activate in long/short pairs (Corollary 7).

Using the wrong one mis-calibrates the radius; it does not make the optimisation incorrect.

Related

source
PortfolioOptimisers.SignedL1UncertaintySetAlgorithm Type
julia
struct SignedL1UncertaintySetAlgorithm{__T_mp, __T_mm, __T_scaled} <: AbstractUncertaintySetAlgorithm

Shape algorithm selecting a signed 1 uncertainty set.

Fields

  • mp: Radius of the positive-error side. A number is the radius itself; an AbstractUncertaintyEpsAlgorithm computes it from the data.

  • mm: Radius of the negative-error side. A number is the radius itself; an AbstractUncertaintyEpsAlgorithm computes it from the data.

  • scaled: Whether to scale the uncertainty set by the estimated standard deviations. false assumes every characteristic suffers the same estimation error; true assumes assets with larger variance suffer larger estimation error, which yields inverse-volatility weights.

Constructors

julia
SignedL1UncertaintySetAlgorithm(;
    mp::Num_UcSEps = ActiveAssetsUncertaintyAlgorithm(),
    mm::Num_UcSEps = ActiveAssetsUncertaintyAlgorithm(),
    scaled::Bool = true
) -> SignedL1UncertaintySetAlgorithm

Keywords correspond to the struct's fields.

Validation

  • If mp/mm is a Number: isfinite and >= 0.

Details

mp yields ϵ+ and mm yields ϵ. When calibrated by an AbstractUncertaintyEpsAlgorithm the ladders are fixed by the paper's construction and need no paired flag: ϵ governs the long leg and is calibrated against the top of the ranking, ϵ+ governs the short leg and is calibrated against the bottom (Corollary 13 of [2]).

Related

source
PortfolioOptimisers.CharacteristicUncertaintySet Type
julia
struct CharacteristicUncertaintySet{__T_pe, __T_alg} <: AbstractUncertaintySetEstimator

Estimator for 1 uncertainty sets on the characteristic vector.

Fits the robust best-characteristic uncertainty sets of [2]. Maximising the worst-case characteristic over one of these sets recovers the heuristic 1/N, quintile, and inverse-volatility portfolios as exact solutions of a robust optimisation problem, with the radius controlling how many assets are held — which is why this library ships no dedicated quintile optimiser. Compose the set with MeanRisk instead:

julia
MeanRisk(; r = NoRisk(), obj = MaximumReturn(),
         opt = JuMPOptimiser(; slv = slv, bgt = 1.0,
                             ret = ArithmeticReturn(; ucs = CharacteristicUncertaintySet())))

Fields

Constructors

julia
CharacteristicUncertaintySet(;
    pe::AbstractLowOrderPriorEstimator = EmpiricalPrior(),
    alg::AbstractUncertaintySetAlgorithm = L1UncertaintySetAlgorithm()
) -> CharacteristicUncertaintySet

Keywords correspond to the struct's fields.

Validation

Details

pe supplies both the characteristic vector μ^ and, when the shape algorithm is scaled, the per-asset scaling σ^=diag(Σ^).

The characteristic need not be an expected return. [2] notes that any characteristic works, and the library already carries the machinery: a prior built on StandardDeviationExpectedReturns ranks on volatility (the Low Volatility factor), reproducing Table III of the paper.

Notes

This estimator is mean-only. ucs and sigma_ucs are defined solely to throw an informative error: the 1 ball bounds a characteristic vector, and the paper defines no covariance analogue.

Related

source
PortfolioOptimisers.ActiveAssetsUncertaintyAlgorithm Type
julia
struct ActiveAssetsUncertaintyAlgorithm{__T_active} <: AbstractUncertaintyEpsAlgorithm

Radius algorithm that calibrates the 1 uncertainty radius to a target number of active assets.

Fields

  • active: Target number of active assets on the unconstrained problem, as a count (integer >= 1) or a fraction of the universe (float in (0, 1)). This is a radius calibration, not a cardinality constraint: it selects the radius that would activate this many assets subject only to the budget and sign constraints. Any further constraint may change the realised count. Use card for a hard cardinality constraint.

Constructors

julia
ActiveAssetsUncertaintyAlgorithm(;
    active::Union{<:Integer, <:AbstractFloat} = 0.2
) -> ActiveAssetsUncertaintyAlgorithm

Keywords correspond to the struct's fields.

Validation

  • If active is an Integer: active >= 1.

  • If active is an AbstractFloat: 0 < active < 1.

Details

The radius ϵ of an 1 uncertainty set has no natural scale — it is a sum of characteristic differences, so on daily returns it is of order 103, and on annualised returns roughly 250 times larger. This algorithm converts the quantity a caller can reason about (how many assets should the portfolio hold?) into the radius that produces it, by inverting the closed forms of [2].

The map is exact, but only for the bare problem the closed forms assume: the budget and sign constraints, and nothing else. It is a radius calibration, not a cardinality constraint. Adding weight bounds, cardinality, or linear constraints may change the realised number of active assets, and this algorithm neither knows nor checks. For a hard bound on the number of holdings use the card field of JuMPOptimiser.

Related

source
PortfolioOptimisers.l1_activation_ladder Function
julia
l1_activation_ladder(mu::VecNum, sd::Option{<:VecNum})

Return the vector g whose k-th entry is the radius at which the k-th asset becomes active in the long-only problem.

mu must be sorted in non-increasing order, with sd (when given) under the same permutation. Entry k is i=1k(μ^iμ^k)/σi, the threshold of Lemma 2 (sd === nothing) or Lemma 9 (scaled) of [2]. The sequence is non-decreasing, so a radius eps activates the largest k with g[k] < eps.

Related

source
PortfolioOptimisers.l1_active_count Function
julia
l1_active_count(active::Union{<:Integer, <:AbstractFloat}, N::Int)

Convert an active target — a count or a fraction of the universe — into an asset count in 1:N.

Related

source
PortfolioOptimisers.l1_eps_from_ladder Function
julia
l1_eps_from_ladder(method, ladder::VecNum, N::Int)

Resolve a radius from a Num_UcSEps against an activation ladder.

A number passes through unchanged. An ActiveAssetsUncertaintyAlgorithm returns the midpoint of the open interval (ladder[q], ladder[q+1]) that the closed forms of [2] require for exactly q active entries; at the top of the ladder it continues the final increment by a half step, since any radius above ladder[end] activates everything.

Throws when the target interval is empty, which happens when the characteristic has ties across the cut — the paper excludes this case by assumption, and it cannot be satisfied.

Related

source
PortfolioOptimisers.l1_resolve_eps Function
julia
l1_resolve_eps(method, mus::VecNum, sds::Option{<:VecNum}, paired::Bool)

Resolve a radius from a Num_UcSEps against the characteristic vector.

mus must be sorted in non-increasing order, with sds under the same permutation. A number passes through unchanged. An ActiveAssetsUncertaintyAlgorithm is calibrated against the long-only ladder when paired is false, and against the paired ladder — where the i-th best pairs with the i-th worst, so entries activate two assets at a time — when it is true.

Related

source
PortfolioOptimisers.mu_ucs Function
julia
mu_ucs(ue::CharacteristicUncertaintySet, X::MatNum,
       F::Option{<:MatNum} = nothing; dims::Int = 1, kwargs...)

Construct an 1 uncertainty set on the characteristic vector.

Computes the prior, takes μ^ from it (and σ^=diag(Σ^) when the shape algorithm is scaled), then resolves the radius from the shape algorithm.

Arguments

  • ue: Characteristic uncertainty set estimator.

  • X: Data matrix (e.g. returns).

  • F: Optional factor matrix. Used by the prior estimator.

  • dims: Dimension along which to perform the computation.

  • kwargs...: Additional keyword arguments passed to the prior estimator.

Returns

  • mu_ucs::Union{<:L1UncertaintySet, <:SignedL1UncertaintySet}: The uncertainty set.

Related

source
PortfolioOptimisers.ucs Function
julia
ucs(ue::CharacteristicUncertaintySet, X::MatNum, F::Option{<:MatNum} = nothing; kwargs...)
sigma_ucs(ue::CharacteristicUncertaintySet, X::MatNum, F::Option{<:MatNum} = nothing; kwargs...)

Always throw. CharacteristicUncertaintySet is mean-only.

The 1 set bounds a characteristic (mean) vector; [2] defines no covariance analogue, so there is nothing for these to return. Use NormalUncertaintySet, DeltaUncertaintySet, or ARCHUncertaintySet for a covariance uncertainty set.

These take the same (X, F) signature as the rest of the family rather than a catch-all, so that the ReturnsResult forwarders in the base reach them without ambiguity.

Related

source