CAPM.beta rollapply

Posted

技术标签:

【中文标题】CAPM.beta rollapply【英文标题】: 【发布时间】:2018-12-08 15:45:50 【问题描述】:

我已经成功计算了我在xts 对象中的滚动相关性

x <- cbind(market_return,stock_returns)
rollcor_3year <- rollapplyr(
    x, width=width_cor,function(x) cor(x[,1],x[,-1],
    use="pairwise.complete.obs"),by.column=FALSE)

该相关性后来用于计算滚动 Beta。

现在我从PerformanceAnalytics 包中找到了函数CAPM.beta,我想知道为什么我不能使用

beta <- rollapplyr(x,width=width_cor,function(x) CAPM.beta(x[,1],x[,-1]),by.column=FALSE)

beta <- rollapplyr(stock_returns,width=width_cor,CAPM.beta,Rb=market_return,by.column=FALSE)

直接。

这两个函数都会开始计算但不会停止...

如果我从预定义的函数中获得相同的测试版,我会很高兴,但显然它不是那样工作的。我做错了什么?

【问题讨论】:

您能否提供示例数据以重现该问题? 【参考方案1】:

实际上,PerformanceAnalytics 函数会给出相同的结果,但需要更长的时间才能完成。下面的代码使用 2017-01-01 到现在的样本数据,其中 AMZN 和 XOM 作为股票,SPY 作为市场回报的代理。滚动计算中使用 40 个交易日的窗口。滚动 beta 值是使用来自 PerformanceAnalyticsCAPM.betaBetaCoVariance 函数以及通过直接计算协方差矩阵然后取成对协方差与市场方差的比率的三种方法来计算的。显示方法的结果以表明它们是相同的。 microbenchmark 包中的 microbenchmark 用于测量所有方法的执行时间。直接计算要快一到两个数量级。

  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)

执行时间为:

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 是最快的,因为至少对于少数维数,R 计算单个矩阵输入的协方差矩阵的效率要高于必须对齐矩阵的两个矩阵的输入。对于一些更大的维度,CovVar2 应该更快。

【讨论】:

啊,非常感谢。我以为它会花费更长的时间,但结果确实和我看到的一样。跟进:在你的函数中,cv[1,-1] 是成对股票和市场的协方差,对吗? rollapply 如何知道它必须只针对下一个股票列进行迭代并保持市场列相同? 在 CovVar 中,rollapply 为函数提供时间窗口的 stock_returns 行,Ra。但是,它没有窗口 Rb,market_return,因此该函数使用带有 join = "inner" 的 merge.xts 来形成 Rb 和 Ra 共同日期的返回矩阵,即该迭代的窗口日期。在第二种方法 CovVar_roll1 中,market_return 首先与 stock_returns 合并,然后交给 rollapply。对于两者,market_return 是返回矩阵的第一列,因此是 cov 矩阵的第一行和第一列。 cv[1,-1] 是该时间窗口内股票与市场的协方差。 好吧,你知道为什么在我的滚动相关函数中它被指定为(...),function(x) cor(x[,1],x[,-1](...),因为我从***得到了这个函数,但不能真正理解它背后的概念。似乎它需要第一列和没有最后一列的矩阵之间的相关性? x[,-1] 从 x 中删除第一列,而不是最后一列。 cor(x[,1],x[,-1]) 计算 x 的第一列与其余列之间的相关性。我已经按答案进行了编辑,以在我的第一个方法中展示这种方法。上面的结果表明,对于较少数量的股票,计算单个矩阵输入的全协方差会更快。随着股票数量的增加,只计算市场和股票之间的协方差应该会更快。【参考方案2】:

这是WaltS's answer 中的解决方案,但比使用rollRegres 包的CovVar2 函数快约16 倍

library(xts)
library(quantmod)
library(PerformanceAnalytics)
library(microbenchmark)

# setup
assets <- c("SPY", "AMZN", "XOM")
getSymbols( assets, from = "2017-01-01", auto.assign = TRUE)
#R [1] "SPY"  "AMZN" "XOM"

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]

# solution from WaltS's answer
width_cor <-  40
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)

# rollRegres solution
library(rollRegres)
dat <- as.matrix(merge(market_return, stock_returns))
X  <- cbind(1, dat[, 1])
Ys <- dat[, -1, drop = FALSE]
roll_out <- apply(Ys, 2, function(y)
  roll_regres.fit(x = X, y = y, width = width_cor)$coefs[, 2])

# gives the same
all.equal(as.matrix(CovVar2_roll), roll_out, check.attributes = FALSE)
#R [1] TRUE

# much faster
microbenchmark(
  CovVar2 = rollapplyr(
    data = merge(market_return, stock_returns), width=width_cor,
    FUN= CovVar2,  by.column=FALSE),
  rollRegres = 
    dat <- as.matrix(merge(market_return, stock_returns))
    X  <- cbind(1, dat[, 1])
    Ys <- dat[, -1, drop = FALSE]
    roll_out <- apply(Ys, 2, function(y)
      roll_regres.fit(x = X, y = y, width = width_cor)$coefs[, 2])
  , times = 10)
#R Unit: milliseconds
#R       expr       min        lq      mean    median        uq      max neval
#R    CovVar2 37.669941 39.086237 39.877981 39.530485 41.011374 41.71893    10
#R rollRegres  1.987162  2.036149  2.486836  2.102717  3.342224  3.73689    10

【讨论】:

rollRegress 是这个答案的作者进行滚动回归的全新软件包。看起来很有希望。

以上是关于CAPM.beta rollapply的主要内容,如果未能解决你的问题,请参考以下文章