The coverage policy: private API

PortfolioOptimisers.CoverageCountsType
struct CoverageCounts{__T_nu, __T_centre, __T_active, __T_stale}

Carries the per-cell denominators and the per-asset bookkeeping of an available-case fold.

The component a partial-fit state holds in its cvg field, which is nothing on the plain path so that the plain state costs nothing. nu has the shape of the accumulator it serves, because the observation count of a covariance cell is a count per pair and not a count per asset, and centre has that shape wherever a cell's own centre differs from the state's per-asset mean.

This type is an implementation detail and is not intended for direct use. partial_fit! writes it, the read-out verbs divide by it, and merge_states folds two of them.

Fields

  • nu: nu: Per-cell surviving observation count, of the shape of the accumulator it serves.
  • centre: centre: Per-cell running mean, of the shape of the accumulator it serves, or nothing when the state's own mu is already the cell's centre. Entry (i, j) is the running mean of asset i over the observations at which the pair (i, j) is valid, so entry (j, i) is the mean of asset j over those same observations.
  • active: active: Active mask of the last observation folded, one entry per asset.
  • stale: stale: Number of observations folded since each asset was last finite and active, one entry per asset.

Related

source
PortfolioOptimisers.coverage_counts_seedFunction
coverage_counts_seed(cvg::Nothing, counts, N::Integer, Tf::Type,
                     pairwise::Bool) -> Nothing
coverage_counts_seed(cvg::CoveragePolicy, counts::CoverageCounts, N::Integer, Tf::Type,
                     pairwise::Bool) -> CoverageCounts
coverage_counts_seed(cvg::CoveragePolicy, counts::Nothing, N::Integer, Tf::Type,
                     pairwise::Bool) -> CoverageCounts

Returns the CoverageCounts an available-case fold writes into, seeding one of zeros when the state carries none.

The seed is written here rather than inside partial_fit!, so that the branch that reads the cvg field of a state has one home and the fold reads as one line. The policy is the first argument because it is what selects the arm: nothing is the plain path and gives nothing, whatever the state carries.

Arguments

  • cvg: The policy the estimator carries.
  • counts: The component the state carries, or nothing.
  • N: Number of assets.
  • Tf: Element type of the accumulators.
  • pairwise: Whether the cell axis is a pair, so that nu and centre are N × N rather than N-long.

Returns

  • counts::Option{<:CoverageCounts}: The component counts holds, or a fresh one of zeros.

Related

source
PortfolioOptimisers.coverage_counts_viewFunction
coverage_counts_view(x::Nothing, i) -> Nothing
coverage_counts_view(x::CoverageCounts, i) -> CoverageCounts

Slices a CoverageCounts to the selected assets.

A per-cell count reads the assets of its own cell alone, so the slice of the component is the component of the sliced universe, entry for entry. The slice copies by index and does not view, as port_opt_view does for every partial-fit state.

Arguments

  • x: The component to slice, or nothing.
  • i: Index or indices of the assets to keep.

Returns

  • counts::Option{<:CoverageCounts}: The component of the same sample over the selected assets.

Related

source
PortfolioOptimisers.coverage_validFunction
coverage_valid(
    x::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
    active_mask::Union{Nothing, AbstractVector{<:Bool}},
    counts::CoverageCounts
) -> Tuple{Any, Any}

Reads the assets that are valid at one observation, and the assets that have just gone inactive.

An asset is valid when its return is finite and the active mask admits it, which is the condition the exponentially weighted family already reads. With no mask every asset is active, so a non-finite return reads as a holiday rather than as a delisting and no asset is ever newly inactive.

Arguments

  • x: One observation, one entry per asset.
  • active_mask: The active mask of the Asset Panel at this observation, or nothing.
  • counts: The component whose active field holds the mask of the previous observation.

Validation

  • active_mask, when it is given, has one entry per asset. A DimensionMismatch is thrown otherwise.

Returns

  • (valid, ni)::Tuple: The valid assets, and the assets that were active at the previous observation and are inactive at this one.

Related

source
PortfolioOptimisers.coverage_valid_blockFunction
coverage_valid_block(
    X::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
    active_mask::Union{Nothing, AbstractMatrix{<:Bool}};
    dims
) -> NTuple{5, Any}

Reads everything a two-pass available-case fit needs out of a block of observations.

The block counterpart of coverage_valid and coverage_step!, for the arms that have no incremental recursion and must see the whole window at once. It returns the oriented block beside the four quantities the fold would otherwise have carried, so that a two-pass arm and a folding arm read the same universe and the same bookkeeping.

Algorithm

  1. Orient X, and the mask when there is one, to observations × assets.
  2. Take the valid entries, finite and active, giving F.
  3. Take each asset's available-case mean over its own valid entries, giving mu, which is zero for an asset with none.
  4. Take the active mask of the last observation, and each asset's staleness at it.

Arguments

  • X: The block of observations.
  • active_mask: The active mask of the Asset Panel over the block, or nothing.
  • dims: Whether the observations lie on the rows, 1, or on the columns, 2.

Validation

  • active_mask, when it is given, has the shape of X. A DimensionMismatch is thrown otherwise.

Returns

  • (Xo, F, mu, active, stale)::Tuple: The oriented block, its valid entries, each asset's available-case mean, the active mask of the last observation, and each asset's staleness at it.

Related

source
PortfolioOptimisers.coverage_step!Function
coverage_step!(
    counts::CoverageCounts,
    valid::AbstractVector{<:Bool},
    active_mask::Union{Nothing, AbstractVector{<:Bool}}
)

Moves the per-asset bookkeeping of a CoverageCounts on by one observation.

Algorithm

  1. Set the staleness of every valid asset to zero, and add one to the staleness of every other.
  2. Rebind active to the mask of this observation, which is every asset when the caller gave none.

Arguments

  • counts: The component to move on, mutated in place.
  • valid: The valid assets of this observation.
  • active_mask: The active mask of this observation, or nothing.

Returns

  • nothing.

Related

source
PortfolioOptimisers.coverage_merge_staleFunction
coverage_merge_stale(
    a::CoverageCounts,
    b::CoverageCounts,
    n_b::Integer
) -> Any

Folds the staleness counters of two CoverageCounts fitted on consecutive blocks.

An asset that was valid somewhere in the second block carries that block's own count, and an asset that was valid nowhere in it has spent the whole block stale, so its staleness is the two counts added. The test for "valid nowhere in b" is that b's counter has reached b's observation count, which is what the counter does when nothing resets it.

Arguments

  • a: The component of the first block of observations.
  • b: The component of the second block of observations.
  • n_b: The number of observations folded into b.

Returns

  • stale::Vector{<:Integer}: The staleness of each asset over the concatenated block.

Related

source
PortfolioOptimisers.coverage_reset!Function
coverage_reset!(A::Nothing, ni::AbstractVector{<:Bool}) -> Nothing
coverage_reset!(A::AbstractVector, ni::AbstractVector{<:Bool}) -> Nothing
coverage_reset!(A::AbstractMatrix, ni::AbstractVector{<:Bool}) -> Nothing

Zeroes the entries of an accumulator that touch a set of assets.

The primitive the ResetCoverage methods of fold_inactive! share, so that the reset of a per-asset accumulator and the reset of a per-pair accumulator are written once.

Arguments

  • A: The accumulator or count array to zero, mutated in place. nothing passes through, which is the centre of a per-asset state.
  • ni: One entry per asset, true where the asset is to be zeroed.

Returns

  • nothing.

Related

source
PortfolioOptimisers.coverage_admissionFunction
coverage_admission(
    cvg::CoveragePolicy,
    counts::CoverageCounts,
    n::Integer
) -> Union{Nothing, BitVector}

Reads the universe of an available-case fit out of its per-cell counts.

The read-out's own mask, and the one place admits is called. It collapses onto the nothing sentinel exactly as coverage_sentinel does, so an answer that admits every asset allocates no mask. Unlike coverage_sentinel it refuses no sample: an available-case fit whose window admits nothing answers all NaN rather than raising, because the fit is opt-in and its caller asked for the gaps.

Algorithm

  1. Take the per-asset observation count, which is the diagonal when the counts are per pair.
  2. Divide it by n, the number of observations folded, giving each asset's coverage share.
  3. Call admits once per asset.
  4. Return nothing when every asset is admitted, and the mask otherwise.

Arguments

  • cvg: The policy the estimator carries.
  • counts: The per-cell counts the state carries.
  • n: The number of observations folded, which is the share's denominator.

Returns

  • cmsk::Option{BitVector}: The admitted assets, or nothing when every asset is admitted.

Related

source
PortfolioOptimisers.coverage_divideFunction
coverage_divide(
    M::AbstractArray,
    nu::AbstractArray,
    corrected::Bool,
    cmsk::Union{Nothing, BitVector}
) -> Any

Divides an available-case accumulator by its per-cell denominator, and frames the assets the policy refuses.

The tail every available-case read-out shares. A cell whose denominator has not reached one is NaN, which is the per-cell half of the rule, and an asset coverage_admission refuses is NaN across its whole row and column, which is the per-asset half.

The answer's element type is the type of the division itself, so a Float32 accumulator reads out as Float32 and is never widened by the sentinel. An element type that cannot hold NaN cannot hold the answer either: an exact accumulator, a Rational among them, raises an InexactError at the first cell the policy refuses rather than silently reading out as Float64. A window with no refused cell is unaffected.

Algorithm

  1. Divide each entry of M by its entry of nu less corrected, writing NaN where that denominator is below one.
  2. Write NaN into every entry that touches an asset outside cmsk.

Arguments

  • M: The accumulator, of the shape of the answer.
  • nu: The per-cell denominator, of the shape of M.
  • corrected: The Bessel correction, subtracted from every denominator.
  • cmsk: The admitted assets, or nothing.

Returns

  • val::Array{<:AbstractFloat}: The answer, of the shape of M, carrying NaN where the fit has no number.

Related

source
PortfolioOptimisers.coverage_frameFunction
coverage_frame(
    A::AbstractArray,
    nu::AbstractArray,
    cmsk::Union{Nothing, BitVector}
) -> Any

Frames an available-case estimate that needs no division, and frames the assets the policy refuses.

The read-out of a quantity that is already a ratio, which is what a running per-asset mean is: the value is copied where its cell has an observation and is NaN where it has none, and coverage_refuse! then writes the frame. It exists beside coverage_divide so that a Welford mean is never multiplied by its count and divided by it again, which would move the last bits of an answer the fold computed exactly.

The answer carries the element type of A, which the fold already derived from its own division. As in coverage_divide, an exact element type cannot hold the NaN sentinel and raises an InexactError at the first cell that has no observation.

Arguments

  • A: The estimate, of the shape of the answer.
  • nu: The per-cell observation count, of the shape of A.
  • cmsk: The admitted assets, or nothing.

Returns

  • val::Array{<:AbstractFloat}: The answer, of the shape of A, carrying NaN where the fit has no number.

Related

source
PortfolioOptimisers.coverage_refuse!Function
coverage_refuse!(val::AbstractArray, cmsk::Nothing) -> Nothing
coverage_refuse!(val::AbstractArray, cmsk::BitVector) -> Nothing

Writes NaN into every entry of an answer that touches an asset the policy refuses.

The per-asset half of the read-out rule, shared by coverage_divide and coverage_frame. A nothing mask is the sentinel of coverage_admission and means that every asset is admitted, so nothing is written.

Arguments

  • val: The answer, mutated in place.
  • cmsk: The admitted assets, or nothing.

Returns

  • nothing.

Related

source
PortfolioOptimisers.coverage_refuse_comoment!Function
coverage_refuse_comoment!(val::AbstractMatrix, cmsk::Nothing, ::Val) -> Nothing
coverage_refuse_comoment!(val::AbstractMatrix, cmsk::BitVector, ::Val{:sk}) -> Nothing
coverage_refuse_comoment!(val::AbstractMatrix, cmsk::BitVector, ::Val{:kt}) -> Nothing

Writes NaN into every entry of a higher-order co-moment answer that touches an asset the policy refuses.

The co-moment form of coverage_refuse!, for an answer whose axes are not all the asset axis. A coskewness tensor is assets × assets², so its rows take the asset mask and its columns the pair mask; a cokurtosis matrix is assets² × assets², so both of its axes take the pair mask. The Val marker names the shape, exactly as it does for expand_moment, and the mask stays one argument so the arm is a plain dispatch rather than a product of two Options.

A pair is admitted when both of its assets are, and a co-moment tensor indexes the pair (i, j) at the column (i - 1) * N + j, which is the layout coverage_pair_index states. kron(cmsk, cmsk) is that conjunction in that order, entry for entry.

Arguments

  • val: The answer, mutated in place.
  • cmsk: The admitted assets, or nothing when every asset is admitted.
  • ::Val: Val(:sk) for a coskewness tensor and Val(:kt) for a cokurtosis matrix.

Returns

  • nothing.

Related

source