Spaces:
Running
Running
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) |