import gradio as gr import yfinance as yf import numpy as np import pandas as pd from scipy.optimize import minimize TICKERS = [ 'AAPL', 'MSFT', 'NVDA', 'AVGO', 'ADBE', 'AMZN', 'TSLA', 'HD', 'PG', 'COST', 'UNH', 'JNJ', 'LLY', 'JPM', 'GS', 'V', 'CAT', 'UNP', 'GE', 'XOM', 'NEE', 'D', 'GOOGL', 'META', 'CMCSA', 'PLD' ] def optimize_portfolio(years, target_return): try: data = yf.download(TICKERS, period=f"{years}y", interval="1mo", group_by="ticker", auto_adjust=True) if isinstance(data.columns, pd.MultiIndex): try: prices = pd.concat([data[ticker]['Close'] for ticker in TICKERS], axis=1) prices.columns = TICKERS except Exception: return pd.DataFrame(), "Error: Failed to extract Close prices from multi-index data.", "", "", "" else: prices = data.get("Adj Close") if prices is None or prices.empty: return pd.DataFrame(), "Error: 'Adj Close' data not found or empty.", "", "", "" returns = prices.pct_change().dropna() mean_returns = returns.mean() * 12 cov_matrix = returns.cov() * 12 num_assets = len(TICKERS) init_weights = np.ones(num_assets) / num_assets def portfolio_volatility(weights): return np.sqrt(weights @ cov_matrix @ weights) constraints = [ {"type": "eq", "fun": lambda w: np.sum(w) - 1}, {"type": "eq", "fun": lambda w: w @ mean_returns - target_return} ] bounds = tuple((0, 1) for _ in range(num_assets)) result = minimize( portfolio_volatility, init_weights, method="SLSQP", bounds=bounds, constraints=constraints ) if not result.success: return pd.DataFrame(), "Optimization failed. Try adjusting inputs.", "", "", "" weights = result.x port_return = weights @ mean_returns port_vol = np.sqrt(weights @ cov_matrix @ weights) risk_free_rate = 0.045 sharpe_ratio = (port_return - risk_free_rate) / port_vol df = pd.DataFrame({ "Ticker": TICKERS, "Weight (%)": np.round(weights * 100, 2) }).sort_values("Weight (%)", ascending=False).reset_index(drop=True) return df, "", f"{port_return*100:.2f}%", f"{port_vol*100:.2f}%", f"{sharpe_ratio:.2f}" except Exception as e: return pd.DataFrame(), f"Error: {str(e)}", "", "", "" with gr.Blocks() as demo: gr.Markdown("# 📈 Modern Portfolio Optimizer (MPT)") gr.Markdown("Optimize a portfolio of 25 S&P 500 stocks for **minimum risk** with a target return.") with gr.Row(): years_slider = gr.Slider(1, 10, value=5, step=1, label="Years of Historical Data") return_slider = gr.Slider(1.0, 15.0, value=5.0, step=0.1, label="Target Annual Return (%)") run_button = gr.Button("Optimize Portfolio") output_table = gr.Dataframe(headers=["Ticker", "Weight (%)"], label="Optimal Allocation") error_box = gr.Textbox(label="Message", lines=1) ret_text = gr.Textbox(label="Expected Return") vol_text = gr.Textbox(label="Expected Volatility") sharpe_text = gr.Textbox(label="Sharpe Ratio") run_button.click( fn=lambda years, target: optimize_portfolio(years, target / 100), inputs=[years_slider, return_slider], outputs=[output_table, error_box, ret_text, vol_text, sharpe_text] ) demo.launch()