Path: blob/master/demo/demo_JPM2024MinDownsideRisk.R
1433 views
## This R script reproduces all the Exhibits in the paper1## "Minimum Downside Risk Portfolios" by R.D. Martin,2## S. Stoyanov, X. Zhao and P. Sarker, published in the3## Oct. 2024 issue of the Journal of Portfolio Management.45##6## Copy/paste this script into your own computer R file. Then7## run code lines 23 to 229, which creates functions needed in8## subsequent code that replicates the Exhibits. We recommend9## running the subsequent code in chunks to replicate each of10## the Exhibits in the JPM paper.1112## Running Times for an Intel(R) Core(TM) i7-10750H Processor:13## Exhibits 1-5: 10 seconds, with 9 seconds for Exhibit 514## Exhibits 6,8,10,12: Approximately 2 minutes each15## Exhibits 14,16,18: Approximately 3.5 minutes each16## Other Exhibits: Negligible1718#### Load needed R functions19####2021## divHHImat.R22divHHImat <- function(wtsmat){23n <- nrow(wtsmat)24if(n < 1){25warning("empty data set")26return()27}28diversification <- rep(0, n)29for(i in 1:n){30diversification[i] <- 1 - sum(wtsmat[i,]^2)31}32DIV <- diversification33return(DIV)34}3536## TOcontrol.R37TOcontrol <- function(wts, delta){38idx <- index(wts)39out <- copy(wts)40TO <- rep(NA, length(idx))41for(i in 2:length(idx)){42currentTO <- sum(abs(coredata(wts[idx[i], ]) - coredata(wts[idx[i-1], ])))43TO[i] <- currentTO44if(currentTO <= delta){45out[idx[i], ] <- out[idx[i-1], ]46}47}48return(wts = out)49}5051## ToDivMES.R52ToDivMES <- function(x){53## wts: a list with xts objects wts.MV, wts.MES05, wts.MES05TOC5455# TO Values56wts.MV <- x$wts.MV57MV.TO <- 100*coredata(turnOver(wts.MV))58muMV.TO <- round(mean(MV.TO), 1)59sdMV.TO <- round(sd(MV.TO), 1)6061wts.MES05 <- x$wts.MES0562MES05.TO <- 100*coredata(turnOver(wts.MES05))63muMES05.TO <- round(mean(MES05.TO), 1)64sdMES05.TO <- round(sd(MES05.TO), 1)6566wts.MES05TOC <- x$wts.MES05TOC67MES05.TOC <- 100*coredata(turnOver(wts.MES05TOC))68muMES05.TOC <- round(mean(MES05.TOC), 1)69sdMES05.TOC <- round(sd(MES05.TOC), 1)7071# DIV Values72MV.DIV <- 100*coredata(divHHI(wts.MV))73muMV.DIV <- round(mean(MV.DIV), 1)74sdMV.DIV <- round(sd(MV.DIV), 1)7576MES05.DIV <- 100*coredata(divHHI(wts.MES05))77muMES05.DIV <- round(mean(MES05.DIV), 1)78sdMES05.DIV <- round(sd(MES05.DIV), 1)7980MES05TOC.DIV <- 100*coredata(divHHI(wts.MES05TOC))81muMES05TOC.DIV <- round(mean(MES05TOC.DIV), 1)82sdMES05TOC.DIV <- round(sd(MES05TOC.DIV), 1)8384# TO and DIV data frame85muSdTO_DIV <- rbind(c(muMV.TO, sdMV.TO, muMV.DIV, sdMV.DIV),86c(muMES05.TO, sdMES05.TO, muMES05.DIV, sdMES05.DIV),87c(muMES05.TOC, sdMES05.TOC, muMES05TOC.DIV, sdMES05TOC.DIV))88muSdTO_DIV <- data.frame(muSdTO_DIV)89names(muSdTO_DIV) <- c("TO Mean", "TO StdDev", "DIV Mean", "DIV StdDev")90row.names(muSdTO_DIV) <- c("MV", "MES05", "MES05-TOC")91return(muSdTO_DIV)92}9394## ToDivMCSM.R95ToDivMCSM <- function(x){96## wts: a list with xts objects wts.MV, wts.MES05, wts.MCSM159798# TO Values99wts.MV <- x$wts.MV100MV.TO <- 100*coredata(turnOver(wts.MV))101muMV.TO <- round(mean(MV.TO), 1)102sdMV.TO <- round(sd(MV.TO), 1)103104wts.MES05 <- x$wts.MES05105MES05.TO <- 100*coredata(turnOver(wts.MES05))106muMES05.TO <- round(mean(MES05.TO), 1)107sdMES05.TO <- round(sd(MES05.TO), 1)108109wts.MCSM15 <- x$wts.MCSM15110MCSM15.TO <- 100*coredata(turnOver(wts.MCSM15))111muMCSM15.TO <- round(mean(MCSM15.TO), 1)112sdMCSM15.TO <- round(sd(MCSM15.TO), 1)113114# DIV Values115MV.DIV <- 100*coredata(divHHI(wts.MV))116muMV.DIV <- round(mean(MV.DIV), 1)117sdMV.DIV <- round(sd(MV.DIV), 1)118119MES05.DIV <- 100*coredata(divHHI(wts.MES05))120muMES05.DIV <- round(mean(MES05.DIV), 1)121sdMES05.DIV <- round(sd(MES05.DIV), 1)122123MCSM15.DIV <- 100*coredata(divHHI(wts.MCSM15))124muMCSM15.DIV <- round(mean(MCSM15.DIV), 1)125sdMCSM15.DIV <- round(sd(MCSM15.DIV), 1)126127# TO and DIV data frame128muSdTO_DIV <- rbind(c(muMV.TO, sdMV.TO, muMV.DIV, sdMV.DIV),129c(muMES05.TO, sdMES05.TO, muMES05.DIV, sdMES05.DIV),130c(muMCSM15.TO, sdMCSM15.TO, muMCSM15.DIV, sdMCSM15.DIV))131muSdTO_DIV <- data.frame(muSdTO_DIV)132names(muSdTO_DIV) <- c("TO Mean", "TO StdDev", "DIV Mean", "DIV StdDev")133row.names(muSdTO_DIV) <- c("MV", "MES05", "MCSM15")134return(muSdTO_DIV)135}136137138## Pushpak function to calculate the mean and stdev of TO and DIV139## Here we changed Pushpak's function name MeanStd_TODIV to ToDivMeanSd140141ToDivMeanSd <- function(weights_list){142143# List objects for storing turnover and diversification for all portfolios144turnover_list <- list()145diversification_list <- list()146147# Calculate turnover and diversification for all portfolios148for (i in 1:length(weights_list)) {149portfolio_name <- names(weights_list)[i]150151# Extract portfolio weights from the list object152assign(paste0("wts_", portfolio_name), weights_list[[i]])153154# Calculate turnover and diversification and save in separate list objects155turnover_list[[paste0("TO_", portfolio_name)]] <- 100*coredata(PCRA::turnOver(get(paste0("wts_", portfolio_name))))156diversification_list[[paste0("DIV_", portfolio_name)]] <- 100*coredata(divHHI(get(paste0("wts_", portfolio_name))))157}158159# Calculate mean of turnover160mu_turnover_list <- lapply(turnover_list, mean)161mu_turnover_list <- lapply(mu_turnover_list, round, 1)162163# Calculate standard deviation of turnover164sd_turnover_list <- lapply(turnover_list, sd)165sd_turnover_list <- lapply(sd_turnover_list, round, 1)166167# Calculate mean of diversification168mu_diversification_list <- lapply(diversification_list, mean)169mu_diversification_list <- lapply(mu_diversification_list, round, 1)170171# Calculate standard deviation of diversification172sd_diversification_list <- lapply(diversification_list, sd)173sd_diversification_list <- lapply(sd_diversification_list, round, 1)174175# Mean and stdev of turnover176mu_turnover <- t(as.data.frame(mu_turnover_list))177row.names(mu_turnover) <- names(weights_list)178colnames(mu_turnover) <- "TO Mean"179180sd_turnover <- t(as.data.frame(sd_turnover_list))181row.names(sd_turnover) <- names(weights_list)182colnames(sd_turnover) <- "TO StdDev"183184# Mean and stdev of diversification185mu_diversification <- t(as.data.frame(mu_diversification_list))186row.names(mu_diversification) <- names(weights_list)187colnames(mu_diversification) <- "DIV Mean"188189sd_diversification <- t(as.data.frame(sd_diversification_list))190row.names(sd_diversification) <- names(weights_list)191colnames(sd_diversification) <- "DIV StdDev"192193# Combine into a single dataframe194muStd_TO_DIV <- cbind(mu_turnover, sd_turnover, mu_diversification, sd_diversification)195196return(muStd_TO_DIV)197}198199200## ratioFromThresholdTdist.R201ratioFromThresholdTdist <- function(eta = 1.0, df = 5)202{203integrand.top <- function(x, eta)204(x - eta) * dt(x, df)205206value.top <- integrate(integrand.top, eta,207Inf, eta = eta)$value208209integrand.bottom <- function(x, eta)210(x - eta)^2 * dt(x, df)211212value.bottom <- integrate(integrand.bottom, eta, Inf,213eta = eta)$value214ratio <- value.top/sqrt(value.bottom)215ratio216}217218## thresholdFromTailProbTdist.R219thresholdFromTailProbTdist <- function(qtl, df = 5,220interval = c(1e-6, 20)) # 1e-6221{222# Tail probabilities gamma = 1 - alpha, e.g., if alpha = 0.9,223# then the upper tail probability is gamma = 0.1224obj <- function(q, eta)225q - ratioFromThresholdTdist(eta, df = df)226227uniroot(obj, interval = interval, q = qtl, check.conv = TRUE, tol = 1e-8)$root228}229230#### End of functions needed for the following R script.231####232233## Load data for Exhibits 1 - 5234library(PCRA)235library(xts)236stocksCRSPweekly <- getPCRAData("stocksCRSPweekly")237dateRange <- c("2004-01-01", "2005-12-31")238stockItems <- c("Date", "TickerLast", "CapGroupLast", "Return",239"MktIndexCRSP", "Ret13WkBill")240returnsAll <- selectCRSPandSPGMI("weekly",241dateRange = dateRange,242stockItems = stockItems,243factorItems = NULL,244subsetType = "CapGroupLast",245subsetValues = "SmallCap",246outputType = "xts")247248returns <- returnsAll[ , 1:30]249RFts <- returnsAll[ , 108]250RFmean <- mean(RFts)251252253## Exhibit 1254255library(PortfolioAnalytics)256library(CVXR)257library(data.table)258pspec <- portfolio.spec(assets = names(returns))259pspecFI <- add.constraint(pspec, type = "full_investment")260pspecLO <- add.constraint(portfolio = pspecFI, type = "long_only")261262## Mean-ES Long-Only Efront p = 5% with ES Ratio263p <- 0.05264pspecESLO <- add.objective(pspecLO, type = "risk", name = "ES",265arguments = list(p = p))266meanESlo.ef <- create.EfficientFrontier(returns, portfolio = pspecESLO,267type = "mean-ES")268269xlim = c(0.0, 0.20)270ylim = c(-0.004, 0.015)271chart.EfficientFrontier(meanESlo.ef, match.col = "ES", type = "l",272chart.assets = TRUE, rf = RFmean,273labels.assets = FALSE, cex.assets = 1,274main = NULL,275RAR.text = "ES ratio", pch = 16, lwd = 1.5,276cex = 2.5, cex.axis = 1.1,277xlim = xlim, ylim = ylim)278279280## Exhibit 2281282efDat <- meanESlo.ef$frontier283efMat <- as.matrix(efDat[ , ])284dimnames(efMat)[[1]] <- 1:25285MU <- efMat[ , 1]286efWts <- efMat[ , -(1:3)]287DIV <- divHHImat(efWts)288plot(MU, DIV, type = "b", pch = 20, ylim = c(0,1))289abline(h = 0.9, lty = "dotted")290291292## Exhibit 3293294p <- 0.05295pspecESLO <- add.objective(pspecLO, type = "risk", name = "ES",296arguments = list(p = p))297pspecLObox <- add.constraint(portfolio = pspecFI, type = "box",298min = 0.02, max = 0.1)299pspecESLObox <- add.objective(pspecLObox, type = "risk", name = "ES",300arguments = list(p = p))301pspecLSbox <- add.constraint(portfolio = pspecFI, type = "box",302min = -0.1, max = 0.1)303pspecESLSbox <- add.objective(pspecLSbox, type = "risk", name = "ES",304arguments = list(p = p))305306portfList <- combine.portfolios(list(pspecESLSbox, pspecESLO, pspecESLObox))307legendLabels <- c("Long Short Box (-0.1, 0.1)", "Long Only", "Long Only Box (0.02, 0.1)")308309chart.EfficientFrontierOverlay(returns, portfolio_list = portfList,310type = "mean-ES", match.col = "ES",311legend.loc = "topright", chart.assets = TRUE,312legend.labels = legendLabels, cex.legend = 1,313labels.assets = FALSE, lwd = c(2, 2, 3),314col = c("dark green", "black", "dark red"),315lty = c("dashed", "solid", "dotted"),316xlim = xlim, ylim = ylim, main = NULL)317318319320## Exhibit 4321322pspecES05 <- add.objective(portfolio = pspecLO, type = "risk", name = "ES",323arguments = list(p=0.05))324pspecES25 <- add.objective(portfolio = pspecLO, type = "risk", name = "ES",325arguments = list(p=0.25))326pspecES50 <- add.objective(portfolio = pspecLO, type = "risk", name = "ES",327arguments = list(p=0.50))328329# Combine the portfolios into a list330portfESlist <- combine.portfolios(list(pspecES50, pspecES25, pspecES05))331332# Plot the efficient frontier overlay of the portfolios with varying tail probabilities333legendESlabels <- c("ES (p = 0.50)", "ES (p = 0.25)", "ES (p = 0.05)")334335portfList <- combine.portfolios(list(pspecESLSbox, pspecESLO, pspecESLObox))336legendLabels <- c("Long Short Box (-0.1, 0.1)", "Long Only", "Long Only Box (0.02, 0.1)")337338chart.EfficientFrontierOverlay(returns, portfolio_list = portfESlist,339type = "mean-ES", match.col = "ES",340legend.loc = "topright", chart.assets = TRUE,341legend.labels = legendESlabels, cex.legend = 1,342labels.assets = FALSE, lwd = c(2,2,2),343col = c("dark green" , "black", "dark red"),344lty = c("solid", "dashed", "dotted"),345xlim = xlim, ylim = ylim, main = NULL)346347348## Exhibit 5 (9 seconds)349350pspecESLO_050 <- add.objective(pspecLO, type = "risk", name = "ES",351arguments = list(p = 0.050))352353# The follow function takes about 1 minute (check)354# reduce n.portfolios for faster, but less accurate, rendition355chart.EfficientFrontierCompare(returns, pspecESLO_050, risk_type = "ES",356guideline = TRUE, cex.axis = 1.2,357match.col = c("StdDev", "ES"),358n.portfolios = 10,359lwd=c(1.2, 1.2, 1.0, 1.0),360col = c(2,1,1,1), lty = c(2,1,4,4),361xlim = c(0.00, 0.08), ylim = c(0.0, 0.013),362legend.loc = "topleft", main = NULL)363# A slightly better plot is obtained with larger n.portfolios, with very364# small % changes in Risk and Return365366367## Load packages368369library(PortfolioAnalytics)370library(CVXR)371library(data.table)372library(xts)373library(PCRA)374375# Load CRSP daily smallcap returns for Exhibits 6-7, 8-9, 10-11376377stocksCRSPdaily <- getPCRAData(dataset = "stocksCRSPdaily")378dateRange <- c("1993-01-01","2015-12-31")379smallcapTS <- selectCRSPandSPGMI(380periodicity = "daily",381dateRange = dateRange,382stockItems = c("Date", "TickerLast", "CapGroupLast", "Return",383"MktIndexCRSP", "Ret13WkBill"),384factorItems = NULL,385subsetType = "CapGroupLast",386subsetValues = "SmallCap",387outputType = "xts")388389# Extract Market and RF from smallcapTS390Market <- smallcapTS[ , 107]391names(Market) <- "Market"392RF <- smallcapTS[ , 108]393names(RF) <- "RF"394395# Remove "MktIndexCRSP", "Ret13WkBill" from smallcapTS396smallcapTS <- smallcapTS[ , -c(107,108)]397398399## Exhibit 6 (2 minutes and 0 seconds)400401ret <- smallcapTS[ , 1:30]402403# Generate MV, and MES portfolios404pspec <- portfolio.spec(assets = names(ret))405pspecFI <- add.constraint(pspec, type = "full_investment")406pspecLO <- add.constraint(pspecFI, type = "long_only")407pspecMV <- add.objective(pspecLO, type = "risk", name = "var")408pspecMES05 <- add.objective(pspecLO, type = "risk", name = "ES",409arguments = list(p=0.05))410411# Optimize Portfolio with Monthly Rebalancing412window <- 260413bt.MV <- optimize.portfolio.rebalancing(ret, pspecMV,414optimize_method = "CVXR",415rebalance_on = "months",416rolling_window = window)417418bt.MES05 <- optimize.portfolio.rebalancing(ret, pspecMES05,419optimize_method = "CVXR",420rebalance_on = "months",421rolling_window = window)422423# Extract time series of portfolio weights424wts.MV <- extractWeights(bt.MV)425wts.MV <- wts.MV[complete.cases(wts.MV),]426wts.MES05 <- extractWeights(bt.MES05)427wts.MES05 <- wts.MES05[complete.cases(wts.MES05),]428429wts.MES05TOC <- TOcontrol(wts.MES05, 0.9) # Optimal for 1-30430431# For table below432wts.comb21 <- list(wts.MV = wts.MV, wts.MES05 = wts.MES05,433wts.MES05TOC = wts.MES05TOC)434435# Compute cumulative returns of the portfolios436MV <- Return.rebalancing(ret, wts.MV)437MES05 <- Return.rebalancing(ret, wts.MES05)438MES05TOC <- Return.rebalancing(ret, wts.MES05TOC)439440# Combine MV, MES05, MES05_TOC gross cumulative returns441ret.comb <- na.omit(merge(MV, MES05, MES05TOC, Market))442names(ret.comb) <- c("MV", "MES05", "MES05-TOC", "Market")443444# For table below445ret.comb21 <- ret.comb[ , -4]446447backtest.plot(ret.comb, plotType = "cumRet",448main = "MV, MES05, MES05-TOC(0.9), Stocks 1-30",449colorSet = c("red","darkgreen","darkblue","black"),450ltySet = c(3, 1, 1, 1), lwdSet = c(0.7, 0.7, 0.7, 0.7))451452453## Exhibit 7454455# Create TO and DIV values data frame456muSdTO_DIV <- ToDivMeanSd(wts.comb21)457458ret.comb21Short <- ret.comb21["2006/2014", ]459460dat <- ret.comb21Short461SR <- RPESE::SR.SE(dat)$SR462DSR <- RPESE::DSR.SE(dat)$DSR463SR_DSR <- data.frame(round(sqrt(252)*cbind(SR,DSR),2))464465# Combine the two data frames466portStats <- data.frame(muSdTO_DIV, SR_DSR)467row.names(portStats) <- names(dat)468portStats469470471## Exhibit 8 (1 minute and 55 seconds)472473ret <- smallcapTS[ , 31:60]474475# Generate MV, and MES portfolios476pspec <- portfolio.spec(assets = names(ret))477pspecFI <- add.constraint(pspec, type = "full_investment")478pspecLO <- add.constraint(pspecFI, type = "long_only")479pspecMV <- add.objective(pspecLO, type = "risk", name = "var")480pspecMES05 <- add.objective(pspecLO, type = "risk", name = "ES",481arguments = list(p=0.05))482483# Optimize Portfolio at Monthly Rebalancing and 500-Day Training484window <- 260485bt.MV <- optimize.portfolio.rebalancing(ret, pspecMV,486optimize_method = "CVXR",487rebalance_on = "months",488rolling_window = window)489490bt.MES05 <- optimize.portfolio.rebalancing(ret, pspecMES05,491optimize_method = "CVXR",492rebalance_on = "months",493rolling_window = window)494495# Extract time series of portfolio weights496wts.MV <- extractWeights(bt.MV)497wts.MV <- wts.MV[complete.cases(wts.MV),]498wts.MES05 <- extractWeights(bt.MES05)499wts.MES05 <- wts.MES05[complete.cases(wts.MES05),]500501wts.MES05TOC <- TOcontrol(wts.MES05, 0.5) # Optimal for 31-60502503# For table below504wts.comb22 <- list(wts.MV = wts.MV, wts.MES05 = wts.MES05,505wts.MES05TOC = wts.MES05TOC)506507# Compute cumulative returns of the portfolios508MV <- Return.rebalancing(ret, wts.MV)509MES05 <- Return.rebalancing(ret, wts.MES05)510MES05TOC <- Return.rebalancing(ret, wts.MES05TOC)511512# Combine MV, MES05, MES05_TOC gross cumulative returns513ret.comb <- na.omit(merge(MV, MES05, MES05TOC, Market, all=F))514names(ret.comb) <- c("MV", "MES05", "MES05-TOC", "Market")515516# For table below517ret.comb22 <- ret.comb[ , -4]518519backtest.plot(ret.comb, plotType = "cumRet",520main = "MV, MES05, MES05-TOC(0.5), Stocks 31-60",521colorSet = c("red","darkgreen","darkblue","black"),522ltySet = c(3, 1, 1, 1), lwdSet = c(0.7, 0.7, 0.7, 0.7))523524525## Exhibit 9526527# Create TO and DIV values data frame528muSdTO_DIV <- ToDivMeanSd(wts.comb22)529530ret.comb22Short <- ret.comb22["2006/2014", ]531532dat <- ret.comb22Short533SR <- RPESE::SR.SE(dat)$SR534DSR <- RPESE::DSR.SE(dat)$DSR535SR_DSR <- data.frame(round(sqrt(252)*cbind(SR,DSR),2))536537# Combine the two data frames538portStats <- data.frame(muSdTO_DIV, SR_DSR)539row.names(portStats) <- names(dat)540portStats541542543## Exhibit 10 (1 minute and 55 seconds)544545ret <- smallcapTS[ , 61:90]546547# Generate MV, and MES portfolios548pspec <- portfolio.spec(assets = names(ret))549pspecFI <- add.constraint(pspec, type = "full_investment")550pspecLO <- add.constraint(pspecFI, type = "long_only")551pspecMV <- add.objective(pspecLO, type = "risk", name = "var")552pspecMES05 <- add.objective(pspecLO, type = "risk", name = "ES",553arguments = list(p=0.05))554555# Optimize Portfolio at Monthly Rebalancing556window <- 260557bt.MV <- optimize.portfolio.rebalancing(ret, pspecMV,558optimize_method = "CVXR",559rebalance_on = "months",560rolling_window = window)561562bt.MES05 <- optimize.portfolio.rebalancing(ret, pspecMES05,563optimize_method = "CVXR",564rebalance_on = "months",565rolling_window = window)566567# Extract time series of portfolio weights568wts.MV <- extractWeights(bt.MV)569wts.MV <- wts.MV[complete.cases(wts.MV),]570wts.MES05 <- extractWeights(bt.MES05)571wts.MES05 <- wts.MES05[complete.cases(wts.MES05),]572573wts.MES05TOC <- TOcontrol(wts.MES05, 0.5) # Optimal for 61-90574575# For table below576wts.comb23 <- list(wts.MV = wts.MV, wts.MES05 = wts.MES05,577wts.MES05TOC = wts.MES05TOC)578579# Compute cumulative returns of the portfolios580MV <- Return.rebalancing(ret, wts.MV)581MES05 <- Return.rebalancing(ret, wts.MES05)582MES05TOC <- Return.rebalancing(ret, wts.MES05TOC)583584# Combine MV, MES05, MES05_TOC gross cumulative returns585ret.comb <- na.omit(merge(MV, MES05, MES05TOC, Market, all=F))586names(ret.comb) <- c("MV", "MES05", "MES05-TOC", "Market")587588# For table below589ret.comb23 <- ret.comb[ , -4]590591backtest.plot(ret.comb, plotType = "cumRet",592main = "MV, MES05, MES05-TOC(0.5), Stocks 61-90",593colorSet = c("red","darkgreen","darkblue","black"),594ltySet = c(3, 1, 1, 1), lwdSet = c(0.7, 0.7, 0.7, 0.7))595596597598## Exhibit 11599600# Create TO and DIV values data frame601602muSdTO_DIV <- ToDivMeanSd(wts.comb23)603604ret.comb23Short <- ret.comb23["2006/2014", ]605dat <- ret.comb23Short606SR <- RPESE::SR.SE(dat)$SR607DSR <- RPESE::DSR.SE(dat)$DSR608SR_DSR <- data.frame(round(sqrt(252)*cbind(SR,DSR),2))609610# Combine the two data frames611portStats <- data.frame(muSdTO_DIV, SR_DSR)612row.names(portStats) <- names(dat)613portStats614615## Load packages again (not necessary)616library(PortfolioAnalytics)617library(CVXR)618library(data.table)619library(xts)620library(PCRA)621622## Load daily microcap returns for Exhibits 12-13623624stocksCRSPdaily <- getPCRAData(dataset = "stocksCRSPdaily")625dateRange <- c("1993-01-01","2015-12-31")626microcapTS <- selectCRSPandSPGMI(627periodicity = "daily",628dateRange = dateRange,629stockItems = c("Date", "TickerLast", "CapGroupLast", "Return",630"MktIndexCRSP", "Ret13WkBill"),631factorItems = NULL,632subsetType = "CapGroupLast",633subsetValues = "MicroCap",634outputType = "xts")635636# Extract Market and RF from microcapTS637Market <- microcapTS[ , 35]638names(Market) <- "Market"639RF <- microcapTS[ , 36]640names(RF) <- "RF"641642643# Remove "MktIndexCRSP", "Ret13WkBill" from smallcapTS644microcapTS <- microcapTS[ , -c(35, 36)]645646647## Exhibit 12 (1 minute and 58 seconds)648ret <- microcapTS649650# Generate MV, and MES portfolios651pspec <- portfolio.spec(assets = names(ret))652pspecFI <- add.constraint(pspec, type = "full_investment")653pspecLO <- add.constraint(pspecFI, type = "long_only")654pspecMV <- add.objective(pspecLO, type = "risk", name = "var")655pspecMES05 <- add.objective(pspecLO, type = "risk", name = "ES",656arguments = list(p=0.05))657658# Optimize Portfolio at Monthly Rebalancing659window <- 260660bt.MV <- optimize.portfolio.rebalancing(ret, pspecMV,661optimize_method = "CVXR",662rebalance_on = "months",663rolling_window = window)664665bt.MES05 <- optimize.portfolio.rebalancing(ret, pspecMES05,666optimize_method = "CVXR",667rebalance_on = "months",668rolling_window = window)669670# Extract time series of portfolio weights671wts.MV <- extractWeights(bt.MV)672wts.MV <- wts.MV[complete.cases(wts.MV),]673wts.MES05 <- extractWeights(bt.MES05)674wts.MES05 <- wts.MES05[complete.cases(wts.MES05),]675676wts.MES05TOC <- TOcontrol(wts.MES05, 0.5)677678# For table below679wts.comb24 <- list(wts.MV = wts.MV, wts.MES05 = wts.MES05,680wts.MES05TOC = wts.MES05TOC)681682# Compute cumulative returns of the portfolios683MV <- Return.rebalancing(ret, wts.MV)684MES05 <- Return.rebalancing(ret, wts.MES05)685MES05TOC <- Return.rebalancing(ret, wts.MES05TOC)686687# Combine MV, MES05, MES05_TOC gross cumulative returns688ret.comb <- na.omit(merge(MV, MES05, MES05TOC, Market, all=F))689names(ret.comb) <- c("MV", "MES05", "MES05-TOC", "Market")690691# For table below692ret.comb24 <- ret.comb[ , -4]693694backtest.plot(ret.comb, plotType = "cumRet",695main = "MV, MES05, MES05-TOC(0.5), 34 Microcap Stocks",696colorSet = c("red","darkgreen","darkblue","black"),697ltySet = c(3, 1, 1, 1), lwdSet = c(0.7, 0.7, 0.7, 0.7))698699700701702## Exhibit 13703704# Create TO and DIV values data frame705muSdTO_DIV <- ToDivMeanSd(wts.comb24)706707ret.comb24Short <- ret.comb24["2006/2014", ]708dat <- ret.comb24Short709SR <- RPESE::SR.SE(dat)$SR710DSR <- RPESE::DSR.SE(dat)$DSR711SR_DSR <- data.frame(round(sqrt(252)*cbind(SR,DSR),2))712713# Combine the two data frames714portStats <- data.frame(muSdTO_DIV, SR_DSR)715row.names(portStats) <- names(dat)716portStats717718719## Load packages again (not necessary to repeat)720721library(PortfolioAnalytics)722library(CVXR)723library(data.table)724library(xts)725library(PCRA)726727# Load CRSP smallcap returns for Exhibits 14-15, 16-17, 18-19728729stocksCRSPdaily <- getPCRAData(dataset = "stocksCRSPdaily")730dateRange <- c("1993-01-01","2015-12-31")731smallcapTS <- selectCRSPandSPGMI(732periodicity = "daily",733dateRange = dateRange,734stockItems = c("Date", "TickerLast", "CapGroupLast", "Return",735"MktIndexCRSP", "Ret13WkBill"),736factorItems = NULL,737subsetType = "CapGroupLast",738subsetValues = "SmallCap",739outputType = "xts")740741# Extract Market and RF from smallcapTS742Market <- smallcapTS[ , 107]743names(Market) <- "Market"744RF <- smallcapTS[ , 108]745names(RF) <- "RF"746747# Remove "MktIndexCRSP", "Ret13WkBill" from smallcapTS748smallcapTS <- smallcapTS[ , -c(107,108)]749750751## Exhibit 14 (3 minutes and 28 seconds)752ret <- smallcapTS[ , 1:30]753754# Generate MV, and MES portfolios755pspec <- portfolio.spec(assets = names(ret))756pspecFI <- add.constraint(pspec, type = "full_investment")757pspecLO <- add.constraint(pspecFI, type = "long_only")758pspecMV <- add.objective(pspecLO, type = "risk", name = "var")759pspecMES05 <- add.objective(pspecLO, type = "risk", name = "ES",760arguments = list(p=0.05))761pspecMCSM15 <- add.objective(pspecLO, type = "risk", name = "CSM",762arguments = list(p=0.15))763764# Optimize Portfolio with Monthly Rebalancing765window <- 260766bt.MV <- optimize.portfolio.rebalancing(ret, pspecMV,767optimize_method = "CVXR",768rebalance_on = "months",769rolling_window = window)770771bt.MES05 <- optimize.portfolio.rebalancing(ret, pspecMES05,772optimize_method = "CVXR",773rebalance_on = "months",774rolling_window = window)775776bt.MCSM15 <- optimize.portfolio.rebalancing(ret, pspecMCSM15,777optimize_method = "CVXR",778rebalance_on = "months",779rolling_window = window)780781# Extract time series of portfolio weights782wts.MV <- extractWeights(bt.MV)783wts.MV <- wts.MV[complete.cases(wts.MV),]784wts.MES05 <- extractWeights(bt.MES05)785wts.MES05 <- wts.MES05[complete.cases(wts.MES05),]786wts.MCSM15 <- extractWeights(bt.MCSM15)787wts.MCSM15 <- wts.MCSM15[complete.cases(wts.MCSM15),]788789wts.MES05TOC <- TOcontrol(wts.MES05, 0.9)790wts.MCSM15TOC <- TOcontrol(wts.MCSM15, 0.8)791792# For table below793wts.comb31TOC <- list(wts.MV = wts.MV, wts.MES05TOC = wts.MES05TOC,794wts.MCSM15TOC = wts.MCSM15TOC)795796# Compute cumulative returns of the portfolios797MV <- Return.rebalancing(ret, wts.MV)798MES05TOC <- Return.rebalancing(ret, wts.MES05TOC)799MCSM15TOC <- Return.rebalancing(ret, wts.MCSM15TOC)800801802# Combine MV, MES05, MES05_TOC gross cumulative returns803ret.comb <- na.omit(merge(MV, MES05TOC, MCSM15TOC, Market, all=F))804names(ret.comb) <- c("MV", "MES05-TOC", "MCSM15-TOC", "Market")805806# For table below807ret.comb31TOC <- ret.comb[ , -4]808809backtest.plot(ret.comb, plotType = "cumRet",810main = "MV, MES05-TOC(0.9), MCSM15-TOC(0.8), Stocks 1-30",811colorSet = c("red","darkgreen","darkblue","black"),812ltySet = c(3, 1, 1, 1), lwdSet = c(0.7, 0.7, 0.7, 0.7))813814815## Exhibit 15816817# Create TO and DIV values data frame818muSdTO_DIV <- ToDivMeanSd(wts.comb31TOC)819820ret.comb31TOCShort <- ret.comb31TOC["2006/2014", ]821dat <- ret.comb31TOCShort822SR <- RPESE::SR.SE(dat)$SR823DSR <- RPESE::DSR.SE(dat)$DSR824SR_DSR <- data.frame(round(sqrt(252)*cbind(SR,DSR),2))825826# Combine the two data frames827portStats <- data.frame(muSdTO_DIV, SR_DSR)828row.names(portStats) <- names(dat)829portStats830831832## Exhibit 16 (3 minutes and 41 seconds)833ret <- smallcapTS[ , 31:60]834835# Optimize Portfolio with Monthly Rebalancing836window <- 260837bt.MV <- optimize.portfolio.rebalancing(ret, pspecMV,838optimize_method = "CVXR",839rebalance_on = "months",840rolling_window = window)841842bt.MES05 <- optimize.portfolio.rebalancing(ret, pspecMES05,843optimize_method = "CVXR",844rebalance_on = "months",845rolling_window = window)846847bt.MCSM15 <- optimize.portfolio.rebalancing(ret, pspecMCSM15,848optimize_method = "CVXR",849rebalance_on = "months",850rolling_window = window)851852# Extract time series of portfolio weights853wts.MV <- extractWeights(bt.MV)854wts.MV <- wts.MV[complete.cases(wts.MV),]855wts.MES05 <- extractWeights(bt.MES05)856wts.MES05 <- wts.MES05[complete.cases(wts.MES05),]857wts.MCSM15 <- extractWeights(bt.MCSM15)858wts.MCSM15 <- wts.MCSM15[complete.cases(wts.MCSM15),]859860wts.MES05TOC <- TOcontrol(wts.MES05, 0.5)861wts.MCSM15TOC <- TOcontrol(wts.MCSM15, 0.6)862863# For table below864wts.comb32TOC <- list(wts.MV = wts.MV, wts.MES05TOC = wts.MES05TOC,865wts.MCSM15TOC = wts.MCSM15TOC)866867# Compute cumulative returns of the portfolios868MV <- Return.rebalancing(ret, wts.MV)869MES05TOC <- Return.rebalancing(ret, wts.MES05TOC)870MCSM15TOC <- Return.rebalancing(ret, wts.MCSM15TOC)871872873# Combine cumulative gross returns874ret.comb <- na.omit(merge(MV, MES05TOC, MCSM15TOC, Market, all=F))875names(ret.comb) <- c("MV", "MES05-TOC", "MCSM15-TOC", "Market")876877# For table below878ret.comb32TOC <- ret.comb[ , -4]879880backtest.plot(ret.comb, plotType = "cumRet",881main = "MV, MES05-TOC(0.5), MCSM15-TOC(0.6), Stocks 31-60",882colorSet = c("red","darkgreen","darkblue","black"),883ltySet = c(3, 1, 1, 1), lwdSet = c(0.7, 0.7, 0.7, 0.7))884885886887## Exhibit 17888889# Create TO and DIV values data frame890muSdTO_DIV <- ToDivMeanSd(wts.comb32TOC)891892ret.comb32TOCShort <- ret.comb32TOC["2006/2014", ]893dat <- ret.comb32TOCShort894SR <- RPESE::SR.SE(dat)$SR895DSR <- RPESE::DSR.SE(dat)$DSR896SR_DSR <- data.frame(round(sqrt(252)*cbind(SR,DSR),2))897898# Combine the two data frames899portStats <- data.frame(muSdTO_DIV, SR_DSR)900row.names(portStats) <- names(dat)901portStats902903904## Exhibit 18 (3 minutes and 41 seconds)905ret <- smallcapTS[ , 61:90]906907# Generate MV, and MES portfolios908pspec <- portfolio.spec(assets = names(ret))909pspecFI <- add.constraint(pspec, type = "full_investment")910pspecLO <- add.constraint(pspecFI, type = "long_only")911pspecMV <- add.objective(pspecLO, type = "risk", name = "var")912pspecMES05 <- add.objective(pspecLO, type = "risk", name = "ES",913arguments = list(p=0.05))914pspecMCSM15 <- add.objective(pspecLO, type = "risk", name = "CSM",915arguments = list(p=0.15))916917# Optimize Portfolio with Monthly Rebalancing918window <- 260919bt.MV <- optimize.portfolio.rebalancing(ret, pspecMV,920optimize_method = "CVXR",921rebalance_on = "months",922rolling_window = window)923924bt.MES05 <- optimize.portfolio.rebalancing(ret, pspecMES05,925optimize_method = "CVXR",926rebalance_on = "months",927rolling_window = window)928929bt.MCSM15 <- optimize.portfolio.rebalancing(ret, pspecMCSM15,930optimize_method = "CVXR",931rebalance_on = "months",932rolling_window = window)933934# Extract time series of portfolio weights935wts.MV <- extractWeights(bt.MV)936wts.MV <- wts.MV[complete.cases(wts.MV),]937wts.MES05 <- extractWeights(bt.MES05)938wts.MES05 <- wts.MES05[complete.cases(wts.MES05),]939wts.MCSM15 <- extractWeights(bt.MCSM15)940wts.MCSM15 <- wts.MCSM15[complete.cases(wts.MCSM15),]941942wts.MES05TOC <- TOcontrol(wts.MES05, 0.6) # Use this value of delta943wts.MCSM15TOC <- TOcontrol(wts.MCSM15, 0.8) # Use this value of delta944945# For table below946wts.comb33TOC <- list(wts.MV = wts.MV, wts.MES05TOC = wts.MES05TOC,947wts.MCSM15TOC = wts.MCSM15TOC)948949# Compute cumulative returns of the portfolios950MV <- Return.rebalancing(ret, wts.MV)951MES05TOC <- Return.rebalancing(ret, wts.MES05TOC)952MCSM15TOC <- Return.rebalancing(ret, wts.MCSM15TOC)953954955# Combine MV, MES05, MES15TOC gross cumulative returns956ret.comb <- na.omit(merge(MV, MES05TOC, MCSM15TOC, Market, all=F))957names(ret.comb) <- c("MV", "MES05-TOC", "MCSM15-TOC", "Market")958959# For table below960ret.comb33TOC <- ret.comb[ , -4]961962backtest.plot(ret.comb, plotType = "cumRet",963main = "MV, MES05-TOC(0.6), MCSM15-TOC(0.8), Stocks 61-90",964colorSet = c("red","darkgreen","darkblue","black"),965ltySet = c(3, 1, 1, 1), lwdSet = c(0.7, 0.7, 0.7, 0.7))966967968969## Exhibit 19970971# Create TO and DIV values data frame972muSdTO_DIV <- ToDivMeanSd(wts.comb33TOC)973974ret.comb33TOCShort <- ret.comb33TOC["2006/2014", ]975dat <- ret.comb33TOCShort976SR <- RPESE::SR.SE(dat)$SR977DSR <- RPESE::DSR.SE(dat)$DSR978SR_DSR <- data.frame(round(sqrt(252)*cbind(SR,DSR),2))979980# Combine the two data frames981portStats <- data.frame(muSdTO_DIV, SR_DSR)982row.names(portStats) <- names(dat)983portStats984985986## Exhibt 20987988## Thresholds from Tail Probs989thresholds <- function(dof, Z = FALSE)990{991# Z = FALSE controls row.names suffix ".T" versus ".Z"992tailProbs <- c(0.01, 0.05, 0.10, 0.15, 0.20)993n <- length(tailProbs)994ThresholdTdist <- rep(0,n)995for(i in 1:n) {996ThresholdTdist[i] <- thresholdFromTailProbTdist(tailProbs[i], df = dof)997}998rnd <- 5999ThresholdTdist <- round(ThresholdTdist,rnd)10001001# TupperBnd <- function(alpha) qt((1-(1 - alpha)^2), df = dof)1002TupperBnd <- function(tailProbs) qt((1-tailProbs^2), df = dof)10031004Tquantiles <- round(qt(1 - tailProbs, df = dof),rnd)1005Tprobs <- round(1 - pt(ThresholdTdist, df = dof), rnd)1006# TupperBnd <- round(TupperBnd(1 - tailProbs),rnd)1007TupperBnd <- round(TupperBnd(tailProbs),rnd)10081009UBprobs <- tailProbs^21010dat <- data.frame(rbind(TupperBnd, ThresholdTdist, Tquantiles, UBprobs, Tprobs))1011if(Z == TRUE){1012row.names(dat) <- c("UpperBnd.Z", "Threshold.Z", "Quantile.Z",1013"UpperBndProbs.Z", "ThresholdProbs.Z")1014} else {1015row.names(dat) <- c("UpperBnd.T", "Threshold.T", "Quantile.T",1016"UpperBndProbs.T", "ThresholdProbs.T")1017}1018names(dat) <- c("1%", "5%", "10%", "15%", "20%")1019return(dat) # UBprobs relatively close to Tprob even with df = 31020}10211022thresh.Z <- thresholds(500, Z = TRUE)1023thresh.T <- thresholds(10, Z = FALSE)1024datOut <- rbind(thresh.Z, thresh.T)1025datOut102610271028