library(PortfolioAnalytics)
require(methods)

Overview


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.

General Objectives

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


Portfolio Optimization Objectives


PortfolioAnalytics Overview

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


Support Multiple Solvers

Linear and Quadratic Programming Solvers

Global (stochastic or continuous solvers)


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


Comparison of Random Portfolio Methods (Interactive!)

load("figures/rp_viz.rda")
rp_viz$show('inline')

Random Portfolios: Simplex Method


Workflow: Specify Portfolio

args(portfolio.spec)

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


Workflow: Add Constraints

args(add.constraint)

Supported Constraint Types


Workflow: Add Objectives

args(add.objective)

Supported Objective types


Workflow: Run Optimization

args(optimize.portfolio)
args(optimize.portfolio.rebalancing)

Workflow: Analyze Results

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


Stock Data Setup

Here we will look at portfolio optimization in the context of stocks.

equity.data <- cbind(largecap_weekly[,1:15], 
                     midcap_weekly[,1:15], 
                     smallcap_weekly[,1:5])

Distribution of Monthly Returns


Example 1: Market Neutral Portfolio

Here we consider a portfolio of stocks. Our objective is to maximize portfolio return with a target of 0.0015 and minimize portfolio StdDev with a target of 0.02 subject to dollar neutral, beta, box, and position limit constraints.


Specify Portfolio: Constraints

portf.dn <- portfolio.spec(stocks)

# Add constraint such that the portfolio weights sum to 0*
portf.dn <- add.constraint(portf.dn, type="weight_sum", 
                                  min_sum=-0.01, max_sum=0.01)

# Add box constraint such that no asset can have a weight of greater than
# 20% or less than -20%
portf.dn <- add.constraint(portf.dn, type="box", min=-0.2, max=0.2)

# Add constraint such that we have at most 20 positions
portf.dn <- add.constraint(portf.dn, type="position_limit", max_pos=20)

# Add constraint such that the portfolio beta is between -0.25 and 0.25
betas <- t(CAPM.beta(equity.data, market, Rf))
portf.dn <- add.constraint(portf.dn, type="factor_exposure", B=betas, 
                           lower=-0.25, upper=0.25)

Specify Portfolio: Objectives

# Add objective to maximize portfolio return with a target of 0.0015
portf.dn.StdDev <- add.objective(portf.dn, type="return", name="mean",
                                 target=0.0015)

# Add objective to minimize portfolio StdDev with a target of 0.02
portf.dn.StdDev <- add.objective(portf.dn.StdDev, type="risk", name="StdDev",
                                 target=0.02)

Run Optimization

# Generate random portfolios
rp <- random_portfolios(portf.dn, 10000, "sample")

# Run the optimization
opt.dn <- optimize.portfolio(equity.data, portf.dn.StdDev, 
                               optimize_method="random", rp=rp,
                               trace=TRUE)

Plot Results

plot(opt.dn, main="Dollar Neutral Portfolio", risk.col="StdDev", neighbors=10)


EDHEC Data Setup

Here we will look at portfolio optimization in the context of portfolio of hedge funds.

Relative Value | Directional -------------- | ----------- Convertible Arbitrage (CA) | CTA Global (CTAG) Equity Market Neutral (EMN) | Emerging Markets (EM) Fixed Income Arbitrage (FIA) | Global Macro (GM)

R <- edhec[,c("Convertible.Arbitrage", "Equity.Market.Neutral", 
              "Fixed.Income.Arbitrage", 
              "CTA.Global", "Emerging.Markets", "Global.Macro")]
# Abreviate column names for convenience and plotting
colnames(R) <- c("CA", "EMN", "FIA", "CTAG", "EM", "GM")

Monthly Returns


Example 2: Minimum Expected Shortfall

Consider an allocation to hedge funds using the EDHEC-Risk Alternative Index as a proxy. This will be an extended example starting with an objective to minimize modified expected shortfall, then add risk budget percent contribution limit, and finally add equal risk contribution limit.


Specify Initial Portfolio

# Specify an initial portfolio
funds <- colnames(R)
portf.init <- portfolio.spec(funds)

# Add constraint such that the weights sum to 1*
portf.init <- add.constraint(portf.init, type="weight_sum", 
                             min_sum=0.99, max_sum=1.01)

# Add box constraint such that no asset can have a weight of greater than
# 40% or less than 5%
portf.init <- add.constraint(portf.init, type="box", 
                             min=0.05, max=0.4)

# Add return objective with multiplier=0 such that the portfolio mean
# return is calculated, but does not impact optimization
portf.init <- add.objective(portf.init, type="return", 
                            name="mean", multiplier=0)

Add Objectives

# Add objective to minimize expected shortfall
portf.minES <- add.objective(portf.init, type="risk", name="ES")

# Add objective to set upper bound on percentage component contribution
portf.minES.RB <- add.objective(portf.minES, type="risk_budget", 
                                name="ES", max_prisk=0.3)
# Relax box constraints
portf.minES.RB$constraints[[2]]$max <- rep(1,ncol(R))

# Add objective to minimize concentration of modified ES
# component contribution
portf.minES.EqRB <- add.objective(portf.minES, type="risk_budget", 
                                  name="ES", min_concentration=TRUE)
# Relax box constraints
portf.minES.EqRB <- add.constraint(portf.minES.EqRB, type="box", 
                                   min=0.05, max=1, indexnum=2)

Run Optimization

# Combine the 3 portfolios
portf <- combine.portfolios(list(minES=portf.minES, 
                                 minES.RB=portf.minES.RB, 
                                 minES.EqRB=portf.minES.EqRB))

# Run the optimization
opt.minES <- optimize.portfolio(R, portf, optimize_method="DEoptim", 
                                search_size=5000, trace=TRUE, traceDE=0)

Plot in Risk-Return Space


Chart Risk Budgets

chart.RiskBudget(opt.minES[[2]], main="Risk Budget Limit", 
                 risk.type="percentage", neighbors=10)

chart.RiskBudget(opt.minES[[3]], main="Equal ES Component Contribution", 
                 risk.type="percentage", neighbors=10)


Set Rebalancing Parameters and Run Backtest

# Set rebalancing frequency
rebal.freq <- "quarters"

# Training Period
training <- 120

# Trailing Period
trailing <- 72

bt.opt.minES <- optimize.portfolio.rebalancing(R, portf,
                                               optimize_method="DEoptim", 
                                               rebalance_on=rebal.freq, 
                                               training_period=training, 
                                               trailing_periods=trailing,
                                               search_size=5000,
                                               traceDE=0)

Min ES Risk Contributions and Weights Through Time


Min ES Risk Budget Limit Risk Contributions and Weights Through Time


Min ES Equal Component Contribution Risk Contributions and Weights Through Time

--- &twocol

Min ES Equal Component Contribution Risk Contributions and Weights (interactive!)

load("figures/bt_w3.rda")
load("figures/bt_rb3.rda")

*** =left

Percent Contribution to Risk

bt_rb3$set(
  height = 400
  ,width = 450
)
bt_rb3$chart( stacked=T )
bt_rb3$setLib("nvd3")
bt_rb3$setTemplate(afterScript="<script></script>")
bt_rb3$show('inline')

*** =right

Weights

bt_w3$set(
  height = 400
  ,width = 450
)
bt_w3$chart( stacked=T )
bt_w3$setLib("nvd3")
bt_w3$setTemplate(afterScript="<script></script>")
bt_w3$show('inline')

Compute Returns

ret.bt.opt <- do.call(cbind, lapply(bt.opt.minES, 
                                    function(x) summary(x)$portfolio_returns))
colnames(ret.bt.opt) <- c("min ES", "min ES RB", "min ES Eq RB")

Chart Performance

charts.PerformanceSummary(ret.bt.opt)


Example 3: Maximize CRRA

Consider an allocation to hedge funds using the EDHEC-Risk Alternative Index as a proxy. Our objective to maximize the fourth order expansion of the Constant Relative Risk Aversion (CRRA) expected utility function as in the Boudt paper and Martellini paper. We use the same data as Example 3.

$$ EU_{\lambda}(w) = - \frac{\lambda}{2} m_{(2)}(w) + \frac{\lambda (\lambda + 1)}{6} m_{(3)}(w) - \frac{\lambda (\lambda + 1) (\lambda + 2)}{24} m_{(4)}(w) $$


Define a function to compute CRRA

CRRA <- function(R, weights, lambda, sigma, m3, m4){
  weights <- matrix(weights, ncol=1)
  M2.w <- t(weights) %*% sigma %*% weights
  M3.w <- t(weights) %*% m3 %*% (weights %x% weights)
  M4.w <- t(weights) %*% m4 %*% (weights %x% weights %x% weights)
  term1 <- (1 / 2) * lambda * M2.w
  term2 <- (1 / 6) * lambda * (lambda + 1) * M3.w
  term3 <- (1 / 24) * lambda * (lambda + 1) * (lambda + 2) * M4.w
  out <- -term1 + term2 - term3
  out
}

Define a custom moment function

The default function for momentFUN is set.portfolio.moments. We need to write our own function to estimate the moments for our objective function.

crra.moments <- function(R, ...){
  out <- list()
  out$mu <- colMeans(R)
  out$sigma <- cov(R)
  out$m3 <- PerformanceAnalytics:::M3.MM(R)
  out$m4 <- PerformanceAnalytics:::M4.MM(R)
  out
}

Specify Portfolio

# Specify portfolio
portf.crra <- portfolio.spec(funds)

# Add constraint such that the weights sum to 1
portf.crra <- add.constraint(portf.crra, type="weight_sum", 
                             min_sum=0.99, max_sum=1.01)

# Add box constraint such that no asset can have a weight of greater than
# 40% or less than 5% 
portf.crra <- add.constraint(portf.crra, type="box", 
                             min=0.05, max=0.4)

# Add objective to maximize CRRA
portf.crra <- add.objective(portf.crra, type="return", 
                            name="CRRA", arguments=list(lambda=10))

"Dummy" Objectives

# Dummy objectives for plotting and/or further analysis
portf.crra <- add.objective(portf.crra, type="return", name="mean", multiplier=0)
portf.crra <- add.objective(portf.crra, type="risk", name="ES", multiplier=0)
portf.crra <- add.objective(portf.crra, type="risk", name="StdDev", multiplier=0)

Run Optimization

opt.crra <- optimize.portfolio(R, portf.crra, optimize_method="DEoptim", 
                                 search_size=5000, trace=TRUE, traceDE=0,
                                 momentFUN="crra.moments")
load("optimization_results/opt.crra.rda")
head(extractStats(opt.crra),4)

Chart Results

chart.RiskReward(opt.crra, risk.col="ES")
chart.RiskReward(opt.crra, risk.col="StdDev")


Run Backtest and Compute Returns

bt.opt.crra <- optimize.portfolio.rebalancing(R, portf.crra, 
                                              optimize_method="DEoptim",
                                              search_size=5000, trace=TRUE,
                                              traceDE=0,
                                              momentFUN="crra.moments",
                                              rebalance_on=rebal.freq, 
                                              training_period=training, 
                                              trailing_periods=trailing)

ret.crra <- summary(bt.opt.crra)$portfolio_returns
colnames(ret.crra) <- "CRRA"

Chart Weights Through Time

chart.Weights(bt.opt.crra, main="CRRA Weights", col=bluemono)


Chart Performance

charts.PerformanceSummary(cbind(ret.bt.opt, ret.crra), 
                          main="Optimization Performance")


Conclusion

Acknowledgements

Many thanks to...


PortfolioAnalytics Links

PortfolioAnalytics is on R-Forge in the ReturnAnalytics project

Source code for the slides

and view it here


Any Questions?


References and Useful Links



braverock/PortfolioAnalytics documentation built on April 18, 2024, 4:09 a.m.