codelion's picture
Update app.py
d9c4174 verified
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()