Brinson-Fachler#

Abstract interface and concrete implementations for the Brinson-Fachler attribution methodology (see Attribution Methodology for the conceptual background), plus the PyArrow table-reshaping utilities used by the Arrow implementation.

class BrinstonFachlerInterface(returns_investible_assets: DataFrame, portfolio_weights: DataFrame, benchmark_weights: DataFrame)[source]#

Bases: object

abstractmethod brinson_fachler_model(date: str)[source]#

Calculate the Brinson-Fachler attribution model for a given date.

Parameters:

date – The date for which to calculate the attribution.

Returns:

A tuple containing the detailed attribution DataFrame and a totals DataFrame.

abstractmethod time_series_calculation()[source]#

Calculate the time series attribution for the entire period.

Returns:

A tuple containing the detailed attribution DataFrame and a totals DataFrame.

class BrinstonFachlerAttribution(returns_investable_assets: DataFrame, complete_portfolio_weights: DataFrame, complete_benchmark_weights: DataFrame)[source]#

Bases: BrinstonFachlerInterface

attribution_plots()[source]#
brinson_fachler_model(date: str)[source]#

Calculate the Brinson-Fachler attribution model for a given date. :param date: The date for which to calculate the attribution. :return: A tuple containing the detailed attribution DataFrame and a totals DataFrame.

time_series_calculation()[source]#

Calculate the time series attribution for the entire period.

Returns:

A tuple containing the detailed attribution DataFrame and a totals DataFrame.

Abstract interface for Brinson-Fachler return attribution using PyArrow Tables.

Defines the contract that concrete Brinson-Fachler implementations must satisfy: computing single-period attribution for a given date and aggregating single-period results into a time series.

class BrinstonFachlerArrowInterface(returns_investible_assets: Table, portfolio_weights: Table, benchmark_weights: Table)[source]#

Bases: object

Abstract interface for Brinson-Fachler attribution using PyArrow Tables.

Defines the contract for computing single-period and multi-period Brinson-Fachler return attribution, decomposing active return into allocation, selection, and interaction effects.

Parameters:
  • returns_investible_assets (pa.Table) – Asset returns table with a date column and one column per asset containing period returns as float64.

  • portfolio_weights (pa.Table) – Portfolio weight table with a date column and one column per asset containing portfolio weights as float64.

  • benchmark_weights (pa.Table) – Benchmark weight table with a date column and one column per asset containing benchmark weights as float64.

abstractmethod brinson_fachler_model(date: str) → tuple[Table, Table][source]#

Calculate the Brinson-Fachler attribution model for a single date.

Decomposes the active return (portfolio minus benchmark) into allocation, selection, and interaction effects for each asset.

Parameters:

date (scalar) – The date for which to calculate the attribution. Must match a value present in the date column of the input tables.

Returns:

  • detail_table (pa.Table) – Per-asset attribution with columns: asset, portfolio_weights, benchmark_weights, returns_data, portfolio_returns, benchmark_returns, alpha, allocation, selection, interaction.

  • totals_table (pa.Table) – Single-row summary with columns: date, portfolio_returns, benchmark_returns, alpha, allocation, selection, interaction.

abstractmethod time_series_calculation() → None[source]#

Calculate the Brinson-Fachler attribution across all available dates.

Iterates over dates common to portfolio weights and benchmark weights, computing the single-period model for each, and concatenates the totals into a time-series table.

Returns:

  • detail_dict (dict) – Mapping of each date to its per-asset pa.Table detail breakdown.

  • totals_table (pa.Table) – Concatenated totals across all dates with columns: date, portfolio_returns, benchmark_returns, alpha, allocation, selection, interaction.

Concrete Brinson-Fachler return attribution implementation using PyArrow.

Implements BrinstonFachlerArrowInterface by decomposing active portfolio return into allocation, selection, and interaction effects at the asset level for each period, then aggregating across time.

class BrinstonFachlerArrowAttribution(returns_investable_assets: Table, complete_portfolio_weights: Table, complete_benchmark_weights: Table, date_column: str = 'date')[source]#

Bases: BrinstonFachlerArrowInterface

Brinson-Fachler return attribution implementation using PyArrow.

Decomposes active portfolio return into allocation, selection, and interaction effects at the asset level for each period, then aggregates across time.

Parameters:
  • returns_investable_assets (pa.Table) – Asset returns with a date_column and one float64 column per asset.

  • complete_portfolio_weights (pa.Table) – Portfolio weights with a date_column and one float64 column per asset. Rows should sum to 1.0 for each date.

  • complete_benchmark_weights (pa.Table) – Benchmark weights with a date_column and one float64 column per asset. Rows should sum to 1.0 for each date.

  • date_column (str, default "date") – Name of the date column common to all input tables.

detail_table#

Per-asset detail from the most recent brinson_fachler_model call.

Type:

pa.Table

totals#

Single-row totals from the most recent brinson_fachler_model call.

Type:

pa.Table

df#

Concatenated totals time series after time_series_calculation.

Type:

pa.Table

output_dict#

Mapping of date to per-asset detail table after time_series_calculation.

Type:

dict

brinston_fach_indexes#

Cumulative attribution indexes after attribution_plots.

Type:

pa.Table

attribution_plots() → None[source]#

Plot cumulative Brinson-Fachler attribution effects.

Produces a 3-panel figure:

  1. Cumulative portfolio vs benchmark returns.

  2. Cumulative alpha.

  3. Cumulative allocation, selection, and interaction effects.

Must be called after time_series_calculation.

Sets Attributes#

brinston_fach_indexespa.Table

Cumulative sums of all attribution columns, keyed by date.

brinson_fachler_model(date: str) → tuple[Table, Table][source]#

Calculate the Brinson-Fachler attribution for a single date.

Decomposes the active return into allocation, selection, and interaction effects for every asset, then sums to produce portfolio-level totals.

Parameters:

date (scalar) – Target date matching a value in the date column.

Returns:

  • detail_table (pa.Table) – Per-asset breakdown with columns: asset, portfolio_weights, benchmark_weights, returns_data, portfolio_returns, benchmark_returns, alpha, allocation, selection, interaction.

  • totals_table (pa.Table) – Single-row aggregate with columns: date, portfolio_returns, benchmark_returns, alpha, allocation, selection, interaction.

time_series_calculation() → None[source]#

Run Brinson-Fachler attribution across all common dates.

Finds dates present in both portfolio and benchmark weights, runs brinson_fachler_model for each date that also has return data, and concatenates results.

Sets Attributes#

dfpa.Table

Concatenated totals time series with columns: date, portfolio_returns, benchmark_returns, alpha, allocation, selection, interaction.

output_dictdict

Mapping of each date to its per-asset pa.Table detail.

Conversion utilities for reshaping PyArrow tables into the format expected by the Brinson-Fachler Arrow attribution engine.

Provides functions equivalent to table_conversion.py but without converting to pandas — all operations stay in PyArrow.

compute_returns_from_prices_arrow(prices_table: Table, date_column: str) → ReturnsTable[source]#

Compute percentage returns from a prices table.

Equivalent to prices_df.pct_change(fill_method=None) — the first row is kept with null values so the date range stays aligned with the original prices table (and therefore with the weights tables).

Parameters:
  • prices_table (pyarrow.Table) – Prices table containing a date column and one or more numeric asset price columns.

  • date_column (str) – Name of the column in prices_table containing the dates. It’s passed through unchanged and always renamed to "date" in the output, regardless of its original name.

Returns:

Wrapper around a table with the same columns and row count as prices_table, where each asset column holds period-over-period percentage returns and the first row is null.

Return type:

ReturnsTable

portfolio_table_to_arrow(table: Table) → PortfolioWeights[source]#

Transpose a wide portfolio/benchmark weights table into attribution format.

This is the PyArrow equivalent of portfolio_table_to_pandas, which does set_index("Ticker").T.

Parameters:

table (pyarrow.Table) – Wide-format weights table with columns [Ticker, date1, date2, ...], one row per ticker and one column per date. Date column names are parsed as "%Y-%m-%d", falling back to "%d/%m/%Y" if that fails.

Returns:

Wrapper around a table with columns [date, asset1, asset2, ...], i.e. one row per date and one column per ticker.

Return type:

PortfolioWeights

pyarrow_prices_to_attribution_format(table: Table, date_column: str) → Table[source]#

Reshape a prices table into the attribution format.

The date column is cast to a timestamp type and all remaining (numeric) columns are cast to float64.

Parameters:
  • table (pyarrow.Table) – Prices table containing a date column and one or more numeric asset price columns.

  • date_column (str) – Name of the column in table containing the dates. It’s cast from a "%Y-%m-%d" string (if not already a timestamp type) and always renamed to "date" in the output, regardless of its original name.

Returns:

Table with columns [date, asset1, asset2, ...], where date is of timestamp("ns") type and every asset column is float64.

Return type:

pyarrow.Table

returns_table_to_arrow(table: Table) → ReturnsTable[source]#

Transpose a wide returns table into attribution format.

Same transpose logic as portfolio_table_to_arrow(), but wraps the result in a ReturnsTable (no weight constraints).

Parameters:

table (pyarrow.Table) – Wide-format returns table with columns [Ticker, date1, date2, ...], one row per ticker and one column per date. Date column names are parsed as "%Y-%m-%d", falling back to "%d/%m/%Y" if that fails.

Returns:

Wrapper around a table with columns [date, asset1, asset2, ...], i.e. one row per date and one column per ticker.

Return type:

ReturnsTable