Authors: Jim Lung
** Use mathematical models to make a decision for portfolio optimization Investigating portfolio optimization with expected return on investiment in risk control **
** Historical stock price data are readily accessible using functions in “Quantmod” package The filtered data for this application selects total 9 stock cases from 2015 to August 2018 The datasets include the date, daily market close price, market volumes, the closing price will be used to make for portfolio optimization **
In the following sections, we use a variety of mathematical tools to perform the following tasks:
* Data loading * Graphical Exploration * Compute daily, monthly and yearly return * Calculate the Mean Variance model by specific stocks * Use minimax model to optimize portfolio * Use linear programming techniques to compare the log return * Use a quadratic programming approach to determine appropriate portfolio weights
suppressMessages(suppressWarnings(library(knitr)))
suppressMessages(suppressWarnings(library(ggplot2)))
suppressMessages(suppressWarnings(library(ggthemes)))
suppressMessages(suppressWarnings(library(dplyr)))
suppressMessages(suppressWarnings(library(tidyr)))
suppressMessages(suppressWarnings(library(stringr)))
suppressMessages(suppressWarnings(library(lpSolveAPI)))
suppressMessages(suppressWarnings(library(xts)))
suppressMessages(suppressWarnings(library(matrixcalc)))
suppressMessages(suppressWarnings(library(dygraphs)))
suppressMessages(suppressWarnings(library(ggthemes)))
suppressMessages(suppressWarnings(library(highcharter)))
suppressMessages(suppressWarnings(library(viridis)))
suppressMessages(suppressWarnings(library(tibbletime)))
suppressMessages(suppressWarnings(library(timetk)))
suppressMessages(suppressWarnings(library(tidyquant)))
suppressMessages(suppressWarnings(library(tidyverse)))
# input and print available stocks
symbols <- c("AAPL", "AMD", "ADI", "ABBV", "AET", "A", "APD", "AA","CF")
stock <- c("Apple Inc.", "Advanced Micro Devices, Inc.", "Analog Devices, Inc.", "AbbVie Inc","AETNA INC", "Agilent Technologies Inc","Air Products & Chemicals, Inc.","Alcoa Corp","CF Industries Holdings, Inc.")
df <- data.frame(ticker=symbols, stocks=stock)
knitr::kable(df[,1:2], col.names = c("Ticker","Stocks"))
| Ticker | Stocks |
|---|---|
| AAPL | Apple Inc. |
| AMD | Advanced Micro Devices, Inc. |
| ADI | Analog Devices, Inc. |
| ABBV | AbbVie Inc |
| AET | AETNA INC |
| A | Agilent Technologies Inc |
| APD | Air Products & Chemicals, Inc. |
| AA | Alcoa Corp |
| CF | CF Industries Holdings, Inc. |
Construct a vector of tickers and gather prices for them using the getSymbols function within quantmod. We will next calculate returns and convert the data to a time series object.
# The symbols vector holds our tickers.
tickers <- c("AAPL", "AMD", "ADI", "ABBV", "AET", "A", "APD", "AA","CF")
# The prices object will hold our raw price data
close_price<-
getSymbols(tickers,src="yahoo", from = "2017-01-01",
auto.assign = TRUE, warnings = FALSE) %>%
map(~Ad(get(.))) %>% #Extract (transformed) data from a suitable OHLC object.
reduce(merge) %>% #reduce() combines from the left, reduce_right() combines from the right
`colnames<-`(tickers )
## 'getSymbols' currently uses auto.assign=TRUE by default, but will
## use auto.assign=FALSE in 0.5-0. You will still be able to use
## 'loadSymbols' to automatically load data. getOption("getSymbols.env")
## and getOption("getSymbols.auto.assign") will still be checked for
## alternate defaults.
##
## This message is shown once per session and may be disabled by setting
## options("getSymbols.warning4.0"=FALSE). See ?getSymbols for details.
##
## WARNING: There have been significant changes to Yahoo Finance data.
## Please see the Warning section of '?getSymbols.yahoo' for details.
##
## This message is shown once per session and may be disabled by setting
## options("getSymbols.yahoo.warning"=FALSE).
## pausing 1 second between requests for more than 5 symbols
## pausing 1 second between requests for more than 5 symbols
## pausing 1 second between requests for more than 5 symbols
## pausing 1 second between requests for more than 5 symbols
## pausing 1 second between requests for more than 5 symbols
close_price<-tk_zoo(close_price)%>%na.omit()
# Calculate returns
prices <- close_price
returns.data <- sapply(close_price, CalculateReturns)
returns.data <- na.omit(returns.data)
returns.data %>%tk_tbl()%>%tail()
## # A tibble: 6 x 10
## index AAPL AMD ADI ABBV AET A APD
## <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2018-11-20 -0.0478 0.00523 0.0409 -0.0155 -0.0168 0.0784 -0.0146
## 2 2018-11-21 -0.00113 -0.0250 0.00741 -0.0220 0.00147 0.00296 0.0103
## 3 2018-11-23 -0.0254 0.0347 -0.0116 -0.00604 0.00519 0.00724 -0.0106
## 4 2018-11-26 0.0135 0.0361 0.0161 0.00712 0.0231 0.0152 0.00692
## 5 2018-11-27 -0.00218 0.0483 -0.00544 0.0167 0.00904 0.00159 -0.0146
## 6 2018-11-28 0.0385 0.0138 0.0182 0.0144 0.00330 0.0306 0.0153
## # ... with 2 more variables: AA <dbl>, CF <dbl>
x = close_price
date = index(close_price)
close_price = data.frame(date,x)
Use adjusted closed price to plot graph from 2017 until now:
start="2017-01-01"
end=today()
dateWindow <-cat(paste(sQuote("2017-01-01"),sQuote(end),sep=","), "\n")
## '2017-01-01','2018-12-04'
dygraph(close_price, main = "Value", group = "stock") %>%
dyRebase(value = 100) %>%
dyRangeSelector(dateWindow = dateWindow)
According to the historial price movements, it indicate the future trends and it concern the maximum of expected return, it is difficult to find out any good investment stratege in plot graph. So we should calculate the return by daily, weekly or yearly to decide the short , long term investment.
#daily return
daily_return <- as.data.frame(apply(close_price[,-1] , 2 , diff )/close_price[-479,-1])
daily_return <- cbind(Date = close_price[-1,1],daily_return)
#monthly returns
temp <- lapply(daily_return[,-1], function(x) x+1)
temp$year <- format(daily_return[,1], "%Y")
temp$month <- format(daily_return[,1], "%m")
temp<- as.data.frame(temp)
monthly_return <- aggregate(temp[-c(10,11)] ,by=list(temp$month,temp$year), FUN = prod, na.rm=TRUE)
monthly_return <-cbind(monthly_return[,c(1,2)],monthly_return[,-c(1,2)]-1)
colnames(monthly_return)[1] <-'Month'
colnames(monthly_return)[2] <-'Year'
monthly_return$Date <- as.Date(paste0(monthly_return$Year,'-',monthly_return$Month,"-01"))
monthly_return <- monthly_return[,c(12,1:11)]
#yearly returns
annual_return <- aggregate(temp[-c(10,11)] ,list(temp$year), FUN = prod, na.rm=TRUE)
annual_return <-cbind(annual_return[,1],annual_return[,-1]-1)
colnames(annual_return)[1] <-'Year'
#$Year <- as.Date(ISOdate(plot_dt_Y$Year, 1, 1))
# time period: year
n <- tail(as.numeric(annual_return$Year), n=1) - as.numeric(as.numeric(annual_return$Year))[1]
# 5 years average annual return
ave_annual_return <- apply(annual_return[,-1]+1,2,prod)
ave_annual_return <- ave_annual_return^(1/n)-1
Compare the annual return:
annualreturn <- as.data.frame(ave_annual_return)
colnames(annualreturn)<-'ave_yearly_return'
kable(annualreturn)
| ave_yearly_return | |
|---|---|
| AAPL | 0.6044175 |
| AMD | 0.8627081 |
| ADI | 0.3042133 |
| ABBV | 0.5339709 |
| AET | 0.7718944 |
| A | 0.5605331 |
| APD | 0.1653859 |
| AA | 0.1571870 |
| CF | 0.4239632 |
| To comp | are the average yearly return, AMD and AET are the most increasing return yearly. |
Investors are risk averse in that they prefer higher return for a given level of risk (variance, standard deviation), or they want to minimize risk for a given level of returns, so we go to minimize the variance and maximize the return.
Suppose data are observed for N securities, over T time periods. Let
\(y_{jt}\) = Return on one dollar invested in security j in time period t.
\(\bar{y_j}\)=Average Return on security j
\(w_j=\) Portfolio allocation to security j.
\(y_{pt}\) = Return on portfolio in time period t
\(E_p\) =Average Return on portfolio
\(M_p\) = Minimum return on portfolio
The objective function: \[min \displaystyle\sum_{j=k}^{N}\displaystyle\sum_{j=1}^{N}w_jw_ks_{jk}\]
subject to: \[\displaystyle\sum_{j=1}^{N}w_j\bar{y_j} \geq G \]
with: \[s_{jk} = \frac{1}{T-N}\displaystyle\sum_{t=1}^{T}(y_{jt} - \bar{y_j})(y_{kt} -\bar{y_k})\]
#The subset of the data as candidate
sub_return <- monthly_return[,c('Date','Month','Year',tickers)]
# convert to matrix and vector
cov_matrix <- cov(sub_return[sub_return$Date > as.Date('2018-01-01'),][,-c(1:3)])
pstart <- rep(1/12,8)
# arithmetic mean of monthly return
mu <- apply(sub_return[sub_return$Date > as.Date('2018-01-01'),][,-c(1:3)],2,mean)
# standard deviation
std <- apply(sub_return[sub_return$Date > as.Date('2018-01-01'),][,-c(1:3)],2,sd)
#sharp ratio
sharpe <- mu /std
############################################
#Minimize the variance
############################################
SSRE <- function(parx) {
par<- c(parx,1-sum(parx))
if(all(par > 0 & par < 1)) { # parameters meet requirements
sqrt(t(par)%*% cov_matrix%*% par) # this is a linear algebra version of your objective without the division by xi
} else 1e7 # penalty for parameters not satisfying constraints
}
SSRE(pstart)
## [,1]
## [1,] 0.06851034
#par<- c(pstart,1-sum(pstart))
#vr <- sqrt(t(par)%*% cov_matrix%*% par)
opt_1 <- optim(pstart,SSRE)
opt_1
## $par
## [1] 2.831649e-02 4.470701e-06 2.141530e-01 1.270327e-01 9.970038e-05
## [6] 2.654679e-01 4.358655e-02 1.120192e-01
##
## $value
## [1] 0.04817881
##
## $counts
## function gradient
## 501 NA
##
## $convergence
## [1] 1
##
## $message
## NULL
para <- c(opt_1$par, 1-sum(opt_1$par)) # final contributions
weight<- as.data.frame(t(round(para,4)))
colnames(weight)<- tickers
display <- cbind(t(weight),'Ave.Return'=mu,'Stdev'=std,'Sharp'=sharpe)
colnames(display)[1]<-'Weight'
data.frame(display)
## Weight Ave.Return Stdev Sharp
## AAPL 0.0283 0.0138927012 0.10329584 0.134494296
## AMD 0.0000 0.0729485380 0.23835453 0.306050566
## ADI 0.2142 0.0025407452 0.06428978 0.039520204
## ABBV 0.1270 -0.0153324794 0.09888394 -0.155055300
## AET 0.0001 0.0148118327 0.04606748 0.321524677
## A 0.2655 -0.0004756023 0.06037735 -0.007877164
## APD 0.0436 -0.0032581455 0.03793639 -0.085884435
## AA 0.1120 -0.0403532553 0.08293752 -0.486550045
## CF 0.2093 0.0068655083 0.09323691 0.073635089
cat('\n','The monthly return of the portfolio at the minimum global variance is:',max_value <-sprintf("%1.3f%%", para%*%mu*100),'\n')
##
## The monthly return of the portfolio at the minimum global variance is: -0.436%
cat('\n','The minimum global variance is:',min_vr <-opt_1$value,'\n')
##
## The minimum global variance is: 0.04817881
#or
#opt_1 <-nlminb(pstart, SSRE)
############################################
#Maximize the return
############################################
MaxReturn <- function(parx) {
par<- c(parx,1-sum(parx))
if(all(par > 0 & par < 1)) { # parameters meet requirements
-t(par)%*%mu # this is a linear algebra version of your objective without the division by xi
} else 1e7 # penalty for parameters not satisfying constraints
}
-MaxReturn (pstart)
## [,1]
## [1,] 0.006019697
opt_2 <-nlminb(pstart, MaxReturn)
opt_2
## $par
## [1] 9.573517e-02 1.999590e-01 7.570084e-02 4.415754e-02 9.735729e-02
## [6] 7.037748e-02 6.546675e-02 1.574947e-15
##
## $objective
## [1] -0.01903878
##
## $convergence
## [1] 1
##
## $iterations
## [1] 43
##
## $evaluations
## function gradient
## 83 352
##
## $message
## [1] "false convergence (8)"
para_2 <- c(opt_2$par, 1-sum(opt_2$par)) # final contributions
weight<- as.data.frame(t(round(para_2,4)))
colnames(weight)<- tickers
data.frame('Weight'=t(weight))
## Weight
## AAPL 0.0957
## AMD 0.2000
## ADI 0.0757
## ABBV 0.0442
## AET 0.0974
## A 0.0704
## APD 0.0655
## AA 0.0000
## CF 0.3512
cat('\n','The maximized monthly return of the portfolio is:',max_value <-sprintf("%1.3f%%", -opt_2$objective*100),'\n')
##
## The maximized monthly return of the portfolio is: 1.904%
cat('\n','The global variance is:',vr_mxreturn <- sqrt(t(para_2)%*%cov_matrix%*%para_2),'\n')
##
## The global variance is: 0.09151696
The average monthly return of the portofolio at the evenly distributed allocation is 6.8 %. After optimization, the average monthly return of portfolio is -0.436 % when the global variance is at minimum 0.048. The maximized monthly return of portfolio is 1.904 % when the global variance is 0.0915.
The minimax model will maximize return with respect to one of these prior distributions providing valuable insight regarding an investor’s risk attitude and decision behavior.
Suppose data are observed for N securities, over T time periods. Let
\(y_{jt}\) = Return on one dollar invested in security j in time period t.
\(\bar{y_j}\)=Average Return on security j
\(w_j=\) Portfolio allocation to security j.
\(y_{pt}\) = Return on portfolio in time period t
\(E_p\) =Average Return on portfolio
\(M_p\) = Minimum return on portfolio
The objective function:
\[max\ M_p\]
subject to:
\[\displaystyle\sum_{j=1}^{N}w_jy_{jt} \geq M_p \] t = 1,…,T
\[\displaystyle\sum_{j=1}^{N}w_j\bar{y_j} \geq G \]
#the worst monthly return was calculated by subtract average return by the standard deviation
worst_return<-mu-std
#The subset of the data as candidate
sub_return <- monthly_return[,c('Date','Month','Year',tickers)]
# convert to matrix and vector for later use
cov_matrix <- cov(sub_return[sub_return$Date > as.Date('2018-01-01'),][,-c(1:3)])
pstart <- rep(1/12,8)
# arithmetic mean of monthly return
mu <- apply(sub_return[sub_return$Date > as.Date('2018-01-01'),][,-c(1:3)],2,mean)
# standard deviation
std <- apply(sub_return[sub_return$Date > as.Date('2018-01-01'),][,-c(1:3)],2,sd)
#sharp ratio
sharpe <- mu /std
worst_return <- mu-std
############################################
#Minimize the variance
############################################
portfolio_return <- function(parx) {
par<- c(parx,1-sum(parx))
if(all(par > 0 & par < 1)) { # parameters meet requirements
-par%*%worst_return # this is a linear algebra version of your objective without the division by xi
} else 1e7 # penalty for parameters not satisfying constraints
}
portfolio_return(pstart)
## [,1]
## [1,] 0.08607118
opt_3 <- optim(pstart, portfolio_return)
opt_3
## $par
## [1] 4.354848e-02 1.180378e-05 2.235999e-01 1.010767e-04 1.569046e-01
## [6] 2.356440e-01 3.175448e-02 4.985266e-04
##
## $value
## [1] 0.06492427
##
## $counts
## function gradient
## 501 NA
##
## $convergence
## [1] 1
##
## $message
## NULL
para_3 <- c(opt_3$par, 1-sum(opt_3$par)) # final contributions
weight<- as.data.frame(t(round(para_3,4)))
colnames(weight)<- tickers
display <- cbind(t(weight),'Worst.Return'=worst_return,'Ave.Return'=mu,'Stdev'=std,'Sharp'=sharpe)
colnames(display)[1]<-'Weight'
data.frame(display)
## Weight Worst.Return Ave.Return Stdev Sharp
## AAPL 0.0435 -0.08940314 0.0138927012 0.10329584 0.134494296
## AMD 0.0000 -0.16540599 0.0729485380 0.23835453 0.306050566
## ADI 0.2236 -0.06174904 0.0025407452 0.06428978 0.039520204
## ABBV 0.0001 -0.11421642 -0.0153324794 0.09888394 -0.155055300
## AET 0.1569 -0.03125565 0.0148118327 0.04606748 0.321524677
## A 0.2356 -0.06085295 -0.0004756023 0.06037735 -0.007877164
## APD 0.0318 -0.04119454 -0.0032581455 0.03793639 -0.085884435
## AA 0.0005 -0.12329078 -0.0403532553 0.08293752 -0.486550045
## CF 0.3079 -0.08637140 0.0068655083 0.09323691 0.073635089
cat('\n','The average minimum losses is:',min_losses <-sprintf("%1.3f%%", opt_3$value*100,'\n'))
##
## The average minimum losses is: 6.492%
cat('\n','The variance is:',SSRE(para_3[-12]))
##
## The variance is: 1e+07
alocation <- rbind('worst_return'=worst_return,'weight'=weight)
colnames(alocation) <- tickers
kable(alocation)
| AAPL | AMD | ADI | ABBV | AET | A | APD | AA | CF | |
|---|---|---|---|---|---|---|---|---|---|
| worst_return | -0.0894031 | -0.165406 | -0.061749 | -0.1142164 | -0.0312557 | -0.060853 | -0.0411945 | -0.1232908 | -0.0863714 |
| weight | 0.0435000 | 0.000000 | 0.223600 | 0.0001000 | 0.1569000 | 0.235600 | 0.0318000 | 0.0005000 | 0.3079000 |
| Average monthly | return is 8. | 6%, After op | timization, | mininum avera | ge loss is 6. | 49 % when va | riance is 1e+ | 07. |
Modeling linear vs log returns: Now we are ready to obtain the sample estimates from the returns\(\mathbf{x}_t\) \[ \begin{align} \hat{\boldsymbol{\mu}} & = \frac{1}{T}\sum_{t=1}^T \mathbf{x}_t\\ \hat{\boldsymbol{\Sigma}} & = \frac{1}{T-1}\sum_{t=1}^T (\mathbf{x}_t - \hat{\boldsymbol{\mu}})(\mathbf{x}_t - \hat{\boldsymbol{\mu}})^T \end{align} \]
Daily rebalancing We will start with a daily rebalancing since we already have the daily returns readily available.
# compute log-returns and linear returns
X_log <- CalculateReturns(prices, "log")[-1]
X_lin <- CalculateReturns(prices)[-1]
N <- ncol(X_log) # number of stocks
T <- nrow(X_log) # number of days
# split data into training and test data
T_trn <- round(0.7*T) # 70% of data
X_log_trn <- X_log[1:T_trn, ]
X_log_tst <- X_log[(T_trn+1):T, ]
X_lin_trn <- X_lin[1:T_trn, ]
X_lin_tst <- X_lin[(T_trn+1):T, ]
# Method 1: directly from linear returns
mu_lin <- colMeans(X_lin_trn)
X_ <- X_lin_trn - matrix(mu_lin, T_trn, N, byrow = TRUE) #remove mean
Sigma_lin <- 1/(T_trn-1) * t(X_) %*% X_
# Method 2: directly from log returns
mu_log <- colMeans(X_log_trn)
Sigma_log <- cov(X_log_trn)
# Method 3: from log returns plus transformation
momentsReturnLog2Lin <- function(mu, Sigma) {
K <- ncol(Sigma)
mu_ <- exp(mu + 0.5*diag(Sigma)) - 1
Sigma_ <- matrix(NA, nrow=K, ncol=K)
for(ii in 1:K)
for(jj in 1:K)
Sigma_[ii,jj] <- exp( mu[ii] + mu[jj] + 0.5*(Sigma[ii,ii]+Sigma[jj,jj]) ) * (exp(Sigma[ii,jj])-1)
return( list(mu=mu_, Sigma=Sigma_) )
}
tmp <- momentsReturnLog2Lin(mu_log, Sigma_log)
mu_log_trans <- tmp$mu
Sigma_log_trans <- tmp$Sigma
Now let compute the three corresponding GMV portfolios:
# create function for GMVP
portolioGMVP <- function(Sigma) {
ones <- rep(1, nrow(Sigma))
Sigma_inv_1 <- solve(Sigma, ones) #same as: inv(Sigma) %*% ones
w <- (1/as.numeric(ones %*% Sigma_inv_1)) * Sigma_inv_1
return(w)
}
# compute the three versions of GMVP
w_lin <- portolioGMVP(Sigma_lin)
w_log <- portolioGMVP(Sigma_log)
w_log_trans <- portolioGMVP(Sigma_log_trans)
w_all <- cbind(w_lin, w_log, w_log_trans)
w_all
## w_lin w_log w_log_trans
## AAPL 0.133266875 0.133093599 0.132663160
## AMD -0.011305201 -0.010945478 -0.010885562
## ADI 0.075478176 0.074269407 0.074486872
## ABBV 0.070315726 0.065447418 0.065214751
## AET 0.302006519 0.313954359 0.313825748
## A 0.098051485 0.094692532 0.094554219
## APD 0.308204376 0.306635367 0.307459326
## AA 0.004160171 0.003564821 0.003389748
## CF 0.019821873 0.019287975 0.019291738
# plot to compare the allocations
barplot(t(w_all), col = c("darkblue","darkcyan", "darkgoldenrod"), legend = colnames(w_all),
main = "Portfolio allocation", xlab = "stocks", ylab = "dollars", beside = TRUE)
By portfolio allocation, AAPL, AET and APD are shown the most positive in investing value, but it is not significate in difference between log and tranformation.
Modelling portfolio optimization as quadratic program
mu <- colMeans(X_log_trn)
Sigma <- cov(X_log_trn)
The uniform portfolio allocates equal weight to each stock: \(\mathbf{w} = \frac{1}{N}\mathbf{1}\)
w_unif <- rep(1/N, N)
The Global Minimum Variance Portfolio (GMVP) is formulated as \[ \begin{array}{ll} \underset{\mathbf{w}}{\textsf{minimize}} & \mathbf{w}^T\mathbf{\Sigma}\mathbf{w}\\ {\textsf{subject to}} & \mathbf{1}^T\mathbf{w} = 1\\ & \mathbf{w}\ge\mathbf{0} \end{array}\] We can compute it with a solver (the closed-form solution does not exist for w???0):
library(CVXR)
##
## Attaching package: 'CVXR'
## The following object is masked from 'package:purrr':
##
## is_vector
## The following object is masked from 'package:matrixcalc':
##
## vec
## The following object is masked from 'package:dplyr':
##
## id
## The following object is masked from 'package:stats':
##
## power
portolioGMVP <- function(Sigma) {
w <- Variable(nrow(Sigma))
prob <- Problem(Minimize(quad_form(w, Sigma)),
constraints = list(w >= 0, sum(w) == 1))
result <- solve(prob)
return(as.vector(result$getValue(w)))
}
w_GMVP <- portolioGMVP(Sigma)
w_GMVP
## [1] 1.296308e-01 1.290853e-07 6.611604e-02 6.360544e-02 3.146232e-01
## [6] 9.372526e-02 3.118962e-01 2.233130e-03 1.816985e-02
The mean-variance Markowitz portfolio with no shorting is formulated as:
\[\begin{array}{ll} \underset{\mathbf{w}}{\textsf{maximize}} & \boldsymbol{\mu}^T\mathbf{w} -\lambda\mathbf{w}^T\mathbf{\Sigma}\mathbf{w}\\ {\textsf{subject to}} & \mathbf{1}^T\mathbf{w} = 1\\ & \mathbf{w}\ge\mathbf{0} \end{array}\]
portolioMarkowitz <- function(mu, Sigma, lmd = 0.5) {
w <- Variable(nrow(Sigma))
prob <- Problem(Maximize(t(mu) %*% w - lmd*quad_form(w, Sigma)),
constraints = list(w >= 0, sum(w) == 1))
result <- solve(prob)
return(as.vector(result$getValue(w)))
}
w_Markowitz <- portolioMarkowitz(mu, Sigma, lmd = 2)
We can now compare the allocations of the portfolios:
# put together all portfolios
w_all <- cbind(w_unif, w_GMVP, w_Markowitz)
rownames(w_all) <- colnames(X_lin)
colnames(w_all) <- c("uniform", "GMVP", "Markowitz")
round(w_all, digits = 2)
## uniform GMVP Markowitz
## AAPL 0.11 0.13 0.38
## AMD 0.11 0.00 0.00
## ADI 0.11 0.07 0.00
## ABBV 0.11 0.06 0.35
## AET 0.11 0.31 0.00
## A 0.11 0.09 0.00
## APD 0.11 0.31 0.00
## AA 0.11 0.00 0.27
## CF 0.11 0.02 0.00
barplot(t(w_all),
main = "Portfolio allocation", xlab = "stocks", ylab = "dollars", beside = TRUE,
legend = c(colnames(w_all)),
col = c("darkblue", "brown", "darkcyan"))
Then we can compare the performance:
# compute returns of all portfolios
ret_all <- xts(X_lin %*% w_all, index(X_lin))
ret_all_trn <- ret_all[1:T_trn, ]
ret_all_tst <- ret_all[-c(1:T_trn), ]
# performance
table.AnnualizedReturns(ret_all_trn)
## uniform GMVP Markowitz
## Annualized Return 0.3301 0.2820 0.5328
## Annualized Std Dev 0.1653 0.1279 0.1921
## Annualized Sharpe (Rf=0%) 1.9970 2.2045 2.7741
table.AnnualizedReturns(ret_all_tst)
## uniform GMVP Markowitz
## Annualized Return 0.1430 0.1373 -0.2311
## Annualized Std Dev 0.1950 0.1329 0.2239
## Annualized Sharpe (Rf=0%) 0.7333 1.0329 -1.0321
{ chart.CumReturns(ret_all, main = "Performance of different portfolios",
wealth.index = TRUE, legend.loc = "topleft", colorset = rich8equal)
addEventLines(xts("training", index(X_lin[T_trn])), srt=90, pos=2, lwd = 2, col = "darkblue") }
Finally, we can plot the expected return vs the standard deviation along with the efficient frontier:
# first, compute the efficient frontier
w_frontier_trn <- NULL
lmd_sweep <- exp(seq(-5, 5, by = 0.5))
for (lmd in lmd_sweep)
w_frontier_trn <- cbind(w_frontier_trn, portolioMarkowitz(mu, Sigma, lmd))
ret_frontier_trn <- xts(X_lin_trn %*% w_frontier_trn, index(X_lin_trn))
mu_frontier_trn <- table.AnnualizedReturns(ret_frontier_trn)[1, ]
sd_frontier_trn <- table.AnnualizedReturns(ret_frontier_trn)[2, ]
# plot in-sample sd-mu scatter plot
#maxSR <- table.AnnualizedReturns(ret_all_trn[, "maxSR"])[3, ]
chart.RiskReturnScatter(ret_all_trn,
main = "Annualized Return and Risk",
symbolset = c(rep(21, 9), rep(22, 4+3)),
colorset = c(rep("red", 9), rep("blue", 4), rep("green", 3)),
bg = "black")
#add.sharpe = maxSR)
lines(sd_frontier_trn, mu_frontier_trn)
From the graph of annualized Return and Risk, Markowitz optimization has larger annualized return and risk.
We can conclude with the following points:
To compare the average yearly return, AMD and AET are the most increasing return yearly. It seems that using linear returns or log-returns does not make any significant difference for daily, weekly, and monthly returns. Mean-variance Markowitz portfolio has larger annualized return and risk, and Global Minimum Variance Portfolio (GMVP) has lower annualized return and risk.