backtester/portfolio/calc/attribution.py attribution.py:
OLS factor exposure and contribution decomposition.
This file provides a compact factor attribution layer: estimate exposures by OLS, extract alpha, multiply factor returns by exposures and aggregate excess-return contributions.
Import
from backtester.portfolio.calc import attribution When To Read This
- 01You want to show whether returns came from factor exposure or residual alpha.
- 02You are connecting factor datasets from the API to local strategy reports.
- 03You need a simple OLS attribution baseline before adding richer risk models.
File Anatomy
- OLS exposure estimation through pseudo-inverse, robust to collinearity in small examples.
- Optional intercept renamed to alpha.
- Factor attribution by matrix multiplication: factor returns dot transposed exposures.
- Performance attribution by summing excess-return contribution columns into Total.
Data Contract
Inputs
- returns: DataFrame dates x instruments or strategies.
- factor_returns: DataFrame dates x factor names.
- factor_exposures: DataFrame instruments x factor names for attribution.
Outputs
- Exposure DataFrame indexed by instrument, columns are alpha and factor names.
- Alpha Series when intercept is present.
- Contribution DataFrames by date and instrument/strategy.
Invariants
- Returns and factor returns are inner-aligned by date before regression.
- Factor names must overlap between factor_returns and factor_exposures.
- This is a linear attribution baseline, not a full risk model.
Public API And Key Internals
compute_factor_exposures_ols
functioncompute_factor_exposures_ols(returns, factor_returns, *, add_intercept=True) Fits factor coefficients per instrument using OLS and pseudo-inverse.
Returns
pd.DataFrame indexed by instrument.
compute_factor_alpha
functioncompute_factor_alpha(returns, factor_returns, *, add_intercept=True) Returns the intercept column from OLS exposures, or zero if no intercept is used.
Returns
pd.Series.
compute_factor_attribution
functioncompute_factor_attribution(factor_returns, factor_exposures) Converts factor returns and exposures into contribution time series.
Returns
pd.DataFrame.
compute_performance_attribution
functioncompute_performance_attribution(excess_returns) Adds a Total column to excess return contribution components.
Returns
pd.DataFrame.
Implementation Notes
- The pseudo-inverse keeps the function usable in small examples where factors may be correlated.
- The function is intentionally transparent; users can replace it with a more sophisticated risk model later.
- The attribution output is suitable for charts, tables and report appendix sections.
Code Walkthrough
Estimate factor exposures and contribution paths
This is the minimal factor workflow: estimate exposures, read alpha, then attribute factor returns.
attribution_usage.py Python
from backtester.portfolio.calc import attribution
exposures_ols = attribution.compute_factor_exposures_ols(
returns=strategy_returns,
factor_returns=factor_returns,
)
alpha = attribution.compute_factor_alpha(
returns=strategy_returns,
factor_returns=factor_returns,
)
contributions = attribution.compute_factor_attribution(
factor_returns=factor_returns,
factor_exposures=exposures_ols.drop(columns=["alpha"], errors="ignore"),
) Key implementation: OLS with pseudo-inverse
The function estimates one coefficient vector per instrument after date alignment.
backtester/portfolio/calc/attribution.py Python
ret_al, fac_al = returns.align(factor_returns, join="inner", axis=0)
X = fac_al.copy()
if add_intercept:
X = pd.concat([pd.Series(1.0, index=X.index, name="const"), X], axis=1)
XtX = X.T.dot(X)
XtX_inv = np.linalg.pinv(XtX.values)
Xt = X.T.values
coefs = {}
for inst in ret_al.columns:
y = ret_al[inst].values
beta = XtX_inv.dot(Xt.dot(y))
coefs[inst] = beta