NIFTY BeES Tracking Error
The purpose of this document is to give pointers to the tracking error project that I had been discussing with you guys. In this document, I will be taking up NIFTY BeES as the instrument and carry out the analysis of its tracking error. You can use this as a guiding document to perform analysis for the respective projects assigned.
The following code is used to import NIFTY Total Returns Index , NIFTY BeES data and create a consolidated time series object.
> library(xts)
> nifty <- read.csv("C:/Cauldron/Benchmark/Interns/nifty.csv",
+ header = T, stringsAsFactors = F)
> niftybees <- read.csv("C:/Cauldron/Benchmark/Interns/niftybees.csv",
+ header = T, stringsAsFactors = F)
> nifty$trade.date <- as.Date(nifty[, 1], format = "%d-%b-%y")
> niftybees$trade.date <- as.Date(niftybees[, 1], format = "%d-%b-%y")
> nifty <- xts(nifty[, 2], nifty$trade.date)
> niftybees <- xts(niftybees[, 2], niftybees$trade.date)
> te <- merge(nifty, niftybees)
> te[, 2] <- te[, 2] * 10
> condition <- is.na(te[, 1]) | is.na(te[, 2])
> te <- te[!condition, ] |
The following code is used to plot NIFTY and NIFTY BeES after appropriate standardization
> par(mfrow = c(1, 1))
> plot(te[, 1], col = "blue", main = "Nifty Vs NIFTY BeES", ylim = c(1000,
+ 7500))
> par(new = T)
> plot(te[, 2], col = "red", main = "", ylim = c(1000, 7500))
> legend("topleft", legend = c("NIFTY", "NIFTYBeES"), fill = c("blue",
+ "red")) |

Computing the daily and cumulative returns of the index and the etf
> te.returns <- returns(te) > te.returns <- te.returns[-1, ] > cum.returns <- cbind(nifty = cumsum(te.returns[, 1]), niftybees = cumsum(te.returns[, + 2])) > tracking.error <- as.xts(te.returns[, 1] - te.returns[, 2]) |
> par(mfrow = c(2, 1)) > plot(te.returns[, 1], col = "blue", type = "l", main = "Daily Returns Nifty ", + ylim = c(-0.15, 0.15), ylab = "Cumulative Returns ") > plot(te.returns[, 2], type = "l", col = "red", main = "Daily Returns NIFTY BeES", + ylim = c(-0.15, 0.15), ylab = "") |

Let’s look at the tracking error on a daily basis
> par(mfrow = c(2, 1)) > plot(tracking.error, main = "Tracking Error") > boxplot(coredata(tracking.error), main = "Quartile Plot") |

Clearly there are a few days when the tracking error has shot up significantly One can remove these points by considering them as outliers
> par(mfrow = c(1, 1)) > outliers <- which(coredata(tracking.error) > 0.005) > temp <- tracking.error[-outliers, ] > boxplot(coredata(temp), main = "Quartile Plot") |

Quantile plots to see whether the distribution is a known one or not.
> par(mfrow = c(1, 2))
> qqnorm(coredata(temp), col = "blue", main = "With Out Removing Outliers")
> qqline(coredata(temp), col = "red", lwd = 2)
> quantile(coredata(temp), probs = seq(0, 1, 0.05))
0% 5% 10% 15% 20%
-1.702406e-03 -1.234580e-04 -6.489466e-05 -3.600757e-05 -1.922662e-05
25% 30% 35% 40% 45%
-8.159498e-06 1.916501e-06 8.768261e-06 1.409085e-05 1.836776e-05
50% 55% 60% 65% 70%
2.251457e-05 2.735141e-05 3.354721e-05 4.038747e-05 4.671687e-05
75% 80% 85% 90% 95%
5.474132e-05 6.478504e-05 7.946505e-05 9.587932e-05 1.388404e-04
100%
1.783116e-03
> relevant <- which(coredata(temp) > -0.000123458 & coredata(temp) <
+ 0.0001388404)
> temp2 <- temp[relevant, ]
> qqnorm(coredata(temp2), col = "blue", main = "After Removing Outliers")
> qqline(coredata(temp2), col = "red", lwd = 2) |

After considering removing the lower 5 percent and higher 5 percent of the values, the distribution does look normal
> te.sanitized <- temp2 > plot(te.sanitized, col = "blue", main = "Tracking Error") |
Now that you can visually see the tracking error, a few interesting questions that can be questioned are :
How does the Daily Error Annualized Rolling Tracking Error behave ?
> par(mfrow = c(1, 1)) > sigma.roll <- as.xts(100 * sqrt(252) * rollapply(te.sanitized[, + 1], width = 252, sd, align = "right")) > plot(sigma.roll, type = "l", col = "blue", lwd = 2, ylab = "Annual Tracking Error (%) ", + main = "") |

> x1 <- as.xts(te.returns[, 1] - te.returns[, 2]) > y <- merge(x1, te.returns) > condition <- is.na(y[, 1]) | is.na(y[, 2]) | is.na(y[, 3]) > z <- y[!condition, ] > relevant <- which(z[, 1] > -0.000123458 & z[, 1] < 0.0001388404) > z <- z[relevant, ] > te.sigma.roll <- as.xts(100 * sqrt(252) * rollapply(z[, 1], width = 252, + sd, align = "right")) > nifty.sigma.roll <- as.xts(100 * sqrt(252) * rollapply(z[, 2], + width = 252, sd, align = "right")) > par(mfrow = c(2, 1)) > plot(te.sigma.roll, type = "l", col = "blue", lwd = 2, ylab = "Annual Tracking Error (%) ", + main = "") > plot(nifty.sigma.roll, type = "l", col = "blue", lwd = 2, ylab = "Rolling Stdev NIFTY (%) ", + main = "") |

Assuming that joint distribution of tracking error and nifty volatility is an elliptical distribution, then the correlation can be considered as an indicator of the comovement
> cor(te.sigma.roll, nifty.sigma.roll)
nifty
x1 0.7235928 |
A rough estimate of comovement between vol of nifty and tracking error is about 0.6777
Questions for you guys to ponder and work on :
- Can you fit a regression equation between the two sigma’s ?
- Fit a CAPM model between NIFTY BeES and NIFTY returns and check the stability of beta ?
- Is the Tracking error stationary ?
- Is the tracking error mean stationary ?
- Is the deseasonalized tracking error stationary ?
- General Gyan that you need to know
Download Nifty High low close data from NSE and calculate the following
a)Parkinson Estimator b)Garman-Klass Estimator c)Rogers-Satchell Estimator d)Yang-Zhang Estimator
Ideally the project should end up with some sort of relationship that you can figure out between NIFTY vol and Tracking error