fix: DRIP Forecast tab improvements - Fix duplicate sections in DRIP Forecast tab - Fix monthly income calculation in detailed allocation table - Add back Detailed Allocation table with improved calculations - Update DRIP vs No-DRIP comparison to use correct variable names

This commit is contained in:
Pascal BIBEHE 2025-06-02 17:22:50 +02:00
parent edf2ce5e9c
commit 163bf0e93b

View File

@ -2080,6 +2080,20 @@ def display_drip_forecast(portfolio_result, tickers):
total_income = portfolio_result.total_income total_income = portfolio_result.total_income
accumulated_cash = portfolio_result.accumulated_cash accumulated_cash = portfolio_result.accumulated_cash
# Calculate initial values
initial_investment = sum(
etf_result.initial_value
for etf_result in portfolio_result.etf_results.values()
)
initial_monthly_income = sum(
etf_result.initial_value * (etf_result.average_yield / 12)
for etf_result in portfolio_result.etf_results.values()
)
# Calculate variations
portfolio_variation = ((total_value - initial_investment) / initial_investment) * 100
monthly_income_variation = ((portfolio_result.monthly_income - initial_monthly_income) / initial_monthly_income) * 100
# Create columns for key metrics # Create columns for key metrics
col1, col2, col3 = st.columns(3) col1, col2, col3 = st.columns(3)
@ -2087,12 +2101,15 @@ def display_drip_forecast(portfolio_result, tickers):
st.metric( st.metric(
"Portfolio Value", "Portfolio Value",
f"${total_value:,.2f}", f"${total_value:,.2f}",
f"${accumulated_cash:,.2f} cash" if accumulated_cash > 0 else None f"{portfolio_variation:+.1f}%" if portfolio_variation >= 0 else f"{portfolio_variation:.1f}%",
delta_color="off" if portfolio_variation < 0 else "normal"
) )
with col2: with col2:
st.metric( st.metric(
"Monthly Income", "Monthly Income",
f"${portfolio_result.monthly_income:,.2f}" f"${portfolio_result.monthly_income:,.2f}",
f"{monthly_income_variation:+.1f}%" if monthly_income_variation >= 0 else f"{monthly_income_variation:.1f}%",
delta_color="off" if monthly_income_variation < 0 else "normal"
) )
with col3: with col3:
st.metric( st.metric(
@ -2108,7 +2125,7 @@ def display_drip_forecast(portfolio_result, tickers):
drip_service = DRIPService() drip_service = DRIPService()
# Calculate DRIP scenario # Calculate DRIP scenario
portfolio_result = drip_service.forecast_portfolio( drip_result = drip_service.forecast_portfolio(
portfolio_df=final_alloc, portfolio_df=final_alloc,
config=DripConfig( config=DripConfig(
months=12, months=12,
@ -2139,7 +2156,7 @@ def display_drip_forecast(portfolio_result, tickers):
comparison_data = { comparison_data = {
"Strategy": ["DRIP", "No-DRIP"], "Strategy": ["DRIP", "No-DRIP"],
"Portfolio Value": [ "Portfolio Value": [
portfolio_result.total_value, drip_result.total_value,
nodrip_result.total_value nodrip_result.total_value
], ],
"Accumulated Cash": [ "Accumulated Cash": [
@ -2147,7 +2164,7 @@ def display_drip_forecast(portfolio_result, tickers):
nodrip_result.accumulated_cash nodrip_result.accumulated_cash
], ],
"Total Value": [ "Total Value": [
portfolio_result.total_value, drip_result.total_value,
nodrip_result.total_value + nodrip_result.accumulated_cash nodrip_result.total_value + nodrip_result.accumulated_cash
] ]
} }
@ -2159,7 +2176,7 @@ def display_drip_forecast(portfolio_result, tickers):
fig.add_trace(go.Bar( fig.add_trace(go.Bar(
name="DRIP", name="DRIP",
x=["Portfolio Value"], x=["Portfolio Value"],
y=[portfolio_result.total_value], y=[drip_result.total_value],
marker_color="#1f77b4" marker_color="#1f77b4"
)) ))
@ -2201,12 +2218,12 @@ def display_drip_forecast(portfolio_result, tickers):
"Share Growth" "Share Growth"
], ],
"DRIP": [ "DRIP": [
f"${portfolio_result.total_value:,.2f}", f"${drip_result.total_value:,.2f}",
"$0.00", "$0.00",
f"${portfolio_result.total_value:,.2f}", f"${drip_result.total_value:,.2f}",
f"${portfolio_result.monthly_income:,.2f}", f"${drip_result.monthly_income:,.2f}",
f"${portfolio_result.total_income:,.2f}", f"${drip_result.total_income:,.2f}",
f"{((portfolio_result.etf_results[tickers[0]].final_shares / portfolio_result.etf_results[tickers[0]].initial_shares - 1) * 100):.1f}%" f"{((drip_result.etf_results[tickers[0]].final_shares / drip_result.etf_results[tickers[0]].initial_shares - 1) * 100):.1f}%"
], ],
"No-DRIP": [ "No-DRIP": [
f"${nodrip_result.total_value:,.2f}", f"${nodrip_result.total_value:,.2f}",
@ -2229,6 +2246,70 @@ def display_drip_forecast(portfolio_result, tickers):
- Portfolio value changes due to NAV erosion and share growth (DRIP) or cash accumulation (No-DRIP) - Portfolio value changes due to NAV erosion and share growth (DRIP) or cash accumulation (No-DRIP)
""") """)
# Add detailed allocation table for validation
st.subheader("Detailed Allocation")
# Create detailed allocation data
allocation_data = []
for ticker, etf_result in portfolio_result.etf_results.items():
# Get initial values
initial_value = etf_result.initial_value
initial_shares = etf_result.initial_shares
initial_yield = etf_result.average_yield
initial_monthly_income = initial_value * (initial_yield / 12)
# Get final values for comparison
final_value = etf_result.final_value
final_shares = etf_result.final_shares
final_monthly_income = final_value * (etf_result.average_yield / 12)
# Calculate variations
value_variation = ((final_value - initial_value) / initial_value) * 100
shares_variation = ((final_shares - initial_shares) / initial_shares) * 100
income_variation = ((final_monthly_income - initial_monthly_income) / initial_monthly_income) * 100
allocation_data.append({
"Ticker": ticker,
"Initial Value": f"${initial_value:,.2f}",
"Initial Shares": f"{initial_shares:,.4f}",
"Initial Monthly Income": f"${initial_monthly_income:,.2f}",
"Final Value": f"${final_value:,.2f}",
"Final Shares": f"{final_shares:,.4f}",
"Final Monthly Income": f"${final_monthly_income:,.2f}",
"Value Change": f"{value_variation:+.1f}%",
"Shares Change": f"{shares_variation:+.1f}%",
"Income Change": f"{income_variation:+.1f}%"
})
# Create DataFrame and display
allocation_df = pd.DataFrame(allocation_data)
st.dataframe(
allocation_df,
use_container_width=True,
hide_index=True,
column_config={
"Ticker": st.column_config.TextColumn("Ticker", disabled=True),
"Initial Value": st.column_config.TextColumn("Initial Value", disabled=True),
"Initial Shares": st.column_config.TextColumn("Initial Shares", disabled=True),
"Initial Monthly Income": st.column_config.TextColumn("Initial Monthly Income", disabled=True),
"Final Value": st.column_config.TextColumn("Final Value", disabled=True),
"Final Shares": st.column_config.TextColumn("Final Shares", disabled=True),
"Final Monthly Income": st.column_config.TextColumn("Final Monthly Income", disabled=True),
"Value Change": st.column_config.TextColumn("Value Change", disabled=True),
"Shares Change": st.column_config.TextColumn("Shares Change", disabled=True),
"Income Change": st.column_config.TextColumn("Income Change", disabled=True)
}
)
# Add explanation
st.info("""
**Table Explanation:**
- Initial Values: Starting values before DRIP and erosion effects
- Final Values: Values after applying DRIP and erosion effects
- Changes: Percentage variations between initial and final values
- Positive changes indicate growth, negative changes indicate erosion
""")
except Exception as e: except Exception as e:
st.error(f"Error calculating DRIP forecast: {str(e)}") st.error(f"Error calculating DRIP forecast: {str(e)}")
logger.error(f"DRIP forecast error: {str(e)}") logger.error(f"DRIP forecast error: {str(e)}")