quantlab.risk.attribution.factor_attribution#

quantlab.risk.attribution.factor_attribution(holdings, nav_returns, symbol_returns, risk_model)[source]#

Split each bar’s NAV return and risk over risk_model’s factors (see the module docstring).

Parameters:
holdingsxr.DataArray

On (timestamp, symbol): row t holds what the book held at the close of the bar before t, as signed fractions of that bar’s NAV. The first row has no bar before it in the window, so its holdings are not attributed (its return is all trading).

nav_returnsxr.DataArray

The book’s return of each bar, on timestamp.

symbol_returnsxr.DataArray

Each symbol’s own return of each bar, on (timestamp, symbol), for the uncovered term; NaN counts as 0.

risk_modelFactorRiskModel

The model whose regression and estimate stores, exposures and risk-free rate are read over the window; its stores must cover it (the estimate store up to the bar before the last).

Returns:
xr.Dataset

On the holdings’ timestamp, with the factor axis carrying each factor’s group (FactorRiskModel.factor_groups) and display label (FactorRiskModel.factor_labels):

  • contribution on (timestamp, term) (TERMS) and log_contribution (each bar’s terms times ln(1+r)/r), factor_contribution and factor_log_contribution on (timestamp, factor), return (the NAV return);

  • exposure on (timestamp, factor): the covered holdings’ net exposures (the exposures of t-1);

  • ex_ante_factor_variance and ex_ante_specific_variance: the one-bar forecast variances of the covered book; and factor_risk_contribution on (timestamp, factor) and specific_risk_contribution: the one-bar x-sigma-rho contributions, NaN where the forecast volatility is 0;

  • covered_weight (the covered share of the gross held weight, NaN on a bar holding nothing) and gross_weight.

Raises:
ValueError

If the regression or estimate store does not cover the window (build or extend it first), or symbols were held but none was ever covered (the book’s and the risk model’s symbol axes may differ).

Examples

>>> attribution = factor_attribution(holdings, nav_returns, symbol_returns, model)
>>> attribution["factor_contribution"].dims
('timestamp', 'factor')
>>> sigma = np.sqrt(
...     attribution["ex_ante_factor_variance"] + attribution["ex_ante_specific_variance"]
... )
>>> parts = attribution["factor_risk_contribution"].sum("factor")
>>> bool(np.allclose((parts + attribution["specific_risk_contribution"])[1:], sigma[1:]))
True