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