Skip to content

Hodrick (1992) Standard Errors (Reverse Regression)¶

In the previous post, I calculate Hodrick (1992) standard error IB in a forward regression setting. This post shows how to use the reverse regression approach of Hodrick (1992) to compute standard errors which account for the overlapping nature of predictors.

Prerequisite¶

library(data.table)
library(sandwich)
library(lmtest)
library(knitr)

1 Hodrick (1992) standard errors¶

1.1 Reverse regression approach¶

The reverse regression of the one-period-ahead return on the previous \(h\)-period sum of the predictor is given by:

hodrick1992vcov.reverse <- function(x.var , r.var.ahead, h){
  x.mat <- as.matrix(x.var)
  r.mat <- as.matrix(r.var.ahead)
  # 1. Construct demeaned returns or one-period residuals
  ee.mat <- r.mat - colMeans(r.mat) 
  # ee.mat <- as.matrix(lm(r.var~1)$residuals) # equivalent to the demeaned returns

  # 2. Construct sum of squares of predictors
  x.mat <- cbind(1, x.mat)  # add the constant
  T <- nrow(x.mat)
  K <- ncol(x.mat)
  Exx <- t(x.mat) %*% x.mat / T # compute average of square  (1/T) * (X'X)
  # Construct the reverse regressor by summing previous h-period values 
  rev.x.mat = as.matrix(frollsum(x.var, n = h, align = "right"))

  # Construct the matrix for sum of squares and standard errors
  rev.x.mat <- cbind(matrix(h,T - h +1 ,1) , rev.x.mat[h:T,])  # add the constant and get rid of NAs
  rev.ee.mat <- as.matrix(ee.mat[h:T,])
  b_reverse <- solve(t(rev.x.mat) %*% rev.x.mat) %*% t(rev.x.mat) %*% r.mat[h:T,]

  # Compute the matrix of sum of squares and directly the standard errors
  wk <- rev.ee.mat[,1] * rev.x.mat
  S  <- 1/nrow(wk) * t(wk) %*% wk
  vcov_hodrick <- (1 / nrow(wk)) * solve(Exx) %*% S %*% solve(Exx)
  std_hodrick <- sqrt(diag(vcov_hodrick))
  return(list(b = b_reverse, vcov_hodrick = vcov_hodrick, std_hodrick = std_hodrick, Nobs = nrow(wk)))
}

1.2 Forward regression approach¶

As mentioned in the previous post, the standard long-horizon predictive regression is given by:

\[ r_{t \rightarrow t+h}=\alpha+\beta \cdot x_t+\varepsilon_{t \rightarrow t+h}. \]
hodrick1992vcov.forward <- function(x.var , r.var.ahead, h){
  x.mat <- as.matrix(x.var)
  r.mat <- as.matrix(r.var.ahead)
  # 1. Construct demeaned returns or one-period residuals
  ee.mat <- r.mat - colMeans(r.mat) 
  # ee.mat <- as.matrix(lm(r.var.ahead~1)$residuals) # equivalent to the demeaned returns
  x.mat <- cbind(1, x.mat)  # add the constant
  T <- nrow(x.mat)
  K <- ncol(x.mat)
  Exx <- t(x.mat) %*% x.mat / T # compute average of square  (1/T) * (X'X)
  b <- solve(t(x.mat) %*% x.mat) %*% t(x.mat) %*% r.mat


  compute_wk_t <- function(t, h){
    wk_t <- matrix(0, nrow = K, ncol = 1)
    XX <- matrix(0, nrow = K, ncol = 1)
    for (i in 0:(h-1)){
      XX <- XX + x.mat[t-i,]
    }
    wk_t <- ee.mat[t] * XX
    return(wk_t)
  }

  compute_S <- function(h){
    S <- matrix(0, nrow = K, ncol = K)
    for (t in h:T){
      S <- S + (compute_wk_t(t,h) %*% t(compute_wk_t(t,h)))
    }
    S <- S / T
    return(S)
  }


  S <- compute_S(h)
  vcov_hodrick <- (1 / T) * solve(Exx) %*% S %*% solve(Exx)
  std_hodrick <- sqrt(diag(vcov_hodrick))
  return(list(b = b, vcov_hodrick = vcov_hodrick, std_hodrick = std_hodrick, Nobs = T))
}

1.3 Combine these two methods¶

hodrick1992vcov <- function(x.var , r.var.ahead, h, method = c("forward", "reverse")){

  if (method == "forward"){
    return(hodrick1992vcov.forward(x.var, r.var.ahead, h))
  }

  if (method == "reverse"){
    return(hodrick1992vcov.reverse(x.var, r.var.ahead, h))
  }
}

2 Examples¶

set.seed(42)
simulate_dt <- simulate_overlapping_data()
kable(head(simulate_dt))
t x_t r_t r_t_plus_1 r_t_plus_3 r_t_plus_6 r_t_plus_12
1 1.3709584 0.0800000 -0.0344801 0.0543657 0.1392246 0.1188966
2 -0.5646982 -0.0344801 0.0088458 -0.0062542 0.1045764 0.0885166
3 0.3631284 0.0088458 0.0193802 0.0495512 0.0290402 0.1400495
4 0.6328626 0.0193802 0.0213252 0.0848590 0.1118831 0.1849328
5 0.4042683 0.0213252 0.0441536 0.1108306 0.1128342 0.1644087
6 -0.1061245 0.0441536 0.0453517 -0.0205110 0.0688945 0.1369073
h <- 6
m_ols <- lm(simulate_dt$r_t_plus_6~simulate_dt$x_t)
m_forward <- hodrick1992vcov(x.var = simulate_dt$x_t, r.var.ahead = simulate_dt$r_t_plus_1, h = h, method = "forward")
m_reverse <- hodrick1992vcov(x.var = simulate_dt$x_t, r.var.ahead = simulate_dt$r_t_plus_1, h = h, method = "reverse")
kable(data.table(
  b_ols = coef(m_ols)[[2]],
  se_ols = sqrt(diag(vcov(m_ols)))[[2]],
  se_forward = m_forward$std_hodrick[2],
  b_reverse = m_reverse$b[2,],
  se_reverse = m_reverse$std_hodrick[2]
))
b_ols se_ols se_forward b_reverse se_reverse
0.0027597 0.0031609 0.0033485 0.0007351 0.0033626