Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
robustport committed Oct 16, 2024
2 parents 1d35a3b + cba8651 commit 84fb197
Show file tree
Hide file tree
Showing 6 changed files with 545 additions and 416 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Authors@R: c(
person(given="Xiaokang",family="Feng",role="ctb") ,
person(given="Yifu",family="Kang",role="ctb") )
Version: 3.0.0
Date: 2024-08-20
Date: 2024-10-11
Maintainer: Brian G. Peterson <brian@braverock.com>
Description: Portfolio optimization and analysis routines and graphics.
Depends:
Expand Down
6 changes: 6 additions & 0 deletions README → README.Rmd
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
title: "README"
output: github_document
---

This V2.1 version of PortfolioAnalytics is an update to the substantial V2.0
version that was released on 2024-07-03. We first describe the V2.0 features,
then discuss the R demo capability, and finally we describe the additional
Expand Down Expand Up @@ -99,3 +104,4 @@ along with contributions from Yifu Kang, under the support of a 2022 Google
Summer of Code (GSOC 2022). Xinran and Yifu were mentored in GSOC 2022 by
Professor Doug Martin and Professor Steve Murray in the Applied Mathematics
Department at the University of Washington.

105 changes: 105 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
README
================

This V2.1 version of PortfolioAnalytics is an update to the substantial
V2.0 version that was released on 2024-07-03. We first describe the V2.0
features, then discuss the R demo capability, and finally we describe
the additional V2.1 features.

# 2.0 Features

A major feature of 2.0 is the integration of the CVXR solver R package
for convex optimization. CVXR supports eleven solver packages, each of
which supports solvers for one or more of the following optimization
problems: LP, QP, SOCP, SDP, EXP, MIP. See the Table near the beginning
of the document “Convex Optimization in R” at <https://cvxr.rbind.io/>.
Thus, with PortfolioAnalytics 2.0, users are able to use any one of a
variety of solvers available in CVXR for their portfolio optimization
problems.

A particular use of CVXR in PortfolioAnalytics 2.0 is for computing
Minimum Coherent Second Moment (MinCSM) portfolios, which are
second-order cone programming (SOCP) optimization problems. This is
quite a new capability that is not available in other portfolio
optimization software products. Details are provided in the Vignette
“cvxrPortfolioAnalytics”.

Another important feature of PortfolioAnalytics 2.0, is that it contains
functionality for computing outliers-robust minimum variance (MV)
optimal portfolios based on any one of several robust covariance matrix
estimators that are not much influenced by outliers Details are provided
in the Vignette “robustCovMatForPA”.

New PortfolioAnalytics Functions:

1. meancsm.efficient.frontier (create Mean-CSM efficient frontier)
utility function
2. meanrisk.efficient.frontier (generate multiple efficient frontiers
for portfolios with the same constraint object.
3. extract_risk (extract the risk value, e.g., StdDev or ES or CSM,
based on the weights of a portfolio)
4. chart.EfficientFrontierCompare (Overlay the efficient frontiers of
different minRisk portfolio objects on a single plot)
5. backtest.plot (based on Peter Carl’s code, generate plots of the
cumulative returns and/or drawdown for back-testing)
6. opt.outputMvo (converts output of `optimize.portfolio` to a list of
the portfolio weights, mean, volatility and Sharpe Ratio)
7. plotFrontiers (plot frontiers based on the result of
`meanvar.efficient.frontier`, `meanetl.efficient.frontier` or
`meancsm.efficient.frontier`)

Enhanced PortfolioAnalytics Functions:

1. optimize.portfolio (enhanced with CVXR solvers, CSM objective,
customizable arg `momentFUN=` and output `~$moment_values`)
2. optimize.portfolio.rebalancing (enhanced with CVXR solvers, CSM
objective and customizable arg `momentFUN=`)
3. create.EfficientFrontier (enhanced with type `mean-CSM` and
`mean-risk`, and customizable arg `momentFUN=`)

Support S3 Methods for CVXR:

1. print.optimize.portfolio.CVXR
2. extractStats.optimize.portfolio.CVXR

Custom Moment Functions for Robust Covariance Matrices:

1. custom.covRob.MM
2. custom.covRob.Rocke
3. custom.covRob.Mcd
4. custom.covRob.TSGS
5. MycovRobMcd
6. MycovRobTSGS

New Vignettes and their Code Functions in the demo Folder:

1. cvxrPortfolioAnalytics: CRAN title = “CVXR for PortfolioAnalytics”.
2. demo_cvxrPortfolioAnalytics.R
3. robustCovMatForPA: CRAN title = “Robust Covariance Matrices for
PortfolioAnalytics”
4. demo_robustCovMatForPA.R

# New 2.1 Features

xxx

# Bug Reportin

Please contribute with bug fixes, comments, and testing scripts!

Please take your data and disguise it when submitting, or use data sets
like “edhec” like we do in the demos or or like “stocksCRSP” and
“factorsSPGMI” in the PCRA package or with your constraints and other
objectives modified to demonstrate your problem on public data.

Please report any bugs or issues on the PortfolioAnalytics GitHub page
at <https://github.com/braverock/PortfolioAnalytics/issues>

# Acknowledgements

The bulk of the work in creating PortfolioAnalytics 2.0 was done by
Xinran Zhao, along with contributions from Yifu Kang, under the support
of a 2022 Google Summer of Code (GSOC 2022). Xinran and Yifu were
mentored in GSOC 2022 by Professor Doug Martin and Professor Steve
Murray in the Applied Mathematics Department at the University of
Washington.
File renamed without changes.
125 changes: 71 additions & 54 deletions demo/demo_cvxrPortfolioAnalytics.R
Original file line number Diff line number Diff line change
Expand Up @@ -153,20 +153,20 @@ opt_es_1 <- optimize.portfolio(ret_edhec, pspec_es_1, optimize_method = "CVXR")
opt_es_1


#' 7 MINIMIZING EXPECTED QUADRATIC SHORTFALL
#' 7 MINIMIZING COHERENT SECOND MOMENT


#' Generate min-EQS portfolio
pspec_eqs <- portfolio.spec(assets = fund_edhec)
pspec_eqs <- add.constraint(pspec_eqs, type = "full_investment")
pspec_eqs <- add.constraint(pspec_eqs, type = "long_only")
#' Add objective of minimizing EQS
pspec_eqs <- add.objective(portfolio = pspec_eqs, type = "risk", name = "EQS",
#' Generate min-CSM portfolio
pspec_csm <- portfolio.spec(assets = fund_edhec)
pspec_csm <- add.constraint(pspec_csm, type = "full_investment")
pspec_csm <- add.constraint(pspec_csm, type = "long_only")
#' Add objective of minimizing CSM
pspec_csm <- add.objective(portfolio = pspec_csm, type = "risk", name = "CSM",
arguments = list(p=0.05))

#' GMEQS with default gamma=0.05
opt_eqs <- optimize.portfolio(ret_edhec, pspec_eqs, optimize_method = "CVXR")
opt_eqs
#' GMCSM with default gamma=0.05
opt_csm <- optimize.portfolio(ret_edhec, pspec_csm, optimize_method = "CVXR")
opt_csm


#' 8 MAXIMIZING MEAN RETURN PER UNIT RISK
Expand Down Expand Up @@ -198,17 +198,17 @@ pspec_ESratio <- add.objective(pspec_ESratio, type = "risk", name = "ES",
optimize.portfolio(ret_edhec, pspec_ESratio, optimize_method = "CVXR", ESratio = TRUE)

#' Create portfolio object
pspec_EQSratio <- portfolio.spec(assets = fund_edhec)
#' Add constraints of maximizing return per unit EQS
pspec_EQSratio <- add.constraint(pspec_EQSratio, type = "full_investment")
pspec_EQSratio <- add.constraint(pspec_EQSratio, type = "long_only")
#' Add objectives of maximizing return per unit EQS
pspec_EQSratio <- add.objective(pspec_EQSratio, type = "return", name = "mean")
pspec_EQSratio <- add.objective(pspec_EQSratio, type = "risk", name = "EQS",
pspec_CSMratio <- portfolio.spec(assets = fund_edhec)
#' Add constraints of maximizing return per unit CSM
pspec_CSMratio <- add.constraint(pspec_CSMratio, type = "full_investment")
pspec_CSMratio <- add.constraint(pspec_CSMratio, type = "long_only")
#' Add objectives of maximizing return per unit CSM
pspec_CSMratio <- add.objective(pspec_CSMratio, type = "return", name = "mean")
pspec_CSMratio <- add.objective(pspec_CSMratio, type = "risk", name = "CSM",
arguments = list(p=0.05))

#' Optimization
optimize.portfolio(ret_edhec, pspec_EQSratio, optimize_method = "CVXR", EQSratio = TRUE)
optimize.portfolio(ret_edhec, pspec_CSMratio, optimize_method = "CVXR", CSMratio = TRUE)


#' 9 COMPARATIVE PERFORMANCE OF PORTFOLIOS
Expand Down Expand Up @@ -244,16 +244,16 @@ retM_CRSP_5 <- tail(retM_CRSP, 60)
#' time series plot of 10 stocks
tsPlotMP(retM_CRSP_5[, 1:10])

#' Test run time for Backtesting with GMV, GMES, GMEQS portfolios
#' Test run time for Backtesting with GMV, GMES, GMCSM portfolios
start_time1 <- Sys.time()

#' Generate GMV, GMES and GMEQS portfolios
#' Generate GMV, GMES and GMCSM portfolios
pspec_sc <- portfolio.spec(assets = sc30largest)
pspec_sc <- add.constraint(pspec_sc, type = "full_investment")
pspec_sc <- add.constraint(pspec_sc, type = "long_only")
pspec_GMV <- add.objective(pspec_sc, type = "risk", name = "var")
pspec_GMES <- add.objective(pspec_sc, type = "risk", name = "ES")
pspec_GMEQS <- add.objective(pspec_sc, type = "risk", name = "EQS")
pspec_GMCSM <- add.objective(pspec_sc, type = "risk", name = "CSM")

#' Optimize Portfolio at Monthly Rebalancing and 500-Day Training
bt.GMV <- optimize.portfolio.rebalancing(retD_CRSP, pspec_GMV,
Expand All @@ -266,7 +266,7 @@ bt.ES <- optimize.portfolio.rebalancing(retD_CRSP, pspec_GMES,
rebalance_on = "months",
training_period = 30,
rolling_window = 500)
bt.EQS <- optimize.portfolio.rebalancing(retD_CRSP, pspec_GMEQS,
bt.CSM <- optimize.portfolio.rebalancing(retD_CRSP, pspec_GMCSM,
optimize_method = "CVXR",
rebalance_on = "months",
training_period = 30,
Expand All @@ -277,34 +277,34 @@ wts.GMV <- extractWeights(bt.GMV)
wts.GMV <- wts.GMV[complete.cases(wts.GMV),]
wts.ES <- extractWeights(bt.ES)
wts.ES <- wts.ES[complete.cases(wts.ES),]
wts.EQS <- extractWeights(bt.EQS)
wts.EQS <- wts.EQS[complete.cases(wts.EQS),]
wts.CSM <- extractWeights(bt.CSM)
wts.CSM <- wts.CSM[complete.cases(wts.CSM),]

#' Compute cumulative returns of three portfolios
GMV <- Return.rebalancing(retM_CRSP, wts.GMV)
ES <- Return.rebalancing(retM_CRSP, wts.ES)
EQS <- Return.rebalancing(retM_CRSP, wts.EQS)
CSM <- Return.rebalancing(retM_CRSP, wts.CSM)

#' Combine GMV, ES and EQS portfolio cumulative returns
ret.comb <- na.omit(merge(GMV, ES, EQS, all=F))
names(ret.comb) <- c("GMV", "GMES", "GMEQS")
#' Combine GMV, ES and CSM portfolio cumulative returns
ret.comb <- na.omit(merge(GMV, ES, CSM, all=F))
names(ret.comb) <- c("GMV", "GMES", "GMCSM")

backtest.plot(ret.comb, colorSet = c("black", "darkblue", "darkgreen"),
ltySet = c(3, 2, 1))

#' Return run-time for Backtesting with GMV, GMES, GMEQS portfolios
#' Return run-time for Backtesting with GMV, GMES, GMCSM portfolios
end_time1 <- Sys.time()
runningtime1 <- end_time1 - start_time1
cat("The run time for Figure 9.2 is", format(round(runningtime1, 2)))

#' Test run-time for Backtesting with SR, ESratio, EQSratio portfolios
#' Test run-time for Backtesting with SR, ESratio, CSMratio portfolios
start_time2 <- Sys.time()

#' Generate GMV, GMES and GMEQS portfolios
#' Generate GMV, GMES and GMCSM portfolios
pspec_sc_ratio <- add.objective(pspec_sc, type = "return", name = "mean")
pspec_Sr <- add.objective(pspec_sc_ratio, type = "risk", name = "var")
pspec_ESr <- add.objective(pspec_sc_ratio, type = "risk", name = "ES")
pspec_EQSr <- add.objective(pspec_sc_ratio, type = "risk", name = "EQS")
pspec_CSMr <- add.objective(pspec_sc_ratio, type = "risk", name = "CSM")

#' Optimize Portfolio at Monthly Rebalancing and 500-Day Training
bt.Sr <- optimize.portfolio.rebalancing(retD_CRSP, pspec_Sr, maxSR = TRUE,
Expand All @@ -317,7 +317,7 @@ bt.ESr <- optimize.portfolio.rebalancing(retD_CRSP, pspec_ESr,
rebalance_on = "months",
training_period = 30,
rolling_window = 500)
bt.EQSr <- optimize.portfolio.rebalancing(retD_CRSP, pspec_EQSr,
bt.CSMr <- optimize.portfolio.rebalancing(retD_CRSP, pspec_CSMr,
optimize_method = "CVXR",
rebalance_on = "months",
training_period = 30,
Expand All @@ -328,21 +328,21 @@ wts.Sr <- extractWeights(bt.Sr)
wts.Sr <- wts.Sr[complete.cases(wts.Sr),]
wts.ESr <- extractWeights(bt.ESr)
wts.ESr <- wts.ESr[complete.cases(wts.ESr),]
wts.EQSr <- extractWeights(bt.EQSr)
wts.EQSr <- wts.EQSr[complete.cases(wts.EQSr),]
wts.CSMr <- extractWeights(bt.CSMr)
wts.CSMr <- wts.CSMr[complete.cases(wts.CSMr),]

#' Compute cumulative returns of three portfolios
Sr <- Return.rebalancing(retM_CRSP, wts.Sr, rebalance_on = "months")
ESr <- Return.rebalancing(retM_CRSP, wts.ESr, rebalance_on = "months")
EQSr <- Return.rebalancing(retM_CRSP, wts.EQSr, rebalance_on = "months")
CSMr <- Return.rebalancing(retM_CRSP, wts.CSMr, rebalance_on = "months")

#' Combine Sr, ESr and EQSr portfolio cumulative returns
ret.comb <- na.omit(merge(Sr, ESr, EQSr, all=F))
names(ret.comb) <- c("Sharpe ratio", "ES ratio", "EQS ratio")
#' Combine Sr, ESr and CSMr portfolio cumulative returns
ret.comb <- na.omit(merge(Sr, ESr, CSMr, all=F))
names(ret.comb) <- c("Sharpe ratio", "ES ratio", "CSM ratio")
backtest.plot(ret.comb, colorSet = c("black", "darkblue", "darkgreen"),
ltySet = c(3, 2, 1))

#' Return run-time for Backtesting with SR, ESratio, EQSratio portfolios
#' Return run-time for Backtesting with SR, ESratio, CSMratio portfolios
end_time2 <- Sys.time()
runningtime2 <- end_time2 - start_time2
cat("The run time for Figure 9.3 is", format(round(runningtime2, 2)))
Expand Down Expand Up @@ -444,29 +444,46 @@ chart.EfficientFrontierOverlay(R = retM_CRSP_5, portfolio_list = portf_ES_list,
main = "Overlay Mean-ES Efficient Frontiers",
xlim = c(0.035, 0.165), ylim = c(0.005, 0.03))

#' Mean-EQS Efficient Frontier
meaneqs.ef <- create.EfficientFrontier(R = retM_CRSP_5, portfolio = pspec_sc,
type = "mean-EQS")
chart.EfficientFrontier(meaneqs.ef, match.col = "EQS", type = "l",
chart.assets = FALSE, main = "Mean-EQS Efficient Frontier",
RAR.text = "EQS ratio", pch = 1)
#' Mean-CSM Efficient Frontier
meancsm.ef <- create.EfficientFrontier(R = retM_CRSP_5, portfolio = pspec_sc,
type = "mean-CSM")
chart.EfficientFrontier(meancsm.ef, match.col = "CSM", type = "l",
chart.assets = FALSE, main = "Mean-CSM Efficient Frontier",
RAR.text = "CSM ratio", pch = 1)

#' usage example: minStd Portfolio
#' minStd Portfolio
minstd_port <- add.objective(pspec_sc, type = "risk", name = "StdDev")
minstd_w <- optimize.portfolio(retM_CRSP_5, minstd_port, optimize_method = "CVXR")$weight
minstd_opt <- optimize.portfolio(retM_CRSP_5, minstd_port, optimize_method = "CVXR")
minstd_w <- minstd_opt$weight

#' risk values with default alpha = 0.05
#' extract risk example 1: risk values with default alpha = 0.05
extract_risk(retM_CRSP_5, minstd_w)

#' risk values with specific alpha
extract_risk(retM_CRSP_5, minstd_w, ES_alpha = 0.1, EQS_alpha = 0.1)
#' extract risk example 2: risk values with specific alpha
extract_risk(retM_CRSP_5, minstd_w, ES_alpha = 0.1, CSM_alpha = 0.1)

#' example 1: Compare StdDev of minStd and minES portfolios with guideline
#' extract risk example 3: risk values with customized momentFUN
minstd_opt_custM <- optimize.portfolio(retM_CRSP_5, minstd_port, optimize_method = "CVXR",
momentFUN = 'custom.covRob.Mcd')
extract_risk(retM_CRSP_5, minstd_w, moment_setting = minstd_opt_custM$moment_values)

#' EFCompare example 1: Compare StdDev of minStd and minES portfolios with guideline
chart.EfficientFrontierCompare(R = retM_CRSP_5, portfolio = pspec_sc, risk_type = "StdDev",
match.col = c("StdDev", "ES"), lwd = c(2, 2))

#' example 2: Compare ES of minStd, minES and minEQS portfolios without guideline
#' EFCompare example 2: Compare ES of minStd, minES and minCSM portfolios without guideline
chart.EfficientFrontierCompare(R = retM_CRSP_5, portfolio = pspec_sc, risk_type = "ES",
match.col = c("StdDev", "ES", "EQS"), guideline = FALSE,
match.col = c("StdDev", "ES", "CSM"), guideline = FALSE,
col = c(1,2,4), lty = c(1, 2, 4), lwd = c(2, 2, 2))

#' plotFrontiers example 1: Compare mean-CSM frontiers for minvar, minES, minCSM portfolio
ef1 = meancsm.efficient.frontier(pspec_sc, retM_CRSP_5, optimize_method = 'CVXR')
ef2 = meanetl.efficient.frontier(pspec_sc, retM_CRSP_5, optimize_method = 'CVXR')
ef3 = meanvar.efficient.frontier(pspec_sc, retM_CRSP_5, optimize_method = 'CVXR')
plotFrontiers(R=retM_CRSP_5, frontiers=list(ef1, ef2, ef3), risk='CSM')

#' plotFrontiers example 2: Compare mean-var frontiers with different moment settings
ef4 = meanvar.efficient.frontier(pspec_sc, retM_CRSP_5, optimize_method = 'CVXR')
ef5 = meanvar.efficient.frontier(pspec_sc, retM_CRSP_5, optimize_method = 'CVXR',
momentFUN = 'custom.covRob.TSGS')
plotFrontiers(R=retM_CRSP_5, frontiers=list(ef4, ef5), risk='StdDev')
Loading

0 comments on commit 84fb197

Please sign in to comment.