pybrinson#
Brinson performance attribution in Python. Zero dependencies. Every formula cited. Math core held to 100% branch coverage in CI.
What it is#
A Python library that decomposes a portfolio's excess return versus a benchmark into allocation, selection, interaction and — for multi-currency portfolios — currency effects, using the Brinson family of models. Every formula ships with its mathematical statement, its primary academic citation, and a clickable URL to the source. You can audit the math without leaving your editor.
Who it is for#
- Performance analysts who want a typed, tested, citable Python
alternative to R's
papackage or MATLAB'sbrinsonAttribution. - Quant developers who need to plug attribution into a Python pipeline without dragging pandas / numpy into a service that did not need them.
- Portfolio managers who want to verify what their PMS or vendor reports.
- Fintech teams who want a zero-dependency library that will still install cleanly three NumPy major versions from now.
30-second demo#
from pybrinson import Segment, bhb
segments = [
Segment("UK Equity", portfolio_weight=0.40, benchmark_weight=0.40,
portfolio_return= 0.20, benchmark_return= 0.10),
Segment("Japan Equity", portfolio_weight=0.30, benchmark_weight=0.20,
portfolio_return=-0.05, benchmark_return=-0.04),
Segment("US Equity", portfolio_weight=0.30, benchmark_weight=0.40,
portfolio_return= 0.06, benchmark_return= 0.08),
]
print(bhb(segments, period="2024-Q1"))
BHB attribution — period 2024-Q1
R_p = 8.3000% R_b = 6.4000% excess = 1.9000%
Segment Allocation Selection Interaction Total
------------ ---------- --------- ----------- --------
UK Equity 0.0000% 4.0000% 0.0000% 4.0000%
Japan Equity -0.4000% -0.2000% -0.1000% -0.7000%
US Equity -0.8000% -0.8000% 0.2000% -1.4000%
Total -1.2000% 3.0000% 0.1000% 1.9000%
xychart-beta
title "Where the +1.9% excess came from (pp)"
x-axis [Allocation, Selection, Interaction, Excess]
y-axis "Contribution (pp)" -2 --> 4
bar [-1.2, 3.0, 0.1, 1.9]
Those numbers reproduce Bacon (2008) Chapter 5. The Brinson-Hood-
Beebower paper, its DOI and the CFA Institute free reprint are all
linked at the top of src/pybrinson/single_period/bhb.py.
Where to go next#
-
New to performance attribution?
Start with a plain-English primer that assumes you know Python but not finance. Ten minutes of reading and the rest of this site will read easily.
-
Install and run it
One
pip install, one import, first numbers on screen in under a minute. -
Methods supported
BHB, Brinson-Fachler, Cariño, GRAP, Frongello, Menchero, geometric linking, Karnosky-Singer currency. Each with its formula and primary source.
-
API reference
Every public class and function, with signatures and types.
Proof of correctness#
- Every formula carries a clickable primary source. Open any
module under
src/pybrinson/. The math is in the docstring; the citation, the DOI, and a working URL are right there. - Math core at 100% branch coverage, enforced in CI. A second
coverage pass (
coverage report --omit=render/*,fixtures/* --fail-under=100) runs after every test run. Drop below 100% on any core file and the pull request goes red. - Identity violations raise.
AttributionErroris thrown the instant \(A + S + I \ne R_p - R_b\) (within \(10^{-9}\)). Never a silentresidualfield you have to remember to check. - Public cited fixture pack.
pybrinson.fixturesexposes worked examples from Bacon (2008), Frongello (2002), Karnosky & Singer (1994). Validate your own implementation, your vendor reports, or a legacy spreadsheet against numbers that come directly from the literature.
Honest positioning#
pybrinson does one thing: the Brinson family. As of v1.3 the Brinson family is considered feature-complete. We will not bolt on a fixed-income Campisi decomposition, a risk-attribution companion, or a Fama-French regressor just because they are adjacent — those belong in sibling packages. The library also will not grow a pandas or polars adapter: zero runtime dependencies is a load-bearing feature, not a preference.
License#
MIT. Use it in production, fork it, vendor it. If something is wrong with the math, open an issue with the page reference.