Simple variance and standard deviation

The variance is used throughout the library, it can be used as part of the expected return, covariance estimation, performance analysis, and constraint generation. It is trivial to compute the standard deviation from the variance, so we provide those too.

PortfolioOptimisers.SimpleVarianceType
struct SimpleVariance{__T_me, __T_w, __T_corrected, __T_cvg, __T_cache} <: AbstractVarianceEstimator

Computes the marginal variance and standard deviation, optionally weighted and optionally bias-corrected.

me centres the data when no mean is supplied, w weights the observations, and corrected selects the bias correction. me reaches the matrix methods only: the vector methods leave the centring to Statistics.

w weights the whole estimate, so it reaches the centre as well as the deviations. The matrix methods send me through factory, which replaces the weights of me with w, and Statistics centres a weighted vector on its weighted mean. Both paths therefore answer the same number over the same data, and w wins over the weights that me carries. Pass mean for a centre that w does not describe.

Fields

  • me: Optional expected returns estimator. It is not needed when used on a vector. If nothing and used on a matrix, defaults to SimpleExpectedReturns.
  • w: Observation weights vector observations × 1.
  • corrected: Whether to apply Bessel's correction.
  • 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

SimpleVariance(;    me::Option{<:AbstractExpectedReturnsEstimator} = SimpleExpectedReturns(),    w::Option{<:ObsWeights} = nothing,    corrected::Bool = true,    cvg::Option{<:CoveragePolicy} = nothing,    cache::Option{<:AbstractPartialFitState} = nothing) -> SimpleVariance

Keywords correspond to the struct's fields.

Validation

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> SimpleVariance()SimpleVariance         me ┼ SimpleExpectedReturns            │   w ┴ nothing          w ┼ nothing  corrected ┴ Bool: truejulia> SimpleVariance(; w = StatsBase.Weights([0.2, 0.3, 0.5]), corrected = false)SimpleVariance         me ┼ SimpleExpectedReturns            │   w ┴ nothing          w ┼ StatsBase.Weights{Float64, Float64, Vector{Float64}}: [0.2, 0.3, 0.5]  corrected ┴ Bool: false

Related

source
Statistics.stdMethod
Statistics.std(
    ve::SimpleVariance,
    X::MatNum;
    dims::Int = 1,
    mean = nothing,
    kwargs...,
) -> ArrNum

Compute the standard deviation using a SimpleVariance estimator for a matrix.

This method computes the standard deviation of the input matrix X using the configuration specified in ve.

Mathematical definition

\[\begin{align} \hat{\sigma}_j &= \sqrt{\hat{\sigma}^2_j}\,. \end{align}\]

Where:

  • $\hat{\sigma}_j$: Estimated standard deviation of asset $j$.
  • $\hat{\sigma}^2_j$: Estimated variance of asset $j$.

var(ve::SimpleVariance, X::MatNum; dims::Int = 1, mean = nothing, kwargs...) defines $\hat{\sigma}^2_j$ in each of the four cases that ve.w and ve.corrected select.

Algorithm

  1. Check that dims is 1 or 2.
  2. When mean is nothing, compute the centring vector mu with ve.me, after factory writes ve.w into it; otherwise take mu from mean.
  3. Resolve the observation weights from ve.w against X, giving w.
  4. When w is nothing, take the unweighted standard deviation of X along dims, centred on mu.
  5. Otherwise take the standard deviation of X weighted by w along dims, centred on mu.

$\hat{\mu}_j$ comes from ve.me, and ve.w reaches ve.me through factory. A SimpleVariance whose w is set therefore weights the centre and the squared deviations alike, so a vector and its one-column matrix answer the same number. Pass mean for any other centre.

Arguments

  • ve: Variance 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 passed to the mean estimator.

Validation

  • dims in (1, 2).
  • corrected = true needs a weight type that carries a bias correction. A plain StatsBase.Weights carries none, and StatsBase raises an ArgumentError.

Returns

  • sd::ArrNum: Standard deviation vector of X, reshaped to be consistent with the dimension along which the value is computed.

Examples

julia> sv = SimpleVariance()SimpleVariance         me ┼ SimpleExpectedReturns            │   w ┴ nothing          w ┼ nothing  corrected ┴ Bool: truejulia> Xmat = [1.0 2.0; 3.0 4.0];julia> std(sv, Xmat; dims = 1)1×2 Matrix{Float64}: 1.41421  1.41421

Related

source
Statistics.stdMethod
Statistics.std(
    ve::SimpleVariance,
    X::VecNum;
    mean = nothing
) -> Number

Compute the standard deviation using a SimpleVariance estimator for a vector.

This method computes the standard deviation of the input vector X using the configuration specified in ve.

Mathematical definition

var(ve::SimpleVariance, X::MatNum; dims::Int = 1, mean = nothing, kwargs...) defines the variance in each of the four cases that ve.w and ve.corrected select, and this method returns its square root.

Algorithm

  1. Resolve the observation weights from ve.w against X, giving w.
  2. When w is nothing, take the unweighted standard deviation of X, centred on mean.
  3. Otherwise take the standard deviation of X weighted by w, centred on mean.

The vector methods ignore ve.me: a mean of nothing reaches Statistics.std, which centres on the mean of X — the weighted mean when w is not nothing. The matrix methods resolve the centre from ve.me under the same ve.w, so the two paths answer the same number for the same data.

Arguments

  • ve: Variance estimator.
  • X: Data vector observations × 1.
  • mean: Optional mean value to use for centering.

Validation

  • corrected = true needs a weight type that carries a bias correction. A plain StatsBase.Weights carries none, and StatsBase raises an ArgumentError.

Returns

  • sd::Number: Standard deviation of X.

Examples

julia> sv = SimpleVariance()SimpleVariance         me ┼ SimpleExpectedReturns            │   w ┴ nothing          w ┼ nothing  corrected ┴ Bool: truejulia> X = [1.0, 2.0, 3.0];julia> std(sv, X)1.0julia> svw = SimpleVariance(; w = StatsBase.Weights([0.2, 0.3, 0.5]), corrected = false)SimpleVariance         me ┼ SimpleExpectedReturns            │   w ┴ nothing          w ┼ StatsBase.Weights{Float64, Float64, Vector{Float64}}: [0.2, 0.3, 0.5]  corrected ┴ Bool: falsejulia> std(svw, X)0.7810249675906654

Related

source
Statistics.varMethod
Statistics.var(
    ve::SimpleVariance,
    X::MatNum;
    dims::Int = 1,
    mean = nothing,
    kwargs...
) -> ArrNum

Compute the variance using a SimpleVariance estimator for a matrix.

This method computes the variance of the input matrix X using the configuration specified in ve.

Mathematical definition

Unweighted, corrected = true:

\[\begin{align} \hat{\sigma}^2_j &= \frac{1}{T-1} \sum_{t=1}^{T} (r_{tj} - \hat{\mu}_j)^2\,. \end{align}\]

Unweighted, corrected = false:

\[\begin{align} \hat{\sigma}^2_j &= \frac{1}{T} \sum_{t=1}^{T} (r_{tj} - \hat{\mu}_j)^2\,. \end{align}\]

Weighted:

\[\begin{align} \hat{\sigma}^2_j &= \frac{\sum_{t=1}^{T} w_t (r_{tj} - \hat{\mu}_j)^2}{\sum_{t=1}^{T} w_t - c}\,. \end{align}\]

Where:

  • $\hat{\sigma}^2_j$: Estimated variance of asset $j$.
  • $r_{tj}$: Return of asset $j$ at time $t$.
  • $\hat{\mu}_j$: Estimated mean of asset $j$.
  • $T$: Number of observations.
  • $w_{t}$: Observation weight of observation $t$.
  • $c$: Bias correction of the weighted denominator. It is fixed by the type of the weights, never by the estimator: corrected = false gives $c = 0$ for every type, and corrected = true gives $c = 1$ for StatsBase.FrequencyWeights, $c = \sum_t w_t^2 / \sum_t w_t$ for StatsBase.AnalyticWeights and $c = \sum_t w_t / T$ for StatsBase.ProbabilityWeights.

Algorithm

  1. Check that dims is 1 or 2.
  2. When mean is nothing, compute the centring vector mu with ve.me, after factory writes ve.w into it; otherwise take mu from mean.
  3. Resolve the observation weights from ve.w against X, giving w.
  4. When w is nothing, take the unweighted variance of X along dims, centred on mu.
  5. Otherwise take the variance of X weighted by w along dims, centred on mu.

$\hat{\mu}_j$ comes from ve.me, and ve.w reaches ve.me through factory. A SimpleVariance whose w is set therefore weights the centre and the squared deviations alike, so a vector and its one-column matrix answer the same number. Pass mean for any other centre.

Arguments

  • ve: Variance 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 passed to the mean estimator.

Validation

  • dims in (1, 2).
  • corrected = true needs a weight type that carries a bias correction. A plain StatsBase.Weights carries none, and StatsBase raises an ArgumentError.

Returns

  • vr::ArrNum: Variance vector of X, reshaped to be consistent with the dimension along which the value is computed.

Examples

julia> sv = SimpleVariance()SimpleVariance         me ┼ SimpleExpectedReturns            │   w ┴ nothing          w ┼ nothing  corrected ┴ Bool: truejulia> Xmat = [1.0 2.0; 3.0 4.0];julia> var(sv, Xmat; dims = 1)1×2 Matrix{Float64}: 2.0  2.0

Related

source
Statistics.varMethod
Statistics.var(
    ve::SimpleVariance,
    X::VecNum;
    mean = nothing
) -> Number

Compute the variance using a SimpleVariance estimator for a vector.

This method computes the variance of the input vector X using the configuration specified in ve.

Mathematical definition

var(ve::SimpleVariance, X::MatNum; dims::Int = 1, mean = nothing, kwargs...) defines the variance in each of the four cases that ve.w and ve.corrected select, and this method returns it for a single series.

Algorithm

  1. Resolve the observation weights from ve.w against X, giving w.
  2. When w is nothing, take the unweighted variance of X, centred on mean.
  3. Otherwise take the variance of X weighted by w, centred on mean.

The vector methods ignore ve.me: a mean of nothing reaches Statistics.var, which centres on the mean of X — the weighted mean when w is not nothing. The matrix methods resolve the centre from ve.me under the same ve.w, so the two paths answer the same number for the same data.

Arguments

  • ve: Variance estimator.
  • X: Data vector observations × 1.
  • mean: Optional mean value to use for centering.

Validation

  • corrected = true needs a weight type that carries a bias correction. A plain StatsBase.Weights carries none, and StatsBase raises an ArgumentError.

Returns

  • vr::Number: Variance of X.

Examples

julia> sv = SimpleVariance()SimpleVariance         me ┼ SimpleExpectedReturns            │   w ┴ nothing          w ┼ nothing  corrected ┴ Bool: truejulia> X = [1.0, 2.0, 3.0];julia> var(sv, X)1.0julia> svw = SimpleVariance(; w = StatsBase.Weights([0.2, 0.3, 0.5]), corrected = false)SimpleVariance         me ┼ SimpleExpectedReturns            │   w ┴ nothing          w ┼ StatsBase.Weights{Float64, Float64, Vector{Float64}}: [0.2, 0.3, 0.5]  corrected ┴ Bool: falsejulia> var(svw, X)0.61

Related

source

Incremental fit

The sample variance folds one observation at a time, so a long history need not be held or re-read. partial_fit! returns a new estimator whose cache field carries the state, and var reads the fit off the estimator alone.

PortfolioOptimisers.partial_fit!Method
partial_fit!(
    state::SimpleVarianceState,
    x::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}
) -> Any

SimpleVarianceState method of partial_fit!. Folds one observation into the running count, mean and per-asset accumulator.

Mathematical definition

\[\begin{align} n &\leftarrow n + 1\\ \boldsymbol{d} &= \boldsymbol{x} - \boldsymbol{\mu}\\ \boldsymbol{\mu} &\leftarrow \boldsymbol{\mu} + \frac{\boldsymbol{d}}{n}\\ \boldsymbol{M} &\leftarrow \boldsymbol{M} + \boldsymbol{d} \odot (\boldsymbol{x} - \boldsymbol{\mu})\, . \end{align}\]

Where:

  • $n$: observation count.
  • $\boldsymbol{x}$: the observation.
  • $\boldsymbol{\mu}$: the running mean.
  • $\boldsymbol{d}$: deviation of the observation from the mean before the fold.
  • $\boldsymbol{M}$: the running per-asset accumulator.

The last line reads $\boldsymbol{\mu}$ after the third line moved it, where $\boldsymbol{d}$ read it before. That asymmetry is Welford's, and it is what keeps the accumulator non-negative.

Algorithm

  1. Refuse an observation whose length is not the number of assets the state describes.
  2. Add one to the count.
  3. Take the deviation of the observation from the mean before the fold, giving d.
  4. Move mu in place along d, by the reciprocal of the new count.
  5. Add d times the deviation from the mean after the fold to M, in place.
  6. Rebind the count with Accessors.@reset, and return the state.
source
PortfolioOptimisers.partial_fit!Method
partial_fit!(
    est::Union{AbstractEstimator, CovarianceEstimator},
    X::Union{AbstractMatrix{<: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}}};
    dims,
    active_mask,
    estimation_mask
) -> Any

Folds observations into the sample buffer an estimator carries.

The buffering arm of partial_fit!, and the method every estimator carrying a SampleBufferState reaches. A family that folds exactly writes methods of its own, and each of them narrows the cache type parameter of its own estimator to the state that fold reads, so a buffer never meets them and this method is what remains. The state's type is therefore the whole route, and nothing refuses the step.

It is one method over both arms of the interface rather than two, because the families that refuse the step declare one method over both arms too, and a pair of narrower methods here would be ambiguous against each of them. So the arm is chosen by the type of X inside the body, which is statically resolved at every call site.

A buffer carries the per-observation masks beside the observations, so a CoveragePolicy mask threads through the wrapper as it does through an estimator's own accumulator, and the read-out hands it back to the batch verb. A wrapped estimator folded under a policy therefore answers what a batch fit over the same window under the same policy answers, and the unwrapped and wrapped paths agree.

Algorithm

  1. Read the buffer out of the cache field with assert_sample_buffer, which refuses an estimator that was never wrapped in Online.
  2. Fold a matrix and its masks through the block arm of partial_fit!, and a vector and its masks through the single-observation arm.
  3. Rebind est.cache with Accessors.@reset, and return the estimator.

Arguments

  • est: Estimator whose buffer is folded forward.
  • X: Observations to fold. A matrix holds one observation per row when dims == 1, and one per column when dims == 2. A vector is a single observation across the assets, and dims is ignored.
  • dims: Dimension along which to perform the computation.
  • active_mask: The active mask of the block, of the shape of X, or of one entry per asset when X is one observation, or nothing.
  • estimation_mask: The estimation mask, on the same terms as active_mask.

Validation

  • est carries a SampleBufferState. An ArgumentError is thrown otherwise.
  • The masks, when they are not nothing, have the shape of X. A DimensionMismatch is thrown otherwise.
  • A buffer holding observations is given the masks it already records. An ArgumentError is thrown otherwise.
  • dims in (1, 2).

Returns

  • est: The estimator, with its cache field rebound to the buffer after the last observation.

Related

source
PortfolioOptimisers.partial_fit!Method
partial_fit!(
    est::Union{AbstractEstimator, CovarianceEstimator},
    X::Union{AbstractMatrix{<: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}}};
    dims,
    active_mask,
    estimation_mask
) -> Any

Folds observations into the sample buffer an estimator carries.

The buffering arm of partial_fit!, and the method every estimator carrying a SampleBufferState reaches. A family that folds exactly writes methods of its own, and each of them narrows the cache type parameter of its own estimator to the state that fold reads, so a buffer never meets them and this method is what remains. The state's type is therefore the whole route, and nothing refuses the step.

It is one method over both arms of the interface rather than two, because the families that refuse the step declare one method over both arms too, and a pair of narrower methods here would be ambiguous against each of them. So the arm is chosen by the type of X inside the body, which is statically resolved at every call site.

A buffer carries the per-observation masks beside the observations, so a CoveragePolicy mask threads through the wrapper as it does through an estimator's own accumulator, and the read-out hands it back to the batch verb. A wrapped estimator folded under a policy therefore answers what a batch fit over the same window under the same policy answers, and the unwrapped and wrapped paths agree.

Algorithm

  1. Read the buffer out of the cache field with assert_sample_buffer, which refuses an estimator that was never wrapped in Online.
  2. Fold a matrix and its masks through the block arm of partial_fit!, and a vector and its masks through the single-observation arm.
  3. Rebind est.cache with Accessors.@reset, and return the estimator.

Arguments

  • est: Estimator whose buffer is folded forward.
  • X: Observations to fold. A matrix holds one observation per row when dims == 1, and one per column when dims == 2. A vector is a single observation across the assets, and dims is ignored.
  • dims: Dimension along which to perform the computation.
  • active_mask: The active mask of the block, of the shape of X, or of one entry per asset when X is one observation, or nothing.
  • estimation_mask: The estimation mask, on the same terms as active_mask.

Validation

  • est carries a SampleBufferState. An ArgumentError is thrown otherwise.
  • The masks, when they are not nothing, have the shape of X. A DimensionMismatch is thrown otherwise.
  • A buffer holding observations is given the masks it already records. An ArgumentError is thrown otherwise.
  • dims in (1, 2).

Returns

  • est: The estimator, with its cache field rebound to the buffer after the last observation.

Related

source
Statistics.varMethod
Statistics.var(
    ve::SimpleVariance,
    state::SimpleVarianceState
) -> VecNum
Statistics.var(
    ve::SimpleVariance
) -> VecNum

Read the variance of an incremental fit out of a SimpleVarianceState.

The two-argument method reads a state the caller holds, and the one-argument method reads the state the cache field of ve carries. Both return the per-asset variance as a vector, assets × 1, where the batch method over a matrix returns a row when dims = 1.

Mathematical definition

\[\begin{align} \hat{\sigma}^2_j &= \frac{M_j}{n - c}\,. \end{align}\]

Where:

  • $\hat{\sigma}^2_j$: Estimated variance of asset $j$.
  • $M_j$: running accumulator of asset $j$.
  • $n$: observation count.
  • $c$: one when ve.corrected holds, and zero otherwise.

Algorithm

  1. Refuse a configuration no incremental fit reproduces, with assert_partial_fittable.
  2. Take the divisor n - c, and return a vector of NaN when it is below one, in the way min_obs reads an asset with too few observations.
  3. Otherwise divide the accumulator by the divisor.

Arguments

  • ve: Variance estimator.
  • state: The state to read.

Validation

  • ve carries no observation weights. An ArgumentError is thrown otherwise.
  • ve.me is a SimpleExpectedReturns carrying no observation weights, or nothing. An ArgumentError is thrown otherwise.
  • ve.cache is not nothing, for the one-argument method. An ArgumentError is thrown otherwise.

Returns

  • vr::VecNum: Per-asset variance of the fit, assets × 1, or NaN where the state holds too few observations.

Examples

julia> ve = foldl(partial_fit!, eachrow([1.0 2.0; 3.0 4.0]); init = SimpleVariance());julia> var(ve)2-element Vector{Float64}: 2.0 2.0

Related

source
PortfolioOptimisers.port_opt_viewMethod
port_opt_view(
    x::SimpleVarianceState,
    i,
    args...
) -> Union{SimpleVarianceState{_A, _B, _C, Nothing} where {_A, _B, _C}, SimpleVarianceState{_A, _B, _C, __T_cvg} where {_A, _B, _C, __T_cvg<:CoverageCounts}}

Slices a SimpleVarianceState to the selected assets.

The Welford accumulator of one asset reads that asset's observations alone, and reads no other asset. So the slice of the state is the state of the sliced universe, entry for entry, and the count is shared by every asset and passes through. The slice copies by index and does not view: a later partial_fit! on the viewed estimator would otherwise write through into the arrays of the estimator the view was taken from.

Arguments

  • x: The state to slice.
  • i: Index or indices of the assets to keep.
  • args...: Additional positional arguments (ignored).

Returns

  • state::SimpleVarianceState: The state of the same sample over the selected assets.

Related

source
PortfolioOptimisers.merge_statesMethod
merge_states(
    a::SimpleVarianceState,
    b::SimpleVarianceState
) -> Union{SimpleVarianceState{_A, _B, _C, Nothing} where {_A, _B, _C}, SimpleVarianceState{_A, _B, _C, __T_cvg} where {_A, _B, _C, __T_cvg<:(CoverageCounts{_A, Nothing} where _A)}}

Folds two SimpleVarianceState fitted on disjoint blocks into the state of the concatenated block.

Algorithm

  1. Refuse the pair with assert_mergeable_states.
  2. Fold the counts, the means and the accumulators with chan_merge, whose elementwise method reads a per-asset accumulator.

Arguments

  • a: The state of the first block of observations.
  • b: The state of the second block of observations.

Validation

Returns

  • state::SimpleVarianceState: The state the two blocks give when they are fitted as one block.

Related

source

Available-case fit

With a CoveragePolicy in its cvg field the estimator fits each asset on that asset's own finite and active observations, and PortfolioOptimisers.coverage_variance routes between that arm and the Coverage Universe one.

PortfolioOptimisers.partial_fit!Method
partial_fit!(
    state::SimpleVarianceState,
    x::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
    _::Nothing,
    _::Union{Nothing, AbstractVector{<:Bool}}
) -> Any

Nothing method of the coverage arm of partial_fit! for a SimpleVarianceState. An estimator that carries no CoveragePolicy folds through partial_fit!(state::SimpleVarianceState, x::VecNum), and the active mask is ignored.

Related

source
PortfolioOptimisers.partial_fit!Method
partial_fit!(
    state::SimpleVarianceState,
    x::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}},
    cvg::CoveragePolicy,
    active_mask::Union{Nothing, AbstractVector{<:Bool}}
) -> Any

CoveragePolicy method of the coverage arm of partial_fit! for a SimpleVarianceState. Folds one observation into the running per-asset count, mean and accumulator, reading each asset's own observations alone.

Mathematical definition

\[\begin{align} \nu_j &\leftarrow \nu_j + 1\\ d_j &= r_{tj} - \mu_j\\ \mu_j &\leftarrow \mu_j + \frac{d_j}{\nu_j}\\ M_j &\leftarrow M_j + d_j (r_{tj} - \mu_j)\, , \end{align}\]

for every asset $j$ that is finite and active at observation $t$, and no line at all for an asset that is not. Where:

  • $\nu_j$: the number of observations at which asset $j$ was finite and active.
  • $r_{tj}$: Return of asset $j$ at time $t$.
  • $\mu_j$: the running mean of asset $j$.
  • $d_j$: the deviation of asset $j$ from its mean before the fold.
  • $M_j$: the running second-moment accumulator of asset $j$.

The last line reads $\mu_j$ after the third line moved it, which is Welford's asymmetry, so the accumulator is exact per asset and a skipped observation costs the asset nothing.

Algorithm

  1. Refuse an observation whose length is not the number of assets the state describes.
  2. Read the valid assets and the newly inactive ones with coverage_valid.
  3. Apply the algorithm's fold-time rule with fold_inactive!.
  4. Fold each valid asset's return into its own count, mean and accumulator.
  5. Move the per-asset bookkeeping on with coverage_step!, add one to the observation count, and return the state.

Arguments

  • state: The state to fold into, mutated in place.
  • x: One observation, one entry per asset.
  • cvg: The policy the estimator carries.
  • active_mask: The active mask of the Asset Panel at this observation, or nothing.

Validation

  • length(x) is the number of assets the state describes. A DimensionMismatch is thrown otherwise.

Returns

  • state::SimpleVarianceState: The state after the observation.

Related

source
PortfolioOptimisers.fold_inactive!Method
fold_inactive!(
    _::ResetCoverage,
    state::SimpleVarianceState,
    ni::AbstractVector{<:Bool}
) -> SimpleVarianceState

SimpleVarianceState method of fold_inactive! under ResetCoverage. Zeroes the count, the centre, the running mean and the accumulator of every asset that has just gone inactive, so that a relisting starts the asset cold. The centre of a per-asset state is nothing, because its mu is already the cell's centre, and coverage_reset! passes that through.

Related

source