skfolio.attribution.rolling_realized_factor_attribution#
- skfolio.attribution.rolling_realized_factor_attribution(*, observations, window_size=60, step=21, asset_names, factor_names, factor_families=None, weights, factor_returns, portfolio_returns, exposures, exposure_lag=1, idio_returns, idio_variances=None, regression_weights=None, family_constraint_basis=None, annualization_factor=252.0, compute_asset_breakdowns=True, compute_asset_factor_contribs=False, compute_uncertainty=False)[source]#
Compute rolling realized (ex-post) factor volatility and return attribution.
This function computes
realized_factor_attributionover rolling windows, returning anAttributionobject where all numeric fields are arrays with an additional leading dimension corresponding to the number of windows.- Parameters:
- observationsarray-like of shape (n_observations,)
Observation labels (e.g., dates) corresponding to each row of the input data. The output
Attribution.observationswill contain the labels for the last observation of each window.- window_sizeint, default=60
Number of observations in each rolling window.
- stepint, default=21
Number of observations to advance between consecutive windows. The default of 21 produces approximately monthly output for daily data. Use
step=1for fully overlapping windows (daily updates), orstep=window_sizefor non-overlapping windows.- asset_namesarray-like of shape (n_assets,)
Names for each asset (e.g., [“AAPL”, “GOOGL”, “MSFT”]).
- factor_namesarray-like of shape (n_factors,)
Names for each factor (e.g., [“Momentum”, “Value”, “Size”]).
- factor_familiesarray-like of shape (n_factors,), optional
Family/category for each factor (e.g., “Style”, “Industry”). If provided, enables family-level aggregation in the output.
- weightsarray-like of shape (n_assets,) or (n_observations, n_assets)
Portfolio weights. If 1D, the same weights are used for all observations (static). If 2D, time-varying weights are used.
- factor_returnsarray-like of shape (n_observations, n_factors)
Factor return time series.
- portfolio_returnsarray-like of shape (n_observations,)
Portfolio return time series.
- exposuresarray-like of shape (n_assets, n_factors) or (n_observations, n_assets, n_factors)
Asset-by-factor exposure (loading) values. If 2D, this is the static loading matrix used for all observations. If 3D, this is a time series of loading matrices following the as-of time-indexing convention (the function applies
exposure_laginternally and trims the returns and weights series accordingly).- exposure_lagint, default=1
Lag applied to time-varying exposures under the as-of time-indexing convention. The default value of
1aligns exposures at \(t-1\) with returns over \((t-1, t]\). Only affects 3D (time-varying) exposures.- idio_returnsarray-like of shape (n_observations, n_assets)
Idiosyncratic returns from the factor model regression. These are the residuals \(\varepsilon_{i,t}\) from the cross-sectional regression.
- idio_variancesarray-like of shape (n_observations, n_assets) or None, optional
Per-asset idiosyncratic (specific) variances \(\sigma^2_{\varepsilon,i,t}\). Required when
compute_uncertainty=True. NaN values are allowed and exclude the corresponding asset-observation pair from the uncertainty estimate.- regression_weightsarray-like of shape (n_observations, n_assets) or None, optional
Per-asset cross-sectional regression weights \(q_{i,t}\) used when estimating factor returns. Required when
compute_uncertainty=True. Must not contain NaN.- family_constraint_basisFamilyConstraintBasis or None, optional
When provided, the uncertainty estimator is computed in the reduced (full-rank) basis defined by the family-constraint change of coordinates. This avoids the singular Gram matrix that arises from collinear constrained families and produces well-conditioned standard errors. Only used when
compute_uncertainty=True.- annualization_factorfloat, default=252.0
Used to annualize expected returns, variances and volatilities. Use 1.0 to disable annualization. Common values: 252 for daily data, 12 for monthly data.
- compute_asset_breakdownsbool, default=True
If True, compute asset-level attribution for each window. Results in 2D arrays of shape
(n_windows, n_assets)in AssetBreakdown. Set to False to skip asset attribution for faster computation.- compute_asset_factor_contribsbool, default=False
If True, compute asset-by-factor contributions for each window. Results in 3D arrays of shape
(n_windows, n_assets, n_factors). Disabled by default for faster computation.- compute_uncertaintybool, default=False
If
True, compute per-window attribution uncertainty (standard errors on the factor/idiosyncratic return split). Requires bothregression_weightsandidio_variances; raisesValueErrorif either is missing. IfFalse(default), uncertainty is not computed.
- Returns:
- attributionAttribution
The
Attributiondataclass with rolling results. All numeric fields inComponentare 1D arrays of shape(n_windows,). All numeric fields inBreakdownare 2D arrays of shape(n_windows, n_factors)or(n_windows, n_families). Ifcompute_asset_breakdowns=True, asset attribution has shape(n_windows, n_assets). Theobservationsfield contains the window end labels.
See also
realized_factor_attributionSingle-point realized factor attribution.
Examples
>>> import numpy as np >>> import pandas as pd >>> from skfolio.attribution import rolling_realized_factor_attribution >>> # Simulated daily returns: 10 assets, 3 factors. >>> rng = np.random.default_rng(0) >>> factor_returns = rng.standard_normal((252, 3)) * 0.01 >>> loading_matrix = rng.uniform(0.5, 1.5, size=(10, 3)) >>> weights = np.full(10, 1.0 / 10) >>> residuals = rng.standard_normal((252, 10)) * 0.005 >>> asset_returns = factor_returns @ loading_matrix.T + residuals >>> portfolio_returns = asset_returns @ weights >>> asset_names = [f"Asset_{i}" for i in range(10)] >>> factor_names = ["Momentum", "Value", "Size"]
Use 60 business days per window, advancing by 21 (approximately monthly):
>>> dates = pd.bdate_range("2023-01-01", periods=252) >>> attribution = rolling_realized_factor_attribution( ... factor_returns=factor_returns, ... portfolio_returns=portfolio_returns, ... exposures=loading_matrix, ... weights=weights, ... idio_returns=residuals, ... asset_names=asset_names, ... factor_names=factor_names, ... observations=dates, ... window_size=60, ... step=21, ... ) >>> print(f"Number of windows: {len(attribution.observations)}") Number of windows: 10
Annualized volatility in each window:
>>> print(attribution.total.vol) [0.26478682 0.28232785 0.28856211 0.27556505 0.2311759 0.23023946 0.22712558 0.26810224 0.26441032 0.28657536]
Inspect the first two windows as a numeric DataFrame:
>>> df = attribution.factors_df(formatted=False) >>> print(df.head(6)) # Index: window end date, factor Exposure Mean ... Correlation with Portfolio Observation Factor ... 2023-03-24 Momentum 0.898797 ... 0.687795 Value 1.054506 ... 0.572425 Size 0.912125 ... 0.564114 2023-04-24 Momentum 0.898797 ... 0.545547 Value 1.054506 ... 0.689650 Size 0.912125 ... 0.561379 [6 rows x 8 columns]