quantlab.risk.base.FactorRiskForecast#

class quantlab.risk.base.FactorRiskForecast(symbols, factor_names, exposures, factor_covariance, specific_variance)[source]#

Bases: object

A factor risk model’s forecast at one bar: the covariance of the next bar’s returns.

B F B' + diag(D) over the n symbols the forecast covers, with k factors: B holds each symbol’s exposures, F the factor covariance and D each symbol’s specific variance, all of one-bar returns. FactorRiskModel.forecast builds it, and decides which symbols and factors it covers, for every user: the portfolio’s covariance estimator (an optimiser prices risk as |F^(1/2) B' w|^2 + w' diag(D) w from factor_form(), never building the dense matrix), factor attribution and the bias statistics. A book’s risk is read with exposure, portfolio_variance and risk_contributions, its weights on symbols.

Attributes:
symbolsnp.ndarray

The n symbols covered, the order of exposures’ rows and specific_variance.

factor_namestuple of str

The k factors, the columns of exposures: the model’s factor_names that have a covariance at the bar.

exposuresnp.ndarray

B, [n, k].

factor_covariancenp.ndarray

F, [k, k].

specific_variancenp.ndarray

D, [n].

Examples

>>> forecast = FactorRiskForecast(
...     symbols=np.array(["AAA", "BBB"]),
...     factor_names=("market",),
...     exposures=np.array([[1.0], [0.5]]),
...     factor_covariance=np.array([[0.04]]),
...     specific_variance=np.array([0.01, 0.02]),
... )
>>> forecast.covariance
array([[0.05, 0.02],
       [0.02, 0.03]])
>>> forecast.variance
array([0.05, 0.03])
property covariance#

The dense [n, n] covariance B F B' + diag(D).

Examples

>>> forecast.covariance.shape
(2, 2)
exposure(weights)[source]#

Return a book’s net exposures w B, [..., k].

Parameters:
weightsnp.ndarray

[..., n]: one book, or several, on symbols.

Examples

>>> forecast.exposure(np.array([0.5, 0.5]))
array([0.75])
factor_exposure(name, symbols)[source]#

Return each of symbols’ exposure to factor name: its column of B.

The forecast’s coverage rule decides it: NaN for a symbol the forecast does not cover, and 0 on every covered symbol when name is not one of factor_names (a factor of the model without a covariance at the bar, which no covered symbol is exposed to).

Parameters:
namestr

A factor of the model.

symbolsnp.ndarray

The symbols to answer for, in any order; ones outside symbols of the forecast included.

Returns:
np.ndarray

[len(symbols)] floats.

Examples

>>> forecast.factor_exposure("market", np.array(["BBB", "ZZZ"]))
array([0.5, nan])
>>> forecast.factor_exposure("value", np.array(["AAA"]))  # not carried at the bar
array([0.])
factor_form()[source]#

Return (exposures, factor_covariance, specific_variance).

Examples

>>> exposures, factor_covariance, specific = forecast.factor_form()
>>> specific
array([0.01, 0.02])
portfolio_variance(weights)[source]#

Return a book’s factor variance x' F x and specific variance sum w^2 D.

Parameters:
weightsnp.ndarray

[..., n]: one book, or several, on symbols.

Returns:
factor, specificnp.ndarray

[...] each; their sum is w' (B F B' + diag(D)) w.

Examples

>>> factor, specific = forecast.portfolio_variance(np.array([0.5, 0.5]))
>>> float(factor), float(specific)
(0.0225, 0.0075)
risk_contributions(weights)[source]#

Return each factor’s and the specific part’s contribution to one book’s volatility.

The x-sigma-rho split: factor j contributes x_j (F x)_j / sigma and the specific part sum w^2 D / sigma, which sum to sigma. NaN for a book without risk.

Parameters:
weightsnp.ndarray

[n]: one book on symbols.

Returns:
factornp.ndarray

[k].

specificfloat

Examples

>>> factor, specific = forecast.risk_contributions(np.array([0.5, 0.5]))
>>> round(float(factor.sum() + specific), 4)
0.1732
scaled(factor)[source]#

Return the forecast with F and D multiplied by factor.

Variance is linear in time, so a one-bar forecast times h is the forecast of h-bar returns.

Examples

>>> forecast.scaled(5).variance
array([0.25, 0.15])
subset(rows)[source]#

Return the forecast over the symbols at positions rows, in that order.

Examples

>>> forecast.subset(np.array([1])).variance
array([0.03])
property variance#

Each symbol’s variance, diag(B F B') + D, without the dense matrix.

Examples

>>> forecast.variance
array([0.05, 0.03])