Path: blob/master/demo/demo_cvxrPortfolioAnalytics.R
1433 views
## Running Times for the following code with an Intel(R)1## Core(TM) i7-10750H Processor. Unless listed as follows,2## sections take less than 10 seconds:3## Section 9.1: 3 min and 40 seconds4## Section 9.2: 4 min and 50 seconds5## Section 11.1: 1 min and 10 seconds6## Section 12: 1 min and 16 seconds789## Section 21011library(PortfolioAnalytics)12library(CVXR)13library(data.table)14library(xts)15library(PCRA)161718## SECTION 2.31920data(edhec)21class(edhec)22ret_edhec <- tail(edhec, 60) # Extract the last 5 years23range(index(edhec)) # Start and end dates of `edhec`24range(index(ret_edhec)) # Start and end dates of ret_edhec25# names(edhec) # Names of `edhec` long, so use shorter names26colnames(ret_edhec) <- c("CA", "CTAG", "DS", "EM", "EMN", "ED", "FIA", "GM", "LSE", "MA", "RV", "SS", "FF")27print(head(ret_edhec, 5))2829tsPlotMP(ret_edhec, layout = c(2, 7))303132## SECTION 3.13334# Create portfolio object35fund_edhec <- colnames(ret_edhec)36pspec_maxret <- portfolio.spec(assets = fund_edhec)37# Add constraints to the portfolio object38pspec_maxret <- add.constraint(pspec_maxret, type = "full_investment")39pspec_maxret <- add.constraint(portfolio = pspec_maxret, type = "box",40min = rep(0.02, 13),41max = c(rep(0.15, 8), rep(0.1, 5)))42# Add objective to the portfolio object43pspec_maxret <- add.objective(portfolio = pspec_maxret,44type = "return", name = "mean")45pspec_maxret464748## SECTION 3.24950# Run the optimization with default solver51opt_maxret <- optimize.portfolio(R = ret_edhec, portfolio = pspec_maxret, optimize_method = "CVXR")52opt_maxret53opt_maxret$solver5455# Run the optimization with a different solver56opt_maxret_glpk <- optimize.portfolio(R = ret_edhec, portfolio = pspec_maxret, optimize_method = c("CVXR", "GLPK"))57opt_maxret_glpk$solver5859class(opt_maxret)6061names(opt_maxret)6263opt_maxret$weights6465opt.outputMvo(opt_maxret, ret_edhec, digits =3)666768## SECTION 3.36970bt_maxret <- optimize.portfolio.rebalancing(R = ret_edhec, portfolio = pspec_maxret,71optimize_method = "CVXR",72rebalance_on = "quarters", training_period = 36)7374names(bt_maxret)7576names(bt_maxret$opt_rebalancing)777879## SECTION 4.18081# Create portfolio object82pspec_gmv <- portfolio.spec(assets = fund_edhec)83# Add full-investment constraint84pspec_gmv <- add.constraint(pspec_gmv, type = "full_investment")85# Add objective of minimizing variance86pspec_gmv <- add.objective(portfolio = pspec_gmv, type = "risk", name = "var")8788opt_gmv <- optimize.portfolio(ret_edhec, pspec_gmv, optimize_method = "CVXR")89opt.outputMvo(opt_gmv, ret_edhec, digits =3)909192## SECTION 4.29394# portfolio object95pspec_gmv <- add.constraint(pspec_gmv, type = "long_only")96pspec_gmvGroup <- add.constraint(pspec_gmv, type = "group",97groups = list(groupA=1,98groupB=c(2:12),99groupC=13),100group_min = c(0, 0.05, 0.05),101group_max = c(0.4, 0.8, 0.5))102pspec_gmvGroup <- add.constraint(pspec_gmvGroup, type = "return", return_target = 0.003)103pspec_gmvGroup104105# optimization106opt_gmvGroup <- optimize.portfolio(ret_edhec, pspec_gmvGroup, optimize_method = "CVXR")107opt.outputMvo(opt_gmvGroup, ret_edhec, digits =3)108109opt_gmvGroup_ecos <- optimize.portfolio(ret_edhec, pspec_gmvGroup, optimize_method = c("CVXR", "ECOS"))110opt.outputMvo(opt_gmvGroup_ecos, ret_edhec, digits =3)111112opt_gmvGroup$solver113opt_gmvGroup_ecos$solver114115116## SECTION 5.1117118pspec_qu <- portfolio.spec(assets = fund_edhec)119pspec_qu <- add.constraint(pspec_qu, type = "full_investment")120pspec_qu <- add.constraint(pspec_qu, type = "long_only")121# Add objectives122pspec_qu <- add.objective(portfolio = pspec_qu, type = "return", name = "mean")123pspec_qu <- add.objective(portfolio = pspec_qu, type = "risk", name = "var",124risk_aversion = 20)125126127## SECTION 5.2128129opt_qu <- optimize.portfolio(ret_edhec, pspec_qu, optimize_method = "CVXR")130opt.outputMvo(opt_qu, ret_edhec, digits =3)131132133## SECTION 6.1134135pspec_es <- portfolio.spec(assets = fund_edhec)136pspec_es <- add.constraint(pspec_es, type = "full_investment")137pspec_es <- add.constraint(pspec_es, type = "long_only")138# Add objective of minimizing ES by using the default gamma139pspec_gmes <- add.objective(portfolio = pspec_es, type = "risk", name = "ES") # Uses default tail probability 0.05140# Add objective of minimizing ES by using the specific gamma=0.1141pspec_gmes_1 <- add.objective(portfolio = pspec_es, type = "risk", name = "ES", arguments = list(p=0.1))142143144## SECTION 6.2145146# GMES with default gamma=0.05147opt_gmes <- optimize.portfolio(ret_edhec, pspec_gmes, optimize_method = "CVXR")148opt_gmes149# GMES with specific gamma=0.1150opt_gmes_1 <- optimize.portfolio(ret_edhec, pspec_gmes_1, optimize_method = "CVXR")151opt_gmes_1152153154## SECTION 7.1155156pspec_csm <- portfolio.spec(assets = fund_edhec)157pspec_csm <- add.constraint(pspec_csm, type = "full_investment")158pspec_csm <- add.constraint(pspec_csm, type = "long_only")159# Add objective of minimizing CSM160pspec_mcsm <- add.objective(portfolio = pspec_csm, type = "risk", name = "CSM",161arguments = list(p=0.05))162163164## SECTION 7.2165166opt_mcsm <- optimize.portfolio(ret_edhec, pspec_mcsm, optimize_method = "CVXR")167opt_mcsm168169170## SECTION 8.1171172# Create portfolio object173pspec_sr <- portfolio.spec(assets = fund_edhec)174## Add constraints of maximizing Sharpe Ratio175pspec_sr <- add.constraint(pspec_sr, type = "full_investment")176pspec_sr <- add.constraint(pspec_sr, type = "long_only")177## Add objectives of maximizing Sharpe Ratio178pspec_sr <- add.objective(pspec_sr, type = "return", name = "mean")179pspec_sr <- add.objective(pspec_sr, type = "risk", name = "var")180181# Optimization182optimize.portfolio(ret_edhec, pspec_sr, optimize_method = "CVXR", maxSR = TRUE)183184185## SECTION 8.2186187# Create portfolio object188pspec_ESratio <- portfolio.spec(assets = fund_edhec)189## Add constraints of maximizing return per unit ES190pspec_ESratio <- add.constraint(pspec_ESratio, type = "full_investment")191pspec_ESratio <- add.constraint(pspec_ESratio, type = "long_only")192## Add objectives of maximizing return per unit ES193pspec_ESratio <- add.objective(pspec_ESratio, type = "return", name = "mean")194pspec_ESratio <- add.objective(pspec_ESratio, type = "risk", name = "ES", arguments = list(p=0.05))195196# Optimization197optimize.portfolio(ret_edhec, pspec_ESratio, optimize_method = "CVXR", ESratio = TRUE)198199200## SECTION 8.3201202# Create portfolio object203pspec_CSMratio <- portfolio.spec(assets = fund_edhec)204## Add constraints of maximizing return per unit CSM205pspec_CSMratio <- add.constraint(pspec_CSMratio, type = "full_investment")206pspec_CSMratio <- add.constraint(pspec_CSMratio, type = "long_only")207## Add objectives of maximizing return per unit CSM208pspec_CSMratio <- add.objective(pspec_CSMratio, type = "return", name = "mean")209pspec_CSMratio <- add.objective(pspec_CSMratio, type = "risk", name = "CSM",210arguments = list(p=0.05))211212# Optimization213optimize.portfolio(ret_edhec, pspec_CSMratio, optimize_method = "CVXR", CSMratio = TRUE)214215216## Section 9217218# Get daily returns of the 30 smallcap stocks219library(PCRA)220library(PortfolioAnalytics)221library(xts)222stocksCRSPdaily <- getPCRAData(dataset = "stocksCRSPdaily")223224smallcapTS <- selectCRSPandSPGMI(225periodicity = "daily",226stockItems = c("Date", "TickerLast", "CapGroupLast", "Return"),227factorItems = NULL,228subsetType = "CapGroupLast",229subsetValues = "SmallCap",230outputType = "xts")231232# find top 30 small cap stocks based on the market capitalization233smallcapDT <- factorsSPGMI[CapGroupLast == "SmallCap"]234scSize <- smallcapDT[, mean(LogMktCap), by = "TickerLast"]235names(scSize)[2] <- "Size"236scSize <- scSize[order(scSize$Size, decreasing = TRUE),]237sc30largest <- scSize[,TickerLast][1:30]238239# daily return of top 30 stocks240retD_CRSP <- smallcapTS[ , sc30largest]241242names(retD_CRSP)243244# monthly return of top 30 stocks needed for monthly rebalancing245ep <- endpoints(retD_CRSP, on= "months", k=1)246prod1 <- function(x){apply(x+1, 2, prod)}247retM_CRSP <- period.apply(retD_CRSP, INDEX = ep, FUN = prod1) - 1248249250## SECTION 9.1251252# Generate GMV, GMES and GMCSM portfolios253pspec_sc <- portfolio.spec(assets = sc30largest)254pspec_sc <- add.constraint(pspec_sc, type = "full_investment")255pspec_sc <- add.constraint(pspec_sc, type = "long_only")256257pspec_GMV <- add.objective(pspec_sc, type = "risk", name = "var")258pspec_GMES <- add.objective(pspec_sc, type = "risk", name = "ES")259pspec_GMCSM <- add.objective(pspec_sc, type = "risk", name = "CSM")260261# Optimize Portfolio at Monthly Rebalancing and 500-Day Training262bt.GMV <- optimize.portfolio.rebalancing(retD_CRSP, pspec_GMV,263optimize_method = "CVXR",264rebalance_on = "months",265rolling_window = 500)266bt.ES <- optimize.portfolio.rebalancing(retD_CRSP, pspec_GMES,267optimize_method = "CVXR",268rebalance_on = "months",269rolling_window = 500)270bt.CSM <- optimize.portfolio.rebalancing(retD_CRSP, pspec_GMCSM,271optimize_method = "CVXR",272rebalance_on = "months",273rolling_window = 500)274275# Extract time series of portfolio weights276wts.GMV <- extractWeights(bt.GMV)277wts.GMV <- wts.GMV[complete.cases(wts.GMV),]278279wts.ES <- extractWeights(bt.ES)280wts.ES <- wts.ES[complete.cases(wts.ES),]281282wts.CSM <- extractWeights(bt.CSM)283wts.CSM <- wts.CSM[complete.cases(wts.CSM),]284285# Compute cumulative returns of three portfolios286GMV <- Return.rebalancing(retM_CRSP, wts.GMV)287ES <- Return.rebalancing(retM_CRSP, wts.ES)288CSM <- Return.rebalancing(retM_CRSP, wts.CSM)289290# Combine GMV, ES and CSM portfolio cumulative returns291ret.comb <- na.omit(merge(GMV, ES, CSM, all=F))292names(ret.comb) <- c("GMV", "GMES", "GMCSM")293294backtest.plot(ret.comb, colorSet = c("black", "darkblue", "darkgreen"), ltySet = c(3, 2, 1))295296297## SECTION 9.2298299# Generate Sr, ESr and CSMr portfolios300pspec_sc_ratio <- add.objective(pspec_sc, type = "return", name = "mean")301pspec_Sr <- add.objective(pspec_sc_ratio, type = "risk", name = "var")302pspec_ESr <- add.objective(pspec_sc_ratio, type = "risk", name = "ES")303pspec_CSMr <- add.objective(pspec_sc_ratio, type = "risk", name = "CSM")304305# Optimize Portfolio at Monthly Rebalancing and 500-Day Training306bt.Sr <- optimize.portfolio.rebalancing(retD_CRSP, pspec_Sr, maxSR = TRUE,307optimize_method = "CVXR",308rebalance_on = "months",309rolling_window = 500)310bt.ESr <- optimize.portfolio.rebalancing(retD_CRSP, pspec_ESr,311optimize_method = "CVXR",312rebalance_on = "months",313rolling_window = 500)314bt.CSMr <- optimize.portfolio.rebalancing(retD_CRSP, pspec_CSMr,315optimize_method = "CVXR",316rebalance_on = "months",317rolling_window = 500)318319# Extract time series of portfolio weights320wts.Sr <- extractWeights(bt.Sr)321wts.Sr <- wts.Sr[complete.cases(wts.Sr),]322323wts.ESr <- extractWeights(bt.ESr)324wts.ESr <- wts.ESr[complete.cases(wts.ESr),]325326wts.CSMr <- extractWeights(bt.CSMr)327wts.CSMr <- wts.CSMr[complete.cases(wts.CSMr),]328329# Compute cumulative returns of three portfolios330Sr <- Return.rebalancing(retM_CRSP, wts.Sr, rebalance_on = "months")331ESr <- Return.rebalancing(retM_CRSP, wts.ESr, rebalance_on = "months")332CSMr <- Return.rebalancing(retM_CRSP, wts.CSMr, rebalance_on = "months")333334# Combine Sr, ESr and CSMr portfolio cumulative returns335ret.comb.ratios <- na.omit(merge(Sr, ESr, CSMr, all=F))336names(ret.comb.ratios) <- c("Sharpe ratio", "ES ratio", "CSM ratio")337338backtest.plot(ret.comb.ratios, colorSet = c("black", "darkblue", "darkgreen"), ltySet = c(3, 2, 1))339340341## SECTION 10342343# monthly return of top 30 stocks in last 5 years344ep <- endpoints(retD_CRSP, on= "months", k=1)345prod1 <- function(x){apply(x+1, 2, prod)}346retM_CRSP <- period.apply(retD_CRSP, INDEX = ep, FUN = prod1) - 1347retM_CRSP_5 <- tail(retM_CRSP, 60)348349tsPlotMP(retM_CRSP_5, layout = c(2,15), yname = "RETURNS",350stripText.cex = 0.7, axis.cex = 0.7)351352353## SECTION 10.1354355# mean-var efficient frontier356pspec_sc <- portfolio.spec(names(retM_CRSP_5))357pspec_sc <- add.constraint(pspec_sc, type = "full_investment")358pspec_sc <- add.constraint(pspec_sc, type = "long_only")359360meanvar.ef <- create.EfficientFrontier(R = retM_CRSP_5,361portfolio = pspec_sc, type = "mean-StdDev")362363chart.EfficientFrontier(meanvar.ef, match.col = "StdDev", type = "l",364chart.assets = FALSE, main = NULL,365RAR.text = "Max Sharpe ratio", pch = 1)366367meanvar.ef$frontier[, 1:2]368369sr <- meanvar.ef$frontier[, 1]/meanvar.ef$frontier[, 2]370maximumSR <- max(sr)371meanMaxSR <- meanvar.ef$frontier[, 1][sr == max(sr)]372stdevMaxSR <- meanvar.ef$frontier[, 2][sr == max(sr)]373dat <- (round(c(maximumSR, meanMaxSR, stdevMaxSR), 3))374dat <- data.frame(dat)375names(dat) <- NULL376row.names(dat) <- c("maximum SR", "maxSRport Mean", "maxSRport Stdev")377dat378379# Mean-StdDev Efficient Frontier380pspec_MV <- add.objective(pspec_sc, type = "risk", name = "var")381pspec_MV <- add.objective(portfolio = pspec_MV, type = "return", name = "mean")382opt_MV <- optimize.portfolio(retM_CRSP_5, pspec_MV, optimize_method = "CVXR", maxSR = TRUE)383opt.outputMvo(opt_MV, retM_CRSP_5, annualize = FALSE, digits = 3)384385386pspec_sc_init <- portfolio.spec(assets = sc30largest)387pspec_sc_init <- add.constraint(pspec_sc_init, type = "full_investment")388389# Portfolio with long-only constraints390pspec_sc_lo <- add.constraint(portfolio = pspec_sc_init, type = "long_only")391392# Portfolio with long-only box constraints393pspec_sc_lobox <- add.constraint(portfolio = pspec_sc_init, type = "box",394min = 0.02, max = 0.1)395396# Portfolio with long-short box constraints397pspec_sc_lsbox <- add.constraint(portfolio = pspec_sc_init, type = "box",398min = -0.1, max = 0.1)399400# Combine the portfolios into a list401portf_list <- combine.portfolios(list(pspec_sc_lo, pspec_sc_lobox, pspec_sc_lsbox))402403# Plot the efficient frontier overlay of the portfolios with varying constraints404legend_labels <- c("Long Only", "Long Only Box (0.02,0.1)", "Long Short Box (-0.01,0.1)")405chart.EfficientFrontierOverlay(R = retM_CRSP_5, portfolio_list = portf_list,406type = "mean-StdDev", match.col = "StdDev",407legend.loc = "bottomright", chart.assets = FALSE,408legend.labels = legend_labels, cex.legend = 1,409labels.assets = FALSE, lwd = c(3,3,3),410col = c("black", "dark red", "dark green"),411main = NULL,412xlim = c(0.03, 0.11), ylim = c(0.005, 0.035))413414415## SECTION 10.2416417# Mean-ES Efficient Frontier418meanes.ef <- create.EfficientFrontier(R = retM_CRSP_5, portfolio = pspec_sc, type = "mean-ES")419chart.EfficientFrontier(meanes.ef, match.col = "ES", type = "l",420chart.assets = FALSE, main = NULL,421RAR.text = "Max ES ratio", pch = 1)422423legend_labels <- c("Long Only ES (p=0.05)",424"Long Only Box ES (p=0.05)", "Long Short Box ES (p=0.05)")425chart.EfficientFrontierOverlay(R = retM_CRSP_5, portfolio_list = portf_list,426type = "mean-ES", match.col = "ES",427legend.loc = "bottomright", chart.assets = FALSE,428legend.labels = legend_labels, cex.legend = 1,429labels.assets = FALSE, lwd = c(3,3,3),430col = c("black", "dark red", "dark green"),431main = NULL,432xlim = c(0.03, 0.17), ylim = c(0.005, 0.035))433434# Create long-only ES portfolios with different tail probabilities435ES_05 <- add.objective(portfolio = pspec_sc_lo, type = "risk", name = "ES",436arguments = list(p=0.05))437438ES_10 <- add.objective(portfolio = pspec_sc_lo, type = "risk", name = "ES",439arguments = list(p=0.1))440441ES_15 <- add.objective(portfolio = pspec_sc_lo, type = "risk", name = "ES",442arguments = list(p=0.15))443444# Combine the portfolios into a list445portf_ES_list <- combine.portfolios(list(ES_05, ES_10, ES_15))446447# Plot the efficient frontier overlay of the portfolios with varying tail probabilities448legend_ES_labels <- c("ES (p=0.05)", "ES (p=0.1)", "ES (p=0.15)")449chart.EfficientFrontierOverlay(R = retM_CRSP_5, portfolio_list = portf_ES_list,450type = "mean-ES", match.col = "ES",451legend.loc = "bottomright", chart.assets = FALSE,452legend.labels = legend_ES_labels, cex.legend = 1,453labels.assets = FALSE, lwd = c(3,3,3),454col = c("black", "dark red", "dark green"),455main = NULL,456xlim = c(0.035, 0.165), ylim = c(0.005, 0.03))457458459## SECTION 10.3460461# Mean-CSM Efficient Frontier462meancsm.ef <- create.EfficientFrontier(R = retM_CRSP_5, portfolio = pspec_sc,463type = "mean-CSM")464chart.EfficientFrontier(meancsm.ef, match.col = "CSM", type = "l",465chart.assets = FALSE, main = NULL,466RAR.text = "Max CSM ratio", pch = 1)467468469## SECTION 11.1470471args(chart.EfficientFrontierCompare)472473# Compare StdDev of minStd and minES portfolios with guideline474chart.EfficientFrontierCompare(R = retM_CRSP_5, portfolio = pspec_sc, risk_type = "StdDev", match.col = c("StdDev", "ES"), lwd = c(2, 2), xlim = c(0.0,0.14), ylim = c(0.005,0.032))475476# Compare ES of minStd and minES portfolios with guideline477chart.EfficientFrontierCompare(R = retM_CRSP_5, portfolio = pspec_sc,478risk_type = "ES", match.col = c("ES", "StdDev"), lwd = c(2, 2),479xlim = c(0.0,0.14), ylim = c(0.005,0.032))480481# Compare ES of minStd, minES and minCSM portfolios without guideline482chart.EfficientFrontierCompare(R = retM_CRSP_5, portfolio = pspec_sc,483risk_type = "ES",match.col = c("StdDev", "ES", "CSM"),484guideline = FALSE, col = c("darkred","darkgreen","blue"),485lty = c("dotted", "solid", "dashed"), lwd = c(1, 1.5, 1.5))486487488## SECTION 11.2489490minStdDev_port <- add.objective(pspec_sc, type = "risk", name = "StdDev")491minStdDev_opt <- optimize.portfolio(retM_CRSP_5, minStdDev_port,492optimize_method = "CVXR")493minStdDev_w <- minStdDev_opt$weight494495minES_port <- add.objective(pspec_sc, type = "risk", name = "ES")496minES_opt <- optimize.portfolio(retM_CRSP_5, minES_port,497optimize_method = "CVXR")498minES_w <- minES_opt$weight499500# Extract risk StdDev portfolio501extract_risk(retM_CRSP_5, minStdDev_w)502503# Extract risk ES portfolio504extract_risk(retM_CRSP_5, minES_w)505506507minStdDev_opt_covRob <-optimize.portfolio(retM_CRSP_5,minStdDev_port,508optimize_method =509"CVXR", momentFUN = 'custom.covRob.Mcd')510extract_risk(retM_CRSP_5, minStdDev_w, moment_setting =511minStdDev_opt_covRob$moment_values)512513514## SECTION 12515516# Compare mean-var frontiers with classic and robust517# covariance matrix estimators.518sampleCov = meanvar.efficient.frontier(pspec_sc, retM_CRSP_5, optimize_method = 'CVXR')519robustCov = meanvar.efficient.frontier(pspec_sc, retM_CRSP_5, optimize_method = 'CVXR', momentFUN = 'custom.covRob.TSGS')520plotFrontiers(retM_CRSP_5, frontiers=list(sampleCov, robustCov), risk='StdDev')521522523524