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"))

TE-002.jpg


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 = "")

TE-004.jpg


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")

TE-005.jpg

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")

TE-006.jpg

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)

TE-007.jpg

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 = "")

TE-009.jpg


> 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 = "")

TE-010.jpg

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 :

  1. Can you fit a regression equation between the two sigma’s ?
  2. Fit a CAPM model between NIFTY BeES and NIFTY returns and check the stability of beta ?
  3. Is the Tracking error stationary ?
  4. Is the tracking error mean stationary ?
  5. Is the deseasonalized tracking error stationary ?
  6. 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