From 5220d00ec97f68b1719b9560f9e344c756928c1a Mon Sep 17 00:00:00 2001 From: Vincenzo Date: Fri, 1 Nov 2024 23:08:39 +0100 Subject: [PATCH 1/6] show just tickers which are still in portfolio, not bought-and-sold ones (included in the assets registry) --- "src/pages/2_\360\237\223\210_Return_Analysis.py" | 13 ++++++++++--- .../3_\342\232\240\357\270\217_Risk_Analysis.py" | 10 ++++++++-- src/returns.py | 7 ++++++- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git "a/src/pages/2_\360\237\223\210_Return_Analysis.py" "b/src/pages/2_\360\237\223\210_Return_Analysis.py" index 56793f6..9d27dac 100644 --- "a/src/pages/2_\360\237\223\210_Return_Analysis.py" +++ "b/src/pages/2_\360\237\223\210_Return_Analysis.py" @@ -28,7 +28,12 @@ st.error("Oops... there's nothing to display. Go through 🏠 first to load the data") st.stop() -ticker_list = df_transactions["ticker_yf"].unique().tolist() +df_n_shares = ( + df_transactions.groupby("ticker_yf") + .agg(n_shares=("shares", "sum")) + .sort_values("n_shares", ascending=False) +) +ticker_list = df_n_shares.loc[~(df_n_shares == 0).all(axis=1)].index.unique().to_list() df_common_history = get_max_common_history(ticker_list=ticker_list) st.markdown("## Global settings") @@ -79,6 +84,7 @@ df_rets = get_period_returns( df=df_common_history.loc[first_day:last_day, :], df_registry=df_registry, + tickers_to_evaluate=ticker_list, period=DICT_FREQ_RESAMPLE[freq], level=DICT_GROUPBY_LEVELS[level], ) @@ -97,7 +103,7 @@ cols = st.multiselect( f"Choose the {level.lower()} to display:", options=default_objs, - default=default_objs[1], + default=default_objs[0], key="sel_lev_1", ) @@ -118,7 +124,7 @@ cols = col_l_lw.multiselect( f"Choose the {level.lower()} to display:", options=default_objs, - default=default_objs[1], + default=default_objs[0], key="sel_lev_2", ) @@ -132,6 +138,7 @@ df_roll_ret = get_rolling_returns( df_prices=df_common_history.loc[first_day:last_day, :].ffill(), df_registry=df_registry, + tickers_to_evaluate=ticker_list, level=DICT_GROUPBY_LEVELS[level], window=window, )[cols] diff --git "a/src/pages/3_\342\232\240\357\270\217_Risk_Analysis.py" "b/src/pages/3_\342\232\240\357\270\217_Risk_Analysis.py" index 8b86a98..9fa67e7 100644 --- "a/src/pages/3_\342\232\240\357\270\217_Risk_Analysis.py" +++ "b/src/pages/3_\342\232\240\357\270\217_Risk_Analysis.py" @@ -29,7 +29,12 @@ st.error("Oops... there's nothing to display. Go through 🏠 first to load the data") st.stop() -ticker_list = df_transactions["ticker_yf"].unique().tolist() +df_n_shares = ( + df_transactions.groupby("ticker_yf") + .agg(n_shares=("shares", "sum")) + .sort_values("n_shares", ascending=False) +) +ticker_list = df_n_shares.loc[~(df_n_shares == 0).all(axis=1)].index.unique().to_list() df_common_history = get_max_common_history(ticker_list=ticker_list) st.markdown("## Global settings") @@ -73,6 +78,7 @@ df_rets = get_period_returns( df=df_common_history.loc[first_day:last_day, :], df_registry=df_registry, + tickers_to_evaluate=ticker_list, period=DICT_FREQ_RESAMPLE[freq], level=DICT_GROUPBY_LEVELS[level], ) @@ -81,7 +87,7 @@ cols = st.multiselect( f"Choose the {level.lower()} to display:", options=default_objs, - default=default_objs[1], + default=default_objs[0], key="sel_lev_3", ) diff --git a/src/returns.py b/src/returns.py index 849a55a..e8bb908 100644 --- a/src/returns.py +++ b/src/returns.py @@ -11,9 +11,12 @@ def get_period_returns( df: pd.DataFrame, df_registry: pd.DataFrame, + tickers_to_evaluate: list[str], period: Literal["Y", "Q", "M", "W", None], level: Literal["ticker", "asset_class", "macro_asset_class"], ): + # Filtra solo i ticker effettivamente in portafoglio + df_registry = df_registry[df_registry["ticker_yf"].isin(tickers_to_evaluate)] # Calcolo il ritorno df_rets = df.pct_change()[1:] # Se il periodo è None, la frequenza è giornaliera @@ -29,7 +32,6 @@ def get_period_returns( cols_to_sum = df_registry[df_registry[level].eq(class_)][ "ticker_yf" ].to_list() - df_rets_classes[class_] = df_rets[cols_to_sum].sum(axis=1) return df_rets_classes # Se il periodo non è None, faccio resampling al periodo desiderato @@ -52,9 +54,12 @@ def get_period_returns( def get_rolling_returns( df_prices: pd.DataFrame, df_registry: pd.DataFrame, + tickers_to_evaluate: list[str], window: int, level: Literal["ticker", "asset_class", "macro_asset_class"], ) -> pd.DataFrame: + # Filtra solo i ticker effettivamente in portafoglio + df_registry = df_registry[df_registry["ticker_yf"].isin(tickers_to_evaluate)] df_log_ret = np.log(df_prices.div(df_prices.shift(1))) df_roll_log_ret = df_log_ret.rolling(window=window).sum() From 30e9afde6590f2e00105f87d4cd223f7fcdf0321 Mon Sep 17 00:00:00 2001 From: Vincenzo Date: Fri, 1 Nov 2024 23:09:27 +0100 Subject: [PATCH 2/6] app version --- src/var.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/var.py b/src/var.py index d82f948..ffbb8b0 100644 --- a/src/var.py +++ b/src/var.py @@ -9,7 +9,7 @@ os.path.join(*split_script_running_path[0 : len(split_script_running_path) - 1]) ) -APP_VERSION = "0.2.0" +APP_VERSION = "0.2.1" # Data/images From 7ce08e37efc0d8437ac756327b6cdd2abb63016d Mon Sep 17 00:00:00 2001 From: Vincenzo Date: Wed, 6 Nov 2024 18:35:25 +0100 Subject: [PATCH 3/6] new data --- data/in/demo.xlsx | Bin 15688 -> 16106 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/data/in/demo.xlsx b/data/in/demo.xlsx index fad93d350ec4681a2e759884ac08caea369c2a6a..461036d2fb3d2da225bf3bfa320022d99b311dc3 100644 GIT binary patch delta 9027 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 D+O9~$ From 7fb77272bd83cb88a3299d6aa66d92d09c43117c Mon Sep 17 00:00:00 2001 From: Vincenzo Date: Wed, 6 Nov 2024 18:36:00 +0100 Subject: [PATCH 4/6] removed unnecessary roundings --- src/aggregation.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/aggregation.py b/src/aggregation.py index 2136d48..6a1b1cd 100644 --- a/src/aggregation.py +++ b/src/aggregation.py @@ -114,10 +114,8 @@ def get_portfolio_pivot( .sum() .reset_index() ) - df_pivot["weight_pf"] = ( - (100 * df_pivot["position_value"].div(pf_actual_value)).astype(float).round(1) - ) - df_pivot["position_value"] = df_pivot["position_value"].astype(float).round(1) + df_pivot["weight_pf"] = 100 * df_pivot["position_value"].div(pf_actual_value) + df_pivot["position_value"] = df_pivot["position_value"] return df_pivot @@ -128,8 +126,6 @@ def get_pnl_by_asset_class( group_by: Literal["asset_class", "macro_asset_class"], ) -> pd.DataFrame: df_pnl = df.merge(df_dimensions, how="left", on="ticker_yf") - df_pnl["pnl"] = np.round( - ((df_pnl["price"] - df_pnl["dca"]) * df_pnl["shares"]).astype(float), 1 - ) + df_pnl["pnl"] = ((df_pnl["price"] - df_pnl["dca"]) * df_pnl["shares"]).astype(float) df_pnl = df_pnl.groupby(group_by)["pnl"].sum().reset_index().sort_values([group_by]) return df_pnl From 47d50b8e2ca85e0b5d3560feceb3a6ab1acb491c Mon Sep 17 00:00:00 2001 From: Vincenzo Date: Wed, 6 Nov 2024 18:36:41 +0100 Subject: [PATCH 5/6] thousands separator --- src/plot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plot.py b/src/plot.py index 95bfdfe..b993465 100644 --- a/src/plot.py +++ b/src/plot.py @@ -40,7 +40,7 @@ def plot_pnl_by_asset_class( hoverinfo="skip", ) ) - fig.update_traces(texttemplate="%{y:.1f}") + fig.update_traces(texttemplate="%{y:,.1f} €") fig.update_layout( autosize=False, height=550, From be1943e0465a9397513f61242400d1f8784bf15c Mon Sep 17 00:00:00 2001 From: Vincenzo Date: Wed, 6 Nov 2024 18:37:18 +0100 Subject: [PATCH 6/6] thousands separator, general formatting, interactive pivot table --- ...360\237\216\257_Asset_Allocation_&_PnL.py" | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git "a/src/pages/1_\360\237\216\257_Asset_Allocation_&_PnL.py" "b/src/pages/1_\360\237\216\257_Asset_Allocation_&_PnL.py" index f88276d..87b9d2b 100644 --- "a/src/pages/1_\360\237\216\257_Asset_Allocation_&_PnL.py" +++ "b/src/pages/1_\360\237\216\257_Asset_Allocation_&_PnL.py" @@ -51,18 +51,15 @@ consider_fees = st.checkbox("Take fees into account") pf_actual_value = (df_j["shares"] * df_j["price"]).sum() -if consider_fees: - pnl = pf_actual_value - expense - fees - pnl_perc = 100 * (pf_actual_value - expense - fees) / expense -else: - pnl = pf_actual_value - expense - pnl_perc = 100 * (pf_actual_value - expense) / expense +total_expense = expense + fees if consider_fees else expense +pnl = pf_actual_value - total_expense +pnl_perc = 100 * pnl / total_expense sign = "+" if pnl >= 0 else "" col_l.metric( label="Actual Portfolio Value", - value=f"{pf_actual_value: .1f} €", - delta=f"{pnl: .1f} € ({sign}{pnl_perc: .1f}%)", + value=f"{pf_actual_value: ,.1f} €", + delta=f"{pnl: ,.1f} € ({sign}{pnl_perc: .1f}%)", ) df_j["position_value"] = df_j["shares"] * df_j["price"] @@ -83,6 +80,7 @@ group_by = st.radio( label="Aggregate by:", options=["Macro Asset Classes", "Asset Classes", "Tickers"], + index=1, horizontal=True, ) df_pivot_ = get_portfolio_pivot( @@ -91,18 +89,24 @@ pf_actual_value=pf_actual_value, aggregation_level=DICT_GROUPBY_LEVELS[group_by], ) - df_pivot_.index += 1 - st.table( + st.dataframe( df_pivot_.rename( columns={ "macro_asset_class": "Macro Asset Class", "asset_class": "Asset Class", "ticker_yf": "Ticker", "name": "Name", - "position_value": "Position Value (€)", - "weight_pf": "Weight (%)", + "position_value": "Position Value", + "weight_pf": "Weight", + } + ).style.format( + { + "Position Value": "{:,.1f} €", + "Weight": "{:,.1f}%", } - ).style.format(precision=1) + ), + use_container_width=True, + hide_index=True, ) st.markdown("***")