Executive Summary

In this assignment we looked at the assumption of normal distributed return of stock indices. We also tried to fit the binomial model and Black-Scholes model to real data and compare with market data. The B&S model gave better results than the binomial model.

Step 1

Data was downloaded from the Tel Aviv Stock Exchange website, 3 months of daily prices for the TA 35 index and one month of daily prices for a call option on the index with a strike price of 1400. The risk free rate was taken from a government bond.

Unfortunately the Yahoo finance API isn’t working so I had to manually download the data which is attached.

Step 2

A model that could use to model the behavior of the index over time could be a geometrical Brownian motion. Here we assume that the natural log of the returns is normally distributed. \[\ln\Big(\frac{S_{t}}{S_{0}}\Big)\sim\mathcal{N}(\mu t,\sigma^{2}t)\] Where \(\mu\) is the drift parameter and \(\sigma\) is the diffusion parameter. This follows the notion of Brownian motion that independent increments are normally distributed with parameters that a proportional to the size of the increment (time in our case).

This is a realistic model since it models percentage changes and not absolute changes in price which makes sense with how market prices work. The expected returns of GBM are independent of the value of the process (stock price), which agrees with what we would expect in reality. A GBM process only assumes positive values, just like real stock prices.

However, GBM is not a completely realistic model, in particular it falls short of reality in the following points:

  1. In real stock prices, volatility changes over time (possibly stochastic ally), but in GBM, volatility is assumed constant.
  2. In real life, stock prices often show jumps caused by unpredictable events or news, but in GBM, the path is continuous (no discontinuity).

The continuous returns were calculated as the following \[r_{t}=\ln\Big(\frac{P_{t}}{P_{t-1}}\Big)=\ln(P_{t})-\ln(P_{t-1})\]

# loading data download from TASE

ta35 <- read.csv("ta35.csv")

# converting to ts

ta35_ts <- xts(ta35$close, order.by=as.Date(ta35$date, "%d/%m/%Y"))
names(ta35_ts) <- "TA35"

## plotting stock price

dygraph(ta35_ts, main = "TA35 Index Price")%>%dyOptions(axisLineWidth = 1.5, fillGraph = TRUE, drawGrid = TRUE)

We can see that even in a short 3 month time period there is volatility in the index.

# Denote n the number of time periods:

n <- nrow(ta35)

# Compute continuously compounded daily returns
ta35_return <- log(ta35$close[2:n]) - log(ta35$close[1:(n-1)])

## qqplotting

p <- ggplot(data.frame(ta35_return), aes(sample=ta35_return))+stat_qq() + geom_abline(intercept=mean(ta35_return),slope=sd(ta35_return))
ggplotly(p)

A Q-Q plot is a scatterplot created by plotting two sets of quantiles against one another. If both sets of quantiles came from the same distribution, we should see the points forming a line that’s roughly straight. As can be seen the plot above, the continous returns does roughly line up on the straight line.

For the binomial model what we need to know is \(u,d,r,C_{u},C_{d,}E\). we also need for \(d<r<u\) to be true. The annual risk free rate is 0.1%. We don’t need to know the true probability of these changes since we will use the risk free probability measure: \[C=\frac{\tilde{p}\cdot C_{u}+(1-\tilde{p})C_{d}}{1+r},\tilde{p}=\frac{r-d}{u-d}\] Unlike the Black-Scholes Model we don’t need to estimate the volatility here.

Step 3

I used \[u=\exp(\sigma\cdot(\sqrt{T/n})-1\] and \[d=\exp(-\sigma\cdot(\sqrt{T/n})-1\] in order to approximate the binomial model to the log-normal Black-Scholes model. The volatility is estimated from the data and \(n=1\).

# loading data and calulating time in days

one_month <- read.csv("one_month.csv")
one_month$date <- as.Date(one_month$date, "%d/%m/%Y")
one_month$exp_day <- as.Date("28/7/2017","%d/%m/%Y")
one_month$T <- as.integer(one_month$exp_day-one_month$date)/365

# cont' risk free rate (from annual rate)

rf <- log(1+0.001)


# volatility of returns - this is daily turned to annual

sigma <- sqrt(var(ta35_return)*365)


# function to calculate price under the binomial model

binom_option <- function(E,S,sigma,rf,T)
{
  # fixing for term of option
  
  u <- exp(sigma*sqrt(T))-1
  d <- exp(-sigma*sqrt(T))-1
  r_t <- exp(T*rf)-1
  
  p_tilde <- (r_t-d)/(u-d)
  
  c_u <- max(S*(u+1)-E,0)
  c_d <- max(S*(d+1)-E,0)
  C <- (p_tilde*c_u + (1-p_tilde)*c_d)/(1+r_t)
  
  #print(c(u1,d1,r_t,p_tilde,c_u,p_tilde*c_u,S))
  
  return(C*100)
}

one_month$binom <- rep(0,nrow(one_month))

for (i in 1:nrow(one_month))
{one_month$binom[i] <- round(binom_option(1400,one_month$s[i],sigma,rf,one_month$T[i]))}

For the Black-Scholes Model we need to calculate the volatility of continous returns and the continous risk free rate. We’ll then use the Black-Scholes formula to get a theoretical price of the options and compare it to the observed market price. I will use the Black-Scholes formula: \[C=S\cdot\mathcal{N}(d_{1})-E\cdot e^{-r_{c}T}\cdot N(d_{2})\] where \[d_{1}=\frac{\ln(S/E)+(r_{c}+\frac{\sigma^{2}}{2})\cdot T}{\sigma\sqrt{T}},d_{2}=d_{1}-\sigma\cdot\sqrt{T}\] Under the following assumptions:

  1. The option is European and can only be exercised at expiration.
  2. No dividends are paid out during the life of the option.
  3. Efficient markets (i.e., market movements cannot be predicted).
  4. There are no transaction costs in buying the option.
  5. The risk-free rate and volatility of the underlying are known and constant.
  6. That the returns on the underlying are normally distributed.
# Function for calculating Black-Scholes Call Option Value
blackscholes <- function(S, X, rf, T, sigma) {
  
    d1 <- (log(S/X)+(rf+sigma^2/2)*T)/(sigma*sqrt(T))
    d2 <- d1 - sigma * sqrt(T)
 
    return(S*pnorm(d1) - X*exp(-rf*T)*pnorm(d2))
}
Date Time TA35 C_1400 BS Binom BS.Market.Ratio Binom.Market.Ratio
2017-06-07 0.14 1,427 3,602 3,725 3,963 103.41% 110.02%
2017-06-06 0.14 1,424 3,420 3,497 3,808 102.25% 111.35%
2017-06-05 0.15 1,427 3,830 3,765 4,017 98.30% 104.88%
2017-06-04 0.15 1,428 4,041 3,811 4,062 94.31% 100.52%
2017-06-01 0.16 1,421 3,295 3,402 3,790 103.25% 115.02%
2017-05-29 0.16 1,419 3,565 3,339 3,770 93.66% 105.75%
2017-05-28 0.17 1,419 3,436 3,307 3,754 96.25% 109.25%
2017-05-25 0.18 1,423 3,783 3,670 4,067 97.01% 107.51%
2017-05-24 0.18 1,419 3,408 3,374 3,844 99.00% 112.79%
2017-05-23 0.18 1,419 3,620 3,431 3,898 94.78% 107.68%
2017-05-22 0.18 1,422 3,846 3,604 4,044 93.71% 105.15%
2017-05-21 0.19 1,422 3,286 3,678 4,112 111.93% 125.14%
2017-05-18 0.19 1,412 3,286 3,087 3,650 93.94% 111.08%
2017-05-17 0.20 1,420 4,190 3,566 4,058 85.11% 96.85%
2017-05-16 0.20 1,433 4,750 4,464 4,750 93.98% 100.00%
2017-05-15 0.20 1,432 4,475 4,456 4,755 99.58% 106.26%
2017-05-14 0.21 1,432 4,475 4,420 4,740 98.77% 105.92%
2017-05-11 0.21 1,423 4,128 3,841 4,328 93.05% 104.84%
2017-05-10 0.22 1,420 4,402 3,702 4,224 84.10% 95.96%
2017-05-09 0.22 1,429 4,474 4,277 4,681 95.60% 104.63%
2017-05-08 0.22 1,424 3,930 3,952 4,440 100.56% 112.98%

We can see that the binomial model’s results were much less accurate, it is a more simplistic model. Also, I might have estimated errors when estimated the volatility since I only used 3 months - trade off between the bias of using a longer period and variance of using less data.

On the other hand we can see that the Black-Scholes price is fairly close to the market price. All parameters of the model are observed and known except for the volatility. The volatility needs to estimated. I can assume that the volatility expected by investors is different from the one I estimated. The volatility implied by market prices under the Black-Scholes model is called the implied volatility.

I will now try to numerically find the implied volatility that best fits the market prices observed (I assume that it is constant during the month). I numerically found the volatility that fulfills: \[\min_{\tilde{\sigma}}\sum_{i=1}^{n}\Big(\frac{abs[observed_{i}-model(\tilde{\sigma})_{i}]}{n}\Big)\]

 # looking for implied volatility

grid <- seq(0.01,0.2,0.001)
stat <- rep(0,length(grid))

for ( i in 1:length(grid))
{
  stat[i] <- mean(abs(one_month$c_1400-blackscholes(one_month$s,1400, rf,one_month$T,grid[i])*100))

}

# best solution

imp <- grid[which(stat==min(stat))]
estimated <- sigma

imp
## [1] 0.104
estimated
## [1] 0.09753878

We can see the investors expected a larger volatility than I estimated, interesting.

Bibliography

  1. Jonathan Regenstein’s Blog.
  2. Class notes.
  3. Rstudio Blog.
  4. Investopedia.
  5. TASE.