ℓ1 Uncertainty Sets: private API

PortfolioOptimisers.l1_activation_ladderFunction
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.

Mathematical definition

\[\begin{align} L_k &= \sum_{i=1}^{k} \frac{\hat{\mu}_i - \hat{\mu}_k}{\sigma_i}\,, \quad k = 1,\, \ldots,\, N\,. \end{align}\]

Where:

  • $L_k$: Activation threshold of the $k$-th asset of the ranking.
  • $\hat{\mu}_i$: $i$-th entry of the characteristic vector, sorted non-increasing.
  • $\sigma_i$: Per-asset scaling of the $i$-th entry of the characteristic vector; $1$ when the set is unscaled.
  • $N$: Number of assets.

This is the threshold of Lemma 2 ($\sigma_i = 1$) and of Lemma 9 (scaled) of [4]. Both lemmas state that the number of active assets is the largest $k$ with $L_k < \epsilon$, so a radius in the open interval $(L_q,\, L_{q+1})$ activates exactly $q$ assets, and the active weights are equal (Lemma 2) or inverse-volatility (Lemma 9). Corollaries 4 and 11 read this off at the quintile.

Two consequences bound the sequence. Every summand of $L_1$ is zero, so $L_1 = 0$: a strictly positive radius always activates at least one asset. And $L_{k+1} - L_k = \sum_{i=1}^{k+1} (\hat{\mu}_k - \hat{\mu}_{k+1}) / \sigma_i \geq 0$ because the ranking is non-increasing, so the sequence is non-decreasing and its rungs bracket. Equality holds exactly when $\hat{\mu}_k = \hat{\mu}_{k+1}$, which is the tie Assumption 1 of the paper excludes.

Algorithm

  1. Read N = length(mu), the number of rungs to build.
  2. For each k in 1:N, sum $(\hat{\mu}_i - \hat{\mu}_k) / \sigma_i$ over i in 1:k, taking $\sigma_i = 1$ when sd is nothing, giving the k-th rung.

Arguments

  • mu: Characteristic vector, sorted in non-increasing order.
  • sd: Per-asset scaling under the same permutation, or nothing for an unscaled ladder.

Returns

  • g::Vector{<:Number}: The activation ladder, one rung per asset.

Examples

julia> PortfolioOptimisers.l1_activation_ladder([0.125, 0.0625, 0.03125], nothing)3-element Vector{Float64}: 0.0 0.0625 0.125julia> PortfolioOptimisers.l1_activation_ladder([0.125, 0.0625, 0.03125], [0.5, 0.25, 0.125])3-element Vector{Float64}: 0.0 0.125 0.3125

Related

References

  • [4] R. Zhou and D. P. Palomar. Understanding the Quintile Portfolio. IEEE Transactions on Signal Processing 68, 4030–4040 (2020).
source
PortfolioOptimisers.l1_active_countFunction
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.

The type of active selects the rule, and the clamp makes a target outside the universe usable rather than an error: a count above N becomes N, and a fraction that rounds to zero becomes 1.

Algorithm

  1. When active is an Integer, take it as the count itself, giving q.
  2. Otherwise take round(Int, active * N), the fraction of the universe rounded to the nearest asset, giving q.
  3. Return clamp(q, 1, N), so the count names an asset of the ranking.

Arguments

  • active: Target number of active assets, as a count (Integer) or a fraction of the universe (AbstractFloat).
  • N: Number of assets in the universe.

Returns

  • q::Int: The target count, in 1:N.

Examples

julia> PortfolioOptimisers.l1_active_count(3, 10)3julia> PortfolioOptimisers.l1_active_count(0.2, 10)2julia> PortfolioOptimisers.l1_active_count(20, 10)10

Related

References

  • [4] R. Zhou and D. P. Palomar. Understanding the Quintile Portfolio. IEEE Transactions on Signal Processing 68, 4030–4040 (2020).
source
PortfolioOptimisers.l1_eps_from_ladderFunction
l1_eps_from_ladder(method, ladder::VecNum, N::Int)

Resolve a radius from a Num_UcSEps against an activation ladder.

The method that takes a Number is a passthrough: it returns the radius the caller gave, runs no procedure, and carries neither an # Algorithm nor a # Validation section. The method that takes an ActiveAssetsUncertaintyAlgorithm returns the midpoint of the open interval $(L_q,\, L_{q+1})$ that the closed forms of [4] require for exactly q active entries.

Above the last rung any radius activates every entry, so the top of the ladder has no interval to bisect and the method continues the final increment by a half step instead. A ladder of one rung has no increment either, and the method returns one(eltype(ladder)). That radius is inert rather than calibrated: a one-rung ladder is a one-asset universe, where the budget pins the single weight and every radius gives the same portfolio.

Algorithm

  1. Read L = length(ladder), the number of rungs.
  2. Clamp q to 1:L, so the target names a rung.
  3. When q < L, read the bracketing rungs lo, hi = ladder[q], ladder[q+1] and return their midpoint, a radius strictly inside the open interval.
  4. Otherwise q == L: return ladder[L] + (ladder[L] - ladder[L-1]) / 2, half a final increment above the last rung, or one(eltype(ladder)) when L == 1 and there is no increment to continue.

Arguments

Validation

  • length(ladder) >= 1, otherwise an IsEmptyError is thrown.
  • ladder[q+1] > ladder[q] when q < L, otherwise a DomainError is thrown. The rungs coincide when the characteristic has ties across the cut, the interval the closed form needs is empty, and no radius delivers the requested count. Assumption 1 of [4] excludes the case, so the message names the two fixes: break the tie, or pass an explicit radius.

Returns

  • eps::Number: The resolved radius.

Examples

julia> PortfolioOptimisers.l1_eps_from_ladder(0.5)0.5julia> alg = ActiveAssetsUncertaintyAlgorithm(; active = 2);julia> PortfolioOptimisers.l1_eps_from_ladder(alg, [0.0, 0.0625, 0.125], 2)0.09375julia> PortfolioOptimisers.l1_eps_from_ladder(alg, [0.0, 0.0625, 0.125], 3)0.15625julia> PortfolioOptimisers.l1_eps_from_ladder(alg, [0.0], 1)1.0

Related

References

  • [4] R. Zhou and D. P. Palomar. Understanding the Quintile Portfolio. IEEE Transactions on Signal Processing 68, 4030–4040 (2020).
source

References

[4]
R. Zhou and D. P. Palomar. Understanding the Quintile Portfolio. IEEE Transactions on Signal Processing 68, 4030–4040 (2020).