Base Uncertainty Sets: private API

PortfolioOptimisers.Num_CptRadType
const Num_CptRad = Union{<:AbstractCompactRadiusAlgorithm, <:Number}

Field bound for the compact covariance radius: the radius itself, or a rule of AbstractCompactRadiusAlgorithm that computes one.

The union names one family and no other, so a rule of any other family is refused at construction, where the caller wrote it. It admits no plain Function, because a rule of this family reads seven arguments the site settles — the metric, the loadings block, the diagonal metric and the span among them — and a closure over a caller's own data would have to restate every one of them to be called at all.

Related

source
PortfolioOptimisers.ucs_priorFunction
ucs_prior(pe::AbstractLowOrderPriorEstimator, X::MatNum,
          F::Option{<:MatNum} = nothing; dims::Int = 1, kwargs...)
ucs_prior(::Nothing, X::MatNum, F::Option{<:MatNum} = nothing; kwargs...)

Fits the prior an uncertainty set calibrates itself on, or refuses by name when the set holds none.

The one door through which every returns-data verb of the four families — DeltaUncertaintySet, NormalUncertaintySet, ARCHUncertaintySet and CharacteristicUncertaintySet — fits its pe, so the refusal is written once rather than once per verb per family. A set whose pe is nothing is calibrated on a prior result it is handed, and nothing says that one thing: it does not resolve to an empirical prior over X at the fit, because that would calibrate the same estimator on two different priors depending on the call site. The returns-data form therefore raises and points at the prior-result form, ucs(ue, pr), and at pe.

Arguments

  • pe: The set's own prior estimator, or nothing.
  • 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.

Validation

  • pe is not nothing. An ArgumentError naming the prior-result form is thrown otherwise.

Returns

  • pr::AbstractPriorResult: The fitted prior.

Related

source
PortfolioOptimisers.ucs_selectorFunction
ucs_selector(risk_ucs::Nothing, prior_ucs::Nothing)
ucs_selector(risk_ucs::UcSE_UcS, prior_ucs::Any)
ucs_selector(risk_ucs::Nothing, prior_ucs::UcSE_UcS)

Chooses between the uncertainty set a risk measure carries and the one a prior carries, so that the risk measure's own set outranks the prior's.

The function is a selector. It states the table below and it carries no # Algorithm section, because each of its three methods returns one of its arguments and takes no step. The three methods are exhaustive over the argument pairs the callers form, and the first row is the only one that gives nothing.

risk_ucsprior_ucsResult
nothingnothingnothing
a UcSE_UcSanythingrisk_ucs
nothinga UcSE_UcSprior_ucs

Arguments

  • risk_ucs: Risk measure uncertainty set estimator or result, or nothing.
  • prior_ucs: Prior result uncertainty set estimator or result, or nothing.

Returns

  • ucs::Option{<:UcSE_UcS}: The selected set or estimator, by the table above.

Related

source
PortfolioOptimisers.investable_ucs_reductionFunction
investable_ucs_reduction(pr::AbstractPriorResult, rd) -> (imsk, pr, rd)
investable_ucs_reduction(::Nothing, pr::AbstractPriorResult, rd) -> (nothing, pr, rd)
investable_ucs_reduction(imsk::BitVector, pr::AbstractPriorResult, rd) -> (imsk, pr_view, rd_view)

Reduce a prior result, and the returns data beside it, to the Investable Mask before a set is fitted on them standalone.

A Prior Result lives on the full asset universe, and an asset outside its Investable Mask carries NaN in its moments and in every block a factor fit wrote, so a set fitted on the whole result meets a NaN where its arithmetic needs a number. Inside an optimiser the result arrives already reduced, because every optimisation family reduces once at its entry, and the mask this verb derives is then nothing. Standalone, the result arrives whole, and this verb takes the same view the optimiser takes, so the two routes fit the same set. expand_investable_ucs writes the fitted set back onto the full universe, and the pair is the reduce-and-expand shape every prior result takes.

Three methods, and the branch is dispatch rather than a condition, as it is in investable_reduction. The first derives the mask; the nothing method is the all-investable path and returns its arguments untouched; the BitVector method takes the two views.

Algorithm

  1. Derive the Investable Mask from the prior result with investable_mask.
  2. Return nothing and the two arguments unchanged when the mask is nothing.
  3. Otherwise take a port_opt_view of the prior result at findall(imsk), and of the returns data when it is not nothing, and return them beside the mask.

Arguments

  • pr: Prior result.
  • rd: Returns data the set is fitted beside, or nothing.

Returns

  • (imsk, pr, rd): The Investable Mask and the two reduced to it, or nothing and the two unchanged.

Related

source
PortfolioOptimisers.expand_investable_ucsMethod
expand_investable_ucs(set::AbstractUncertaintySetResult, ::Nothing, pr::AbstractPriorResult) -> set

Write a set fitted on the Investable Mask back onto the full asset universe.

The nothing method is the all-investable path of investable_ucs_reduction and returns the set untouched, so a set fitted inside an optimiser, on a result that arrived reduced, pays nothing here. Each built set that a prior-result route produces adds its own BitVector method beside its port_opt_view, because the expansion is the inverse of that view: it writes the set's asset-axis blocks into a zero frame of the full width, and carries the full pr.mu or pr.sigma as val, NaN frame and all. The two rows of a view then commute, port_opt_view(expand_investable_ucs(set, imsk, pr), findall(imsk)) recovers set, which is what lets a set fitted standalone on a point-in-time prior be handed back to an optimiser on the full universe.

The method is a passthrough, so it carries no # Algorithm section.

Arguments

  • set: Fitted uncertainty set.
  • imsk: The Investable Mask, or nothing.
  • pr: Prior result.

Returns

  • set::AbstractUncertaintySetResult: The set on the full asset universe.

Related

source
PortfolioOptimisers.expand_investable_ucsMethod
expand_investable_ucs(
    set::BoxUncertaintySet{<:AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}, <:AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}},
    imsk::BitVector,
    pr::AbstractPriorResult
) -> Union{BoxUncertaintySet{var"#s185", <:AbstractArray{var"#s137", N}, Nothing} where {var"#s137"<:(Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}), N, var"#s185"<:AbstractArray{var"#s137", N}, var"#s137"<:(Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}), N}, BoxUncertaintySet{var"#s185", var"#s1851", <:AbstractArray{var"#s137", N}} where {var"#s137"<:(Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}), N, var"#s185"<:AbstractArray{var"#s137", N}, var"#s137"<:(Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}), N, var"#s1851"<:AbstractArray{var"#s137", N}, var"#s137"<:(Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}), N}}

Write a vector BoxUncertaintySet fitted on the Investable Mask back onto the full asset universe.

The expansion is the inverse of port_opt_view: the view slices the two bounds and the centre by the asset index and touches nothing else, so a frame written outside the mask is dropped without a trace and the view recovers the fitted set. The frame is NaN, the moment's own: a bound on mu is in the units of mu and lives where mu lives, and outside the mask the prior states NaN for it. The centre is the full pr.mu.

Algorithm

  1. Write set.lb and set.ub at the rows the mask keeps into two NaN frames of length(imsk) entries, through expand_vector.
  2. Build a BoxUncertaintySet from the two, with pr.mu as val.

Arguments

  • set: Vector box uncertainty set fitted on the reduced prior.
  • imsk: The Investable Mask of the full prior.
  • pr: Prior result.

Returns

  • set::BoxUncertaintySet: The set on the full asset universe.

Related

source
PortfolioOptimisers.expand_investable_ucsMethod
expand_investable_ucs(
    set::BoxUncertaintySet{<:AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}, <:AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}},
    imsk::BitVector,
    pr::AbstractPriorResult
) -> Union{BoxUncertaintySet{var"#s185", <:AbstractArray{var"#s137", N}, Nothing} where {var"#s137"<:(Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}), N, var"#s185"<:AbstractArray{var"#s137", N}, var"#s137"<:(Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}), N}, BoxUncertaintySet{var"#s185", var"#s1851", <:AbstractArray{var"#s137", N}} where {var"#s137"<:(Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}), N, var"#s185"<:AbstractArray{var"#s137", N}, var"#s137"<:(Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}), N, var"#s1851"<:AbstractArray{var"#s137", N}, var"#s137"<:(Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}), N}}

Write a matrix BoxUncertaintySet fitted on the Investable Mask back onto the full asset universe.

The expansion is the inverse of port_opt_view: the view slices the two bounds on both axes and the centre by the asset index, so a frame written outside the mask is dropped without a trace and the view recovers the fitted set. The frame is NaN, the moment's own: a bound on sigma lives where sigma lives, and outside the mask the prior states NaN for its row and column. The centre is the full pr.sigma.

Algorithm

  1. Write set.lb and set.ub at (imsk, imsk) into two NaN frames of length(imsk) rows and columns, through the covariance method of expand_moment.
  2. Build a BoxUncertaintySet from the two, with pr.sigma as val.

Arguments

  • set: Matrix box uncertainty set fitted on the reduced prior.
  • imsk: The Investable Mask of the full prior.
  • pr: Prior result.

Returns

  • set::BoxUncertaintySet: The set on the full asset universe.

Related

source
PortfolioOptimisers.expand_investable_ucsMethod
expand_investable_ucs(
    set::EllipsoidalUncertaintySet{<:AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}, <:Any, <:SigmaUncertaintySetClass},
    imsk::BitVector,
    pr::AbstractPriorResult
) -> Union{EllipsoidalUncertaintySet{Matrix{T}, <:Number, SigmaUncertaintySetClass, Nothing} where T<:(Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}), EllipsoidalUncertaintySet{Matrix{T}, var"#s185", SigmaUncertaintySetClass, <:AbstractArray{var"#s137", N}} where {T<:(Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}), var"#s185"<:Number, var"#s137"<:(Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}), N}}

Write a covariance EllipsoidalUncertaintySet fitted on the Investable Mask back onto the full asset universe.

The expansion is the inverse of port_opt_view: the view slices the shape matrix at the fourth-moment index of the selected assets and the centre at the plain one, so a zero block written outside the mask is dropped without a trace and the view recovers the fitted set. The frame is zero and not NaN, because the shape matrix is geometry: the same set converts into a NormBallUncertaintySet whose map must be finite, and a zero row of that map moves nothing on an asset the prior could not estimate. The centre is the full pr.sigma.

Algorithm

  1. Take the positions the investable pairs occupy in the vectorised covariance with coverage_pair_index, which orders them as fourth_moment_index_generator does.
  2. Allocate a zero frame of length(imsk)^2 rows and columns, and write set.sigma at those positions on both axes.
  3. Build an EllipsoidalUncertaintySet from it, carrying k and class through unchanged and pr.sigma as val.

Arguments

  • set: Covariance ellipsoidal uncertainty set fitted on the reduced prior.
  • imsk: The Investable Mask of the full prior.
  • pr: Prior result.

Returns

  • set::EllipsoidalUncertaintySet: The set on the full asset universe.

Related

source
PortfolioOptimisers.expand_investable_ucsMethod
expand_investable_ucs(
    set::EllipsoidalUncertaintySet{<:AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}, <:Any, <:MuUncertaintySetClass},
    imsk::BitVector,
    pr::AbstractPriorResult
) -> Union{EllipsoidalUncertaintySet{Matrix{T}, <:Number, MuUncertaintySetClass, Nothing} where T<:(Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}), EllipsoidalUncertaintySet{Matrix{T}, var"#s185", MuUncertaintySetClass, <:AbstractArray{var"#s137", N}} where {T<:(Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}), var"#s185"<:Number, var"#s137"<:(Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}), N}}

Write a mean EllipsoidalUncertaintySet fitted on the Investable Mask back onto the full asset universe.

The expansion is the inverse of port_opt_view: the view slices the shape matrix on both axes and the centre by the asset index, so a zero row and column written outside the mask are dropped without a trace and the view recovers the fitted set. The frame is zero and not NaN, because the shape matrix is geometry: the same set converts into a NormBallUncertaintySet whose map must be finite, and a zero row of that map moves nothing on an asset the prior could not estimate. The centre is the full pr.mu.

Algorithm

  1. Allocate a zero frame of length(imsk) rows and columns, and write set.sigma at (imsk, imsk).
  2. Build an EllipsoidalUncertaintySet from it, carrying k and class through unchanged and pr.mu as val.

Arguments

  • set: Mean ellipsoidal uncertainty set fitted on the reduced prior.
  • imsk: The Investable Mask of the full prior.
  • pr: Prior result.

Returns

  • set::EllipsoidalUncertaintySet: The set on the full asset universe.

Related

source
PortfolioOptimisers.vec_quantile_boundsFunction
vec_quantile_bounds(mus::MatNum, q::Number, kwargs)

Element-wise lower and upper quantile bounds for a vector-valued statistic.

Shared by the bootstrap box ucs and mu_ucs mean constructions. The sample axis is the second one: the body reads mus[j, :], so mus is $N \times M$, one row per component and one column per sample. A caller that passes the transpose gets bounds of length $M$, which the BoxUncertaintySet constructor accepts, so the axis is a contract this method cannot check.

Algorithm

  1. Read N = size(mus, 1), the number of components, and allocate lb and ub of that length and of eltype(mus).
  2. For each component j, read the row mu_j = mus[j, :], the M sampled values of that component.
  3. Write the q quantile of mu_j into lb[j] and the 1 - q quantile into ub[j].
  4. Return lb and ub. They satisfy lb .<= ub entrywise, and they bracket the sample mean of each component.

Arguments

  • mus: Sampled values, $N \times M$, one row per component.
  • q: Significance level, already halved by the caller.
  • kwargs: Splatted into Statistics.quantile.

Returns

  • lb::Vector: Element-wise lower bound, length $N$.
  • ub::Vector: Element-wise upper bound, length $N$.

Related

source
PortfolioOptimisers.ellipsoidal_setFunction
ellipsoidal_set(diagonal::Bool, method, q::Number, samples, cov::MatNum,
                class::AbstractUncertaintySetClass,
                val::Option{<:ArrNum} = nothing)

Assemble an EllipsoidalUncertaintySet from an already-computed asymptotic covariance cov.

Shared by every ellipsoidal ucs, mu_ucs and sigma_ucs construction across estimator families. k_ucs absorbs the trailing arguments its own algorithm does not read, so samples may be the deviation matrix, a 1:n_sim range, or nothing, whichever the caller has.

The order of the two steps below is load-bearing. The diagonal is taken before the radius is fitted, so under the diagonal = true default an empirical radius is a quantile of Mahalanobis distances measured against the diagonal shape and not against the full one, and neither shape reliably gives the larger radius. Taking the diagonal afterwards would pair a radius calibrated on one shape with a different shape, and the set would not hold the coverage its significance level names.

Algorithm

  1. When diagonal is true, replace cov with LinearAlgebra.Diagonal(cov), discarding the estimation-error correlations between entries. The result is stored as a Diagonal, not as a dense matrix.
  2. Compute k = k_ucs(method, q, samples, cov), the radius, measured against whichever shape step 1 left.
  3. Build an EllipsoidalUncertaintySet from cov, k, class and val.

Arguments

  • diagonal: Whether to restrict cov to its diagonal before the radius is fitted.
  • method: Radius algorithm, or the radius itself as a Number.
  • q: Significance level.
  • samples: Sampled estimation errors, or whatever container method reads. An algorithm that runs no simulation absorbs it.
  • cov: Asymptotic covariance of the statistic, which becomes the shape matrix.
  • class: Axis tag, which fixes the size of the shape matrix and the index a view applies.
  • val: Quantity the set is a neighbourhood of — the fitted characteristic vector on the mean axis, the fitted covariance on the covariance axis. Every caller has it in hand, because every one of them fits a prior before it calls here.

Returns

  • ucs::EllipsoidalUncertaintySet: The assembled set.

Related

source
PortfolioOptimisers.box_quantile_boundsFunction
box_quantile_bounds(::Type{TE}, get_ij, N::Integer, q::Number, kwargs) where {TE}

Element-wise lower and upper quantile bounds for a symmetric $N \times N$ statistic.

Shared by the box ucs and sigma_ucs constructions across estimator families. The get_ij accessor is what lets one body serve them all: it bridges the Wishart sample container, a vector of matrices, and the bootstrap one, a three-dimensional array. Positive-definite projection, if any, is applied by the caller.

Algorithm

  1. Allocate lb and ub, both N × N and of element type TE.
  2. For each ordered pair with j <= i, read s_ij = get_ij(i, j), the sampled values of that entry.
  3. Write the q quantile of s_ij into both lb[i, j] and lb[j, i], and the 1 - q quantile into both ub[i, j] and ub[j, i]. Writing each quantile to both positions makes the two bounds symmetric by construction, and it costs one quantile per pair rather than two.
  4. Return lb and ub. They satisfy lb .<= ub entrywise, because q is the smaller quantile level of the same sample, and the sample mean of the statistic lies between them.

Arguments

  • TE: Element type of the two bounds.
  • get_ij: Accessor. get_ij(i, j) returns the vector of sampled values for entry $(i, j)$.
  • N: Side of the statistic.
  • q: Significance level, already halved by the caller.
  • kwargs: Splatted into Statistics.quantile.

Returns

  • lb::Matrix{TE}: Element-wise lower bound, symmetric.
  • ub::Matrix{TE}: Element-wise upper bound, symmetric.

Related

source