Download Stock Data

stks =c("AAPL", "MSFT", "AMZN", "NVDA", "GOOGL", "GOOG", "META", "BRK-B", 
        "TSLA", "UNH", "XOM", "JNJ", "JPM", "V", "PG", "LLY", "AVGO", 
        "MA", "HD", "MRK")

stk_data <- stockDataDownload(stks, from = "2019-01-01", to = "2023-05-31", rm_stocks_with_na = TRUE)
## Loading stock data from local file /cloud/project/stockdata_from_2019-01-01_to_2023-05-31_(0f9cf120d38e5ba793ee9b2a12c3efc1).RData
save.image("STKS_riskParity.RData")

Optimization

load("STKS_riskParity.RData")

prices <- stk_data$adjusted
prices <- do.call(merge,lapply(as.list(1:ncol(prices)), function(ii){
  tmp <- na.locf(Cl(to.monthly(prices[,ii], name=names(prices[,ii]))))
  names(tmp) <- gsub(".Close","",names(tmp))
  tmp
}))
stk_data = list(`adjusted`= prices)

risk_parity <- function(dataset, ...) {
  prices <- dataset$adjusted
  log_returns <- diff(log(prices))[-1]
  log_returns[is.na(log_returns)] <- 0
  riskParityPortfolio(cov(log_returns))$w
}

max_sharpe_ratio <- function(dataset, ...) {
    prices <- dataset$adjusted
    log_returns <- diff(log(prices))[-1]
    log_returns[is.na(log_returns)] <- 0
    N <- ncol(prices)
    Sigma <- cov(log_returns)
    mu <- colMeans(log_returns)
    if (all(mu <= 1e-8))
        return(rep(0, N))
    Dmat <- 2 * Sigma
    Amat <- diag(N)
    Amat <- cbind(mu, Amat)
    bvec <- c(1, rep(0, N))
    dvec <- rep(0, N)
    res <- try(solve.QP(Dmat = Dmat, dvec = dvec, Amat = Amat, bvec = bvec, meq = 1), silent = TRUE)
    if(inherits(res,'try-error')){
      D_mat <- nearPD(Dmat)
      res <- solve.QP(Dmat = as.matrix(D_mat$mat), dvec = dvec, Amat = Amat, bvec = bvec, meq = 1)
    }
    w <- res$solution
    w/sum(w)
}

bt <- portfolioBacktest(list("risk parity portfolio" = risk_parity,
                             "tangency portfolio"    = max_sharpe_ratio),
                        list(stk_data),
                        lookback = 6,      
                        optimize_every = 3, 
                        rebalance_every = 1,
                        shortselling = TRUE,
                        benchmarks = '1/N',
                        execution = "next period"
                        )
## Backtesting 2 portfolios over 1 datasets (periodicity = monthly data)...
## Backtesting benchmarks...

Table of Stats

cat("\nRebalanced: ")
## 
## Rebalanced:
index(bt$`risk parity portfolio`$data1$w_rebalanced)
##  [1] "Jun 2019" "Jul 2019" "Aug 2019" "Sep 2019" "Oct 2019" "Nov 2019"
##  [7] "Dec 2019" "Jan 2020" "Feb 2020" "Mar 2020" "Apr 2020" "May 2020"
## [13] "Jun 2020" "Jul 2020" "Aug 2020" "Sep 2020" "Oct 2020" "Nov 2020"
## [19] "Dec 2020" "Jan 2021" "Feb 2021" "Mar 2021" "Apr 2021" "May 2021"
## [25] "Jun 2021" "Jul 2021" "Aug 2021" "Sep 2021" "Oct 2021" "Nov 2021"
## [31] "Dec 2021" "Jan 2022" "Feb 2022" "Mar 2022" "Apr 2022" "May 2022"
## [37] "Jun 2022" "Jul 2022" "Aug 2022" "Sep 2022" "Oct 2022" "Nov 2022"
## [43] "Dec 2022" "Jan 2023" "Feb 2023" "Mar 2023"
round(backtestSummary(bt)$performance,4)
##                    risk parity portfolio tangency portfolio       1/N
## Sharpe ratio                      5.1805             4.9795    5.8403
## max drawdown                      0.2098             0.2226    0.2057
## annual return                     5.0913             5.1154    5.4966
## annual volatility                 0.9828             1.0273    0.9411
## Sortino ratio                     8.9534             9.2194   10.9557
## downside deviation                0.5686             0.5548    0.5017
## Sterling ratio                   24.2706            22.9819   26.7218
## Omega ratio                       2.1958             2.1866    2.3825
## VaR (0.95)                        0.0846             0.0918    0.0782
## CVaR (0.95)                       0.1087             0.0984    0.0827
## rebalancing period                1.0217             1.0217    1.0217
## turnover                          0.2899             0.5199    0.0530
## ROT (bps)                       688.6805           376.1225 4072.5262
## cpu time                          0.0037             0.0154    0.0008
## failure rate                      0.0000             0.0000    0.0000

Returns + Drawdown Charts

portfolioBacktest::backtestChartCumReturn(bt)

portfolioBacktest::backtestChartDrawdown(bt)

Weight Allocation through Time

backtestChartStackedBar(bt, portfolio = "risk parity portfolio", legend = TRUE)

backtestChartStackedBar(bt, portfolio = "tangency portfolio" , legend = TRUE)