Geodesic shrinkage covariance: private API

These are the helpers of GeodesicShrinkageCovariance. geodesic_point computes the shrunk matrix, and geodesic_shrinkage! runs the repair and the shrinkage on the assets with a finite variance.

PortfolioOptimisers.GeodesicShrinkageTarget — Type
const GeodesicShrinkageTarget = Union{<:AbstractCovarianceShrinkageTarget,
                                      <:DiagonalTarget, <:MatNum}

Groups the targets a GeodesicShrinkageCovariance accepts: the rules that build the target from the matrix being shrunk, and a fixed positive definite matrix.

The members share no supertype. DiagonalTarget is also a RegimeAdjustedTarget, and a matrix is data. So this union bounds the tgt field, and shrinkage_target dispatches on its members.

Related

source
PortfolioOptimisers.assert_shrinkage_target — Function
assert_shrinkage_target(
    _::Union{DiagonalTarget, AbstractCovarianceShrinkageTarget}
)

Checks a covariance shrinkage target at construction.

A target rule needs no check, because shrinkage_target builds it from the matrix it shrinks. A fixed matrix must be a valid target for every matrix it meets, so this function checks it once, when the estimator is built.

Arguments

  • tgt: The target.

Validation

  • A matrix tgt is square. The function throws a DimensionMismatch otherwise.
  • A matrix tgt is finite, symmetric within the default tolerance of isapprox, and positive definite. The function throws a DomainError otherwise.

Returns

  • nothing.

Related

source
PortfolioOptimisers.geodesic_power — Function
geodesic_power(
    vals::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
    p::Number
) -> Any

Raises the eigenvalues of a positive semidefinite matrix to a power, and reads an eigenvalue inside the round-off tolerance as zero.

An eigendecomposition of a singular positive semidefinite matrix returns its zero eigenvalues as small numbers of either sign. A power below one makes such a number larger. For example, (1e-17)^0.5 is about 3e-9. The threshold keeps a zero eigenvalue of the start at zero.

Mathematical definition

\[\begin{align} \lambda_i^{(p)} &= \begin{cases} \lambda_i^{p} & \lambda_i > \tau\,, \\ 0 & \lambda_i \leq \tau\,, \end{cases} \\ \tau &= N \, \epsilon \, \max_{j} \lvert \lambda_j \rvert\,. \end{align}\]

Where:

  • $\lambda_i^{(p)}$: $i$-th eigenvalue after the power.
  • $\lambda_i$: $i$-th eigenvalue of the input matrix.
  • $p$: The power, positive.
  • $\tau$: Round-off tolerance of a symmetric eigensolver.
  • $\epsilon$: Machine epsilon of the element type of the eigenvalues.
  • $N$: Number of assets.

An eigenvalue below $-\tau$ belongs to an indefinite matrix, which no geodesic reaches.

Arguments

  • vals: Eigenvalues of a symmetric matrix.
  • p: The power, positive.

Validation

  • minimum(vals) is not below $-\tau$. The function throws a DomainError otherwise.

Returns

  • vals_p::VecNum: The eigenvalues raised to p, with every eigenvalue inside the tolerance at zero.

Related

source
PortfolioOptimisers.geodesic_point — Function
geodesic_point(
    _::ScaledIdentityTarget,
    sigma::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
    alpha::Number
) -> Any

Computes the point at intensity alpha on the geodesic from sigma to the target of tgt.

Algorithm

For ScaledIdentityTarget:

  1. Take the average variance v from the trace of sigma.
  2. At alpha = 1, return v times the identity matrix, as a Diagonal.
  3. Eigendecompose sigma, giving vals and vecs.
  4. Raise vals to 1 - alpha with geodesic_power and multiply them by v ^ alpha.
  5. Rebuild the matrix from vecs and the new eigenvalues, and return its symmetric part.

For every other target:

  1. Build the target matrix tgt_mat with shrinkage_target.
  2. At alpha = 1, return a copy of tgt_mat.
  3. Factor tgt_mat by Cholesky into chol, with lower factor L. Refuse a tgt_mat that is not finite, or whose factorisation fails. A Diagonal target factors into Diagonal factors, so its whitening only rescales the rows and columns of sigma.
  4. Eigendecompose the whitened matrix L \ sigma / L', giving vals and vecs.
  5. Take the basis basis = L * vecs, raise vals to 1 - alpha with geodesic_power, rebuild the matrix from basis and the new eigenvalues, and return its symmetric part.

Arguments

  • tgt: The target.
  • sigma: Covariance-like or correlation-like matrix assets × assets.
  • alpha: Shrinkage intensity, strictly positive.

Validation

Returns

  • sigma_alpha::MatNum: A new matrix, the point on the geodesic.

Related

source
PortfolioOptimisers.geodesic_shrinkage! — Function
geodesic_shrinkage!(
    ce::GeodesicShrinkageCovariance,
    sigma::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}}

Repairs and shrinks the finite block of a covariance matrix in place, and leaves the frame of NaN around it alone.

An inner estimator that reads the gaps of a sample returns NaN for an asset it could not estimate. The repair and the shrinkage cannot take a NaN. So they run on the block of the assets whose variance is finite, and the NaN rows and columns of the frame stay as they are.

Algorithm

  1. Take the block blk as the assets whose variance on the diagonal of sigma is finite. Return sigma when blk holds no asset.
  2. Refuse a non-finite entry inside the block with assert_finite_block.
  3. Copy the block out as block, or take sigma itself when blk holds every asset.
  4. Repair block with posdef! under ce.pdm.
  5. When ce.alpha is positive, write the point at ce.alpha on the geodesic into block, with geodesic_point.
  6. Write block back into sigma, and return sigma.

Arguments

  • ce: The geodesic shrinkage covariance estimator.
  • sigma: Covariance-like or correlation-like matrix assets × assets.

Validation

  • The block of sigma is finite. The function throws an IsNonFiniteError otherwise.
  • Every check of geodesic_point applies to the block.

Returns

  • sigma::MatNum: The input matrix, with its block repaired and shrunk in place.

Related

source
PortfolioOptimisers.frame_cov2cor! — Function
frame_cov2cor!(
    sigma::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}}

Rescales a covariance matrix to its correlation matrix in place, and keeps the NaN of an asset whose variance is not finite.

StatsBase.cov2cor! writes one on the whole diagonal, so it turns the NaN variance of an asset outside the Coverage Universe into a correlation of one. The entries off the diagonal of such an asset are already NaN, so this function writes the NaN back on the diagonal alone.

Algorithm

  1. Take the standard deviations s from the diagonal of sigma.
  2. Rescale sigma with StatsBase.cov2cor! and s.
  3. Write s[i] back onto the diagonal wherever s[i] is not finite.

Arguments

  • sigma: Covariance-like or correlation-like matrix assets × assets.

Returns

  • rho::MatNum: The input matrix, rescaled in place to a correlation matrix.

Related

source