Space16 / app.py
QuantumLearner's picture
Update app.py
4b6255e verified
import yfinance as yf
import numpy as np
import pandas as pd
import streamlit as st
import plotly.graph_objects as go
from pypfopt import EfficientFrontier, HRPOpt, EfficientCVaR, risk_models, expected_returns, black_litterman, objective_functions
from pypfopt.risk_models import CovarianceShrinkage
from scipy.optimize import minimize
from scipy.cluster.hierarchy import dendrogram
import matplotlib.pyplot as plt
# Helper functions
def fetch_stock_data(tickers, start_date, end_date):
data = yf.download(tickers, start=start_date, end=end_date, auto_adjust=False)['Adj Close']
if isinstance(data.columns, pd.MultiIndex):
data.columns = data.columns.get_level_values(0)
if data.empty:
raise ValueError(f"No data fetched for {tickers} from {start_date} to {end_date}.")
return data
def plot_efficient_frontier(mean_returns, cov_matrix, title):
ef = EfficientFrontier(mean_returns, cov_matrix)
ef.max_sharpe() # Maximize Sharpe ratio to get the optimal portfolio
cleaned_weights = ef.clean_weights()
mu, sigma, sharpe = ef.portfolio_performance()
ef = EfficientFrontier(mean_returns, cov_matrix)
frontier_y = []
frontier_x = []
for r in np.linspace(0, mu, 100):
ef.efficient_return(r)
perf = ef.portfolio_performance()
frontier_y.append(perf[0])
frontier_x.append(perf[1])
fig = go.Figure()
fig.add_trace(go.Scatter(x=frontier_x, y=frontier_y, mode='lines', name='Efficient Frontier'))
fig.add_trace(go.Scatter(x=[sigma], y=[mu], mode='markers',
marker=dict(color='red', size=10), name='Optimal Portfolio'))
asset_returns = mean_returns.values
asset_volatility = np.sqrt(np.diag(cov_matrix))
fig.add_trace(go.Scatter(x=asset_volatility, y=asset_returns,
mode='markers+text',
text=mean_returns.index,
name='Assets',
textposition='top center'))
fig.update_layout(title=title, xaxis_title='Volatility', yaxis_title='Return')
return fig
def plot_portfolio_weights(weights, tickers, title):
fig = go.Figure(data=[go.Bar(x=tickers, y=weights)])
fig.update_layout(title=title, xaxis_title='Assets', yaxis_title='Weights')
return fig
def plot_cumulative_returns(portfolio_returns, sp500_returns, equal_weighted_returns, title):
portfolio_cumulative = (1 + portfolio_returns).cumprod()
sp500_cumulative = (1 + sp500_returns).cumprod()
equal_weighted_cumulative = (1 + equal_weighted_returns).cumprod()
fig = go.Figure()
fig.add_trace(go.Scatter(x=portfolio_cumulative.index, y=portfolio_cumulative,
mode='lines', name='Portfolio'))
fig.add_trace(go.Scatter(x=sp500_cumulative.index, y=sp500_cumulative,
mode='lines', name='S&P 500'))
fig.add_trace(go.Scatter(x=equal_weighted_cumulative.index, y=equal_weighted_cumulative,
mode='lines', name='Equal Weighted'))
fig.update_layout(title=title, xaxis_title='Date', yaxis_title='Cumulative Returns')
return fig
def plot_rolling_metrics(portfolio_returns, sp500_returns, equal_weighted_returns, risk_free_rate, title_volatility, title_sharpe):
rolling_window = 252
rolling_volatility_portfolio = portfolio_returns.rolling(window=rolling_window).std() * np.sqrt(252)
rolling_sharpe_portfolio = portfolio_returns.rolling(window=rolling_window).apply(
lambda x: (x.mean() - risk_free_rate / 252) / x.std()) * np.sqrt(252)
rolling_volatility_sp500 = sp500_returns.rolling(window=rolling_window).std() * np.sqrt(252)
rolling_sharpe_sp500 = sp500_returns.rolling(window=rolling_window).apply(
lambda x: (x.mean() - risk_free_rate / 252) / x.std()) * np.sqrt(252)
rolling_volatility_equal = equal_weighted_returns.rolling(window=rolling_window).std() * np.sqrt(252)
rolling_sharpe_equal = equal_weighted_returns.rolling(window=rolling_window).apply(
lambda x: (x.mean() - risk_free_rate / 252) / x.std()) * np.sqrt(252)
fig_volatility = go.Figure()
fig_volatility.add_trace(go.Scatter(x=rolling_volatility_portfolio.index, y=rolling_volatility_portfolio,
mode='lines', name='Portfolio Volatility'))
fig_volatility.add_trace(go.Scatter(x=rolling_volatility_sp500.index, y=rolling_volatility_sp500,
mode='lines', name='S&P 500 Volatility'))
fig_volatility.add_trace(go.Scatter(x=rolling_volatility_equal.index, y=rolling_volatility_equal,
mode='lines', name='Equal Weighted Volatility'))
fig_volatility.update_layout(title=title_volatility, xaxis_title='Date', yaxis_title='Volatility')
fig_sharpe = go.Figure()
fig_sharpe.add_trace(go.Scatter(x=rolling_sharpe_portfolio.index, y=rolling_sharpe_portfolio,
mode='lines', name='Portfolio Sharpe Ratio'))
fig_sharpe.add_trace(go.Scatter(x=rolling_sharpe_sp500.index, y=rolling_sharpe_sp500,
mode='lines', name='S&P 500 Sharpe Ratio'))
fig_sharpe.add_trace(go.Scatter(x=rolling_sharpe_equal.index, y=rolling_sharpe_equal,
mode='lines', name='Equal Weighted Sharpe Ratio'))
fig_sharpe.update_layout(title=title_sharpe, xaxis_title='Date', yaxis_title='Sharpe Ratio')
return fig_volatility, fig_sharpe
def plot_max_drawdown(portfolio_returns, sp500_returns, equal_weighted_returns, title):
rolling_window = 252
def max_drawdown(return_series):
cumulative = (1 + return_series).cumprod()
peak = cumulative.cummax()
drawdown = (cumulative - peak) / peak
return drawdown.min()
portfolio_drawdown = portfolio_returns.rolling(window=rolling_window).apply(max_drawdown, raw=False)
sp500_drawdown = sp500_returns.rolling(window=rolling_window).apply(max_drawdown, raw=False)
equal_weighted_drawdown = equal_weighted_returns.rolling(window=rolling_window).apply(max_drawdown, raw=False)
fig = go.Figure()
fig.add_trace(go.Scatter(x=portfolio_drawdown.index, y=portfolio_drawdown,
mode='lines', name='Portfolio Drawdown'))
fig.add_trace(go.Scatter(x=sp500_drawdown.index, y=sp500_drawdown,
mode='lines', name='S&P 500 Drawdown'))
fig.add_trace(go.Scatter(x=equal_weighted_drawdown.index, y=equal_weighted_drawdown,
mode='lines', name='Equal Weighted Drawdown'))
fig.update_layout(title=title, xaxis_title='Date', yaxis_title='Maximum Drawdown')
return fig
def plot_dendrogram(hrp, tickers, title):
plt.figure(figsize=(10, 6))
dendrogram(hrp.clusters, labels=tickers)
plt.title(title)
plt.xlabel('Assets')
plt.ylabel('Distance')
st.pyplot(plt.gcf())
def multi_objective_function(weights, mean_returns, cov_matrix, dividend_yields,
maximize_return, minimize_risk, maximize_sharpe,
maximize_diversification, minimize_cvar,
minimize_transaction_costs, maximize_dividend_yield,
risk_free_rate=0.01):
obj_value = 0
if maximize_return:
obj_value -= np.dot(weights, mean_returns)
if minimize_risk:
obj_value += np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
if maximize_sharpe:
portfolio_return = np.dot(weights, mean_returns)
portfolio_volatility = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
sharpe_ratio = (portfolio_return - risk_free_rate) / portfolio_volatility
obj_value -= sharpe_ratio
if maximize_diversification:
w_vol = np.dot(weights.T, np.sqrt(np.diag(cov_matrix)))
p_vol = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
diversification_ratio = w_vol / p_vol
obj_value -= diversification_ratio
if minimize_cvar:
cvar = np.percentile(np.dot(returns.values, weights), 5)
obj_value += cvar
if minimize_transaction_costs:
prev_weights = np.ones(len(weights)) / len(weights)
transaction_costs = np.sum(np.abs(weights - prev_weights)) * 0.001
obj_value += transaction_costs
if maximize_dividend_yield:
obj_value -= np.dot(weights, dividend_yields)
return obj_value
def optimize_portfolio(tickers, mean_returns, cov_matrix, dividend_yields,
maximize_return, minimize_risk, maximize_sharpe,
maximize_diversification, minimize_cvar,
minimize_transaction_costs, maximize_dividend_yield):
num_assets = len(tickers)
init_guess = num_assets * [1. / num_assets,]
constraints = (
{'type': 'eq', 'fun': lambda x: np.sum(x) - 1},
{'type': 'ineq', 'fun': lambda x: x}
)
opt_result = minimize(multi_objective_function,
init_guess,
args=(mean_returns, cov_matrix, dividend_yields,
maximize_return, minimize_risk, maximize_sharpe,
maximize_diversification, minimize_cvar,
minimize_transaction_costs, maximize_dividend_yield),
method='SLSQP',
bounds=[(0, 1) for _ in range(num_assets)],
constraints=constraints)
return opt_result.x
def diversification_ratio(weights, cov_matrix, volatilities):
portfolio_volatility = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
weighted_volatilities = np.dot(weights, volatilities)
return -weighted_volatilities / portfolio_volatility
def optimize_maximum_diversification(tickers, returns, mean_returns, cov_matrix, volatilities):
num_assets = len(tickers)
init_guess = num_assets * [1. / num_assets,]
constraints = (
{'type': 'eq', 'fun': lambda x: np.sum(x) - 1},
{'type': 'ineq', 'fun': lambda x: x}
)
opt_result = minimize(diversification_ratio,
init_guess,
args=(cov_matrix, volatilities),
method='SLSQP',
bounds=[(0, 1) for _ in range(num_assets)],
constraints=constraints)
return opt_result.x
def optimize_maximum_sharpe(mean_returns, cov_matrix):
ef = EfficientFrontier(mean_returns, cov_matrix)
optimal_weights = ef.max_sharpe()
return ef.clean_weights()
def optimize_equal_risk_contribution(mean_returns, cov_matrix):
ef = EfficientFrontier(mean_returns, cov_matrix)
ef.add_objective(objective_functions.L2_reg)
optimal_weights = ef.min_volatility()
return ef.clean_weights()
def optimize_cvar_minimization(mean_returns, returns):
cvar = EfficientCVaR(mean_returns, returns)
optimal_weights = cvar.min_cvar()
return cvar.clean_weights()
def optimize_max_return(mean_returns, cov_matrix):
ef = EfficientFrontier(mean_returns, cov_matrix)
target_return = mean_returns.max() * 0.99
optimal_weights = ef.efficient_return(target_return=target_return)
return ef.clean_weights()
def optimize_max_quadratic_utility(mean_returns, cov_matrix, risk_aversion):
ef = EfficientFrontier(mean_returns, cov_matrix)
optimal_weights = ef.max_quadratic_utility(risk_aversion=risk_aversion)
return ef.clean_weights()
# Streamlit app
st.set_page_config(page_title="Portfolio Optimization", layout="wide")
st.title('Portfolio Optimization Methods')
st.markdown("""
This tool allows you to optimize a portfolio of stocks using different optimization methods. You can choose between optimization methods simply by selecting the method, enterING the stock tickers, and setting the parameters. Click the "Run" button to see the results.
""")
with st.sidebar.expander("How to Use", expanded=False):
st.write("""
## Instructions:
1. Select the optimization method from the sidebar.
2. Enter the stock tickers, start date, and end date.
3. For Multi-Objective Optimization, set the optimization objectives.
4. For Robust Optimization, set the L2 regularization parameter (gamma).
5. For Black-Litterman Model, set the market data and your views.
6. Click the "Run" button to perform the optimization and view the results.
""")
st.sidebar.header("Input Parameters")
# Sidebar: Asset Symbols and Dates
with st.sidebar.expander("Asset Symbols and Dates", expanded=True):
tickers_input = st.text_input('Enter Stock Tickers (comma-separated)',
'AAPL, MSFT, GOOGL, AMZN, ASML, META, JPM, V',
help="Input stock tickers separated by commas. Example: 'AAPL, MSFT, GOOGL'")
tickers = [ticker.strip() for ticker in tickers_input.split(',')]
start_date = st.date_input('Start Date', pd.to_datetime('2015-01-01'),
help="Select the start date for the analysis.")
end_date = st.date_input('End Date', pd.to_datetime(pd.Timestamp.now().date() + pd.Timedelta(days=1)),
help="Select the end date for the analysis.")
# Sidebar: Optimization Method
with st.sidebar.expander("Optimization Method", expanded=True):
selected = st.radio("Optimization Methods",
["Multi-Objective Optimization",
"Robust Optimization",
"Mean-Variance Optimization",
"Black-Litterman Model",
"Hierarchical Risk Parity",
"Maximum Diversification",
"Maximum Sharpe Ratio",
"Equal Risk Contribution",
"CVaR Minimization",
"Maximum Return",
"Maximum Quadratic Utility"],
help="Select the optimization method you want to apply.")
# Method-specific parameters
if selected == "Multi-Objective Optimization":
st.markdown("""
### Multi-Objective Optimization
Multi-Objective Optimization in portfolio management allows investors to consider multiple objectives simultaneously.
This method aims to find the best portfolio by balancing various goals such as maximizing returns, minimizing risk, maximizing the Sharpe ratio, enhancing diversification, and more.
""")
st.sidebar.header("Optimization Parameters")
maximize_return = st.sidebar.checkbox('Maximize Return', value=True)
minimize_risk = st.sidebar.checkbox('Minimize Risk', value=True)
maximize_sharpe = st.sidebar.checkbox('Maximize Sharpe Ratio', value=False)
maximize_diversification = st.sidebar.checkbox('Maximize Diversification', value=False)
minimize_cvar = st.sidebar.checkbox('Minimize CVaR', value=False)
minimize_transaction_costs = st.sidebar.checkbox('Minimize Transaction Costs', value=False)
maximize_dividend_yield = st.sidebar.checkbox('Maximize Dividend Yield', value=True)
elif selected == "Robust Optimization":
st.markdown("""
### Robust Optimization
Robust Optimization aims to create more stable portfolios by incorporating regularization techniques.
This method adjusts the covariance matrix to reduce the impact of estimation errors, noisy or uncertain data.
""")
st.sidebar.header("Optimization Parameters")
gamma = st.sidebar.slider('L2 Regularization (Gamma)', min_value=0.0, max_value=1.0, value=0.1, step=0.01)
elif selected == "Mean-Variance Optimization":
st.markdown("""
### Mean-Variance Optimization
Mean-Variance Optimization, introduced by Harry Markowitz aims to balance return and risk by maximizing the Sharpe ratio, thereby finding the portfolio with the best risk-adjusted returns.
""")
st.sidebar.header("Optimization Parameters")
# No additional parameters typically required, but could add risk-free rate or constraints if desired
elif selected == "Black-Litterman Model":
st.markdown("""
### Black-Litterman Model
The Black-Litterman Model combines market equilibrium with investor views to adjust the expected returns and then applies mean-variance optimization. This approach is particularly useful for incorporating subjective views into a theoretically sound framework.
""")
st.sidebar.header("Optimization Parameters")
# Views could be added here, but keeping simple as per original
elif selected == "Hierarchical Risk Parity":
st.markdown("""
### Hierarchical Risk Parity (HRP)
Hierarchical Risk Parity (HRP) is a method that groups assets into clusters based on their correlations and then allocates risk evenly among these clusters.
This approach aims to improve diversification and reduce risk concentration in the portfolio.
""")
st.sidebar.header("Optimization Parameters")
# HRP typically doesn't need additional parameters
elif selected == "Maximum Diversification":
st.markdown("""
### Maximum Diversification Portfolio
The Maximum Diversification Portfolio method aims to create a portfolio that maximizes the diversification ratio. This approach allocates weights to assets to maximize the ratio of the weighted average volatility of individual assets to the overall portfolio volatility.
""")
st.sidebar.header("Optimization Parameters")
# No additional parameters typically needed
elif selected == "Maximum Sharpe Ratio":
st.markdown("""
### Maximum Sharpe Ratio Portfolio
The Maximum Sharpe Ratio Portfolio method aims to create a portfolio that maximizes the Sharpe ratio. The Sharpe ratio is the measure of the portfolio's excess return per unit of risk, providing a risk-adjusted return metric.
""")
st.sidebar.header("Optimization Parameters")
# Could add risk-free rate, but keeping as is
elif selected == "Equal Risk Contribution":
st.markdown("""
### Equal Risk Contribution Portfolio
The Equal Risk Contribution (ERC) Portfolio method aims to allocate risk equally among all assets in the portfolio. This approach ensures that each asset contributes the same amount of risk, leading to a well-balanced portfolio.
""")
st.sidebar.header("Optimization Parameters")
# No additional parameters typically needed
elif selected == "CVaR Minimization":
st.markdown("""
### CVaR Minimization Portfolio
The CVaR Minimization Portfolio method focuses on minimizing Conditional Value at Risk (CVaR), which measures the expected loss of the portfolio in the worst-case scenarios. This method is particularly useful for investors who want to limit their downside risk.
""")
st.sidebar.header("Optimization Parameters")
# Could add confidence level, but keeping simple
elif selected == "Maximum Return":
st.markdown("""
### Maximum Return Portfolio
The Maximum Return Portfolio method focuses solely on maximizing the expected return of the portfolio, without considering risk. This method allocates the highest weights to the assets with the highest expected returns.
""")
st.sidebar.header("Optimization Parameters")
# No additional parameters typically needed
elif selected == "Maximum Quadratic Utility":
st.markdown("""
### Maximum Quadratic Utility Portfolio
The Maximum Quadratic Utility Portfolio method maximizes the quadratic utility function, which balances the trade-off between expected return and risk. It incorporates a risk aversion coefficient to adjust the level of risk tolerance.
""")
st.sidebar.header("Optimization Parameters")
risk_aversion = st.sidebar.slider('Risk Aversion Coefficient', min_value=0.0, max_value=10.0, value=1.0, step=0.1)
with st.expander("Methodology and Formulas", expanded=False):
st.markdown("""
#### Formulas:
**Expected Return**:
""")
st.latex(r"""
\mu = \sum_{i=1}^{n} w_i \mu_i
""")
st.markdown("""
**Portfolio Variance**:
""")
st.latex(r"""
\sigma^2 = \sum_{i=1}^{n} \sum_{j=1}^{n} w_i w_j \sigma_{ij}
""")
st.markdown("""
**Sharpe Ratio**:
""")
st.latex(r"""
S = \frac{\mu_p - r_f}{\sigma_p}
""")
st.markdown("""
**Diversification Ratio**:
""")
st.latex(r"""
DR = \frac{\sum_{i=1}^{n} w_i \sigma_i}{\sigma_p}
""")
st.markdown("""
**Conditional Value at Risk (CVaR)**:
""")
st.latex(r"""
\text{CVaR}_\alpha = \mathbb{E}[X | X \leq \text{VaR}_\alpha]
""")
if st.sidebar.button('Run'):
if selected == "Multi-Objective Optimization":
if 'multi_objective_result' not in st.session_state:
st.session_state.multi_objective_result = {}
data = fetch_stock_data(tickers, start_date, end_date)
returns = data.pct_change().dropna()
mean_returns = expected_returns.mean_historical_return(data)
cov_matrix = risk_models.sample_cov(data)
dividend_yields = {}
for ticker in tickers:
stock = yf.Ticker(ticker)
info = stock.info
dividend_yield = info.get('dividendYield') or info.get('trailingAnnualDividendYield', 0)
if dividend_yield is None:
dividend_yield = 0
dividend_yields[ticker] = dividend_yield
dividend_yields = pd.Series(dividend_yields)
optimal_weights = optimize_portfolio(tickers, mean_returns, cov_matrix, dividend_yields,
maximize_return, minimize_risk, maximize_sharpe,
maximize_diversification, minimize_cvar,
minimize_transaction_costs, maximize_dividend_yield)
cleaned_weights = dict(zip(tickers, optimal_weights))
st.session_state.multi_objective_result['cleaned_weights'] = cleaned_weights
st.session_state.multi_objective_result['returns'] = returns
st.session_state.multi_objective_result['optimal_weights'] = optimal_weights
if 'multi_objective_result' in st.session_state:
cleaned_weights = st.session_state.multi_objective_result['cleaned_weights']
returns = st.session_state.multi_objective_result['returns']
optimal_weights = st.session_state.multi_objective_result['optimal_weights']
fig_weights = plot_portfolio_weights(list(cleaned_weights.values()),
list(cleaned_weights.keys()),
'Multi-Objective Optimization Portfolio Weights')
st.plotly_chart(fig_weights)
portfolio_returns = returns.dot(optimal_weights)
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
if isinstance(sp500.columns, pd.MultiIndex):
sp500.columns = sp500.columns.get_level_values(0)
if sp500.empty:
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
sp500_returns = sp500['Adj Close'].pct_change().dropna()
equal_weighted_returns = returns.mean(axis=1)
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns,
sp500_returns,
equal_weighted_returns,
'Cumulative Returns Over Time')
st.plotly_chart(fig_cumulative_returns)
fig_volatility, fig_sharpe = plot_rolling_metrics(portfolio_returns,
sp500_returns,
equal_weighted_returns,
0.01,
'Rolling Volatility',
'Rolling Sharpe Ratio')
st.plotly_chart(fig_volatility)
st.plotly_chart(fig_sharpe)
fig_drawdown = plot_max_drawdown(portfolio_returns,
sp500_returns,
equal_weighted_returns,
'Maximum Drawdown')
st.plotly_chart(fig_drawdown)
elif selected == "Robust Optimization":
if 'robust_result' not in st.session_state:
st.session_state.robust_result = {}
data = fetch_stock_data(tickers, start_date, end_date)
returns = data.pct_change().dropna()
mean_returns = expected_returns.mean_historical_return(data)
cov_matrix_shrinked = CovarianceShrinkage(data).ledoit_wolf()
ef = EfficientFrontier(mean_returns, cov_matrix_shrinked)
ef.add_objective(objective_functions.L2_reg, gamma=gamma)
optimal_weights_robust = ef.max_sharpe()
cleaned_weights_robust = ef.clean_weights()
st.session_state.robust_result['cleaned_weights'] = cleaned_weights_robust
st.session_state.robust_result['returns'] = returns
st.session_state.robust_result['optimal_weights'] = optimal_weights_robust
if 'robust_result' in st.session_state:
cleaned_weights_robust = st.session_state.robust_result['cleaned_weights']
returns = st.session_state.robust_result['returns']
optimal_weights_robust = st.session_state.robust_result['optimal_weights']
fig_weights = plot_portfolio_weights(list(cleaned_weights_robust.values()),
list(cleaned_weights_robust.keys()),
'Robust Optimization Portfolio Weights')
st.plotly_chart(fig_weights)
portfolio_returns_robust = returns.dot(np.array(list(cleaned_weights_robust.values())))
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
if isinstance(sp500.columns, pd.MultiIndex):
sp500.columns = sp500.columns.get_level_values(0)
if sp500.empty:
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
sp500_returns = sp500['Adj Close'].pct_change().dropna()
equal_weighted_returns = returns.mean(axis=1)
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_robust,
sp500_returns,
equal_weighted_returns,
'Cumulative Returns Over Time')
st.plotly_chart(fig_cumulative_returns)
fig_volatility, fig_sharpe = plot_rolling_metrics(portfolio_returns_robust,
sp500_returns,
equal_weighted_returns,
0.01,
'Rolling Volatility',
'Rolling Sharpe Ratio')
st.plotly_chart(fig_volatility)
st.plotly_chart(fig_sharpe)
fig_drawdown = plot_max_drawdown(portfolio_returns_robust,
sp500_returns,
equal_weighted_returns,
'Maximum Drawdown')
st.plotly_chart(fig_drawdown)
elif selected == "Mean-Variance Optimization":
if 'mean_variance_result' not in st.session_state:
st.session_state.mean_variance_result = {}
data = fetch_stock_data(tickers, start_date, end_date)
returns = data.pct_change().dropna()
mean_returns = expected_returns.mean_historical_return(data)
cov_matrix = risk_models.sample_cov(data)
ef = EfficientFrontier(mean_returns, cov_matrix)
optimal_weights = ef.max_sharpe()
cleaned_weights = ef.clean_weights()
st.session_state.mean_variance_result['cleaned_weights'] = cleaned_weights
st.session_state.mean_variance_result['returns'] = returns
st.session_state.mean_variance_result['optimal_weights'] = optimal_weights
st.session_state.mean_variance_result['mean_returns'] = mean_returns
st.session_state.mean_variance_result['cov_matrix'] = cov_matrix
if 'mean_variance_result' in st.session_state:
cleaned_weights = st.session_state.mean_variance_result['cleaned_weights']
returns = st.session_state.mean_variance_result['returns']
optimal_weights = np.array(list(st.session_state.mean_variance_result['optimal_weights'].values()))
mean_returns = st.session_state.mean_variance_result['mean_returns']
cov_matrix = st.session_state.mean_variance_result['cov_matrix']
fig_weights = plot_portfolio_weights(list(cleaned_weights.values()),
list(cleaned_weights.keys()),
'Mean-Variance Optimization Portfolio Weights')
st.plotly_chart(fig_weights)
portfolio_returns = returns.dot(optimal_weights)
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
if isinstance(sp500.columns, pd.MultiIndex):
sp500.columns = sp500.columns.get_level_values(0)
if sp500.empty:
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
sp500_returns = sp500['Adj Close'].pct_change().dropna()
equal_weighted_returns = returns.mean(axis=1)
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns,
sp500_returns,
equal_weighted_returns,
'Cumulative Returns Over Time')
st.plotly_chart(fig_cumulative_returns)
fig_volatility, fig_sharpe = plot_rolling_metrics(portfolio_returns,
sp500_returns,
equal_weighted_returns,
0.01,
'Rolling Volatility',
'Rolling Sharpe Ratio')
st.plotly_chart(fig_volatility)
st.plotly_chart(fig_sharpe)
fig_drawdown = plot_max_drawdown(portfolio_returns,
sp500_returns,
equal_weighted_returns,
'Maximum Drawdown')
st.plotly_chart(fig_drawdown)
fig_efficient_frontier = plot_efficient_frontier(mean_returns,
cov_matrix,
'Efficient Frontier with Optimal Portfolio')
st.plotly_chart(fig_efficient_frontier)
elif selected == "Black-Litterman Model":
if 'black_litterman_result' not in st.session_state:
st.session_state.black_litterman_result = {}
data = fetch_stock_data(tickers, start_date, end_date)
returns = data.pct_change().dropna()
mean_returns = expected_returns.mean_historical_return(data)
cov_matrix = risk_models.sample_cov(data)
market_data = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
if isinstance(market_data.columns, pd.MultiIndex):
market_data.columns = market_data.columns.get_level_values(0)
if market_data.empty:
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
market_data = market_data['Adj Close']
delta = black_litterman.market_implied_risk_aversion(market_data)
market_caps = {}
for ticker in tickers:
stock = yf.Ticker(ticker)
market_caps[ticker] = stock.info['marketCap']
market_caps_series = pd.Series(market_caps)
prior = black_litterman.market_implied_prior_returns(market_caps_series,
delta,
cov_matrix)
P = np.identity(len(tickers))
Q = np.zeros(len(tickers))
bl = black_litterman.BlackLittermanModel(cov_matrix, pi=prior, P=P, Q=Q)
bl_return = bl.bl_returns()
ef = EfficientFrontier(bl_return, cov_matrix)
optimal_weights_bl = ef.max_sharpe()
cleaned_weights_bl = ef.clean_weights()
st.session_state.black_litterman_result['cleaned_weights'] = cleaned_weights_bl
st.session_state.black_litterman_result['returns'] = returns
st.session_state.black_litterman_result['optimal_weights'] = optimal_weights_bl
if 'black_litterman_result' in st.session_state:
cleaned_weights_bl = st.session_state.black_litterman_result['cleaned_weights']
returns = st.session_state.black_litterman_result['returns']
optimal_weights_bl = np.array(list(st.session_state.black_litterman_result['optimal_weights'].values()))
fig_weights = plot_portfolio_weights(list(cleaned_weights_bl.values()),
list(cleaned_weights_bl.keys()),
'Black-Litterman Portfolio Weights')
st.plotly_chart(fig_weights)
portfolio_returns_bl = returns.dot(optimal_weights_bl)
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
if isinstance(sp500.columns, pd.MultiIndex):
sp500.columns = sp500.columns.get_level_values(0)
if sp500.empty:
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
sp500_returns = sp500['Adj Close'].pct_change().dropna()
equal_weighted_returns = returns.mean(axis=1)
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_bl,
sp500_returns,
equal_weighted_returns,
'Cumulative Returns Over Time')
st.plotly_chart(fig_cumulative_returns)
fig_volatility, fig_sharpe = plot_rolling_metrics(portfolio_returns_bl,
sp500_returns,
equal_weighted_returns,
0.01,
'Rolling Volatility',
'Rolling Sharpe Ratio')
st.plotly_chart(fig_volatility)
st.plotly_chart(fig_sharpe)
fig_drawdown = plot_max_drawdown(portfolio_returns_bl,
sp500_returns,
equal_weighted_returns,
'Maximum Drawdown')
st.plotly_chart(fig_drawdown)
elif selected == "Hierarchical Risk Parity":
if 'hrp_result' not in st.session_state:
st.session_state.hrp_result = {}
data = fetch_stock_data(tickers, start_date, end_date)
returns = data.pct_change().dropna()
mean_returns = expected_returns.mean_historical_return(data)
cov_matrix = risk_models.sample_cov(data)
hrp = HRPOpt(returns)
hrp_weights = hrp.optimize()
cleaned_weights_hrp = hrp.clean_weights()
st.session_state.hrp_result['cleaned_weights'] = cleaned_weights_hrp
st.session_state.hrp_result['returns'] = returns
st.session_state.hrp_result['optimal_weights'] = hrp_weights
st.session_state.hrp_result['hrp'] = hrp
if 'hrp_result' in st.session_state:
cleaned_weights_hrp = st.session_state.hrp_result['cleaned_weights']
returns = st.session_state.hrp_result['returns']
optimal_weights_hrp = np.array(list(st.session_state.hrp_result['optimal_weights'].values()))
hrp = st.session_state.hrp_result['hrp']
fig_weights = plot_portfolio_weights(list(cleaned_weights_hrp.values()),
list(cleaned_weights_hrp.keys()),
'Hierarchical Risk Parity Portfolio Weights')
st.plotly_chart(fig_weights)
portfolio_returns_hrp = returns.dot(optimal_weights_hrp)
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
if isinstance(sp500.columns, pd.MultiIndex):
sp500.columns = sp500.columns.get_level_values(0)
if sp500.empty:
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
sp500_returns = sp500['Adj Close'].pct_change().dropna()
equal_weighted_returns = returns.mean(axis=1)
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_hrp,
sp500_returns,
equal_weighted_returns,
'Cumulative Returns Over Time')
st.plotly_chart(fig_cumulative_returns)
fig_volatility, fig_sharpe = plot_rolling_metrics(portfolio_returns_hrp,
sp500_returns,
equal_weighted_returns,
0.01,
'Rolling Volatility',
'Rolling Sharpe Ratio')
st.plotly_chart(fig_volatility)
st.plotly_chart(fig_sharpe)
fig_drawdown = plot_max_drawdown(portfolio_returns_hrp,
sp500_returns,
equal_weighted_returns,
'Maximum Drawdown')
st.plotly_chart(fig_drawdown)
st.write("### Dendrogram of Asset Clusters")
plot_dendrogram(hrp, tickers, 'Dendrogram of Asset Clusters')
elif selected == "Maximum Diversification":
if 'mdp_result' not in st.session_state:
st.session_state.mdp_result = {}
data = fetch_stock_data(tickers, start_date, end_date)
returns = data.pct_change().dropna()
mean_returns = returns.mean()
cov_matrix = returns.cov()
volatilities = returns.std()
optimal_weights_mdp = optimize_maximum_diversification(tickers,
returns,
mean_returns,
cov_matrix,
volatilities)
cleaned_weights_mdp = dict(zip(tickers, optimal_weights_mdp))
st.session_state.mdp_result['cleaned_weights'] = cleaned_weights_mdp
st.session_state.mdp_result['returns'] = returns
st.session_state.mdp_result['optimal_weights'] = optimal_weights_mdp
if 'mdp_result' in st.session_state:
cleaned_weights_mdp = st.session_state.mdp_result['cleaned_weights']
returns = st.session_state.mdp_result['returns']
optimal_weights_mdp = st.session_state.mdp_result['optimal_weights']
fig_weights = plot_portfolio_weights(list(cleaned_weights_mdp.values()),
list(cleaned_weights_mdp.keys()),
'Maximum Diversification Portfolio Weights')
st.plotly_chart(fig_weights)
portfolio_returns_mdp = returns.dot(optimal_weights_mdp)
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
if isinstance(sp500.columns, pd.MultiIndex):
sp500.columns = sp500.columns.get_level_values(0)
if sp500.empty:
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
sp500_returns = sp500['Adj Close'].pct_change().dropna()
equal_weighted_returns = returns.mean(axis=1)
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_mdp,
sp500_returns,
equal_weighted_returns,
'Cumulative Returns Over Time')
st.plotly_chart(fig_cumulative_returns)
fig_volatility, fig_sharpe = plot_rolling_metrics(portfolio_returns_mdp,
sp500_returns,
equal_weighted_returns,
0.01,
'Rolling Volatility',
'Rolling Sharpe Ratio')
st.plotly_chart(fig_volatility)
st.plotly_chart(fig_sharpe)
fig_drawdown = plot_max_drawdown(portfolio_returns_mdp,
sp500_returns,
equal_weighted_returns,
'Maximum Drawdown')
st.plotly_chart(fig_drawdown)
elif selected == "Maximum Sharpe Ratio":
if 'msr_result' not in st.session_state:
st.session_state.msr_result = {}
data = fetch_stock_data(tickers, start_date, end_date)
returns = data.pct_change().dropna()
mean_returns = expected_returns.mean_historical_return(data)
cov_matrix = risk_models.sample_cov(data)
optimal_weights_msr = optimize_maximum_sharpe(mean_returns, cov_matrix)
cleaned_weights_msr = dict(zip(tickers, optimal_weights_msr.values()))
st.session_state.msr_result['cleaned_weights'] = cleaned_weights_msr
st.session_state.msr_result['returns'] = returns
st.session_state.msr_result['optimal_weights'] = optimal_weights_msr
if 'msr_result' in st.session_state:
cleaned_weights_msr = st.session_state.msr_result['cleaned_weights']
returns = st.session_state.msr_result['returns']
optimal_weights_msr = st.session_state.msr_result['optimal_weights']
fig_weights = plot_portfolio_weights(list(cleaned_weights_msr.values()),
list(cleaned_weights_msr.keys()),
'Maximum Sharpe Ratio Portfolio Weights')
st.plotly_chart(fig_weights)
portfolio_returns_msr = returns.dot(np.array(list(optimal_weights_msr.values())))
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
if isinstance(sp500.columns, pd.MultiIndex):
sp500.columns = sp500.columns.get_level_values(0)
if sp500.empty:
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
sp500_returns = sp500['Adj Close'].pct_change().dropna()
equal_weighted_returns = returns.mean(axis=1)
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_msr,
sp500_returns,
equal_weighted_returns,
'Cumulative Returns Over Time')
st.plotly_chart(fig_cumulative_returns)
fig_volatility, fig_sharpe = plot_rolling_metrics(portfolio_returns_msr,
sp500_returns,
equal_weighted_returns,
0.01,
'Rolling Volatility',
'Rolling Sharpe Ratio')
st.plotly_chart(fig_volatility)
st.plotly_chart(fig_sharpe)
fig_drawdown = plot_max_drawdown(portfolio_returns_msr,
sp500_returns,
equal_weighted_returns,
'Maximum Drawdown')
st.plotly_chart(fig_drawdown)
elif selected == "Equal Risk Contribution":
if 'erc_result' not in st.session_state:
st.session_state.erc_result = {}
data = fetch_stock_data(tickers, start_date, end_date)
returns = data.pct_change().dropna()
mean_returns = expected_returns.mean_historical_return(data)
cov_matrix = risk_models.sample_cov(data)
optimal_weights_erc = optimize_equal_risk_contribution(mean_returns, cov_matrix)
cleaned_weights_erc = dict(zip(tickers, optimal_weights_erc.values()))
st.session_state.erc_result['cleaned_weights'] = cleaned_weights_erc
st.session_state.erc_result['returns'] = returns
st.session_state.erc_result['optimal_weights'] = optimal_weights_erc
if 'erc_result' in st.session_state:
cleaned_weights_erc = st.session_state.erc_result['cleaned_weights']
returns = st.session_state.erc_result['returns']
optimal_weights_erc = st.session_state.erc_result['optimal_weights']
fig_weights = plot_portfolio_weights(list(cleaned_weights_erc.values()),
list(cleaned_weights_erc.keys()),
'Equal Risk Contribution Portfolio Weights')
st.plotly_chart(fig_weights)
portfolio_returns_erc = returns.dot(np.array(list(optimal_weights_erc.values())))
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
if isinstance(sp500.columns, pd.MultiIndex):
sp500.columns = sp500.columns.get_level_values(0)
if sp500.empty:
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
sp500_returns = sp500['Adj Close'].pct_change().dropna()
equal_weighted_returns = returns.mean(axis=1)
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_erc,
sp500_returns,
equal_weighted_returns,
'Cumulative Returns Over Time')
st.plotly_chart(fig_cumulative_returns)
fig_volatility, fig_sharpe = plot_rolling_metrics(portfolio_returns_erc,
sp500_returns,
equal_weighted_returns,
0.01,
'Rolling Volatility',
'Rolling Sharpe Ratio')
st.plotly_chart(fig_volatility)
st.plotly_chart(fig_sharpe)
fig_drawdown = plot_max_drawdown(portfolio_returns_erc,
sp500_returns,
equal_weighted_returns,
'Maximum Drawdown')
st.plotly_chart(fig_drawdown)
elif selected == "CVaR Minimization":
if 'cvar_result' not in st.session_state:
st.session_state.cvar_result = {}
data = fetch_stock_data(tickers, start_date, end_date)
returns = data.pct_change().dropna()
mean_returns = expected_returns.mean_historical_return(data)
optimal_weights_cvar = optimize_cvar_minimization(mean_returns, returns)
cleaned_weights_cvar = dict(zip(tickers, optimal_weights_cvar.values()))
st.session_state.cvar_result['cleaned_weights'] = cleaned_weights_cvar
st.session_state.cvar_result['returns'] = returns
st.session_state.cvar_result['optimal_weights'] = optimal_weights_cvar
if 'cvar_result' in st.session_state:
cleaned_weights_cvar = st.session_state.cvar_result['cleaned_weights']
returns = st.session_state.cvar_result['returns']
optimal_weights_cvar = st.session_state.cvar_result['optimal_weights']
fig_weights = plot_portfolio_weights(list(cleaned_weights_cvar.values()),
list(cleaned_weights_cvar.keys()),
'CVaR Minimization Portfolio Weights')
st.plotly_chart(fig_weights)
portfolio_returns_cvar = returns.dot(np.array(list(optimal_weights_cvar.values())))
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
if isinstance(sp500.columns, pd.MultiIndex):
sp500.columns = sp500.columns.get_level_values(0)
if sp500.empty:
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
sp500_returns = sp500['Adj Close'].pct_change().dropna()
equal_weighted_returns = returns.mean(axis=1)
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_cvar,
sp500_returns,
equal_weighted_returns,
'Cumulative Returns Over Time')
st.plotly_chart(fig_cumulative_returns)
fig_volatility, fig_sharpe = plot_rolling_metrics(portfolio_returns_cvar,
sp500_returns,
equal_weighted_returns,
0.01,
'Rolling Volatility',
'Rolling Sharpe Ratio')
st.plotly_chart(fig_volatility)
st.plotly_chart(fig_sharpe)
fig_drawdown = plot_max_drawdown(portfolio_returns_cvar,
sp500_returns,
equal_weighted_returns,
'Maximum Drawdown')
st.plotly_chart(fig_drawdown)
elif selected == "Maximum Return":
if 'max_return_result' not in st.session_state:
st.session_state.max_return_result = {}
data = fetch_stock_data(tickers, start_date, end_date)
returns = data.pct_change().dropna()
mean_returns = expected_returns.mean_historical_return(data)
cov_matrix = risk_models.sample_cov(data)
optimal_weights_max_return = optimize_max_return(mean_returns, cov_matrix)
cleaned_weights_max_return = dict(zip(tickers, optimal_weights_max_return.values()))
st.session_state.max_return_result['cleaned_weights'] = cleaned_weights_max_return
st.session_state.max_return_result['returns'] = returns
st.session_state.max_return_result['optimal_weights'] = optimal_weights_max_return
if 'max_return_result' in st.session_state:
cleaned_weights_max_return = st.session_state.max_return_result['cleaned_weights']
returns = st.session_state.max_return_result['returns']
optimal_weights_max_return = st.session_state.max_return_result['optimal_weights']
fig_weights = plot_portfolio_weights(list(cleaned_weights_max_return.values()),
list(cleaned_weights_max_return.keys()),
'Maximum Return Portfolio Weights')
st.plotly_chart(fig_weights)
portfolio_returns_max_return = returns.dot(np.array(list(optimal_weights_max_return.values())))
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
if isinstance(sp500.columns, pd.MultiIndex):
sp500.columns = sp500.columns.get_level_values(0)
if sp500.empty:
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
sp500_returns = sp500['Adj Close'].pct_change().dropna()
equal_weighted_returns = returns.mean(axis=1)
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_max_return,
sp500_returns,
equal_weighted_returns,
'Cumulative Returns Over Time')
st.plotly_chart(fig_cumulative_returns)
fig_volatility, fig_sharpe = plot_rolling_metrics(portfolio_returns_max_return,
sp500_returns,
equal_weighted_returns,
0.01,
'Rolling Volatility',
'Rolling Sharpe Ratio')
st.plotly_chart(fig_volatility)
st.plotly_chart(fig_sharpe)
fig_drawdown = plot_max_drawdown(portfolio_returns_max_return,
sp500_returns,
equal_weighted_returns,
'Maximum Drawdown')
st.plotly_chart(fig_drawdown)
elif selected == "Maximum Quadratic Utility":
if 'mqu_result' not in st.session_state:
st.session_state.mqu_result = {}
data = fetch_stock_data(tickers, start_date, end_date)
returns = data.pct_change().dropna()
mean_returns = expected_returns.mean_historical_return(data)
cov_matrix = risk_models.sample_cov(data)
optimal_weights_mqu = optimize_max_quadratic_utility(mean_returns, cov_matrix, risk_aversion)
cleaned_weights_mqu = dict(zip(tickers, optimal_weights_mqu.values()))
st.session_state.mqu_result['cleaned_weights'] = cleaned_weights_mqu
st.session_state.mqu_result['returns'] = returns
st.session_state.mqu_result['optimal_weights'] = optimal_weights_mqu
if 'mqu_result' in st.session_state:
cleaned_weights_mqu = st.session_state.mqu_result['cleaned_weights']
returns = st.session_state.mqu_result['returns']
optimal_weights_mqu = st.session_state.mqu_result['optimal_weights']
fig_weights = plot_portfolio_weights(list(cleaned_weights_mqu.values()),
list(cleaned_weights_mqu.keys()),
'Maximum Quadratic Utility Portfolio Weights')
st.plotly_chart(fig_weights)
portfolio_returns_mqu = returns.dot(np.array(list(optimal_weights_mqu.values())))
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
if isinstance(sp500.columns, pd.MultiIndex):
sp500.columns = sp500.columns.get_level_values(0)
if sp500.empty:
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
sp500_returns = sp500['Adj Close'].pct_change().dropna()
equal_weighted_returns = returns.mean(axis=1)
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_mqu,
sp500_returns,
equal_weighted_returns,
'Cumulative Returns Over Time')
st.plotly_chart(fig_cumulative_returns)
fig_volatility, fig_sharpe = plot_rolling_metrics(portfolio_returns_mqu,
sp500_returns,
equal_weighted_returns,
0.01,
'Rolling Volatility',
'Rolling Sharpe Ratio')
st.plotly_chart(fig_volatility)
st.plotly_chart(fig_sharpe)
fig_drawdown = plot_max_drawdown(portfolio_returns_mqu,
sp500_returns,
equal_weighted_returns,
'Maximum Drawdown')
st.plotly_chart(fig_drawdown)
st.markdown(
"""
<style>
[data-testid="stSidebar"] {
width: 500px;
}
</style>
""",
unsafe_allow_html=True
)
hide_streamlit_style = """
<style>
#MainMenu {visibility: hidden;}
footer {visibility: hidden;}
</style>
"""
st.markdown(hide_streamlit_style, unsafe_allow_html=True)