Source code for kaxanuk.attribution_analysis.interfaces.brinston_fachler_method

import pandas as pd
import matplotlib.pyplot as plt

from kaxanuk.attribution_analysis.interfaces.brinston_fachler_interface import BrinstonFachlerInterface

[docs] class BrinstonFachlerAttribution(BrinstonFachlerInterface): def __init__( self, returns_investable_assets: pd.DataFrame, complete_portfolio_weights: pd.DataFrame, complete_benchmark_weights: pd.DataFrame ): self.returns_investable_assets = returns_investable_assets self.complete_portfolio_weights = complete_portfolio_weights self.complete_benchmark_weights = complete_benchmark_weights
[docs] def brinson_fachler_model(self, date : str): """ 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. """ r = self.returns_investable_assets.loc[date] w_p = self.complete_portfolio_weights.loc[date] w_b = self.complete_benchmark_weights.loc[date] # Returns r_p = w_p * r r_b = w_b * r # r_b_daysum alpha_index = r_p - r_b allocation = (w_p - w_b) * r_b selection = alpha_index * w_p interaction = alpha_index - allocation - selection # Output df_output_brinson_fachler = pd.concat([w_p, w_b, r, r_p, r_b, alpha_index, allocation, selection, interaction], axis = 1) df_output_brinson_fachler.columns = ["portfolio_weights", "benchmark_weights", "returns_data", "portfolio_returns", "benchmark_returns", "alpha", "allocation", "selection", "interaction"] totals = df_output_brinson_fachler[["portfolio_returns", "benchmark_returns", "alpha", "allocation", "selection", "interaction"]].sum() totals = totals.to_frame().T totals["date"] = date self.df_output_brinson_fachler = df_output_brinson_fachler self.totals = totals return df_output_brinson_fachler, totals
[docs] def time_series_calculation(self): output_dict = {} df_totals_list = [] dates_portfolio = pd.to_datetime(self.complete_portfolio_weights.index) dates_benchmark = pd.to_datetime(self.complete_benchmark_weights.index) dates_returns = pd.to_datetime(self.returns_investable_assets.index) dates = list( set(dates_benchmark).intersection(set(dates_portfolio)) ) dates.sort() for date in dates: if date in dates_returns: df_output_brinson_fachler, df_totals = self.brinson_fachler_model(date) output_dict[date] = df_output_brinson_fachler df_totals_list.append(df_totals) # a = 0 df = pd.concat(df_totals_list).set_index("date") self.output_dict = output_dict self.df = df
[docs] def attribution_plots(self): self.brinston_fach_indexes = self.df.cumsum() # self.brinston_fach_indexes = (1 + self.df).cumprod() _fig, axes = plt.subplots(3, 1, figsize=(10, 14)) # fig.suptitle(f"Portfolio Attribution", fontsize=16) self.brinston_fach_indexes[['portfolio_returns', 'benchmark_returns']].plot(ax=axes[0]) axes[0].set_title("Strategy vs Benchmark Returns") axes[0].grid(visible=True) self.brinston_fach_indexes[['alpha', ]].plot(ax=axes[1]) axes[1].set_title("Cummulative Alpha") axes[1].grid(visible=True) self.brinston_fach_indexes[['allocation', 'selection', 'interaction']].plot(ax=axes[2]) axes[2].set_title("Allocation, Selection and Interaction Effects") axes[2].grid(visible=True) # brinston_fach_indexes.plot(title = "Brinston Fachler Indexes", figsize=(10, 6)) plt.tight_layout(rect=[0, 0, 1, 0.95]) plt.show()