Chapter 4 Managing Portfolios
In this chapter we show how to explore and analyze data using the dataset created in Chapter @ref(#s_2Data):
At first we will learn how to full-sample optimize portfolios, then (in the next chapters) we will do the same thing in a rolling analysis and also perform some backtesting. The major workhorse of this chapter is the portfolioAnalytics
-package developed by Peterson and Carl (2018).
portfolioAnalytics
comes with an excellent introductory vignette vignette("portfolio_vignette")
and includes more documents, detailing on the use of ROI
-solvers vignette("ROI_vignette")
, how to create custom moment functions vignette("custom_moments_objectives")
and how to introduce CVaR-budgets vignette("risk_budget_optimization")
.
4.1 Introduction
SHORT INTRODUCTION TO PORTFOLIOMANAGEMENT
We start by first creating a portfolio
object, before we…
4.2 Tools for Portfolio Management
4.2.1 The Portfolio Object
The portfolio object is a so-called S3
-object, which means, that it has a certain class
(portfolio) describing its properties, behavior and relation to other objects. Usually such an objects comes with a variety of methods
. To create such an object, we reuse the stock data set that we have created in Chapter @ref(#s_2Data):
load("stocks.RData")
glimpse(stocks.final)
## Observations: 2,160
## Variables: 10
## $ symbol <chr> "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL", "AAPL...
## $ date <S3: yearmon> Jan 2000, Feb 2000, Mrz 2000, Apr 2000, Mai 2...
## $ return <dbl> -0.07314358, 0.10481940, 0.18484208, -0.08651642, -0....
## $ adjusted <dbl> 2.489997, 2.750997, 3.259497, 2.977497, 2.015998, 2.5...
## $ volume <dbl> 175420000, 92240400, 101158400, 62395200, 108376800, ...
## $ sp500 <dbl> -0.041753145, -0.020108083, 0.096719828, -0.030795756...
## $ Mkt.RF <dbl> -0.0474, 0.0245, 0.0520, -0.0640, -0.0442, 0.0464, -0...
## $ SMB <dbl> 0.0505, 0.2214, -0.1728, -0.0771, -0.0501, 0.1403, -0...
## $ HML <dbl> -0.0045, -0.1057, 0.0794, 0.0856, 0.0243, -0.1010, 0....
## $ RF <dbl> 0.0041, 0.0043, 0.0047, 0.0046, 0.0050, 0.0040, 0.004...
stocks.final %>% slice(1:2)
## # A tibble: 2 x 10
## symbol date return adjusted volume sp500 Mkt.RF SMB HML
## <chr> <S3:> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 AAPL Jan ~ -0.0731 2.49 1.75e8 -0.0418 -0.0474 0.0505 -0.0045
## 2 AAPL Feb ~ 0.105 2.75 9.22e7 -0.0201 0.0245 0.221 -0.106
## # ... with 1 more variable: RF <dbl>
For the portfolioAnalytics
-package we need our data in xts
-format (see @ref(#sss_112xts)) and therefore first spread
the dataset returns in columns of stocks and the convert to xts
using tk_xts()
from the timetk
-package.
returns <- stocks.final %>%
select(symbol,date,return) %>%
spread(symbol,return) %>%
tk_xts(silent = TRUE)
Now its time to initialize the portfolio.spec()
object passing along the names of our assets. Afterwards we print
the object (most S3
obejcts come with a printing methods that nicely displays some nice information).
pspec <- portfolio.spec(assets = stocks.selection$symbol,
category_labels = stocks.selection$sector)
print(pspec)
## **************************************************
## PortfolioAnalytics Portfolio Specification
## **************************************************
##
## Call:
## portfolio.spec(assets = stocks.selection$symbol, category_labels = stocks.selection$sector)
##
## Number of assets: 10
## Asset Names
## [1] "AAPL" "MSFT" "AMZN" "CSCO" "NVDA" "ORCL" "AMGN" "ADBE" "QCOM" "GILD"
##
## Category Labels
## Information Technology : AAPL MSFT CSCO NVDA ORCL ADBE QCOM
## Consumer Discretionary : AMZN
## Health Care : AMGN GILD
str(pspec)
## List of 6
## $ assets : Named num [1:10] 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1
## ..- attr(*, "names")= chr [1:10] "AAPL" "MSFT" "AMZN" "CSCO" ...
## $ category_labels:List of 3
## ..$ Information Technology: int [1:7] 1 2 4 5 6 8 9
## ..$ Consumer Discretionary: int 3
## ..$ Health Care : int [1:2] 7 10
## $ weight_seq : NULL
## $ constraints : list()
## $ objectives : list()
## $ call : language portfolio.spec(assets = stocks.selection$symbol, category_labels = stocks.selection$sector)
## - attr(*, "class")= chr [1:2] "portfolio.spec" "portfolio"
Checking the structure of the object str()
we find that it contains several elements: assets which contains the asset names and initial weights that are equally distributed unless otherwise specified (e.g. portfolio.spec(assets=c(0.6,0.4))
), category_labels to categorize assets by sector (or geography etc.), weight_seq (sequence of weights for later use by random_portfolios
), constraints that we will set soon, objectives and the call
that initialised the object. Before we go and optimize any portfolio we will show how to set contraints.
4.2.2 Constraints
Constraints define restrictions and boundary conditions on the weights of a portfolio. Constraints are defined by add.constraint
specifying certain types
and arguments for each type
, as well as whether the constraint should be enabled or not (enabled=TRUE
is the default).
4.2.2.1 Sum of Weights Constraint
Here we define how much of the available budget can/must be invested by specifying the maximum/minimum sum of portfolio weights. Usually we want to invest our entire budget and therefore set type="full_investment"
which sets the sum of weights to 1. Alternatively we can set the type="weight_sum"
to have mimimum/maximum weight_sum
equal to 1.
pspec <- add.constraint(portfolio=pspec,
type="full_investment")
# print(pspec)
# pspec <- add.constraint(portfolio=pspec,type="weight_sum", min_sum=1, max_sum=1)
Another common constraint is to have the portfolio dollar-neutral type="dollar_neutral"
(or equivalent formulations specified below)
# pspec <- add.constraint(portfolio=pspec,
# type="dollar_neutral")
# print(pspec)
# pspec <- add.constraint(portfolio=pspec, type="active")
# pspec <- add.constraint(portfolio=pspec, type="weight_sum", min_sum=0, max_sum=0)
4.2.2.2 Box Constraint
Box constraints specify upper and lower bounds on the asset weights. If we pass min
and max
as scalars then the same max and min weights are set per asset. If we pass vectors (that should be of the same length as the number of assets) we can specify position limits on individual stocks
pspec <- add.constraint(portfolio=pspec,
type="box",
min=0,
max=0.4)
# print(pspec)
# add.constraint(portfolio=pspec,
# type="box",
# min=c(0.05, 0, rep(0.05,8)),
# max=c(0.4, 0.3, rep(0.4,8)))
Another special type of box constraints are long-only constraints, where we only allow positive weights per asset. These are set automatically, if no min
and max
are set or when we use type="long_only"
# pspec <- add.constraint(portfolio=pspec, type="box")
# pspec <- add.constraint(portfolio=pspec, type="long_only")
4.2.2.3 Group Constraints
Group constraints allow the user to specify constraints per groups, such as industries, sectors or geography.3 These groups can be randomly defined, below we will set group constraints for the sectors as specified above. The input arguments are the following: groups
list of vectors specifying the groups of the assets, group_labels
character vector to label the groups (e.g. size, asset class, style, etc.), group_min
and group_max
specifying minimum and maximum weight per group, group_pos
to specifying the number of non-zero weights per group (optional).
pspec <- add.constraint(portfolio=pspec,
type="group",
groups=list(pspec$category_labels$`Information Technology`, # list of vectors specifying the groups of the assets
pspec$category_labels$`Consumer Discretionary`,
pspec$category_labels$`Health Care`),
group_min=c(0.1, 0.15,0.1),
group_max=c(0.85, 0.55,0.4),
group_labels=pspec$category_labels)
# print(pspec)
4.2.2.4 Position Limit Constraint
The position limit constraint allows the user to specify limits on the number of assets with non-zero, long, or short positions. Its arguments are: max_pos
which defines the maximum number of assets with non-zero weights and max_pos_long
/ max_pos_short
that specify the maximum number of assets with long (i.e. buy) and short (i.e. sell) positions.4
pspec <- add.constraint(portfolio=pspec, type="position_limit", max_pos=3)
# pspec <- add.constraint(portfolio=pspec, type="position_limit", max_pos_long=3, max_pos_short=3))
# print(pspec)
4.2.2.5 Diversification Constraint
The diversification constraint enables to set a minimum diversification limit by penalizing the optimizer if the deviation is larger than 5%. Diversification is defined as \(\sum_{i=1}^{N}w_i^2\) for \(N\) assets.5 Its only argument is the diversification taregt div_target
.
pspec <- add.constraint(portfolio=pspec, type="diversification", div_target=0.7)
# print(pspec)
4.2.2.6 Turnover Constraint
The turnover constraint allows to specify a maximum turnover from a set of initial weights that can either be given or are the weights initially specified for the portfolio object. It is also implemented as an optimization penalty if the turnover deviates more than 5% from the turnover_target
.6
pspec <- add.constraint(portfolio=pspec, type="turnover", turnover_target=0.2)
# print(pspec)
4.2.2.7 Target Return Constraint
The target return constraint allows the user to target an average return specified by return_target
.
pspec <- add.constraint(portfolio=pspec, type="return", return_target=0.007)
# print(pspec)
4.2.2.8 Factor Exposure Constraint
The factor exposure constraint allows the user to set upper and lower bounds on exposures to risk factors. We will use the factor exposures that we have calculated in @(#sss_3FactorExposure). The major input is a vector or matrix B
and upper
/lower
bounds for the portfolio factor exposure. If B
is a vector (with length equal to the number of assets), lower and upper bounds must be scalars. If B
is a matrix, the number of rows must be equal to the number of assets and the number of columns represent the number of factors. In this case, the length of lower and upper bounds must be equal to the number of factors. B
should have column names specifying the factors and row names specifying the assets.
B <- stocks.factor_exposure %>% as.data.frame() %>% column_to_rownames("symbol")
pspec <- add.constraint(portfolio=pspec, type="factor_exposure",
B=B,
lower=c(0.8,0,-1),
upper=c(1.2,0.8,0))
# print(pspec)
4.2.2.9 Transaction Cost Constraint
The transaction cost constraint enables the user to specify (porportional) transaction costs.7 Here we will assume the proportional transation cost ptc
to be equal to 1%.
pspec <- add.constraint(portfolio=pspec, type="transaction_cost", ptc=0.01)
# print(pspec)
4.2.2.10 Leverage Exposure Constraint
The leverage exposure constraint specifies a maximum level of leverage. Below we set leverage
to 1.3 to create a 130/30 portfolio.
pspec <- add.constraint(portfolio=pspec, type="leverage_exposure", leverage=1.3)
# print(pspec)
4.2.2.11 Checking and en-/disabling constraints
Every constraint that is added to the portfolio object gets a number according to the order it was set. If one wants to update (enable/disable) a specific constraints this can be done by the indexnum
argument.
summary(pspec)
# To get an overview on the specs, their indexnum and whether they are enabled I suggest the follwoing
consts <- plyr::ldply(pspec$constraints, function(x){c(x$type,x$enabled)})
consts
pspec$constraints[[which(consts$V1=="box")]]
pspec <- add.constraint(pspec, type="box",
min=0, max=0.5,
indexnum=which(consts$V1=="box"))
pspec$constraints[[which(consts$V1=="box")]]
# to disable constraints
pspec$constraints[[which(consts$V1=="position_limit")]]
pspec <- add.constraint(pspec, type="position_limit", enable=FALSE, # only specify argument if you do enable the constraint
indexnum=which(consts$V1=="position_limit"))
pspec$constraints[[which(consts$V1=="position_limit")]]
4.2.3 Objectives
For an optimal portfolio there first has to be specified what optimal in terms of the relevant (business) objective. Such objectives (target functions) can be added to the portfolio object with add.objective
. With this function, the user can specify the type of objective to add to the portfolio object. Currently available are ‘return’, ‘risk’, ‘risk budget’, ‘quadratic utility’, ‘weight concentration’, ‘turnover’ and ‘minmax’. Each type of objective has additional arguments that need to be specified. Several types of objectives can be added and enabled or disabled by specifying the indexnum
argument.
4.2.3.1 Portfolio Risk Objective
Here, the user can specify a risk function that should be minimized. We start by adding a risk objective to minimize portfolio variance (minimum variance portfolio). Another example could be the expected tail loss with a confidence level 0.95. Whatever function (even user defined ones are possble, the name must correspond to a function in R
), necessary additional arguments to the function have to be passed as a named list to arguments
. Possible functions are:
pspec <- add.objective(portfolio=pspec,
type='risk',
name='var')
pspec <- add.objective(portfolio=pspec,
type='risk',
name='ETL',
arguments=list(p=0.95),
enabled=FALSE)
# print(pspec)
4.2.3.2 Portfolio Return Objective
The return objective allows the user to specify a return function to maximize. Here we add a return objective to maximize the portfolio mean return.
pspec <- add.objective(portfolio=pspec,
type='return',
name='mean')
# print(pspec)
4.2.3.3 Portfolio Risk Budget Objective
The portfolio risk objective allows the user to specify constraints to minimize component contribution (i.e. equal risk contribution) or specify upper and lower bounds on percentage risk contribution. Here we specify that no asset can contribute more than 30% to total portfolio risk.
See the risk budget optimization vignette for more detailed examples of portfolio optimizationswith risk budgets.
pspec <- add.objective(portfolio=pspec,
type="risk_budget",
name="var",
max_prisk=0.3)
pspec <- add.objective(portfolio=pspec,
type="risk_budget",
name="ETL",
arguments=list(p=0.95),
max_prisk=0.3,
enabled=FALSE)
# for an equal risk contribution portfolio, set min_concentration=TRUE
pspec <- add.objective(portfolio=pspec,
type="risk_budget",
name="ETL",
arguments=list(p=0.95),
min_concentration=TRUE,
enabled=FALSE)
print(pspec)
## **************************************************
## PortfolioAnalytics Portfolio Specification
## **************************************************
##
## Call:
## portfolio.spec(assets = stocks.selection$symbol, category_labels = stocks.selection$sector)
##
## Number of assets: 10
## Asset Names
## [1] "AAPL" "MSFT" "AMZN" "CSCO" "NVDA" "ORCL" "AMGN" "ADBE" "QCOM" "GILD"
##
## Category Labels
## Information Technology : AAPL MSFT CSCO NVDA ORCL ADBE QCOM
## Consumer Discretionary : AMZN
## Health Care : AMGN GILD
##
##
## Constraints
## Enabled constraint types
## - full_investment
## - box
## - group
## - position_limit
## - diversification
## - turnover
## - return
## - factor_exposure
## - transaction_cost
## - leverage_exposure
##
## Objectives:
## Enabled objective names
## - var
## - mean
## - var
## Disabled objective names
## - ETL
4.2.4 Solvers
Solvers are the workhorse of our portfolio optimization framework, and there are a variety of them available to us through the portfolioAnalytics
-package. I will briefly introduce the available solvers. Note that these solvers can be specified through optimize_method
in the optimize.portfolio
and optimize.portfolio.rebalancing
method.
4.2.4.1 DEOptim
This solver comes from the R package DEoptim
and is a differential evolution algorithm (a global stochastic optimization algorithm) developed by (???). The help on ?DEoptim
gives many more references. There is also a nice vignette("DEoptimPortfolioOptimization")
on large scale portfolio optimization using the portfolioAnalytics
-package.
4.2.4.2 Random Portfolios
There are three methods to generate random portfolios contained in portfolioAnalytics
:8
- The most flexible but also slowest method is ‘sample’. It can take leverage, box, group, and position limit constraints into account.
- The ‘simplex’ method is useful to generate random portfolios with the full investment and min box constraints (values for min_sum/ max_sum are ignored). Other constraints (box max, group and position limit constraints will be handled by elimination) which might leave only very few feasible portfolios. Sometimes it will also lead to suboptimal solutions.
- Using grid search, the ‘grid’ method only satisfies the min and max box constraints.
4.2.4.3 pso
The psoptim
function from the R package pso
(Bendtsen. 2012) and uses particle swarm optimization.
4.2.4.4 GenSA
The GenSA
function from the R package GenSA
(Gubian et al. 2018) and is based on generalized simmulated annealing (a generic probabilistic heuristic optimization algorithm)
4.2.4.5 ROI
The ROI
(R
Optimization Infrastructure) is a framework to handle optimization problems in R
. It serves as an interface to the Rglpk
package and the quadprog
package which solve linear and quadratic programming problems. Available methods in the context of the portfolioAnalytics
-package are given below (see section @(#sss_4Objectives) for available objectives.
- Maxmimize portfolio return subject leverage, box, group, position limit, target mean return, and/or factor exposure constraints on weights.
- Globally minimize portfolio variance subject to leverage, box, group, turnover, and/or factor exposure constraints.
- Minimize portfolio variance subject to leverage, box, group, and/or factor exposure constraints given a desired portfolio return.
- Maximize quadratic utility subject to leverage, box, group, target mean return, turnover, and/or factor exposure constraints and risk aversion parameter. (The risk aversion parameter is passed into
optimize.portfolio
as an added argument to the portfolio object). - Minimize ETL subject to leverage, box, group, position limit, target mean return, and/or factor exposure constraints and target portfolio return.
4.2.4.6 Multicore capabilities
For large sets of assets and solvers that can parallelize, the doParallel
-package provides multicore capabilities to the portfolioAnalytics
package.
# require(doParallel)
# registerDoParallel()
4.3 Optimization examples
Modern portfolio theory suggests how rational investors should optimize their portfolio(s) of risky assets to take advantage of diversification effects. This is possible, because risk as opposed to return is not additive and depends very much on the pairwise comovement (correlation) between the risky assets. The concepts of modern portfolio theory go back to Markowitz (1952) considers asset returns as random variables and estimates their risk and return plainly as mean and standard deviation within the available data sample. Usually we do not introduce any constraints except the full investment constraint that constrains the portfolio weights \(w\) to sum to \(1\), but allow for unlimited shortselling. The two basic portfolio problems are either to minimize the portfolio risk \(w'\Sigma w\) given a target return \(\bar{r}\) (4.1) or maximize the portfolio return \(w'\mu\) given a target level of risk \(\bar{\sigma}\) (4.2): \[\begin{align} & \underset{w}{\text{min}} & & w'\hat{\Sigma}w \\ & \text{subject to} & & w'\hat{\mu}=\bar{r}, \\ &&& w'\mathbf{1}=1. \tag{4.1} \end{align}\] and \[\begin{align} & \underset{w}{\text{max}} & & w'\hat{\mu}\\ & \text{subject to} & & w'\hat{\Sigma}w=\bar{\sigma}, \\ &&& w'\mathbf{1}=1. \tag{4.2} \end{align}\]The solutions to these two problems is a hyperbola that depicts the efficient frontier in the \(\mu\)-\(\sigma\)-diagram. We can plot the efficient frontier using create.EfficientFrontier
9 if we specify a return (mean) and a risk (sd) objective:10
pspec <- portfolio.spec(assets = stocks.selection$symbol,
category_labels = stocks.selection$sector)
pspec <- add.constraint(portfolio=pspec,
type="full_investment") # weights sum to 1
p <- add.constraint(portfolio=p,
type="box",
min = -0.5,
max = +0.5)
# to create the efficient frontier
pspec <- add.objective(portfolio=pspec,
type="return",
name="mean") # mean
pspec <- add.objective(portfolio=pspec,
type="risk",
name="var") # uses sd
meansd.ef <- create.EfficientFrontier(
R = returns,
portfolio = pspec,
type = "mean-sd",
n.portfolios = 25,
)
# summary(meansd.ef, digits=2) # to print the whole efficient frontier
# meansd.ef$frontier[1:2,] # shows the first two portfolios
chart.EfficientFrontier(meansd.ef,
match.col="StdDev", # which column to use for risk
type="l",
RAR.text="Sharpe Ratio",
tangent.line = FALSE,
chart.assets=TRUE,
labels.assets=TRUE,xlim=c(0.03,0.20),ylim=c(0,0.055))
where, again, the weights sum exactly to one but are itself unconstrained. In our example, assuming a risk-free rate of \(0.01\), we therefore get:
mu <- colMeans(returns); Sigma <- cov(returns);
ones <- rep(1,ncol(returns)); rf <- 0.04/12
# Minimum Variance portfolio
wMVP <- t(solve(Sigma) %*% ones)/drop(ones %*% solve(Sigma) %*% ones)
muMVP <- drop(wMVP%*%mu); sigmaMVP <- drop(wMVP %*% Sigma %*% t(wMVP))^0.5
srMVP <- muMVP/sigmaMVP
round(cbind(wMVP,"mean"=muMVP,"sd"=sigmaMVP,"sr"=srMVP),4)
## AAPL ADBE AMGN AMZN CSCO GILD MSFT NVDA ORCL
## [1,] 0.0409 0.0498 0.3805 -0.0567 0.0242 0.1132 0.2654 -0.0412 0.0967
## QCOM mean sd sr
## [1,] 0.1272 0.0082 0.0552 0.1492
# Tangency portfolio
wTP <- t(solve(Sigma) %*% (mu-rf*ones))/drop(ones %*% solve(Sigma) %*% (mu-rf*ones))
muTP <- drop(wTP%*%mu); sigmaTP <- drop(wTP %*% Sigma %*% t(wTP))^0.5
srTP <- (muTP-rf)/sigmaTP; srTP2 <- sqrt(drop((mu-rf*ones) %*% solve(Sigma) %*% (mu-rf*ones)))
round(cbind(wTP,"mean"=muTP,"sd"=sigmaTP,"sr"=srTP),4)
## AAPL ADBE AMGN AMZN CSCO GILD MSFT NVDA ORCL
## [1,] 0.9371 0.0654 0.152 0.318 -0.7013 0.9046 -0.3374 0.4003 -0.4731
## QCOM mean sd sr
## [1,] -0.2656 0.0583 0.1849 0.2975
Note the negative and large weights in both portolios! Below, we find another plot of the efficient frontier with the additional information.
chart.EfficientFrontier(meansd.ef,
match.col="StdDev", # which column to use for risk
type="l",
RAR.text="Sharpe Ratio",
rf=0.04/12,
tangent.line = TRUE,
chart.assets=TRUE,
labels.assets=TRUE,xlim=c(0.03,0.20),ylim=c(0,0.055))
4.3.1 Mean-variance Portfolios
4.3.1.1 Introduction and Theoretics
4.3.1.2 The minimum risk mean-variance portfolio
4.3.1.3 Feasible Set and Efficient Frontier
4.3.1.4 Minimum variance portfolio
4.3.1.5 Capital market line and tangency portfolio
4.3.1.6 Box and Group Constrained mean-variance portfolios
4.3.1.7 Maximum return mean-variance portfolio
4.3.1.8 Covariance risk budget constraints
4.3.2 Mean-CVaR Portfolios
References
Peterson, Brian G., and Peter Carl. 2018. PortfolioAnalytics: Portfolio Analysis, Including Numerical Methods for Optimization of Portfolios. https://github.com/braverock/PortfolioAnalytics.
Bendtsen., Claus. 2012. Pso: Particle Swarm Optimization. https://CRAN.R-project.org/package=pso.
Gubian, Sylvain, Yang Xiang, Brian Suomela, Julia Hoeng, and PMP SA. 2018. GenSA: Generalized Simulated Annealing. https://CRAN.R-project.org/package=GenSA.
Markowitz, Harry. 1952. “Portfolio Selection.” The Journal of Finance 7 (1): 77–91.
Note, that only the ROI, DEoptim and random portfolio solvers support group constraints. See also @(#sss_4solvers).↩
Note that not all solvers suüpport the different options. All of them are supported by the DEoptim and random portfolio solvres, while no ROI solver supports this type of constraint. The ROI solvers do not support the long/short position limit constraintsm, and (only) quadprog allows for the
max_pos
argument.↩Note that diversification constraint is only supported for the global numeric solvers (not the ROI solvers).↩
Note, that the turnover constraint is not currently supported using the ROI solver for quadratic utility and minimum variance problems.↩
For usage of the ROI (quadprog) solvers, transaction costs are currently only supported for global minimum variance and quadratic utility problems.↩
Due to a bug in the current version of the package, in case you want to allow for negative asset weights, specify the
weight_seq=seq(-1,1,by=0.01)
when creating the portfolio object, because it cannot create negative weights itself.↩Actually uses the function
meanvar.efficient.frontier
and employs thequadprog
solver through theROI
package as along as the constraints are not too complex. Other available functions aremeanetl.efficient.frontier
for an efficient frontier based on mean and expected shortfall, that usesRglpk
through theROI
. For more complex constraints, the portfolio is actually optimized (e.g. using theDEOptim
orrandom
solvers) and the efficient frontier is extracted withextract.efficient.frontier
.↩Note, that I polished the standard plots somewhat using the
plotly
package. Credit for the inital graph is due to [Nana Boateng]{https://rpubs.com/mr148/333559}.↩