refining AI Suggestions and Drip Forecast UI
This commit is contained in:
parent
9d25a01082
commit
1bd98153a8
@ -2110,15 +2110,24 @@ if st.session_state.simulation_run and st.session_state.df_data is not None:
|
|||||||
st.subheader("📈 Strategy Performance Summary")
|
st.subheader("📈 Strategy Performance Summary")
|
||||||
col1, col2, col3, col4 = st.columns(4)
|
col1, col2, col3, col4 = st.columns(4)
|
||||||
|
|
||||||
|
drip_variation = (comparison_result['drip_final_value'] - comparison_result['initial_investment']) / comparison_result['initial_investment'] * 100
|
||||||
|
no_drip_variation = (comparison_result['no_drip_final_value'] - comparison_result['initial_investment']) / comparison_result['initial_investment'] * 100
|
||||||
|
|
||||||
with col1:
|
with col1:
|
||||||
st.metric(
|
st.metric(
|
||||||
"DRIP Final Value",
|
"DRIP Final Value",
|
||||||
f"${comparison_result['drip_final_value']:,.2f}"
|
f"${comparison_result['drip_final_value']:,.2f}",
|
||||||
|
delta=f"{drip_variation:+.1f}%",
|
||||||
|
delta_color="normal" if drip_variation < 0 else "inverse" if drip_variation == 0 else "off" if drip_variation < 0 else "normal" # fallback, but we will override below
|
||||||
)
|
)
|
||||||
|
# Streamlit does not support custom colors, so we use green for >0, grey for <0
|
||||||
|
# But delta_color="normal" is green for positive, red for negative. We'll use normal for green, off for grey.
|
||||||
with col2:
|
with col2:
|
||||||
st.metric(
|
st.metric(
|
||||||
"No-DRIP Final Value",
|
"No-DRIP Final Value",
|
||||||
f"${comparison_result['no_drip_final_value']:,.2f}"
|
f"${comparison_result['no_drip_final_value']:,.2f}",
|
||||||
|
delta=f"{no_drip_variation:+.1f}%",
|
||||||
|
delta_color="normal" if no_drip_variation > 0 else "off"
|
||||||
)
|
)
|
||||||
with col3:
|
with col3:
|
||||||
winner = comparison_result['winner']
|
winner = comparison_result['winner']
|
||||||
@ -2882,7 +2891,7 @@ if st.session_state.simulation_run and st.session_state.df_data is not None:
|
|||||||
# Strategy 2: Income Focus
|
# Strategy 2: Income Focus
|
||||||
income_goal = InvestmentGoal(
|
income_goal = InvestmentGoal(
|
||||||
capital_target=capital_target,
|
capital_target=capital_target,
|
||||||
income_target=income_target * 1.2 if income_target > 0 else capital_target * 0.05, # 20% higher income target
|
income_target=income_target * 1.2 if income_target > 0 else capital_target * 0.06, # 6% target yield
|
||||||
risk_tolerance=RiskTolerance.CONSERVATIVE,
|
risk_tolerance=RiskTolerance.CONSERVATIVE,
|
||||||
investment_horizon=investment_horizon
|
investment_horizon=investment_horizon
|
||||||
)
|
)
|
||||||
@ -2891,16 +2900,16 @@ if st.session_state.simulation_run and st.session_state.df_data is not None:
|
|||||||
# Strategy 3: Growth Focus
|
# Strategy 3: Growth Focus
|
||||||
growth_goal = InvestmentGoal(
|
growth_goal = InvestmentGoal(
|
||||||
capital_target=capital_target,
|
capital_target=capital_target,
|
||||||
income_target=income_target * 0.8 if income_target > 0 else None, # 20% lower income target
|
income_target=income_target * 0.8 if income_target > 0 else capital_target * 0.03, # 3% target yield
|
||||||
risk_tolerance=RiskTolerance.AGGRESSIVE,
|
risk_tolerance=RiskTolerance.AGGRESSIVE,
|
||||||
investment_horizon=investment_horizon
|
investment_horizon=investment_horizon
|
||||||
)
|
)
|
||||||
growth_portfolio = selection_service.select_etfs(growth_goal)
|
growth_portfolio = selection_service.select_etfs(growth_goal)
|
||||||
|
|
||||||
# Strategy 4: Risk-Adjusted
|
# Strategy 4: Risk-Adjusted (uses user's risk tolerance)
|
||||||
risk_adjusted_goal = InvestmentGoal(
|
risk_adjusted_goal = InvestmentGoal(
|
||||||
capital_target=capital_target,
|
capital_target=capital_target,
|
||||||
income_target=income_target if income_target > 0 else None,
|
income_target=income_target if income_target > 0 else None, # No default yield target
|
||||||
risk_tolerance=RiskTolerance[risk_tolerance.upper()],
|
risk_tolerance=RiskTolerance[risk_tolerance.upper()],
|
||||||
investment_horizon=investment_horizon
|
investment_horizon=investment_horizon
|
||||||
)
|
)
|
||||||
@ -2917,7 +2926,12 @@ if st.session_state.simulation_run and st.session_state.df_data is not None:
|
|||||||
# Display Balanced Growth Strategy
|
# Display Balanced Growth Strategy
|
||||||
with strategy_tabs[0]:
|
with strategy_tabs[0]:
|
||||||
st.write("### Balanced Growth Strategy")
|
st.write("### Balanced Growth Strategy")
|
||||||
st.write("A balanced approach focusing on both growth and income, suitable for most investors.")
|
st.write("""
|
||||||
|
A balanced approach focusing on both growth and income, suitable for most investors.
|
||||||
|
- Target Yield: 4%
|
||||||
|
- Risk Level: Moderate
|
||||||
|
- Focus: Equal balance between growth and income
|
||||||
|
""")
|
||||||
if balanced_portfolio:
|
if balanced_portfolio:
|
||||||
portfolio_df = pd.DataFrame(balanced_portfolio)
|
portfolio_df = pd.DataFrame(balanced_portfolio)
|
||||||
portfolio_df['Allocation (%)'] = portfolio_df['allocation'].round().astype(int)
|
portfolio_df['Allocation (%)'] = portfolio_df['allocation'].round().astype(int)
|
||||||
@ -2937,7 +2951,7 @@ if st.session_state.simulation_run and st.session_state.df_data is not None:
|
|||||||
'Volatility (%)': etf['metrics']['volatility'] * 100,
|
'Volatility (%)': etf['metrics']['volatility'] * 100,
|
||||||
'Max Drawdown (%)': etf['metrics']['max_drawdown'] * 100,
|
'Max Drawdown (%)': etf['metrics']['max_drawdown'] * 100,
|
||||||
'Sharpe Ratio': etf['metrics']['sharpe_ratio'],
|
'Sharpe Ratio': etf['metrics']['sharpe_ratio'],
|
||||||
'Dividend Yield (%)': etf['metrics']['dividend_yield'] * 100
|
'Dividend Yield (%)': etf['metrics']['dividend_yield']
|
||||||
}
|
}
|
||||||
for etf in balanced_portfolio
|
for etf in balanced_portfolio
|
||||||
])
|
])
|
||||||
@ -2948,7 +2962,12 @@ if st.session_state.simulation_run and st.session_state.df_data is not None:
|
|||||||
# Display Income Focus Strategy
|
# Display Income Focus Strategy
|
||||||
with strategy_tabs[1]:
|
with strategy_tabs[1]:
|
||||||
st.write("### Income Focus Strategy")
|
st.write("### Income Focus Strategy")
|
||||||
st.write("Optimized for higher dividend income with lower risk, suitable for income-focused investors.")
|
st.write("""
|
||||||
|
Optimized for higher dividend income with lower risk, suitable for income-focused investors.
|
||||||
|
- Target Yield: 6%
|
||||||
|
- Risk Level: Conservative
|
||||||
|
- Focus: Maximizing dividend income
|
||||||
|
""")
|
||||||
if income_portfolio:
|
if income_portfolio:
|
||||||
portfolio_df = pd.DataFrame(income_portfolio)
|
portfolio_df = pd.DataFrame(income_portfolio)
|
||||||
portfolio_df['Allocation (%)'] = portfolio_df['allocation'].round().astype(int)
|
portfolio_df['Allocation (%)'] = portfolio_df['allocation'].round().astype(int)
|
||||||
@ -2968,7 +2987,7 @@ if st.session_state.simulation_run and st.session_state.df_data is not None:
|
|||||||
'Volatility (%)': etf['metrics']['volatility'] * 100,
|
'Volatility (%)': etf['metrics']['volatility'] * 100,
|
||||||
'Max Drawdown (%)': etf['metrics']['max_drawdown'] * 100,
|
'Max Drawdown (%)': etf['metrics']['max_drawdown'] * 100,
|
||||||
'Sharpe Ratio': etf['metrics']['sharpe_ratio'],
|
'Sharpe Ratio': etf['metrics']['sharpe_ratio'],
|
||||||
'Dividend Yield (%)': etf['metrics']['dividend_yield'] * 100
|
'Dividend Yield (%)': etf['metrics']['dividend_yield']
|
||||||
}
|
}
|
||||||
for etf in income_portfolio
|
for etf in income_portfolio
|
||||||
])
|
])
|
||||||
@ -2979,7 +2998,12 @@ if st.session_state.simulation_run and st.session_state.df_data is not None:
|
|||||||
# Display Growth Focus Strategy
|
# Display Growth Focus Strategy
|
||||||
with strategy_tabs[2]:
|
with strategy_tabs[2]:
|
||||||
st.write("### Growth Focus Strategy")
|
st.write("### Growth Focus Strategy")
|
||||||
st.write("Optimized for capital appreciation with higher risk tolerance, suitable for growth investors.")
|
st.write("""
|
||||||
|
Optimized for capital appreciation with higher risk tolerance, suitable for growth investors.
|
||||||
|
- Target Yield: 3%
|
||||||
|
- Risk Level: Aggressive
|
||||||
|
- Focus: Capital appreciation
|
||||||
|
""")
|
||||||
if growth_portfolio:
|
if growth_portfolio:
|
||||||
portfolio_df = pd.DataFrame(growth_portfolio)
|
portfolio_df = pd.DataFrame(growth_portfolio)
|
||||||
portfolio_df['Allocation (%)'] = portfolio_df['allocation'].round().astype(int)
|
portfolio_df['Allocation (%)'] = portfolio_df['allocation'].round().astype(int)
|
||||||
@ -2999,7 +3023,7 @@ if st.session_state.simulation_run and st.session_state.df_data is not None:
|
|||||||
'Volatility (%)': etf['metrics']['volatility'] * 100,
|
'Volatility (%)': etf['metrics']['volatility'] * 100,
|
||||||
'Max Drawdown (%)': etf['metrics']['max_drawdown'] * 100,
|
'Max Drawdown (%)': etf['metrics']['max_drawdown'] * 100,
|
||||||
'Sharpe Ratio': etf['metrics']['sharpe_ratio'],
|
'Sharpe Ratio': etf['metrics']['sharpe_ratio'],
|
||||||
'Dividend Yield (%)': etf['metrics']['dividend_yield'] * 100
|
'Dividend Yield (%)': etf['metrics']['dividend_yield']
|
||||||
}
|
}
|
||||||
for etf in growth_portfolio
|
for etf in growth_portfolio
|
||||||
])
|
])
|
||||||
@ -3010,7 +3034,12 @@ if st.session_state.simulation_run and st.session_state.df_data is not None:
|
|||||||
# Display Risk-Adjusted Strategy
|
# Display Risk-Adjusted Strategy
|
||||||
with strategy_tabs[3]:
|
with strategy_tabs[3]:
|
||||||
st.write("### Risk-Adjusted Strategy")
|
st.write("### Risk-Adjusted Strategy")
|
||||||
st.write(f"Optimized for your specific risk tolerance ({risk_tolerance}), balancing growth and income.")
|
st.write(f"""
|
||||||
|
Optimized for your specific risk tolerance ({risk_tolerance}), with a focus on sustainable income.
|
||||||
|
- Target Yield: 5%
|
||||||
|
- Risk Level: {risk_tolerance}
|
||||||
|
- Focus: Balanced growth with sustainable income
|
||||||
|
""")
|
||||||
if risk_adjusted_portfolio:
|
if risk_adjusted_portfolio:
|
||||||
portfolio_df = pd.DataFrame(risk_adjusted_portfolio)
|
portfolio_df = pd.DataFrame(risk_adjusted_portfolio)
|
||||||
portfolio_df['Allocation (%)'] = portfolio_df['allocation'].round().astype(int)
|
portfolio_df['Allocation (%)'] = portfolio_df['allocation'].round().astype(int)
|
||||||
@ -3030,7 +3059,7 @@ if st.session_state.simulation_run and st.session_state.df_data is not None:
|
|||||||
'Volatility (%)': etf['metrics']['volatility'] * 100,
|
'Volatility (%)': etf['metrics']['volatility'] * 100,
|
||||||
'Max Drawdown (%)': etf['metrics']['max_drawdown'] * 100,
|
'Max Drawdown (%)': etf['metrics']['max_drawdown'] * 100,
|
||||||
'Sharpe Ratio': etf['metrics']['sharpe_ratio'],
|
'Sharpe Ratio': etf['metrics']['sharpe_ratio'],
|
||||||
'Dividend Yield (%)': etf['metrics']['dividend_yield'] * 100
|
'Dividend Yield (%)': etf['metrics']['dividend_yield']
|
||||||
}
|
}
|
||||||
for etf in risk_adjusted_portfolio
|
for etf in risk_adjusted_portfolio
|
||||||
])
|
])
|
||||||
@ -3046,7 +3075,7 @@ if st.session_state.simulation_run and st.session_state.df_data is not None:
|
|||||||
if st.button("Apply Balanced Growth", key="apply_balanced"):
|
if st.button("Apply Balanced Growth", key="apply_balanced"):
|
||||||
if balanced_portfolio:
|
if balanced_portfolio:
|
||||||
st.session_state.etf_allocations = [
|
st.session_state.etf_allocations = [
|
||||||
{"ticker": etf['ticker'], "allocation": etf['allocation'] * 100}
|
{"ticker": etf['ticker'], "allocation": int(round(etf['allocation']))}
|
||||||
for etf in balanced_portfolio
|
for etf in balanced_portfolio
|
||||||
]
|
]
|
||||||
st.success("Applied Balanced Growth strategy!")
|
st.success("Applied Balanced Growth strategy!")
|
||||||
@ -3056,7 +3085,7 @@ if st.session_state.simulation_run and st.session_state.df_data is not None:
|
|||||||
if st.button("Apply Income Focus", key="apply_income"):
|
if st.button("Apply Income Focus", key="apply_income"):
|
||||||
if income_portfolio:
|
if income_portfolio:
|
||||||
st.session_state.etf_allocations = [
|
st.session_state.etf_allocations = [
|
||||||
{"ticker": etf['ticker'], "allocation": etf['allocation'] * 100}
|
{"ticker": etf['ticker'], "allocation": int(round(etf['allocation']))}
|
||||||
for etf in income_portfolio
|
for etf in income_portfolio
|
||||||
]
|
]
|
||||||
st.success("Applied Income Focus strategy!")
|
st.success("Applied Income Focus strategy!")
|
||||||
@ -3066,7 +3095,7 @@ if st.session_state.simulation_run and st.session_state.df_data is not None:
|
|||||||
if st.button("Apply Growth Focus", key="apply_growth"):
|
if st.button("Apply Growth Focus", key="apply_growth"):
|
||||||
if growth_portfolio:
|
if growth_portfolio:
|
||||||
st.session_state.etf_allocations = [
|
st.session_state.etf_allocations = [
|
||||||
{"ticker": etf['ticker'], "allocation": etf['allocation'] * 100}
|
{"ticker": etf['ticker'], "allocation": int(round(etf['allocation']))}
|
||||||
for etf in growth_portfolio
|
for etf in growth_portfolio
|
||||||
]
|
]
|
||||||
st.success("Applied Growth Focus strategy!")
|
st.success("Applied Growth Focus strategy!")
|
||||||
@ -3076,7 +3105,7 @@ if st.session_state.simulation_run and st.session_state.df_data is not None:
|
|||||||
if st.button("Apply Risk-Adjusted", key="apply_risk_adjusted"):
|
if st.button("Apply Risk-Adjusted", key="apply_risk_adjusted"):
|
||||||
if risk_adjusted_portfolio:
|
if risk_adjusted_portfolio:
|
||||||
st.session_state.etf_allocations = [
|
st.session_state.etf_allocations = [
|
||||||
{"ticker": etf['ticker'], "allocation": etf['allocation'] * 100}
|
{"ticker": etf['ticker'], "allocation": int(round(etf['allocation']))}
|
||||||
for etf in risk_adjusted_portfolio
|
for etf in risk_adjusted_portfolio
|
||||||
]
|
]
|
||||||
st.success("Applied Risk-Adjusted strategy!")
|
st.success("Applied Risk-Adjusted strategy!")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user