Actually, the PerformanceAnalytics
function will give the same results but it takes longer to do it. The code below uses sample data for 2017-01-01 to now with AMZN and XOM as stocks and SPY as a proxy for market returns. A window of 40 trading days is used in the rolling calculations. The rolling beta value is calculated using the CAPM.beta
and BetaCoVariance
functions from PerformanceAnalytics
and by three methods which directly calculate the covariance matrix and then taking the ratio of the pairwise covariances to the market variance. The results from the methods are displayed to show they are the same. microbenchmark
from the microbenchmark
package is used to measure execution times for all methods. The direct calculations is one to two orders of magnitude faster.
library(xts)
library(quantmod)
library(PerformanceAnalytics)
library(microbenchmark)
#
# get price time histories and calculate returns
# use SPY as proxy for S&P 500; SPY should be first symbol in assets
#
assets <- c("SPY", "AMZN", "XOM")
getSymbols( assets, from = "2017-01-01", auto.assign = TRUE)
asset_prices <- xts()
asset_prices <- Reduce(f=function(x,y) {y_sym=eval(as.name(y)); merge(x,y_sym[,paste0(y,".Adjusted")])},
x = assets, init=asset_prices)
asset_returns <- diff.xts(asset_prices, arithmetic = FALSE, na.pad=FALSE)-1
market_return <- asset_returns$SPY.Adjusted
stock_returns <- asset_returns[,-1]
#
# calculate rolling beta with a 40 trading-day window using CAPM.beta.roll
# For this amount of data and calculating daily betas (by = 1), calculation should take 5-10 seconds
#
width_cor = 40
CAPM.beta_roll <- rollapply(data=stock_returns, FUN=CAPM.beta, Rb= market_return, Rf = 2.5/252,
width = width_cor, by = 1, align = "right", by.column=TRUE)
#
# calculate rolling beta with a 40 trading-day window by calculating the covariance matrix and taking ratio of two elements
# For this amount of data and calculating daily betas (by = 1), calculation should be very quick
#
CovVar <- function(Ra, Rb) {R = merge.xts(Rb, Ra, join="inner"); cv=cov(x=R);
cv[1,-1]/cv[1,1,drop=TRUE]}
CovVar_roll <- rollapplyr(data=stock_returns, width=width_cor,
FUN= CovVar, Rb = market_return, by.column=FALSE)
#
# since rollapply does not apply the window to Rb, it is done in CovVar for each time window
# CovVar1 is a faster version which passes the merged market and stock return to cov directly
# Its single argument R must be the merged data matrix R
#
CovVar1 <- function(R){ cv=cov(x=R); cv[-1,1]/cv[1,1]}
CovVar1_roll <- rollapplyr(data=merge(market_return, stock_returns), width=width_cor,
FUN= CovVar1, by.column=FALSE)
#
# CovVar2 is a faster version which passes the merged market and stock return to cov directly and
# calculates the covariances only between the market returns and stock_returns. For a small number of stocks,
# this is less efficient than calculating the entire covariance for a single matrix as in CovVar1 but it should become more
# efficient for a larger number of stocks.
# Its single argument R must be the merged data matrix R
#
CovVar2 <- function(R){ cv = cov(R[,1], R ); cv[,-1]/cv[1,1] }
CovVar2_roll <- rollapplyr(data=merge(market_return, stock_returns), width=width_cor,
FUN= CovVar2, by.column=FALSE)
#
# Compare to verify that results are the same
#
print(tail(merge(CAPM.beta_roll, CovVar_roll, CovVar1_roll, CovVar2_roll )))
#
# Compare execution times for four above methods and third method using BetaCovariance function from PerformanceAnalytics
# This should take 25-35 seconds to run
#
elapsed_times <- microbenchmark(
CAPM.beta_roll = rollapplyr(data=stock_returns, width=width_cor,
FUN= CAPM.beta, Rb=market_return,by.column=FALSE),
BetaCoVar_roll = rollapplyr(data=stock_returns, width=width_cor,
FUN= BetaCoVariance, Rb=market_return,by.column=FALSE),
CovVar_roll = rollapplyr(data=stock_returns, width=width_cor,
FUN= CovVar, Rb = market_return, by.column=FALSE),
CovVar1_roll = rollapplyr(data=merge(market_return, stock_returns), width=width_cor,
FUN= CovVar1, by.column=FALSE),
CovVar2_roll = rollapplyr(data=merge(market_return, stock_returns), width=width_cor,
FUN= CovVar2, by.column=FALSE),
times = 3)
#
# Direct calculation using covariance matrix, CovVar, is 50 - 100 times faster than PerformanceAnalytics functions
#
print(elapsed_times)
The execution times are:
Unit: milliseconds
expr min lq mean median uq max neval
CAPM.beta_roll 3007.34309 3009.92618 3016.57905 3012.50928 3021.19703 3029.88477 3
BetaCoVar_roll 3453.83531 3471.70954 3478.91433 3489.58377 3491.45383 3493.32390 3
CovVar_roll 69.19571 69.57012 69.83189 69.94453 70.14999 70.35544 3
CovVar1_roll 38.72437 39.17021 39.33052 39.61605 39.63359 39.65113 3
CovVar2_roll 60.75020 61.08255 61.36130 61.41490 61.66684 61.91878 3
CovVar1 is the quickest since at least for a small number of dimensions, R calculates the covariance matrix much more efficiently for a single matrix input than for an input of two matrices where it has to align the matrices. For some larger number of dimensions, the CovVar2 should be faster.