Skip to content
All library documents

Efficiently Computing Ledoit–Wolf Covariance Shrinkage Terms with Matrix Products

Article Quant Q&A · Author: math

Summary

The document examines how an R implementation computes the Ledoit–Wolf shrinkage estimator’s variance term from a time-by-asset return matrix. It focuses on why the product of the transpose of elementwise-squared demeaned returns with the squared matrix produces pairwise sums of squared cross-products. The same matrix dimensions and alignment explain the related term that combines demeaned returns with sample covariance entries.

Key ideas

  • Demeaning the return series produces residuals for each asset across time.
  • In R, squaring a matrix elementwise creates squared residual observations.
  • Multiplying the transpose of that squared matrix by itself sums pairwise products across time.
  • Using the unsquared residual matrix in the related product yields the cross term involving sample covariances.
  • The explanation concerns the estimator’s implementation and does not compare shrinkage performance empirically.

Tags

Full text
# Implementation of Ledoit Wolf shrinkage estimator within R package tawny


# Implementation of Ledoit Wolf shrinkage estimator within R package tawny












I want to implement the shrinkage intensity given by Ledoit and Wolf, see here page 13. They define $y_{it}$ with $1\le i\le N$ and $1\le t\le t$ be the return on stock $i$ at time $t$. Moreover, $z_i:=\bar{y}_i:=\frac{1}{T}\sum_{t=1}^Ty_{it}$ the mean estimator of the $i$-th stock. As they explain the optimal shrinkage intensity estimator is given by (for simplicity I drop the hat notation)

$$\kappa = \frac{\pi-\rho}{\gamma}$$

I have a question about the implementation of $\pi$. They define

$$\pi_{ij} = \frac{1}{T}\sum_{t=1}^T((y_{it}-z_i)(y_{jt}-z_j)-s_{ij})^2$$

I wanted to implement this in a efficient way. The package 'tawny' has a preimplemented function of this. I checked the source code, which can be found here in the file shrinkage.R. They use (I copy):

```
# Sum of the asymptotic variances
# returns : T x N (zoo) - Matrix of asset returns
# sample : N x N - Sample covariance matrix
# Used internally.
# S <- cov.sample(ys)
# ys.p <- shrinkage.p(ys, S)
shrinkage.p <- function(returns, sample)
{
  T <- nrow(returns)
  N <- ncol(returns)
  ones <- rep(1,T)
  means <- t(returns) %*% ones / T
  z <- returns - matrix(rep(t(means), T), ncol=N, byrow=TRUE)

  term.1 <- t(z^2) %*% z^2
  term.2 <- 2 * sample * (t(z) %*% z)
  term.3 <- sample^2
  phi.mat <- (term.1 - term.2 + term.3) / T

  phi <- list()
  phi$sum <- sum(phi.mat)
      phi$diags <- diag(phi.mat)
  phi
}
```

The input is the transposed $y$ as they define it to be a $T\times N$ matrix. Their final output I'm interested in is the following:

```
phi.mat <- (term.1 - term.2 + term.3) / T
```

If their code works properly (which for sure it does) its true that $(i,j)$ entry of `phi.mat` is equal $\pi_{i,j}$. However, this is the point I cant figure out why this is true. Since in `R` addition / subtraction of matrices is elementwise it should be true that `term.1` is equal to

$$\sum_{t=1}^T(y_{it}-z_i)^2(y_{jt}-z_j)^2$$

and `term.2` equal to

$$2\sum_{t=1}^T(y_{it}-z_i)(y_{jt}-z_j)s_{ij}$$

and last `term.3` equal to

$$\sum_{t=1}^T s_{ij}^2$$

I understand `term.3`. However, `term.1` and `term.2` are not that clear to me. I guess understanding one of them will help me understand the other. So lets focus on `term.1`:

> Question Why is it true that $(i,j)$ entry of their code `term.1` is equal to $\sum_{t=1}^T(y_{it}-z_i)^2(y_{jt}-z_j)^2$? Isn't the matrix product, which they suggest, equal to $$\sum_{t=1}^T(y_{it}-z_i)(y_{tj}-z_j)$$ and the matrix of returns is clearly not symmetric?

## Answer by user12348 (score 3, accepted)

https://quant.stackexchange.com/a/21432

The question you asked can be explained by these two lines of the code

```
e   means <- t(returns) %*% ones / T
z <- returns - matrix(rep(t(means), T), ncol=N, byrow=TRUE)
term.1 <- t(z^2) %*% z^2 e
```

Here returns is TxN which gives you matrix ${y_{nt}}$ where n has i and j elements ; means is TxN of matrix ${z_i}$ , same mean for each asset for the time series. Therefore, line 2 of code above gives you z variable which is matrix ${y_{nt}-z_i}$. When you take a transpose and multiply you get a sum of cross multiplication elements. Elements were squared when you do z^2. therefore you are taking a transpose of matrix of squared elements like $({y_{nt}-z_i})^2$ and multiplying by matrix $({y_{nt}-z_i})^2$. That is how you get cross multiplication of the squared elements for term.1. Likewise for term 2, the code uses t(z) and not t(z^2) which explains the elements are not squared. I hope this explains it.

## Answer by Drew (score -1)

https://quant.stackexchange.com/a/21357

I believe the "^2" operation is performed element by element in this instance. The matrix square in R is ... um... different. Memory fails me on the exact notation.

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.