Path: blob/master/vignettes/custom_moments_objectives.Rnw
1433 views
\documentclass[a4paper]{article}1\usepackage[OT1]{fontenc}2\usepackage{Rd}3\usepackage{amsmath}4\usepackage{hyperref}56\usepackage[round]{natbib}7\usepackage{bm}8\usepackage{verbatim}9\usepackage[latin1]{inputenc}10\bibliographystyle{abbrvnat}1112\usepackage{url}1314\let\proglang=\textsf15%\newcommand{\pkg}[1]{{\fontseries{b}\selectfont #1}}16%\newcommand{\R}[1]{{\fontseries{b}\selectfont #1}}17%\newcommand{\email}[1]{\href{mailto:#1}{\normalfont\texttt{#1}}}18%\newcommand{\E}{\mathsf{E}}19%\newcommand{\VAR}{\mathsf{VAR}}20%\newcommand{\COV}{\mathsf{COV}}21%\newcommand{\Prob}{\mathsf{P}}2223\renewcommand{\topfraction}{0.85}24\renewcommand{\textfraction}{0.1}25\renewcommand{\baselinestretch}{1.5}26\setlength{\textwidth}{15cm} \setlength{\textheight}{22cm} \topmargin-1cm \evensidemargin0.5cm \oddsidemargin0.5cm2728\usepackage[latin1]{inputenc}29% or whatever3031\usepackage{lmodern}32\usepackage[T1]{fontenc}33% Or whatever. Note that the encoding and the font should match. If T134% does not look nice, try deleting the line with the fontenc.3536% \VignetteIndexEntry{Custom Moment and Objective Functions}3738\begin{document}39\SweaveOpts{concordance=TRUE}4041\title{Custom Moment and Objective Functions}42\author{Ross Bennett}4344\date{May 17, 2018}4546\maketitle4748\begin{abstract}49The purpose of this vignette is to demonstrate how to write and use custom moment functions and custom objective functions to solve complex optimization problems.50\end{abstract}5152\tableofcontents5354\section{Getting Started}55\subsection{Load Packages}56Load the necessary packages.5758<<>>=59library(PortfolioAnalytics)60library(DEoptim)61@6263\subsection{Data}64The edhec data set from the PerformanceAnalytics package will be used as data for the following examples.65<<>>=66data(edhec)6768# Use the first 4 columns in edhec for a returns object69R <- edhec[, 1:4]70colnames(R) <- c("CA", "CTAG", "DS", "EM")71head(R, 5)7273# Get a character vector of the fund names74funds <- colnames(R)75@7677\section{Setting the Portfolio Moments}78The 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}.7980<<tidy=FALSE>>=81# Construct initial portfolio with basic constraints.82init.portf <- portfolio.spec(assets=funds)83init.portf <- add.constraint(portfolio=init.portf, type="full_investment")84init.portf <- add.constraint(portfolio=init.portf, type="long_only")8586# Portfolio with standard deviation as an objective87SD.portf <- add.objective(portfolio=init.portf, type="risk", name="StdDev")8889# Portfolio with expected shortfall as an objective90ES.portf <- add.objective(portfolio=init.portf, type="risk", name="ES")91@9293Here we see the names of the list object that is returned by \code{set.portfolio.moments}.94<<>>=95sd.moments <- set.portfolio.moments(R, SD.portf)96names(sd.moments)9798es.moments <- set.portfolio.moments(R, ES.portf)99names(es.moments)100@101102\section{Custom Moment Functions}103In 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.104105The moment function should return a named list object where the elements represent the moments:106\begin{description}107\item[\code{\$mu}]{ first moment; expected returns vector}108\item[\code{\$sigma}]{ second moment; covariance matrix}109\item[\code{\$m3}]{ third moment; coskewness matrix}110\item[\code{\$m4}]{ fourth moment; cokurtosis matrix}111\end{description}112113The 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.114115Here we define a function to estimate the covariance matrix using a robust method.116<<>>=117sigma.robust <- function(R){118require(MASS)119out <- list()120set.seed(1234)121out$sigma <- cov.rob(R, method="mcd")$cov122return(out)123}124@125126Now we can use the custom moment function in \code{optimize.portfolio} to estimate the solution to the minimum standard deviation portfolio.127<<tidy=FALSE>>=128opt.sd <- optimize.portfolio(R, SD.portf,129optimize_method="ROI",130momentFUN="sigma.robust")131opt.sd132@133134Here 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.135<<tidy=FALSE>>=136weights <- extractWeights(opt.sd)137sigma <- sigma.robust(R)$sigma138139sqrt(t(weights) %*% sigma %*% weights)140extractObjectiveMeasures(opt.sd)$StdDev141@142143\section{Custom Objective Functions}144A 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.145146Here we define a very simple function to compute annualized standard deviation for monthly data that we will use as an objective function.147<<>>=148pasd <- function(R, weights, sigma, N=36){149R <- tail(R, N)150tmp.sd <- sqrt(as.numeric(t(weights) %*% sigma %*% weights))151sqrt(12) * tmp.sd152}153@154155A few guidelines should be followed for defining a custom objective function.156157\begin{itemize}158\item The objective function must return a single value for the optimizer to minimize.159\item It is strongly encouraged to use the following argument names in the objective function:160\begin{description}161\item[\code{R}] {for the asset returns}162\item[\code{weights}] {for the portfolio weights}163\end{description}164\end{itemize}165166These 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.167168For 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.169170<<tidy=FALSE>>=171# Construct initial portfolio with basic constraints.172pasd.portf <- portfolio.spec(assets=funds)173pasd.portf <- add.constraint(portfolio=pasd.portf, type="full_investment")174pasd.portf <- add.constraint(portfolio=pasd.portf, type="long_only")175176# Portfolio with pasd as an objective177# Note how we can specify N as an argument178pasd.portf <- add.objective(portfolio=pasd.portf, type="risk", name="pasd",179arguments=list(N=48))180@181182183Now we can run the optimization to estimate a solution to our optimization problem.184<<>>=185opt.pasd <- optimize.portfolio(R, pasd.portf,186optimize_method="DEoptim",187search_size=5000, trace=TRUE, traceDE=0,188momentFUN="sigma.robust")189opt.pasd190@191192We 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}.193194\begin{equation*}195EU_{\lambda}(w) = - \frac{\lambda}{2} m_{(2)}(w) +196\frac{\lambda (\lambda + 1)}{6} m_{(3)}(w) -197\frac{\lambda (\lambda + 1) (\lambda + 2)}{24} m_{(4)}(w)198\end{equation*}199200Here 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.201202<<>>=203CRRA <- function(R, weights, lambda, sigma, m3, m4){204weights <- matrix(weights, ncol=1)205M2.w <- t(weights) %*% sigma %*% weights206M3.w <- t(weights) %*% m3 %*% (weights %x% weights)207M4.w <- t(weights) %*% m4 %*% (weights %x% weights %x% weights)208term1 <- (1 / 2) * lambda * M2.w209term2 <- (1 / 6) * lambda * (lambda + 1) * M3.w210term3 <- (1 / 24) * lambda * (lambda + 1) * (lambda + 2) * M4.w211out <- -term1 + term2 - term3212out213}214@215216We now define the custom moment function to compute the moments for the objective function.217<<>>=218crra.moments <- function(R, ...){219out <- list()220out$sigma <- cov(R)221out$m3 <- PerformanceAnalytics:::M3.MM(R)222out$m4 <- PerformanceAnalytics:::M4.MM(R)223out224}225@226227Finally, 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.228<<tidy=FALSE>>=229# Construct initial portfolio with basic constraints.230crra.portf <- portfolio.spec(assets=funds)231crra.portf <- add.constraint(portfolio=crra.portf, type="weight_sum",232min_sum=0.99, max_sum=1.01)233crra.portf <- add.constraint(portfolio=crra.portf, type="box",234min=0.05, max=0.4)235236# Portfolio with crra as an objective237# Note how we can specify lambda as an argument238crra.portf <- add.objective(portfolio=crra.portf, type="return", name="CRRA",239arguments=list(lambda=10))240@241242<<>>=243opt.crra <- optimize.portfolio(R, crra.portf, optimize_method="DEoptim",244search_size=5000, trace=TRUE, traceDE=0,245momentFUN="crra.moments")246opt.crra247@248249\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.250251\bibliography{PA}252253\end{document}254255256