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:
parent
edf2ce5e9c
commit
163bf0e93b
@ -2080,6 +2080,20 @@ def display_drip_forecast(portfolio_result, tickers):
|
||||
total_income = portfolio_result.total_income
|
||||
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
|
||||
col1, col2, col3 = st.columns(3)
|
||||
|
||||
@ -2087,12 +2101,15 @@ def display_drip_forecast(portfolio_result, tickers):
|
||||
st.metric(
|
||||
"Portfolio Value",
|
||||
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:
|
||||
st.metric(
|
||||
"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:
|
||||
st.metric(
|
||||
@ -2108,7 +2125,7 @@ def display_drip_forecast(portfolio_result, tickers):
|
||||
drip_service = DRIPService()
|
||||
|
||||
# Calculate DRIP scenario
|
||||
portfolio_result = drip_service.forecast_portfolio(
|
||||
drip_result = drip_service.forecast_portfolio(
|
||||
portfolio_df=final_alloc,
|
||||
config=DripConfig(
|
||||
months=12,
|
||||
@ -2139,7 +2156,7 @@ def display_drip_forecast(portfolio_result, tickers):
|
||||
comparison_data = {
|
||||
"Strategy": ["DRIP", "No-DRIP"],
|
||||
"Portfolio Value": [
|
||||
portfolio_result.total_value,
|
||||
drip_result.total_value,
|
||||
nodrip_result.total_value
|
||||
],
|
||||
"Accumulated Cash": [
|
||||
@ -2147,7 +2164,7 @@ def display_drip_forecast(portfolio_result, tickers):
|
||||
nodrip_result.accumulated_cash
|
||||
],
|
||||
"Total Value": [
|
||||
portfolio_result.total_value,
|
||||
drip_result.total_value,
|
||||
nodrip_result.total_value + nodrip_result.accumulated_cash
|
||||
]
|
||||
}
|
||||
@ -2159,7 +2176,7 @@ def display_drip_forecast(portfolio_result, tickers):
|
||||
fig.add_trace(go.Bar(
|
||||
name="DRIP",
|
||||
x=["Portfolio Value"],
|
||||
y=[portfolio_result.total_value],
|
||||
y=[drip_result.total_value],
|
||||
marker_color="#1f77b4"
|
||||
))
|
||||
|
||||
@ -2201,12 +2218,12 @@ def display_drip_forecast(portfolio_result, tickers):
|
||||
"Share Growth"
|
||||
],
|
||||
"DRIP": [
|
||||
f"${portfolio_result.total_value:,.2f}",
|
||||
f"${drip_result.total_value:,.2f}",
|
||||
"$0.00",
|
||||
f"${portfolio_result.total_value:,.2f}",
|
||||
f"${portfolio_result.monthly_income:,.2f}",
|
||||
f"${portfolio_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.total_value:,.2f}",
|
||||
f"${drip_result.monthly_income:,.2f}",
|
||||
f"${drip_result.total_income:,.2f}",
|
||||
f"{((drip_result.etf_results[tickers[0]].final_shares / drip_result.etf_results[tickers[0]].initial_shares - 1) * 100):.1f}%"
|
||||
],
|
||||
"No-DRIP": [
|
||||
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)
|
||||
""")
|
||||
|
||||
# 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:
|
||||
st.error(f"Error calculating DRIP forecast: {str(e)}")
|
||||
logger.error(f"DRIP forecast error: {str(e)}")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user