Regime switches

Types

PortfolioOptimisers.ExactDebias — Type
struct ExactDebias <: AbstractRegimeDebias

Divides the regime statistic by the exact bias that its method reads in the inverse of an estimated variance. This is the default debias rule.

The inverse of an estimated variance is too large on average (Jensen's inequality), so a raw statistic has a mean above the one the method assumes. Each method reads its own moment of the ratio $Q = \hat{v} / \sigma^2$: the root mean square reads $\mathbb{E}[1 / Q]$, the first moment reads $\mathbb{E}[Q^{-1/2}]^2$ and the log reads $\exp(-\mathbb{E}[\ln Q])$. regime_bias_table computes each one exactly from the law of the estimate. The statistic skips an estimate too young for its variance to be finite.

Under EstimatedCentring the deviation that the statistic reads shares its location with the terms of the estimate, so the two are not independent, and each method reads the moment of the deviation and the estimate together: $\mathbb{E}[u^{2} / Q]$ for the root mean square, and so on. This rule reads that dependence for every method, for the scalar estimator and the diagonal target on every path, and for the portfolio and the Mahalanobis targets on one decay. On the separate correlation path the portfolio and the Mahalanobis targets read the pre-centred law at cor_decay. LawDebias reads the law of the estimate alone.

Constructors

ExactDebias() -> ExactDebias

Examples

julia> RegimeAdjustedExpWeightedVariance().debiasExactDebias()

Related

source
PortfolioOptimisers.LawDebias — Type
struct LawDebias <: AbstractRegimeDebias

Divides the regime statistic by the bias of the law of the estimate alone, without the dependence of the deviation on the estimate.

The factor of each method is the one of ExactDebias with the deviation taken as independent of the estimate. Under PreCentred the two rules agree. Under EstimatedCentring the deviation shares its location with the terms of the estimate, and this rule omits that part: on the scalar estimator the root mean square reads 0.2 % low at a half-life of 10 and two lags, and on the Mahalanobis target at 5 to 12 assets the three methods read 0.2 % to 0.7 % high without HAC and 0.4 % to 1.4 % high with it. The Mahalanobis nodes cost about half of those of ExactDebias for the first moment and the log, and a third for the root mean square.

Constructors

LawDebias() -> LawDebias

Examples

julia> RegimeAdjustedExpWeightedCovariance(; debias = LawDebias()).debiasLawDebias()

Related

source
PortfolioOptimisers.RawStatistic — Type
struct RawStatistic <: AbstractRegimeDebias

Scores the raw regime statistic, with no correction of the bias of the estimated variance it reads.

Every observation above min_obs is scored. The mean of the statistic is above the one the method assumes, by 7 % at a half-life of 10 and 1.7 % at a half-life of 40 for the root mean square, so the regime multiplier is too large on average.

Constructors

RawStatistic() -> RawStatistic

Examples

julia> RegimeAdjustedExpWeightedVariance(; debias = RawStatistic()).debiasRawStatistic()

Related

source
PortfolioOptimisers.NoHacFloor — Type
struct NoHacFloor <: AbstractHacFloor

Lets a HAC-adjusted product enter the variance recursion as it is, negative or not. This is the default floor rule.

A HAC term adds the lagged products of the deviations to the square, so it can be negative. Under returns with no autocorrelation each term has the mean of the variance, and the recursion is an unbiased quadratic form. Only the variance that the estimator returns is floored at zero.

Constructors

NoHacFloor() -> NoHacFloor

Examples

julia> RegimeAdjustedExpWeightedVariance().hac_floorNoHacFloor()

Related

source
PortfolioOptimisers.PerTermHacFloor — Type
struct PerTermHacFloor <: AbstractHacFloor

Floors each HAC-adjusted square at zero before it enters the variance recursion.

The floor removes the negative part of each term, so the mean of a term is above the variance. Under returns with no autocorrelation the variance is 6.7 %, 16 % and 41 % too large at one, two and five lags.

Constructors

PerTermHacFloor() -> PerTermHacFloor

Examples

julia> RegimeAdjustedExpWeightedVariance(; hac_floor = PerTermHacFloor()).hac_floorPerTermHacFloor()

Related

source
PortfolioOptimisers.VolatilityBeforeUpdate — Type
struct VolatilityBeforeUpdate <: AbstractHacVolatilityTiming

Divides each row of the separate correlation path under HAC by the volatility before the variance takes the row. This is the default rule.

Under HAC the diagonal of a row can be negative. A volatility that already holds the row damps a positive row and amplifies a negative one, so the diagonal of the correlation state is skewed down and can fall to zero. The volatility before the step is independent of the row. At two lags the rule after the step has 9 % to 19 % more mean square error.

Constructors

VolatilityBeforeUpdate() -> VolatilityBeforeUpdate

Examples

julia> RegimeAdjustedExpWeightedCovariance().hac_vol_beforeVolatilityBeforeUpdate()

Related

source
PortfolioOptimisers.VolatilityAfterUpdate — Type
struct VolatilityAfterUpdate <: AbstractHacVolatilityTiming

Divides each row of the separate correlation path under HAC by the volatility after the variance takes the row.

This is the rule of the path without HAC, where it is the better one. Under HAC it skews the diagonal of the correlation state down.

Constructors

VolatilityAfterUpdate() -> VolatilityAfterUpdate

Examples

julia> RegimeAdjustedExpWeightedCovariance(; hac_vol_before = VolatilityAfterUpdate()).hac_vol_beforeVolatilityAfterUpdate()

Related

source
PortfolioOptimisers.BiasCorrectedVolatility — Type
struct BiasCorrectedVolatility <: AbstractCorrelationVolatility

Standardises each product of the separate correlation path by the bias-corrected variance. This is the default rule.

The variance state is seeded at zero, so after $K$ steps it holds $1 - \lambda^{K}$ times the variance. A product divided by the raw state thus carries the weight $1 / (1 - \lambda^{K})$, which falls with $K$. The correlation cancels a scale that is the same for every product, but not this one, so in the warm-up the first products weigh more than the weights that the bias tables of PortfolioTarget assume. This rule divides the state by $1 - \lambda^{K}$ first. At 4 uncorrelated assets and 20 observations it moved the factor of PortfolioTarget from 3.5 % high to 0.05 %, and at 12 assets with a correlation of 0.5 from 8.0 % to 1.7 %. The steady state does not change.

Constructors

BiasCorrectedVolatility() -> BiasCorrectedVolatility

Examples

julia> RegimeAdjustedExpWeightedCovariance().cor_volBiasCorrectedVolatility()

Related

source
PortfolioOptimisers.RawVolatility — Type
struct RawVolatility <: AbstractCorrelationVolatility

Standardises each product of the separate correlation path by the raw variance state, seeded at zero.

This is the rule of the oracle. In the warm-up it weighs the first products of the correlation more than the bias tables of PortfolioTarget assume. The steady state is the same as under BiasCorrectedVolatility.

Constructors

RawVolatility() -> RawVolatility

Examples

julia> RegimeAdjustedExpWeightedCovariance(; cor_vol = RawVolatility()).cor_volRawVolatility()

Related

source
PortfolioOptimisers.RefuseIndefiniteWarmUp — Type
struct RefuseIndefiniteWarmUp <: AbstractIndefiniteBlockRule

Refuses a covariance block with a negative eigenvalue while the block is young, and scores it once the block is old. This is the default rule of PortfolioTarget.

The block is young while the gate that MahalanobisTarget reads under ExactDebias is closed for the same assets: regime_bias_open under ExactDebias() at the count of the youngest asset, under every debias of the estimator, on the decay of the correlation structure, with the HAC lags of the estimator. Under HAC that gate asks that the effective count of observations of the banded weight matrix pass $n + 1$, and in the warm-up before it a HAC block need not be positive definite. A block of assets with different histories need not be positive definite at any count, and once the gate is open this rule scores it, so the steady state of a panel with a holiday or a late listing does not move. An estimate whose steady state never opens the gate, such as 12 assets at a half-life of 5 under HAC, is young at every count.

On a case of iid Normal returns at 8 assets, an equicorrelation of 0.5, a half-life of 10, two lags and 30 seeds of 8 rows, the largest regime state of the inverse-volatility direction was 51.4 on the separate path and 14.8 on one decay where the block was scored as it is. On one decay the young blocks of those rows are singular but have no negative eigenvalue, so this rule scores them, and the state stays at 14.8. The separate path refuses every one of those first rows, and it scores its first row at row 14 in the median and at row 21 at most, where its gate opens at 20.

Constructors

RefuseIndefiniteWarmUp() -> RefuseIndefiniteWarmUp

Examples

julia> PortfolioTarget().indefiniteRefuseIndefiniteWarmUp()

Related

source
PortfolioOptimisers.ClipIndefiniteBlock — Type
struct ClipIndefiniteBlock <: AbstractIndefiniteBlockRule

Clips the eigenvalues of the correlation of a covariance block at zero, keeps the variances, and scores the direction on the clipped block, at every count.

The clip is the one that regime_adjusted_covariance applies to the report, restore_psd!. A block that has a Cholesky factor is kept as it is, so the rule costs one Cholesky factor per row, and one eigendecomposition only where the block is not positive definite. It moves the statistic of a panel with a holiday or a late listing in the steady state too, where the block need not be positive semidefinite. On the case of iid Normal returns above, the largest regime state of the inverse-volatility direction after 8 rows fell from 51.4 to 24.3 on the separate path, and on one decay it stayed at 14.8.

Constructors

ClipIndefiniteBlock() -> ClipIndefiniteBlock

Examples

julia> PortfolioTarget(; indefinite = ClipIndefiniteBlock()).indefiniteClipIndefiniteBlock()

Related

source
PortfolioOptimisers.ScoreIndefiniteBlock — Type
struct ScoreIndefiniteBlock <: AbstractIndefiniteBlockRule

Scores a covariance block as it is, positive definite or not.

A direction whose variance is at or below min_val is still refused, as regime_statistic states. A direction with a small positive variance on an indefinite block scores a large statistic, which the clip regime_lohi_mult of the estimator bounds where it is set.

Constructors

ScoreIndefiniteBlock() -> ScoreIndefiniteBlock

Examples

julia> PortfolioTarget(; indefinite = ScoreIndefiniteBlock()).indefiniteScoreIndefiniteBlock()

Related

source

Functions

PortfolioOptimisers.correlation_variance — Function
correlation_variance(::BiasCorrectedVolatility, v::VecNum, decay::Number, K::AbstractVector{<:Integer})
correlation_variance(::RawVolatility, v::VecNum, decay::Number, K::AbstractVector{<:Integer})

The variance that standardises each product of the separate correlation path.

Arguments

  • v: The variance state that the product divides by.
  • decay: The decay of the variance.
  • K: The count of the steps that each entry of v holds.

Returns

Related

source
PortfolioOptimisers.indefinite_block — Function
indefinite_block(::RefuseIndefiniteWarmUp, C::MatNum, young::Bool)
indefinite_block(::ClipIndefiniteBlock, C::MatNum, young::Bool)
indefinite_block(::ScoreIndefiniteBlock, C::MatNum, young::Bool)

The covariance block that the portfolio regime statistic reads.

Arguments

  • C: The bias-corrected covariance block of the assets that contribute to the statistic.
  • young: Whether the block is young, that is whether the gate that MahalanobisTarget reads under ExactDebias() is closed for the same assets.

Returns

  • block::Option{<:MatNum}: nothing under RefuseIndefiniteWarmUp where the block is young and has an eigenvalue below $-n \epsilon$ times the largest in size, and C else. A singular block, such as a block of fewer observations than assets, is kept: each direction reads a true variance on it. A copy of C with the clip of restore_psd! under ClipIndefiniteBlock. C under ScoreIndefiniteBlock.

Related

source