This release is a pre-release and may not be stable for production use.
perfattr
perfattr is a small, auditable portfolio performance-attribution calculation
library built with pandas and NumPy.
The package provides a reusable Brinson-Fachler calculation core and a portable preparation layer for source-period weights and returns. Portfolio accounting, vendor schemas, and presentation remain outside the package boundary.
Main features
- Accept weights and returns, or authoritative contributions when accounting results are available.
- Validate, select, and align portfolio and benchmark histories.
- Consolidate smaller source periods into complete monthly, quarterly, or yearly reporting periods using calendar and holiday rules.
- Apply static or effective-dated classification mappings before consolidation.
- Calculate Brinson-Fachler allocation with either compact two-effect selection or explicit three-effect selection and interaction.
- Link contributions logarithmically and attribution effects using Carino linking.
- Preserve zero-weight fee and financing contributions without inventing returns.
- Return deterministic pandas result frames with explicit financial reconciliation.
The completed initial calculation roadmap is recorded in
_extras/perfattr_roadmap_1.md. The completed portable
preparation work is recorded in
_extras/perfattr_roadmap_2.md, while later candidates
are kept in the noncommitted
_extras/perfattr_roadmap_3.md. Effective-dated
classification was the first promoted candidate and its completed work is recorded in
_extras/perfattr_roadmap_4_effective_dated_classification.md, with
its accepted contract in
docs/effective_dated_classification_specification.md.
The opt-in Brinson-Fachler three-effect work is governed by
_extras/perfattr_roadmap_5_brinson_fachler_three_effect.md
and docs/brinson_fachler_three_effect_specification.md.
The complete portable calculation contract is defined in
docs/specification.md, and the accepted roadmap 2 preparation
contract is in docs/preparation_specification.md.
import pandas as pd
from perfattr import calculate_attribution, prepare_attribution
portfolio = pd.DataFrame(
[
{
"from_date": "2024-01-01",
"thru_date": "2024-01-31",
"identifier": "Equity",
"weight": 0.60,
"return": 0.04,
},
{
"from_date": "2024-01-01",
"thru_date": "2024-01-31",
"identifier": "Bonds",
"weight": 0.40,
"return": 0.01,
},
]
)
benchmark = pd.DataFrame(
[
{
"from_date": "2024-01-01",
"thru_date": "2024-01-31",
"identifier": "Equity",
"weight": 0.50,
"return": 0.03,
},
{
"from_date": "2024-01-01",
"thru_date": "2024-01-31",
"identifier": "Bonds",
"weight": 0.50,
"return": 0.015,
},
]
)
prepared = prepare_attribution(portfolio, benchmark)
result = calculate_attribution(prepared.portfolio, prepared.benchmark)
print(result.period_detail)
Attribution methods
The default remains the released two-effect Brinson-Fachler convention: allocation is reported separately, while portfolio-weighted selection absorbs interaction. Opt in to explicit interaction with the public method enum:
from perfattr import AttributionMethod, calculate_attribution
three_effect = calculate_attribution(
prepared.portfolio,
prepared.benchmark,
method=AttributionMethod.BRINSON_FACHLER_THREE_EFFECT,
)
print(
three_effect.period_detail[
["allocation_effect", "selection_effect", "interaction_effect"]
]
)
For portfolio and benchmark weights wP and wB, effective returns rP and rB,
and total benchmark return B, the three effects are:
allocation = (wP - wB) * (rB - B)
selection = wB * (rP - rB)
interaction = (wP - wB) * (rP - rB)
Positive and negative values follow these signed formulas; perfattr does not label
an effect as favorable or unfavorable. Supplied contribution remains authoritative.
If either effective return is undefined, interaction is zero and selection retains
the reconciled residual rather than inventing a return.
The opt-in result inserts interaction_effect immediately after selection_effect
and linked_interaction_effect immediately after linked_selection_effect wherever
those channels apply. Cumulative output also places
cumulative_interaction_effect immediately after cumulative_selection_effect.
AttributionResult.method records the selected convention. See the
three-effect specification for the complete schemas, linking
rules, null policy, and independently calculated example.
Canonical CSV inputs can be loaded with read_performance_csv; optional mapping and
classification readers are also available at the package root.
Mapping CSV files are headerless and use one uniform form per file. Existing static
files remain two columns (identifier,classification_identifier). Effective-dated
files use four columns in the order from_date,thru_date,identifier, classification_identifier. For example:
2024-01-01,2024-01-31,ASSET,Equity
2024-02-01,2024-12-31,ASSET,Fixed Income
The dates are closed and inclusive. A source period for a mapped identifier must be
contained in exactly one assignment; perfattr does not split a source period at a
classification boundary.
Cash, fees, and financing
Cash receives no special treatment: supply it as an ordinary identifier, or map it to
a Cash classification, with the weight and return chosen by the host accounting
system. perfattr never invents cash or hides a residual in it.
A fee or financing charge without exposure can be supplied as a zero-weight row with
an authoritative nonzero contribution and a null return. The contribution is
preserved and included in the ordinary attribution and linking calculations;
perfattr does not infer the row from its name or calculate the charge. Financing with
an explicit exposure and return can instead be represented as an ordinary identifier.
Development
Create and activate a virtual environment:
python3 -m venv .venv
source .venv/bin/activate
Install the package and development dependencies:
python -m pip install --editable ".[dev]"
Run the initial checks:
python -m pytest
python -m pylint src/perfattr tests scripts
python -m pyright
Run the four roadmap performance workloads:
python scripts/benchmark_core.py --samples 5
python scripts/benchmark_core.py --samples 5 --input-form authoritative
python scripts/benchmark_preparation.py --samples 5
Pass --method three-effect to benchmark_core.py to measure the opt-in calculation;
the default remains --method two-effect.
Add --workload monthly_121260 --profile to inspect one workload's cumulative
calculation-core call profile. The preparation benchmark compares static and
effective-dated mappings through quarterly consolidation. The benchmark methodology
and observations are recorded in docs/performance.md.
License
perfattr is distributed under the MIT License.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file perfattr-0.4.0a1.tar.gz.
File metadata
- Download URL: perfattr-0.4.0a1.tar.gz
- Upload date:
- Size: 110.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a2b4ba02298248493e6022aec06986f4025759fc71b0e2430dc2a6688d4084d5
|
|
| MD5 |
8fe47fcb2c0d5bf74834bdc89d6aefff
|
|
| BLAKE2b-256 |
a925a5d658ffd623fa819b5ae5f86ebc326a853d97a468dbd67cb835d4e1a142
|
Provenance
The following attestation bundles were made for perfattr-0.4.0a1.tar.gz:
Publisher:
publish.yml on JohnDReynolds/perfattr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
perfattr-0.4.0a1.tar.gz -
Subject digest:
a2b4ba02298248493e6022aec06986f4025759fc71b0e2430dc2a6688d4084d5 - Sigstore transparency entry: 2727334883
- Sigstore integration time:
-
Permalink:
JohnDReynolds/perfattr@59d6fa1ef2420df7b4445e8b48203333928c4f22 -
Branch / Tag:
refs/tags/v0.4.0a1 - Owner: https://github.com/JohnDReynolds
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@59d6fa1ef2420df7b4445e8b48203333928c4f22 -
Trigger Event:
release
-
Statement type:
File details
Details for the file perfattr-0.4.0a1-py3-none-any.whl.
File metadata
- Download URL: perfattr-0.4.0a1-py3-none-any.whl
- Upload date:
- Size: 51.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fa1218412d787bb10a67035281a161c3e1b2e4f525d3ee1b0ef137694480fb71
|
|
| MD5 |
5aad04b847d75e18d31e641fa034f36f
|
|
| BLAKE2b-256 |
1682981f5cac4d525c9e69d934a0eb12d3234683692b7b84cca2063df3810430
|
Provenance
The following attestation bundles were made for perfattr-0.4.0a1-py3-none-any.whl:
Publisher:
publish.yml on JohnDReynolds/perfattr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
perfattr-0.4.0a1-py3-none-any.whl -
Subject digest:
fa1218412d787bb10a67035281a161c3e1b2e4f525d3ee1b0ef137694480fb71 - Sigstore transparency entry: 2727335179
- Sigstore integration time:
-
Permalink:
JohnDReynolds/perfattr@59d6fa1ef2420df7b4445e8b48203333928c4f22 -
Branch / Tag:
refs/tags/v0.4.0a1 - Owner: https://github.com/JohnDReynolds
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@59d6fa1ef2420df7b4445e8b48203333928c4f22 -
Trigger Event:
release
-
Statement type: