Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -11,7 +11,12 @@ import matplotlib.pyplot as plt
|
|
| 11 |
|
| 12 |
# Helper functions
|
| 13 |
def fetch_stock_data(tickers, start_date, end_date):
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
def plot_efficient_frontier(mean_returns, cov_matrix, title):
|
| 17 |
ef = EfficientFrontier(mean_returns, cov_matrix)
|
|
@@ -259,7 +264,6 @@ def optimize_max_quadratic_utility(mean_returns, cov_matrix, risk_aversion):
|
|
| 259 |
return ef.clean_weights()
|
| 260 |
|
| 261 |
# Streamlit app
|
| 262 |
-
st.set_page_config(page_title="Portfolio Optimization", layout="wide")
|
| 263 |
st.title('Portfolio Optimization Methods')
|
| 264 |
st.markdown("""
|
| 265 |
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.
|
|
@@ -319,7 +323,6 @@ if selected == "Multi-Objective Optimization":
|
|
| 319 |
with st.expander("Methodology and Formulas", expanded=False):
|
| 320 |
st.markdown("""
|
| 321 |
#### Formulas:
|
| 322 |
-
|
| 323 |
**Expected Return**:
|
| 324 |
""")
|
| 325 |
st.latex(r"""
|
|
@@ -356,9 +359,7 @@ with st.expander("Methodology and Formulas", expanded=False):
|
|
| 356 |
|
| 357 |
st.markdown("""
|
| 358 |
#### Mathematical Optimization Process
|
| 359 |
-
|
| 360 |
The optimization process involves solving a mathematical problem to find the portfolio weights \( wi \) that best meet the selected objectives. The optimization problem can be formulated as follows:
|
| 361 |
-
|
| 362 |
**Objective Function**:
|
| 363 |
Minimize or maximize a weighted sum of multiple objectives. The overall objective function can be expressed as:
|
| 364 |
""")
|
|
@@ -370,7 +371,6 @@ with st.expander("Methodology and Formulas", expanded=False):
|
|
| 370 |
where:
|
| 371 |
- \( \lambda_i \) are the weights assigned to each objective.
|
| 372 |
- \( \mathbf{w} \) is the vector of portfolio weights.
|
| 373 |
-
|
| 374 |
**Constraints**:
|
| 375 |
1. The sum of the portfolio weights must equal 1 (fully invested portfolio):
|
| 376 |
""")
|
|
@@ -387,9 +387,7 @@ with st.expander("Methodology and Formulas", expanded=False):
|
|
| 387 |
|
| 388 |
st.markdown("""
|
| 389 |
3. Additional constraints based on user preferences or regulatory requirements can also be included.
|
| 390 |
-
|
| 391 |
The optimization is performed using numerical methods, i.e. using the Sequential Least Squares Programming (SLSQP) algorithm. This algorithm iteratively adjusts the portfolio weights to find the optimal solution that satisfies the constraints and minimizes (or maximizes) the objective function.
|
| 392 |
-
|
| 393 |
""")
|
| 394 |
|
| 395 |
st.sidebar.header("Optimization Parameters")
|
|
@@ -441,8 +439,12 @@ if 'multi_objective_result' in st.session_state:
|
|
| 441 |
st.plotly_chart(fig_weights)
|
| 442 |
|
| 443 |
portfolio_returns = returns.dot(optimal_weights)
|
| 444 |
-
sp500 = yf.download('^GSPC', start=start_date, end=end_date
|
| 445 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 446 |
equal_weighted_returns = returns.mean(axis=1)
|
| 447 |
|
| 448 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns,
|
|
@@ -479,7 +481,6 @@ elif selected == "Robust Optimization":
|
|
| 479 |
#### Concept:
|
| 480 |
The covariance matrix can be unstable due to limited data or market volatility.
|
| 481 |
Robust Optimization addresses this by applying techniques such as covariance shrinkage to produce a more stable covariance matrix.
|
| 482 |
-
|
| 483 |
#### Regularization Technique:
|
| 484 |
One common approach in Robust Optimization is the Ledoit-Wolf shrinkage method, which adjusts the sample covariance matrix towards a more structured target, such as the identity matrix.
|
| 485 |
**Covariance Shrinkage**:
|
|
@@ -518,7 +519,6 @@ elif selected == "Robust Optimization":
|
|
| 518 |
""")
|
| 519 |
st.markdown("""
|
| 520 |
#### Practical Benefits:
|
| 521 |
-
|
| 522 |
- **Stability**: By reducing the impact of outliers and estimation errors, Robust Optimization produces more stable portfolios.
|
| 523 |
- **Performance**: It can improve portfolio performance by avoiding extreme weights that might result from noisy data.
|
| 524 |
- **Applicability**: Suitable for environments with high market volatility or limited historical data.
|
|
@@ -556,8 +556,12 @@ elif selected == "Robust Optimization":
|
|
| 556 |
st.plotly_chart(fig_weights)
|
| 557 |
|
| 558 |
portfolio_returns_robust = returns.dot(np.array(list(cleaned_weights_robust.values())))
|
| 559 |
-
sp500 = yf.download('^GSPC', start=start_date, end=end_date
|
| 560 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 561 |
equal_weighted_returns = returns.mean(axis=1)
|
| 562 |
|
| 563 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_robust,
|
|
@@ -593,7 +597,6 @@ elif selected == "Mean-Variance Optimization":
|
|
| 593 |
#### Concept:
|
| 594 |
The Mean-Variance Optimization approach is based on the idea of creating a portfolio that provides the maximum expected return for a given level of risk or, equivalently, the minimum risk for a given level of expected return.
|
| 595 |
This is achieved by selecting the proportions of various assets in the portfolio.
|
| 596 |
-
|
| 597 |
#### Sharpe Ratio:
|
| 598 |
The Sharpe ratio is a measure of the risk-adjusted return of a portfolio. It is calculated as:
|
| 599 |
""")
|
|
@@ -605,10 +608,8 @@ elif selected == "Mean-Variance Optimization":
|
|
| 605 |
- \( \mathbb{E}[R_p] \) is the expected return of the portfolio.
|
| 606 |
- \( R_f \) is the risk-free rate.
|
| 607 |
- \( \sigma_p \) is the standard deviation of the portfolio returns.
|
| 608 |
-
|
| 609 |
#### Mathematical Optimization Process:
|
| 610 |
The optimization problem can be formulated as follows:
|
| 611 |
-
|
| 612 |
**Objective Function**:
|
| 613 |
""")
|
| 614 |
st.latex(r"""
|
|
@@ -619,7 +620,6 @@ elif selected == "Mean-Variance Optimization":
|
|
| 619 |
- \( w \) is the vector of portfolio weights.
|
| 620 |
- \( \mu \) is the vector of expected asset returns.
|
| 621 |
- \( \Sigma \) is the covariance matrix of asset returns.
|
| 622 |
-
|
| 623 |
**Constraints**:
|
| 624 |
1. The sum of the portfolio weights must equal 1 (fully invested portfolio):
|
| 625 |
""")
|
|
@@ -634,7 +634,6 @@ elif selected == "Mean-Variance Optimization":
|
|
| 634 |
""")
|
| 635 |
st.markdown("""
|
| 636 |
#### Benefits of Mean-Variance Optimization:
|
| 637 |
-
|
| 638 |
- **Risk Management**: It helps in managing the trade-off between risk and return by considering the covariance among asset returns.
|
| 639 |
- **Diversification**: By incorporating the covariance matrix, it promotes diversification and reduces unsystematic risk.
|
| 640 |
- **Simplicity**: It provides a straightforward framework for constructing efficient portfolios.
|
|
@@ -674,8 +673,12 @@ elif selected == "Mean-Variance Optimization":
|
|
| 674 |
st.plotly_chart(fig_weights)
|
| 675 |
|
| 676 |
portfolio_returns = returns.dot(optimal_weights)
|
| 677 |
-
sp500 = yf.download('^GSPC', start=start_date, end=end_date
|
| 678 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 679 |
equal_weighted_returns = returns.mean(axis=1)
|
| 680 |
|
| 681 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns,
|
|
@@ -715,7 +718,6 @@ elif selected == "Black-Litterman Model":
|
|
| 715 |
st.markdown("""
|
| 716 |
#### Concept:
|
| 717 |
The Black-Litterman Model starts with the market equilibrium returns, which are implied by market capitalizations and a given risk aversion coefficient. Investors can then specify their own views on expected returns for certain assets or combinations of assets.
|
| 718 |
-
|
| 719 |
#### Market Equilibrium Returns:
|
| 720 |
The market equilibrium returns, \( \Pi \), are given by:
|
| 721 |
""")
|
|
@@ -727,7 +729,6 @@ elif selected == "Black-Litterman Model":
|
|
| 727 |
- \( \delta \) is the risk aversion coefficient.
|
| 728 |
- \( \Sigma \) is the covariance matrix of asset returns.
|
| 729 |
- \( w_{mkt} \) is the vector of market capitalization weights.
|
| 730 |
-
|
| 731 |
#### Investor Views:
|
| 732 |
Investor views are expressed in the form of a matrix \( P \) and a vector \( Q \):
|
| 733 |
""")
|
|
@@ -739,7 +740,6 @@ elif selected == "Black-Litterman Model":
|
|
| 739 |
- \( P \) is the matrix that identifies the assets involved in each view.
|
| 740 |
- \( \mu \) is the vector of expected returns.
|
| 741 |
- \( Q \) is the vector of view returns.
|
| 742 |
-
|
| 743 |
#### Adjusted Expected Returns:
|
| 744 |
The adjusted expected returns, \( \mu \), are then calculated as:
|
| 745 |
""")
|
|
@@ -750,10 +750,8 @@ elif selected == "Black-Litterman Model":
|
|
| 750 |
Where:
|
| 751 |
- \( \tau \) is a scalar representing the uncertainty in the prior estimates.
|
| 752 |
- \( \Omega \) is the diagonal covariance matrix of the error terms in the views.
|
| 753 |
-
|
| 754 |
#### Optimization:
|
| 755 |
Once the adjusted expected returns, \( \mu \), are obtained, we can apply mean-variance optimization to find the optimal portfolio weights.
|
| 756 |
-
|
| 757 |
**Objective Function**:
|
| 758 |
""")
|
| 759 |
st.latex(r"""
|
|
@@ -774,7 +772,6 @@ elif selected == "Black-Litterman Model":
|
|
| 774 |
""")
|
| 775 |
st.markdown("""
|
| 776 |
#### Benefits of the Black-Litterman Model:
|
| 777 |
-
|
| 778 |
- **Flexibility**: Incorporates investor views into the optimization process.
|
| 779 |
- **Stability**: Produces more stable and intuitive expected returns than traditional mean-variance optimization.
|
| 780 |
- **Theoretical Foundation**: Combines the principles of modern portfolio theory with practical investor insights.
|
|
@@ -792,7 +789,12 @@ elif selected == "Black-Litterman Model":
|
|
| 792 |
cov_matrix = risk_models.sample_cov(data)
|
| 793 |
|
| 794 |
# Download market data for market capitalization weights (here using S&P 500 as proxy)
|
| 795 |
-
market_data = yf.download('^GSPC', start=start_date, end=end_date
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 796 |
|
| 797 |
# Calculate market-implied risk aversion
|
| 798 |
delta = black_litterman.market_implied_risk_aversion(market_data)
|
|
@@ -838,8 +840,12 @@ elif selected == "Black-Litterman Model":
|
|
| 838 |
st.plotly_chart(fig_weights)
|
| 839 |
|
| 840 |
portfolio_returns_bl = returns.dot(optimal_weights_bl)
|
| 841 |
-
sp500 = yf.download('^GSPC', start=start_date, end=end_date
|
| 842 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 843 |
equal_weighted_returns = returns.mean(axis=1)
|
| 844 |
|
| 845 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_bl,
|
|
@@ -890,10 +896,8 @@ elif selected == "Hierarchical Risk Parity":
|
|
| 890 |
st.markdown("""
|
| 891 |
Where:
|
| 892 |
- \( \rho_{i,j} \) is the correlation coefficient between assets \( i \) and \( j \).
|
| 893 |
-
|
| 894 |
#### Quasi-Diagonalization:
|
| 895 |
The covariance matrix \( \Sigma \) is reordered to a block-diagonal form using the clustering information. This step ensures that the assets with higher correlations are grouped together.
|
| 896 |
-
|
| 897 |
#### Recursive Bisection:
|
| 898 |
Risk is allocated recursively using the following steps:
|
| 899 |
1. **Top-Level Allocation**:
|
|
@@ -905,13 +909,11 @@ elif selected == "Hierarchical Risk Parity":
|
|
| 905 |
st.markdown("""
|
| 906 |
2. **Sub-Level Allocation**:
|
| 907 |
The risk within each cluster is further split recursively until the individual assets are reached.
|
| 908 |
-
|
| 909 |
This recursive process ensures that risk is allocated evenly across all clusters and individual assets.
|
| 910 |
""")
|
| 911 |
|
| 912 |
st.markdown("""
|
| 913 |
#### Benefits of HRP:
|
| 914 |
-
|
| 915 |
- **Improved Diversification**: By clustering similar assets and allocating risk evenly, HRP achieves better diversification compared to traditional methods.
|
| 916 |
- **Reduced Risk Concentration**: HRP avoids concentrating risk in highly correlated assets, resulting in a more balanced portfolio.
|
| 917 |
- **Robustness**: HRP is less sensitive to estimation errors in the covariance matrix, making it a robust method for portfolio optimization.
|
|
@@ -949,8 +951,12 @@ elif selected == "Hierarchical Risk Parity":
|
|
| 949 |
st.plotly_chart(fig_weights)
|
| 950 |
|
| 951 |
portfolio_returns_hrp = returns.dot(optimal_weights_hrp)
|
| 952 |
-
sp500 = yf.download('^GSPC', start=start_date, end=end_date
|
| 953 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 954 |
equal_weighted_returns = returns.mean(axis=1)
|
| 955 |
|
| 956 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_hrp,
|
|
@@ -998,10 +1004,8 @@ elif selected == "Maximum Diversification":
|
|
| 998 |
- \( \sigma_i \) is the volatility of asset \( i \).
|
| 999 |
- \( \Sigma \) is the covariance matrix of asset returns.
|
| 1000 |
- \( w \) is the weight vector of the portfolio.
|
| 1001 |
-
|
| 1002 |
#### Objective:
|
| 1003 |
The objective of the Maximum Diversification Portfolio is to maximize this diversification ratio. By doing so, the portfolio aims to achieve the highest possible diversification benefit, reducing the overall portfolio risk.
|
| 1004 |
-
|
| 1005 |
#### Mathematical Formulation:
|
| 1006 |
The optimization problem can be formulated as:
|
| 1007 |
""")
|
|
@@ -1011,21 +1015,17 @@ elif selected == "Maximum Diversification":
|
|
| 1011 |
""")
|
| 1012 |
st.markdown("""
|
| 1013 |
This ensures that the sum of the weights is equal to 1 (fully invested portfolio) and that all weights are non-negative (long-only portfolio).
|
| 1014 |
-
|
| 1015 |
#### Steps Involved:
|
| 1016 |
1. **Calculate Individual Volatilities**:
|
| 1017 |
Compute the volatility (\( \sigma_i \)) for each asset in the portfolio.
|
| 1018 |
-
|
| 1019 |
2. **Compute the Covariance Matrix**:
|
| 1020 |
Calculate the covariance matrix (\( \Sigma \)) of asset returns.
|
| 1021 |
-
|
| 1022 |
3. **Optimize the Diversification Ratio**:
|
| 1023 |
Use optimization techniques to find the weight vector (\( w \)) that maximizes the diversification ratio, subject to the constraints.
|
| 1024 |
""")
|
| 1025 |
|
| 1026 |
st.markdown("""
|
| 1027 |
#### Benefits:
|
| 1028 |
-
|
| 1029 |
- **Enhanced Diversification**: By focusing on maximizing the diversification ratio, this method ensures that the portfolio is well-diversified, reducing the impact of any single asset on the overall portfolio risk.
|
| 1030 |
- **Reduced Portfolio Risk**: By maximizing the diversification benefit, the overall portfolio risk is minimized compared to other traditional optimization methods.
|
| 1031 |
""")
|
|
@@ -1064,8 +1064,12 @@ elif selected == "Maximum Diversification":
|
|
| 1064 |
st.plotly_chart(fig_weights)
|
| 1065 |
|
| 1066 |
portfolio_returns_mdp = returns.dot(optimal_weights_mdp)
|
| 1067 |
-
sp500 = yf.download('^GSPC', start=start_date, end=end_date
|
| 1068 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1069 |
equal_weighted_returns = returns.mean(axis=1)
|
| 1070 |
|
| 1071 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_mdp,
|
|
@@ -1109,7 +1113,6 @@ elif selected == "Maximum Sharpe Ratio":
|
|
| 1109 |
- \( R_p \) is the expected portfolio return.
|
| 1110 |
- \( R_f \) is the risk-free rate.
|
| 1111 |
- \( \sigma_p \) is the portfolio's standard deviation (volatility).
|
| 1112 |
-
|
| 1113 |
#### Objective:
|
| 1114 |
The objective of the Maximum Sharpe Ratio Portfolio is to find the portfolio weights that maximize the Sharpe ratio. This ensures the highest possible return for a given level of risk.
|
| 1115 |
""")
|
|
@@ -1149,8 +1152,12 @@ elif selected == "Maximum Sharpe Ratio":
|
|
| 1149 |
st.plotly_chart(fig_weights)
|
| 1150 |
|
| 1151 |
portfolio_returns_msr = returns.dot(np.array(list(optimal_weights_msr.values())))
|
| 1152 |
-
sp500 = yf.download('^GSPC', start=start_date, end=end_date
|
| 1153 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1154 |
equal_weighted_returns = returns.mean(axis=1)
|
| 1155 |
|
| 1156 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_msr,
|
|
@@ -1185,7 +1192,6 @@ elif selected == "Equal Risk Contribution":
|
|
| 1185 |
st.markdown("""
|
| 1186 |
#### Concept:
|
| 1187 |
The key idea is to balance the contribution of each asset to the total portfolio risk, such that the risk contribution from each asset is equal.
|
| 1188 |
-
|
| 1189 |
#### Objective:
|
| 1190 |
The objective of the ERC Portfolio is to find the weights of the assets that equalize their risk contributions. The risk contribution of an asset \( i \) to the total portfolio risk is given by:
|
| 1191 |
""")
|
|
@@ -1197,7 +1203,6 @@ elif selected == "Equal Risk Contribution":
|
|
| 1197 |
- \( RC_i \) is the risk contribution of asset \( i \).
|
| 1198 |
- \( w_i \) is the weight of asset \( i \).
|
| 1199 |
- \( \sigma_p \) is the portfolio's standard deviation (volatility).
|
| 1200 |
-
|
| 1201 |
#### Mathematical Formulation:
|
| 1202 |
The optimization problem can be formulated as:
|
| 1203 |
""")
|
|
@@ -1209,7 +1214,6 @@ elif selected == "Equal Risk Contribution":
|
|
| 1209 |
Where:
|
| 1210 |
- \( n \) is the number of assets.
|
| 1211 |
- The goal is to minimize the sum of squared deviations of each asset's risk contribution from the average risk contribution \( \frac{\sigma_p}{n} \).
|
| 1212 |
-
|
| 1213 |
#### Steps Involved:
|
| 1214 |
1. **Estimate Expected Returns and Covariance Matrix**:
|
| 1215 |
Compute the expected returns (\( \mu \)) and the covariance matrix (\( \Sigma \)) for the assets.
|
|
@@ -1221,7 +1225,6 @@ elif selected == "Equal Risk Contribution":
|
|
| 1221 |
|
| 1222 |
st.markdown("""
|
| 1223 |
#### Benefits:
|
| 1224 |
-
|
| 1225 |
- **Balanced Risk Allocation**: Ensures that no single asset dominates the portfolio risk, leading to a more balanced and diversified portfolio.
|
| 1226 |
- **Risk Management**: Helps in managing the overall portfolio risk by spreading it equally across all assets.
|
| 1227 |
""")
|
|
@@ -1255,8 +1258,12 @@ elif selected == "Equal Risk Contribution":
|
|
| 1255 |
st.plotly_chart(fig_weights)
|
| 1256 |
|
| 1257 |
portfolio_returns_erc = returns.dot(np.array(list(optimal_weights_erc.values())))
|
| 1258 |
-
sp500 = yf.download('^GSPC', start=start_date, end=end_date
|
| 1259 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1260 |
equal_weighted_returns = returns.mean(axis=1)
|
| 1261 |
|
| 1262 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_erc,
|
|
@@ -1291,10 +1298,8 @@ elif selected == "CVaR Minimization":
|
|
| 1291 |
st.markdown("""
|
| 1292 |
#### Concept:
|
| 1293 |
Conditional Value at Risk (CVaR) is a risk assessment measure that quantifies the expected loss in the worst-case scenarios beyond a certain confidence level. It provides a more comprehensive risk measure compared to Value at Risk (VaR).
|
| 1294 |
-
|
| 1295 |
#### Objective:
|
| 1296 |
The objective of the CVaR Minimization Portfolio is to find the weights of the assets that minimize the CVaR at a given confidence level.
|
| 1297 |
-
|
| 1298 |
#### Mathematical Formulation:
|
| 1299 |
The optimization problem can be formulated as:
|
| 1300 |
""")
|
|
@@ -1309,7 +1314,6 @@ elif selected == "CVaR Minimization":
|
|
| 1309 |
- \( \alpha \) is the confidence level (e.g., 95%).
|
| 1310 |
- \( T \) is the number of observations.
|
| 1311 |
- \( L_t(w) \) is the loss of the portfolio at time \( t \).
|
| 1312 |
-
|
| 1313 |
#### Steps Involved:
|
| 1314 |
1. **Estimate Expected Returns and Covariance Matrix**:
|
| 1315 |
Compute the expected returns (\( \mu \)) and the covariance matrix (\( \Sigma \)) for the assets.
|
|
@@ -1354,8 +1358,12 @@ elif selected == "CVaR Minimization":
|
|
| 1354 |
st.plotly_chart(fig_weights)
|
| 1355 |
|
| 1356 |
portfolio_returns_cvar = returns.dot(np.array(list(optimal_weights_cvar.values())))
|
| 1357 |
-
sp500 = yf.download('^GSPC', start=start_date, end=end_date
|
| 1358 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1359 |
equal_weighted_returns = returns.mean(axis=1)
|
| 1360 |
|
| 1361 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_cvar,
|
|
@@ -1390,10 +1398,8 @@ elif selected == "Maximum Return":
|
|
| 1390 |
st.markdown("""
|
| 1391 |
#### Concept:
|
| 1392 |
The Maximum Return Portfolio aims to achieve the highest possible return by investing in assets that have the highest expected returns. This approach does not take into account the risk or volatility associated with the assets.
|
| 1393 |
-
|
| 1394 |
#### Objective:
|
| 1395 |
The objective of the Maximum Return Portfolio is to find the weights of the assets that maximize the expected return.
|
| 1396 |
-
|
| 1397 |
#### Mathematical Formulation:
|
| 1398 |
The optimization problem can be formulated as:
|
| 1399 |
""")
|
|
@@ -1406,7 +1412,6 @@ elif selected == "Maximum Return":
|
|
| 1406 |
- \( w \) is the weight vector of the assets.
|
| 1407 |
- \( \mu \) is the expected return vector of the assets.
|
| 1408 |
- \( n \) is the number of assets.
|
| 1409 |
-
|
| 1410 |
#### Steps Involved:
|
| 1411 |
1. **Estimate Expected Returns**:
|
| 1412 |
Compute the expected returns (\( \mu \)) for each asset.
|
|
@@ -1416,12 +1421,9 @@ elif selected == "Maximum Return":
|
|
| 1416 |
|
| 1417 |
st.markdown("""
|
| 1418 |
#### Benefits:
|
| 1419 |
-
|
| 1420 |
- **High Potential Returns**: Focuses on achieving the highest possible returns.
|
| 1421 |
- **Simple Approach**: Easy to understand and implement.
|
| 1422 |
-
|
| 1423 |
#### Drawbacks:
|
| 1424 |
-
|
| 1425 |
- **Ignores Risk**: Does not consider the risk or volatility associated with the assets.
|
| 1426 |
- **Potential for High Volatility**: Can result in a highly volatile portfolio.
|
| 1427 |
""")
|
|
@@ -1455,8 +1457,12 @@ elif selected == "Maximum Return":
|
|
| 1455 |
st.plotly_chart(fig_weights)
|
| 1456 |
|
| 1457 |
portfolio_returns_max_return = returns.dot(np.array(list(optimal_weights_max_return.values())))
|
| 1458 |
-
sp500 = yf.download('^GSPC', start=start_date, end=end_date
|
| 1459 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1460 |
equal_weighted_returns = returns.mean(axis=1)
|
| 1461 |
|
| 1462 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_max_return,
|
|
@@ -1491,7 +1497,6 @@ elif selected == "Maximum Quadratic Utility":
|
|
| 1491 |
st.markdown("""
|
| 1492 |
#### Concept:
|
| 1493 |
The Maximum Quadratic Utility Portfolio aims to find the optimal portfolio that maximizes an investor's utility, which is a measure of satisfaction or preference. This approach takes into account both the expected return and the risk of the portfolio, weighted by a risk aversion coefficient.
|
| 1494 |
-
|
| 1495 |
#### Objective:
|
| 1496 |
The objective of the Maximum Quadratic Utility Portfolio is to maximize the utility function, which is defined as:
|
| 1497 |
""")
|
|
@@ -1505,7 +1510,6 @@ elif selected == "Maximum Quadratic Utility":
|
|
| 1505 |
- \( \mu \) is the expected return vector of the assets.
|
| 1506 |
- \( \Sigma \) is the covariance matrix of the asset returns.
|
| 1507 |
- \( \lambda \) is the risk aversion coefficient.
|
| 1508 |
-
|
| 1509 |
#### Steps Involved:
|
| 1510 |
1. **Estimate Expected Returns and Covariance Matrix**:
|
| 1511 |
Compute the expected returns (\( \mu \)) and the covariance matrix (\( \Sigma \)) for the assets.
|
|
@@ -1517,12 +1521,9 @@ elif selected == "Maximum Quadratic Utility":
|
|
| 1517 |
|
| 1518 |
st.markdown("""
|
| 1519 |
#### Benefits:
|
| 1520 |
-
|
| 1521 |
- **Balances Return and Risk**: Provides a trade-off between maximizing returns and minimizing risk.
|
| 1522 |
- **Customizable Risk Tolerance**: Allows adjustment of the risk aversion coefficient to match the investor's risk preference.
|
| 1523 |
-
|
| 1524 |
#### Drawbacks:
|
| 1525 |
-
|
| 1526 |
- **Requires Accurate Estimates**: The effectiveness of the method depends on accurate estimates of expected returns and the covariance matrix.
|
| 1527 |
- **Complexity**: More complex to implement compared to simpler methods like Maximum Return Portfolio.
|
| 1528 |
""")
|
|
@@ -1557,8 +1558,12 @@ elif selected == "Maximum Quadratic Utility":
|
|
| 1557 |
st.plotly_chart(fig_weights)
|
| 1558 |
|
| 1559 |
portfolio_returns_mqu = returns.dot(np.array(list(optimal_weights_mqu.values())))
|
| 1560 |
-
sp500 = yf.download('^GSPC', start=start_date, end=end_date
|
| 1561 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1562 |
equal_weighted_returns = returns.mean(axis=1)
|
| 1563 |
|
| 1564 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_mqu,
|
|
@@ -1600,4 +1605,4 @@ hide_streamlit_style = """
|
|
| 1600 |
footer {visibility: hidden;}
|
| 1601 |
</style>
|
| 1602 |
"""
|
| 1603 |
-
st.markdown(hide_streamlit_style, unsafe_allow_html=True)
|
|
|
|
| 11 |
|
| 12 |
# Helper functions
|
| 13 |
def fetch_stock_data(tickers, start_date, end_date):
|
| 14 |
+
data = yf.download(tickers, start=start_date, end=end_date, auto_adjust=False)['Adj Close']
|
| 15 |
+
if isinstance(data.columns, pd.MultiIndex):
|
| 16 |
+
data.columns = data.columns.get_level_values(0)
|
| 17 |
+
if data.empty:
|
| 18 |
+
raise ValueError(f"No data fetched for {tickers} from {start_date} to {end_date}.")
|
| 19 |
+
return data
|
| 20 |
|
| 21 |
def plot_efficient_frontier(mean_returns, cov_matrix, title):
|
| 22 |
ef = EfficientFrontier(mean_returns, cov_matrix)
|
|
|
|
| 264 |
return ef.clean_weights()
|
| 265 |
|
| 266 |
# Streamlit app
|
|
|
|
| 267 |
st.title('Portfolio Optimization Methods')
|
| 268 |
st.markdown("""
|
| 269 |
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.
|
|
|
|
| 323 |
with st.expander("Methodology and Formulas", expanded=False):
|
| 324 |
st.markdown("""
|
| 325 |
#### Formulas:
|
|
|
|
| 326 |
**Expected Return**:
|
| 327 |
""")
|
| 328 |
st.latex(r"""
|
|
|
|
| 359 |
|
| 360 |
st.markdown("""
|
| 361 |
#### Mathematical Optimization Process
|
|
|
|
| 362 |
The optimization process involves solving a mathematical problem to find the portfolio weights \( wi \) that best meet the selected objectives. The optimization problem can be formulated as follows:
|
|
|
|
| 363 |
**Objective Function**:
|
| 364 |
Minimize or maximize a weighted sum of multiple objectives. The overall objective function can be expressed as:
|
| 365 |
""")
|
|
|
|
| 371 |
where:
|
| 372 |
- \( \lambda_i \) are the weights assigned to each objective.
|
| 373 |
- \( \mathbf{w} \) is the vector of portfolio weights.
|
|
|
|
| 374 |
**Constraints**:
|
| 375 |
1. The sum of the portfolio weights must equal 1 (fully invested portfolio):
|
| 376 |
""")
|
|
|
|
| 387 |
|
| 388 |
st.markdown("""
|
| 389 |
3. Additional constraints based on user preferences or regulatory requirements can also be included.
|
|
|
|
| 390 |
The optimization is performed using numerical methods, i.e. using the Sequential Least Squares Programming (SLSQP) algorithm. This algorithm iteratively adjusts the portfolio weights to find the optimal solution that satisfies the constraints and minimizes (or maximizes) the objective function.
|
|
|
|
| 391 |
""")
|
| 392 |
|
| 393 |
st.sidebar.header("Optimization Parameters")
|
|
|
|
| 439 |
st.plotly_chart(fig_weights)
|
| 440 |
|
| 441 |
portfolio_returns = returns.dot(optimal_weights)
|
| 442 |
+
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
|
| 443 |
+
if isinstance(sp500.columns, pd.MultiIndex):
|
| 444 |
+
sp500.columns = sp500.columns.get_level_values(0)
|
| 445 |
+
if sp500.empty:
|
| 446 |
+
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
|
| 447 |
+
sp500_returns = sp500['Adj Close'].pct_change().dropna()
|
| 448 |
equal_weighted_returns = returns.mean(axis=1)
|
| 449 |
|
| 450 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns,
|
|
|
|
| 481 |
#### Concept:
|
| 482 |
The covariance matrix can be unstable due to limited data or market volatility.
|
| 483 |
Robust Optimization addresses this by applying techniques such as covariance shrinkage to produce a more stable covariance matrix.
|
|
|
|
| 484 |
#### Regularization Technique:
|
| 485 |
One common approach in Robust Optimization is the Ledoit-Wolf shrinkage method, which adjusts the sample covariance matrix towards a more structured target, such as the identity matrix.
|
| 486 |
**Covariance Shrinkage**:
|
|
|
|
| 519 |
""")
|
| 520 |
st.markdown("""
|
| 521 |
#### Practical Benefits:
|
|
|
|
| 522 |
- **Stability**: By reducing the impact of outliers and estimation errors, Robust Optimization produces more stable portfolios.
|
| 523 |
- **Performance**: It can improve portfolio performance by avoiding extreme weights that might result from noisy data.
|
| 524 |
- **Applicability**: Suitable for environments with high market volatility or limited historical data.
|
|
|
|
| 556 |
st.plotly_chart(fig_weights)
|
| 557 |
|
| 558 |
portfolio_returns_robust = returns.dot(np.array(list(cleaned_weights_robust.values())))
|
| 559 |
+
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
|
| 560 |
+
if isinstance(sp500.columns, pd.MultiIndex):
|
| 561 |
+
sp500.columns = sp500.columns.get_level_values(0)
|
| 562 |
+
if sp500.empty:
|
| 563 |
+
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
|
| 564 |
+
sp500_returns = sp500['Adj Close'].pct_change().dropna()
|
| 565 |
equal_weighted_returns = returns.mean(axis=1)
|
| 566 |
|
| 567 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_robust,
|
|
|
|
| 597 |
#### Concept:
|
| 598 |
The Mean-Variance Optimization approach is based on the idea of creating a portfolio that provides the maximum expected return for a given level of risk or, equivalently, the minimum risk for a given level of expected return.
|
| 599 |
This is achieved by selecting the proportions of various assets in the portfolio.
|
|
|
|
| 600 |
#### Sharpe Ratio:
|
| 601 |
The Sharpe ratio is a measure of the risk-adjusted return of a portfolio. It is calculated as:
|
| 602 |
""")
|
|
|
|
| 608 |
- \( \mathbb{E}[R_p] \) is the expected return of the portfolio.
|
| 609 |
- \( R_f \) is the risk-free rate.
|
| 610 |
- \( \sigma_p \) is the standard deviation of the portfolio returns.
|
|
|
|
| 611 |
#### Mathematical Optimization Process:
|
| 612 |
The optimization problem can be formulated as follows:
|
|
|
|
| 613 |
**Objective Function**:
|
| 614 |
""")
|
| 615 |
st.latex(r"""
|
|
|
|
| 620 |
- \( w \) is the vector of portfolio weights.
|
| 621 |
- \( \mu \) is the vector of expected asset returns.
|
| 622 |
- \( \Sigma \) is the covariance matrix of asset returns.
|
|
|
|
| 623 |
**Constraints**:
|
| 624 |
1. The sum of the portfolio weights must equal 1 (fully invested portfolio):
|
| 625 |
""")
|
|
|
|
| 634 |
""")
|
| 635 |
st.markdown("""
|
| 636 |
#### Benefits of Mean-Variance Optimization:
|
|
|
|
| 637 |
- **Risk Management**: It helps in managing the trade-off between risk and return by considering the covariance among asset returns.
|
| 638 |
- **Diversification**: By incorporating the covariance matrix, it promotes diversification and reduces unsystematic risk.
|
| 639 |
- **Simplicity**: It provides a straightforward framework for constructing efficient portfolios.
|
|
|
|
| 673 |
st.plotly_chart(fig_weights)
|
| 674 |
|
| 675 |
portfolio_returns = returns.dot(optimal_weights)
|
| 676 |
+
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
|
| 677 |
+
if isinstance(sp500.columns, pd.MultiIndex):
|
| 678 |
+
sp500.columns = sp500.columns.get_level_values(0)
|
| 679 |
+
if sp500.empty:
|
| 680 |
+
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
|
| 681 |
+
sp500_returns = sp500['Adj Close'].pct_change().dropna()
|
| 682 |
equal_weighted_returns = returns.mean(axis=1)
|
| 683 |
|
| 684 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns,
|
|
|
|
| 718 |
st.markdown("""
|
| 719 |
#### Concept:
|
| 720 |
The Black-Litterman Model starts with the market equilibrium returns, which are implied by market capitalizations and a given risk aversion coefficient. Investors can then specify their own views on expected returns for certain assets or combinations of assets.
|
|
|
|
| 721 |
#### Market Equilibrium Returns:
|
| 722 |
The market equilibrium returns, \( \Pi \), are given by:
|
| 723 |
""")
|
|
|
|
| 729 |
- \( \delta \) is the risk aversion coefficient.
|
| 730 |
- \( \Sigma \) is the covariance matrix of asset returns.
|
| 731 |
- \( w_{mkt} \) is the vector of market capitalization weights.
|
|
|
|
| 732 |
#### Investor Views:
|
| 733 |
Investor views are expressed in the form of a matrix \( P \) and a vector \( Q \):
|
| 734 |
""")
|
|
|
|
| 740 |
- \( P \) is the matrix that identifies the assets involved in each view.
|
| 741 |
- \( \mu \) is the vector of expected returns.
|
| 742 |
- \( Q \) is the vector of view returns.
|
|
|
|
| 743 |
#### Adjusted Expected Returns:
|
| 744 |
The adjusted expected returns, \( \mu \), are then calculated as:
|
| 745 |
""")
|
|
|
|
| 750 |
Where:
|
| 751 |
- \( \tau \) is a scalar representing the uncertainty in the prior estimates.
|
| 752 |
- \( \Omega \) is the diagonal covariance matrix of the error terms in the views.
|
|
|
|
| 753 |
#### Optimization:
|
| 754 |
Once the adjusted expected returns, \( \mu \), are obtained, we can apply mean-variance optimization to find the optimal portfolio weights.
|
|
|
|
| 755 |
**Objective Function**:
|
| 756 |
""")
|
| 757 |
st.latex(r"""
|
|
|
|
| 772 |
""")
|
| 773 |
st.markdown("""
|
| 774 |
#### Benefits of the Black-Litterman Model:
|
|
|
|
| 775 |
- **Flexibility**: Incorporates investor views into the optimization process.
|
| 776 |
- **Stability**: Produces more stable and intuitive expected returns than traditional mean-variance optimization.
|
| 777 |
- **Theoretical Foundation**: Combines the principles of modern portfolio theory with practical investor insights.
|
|
|
|
| 789 |
cov_matrix = risk_models.sample_cov(data)
|
| 790 |
|
| 791 |
# Download market data for market capitalization weights (here using S&P 500 as proxy)
|
| 792 |
+
market_data = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
|
| 793 |
+
if isinstance(market_data.columns, pd.MultiIndex):
|
| 794 |
+
market_data.columns = market_data.columns.get_level_values(0)
|
| 795 |
+
if market_data.empty:
|
| 796 |
+
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
|
| 797 |
+
market_data = market_data['Adj Close']
|
| 798 |
|
| 799 |
# Calculate market-implied risk aversion
|
| 800 |
delta = black_litterman.market_implied_risk_aversion(market_data)
|
|
|
|
| 840 |
st.plotly_chart(fig_weights)
|
| 841 |
|
| 842 |
portfolio_returns_bl = returns.dot(optimal_weights_bl)
|
| 843 |
+
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
|
| 844 |
+
if isinstance(sp500.columns, pd.MultiIndex):
|
| 845 |
+
sp500.columns = sp500.columns.get_level_values(0)
|
| 846 |
+
if sp500.empty:
|
| 847 |
+
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
|
| 848 |
+
sp500_returns = sp500['Adj Close'].pct_change().dropna()
|
| 849 |
equal_weighted_returns = returns.mean(axis=1)
|
| 850 |
|
| 851 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_bl,
|
|
|
|
| 896 |
st.markdown("""
|
| 897 |
Where:
|
| 898 |
- \( \rho_{i,j} \) is the correlation coefficient between assets \( i \) and \( j \).
|
|
|
|
| 899 |
#### Quasi-Diagonalization:
|
| 900 |
The covariance matrix \( \Sigma \) is reordered to a block-diagonal form using the clustering information. This step ensures that the assets with higher correlations are grouped together.
|
|
|
|
| 901 |
#### Recursive Bisection:
|
| 902 |
Risk is allocated recursively using the following steps:
|
| 903 |
1. **Top-Level Allocation**:
|
|
|
|
| 909 |
st.markdown("""
|
| 910 |
2. **Sub-Level Allocation**:
|
| 911 |
The risk within each cluster is further split recursively until the individual assets are reached.
|
|
|
|
| 912 |
This recursive process ensures that risk is allocated evenly across all clusters and individual assets.
|
| 913 |
""")
|
| 914 |
|
| 915 |
st.markdown("""
|
| 916 |
#### Benefits of HRP:
|
|
|
|
| 917 |
- **Improved Diversification**: By clustering similar assets and allocating risk evenly, HRP achieves better diversification compared to traditional methods.
|
| 918 |
- **Reduced Risk Concentration**: HRP avoids concentrating risk in highly correlated assets, resulting in a more balanced portfolio.
|
| 919 |
- **Robustness**: HRP is less sensitive to estimation errors in the covariance matrix, making it a robust method for portfolio optimization.
|
|
|
|
| 951 |
st.plotly_chart(fig_weights)
|
| 952 |
|
| 953 |
portfolio_returns_hrp = returns.dot(optimal_weights_hrp)
|
| 954 |
+
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
|
| 955 |
+
if isinstance(sp500.columns, pd.MultiIndex):
|
| 956 |
+
sp500.columns = sp500.columns.get_level_values(0)
|
| 957 |
+
if sp500.empty:
|
| 958 |
+
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
|
| 959 |
+
sp500_returns = sp500['Adj Close'].pct_change().dropna()
|
| 960 |
equal_weighted_returns = returns.mean(axis=1)
|
| 961 |
|
| 962 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_hrp,
|
|
|
|
| 1004 |
- \( \sigma_i \) is the volatility of asset \( i \).
|
| 1005 |
- \( \Sigma \) is the covariance matrix of asset returns.
|
| 1006 |
- \( w \) is the weight vector of the portfolio.
|
|
|
|
| 1007 |
#### Objective:
|
| 1008 |
The objective of the Maximum Diversification Portfolio is to maximize this diversification ratio. By doing so, the portfolio aims to achieve the highest possible diversification benefit, reducing the overall portfolio risk.
|
|
|
|
| 1009 |
#### Mathematical Formulation:
|
| 1010 |
The optimization problem can be formulated as:
|
| 1011 |
""")
|
|
|
|
| 1015 |
""")
|
| 1016 |
st.markdown("""
|
| 1017 |
This ensures that the sum of the weights is equal to 1 (fully invested portfolio) and that all weights are non-negative (long-only portfolio).
|
|
|
|
| 1018 |
#### Steps Involved:
|
| 1019 |
1. **Calculate Individual Volatilities**:
|
| 1020 |
Compute the volatility (\( \sigma_i \)) for each asset in the portfolio.
|
|
|
|
| 1021 |
2. **Compute the Covariance Matrix**:
|
| 1022 |
Calculate the covariance matrix (\( \Sigma \)) of asset returns.
|
|
|
|
| 1023 |
3. **Optimize the Diversification Ratio**:
|
| 1024 |
Use optimization techniques to find the weight vector (\( w \)) that maximizes the diversification ratio, subject to the constraints.
|
| 1025 |
""")
|
| 1026 |
|
| 1027 |
st.markdown("""
|
| 1028 |
#### Benefits:
|
|
|
|
| 1029 |
- **Enhanced Diversification**: By focusing on maximizing the diversification ratio, this method ensures that the portfolio is well-diversified, reducing the impact of any single asset on the overall portfolio risk.
|
| 1030 |
- **Reduced Portfolio Risk**: By maximizing the diversification benefit, the overall portfolio risk is minimized compared to other traditional optimization methods.
|
| 1031 |
""")
|
|
|
|
| 1064 |
st.plotly_chart(fig_weights)
|
| 1065 |
|
| 1066 |
portfolio_returns_mdp = returns.dot(optimal_weights_mdp)
|
| 1067 |
+
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
|
| 1068 |
+
if isinstance(sp500.columns, pd.MultiIndex):
|
| 1069 |
+
sp500.columns = sp500.columns.get_level_values(0)
|
| 1070 |
+
if sp500.empty:
|
| 1071 |
+
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
|
| 1072 |
+
sp500_returns = sp500['Adj Close'].pct_change().dropna()
|
| 1073 |
equal_weighted_returns = returns.mean(axis=1)
|
| 1074 |
|
| 1075 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_mdp,
|
|
|
|
| 1113 |
- \( R_p \) is the expected portfolio return.
|
| 1114 |
- \( R_f \) is the risk-free rate.
|
| 1115 |
- \( \sigma_p \) is the portfolio's standard deviation (volatility).
|
|
|
|
| 1116 |
#### Objective:
|
| 1117 |
The objective of the Maximum Sharpe Ratio Portfolio is to find the portfolio weights that maximize the Sharpe ratio. This ensures the highest possible return for a given level of risk.
|
| 1118 |
""")
|
|
|
|
| 1152 |
st.plotly_chart(fig_weights)
|
| 1153 |
|
| 1154 |
portfolio_returns_msr = returns.dot(np.array(list(optimal_weights_msr.values())))
|
| 1155 |
+
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
|
| 1156 |
+
if isinstance(sp500.columns, pd.MultiIndex):
|
| 1157 |
+
sp500.columns = sp500.columns.get_level_values(0)
|
| 1158 |
+
if sp500.empty:
|
| 1159 |
+
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
|
| 1160 |
+
sp500_returns = sp500['Adj Close'].pct_change().dropna()
|
| 1161 |
equal_weighted_returns = returns.mean(axis=1)
|
| 1162 |
|
| 1163 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_msr,
|
|
|
|
| 1192 |
st.markdown("""
|
| 1193 |
#### Concept:
|
| 1194 |
The key idea is to balance the contribution of each asset to the total portfolio risk, such that the risk contribution from each asset is equal.
|
|
|
|
| 1195 |
#### Objective:
|
| 1196 |
The objective of the ERC Portfolio is to find the weights of the assets that equalize their risk contributions. The risk contribution of an asset \( i \) to the total portfolio risk is given by:
|
| 1197 |
""")
|
|
|
|
| 1203 |
- \( RC_i \) is the risk contribution of asset \( i \).
|
| 1204 |
- \( w_i \) is the weight of asset \( i \).
|
| 1205 |
- \( \sigma_p \) is the portfolio's standard deviation (volatility).
|
|
|
|
| 1206 |
#### Mathematical Formulation:
|
| 1207 |
The optimization problem can be formulated as:
|
| 1208 |
""")
|
|
|
|
| 1214 |
Where:
|
| 1215 |
- \( n \) is the number of assets.
|
| 1216 |
- The goal is to minimize the sum of squared deviations of each asset's risk contribution from the average risk contribution \( \frac{\sigma_p}{n} \).
|
|
|
|
| 1217 |
#### Steps Involved:
|
| 1218 |
1. **Estimate Expected Returns and Covariance Matrix**:
|
| 1219 |
Compute the expected returns (\( \mu \)) and the covariance matrix (\( \Sigma \)) for the assets.
|
|
|
|
| 1225 |
|
| 1226 |
st.markdown("""
|
| 1227 |
#### Benefits:
|
|
|
|
| 1228 |
- **Balanced Risk Allocation**: Ensures that no single asset dominates the portfolio risk, leading to a more balanced and diversified portfolio.
|
| 1229 |
- **Risk Management**: Helps in managing the overall portfolio risk by spreading it equally across all assets.
|
| 1230 |
""")
|
|
|
|
| 1258 |
st.plotly_chart(fig_weights)
|
| 1259 |
|
| 1260 |
portfolio_returns_erc = returns.dot(np.array(list(optimal_weights_erc.values())))
|
| 1261 |
+
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
|
| 1262 |
+
if isinstance(sp500.columns, pd.MultiIndex):
|
| 1263 |
+
sp500.columns = sp500.columns.get_level_values(0)
|
| 1264 |
+
if sp500.empty:
|
| 1265 |
+
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
|
| 1266 |
+
sp500_returns = sp500['Adj Close'].pct_change().dropna()
|
| 1267 |
equal_weighted_returns = returns.mean(axis=1)
|
| 1268 |
|
| 1269 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_erc,
|
|
|
|
| 1298 |
st.markdown("""
|
| 1299 |
#### Concept:
|
| 1300 |
Conditional Value at Risk (CVaR) is a risk assessment measure that quantifies the expected loss in the worst-case scenarios beyond a certain confidence level. It provides a more comprehensive risk measure compared to Value at Risk (VaR).
|
|
|
|
| 1301 |
#### Objective:
|
| 1302 |
The objective of the CVaR Minimization Portfolio is to find the weights of the assets that minimize the CVaR at a given confidence level.
|
|
|
|
| 1303 |
#### Mathematical Formulation:
|
| 1304 |
The optimization problem can be formulated as:
|
| 1305 |
""")
|
|
|
|
| 1314 |
- \( \alpha \) is the confidence level (e.g., 95%).
|
| 1315 |
- \( T \) is the number of observations.
|
| 1316 |
- \( L_t(w) \) is the loss of the portfolio at time \( t \).
|
|
|
|
| 1317 |
#### Steps Involved:
|
| 1318 |
1. **Estimate Expected Returns and Covariance Matrix**:
|
| 1319 |
Compute the expected returns (\( \mu \)) and the covariance matrix (\( \Sigma \)) for the assets.
|
|
|
|
| 1358 |
st.plotly_chart(fig_weights)
|
| 1359 |
|
| 1360 |
portfolio_returns_cvar = returns.dot(np.array(list(optimal_weights_cvar.values())))
|
| 1361 |
+
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
|
| 1362 |
+
if isinstance(sp500.columns, pd.MultiIndex):
|
| 1363 |
+
sp500.columns = sp500.columns.get_level_values(0)
|
| 1364 |
+
if sp500.empty:
|
| 1365 |
+
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
|
| 1366 |
+
sp500_returns = sp500['Adj Close'].pct_change().dropna()
|
| 1367 |
equal_weighted_returns = returns.mean(axis=1)
|
| 1368 |
|
| 1369 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_cvar,
|
|
|
|
| 1398 |
st.markdown("""
|
| 1399 |
#### Concept:
|
| 1400 |
The Maximum Return Portfolio aims to achieve the highest possible return by investing in assets that have the highest expected returns. This approach does not take into account the risk or volatility associated with the assets.
|
|
|
|
| 1401 |
#### Objective:
|
| 1402 |
The objective of the Maximum Return Portfolio is to find the weights of the assets that maximize the expected return.
|
|
|
|
| 1403 |
#### Mathematical Formulation:
|
| 1404 |
The optimization problem can be formulated as:
|
| 1405 |
""")
|
|
|
|
| 1412 |
- \( w \) is the weight vector of the assets.
|
| 1413 |
- \( \mu \) is the expected return vector of the assets.
|
| 1414 |
- \( n \) is the number of assets.
|
|
|
|
| 1415 |
#### Steps Involved:
|
| 1416 |
1. **Estimate Expected Returns**:
|
| 1417 |
Compute the expected returns (\( \mu \)) for each asset.
|
|
|
|
| 1421 |
|
| 1422 |
st.markdown("""
|
| 1423 |
#### Benefits:
|
|
|
|
| 1424 |
- **High Potential Returns**: Focuses on achieving the highest possible returns.
|
| 1425 |
- **Simple Approach**: Easy to understand and implement.
|
|
|
|
| 1426 |
#### Drawbacks:
|
|
|
|
| 1427 |
- **Ignores Risk**: Does not consider the risk or volatility associated with the assets.
|
| 1428 |
- **Potential for High Volatility**: Can result in a highly volatile portfolio.
|
| 1429 |
""")
|
|
|
|
| 1457 |
st.plotly_chart(fig_weights)
|
| 1458 |
|
| 1459 |
portfolio_returns_max_return = returns.dot(np.array(list(optimal_weights_max_return.values())))
|
| 1460 |
+
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
|
| 1461 |
+
if isinstance(sp500.columns, pd.MultiIndex):
|
| 1462 |
+
sp500.columns = sp500.columns.get_level_values(0)
|
| 1463 |
+
if sp500.empty:
|
| 1464 |
+
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
|
| 1465 |
+
sp500_returns = sp500['Adj Close'].pct_change().dropna()
|
| 1466 |
equal_weighted_returns = returns.mean(axis=1)
|
| 1467 |
|
| 1468 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_max_return,
|
|
|
|
| 1497 |
st.markdown("""
|
| 1498 |
#### Concept:
|
| 1499 |
The Maximum Quadratic Utility Portfolio aims to find the optimal portfolio that maximizes an investor's utility, which is a measure of satisfaction or preference. This approach takes into account both the expected return and the risk of the portfolio, weighted by a risk aversion coefficient.
|
|
|
|
| 1500 |
#### Objective:
|
| 1501 |
The objective of the Maximum Quadratic Utility Portfolio is to maximize the utility function, which is defined as:
|
| 1502 |
""")
|
|
|
|
| 1510 |
- \( \mu \) is the expected return vector of the assets.
|
| 1511 |
- \( \Sigma \) is the covariance matrix of the asset returns.
|
| 1512 |
- \( \lambda \) is the risk aversion coefficient.
|
|
|
|
| 1513 |
#### Steps Involved:
|
| 1514 |
1. **Estimate Expected Returns and Covariance Matrix**:
|
| 1515 |
Compute the expected returns (\( \mu \)) and the covariance matrix (\( \Sigma \)) for the assets.
|
|
|
|
| 1521 |
|
| 1522 |
st.markdown("""
|
| 1523 |
#### Benefits:
|
|
|
|
| 1524 |
- **Balances Return and Risk**: Provides a trade-off between maximizing returns and minimizing risk.
|
| 1525 |
- **Customizable Risk Tolerance**: Allows adjustment of the risk aversion coefficient to match the investor's risk preference.
|
|
|
|
| 1526 |
#### Drawbacks:
|
|
|
|
| 1527 |
- **Requires Accurate Estimates**: The effectiveness of the method depends on accurate estimates of expected returns and the covariance matrix.
|
| 1528 |
- **Complexity**: More complex to implement compared to simpler methods like Maximum Return Portfolio.
|
| 1529 |
""")
|
|
|
|
| 1558 |
st.plotly_chart(fig_weights)
|
| 1559 |
|
| 1560 |
portfolio_returns_mqu = returns.dot(np.array(list(optimal_weights_mqu.values())))
|
| 1561 |
+
sp500 = yf.download('^GSPC', start=start_date, end=end_date, auto_adjust=False)
|
| 1562 |
+
if isinstance(sp500.columns, pd.MultiIndex):
|
| 1563 |
+
sp500.columns = sp500.columns.get_level_values(0)
|
| 1564 |
+
if sp500.empty:
|
| 1565 |
+
raise ValueError(f"No S&P 500 data fetched from {start_date} to {end_date}.")
|
| 1566 |
+
sp500_returns = sp500['Adj Close'].pct_change().dropna()
|
| 1567 |
equal_weighted_returns = returns.mean(axis=1)
|
| 1568 |
|
| 1569 |
fig_cumulative_returns = plot_cumulative_returns(portfolio_returns_mqu,
|
|
|
|
| 1605 |
footer {visibility: hidden;}
|
| 1606 |
</style>
|
| 1607 |
"""
|
| 1608 |
+
st.markdown(hide_streamlit_style, unsafe_allow_html=True)
|