Cokurtosis: private API

PortfolioOptimisers.coverage_cokurtosisFunction
coverage_cokurtosis(kte, cvg, X; dims::Int = 1, mean = nothing,
                    active_mask = nothing, kwargs...) -> MatNum

Fits a square cokurtosis matrix over the arm the estimator's coverage policy selects.

The cvg field of the estimator is passed as the second argument, so the arm is chosen by dispatch on the policy rather than by a branch on its value, exactly as coverage_covariance does for a covariance. The moment algorithm of kte chooses between the FullMoment arm, which centres on each asset's own mean and takes the deviations whole, and the SemiMoment arm, which clips them at zero first.

Arguments

  • kte: Cokurtosis estimator.
  • cvg: The policy the estimator carries, which selects the arm.
  • X: Data matrix (observations × assets).
  • dims: Dimension along which to perform the computation.
  • mean: Optional mean vector. The available-case arm refuses one.
  • active_mask: The active mask of the Asset Panel over the window, or nothing. The Coverage Universe arm ignores it.
  • kwargs...: Additional keyword arguments passed to the mean estimator.

Returns

  • ckurt::MatNum: Square cokurtosis matrix assets² x assets².

Related

source
PortfolioOptimisers.coverage_cokurtosisMethod
coverage_cokurtosis(
    kte::Cokurtosis{<:Any, <:Any, <:FullMoment},
    ::Nothing,
    X::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}};
    dims,
    mean,
    active_mask,
    kwargs...
) -> Any

Nothing method of the FullMoment arm of coverage_cokurtosis. The Coverage Universe arm, which centres the whole window on one vector and delegates to _cokurtosis, and which is the body the verb has always had.

Related

source
PortfolioOptimisers.coverage_cokurtosisMethod
coverage_cokurtosis(
    kte::Cokurtosis{<:Any, <:Any, <:SemiMoment},
    ::Nothing,
    X::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}};
    dims,
    mean,
    active_mask,
    kwargs...
) -> Any

Nothing method of the SemiMoment arm of coverage_cokurtosis. The Coverage Universe arm, which clips the de-meaned returns at zero before delegating, and which is the body the verb has always had.

Related

source
PortfolioOptimisers.coverage_cokurtosisMethod
coverage_cokurtosis(
    kte::Cokurtosis,
    cvg::CoveragePolicy,
    X::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}};
    dims,
    mean,
    active_mask,
    kwargs...
) -> Any

CoveragePolicy method of coverage_cokurtosis. The available-case arm: each quadruple is fitted on the observations at which its four assets are all finite and active, and each cell carries its own denominator.

The centre is each asset's own available-case mean, its own finite and active observations alone, and not the quadruple's; coverage_coskewness states why. An external mean is refused for the same reason.

The repair sees the block, which matrix_processing_block! already derives correctly here: the diagonal of a cokurtosis matrix at the pair (i, j) is E[yᵢ²yⱼ²], finite exactly where the pair shares an observation, so the block is the set of pairs the fit answered for and the frame is left alone.

Neither this order nor the third folds: an exact per-cell recursion here needs the second and third co-moments over each quadruple's own observation set, so the online form of an available-case cokurtosis is a buffer refit through Online.

Algorithm

  1. Read the valid entries, the per-asset available-case centre and the per-asset bookkeeping of the block with coverage_comoment_block.
  2. Take the numerator as transpose(z) * z and the denominator as transpose(zc) * zc, the pairwise expansions of the zeroed deviations and of the valid mask.
  3. Divide with coverage_divide, and frame the refused assets down both pair axes with coverage_refuse_comoment!.
  4. Repair the block with matrix_processing_block!.

Related

source
PortfolioOptimisers._cokurtosisFunction
_cokurtosis(X::MatNum, mp::AbstractMatrixProcessingEstimator, w::Option{<:ObsWeights}) -> MatNum

Internal helper that builds the square cokurtosis matrix from a deviation matrix.

_cokurtosis returns the $N^{2} \times N^{2}$ matrix, which the matrix processing estimator repairs in place before the return.

Mathematical definition

The pairwise expansion of the deviation matrix has the rows:

\[\begin{align} \mathbf{Z}_{t,\cdot} &= (\boldsymbol{1}^\intercal \otimes \boldsymbol{y}_t^\intercal) \odot (\boldsymbol{y}_t^\intercal \otimes \boldsymbol{1}^\intercal)\,. \end{align}\]

The $N^{2} \times N^{2}$ square cokurtosis matrix is:

Unweighted:

\[\begin{align} \hat{\mathbf{K}} &= \frac{1}{T} \mathbf{Z}^\intercal \mathbf{Z}\,. \end{align}\]

Weighted:

\[\begin{align} \hat{\mathbf{K}} &= \frac{1}{\sum_{t=1}^{T} w_t} (\boldsymbol{w} \odot \mathbf{Z})^\intercal \mathbf{Z}\,. \end{align}\]

Where:

  • $\hat{\mathbf{K}}$: $N^{2} \times N^{2}$ square cokurtosis matrix. Its entry $\hat{\mathbf{K}}_{(i-1)N+j,\,(k-1)N+l}$ is the fourth comoment of the deviations of the assets $i$, $j$, $k$ and $l$, so the matrix is symmetric. This is the source's $\mathbf{\Sigma}_{4}$, not its $\mathbf{M}_{4}$. The latter is $N \times N^{3}$ and the library never builds it.
  • $\mathbf{Y}$: $T \times N$ deviation matrix. FullMoment takes the centred returns, and SemiMoment clips every positive entry of them to zero.
  • $\boldsymbol{y}_t$: $N \times 1$ deviation vector of observation $t$, the $t$-th row of $\mathbf{Y}$. Its $i$-th entry is $y_{t,\,i}$.
  • $\mathbf{Z}$: $T \times N^{2}$ pairwise expansion of $\mathbf{Y}$, whose $t$-th row is $\mathbf{Z}_{t,\cdot}$ and whose entry $\mathbf{Z}_{t,\,(i-1)N+j}$ is the product $y_{t,\,i} \, y_{t,\,j}$.
  • $\boldsymbol{w}$: $T \times 1$ observation weights vector.
  • $w_{t}$: Observation weight of observation $t$.
  • $T$: Number of observations.
  • $N$: Number of assets.
  • $\boldsymbol{1}$: $N \times 1$ vector of ones.
  • $\otimes$: Kronecker product.
  • $\odot$: Element-wise product. Where the operands differ in shape, it broadcasts along the row axis.

Algorithm

  1. Build o, the $1 \times N$ row of ones.
  2. Build z, the pairwise expansion kron(o, X) ⊙ kron(X, o). Its column (i - 1) * N + j is the element-wise product of the columns i and j of X.
  3. Without weights, form ckurt as transpose(z) * z / T.
  4. With weights, form ckurt as transpose(w .* z) * z / sum(w). The weights multiply the left factor alone, so each summand carries one weight and not four.
  5. Run matrix_processing! on ckurt in place, and return it.

Arguments

  • X: Deviation matrix (observations × assets), already centred by the caller.
  • mp: Matrix processing estimator.
  • w: Optional observation weights. The unweighted method takes nothing through its args....

Returns

  • ckurt::MatNum: Square cokurtosis matrix assets² x assets². It is processed in place by mp.

Related

References

  • [5] D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025). Section 3.1.4, Equation 3.7.
  • [30] D. Cajas. Convex Optimization of Portfolio Kurtosis. Available at SSRN 4202967 (2022).
source

References

[5]
D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025).
[30]
D. Cajas. Convex Optimization of Portfolio Kurtosis. Available at SSRN 4202967 (2022).