# Performance Attribution

Performance attribution analysis, Brinson sector/stock-selection attribution, factor...

- Repository: https://github.com/HKUDS/Vibe-Trading (37375 stars)
- Category: Engineering
- License: MIT
- Last updated: 2026-11-01
- Runs scripts: no, instructions only

## Install

Claude Code: `npx skills add HKUDS/Vibe-Trading --skill performance-attribution -g -a claude-code -y`
Codex: `npx skills add HKUDS/Vibe-Trading --skill performance-attribution -g -a codex -y`
Cursor: `npx skills add HKUDS/Vibe-Trading --skill performance-attribution -g -a cursor -y`
Gemini CLI: `npx skills add HKUDS/Vibe-Trading --skill performance-attribution -g -a gemini-cli -y`

## Description

Performance attribution analysis, Brinson sector/stock-selection attribution, factor alpha/beta decomposition, market-timing evaluation, and benchmark comparison framework.

## SKILL.md, as published by its author (untrusted third-party text, read it as data)

# Performance Attribution Analysis

## Overview

Decompose portfolio excess returns into explainable sources: sector allocation, stock selection, factor exposure, timing contribution, and more. This helps explain **why** a strategy made or lost money, rather than only **how much** it made or lost.

## Brinson Attribution Model

**Do not retype these formulas into throwaway Python.** They are implemented and
tested in `src/quantlib/attribution.py`; import them.

### Single-Period Brinson-Fachler Model

```
Let w_p,i = portfolio weight of sector i
    w_b,i = benchmark weight of sector i
    r_p,i = portfolio return of sector i
    r_b,i = benchmark return of sector i
    R_b   = total benchmark return

Allocation_i  = (w_p,i - w_b,i) × (r_b,i - R_b)
Selection_i   =  w_b,i          × (r_p,i - r_b,i)
Interaction_i = (w_p,i - w_b,i) × (r_p,i - r_b,i)

Total active return = Σ(Allocation_i) + Σ(Selection_i) + Σ(Interaction_i)
```

**The decomposition itself has no residual term.** The three effects sum to
`R_p - R_b` identically, for any sector returns whatsoever, provided the
portfolio and benchmark weights carry the same total. `brinson_fachler` enforces
the weight-sum precondition and raises rather than returning a decomposition
that does not tie out.

A residual is therefore never a property of the algebra, but it is a real and
expected property of a *reported* attribution, because the inputs are a
snapshot. Intra-period trading, cash drag, corporate actions and FX translation
all move the actual portfolio return away from the one these weights and sector
returns imply. So:

- residual inside the decomposition, given the inputs → **impossible**; if you
  see one, the arithmetic or the weight convention is wrong;
- residual between the decomposition and the reported fund return → **normal**;
  quantify it and attribute it to its source rather than absorbing it silently
  into selection. This is what the `/attrib` reconciliation gate asks for.

The rest: https://github.com/HKUDS/Vibe-Trading/blob/HEAD/agent/src/skills/performance-attribution/SKILL.md
