Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
joellewis avatar

Performance Attribution

  • 384 installs
  • 161 repo stars
  • Updated July 18, 2026
  • joellewis/finance_skills

performance-attribution is a finance_skills wealth-management skill that decomposes portfolio returns into allocation, selection, timing, currency, and benchmark effects for developers building advisory analytics and cli

About

performance-attribution is a JoelLewis/finance_skills wealth-management plugin skill for explaining why a portfolio beat or lagged its benchmark. It documents Brinson-Fachler single-period attribution with allocation, selection, and interaction effects; multi-period geometric linking via Carino, Menchero, and GRAP methods; factor-based attribution with market, size, value, momentum, quality, and low-volatility loadings; fixed-income decomposition into yield, roll, curve, spread, and credit components; and currency attribution for international holdings. A worked two-sector Tech and Healthcare example shows 2.95% total active return split across effects, and a factor example isolates 0.70% alpha after Fama-French style contributions. The bundled scripts/performance_attribution.py module exposes BrinsonFachler, OLS factor attribution, and Carino linking, runnable via uv run or python3 with numpy and scipy, plus --verify against worked numbers. Developers reach for performance-attribution when building client review narratives, fund reporting pipelines, or fintech analytics explaining stock picking versus sector bets.

  • Decomposes returns into allocation and selection effects
  • Compares results against benchmarks and policy targets
  • Produces advisor- and client-facing review narratives
  • Supports multi-period and multi-asset attribution views

Performance Attribution by the numbers

  • 384 all-time installs (skills.sh)
  • +16 installs in the week ending Aug 2, 2026 (Skillselion tracking)
  • Ranked #288 of 1,106 Finance & Trading skills by installs in the Skillselion catalog
  • Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/joellewis/finance_skills --skill performance-attribution

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs384
repo stars161
Last updatedJuly 18, 2026
Repositoryjoellewis/finance_skills

How do you explain portfolio returns versus benchmark?

Explain portfolio or fund returns by decomposing performance into allocation, selection, timing, currency, and benchmark effects for reporting and client reviews.

Who is it for?

Developers building wealth-management analytics, client review reports, or fund reporting that must explain benchmark-relative performance drivers.

Skip if: Teams needing trade execution, order routing, or tax-lot accounting without return decomposition—use trading or tax skills instead.

When should I use this skill?

User asks what drove returns, allocation vs selection effects, Brinson attribution, alpha decomposition, or why a portfolio beat the benchmark.

What you get

Attribution tables, factor contribution breakdowns, multi-period linked effects, and verified Brinson-Fachler decomposition outputs.

  • attribution tables
  • factor breakdowns
  • linked multi-period effects

By the numbers

  • worked Brinson example yields 2.95% total active return
  • factor example isolates 0.70% alpha after factor contributions
  • performance_attribution.py includes 3 demonstration paths

Files

SKILL.mdMarkdownGitHub ↗

Performance Attribution

Core Concepts

Brinson-Fachler Attribution (Single Period)

The classic equity attribution model decomposes active return (portfolio return minus benchmark return) into three effects:

  • Allocation effect: Value added by over/underweighting sectors relative to the benchmark
  • A_i = (w_p,i - w_b,i) × (R_b,i - R_b)
  • Rewards overweighting sectors that outperform the total benchmark
  • Selection effect: Value added by picking better securities within each sector
  • S_i = w_b,i × (R_p,i - R_b,i)
  • Rewards outperforming the sector benchmark regardless of weight
  • Interaction effect: Combined effect of both overweighting and outperforming (or vice versa)
  • I_i = (w_p,i - w_b,i) × (R_p,i - R_b,i)
  • Captures the joint benefit of overweighting a sector AND selecting better securities in it
  • Total active return: R_p - R_b = Σ A_i + Σ S_i + Σ I_i

Where: w_p,i = portfolio weight in sector i, w_b,i = benchmark weight in sector i, R_p,i = portfolio return in sector i, R_b,i = benchmark return in sector i, R_b = total benchmark return.

Multi-Period Attribution

Single-period attribution does not compound across periods. Geometric linking methods are required:

  • Carino method: Applies a smoothing factor to make arithmetic effects compound to the correct geometric total
  • Menchero method: Uses a logarithmic approach for smoother decomposition
  • GRAP (Geometric Return Attribution Program): Converts arithmetic effects to geometric equivalents
  • Key principle: the sum of linked attribution effects must equal the total geometric active return over the full period

Factor-Based Attribution

Decomposes returns into exposures to systematic risk factors:

  • Model: R_p = Σ β_k × F_k + α
  • β_k = portfolio's exposure (loading) to factor k
  • F_k = return of factor k during the period
  • α = residual return unexplained by factors (true alpha)
  • Common factors: Market (MKT), Size (SMB), Value (HML), Momentum (UMD), Quality (QMJ), Low Volatility (BAB)
  • Factor contribution: β_k × F_k for each factor
  • Active factor contribution: (β_p,k - β_b,k) × F_k
  • The model chosen (Fama-French 3, Carhart 4, Fama-French 5, Barra, Axioma) affects results

Fixed-Income Attribution

Decomposes bond portfolio returns into component sources:

  • Yield return (income): Coupon income accrued during the period (yield × time)
  • Roll return: Price appreciation as bonds "roll down" the yield curve toward maturity
  • Curve change return: Impact of parallel and non-parallel yield curve shifts
  • Duration effect: -D × Δy (parallel shift)
  • Curve reshaping: key rate duration contributions
  • Spread change return: Impact of credit spread changes: -spread_duration × Δspread
  • Credit/default return: Losses from defaults or credit events
  • Residual: Unexplained return (convexity effects, model error)

Currency Attribution

For international portfolios, returns decompose into:

  • Local return: Return of the asset in its local currency
  • Currency return: Gain/loss from exchange rate movements
  • Cross-product: Interaction between local return and currency return
  • Total return (base currency): R_base ≈ R_local + R_currency + R_local × R_currency
  • Hedged return: Local return + hedge cost (forward premium/discount)
  • Attribution of active currency decisions: actual currency exposure vs benchmark currency exposure

Holdings-Based vs Returns-Based Attribution

  • Holdings-based: Uses actual portfolio positions; more accurate but requires detailed holdings data at each evaluation point
  • Returns-based (style analysis): Regresses portfolio returns against a set of style indices (e.g., Sharpe style analysis); less precise but requires only return series
  • Transaction-based: Most accurate; accounts for intra-period trading by using actual transaction records

Key Formulas

FormulaExpressionUse Case
Allocation effect (sector i)A_i = (w_p,i - w_b,i) × (R_b,i - R_b)Sector weighting decisions
Selection effect (sector i)S_i = w_b,i × (R_p,i - R_b,i)Security selection within sector
Interaction effect (sector i)I_i = (w_p,i - w_b,i) × (R_p,i - R_b,i)Joint allocation-selection effect
Total active returnR_p - R_b = Σ(A_i + S_i + I_i)Sum of all effects equals active return
Factor return contributionC_k = β_k × F_kReturn from factor k exposure
Duration effectΔP/P ≈ -D × ΔyBond price change from yield shift
Currency returnR_fx = (S_end - S_start) / S_startExchange rate impact

Worked Examples

Example 1: Brinson-Fachler equity attribution

Given: Two-sector portfolio (Tech and Healthcare). Portfolio: 35% Tech (returned 15%), 65% Healthcare (returned 8%). Benchmark: 25% Tech (returned 12%), 75% Healthcare (returned 6%). Total benchmark return: 0.25×12% + 0.75×6% = 7.5%. Calculate: Allocation, selection, and interaction effects for each sector, and total active return. Solution: 1. Total portfolio return: 0.35×15% + 0.65×8% = 5.25% + 5.20% = 10.45%. 2. Total active return: 10.45% - 7.50% = 2.95%. 3. Tech allocation effect: (0.35 - 0.25) × (12% - 7.5%) = 0.10 × 4.5% = +0.45% (overweight a sector that beat the benchmark). 4. Tech selection effect: 0.25 × (15% - 12%) = 0.25 × 3% = +0.75% (stock picks in Tech beat Tech benchmark). 5. Tech interaction effect: (0.35 - 0.25) × (15% - 12%) = 0.10 × 3% = +0.30% (overweight AND outperformed). 6. Healthcare allocation effect: (0.65 - 0.75) × (6% - 7.5%) = -0.10 × -1.5% = +0.15% (underweight a sector that lagged the benchmark). 7. Healthcare selection effect: 0.75 × (8% - 6%) = 0.75 × 2% = +1.50% (stock picks in Healthcare beat Healthcare benchmark). 8. Healthcare interaction effect: (0.65 - 0.75) × (8% - 6%) = -0.10 × 2% = -0.20% (underweight but outperformed — interaction is negative). 9. Totals: Allocation = 0.45 + 0.15 = 0.60%. Selection = 0.75 + 1.50 = 2.25%. Interaction = 0.30 + (-0.20) = 0.10%. Sum = 0.60 + 2.25 + 0.10 = 2.95% ✓.

Example 2: Factor-based attribution

Given: A fund has factor loadings: β_mkt = 1.1, β_smb = 0.3, β_hml = -0.2. During the period: MKT = 5%, SMB = 2%, HML = -1%. Risk-free rate = 1%. Fund excess return = 7%. Calculate: Factor contributions and alpha. Solution: 1. Market contribution: 1.1 × 5% = 5.50%. 2. Size (SMB) contribution: 0.3 × 2% = 0.60%. 3. Value (HML) contribution: -0.2 × (-1%) = +0.20%. 4. Total factor-explained return: 5.50 + 0.60 + 0.20 = 6.30%. 5. Alpha (residual): 7.00% - 6.30% = +0.70%. 6. Interpretation: The fund's excess return of 7% is mostly explained by above-market beta (5.5%) and a small-cap tilt (0.6%). The negative value loading helped (+0.2%) as value underperformed. After accounting for all factors, the manager generated 0.70% of true alpha.

Common Pitfalls

  • Interaction effect is hard to interpret — some attribution models fold it into allocation or selection, which changes reported results significantly
  • Multi-period attribution requires geometric linking — simple arithmetic attribution does not compound correctly and residuals grow over time
  • Returns-based attribution (style analysis) may not reflect actual holdings, especially for managers who trade actively or change style
  • Factor attribution results depend heavily on the chosen factor model — different models yield different alpha estimates
  • Currency attribution is often overlooked in international portfolios, hiding or inflating apparent skill
  • Survivorship bias in manager evaluation: only surviving funds are analyzed, overstating average skill
  • Confusing gross-of-fee and net-of-fee returns when comparing to benchmarks
  • Using inappropriate benchmarks that do not match the portfolio's investment universe

Cross-References

  • investment-policy (wealth-management plugin, Layer 5): Benchmark selection in IPS directly feeds performance attribution analysis
  • tax-efficiency (wealth-management plugin, Layer 5): After-tax attribution requires adjusting returns for tax impact
  • savings-goals (wealth-management plugin, Layer 6): Attribution helps assess whether investment strategy is on track to meet goals
  • liquidity-management (wealth-management plugin, Layer 6): Cash drag from liquidity reserves affects portfolio-level attribution
  • client-review-prep (advisory-practice plugin, Layer 10): attribution analysis highlights are key talking points in client review meetings
  • tax-loss-harvesting (wealth-management plugin, Layer 5): tax alpha from TLH should be tracked and attributed separately

Running the script

Run with uv run scripts/performance_attribution.py (the PEP 723 header resolves numpy automatically) or with python3 scripts/performance_attribution.py after pip install numpy scipy. A bare run prints three demos: the Brinson-Fachler attribution from Worked Example 1, an OLS factor attribution on seeded synthetic data, and Carino multi-period linking. Use --verify to assert outputs match this skill's worked example numbers (exit code 0 on PASS) and --help for an overview of the classes. The file is primarily meant to be imported as a module (e.g., from performance_attribution import BrinsonFachler).

Related skills

How it compares

Pick performance-attribution to explain realized benchmark-relative returns; use books-and-records when the task is regulatory record retention rather than portfolio analytics.

FAQ

What models does performance-attribution support?

performance-attribution covers Brinson-Fachler sector attribution, factor-based alpha decomposition, fixed-income return sources, currency attribution, and multi-period linking with Carino, Menchero, and GRAP methods.

Is there a script for performance-attribution?

performance-attribution includes scripts/performance_attribution.py with BrinsonFachler, OLS factor attribution, and Carino linking demos. Run via uv run or python3 after installing numpy and scipy, or use --verify for worked-example checks.

What is the Brinson interaction effect?

performance-attribution defines interaction as (portfolio weight minus benchmark weight) times (portfolio sector return minus benchmark sector return). It captures joint allocation-and-selection benefits and can be folded into other effects in some reports.

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.