#install.packages("PortfolioAnalytics")1. Introduction and Portfolio Theory
This chapter will give you a brief review of Modern Portfolio Theory and introduce you to the PortfolioAnalytics package by solving a couple portfolio optimization problems.
1.1 Welcome
Build on fundamental concepts from Introduction tp Portfolio Analysis in R
- Explore advanced concepts in the portfolio optimization process
- Complex constraints and objective sets
- Methods fo estimating moments (mean,var,)
- Periodic rebalancing (backtesting)
Visualization
Modern Portfolio Theory (MPT) was introduced by Harry Markowitz in 1952.
MPT states that an investor’s ojective is to maximize portfolio expected return for a given amount of risk.
- Common Academic Objective:
- Maximize a measure of gain per unit measure of risk
Minimize a measue of risk
- Real World Opt:
- Different measures of risk
- Multple objectives and constraints
Legality (gearing + L/S)
How to Do Mean/Standard Deviation
library(PortfolioAnalytics)## Warning: package 'PortfolioAnalytics' was built under R version 3.5.3
## Loading required package: zoo
## Warning: package 'zoo' was built under R version 3.5.3
##
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
## Loading required package: xts
## Warning: package 'xts' was built under R version 3.5.3
## Loading required package: foreach
## Warning: package 'foreach' was built under R version 3.5.3
## Loading required package: PerformanceAnalytics
## Warning: package 'PerformanceAnalytics' was built under R version 3.5.3
##
## Attaching package: 'PerformanceAnalytics'
## The following object is masked from 'package:graphics':
##
## legend
data(edhec)
data <- edhec[,1:8]
summary(edhec)## Index Convertible Arbitrage CTA Global
## Min. :1997-01-31 Min. :-0.123700 Min. :-0.054300
## 1st Qu.:2000-03-23 1st Qu.: 0.000925 1st Qu.:-0.011500
## Median :2003-05-15 Median : 0.009200 Median : 0.005250
## Mean :2003-05-16 Mean : 0.006409 Mean : 0.006489
## 3rd Qu.:2006-07-07 3rd Qu.: 0.014600 3rd Qu.: 0.022675
## Max. :2009-08-31 Max. : 0.061100 Max. : 0.069100
## Distressed Securities Emerging Markets Equity Market Neutral
## Min. :-0.083600 Min. :-0.192200 Min. :-0.058700
## 1st Qu.:-0.000175 1st Qu.:-0.011400 1st Qu.: 0.002400
## Median : 0.009700 Median : 0.013650 Median : 0.006300
## Mean : 0.007953 Mean : 0.008246 Mean : 0.006003
## 3rd Qu.: 0.018225 3rd Qu.: 0.028875 3rd Qu.: 0.010125
## Max. : 0.050400 Max. : 0.123000 Max. : 0.025300
## Event Driven Fixed Income Arbitrage Global Macro
## Min. :-0.088600 Min. :-0.086700 Min. :-0.031300
## 1st Qu.:-0.000025 1st Qu.: 0.002275 1st Qu.:-0.003600
## Median : 0.010150 Median : 0.006000 Median : 0.006200
## Mean : 0.007622 Mean : 0.004231 Mean : 0.007672
## 3rd Qu.: 0.018650 3rd Qu.: 0.009950 3rd Qu.: 0.016450
## Max. : 0.044200 Max. : 0.036500 Max. : 0.073800
## Long/Short Equity Merger Arbitrage Relative Value
## Min. :-0.06750 Min. :-0.054400 Min. :-0.069200
## 1st Qu.:-0.00370 1st Qu.: 0.001775 1st Qu.: 0.001850
## Median : 0.01015 Median : 0.007700 Median : 0.008450
## Mean : 0.00776 Mean : 0.006785 Mean : 0.006701
## 3rd Qu.: 0.02145 3rd Qu.: 0.013725 3rd Qu.: 0.014500
## Max. : 0.07450 Max. : 0.027200 Max. : 0.039200
## Short Selling Funds of Funds
## Min. :-0.134000 Min. :-0.061800
## 1st Qu.:-0.025450 1st Qu.:-0.003325
## Median :-0.000200 Median : 0.006800
## Mean : 0.004161 Mean : 0.005918
## 3rd Qu.: 0.035925 3rd Qu.: 0.015225
## Max. : 0.246300 Max. : 0.066600
# Create the porfolio specification for the 8 in data
port_spec <- portfolio.spec(colnames(data))
port_spec## **************************************************
## PortfolioAnalytics Portfolio Specification
## **************************************************
##
## Call:
## portfolio.spec(assets = colnames(data))
##
## Number of assets: 8
## Asset Names
## [1] "Convertible Arbitrage" "CTA Global"
## [3] "Distressed Securities" "Emerging Markets"
## [5] "Equity Market Neutral" "Event Driven"
## [7] "Fixed Income Arbitrage" "Global Macro"
# constraint 1: weighs must sum to 100%:
port_spec <- add.constraint(portfolio = port_spec, type= "full_investment")
# constraint 2: long only:
port_spec <- add.constraint(portfolio = port_spec, type= "long_only")
port_spec## **************************************************
## PortfolioAnalytics Portfolio Specification
## **************************************************
##
## Call:
## portfolio.spec(assets = colnames(data))
##
## Number of assets: 8
## Asset Names
## [1] "Convertible Arbitrage" "CTA Global"
## [3] "Distressed Securities" "Emerging Markets"
## [5] "Equity Market Neutral" "Event Driven"
## [7] "Fixed Income Arbitrage" "Global Macro"
##
## Constraints
## Enabled constraint types
## - full_investment
## - long_only
# objective 1: Maximizing pf return
port_spec <- add.objective(portfolio = port_spec,
type = "return",
name = "mean")
# objective 2: minimizing pf risk
port_spec <- add.objective(portfolio = port_spec,
type = "risk",
name = "StdDev")
print(port_spec)## **************************************************
## PortfolioAnalytics Portfolio Specification
## **************************************************
##
## Call:
## portfolio.spec(assets = colnames(data))
##
## Number of assets: 8
## Asset Names
## [1] "Convertible Arbitrage" "CTA Global"
## [3] "Distressed Securities" "Emerging Markets"
## [5] "Equity Market Neutral" "Event Driven"
## [7] "Fixed Income Arbitrage" "Global Macro"
##
## Constraints
## Enabled constraint types
## - full_investment
## - long_only
##
## Objectives:
## Enabled objective names
## - mean
## - StdDev
# Run optimization and chat results in risk-reward space
opt <- optimize.portfolio(data, portfolio = port_spec,
optimize_method = "random",
trace=TRUE)## Leverage constraint min_sum and max_sum are restrictive,
## consider relaxing. e.g. 'full_investment' constraint should be min_sum=0.99 and max_sum=1.01
## Warning: executing %dopar% sequentially: no parallel backend registered
chart.RiskReward(opt, risk.col = "StdDev", return.col = "mean",
chart.assets = TRUE) ### EX 2: Load the PortfolioAnalytics package The package PortfolioAnalytics will be used throughout this course for portfolio optimization and analysis. We will use the indexes dataset that is included with the PortfolioAnalytics package for the remaining exercises in this chapter. In this exercise, we will load the package and prepare the data for the portfolio optimization problem in the next exercise.
# Load the package
library(PortfolioAnalytics)
# Load the data
data(indexes)
# Subset the data
index_returns <- indexes[,1:4]
# Print the head of the data
print(head(index_returns))## Warning: index class is Date, which does not support timezones.
## Expected 'UTC' timezone, but indexTZ is ''
## US Bonds US Equities Int'l Equities Commodities
## 1980-01-31 -0.0272 0.0610 0.0462 0.0568
## 1980-02-29 -0.0669 0.0031 -0.0040 -0.0093
## 1980-03-31 0.0053 -0.0987 -0.1188 -0.1625
## 1980-04-30 0.0992 0.0429 0.0864 0.0357
## 1980-05-31 0.0000 0.0562 0.0446 0.0573
## 1980-06-30 0.0605 0.0296 0.0600 0.0533
EX 3: Solve a simple portfolio optimization problem
This first exercise will teach you how to solve a simple portfolio optimization problem using PortfolioAnalytics. You will learn how to create a portfolio specification object, add constraints and objectives, and solve the optimization problem. The portfolio problem is to form a minimum variance portfolio subject to full investment and long only constraints. The objective is to minimize portfolio variance. There are two constraints in this problem: the full investment constraint means that the weights must sum to 1, and the long only constraint means that all weights must be greater than or equal to 0 (i.e. no short positions are allowed).
#install.packages("ROI")
#install.packages("ROI.plugin.quadprog")
#install.packages("ROI.plugin.glpk")
library(ROI)## Warning: package 'ROI' was built under R version 3.5.3
## ROI: R Optimization Infrastructure
## Registered solver plugins: nlminb, glpk, quadprog.
## Default solver: auto.
##
## Attaching package: 'ROI'
## The following objects are masked from 'package:PortfolioAnalytics':
##
## is.constraint, objective
library(ROI.plugin.quadprog)## Warning: package 'ROI.plugin.quadprog' was built under R version 3.5.3
library(ROI.plugin.glpk)## Warning: package 'ROI.plugin.glpk' was built under R version 3.5.3
library(PortfolioAnalytics)
data(edhec)
# Create the porfolio specification for the 8 in data
port_spec <- portfolio.spec(colnames(data))
port_spec## **************************************************
## PortfolioAnalytics Portfolio Specification
## **************************************************
##
## Call:
## portfolio.spec(assets = colnames(data))
##
## Number of assets: 8
## Asset Names
## [1] "Convertible Arbitrage" "CTA Global"
## [3] "Distressed Securities" "Emerging Markets"
## [5] "Equity Market Neutral" "Event Driven"
## [7] "Fixed Income Arbitrage" "Global Macro"
# constraint 1: weighs must sum to 100%:
port_spec <- add.constraint(portfolio = port_spec, type= "full_investment")
# constraint 2: long only:
port_spec <- add.constraint(portfolio = port_spec, type= "long_only")
port_spec## **************************************************
## PortfolioAnalytics Portfolio Specification
## **************************************************
##
## Call:
## portfolio.spec(assets = colnames(data))
##
## Number of assets: 8
## Asset Names
## [1] "Convertible Arbitrage" "CTA Global"
## [3] "Distressed Securities" "Emerging Markets"
## [5] "Equity Market Neutral" "Event Driven"
## [7] "Fixed Income Arbitrage" "Global Macro"
##
## Constraints
## Enabled constraint types
## - full_investment
## - long_only
# Create the portfolio specification
port_spec <- portfolio.spec(colnames(index_returns))
# Add a full investment constraint such that the weights sum to 1
port_spec <- add.constraint(portfolio = port_spec, type = "full_investment")
# Add a long only constraint such that the weight of an asset is between 0 and 1
port_spec <- add.constraint(portfolio = port_spec, type = "long_only")
# Add an objective to minimize portfolio standard deviation
port_spec <- add.objective(portfolio = port_spec, type = "risk", name = "StdDev")
# Solve the optimization problem
opt <- optimize.portfolio(index_returns, portfolio = port_spec, optimize_method= "ROI")EX 4: Visualize results
Now that we have run the optimization, we would like to take a look at the output and results. Recall that the optimization output is in a variable named opt. In our case, for the portfolio optimization in the previous exercise, we are interested in the optimal weights and estimated objective value. The weights are considered optimal in the sense that the set of weights minimizes the objective value, portfolio standard deviation, and satisfies the full investment and long only constraints based on historical data.
Note that you won’t recognize some of these functions right now. Don’t worry! They will all be introduced throughout the course.
# Print the results of the optimization.
print(opt)## ***********************************
## PortfolioAnalytics Optimization
## ***********************************
##
## Call:
## optimize.portfolio(R = index_returns, portfolio = port_spec,
## optimize_method = "ROI")
##
## Optimal Weights:
## US Bonds US Equities Int'l Equities Commodities
## 0.8544 0.0587 0.0000 0.0869
##
## Objective Measure:
## StdDev
## 0.01668
# Extract the optimal weights
extractWeights(opt)## US Bonds US Equities Int'l Equities Commodities
## 0.85441777 0.05872385 0.00000000 0.08685838
# Chart the optimal weights
chart.Weights(opt) ## 1.2 Challenges of portfolio optimization
- Challenges
- Many solvers are not specific to portfolio optimization
- Understanding the capabilities and limits of solvers to select the appropriate solver for the problem or formulate the problem to fit the solver
- Difficult to switch between solvers
- Closed-Form solver (eg. quadratic programming)
- Global solver (eg. differential evolution optimization)
Quadratic Utility Optimization
# load quadprog
library(quadprog)
data(edhec)
dat <- edhec[,1:4]
# create the constraint matrix
Amat <- cbind(1, diag(ncol(dat)), -diag(ncol(dat)))
#create the constraint vector
bvec <- c(1, rep(0, ncol(dat)), -rep(1, ncol(dat)))
# create the objective matrix
Dmat <- 10 * cov(dat)
# create the objective vector
dvec <- colMeans(dat)
# Specify the number of equality contraints
meq <- 1
# solve the optimization problem
opt <- solve.QP(Dmat, dvec, Amat, bvec, meq)EX 9: Maximize quadratic utility function
In the video on challenges of portfolio optimization, you saw how to solve a quadratic utility optimization problem with the package quadprog. This exercise will show you how to solve a quadratic utility problem using the PortfolioAnalytics package. Recall the quadratic utility formulation has two terms, one for portfolio mean return and another for portfolio variance with a risk aversion parameter, lambda.
# Create the portfolio specification
port_spec <- portfolio.spec(assets = colnames(index_returns))
# Add a full investment constraint such that the weights sum to 1
port_spec <- add.constraint(portfolio = port_spec, type = "full_investment")
# Add a long only constraint such that the weight of an asset is between 0 and 1
port_spec <- add.constraint(portfolio = port_spec, type = "long_only")
# Add an objective to maximize portfolio mean return
port_spec <- add.objective(portfolio = port_spec, type = "return", name = "mean")
# Add an objective to minimize portfolio variance
port_spec <- add.objective(portfolio = port_spec, type = "risk", name = "var", risk_aversion = 10)
# Solve the optimization problem
opt <- optimize.portfolio(R = index_returns, portfolio = port_spec, optimize_method = "ROI")1.3 Introduction to PortfolioAnalytics
PortfolioAnalytics is designed to provide numerical solutions and visualzations for portfolio optimization problems with complex constraints and objectives.
- Supports:
- Multiple and modular constraint and objective types
- An objective funcion can be any valid R function
- User defned moment functions (covariance matrix, return projections)
- Visualization (builds intuition)
- Solver agnostic (supports several solvers)
Parallel computng
=====================================================================================================
2. Portfolio Optimization Workflow
The focus of this chapter is a detailed overview of the recommended workflow for solving portfolio optimization problems with PortfolioAnalytics. You will learn how to create a portfolio specification, add constraints, objectives, run the optimization, and analyze the results of the optimization output.
2.1 Portfolio specification, constraints, and objectves
- Workflow Overview General portfolio optimization problem wokflow in PortfolioAnalytics:
- Portfolio specification
- Add constraints and objectives
- Run optimization
- Analyze optimization results
- portfolio specification:
# character vector of assets
portfolio.spec(assets = c("SP500", "DJIA", "Nasdaq", "FTSE100", "DAX", "CAC40"))## **************************************************
## PortfolioAnalytics Portfolio Specification
## **************************************************
##
## Call:
## portfolio.spec(assets = c("SP500", "DJIA", "Nasdaq", "FTSE100",
## "DAX", "CAC40"))
##
## Number of assets: 6
## Asset Names
## [1] "SP500" "DJIA" "Nasdaq" "FTSE100" "DAX" "CAC40"
# Named vector of assets with initial weights
initial_weights <- c("SP500" = 0.5, "FTSE100" = 0.3, "NIKKEI" = 0.2)
portfolio.spec(assets = initial_weights)## **************************************************
## PortfolioAnalytics Portfolio Specification
## **************************************************
##
## Call:
## portfolio.spec(assets = initial_weights)
##
## Number of assets: 3
## Asset Names
## [1] "SP500" "FTSE100" "NIKKEI"
# Scalar of number of assets:
portfolio.spec(assets = 4)## **************************************************
## PortfolioAnalytics Portfolio Specification
## **************************************************
##
## Call:
## portfolio.spec(assets = 4)
##
## Number of assets: 4
## Asset Names
## [1] "Asset.1" "Asset.2" "Asset.3" "Asset.4"
- Add constraints
# Initialize portfolio specificaton
p <- portfolio.spec(assets = 4)
# Add full investment constraint
p <- add.constraint(portfolio = p, type = "weight_sum",
min_sum = 1, max_sum = 1)
# Add box constraint
p <- add.constraint(portfolio = p, type = "box", min = 0.2, max = 0.6)
p## **************************************************
## PortfolioAnalytics Portfolio Specification
## **************************************************
##
## Call:
## portfolio.spec(assets = 4)
##
## Number of assets: 4
## Asset Names
## [1] "Asset.1" "Asset.2" "Asset.3" "Asset.4"
##
## Constraints
## Enabled constraint types
## - weight_sum
## - box
- Add objective
# nitalize portfolio specification
p <- portfolio.spec(assets = 4)
# add mean return objective
p <- add.objective(portfolio = p, type = "return", name = "mean")
# add expected shortfall risk objective
p <- add.objective(portfolio = p, type = "risk", name = "ES",
arguments = list(p=0.9, method = "gaussian"))
p## **************************************************
## PortfolioAnalytics Portfolio Specification
## **************************************************
##
## Call:
## portfolio.spec(assets = 4)
##
## Number of assets: 4
## Asset Names
## [1] "Asset.1" "Asset.2" "Asset.3" "Asset.4"
##
## Objectives:
## Enabled objective names
## - mean
## - ES
EX 2: Create a portfolio specification
The first step in the workflow of PortfolioAnalytics is to create the portfolio specification object. The portfolio specification holds portfolio level data, constraints and objectives. The only required argument to portfolio.spec() is assets. assets can be the number of assets, a named vector of seed weights, or a character vector of the asset names. The category_labels argument is used for labeling assets by a category such as sector, industry, region, asset class, or currency. The weight_seq argument allows you to specify a seed sequence of weights that are used by the random portfolios algorithms. Common practice is to pass in the column names of the returns object for the assets argument.
# Load the package
library(PortfolioAnalytics)
data(edhec)
# Load the data
asset_returns <- edhec[,1:13]
head(asset_returns)## Convertible Arbitrage CTA Global Distressed Securities
## 1997-01-31 0.0119 0.0393 0.0178
## 1997-02-28 0.0123 0.0298 0.0122
## 1997-03-31 0.0078 -0.0021 -0.0012
## 1997-04-30 0.0086 -0.0170 0.0030
## 1997-05-31 0.0156 -0.0015 0.0233
## 1997-06-30 0.0212 0.0085 0.0217
## Emerging Markets Equity Market Neutral Event Driven
## 1997-01-31 0.0791 0.0189 0.0213
## 1997-02-28 0.0525 0.0101 0.0084
## 1997-03-31 -0.0120 0.0016 -0.0023
## 1997-04-30 0.0119 0.0119 -0.0005
## 1997-05-31 0.0315 0.0189 0.0346
## 1997-06-30 0.0581 0.0165 0.0258
## Fixed Income Arbitrage Global Macro Long/Short Equity
## 1997-01-31 0.0191 0.0573 0.0281
## 1997-02-28 0.0122 0.0175 -0.0006
## 1997-03-31 0.0109 -0.0119 -0.0084
## 1997-04-30 0.0130 0.0172 0.0084
## 1997-05-31 0.0118 0.0108 0.0394
## 1997-06-30 0.0108 0.0218 0.0223
## Merger Arbitrage Relative Value Short Selling Funds of Funds
## 1997-01-31 0.0150 0.0180 -0.0166 0.0317
## 1997-02-28 0.0034 0.0118 0.0426 0.0106
## 1997-03-31 0.0060 0.0010 0.0778 -0.0077
## 1997-04-30 -0.0001 0.0122 -0.0129 0.0009
## 1997-05-31 0.0197 0.0173 -0.0737 0.0275
## 1997-06-30 0.0231 0.0198 -0.0065 0.0225
#asset_names <- colnames(asset_returns)
#colnames(asset_returns)# Get the column names of the returns data
asset_names <- colnames(asset_returns)
asset_names## [1] "Convertible Arbitrage" "CTA Global"
## [3] "Distressed Securities" "Emerging Markets"
## [5] "Equity Market Neutral" "Event Driven"
## [7] "Fixed Income Arbitrage" "Global Macro"
## [9] "Long/Short Equity" "Merger Arbitrage"
## [11] "Relative Value" "Short Selling"
## [13] "Funds of Funds"
# Add a full investment constraint such that the weights sum to 1
port_spec <- portfolio.spec(assets = asset_names)
# Check the class of the portfolio specification object
class(port_spec)## [1] "portfolio.spec" "portfolio"
# Print the portfolio specification object
print(port_spec)## **************************************************
## PortfolioAnalytics Portfolio Specification
## **************************************************
##
## Call:
## portfolio.spec(assets = asset_names)
##
## Number of assets: 13
## Asset Names
## [1] "Convertible Arbitrage" "CTA Global"
## [3] "Distressed Securities" "Emerging Markets"
## [5] "Equity Market Neutral" "Event Driven"
## [7] "Fixed Income Arbitrage" "Global Macro"
## [9] "Long/Short Equity" "Merger Arbitrage"
## More than 10 assets, only printing the first 10
EX 3: Add Constraints
Constraints are added to the portfolio specification object with the add.constraint() function. Each constraint added is a separate object and stored in the constraints slot in the portfolio object. In this way, the constraints are modular and one can easily add, remove, or modify the constraints in the portfolio object. The required arguments for add.constraint() are the portfolio the constraint is added to, the constraint type, and named arguments passed via … to the constructor of the constraint type.
Basic constraint types:
Specify the constraint on the sum of the weights weight_sum, weight, leverage full_investment is a special case that sets min_sum = max_sum = 1 dollar_neutral is a special case that sets min_sum = max_sum = 0 Specify constraints for the individual asset weights box long_only is a special case that sets min = 0 and max = 1 Specify the constraint for the sum of weights of assets by group (sector, region, asset class, etc.) group Specify a constraint on the target mean return return In this exercise, you will add a few of the more common constraint types. In addition to the basic constraint types listed above, PortfolioAnalytics also supports position limit, turnover, diversification, factor exposure, and leverage exposure constraint types. If you are interested in the other constraint types, look at the help files for the constraint constructors. The help files include a description of the constraint type as well as example code.
# Add the weight sum constraint
port_spec <- add.constraint(portfolio = port_spec, type = "weight_sum", min_sum = 1, max_sum = 1)
# Add the box constraint
port_spec <- add.constraint(portfolio = port_spec, type = "box", min = c(0.1, 0.1, 0.1, 0.1, 0.1, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05), max = 0.4)
# Add the group constraint
port_spec <- add.constraint(portfolio = port_spec, type = "group", groups = list(c(1, 5, 7, 9, 10, 11), c(2, 3, 4, 6, 8, 12)), group_min = 0.4, group_max = 0.6)
# Print the portfolio specification object
print(port_spec)## **************************************************
## PortfolioAnalytics Portfolio Specification
## **************************************************
##
## Call:
## portfolio.spec(assets = asset_names)
##
## Number of assets: 13
## Asset Names
## [1] "Convertible Arbitrage" "CTA Global"
## [3] "Distressed Securities" "Emerging Markets"
## [5] "Equity Market Neutral" "Event Driven"
## [7] "Fixed Income Arbitrage" "Global Macro"
## [9] "Long/Short Equity" "Merger Arbitrage"
## More than 10 assets, only printing the first 10
##
## Constraints
## Enabled constraint types
## - weight_sum
## - box
## - group
EX 4: Add Objectives
Objectives are added to the portfolio object with the add.objective() function. Each objective added is a separate object and stored in the objectives slot in the portfolio specification object. In this way, the objectives are modular and one can easily add, remove, or modify the objective objects. The name argument must be a valid R function. Several functions are available in the PerformanceAnalytics package, but user defined functions can also be used as objective functions. The required arguments for add.objective() are the portfolio the objective is added to, the objective type, the objective name, and named arguments passed via … to the constructor of the objective type. Arguments for the objective function are specified as a named list to arguments.
Basic objective types:
return: This objective type seeks to maximize the objective. risk: This objective type seeks to minimize the objective. risk_budget: This objective type seeks to minimize risk concentration or penalize contribution to risk that exceeds the minimum or maximum allowable percentage contribution to risk. In addition to the objective types listed above, PortfolioAnalytics also supports quadratic utility and weight concentration objective types. If you are interested in the other constraint types, look at the help files for the constraint constructors. The help files include a description of the constraint type as well as example code.
# Add a return objective to maximize mean return
port_spec <- add.objective(portfolio = port_spec, type = "return", name = "mean")
# Add a risk objective to minimize portfolio standard deviation
port_spec <- add.objective(portfolio = port_spec, type = "risk", name = "StdDev")
# Add a risk budget objective
port_spec <- add.objective(portfolio = port_spec, type = "risk_budget", name = "StdDev", min_prisk = 0.05, max_prisk = 0.1)
# Print the portfolio specification object
print(port_spec)## **************************************************
## PortfolioAnalytics Portfolio Specification
## **************************************************
##
## Call:
## portfolio.spec(assets = asset_names)
##
## Number of assets: 13
## Asset Names
## [1] "Convertible Arbitrage" "CTA Global"
## [3] "Distressed Securities" "Emerging Markets"
## [5] "Equity Market Neutral" "Event Driven"
## [7] "Fixed Income Arbitrage" "Global Macro"
## [9] "Long/Short Equity" "Merger Arbitrage"
## More than 10 assets, only printing the first 10
##
## Constraints
## Enabled constraint types
## - weight_sum
## - box
## - group
##
## Objectives:
## Enabled objective names
## - mean
## - StdDev
## - StdDev
2.2 Running optimizations
- Single period Optimization
- optimize.portfolio()
Optimization with periodic rebalancing (backtesting) with optimize.portfolio.rebalancing()
- Optimization Methods
- The following optimization methods are supported:
- Global Solvers:
- DEoptim: Differential Evolution Optimization
- random: Random Portfolio Optimization
- GenSA: Generalized Simulated AnnealingAnalyze optimization results
pso: Particle Swam Optimization
- LP og QP Solvers:
ROI: R Optimization Infrastructure for linear and quadratic programming solvers
data(edhec)
ret <- edhec[,1:6]
# Portfolio
p <- portfolio.spec(assets = colnames(ret))
p <- add.constraint(portfolio = p, type = "full_investment")
p <- add.constraint(portfolio = p, type = "long_only")
p <- add.objective(portfolio = p, type = "risk", name = "StdDev")
p## **************************************************
## PortfolioAnalytics Portfolio Specification
## **************************************************
##
## Call:
## portfolio.spec(assets = colnames(ret))
##
## Number of assets: 6
## Asset Names
## [1] "Convertible Arbitrage" "CTA Global" "Distressed Securities"
## [4] "Emerging Markets" "Equity Market Neutral" "Event Driven"
##
## Constraints
## Enabled constraint types
## - full_investment
## - long_only
##
## Objectives:
## Enabled objective names
## - StdDev
# Optimizations
opt_single <- optimize.portfolio(R= ret, portfolio = p, optimize_method = "ROI")
opt_rebal <- optimize.portfolio(R= ret, portfolio = p, optimize_method = "ROI",
rebalance_on = "years", training_period = 60,
rolling_window = 60)EX 6: Single-Period optimization
There are two functions for running the optimization, optimize.portfolio() and optimize.portfolio.rebalancing(). This exercise will focus on single period optimization and the next exercise will use optimize.portfolio.rebalancing() for optimization with periodic rebalancing. optimize.portfolio() supports single-period optimization. Key arguments include R for the asset returns, portfolio for the portfolio specification object, and optimize_method to specify the optimization method used to solve the problem. In many cases, it is useful to specify trace = TRUE to store additional information for each iteration/trial of the optimization.
The following optimization methods are supported:
DEoptim: Differential evolution random: Random portfolios GenSA: Generalized Simulated Annealing pso: Particle swarm optimization ROI: R Optimization Infrastructure for linear and quadratic programming solvers The optimization method you choose should be based on the type of problem you are solving. For example, a problem that can be formulated as a quadratic programming problem should be solved using a quadratic programming solver, whereas a non-convex problem should be solved using a global solver such as DEoptim.
In this exercise, we will define the portfolio optimization problem to maximize mean return and minimize portfolio standard deviation with a standard deviation risk budget where the minimum percentage risk is 5% and the maximum percentage risk is 10%, subject to full investment and long only constraints. The risk budget objective requires a global solver so we will solve the problem using random portfolios. The set of random portfolios, rp, is generated using 500 permutations for this exercise.
# Run a single period optimization using random portfolios as the optimization method
#opt <- optimize.portfolio(R = asset_returns, portfolio = port_spec, optimize_method = "random", #rp = rp, trace = TRUE)
# Print the output of the single-period optimization
#print(opt)## EX 7: Optimization with periodic rebalancing Running the optimization with periodic rebalancing and analyzing the out-of-sample results of the backtest is an important step to better understand and potentially refine the constraints and objectives. optimize.portfolio.rebalancing() supports optimization with periodic rebalancing (backtesting) to examine out of sample performance. In addition to the arguments for optimize.portfolio(), a periodic rebalancing frequency must be specified with rebalance_on, training_period to specify the number of periods to use as the training data for the initial optimization, and rolling_window to specify the number of periods for the window width of the optimization. If rolling_window is set to NULL each optimization will use all data available at the given period the optimization is run.
To reduce computation time for this exercise, the set of random portfolios, rp, is generated using 50 permutations, and search_size, how many portfolios to test, is set to 1000. If you are actually optimizing portfolios yourself, you’ll probably want to test more portfolios (the default value for search_size is 20,000)!
# Run the optimization backtest with quarterly rebalancing
#opt_rebal <- optimize.portfolio.rebalancing(R = asset_returns, portfolio = port_spec, #optimize_method = "random", rp = rp, trace = TRUE, search_size = 1000, rebalance_on = #"quarters", training_period = 60, rolling_window = 60)
# Print the output of the optimization backtest
#print(opt_rebal)2.3 Analyzing Optimization Results
Visualization: * Plot() * chart.Concentration() * chart.EfficientFrontier() * chart.RiskReward() * chart.RiskBudget() * chart.Weights()
Data Extraction: * extractObjectiveMeasures() * extractStats() * extractWeights() * print() * summary()
chart.Weights(opt)chart.Weights(opt_rebal)rr <- Return.portfolio(ret, weights = extractWeights(opt_rebal))
charts.PerformanceSummary(rr)EX 10: Objective measure values
This exercise is a continuation of the last exercises analysing the output of opt and opt_rebal. All objectives in the portfolio specification are evaluated during the optimization and the values can be extracted with extractObjectiveMeasures(). The objective measures are an important component of the optimization output to analyze. An important use case looks at the result of each iteration/trial of the optimizer. This can give you insight into how the optimizer approaches the optimal solution and potentially tune parameters. We can also analyse the distribution of the in-sample objective values of the optimal portfolios and compare to out-of-sample performance.
# Extract the objective measures for the single period optimization
extractObjectiveMeasures(opt)## $mean
## mean
## 0.006914315
##
## $StdDev
## StdDev
## 0.01707766
# Extract the objective measures for the optimization backtest
extractObjectiveMeasures(opt_rebal)## $StdDev
## StdDev
## 0.008855401
EX 11: Optimal weights
This exercise is a continuation of the last exercises analysing the the output of opt and opt_rebal. Extracting and visualizing the optimal weights is an important component of the optimization. The optimal weights can be extracted with extractWeights() and charted with chart.Weights(). This is particularly useful for backtests to understand the evolution of weights over time. We can then answer questions about how allocations change through time.
# Extract the optimal weights for the single period optimization
extractWeights(opt)## US Bonds US Equities Int'l Equities Commodities
## 0.85072822 0.11516249 0.00000000 0.03410929
# Chart the weights for the single period optimization
chart.Weights(opt)# Extract the optimal weights for the optimization backtest
extractWeights(opt_rebal)## Convertible Arbitrage CTA Global Distressed Securities
## 0.000000e+00 6.515184e-02 5.840055e-18
## Emerging Markets Equity Market Neutral Event Driven
## -8.501425e-18 9.348482e-01 4.105887e-18
# Chart the weights for the optimization backtest
chart.Weights(opt_rebal)=====================================================================================================
3. Objective Functions and Moment Estimation
In this chapter, you will learn about estimating moments, characteristics of the distribution of asset returns, as well as custom objective functions.
3.1 Introduction to Moments
Portfolio optimization problem inputs: * Assets * Constraints * Objectives * Moments of asset returns
Asset Return Moments: * First Moment: expected return vector * Second Moment: variance-covariance matrix * Third Moment: coskewness matrix * Fourth Moment: cokurtosis matrix
Moments to estimate are determined by objectives and constraints
- Fx Mean - Variance optimization:
- Expected return vector
Coveriance matrix
- Fx min Var optimization only requires:
Covariance matrix
Asset Return Moment Estimates: Ledoit and Wolf (2003): “The central message of this paper is that nobody should be using the sample covariance matrix for the purpose of portfolio optimization”
Example of Methods: * Sample * Shrinkage Estimators * Factor Model * Expressing Views * Robust Statistics
20 Asset Portfolio: Method Sample k = 3 factors # of parameters 210 86
Calculating Moments in PortfolioAnalytics
- Set.portfolio.moments() supports several methods:
- Sample
- Boudt
- Black-Litterman
EX 2: Sample moment estimates
The default method for estimating portfolio moments is the sample method. The moments are calculated in optimize.portfolio() by evaluating the function passed to the momentFUN argument. The default for momentFUN is set.portfolio.moments() which defaults to calculating the sample moments. The moments are then used as inputs to the objective functions. The moments that must be estimated depend on the objectives. For example, an objective to minimize portfolio standard deviation requires only an estimate of the second moment. Compare that to the objective to maximize Sharpe Ratio which requires the first and second moments to be estimated. Sample estimates of the moments have disadvantages including estimation error and the curse of dimensionality. There is an increased risk of estimation error as the dimension of assets and parameters to estimate increase.
# Add a return objective with "mean" as the objective name
port_spec <- add.objective(portfolio = port_spec, type = "return", name = "mean")
# Calculate the sample moments
moments <- set.portfolio.moments(R = asset_returns, portfolio = port_spec)
# Check if moments$mu is equal to the sample estimate of mean returns using the provided code
moments$mu == colMeans(asset_returns)## [,1]
## [1,] TRUE
## [2,] TRUE
## [3,] TRUE
## [4,] TRUE
## [5,] TRUE
## [6,] TRUE
## [7,] TRUE
## [8,] TRUE
## [9,] TRUE
## [10,] TRUE
## [11,] TRUE
## [12,] TRUE
## [13,] TRUE
# Add a risk objective with "StdDev" as the objective name
port_spec <- add.objective(portfolio = port_spec, type = "risk", name = "StdDev")
# Calculate the sample moments using set.portfolio.moments. Assign to a variable named moments.
moments <- set.portfolio.moments(R = asset_returns, portfolio = port_spec)
# Check if moments$sigma is equal to the sample estimate of the variance-covariance matrix using the provided code
moments$sigma == cov(asset_returns)## Convertible Arbitrage CTA Global
## Convertible Arbitrage TRUE TRUE
## CTA Global TRUE TRUE
## Distressed Securities TRUE TRUE
## Emerging Markets TRUE TRUE
## Equity Market Neutral TRUE TRUE
## Event Driven TRUE TRUE
## Fixed Income Arbitrage TRUE TRUE
## Global Macro TRUE TRUE
## Long/Short Equity TRUE TRUE
## Merger Arbitrage TRUE TRUE
## Relative Value TRUE TRUE
## Short Selling TRUE TRUE
## Funds of Funds TRUE TRUE
## Distressed Securities Emerging Markets
## Convertible Arbitrage TRUE TRUE
## CTA Global TRUE TRUE
## Distressed Securities TRUE TRUE
## Emerging Markets TRUE TRUE
## Equity Market Neutral TRUE TRUE
## Event Driven TRUE TRUE
## Fixed Income Arbitrage TRUE TRUE
## Global Macro TRUE TRUE
## Long/Short Equity TRUE TRUE
## Merger Arbitrage TRUE TRUE
## Relative Value TRUE TRUE
## Short Selling TRUE TRUE
## Funds of Funds TRUE TRUE
## Equity Market Neutral Event Driven
## Convertible Arbitrage TRUE TRUE
## CTA Global TRUE TRUE
## Distressed Securities TRUE TRUE
## Emerging Markets TRUE TRUE
## Equity Market Neutral TRUE TRUE
## Event Driven TRUE TRUE
## Fixed Income Arbitrage TRUE TRUE
## Global Macro TRUE TRUE
## Long/Short Equity TRUE TRUE
## Merger Arbitrage TRUE TRUE
## Relative Value TRUE TRUE
## Short Selling TRUE TRUE
## Funds of Funds TRUE TRUE
## Fixed Income Arbitrage Global Macro
## Convertible Arbitrage TRUE TRUE
## CTA Global TRUE TRUE
## Distressed Securities TRUE TRUE
## Emerging Markets TRUE TRUE
## Equity Market Neutral TRUE TRUE
## Event Driven TRUE TRUE
## Fixed Income Arbitrage TRUE TRUE
## Global Macro TRUE TRUE
## Long/Short Equity TRUE TRUE
## Merger Arbitrage TRUE TRUE
## Relative Value TRUE TRUE
## Short Selling TRUE TRUE
## Funds of Funds TRUE TRUE
## Long/Short Equity Merger Arbitrage Relative Value
## Convertible Arbitrage TRUE TRUE TRUE
## CTA Global TRUE TRUE TRUE
## Distressed Securities TRUE TRUE TRUE
## Emerging Markets TRUE TRUE TRUE
## Equity Market Neutral TRUE TRUE TRUE
## Event Driven TRUE TRUE TRUE
## Fixed Income Arbitrage TRUE TRUE TRUE
## Global Macro TRUE TRUE TRUE
## Long/Short Equity TRUE TRUE TRUE
## Merger Arbitrage TRUE TRUE TRUE
## Relative Value TRUE TRUE TRUE
## Short Selling TRUE TRUE TRUE
## Funds of Funds TRUE TRUE TRUE
## Short Selling Funds of Funds
## Convertible Arbitrage TRUE TRUE
## CTA Global TRUE TRUE
## Distressed Securities TRUE TRUE
## Emerging Markets TRUE TRUE
## Equity Market Neutral TRUE TRUE
## Event Driven TRUE TRUE
## Fixed Income Arbitrage TRUE TRUE
## Global Macro TRUE TRUE
## Long/Short Equity TRUE TRUE
## Merger Arbitrage TRUE TRUE
## Relative Value TRUE TRUE
## Short Selling TRUE TRUE
## Funds of Funds TRUE TRUE
EX 3: Advanced moment estimates
PortfolioAnalytics supports the “sample” method as well as three more advanced methods for estimating portfolio moments.
“sample”: Basic sample estimate of first four moments. “boudt”: The first four moments are estimated by fitting a statistical factor model based on the work of Boudt et al., 2014. “black_litterman”: The first two moments are estimated using the Black-Litterman framework. “Meucci”: The first two moments are estimated using the Fully Flexible Views framework. In this exercise, you will estimate the second moment using the “boudt” method. A portfolio specification object named port_spec with a “StdDev” objective has already been created.
# Print the portfolio specification object
print(port_spec)## **************************************************
## PortfolioAnalytics Portfolio Specification
## **************************************************
##
## Call:
## portfolio.spec(assets = asset_names)
##
## Number of assets: 13
## Asset Names
## [1] "Convertible Arbitrage" "CTA Global"
## [3] "Distressed Securities" "Emerging Markets"
## [5] "Equity Market Neutral" "Event Driven"
## [7] "Fixed Income Arbitrage" "Global Macro"
## [9] "Long/Short Equity" "Merger Arbitrage"
## More than 10 assets, only printing the first 10
##
## Constraints
## Enabled constraint types
## - weight_sum
## - box
## - group
##
## Objectives:
## Enabled objective names
## - mean
## - StdDev
## - StdDev
## - mean
## - StdDev
# Fit a statistical factor model with k = 3 factors to the asset returns
fit <- statistical.factor.model(R = asset_returns, k = 3)
# Estimate the portfolio moments using the "boudt" method with k = 3 factors
moments_boudt <- set.portfolio.moments(R = asset_returns, portfolio = port_spec, method = "boudt", k = 3)
# Check if the covariance matrix extracted from the model fit is equal to the estimate in `moments_boudt`
moments_boudt$sigma == extractCovariance(fit)## Convertible Arbitrage CTA Global
## Convertible Arbitrage TRUE TRUE
## CTA Global TRUE TRUE
## Distressed Securities TRUE TRUE
## Emerging Markets TRUE TRUE
## Equity Market Neutral TRUE TRUE
## Event Driven TRUE TRUE
## Fixed Income Arbitrage TRUE TRUE
## Global Macro TRUE TRUE
## Long/Short Equity TRUE TRUE
## Merger Arbitrage TRUE TRUE
## Relative Value TRUE TRUE
## Short Selling TRUE TRUE
## Funds of Funds TRUE TRUE
## Distressed Securities Emerging Markets
## Convertible Arbitrage TRUE TRUE
## CTA Global TRUE TRUE
## Distressed Securities TRUE TRUE
## Emerging Markets TRUE TRUE
## Equity Market Neutral TRUE TRUE
## Event Driven TRUE TRUE
## Fixed Income Arbitrage TRUE TRUE
## Global Macro TRUE TRUE
## Long/Short Equity TRUE TRUE
## Merger Arbitrage TRUE TRUE
## Relative Value TRUE TRUE
## Short Selling TRUE TRUE
## Funds of Funds TRUE TRUE
## Equity Market Neutral Event Driven
## Convertible Arbitrage TRUE TRUE
## CTA Global TRUE TRUE
## Distressed Securities TRUE TRUE
## Emerging Markets TRUE TRUE
## Equity Market Neutral TRUE TRUE
## Event Driven TRUE TRUE
## Fixed Income Arbitrage TRUE TRUE
## Global Macro TRUE TRUE
## Long/Short Equity TRUE TRUE
## Merger Arbitrage TRUE TRUE
## Relative Value TRUE TRUE
## Short Selling TRUE TRUE
## Funds of Funds TRUE TRUE
## Fixed Income Arbitrage Global Macro
## Convertible Arbitrage TRUE TRUE
## CTA Global TRUE TRUE
## Distressed Securities TRUE TRUE
## Emerging Markets TRUE TRUE
## Equity Market Neutral TRUE TRUE
## Event Driven TRUE TRUE
## Fixed Income Arbitrage TRUE TRUE
## Global Macro TRUE TRUE
## Long/Short Equity TRUE TRUE
## Merger Arbitrage TRUE TRUE
## Relative Value TRUE TRUE
## Short Selling TRUE TRUE
## Funds of Funds TRUE TRUE
## Long/Short Equity Merger Arbitrage Relative Value
## Convertible Arbitrage TRUE TRUE TRUE
## CTA Global TRUE TRUE TRUE
## Distressed Securities TRUE TRUE TRUE
## Emerging Markets TRUE TRUE TRUE
## Equity Market Neutral TRUE TRUE TRUE
## Event Driven TRUE TRUE TRUE
## Fixed Income Arbitrage TRUE TRUE TRUE
## Global Macro TRUE TRUE TRUE
## Long/Short Equity TRUE TRUE TRUE
## Merger Arbitrage TRUE TRUE TRUE
## Relative Value TRUE TRUE TRUE
## Short Selling TRUE TRUE TRUE
## Funds of Funds TRUE TRUE TRUE
## Short Selling Funds of Funds
## Convertible Arbitrage TRUE TRUE
## CTA Global TRUE TRUE
## Distressed Securities TRUE TRUE
## Emerging Markets TRUE TRUE
## Equity Market Neutral TRUE TRUE
## Event Driven TRUE TRUE
## Fixed Income Arbitrage TRUE TRUE
## Global Macro TRUE TRUE
## Long/Short Equity TRUE TRUE
## Merger Arbitrage TRUE TRUE
## Relative Value TRUE TRUE
## Short Selling TRUE TRUE
## Funds of Funds TRUE TRUE
3.2 Custom Moment Functions
A custom moment function is a user defined function
- Arguments
- R for asset returns
- portfolio for the portfolio specification object
- return a named lsit where the elements represent the moments
- mu: Expected returns vector
- sigma: Variance-covariance matrix
- m3: Coskewness matrix
- m4: Cokurtosis matrix
EX: 6: Define a custom moment function
In many cases for constrained optimization problems, the portfolio manager or analyst may want to estimate moments for a specific technique and/or further extend the idea of set.portfolio.moments(). A user defined custom moment function can have any arbitrary named arguments. However, arguments named R for the asset returns and portfolio for the portfolio object will be detected automatically and handled in an efficient manner. Because of this, it is strongly encouraged to use R for the asset returns object and portfolio for the portfolio object.
The custom moment function should return a named list where the elements represent the moments:
$mu: first moment (expected returns vector) $sigma: second moment (variance-covariance matrix) $m3: third moment (coskewness matrix) $m4: fourth moment (cokurtosis matrix) In this exercise, you will write a custom moment function to estimate the variance-covariance matrix using a robust method. We will use the cov.rob() function from the MASS package. The function signature should have arguments named R for the asset returns and portfolio for the specification object. The function should return a named list. Because you are only estimating the second moment, you only need to return a list with one element appropriately named. You can apply these rules to write custom moment functions for other models such as factor models, GARCH models, or any other class of models that theoretically should be a better estimate than the sample estimate.
# Define a function named moments_robust that estimates the variance-covariance matrix of the asset returns
moments_robust <- function(R, portfolio){
out <- list()
out$sigma <- cov.rob(R, method = "mcd")$cov
out
}
# Estimate the portfolio moments using the function you just defined
# moments <- moments_robust(R = asset_returns, portfolio = port_spec)
# Check if the variance-covariance matrix estimated using cov.rob directly is equal to the estimate in `moments`
# cov.rob(asset_returns, method = "mcd")$cov == moments$sigmaEX 7: Optimization with custom moment function
Now we would like to run the optimization using our custom moment function. Recall that the portfolio moments are set in optimize.portfolio() when the moment function is evaluated. We use the custom moment function by passing in the name to the momentFUN argument in optimize.portfolio(). Note how we can use PortfolioAnalytics to easily run optimizations using different methods for estimating moments, which will allow us to evaluate different techniques for moment estimates and refine those estimates by analyzing the optimization results.
# Run the optimization with custom moment estimates
#opt_custom <- optimize.portfolio(R = asset_returns, portfolio = port_spec, optimize_method = "random", rp = rp, momentFUN = "moments_robust")
# Print the results of the optimization with custom moment estimates
# print(opt_custom)
# Run the optimization with sample moment estimates
#opt_sample <- optimize.portfolio(R = asset_returns, portfolio = port_spec, optimize_method = "random", rp = rp)
# Print the results of the optimization with sample moment estimates
#print(opt_sample)3.3 Objective functions
Objective functions compute the objective value. In PortfolioAnalytics, objective functions can be any valid R function. * Common portfolio risk measures: * Stdev, Exp. Shortfall, VAR, Component contribution to risk, Max DD, SR * Common benchmark relative perofrmance measures (Long only) * Information ratio, tracking error, excess rtn, max rel DD
EX 9: Custom objective function
A key feature of PortfolioAnalytics is that the name for an objective is a valid R function. The package was designed to be flexible and modular, and custom objective functions are a great example of this. A few guidelines should be followed for defining a custom moment function:
The objective function must return a single value for the optimizer to minimize or maximize. It is strongly encouraged to use R for the asset returns and weights for the portfolio weights. These argument names are detected automatically and handled in an efficient manner. Any other arguments for the objective function can be passed in as a named list to arguments in the add.objective() function.
# Custom annualized portfolio standard deviation
pasd <- function(R, weights, sigma, scale = 12){
sqrt(as.numeric(t(weights) %*% sigma %*% weights)) * sqrt(scale)
}EX 10: Optimization with custom objective function
This exercise builds on the previous exercise and we will run the optimization with the custom objective function that computes portfolio annualized standard deviation. Because an objective function can be any valid R function, we add a risk objective for the pasd() function. The set.portfolio.moments() function will not recognize the pasd() objective name, so we need to create a custom moment function to calculate the second moment, sigma. We will solve the problem using random portfolios as the optimization method.
# Add custom objective to portfolio specification
port_spec <- add.objective(portfolio = port_spec, type = "risk", name = "pasd")
# Print the portfolio specificaton object
print(port_spec)## **************************************************
## PortfolioAnalytics Portfolio Specification
## **************************************************
##
## Call:
## portfolio.spec(assets = asset_names)
##
## Number of assets: 13
## Asset Names
## [1] "Convertible Arbitrage" "CTA Global"
## [3] "Distressed Securities" "Emerging Markets"
## [5] "Equity Market Neutral" "Event Driven"
## [7] "Fixed Income Arbitrage" "Global Macro"
## [9] "Long/Short Equity" "Merger Arbitrage"
## More than 10 assets, only printing the first 10
##
## Constraints
## Enabled constraint types
## - weight_sum
## - box
## - group
##
## Objectives:
## Enabled objective names
## - mean
## - StdDev
## - StdDev
## - mean
## - StdDev
## - pasd
# Run the optimization
#opt <- optimize.portfolio(R = asset_returns, portfolio = port_spec, momentFUN = set_sigma, optimize_method = "random", rp = rp)
# Print the results of the optimization
print(opt)## ***********************************
## PortfolioAnalytics Optimization
## ***********************************
##
## Call:
## optimize.portfolio(R = index_returns, portfolio = port_spec,
## optimize_method = "ROI")
##
## Optimal Weights:
## US Bonds US Equities Int'l Equities Commodities
## 0.8507 0.1152 0.0000 0.0341
##
## Objective Measure:
## mean
## 0.006914
##
##
## StdDev
## 0.01708
=====================================================================================================
4. Application
In the final chapter of the course, you will solve a portfolio optimization problem that mimics a real world real world example of constructing a portfolio of hedge fund strategy with different style definitions.
4.1 Time to apply what you have learned
- Real world example
- Solve a portfolio optimization problem similar to the types of problems in the industry
- Apply techniques learned throughout the course
- Specify a portfolio with constants and objectives
- Run the optimization with period rebalancing on historical data
- Analyse the results
- Refine constraints, objhectives, and moment estimates
- Data:
- EDHEC-RISK Alternative Indexes monthly returns
- Jan 1997 - March 2016
data(indexes)
returns <- indexes[,1:4]
head(returns)## Warning: index class is Date, which does not support timezones.
## Expected 'UTC' timezone, but indexTZ is ''
## US Bonds US Equities Int'l Equities Commodities
## 1980-01-31 -0.0272 0.0610 0.0462 0.0568
## 1980-02-29 -0.0669 0.0031 -0.0040 -0.0093
## 1980-03-31 0.0053 -0.0987 -0.1188 -0.1625
## 1980-04-30 0.0992 0.0429 0.0864 0.0357
## 1980-05-31 0.0000 0.0562 0.0446 0.0573
## 1980-06-30 0.0605 0.0296 0.0600 0.0533
Base Portfolio Definition
Define a portfolio specification to be used at the base case
- The base portfolio specification is meant to be a simple approach with relexed constraints and basic objectives
- Do not overcomplicate or over-constrain
# base portfolio spec:
base_port_spec <- portfolio.spec(assets = colnames(returns))
base_port_spec <- add.constraint(portfolio = base_port_spec, type = "full_investments")
base_port_spec <- add.constraint(portfolio = base_port_spec, type="long_only")
base_port_spec <- add.objective(portfolio=base_port_spec, type = "risk", name = "StdDev")EX 2:Compute benchmark returns
In this exercise, we will create a benchmark to evaluate the performance of the optimization models in later exercises. An equal weight benchmark is a simple weighting scheme to construct the benchmark portfolio. The intuition of an equal weight approach is that there is no preference for any given asset. We are setting this up to answer the question, “Can optimization outperform a simple weighting scheme to construct a portfolio?”
# Load the package
library(PortfolioAnalytics)
# Load the data
data(edhec)
# Assign the data to a variable
asset_returns <- edhec
# Create a vector of equal weights
equal_weights <- rep(1 / ncol(asset_returns), ncol(asset_returns))
# Compute the benchmark returns
r_benchmark <- Return.portfolio(asset_returns, weights = equal_weights, rebalance_on = "quarters")
colnames(r_benchmark) <- "benchmark"
# Plot the benchmark returns
plot(r_benchmark)EX 3: Define the portfolio optimization problem
We define the portfolio optimization problem to minimize portfolio standard deviation subject to full investment and long only constraints. In this problem, we will set up the portfolio specification based on the defined problem. The following exercises in this chapter will build on the initial portfolio specification set up here.
# Create the portfolio specification
port_spec <- portfolio.spec(colnames(asset_returns))
# Add a full investment constraint such that the weights sum to 1
port_spec <- add.constraint(portfolio = port_spec, type = "full_investment")
# Add a long only constraint such that the weight of an asset is between 0 and 1
port_spec <- add.constraint(portfolio = port_spec, type = "long_only")
# Add an objective to minimize portfolio standard deviation
port_spec <- add.objective(portfolio = port_spec, type = "risk", name = "StdDev")
# Print the portfolio specification
print(port_spec)## **************************************************
## PortfolioAnalytics Portfolio Specification
## **************************************************
##
## Call:
## portfolio.spec(assets = colnames(asset_returns))
##
## Number of assets: 13
## Asset Names
## [1] "Convertible Arbitrage" "CTA Global"
## [3] "Distressed Securities" "Emerging Markets"
## [5] "Equity Market Neutral" "Event Driven"
## [7] "Fixed Income Arbitrage" "Global Macro"
## [9] "Long/Short Equity" "Merger Arbitrage"
## More than 10 assets, only printing the first 10
##
## Constraints
## Enabled constraint types
## - full_investment
## - long_only
##
## Objectives:
## Enabled objective names
## - StdDev
4.2 Optimization Backtest
opt_base <- optimize.portfolio.rebalancing(R=returns,
optimize_method= "ROI",
portfolio= base_port_spec,
rebalance_on="quarters",
training_period=60,
rolling_window=60)
#Calculate portfolio returns
base_returns <- Return.portfolio(returns,extractWeights(opt_base))
colnames(base_returns) <- "base"
# Chart the optimal weights
chart.Weights(opt_base)# Merge benchmark and portfolio rtn
#ret <- cbind(benchmark_returns, base_returns)
#annualized retrurns
#table.AnnualizedReturns((ret))EX 6: Backtest with periodic rebalancing
Now we will run the backtest using the portfolio specification created in the last exercise with quarterly rebalancing to evaluate out-of-sample performance. The other backtest parameters we need to set are the training period and rolling window. The training period sets the number of data points to use for the initial optimization. The rolling window sets the number of periods to use in the window. This problem can be solved with a quadratic programming solver so we will use “ROI” for the optimization method.
# Run the optimization
opt_rebal_base <- optimize.portfolio.rebalancing(R = asset_returns,
portfolio = port_spec,
optimize_method = "ROI",
rebalance_on = "quarters",
training_period = 60,
rolling_window = 60)
# Print the results
print(opt_rebal_base)## **************************************************
## PortfolioAnalytics Optimization with Rebalancing
## **************************************************
##
## Call:
## optimize.portfolio.rebalancing(R = asset_returns, portfolio = port_spec,
## optimize_method = "ROI", rebalance_on = "quarters", training_period = 60,
## rolling_window = 60)
##
## Number of rebalancing dates: 32
## First rebalance date:
## [1] "2001-12-31"
## Last rebalance date:
## [1] "2009-08-31"
##
## Annualized Portfolio Rebalancing Return:
## [1] 0.05127487
##
## Annualized Portfolio Standard Deviation:
## [1] 0.02049866
# Chart the weights
chart.Weights(opt_rebal_base)# Compute the portfolio returns
returns_base <- Return.portfolio(R = asset_returns, weights = extractWeights(opt_rebal_base))
colnames(returns_base) <- "base"EX 7: Refine constraints and objectives
Here we hypothesize that refining constraints and/or objectives will improve performance. Let us add a risk budget objective to set a minimum and maximum percentage contribution to risk for each asset. We will be building on the portfolio specification we created. This is a more complex optimization problem and will require a global solver so we will use random portfolios as the optimization method.
# Add a risk budge objective
port_spec <- add.objective(portfolio = port_spec,
type = "risk_budget",
name = "StdDev",
min_prisk = 0.05,
max_prisk = 0.1)
# Run the optimization
# opt_rebal_rb <- optimize.portfolio.rebalancing(R = asset_returns,
# portfolio = port_spec,
# optimize_method = "random", rp =rp,
# trace = TRUE,
# rebalance_on = "quarters",
# training_period = 60,
# rolling_window = 60)
# Chart the weights
# chart.Weights(opt_rebal_rb)
# Chart the percentage contribution to risk
# chart.RiskBudget(opt_rebal_rb, match.col = "StdDev", risk.type = "percentage")
# Compute the portfolio returns
#returns_rb <- Return.portfolio(R = asset_returns, weights = extractWeights(opt_rebal_rb))
#colnames(returns_rb) <- "risk_budget"EX 8: Do improved estimates lead to improved performance?
Let us hypothesize that using a robust estimate of the variance-covariance matrix will outperform the sample variance covariance matrix. In theory, better estimates should lead to better results. We will use the moments_robust() function that was defined in chapter 3 and the portfolio specification from the last exercise.
# Run the optimization
#pt_rebal_rb_robust <- optimize.portfolio.rebalancing(R = asset_returns,
# momentFUN = "moments_robust",
# portfolio = port_spec,
# optimize_method = "random", rp = rp,
# trace = TRUE,
# rebalance_on = "quarters",
# training_period = 60,
# rolling_window = 60)
# Chart the weights
#hart.Weights(opt_rebal_rb_robust)
# Chart the percentage contribution to risk
#Chart.RiskBudget(opt_rebal_rb_robust, match.col = "StdDev", risk.type = "percentage")
# Compute the portfolio returns
# returns_rb_robust <- Return.portfolio(R = asset_returns, weights = #extractWeights(opt_rebal_rb_robust))
# colnames(returns_rb_robust) <- "rb_robust"ex 9: Analyze results and compare to benchmark
In the previous exercises of the chapter, we created an equal weight benchmark r_benchmark and ran the following optimizations:
Minimize portfolio standard deviation with sample estimates (returns stored in returns_base). Minimize portfolio standard deviation with percentage contribution to risk using sample estimates (returns stored in returns_rb). Minimize portfolio standard deviation with percentage contribution to risk using robust estimates (returns stored in returns_rb_robust). Now we wish to analyze the performance of the optimization backtests and compare with the benchmark.
# Combine the returns
#ret <- cbind(r_benchmark, returns_base, returns_rb, returns_rb_robust)
# Compute annualized returns
# table.AnnualizedReturns(R = ret)
# Chart the performance summary
# charts.PerformanceSummary(R = ret)