- Discuss Portfolio Optimization
- Introduce PortfolioAnalytics
- Demonstrate PortfolioAnalytics with Examples
Ross Bennett
"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
How do we define risk? What about more complex objectives and constraints?
PortfolioAnalytics is an R package designed to provide numerical solutions and visualizations for portfolio optimization problems with complex constraints and objectives.
Supports:
Linear and Quadratic Programming Solvers
Global (stochastic or continuous solvers)
PortfolioAnalytics has three methods to generate random portfolios.
gridSearch
function in the NMOF package.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
args(add.constraint)
## function (portfolio, type, enabled = TRUE, message = FALSE, ...,
## indexnum = NULL)
## NULL
Supported Constraint Types
args(add.objective)
## function (portfolio, constraints = NULL, type, name, arguments = NULL,
## enabled = TRUE, ..., indexnum = NULL)
## NULL
Supported Objective types
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
Visualization | Data Extraction |
---|---|
plot | extractObjectiveMeasures |
chart.Concentration | extractStats |
chart.EfficientFrontier | extractWeights |
chart.RiskReward | |
chart.RiskBudget | summary |
chart.Weights |
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."
Here we will analyse the feasible space of allocations to hedge fund strategies
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) |
# 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")
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)
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
Consider an allocation to equity sectors using 9 sector ETFs
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)
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)
# 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')
# 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)
# 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)
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)))
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)
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)
Many thanks to...
Source code for the slides
and view it here