Simple covariance
The covariance is an important measure of risk used in portfolio selection and performance analysis. The classic Markowitz [11] portfolio uses the portfolio variance as its risk measure, which is computed from the covariance matrix and portfolio weights. Here we define the most basic covariance/correlation estimator.
General covariance
PortfolioOptimisers.GeneralCovariance — Type
struct GeneralCovariance{__T_ce, __T_w, __T_cache} <: AbstractCovarianceEstimatorAdapts any StatsBase.CovarianceEstimator to the library's calling convention, carrying its observation weights alongside it.
The estimator and the weights travel together in one object, so a caller passes one value where the StatsBase API takes a separate estimator and weight vector at every call site. ce accepts any subtype of StatsBase.CovarianceEstimator, so an estimator from a package such as CovarianceEstimation.jl reaches the library unchanged.
Fields
ce: Covariance estimator.
w: Optional observation weights vectorobservations × 1, or a concrete subtype ofDynamicAbstractWeights. Ifnothing, the computation is unweighted.
cache: Optional partial-fit state. It isnothinguntilpartial_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:factorycarries it unchanged, because a factory call resolves configuration rather than the sample;port_opt_viewslices it to the selected assets by index copy, so the viewed estimator answers over those assets alone; andobs_weights_viewdrops 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
GeneralCovariance(; ce::StatsBase.CovarianceEstimator = StatsBase.SimpleCovariance(; corrected = true), w::Option{<:ObsWeights} = nothing, cache::Option{<:AbstractPartialFitState} = nothing) -> GeneralCovarianceKeywords correspond to the struct's fields.
Validation
- If
wis notnothing,!isempty(w).
Propagated parameters
When factory is called on this type, the following @fprop-tagged fields are automatically propagated:
ce: Recursively updated viafactory.w: Replaced with the incomingObsWeights.cache: Carried unchanged viafactory.
View parameters
When port_opt_view is called on this type, the following @vprop-tagged fields are automatically subset to the selected indices:
ce: Recursively viewed viaport_opt_view.cache: Sliced to the selected assets viaport_opt_view.
Observation weight parameters
When obs_weights_view is called on this type, the following fields are automatically indexed to the selected observations:
ce: Recursively indexed viaobs_weights_view.w: Indexed to the selected observations viaobs_weights_view.cache: Dropped viaobs_weights_view, because no slice of a state exists on the observation axis.
Examples
julia> GeneralCovariance()GeneralCovariance ce ┼ StatsBase.SimpleCovariance: StatsBase.SimpleCovariance(true) w ┴ nothingjulia> GeneralCovariance(; w = StatsBase.Weights([0.1, 0.2, 0.7]))GeneralCovariance ce ┼ StatsBase.SimpleCovariance: StatsBase.SimpleCovariance(true) w ┴ StatsBase.Weights{Float64, Float64, Vector{Float64}}: [0.1, 0.2, 0.7]Related
Statistics.cov — Method
Statistics.cov(
ce::GeneralCovariance,
X::MatNum;
dims::Int = 1,
mean = nothing,
kwargs...
) -> MatNumCompute the covariance matrix using a GeneralCovariance estimator.
This method dispatches to the appropriate robust_cov depending on ce.w, which computes the covariance matrix using ce.ce.
Algorithm
- Resolve the observation weights from
ce.wagainstX, givingw. - When
wisnothing, callrobust_covwithce.ceandXalone. - Otherwise call
robust_covwithce.ce,Xandw.
Arguments
ce: Covariance estimator.X: Data matrixobservations × assetsif thedimskeyword does not exist ordims = 1,assets × observationswhendims = 2.dims: Dimension along which to perform the computation.mean: Optional mean value to use for centering.kwargs...: Additional keyword arguments passed torobust_cov.
Returns
sigma::MatNum: Covariance matrixassets x assets.
Examples
julia> X = [0.01 0.02; 0.03 0.04; 0.02 0.03];julia> cov(GeneralCovariance(), X)2×2 Matrix{Float64}: 0.0001 0.0001 0.0001 0.0001Related
Statistics.cor — Method
Statistics.cor(
ce::GeneralCovariance,
X::MatNum;
dims::Int = 1,
mean = nothing,
kwargs...
) -> MatNumCompute the correlation matrix using a GeneralCovariance estimator.
This method dispatches to the appropriate robust_cor depending on ce.w, which computes the correlation matrix using ce.ce.
Algorithm
- Resolve the observation weights from
ce.wagainstX, givingw. - When
wisnothing, callrobust_corwithce.ceandXalone. - Otherwise call
robust_corwithce.ce,Xandw.
Arguments
ce: Covariance estimator.X: Data matrixobservations × assetsif thedimskeyword does not exist ordims = 1,assets × observationswhendims = 2.dims: Dimension along which to perform the computation.mean: Optional mean value to use for centering.kwargs...: Additional keyword arguments passed torobust_cor.
Returns
rho::MatNum: Correlation matrixassets x assets.
Examples
julia> X = [0.01 0.02; 0.03 0.04; 0.02 0.03];julia> cor(GeneralCovariance(), X)2×2 Matrix{Float64}: 1.0 1.0 1.0 1.0Related
Covariance
PortfolioOptimisers.Covariance — Type
struct Covariance{__T_me, __T_ce, __T_alg, __T_w, __T_cvg, __T_cache} <: AbstractCovarianceEstimatorEstimates the covariance matrix of asset returns from a centring estimator, a covariance estimator, and a moment algorithm.
Covariance encapsulates all components required for estimating the covariance matrix of asset returns, including the expected returns estimator for centering the data, the covariance estimator, and the moment algorithm.
w weights the whole estimate, so it reaches the centre as well as the deviations. The four methods send me and ce through factory, which replaces the weights of each with w, so w wins over the weights that me and ce carry. Pass mean for a centre that w does not describe.
ce admits any StatsBase.CovarianceEstimator, and no verb of this library reads the weights of one that the library does not own. A ce from a package such as CovarianceEstimation.jl therefore keeps its own configuration, and w is the field that weights a Covariance.
Fields
me: Expected returns estimator.
ce: Covariance estimator.
alg: Moment algorithm.
w: Optional observation weights vectorobservations × 1, or a concrete subtype ofDynamicAbstractWeights. Ifnothing, the computation is unweighted.
cvg: OptionalCoveragePolicy.nothingis 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 isNaNthroughout 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 whereadmitssays so.
cache: Optional partial-fit state. It isnothinguntilpartial_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:factorycarries it unchanged, because a factory call resolves configuration rather than the sample;port_opt_viewslices it to the selected assets by index copy, so the viewed estimator answers over those assets alone; andobs_weights_viewdrops 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
Covariance(; me::AbstractExpectedReturnsEstimator = SimpleExpectedReturns(), ce::StatsBase.CovarianceEstimator = GeneralCovariance(), alg::AbstractMomentAlgorithm = FullMoment(), w::Option{<:ObsWeights} = nothing, cvg::Option{<:CoveragePolicy} = nothing, cache::Option{<:AbstractPartialFitState} = nothing) -> CovarianceKeywords correspond to the struct's fields.
Validation
- If
wis notnothing,!isempty(w).
Propagated parameters
When factory is called on this type, the following @fprop-tagged fields are automatically propagated:
me: Recursively updated viafactory.ce: Recursively updated viafactory.w: Replaced with the incomingObsWeights.cache: Carried unchanged viafactory.
View parameters
When port_opt_view is called on this type, the following @vprop-tagged fields are automatically subset to the selected indices:
me: Recursively viewed viaport_opt_view.ce: Recursively viewed viaport_opt_view.cache: Sliced to the selected assets viaport_opt_view.
Observation weight parameters
When obs_weights_view is called on this type, the following fields are automatically indexed to the selected observations:
me: Recursively indexed viaobs_weights_view.ce: Recursively indexed viaobs_weights_view.w: Indexed to the selected observations viaobs_weights_view.cache: Dropped viaobs_weights_view, because no slice of a state exists on the observation axis.
Examples
julia> Covariance()Covariance me ┼ SimpleExpectedReturns │ w ┴ nothing ce ┼ GeneralCovariance │ ce ┼ StatsBase.SimpleCovariance: StatsBase.SimpleCovariance(true) │ w ┴ nothing alg ┼ FullMoment() w ┴ nothingjulia> Covariance(; w = StatsBase.AnalyticWeights([0.2, 0.3, 0.5]))Covariance me ┼ SimpleExpectedReturns │ w ┴ nothing ce ┼ GeneralCovariance │ ce ┼ StatsBase.SimpleCovariance: StatsBase.SimpleCovariance(true) │ w ┴ nothing alg ┼ FullMoment() w ┴ StatsBase.AnalyticWeights{Float64, Float64, Vector{Float64}}: [0.2, 0.3, 0.5]Related
Statistics.cov — Method
Statistics.cov(
ce::Covariance,
X::MatNum;
dims::Int = 1,
mean = nothing,
kwargs...
) -> MatNumCompute the covariance matrix using a Covariance estimator.
Mathematical definition
FullMoment covariance:
\[\begin{align} \hat{\mathbf{\Sigma}}_{ij} &= \frac{1}{T-1} \sum_{t=1}^{T} (r_{ti} - \hat{\mu}_i)(r_{tj} - \hat{\mu}_j)\,. \end{align}\]
SemiMoment (downside) covariance, from the de-meaned returns clipped at zero:
\[\begin{align} \tilde{r}_{tj} &= \min(r_{tj} - \hat{\mu}_j,\, 0)\,,\\ \hat{\mathbf{\Sigma}}^{\text{semi}}_{ij} &= \frac{1}{T-1} \sum_{t=1}^{T} \tilde{r}_{ti} \, \tilde{r}_{tj}\,. \end{align}\]
Where:
- $\hat{\mathbf{\Sigma}}_{ij}$: Estimated covariance between assets $i$ and $j$.
- $\hat{\mathbf{\Sigma}}^{\text{semi}}_{ij}$: Estimated semi-covariance between assets $i$ and $j$.
- $r_{tj}$: Return of asset $j$ at time $t$.
- $r_{ti}$: Return of asset $i$ at time $t$.
- $\hat{\mu}_j$: Estimated mean of asset $j$.
- $\hat{\mu}_i$: Estimated mean of asset $i$.
- $\tilde{r}_{ti}$, $\tilde{r}_{tj}$: De-meaned returns of assets $i$ and $j$, clipped at zero.
- $T$: Number of observations.
The semi-covariance keeps the $T-1$ divisor of the full moment, so it is not the covariance of the clipped returns about their own mean.
Algorithm
- Resolve the centring vector
muand the inner estimatorcelwithcovariance_centre_and_estimator. Whenmeanisnothing,mucomes fromce.me; otherwise it comes frommean. Whence.wis notnothing,ce.wreachesce.meandce.cethroughfactoryfirst. - Delegate to
Statistics.cov(cel, X; dims = dims, mean = mu, kwargs...).
Arguments
ce: Covariance estimator.ce::Covariance{<:Any, <:Any, <:FullMoment}: Covariance estimator withFullMomentmoment algorithm.ce::Covariance{<:Any, <:Any, <:SemiMoment}: Covariance estimator withSemiMomentmoment algorithm.
X: Data matrixobservations × assetsif thedimskeyword does not exist ordims = 1,assets × observationswhendims = 2.dims: Dimension along which to perform the computation.mean: Optional mean value to use for centering. If not provided, computed usingce.me.kwargs...: Additional keyword arguments passed to the underlying covariance estimator.
Returns
sigma::MatNum: Covariance matrixassets x assets.
Examples
julia> X = [0.01 0.02; 0.03 0.04; 0.02 0.03];julia> cov(Covariance(), X)2×2 Matrix{Float64}: 0.0001 0.0001 0.0001 0.0001julia> cov(Covariance(; alg = SemiMoment()), X)2×2 Matrix{Float64}: 5.0e-5 5.0e-5 5.0e-5 5.0e-5Related
Statistics.cov — Method
cov(
ce::Covariance{<:Any, <:Any, <:SemiMoment},
X::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}};
dims,
mean,
active_mask,
kwargs...
) -> Any
SemiMoment variant of cov(ce::Covariance, X::MatNum; dims::Int = 1, mean = nothing, kwargs...). Clips de-meaned returns to zero before computing the covariance matrix, capturing only downside co-movements.
Algorithm
- Resolve the centring vector
muand the inner estimatorcelwithcovariance_centre_and_estimator. - Replace
Xwithmin.(X .- mu, 0), the de-meaned returns clipped at zero. - Delegate to
Statistics.cov(cel, X; dims = dims, mean = 0, kwargs...). The zero mean is what stops the clipped returns being centred a second time.
Statistics.cor — Method
Statistics.cor(
ce::Covariance,
X::MatNum;
dims::Int = 1,
mean = nothing,
kwargs...
) -> MatNumCompute the correlation matrix using a Covariance estimator.
Mathematical definition
\[\begin{align} \hat{\mathbf{P}}_{ij} &= \frac{\hat{\mathbf{\Sigma}}_{ij}}{\hat{\sigma}_i \hat{\sigma}_j}\,. \end{align}\]
Where:
- $\hat{\mathbf{P}}_{ij}$: Estimated correlation between assets $i$ and $j$.
- $\hat{\mathbf{\Sigma}}_{ij}$: Estimated covariance between assets $i$ and $j$.
- $\hat{\sigma}_i$: Estimated standard deviation of asset $i$.
- $\hat{\sigma}_j$: Estimated standard deviation of asset $j$.
The alg field of ce reaches $\hat{\mathbf{\Sigma}}$: SemiMoment standardises the semi-covariance, so the diagonal is one and an off-diagonal entry is a downside correlation.
Algorithm
- Resolve the centring vector
muand the inner estimatorcelwithcovariance_centre_and_estimator. Whenmeanisnothing,mucomes fromce.me; otherwise it comes frommean. Whence.wis notnothing,ce.wreachesce.meandce.cethroughfactoryfirst. - Delegate to
Statistics.cor(cel, X; dims = dims, mean = mu, kwargs...).
Arguments
ce: Covariance estimator.ce::Covariance{<:Any, <:Any, <:FullMoment}: Covariance estimator withFullMomentmoment algorithm.ce::Covariance{<:Any, <:Any, <:SemiMoment}: Covariance estimator withSemiMomentmoment algorithm.
X: Data matrixobservations × assetsif thedimskeyword does not exist ordims = 1,assets × observationswhendims = 2.dims: Dimension along which to perform the computation.mean: Optional mean value to use for centering. If not provided, computed usingce.me.kwargs...: Additional keyword arguments passed to the underlying correlation estimator.
Returns
rho::MatNum: Correlation matrixassets x assets.
Examples
julia> X = [0.01 0.02; 0.03 0.04; 0.02 0.03];julia> cor(Covariance(), X)2×2 Matrix{Float64}: 1.0 1.0 1.0 1.0Related
Statistics.cor — Method
cor(
ce::Covariance{<:Any, <:Any, <:SemiMoment},
X::AbstractMatrix{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}};
dims,
mean,
active_mask,
kwargs...
) -> Any
SemiMoment variant of cor(ce::Covariance, X::MatNum; dims::Int = 1, mean = nothing, kwargs...). Clips de-meaned returns to zero before computing the correlation matrix, capturing only downside co-movements.
Algorithm
- Resolve the centring vector
muand the inner estimatorcelwithcovariance_centre_and_estimator. - Replace
Xwithmin.(X .- mu, 0), the de-meaned returns clipped at zero. - Delegate to
Statistics.cor(cel, X; dims = dims, mean = 0, kwargs...). The zero mean is what stops the clipped returns being centred a second time.
Incremental fit
The full-moment sample covariance folds one observation at a time, so a long history need not be held or re-read. One state serves both estimators, because they run the same recursion over the same three quantities. partial_fit! returns a new estimator whose cache field carries the state, and cov reads the fit off the estimator alone.
PortfolioOptimisers.partial_fit! — Method
partial_fit!(
state::CovarianceState,
x::AbstractVector{<:Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar}}
) -> Any
CovarianceState method of partial_fit!. Folds one observation into the running count, mean and co-moment 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} (\boldsymbol{x} - \boldsymbol{\mu})^{\intercal}\, . \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 co-moment 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 positive semi-definite.
Algorithm
- Refuse an observation whose length is not the number of assets the state describes.
- Add one to the count.
- Take the deviation of the observation from the mean before the fold, giving
d. - Move
muin place alongd, by the reciprocal of the new count. - Add the outer product of
dand the deviation from the mean after the fold toM, in place. - Rebind the count with
Accessors.@reset, and return the state.
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
- Read the buffer out of the
cachefield withassert_sample_buffer, which refuses an estimator that was never wrapped inOnline. - Fold a matrix and its masks through the block arm of
partial_fit!, and a vector and its masks through the single-observation arm. - Rebind
est.cachewithAccessors.@reset, and return the estimator.
Arguments
est: Estimator whose buffer is folded forward.X: Observations to fold. A matrix holds one observation per row whendims == 1, and one per column whendims == 2. A vector is a single observation across the assets, anddimsis ignored.dims: Dimension along which to perform the computation.active_mask: The active mask of the block, of the shape ofX, or of one entry per asset whenXis one observation, ornothing.estimation_mask: The estimation mask, on the same terms asactive_mask.
Validation
estcarries aSampleBufferState. AnArgumentErroris thrown otherwise.- The masks, when they are not
nothing, have the shape ofX. ADimensionMismatchis thrown otherwise. - A buffer holding observations is given the masks it already records. An
ArgumentErroris thrown otherwise. dims in (1, 2).
Returns
est: The estimator, with itscachefield rebound to the buffer after the last observation.
Related
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
- Read the buffer out of the
cachefield withassert_sample_buffer, which refuses an estimator that was never wrapped inOnline. - Fold a matrix and its masks through the block arm of
partial_fit!, and a vector and its masks through the single-observation arm. - Rebind
est.cachewithAccessors.@reset, and return the estimator.
Arguments
est: Estimator whose buffer is folded forward.X: Observations to fold. A matrix holds one observation per row whendims == 1, and one per column whendims == 2. A vector is a single observation across the assets, anddimsis ignored.dims: Dimension along which to perform the computation.active_mask: The active mask of the block, of the shape ofX, or of one entry per asset whenXis one observation, ornothing.estimation_mask: The estimation mask, on the same terms asactive_mask.
Validation
estcarries aSampleBufferState. AnArgumentErroris thrown otherwise.- The masks, when they are not
nothing, have the shape ofX. ADimensionMismatchis thrown otherwise. - A buffer holding observations is given the masks it already records. An
ArgumentErroris thrown otherwise. dims in (1, 2).
Returns
est: The estimator, with itscachefield rebound to the buffer after the last observation.
Related
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
- Read the buffer out of the
cachefield withassert_sample_buffer, which refuses an estimator that was never wrapped inOnline. - Fold a matrix and its masks through the block arm of
partial_fit!, and a vector and its masks through the single-observation arm. - Rebind
est.cachewithAccessors.@reset, and return the estimator.
Arguments
est: Estimator whose buffer is folded forward.X: Observations to fold. A matrix holds one observation per row whendims == 1, and one per column whendims == 2. A vector is a single observation across the assets, anddimsis ignored.dims: Dimension along which to perform the computation.active_mask: The active mask of the block, of the shape ofX, or of one entry per asset whenXis one observation, ornothing.estimation_mask: The estimation mask, on the same terms asactive_mask.
Validation
estcarries aSampleBufferState. AnArgumentErroris thrown otherwise.- The masks, when they are not
nothing, have the shape ofX. ADimensionMismatchis thrown otherwise. - A buffer holding observations is given the masks it already records. An
ArgumentErroris thrown otherwise. dims in (1, 2).
Returns
est: The estimator, with itscachefield rebound to the buffer after the last observation.
Related
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
- Read the buffer out of the
cachefield withassert_sample_buffer, which refuses an estimator that was never wrapped inOnline. - Fold a matrix and its masks through the block arm of
partial_fit!, and a vector and its masks through the single-observation arm. - Rebind
est.cachewithAccessors.@reset, and return the estimator.
Arguments
est: Estimator whose buffer is folded forward.X: Observations to fold. A matrix holds one observation per row whendims == 1, and one per column whendims == 2. A vector is a single observation across the assets, anddimsis ignored.dims: Dimension along which to perform the computation.active_mask: The active mask of the block, of the shape ofX, or of one entry per asset whenXis one observation, ornothing.estimation_mask: The estimation mask, on the same terms asactive_mask.
Validation
estcarries aSampleBufferState. AnArgumentErroris thrown otherwise.- The masks, when they are not
nothing, have the shape ofX. ADimensionMismatchis thrown otherwise. - A buffer holding observations is given the masks it already records. An
ArgumentErroris thrown otherwise. dims in (1, 2).
Returns
est: The estimator, with itscachefield rebound to the buffer after the last observation.
Related
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
- Read the buffer out of the
cachefield withassert_sample_buffer, which refuses an estimator that was never wrapped inOnline. - Fold a matrix and its masks through the block arm of
partial_fit!, and a vector and its masks through the single-observation arm. - Rebind
est.cachewithAccessors.@reset, and return the estimator.
Arguments
est: Estimator whose buffer is folded forward.X: Observations to fold. A matrix holds one observation per row whendims == 1, and one per column whendims == 2. A vector is a single observation across the assets, anddimsis ignored.dims: Dimension along which to perform the computation.active_mask: The active mask of the block, of the shape ofX, or of one entry per asset whenXis one observation, ornothing.estimation_mask: The estimation mask, on the same terms asactive_mask.
Validation
estcarries aSampleBufferState. AnArgumentErroris thrown otherwise.- The masks, when they are not
nothing, have the shape ofX. ADimensionMismatchis thrown otherwise. - A buffer holding observations is given the masks it already records. An
ArgumentErroris thrown otherwise. dims in (1, 2).
Returns
est: The estimator, with itscachefield rebound to the buffer after the last observation.
Related
Statistics.cov — Method
Statistics.cov(
ce::Union{<:GeneralCovariance, <:Covariance{<:Any, <:Any, <:FullMoment}},
state::CovarianceState
) -> MatNum
Statistics.cov(ce::Union{<:GeneralCovariance, <:Covariance}) -> MatNumRead the covariance matrix of an incremental fit out of a CovarianceState.
The two-argument method reads a state the caller holds, and the one-argument method reads the state the cache field of ce carries. The bias correction comes from the innermost StatsBase.SimpleCovariance, which partial_fit_corrected resolves.
The one-argument method is bound to every moment algorithm, not to FullMoment alone, because an estimator wrapped in Online carries a SampleBufferState rather than a CovarianceState, and reading it runs the batch verb over the buffer's rows — which every algorithm answers. It dispatches on what cache holds, so a SemiMoment estimator answers when it was wrapped and meets the named refusal of partial_fit_cache when it carries nothing.
Mathematical definition
\[\begin{align} \hat{\mathbf{\Sigma}} &= \frac{M}{n - c}\,. \end{align}\]
Where:
- $\hat{\mathbf{\Sigma}}$: Estimated covariance matrix.
- $M$: Running co-moment accumulator.
- $n$: Observation count.
- $c$: One when the innermost
StatsBase.SimpleCovarianceis corrected, and zero otherwise.
Algorithm
- Resolve the bias correction with
partial_fit_corrected, which refuses every estimator an incremental fit does not reproduce. - Take the divisor
n - c, and return a matrix ofNaNwhen it is below one, in the waymin_obsreads an asset with too few observations. - Otherwise divide the accumulator by the divisor.
Arguments
ce: Covariance estimator.state: The state to read.
Validation
cepassespartial_fit_corrected. AnArgumentErroris thrown otherwise.ce.cacheis notnothing, for the one-argument method. AnArgumentErroris thrown otherwise.
Returns
sigma::MatNum: Covariance matrixassets x assets.NaNwhere the state holds too few observations.
Examples
julia> ce = foldl(partial_fit!, eachrow([0.01 0.02; 0.03 0.04; 0.02 0.03]); init = Covariance());julia> cov(ce)2×2 Matrix{Float64}: 0.0001 0.0001 0.0001 0.0001Related
Statistics.cor — Method
Statistics.cor(ce::Union{<:GeneralCovariance,
<:Covariance{<:Any, <:Any, <:FullMoment}},
state::CovarianceState)
Statistics.cor(ce::Union{<:GeneralCovariance, <:Covariance})Reads a correlation matrix out of a folded covariance estimator.
The correlation twin of the state read-out of Statistics.cov, and the same two steps the batch method takes: the folded covariance first, then coverage_correlation, which is the one place the conversion lives. So a folded estimator answers cor for the same configurations it answers cov, and the composite PortfolioOptimisersCovariance can offer both.
Arguments
ce: Covariance estimator.state: The state the estimator carries.
Validation
ce.cacheis notnothing, for the one-argument form. AnArgumentErroris thrown otherwise.
Returns
rho::MatNum: Correlation matrix of the observations the state was fitted on.
Related
PortfolioOptimisers.port_opt_view — Method
port_opt_view(
x::CovarianceState,
i,
args...
) -> Union{CovarianceState{_A, _B, _C, Nothing} where {_A, _B, _C}, CovarianceState{_A, _B, _C, __T_cvg} where {_A, _B, _C, __T_cvg<:CoverageCounts}}
Slices a CovarianceState to the selected assets.
The Welford accumulator of one pair of assets reads those two assets' observations alone, and reads no third 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::CovarianceState: The state of the same sample over the selected assets.
Related
PortfolioOptimisers.merge_states — Method
merge_states(
a::CovarianceState,
b::CovarianceState
) -> Union{CovarianceState{_A, _B, _C, Nothing} where {_A, _B, _C}, CovarianceState{_A, _B, _C, __T_cvg} where {_A, _B, _C, __T_cvg<:CoverageCounts}}
Folds two CovarianceState fitted on disjoint blocks into the state of the concatenated block.
Algorithm
- Refuse the pair with
assert_mergeable_states. - Fold the counts, the means and the accumulators with
chan_merge, whose outer-product method reads a co-moment accumulator.
Arguments
a: The state of the first block of observations.b: The state of the second block of observations.
Validation
aandbpassassert_mergeable_states.
Returns
state::CovarianceState: The state the two blocks give when they are fitted as one block.
Related
Available-case fit
With a CoveragePolicy in its cvg field the estimator fits each pair on the observations that pair shares, and PortfolioOptimisers.coverage_covariance routes between that arm and the Coverage Universe one.
PortfolioOptimisers.partial_fit! — Method
partial_fit!(
state::CovarianceState,
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 CovarianceState. An estimator that carries no CoveragePolicy folds through partial_fit!(state::CovarianceState, x::VecNum), and the active mask is ignored.
Related
PortfolioOptimisers.partial_fit! — Method
partial_fit!(
state::CovarianceState,
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 CovarianceState. Folds one observation into the running per-pair count, centre and co-moment accumulator, reading each pair's own observations alone.
Mathematical definition
\[\begin{align} \nu_{jk} &\leftarrow \nu_{jk} + 1\\ d_{jk} &= r_{tj} - c_{jk}\\ c_{jk} &\leftarrow c_{jk} + \frac{d_{jk}}{\nu_{jk}}\\ d_{kj} &= r_{tk} - c_{kj}\\ c_{kj} &\leftarrow c_{kj} + \frac{d_{kj}}{\nu_{jk}}\\ M_{jk} &\leftarrow M_{jk} + d_{jk} (r_{tk} - c_{kj})\, , \end{align}\]
for every pair $(j, k)$ both of whose assets are finite and active at observation $t$, and no line at all for a pair that is not. Where:
- $\nu_{jk}$: the number of observations at which both assets of the pair were finite and active.
- $r_{tj}$: Return of asset $j$ at time $t$.
- $c_{jk}$: the running mean of asset $j$ over the observations of the pair $(j, k)$, so that $c_{kj}$ is the running mean of asset $k$ over those same observations.
- $M_{jk}$: the running co-moment accumulator of the pair.
This is Welford's recursion per pair, so a covariance folded observation by observation is the covariance of the same rows fitted as a block, and the diagonal is each asset's ordinary available-case variance. The pair is the unit of the centre as well as of the count, which is what makes the recursion exact: an entry centred on the whole window's mean would need every past term corrected as the window grows.
Algorithm
- Refuse an observation whose length is not the number of assets the state describes.
- Read the valid assets and the newly inactive ones with
coverage_valid. - Apply the algorithm's fold-time rule with
fold_inactive!. - Fold the observation into every pair of valid assets, taking the upper triangle and mirroring it, so that the accumulator stays exactly symmetric.
- Copy the diagonal of the centre onto
mu, which is each asset's own available-case mean. - 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, ornothing.
Validation
length(x)is the number of assets the state describes. ADimensionMismatchis thrown otherwise.
Returns
state::CovarianceState: The state after the observation.
Related
PortfolioOptimisers.fold_inactive! — Method
fold_inactive!(
_::ResetCoverage,
state::CovarianceState,
ni::AbstractVector{<:Bool}
) -> CovarianceState
CovarianceState method of fold_inactive! under ResetCoverage. Zeroes the count, the centre and the accumulator of every row and column that touches an asset that has just gone inactive, so that a relisting starts every pair the asset belongs to cold.
Related
References
- [11]
- H. Markowitz. Modern portfolio theory. Journal of Finance 7, 77–91 (1952).