Cross-Sectional Factor Model: private API

Functions

PortfolioOptimisers.assert_idiosyncratic_covarianceFunction
assert_idiosyncratic_covariance(esigma::Nothing, N::Integer)
assert_idiosyncratic_covariance(esigma::VecNum, N::Integer)
assert_idiosyncratic_covariance(esigma::MatNum, N::Integer)

Check the idiosyncratic covariance of a CrossSectionalFactorModel against the asset count N.

The idiosyncratic covariance takes either of two shapes, and the shape is the dispatch rather than a branch: a vector holds the idiosyncratic variances alone, and a matrix holds the full covariance an idiosyncratic correlation threshold produces. An absent covariance is checked by the method over Nothing, so no caller writes an isnothing test.

Arguments

  • esigma: Idiosyncratic covariance, a vector of variances, a square matrix, or nothing.
  • N: Number of assets the model carries.

Validation

  • !isempty(esigma).
  • length(esigma) == N when esigma is a vector.
  • esigma is square, and size(esigma, 1) == N, when esigma is a matrix.

Returns

  • nothing.

Related

source
PortfolioOptimisers.idiosyncratic_covariance_viewFunction
idiosyncratic_covariance_view(esigma::Nothing, i)
idiosyncratic_covariance_view(esigma::VecNum, i)
idiosyncratic_covariance_view(esigma::MatNum, i)

Return a view of an idiosyncratic covariance, selecting only the assets indexed by i.

The shape is the dispatch, as it is in assert_idiosyncratic_covariance: a vector of variances is indexed once, and a full covariance matrix is indexed on both of its axes, which keeps the selected block square.

Arguments

  • esigma: Idiosyncratic covariance, a vector of variances, a square matrix, or nothing.
  • i: Indices of the assets to select.

Returns

  • esigma::Option{<:VecNum_MatNum}: A view over the selected assets, or nothing when the model carries no idiosyncratic covariance.

Examples

julia> PortfolioOptimisers.idiosyncratic_covariance_view([1.0, 2.0, 3.0], [1, 3])2-element view(::Vector{Float64}, [1, 3]) with eltype Float64: 1.0 3.0julia> PortfolioOptimisers.idiosyncratic_covariance_view([1.0 0.0; 0.0 2.0], [2])1×1 view(::Matrix{Float64}, [2], [2]) with eltype Float64: 2.0

Related

source
PortfolioOptimisers.idiosyncratic_variancesMethod
idiosyncratic_variances(rr::AbstractLoadingsRegressionResult)
idiosyncratic_variances(esigma::VecNum, rr::AbstractLoadingsRegressionResult)
idiosyncratic_variances(esigma::MatNum, rr::AbstractLoadingsRegressionResult)
idiosyncratic_variances(esigma::Nothing, rr::Regression)
idiosyncratic_variances(esigma::Nothing, rr::CrossSectionalFactorModel)

Read the idiosyncratic variance vector off a loadings block, whatever shape the block stores it in.

Both members of AbstractLoadingsRegressionResult carry esigma under one name, and both admit the two shapes: a vector of variances, or a full covariance. A consumer that needs the variances alone — an AbstractUncertaintySetEstimator that weights the cross-section by the inverse idiosyncratic variance is the first one — asks for them here rather than testing the shape at its own site.

The shape is the dispatch, as it is in assert_idiosyncratic_covariance and idiosyncratic_covariance_view. The one-argument entry reads the field and forwards it beside the block, so the two refusals name the block they came from: a Regression is filled by the prior that lifts the factor moments, so its message names rsd, and a CrossSectionalFactorModel is filled by its own fit, so its message names the field.

There is no fallback. A block that carries no idiosyncratic covariance cannot answer, and an answer of ones or of zeros is a different weighting rather than a missing one.

Arguments

  • esigma: Idiosyncratic covariance, a vector of variances, a square matrix, or nothing.
  • rr: The loadings block the field was read from, which the raise reports.

Validation

  • !isnothing(esigma), raising an IsNothingError.

Returns

  • esigma::VecNum: The idiosyncratic variances, one per asset. A vector comes back unchanged, and a matrix comes back as its diagonal.

Examples

julia> re = Regression(; M = [1.0 2.0; 3.0 4.0], esigma = [0.1, 0.2]);julia> PortfolioOptimisers.idiosyncratic_variances(re)2-element Vector{Float64}: 0.1 0.2

Related

source
PortfolioOptimisers.cs_history_assetsFunction
cs_history_assets(A::Nothing, N::Integer, sym::Symbol)
cs_history_assets(A::MatNum, N::Integer, sym::Symbol)

Check the asset axis of an optional per-asset history of a CrossSectionalFactorModel, and return its observation count.

A per-asset history holds one row per observation and one column per asset, so the asset axis is the second one. The count comes back so that the caller pins the observation axis of two histories against each other with assert_cs_history_obs and needs no isnothing test of its own.

Arguments

  • A: A per-asset history, or nothing.
  • N: Number of assets the model carries.
  • sym: Name of the field, which the raise reports.

Validation

  • !isempty(A).
  • size(A, 2) == N.

Returns

  • T::Option{<:Integer}: The observation count of A, or nothing when A is absent.

Related

source
PortfolioOptimisers.assert_cs_history_obsFunction
assert_cs_history_obs(a::Option{<:Integer}, b::Option{<:Integer}, asym::Symbol, bsym::Symbol)
assert_cs_history_obs(a::Integer, b::Integer, asym::Symbol, bsym::Symbol)

Check that two per-asset histories of a CrossSectionalFactorModel agree on the observation axis.

A history that the model does not carry constrains nothing, so the pair is checked only when cs_history_assets returned a count for both.

Arguments

  • a: Observation count of the first history, or nothing.
  • b: Observation count of the second history, or nothing.
  • asym: Name of the first field, which the raise reports.
  • bsym: Name of the second field, which the raise reports.

Validation

  • a == b, when both counts are present.

Returns

  • nothing.

Related

source
PortfolioOptimisers.assert_exposure_historyFunction
assert_exposure_history(Ms::Nothing, N::Integer, K::Integer)
assert_exposure_history(Ms::Arr3Num, N::Integer, K::Integer)

Check the exposure history of a CrossSectionalFactorModel against the asset count N and the factor count K.

The exposure history holds one slice per observation, and a slice has the shape of the loadings matrix, so its second and third axes are the asset axis and the factor axis.

Arguments

  • Ms: An exposure history observations × assets × factors, or nothing.
  • N: Number of assets the model carries.
  • K: Number of raw factors the model carries.

Validation

  • !isempty(Ms).
  • size(Ms, 2) == N.
  • size(Ms, 3) == K.

Returns

  • nothing.

Related

source
PortfolioOptimisers.assert_cs_regression_assetsFunction
assert_cs_regression_assets(csr::Nothing, N::Integer)
assert_cs_regression_assets(csr::CrossSectionalRegression, N::Integer)

Check the asset axis of the fit a CrossSectionalFactorModel nests against the asset count N.

The residuals of a CrossSectionalRegression hold one row per observation and one column per asset, so the asset axis is the second one, as it is for every per-asset history of the model.

Arguments

  • csr: The nested cross-sectional regression result, or nothing.
  • N: Number of assets the model carries.

Validation

  • size(csr.eps, 2) == N.

Returns

  • nothing.

Related

source
PortfolioOptimisers.assert_return_forecast_assetsFunction
assert_return_forecast_assets(rf::Nothing, N::Integer)
assert_return_forecast_assets(rf::AbstractReturnForecastResult, N::Integer)

Check the Return Forecast of a CrossSectionalFactorModel against the asset count N.

A model carries the Return Forecast its prior fitted, or nothing, and the absent case is the method over Nothing. The check reads the two fields every member of the family answers, mu and hist, and never the member's own fields, so a new member needs no new method here.

Arguments

  • rf: Return Forecast Result, or nothing.
  • N: Number of assets the model carries.

Validation

Returns

  • nothing.

Related

source
PortfolioOptimisers.has_family_rebasisMethod
has_family_rebasis(csfm::CrossSectionalFactorModel) -> Bool

State whether the model was fitted in a re-based factor family.

L and fcb are present together and absent together, and the pair is present exactly when a Factor Family was re-based before the fit. The answer is therefore the presence of L, read with getfield rather than through property access: the swap(L, M) rule of CrossSectionalFactorModel makes csfm.L return csfm.M when L is unset, so a property read would answer true for every model.

Arguments

  • csfm: A cross-sectional factor model result.

Returns

  • val::Bool: true when L is set, so the raw factor axis of M is a linear image of the re-based one and a factor covariance stated on it is singular; false when the fit ran in the raw basis.

Examples

julia> PortfolioOptimisers.has_family_rebasis(CrossSectionalFactorModel(; M = [1.0 2.0; 3.0 4.0],                                                                        b = [0.1, 0.2]))falsejulia> fcb = FactorFamilyBasis(; fnm = ["f"], fi = [[1, 2]], di = [2],                               ratios = reshape([1.0], 1, 1), K = 2);julia> PortfolioOptimisers.has_family_rebasis(CrossSectionalFactorModel(; M = [1.0 2.0; 3.0 4.0],                                                                        L = reshape([1.0, 2.0], 2,                                                                                    1),                                                                        b = [0.1, 0.2], fcb = fcb))true

Related

source