Draft:The COS method

From Wikipedia, the free encyclopedia

The COS method is a numerical technique in computational finance to efficiently price European plain vanilla put and call options provided the characteristic function of the logarithmic returns is given in closed-form. The main idea of the COS method is to approximate the (usually unknown) probability density function of the logarithmic returns by a cosine series expansion. The method has been developed in 2008 by Fang Fang and Cornelis W. Oosterlee.[1] The COS method converges for a large family of densities.[2] The COS method converges exponentially if the density of the logarithmic returns is smooth and has semi-heavy tails.[3]

Introduction[edit]

Consider a financial market with a bank-account paying a risk-free rate and stock with deterministic price today and random price at some future date . Let be the expectation of under the risk-neutral measure and assume the characteristic function of the centralized log-returns is given in closed-form. The characteristic function is given explicitly for many models and processes, e.g., the Black-Scholes model, the variance gamma process, the CGMY model or the Heston model[4]. The price of a European put option with maturity and strike is given by the fundamental theorem of asset pricing by

where is the probability density function of . The price of a call option can be obtained by the put-call parity. Very often, is not given explicitly and the COS method is used to approximate and the price of the option.

The method[edit]

For some , the density is truncated and the truncated density is approximated by a cosine series expansion:

where for the coefficients are defined by
denotes the real part of a complex number and is the imaginary unit. The price of a European put option can be approximated by[1]
where
The coefficients are given in closed-form provided is given analytically and the coefficients can also be computed explicitly in important cases, e.g., for plain vanilla European put or call options and digital options. This makes the COS method numerically very efficient and robust.

Let . For a European put option it holds that if and otherwise

where

and
To price a call option it is numerically more stable to price a put option instead and use the put-call parity.[1]

Parameters[edit]

To apply the COS method, one has to specify the truncation range and the number of terms . Let be the error tolerance between the true price and its approximation by the COS method. If is small enough and has semi-heavy tails, the truncation range of a put option can be chosen using Markov’s inequality by[2][3]

where is even and is the -th moment of , which can be obtained using a computer algebra system and the following relation of and the characteristic function:
Often, is a reasonable choice.[2] In particular, the fourth moment can also be obtained by the Kurtosis and variance of by .

If is also a times differentiable function with bounded derivatives, the number of terms can then be chosen using integration by parts by[3]

denotes the uniform norm. There is the following useful inequality:[3]
The last integral can be solved numerically with standard techniques and it is also given explicitly in some cases.[3]

Black-Scholes model[edit]

In the Black-Scholes model with volatility , the stock price is modelled at maturity by

where is a standard normal random variable. The expectation of under the risk-neutral measure is given by
and the characteristic function of is given by
since follows a normal distribution. The -th moment of for even in the Black-Scholes model is given by
The derivative of the probability density function of is bounded by[3]
where is the Gamma function.

Simple implementation[edit]

In the following code, the COS method is implemented in R for the Black-Scholes model to price European call option and the Heston model to price a put option.

########### Black-Scholes model ###########
#Characteristic function of the centralized log-returns in the BS model. params is volatility
phi = function(u, mat, params, S0, r){
  return(exp(-params^2 * mat * u^2 / 2))
}

#mu is equal to E[log(S_T)]
mu = function(mat, params, S0, r){
    return(log(S0) + r * mat - 1/2 * params^2 * mat)
}   

#cosine coefficients of the density.
ck = function(L, mat, N, params, S0, r){
  k = 0:N
  return(1 / L * Re(phi(k * pi / (2 * L), mat, params, S0, r) *  exp(1i * k * pi/2)))
}

#cosine coefficients of a put option, see Appendix Junike and Pankrashkin (2022).
vk = function(K, L, mat, N, params, S0, r){
  mymu = mu(mat, params, S0, r)   #mu = E[log(S_T)]
  d = min(log(K) - mymu, L)
  if(d <= -L)
    return(rep(0, N + 1)) #Return zero vector
  k = 0:N 
  psi0 = 2 * L / (k * pi) * (sin(k * pi * (d + L) / (2 * L)))
  psi0[1] = d + L
  tmp1 = k * pi / (2 * L) * sin( k * pi * (d + L) / (2 * L))
  tmp2 = cos(k * pi * (d + L) / (2 * L))
  tmp3 = 1 + (k * pi / (2 * L))^2
  psi1 = (exp(d) * (tmp1 + tmp2) - exp(-L)) / tmp3
  return(exp(-r * mat) * (K * psi0 - exp(mymu) * psi1))
}

#approximation of put option by COS method
put_COS = function(K, L, mat, N, params, S0, r){
  tmp = ck(L, mat, N, params, S0, r) * vk(K, L, mat, N, params, S0, r)
  tmp[1] = 0.5 * tmp[1] #First term is weighted by 1/2
  return(sum(tmp))
}

#approximation of call option by COS method using put-call parity
call_COS = function(K, L, mat, N, params, S0, r){
  return(put_COS(K, L, mat, N, params, S0, r) + S0 - K * exp(-r * mat))
}

#Price a call option in BS model by the COS method
eps = 10^-4 #error tolerance
K = 90 #strike
S0 = 100 #current stock price
r = 0.1 #interest rates
params = 0.2 #volatility
mat = 0.7 #maturity
n = 8 #number of moments to get truncation range
mu_n = (params * sqrt(mat))^n * prod(seq(1, n, by = 2)) #n-th moment of log-returns
L = (2 * K * exp(-r * mat) * mu_n / eps)^(1 / n) #Truncation range, Junike (2024, Eq. (3.10))
s = 40 #number of derivatives to determine the number of terms
boundDeriv = gamma(s / 2 + 1) * 2^(s / 2) / (pi * (params * sqrt(mat))^(s + 2))
tmp = 2^(s + 5 / 2) * boundDeriv * L^(s + 2) * 12 * K * exp(-r * mat)
N = ceiling((tmp / (s * pi^(s + 1) * eps))^(1 / s)) #Number of terms, Junike (2024, Sec. 6.1)
call_COS(K, L, mat, N, params, S0, r) #The price of call option is 17.24655.


########### Heston model ###########
#Use functions vk, put_COS, call_COS from the Black-Scholes model above.

#characteristic function of log-returns in the Heston with parameters params.
#The characteristic function is taken from Schoutens et. al (2004).
psiLogST_Heston = function(u, mat, params, S0, r){
  kappa = params[1] #speed of mean reversion
  theta = params[2] #level of mean reversion
  xi = params[3] #vol of vol
  rho = params[4] #correlation vol stock
  v0 = params[5] #initial vol
  d = sqrt((rho * xi * u * 1i - kappa)^2 - xi^2 * (-1i * u - u^2))
  mytmp = kappa - rho * xi * u * 1i
  g = (mytmp - d) / (mytmp + d)
  expdmat = exp(-d * mat)
  tmp0 = 1i * u * (log(S0) + r * mat)
  tmp1 = (mytmp - d) * mat - 2 * log((1 - g * expdmat) / (1 - g))
  tmp2 = theta * kappa * xi^(-2) * tmp1
  tmp3 = v0 * xi^(-2) * (mytmp - d) * (1 - expdmat) / (1 - g * expdmat)
  exp(tmp0 + tmp2 + tmp3)
}

library(Deriv) #There are much faster alternatives like SageMath.
psiLogST_Heston1=Deriv(psiLogST_Heston, "u") 

#mu is equal to E[log(S_T)]
mu = function(mat, params, S0, r){
    Re(-1i * psiLogST_Heston1(0, mat, params, S0, r))
}
#Characteristic function of centralized log-returns in the Heston model.
phi = function(u, mat, params, S0, r){
    psiLogST_Heston(u, mat, params, S0, r) * exp(-1i * u * mu(mat, params, S0, r))
}

#Derivatives of the characteristic function of the centralized log-returns in the Heston model.
phi1 = Deriv(phi, "u")
phi2 = Deriv(phi1, "u")
phi3 = Deriv(phi2, "u") #Takes very long but has to be done only once.
phi4 = Deriv(phi3, "u") #Takes very long but has to be done only once.
save(phi4, file = "phi4.RData") #save for later use. Load with load("phi4.RData").

#Price a put option in Heston model by the COS method.
eps = 10^-6 #error tolerance
K = 90 #strike
S0 = 100 #current stock price
r = 0.1 #interest rates
params = c(0.6067, 0.0707, 0.2928, -0.7571, 0.0654)
mat = 0.7 #maturity
mu_n = abs(phi4(0, mat, params, S0, r)) #4-th moment of log-returns. 
L = (2 * K * exp(-r * mat) * mu_n / eps)^(1 / 4) #Truncation range, Junike (2024, Eq. (3.10)). It is 36.99.
s = 20 #number of derivatives to determine the number of terms
integrand = function(u){1 / (2 * pi) * abs(u)^(s + 1) * abs(phi(u, mat, params, S0, r))} 
boundDeriv = integrate(integrand, -Inf, Inf)$value
tmp = 2^(s + 5 / 2) * boundDeriv * L^(s + 2) * 12 * K * exp(-r * mat)
N = ceiling((tmp / (s * pi^(s + 1) * eps))^(1 / s)) #Number of terms, Junike (2024, Sec. 6.1). It is 4418.   
put_COS(K, L, mat, N, params, S0, r) #The price of put option is 2.773954.

See also[edit]

References[edit]

  1. ^ a b c Fang, Fang; Oosterlee, Cornelis W. (2008). "A novel pricing method for European options based on Fourier-cosine series expansions". SIAM Journal on Scientific Computing. 31 (2): 826–848. doi:10.1137/080718061.
  2. ^ a b c Junike, Gero; Pankrashkin, Konstantin (2022). "Precise option pricing by the cos method–How to choose the truncation range" (PDF). Applied Mathematics and Computation. 451 (126935): 1–14. doi:10.1016/j.amc.2022.126935.
  3. ^ a b c d e f Junike, Gero (2024). "On the number of terms in the COS method for European option pricing" (PDF). Numerische Mathematik. doi:10.1007/s00211-024-01402-1.
  4. ^ Schoutens, Wim; Simons, Erwin; Tistaert, Jurgen (2004). "A perfect calibration! Now what?" (PDF). The Best of Wilmott: 66–78. doi:10.1002/wilm.42820040216 (inactive 2024-04-18).{{cite journal}}: CS1 maint: DOI inactive as of April 2024 (link)