Feasible Space Analysis & Hierarchical Optimization with PortfolioAnalytics

R/Finance 2016

Ross Bennett

Overview

  • Discuss Portfolio Optimization
  • Introduce PortfolioAnalytics
  • Demonstrate PortfolioAnalytics with Examples

Modern Portfolio Theory

"Modern" Portfolio Theory (MPT) was introduced by Harry Markowitz in 1952.

In general, MPT states that an investor's objective is to maximize portfolio expected return for a given amount of risk.

Common Objectives

  • Maximize a measure of gain per unit measure of risk
  • Minimize a measure of risk

How do we define risk? What about more complex objectives and constraints?

Portfolio Optimization Objectives

  • Minimize Risk
    • Volatility
    • Tail Loss (VaR, ES)
    • Other Downside Risk Measure
  • Maximize Risk Adjusted Return
    • Sharpe Ratio, Modified Sharpe Ratio
    • Several Others
  • Risk Budgets
    • Equal Component Contribution to Risk (i.e. Risk Parity)
    • Limits on Component Contribution
  • Maximize a Utility Function
    • Quadratic, Constant Relative Risk Aversion (CRRA), etc.

PortfolioAnalytics Overview

PortfolioAnalytics is an R package designed to provide numerical solutions and visualizations for portfolio optimization problems with complex constraints and objectives.

Supports:

  • multiple constraint and objective types
  • modular constraints and objectives
  • an objective function can be any valid R function
  • user defined moment functions (e.g. covariance matrix, return projections)
  • visualizations
  • solver agnostic
  • parallel computing

Hierarchical (i.e. Multilayer) Optimization

Support Multiple Solvers

Linear and Quadratic Programming Solvers

  • R Optimization Infrastructure (ROI)
    • GLPK (Rglpk)
    • Symphony (Rsymphony)
    • Quadprog (quadprog)

Global (stochastic or continuous solvers)

  • Random Portfolios
  • Differential Evolution (DEoptim)
  • Particle Swarm Optimization (pso)
  • Generalized Simulated Annealing (GenSA)

Random Portfolios

PortfolioAnalytics has three methods to generate random portfolios.

  1. The sample method to generate random portfolios is based on an idea by Pat Burns.
  2. The simplex method to generate random portfolios is based on a paper by W. T. Shaw.
  3. The grid method to generate random portfolios is based on the gridSearch function in the NMOF package.

Comparison of Random Portfolio Methods (Interactive!)

Random Portfolios: Simplex Method

Workflow: Specify Portfolio

args(portfolio.spec)
## function (assets = NULL, category_labels = NULL, weight_seq = NULL, 
##     message = FALSE) 
## NULL

Initializes the portfolio object that holds portfolio level data, constraints, and objectives

Workflow: Add Constraints

args(add.constraint)
## function (portfolio, type, enabled = TRUE, message = FALSE, ..., 
##     indexnum = NULL) 
## NULL

Supported Constraint Types

  • Sum of Weights
  • Box
  • Group
  • Factor Exposure
  • Position Limit
  • and many more

Workflow: Add Objectives

args(add.objective)
## function (portfolio, constraints = NULL, type, name, arguments = NULL, 
##     enabled = TRUE, ..., indexnum = NULL) 
## NULL

Supported Objective types

  • Return
  • Risk
  • Risk Budget
  • Weight Concentration

Workflow: Run Optimization

args(optimize.portfolio)
## function (R, portfolio = NULL, constraints = NULL, objectives = NULL, 
##     optimize_method = c("DEoptim", "random", "ROI", "pso", "GenSA"), 
##     search_size = 20000, trace = FALSE, ..., rp = NULL, momentFUN = "set.portfolio.moments", 
##     message = FALSE) 
## NULL
args(optimize.portfolio.rebalancing)
## function (R, portfolio = NULL, constraints = NULL, objectives = NULL, 
##     optimize_method = c("DEoptim", "random", "ROI"), search_size = 20000, 
##     trace = FALSE, ..., rp = NULL, rebalance_on = NULL, training_period = NULL, 
##     rolling_window = NULL) 
## NULL

Workflow: Analyze Results

Visualization Data Extraction
plot extractObjectiveMeasures
chart.Concentration extractStats
chart.EfficientFrontier extractWeights
chart.RiskReward print
chart.RiskBudget summary
chart.Weights

Portfolio Optimization

Estimating Moments

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."

Feasible Space Analysis: Data

Here we will analyse the feasible space of allocations to hedge fund strategies

  • EDHEC-Risk Alternative Indexes monthly returns from 1/31/1997 to 12/31/2014
Relative Value Directional
Convertible Arbitrage (CA) CTA Global (CTAG)
Equity Market Neutral (EMN) Emerging Markets (EM)
Fixed Income Arbitrage (FIA) Global Macro (GM)
Event Driven (ED) Distressed Securites (DS)

Feasible Space Analysis: Simple Efficient Frontier

# define base portfolio
portf.base <- portfolio.spec(colnames(R))
portf.base <- add.constraint(portf.base, type="weight_sum",
                             min_sum=0.99, max_sum=1.01)
portf.base <- add.constraint(portf.base, type="box", min=0, max=1)
ef <- create.EfficientFrontier(R, portfolio=portf.base, 
                               type="mean-StdDev", n.portfolios=100)
chart.EfficientFrontier(ef, match.col="StdDev", pch=18, col="lightblue")

Feasible Space Analysis: Simple Efficient Frontier

Feasible Space Analysis: Entire Feasible Space

p <- portfolio.spec(colnames(R))
p <- add.constraint(p, type="weight_sum", min_sum=0.99, max_sum=1.01)
p <- add.constraint(p, type="box", min=0, max=1)
p <- add.objective(p, type="return", name="mean", multiplier=0)
p <- add.objective(p, type="risk", name="StdDev", multiplier=0)
rp <- random_portfolios(p, permutations=5000, rp_method='sample')
opt <- optimize.portfolio(R, p, optimize_method="random", rp=rp, trace=TRUE)
xt <- extractStats(opt)

Feasible Space Analysis: Relaxed Constraints

Feasible Space Analysis: Box Constraints

Feasible Space Analysis: Long Only and Group Constraints

Feasible Space Analysis: Long Only and Position Limit Constraints

Feasible Space Analysis: Shorting Allowed and Leverage Constraints

Heierarchical Optimization Example

Consider an equity long short portfolio

  • We first consider a 'naive' model and specify the portfolio and constraints

  • Extend the model and separate the portfolio into a long portfolio and a short portfolio

  • Further extend the model and use a framework that allows one to express a view and reduce allocation/exposure to the short portfolio

Hierarchical Optimization: Data

Consider an allocation to equity sectors using 9 sector ETFs

Hierarchical Optimization: Naive Portfolio

portf.naive <- portfolio.spec(colnames(R))
portf.naive <- add.constraint(portf.naive, type="weight_sum",
                              min_sum=-0.05, max_sum=0.05)
portf.naive <- add.constraint(portf.naive, type="box", min=-0.5, max=0.5)
portf.naive <- add.constraint(portf.naive, type = "leverage_exposure", leverage=2)
portf.naive <- add.objective(portf.naive, type="risk", name="StdDev")
portf.naive <- add.objective(portf.naive, type="risk_budget",
                             name="StdDev", max_prisk=0.50)

Hierarchical Optimization: Naive Portfolio Optimization

rp.naive <- random_portfolios(portf.naive, permutations=1000, rp_method='sample')
opt.naive <- optimize.portfolio.rebalancing(R, portf.naive,
                                            optimize_method="random",
                                            rebalance_on="quarters",
                                            training_period=36,
                                            rp=rp.naive, trace=TRUE)
# compute arithmetic portfolio returns because of negative weights
ret.naive <- Return.portfolio(R, extractWeights(opt.naive), geometric = FALSE)
colnames(ret.naive) <- "naive.ls"
charts.PerformanceSummary(ret.naive)

Hierarchical Optimization: Naive Portfolio Optimization Results

Hierarchical Optimization: Define Long and Short Portfolio

# long portfolio
p.long <- portfolio.spec(assets=colnames(R))
p.long <- add.constraint(p.long, type="weight_sum", min_sum=0.99, max_sum=1.01)
p.long <- add.constraint(p.long, type="box", min=0, max=0.85)
p.long <- add.objective(p.long, type="risk", name="StdDev")
p.long <- add.objective(p.long, type="risk_budget", name="StdDev", max_prisk=0.50)
rp.long <- random_portfolios(p.long, permutations=1000, rp_method='sample')
# short portfolio
p.short <- portfolio.spec(assets=colnames(R))
p.short <- add.constraint(p.short, type="weight_sum", min_sum=-1.01, max_sum=-0.99)
p.short <- add.constraint(p.short, type="box", min=-0.85, max=0)
p.short <- add.objective(p.short, type="risk", name="StdDev")
p.short <- add.objective(p.short, type="risk_budget", name="StdDev", max_prisk=0.50)
rp.short <- random_portfolios(p.short, permutations=1000, rp_method='sample')

Hierarchical Optimization: Specify Portfolio Heirerarchy

# combined portfolio
p <- portfolio.spec(assets=paste("proxy",1:2, sep="."))
p <- add.constraint(p, type="weight_sum", min_sum=0.99, max_sum=1.01)
p <- add.constraint(p, type="box", min=0.1, max=1)
p <- add.objective(p, type="return", name="mean")
p <- add.objective(p, type="risk", name="StdDev")
rp <- random_portfolios(p, permutations=1000, rp_method='sample')
mult.portf <- mult.portfolio.spec(p)
mult.portf <- add.sub.portfolio(mult.portf, p.long, rp=rp.long, 
                                optimize_method="random", rebalance_on="quarters", 
                                training_period=36)
mult.portf <- add.sub.portfolio(mult.portf, p.short, rp=rp.short, 
                                optimize_method="random", rebalance_on="quarters", 
                                training_period=36)

Hierarchical Optimization: Long and Short Portfolio Optimization

# run the optimization
opt.m.ls <- optimize.portfolio.rebalancing(R, mult.portf,
                                           optimize_method = "random",
                                           trace = TRUE, rp = rp,
                                           rebalance_on = "quarters",
                                           training_period = 36)
r.ls <- Return.portfolio(opt.m.ls$R, extractWeights(opt.m.ls), geometric = FALSE)
charts.PerformanceSummary(r.ls)

Hierarchical Optimization: Long and Short Portfolio Optimization Results

Hierarchical Optimization: Long/Short Portfolio with View

  • Define an indicator for overall market direction
    • 12 period exponentially weighted moving average on monthly closing prices of SPY
  • Use the trend indicator for a simple regime model
    • Regime 1: Price > EMA (Uptrend)
    • Regime 2: Price <= EMA (Downtrend)
  • Define the portfolio optimization problem such that we reduce exposure to the short portfolio when in regime 1

Hierarchical Optimization: Long/Short Portfolio with View Specification

p.short.1 <- portfolio.spec(assets=colnames(R))
p.short.1 <- add.constraint(p.short.1, type="weight_sum",
                            min_sum=-0.26, max_sum=-0.24)
p.short.1 <- add.constraint(portfolio=p.short.1, type="box", min=-0.25, max=0)
p.short.1 <- add.objective(portfolio=p.short.1, type="risk", name="StdDev")
p.short.1 <- add.objective(p.short.1, type="risk_budget",
                           name="StdDev", max_prisk=0.50)
p.short.2 <- p.short
# define the portfolios for the regime model
# regime 1: Price >  EMA ; regime 2: Price <= EMA
trend.ind <- na.omit(cbind(Ad(SPY), TTR::EMA(Ad(SPY), n = 12)))
regime <- ifelse(trend.ind$SPY.Adjusted > trend.ind$EMA, 1, 2)
regime.port <- regime.portfolios(regime, combine.portfolios(list(p.short.1, p.short.2)))

Hierarchical Optimization: Long/Short Portfolio with View Optimization

p <- portfolio.spec(assets=paste("proxy",1:2, sep="."))
p <- add.constraint(p, type="weight_sum",min_sum=0.99, max_sum=1.01)
p <- add.constraint(p, type="box", min=0.1, max=1)
p <- add.objective(p, type="return", name="mean")
p <- add.objective(p, type="risk", name="StdDev")
rp <- random_portfolios(p, permutations=1000, rp_method='sample')

# initialize multi-layer (i.e. hierarchical) portfolio specification
mult.portf <- mult.portfolio.spec(p)
mult.portf <- add.sub.portfolio(mult.portf, p.long, rp=rp.long,
                                optimize_method="random", rebalance_on="quarters",
                                training_period=36)
mult.portf <- add.sub.portfolio(mult.portf, regime.port,
                                search_size = 1000, optimize_method="random", 
                                rebalance_on="quarters", training_period=36)

Hierarchical Optimization: Long/Short Portfolio with View Optimization

opt.mr.ls <- optimize.portfolio.rebalancing(R, mult.portf,
                                            optimize_method = "random",
                                            trace = TRUE, rp = rp,
                                            rebalance_on = "quarters",
                                            training_period = 36)
r.ls.regime <- Return.portfolio(opt.mr.ls$R, extractWeights(opt.mr.ls), geometric = FALSE)
ret.all <- na.omit(cbind(ret.naive, r.ls, r.ls.regime))
charts.PerformanceSummary(ret.all)

Hierarchical Optimization: Long/Short Portfolio with View Optimization Results

Conclusion

  • Introduced the goals and summary of PortfolioAnalytics
  • Demonstrated the flexibility through examples
  • Plans for continued development
    • Interface to \(parma\)
    • Additional solvers
    • "Gallery" of examples

Acknowledgements

Many thanks to...

  • Google: funding Google Summer of Code (GSoC) for 2013 and 2014
  • UW CF&RM Program: continued work on PortfolioAnalytics
  • GSoC Mentors: Brian Peterson, Peter Carl, Doug Martin, and Guy Yollin
  • R/Finance Committee

PortfolioAnalytics Links

Any Questions?

References and Useful Links