Spaces:
Sleeping
Sleeping
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() | |