Allocation (%) can be updated
This commit is contained in:
parent
b339dff0d7
commit
783b2580d8
@ -1133,62 +1133,22 @@ def portfolio_summary(final_alloc: pd.DataFrame) -> None:
|
||||
if final_alloc is None or final_alloc.empty:
|
||||
st.warning("No portfolio data available.")
|
||||
return
|
||||
|
||||
|
||||
try:
|
||||
# Calculate key metrics
|
||||
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")
|
||||
# Use the allocation from session state for all calculations and display
|
||||
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
|
||||
|
||||
# Format large numbers in the display DataFrame
|
||||
display_df["Capital Allocated ($)"] = display_df["Capital Allocated ($)"].apply(format_large_number)
|
||||
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
|
||||
display_df["Capital Allocated ($)"] = display_df["Capital Allocated ($)"].apply(lambda x: f"${float(x):,.2f}")
|
||||
display_df["Income Contributed ($)"] = display_df["Income Contributed ($)"].apply(lambda x: f"${float(x):,.2f}")
|
||||
display_df["Monthly Income"] = display_df["Monthly Income"].apply(lambda x: f"${float(x):,.2f}")
|
||||
if "data_source" in display_df.columns:
|
||||
display_df = display_df.rename(columns={"data_source": "Data Source"})
|
||||
else:
|
||||
display_df["Data Source"] = "Unknown"
|
||||
|
||||
# Select and order columns for display
|
||||
display_columns = [
|
||||
"Ticker",
|
||||
"Allocation (%)",
|
||||
@ -1201,15 +1161,43 @@ def portfolio_summary(final_alloc: pd.DataFrame) -> None:
|
||||
"Risk Level",
|
||||
"Data Source"
|
||||
]
|
||||
|
||||
# Format the display
|
||||
st.dataframe(
|
||||
display_df[display_columns].style.format({
|
||||
"Allocation (%)": "{:.2f}%",
|
||||
"Yield (%)": "{:.2f}%",
|
||||
"Price": "${:,.2f}",
|
||||
"Shares": "{:,.4f}"
|
||||
}),
|
||||
editor_key = "allocation_editor"
|
||||
def parse_currency(val):
|
||||
if isinstance(val, str):
|
||||
return float(val.replace('$', '').replace(',', ''))
|
||||
return float(val)
|
||||
# --- Portfolio Summary Metrics (use session state allocation) ---
|
||||
total_capital = display_df["Capital Allocated ($)"].apply(parse_currency).sum()
|
||||
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={
|
||||
"Ticker": st.column_config.TextColumn("Ticker", disabled=True),
|
||||
"Allocation (%)": st.column_config.NumberColumn(
|
||||
@ -1220,9 +1208,21 @@ def portfolio_summary(final_alloc: pd.DataFrame) -> None:
|
||||
format="%.1f",
|
||||
required=True
|
||||
),
|
||||
"Yield (%)": st.column_config.TextColumn("Yield (%)", disabled=True),
|
||||
"Price": st.column_config.TextColumn("Price", disabled=True),
|
||||
"Shares": st.column_config.TextColumn("Shares", disabled=True),
|
||||
"Yield (%)": st.column_config.NumberColumn(
|
||||
"Yield (%)",
|
||||
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),
|
||||
"Monthly Income": st.column_config.TextColumn("Monthly Income", 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)
|
||||
},
|
||||
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:
|
||||
st.error(f"Error calculating portfolio summary: {str(e)}")
|
||||
logger.error(f"Error in portfolio_summary: {str(e)}")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user