Allocation (%) can be updated
This commit is contained in:
parent
b339dff0d7
commit
783b2580d8
@ -1135,60 +1135,20 @@ def portfolio_summary(final_alloc: pd.DataFrame) -> None:
|
|||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Calculate key metrics
|
# Use the allocation from session state for all calculations and display
|
||||||
total_capital = final_alloc["Capital Allocated ($)"].sum()
|
|
||||||
total_income = final_alloc["Income Contributed ($)"].sum()
|
|
||||||
|
|
||||||
# Calculate weighted average yield
|
|
||||||
weighted_yield = (final_alloc["Allocation (%)"] * final_alloc["Yield (%)"]).sum() / 100
|
|
||||||
|
|
||||||
# Display metrics in columns
|
|
||||||
col1, col2, col3 = st.columns(3)
|
|
||||||
|
|
||||||
with col1:
|
|
||||||
st.metric("Total Capital", format_large_number(total_capital))
|
|
||||||
|
|
||||||
with col2:
|
|
||||||
st.metric("Annual Income", format_large_number(total_income))
|
|
||||||
st.metric("Monthly Income", format_large_number(total_income/12))
|
|
||||||
|
|
||||||
with col3:
|
|
||||||
st.metric("Average Yield", f"{weighted_yield:.2f}%")
|
|
||||||
st.metric("Effective Yield", f"{(total_income/total_capital*100):.2f}%")
|
|
||||||
|
|
||||||
# Display allocation chart
|
|
||||||
fig = px.pie(
|
|
||||||
final_alloc,
|
|
||||||
values="Allocation (%)",
|
|
||||||
names="Ticker",
|
|
||||||
title="Portfolio Allocation by ETF",
|
|
||||||
hover_data={
|
|
||||||
"Ticker": True,
|
|
||||||
"Allocation (%)": ":.2f",
|
|
||||||
"Yield (%)": ":.2f",
|
|
||||||
"Capital Allocated ($)": ":,.2f",
|
|
||||||
"Income Contributed ($)": ":,.2f"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
st.plotly_chart(fig, use_container_width=True)
|
|
||||||
|
|
||||||
# Display detailed allocation table
|
|
||||||
st.subheader("Detailed Allocation")
|
|
||||||
display_df = final_alloc.copy()
|
display_df = final_alloc.copy()
|
||||||
|
numeric_columns = ["Allocation (%)", "Yield (%)", "Price", "Shares", "Capital Allocated ($)", "Income Contributed ($)"]
|
||||||
|
for col in numeric_columns:
|
||||||
|
if col in display_df.columns:
|
||||||
|
display_df[col] = pd.to_numeric(display_df[col], errors='coerce')
|
||||||
display_df["Monthly Income"] = display_df["Income Contributed ($)"] / 12
|
display_df["Monthly Income"] = display_df["Income Contributed ($)"] / 12
|
||||||
|
display_df["Capital Allocated ($)"] = display_df["Capital Allocated ($)"].apply(lambda x: f"${float(x):,.2f}")
|
||||||
# Format large numbers in the display DataFrame
|
display_df["Income Contributed ($)"] = display_df["Income Contributed ($)"].apply(lambda x: f"${float(x):,.2f}")
|
||||||
display_df["Capital Allocated ($)"] = display_df["Capital Allocated ($)"].apply(format_large_number)
|
display_df["Monthly Income"] = display_df["Monthly Income"].apply(lambda x: f"${float(x):,.2f}")
|
||||||
display_df["Income Contributed ($)"] = display_df["Income Contributed ($)"].apply(format_large_number)
|
|
||||||
display_df["Monthly Income"] = display_df["Monthly Income"].apply(format_large_number)
|
|
||||||
|
|
||||||
# Ensure data_source column exists and rename it for display
|
|
||||||
if "data_source" in display_df.columns:
|
if "data_source" in display_df.columns:
|
||||||
display_df = display_df.rename(columns={"data_source": "Data Source"})
|
display_df = display_df.rename(columns={"data_source": "Data Source"})
|
||||||
else:
|
else:
|
||||||
display_df["Data Source"] = "Unknown"
|
display_df["Data Source"] = "Unknown"
|
||||||
|
|
||||||
# Select and order columns for display
|
|
||||||
display_columns = [
|
display_columns = [
|
||||||
"Ticker",
|
"Ticker",
|
||||||
"Allocation (%)",
|
"Allocation (%)",
|
||||||
@ -1201,15 +1161,43 @@ def portfolio_summary(final_alloc: pd.DataFrame) -> None:
|
|||||||
"Risk Level",
|
"Risk Level",
|
||||||
"Data Source"
|
"Data Source"
|
||||||
]
|
]
|
||||||
|
editor_key = "allocation_editor"
|
||||||
# Format the display
|
def parse_currency(val):
|
||||||
st.dataframe(
|
if isinstance(val, str):
|
||||||
display_df[display_columns].style.format({
|
return float(val.replace('$', '').replace(',', ''))
|
||||||
"Allocation (%)": "{:.2f}%",
|
return float(val)
|
||||||
"Yield (%)": "{:.2f}%",
|
# --- Portfolio Summary Metrics (use session state allocation) ---
|
||||||
"Price": "${:,.2f}",
|
total_capital = display_df["Capital Allocated ($)"].apply(parse_currency).sum()
|
||||||
"Shares": "{:,.4f}"
|
total_income = display_df["Income Contributed ($)"].apply(parse_currency).sum()
|
||||||
}),
|
weighted_yield = (display_df["Allocation (%)"] * display_df["Yield (%)"]).sum() / 100
|
||||||
|
col1, col2, col3 = st.columns(3)
|
||||||
|
with col1:
|
||||||
|
st.metric("Total Capital", f"${total_capital:,.2f}")
|
||||||
|
with col2:
|
||||||
|
st.metric("Annual Income", f"${total_income:,.2f}")
|
||||||
|
st.metric("Monthly Income", f"${total_income/12:,.2f}")
|
||||||
|
with col3:
|
||||||
|
st.metric("Average Yield", f"{weighted_yield:.2f}%")
|
||||||
|
st.metric("Effective Yield", f"{(total_income/total_capital*100):.2f}%")
|
||||||
|
# --- Pie chart (use session state allocation) ---
|
||||||
|
fig = px.pie(
|
||||||
|
display_df,
|
||||||
|
values="Allocation (%)",
|
||||||
|
names="Ticker",
|
||||||
|
title="Portfolio Allocation by ETF",
|
||||||
|
hover_data={
|
||||||
|
"Ticker": True,
|
||||||
|
"Allocation (%)": ":.2f",
|
||||||
|
"Yield (%)": ":.2f",
|
||||||
|
"Capital Allocated ($)": ":,.2f",
|
||||||
|
"Income Contributed ($)": ":,.2f"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
st.plotly_chart(fig, use_container_width=True)
|
||||||
|
# --- Editable allocation table below the pie chart ---
|
||||||
|
st.subheader("Detailed Allocation")
|
||||||
|
edited_df = st.data_editor(
|
||||||
|
display_df[display_columns],
|
||||||
column_config={
|
column_config={
|
||||||
"Ticker": st.column_config.TextColumn("Ticker", disabled=True),
|
"Ticker": st.column_config.TextColumn("Ticker", disabled=True),
|
||||||
"Allocation (%)": st.column_config.NumberColumn(
|
"Allocation (%)": st.column_config.NumberColumn(
|
||||||
@ -1220,9 +1208,21 @@ def portfolio_summary(final_alloc: pd.DataFrame) -> None:
|
|||||||
format="%.1f",
|
format="%.1f",
|
||||||
required=True
|
required=True
|
||||||
),
|
),
|
||||||
"Yield (%)": st.column_config.TextColumn("Yield (%)", disabled=True),
|
"Yield (%)": st.column_config.NumberColumn(
|
||||||
"Price": st.column_config.TextColumn("Price", disabled=True),
|
"Yield (%)",
|
||||||
"Shares": st.column_config.TextColumn("Shares", disabled=True),
|
format="%.2f",
|
||||||
|
disabled=True
|
||||||
|
),
|
||||||
|
"Price": st.column_config.NumberColumn(
|
||||||
|
"Price",
|
||||||
|
format="%.2f",
|
||||||
|
disabled=True
|
||||||
|
),
|
||||||
|
"Shares": st.column_config.NumberColumn(
|
||||||
|
"Shares",
|
||||||
|
format="%.2f",
|
||||||
|
disabled=True
|
||||||
|
),
|
||||||
"Capital Allocated ($)": st.column_config.TextColumn("Capital Allocated ($)", disabled=True),
|
"Capital Allocated ($)": st.column_config.TextColumn("Capital Allocated ($)", disabled=True),
|
||||||
"Monthly Income": st.column_config.TextColumn("Monthly Income", disabled=True),
|
"Monthly Income": st.column_config.TextColumn("Monthly Income", disabled=True),
|
||||||
"Income Contributed ($)": st.column_config.TextColumn("Income Contributed ($)", disabled=True),
|
"Income Contributed ($)": st.column_config.TextColumn("Income Contributed ($)", disabled=True),
|
||||||
@ -1230,9 +1230,35 @@ def portfolio_summary(final_alloc: pd.DataFrame) -> None:
|
|||||||
"Data Source": st.column_config.TextColumn("Data Source", disabled=True)
|
"Data Source": st.column_config.TextColumn("Data Source", disabled=True)
|
||||||
},
|
},
|
||||||
hide_index=True,
|
hide_index=True,
|
||||||
use_container_width=True
|
use_container_width=True,
|
||||||
|
key=editor_key
|
||||||
)
|
)
|
||||||
|
edited_df = edited_df.reset_index(drop=True)
|
||||||
|
# --- Update Allocations Button ---
|
||||||
|
if st.button("Update Allocations", type="primary"):
|
||||||
|
try:
|
||||||
|
edited_df["Allocation (%)"] = pd.to_numeric(edited_df["Allocation (%)"], errors='coerce')
|
||||||
|
total_allocation = edited_df["Allocation (%)"].sum()
|
||||||
|
if abs(total_allocation - 100.0) > 0.01:
|
||||||
|
st.error(f"Total allocation must be 100%. Current total: {total_allocation:.2f}%")
|
||||||
|
else:
|
||||||
|
edited_df["Capital Allocated ($)"] = edited_df["Capital Allocated ($)"].apply(parse_currency)
|
||||||
|
edited_df["Income Contributed ($)"] = edited_df["Income Contributed ($)"].apply(parse_currency)
|
||||||
|
edited_df["Monthly Income"] = edited_df["Monthly Income"].apply(parse_currency)
|
||||||
|
total_capital = edited_df["Capital Allocated ($)"].sum()
|
||||||
|
for idx, row in edited_df.iterrows():
|
||||||
|
edited_df.at[idx, "Capital Allocated ($)"] = total_capital * (row["Allocation (%)"] / 100)
|
||||||
|
edited_df.at[idx, "Income Contributed ($)"] = edited_df.at[idx, "Capital Allocated ($)"] * (row["Yield (%)"] / 100)
|
||||||
|
edited_df.at[idx, "Monthly Income"] = edited_df.at[idx, "Income Contributed ($)"] / 12
|
||||||
|
edited_df.at[idx, "Shares"] = edited_df.at[idx, "Capital Allocated ($)"] / row["Price"]
|
||||||
|
# Update the session state with new allocations and trigger rerun
|
||||||
|
st.session_state.final_alloc = edited_df
|
||||||
|
st.success("Allocations updated successfully! Click 'Run Portfolio Simulation' to recalculate.")
|
||||||
|
st.rerun()
|
||||||
|
except Exception as e:
|
||||||
|
st.error(f"Error updating allocations: {str(e)}")
|
||||||
|
logger.error(f"Error in allocation update: {str(e)}")
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
st.error(f"Error calculating portfolio summary: {str(e)}")
|
st.error(f"Error calculating portfolio summary: {str(e)}")
|
||||||
logger.error(f"Error in portfolio_summary: {str(e)}")
|
logger.error(f"Error in portfolio_summary: {str(e)}")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user