Eric Zivot
August 11, 2015
options(digits=3, width=70)
# install IntroCompFinR package from R-forge
# use install.packages("IntroCompFinR", repos="http://R-Forge.R-project.org")
library(quadprog)
library(IntroCompFinR)
## Loading required package: xts
## Loading required package: zoo
##
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
library(PerformanceAnalytics)
##
## Attaching package: 'PerformanceAnalytics'
## The following object is masked from 'package:graphics':
##
## legend
library(zoo)
Sys.setenv(TZ="UTC")
cex.val = 2
Estimates of CER model for Microsoft, Nordstrom and Starbucks stock from monthly returns over the period January 1995 to January 2000.
asset.names <- c("MSFT", "NORD", "SBUX")
mu.vec = c(0.0427, 0.0015, 0.0285)
names(mu.vec) = asset.names
sigma.mat = matrix(c(0.0100, 0.0018, 0.0011,
0.0018, 0.0109, 0.0026,
0.0011, 0.0026, 0.0199),
nrow=3, ncol=3)
dimnames(sigma.mat) = list(asset.names, asset.names)
r.f = 0.005
mu.vec
## MSFT NORD SBUX
## 0.0427 0.0015 0.0285
sd.vec = sqrt(diag(sigma.mat))
cov2cor(sigma.mat)
## MSFT NORD SBUX
## MSFT 1.000 0.172 0.078
## NORD 0.172 1.000 0.177
## SBUX 0.078 0.177 1.000
set.seed(123)
x.msft = runif(400, min=0, max=1)
x.nord = runif(400, min=0, max=1)
x.sbux = 1 - x.msft - x.nord
long.only = which(x.msft > 0 & x.nord > 0 & x.sbux > 0)
x.msft = x.msft[long.only]
x.nord = x.nord[long.only]
x.sbux = x.sbux[long.only]
length(long.only)
## [1] 191
# plot portfolio weights
chart.StackedBar(cbind(x.msft, x.nord, x.sbux),
main="Random long-only portfolio weight vectors",
xlab = "portfolio", ylab = "weights",
xaxis.labels=as.character(1:length(long.only)))
Use the IntroCompFinR package function globalMin.portfolio()
gmin.port = globalMin.portfolio(mu.vec, sigma.mat)
gmin.port
## Call:
## globalMin.portfolio(er = mu.vec, cov.mat = sigma.mat)
##
## Portfolio expected return: 0.0249
## Portfolio standard deviation: 0.0727
## Portfolio weights:
## MSFT NORD SBUX
## 0.441 0.366 0.193
solve.QP()
to compute global minimum variance portfolioFirst we set up the appropriate restriction matrices required by the function slove.QP()
D.mat = 2*sigma.mat
d.vec = rep(0, 3)
A.mat = cbind(rep(1,3), diag(3))
b.vec = c(1, rep(0,3))
D.mat
## MSFT NORD SBUX
## MSFT 0.0200 0.0036 0.0022
## NORD 0.0036 0.0218 0.0052
## SBUX 0.0022 0.0052 0.0398
d.vec
## [1] 0 0 0
t(A.mat)
## [,1] [,2] [,3]
## [1,] 1 1 1
## [2,] 1 0 0
## [3,] 0 1 0
## [4,] 0 0 1
b.vec
## [1] 1 0 0 0
solve.QP()
to compute global minimum variance portfolioThe function solve.QP()
takes the restrictions matrices as inputs
args(solve.QP)
## function (Dmat, dvec, Amat, bvec, meq = 0, factorized = FALSE)
## NULL
solve.QP()
returns a list containing information about the optimization
qp.out = solve.QP(Dmat=D.mat, dvec=d.vec,
Amat=A.mat, bvec=b.vec, meq=1)
class(qp.out)
## [1] "list"
The components of the returned list are
names(qp.out)
## [1] "solution" "value"
## [3] "unconstrained.solution" "iterations"
## [5] "Lagrangian" "iact"
The portfolio weights are in the solution
component
qp.out$solution
## [1] 0.441 0.366 0.193
The solution satisfies the required constraints (weights sum to one and all weights are positive)
sum(qp.out$solution)
## [1] 1
The minimized value of the objective function (portfolio variance) is in the value
component
qp.out$value
## [1] 0.00528
solve.QP()
to compute global minimum variance portfolioCompute the mean and volatility of the minimum variance portfolio
# Expected return
er.gmin.ns = as.numeric(crossprod(qp.out$solution, mu.vec))
# Volatility
sd.gmin.ns = sqrt(qp.out$value)
er.gmin.ns
## [1] 0.0249
sd.gmin.ns
## [1] 0.0727
globalMin.Portfolio()
to compute the minimum variance portfolioUse the optional argument shorts=TRUE
to impose no-short sales restrictions
args(globalMin.portfolio)
## function (er, cov.mat, shorts = TRUE)
## NULL
The function sets up the restriction matrices for solve.QP()
to compute the global minimum variance portfolio.
gmin.port = globalMin.portfolio(mu.vec, sigma.mat,
shorts=FALSE)
gmin.port
## Call:
## globalMin.portfolio(er = mu.vec, cov.mat = sigma.mat, shorts = FALSE)
##
## Portfolio expected return: 0.0249
## Portfolio standard deviation: 0.0727
## Portfolio weights:
## MSFT NORD SBUX
## 0.441 0.366 0.193
First find the minimum variance portfolio allowing short sales
eMsft.port = efficient.portfolio(mu.vec, sigma.mat, target.return = mu.vec["MSFT"])
eMsft.port
## Call:
## efficient.portfolio(er = mu.vec, cov.mat = sigma.mat, target.return = mu.vec["MSFT"])
##
## Portfolio expected return: 0.0427
## Portfolio standard deviation: 0.0917
## Portfolio weights:
## MSFT NORD SBUX
## 0.8275 -0.0907 0.2633
First we set up the appropriate restriction matrices required by the function slove.QP()
D.mat = 2*sigma.mat
d.vec = rep(0, 3)
A.mat = cbind(mu.vec, rep(1,3), diag(3))
b.vec = c(mu.vec["MSFT"], 1, rep(0,3))
D.mat
## MSFT NORD SBUX
## MSFT 0.0200 0.0036 0.0022
## NORD 0.0036 0.0218 0.0052
## SBUX 0.0022 0.0052 0.0398
d.vec
## [1] 0 0 0
A.mat
## mu.vec
## MSFT 0.0427 1 1 0 0
## NORD 0.0015 1 0 1 0
## SBUX 0.0285 1 0 0 1
b.vec
## MSFT
## 0.0427 1.0000 0.0000 0.0000 0.0000
Next we use solve.QP()
to find the solution
qp.out = solve.QP(Dmat=D.mat, dvec=d.vec,
Amat=A.mat, bvec=b.vec, meq=2)
names(qp.out$solution) = names(mu.vec)
round(qp.out$solution, digits=3)
## MSFT NORD SBUX
## 1 0 0
The argument meq = 2
specifies 2 equality constraints. It tells solve.QP()
how to determine \(A_{eq}\) and \(A_{neq}\) from \(A\)
Notice that the no-short sales solution forces the weights on Nordstrom and Starbucks to 0
It is not possible to find an no-short sale efficient portfolio that has a higher mean than Microsoft
The IntroCompFinR function efficient.portfolio()
with shorts=FALSE
uses solve.QP()
to compute a no-short sales efficient portfolio
efficient.portfolio(mu.vec, sigma.mat, target.return = mu.vec["MSFT"], shorts = FALSE)
## Call:
## efficient.portfolio(er = mu.vec, cov.mat = sigma.mat, target.return = mu.vec["MSFT"],
## shorts = FALSE)
##
## Portfolio expected return: 0.0427
## Portfolio standard deviation: 0.1
## Portfolio weights:
## MSFT NORD SBUX
## 1 0 0
Suppose you try to find an efficient portfolio with target return higher than the mean for Microsoft
# efficient.portfolio(mu.vec, sigma.mat, target.return = mu.vec["MSFT"]+0.01, shorts = FALSE)
# Error in quadprog::solve.QP(Dmat = Dmat, dvec = dvec, Amat = Amat, bvec = bvec, :
# constraints are inconsistent, no solution!
solve.QP()
throws an error saying there is no compatible solutionUse the IntroCompFinR function efficient.frontier()
to compute the efficient set of frontier portfolios that allow short sales
ef = efficient.frontier(mu.vec, sigma.mat, alpha.min=0,
alpha.max=1, nport=10)
ef$weights
## MSFT NORD SBUX
## port 1 0.827 -0.0907 0.263
## port 2 0.785 -0.0400 0.256
## port 3 0.742 0.0107 0.248
## port 4 0.699 0.0614 0.240
## port 5 0.656 0.1121 0.232
## port 6 0.613 0.1628 0.224
## port 7 0.570 0.2135 0.217
## port 8 0.527 0.2642 0.209
## port 9 0.484 0.3149 0.201
## port 10 0.441 0.3656 0.193
It is easy to compute the efficient frontier without short sales by running a simple loop
mu.vals = seq(gmin.port$er, max(mu.vec), length.out=10)
w.mat = matrix(0, length(mu.vals), 3)
sd.vals = rep(0, length(sd.vec))
colnames(w.mat) = names(mu.vec)
D.mat = 2*sigma.mat
d.vec = rep(0, 3)
A.mat = cbind(mu.vec, rep(1,3), diag(3))
for (i in 1:length(mu.vals)) {
b.vec = c(mu.vals[i],1,rep(0,3))
qp.out = solve.QP(Dmat=D.mat, dvec=d.vec,
Amat=A.mat, bvec=b.vec, meq=2)
w.mat[i, ] = qp.out$solution
sd.vals[i] = sqrt(qp.out$value)
}
ef.ns = efficient.frontier(mu.vec, sigma.mat, alpha.min=0,
alpha.max=1, nport=10, shorts=FALSE)
ef.ns$weights
## MSFT NORD SBUX
## port 1 0.441 0.3656 0.193
## port 2 0.484 0.3149 0.201
## port 3 0.527 0.2642 0.209
## port 4 0.570 0.2135 0.217
## port 5 0.613 0.1628 0.224
## port 6 0.656 0.1121 0.232
## port 7 0.699 0.0614 0.240
## port 8 0.742 0.0107 0.248
## port 9 0.861 0.0000 0.139
## port 10 1.000 0.0000 0.000
plot(ef$sd, ef$er, type="b", ylim=c(0.001, 0.05), xlim=c(0.06, 0.15),
pch=16, col="blue", cex=2, ylab=expression(mu[p]), xlab=expression(sigma[p]))
points(sd.vec, mu.vec, pch=16, col="black", cex=2)
text(sd.vec, mu.vec, labels=asset.names, pos=4)
points(ef.ns$sd, ef.ns$er, type="b", pch=16, col="red")
text(ef$sd[1], ef$er[1], labels="port 10", col="blue", pos=3)
text(ef$sd[2], ef$er[2], labels="port 9", col="blue", pos=3)
text(ef.ns$sd[10], ef.ns$er[10], labels="port 10", col="red", pos=1)
text(ef.ns$sd[9], ef.ns$er[9], labels="port 9", col="red", pos=1)