Cokurtosis

PortfolioOptimisers.CokurtosisEstimatorType
abstract type CokurtosisEstimator <: AbstractEstimator

Abstract supertype for all cokurtosis estimators.

All concrete and/or abstract types implementing cokurtosis estimation algorithms should be subtypes of CokurtosisEstimator.

Interfaces

In order to implement a new cokurtosis estimator which will work seamlessly with the library, subtype CokurtosisEstimator with all necessary parameters—including observation weights—as part of the struct, and implement the following methods:

Cokurtosis

  • PortfolioOptimisers.cokurtosis(kte::CokurtosisEstimator, X::MatNum; dims::Int = 1, mean = nothing, kwargs...) -> MatNum: Computes the cokurtosis tensor.

Arguments

  • kte: Cokurtosis estimator.
  • X: Data matrix observations × assets if the dims keyword does not exist or dims = 1, assets × observations when dims = 2.
  • dims: Dimension along which to perform the computation.
  • mean: Optional mean value to use for centering.
  • kwargs...: Additional keyword arguments.

Returns

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

Factory

  • PortfolioOptimisers.factory(kte::CokurtosisEstimator, w::PortfolioOptimisers.ObsWeights) -> CokurtosisEstimator: Factory method for creating instances of the estimator with new observation weights.

Arguments

  • kte: Cokurtosis estimator.
  • w: Observation weights vector observations × 1.

Returns

  • kte::CokurtosisEstimator: New cokurtosis estimator of the same type, with the new weights applied.

View

  • PortfolioOptimisers.port_opt_view(kte::CokurtosisEstimator, i) -> CokurtosisEstimator: Returns a view of the estimator for the i-th element(s).

Arguments

  • kte: Cokurtosis estimator.
  • i: Index or indices.

Returns

  • kev: New cokurtosis estimator of the same type as the argument, for the new view.

Examples

We can create a dummy cokurtosis estimator as follows:

julia> struct MyCokurtosisEstimator{T1} <: PortfolioOptimisers.CokurtosisEstimator           w::T1           function MyCokurtosisEstimator(w::PortfolioOptimisers.Option{<:PortfolioOptimisers.ObsWeights})               PortfolioOptimisers.assert_nonempty_nonneg_finite_val(w, :w)               return new{typeof(w)}(w)           end       endjulia> function MyCokurtosisEstimator(;                                      w::PortfolioOptimisers.Option{<:PortfolioOptimisers.ObsWeights} = nothing)           return MyCokurtosisEstimator(w)       endMyCokurtosisEstimatorjulia> function PortfolioOptimisers.factory(::MyCokurtosisEstimator,                                            w::PortfolioOptimisers.ObsWeights)           return MyCokurtosisEstimator(; w = w)       endjulia> function PortfolioOptimisers.port_opt_view(kte::MyCokurtosisEstimator, i)           return kte       endjulia> function PortfolioOptimisers.cokurtosis(kte::MyCokurtosisEstimator,                                               X::PortfolioOptimisers.MatNum; dims::Int = 1,                                               mean = nothing, kwargs...)           N = size(X, 2)           return zeros(N^2, N^2)       endjulia> cokurtosis(MyCokurtosisEstimator(), [1.0 2.0; 0.3 0.7; 0.5 1.1])4×4 Matrix{Float64}: 0.0  0.0  0.0  0.0 0.0  0.0  0.0  0.0 0.0  0.0  0.0  0.0 0.0  0.0  0.0  0.0julia> PortfolioOptimisers.factory(MyCokurtosisEstimator(), StatsBase.Weights([1, 2, 3]))MyCokurtosisEstimator  w ┴ StatsBase.Weights{Int64, Int64, Vector{Int64}}: [1, 2, 3]

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
PortfolioOptimisers.CokurtosisType
struct Cokurtosis{__T_me, __T_mp, __T_alg, __T_w, __T_cvg, __T_cache} <: CokurtosisEstimator

Estimates the square cokurtosis matrix of a returns matrix.

Cokurtosis composes a mean estimator, a matrix processing estimator and a moment algorithm. cokurtosis returns one assets² × assets² matrix, which is the source's stacked fourth comoment and not the assets × assets³ tensor of the same name.

Fields

  • me: Expected returns estimator.
  • mp: Matrix processing estimator.
  • alg: Moment algorithm.
  • w: Optional observation weights vector observations × 1, or a concrete subtype of DynamicAbstractWeights. If nothing, the computation is unweighted.
  • cvg: Optional CoveragePolicy. nothing is the reduce-and-expand path of the Coverage Universe, in which an asset that is non-finite or inactive at any observation of the window is NaN throughout the answer. A policy replaces it by available-case estimation: every cell is fitted on the observations at which the assets of that cell are all finite and active, each cell carries its own denominator, and an asset reaches the answer where admits says so.
  • cache: Optional partial-fit state. It is nothing until partial_fit! writes one, and the estimator's read-out verb reads it when the caller gives no data matrix. Each propagation channel does one thing with it: factory carries it unchanged, because a factory call resolves configuration rather than the sample; port_opt_view slices it to the selected assets by index copy, so the viewed estimator answers over those assets alone; and obs_weights_view drops it, because no slice of a state exists on the observation axis. A family whose state has no exact asset slice drops it on both axes and names the reason.

Constructors

Cokurtosis(;    me::AbstractExpectedReturnsEstimator = SimpleExpectedReturns(),    mp::AbstractMatrixProcessingEstimator = MatrixProcessing(),    alg::AbstractMomentAlgorithm = FullMoment(),    w::Option{<:ObsWeights} = nothing,    cvg::Option{<:CoveragePolicy} = nothing,    cache::Option{<:AbstractPartialFitState} = nothing) -> Cokurtosis

Keywords correspond to the struct's fields.

Validation

  • If w is not nothing, !isempty(w).

Propagated parameters

When factory is called on this type, the following @fprop-tagged fields are automatically propagated:

View parameters

When port_opt_view is called on this type, the following @vprop-tagged fields are automatically subset to the selected indices:

Observation weight parameters

When obs_weights_view is called on this type, the following fields are automatically indexed to the selected observations:

Examples

julia> Cokurtosis()Cokurtosis     me ┼ SimpleExpectedReturns        │   w ┴ nothing     mp ┼ MatrixProcessing        │     pdm ┼ Posdef        │         │      alg ┼ UnionAll: NearestCorrelationMatrix.Newton        │         │   kwargs ┴ @NamedTuple{}: NamedTuple()        │      dn ┼ nothing        │      dt ┼ nothing        │     alg ┼ nothing        │   order ┴ NTuple{4, Symbol}: (:pdm, :dn, :dt, :alg)    alg ┼ FullMoment()      w ┼ nothing  cache ┴ nothing

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
PortfolioOptimisers.cokurtosisFunction
cokurtosis(kte::Option{<:Cokurtosis}, X::MatNum; dims::Int = 1,
           mean = nothing, kwargs...)

Compute the square cokurtosis matrix of a dataset.

This method centres the data with the estimator's mean estimator and repairs the result with its matrix processing estimator. Observation weights in kte.w are applied if set. FullMoment takes the centred returns, and SemiMoment clips every positive deviation to zero. If the estimator is nothing, returns nothing.

kte.w weights the whole estimate, so it reaches the centre as well as the deviations. When mean is nothing and kte.w is not, the method sends kte.me through factory with kte.w, so kte.w wins over the weights that kte.me carries. Pass mean for a centre that kte.w does not describe.

Algorithm

  1. Orient X to observations × assets with dims_oriented, which validates dims.
  2. Resolve the observation weights w from kte.w with get_observation_weights.
  3. Resolve the centre mu from kte.me and kte.w with weighted_centre, which reads mean when the caller gave one.
  4. Replace X with the deviation matrix. FullMoment takes X .- mu, and SemiMoment takes min.(X .- mu, 0).
  5. Delegate to _cokurtosis with the deviation matrix, kte.mp and w, and return the matrix it returns.

Arguments

  • kte: Cokurtosis estimator.

    • kte::Cokurtosis{<:Any, <:Any, <:FullMoment}: Cokurtosis estimator with FullMoment moment algorithm.
    • kte::Cokurtosis{<:Any, <:Any, <:SemiMoment}: Cokurtosis estimator with SemiMoment moment algorithm.
    • kte::Nothing: No-op, returns nothing.
  • X: Data matrix (observations × assets).

  • dims: Dimension along which to perform the computation.

  • mean: Optional mean vector. If not provided, computed using the estimator's mean estimator.

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

Validation

  • dims is either 1 or 2.

Returns

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

Examples

julia> using StableRNGsjulia> rng = StableRNG(123456789);julia> X = randn(rng, 10, 2);julia> cokurtosis(Cokurtosis(), X)4×4 Matrix{Float64}:  1.33947   -0.246726  -0.246726   0.493008 -0.246726   0.493008   0.493008  -0.201444 -0.246726   0.493008   0.493008  -0.201444  0.493008  -0.201444  -0.201444   0.300335

Related

source
cokurtosis(kte::WindowedCokurtosis, X::MatNum; dims::Int = 1, mean = nothing, iv::Option{<:MatNum} = nothing, kwargs...)

Compute cokurtosis over a rolling or indexed observation window (matrix input).

This method selects a window of observations from X (and applies observation weights if specified), then delegates to the underlying cokurtosis estimator.

Algorithm

  1. Resolve the window and the observation weights with windowed_preamble, giving inner, a copy of kte.kte that carries the windowed weights, together with the windowed X and the windowed iv.
  2. Call cokurtosis on inner and the windowed X, and return its result. The inner estimator alone decides the value, so the window and the weights are the whole of this method's contribution.

Arguments

  • kte: Windowed cokurtosis estimator.
  • X: Data matrix of asset returns (observations × assets).
  • dims: Dimension along which to perform the computation.
  • mean: Optional pre-computed mean passed to the underlying estimator.
  • iv: Optional implied volatility matrix. Used if any internal covariance estimator is an instance of ImpliedVolatility.
  • kwargs...: Additional keyword arguments passed to the underlying estimator.

Returns

  • kte::MatNum: Cokurtosis matrix assets x assets.

Related

source
cokurtosis(
    kte::Cokurtosis{<:Any, <:Any, <:FullMoment},
    state::CokurtosisPartialFitState
) -> Any

Reads the square cokurtosis matrix out of a partial-fit state.

The state read-out of cokurtosis. It returns the matrix the batch method returns, to machine precision, for the sample the state was fitted on.

Only the shape of that sample survives a partial fit, so the matrix processing estimator is handed a matrix of the right shape whose entries are zero. Of the shipped steps only :dn reads it, and it reads only size(X), so the two routes agree. A custom :alg step that reads the values of the sample must be run from the batch verb.

Algorithm

  1. Refuse a configuration the state no longer matches, with assert_partial_fittable. factory carries the state and replaces w, so an estimator that says weighted may hold a state fitted unweighted. The state stays on the estimator, so a caller who restores w = nothing reads it again.
  2. Divide the fourth accumulator by the observation count, giving the cokurtosis matrix.
  3. Process it in place through matrix_processing_block!, under kte.mp and the shape of the fitted sample. It is the block arm rather than the plain one for the reason the low order's read-out takes it: a plain fold over a changing universe answers NaN for an asset outside the Coverage Universe at every pair naming it, and a positive-definite repair over that frame meets a LAPACK refusal rather than a named error. The block arm repairs the finite block and leaves the frame for investable_mask to read, which is what the batch verb's reduce-and-expand leaves it too; a complete matrix runs the plain arm over the whole of it.

Arguments

  • kte: Cokurtosis estimator with a FullMoment moment algorithm.
  • state: Partial-fit state written by partial_fit!.

Validation

  • kte.w is nothing and kte.me is an unweighted SimpleExpectedReturns. An ArgumentError is thrown otherwise.

Returns

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

Related

source
cokurtosis(kte::Cokurtosis, state::SampleBufferState) -> Any

Reads a cokurtosis out of a sample buffer, by refitting the batch verb over the observations the buffer holds.

The buffer read-out of cokurtosis, and the whole of the online form at this order. The Coskewness method states the rule; carried one order further, an exact per-cell recursion would need the second and third co-moments over each quadruple's own observation set. The masks the buffer holds are handed to the batch verb on the same terms.

Arguments

  • kte: Cokurtosis estimator.
  • state: The sample buffer the wrapper seeded.

Returns

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

Related

source
cokurtosis(kte::Cokurtosis) -> Any

Reads the square cokurtosis matrix out of the state the estimator carries.

The one-argument forwarder of cokurtosis. It reads kte.cache, which partial_fit! writes, and refuses an estimator that has been shown no observation.

Algorithm

  1. Refuse the estimator whose cache is nothing.
  2. Forward to the state method of cokurtosis.

Arguments

  • kte: Cokurtosis estimator.

Validation

  • kte.cache is not nothing. An ArgumentError is thrown otherwise.

Returns

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

Related

source
cokurtosis(
    kte::CokurtosisEstimator,
    state::SampleBufferState
) -> Any

Reads a cokurtosis matrix out of a SampleBufferState, by running the batch verb over the observations the buffer holds.

The buffer read-out arm of cokurtosis. An estimator wrapped in Online carries a buffer rather than a family state, so it takes no exact fold, and its estimate is whatever a batch fit over the rows the buffer holds gives — every observation folded so far when the buffer is uncapped, and the last max_history of them when it is capped. It runs no assert_partial_fittable check of its own: the batch verb answers configurations an incremental fold cannot, and a wrapper is how a caller reaches them. The buffer also holds the per-observation masks it was folded with, so the batch verb is given the mask that explains the rows and a wrapped estimator under a CoveragePolicy answers what an unwrapped one answers.

Arguments

  • kte: Cokurtosis estimator.
  • state: The buffer to read.

Returns

  • ckurt::MatNum: Cokurtosis matrix of the observations the buffer holds.

Related

source
cokurtosis(
    kte::CokurtosisEstimator,
    X::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
    pnl::Union{Nothing, AssetPanel};
    dims,
    kwargs...
) -> Any

The cokurtosis root of the Asset Panel seam. It is the reduce-and-expand of Statistics.cov(ce::AbstractCovarianceEstimator, X::MatNum, pnl::Option{<:AssetPanel}; dims::Int = 1, kwargs...), which states the rule and its cost. The answer is assets² × assets², so it expands at the pair index on both axes.

Related

source
cokurtosis(
    kte::Cokurtosis,
    X::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
    pnl::Union{Nothing, AssetPanel};
    dims,
    kwargs...
) -> Any

Asset Panel method of cokurtosis for a Cokurtosis, which routes on its cvg field with coverage_panel_moment. The answer is assets² × assets², so the Coverage Universe seam frames it at the pair index on both axes.

Related

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).