Compute monthly equity market returns (in simple returns) based on market capitalization data.
require(pacman)
## Loading required package: pacman
require(xts)
## Loading required package: xts
## Loading required package: zoo
##
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
require(openxlsx)
## Loading required package: openxlsx
require(quantmod)
## Loading required package: quantmod
## Loading required package: TTR
## Registered S3 method overwritten by 'quantmod':
## method from
## as.zoo.data.frame zoo
require(devtools)
## Loading required package: devtools
## Loading required package: usethis
devtools::install_github('systematicinvestor/SIT.date')
## Skipping install of 'SIT.date' from a github remote, the SHA1 (6263da60) has not changed since last install.
## Use `force = TRUE` to force installation
require(curl)
## Loading required package: curl
## Using libcurl 7.68.0 with OpenSSL/1.1.1f
curl_download('https://github.com/systematicinvestor/SIT/raw/master/SIT.tar.gz', 'SIT',mode = 'wb',quiet=T)
install.packages('SIT', repos = NULL, type='source')
## Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.2'
## (as 'lib' is unspecified)
require(SIT)
## Loading required package: SIT
## Loading required package: SIT.date
##
## Attaching package: 'SIT'
## The following object is masked from 'package:TTR':
##
## DVI
## The following object is masked from 'package:base':
##
## close
require(ggplot2)
## Loading required package: ggplot2
p_load(quantmod, quadprog,lpSolve)
p_load(xts)
p_load(TTR)
require(readxl)
## Loading required package: readxl
library(readxl)
library(ggplot2)
library(lubridate)
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
library(DT)
library(curl)
library(tidyquant)
## Loading required package: PerformanceAnalytics
##
## Attaching package: 'PerformanceAnalytics'
## The following object is masked from 'package:graphics':
##
## legend
library(quantmod)
library(purrr)
##
## Attaching package: 'purrr'
## The following object is masked from 'package:SIT':
##
## cross
library(SIT)
rm(list=ls())
devtools::install_github('joshuaulrich/xts', force = T)
## Downloading GitHub repo joshuaulrich/xts@HEAD
##
## * checking for file ‘/tmp/RtmpVGOl4Q/remotes139d11af2f6c/joshuaulrich-xts-96612b7/DESCRIPTION’ ... OK
## * preparing ‘xts’:
## * checking DESCRIPTION meta-information ... OK
## * cleaning src
## * checking for LF line-endings in source and make files and shell scripts
## * checking for empty or unneeded directories
## * looking to see if a ‘data/datalist’ file should be added
## * building ‘xts_0.12.1.3.tar.gz’
## Warning in sprintf(gettext(fmt, domain = domain, trim = trim), ...) :
## one argument not used by format 'invalid uid value replaced by that for user 'nobody''
## Warning: invalid uid value replaced by that for user 'nobody'
## Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.2'
## (as 'lib' is unspecified)
devtools::install_github('joshuaulrich/quantmod', force = T)
## Downloading GitHub repo joshuaulrich/quantmod@HEAD
##
## * checking for file ‘/tmp/RtmpVGOl4Q/remotes139d797c0787/joshuaulrich-quantmod-d1b4b17/DESCRIPTION’ ... OK
## * preparing ‘quantmod’:
## * checking DESCRIPTION meta-information ... OK
## * installing the package to process help pages
## * saving partial Rd database
## * checking for LF line-endings in source and make files and shell scripts
## * checking for empty or unneeded directories
## * building ‘quantmod_0.4.20.2.tar.gz’
## Warning in sprintf(gettext(fmt, domain = domain, trim = trim), ...) :
## one argument not used by format 'invalid uid value replaced by that for user 'nobody''
## Warning: invalid uid value replaced by that for user 'nobody'
## Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.2'
## (as 'lib' is unspecified)
#
library(SIT)
library(quantmod)
library(quadprog)
Betting_Against_Beta_Equity_Factors_Monthly_1_ <- read_excel("Betting Against Beta Equity Factors Monthly (1).xlsx",
sheet = "ME(t-1)", range = "A19:AD1165")
data <- Betting_Against_Beta_Equity_Factors_Monthly_1_ ########
date <- as.Date(data$DATE, "%m/%d/%Y")
Betting_Against_Beta_Equity_Factors_Monthly_1_ <- xts(coredata(Betting_Against_Beta_Equity_Factors_Monthly_1_[, -1]), order.by = date)
stockpr = subset(Betting_Against_Beta_Equity_Factors_Monthly_1_, select = c(AUS,CAN,FRA,DEU,JPN,GBR,USA) )
stockpr2 <- with(stockpr, stockpr[(date >= "1989-12-31" & date <= "2021-12-31")])
monthly.return <- na.omit(Return.calculate(stockpr2, method = "discrete"))
head(monthly.return)
## AUS CAN FRA DEU JPN
## 1990-01-31 0.03066617 0.0242062712 0.106772375 0.175900236 0.02123232
## 1990-02-28 0.01574424 -0.0631233515 -0.039850578 0.022063077 -0.04670123
## 1990-03-31 -0.06035407 0.0006305838 -0.028605990 0.002080930 -0.08398405
## 1990-04-30 -0.02950981 -0.0104092150 0.067718048 0.095673570 -0.17803619
## 1990-05-31 -0.08247848 -0.0819545270 0.041442220 -0.061484068 -0.01682893
## 1990-06-30 0.09444762 0.0728235944 0.002914819 0.005735468 0.15212730
## GBR USA
## 1990-01-31 0.09773906 0.01799629
## 1990-02-28 0.01176512 -0.07791268
## 1990-03-31 -0.03207884 0.01297222
## 1990-04-30 -0.03012199 0.02484288
## 1990-05-31 -0.06753591 -0.02987430
## 1990-06-30 0.13266871 0.08571573
tail(monthly.return)
## AUS CAN FRA DEU JPN
## 2021-07-31 -0.007808452 -0.004923728 -0.0159977640 -0.005650794 6.743379e-05
## 2021-08-31 -0.006427044 -0.005186644 0.0148548942 0.012951049 -1.218586e-02
## 2021-09-30 0.019011675 0.001517633 -0.0004440893 0.015669145 2.783157e-02
## 2021-10-31 -0.024477429 -0.023212521 -0.0501314877 -0.052374557 1.671376e-02
## 2021-11-30 0.041072770 0.068697882 0.0495422303 0.022591338 -3.342947e-02
## 2021-12-31 -0.058206115 -0.046620684 -0.0319030400 -0.047759221 -3.347900e-02
## GBR USA
## 2021-07-31 -0.033224039 0.026827519
## 2021-08-31 0.025475826 0.009958025
## 2021-09-30 0.006104884 0.026998156
## 2021-10-31 -0.036415768 -0.040433011
## 2021-11-30 0.023093370 0.066567980
## 2021-12-31 -0.054600574 -0.016133549
Denote this strategy as the Benchmark portfolio and create its backtesting report using SIT package.
stockpr3 <- with(stockpr, stockpr[(date >= "1993-01-31" & date <= "2021-12-31")])
#convert returns into price (data market value of equity)
#stockpr3 <- cumprod(stockpr3 + 1)*100
#head(stockpr3)
data <- new.env()
#create 4 required input elements in data
data$prices <- stockpr3
data$weight <- stockpr3
data$execution.price <- stockpr3
data$execution.price[] <- NA
data$symbolnames <- colnames(data$prices)
prices <- data$prices
n = ncol(prices)
data$weight = ntop(prices, n)
model <-list()
model$equal.weight <- bt.run(data, trade.summary=T)
## Latest weights :
## AUS CAN FRA DEU JPN GBR USA
## 2021-12-31 14.29 14.29 14.29 14.29 14.29 14.29 14.29
##
## Performance summary :
## CAGR Best Worst
## 8.2 14.1 -21.7
capital = 100000
data$weight[] = (capital / prices) * data$weight
equal.weight = bt.run(data, type='share')
## Latest weights :
## AUS CAN FRA DEU JPN GBR USA
## 2021-12-31 14.29 14.29 14.29 14.29 14.29 14.29 14.29
##
## Performance summary :
## CAGR Best Worst
## 8.2 14.1 -21.7
head(equal.weight$ret)
## AUS
## 1993-01-31 0.000000000
## 1993-02-28 0.003976969
## 1993-03-31 0.044861517
## 1993-04-30 0.065207441
## 1993-05-31 0.035969320
## 1993-06-30 0.024251503
bt.detail.summary(model$equal.weight)
## $System
## $System$Period
## [1] "Jan1993 - Dec2021"
##
## $System$Cagr
## [1] 8.2
##
## $System$Sharpe
## [1] 0.57
##
## $System$DVR
## [,1]
## AUS 0.51
##
## $System$Volatility
## [1] 16.02
##
## $System$MaxDD
## [1] -57.92
##
## $System$AvgDD
## [1] -7.69
##
## $System$VaR
## 5%
## -7.26
##
## $System$CVaR
## [1] -11.07
##
## $System$Exposure
## [1] 99.71
##
##
## $Trade
## $Trade$Win.Percent
## [1] 100
##
## $Trade$Avg.Trade
## [1] 130.2
##
## $Trade$Avg.Win
## [1] 130.2
##
## $Trade$Avg.Loss
## [1] NaN
##
## $Trade$Best.Trade
## [1] 213.68
##
## $Trade$Worst.Trade
## [1] 25.81
##
## $Trade$WinLoss.Ratio
## [1] NaN
##
## $Trade$Avg.Len
## [1] 347
##
## $Trade$Num.Trades
## [1] 7
##
##
## $Period
## $Period$Win.Percent.Day
## [1] 60.3
##
## $Period$Best.Day
## [1] 14.1
##
## $Period$Worst.Day
## [1] -21.7
##
## $Period$Win.Percent.Month
## [1] 60.3
##
## $Period$Best.Month
## [1] 14.1
##
## $Period$Worst.Month
## [1] -21.7
##
## $Period$Win.Percent.Year
## [1] 69
##
## $Period$Best.Year
## [1] 43
##
## $Period$Worst.Year
## [1] -48.3
plotbt.monthly.table(model$equal.weight$equity)
## Jan Feb Mar Apr May Jun Jul Aug Sep
## 1993 " NA" " 0.4" " 4.5" " 6.5" " 3.6" " 2.4" " -1.1" " 3.1" " 7.0"
## 1994 " 7.4" " 6.5" " -1.3" " -3.5" " 2.4" " -0.3" " -1.7" " 4.3" " 3.8"
## 1995 " 1.1" " -2.6" " 2.0" " 4.4" " 3.1" " 0.4" " -0.4" " 5.9" " -1.9"
## 1996 " 1.5" " 2.8" " 1.3" " 1.0" " 3.3" " 0.6" " -0.4" " -2.3" " 2.1"
## 1997 " -0.6" " -0.8" " 1.8" " -0.7" " 0.8" " 5.2" " 3.9" " 3.8" " -5.4"
## 1998 " 1.0" " 4.1" " 6.2" " 3.5" " 0.8" " -0.5" " 1.1" " -0.7" "-13.4"
## 1999 " 2.4" " 4.4" " -3.4" " 4.2" " 6.6" " -4.3" " 5.8" " 1.6" " 0.5"
## 2000 " 9.4" " -4.1" " 4.3" " 3.6" " -5.7" " -3.6" " 6.1" " -1.8" " 2.7"
## 2001 " 3.3" " 2.4" " -7.5" " -7.4" " 8.0" " -1.6" " -1.6" " -2.5" " -2.9"
## 2002 " 0.4" " -2.6" " 0.3" " 4.9" " -0.1" " 2.1" " -4.3" " -9.1" " -1.9"
## 2003 " -2.4" " -1.4" " -1.9" " -1.0" " 9.6" " 6.5" " 3.3" " 1.6" " 2.4"
## 2004 " 7.1" " 1.5" " 2.3" " 0.9" " -3.9" " 0.5" " 2.6" " -2.4" " 0.1"
## 2005 " 3.9" " -1.4" " 4.2" " -1.9" " -3.3" " 0.6" " 2.1" " 3.8" " 1.4"
## 2006 " 3.9" " 6.7" " -0.4" " 2.7" " 4.7" " -4.0" " -0.6" " 0.2" " 2.5"
## 2007 " 2.7" " 0.4" " 1.3" " 2.5" " 4.4" " 2.9" " 0.0" " -1.9" " -2.2"
## 2008 " -1.5" " -8.7" " 1.7" " -2.6" " 4.8" " 2.3" " -6.8" " -3.4" " -3.7"
## 2009 " 5.3" " -9.9" " -9.4" " 8.1" " 12.5" " 11.6" " 0.0" " 9.6" " 4.4"
## 2010 " 1.7" " -4.4" " 0.7" " 6.2" " -0.5" "-10.9" " -2.8" " 8.9" " -3.7"
## 2011 " 8.5" " 2.2" " 4.3" " -1.7" " 4.8" " -2.9" " -1.2" " -1.6" " -8.3"
## 2012 " -2.1" " 6.5" " 5.0" " -0.6" " -1.4" "-11.3" " 4.6" " 1.5" " 2.6"
## 2013 " 2.4" " 4.8" " -0.6" " 0.7" " 3.4" " -2.0" " -4.1" " 5.4" " -1.1"
## 2014 " 1.6" " -3.7" " 5.0" " -0.2" " 0.8" " 1.5" " 2.0" " -1.9" " 0.8"
## 2015 " -2.0" " -1.4" " 6.1" " -2.3" " 3.5" " -1.3" " -2.5" " 0.9" " -7.0"
## 2016 " -1.9" " -6.3" " -1.0" " 7.6" " 2.7" " -1.3" " -2.7" " 5.4" " -0.5"
## 2017 " 2.9" " 3.0" " 1.6" " 1.9" " 1.5" " 1.7" " 0.8" " 2.9" " 0.0"
## 2018 " 2.5" " 4.5" " -4.8" " -1.6" " 1.7" " -0.7" " -1.1" " 2.0" " -1.1"
## 2019 " -6.0" " 7.4" " 2.5" " -0.4" " 2.7" " -4.8" " 5.6" " -0.7" " -2.6"
## 2020 " 3.2" " -1.7" " -9.7" "-17.0" " 10.1" " 5.2" " 3.6" " 3.8" " 6.0"
## 2021 " 6.0" " -1.0" " 3.2" " 2.6" " 4.4" " 2.8" " -0.6" " 0.6" " 1.4"
## Avg " 2.2" " 0.3" " 0.6" " 0.7" " 2.9" " -0.1" " 0.3" " 1.3" " -0.6"
## Oct Nov Dec Year MaxDD
## 1993 " -1.4" " 4.6" " -4.1" " 28.0" " -4.1"
## 1994 " -3.2" " 2.2" " -3.7" " 12.6" " -4.7"
## 1995 " 1.2" " -0.9" " 2.8" " 15.6" " -2.6"
## 1996 " 2.7" " 1.9" " 6.9" " 23.3" " -2.7"
## 1997 " 5.5" " -4.9" " 0.1" " 8.4" " -5.4"
## 1998 " 0.7" " 9.3" " 5.9" " 17.4" "-14.0"
## 1999 " 0.9" " 5.0" " 5.3" " 32.2" " -4.3"
## 2000 " -6.5" " -3.7" " -5.1" " -5.8" "-16.9"
## 2001 "-10.4" " 3.4" " 4.6" "-13.2" "-24.1"
## 2002 "-10.4" " 4.9" " 3.6" "-12.8" "-23.4"
## 2003 " 3.0" " 7.3" " 1.7" " 31.8" " -6.6"
## 2004 " 4.2" " 4.0" " 6.4" " 25.3" " -3.9"
## 2005 " 3.8" " -4.0" " 3.5" " 12.8" " -5.1"
## 2006 " -0.5" " 4.7" " 2.6" " 24.3" " -4.5"
## 2007 " 6.1" " 5.0" " -5.2" " 16.5" " -5.2"
## 2008 "-14.4" "-21.7" " -6.8" "-48.3" "-48.3"
## 2009 " 5.5" " -2.1" " 3.6" " 43.0" "-18.4"
## 2010 " 10.4" " 4.0" " -2.7" " 5.0" "-13.8"
## 2011 "-11.0" " 10.8" " -4.5" " -3.0" "-23.0"
## 2012 " 2.7" " 1.3" " 1.2" " 9.1" "-13.1"
## 2013 " 6.5" " 3.6" " 0.3" " 20.2" " -6.0"
## 2014 " -5.2" " -0.5" " 0.8" " 0.7" " -6.7"
## 2015 " -4.5" " 7.3" " -0.5" " -4.5" "-13.8"
## 2016 " 1.0" " -2.3" " -0.7" " -0.7" " -9.0"
## 2017 " 2.5" " 1.4" " 1.2" " 23.4" " 0.0"
## 2018 " 0.1" " -8.5" " -0.6" " -8.0" "-14.1"
## 2019 " 2.2" " 2.7" " 1.8" " 10.1" " -6.0"
## 2020 " -3.2" " -3.0" " 14.1" " 7.4" "-26.3"
## 2021 " -3.0" " 3.4" " -4.1" " 16.2" " -4.1"
## Avg " -0.5" " 1.2" " 1.0" " 9.9" "-11.4"
strategy.performance.snapshoot(model, T)
## NULL
Use in-sample data range of previous 36 months to compute covariance matrix. Denote this strategy as the MVP portfolio and create its backtesting report using SIT.
data$prices <- stockpr3
data$weight <- stockpr3
data$execution.price <- stockpr3
data$execution.price[] <- NA
prices <- data$prices
constraints = new.constraints(n, lb = -Inf, ub = +Inf)
# SUM x.i = 1
constraints = add.constraints(rep(1, n), 1, type = '=', constraints)
ret = prices / mlag(prices) - 1
weight = coredata(prices)
weight[] = NA
i = 36
for (i in 36:dim(weight)[1]) {
hist = ret[ (i- 36 +1):i, ]
hist = na.omit(hist)
ia = create.historical.ia(hist, 12)
ia$cov = cov(coredata(hist))
weight[i,] = min.risk.portfolio(ia, constraints)
}
data$weight[] = weight
capital = 100000
data$weight[] = (capital / prices) * data$weight
min.var = bt.run(data, type='share', capital=capital)
## Latest weights :
## AUS CAN FRA DEU JPN GBR USA
## 2021-12-31 -33.98 21.66 -8.95 15.53 107.47 -4.46 2.72
##
## Performance summary :
## CAGR Best Worst
## 4.5 11.4 -15.9
model$min.var.monthly <- bt.run(data, trade.summary = T)
## Latest weights :
## AUS CAN FRA DEU JPN GBR USA
## 2021-12-31 -1.8 0.72 -0.26 0.53 1.56 -0.12 0.01
##
## Performance summary :
## CAGR Best Worst
## -0.1 1.1 -1.5
sum(as.numeric(weight[36,])*as.numeric(ret[37,]))
## [1] 0.0318602
model$min.var.monthly$ret[37, ]
## AUS
## 1996-01-31 0.005077245
plotbt.custom.report.part1(model$min.var.monthly, model$equal.weight)
layout(1:2)
plotbt.transition.map(model$min.var.monthly$weight)
legend('topright', legend = 'min.var.monthly', bty = 'n')
plotbt.transition.map(model$equal.weight$weight)
legend('topright', legend = 'equal weight', bty = 'n')
strategy.performance.snapshoot(model, T)
## NULL
model <- rev(model)
plotbt.custom.report(model)
stockpr4 <- with(stockpr, stockpr[(date >= "1988-01-31" & date <= "2021-12-31")])
hist.caps = stockpr4
hist.caps.weight = hist.caps/rowSums(hist.caps)
# Plot Transition of Market Cap Weights in time
plot.transition.map(hist.caps.weight, index(hist.caps.weight), xlab='', name='Market Capitalization Weight History')
# Plot History for each Country's Market Cap
layout( matrix(1:9, nrow = 3, byrow=T) )
col = plota.colors(ncol(hist.caps))
for(i in 1:ncol(hist.caps)) {
plota(hist.caps[,i], type='l', lwd=5, col=col[i], main=colnames(hist.caps)[i])
}
aa.test.create.ia.country <- function(dates = '1990::2021')
{
# load.packages('quantmod,quadprog')
symbols = spl('EWA,EWC,EWQ,EWG,EWJ,EWU,SPY')
symbol.names = spl('Australia, Canada, France, Germany, Japan, UK, USA')
getSymbols(symbols, from = '1991-01-01', auto.assign = TRUE)
hist.prices = merge(EWA,EWC,EWQ,EWG,EWJ,EWU,SPY)
period.ends = endpoints(hist.prices, 'months')
hist.prices = Ad(hist.prices)[period.ends, ]
colnames(hist.prices) = symbol.names
annual.factor = 12
hist.prices = na.omit(hist.prices[dates])
hist.returns = na.omit( ROC(hist.prices, type = 'discrete') )
ia = create.historical.ia(hist.returns, annual.factor)
return(ia)
}
# 3. Load up efficient frontier plotting function:
plot.ef <- function(
ia,
efs,
portfolio.risk.fn = portfolio.risk,
transition.map = TRUE,
layout = NULL
)
{
risk.label = as.character(substitute(portfolio.risk.fn))
n = ia$n
x = match.fun(portfolio.risk.fn)(diag(n), ia)
y = ia$expected.return
xlim = range(c(0, x,
max( sapply(efs, function(x) max(match.fun(portfolio.risk.fn)(x$weight,ia))) )
), na.rm = T)
ylim = range(c(0, y,
min( sapply(efs, function(x) min(portfolio.return(x$weight,ia))) ),
max( sapply(efs, function(x) max(portfolio.return(x$weight,ia))) )
), na.rm = T)
x = 100 * x
y = 100 * y
xlim = 100 * xlim
ylim = 100 * ylim
if( !transition.map ) layout = T
if( is.null(layout) ) layout(1:2)
par(mar = c(4,3,2,1), cex = 0.8)
plot(x, y, xlim = xlim, ylim = ylim,
xlab='', ylab='', main=paste(risk.label, 'vs Return'), col='black')
mtext('Return', side = 2,line = 2, cex = par('cex'))
mtext(risk.label, side = 1,line = 2, cex = par('cex'))
grid();
text(x, y, ia$symbols, col = 'blue', adj = c(1,1), cex = 0.8)
for(i in len(efs):1) {
ef = efs[[ i ]]
x = 100 * match.fun(portfolio.risk.fn)(ef$weight, ia)
y = 100 * ef$return
lines(x, y, col=i)
}
plota.legend(sapply(efs, function(x) x$name), 1:len(efs))
if(transition.map) {
plot.transition.map(efs[[i]]$weight, x, risk.label, efs[[i]]$name)
}
}
# Use reverse optimization to compute the vector of equilibrium returns
bl.compute.eqret <- function(
risk.aversion, # Risk Aversion
cov, # Covariance matrix
cap.weight, # Market Capitalization Weights
risk.free = 0 # Rsik Free Interest Rate
)
{
return( risk.aversion * cov %*% cap.weight + risk.free)
}
#--------------------------------------------------------------------------
# Compute Risk Aversion, prepare Black-Litterman input assumptions
#--------------------------------------------------------------------------
ia = aa.test.create.ia.country()
# compute Risk Aversion
risk.aversion = bl.compute.risk.aversion( ia$hist.returns$` USA` )
# the latest market capitalization weights
cap.weight = last(hist.caps.weight)
# create Black-Litterman input assumptions
ia.bl = ia
ia.bl$expected.return = bl.compute.eqret( risk.aversion, ia$cov, as.vector(cap.weight) )
# Plot market capitalization weights and implied equilibrium returns
layout( matrix(c(1,1,2,3), nrow=2, byrow=T) )
pie(coredata(cap.weight), paste(colnames(cap.weight), round(100*cap.weight), '%'),
main = paste('Country Market Capitalization Weights for', format(index(cap.weight),'%b %Y'))
, col=plota.colors(ia$n))
plot.ia(ia.bl, T)