Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
braverock
GitHub Repository: braverock/portfolioanalytics
Path: blob/master/vignettes/portfolio_vignette.Rnw
1433 views
1
\documentclass[a4paper]{article}
2
\usepackage[OT1]{fontenc}
3
\usepackage{Rd}
4
\usepackage{amsmath}
5
\usepackage{hyperref}
6
7
\usepackage[round]{natbib}
8
\usepackage{bm}
9
\usepackage{verbatim}
10
\usepackage[latin1]{inputenc}
11
\bibliographystyle{abbrvnat}
12
13
\usepackage{url}
14
15
\let\proglang=\textsf
16
%\newcommand{\pkg}[1]{{\fontseries{b}\selectfont #1}}
17
%\newcommand{\R}[1]{{\fontseries{b}\selectfont #1}}
18
%\newcommand{\email}[1]{\href{mailto:#1}{\normalfont\texttt{#1}}}
19
%\newcommand{\E}{\mathsf{E}}
20
%\newcommand{\VAR}{\mathsf{VAR}}
21
%\newcommand{\COV}{\mathsf{COV}}
22
%\newcommand{\Prob}{\mathsf{P}}
23
24
\renewcommand{\topfraction}{0.85}
25
\renewcommand{\textfraction}{0.1}
26
\renewcommand{\baselinestretch}{1.5}
27
\setlength{\textwidth}{15cm} \setlength{\textheight}{22cm} \topmargin-1cm \evensidemargin0.5cm \oddsidemargin0.5cm
28
29
\usepackage[latin1]{inputenc}
30
% or whatever
31
32
\usepackage{lmodern}
33
\usepackage[T1]{fontenc}
34
% Or whatever. Note that the encoding and the font should match. If T1
35
% does not look nice, try deleting the line with the fontenc.
36
37
% \VignetteIndexEntry{An Introduction to Portfolio Optimization with PortfolioAnalytics}
38
39
\begin{document}
40
\SweaveOpts{concordance=TRUE}
41
42
\title{An Introduction to Portfolio Optimization with PortfolioAnalytics}
43
\author{Ross Bennett}
44
45
\date{May 17, 2018}
46
47
\maketitle
48
49
\begin{abstract}
50
The purpose of this vignette is to demonstrate the new interface in PortfolioAnalytics to specify a portfolio object, add constraints and objectis, and run optimizations.
51
\end{abstract}
52
53
\tableofcontents
54
55
\section{Getting Started}
56
\subsection{Load Packages}
57
Load the necessary packages.
58
59
<<>>=
60
library(PortfolioAnalytics)
61
@
62
63
\subsection{Data}
64
The edhec data set from the PerformanceAnalytics package will be used as example data.
65
<<>>=
66
data(edhec)
67
68
# Use the first 4 columns in edhec for a returns object
69
returns <- edhec[, 1:4]
70
colnames(returns) <- c("CA", "CTAG", "DS", "EM")
71
print(head(returns, 5))
72
73
# Get a character vector of the fund names
74
fund.names <- colnames(returns)
75
@
76
77
\section{Creating the Portfolio Object}
78
The portfolio object is instantiated with the \code{portfolio.spec} function. The main argument to \code{portfolio.spec} is assets, this is a required argument. The assets argument can be a scalar value for the number of assets, a character vector of fund names, or a named vector of initial weights. If initial weights are not specified, an equal weight portfolio will be assumed.
79
80
The \code{pspec} object is an S3 object of class "portfolio". When first created, the portfolio object has an element named \code{assets} with the initial weights, an element named \code{category\_labels}, an element named \code{weight\_seq} with sequence of weights if specified, an empty constraints list and an empty objectives list.
81
82
<<>>=
83
# Specify a portfolio object by passing a character vector for the
84
# assets argument.
85
pspec <- portfolio.spec(assets=fund.names)
86
print.default(pspec)
87
@
88
89
\section{Adding Constraints to the Portfolio Object}
90
Adding constraints to the portfolio object is done with \code{add.constraint}. The \code{add.constraint} function is the main interface for adding and/or updating constraints to the portfolio object. This function allows the user to specify the portfolio to add the constraints to, the type of constraints, arguments for the constraint, and whether or not to enable the constraint (\code{enabled=TRUE} is the default). If updating an existing constraint, the indexnum argument can be specified.
91
92
\subsection{Sum of Weights Constraint}
93
94
The \code{weight\_sum} constraint specifies the constraint on the sum of the weights. Aliases for the \code{weight\_sum} constraint type include \code{weight} and \code{leverage}. Here we add a constraint that the weights must sum to 1, or the full investment constraint.
95
<<tidy=FALSE>>=
96
# Add the full investment constraint that specifies the weights must sum to 1.
97
pspec <- add.constraint(portfolio=pspec,
98
type="weight_sum",
99
min_sum=1,
100
max_sum=1)
101
@
102
103
There are two special cases for the leverage constraint:
104
\begin{enumerate}
105
\item The sum of the weights equal 1, i.e. the full investment constraint. The full investment constraint can be specified with \code{type="full\_investment"}. This automatically sets \code{min\_sum=1} and \code{max\_sum=1.}
106
\item The sum of the weights equal 0, i.e. the dollar neutral or active constraint. This constraint can be specified with \code{type="dollar\_neutral"} or \code{type="active"}.
107
\end{enumerate}
108
109
<<tidy=FALSE>>=
110
# The full investment constraint can also be specified with type="full_investment"
111
# pspec <- add.constraint(portfolio=pspec, type="full_investment")
112
113
# Another common constraint is that portfolio weights sum to 0.
114
# This can be specified any of the following ways
115
# pspec <- add.constraint(portfolio=pspec, type="weight_sum",
116
# min_sum=0,
117
# max_sum=0)
118
# pspec <- add.constraint(portfolio=pspec, type="dollar_neutral")
119
# pspec <- add.constraint(portfolio=pspec, type="active")
120
@
121
122
\subsection{Box Constraint}
123
Box constraints allows the user to specify upper and lower bounds on the weights of the assets. Here we add box constraints for the asset weights so that the minimum weight of any asset must be greater than or equal to 0.05 and the maximum weight of any asset must be less than or equal to 0.4. The values for min and max can be passed in as scalars or vectors. If min and max are scalars, the values for min and max will be replicated as vectors to the length of assets. If min and max are not specified, a minimum weight of 0 and maximum weight of 1 are assumed. Note that min and max can be specified as vectors with different weights for linear inequality constraints.
124
<<tidy=FALSE>>=
125
# Add box constraints
126
pspec <- add.constraint(portfolio=pspec,
127
type="box",
128
min=0.05,
129
max=0.4)
130
131
# min and max can also be specified per asset
132
# pspec <- add.constraint(portfolio=pspec,
133
# type="box",
134
# min=c(0.05, 0, 0.08, 0.1),
135
# max=c(0.4, 0.3, 0.7, 0.55))
136
137
# A special case of box constraints is long only where min=0 and max=1
138
# The default action is long only if min and max are not specified
139
# pspec <- add.constraint(portfolio=pspec, type="box")
140
# pspec <- add.constraint(portfolio=pspec, type="long_only")
141
@
142
143
144
\subsection{Group Constraint}
145
Group constraints allow the user to specify the the sum of weights by group. Group constraints are currently supported by the ROI, DEoptim, and random portfolio solvers. The following code groups the assets such that the first 3 assets are grouped together labeled GroupA and the fourth asset is in its own group labeled GroupB. The \code{group\_min} argument specifies that the sum of the weights in GroupA must be greater than or equal to 0.1 and the sum of the weights in GroupB must be greater than or equal to 0.15. The \code{group\_max} argument specifies that the sum of the weights in GroupA must be less than or equal to 0.85 and the sum of the weights in GroupB must be less than or equal to 0.55.The \code{group\_labels} argument is optional and is useful if groups is not a named list for labeling groups in terms of market capitalization, sector, etc.
146
<<tidy=FALSE>>=
147
# Add group constraints
148
pspec <- add.constraint(portfolio=pspec, type="group",
149
groups=list(groupA=c(1, 2, 3),
150
grouB=4),
151
group_min=c(0.1, 0.15),
152
group_max=c(0.85, 0.55))
153
@
154
155
\subsection{Position Limit Constraint}
156
The position limit constraint allows the user to specify limits on the number of assets with non-zero, long, or short positions. The ROI solver interfaces to the Rglpk package (i.e. using the glpk plugin) for solving maximizing return and ETL/ES/cVaR objectives. The Rglpk package supports integer programming and thus supports position limit constraints for the \code{max\_pos} argument. The quadprog package does not support integer programming, and therefore \code{max\_pos} is not supported for the ROI solver using the quadprog plugin. Note that \code{max\_pos\_long} and \code{max\_pos\_short} are not supported for either ROI solver. All position limit constraints are fully supported for DEoptim and random solvers.
157
158
<<tidy=FALSE>>=
159
# Add position limit constraint such that we have a maximum number of three assets with non-zero weights.
160
pspec <- add.constraint(portfolio=pspec, type="position_limit", max_pos=3)
161
162
# Can also specify maximum number of long positions and short positions
163
# pspec <- add.constraint(portfolio=pspec, type="position_limit", max_pos_long=3, max_pos_short=3)
164
@
165
166
\subsection{Diversification Constraint}
167
The diversification constraint allows the user to target diversification. Diversification is defined as $diversification = \sum_{i=1}^N w_i^2$ for $N$ assets. The diversification constraint is implemented for the global optimizers by applying a penalty if the diversification value is more than 5\% away from \code{div\_target}. Note that diversification as a constraint is not supported for the ROI solvers, it is only supported for the global numeric solvers.
168
<<>>=
169
pspec <- add.constraint(portfolio=pspec, type="diversification", div_target=0.7)
170
@
171
172
\subsection{Turnover Constraint}
173
A target turnover can be specified as a constraint. The turnover is calculated from a set of initial weights. The initial weights can be specified, by default they are the initial weights in the portfolio object. The turnover constraint is implemented for the global optimizers by applying a penalty if the turnover value is more than 5\% away from \code{turnover\_target}. Note that the turnover constraint is not currently supported for quadratic utility and minimum variance problems using the ROI solver.
174
<<>>=
175
pspec <- add.constraint(portfolio=pspec, type="turnover", turnover_target=0.2)
176
@
177
178
\subsection{Target Return Constraint}
179
The target return constraint allows the user to specify a target mean return.
180
<<>>=
181
pspec <- add.constraint(portfolio=pspec, type="return", return_target=0.007)
182
@
183
184
\subsection{Factor Exposure Constraint}
185
The factor exposure constraint allows the user to set upper and lower bounds on exposures to risk factors. The exposures can be passed in as a vector or matrix. Here we specify a vector for \code{B} with arbitrary values, e.g. betas of the assets, with a market risk exposure range of 0.6 to 0.9.
186
<<tidy=FALSE>>=
187
pspec <- add.constraint(portfolio=pspec, type="factor_exposure",
188
B=c(-0.08, 0.37, 0.79, 1.43),
189
lower=0.6, upper=0.9)
190
@
191
192
\subsection{Transaction Cost Constraint}
193
The transaction cost constraint allows the user to specify proportional transaction costs. Proportional transaction cost constraints can be implemented for quadratic utility and minimum variance problems using the ROI solver. Transaction costs are supported as a penalty for the global numeric solvers. Here we add the transaction cost contraint with the proportional transaction cost value of 1\%.
194
<<>>=
195
pspec <- add.constraint(portfolio=pspec, type="transaction_cost", ptc=0.01)
196
@
197
198
The print method for the portfolio object shows a concise view of the portfolio and the constraints that have been added.
199
<<>>=
200
print(pspec)
201
@
202
203
The summary method gives a more detailed view of the constraints.
204
<<>>=
205
summary(pspec)
206
@
207
208
This demonstrates adding constraints to the portfolio object. As an alternative to adding constraints directly to the portfolio object, constraints can be specified as separate objects.
209
210
\subsection{Specifying Constraints as Separate Objects}
211
The following examples will demonstrate how to specify constraints as separate objects for all constraints types.
212
213
<<tidy=FALSE>>=
214
# full investment constraint
215
weight_constr <- weight_sum_constraint(min_sum=1, max_sum=1)
216
217
# box constraint
218
box_constr <- box_constraint(assets=pspec$assets, min=0, max=1)
219
220
# group constraint
221
group_constr <- group_constraint(assets=pspec$assets,
222
groups=list(c(1, 2, 3),
223
4),
224
group_min=c(0.1, 0.15),
225
group_max=c(0.85, 0.55),
226
group_labels=c("GroupA", "GroupB"))
227
228
# position limit constraint
229
poslimit_constr <- position_limit_constraint(assets=pspec$assets, max_pos=3)
230
231
# diversification constraint
232
div_constr <- diversification_constraint(div_target=0.7)
233
234
# turnover constraint
235
to_constr <- turnover_constraint(turnover_target=0.2)
236
237
# target return constraint
238
ret_constr <- return_constraint(return_target=0.007)
239
240
# factor exposure constraint
241
exp_constr <- factor_exposure_constraint(assets=pspec$assets,
242
B=c(-0.08, 0.37, 0.79, 1.43),
243
lower=0.6, upper=0.9)
244
245
# transaction cost constraint
246
ptc_constr <- transaction_cost_constraint(assets=pspec$assets, ptc=0.01)
247
@
248
249
\section{Adding Objectives}
250
Objectives can be added to the portfolio object with \code{add.objective}. The \code{add.objective} function is the main function for adding and/or updating business objectives to the portfolio object. This function allows the user to specify the \verb"portfolio" to add the objectives to, the \verb"type" (currently 'return', 'risk', 'risk\_budget', or 'weight\_concentration'), \verb"name" of the objective function, \verb"arguments" to the objective function, and whether or not to \verb"enable" the objective. If updating an existing constraint, the \verb"indexnum" argument can be specified.
251
252
\subsection{Portfolio Risk Objective}
253
The portfolio risk objective allows the user to specify a risk function to minimize
254
Here we add a risk objective to minimize portfolio expected tail loss with a confidence level of 0.95. Other default arguments to the function can be passed in as a named list to arguments. Note that the name of the function must correspond to a function in R. Many functions are available in the \verb"PerformanceAnalytics" package or a user defined function.
255
<<tidy=FALSE>>=
256
pspec <- add.objective(portfolio=pspec,
257
type='risk',
258
name='ETL',
259
arguments=list(p=0.95))
260
@
261
262
\subsection{Portfolio Return Objective}
263
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.
264
<<tidy=FALSE>>=
265
pspec <- add.objective(portfolio=pspec,
266
type='return',
267
name='mean')
268
@
269
270
\subsection{Portfolio Risk Budget Objective}
271
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 optimizations with risk budgets.
272
<<tidy=FALSE>>=
273
pspec <- add.objective(portfolio=pspec, type="risk_budget", name="ETL",
274
arguments=list(p=0.95), max_prisk=0.3)
275
276
# for an equal risk contribution portfolio, set min_concentration=TRUE
277
# pspec <- add.objective(portfolio=pspec, type="risk_budget", name="ETL",
278
# arguments=list(p=0.95), min_concentration=TRUE)
279
@
280
281
282
\subsection{Portfolio Weight Concentration Objective}
283
The weight concentration objective allows the user to specify an objective to minimize concentration as measured by the Herfindahl-Hirschman Index. For otpimization problems solved with the global numeric optimizers, the portfolio HHI value is penalized using \code{conc\_aversion} value as the multiplier.
284
285
For quadratic utility problems with weight concentration as an objective using the ROI solver, this is implemented as a penalty to the objective function. The objective function is implemented as follows:
286
287
\begin{eqnarray}
288
\underset{\boldsymbol{w}}{\text{maximize}}
289
\boldsymbol{w}' \boldsymbol{\mu} - \frac{\lambda}{2}(\boldsymbol{w}' \boldsymbol{\Sigma} \boldsymbol{w} + \lambda_{hhi} * HHI)\\
290
\end{eqnarray}
291
Where $\mu$ is the estimated mean asset returns, $\lambda$ is the risk aversion parameter, $lambda_{hhi}$ is the concentration aversion parameter, $HHI$ is the portfolio $HHI$, $\boldsymbol{\Sigma}$ is the estimated covariance matrix of asset returns and $\boldsymbol{w}$ is the set of weights.
292
293
Here we add a weight concentration objective for the overall portfolio HHI.
294
<<tidy=FALSE>>=
295
pspec <- add.objective(portfolio=pspec, type="weight_concentration",
296
name="HHI", conc_aversion=0.1)
297
@
298
299
The weight concentration aversion parameter by groups can also be specified. Here we add a weight concentration objective specifying groups and concentration aversion parameters by group.
300
<<tidy=FALSE>>=
301
pspec <- add.objective(portfolio=pspec, type="weight_concentration",
302
name="HHI",
303
conc_aversion=c(0.03, 0.06),
304
conc_groups=list(c(1, 2),
305
c(3, 4)))
306
@
307
308
The print method for the portfolio object will now show all the constraints and objectives that have been added.
309
<<>>=
310
print(pspec)
311
@
312
313
The \code{summary} function gives a more detailed view.
314
<<>>=
315
summary(pspec)
316
@
317
318
\section{Solvers}
319
The PortfolioAnalytics package currently supports random portfolios, DEoptim, pso, GenSA, and ROI as back ends. Note that some of the QP/LP problems are solved directly with Rglpk and quadprog. The solver can be specified with the \code{optimize\_method} argument in \code{optimize.portfolio} and \code{optimize.portfolio.rebalancing}.
320
321
\subsection{DEoptim}
322
PortfolioAnalytics uses the \code{DEoptim} function from the R package \verb"DEoptim". Differential evolution is a stochastic global optimization algorithm. See \code{?DEoptim} and the references contained therein for more information. See also \href{https://cran.r-project.org/web/packages/DEoptim/vignettes/DEoptimPortfolioOptimization.pdf}{Large scale portfolio optimization with DEoptim}.
323
\subsection{Random Portfolios}
324
PortfolioAnalytics has three methods to generate random portfolios.
325
\begin{enumerate}
326
\item The 'sample' method to generate random portfolios is based on an idea by Pat Burns. This is the most flexible method, but also the slowest, and can generate portfolios to satisfy leverage, box, group, and position limit constraints.
327
\item The 'simplex' method to generate random portfolios is based on a paper by W. T. Shaw. The simplex method is useful to generate random portfolios with the full investment constraint, where the sum of the weights is equal to 1, and min box constraints. Values for \code{min\_sum} and \code{max\_sum} of the leverage constraint will be ignored, the sum of weights will equal 1. All other constraints such as the box constraint max, group and position limit constraints will be handled by elimination. If the constraints are very restrictive, this may result in very few feasible portfolios remaining. Another key point to note is that the solution may not be along the vertexes depending on the objective. For example, a risk budget objective will likely place the portfolio somewhere on the interior.
328
\item The 'grid' method to generate random portfolios is based on the \code{gridSearch} function in package \verb"NMOF". The grid search method only satisfies the \code{min} and \code{max} box constraints. The \code{min\_sum} and \code{max\_sum} leverage constraint will likely be violated and the weights in the random portfolios should be normalized. Normalization may cause the box constraints to be violated and will be penalized in \code{constrained\_objective}.
329
\end{enumerate}
330
331
The following plots illustrate the various methods to generate random portfolios.
332
333
<<fig.cap="Random portfolio methods", fig.width=5, fig.height=5, tidy=FALSE>>=
334
R <- edhec[, 1:4]
335
336
# set up simple portfolio with leverage and box constraints
337
pspec <- portfolio.spec(assets=colnames(R))
338
pspec <- add.constraint(portfolio=pspec, type="leverage",
339
min_sum=0.99, max_sum=1.01)
340
pspec <- add.constraint(portfolio=pspec, type="box", min=0, max=1)
341
342
# generate random portfolios using the 3 methods
343
rp1 <- random_portfolios(portfolio=pspec, permutations=5000,
344
rp_method='sample')
345
rp2 <- random_portfolios(portfolio=pspec, permutations=5000,
346
rp_method='simplex')
347
rp3 <- random_portfolios(portfolio=pspec, permutations=5000,
348
rp_method='grid')
349
350
# show feasible portfolios in mean-StdDev space
351
tmp1.mean <- apply(rp1, 1, function(x) mean(R %*% x))
352
tmp1.StdDev <- apply(rp1, 1, function(x) StdDev(R=R, weights=x))
353
tmp2.mean <- apply(rp2, 1, function(x) mean(R %*% x))
354
tmp2.StdDev <- apply(rp2, 1, function(x) StdDev(R=R, weights=x))
355
tmp3.mean <- apply(rp3, 1, function(x) mean(R %*% x))
356
tmp3.StdDev <- apply(rp3, 1, function(x) StdDev(R=R, weights=x))
357
358
# plot feasible portfolios
359
plot(x=tmp1.StdDev, y=tmp1.mean, col="gray", main="Random Portfolio Methods",
360
ylab="mean", xlab="StdDev")
361
points(x=tmp2.StdDev, y=tmp2.mean, col="red", pch=2)
362
points(x=tmp3.StdDev, y=tmp3.mean, col="lightgreen", pch=5)
363
legend("bottomright", legend=c("sample", "simplex", "grid"),
364
col=c("gray", "red", "lightgreen"),
365
pch=c(1, 2, 5), bty="n")
366
@
367
368
Figure 1 shows the feasible space using the different random portfolio methods. The 'sample' method has relatively even coverage of the feasible space. The 'simplex' method also has relatively even coverage of the space, but it is also more concentrated around the assets. The 'grid' method is pushed to the interior of the space due to the normalization.
369
370
The \code{fev} argument controls the face-edge-vertex biasing. Higher values for \code{fev} will result in the weights vector more concentrated on a single asset. This can be seen in the following charts.
371
<<fig.cap="FEV biasing values">>=
372
fev <- 0:5
373
par(mfrow=c(2, 3))
374
for(i in 1:length(fev)){
375
rp <- rp_simplex(portfolio=pspec, permutations=2000, fev=fev[i])
376
tmp.mean <- apply(rp, 1, function(x) mean(R %*% x))
377
tmp.StdDev <- apply(rp, 1, function(x) StdDev(R=R, weights=x))
378
plot(x=tmp.StdDev, y=tmp.mean, main=paste("FEV =", fev[i]),
379
ylab="mean", xlab="StdDev", col=rgb(0, 0, 100, 50, maxColorValue=255))
380
}
381
par(mfrow=c(1,1))
382
@
383
384
Figure 2 shows the feasible space varying the fev values.
385
386
The \code{fev} argument can be passed in as a vector for more control over the coverage of the feasible space. The default value is \code{fev=0:5}.
387
<<fig.cap="Random portfolio 'sample' and 'simplex' comparison">>=
388
par(mfrow=c(1, 2))
389
# simplex
390
rp_simplex <- random_portfolios(portfolio=pspec, permutations=2000,
391
rp_method='simplex')
392
tmp.mean <- apply(rp_simplex, 1, function(x) mean(R %*% x))
393
tmp.StdDev <- apply(rp_simplex, 1, function(x) StdDev(R=R, weights=x))
394
plot(x=tmp.StdDev, y=tmp.mean, main="rp_method=simplex fev=0:5",
395
ylab="mean", xlab="StdDev", col=rgb(0, 0, 100, 50, maxColorValue=255))
396
#sample
397
rp_sample <- random_portfolios(portfolio=pspec, permutations=2000,
398
rp_method='sample')
399
tmp.mean <- apply(rp_sample, 1, function(x) mean(R %*% x))
400
tmp.StdDev <- apply(rp_sample, 1, function(x) StdDev(R=R, weights=x))
401
plot(x=tmp.StdDev, y=tmp.mean, main="rp_method=sample",
402
ylab="mean", xlab="StdDev", col=rgb(0, 0, 100, 50, maxColorValue=255))
403
par(mfrow=c(1,1))
404
@
405
406
\subsection{pso}
407
PortfolioAnalytics uses the \code{psoptim} function from the R package \verb"pso". Particle swarm optimization is a heuristic optimization algorithm. See \code{?psoptim} and the references contained therein for more information.
408
409
\subsection{GenSA}
410
PortfolioAnalytics uses the \code{GenSA} function from the R package \verb"GenSA". Generalized simmulated annealing is generic probabilistic heuristic optimization algorithm. See \code{?GenSA} and the references contained therein for more information.
411
412
\subsection{ROI}
413
The \verb"ROI" package serves as an interface to the \verb"Rglpk" package and the \verb"quadprog" package to solve linear and quadratic programming problems. The interface to the \verb"ROI" package solves a limited type of convex optimization problems:
414
415
\begin{enumerate}
416
\item Maxmimize portfolio return subject leverage, box, group, position limit, target mean return, and/or factor exposure constraints on weights.
417
\item Minimize portfolio variance subject to leverage, box, group, turnover, and/or factor exposure constraints (otherwise known as global minimum variance portfolio).
418
\item Minimize portfolio variance subject to leverage, box, group, and/or factor exposure constraints and a desired portfolio return.
419
\item Maximize quadratic utility subject to leverage, box, group, target mean return, turnover, and/or factor exposure constraints and risk aversion parameter.
420
(The risk aversion parameter is passed into \code{optimize.portfolio} as an added argument to the \code{portfolio} object).
421
\item Minimize ETL subject to leverage, box, group, position limit, target mean return, and/or factor exposure constraints and target portfolio return.
422
\end{enumerate}
423
424
425
\section{Optimization}
426
The previous sections demonstrated how to specify a portfolio object, add constraints, add objectives, and the solvers available. This section will demonstrate run the optimizations via \code{optimize.portfolio}. Only a small number of examples will be shown here, see the demos for several more examples.
427
428
\subsection{Initial Portfolio Object}
429
<<>>=
430
library(DEoptim)
431
library(ROI)
432
require(ROI.plugin.glpk)
433
require(ROI.plugin.quadprog)
434
435
data(edhec)
436
R <- edhec[, 1:6]
437
colnames(R) <- c("CA", "CTAG", "DS", "EM", "EQMN", "ED")
438
funds <- colnames(R)
439
440
# Create an initial portfolio object with leverage and box constraints
441
init <- portfolio.spec(assets=funds)
442
init <- add.constraint(portfolio=init, type="leverage",
443
min_sum=0.99, max_sum=1.01)
444
init <- add.constraint(portfolio=init, type="box", min=0.05, max=0.65)
445
@
446
447
\subsection{Maximize mean return with ROI}
448
Add an objective to maximize mean return.
449
<<>>=
450
maxret <- add.objective(portfolio=init, type="return", name="mean")
451
@
452
453
Run the optimization.
454
<<tidy=FALSE>>=
455
opt_maxret <- optimize.portfolio(R=R, portfolio=maxret,
456
optimize_method="ROI",
457
trace=TRUE)
458
459
print(opt_maxret)
460
@
461
462
Chart the weights and optimal portfolio in risk-return space. The weights and a risk-reward scatter plot can be plotted separately as shown below with the \code{chart.Weights} and \code{chart.RiskReward} functions. The \code{plot} function will plot the weights and risk-reward scatter together.
463
<<fig.cap="Maximum Return Optimization", fig.align='center', fig.width=5, fig.height=8, tidy=FALSE>>=
464
plot(opt_maxret, risk.col="StdDev", return.col="mean",
465
main="Maximum Return Optimization", chart.assets=TRUE,
466
xlim=c(0, 0.05), ylim=c(0,0.0085))
467
@
468
469
\subsection{Minimize variance with ROI}
470
Add an objective to minimize portfolio variance.
471
<<>>=
472
minvar <- add.objective(portfolio=init, type="risk", name="var")
473
@
474
475
Run the optimization. Note that although 'var' is the risk metric, 'StdDev' is returned as an objective measure.
476
<<tidy=FALSE>>=
477
opt_minvar <- optimize.portfolio(R=R, portfolio=minvar,
478
optimize_method="ROI", trace=TRUE)
479
print(opt_minvar)
480
@
481
482
Chart the weights and optimal portfolio in risk-return space.
483
<<fig.cap="Minimum Variance Optimization", fig.align='center', fig.width=5, fig.height=8, tidy=FALSE>>=
484
plot(opt_minvar, risk.col="StdDev", return.col="mean",
485
main="Minimum Variance Optimization", chart.assets=TRUE,
486
xlim=c(0, 0.05), ylim=c(0,0.0085))
487
@
488
489
\subsection{Maximize quadratic utility with ROI}
490
Add mean and var objectives for quadratic utility. Note that the risk aversion parameter for quadratic utility is specifed in the objective as shown below.
491
<<>>=
492
qu <- add.objective(portfolio=init, type="return", name="mean")
493
qu <- add.objective(portfolio=qu, type="risk", name="var", risk_aversion=0.25)
494
@
495
496
Run the optimization.
497
<<tidy=FALSE>>=
498
opt_qu <- optimize.portfolio(R=R, portfolio=qu,
499
optimize_method="ROI",
500
trace=TRUE)
501
print(opt_qu)
502
@
503
504
<<fig.cap="Maximum Quadratic Utility Optimization", fig.align='center', fig.width=5, fig.height=8, tidy=FALSE>>=
505
plot(opt_qu, risk.col="StdDev", return.col="mean",
506
main="Quadratic Utility Optimization", chart.assets=TRUE,
507
xlim=c(0, 0.05), ylim=c(0, 0.0085))
508
@
509
510
\subsection{Minimize expected tail loss with ROI}
511
Add ETL objective.
512
<<>>=
513
etl <- add.objective(portfolio=init, type="risk", name="ETL")
514
@
515
516
Run the optimization.
517
<<tidy=FALSE>>=
518
opt_etl <- optimize.portfolio(R=R, portfolio=etl,
519
optimize_method="ROI",
520
trace=TRUE)
521
print(opt_etl)
522
@
523
524
<<fig.cap="Minimum ETL Optimization", fig.align='center', fig.width=5, fig.height=8, tidy=FALSE>>=
525
plot(opt_etl, risk.col="ES", return.col="mean",
526
main="ETL Optimization", chart.assets=TRUE,
527
xlim=c(0, 0.14), ylim=c(0,0.0085))
528
@
529
530
\subsection{Maximize mean return per unit ETL with random portfolios}
531
Add mean and ETL objectives.
532
<<tidy=FALSE>>=
533
meanETL <- add.objective(portfolio=init, type="return", name="mean")
534
meanETL <- add.objective(portfolio=meanETL, type="risk", name="ETL",
535
arguments=list(p=0.95))
536
@
537
538
Run the optimization. The default random portfolio method is 'sample'.
539
<<tidy=FALSE>>=
540
opt_meanETL <- optimize.portfolio(R=R, portfolio=meanETL,
541
optimize_method="random",
542
trace=TRUE, search_size=2000)
543
print(opt_meanETL)
544
@
545
546
The optimization was run with \code{trace=TRUE} so that iterations and other output from random portfolios is stored in the \code{opt\_meanETL} object. The \code{extractStats} function can be used to get a matrix of the weights and objective measures at each iteration.
547
<<>>=
548
stats_meanETL <- extractStats(opt_meanETL)
549
dim(stats_meanETL)
550
head(stats_meanETL)
551
@
552
553
Chart the optimal weights and optimal portfolio in risk-return space. Because the optimization was run with \code{trace=TRUE}, the chart of the optimal portfolio also includes the trace portfolios of the optimization. This is usefule to visualize the feasible space of the portfolios. The 'neighbor' portfolios relative to the optimal portfolio weights can be included the chart of the optimal weights.
554
<<fig.cap="mean-ETL Optimization", fig.align='center', fig.width=5, fig.height=8,tidy=FALSE>>=
555
plot(opt_meanETL, risk.col="ETL", return.col="mean",
556
main="mean-ETL Optimization", neighbors=25)
557
@
558
559
Calculate and plot the portfolio component ETL contribution.
560
<<fig.cap="mean-ETL risk contribution", fig.height=3.5, fig.width=3.5, fig.align='center', tidy=FALSE>>=
561
pct_contrib <- ES(R=R, p=0.95, portfolio_method="component",
562
weights=extractWeights(opt_meanETL))
563
barplot(pct_contrib$pct_contrib_MES, cex.names=0.8, las=3, col="lightblue")
564
@
565
566
This figure shows that the Equity Market Nuetral strategy has greater than 50\% risk contribution. A risk budget objective can be added to limit risk contribution percentage to 40\%.
567
568
\subsection{Maximize mean return per unit ETL with ETL risk budgets}
569
Add objectives to maximize mean return per unit ETL with 40\% limit ETL risk budgets.
570
<<tidy=FALSE>>=
571
# change the box constraints to long only
572
init$constraints[[2]]$min <- rep(0, 6)
573
init$constraints[[2]]$max <- rep(1, 6)
574
575
rb_meanETL <- add.objective(portfolio=init, type="return", name="mean")
576
rb_meanETL <- add.objective(portfolio=rb_meanETL, type="risk", name="ETL",
577
arguments=list(p=0.95))
578
rb_meanETL <- add.objective(portfolio=rb_meanETL, type="risk_budget",
579
name="ETL", max_prisk=0.4, arguments=list(p=0.95))
580
@
581
582
Run the optimization. Set \code{traceDE=5} so that every fifth iteration is printed. The default is to print every iteration.
583
<<tidy=FALSE>>=
584
opt_rb_meanETL <- optimize.portfolio(R=R, portfolio=rb_meanETL,
585
optimize_method="DEoptim",
586
search_size=2000,
587
trace=TRUE, traceDE=5)
588
print(opt_rb_meanETL)
589
@
590
591
<<fig.cap="mean-ETL Optimization with Risk Budget", fig.align="center", fig.width=5, fig.height=8, tidy=FALSE>>=
592
plot(opt_rb_meanETL, risk.col="ETL", return.col="mean",
593
main="Risk Budget mean-ETL Optimization",
594
xlim=c(0,0.12), ylim=c(0.005,0.009))
595
@
596
597
Chart the contribution to risk in percentage terms.
598
<<fig.cap="mean-ETL with Risk Budget Objective", fig.height=3.5, fig.width=5, fig.align='center'>>=
599
plot.new()
600
chart.RiskBudget(opt_rb_meanETL, risk.type="percentage", neighbors=25)
601
@
602
603
604
\subsection{Maximize mean return per unit ETL with ETL equal contribution to risk}
605
Add objective to maximize mean return per unit ETL with ETL equal contribution to risk.
606
<<tidy=FALSE>>=
607
eq_meanETL <- add.objective(portfolio=init, type="return", name="mean")
608
eq_meanETL <- add.objective(portfolio=eq_meanETL, type="risk", name="ETL",
609
arguments=list(p=0.95))
610
eq_meanETL <- add.objective(portfolio=eq_meanETL, type="risk_budget",
611
name="ETL", min_concentration=TRUE,
612
arguments=list(p=0.95))
613
@
614
615
Run the optimization. Set \code{traceDE=5} so that every fifth iteration is printed. The default is to print every iteration.
616
<<tidy=FALSE>>=
617
opt_eq_meanETL <- optimize.portfolio(R=R, portfolio=eq_meanETL,
618
optimize_method="DEoptim",
619
search_size=2000,
620
trace=TRUE, traceDE=5)
621
print(opt_eq_meanETL)
622
@
623
624
Chart the optimal weights and optimal portfolio in risk-return space.
625
<<fig.cap="mean-ETL with ETL Equal Risk Contribution", fig.align='center', fig.width=5, fig.height=8, tidy=FALSE>>=
626
plot.new()
627
plot(opt_eq_meanETL, risk.col="ETL", return.col="mean",
628
main="Risk Budget mean-ETL Optimization",
629
xlim=c(0,0.12), ylim=c(0.005,0.009))
630
@
631
632
Chart the contribution to risk in percentage terms. It is clear in this chart that the optimization results in a near equal risk contribution portfolio.
633
<<fig.cap="Percentage Contibution to Risk", fig.height=3.5, fig.width=5, fig.align='center'>>=
634
plot.new()
635
chart.RiskBudget(opt_eq_meanETL, risk.type="percentage", neighbors=25)
636
@
637
638
639
The \code{opt\_meanETL}, \code{opt\_rb\_meanETL}, and \code{opt\_eq\_meanETL} optimizations are similar and can be easily compared.
640
\begin{enumerate}
641
\item[opt\_meanETL] Objective to maximize mean return per unit ETL. The constraints are full investment and box constraints such that the minimum weight of any asset is 0.05 and maximum weight of any asset is 0.65.
642
\item[opt\_rb\_meanETL] Objective to maximize mean return per unit ETL with risk budget objective to limit maximum percent risk 40\%. The constraints are full investment and long only constraints.
643
\item[opt\_eq\_meanETL] Objective to maximize mean return per unit ETL with equal contribution to risk. The constraints are full investment and long only constraints.
644
\end{enumerate}
645
646
Combine the optimizations for easy comparison.
647
<<tidy=FALSE>>=
648
opt_combine <- combine.optimizations(list(meanETL=opt_meanETL,
649
rbmeanETL=opt_rb_meanETL,
650
eqmeanETL=opt_eq_meanETL))
651
652
# View the weights and objective measures of each optimization
653
extractWeights(opt_combine)
654
obj_combine <- extractObjectiveMeasures(opt_combine)
655
@
656
657
<<fig.cap="Optimal Weights of Optimizations", fig.height=3.5, fig.width=5, fig.align='center'>>=
658
chart.Weights(opt_combine, plot.type="bar", legend.loc="topleft", ylim=c(0, 1))
659
@
660
661
Chart the optimal portfolios of each optimization in risk-return space.
662
<<fig.cap="Optimal Portfolios in Risk-Return Space", fig.height=4, fig.width=6, fig.align='center', tidy=FALSE>>=
663
plot.new()
664
chart.RiskReward(opt_combine, risk.col="ETL", return.col="mean",
665
main="ETL Optimization Comparison", xlim=c(0.018, 0.024),
666
ylim=c(0.005, 0.008))
667
@
668
669
Calculate the STARR of each optimization
670
<<fig.cap="STARR of Optimizations", fig.height=3.5, fig.width=5, fig.align='center', tidy=FALSE>>=
671
STARR <- obj_combine[, "mean"] / obj_combine[, "ETL"]
672
barplot(STARR, col="blue", cex.names=0.8, cex.axis=0.8,
673
las=3, main="STARR", ylim=c(0,1))
674
@
675
676
<<fig.cap="Percentage Contribution to Risk of Optimizations", fig.height=3.5, fig.width=5, fig.align='center', tidy=FALSE>>=
677
plot.new()
678
chart.RiskBudget(opt_combine, match.col="ETL", risk.type="percent",
679
ylim=c(0,1), legend.loc="topright")
680
@
681
682
683
\end{document}
684