Cross-Sectional Exposure Diagnostics: private API

Functions

PortfolioOptimisers.exposure_weightsFunction
exposure_weights(B::Arr3Num, w::Nothing)
exposure_weights(B::Arr3Num, w::MatNum)

Return the cross-sectional weight history an exposure diagnostic reads, checking it against the exposure history.

Every diagnostic of this file weights the assets of one observation, and an absent weight history means equal weights. The absent case is resolved once, into a history of ones, so each kernel reads one matrix and no kernel carries a branch. A weight of zero excludes the asset from the observation, which is the same answer the history of ones gives when no asset is excluded.

Arguments

  • B: Exposure history observations × assets × factors, unlagged.
  • w: Cross-sectional weight history observations × assets, or nothing for equal weights.

Validation

  • !isempty(B).
  • size(w) == (size(B, 1), size(B, 2)), when w is present.
  • No finite entry of w is negative.

Returns

  • u::MatNum: Weight history observations × assets. It is a history of ones when w is nothing, and w itself otherwise.

Related

source
PortfolioOptimisers.cs_weighted_correlationFunction
cs_weighted_correlation(a::AbstractVector, b::AbstractVector, u::AbstractVector;
                        min_count::Integer = 3, eps::Real = 1e-12)

Return the weighted correlation of two cross-sections of one observation.

It is the kernel of every diagnostic of this file that correlates two cross-sections: the information coefficient of an exposure against the forward return, and the stability of an exposure against its own later self. An asset enters when both of its values are finite and its weight is finite and positive, so a missing value costs the pair alone and not the observation.

Mathematical definition

\[\rho = \frac{\sum_{i} u_{i} (a_{i} - \bar{a}) (b_{i} - \bar{b})}{\sqrt{\sum_{i} u_{i} (a_{i} - \bar{a})^{2}} \sqrt{\sum_{i} u_{i} (b_{i} - \bar{b})^{2}}}\]

Where:

  • $a_{i}$, $b_{i}$: The two values of asset $i$.
  • $u_{i}$: Weight of asset $i$.
  • $\bar{a} = \sum_{i} u_{i} a_{i} / \sum_{i} u_{i}$: Weighted mean of the first cross-section, and likewise $\bar{b}$.

Algorithm

  1. Count the assets that enter, and accumulate the weight sum and the two weighted means over them.
  2. Answer NaN when fewer than min_count assets enter, or when the weight sum is zero.
  3. Accumulate the weighted central moments over the same assets.
  4. Answer NaN when the denominator is at or under eps, which is a cross-section that is constant in one of the two values.

Arguments

  • a: First cross-section, one entry per asset.
  • b: Second cross-section, one entry per asset.
  • u: Weight of each asset. An entry that is not finite, or not positive, excludes the asset.
  • min_count: Least number of assets an answer needs.
  • eps: Denominator at or under which the answer is NaN.

Returns

  • rho::Real: The correlation, or NaN.

Examples

julia> PortfolioOptimisers.cs_weighted_correlation([1.0, 2.0, 3.0], [2.0, 4.0, 6.0], ones(3))1.0

Related

source
PortfolioOptimisers.cs_correlation_entersFunction
cs_correlation_enters(a::AbstractVector, b::AbstractVector, u::AbstractVector,
                      i::Integer)

Return whether one asset enters the correlation of two cross-sections.

An asset enters when both of its values are finite and its weight is finite and positive. The two passes of cs_weighted_correlation read the same rule, so it is stated once.

Arguments

  • a: First cross-section, one entry per asset.
  • b: Second cross-section, one entry per asset.
  • u: Weight of each asset.
  • i: Position of the asset.

Returns

  • enters::Bool: true when the asset enters.

Related

source
PortfolioOptimisers.cs_ordinal_ranksFunction
cs_ordinal_ranks(key::AbstractVector, valid::AbstractVector{Bool})

Return the ordinal rank of every entry of a cross-section, and NaN outside a mask.

The rank is the position the entry takes in the sorted order, so two equal values take two different ranks, in the order of the asset axis. The masked entries are sorted to the end by their key and then written as NaN, so they take no rank and shift none.

Arguments

  • key: Sort key of each asset. The key of a masked asset must be Inf, so the mask sorts to the end.
  • valid: Mask, one entry per asset. A false entry gets NaN.

Returns

  • r::VecNum: Rank of each asset, or NaN outside the mask.

Related

source
PortfolioOptimisers.cs_spearman_correlationFunction
cs_spearman_correlation(a::AbstractVector, b::AbstractVector;
                        min_count::Integer = 3, eps::Real = 1e-12)

Return the rank correlation of two cross-sections of one observation.

It is the unweighted correlation of the ordinal ranks of the two cross-sections, taken over the assets at which both values are finite. The rank measures the order of the assets and not their level, so one extreme value moves the answer no more than one ordinary value does.

Algorithm

  1. Mark the assets at which both values are finite.
  2. Rank each cross-section over those assets with cs_ordinal_ranks.
  3. Correlate the two rank vectors with cs_weighted_correlation under equal weights.

Arguments

  • a: First cross-section, one entry per asset.
  • b: Second cross-section, one entry per asset.
  • min_count: Least number of assets an answer needs.
  • eps: Denominator at or under which the answer is NaN.

Returns

  • rho::Real: The rank correlation, or NaN.

Related

source
PortfolioOptimisers.exposure_forward_mean_returnFunction
exposure_forward_mean_return(R::MatNum, horizon::Integer)

Return the forward mean asset return of every observation but the last horizon of them.

The information coefficient scores an exposure against what the asset earned after the exposure was known, and this verb builds that target. An observation whose forward window carries no finite return gets NaN, and a window that is part finite is averaged over the finite part alone.

Mathematical definition

\[y_{ti} = \frac{1}{|\mathcal{H}_{ti}|} \sum_{h \in \mathcal{H}_{ti}} r_{t+h,i}\]

Where:

  • $r_{ti}$: Return of asset $i$ at observation $t$.
  • $\mathcal{H}_{ti}$: The offsets $1 \le h \le H$ at which $r_{t+h,i}$ is finite.
  • $H$: Forward window, in observations.

Arguments

  • R: Asset return history observations × assets.
  • horizon: Forward window, in observations.

Validation

  • size(R, 1) > horizon.

Returns

  • y::MatNum: Forward mean return (observations - horizon) × assets. Row t averages the returns of observations t + 1 to t + horizon.

Related

source
PortfolioOptimisers.exposure_pair_correlationFunction
exposure_pair_correlation(B::Arr3Num, u::MatNum, k::Integer, l::Integer)

Return the time-averaged correlation of one pair of factor exposures.

The pair is correlated at each observation, and the answers are averaged over the observations at which the pair is defined. A pair that is degenerate at every observation it covers reads 0 by convention, and one that never reaches three common assets reads NaN.

Arguments

  • B: Exposure history observations × assets × factors, unlagged.
  • u: Resolved weight history observations × assets.
  • k, l: Positions of the two factors.

Returns

  • rho::Real: The time-averaged correlation, 0 by convention, or NaN.

Related

source
PortfolioOptimisers.exposure_pair_observationFunction
exposure_pair_observation(B::Arr3Num, u::MatNum, t::Integer, k::Integer, l::Integer)

Return the correlation of one pair of factor exposures at one observation, and whether the pair is degenerate there.

The pair is correlated over the assets at which both exposures are finite. It has no answer at an observation that shares fewer than three such assets, and none at one where either weighted variance falls to the tolerance of its own weighted square sum. The second case is degenerate, which the caller reads as the 0 convention, and the first is not.

Arguments

  • B: Exposure history observations × assets × factors, unlagged.
  • u: Resolved weight history observations × assets.
  • t: Position of the observation.
  • k, l: Positions of the two factors.

Returns

  • rho::Real: The correlation, or NaN.
  • degenerate::Bool: true when the pair shares three assets and one of the two cross-sections is constant over them.

Related

source
PortfolioOptimisers.exposure_pair_sumsFunction
exposure_pair_sums(B::Arr3Num, u::MatNum, t::Integer, k::Integer, l::Integer)

Return the weighted sums of one pair of factor exposures over the assets they share at one observation.

An asset enters when both of its exposures are finite, so the support is the one the pair shares and not the one either factor holds alone. The six sums are what the correlation of the pair is a function of.

Arguments

  • B: Exposure history observations × assets × factors, unlagged.
  • u: Resolved weight history observations × assets.
  • t: Position of the observation.
  • k, l: Positions of the two factors.

Returns

  • m::NamedTuple: (; nv, W, Sk, Sl, Qk, Ql, C), the count of the shared assets, the weight sum, the two weighted sums, the two weighted square sums and the weighted cross sum.

Related

source
PortfolioOptimisers.exposure_ic_factor_summaryFunction
exposure_ic_factor_summary(ic::MatNum, k::Integer)

Return the four summary numbers of one factor's information coefficient series.

The mean, the standard deviation and the hit rate read the observations at which the coefficient is defined, so an observation with no coefficient is in no denominator and the three agree on their sample; a series with no defined observation has no hit rate. The ratio has no answer where either of its two terms has none, and none where the standard deviation is zero. The t-statistic scales the ratio by the root of the number of observations that carried a coefficient, so it says whether the mean is far enough from zero to believe over the evidence there was; it inherits the ratio's absence.

It is the kernel of every summary of a per-observation correlation series, so forecast_ic_summary reads it too, and a coefficient of a factor exposure and a coefficient of a Return Forecast are summarised on the same terms.

Arguments

  • ic: Information coefficient series pairs × factors.
  • k: Position of the factor.

Returns

  • m::NamedTuple: (; mean_ic, std_ic, ic_ir, t_stat, hit_rate), five numbers.

Related

source
PortfolioOptimisers.exposure_cross_section_stdFunction
exposure_cross_section_std(B::Arr3Num, u::MatNum, t::Integer, k::Integer)

Return the weighted cross-sectional standard deviation of one factor exposure at one observation.

The weights are normalised over the assets at which the exposure is finite, so an absent exposure costs its own asset and no other. An observation at which the factor carries no weight has no answer.

Arguments

  • B: Exposure history observations × assets × factors, unlagged.
  • u: Resolved weight history observations × assets.
  • t: Position of the observation.
  • k: Position of the factor.

Returns

  • sigma::Real: The standard deviation, or NaN.

Related

source
PortfolioOptimisers.cs_diagnostic_exposuresFunction
cs_diagnostic_exposures(csfm::CrossSectionalFactorModel)
cs_diagnostic_exposures(Ms::Nothing)
cs_diagnostic_exposures(Ms::Arr3Num)

Return the unlagged exposure history an exposure diagnostic reads off a factor model block.

The exposure group reads the history as the panel wrote it, so it takes no lag and no family re-basis. The absent case is the dispatch rather than a branch, and its message names the field the caller must populate.

Arguments

  • csfm: A cross-sectional factor model block.
  • Ms: The Ms field of the block, or nothing.

Validation

  • csfm.Ms is not nothing, else an IsNothingError naming Ms is raised.

Returns

  • Ms::Arr3Num: Exposure history observations × assets × factors.

Related

source
PortfolioOptimisers.exposure_ic_dataFunction
exposure_ic_data(csfm::CrossSectionalFactorModel, reduced::Bool)

Return the exposure history, the reconstructed asset returns and the regression weights the information coefficient reads off a factor model block.

The block carries no asset returns, so they are reconstructed from the parts it does carry: the systematic return of the fit and the residual. The reconstruction is defined from observation $\ell + 1$, so the answer starts at observation $\max(1, \ell)$, whose own return row is written NaN and is never read: the information coefficient scores an exposure against the returns that follow it.

Algorithm

  1. Refuse a block that carries no exposure history, or no cross-sectional fit.
  2. Reconstruct the return of each observation as $\mathbf{B}_{t-\ell} \boldsymbol{f}_{t} + \boldsymbol{\varepsilon}_{t}$, and write NaN where the lag leaves it undefined.
  3. Map the exposures through the family re-basis when reduced, and trim all three histories to the observations from $\max(1, \ell)$.

Arguments

  • csfm: A cross-sectional factor model block.
  • reduced: Map the exposures through the family re-basis of the block.

Validation

  • csfm.Ms is not nothing, else an IsNothingError naming Ms is raised.
  • csfm.csr is not nothing, else an IsNothingError naming csr is raised.
  • size(csfm.Ms, 1) > csfm.lag, and csfm.csr.f carries the observation axis of csfm.Ms.

Returns

  • B::Arr3Num: Exposure history of the trimmed observation axis.
  • R::MatNum: Reconstructed asset returns of the same axis.
  • w::Option{<:MatNum}: Regression weights of the same axis, or nothing.

Related

source
PortfolioOptimisers.exposure_ic_exposuresFunction
exposure_ic_exposures(fcb::Nothing, Ms::Arr3Num, reduced::Bool)
exposure_ic_exposures(fcb::FactorFamilyBasis, Ms::Arr3Num, reduced::Bool)

Return the exposure history the information coefficient scores, on the raw axis or on the reduced one.

A block that carries no family re-basis has one axis, so that case returns the history unchanged whatever reduced states.

Arguments

  • fcb: The fcb field of a CrossSectionalFactorModel, or nothing.
  • Ms: Unlagged exposure history observations × assets × factors.
  • reduced: Map the history through the re-basis.

Returns

  • B::Arr3Num: The history, on the reduced axis when a re-basis is set and reduced is true.

Related

source