codelion's picture
Update app.py
c54e392 verified
raw
history blame
2.97 kB
import gradio as gr
import yfinance as yf
import numpy as np
import pandas as pd
from scipy.optimize import minimize
# Define stock tickers (25 from S&P 500)
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")['Adj Close']
returns = data.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 "Optimization failed. Try adjusting inputs.", None, None, None
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 f"Error: {str(e)}", None, None, None
with gr.Blocks() as demo:
gr.Markdown("# πŸ“ˆ Modern Portfolio Optimizer (MPT)")
gr.Markdown("Select number of years of historical data and your target annual return. This app computes the **minimum risk portfolio** of 25 S&P 500 stocks.")
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")
ret_text = gr.Text(label="Expected Return")
vol_text = gr.Text(label="Expected Volatility")
sharpe_text = gr.Text(label="Sharpe Ratio")
run_button.click(
fn=lambda years, target: optimize_portfolio(years, target / 100),
inputs=[years_slider, return_slider],
outputs=[output_table, ret_text, vol_text, sharpe_text]
)
demo.launch()