diff --git a/pages/ETF_Portfolio_Builder.py b/pages/ETF_Portfolio_Builder.py index 747edee..6f3b5f0 100644 --- a/pages/ETF_Portfolio_Builder.py +++ b/pages/ETF_Portfolio_Builder.py @@ -1659,45 +1659,28 @@ def remove_ticker(ticker_to_remove: str) -> None: # Display current tickers in the main space if st.session_state.etf_allocations: st.subheader("Selected ETFs") - st.markdown(""" - - """, unsafe_allow_html=True) - # Create a container for tickers - ticker_container = st.container() - with ticker_container: - # Display each ticker with a close button - for etf in st.session_state.etf_allocations: - col1, col2 = st.columns([0.05, 0.95]) # Adjusted column ratio - with col1: + # Create four columns for the tables with smaller widths + col1, col2, col3, col4 = st.columns([1, 1, 1, 1]) + + # Split the ETFs into four groups + etf_groups = [[] for _ in range(4)] + for i, etf in enumerate(st.session_state.etf_allocations): + 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""" +
+ {etf['ticker']} +
+ """, unsafe_allow_html=True) if st.button("×", key=f"remove_{etf['ticker']}", help=f"Remove {etf['ticker']} from portfolio"): remove_ticker(etf['ticker']) - with col2: - st.markdown(f"
{etf['ticker']}
", unsafe_allow_html=True) # Debug information logger.info("=== Session State Debug ===") @@ -1829,7 +1812,15 @@ with st.sidebar: # Create a container for ETF input with st.container(): - # Input field for ETF ticker only + # Input field for ETF ticker with improved visibility + st.markdown(""" + + """, unsafe_allow_html=True) new_ticker = st.text_input("ETF Ticker", help="Enter a valid ETF ticker (e.g., SCHD)") # Add button to add ETF @@ -2872,55 +2863,221 @@ if st.session_state.simulation_run and st.session_state.df_data is not None: investment_horizon=investment_horizon ) - # Get AI suggestions + # Get AI suggestions for different strategies with st.spinner("Analyzing ETFs and generating portfolio suggestions..."): 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) + + # Strategy 2: Income Focus + income_goal = InvestmentGoal( + 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) + + # Strategy 3: Growth Focus + 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['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 balanced_portfolio + ]) + st.dataframe(metrics_df, hide_index=True) + else: + 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() - if portfolio: - # Display portfolio suggestions - st.success("Portfolio suggestions generated successfully!") - - # Create a DataFrame for better display - portfolio_df = pd.DataFrame(portfolio) - portfolio_df['Allocation (%)'] = portfolio_df['allocation'] * 100 - portfolio_df['Amount ($)'] = portfolio_df['amount'] - - # Display portfolio summary - st.write("### Portfolio Summary") - st.dataframe( - portfolio_df[['ticker', 'name', 'Allocation (%)', 'Amount ($)']], - hide_index=True - ) - - # Display detailed metrics - st.write("### Detailed 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 portfolio - ]) - 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: - st.error("No portfolio suggestions could be generated. Please try different parameters.") - except ValueError as e: st.error(str(e)) except Exception as e: