updating the AI Suggestions yab
This commit is contained in:
parent
f7cf624721
commit
27ef418f84
@ -1659,45 +1659,28 @@ def remove_ticker(ticker_to_remove: str) -> None:
|
|||||||
# Display current tickers in the main space
|
# Display current tickers in the main space
|
||||||
if st.session_state.etf_allocations:
|
if st.session_state.etf_allocations:
|
||||||
st.subheader("Selected ETFs")
|
st.subheader("Selected ETFs")
|
||||||
st.markdown("""
|
|
||||||
<style>
|
|
||||||
.ticker-container {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 8px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
.ticker-item {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
color: #2ecc71;
|
|
||||||
font-size: 1.1em;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
.ticker-close {
|
|
||||||
margin-left: 2px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 1.1em;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
.ticker-close:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
""", unsafe_allow_html=True)
|
|
||||||
|
|
||||||
# Create a container for tickers
|
# Create four columns for the tables with smaller widths
|
||||||
ticker_container = st.container()
|
col1, col2, col3, col4 = st.columns([1, 1, 1, 1])
|
||||||
with ticker_container:
|
|
||||||
# Display each ticker with a close button
|
# Split the ETFs into four groups
|
||||||
for etf in st.session_state.etf_allocations:
|
etf_groups = [[] for _ in range(4)]
|
||||||
col1, col2 = st.columns([0.05, 0.95]) # Adjusted column ratio
|
for i, etf in enumerate(st.session_state.etf_allocations):
|
||||||
with col1:
|
group_index = i % 4
|
||||||
|
etf_groups[group_index].append(etf)
|
||||||
|
|
||||||
|
# Display each group in its own column
|
||||||
|
for i, (col, etf_group) in enumerate(zip([col1, col2, col3, col4], etf_groups)):
|
||||||
|
with col:
|
||||||
|
for etf in etf_group:
|
||||||
|
st.markdown(f"""
|
||||||
|
<div style="display: flex; align-items: center; gap: 2px; margin-bottom: -15px;">
|
||||||
|
<span style="color: #2ecc71; font-weight: 500;">{etf['ticker']}</span>
|
||||||
|
</div>
|
||||||
|
""", unsafe_allow_html=True)
|
||||||
if st.button("×", key=f"remove_{etf['ticker']}",
|
if st.button("×", key=f"remove_{etf['ticker']}",
|
||||||
help=f"Remove {etf['ticker']} from portfolio"):
|
help=f"Remove {etf['ticker']} from portfolio"):
|
||||||
remove_ticker(etf['ticker'])
|
remove_ticker(etf['ticker'])
|
||||||
with col2:
|
|
||||||
st.markdown(f"<div class='ticker-item'>{etf['ticker']}</div>", unsafe_allow_html=True)
|
|
||||||
|
|
||||||
# Debug information
|
# Debug information
|
||||||
logger.info("=== Session State Debug ===")
|
logger.info("=== Session State Debug ===")
|
||||||
@ -1829,7 +1812,15 @@ with st.sidebar:
|
|||||||
|
|
||||||
# Create a container for ETF input
|
# Create a container for ETF input
|
||||||
with st.container():
|
with st.container():
|
||||||
# Input field for ETF ticker only
|
# Input field for ETF ticker with improved visibility
|
||||||
|
st.markdown("""
|
||||||
|
<style>
|
||||||
|
.stTextInput input {
|
||||||
|
color: #2ecc71 !important;
|
||||||
|
font-weight: 500 !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
""", unsafe_allow_html=True)
|
||||||
new_ticker = st.text_input("ETF Ticker", help="Enter a valid ETF ticker (e.g., SCHD)")
|
new_ticker = st.text_input("ETF Ticker", help="Enter a valid ETF ticker (e.g., SCHD)")
|
||||||
|
|
||||||
# Add button to add ETF
|
# Add button to add ETF
|
||||||
@ -2872,29 +2863,68 @@ if st.session_state.simulation_run and st.session_state.df_data is not None:
|
|||||||
investment_horizon=investment_horizon
|
investment_horizon=investment_horizon
|
||||||
)
|
)
|
||||||
|
|
||||||
# Get AI suggestions
|
# Get AI suggestions for different strategies
|
||||||
with st.spinner("Analyzing ETFs and generating portfolio suggestions..."):
|
with st.spinner("Analyzing ETFs and generating portfolio suggestions..."):
|
||||||
try:
|
try:
|
||||||
portfolio = selection_service.select_etfs(goal)
|
# Strategy 1: Balanced Growth
|
||||||
|
balanced_goal = InvestmentGoal(
|
||||||
|
capital_target=capital_target,
|
||||||
|
income_target=income_target if income_target > 0 else None,
|
||||||
|
risk_tolerance=RiskTolerance.MODERATE,
|
||||||
|
investment_horizon=investment_horizon
|
||||||
|
)
|
||||||
|
balanced_portfolio = selection_service.select_etfs(balanced_goal)
|
||||||
|
|
||||||
if portfolio:
|
# Strategy 2: Income Focus
|
||||||
# Display portfolio suggestions
|
income_goal = InvestmentGoal(
|
||||||
st.success("Portfolio suggestions generated successfully!")
|
capital_target=capital_target,
|
||||||
|
income_target=income_target * 1.2 if income_target > 0 else capital_target * 0.05, # 20% higher income target
|
||||||
|
risk_tolerance=RiskTolerance.CONSERVATIVE,
|
||||||
|
investment_horizon=investment_horizon
|
||||||
|
)
|
||||||
|
income_portfolio = selection_service.select_etfs(income_goal)
|
||||||
|
|
||||||
# Create a DataFrame for better display
|
# Strategy 3: Growth Focus
|
||||||
portfolio_df = pd.DataFrame(portfolio)
|
growth_goal = InvestmentGoal(
|
||||||
|
capital_target=capital_target,
|
||||||
|
income_target=income_target * 0.8 if income_target > 0 else None, # 20% lower income target
|
||||||
|
risk_tolerance=RiskTolerance.AGGRESSIVE,
|
||||||
|
investment_horizon=investment_horizon
|
||||||
|
)
|
||||||
|
growth_portfolio = selection_service.select_etfs(growth_goal)
|
||||||
|
|
||||||
|
# Strategy 4: Risk-Adjusted
|
||||||
|
risk_adjusted_goal = InvestmentGoal(
|
||||||
|
capital_target=capital_target,
|
||||||
|
income_target=income_target if income_target > 0 else None,
|
||||||
|
risk_tolerance=RiskTolerance[risk_tolerance.upper()],
|
||||||
|
investment_horizon=investment_horizon
|
||||||
|
)
|
||||||
|
risk_adjusted_portfolio = selection_service.select_etfs(risk_adjusted_goal)
|
||||||
|
|
||||||
|
# Create tabs for each strategy
|
||||||
|
strategy_tabs = st.tabs([
|
||||||
|
"🔄 Balanced Growth",
|
||||||
|
"💰 Income Focus",
|
||||||
|
"📈 Growth Focus",
|
||||||
|
"⚖️ Risk-Adjusted"
|
||||||
|
])
|
||||||
|
|
||||||
|
# Display Balanced Growth Strategy
|
||||||
|
with strategy_tabs[0]:
|
||||||
|
st.write("### Balanced Growth Strategy")
|
||||||
|
st.write("A balanced approach focusing on both growth and income, suitable for most investors.")
|
||||||
|
if balanced_portfolio:
|
||||||
|
portfolio_df = pd.DataFrame(balanced_portfolio)
|
||||||
portfolio_df['Allocation (%)'] = portfolio_df['allocation'] * 100
|
portfolio_df['Allocation (%)'] = portfolio_df['allocation'] * 100
|
||||||
portfolio_df['Amount ($)'] = portfolio_df['amount']
|
portfolio_df['Amount ($)'] = portfolio_df['amount']
|
||||||
|
|
||||||
# Display portfolio summary
|
|
||||||
st.write("### Portfolio Summary")
|
|
||||||
st.dataframe(
|
st.dataframe(
|
||||||
portfolio_df[['ticker', 'name', 'Allocation (%)', 'Amount ($)']],
|
portfolio_df[['ticker', 'name', 'Allocation (%)', 'Amount ($)']],
|
||||||
hide_index=True
|
hide_index=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# Display detailed metrics
|
# Display metrics
|
||||||
st.write("### Detailed Metrics")
|
|
||||||
metrics_df = pd.DataFrame([
|
metrics_df = pd.DataFrame([
|
||||||
{
|
{
|
||||||
'Ticker': etf['ticker'],
|
'Ticker': etf['ticker'],
|
||||||
@ -2905,21 +2935,148 @@ if st.session_state.simulation_run and st.session_state.df_data is not None:
|
|||||||
'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'] * 100
|
||||||
}
|
}
|
||||||
for etf in portfolio
|
for etf in balanced_portfolio
|
||||||
])
|
])
|
||||||
st.dataframe(metrics_df, hide_index=True)
|
st.dataframe(metrics_df, hide_index=True)
|
||||||
|
|
||||||
# Display portfolio allocation chart
|
|
||||||
st.write("### Portfolio Allocation")
|
|
||||||
fig = px.pie(
|
|
||||||
portfolio_df,
|
|
||||||
values='Allocation (%)',
|
|
||||||
names='ticker',
|
|
||||||
title='Portfolio Allocation by ETF'
|
|
||||||
)
|
|
||||||
st.plotly_chart(fig)
|
|
||||||
else:
|
else:
|
||||||
st.error("No portfolio suggestions could be generated. Please try different parameters.")
|
st.error("Could not generate balanced growth portfolio.")
|
||||||
|
|
||||||
|
# Display Income Focus Strategy
|
||||||
|
with strategy_tabs[1]:
|
||||||
|
st.write("### Income Focus Strategy")
|
||||||
|
st.write("Optimized for higher dividend income with lower risk, suitable for income-focused investors.")
|
||||||
|
if income_portfolio:
|
||||||
|
portfolio_df = pd.DataFrame(income_portfolio)
|
||||||
|
portfolio_df['Allocation (%)'] = portfolio_df['allocation'] * 100
|
||||||
|
portfolio_df['Amount ($)'] = portfolio_df['amount']
|
||||||
|
|
||||||
|
st.dataframe(
|
||||||
|
portfolio_df[['ticker', 'name', 'Allocation (%)', 'Amount ($)']],
|
||||||
|
hide_index=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Display metrics
|
||||||
|
metrics_df = pd.DataFrame([
|
||||||
|
{
|
||||||
|
'Ticker': etf['ticker'],
|
||||||
|
'Expense Ratio (%)': etf['metrics']['expense_ratio'] * 100,
|
||||||
|
'AUM ($B)': etf['metrics']['aum'] / 1e9,
|
||||||
|
'Volatility (%)': etf['metrics']['volatility'] * 100,
|
||||||
|
'Max Drawdown (%)': etf['metrics']['max_drawdown'] * 100,
|
||||||
|
'Sharpe Ratio': etf['metrics']['sharpe_ratio'],
|
||||||
|
'Dividend Yield (%)': etf['metrics']['dividend_yield'] * 100
|
||||||
|
}
|
||||||
|
for etf in income_portfolio
|
||||||
|
])
|
||||||
|
st.dataframe(metrics_df, hide_index=True)
|
||||||
|
else:
|
||||||
|
st.error("Could not generate income focus portfolio.")
|
||||||
|
|
||||||
|
# Display Growth Focus Strategy
|
||||||
|
with strategy_tabs[2]:
|
||||||
|
st.write("### Growth Focus Strategy")
|
||||||
|
st.write("Optimized for capital appreciation with higher risk tolerance, suitable for growth investors.")
|
||||||
|
if growth_portfolio:
|
||||||
|
portfolio_df = pd.DataFrame(growth_portfolio)
|
||||||
|
portfolio_df['Allocation (%)'] = portfolio_df['allocation'] * 100
|
||||||
|
portfolio_df['Amount ($)'] = portfolio_df['amount']
|
||||||
|
|
||||||
|
st.dataframe(
|
||||||
|
portfolio_df[['ticker', 'name', 'Allocation (%)', 'Amount ($)']],
|
||||||
|
hide_index=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Display metrics
|
||||||
|
metrics_df = pd.DataFrame([
|
||||||
|
{
|
||||||
|
'Ticker': etf['ticker'],
|
||||||
|
'Expense Ratio (%)': etf['metrics']['expense_ratio'] * 100,
|
||||||
|
'AUM ($B)': etf['metrics']['aum'] / 1e9,
|
||||||
|
'Volatility (%)': etf['metrics']['volatility'] * 100,
|
||||||
|
'Max Drawdown (%)': etf['metrics']['max_drawdown'] * 100,
|
||||||
|
'Sharpe Ratio': etf['metrics']['sharpe_ratio'],
|
||||||
|
'Dividend Yield (%)': etf['metrics']['dividend_yield'] * 100
|
||||||
|
}
|
||||||
|
for etf in growth_portfolio
|
||||||
|
])
|
||||||
|
st.dataframe(metrics_df, hide_index=True)
|
||||||
|
else:
|
||||||
|
st.error("Could not generate growth focus portfolio.")
|
||||||
|
|
||||||
|
# Display Risk-Adjusted Strategy
|
||||||
|
with strategy_tabs[3]:
|
||||||
|
st.write("### Risk-Adjusted Strategy")
|
||||||
|
st.write(f"Optimized for your specific risk tolerance ({risk_tolerance}), balancing growth and income.")
|
||||||
|
if risk_adjusted_portfolio:
|
||||||
|
portfolio_df = pd.DataFrame(risk_adjusted_portfolio)
|
||||||
|
portfolio_df['Allocation (%)'] = portfolio_df['allocation'] * 100
|
||||||
|
portfolio_df['Amount ($)'] = portfolio_df['amount']
|
||||||
|
|
||||||
|
st.dataframe(
|
||||||
|
portfolio_df[['ticker', 'name', 'Allocation (%)', 'Amount ($)']],
|
||||||
|
hide_index=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Display metrics
|
||||||
|
metrics_df = pd.DataFrame([
|
||||||
|
{
|
||||||
|
'Ticker': etf['ticker'],
|
||||||
|
'Expense Ratio (%)': etf['metrics']['expense_ratio'] * 100,
|
||||||
|
'AUM ($B)': etf['metrics']['aum'] / 1e9,
|
||||||
|
'Volatility (%)': etf['metrics']['volatility'] * 100,
|
||||||
|
'Max Drawdown (%)': etf['metrics']['max_drawdown'] * 100,
|
||||||
|
'Sharpe Ratio': etf['metrics']['sharpe_ratio'],
|
||||||
|
'Dividend Yield (%)': etf['metrics']['dividend_yield'] * 100
|
||||||
|
}
|
||||||
|
for etf in risk_adjusted_portfolio
|
||||||
|
])
|
||||||
|
st.dataframe(metrics_df, hide_index=True)
|
||||||
|
else:
|
||||||
|
st.error("Could not generate risk-adjusted portfolio.")
|
||||||
|
|
||||||
|
# Add buttons to apply each strategy
|
||||||
|
st.write("### Apply Strategy")
|
||||||
|
col1, col2, col3, col4 = st.columns(4)
|
||||||
|
|
||||||
|
with col1:
|
||||||
|
if st.button("Apply Balanced Growth", key="apply_balanced"):
|
||||||
|
if balanced_portfolio:
|
||||||
|
st.session_state.etf_allocations = [
|
||||||
|
{"ticker": etf['ticker'], "allocation": etf['allocation'] * 100}
|
||||||
|
for etf in balanced_portfolio
|
||||||
|
]
|
||||||
|
st.success("Applied Balanced Growth strategy!")
|
||||||
|
st.rerun()
|
||||||
|
|
||||||
|
with col2:
|
||||||
|
if st.button("Apply Income Focus", key="apply_income"):
|
||||||
|
if income_portfolio:
|
||||||
|
st.session_state.etf_allocations = [
|
||||||
|
{"ticker": etf['ticker'], "allocation": etf['allocation'] * 100}
|
||||||
|
for etf in income_portfolio
|
||||||
|
]
|
||||||
|
st.success("Applied Income Focus strategy!")
|
||||||
|
st.rerun()
|
||||||
|
|
||||||
|
with col3:
|
||||||
|
if st.button("Apply Growth Focus", key="apply_growth"):
|
||||||
|
if growth_portfolio:
|
||||||
|
st.session_state.etf_allocations = [
|
||||||
|
{"ticker": etf['ticker'], "allocation": etf['allocation'] * 100}
|
||||||
|
for etf in growth_portfolio
|
||||||
|
]
|
||||||
|
st.success("Applied Growth Focus strategy!")
|
||||||
|
st.rerun()
|
||||||
|
|
||||||
|
with col4:
|
||||||
|
if st.button("Apply Risk-Adjusted", key="apply_risk_adjusted"):
|
||||||
|
if risk_adjusted_portfolio:
|
||||||
|
st.session_state.etf_allocations = [
|
||||||
|
{"ticker": etf['ticker'], "allocation": etf['allocation'] * 100}
|
||||||
|
for etf in risk_adjusted_portfolio
|
||||||
|
]
|
||||||
|
st.success("Applied Risk-Adjusted strategy!")
|
||||||
|
st.rerun()
|
||||||
|
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
st.error(str(e))
|
st.error(str(e))
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user