Attribution analysis
@qtrader/attribution-analysis
Explain why a portfolio beat or lagged its benchmark. Brinson-Fachler allocation, selection, and interaction effects by sector and period, Carino-linked across periods so they add up exactly, plus a plain-English summary. Works offline from a CSV. Not financial advice.
- Activity
- New: be the first to try it
- Installs
- 0 installs
- Likes
- 0 likes
- Clones
- 0 clones
- 1{
- 2 "name": "attribution-analysis",
- 3 "owner": "qtrader",
- 4 "version": "1.0.0",
- 5 "type": "skill",
- 6 "description": "Explain why a portfolio beat or lagged its benchmark. Brinson-Fachler allocation, selection, and interaction effects by sector and period, Carino-linked across periods so they add up exactly, plus a plain-English summary. Works offline from a CSV. Not financial advice.",
- 7 "category": "trading",
- 8 "license": "MIT",
- 9 "compat": [
- 10 "claude-code"
- 11 ],
- 12 "permissions": {
- 13 "shell": [
- 14 "node scripts/attribution.mjs",
- 15 "node scripts/selftest.mjs"
- 16 ],
- 17 "files": {
- 18 "read": [
- 19 "./data/**"
- 20 ],
- 21 "write": [
- 22 "./out/**"
- 23 ]
- 24 }
- 25 },
- 26 "tests": {
- 27 "cases": [
- 28 {
- 29 "name": "matches the hand calculation in the README",
- 30 "inputs": {},
- 31 "checks": [
- 32 {
- 33 "type": "command_exits_zero",
- 34 "command": "node scripts/selftest.mjs"
- 35 }
- 36 ]
- 37 },
- 38 {
- 39 "name": "writes linked attribution for the two-period example",
- 40 "inputs": {},
- 41 "checks": [
- 42 {
- 43 "type": "command_exits_zero",
- 44 "command": "node scripts/attribution.mjs examples/holdings.csv --out out/attribution"
- 45 },
- 46 {
- 47 "type": "output_matches_schema",
- 48 "path": "out/attribution.json",
- 49 "schema": {
- 50 "type": "object",
- 51 "required": [
- 52 "periods",
- 53 "linked",
- 54 "summary",
- 55 "warnings"
- 56 ],
- 57 "properties": {
- 58 "periods": {
- 59 "type": "array",
- 60 "minItems": 2,
- 61 "maxItems": 2
- 62 },
- 63 "linked": {
- 64 "type": "object",
- 65 "required": [
- 66 "method",
- 67 "port_return",
- 68 "bench_return",
- 69 "active_return",
- 70 "sectors",
- 71 "total"
- 72 ],
- 73 "properties": {
- 74 "method": {
- 75 "const": "carino"
- 76 },
- 77 "sectors": {
- 78 "type": "array",
- 79 "minItems": 3,
- 80 "maxItems": 3
- 81 }
- 82 }
- 83 },
- 84 "warnings": {
- 85 "type": "array",
- 86 "maxItems": 0
- 87 }
- 88 }
- 89 }
- 90 },
- 91 {
- 92 "type": "file_contains",
- 93 "path": "out/attribution.md",
- 94 "text": "| **Total** | +0.3870% | -0.1910% | +0.5022% | +0.6982% |"
- 95 },
- 96 {
- 97 "type": "file_contains",
- 98 "path": "out/attribution.md",
- 99 "text": "so it beat by 70 bps"
- 100 }
- 101 ]
- 102 }
- 103 ]
- 104 }
- 105}
