Brinson Attribution
Calculate the Brinson-Fachler attribution of the portfolio’s return over the benchmark, by sector, over the Toolkit’s date range. The excess return is split into three effects per sector:
- Allocation: the reward for overweighting sectors that beat the benchmark (and underweighting those that lagged).
- Selection: the reward for holding better stocks than the benchmark within a sector.
- Interaction: the combined effect of both decisions.
The portfolio’s sector returns come from its holdings (buy and hold from the start date). The benchmark’s sector weights come from the benchmark fund and its sector returns from the Select Sector SPDR funds (XLK, XLF, XLV, …), which track the sectors of the S&P 500.
For more information, see the following papers:
- Brinson, G.P. and Fachler, N. (1985). “Measuring Non-US Equity Portfolio Performance.” The Journal of Portfolio Management, 11(3), 73-76.
- Brinson, G.P., Hood, L.R. and Beebower, G.L. (1986). “Determinants of Portfolio Performance.” Financial Analysts Journal, 42(4), 39-44.
Also known as: performance attribution, BHB attribution, sector attribution.
Brinson Attribution in Python or via the MCP
Install the Finance Toolkit with:
pip install financetoolkit -U
Then call get_brinson_attribution as shown below.
from financetoolkit import Toolkit
toolkit = Toolkit(
["AAPL", "MSFT", "JNJ", "XOM", "JPM"],
api_key="FINANCIAL_MODELING_PREP_KEY",
start_date="2025-01-01",
end_date="2025-12-31",
)
toolkit.portfolio.get_brinson_attribution()
Or, after setting up the Finance Toolkit MCP, ask:
Which sector allocation and stock selection effects explain how an equal-weight portfolio of Apple, Microsoft, Johnson & Johnson, ExxonMobil and JPMorgan Chase did against the S&P 500 in 2025?
Which returns:
| Sector | Portfolio Weight | Benchmark Weight | Portfolio Return | Benchmark Return | Allocation Effect | Selection Effect | Interaction Effect | Total Effect |
|---|---|---|---|---|---|---|---|---|
| Basic Materials | 0 | 0.017 | nan | 0.1115 | 0.0012 | 0 | -0 | 0.0012 |
| Communication Services | 0 | 0.0923 | nan | 0.2249 | -0.0042 | 0 | -0 | -0.0042 |
| Consumer Cyclical | 0 | 0.0896 | nan | 0.0881 | 0.0082 | 0 | -0 | 0.0082 |
| Consumer Defensive | 0 | 0.0495 | nan | 0.0183 | 0.008 | 0 | -0 | 0.008 |
| Energy | 0.2 | 0.0373 | 0.1625 | 0.0661 | -0.0185 | 0.0036 | 0.0157 | 0.0008 |
| Financial Services | 0.2 | 0.1208 | 0.371 | 0.152 | -0.0022 | 0.0265 | 0.0173 | 0.0416 |
| Healthcare | 0.2 | 0.0994 | 0.4809 | 0.1447 | -0.0035 | 0.0334 | 0.0338 | 0.0637 |
| Industrials | 0 | 0.0803 | nan | 0.1976 | -0.0014 | 0 | -0 | -0.0014 |
| Real Estate | 0 | 0.0175 | nan | 0.036 | 0.0025 | 0 | -0 | 0.0025 |
| Technology | 0.4 | 0.3758 | 0.1419 | 0.2489 | 0.0017 | -0.0402 | -0.0026 | -0.0411 |
| Utilities | 0 | 0.0204 | nan | 0.1511 | 0.0006 | 0 | -0 | 0.0006 |
| Total | 1 | 1 | 0.2596 | 0.1798 | -0.0077 | 0.0233 | 0.0642 | 0.0798 |
Parameters
get_brinson_attribution accepts the following parameters.
| Parameter | Type | Description | Example |
|---|---|---|---|
weights | dict[str, float] | None | The weight per asset at the start of the date range. Defaults to None, which uses the latest weights of the Portfolio the Toolkit came from, or equal weights. | None, which uses the latest weights of the Portfolio the Toolkit came from, or equal weights |
rounding | int | None | The number of decimals to round the results to. Defaults to None, which uses the Toolkit’s rounding. | 2 |