---------------------------------------------------
#                                                 #
#     Chapter 6: Maximum Likelihood Estimation    #
#              of the ARCH(d)-Model               #
#                                                 #
---------------------------------------------------



In the last chapter we familiarized ourselves with the
maximum likelihood method and we derived the following 
expression for the log-likelihood function (we discarded 
a constant involving logarithms of 2*pi's):

log(L) =                                                          (1)

 - sum_{k=1}^N { log[vol(t_{k-1})] + 1/2 * y_k^2 / vol^2(t_{k-1}) }  

The y_k are given by the realized returns of the actual 
time series data,

y_k = ret(t_k) = [ S(t_k)-S(t_{k-1}) ] / S(t_{k-1})               (2)

and the exact form of the volatility vol(t_k) depends 
on the model under consideration. For the ARCH(1)-model, 
it is given by 

vol^2(t_{k-1}) = w0*bsvol^2 + (1-w0)*ret^2(t_{k-1})               (3)

with w0 and bsvol being the model parameters. For the 
DAX time series, we found 

DAX:    w0 = 0.7  und  bsvol = 1.4%                               (4)

by maximizing the ARCH(1) log-likelihood function. The 
parameter bsvol of 1.4% turned out to be very close to 
the standard deviation sd(ret) of the returns taken over 
the whole time series length, sd(ret) = 1.39%. 


The more realistic ARCH(d)-model which is to be estimated 
in this chapter has the following vol-specification:

vol^2(t_{k-1}) = 

 w0*bsvol^2 + (1-w0)*[ret^2(t_{k-1})+...+ret^2(t_{k-d})] / d      (5)

with ARCH(d) model parameters w0, bsvol and the time 
horizon d. The maximization of the likelihood function 
again has to be done numerically and to this end we 
start an R-session:



# Start R-Session: 
#
# before we fit to DAX-data, we check on an artificially 
# generated ARCH(d) return set: let's use the pathArchd 
# function from the last chapter to generate these arti-
# ficial return data:


pathArchd = function( N , bsvol , w0 , d )
{
  S = rep(0,N)
  ret = rep(0,N)
  phi = rnorm(N)
  S0 = 100
  vol0 = bsvol            # we put startvol to bsvol
  vol = rep(vol0,N)       # ARCH-vol

  sumretd = 0

  # the first step, k=1, is special:
  S[1] = S0*(1+vol0*phi[1])
  ret[1] = (S[1]-S0)/S0
  vol[1] = sqrt( w0*bsvol^2 + (1-w0)*ret[1]^2 )
  sumretd = sumretd + ret[1]^2

  for(k in 2:N)
  {
    if(k<=d)
    {
      S[k] = S[k-1]*(1+vol[k-1]*phi[k])
      ret[k] = (S[k]-S[k-1])/S[k-1]
      sumretd = sumretd + ret[k]^2
      vol[k] = sqrt( w0*bsvol^2 + (1-w0)*sumretd/k )
    }
    else
    {
      S[k] = S[k-1]*(1+vol[k-1]*phi[k])
      ret[k] = (S[k]-S[k-1])/S[k-1]
      sumretd = sumretd + ret[k]^2
      sumretd = sumretd - ret[k-d]^2
      vol[k] = sqrt( w0*bsvol^2 + (1-w0)*abs(sumretd)/d )
    }
  }#next k
  
  par(mfrow=c(2,1))
  plot(S,type="l",ylim=c(0,250))
  plot(ret,type="l",ylim=c(-0.08,0.08))

  result = list(S,ret)
  names(result) = c("S","ret")
  return(result)
}


# we generate an artificial ARCH(d) return set where we 
# know the actual model parameters ( bsvol , w0 , d ):
# let's set a seed for the random numbers to make this 
# reproducible:
set.seed(234)

N = 2500
bsvol = 0.01
w0 = 0.3
d = 20
res = pathArchd( N , bsvol , w0 , d )




ret = res$ret
summary(ret)


# we have to calculate the log-Likelihood function:
#
# use 'vectorized' calculation logic of R to make the code 
# a bit more performant -> code looks a bit less intuitive:


logL = function( bsvol , w0 , d )
{
  w1 = 1 - w0
  n = length(ret)

  cumsum_retsquared = cumsum(ret^2)
  
  # remove last d elements:
  cumsum_retsquared_shifted = cumsum_retsquared[-((n-d+1):n)]

  # fill in d zeros at the first d places:
  cumsum_retsquared_shifted = c(rep(0,d),cumsum_retsquared_shifted)

  sum_dretsquared = cumsum_retsquared - cumsum_retsquared_shifted
  
  # now we have to divide this sum by d, but the first d elements 
  # are special:
  divisor1 = 1:d
  divisor2 = rep(d,n-d)
  divisor = c(divisor1,divisor2)

  sum_dretsquared = sum_dretsquared/divisor
  
  # now we can calculate ARCH(d)-vol:
  vol = sqrt( w0*bsvol^2 + w1*sum_dretsquared )

  # shift vol-vector by 1 element ( there is a vol(t_{k-1}) in 
  # the likelihood-function, not a vol(t_k) )
  vol = vol[-n]                           # remove last one
  vol = c(sqrt(w0*bsvol^2),vol)           # add a new first one

  # the actual calculation:
  terms = log(vol) + 1/2*ret^2/vol^2
  result = -sum(terms)

  return(result)
}


# let's check:
logL( bsvol=0.01, w0=0.3, d=20 )          # alright, some number

# let's look for the maximum:
# first, we fix bsvol to its natural value:
bsvol = sd(ret)
bsvol

# we make a contour-plot in the (w0,d)-plane:

w0 = seq(from=0.05,to=0.95,by=0.05)
dd = 1:40
nw = length(w0)
nd = length(dd)

# matrix z = logL for contour-plot:
z = matrix( 0 , nrow=nd , ncol=nw )
for(i in 1:nd)
{
  for(j in 1:nw )
  {
    z[i,j] = logL( bsvol , w0[j] , dd[i] )
  }
}# still very fast evaluation of logL!

# let's look at the result:
contour( dd , w0 , z , nlevels = 50 )




contour( dd , w0 , z , nlevels = 50 , zlim=c(10000,11000) )




contour( dd , w0 , z , nlevels = 100 , zlim=c(10200,10600) )





# we recover correct parameter values:
# d around 20 and w0 around 0.3;
# differences between d = 15, 20 and 25 not that big, 
# depending on the particular realization of random numbers; 
# d below 8 clearly less favorable -> redo calculation for 
# dd = 10:40 and w0 < 0.5 (-> resolution on pictures more nice)

w0 = seq(from=0.05,to=0.5,by=0.01)
dd = 10:40
nw = length(w0)
nd = length(dd)
z = matrix( 0 , nrow=nd , ncol=nw )
for(i in 1:nd)
{
  for(j in 1:nw )
  {
    z[i,j] = logL( bsvol , w0[j] , dd[i] )
  }
}
contour( dd , w0 , z , nlevels = 150 , zlim=c(10200,10600) )




# some more pictures:
image( dd , w0 , z )




persp( dd , w0 , z )




require(plot3D)
persp3D( dd , w0 , z )





# let's fix d=20 and check that bsvol=sd(ret) corresponds 
# to the maximum:

bsvol = seq(from=0.1/100,to=4/100,by=0.1/100)
nbs = length(bsvol)
z = matrix( 0 , nrow=nbs , ncol=nw )
for(i in 1:nbs)
{
  for(j in 1:nw )
  {
    z[i,j]=logL( bsvol[i] , w0[j] , d=20 )
  }
}
contour( bsvol , w0 , z , nlevels = 50 )




contour( bsvol , w0 , z , nlevels = 80 , zlim=c(10000,10500) )




# -> looks all good.


#
# finally, let's fix w0 and vary bsvol and d:
#
bsvol = seq(from=0.1/100,to=2/100,by=0.05/100)
nbs = length(bsvol)
dd = 10:40
nd = length(dd)
z = matrix( 0 , nrow=nd , ncol=nbs )
for(i in 1:nd)
{
  for(j in 1:nbs )
  {
    z[i,j]=logL( bsvol[j] , w0=0.3 , d=dd[i] )
  }
}
contour( dd , bsvol , z , nlevels = 50 )




 


-----------------------------------------
#      ARCH(d)-Fit to DAX-Data:         #
-----------------------------------------
dax = read.table("C:/Users/Admin/Courses/FinancialStatistics/DAX.txt",header=TRUE,sep=";")
dax = na.omit(dax)
summary(dax)
rm(ret)
S = dax$index
n=length(S)
ret = rep(0,n)       
for(i in 2:n)
{
  ret[i] = (S[i]-S[i-1])/S[i-1]
}
plot(ret,type="l")





w0 = seq(from=0.05,to=0.95,by=0.05)
dd = 1:40
bsvol = sd(ret)
bsvol
nw = length(w0)
nd = length(dd)
z = matrix( 0 , nrow=nd , ncol=nw )
for(i in 1:nd)
{
  for(j in 1:nw )
  {
    z[i,j] = logL( bsvol , w0[j] , dd[i] )
  }
}

# let's look at the result:
contour( dd , w0 , z , nlevels = 50 )




contour( dd , w0 , z , nlevels = 50 , zlim=c(9500,10500) )




# apparently w0 less than 0.4 and d between 8 and 28:

w0 = seq(from=0.01,to=0.4,by=0.01)
dd = 8:28
bsvol = sd(ret)
#
# reevaluate z with the code above
#
contour( dd , w0 , z , nlevels = 50 , zlim=c(9500,10500) )




contour( dd , w0 , z , nlevels = 100 , zlim=c(9800,10000) )




# -> d = 14  and w0 around 0.15:
max(z)                                        # 9873.06
logL( bsvol=sd(ret) , w0=0.15 , d=14 )        # 9872.65


# some more pictures:
image( dd , w0 , z )




persp( dd , w0 , z )




persp3D( dd , w0 , z )





# finally we check for bsvol:
w0 = seq(from=0.01,to=0.4,by=0.01)
bsvol = seq(from=0.1/100,to=4/100,by=0.1/100)
nbs = length(bsvol)
z = matrix( 0 , nrow=nbs , ncol=nw )
for(i in 1:nbs)
{
  for(j in 1:nw )
  {
    z[i,j]=logL( bsvol[i] , w0[j] , d=14 )
  }
}
contour( bsvol , w0 , z , nlevels = 50)




contour( bsvol , w0 , z , nlevels = 80 , zlim=c(9800,10000))




max(z)                                        # 9872.76
logL( bsvol=sd(ret) , w0=0.15 , d=14 )        # 9872.65

# -> Maximum at w0=15%, bsvol=sd(ret)=1.39%, d=14.





-----------------------------------------
#      ARCH(d)-Fit to SPX-Data:         #
-----------------------------------------

# load SPX.txt data and generate return vector ret:
spx = read.table("C:/Users/Admin/Courses/FinancialStatistics/spx.txt",header=TRUE,sep=";")
head(spx)
summary(spx)     # no NA's
S = spx[,2]
n = length(S)
ret = rep(0,n)       
for(i in 2:n)
{
  ret[i] = (S[i]-S[i-1])/S[i-1]
}
plot(ret,type="l")   # returns look ok




# compute spx-logL and make setup for contour-plot:
# first, we fix bsvol to its natural value:
bsvol = sd(ret)
bsvol
# we make a contour-plot in the (w0,d)-plane:
w0 = seq( from=0.05 , to=0.95 , by=0.05 )
dd = 1:40
nw = length(w0)
nd = length(dd)
z = matrix( 0 , nrow=nd , ncol=nw )
for(i in 1:nd)
{
  for(j in 1:nw )
  {
    z[i,j] = logL( bsvol , w0[j] , dd[i] )
  }
}
contour( dd , w0 , z , nlevels = 50 )




contour( dd , w0 , z , nlevels = 80 , zlim=c(70000,72000) )




# apparently w0 less than 0.3 and d between 8 and 28:
w0 = seq( from=0.01 , to= 0.3 , by=0.005 )
dd = 8:28
#
# reevaluate z = logL with the code above
#
contour( dd , w0 , z , nlevels = 100 , zlim=c(70000,72000) )




contour( dd , w0 , z , nlevels = 200 , zlim=c(71000,72000) )




# -> d = 14  and w0 around 0.15
max(z)                                         # 71413.99
logL( bsvol=sd(ret) , w0=0.15 , d=14 )         # 71413.54

# some more pictures:
persp( dd , w0 , z )




persp3D( dd , w0 , z )




# finally we check for bsvol:
w0 = seq( from=0.01 , to=0.3 , by=0.005 )
bsvol = seq( from=0.1/100 , to=2/100 , by=0.05/100 )
nbs = length(bsvol)
nw = length(w0)
z = matrix( 0 , nrow=nbs , ncol=nw )
for(i in 1:nbs)
{
  for(j in 1:nw )
  {
    z[i,j]=logL( bsvol[i] , w0[j] , d=14 )
  }
}
contour( bsvol , w0 , z , nlevels = 100 , zlim=c(70000,72000) )




contour( bsvol , w0 , z , nlevels = 200 , zlim=c(71000,72000) )




# thus: Maximum at approximately
logL( bsvol=sd(ret) , w0=0.15 , d=14 )         # 71413.54
max(z)                                         # okay..
sd(ret)






-----------------------------------------
#       ARCH(d)-Fit to GE-Data:         #
-----------------------------------------

# load GE.txt data and generate return vector ret:
ge = read.table("C:/Users/Admin/Courses/FinancialStatistics/GE.txt",header=TRUE,sep=";")
head(ge)
summary(ge)     # no NA's
S = ge[,2]
n = length(S)
ret = rep(0,n)       
for(i in 2:n)
{
  ret[i] = (S[i]-S[i-1])/S[i-1]
}
plot(ret,type="l")   # returns look ok




# compute GE-logL and make setup for contour-plot:
# first, we fix bsvol to its natural value:
bsvol = sd(ret)
bsvol
# we make a contour-plot in the (w0,d)-plane:
w0 = seq( from=0.05 , to=0.95 , by=0.05 )
dd = 1:40
nw = length(w0)
nd = length(dd)
z = matrix( 0 , nrow=nd , ncol=nw )
for(i in 1:nd)
{
  for(j in 1:nw )
  {
    z[i,j] = logL( bsvol , w0[j] , dd[i] )
  }
}
contour( dd , w0 , z , nlevels = 50 )




contour( dd , w0 , z , nlevels = 70 , zlim=c(50000,51000) )




# apparently w0 less than 0.3 and d between 10 and 50:
w0 = seq( from=0.01 , to= 0.3 , by=0.005 )
dd = 10:50
#
# reevaluate z = logL with the code above
#
contour( dd , w0 , z , nlevels = 70 , zlim=c(50000,51000) )




contour( dd , w0 , z , nlevels = 100 , zlim=c(50500,51000) )




# -> d = 29  and w0 around 0.15
max(z)                                        # 50936.45
logL( bsvol=sd(ret) , w0=0.15 , d=29 )        # 50936.28

# some more pictures:
persp( dd , w0 , z )




persp3D( dd , w0 , z )




# finally we check for bsvol:
w0 = seq( from=0.01 , to=0.3 , by=0.005 )
bsvol = seq( from=0.5/100 , to=2.5/100 , by=0.05/100 )
nbs = length(bsvol)
nw = length(w0)
z = matrix( 0 , nrow=nbs , ncol=nw )
for(i in 1:nbs)
{
  for(j in 1:nw )
  {
    z[i,j]=logL( bsvol[i] , w0[j] , d=29 )
  }
}
contour( bsvol , w0 , z , nlevels = 50)




contour( bsvol , w0 , z , nlevels = 100 , zlim=c(50500,51000) )




# thus: Maximum at approximately
logL( bsvol=sd(ret) , w0=0.15 , d=29 )        # 50936.28
max(z)                                        # 50936.61
sd(ret)   # 1.62%