Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
braverock
GitHub Repository: braverock/portfolioanalytics
Path: blob/master/vignettes/custom_moments_objectives.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{Custom Moment and Objective Functions}
38
39
\begin{document}
40
\SweaveOpts{concordance=TRUE}
41
42
\title{Custom Moment and Objective Functions}
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 how to write and use custom moment functions and custom objective functions to solve complex optimization problems.
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
library(DEoptim)
62
@
63
64
\subsection{Data}
65
The edhec data set from the PerformanceAnalytics package will be used as data for the following examples.
66
<<>>=
67
data(edhec)
68
69
# Use the first 4 columns in edhec for a returns object
70
R <- edhec[, 1:4]
71
colnames(R) <- c("CA", "CTAG", "DS", "EM")
72
head(R, 5)
73
74
# Get a character vector of the fund names
75
funds <- colnames(R)
76
@
77
78
\section{Setting the Portfolio Moments}
79
The PortfolioAnalytics framework to estimate solutions to constrained optimization problems is implemented in such a way that the moments of the returns are calculated only once and then used in lower level optimization functions. The \code{set.portfolio.moments} function computes the first, second, third, and fourth moments depending on the objective function(s) in the \code{portfolio} object. For example, if the third and fourth moments do not need to be calculated for a given objective, then \code{set.portfolio.moments} will try to detect this and not compute those moments. Currently, \code{set.portfolio.moments} implements methods to compute moments based on sample estimates, higher moments from fitting a statistical factor model based on the work of Kris Boudt \citep{Boudt2014}, the Black Litterman model \citep{MeucciBL2008}, and the Fully Flexible Framework based on the work of Attilio Meucci \citep{Meucci2008}.
80
81
<<tidy=FALSE>>=
82
# Construct initial portfolio with basic constraints.
83
init.portf <- portfolio.spec(assets=funds)
84
init.portf <- add.constraint(portfolio=init.portf, type="full_investment")
85
init.portf <- add.constraint(portfolio=init.portf, type="long_only")
86
87
# Portfolio with standard deviation as an objective
88
SD.portf <- add.objective(portfolio=init.portf, type="risk", name="StdDev")
89
90
# Portfolio with expected shortfall as an objective
91
ES.portf <- add.objective(portfolio=init.portf, type="risk", name="ES")
92
@
93
94
Here we see the names of the list object that is returned by \code{set.portfolio.moments}.
95
<<>>=
96
sd.moments <- set.portfolio.moments(R, SD.portf)
97
names(sd.moments)
98
99
es.moments <- set.portfolio.moments(R, ES.portf)
100
names(es.moments)
101
@
102
103
\section{Custom Moment Functions}
104
In many cases for constrained optimization problems, one may want to estimate moments for a specific use case or further extend the idea of \code{set.portfolio.moments}. A user defined custom moment function can have any arbitrary named arguments. However, arguments named \code{R} for the asset returns and \code{portfolio} for the portfolio object will be detected automatically and handled in an efficient manner. Because of this, it is strongly encouraged to use \code{R} for the asset returns object and \code{portfolio} for the portfolio object.
105
106
The moment function should return a named list object where the elements represent the moments:
107
\begin{description}
108
\item[\code{\$mu}]{ first moment; expected returns vector}
109
\item[\code{\$sigma}]{ second moment; covariance matrix}
110
\item[\code{\$m3}]{ third moment; coskewness matrix}
111
\item[\code{\$m4}]{ fourth moment; cokurtosis matrix}
112
\end{description}
113
114
The lower level optimization functions expect an object with the structure described above. List elements with the names \code{mu}, \code{sigma}, \code{m3}, and \code{m4} are matched automatically and handled in an efficient manner.
115
116
Here we define a function to estimate the covariance matrix using a robust method.
117
<<>>=
118
sigma.robust <- function(R){
119
require(MASS)
120
out <- list()
121
set.seed(1234)
122
out$sigma <- cov.rob(R, method="mcd")$cov
123
return(out)
124
}
125
@
126
127
Now we can use the custom moment function in \code{optimize.portfolio} to estimate the solution to the minimum standard deviation portfolio.
128
<<tidy=FALSE>>=
129
opt.sd <- optimize.portfolio(R, SD.portf,
130
optimize_method="ROI",
131
momentFUN="sigma.robust")
132
opt.sd
133
@
134
135
Here we extract the weights and compute the portfolio standard deviation to verify that the the robust estimate of the covariance matrix was used in the optimization.
136
<<tidy=FALSE>>=
137
weights <- extractWeights(opt.sd)
138
sigma <- sigma.robust(R)$sigma
139
140
sqrt(t(weights) %*% sigma %*% weights)
141
extractObjectiveMeasures(opt.sd)$StdDev
142
@
143
144
\section{Custom Objective Functions}
145
A key feature of \verb"PortfolioAnalytics" is that the name for an objective can be any valid \R function. \verb"PortfolioAnalytics" was designed to be flexible and modular, and custom objective functions are a key example of this.
146
147
Here we define a very simple function to compute annualized standard deviation for monthly data that we will use as an objective function.
148
<<>>=
149
pasd <- function(R, weights, sigma, N=36){
150
R <- tail(R, N)
151
tmp.sd <- sqrt(as.numeric(t(weights) %*% sigma %*% weights))
152
sqrt(12) * tmp.sd
153
}
154
@
155
156
A few guidelines should be followed for defining a custom objective function.
157
158
\begin{itemize}
159
\item The objective function must return a single value for the optimizer to minimize.
160
\item It is strongly encouraged to use the following argument names in the objective function:
161
\begin{description}
162
\item[\code{R}] {for the asset returns}
163
\item[\code{weights}] {for the portfolio weights}
164
\end{description}
165
\end{itemize}
166
167
These argument names are detected automatically and handled in an efficient manner. Any other arguments for the objective function can be for the moments or passed in through the \code{arguments} list in the objective.
168
169
For our \code{pasd} function, we need custom moments function to return a named list with \code{sigma} as an element. We can use the \code{sigma.robust} function we defined in the previous section. Here we construct a portfolio with our \code{pasd} function as an objective to minimize.
170
171
<<tidy=FALSE>>=
172
# Construct initial portfolio with basic constraints.
173
pasd.portf <- portfolio.spec(assets=funds)
174
pasd.portf <- add.constraint(portfolio=pasd.portf, type="full_investment")
175
pasd.portf <- add.constraint(portfolio=pasd.portf, type="long_only")
176
177
# Portfolio with pasd as an objective
178
# Note how we can specify N as an argument
179
pasd.portf <- add.objective(portfolio=pasd.portf, type="risk", name="pasd",
180
arguments=list(N=48))
181
@
182
183
184
Now we can run the optimization to estimate a solution to our optimization problem.
185
<<>>=
186
opt.pasd <- optimize.portfolio(R, pasd.portf,
187
optimize_method="DEoptim",
188
search_size=5000, trace=TRUE, traceDE=0,
189
momentFUN="sigma.robust")
190
opt.pasd
191
@
192
193
We now consider an example with a more complicated objective function. Our objective to maximize the fourth order expansion of the Constant Relative Risk Aversion (CRRA) expected utility function as in \citep{Boudt2014}.
194
195
\begin{equation*}
196
EU_{\lambda}(w) = - \frac{\lambda}{2} m_{(2)}(w) +
197
\frac{\lambda (\lambda + 1)}{6} m_{(3)}(w) -
198
\frac{\lambda (\lambda + 1) (\lambda + 2)}{24} m_{(4)}(w)
199
\end{equation*}
200
201
Here we define a function to compute CRRA estimate. Note how we define the function to use \code{sigma}, \code{m3}, and \code{m4} as arguments that will use the output from a custom moment function. We could compute the moments inside this function, but re-computing the moments potentially tens of thousands of times (i.e. at each iteration) can be very compute intensive.
202
203
<<>>=
204
CRRA <- function(R, weights, lambda, sigma, m3, m4){
205
weights <- matrix(weights, ncol=1)
206
M2.w <- t(weights) %*% sigma %*% weights
207
M3.w <- t(weights) %*% m3 %*% (weights %x% weights)
208
M4.w <- t(weights) %*% m4 %*% (weights %x% weights %x% weights)
209
term1 <- (1 / 2) * lambda * M2.w
210
term2 <- (1 / 6) * lambda * (lambda + 1) * M3.w
211
term3 <- (1 / 24) * lambda * (lambda + 1) * (lambda + 2) * M4.w
212
out <- -term1 + term2 - term3
213
out
214
}
215
@
216
217
We now define the custom moment function to compute the moments for the objective function.
218
<<>>=
219
crra.moments <- function(R, ...){
220
out <- list()
221
out$sigma <- cov(R)
222
out$m3 <- PerformanceAnalytics:::M3.MM(R)
223
out$m4 <- PerformanceAnalytics:::M4.MM(R)
224
out
225
}
226
@
227
228
Finally, we set up the portfolio and run the optimization using our custom moment function and objective function to maximize CRRA. Note that \code{type="return"} is used to maximize an objective function.
229
<<tidy=FALSE>>=
230
# Construct initial portfolio with basic constraints.
231
crra.portf <- portfolio.spec(assets=funds)
232
crra.portf <- add.constraint(portfolio=crra.portf, type="weight_sum",
233
min_sum=0.99, max_sum=1.01)
234
crra.portf <- add.constraint(portfolio=crra.portf, type="box",
235
min=0.05, max=0.4)
236
237
# Portfolio with crra as an objective
238
# Note how we can specify lambda as an argument
239
crra.portf <- add.objective(portfolio=crra.portf, type="return", name="CRRA",
240
arguments=list(lambda=10))
241
@
242
243
<<>>=
244
opt.crra <- optimize.portfolio(R, crra.portf, optimize_method="DEoptim",
245
search_size=5000, trace=TRUE, traceDE=0,
246
momentFUN="crra.moments")
247
opt.crra
248
@
249
250
\verb"PortfolioAnalytics" supports several methods to estimate moments as well as user defined moment functions. The name of the objective must be the name of a valid \R function and \verb"PortfolioAnalytics" integrates well with \kbd{PerformanceAnalytics} to utilize several of the risk measure functions such as \code{StdDev} and \code{ES}. Because an objective function can be a valid \R function, user defined objective functions are supported. The modular framework of \verb"PortfolioAnalytics" allows one to easily define custom moment functions and objective functions as valid \R functions to solve complex and specialized objective functions.
251
252
\bibliography{PA}
253
254
\end{document}
255
256