library(xts)
library(quantmod)
library(riskParityPortfolio)
library(PerformanceAnalytics)
library(portfolioBacktest)
library(CVXR)
library(DT)
library(ggplot2)Backtesting Risk-Based Portfolios
Overview
The project consists of empirically testing the performance of selected portfolio risk based investment strategies.
Tasks
Download monthly market data for S&P500 listed stocks from 01-01-2010 to 31-12-2022.
Generate 100 random re-samples of 20 S&P500 listed stocks and 3 consecutive data.
Empirically investigate the performance of the following traditional and risk-based portfolios.
- Global Minimum-Variance Portfolio with no short-selling
- Inverse Volatility Portfolio
- Risk parity portfolio
- Most diversified portfolio
- Maximum de-correlation portfolio
- Hierarchical Risk Parity Portfolio
- Markowitz’s mean-variance portfolio (MVP) with no short-selling
Critically discuss the results considering alternative risk-adjusted performance metrics (e.g., returns, volatility, Sharpe ratio, Sterling ratio, drawdown).
Extra Points: Use the same data set to investigate the performance of portfolio management strategies using alternative risk measures, e.g., downside risk, value-at-risk (VaR),Conditional VaR (CVaR) or expected shortfall (ES).
Loading Essential Libraries
Loading Data
startDate <- "2010-01-01"
endDate <- "2022-12-31"
data(SP500_symbols)
sp500 <- stockDataDownload(stock_symbols = SP500_symbols, from=startDate,to=endDate, periodicity="monthly",rm_stocks_with_na = TRUE)Loading stock data from local file /Users/vivekraj/Coding/R/Asset Pricing & Portfolio Management/stockdata_from_2010-01-01_to_2022-12-31_(065fe3c9e1991cd1ec13c8d1c18d3e2c).RData
Generate Random Re-samples
we have used the financialDataResample function to create 100 re-samples of randomly selected 20 S&P500 listed assets with desired length of 3 years consecutive years. The seed has been set to show consistent result on each run.
# 100 random resamples of 20 S&P500 listed stocks and 3 consecutive data.
set.seed(123)
dataset_list <- financialDataResample(sp500, # dataset
N_sample = 20, # Desired number of financial instruments in each resample
T_sample = 12 * 3, # Desired length of each resample (consecutive samples with a random initial time)
num_datasets = 100) # Number of resampled datasets100 datasets resampled (with N = 20 instruments and length T = 36) from the original data between 2010-01-01 and 2022-12-01.
Defining the Portfolios
1. Global Minimum-Variance Portfolio with no short-selling
Global Minimum Variance Portfolio aims to find an allocation of assets that minimizes the portfolio variance, which is equivalent to maximizing the diversification of the portfolio. This portfolio is set for an investor considered risk averse that is interested in decrease the volatility of the investments, even though it means a lower expected return.
# Global Minimum-Variance Portfolio with no short-selling
gmvp_portfolio_fn <- function(dataset, ...) {
X <- CalculateReturns(dataset$adjusted,"log")[-1] # computed the log returns
mu <- colMeans(X) # mean return
Sigma <- cov(X) # covariance matrix
## Defining the Optimization variables and problem for convex optimization
w <- Variable(nrow(Sigma)) # defining the optimization variables i.e. the weight of each asset
objective <- Minimize(quad_form(w, Sigma)) # setting objective function
constraints <- list(w >= 0, sum(w) == 1) # setting the constraints
prob <- Problem(objective,constraints) # defining the problem
result <- CVXR::solve(prob) # solve the optimization problem
weights <- as.vector(result$getValue(w))
return(weights)
}This objective function aims to minimize the portfolio variance (quad_form(w, Sigma)), where w represents the vector of weights and Sigma represents the covariance matrix.The constraints ensures that the weight of assets should always be positive (no short-selling) and the sum must be equal to 1 ensures that entire portfolio is fully invested.
let’s investigate the portfolio weights of all 20 assets in a sample dataset.
sum(gmvp_portfolio_fn(dataset_list$`dataset 4`)) # checking the weight of assets for one sample of data[1] 1
Following chart shows portfolio capital and risk distribution.
X <- CalculateReturns(dataset_list$`dataset 5`$adjusted,"log")[-1]
Sigma <- cov(X)
barplotPortfolioRisk(gmvp_portfolio_fn(dataset_list$`dataset 5`),Sigma) + scale_y_continuous(labels = scales::percent)2. Inverse Volatility Portfolio
An inverse volatility portfolio aims to minimize the portfolio’s volatility by allocating more weight to assets with lower volatility and less weight to assets with higher volatility.
inv_vol_portfolio_fn <- function(dataset, ...) {
X <- CalculateReturns(dataset$adjusted,"log")[-1] # computed the log returns
Sigma <- cov(X) # covariance matrix
## Defining the Optimization variables and problem for convex optimization
w <- Variable(nrow(Sigma))
vol <- sqrt(diag(Sigma)) ## volatility
inv_vol <- 1 / vol ## inverse-volatility
inv_vol_mat <- diag(inv_vol)
objective <- Minimize(quad_form(w, inv_vol_mat))
constraints = list(sum(w) == 1)
prob <- Problem(objective, constraints)
## Solve the convex optimization problem
result <- CVXR::solve(prob)
## Extract the portfolio weights
weights <- as.vector(result$getValue(w))
## Return the portfolio weights
return(weights)
}The function inv_vol_portfolio_fn does exactly this by minimizing the quadratic form quad_form(w,inv_vol_mat), which is a measure of the portfolio’s volatility. It minimizes the portfolio’s volatility using a convex optimization problem and uses the CVXR package to solve the convex optimization problem, which is known for its efficiency.
sum(inv_vol_portfolio_fn(dataset_list$`dataset 3`))[1] 1
X <- CalculateReturns(dataset_list$`dataset 3`$adjusted,"log")[-1]
Sigma <- cov(X)
barplotPortfolioRisk(inv_vol_portfolio_fn(dataset_list$`dataset 3`),Sigma) + scale_y_continuous(labels = scales::percent)3. Risk Parity Portfolio
A Risk Parity portfolio is a type of investment portfolio that aims to allocate assets in a way that equalizes the risk contribution of each asset to the overall portfolio risk. This approach is in contrast to traditional portfolio construction methods, which typically allocate assets based on expected returns.
Risk Parity portfolios are typically constructed using a mean-variance optimization framework. In this framework, the portfolio weights are chosen to maximize the portfolio’s expected return while minimizing the portfolio’s variance, subject to the constraint that the risk contribution of each asset is equal.
Risk Parity portfolios can offer a number of potential benefits, including:
Reduced volatility: Risk Parity portfolios are typically less volatile than traditional portfolios, which can make them more attractive to risk-averse investors.
Enhanced returns: Risk Parity portfolios can potentially generate higher returns than traditional portfolios, especially in periods of market stress.
Diversification benefits: Risk Parity portfolios are typically more diversified than traditional portfolios, which can help to reduce portfolio risk.
risk_parity_portfolio_fn <- function(dataset, ...) {
X <- CalculateReturns(dataset$adjusted,"log")[-1] # computed the log returns
mu <- colMeans(X) # mean log return
Sigma <- cov(X) # covariance matrix
## Defining the Optimization variables and problem for convex optimization
w <- Variable(nrow(Sigma))
risk_contri <- diag(Sigma %*% w)
constraints = list(sum(w) == 1)
objective <- Minimize(sum_squares(risk_contri))
prob <- Problem(objective, constraints)
result <- CVXR::solve(prob)
weights <- as.vector(result$getValue(w))
return(weights)
}This above function constructs a Risk Parity Portfolio, which is a type of portfolio that aims to allocate assets in a way that equalizes the risk contribution of each asset to the overall portfolio risk. The objective function is to minimize the sum of the squares of the risk contributions of the assets, which is represented by the term sum_squares(risk_contri). The risk contribution of each asset is represented by the term diag(Sigma %*% w). The constraint is that the sum of the portfolio weights must be equal to 1. This ensures that the portfolio is feasible.
sum(risk_parity_portfolio_fn(dataset_list$`dataset 3`))[1] 1
X <- CalculateReturns(dataset_list$`dataset 2`$adjusted,"log")[-1]
Sigma <- cov(X)
barplotPortfolioRisk(risk_parity_portfolio_fn(dataset_list$`dataset 2`),Sigma) + scale_y_continuous(labels = scales::percent)4. Most Diversified Portfolio
A most diversified portfolio is a portfolio that is constructed to minimize the risk of concentration in any one asset or asset class. This is achieved by allocating capital across a variety of different assets, with no one asset or asset class dominating the portfolio.
most_diverse_portfolio_fn <- function(dataset, ...) {
X <- CalculateReturns(dataset$adjusted,"log")[-1] # computed the log returns
mu <- colMeans(X) # mean return
Sigma <- cov(X)
MSRP <- function(mu, Sigma) {
w <- Variable(nrow(Sigma))
objective <- Minimize(quad_form(w,Sigma))
constraints <- list(w >= 0, t(mu) %*% w == 1)
prob <- Problem(objective,constraints)
result <- CVXR::solve(prob)
weights <- as.vector(result$getValue(w)/sum(result$getValue(w)))
return(weights)
}
return(MSRP(mu=sqrt(diag(Sigma)),Sigma))
}The above portfolio function for Most Diversified Portfolio has been constructed using maximum Sharpe ratio function. A maximum Sharpe ratio portfolio is a portfolio that maximizes the Sharpe ratio, which is a measure of risk-adjusted return. The Sharpe ratio is calculated by dividing the portfolio’s expected return by its standard deviation. A higher Sharpe ratio indicates a portfolio that is generating more return per unit of risk.
To do this, we have used an optimization algorithm to identify the portfolio weights that maximize the Sharpe ratio, subject to the constraint that the portfolio is diversified.
This approach to constructing most diversified portfolios has a number of advantages:
It is a systematic approach to diversification.
It takes into account the risk-adjusted returns of the assets in the portfolio.
It can be used to construct portfolios for a variety of different investment goals and risk tolerances.
constructing most diversified portfolios using the maximum Sharpe ratio criterion is a viable approach for investors who are looking to reduce risk, enhance returns, and reduce volatility.
It is important to re-balance the portfolio regularly to ensure that the portfolio weights remain aligned with the target allocation.
sum(most_diverse_portfolio_fn(dataset_list$`dataset 2`))[1] 1
X <- CalculateReturns(dataset_list$`dataset 2`$adjusted,"log")[-1]
Sigma <- cov(X)
barplotPortfolioRisk(most_diverse_portfolio_fn(dataset_list$`dataset 2`),Sigma) + scale_y_continuous(labels = scales::percent)5. Maximum De-correlation Portfolio
A Maximum De-correlation Portfolio (MDCP) is a type of portfolio that aims to maximize the de-correlation between the assets in the portfolio. This is achieved by allocating capital to assets that have low correlations with each other.
MDCPs are constructed using a mean-variance optimization framework. In this framework, the portfolio weights are chosen to maximize the portfolio’s expected return while minimizing the portfolio’s variance, subject to the constraint that the correlation between the assets in the portfolio is minimized.
max_deco_portfolio_fn <- function(dataset, ...) {
X <- CalculateReturns(dataset$adjusted, "log")[-1] # computed the simple returns
mu <- colMeans(X) # mean log return
Sigma <- cov(X) # covariance matrix
corr_mat <- cor(X) # correlation matrix
## Defining the Optimization variables and problem for convex optimization
w <- Variable(nrow(Sigma))
C <- diag(1/sqrt(diag(Sigma))) %*% Sigma %*% diag(1/sqrt(diag(Sigma)))
objective <- Minimize(quad_form(w,C))
constraints = list(sum(w) == 1)
prob <- Problem(objective, constraints)
result <- CVXR::solve(prob)
weights <- as.vector(result$getValue(w))
return(weights)
}This function constructs a Maximum De-correlation Portfolio (MDCP). The objective function is to minimize the quadratic form quad_form(w,C). The quadratic form is a measure of the dispersion of the portfolio weights, and the matrix C is a normalized version of the covariance matrix.
The diagonal matrix diag(1/sqrt(diag(Sigma))) normalizes the covariance matrix by dividing each element by the square root of the corresponding diagonal element of the covariance matrix. This makes the covariance matrix more diagonally dominant, which makes it easier to optimize.
The normalized covariance matrix C is used in the objective function of the MDCP problem to minimize the dispersion of the portfolio weights. This is because the dispersion of the portfolio weights is a measure of the de-correlation between the assets in the portfolio. By minimizing the dispersion of the portfolio weights, the objective function of the MDCP problem is able to maximize the de-correlation between the assets in the portfolio.
sum(max_deco_portfolio_fn(dataset_list$`dataset 3`))[1] 1
X <- CalculateReturns(dataset_list$`dataset 2`$adjusted,"log")[-1]
Sigma <- cov(X)
barplotPortfolioRisk(max_deco_portfolio_fn(dataset_list$`dataset 2`),Sigma) + scale_y_continuous(labels = scales::percent)6. Hierarchical Risk Parity Portfolio
A Hierarchical Risk Parity Portfolio (HRP) is a type of portfolio that aims to equalize the risk contribution of each asset to the overall portfolio risk in a hierarchical manner. This is achieved by first clustering the assets into groups based on their risk characteristics, and then allocating capital to each cluster in a way that equalizes the risk contribution of each cluster to the overall portfolio risk.
library(HierPortfolios)
library(ggplot2)
HRPP_fun <- function(dataset, ...) {
X <- CalculateReturns(dataset$adjusted,"log")[-1] # computed the log returns
mu <- colMeans(X) # mean log return
Sigma <- cov(X) # covariance matrix
HRPP_average = HRP_Portfolio(as.matrix(Sigma), linkage = "ward", graph = T)
weights <- HRPP_average$w
weights <- weights/sum(weights)
return(weights)
}HRP portfolios has following potential benefits:
Reduced risk: HRP portfolios are typically less risky than traditional portfolios because they are less exposed to any one particular asset or asset class.
Enhanced returns: HRP portfolios can potentially generate higher returns than traditional portfolios because they are able to capture the benefits of diversification more effectively.
Reduced transaction costs: HRP portfolios may require less frequent rebalancing than traditional portfolios because they are less sensitive to changes in the risk characteristics of individual assets.
To construct the portfolio we have used the HierPortfolios library. The linkage method used to construct this portfolio is set to ward which is more robust linkage method which takes care of noisy data and data with outliers.
HRPP_fun(dataset_list$`dataset 11`) [1] 0.055094151 0.030933016 0.039837217 0.021891117 0.030650039 0.062923524
[7] 0.083161232 0.031896265 0.052025092 0.044187511 0.143224172 0.068830701
[13] 0.040128536 0.040369466 0.046033512 0.026303253 0.009742726 0.072387778
[19] 0.064125202 0.036255491
X <- CalculateReturns(dataset_list$`dataset 2`$adjusted,"log")[-1]
Sigma <- cov(X)
barplotPortfolioRisk(HRPP_fun(dataset_list$`dataset 2`),Sigma) + scale_y_continuous(labels = scales::percent)7. Markowitz’s Mean Variance Portfolio with No Short-Selling
Markowitz’s mean-variance portfolio theory is a mathematical framework for constructing investment portfolios that optimize the trade-off between expected return and risk. The theory is based on the following assumptions:
Investors are rational and are willing to maximize their expected return while minimizing their risk.
Investors are able to estimate the expected returns and co-variances of the assets in the portfolio.
Investors have a quadratic utility function, which means that they are indifferent to risk and return distributions that have the same mean and variance.
The disadvantages of Markowitz’s Mean Variance Portfolio is that it relies on the assumption that investors are rational and have quadratic utility function. It May not be suitable for all investors, as some investors may be willing to accept higher risk in order to achieve higher returns.
Markowitz_portfolio_fn <- function(dataset, lambda=0.5, ...) {
X <- CalculateReturns(dataset$adjusted,"log")[-1] # computed the log returns
mu <- colMeans(X) # mean log return
Sigma <- cov(X) # covariance matrix
# design mean-variance portfolio
w <- Variable(nrow(Sigma))
objective <- Maximize(t(mu) %*% w - lambda*quad_form(w, Sigma))
constraints = list(w >= 0, sum(w) == 1)
prob <- Problem(objective,constraints)
result <- solve(prob)
weights <- as.vector(result$getValue(w))
return(weights)
}The objective function is to maximize the expected return of the portfolio minus a penalty term for risk, where the penalty term is proportional to the variance of the portfolio. The trade-off parameter lambda controls the importance of the risk penalty term. The constraints are that the portfolio weights must be non-negative (no-short selling) and sum to one. This ensures that the portfolio is feasible.
Markowitz_portfolio_fn(dataset_list$`dataset 4`) [1] -2.075715e-22 -2.076769e-22 1.079581e-22 -1.785300e-22 -2.040117e-22
[6] -3.036716e-22 -3.982435e-23 -1.498345e-22 -9.884898e-23 -3.202000e-22
[11] 1.000000e+00 -3.207739e-22 -2.630098e-22 -2.067696e-22 1.341028e-23
[16] -7.328801e-23 -3.320765e-23 7.422664e-23 -9.596430e-23 -3.159236e-22
X <- CalculateReturns(dataset_list$`dataset 2`$adjusted,"log")[-1]
Sigma <- cov(X)
barplotPortfolioRisk(Markowitz_portfolio_fn(dataset_list$`dataset 2`),Sigma) + scale_y_continuous(labels = scales::percent)Back-testing the Portfolios
With the datasets and portfolios ready, we can now do the backtest easily. we just need combine them in a list and run the back-test.
my_portfolio <- list("GMVP" = gmvp_portfolio_fn,
"IVP" = inv_vol_portfolio_fn,
"RPP" = risk_parity_portfolio_fn,
"MDP" = most_diverse_portfolio_fn,
"MDCP" = max_deco_portfolio_fn,
"HRPP" = HRPP_fun,
"Markowitz" = Markowitz_portfolio_fn)We are benchmarking the back-test using equally weighted portfolio(naive portfolio) & index portfolio.
The look-back period is the number of periods of historical data that are used to calculate the portfolio returns and covariance matrix. This has been set to 12 months/1 Year.
Optimization is the process of finding the optimal portfolio weights given the estimated returns and covariance matrix. Re-balancing is the process of adjusting the portfolio weights to ensure that they remain aligned with the target weights.
## Running the backtesting
bt <- portfolioBacktest(my_portfolio, dataset_list, benchmark = c("1/N", "index"),
lookback = 12,
optimize_every = 4,
rebalance_every= 2,
show_progress_bar=TRUE,
paral_portfolios = 2,
paral_datasets = 2)Backtesting 7 portfolios over 100 datasets (periodicity = monthly data)...
Backtesting benchmarks...
Backtesting benchmark "1/N " (1/2)
Backtesting "index " (2/2)
names(bt)[1] "GMVP" "IVP" "RPP" "MDP" "MDCP" "HRPP"
[7] "Markowitz" "1/N" "index"
Investigating the Performance
After the back-testing is done for all the 7 portfolios and 2 benchmark portfolios, the table below shows the summary of all the portfolios result in descending order of Sharpe ratio. There are other performance parameters also shown (max drawdown, annual return, annual volatility, Sortino ratio, downside deviation, Sterling ratio, Omega Ratio, VaR, CVaR).
portfolio_summary <- backtestSummary(bt)
summaryTable(portfolio_summary, type = "kable", digits=2, page_length = 12,
order_col = "Sharpe ratio", # performance measure to be used to sort the rows
order_dir = "desc")| Portfolio | Sharpe ratio | max drawdown | annual return | annual volatility | Sortino ratio | downside deviation | Sterling ratio | Omega ratio | VaR (0.95) | CVaR (0.95) | rebalancing period | turnover | ROT (bps) | cpu time | failure rate |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| GMVP | 5.58 | 10% | 312% | 54% | 9.69 | 0.32 | 34.29 | 2.41 | 4% | 6% | 2 | 0.25 | 423.91 | 0.54 | 0 |
| IVP | 3.91 | 12% | 305% | 68% | 6.34 | 0.40 | 22.37 | 2.02 | 5% | 7% | 2 | 0.06 | 2034.49 | 0.38 | 0 |
| RPP | 4.13 | 11% | 274% | 68% | 7.22 | 0.37 | 24.58 | 1.99 | 5% | 7% | 2 | 0.52 | 164.81 | 0.57 | 0 |
| MDP | 5.35 | 8% | 325% | 57% | 9.89 | 0.32 | 35.04 | 2.43 | 5% | 6% | 2 | 0.24 | 483.20 | 0.37 | 0 |
| MDCP | 3.14 | 17% | 290% | 86% | 5.30 | 0.52 | 18.31 | 1.72 | 7% | 9% | 2 | 0.70 | 138.58 | 0.38 | 0 |
| HRPP | 5.02 | 8% | 269% | 52% | 8.65 | 0.30 | 26.66 | 2.26 | 4% | 5% | 2 | 0.13 | 737.80 | 0.02 | 0 |
| Markowitz | 4.63 | 21% | 569% | 126% | 9.27 | 0.65 | 30.20 | 2.19 | 9% | 11% | 2 | 0.29 | 727.15 | 0.97 | 0 |
| 1/N | 4.12 | 11% | 291% | 60% | 6.99 | 0.35 | 23.83 | 2.04 | 4% | 6% | 2 | 0.03 | 3729.56 | 0.00 | 0 |
| index | 4.45 | 9% | 241% | 54% | 7.27 | 0.32 | 20.67 | 1.97 | 5% | 6% | 24 | 0.00 | NA | 0.00 | 0 |
The performance table shown above shows the performance of different portfolio strategies over a given period of time. The strategies are compared based on a number of risk-adjusted return and risk metrics, including the Sharpe ratio, max drawdown, annual return, annual volatility, Sortino ratio, downside deviation, Sterling ratio, Omega ratio, VaR (0.95), CVaR (0.95).
Let’s first discuss the different risk adjusted return metrics.
Sharpe Ratio: Sharpe ratio is the most common risk-adjusted performance measure. It is calculated by dividing the excess return of an investment or portfolio by its volatility. The Sharpe ratio is a good measure of overall risk-adjusted performance, but it does not take into account the asymmetry of returns (i.e., the fact that losses are more painful than gains).
Sterling Ratio: Sterling ratio is a risk-adjusted performance measure that focuses on the information ratio of an investment or portfolio relative to a benchmark. The Sterling ratio is a good measure of performance relative to a benchmark, but it does not take into account the volatility of the investment or portfolio.
Omega ratio: Omega ratio is a risk-adjusted performance measure that focuses on the upside return of an investment or portfolio. The Omega ratio is a good measure of the potential for outperformance, but it does not take into account the downside risk of the investment or portfolio.
Max Drawdown: Max drawdown is the largest percentage loss from peak to trough that an investment or portfolio has experienced over a given period of time. It is a measure of downside risk.
Sortino Ratio: The Sortino ratio is a risk-adjustment metric used to determine the additional return for each unit of downside risk. It is computed by first finding the difference between an investment’s average return rate and the risk-free rate. The result is then divided by the standard deviation of negative returns. Ideally, a high Sortino ratio is preferred, as it indicates that an investor will earn a higher return for each unit of a downside risk.
Compared to the Sharpe ratio, the Sortino ratio is a superior metric, as it only accounts for the downside variability of risks. Such an analysis makes sense, as it enables investors to assess downside risks, which is what they should worry about. Upward risks (i.e., when an investment generates an unexpected financial gain) isn’t really a cause for concern.
By comparison, the Sharpe ratio treats upside and downside risks in the same way. It means that even those investments that produce gains are penalized, which should not be the case.
Therefore, the Sortino ratio should be used to assess the performance of high volatility assets, such as shares.
VaR (95%) & CVaR(95%): VaR(95%) and CVaR(95%) are two risk metrics that are used to measure the potential losses of an investment or portfolio over a given period of time with a 95% probability.
VaR(95%), also known as Value at Risk, is the maximum loss that is expected to occur with a 95% probability over a given period of time. It is a measure of downside risk.
CVaR(95%), also known as Conditional Value at Risk or Expected Shortfall, is the average loss that is expected to occur if a loss does occur, given that a loss has occurred with a 95% probability over a given period of time. It is a measure of tail risk.
Overall,
Global Minimum Variance Portfolio (GMVP)has best Sharpe Ratio of 5.58 followed by MDP and HRPP with corresponding Sharpe ratio of 5.35 and 5.02. The GMV portfolio has a Sharpe ratio of 5.58, a max drawdown of 10%, and an annual return of 312%.In terms of annual return Markowitz’s Mean variance portfolio(MMVP) without short-selling has the highest return of 569% followed by MDP(325%) and GMVP(312%)
Overall, the GMV portfolio has the best risk-adjusted performance, followed by the IVP and RPP portfolios. The GMV portfolio has a Sharpe ratio of 5.58, a max drawdown of 10%, and an annual return of 312%. The IVP portfolio has a Sharpe ratio of 3.91, a max drawdown of 12%, and an annual return of 305%. The RPP portfolio has a Sharpe ratio of 4.13, a max drawdown of 11%, and an annual return of 274%.
The MDP and HRPP portfolios have the lowest max drawdown of 8% indicating that they are less risky than other portfolio strategies.
summaryBarPlot(portfolio_summary, measures = c("Sharpe ratio", "max drawdown"))backtestBoxPlot(bt, measure = "Sharpe ratio")summaryBarPlot(portfolio_summary, measures = "Sterling ratio")summaryBarPlot(portfolio_summary, measures = "Omega ratio")summaryBarPlot(portfolio_summary, measures = c("VaR (0.95)","CVaR (0.95)"))backtestChartCumReturn(bt, c("Markowitz","HRPP", "GMVP",'IVP',"RPP","MDP","MDCP", "index"))backtestChartDrawdown(bt, c("Markowitz","HRPP", "GMVP",'IVP',"RPP","MDP","MDCP", "index"))backtestChartStackedBar(bt, "GMVP", legend = TRUE)