codelion's picture
Update app.py
e44a316 verified
raw
history blame
3.12 kB
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")
if "Adj Close" not in data.columns:
return pd.DataFrame(), "Error: 'Adj Close' column missing.", "", "", ""
prices = data['Adj Close']
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()