Path: blob/master/R/extract.efficient.frontier.R
1433 views
###############################################################################1# R (https://r-project.org/) Numeric Methods for Optimization of Portfolios2#3# Copyright (c) 2004-2023 Brian G. Peterson, Peter Carl, Ross Bennett, Kris Boudt, Xinran Zhao4#5# This library is distributed under the terms of the GNU Public License (GPL)6# for full details see the file COPYING7#8# $Id$9#10###############################################################################111213extract.efficient.frontier <- function (object=NULL, match.col='ES', from=NULL, to=NULL, by=0.005, n.portfolios=NULL, ..., R=NULL, portfolio=NULL, optimize_method='random')14{15#TODO add a threshold argument for how close it has to be to count16# do we need to recalc the constrained_objective too? I don't think so.17if(!inherits(object, "optimize.portfolio")) stop("object passed in must of of class 'portfolio'")1819#set<-seq(from=from,to=to,by=by)20#set<-cbind(quantmod::Lag(set,1),as.matrix(set))[-1,]21if(is.null(object)){22if(!is.null(R) & !is.null(portfolio)){23portfolios<-optimize.portfolio(portfolio=portfolio, R=R, optimize_method=optimize_method[1], trace=TRUE, ...)24} else {25stop('you must specify a portfolio object and a return series or an objective of class optimize.portfolio')26}27}2829xtract<-extractStats(object)30columnnames=colnames(xtract)31# optimal portfolio stats from xtract32opt <- xtract[which.min(xtract[, "out"]),]33#if("package:multicore" %in% search() || requireNamespace("multicore",quietly = TRUE)){34# mclapply35#}36stopifnot("package:foreach" %in% search() || requireNamespace("foreach",quietly = TRUE))37# rtc = pmatch(return.col,columnnames)38# if(is.na(rtc)) {39# rtc = pmatch(paste(return.col,return.col,sep='.'),columnnames)40# }41mtc = pmatch(match.col,columnnames)42if(is.na(mtc)) {43mtc = pmatch(paste(match.col,match.col,sep='.'),columnnames)44}45if(is.na(mtc)) stop("could not match match.col with column name of extractStats output")4647if(is.null(from)){48from <- min(xtract[, mtc])49}50if(is.null(to)){51to <- max(xtract[, mtc])52}53if(!is.null(n.portfolios)){54# create the sequence using length.out if the user has specified a value for the n.portfolios arg55set<-seq(from=from, to=to, length.out=n.portfolios)56} else {57# fall back to using by to create the sequence58set<-seq(from=from, to=to, by=by)59}6061set<-cbind(quantmod::Lag(set,1),as.matrix(set))[-1,]62i <- 163result <- foreach::foreach(i=1:nrow(set),.inorder=TRUE, .combine=rbind, .errorhandling='remove') %do% {64tmp<-xtract[which(xtract[,mtc]>=set[i,1] & xtract[,mtc]<set[i,2]),]65#tmp<-tmp[which.min(tmp[,'out']),]66tmp<-tmp[which.max(tmp[,'mean']),]67#tmp68}69# combine the stats from the optimal portfolio to result matrix70result <- rbind(opt, result)71return(structure(result, class="frontier"))72}7374#' Generate the efficient frontier for a mean-variance portfolio75#'76#' This function generates the mean-variance efficient frontier of a portfolio77#' specifying the constraints and objectives. The \code{portfolio} object78#' should have two objectives: 1) mean and 2) var (or sd or StdDev). If the79#' portfolio object does not contain these objectives, they will be added80#' using default parameters.81#'82#' @param portfolio a portfolio object with constraints created via \code{\link{portfolio.spec}}83#' @param R an xts or matrix of asset returns84#' @param optimize_method the optimize method to get the efficient frontier, default is ROI85#' @param n.portfolios number of portfolios to plot along the efficient frontier86#' @param risk_aversion vector of risk_aversion values to construct the efficient frontier.87#' \code{n.portfolios} is ignored if \code{risk_aversion} is specified and the number88#' of points along the efficient frontier is equal to the length of \code{risk_aversion}.89#' @param \dots passthru parameters to \code{\link{optimize.portfolio}}90#' @return a matrix of objective measure values and weights along the efficient frontier91#' @author Ross Bennett92#' @export93meanvar.efficient.frontier <- function(portfolio, R, optimize_method='CVXR', n.portfolios=25, risk_aversion=NULL, ...){94if(!is.portfolio(portfolio)) stop("portfolio object must be of class 'portfolio'")95# step 1: find the minimum return given the constraints96# step 2: find the maximum return given the constraints97# step 3: 'step' along the returns and run the optimization to calculate98# the weights and objective measures along the efficient frontier99100# Use the portfolio_risk_objective from the portfolio if they have it101# check for a var, StdDev, or sd objective102var_idx <- which(unlist(lapply(portfolio$objectives, function(x) x$name)) %in% c("var", "StdDev", "sd"))103if(length(var_idx) >= 1){104# the portfolio object has a var, StdDev, or sd objective105var_obj <- portfolio$objectives[[var_idx[1]]]106} else {107var_obj <- portfolio_risk_objective(name="var")108}109110hhi_idx <- which(unlist(lapply(portfolio$objectives, function(x) x$name)) == "HHI")111if(length(hhi_idx) >= 1){112# the portfolio object has an HHI objective113hhi_obj <- portfolio$objectives[[hhi_idx[1]]]114} else {115hhi_obj <- NULL116}117118# Clear out the objectives in portfolio and add them here to simplify checks119# and so we can control the optimization along the efficient frontier.120portfolio$objectives <- list()121portfolio$objectives[[1]] <- var_obj122portfolio$objectives[[2]] <- hhi_obj123portfolio <- add.objective(portfolio=portfolio, type="return", name="mean")124125# If the user has passed in a portfolio object with return_constraint, we need to disable it126for(i in 1:length(portfolio$constraints)){127if(inherits(portfolio$constraints[[i]], "return_constraint")){128portfolio$constraints[[i]]$enabled <- FALSE129}130}131132# get the index number of the var objective133var_idx <- which(unlist(lapply(portfolio$objectives, function(x) x$name)) %in% c("var", "StdDev", "sd"))134# get the index number of the mean objective135mean_idx <- which(unlist(lapply(portfolio$objectives, function(x) x$name)) == "mean")136# get the index number of the hhi objective137hhi_idx <- which(unlist(lapply(portfolio$objectives, function(x) x$name)) == "HHI")138139##### get the maximum return #####140141# Disable the risk objective and hhi objective if applicable142portfolio$objectives[[var_idx]]$enabled <- FALSE143if(length(hhi_idx) >= 1) portfolio$objectives[[hhi_idx]]$enabled <- FALSE144145# run the optimization to get the maximum return146tmp <- optimize.portfolio(R=R, portfolio=portfolio, optimize_method=optimize_method, ...=...)147mean_ret <- colMeans(R)148maxret <- extract_risk(R, tmp$weights)$mean149150##### Get the return at the minimum variance portfolio #####151152# Disable the return objective153portfolio$objectives[[mean_idx]]$enabled <- FALSE154155# Enable the risk objective and hhi objective if applicable156portfolio$objectives[[var_idx]]$enabled <- TRUE157if(length(hhi_idx) >= 1) portfolio$objectives[[hhi_idx]]$enabled <- TRUE158159# Run the optimization to get the global minimum variance portfolio with the160# given constraints.161# Do we want to disable the turnover or transaction costs constraints here?162tmp <- optimize.portfolio(R=R, portfolio=portfolio, optimize_method=optimize_method, ...=...)163stats <- extractStats(tmp)164minret <- extract_risk(R, tmp$weights)$mean165166# length.out is the number of portfolios to create167ret_seq <- seq(from=minret, to=maxret, length.out=n.portfolios)168169# Add target return constraint to step along the efficient frontier for target returns170portfolio <- add.constraint(portfolio=portfolio, type="return", return_target=minret, enabled=FALSE)171ret_constr_idx <- which(unlist(lapply(portfolio$constraints, function(x) inherits(x, "return_constraint"))))172173stopifnot("package:foreach" %in% search() || requireNamespace("foreach",quietly = TRUE))174stopifnot("package:iterators" %in% search() || requireNamespace("iterators",quietly = TRUE))175if(!is.null(risk_aversion)){176# Enable the return objective so we are doing quadratic utility177portfolio$objectives[[mean_idx]]$enabled <- TRUE178lambda <- risk_aversion[1]179out <- foreach::foreach(lambda=iterators::iter(risk_aversion), .inorder=TRUE, .combine=rbind, .errorhandling='remove', .packages='PortfolioAnalytics') %dopar% {180portfolio$objectives[[var_idx]]$risk_aversion <- lambda181extractStats(optimize.portfolio(R=R, portfolio=portfolio, optimize_method=optimize_method, ...=...))182}183out <- cbind(out, risk_aversion)184colnames(out) <- c(names(stats), "lambda")185} else {186# Enable the return constraint187portfolio$constraints[[ret_constr_idx]]$enabled <- TRUE188ret <- ret_seq[1]189out <- foreach::foreach(ret=iterators::iter(ret_seq), .inorder=TRUE, .combine=rbind, .errorhandling='remove', .packages='PortfolioAnalytics') %dopar% {190portfolio$constraints[[ret_constr_idx]]$return_target <- ret191opt <- optimize.portfolio(R=R, portfolio=portfolio, optimize_method=optimize_method, ...=...)192c(sum(extractWeights(opt) * mean_ret), extractStats(opt))193}194colnames(out) <- c("mean", names(stats))195}196out <- na.omit(out)197return(structure(out, class="frontier"))198}199200#' Generate the efficient frontier for a mean-etl portfolio201#'202#' This function generates the mean-ETL efficient frontier of a portfolio203#' specifying the constraints and objectives. The \code{portfolio} object204#' should have two objectives: 1) mean and 2) ES (or ETL or cVaR). If the205#' portfolio object does not contain these objectives, they will be added206#' using default parameters.207#'208#' @param portfolio a portfolio object with constraints and objectives created via \code{\link{portfolio.spec}}209#' @param R an xts or matrix of asset returns210#' @param optimize_method the optimize method to get the efficient frontier, default is ROI211#' @param n.portfolios number of portfolios to generate the efficient frontier212#' @param \dots passthru parameters to \code{\link{optimize.portfolio}}213#' @return a matrix of objective measure values and weights along the efficient frontier214#' @author Ross Bennett215#' @export216meanetl.efficient.frontier <- meanes.efficient.frontier <- function(portfolio, R, optimize_method='CVXR', n.portfolios=25, ...){217if(!is.portfolio(portfolio)) stop("portfolio object must be of class 'portfolio'")218# step 1: find the minimum return given the constraints219# step 2: find the maximum return given the constraints220# step 3: 'step' along the returns and run the optimization to calculate221# the weights and objective measures along the efficient frontier222223# Use the portfolio_risk_objective from the portfolio if they have it224# check for a ETL, ES, or cVaR objective225etl_idx <- which(unlist(lapply(portfolio$objectives, function(x) x$name)) %in% c("ETL", "ES", "CVaR"))226if(length(etl_idx) >= 1){227# the portfolio object has a ETL, ES, CVaR objective228etl_obj <- portfolio$objectives[[etl_idx[1]]]229} else {230etl_obj <- portfolio_risk_objective(name="ES", arguments=list(p=0.95))231}232233# Clear out the objectives in portfolio and add them here to simplify checks234# and so we can control the optimization along the efficient frontier.235portfolio$objectives <- list()236portfolio$objectives[[1]] <- etl_obj237portfolio <- add.objective(portfolio=portfolio, type="return", name="mean")238239# get the objective names from the portfolio object240objnames <- unlist(lapply(portfolio$objectives, function(x) x$name))241242# If the user has passed in a portfolio object with return_constraint, we need to disable it243for(i in 1:length(portfolio$constraints)){244if(inherits(portfolio$constraints[[i]], "return_constraint")){245portfolio$constraints[[i]]$enabled <- FALSE246}247}248249# get the index number of the etl objective250etl_idx <- which(objnames %in% c("ETL", "ES", "CVaR"))251# get the index number of the mean objective252mean_idx <- which(objnames == "mean")253254# create a temporary portfolio to find the max mean return255ret_obj <- return_objective(name="mean")256tportf <- insert_objectives(portfolio, list(ret_obj))257258# run the optimization to get the maximum return259tmp <- optimize.portfolio(R=R, portfolio=tportf, optimize_method=optimize_method, ...)260maxret <- extractObjectiveMeasures(tmp)$mean261262# run the optimization to get the return at the min ETL portfolio263tmp <- optimize.portfolio(R=R, portfolio=portfolio, optimize_method=optimize_method, ef=TRUE, ...)264stats <- extractStats(tmp)265minret <- stats["mean"]266267# length.out is the number of portfolios to create268ret_seq <- seq(from=minret, to=maxret, length.out=n.portfolios)269270# out <- matrix(0, nrow=length(ret_seq), ncol=length(extractStats(tmp)))271# for(i in 1:length(ret_seq)){272# portfolio$objectives[[mean_idx]]$target <- ret_seq[i]273# out[i, ] <- extractStats(optimize.portfolio(R=R, portfolio=portfolio, optimize_method="ROI"))274# }275stopifnot("package:foreach" %in% search() || requireNamespace("foreach",quietly = TRUE))276stopifnot("package:iterators" %in% search() || requireNamespace("iterators",quietly = TRUE))277ret <- ret_seq[1]278out <- foreach::foreach(ret=iterators::iter(ret_seq), .inorder=TRUE, .combine=rbind, .errorhandling='remove', .packages='PortfolioAnalytics') %dopar% {279portfolio$objectives[[mean_idx]]$target <- ret280extractStats(optimize.portfolio(R=R, portfolio=portfolio, optimize_method=optimize_method, ef=TRUE, ...=...))281}282colnames(out) <- names(stats)283out <- na.omit(out)284return(structure(out, class="frontier"))285}286287#' Generate the efficient frontier for a mean-CSM portfolio288#'289#' This function generates the mean-CSM efficient frontier of a portfolio290#' specifying the constraints and objectives. The \code{portfolio} object291#' should have two objectives: 1) mean and 2) CSM. If the292#' portfolio object does not contain these objectives, they will be added293#' using default parameters.294#'295#' @param portfolio a portfolio object with constraints and objectives created via \code{\link{portfolio.spec}}296#' @param R an xts or matrix of asset returns297#' @param optimize_method the optimize method to get the efficient frontier, default is CVXR298#' @param n.portfolios number of portfolios to generate the efficient frontier299#' @param \dots passthru parameters to \code{\link{optimize.portfolio}}300#' @return a matrix of objective measure values and weights along the efficient frontier301#' @author Xinran Zhao302#' @export303meancsm.efficient.frontier <- function(portfolio, R, optimize_method='CVXR', n.portfolios=25, ...){304if(!is.portfolio(portfolio)) stop("portfolio object must be of class 'portfolio'")305# step 1: find the minimum return given the constraints306# step 2: find the maximum return given the constraints307# step 3: 'step' along the returns and run the optimization to calculate308# the weights and objective measures along the efficient frontier309310# Use the portfolio_risk_objective from the portfolio if they have it311# check for a ETL, ES, or cVaR objective312CSM_idx <- which(unlist(lapply(portfolio$objectives, function(x) x$name)) == "CSM")313if(length(CSM_idx) >= 1){314# the portfolio object has a ETL, ES, CVaR objective315CSM_obj <- portfolio$objectives[[CSM_idx[1]]]316} else {317CSM_obj <- portfolio_risk_objective(name="CSM", arguments=list(p=0.95))318}319320# Clear out the objectives in portfolio and add them here to simplify checks321# and so we can control the optimization along the efficient frontier.322portfolio$objectives <- list()323portfolio$objectives[[1]] <- CSM_obj324portfolio <- add.objective(portfolio=portfolio, type="return", name="mean")325326# get the objective names from the portfolio object327objnames <- unlist(lapply(portfolio$objectives, function(x) x$name))328329# If the user has passed in a portfolio object with return_constraint, we need to disable it330for(i in 1:length(portfolio$constraints)){331if(inherits(portfolio$constraints[[i]], "return_constraint")){332portfolio$constraints[[i]]$enabled <- FALSE333}334}335336# get the index number of the CSM objective337CSM_idx <- which(objnames == "CSM")338# get the index number of the mean objective339mean_idx <- which(objnames == "mean")340341# create a temporary portfolio to find the max mean return342ret_obj <- return_objective(name="mean")343tportf <- insert_objectives(portfolio, list(ret_obj))344345# run the optimization to get the maximum return346tmp <- optimize.portfolio(R=R, portfolio=tportf, optimize_method=optimize_method, ...)347maxret <- extractObjectiveMeasures(tmp)$mean348349# run the optimization to get the return at the min ETL portfolio350tmp <- optimize.portfolio(R=R, portfolio=portfolio, optimize_method=optimize_method, ef=TRUE, ...)351stats <- extractStats(tmp)352minret <- stats["mean"]353354# length.out is the number of portfolios to create355ret_seq <- seq(from=minret, to=maxret, length.out=n.portfolios)356357stopifnot("package:foreach" %in% search() || requireNamespace("foreach",quietly = TRUE))358stopifnot("package:iterators" %in% search() || requireNamespace("iterators",quietly = TRUE))359ret <- ret_seq[1]360out <- foreach::foreach(ret=iterators::iter(ret_seq), .inorder=TRUE, .combine=rbind, .errorhandling='remove', .packages='PortfolioAnalytics') %dopar% {361portfolio$objectives[[mean_idx]]$target <- ret362extractStats(optimize.portfolio(R=R, portfolio=portfolio, optimize_method=optimize_method, ef=TRUE, ...=...))363}364colnames(out) <- names(stats)365out <- na.omit(out)366return(structure(out, class="frontier"))367}368369#' Generate multiple efficient frontiers for the same portfolio370#'371#' This function generates the mean-risk efficient frontier of a portfolio372#' specifying the constraints and objectives. The \code{risk_type} object373#' is for the basic mean-risk efficient frontier, other efficient frontiers374#' will be generated with the same target returns. All mean-StdDev, mean-ES375#' and mean-CSM efficient frontiers will be generated.376#'377#' @param portfolio a portfolio object with constraints and objectives created via \code{\link{portfolio.spec}}378#' @param R an xts or matrix of asset returns379#' @param optimize_method the optimize method to get the efficient frontier, default is CVXR380#' @param n.portfolios number of portfolios to generate the efficient frontier381#' @param risk_type one of "StdDev", "ES" and "CSM", which determines the type of basic efficient frontier.382#' @param compare_port vector composed of any risk "StdDev", "ES", "CSM", for example, compare_port=c("StdDev", "ES")383#' @param \dots passthru parameters to \code{\link{optimize.portfolio}}384#' @return a matrix of objective measure values and weights along the efficient frontier385#' @author Xinran Zhao386#' @export387meanrisk.efficient.frontier <- function(portfolio, R, optimize_method='CVXR', n.portfolios=25, risk_type="StdDev", compare_port = c("StdDev", "ES"),...){388if(!is.portfolio(portfolio)) stop("portfolio object must be of class 'portfolio'")389# step 1: mean-StdDev efficient frontier390# step 2: calculate minimum ES with target return391392risk_compare <- compare_port[-which(compare_port == risk_type)]393394# Use the portfolio_risk_objective from the portfolio if they have it395risk_idx <- which(unlist(lapply(portfolio$objectives, function(x) x$name)) == risk_type)396if(length(risk_idx) >= 1){397risk_obj <- portfolio$objectives[[risk_idx[1]]]398} else {399risk_obj <- portfolio_risk_objective(name=risk_type, arguments=list(p=0.05))400}401alpha <- ifelse(is.numeric(risk_obj$arguments$p), risk_obj$arguments$p, 0.05)402if(alpha > 0.5) alpha <- (1 - alpha)403404# Clear out the objectives in portfolio and add them here to simplify checks405# and so we can control the optimization along the efficient frontier.406portfolio$objectives <- list()407portfolio$objectives[[1]] <- risk_obj408portfolio <- add.objective(portfolio=portfolio, type="return", name="mean")409410# get the objective names from the portfolio object411objnames <- unlist(lapply(portfolio$objectives, function(x) x$name))412413# If the user has passed in a portfolio object with return_constraint, we need to disable it414for(i in 1:length(portfolio$constraints)){415if(inherits(portfolio$constraints[[i]], "return_constraint")){416portfolio$constraints[[i]]$enabled <- FALSE417}418}419420# get the index number of the risk objective421risk_idx <- which(objnames == risk_type)422# get the index number of the mean objective423mean_idx <- which(objnames == "mean")424425# create a temporary portfolio to find the max mean return426ret_obj <- return_objective(name="mean")427tportf <- insert_objectives(portfolio, list(ret_obj))428429# run the optimization to get the maximum return430tmp <- optimize.portfolio(R=R, portfolio=tportf, optimize_method=optimize_method, ...)431maxret <- extractObjectiveMeasures(tmp)$mean432433# run the optimization to get the return at the min ETL portfolio434tmp <- optimize.portfolio(R=R, portfolio=portfolio, optimize_method=optimize_method, ef=TRUE, ...)435stats <- extractStats(tmp)436minret <- stats["mean"]437438# length.out is the number of portfolios to create439ret_seq <- seq(from=minret, to=maxret, length.out=n.portfolios)440441stopifnot("package:foreach" %in% search() || requireNamespace("foreach",quietly = TRUE))442stopifnot("package:iterators" %in% search() || requireNamespace("iterators",quietly = TRUE))443ret <- ret_seq[1]444out <- foreach::foreach(ret=iterators::iter(ret_seq), .inorder=TRUE, .combine=rbind, .errorhandling='remove', .packages='PortfolioAnalytics') %dopar% {445portfolio$objectives[[mean_idx]]$target <- ret446res <- extractStats(optimize.portfolio(R=R, portfolio=portfolio, optimize_method=optimize_method, ef=TRUE, ...=...))447for(rc in risk_compare){448tmpportfolio <- portfolio449tmpportfolio$objectives[[risk_idx]]$name <- rc450tmpw <- optimize.portfolio(R=R, portfolio=tmpportfolio, optimize_method=optimize_method, ef=TRUE, ...=...)$weight451res <- append(res, extract_risk(R=R, w = tmpw, ES_alpha = alpha, CSM_alpha = alpha)[[risk_type]])452}453res454}455colnames(out) <- c(names(stats), paste(risk_compare, 'portfolio', risk_type))456out <- na.omit(out)457return(structure(out, class="frontier"))458}459460#' create an efficient frontier461#'462#' @details Currently there are 4 'types' supported to create an efficient frontier:463#' \describe{464#' \item{"mean-var", "mean-sd", or "mean-StdDev":}{ This is a special case for465#' an efficient frontier that can be created by a QP solver.466#' The \code{portfolio} object should have two467#' objectives: 1) mean and 2) var. If the portfolio object does not contain these468#' objectives, they will be added using default parameters.469#' The efficient frontier will be created via470#' \code{\link{meanvar.efficient.frontier}}.}471#' \item{"mean-ETL", "mean-ES", "mean-CVaR", "mean-etl":}{ This is a special472#' case for an efficient frontier that can be created by an LP solver.473#' The \code{portfolio} object should have two objectives: 1) mean474#' and 2) ETL/ES/CVaR. If the portfolio object does not contain these475#' objectives, they will be added using default parameters.476#' The efficient frontier is created via477#' \code{\link{meanetl.efficient.frontier}}.}478#' \item{"mean-CSM":}{ This is a special479#' case for an efficient frontier that can be created by CVXR solvers.480#' The \code{portfolio} object should have two objectives: 1) mean481#' and 2) CSM. If the portfolio object does not contain these482#' objectives, they will be added using default parameters.483#' The efficient frontier is created via484#' \code{\link{meanrisk.efficient.frontier}}.}485#' \item{"mean-risk":}{ This is a special case for multiple efficient frontiers.486#' The efficient frontier is created via487#' \code{\link{meanrisk.efficient.frontier}}.}488#' \item{"DEoptim":}{ This can handle more complex constraints and objectives489#' than the simple mean-var and mean-ETL cases. For this type, we actually490#' call \code{\link{optimize.portfolio}} with \code{optimize_method="DEoptim"}491#' and then extract the efficient frontier with492#' \code{extract.efficient.frontier}.}493#' \item{"random":}{ This can handle more complex constraints and objectives494#' than the simple mean-var and mean-ETL cases. For this type, we actually495#' call \code{\link{optimize.portfolio}} with \code{optimize_method="random"}496#' and then extract the efficient frontier with497#' \code{extract.efficient.frontier}.}498#' }499#'500#' @param R xts object of asset returns501#' @param portfolio object of class 'portfolio' specifying the constraints and objectives, see \code{\link{portfolio.spec}}.502#' @param type type of efficient frontier, see Details.503#' @param optimize_method the optimize method to get the efficient frontier, default is CVXR504#' @param n.portfolios number of portfolios to calculate along the efficient frontier505#' @param risk_aversion vector of risk_aversion values to construct the efficient frontier.506#' \code{n.portfolios} is ignored if \code{risk_aversion} is specified and the number507#' of points along the efficient frontier will be equal to the length of \code{risk_aversion}.508#' @param match.col column to match when extracting the efficient frontier from an objected created by \code{\link{optimize.portfolio}}.509#' @param search_size passed to \code{\link{optimize.portfolio}} for type="DEoptim" or type="random".510#' @param \dots passthrough parameters to \code{\link{optimize.portfolio}}.511#' @return an object of class 'efficient.frontier' with the objective measures512#' and weights of portfolios along the efficient frontier.513#' @author Ross Bennett, Xinran Zhao514#' @seealso \code{\link{optimize.portfolio}},515#' \code{\link{portfolio.spec}},516#' \code{\link{meanvar.efficient.frontier}},517#' \code{\link{meanetl.efficient.frontier}}518#' @export519create.EfficientFrontier <- function(R, portfolio, type, optimize_method = 'CVXR', n.portfolios=25, risk_aversion=NULL, match.col="ES", search_size=2000, ...){520# This is just a wrapper around a few functions to easily create efficient frontiers521# given a portfolio object and other parameters522call <- match.call()523if(!is.portfolio(portfolio)) stop("portfolio must be of class 'portfolio'")524type <- type[1]525switch(type,526"mean-sd"=,527"mean-StdDev"=,528"mean-var" = {frontier <- meanvar.efficient.frontier(portfolio=portfolio,529R=R,530n.portfolios=n.portfolios,531risk_aversion=risk_aversion,532...=...)533},534"mean-ETL"=,535"mean-CVaR"=,536"mean-ES"=,537"mean-es"=,538"mean-etl" = {frontier <- meanetl.efficient.frontier(portfolio=portfolio,539R=R,540n.portfolios=n.portfolios,541...=...)542},543"mean-CSM"=,544"mean-CSM" = {frontier <- meancsm.efficient.frontier(portfolio=portfolio,545R=R,546n.portfolios=n.portfolios,547...=...)548},549"mean-risk" = {frontier <- meanrisk.efficient.frontier(portfolio=portfolio,550R=R,551n.portfolios=n.portfolios,552...=...)553},554"random" = {tmp <- optimize.portfolio(R=R,555portfolio=portfolio,556optimize_method=type,557trace=TRUE,558search_size=search_size,559...=...)560frontier <- extract.efficient.frontier(object=tmp,561match.col=match.col,562n.portfolios=n.portfolios)563},564"DEoptim" = {tmp <- optimize.portfolio(R=R,565portfolio=portfolio,566optimize_method=type,567trace=TRUE,568search_size=search_size,569...=...)570frontier <- extract.efficient.frontier(object=tmp,571match.col=match.col,572n.portfolios=n.portfolios)573}574)575return(structure(list(call=call,576frontier=frontier,577R=R,578portfolio=portfolio), class="efficient.frontier"))579}580581#' Extract the efficient frontier data points582#'583#' This function extracts the efficient frontier from an object created by584#' \code{\link{optimize.portfolio}}.585#'586#' If the object is an \code{optimize.portfolio.ROI} object and \code{match.col}587#' is "ES", "ETL", or "CVaR", then the mean-ETL efficient frontier will be588#' created via \code{meanetl.efficient.frontier}.589#'590#' If the object is an \code{optimize.portfolio.ROI} object and \code{match.col}591#' is "StdDev", then the mean-StdDev efficient frontier will be created via592#' \code{meanvar.efficient.frontier}. Note that if 'var' is specified as the593#' name of an objective, the value returned will be 'StdDev'.594#'595#' For objects created by \code{optimize.portfolo} with the DEoptim, random, or596#' pso solvers, the efficient frontier will be extracted from the object via597#' \code{extract.efficient.frontier}. This means that \code{optimize.portfolio} must598#' be run with \code{trace=TRUE}.599#'600#' @param object an optimal portfolio object created by \code{optimize.portfolio}601#' @param match.col string name of column to use for risk (horizontal axis).602#' \code{match.col} must match the name of an objective measure in the603#' \code{objective_measures} or \code{opt_values} slot in the object created604#' by \code{\link{optimize.portfolio}}.605#' @param n.portfolios number of portfolios to use to plot the efficient frontier606#' @param risk_aversion vector of risk_aversion values to construct the efficient frontier.607#' \code{n.portfolios} is ignored if \code{risk_aversion} is specified and the number608#' of points along the efficient frontier is equal to the length of \code{risk_aversion}.609#' @return an \code{efficient.frontier} object with weights and other metrics along the efficient frontier610#' @author Ross Bennett611#' @export612extractEfficientFrontier <- function(object, match.col="ES", n.portfolios=25, risk_aversion=NULL){613# extract the efficient frontier from an optimize.portfolio output object614call <- match.call()615if(!inherits(object, "optimize.portfolio")) stop("object must be of class 'optimize.portfolio'")616617if(inherits(object, "optimize.portfolio.GenSA")){618stop("GenSA does not return any useable trace information for portfolios tested, thus we cannot extract an efficient frontier.")619}620621# get the portfolio and returns622portf <- object$portfolio623R <- object$R624if(is.null(R)) stop(paste("Not able to get asset returns from", object, "run optimize.portfolio with trace=TRUE"))625626# get the objective names and check if match.col is an objective name627# objnames <- unlist(lapply(portf$objectives, function(x) x$name))628# if(!(match.col %in% objnames)){629# stop("match.col must match an objective name")630# }631632# We need to create the efficient frontier if object is of class optimize.portfolio.ROI633if(inherits(object, "optimize.portfolio.ROI")){634if(match.col %in% c("ETL", "ES", "CVaR")){635frontier <- meanetl.efficient.frontier(portfolio=portf, R=R, n.portfolios=n.portfolios)636}637if(match.col == "StdDev"){638frontier <- meanvar.efficient.frontier(portfolio=portf, R=R, n.portfolios=n.portfolios, risk_aversion=risk_aversion)639}640} # end optimize.portfolio.ROI641642# use extract.efficient.frontier for otpimize.portfolio output objects with global solvers643if(inherits(object, c("optimize.portfolio.random", "optimize.portfolio.DEoptim", "optimize.portfolio.pso"))){644frontier <- extract.efficient.frontier(object=object, match.col=match.col, n.portfolios=n.portfolios)645}646return(structure(list(call=call,647frontier=frontier,648R=R,649portfolio=portf), class="efficient.frontier"))650}651652653654