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
>>> from skfolio.attribution import rolling_realized_factor_attribution >>> import numpy as np >>> import pandas as pd >>> >>> # Rolling attribution with 60-day windows, advancing 21 days (monthly) >>> dates = pd.bdate_range("2023-01-01", periods=252) >>> attribution = rolling_realized_factor_attribution( ... factor_returns=factor_returns, # (252, 3) ... portfolio_returns=portfolio_returns, # (252,) ... exposures=loading_matrix, # (10, 3) ... weights=weights, # (10,) ... idio_returns=residuals, # (252, 10) ... factor_names=["Momentum", "Value", "Size"], ... observations=dates, ... window_size=60, ... step=21, ... ) >>> print(f"Number of windows: {len(attribution.observations)}") >>> print(f"Total vol over time: {attribution.total.vol}") >>> >>> # Get MultiIndex DataFrame of factor attribution over time >>> df = attribution.factors_df(formatted=False) >>> print(df.head())