Skip to content

Cash or Nothing COS Method

Background

Cos Method Cash Or Nothing

Educational script demonstrating cos method cash or nothing concepts.


Code

```python """ Cos Method Cash Or Nothing

Educational script demonstrating cos method cash or nothing concepts. """

@title Cash or Nothing COS Method

Source Code

https://github.com/LechGrzelak/Computational-Finance-Course/blob/main/

Lecture%2008-%20Fourier%20Transformation%20for%20Option%20Pricing/Materials/

CashOrNothing_COS_Method.py

import numpy as np import matplotlib.pyplot as plt import scipy.stats as st import time

======================================================================

def CashOrNothingPriceCOSMthd(cf,CP,S0,r,tau,K,N,L): # cf - characteristic function as a functon, in the book denoted as \varphi # CP - C for call and P for put # S0 - Initial stock price # r - interest rate (constant) # tau - time to maturity # K - list of strikes # N - Number of expansion terms # L - size of truncation domain (typ.:L=8 or L=10)

# reshape K to a column vector
K = np.array(K).reshape([len(K),1])

#assigning i=sqrt(-1)
i = np.complex(0.0,1.0)

x0 = np.log(S0 / K)

# truncation domain
a = 0.0 - L * np.sqrt(tau)
b = 0.0 + L * np.sqrt(tau)

# sumation from k = 0 to k=N-1
k = np.linspace(0,N-1,N).reshape([N,1])
u = k * np.pi / (b - a);

# Determine coefficients for Put Prices
H_k = CashOrNothingCoefficients(CP,a,b,k)#CallPutCoefficients(CP,a,b,k)

mat = np.exp(i * np.outer((x0 - a) , u))

temp = cf(u) * H_k
temp[0] = 0.5 * temp[0]

value = np.exp(-r * tau) *K * np.real(mat.dot(temp))

return value

""" Determine coefficients for CashOrNothing """ def CashOrNothingCoefficients(CP,a,b,k): if str(CP).lower()=="c" or str(CP).lower()=="1": c = 0.0 d = b coef = Chi_Psi(a,b,c,d,k) Psi_k = coef["psi"] if a < b and b < 0.0: H_k = np.zeros([len(k),1]) else: H_k = 2.0 / (b - a) * (Psi_k)

elif str(CP).lower()=="p" or str(CP).lower()=="-1":
    c = a
    d = 0.0
    coef = Chi_Psi(a,b,c,d,k)
    Psi_k = coef["psi"]
    H_k      = 2.0 / (b - a) * (Psi_k)

return H_k

def Chi_Psi(a,b,c,d,k): psi = np.sin(k * np.pi * (d - a) / (b - a)) - np.sin(k * np.pi * (c - a)/(b - a)) psi[1:] = psi[1:] * (b - a) / (k[1:] * np.pi) psi[0] = d - c

chi = 1.0 / (1.0 + np.power((k * np.pi / (b - a)) , 2.0))
expr1 = np.cos(k * np.pi * (d - a)/(b - a)) * np.exp(d)  - np.cos(k * np.pi
              * (c - a) / (b - a)) * np.exp(c)
expr2 = k * np.pi / (b - a) * np.sin(k * np.pi *
                    (d - a) / (b - a))   - k * np.pi / (b - a) * np.sin(k
                    * np.pi * (c - a) / (b - a)) * np.exp(c)
chi = chi * (expr1 + expr2)

value = {"chi":chi,"psi":psi }
return value

def BS_Cash_Or_Nothing_Price(CP,S_0,K,sigma,tau,r): #Black-Scholes Call option price cp = str(CP).lower() K = np.array(K).reshape([len(K),1]) d1 = (np.log(S_0 / K) + (r + 0.5 * np.power(sigma,2.0)) * tau) / float(sigma * np.sqrt(tau)) d2 = d1 - sigma * np.sqrt(tau) if cp == "c" or cp == "1": value = K * np.exp(-r * tau) * st.norm.cdf(d2) elif cp == "p" or cp =="-1": value = K * np.exp(-r * tau) *(1.0 - st.norm.cdf(d2)) return value

def main(): i = np.complex(0.0,1.0)

CP    = "p"
S0    = 100.0
r     = 0.05
tau   = 0.1
sigma = 0.2
K     = [120] #np.linspace(10,S0*2.0,50)#[120.0]#
N     = [40, 60, 80, 100, 120, 140]
L     = 6

# Definition of the characteristic function for the GBM, this is an input
# for the COS method
# Note that Chf does not include coefficient "+iuX(t_0)" this coefficient
# is included internally in the evaluation
# In the book we denote this function as \varphi(u)
cf = lambda u: np.exp((r - 0.5 * np.power(sigma,2.0)) * i * u * tau - 0.5
                      * np.power(sigma, 2.0) * np.power(u, 2.0) * tau)

val_COS_Exact = CashOrNothingPriceCOSMthd(cf,CP,S0,r,tau,K,np.power(2,14),L);
print("Reference value is equal to ={0}".format(val_COS_Exact[0][0]))
# Timing results
NoOfIterations = 1000

for n in N:
    time_start = time.time()
    for k in range(0,NoOfIterations):
        val_COS = CashOrNothingPriceCOSMthd(cf,CP,S0,r,tau,K,n,L)[0]
    print("For N={0} the error is ={1}".format(n,val_COS[0]-val_COS_Exact[0]))
    time_stop = time.time()
    print("It took {0} seconds to price.".format((time_stop-time_start)/float(NoOfIterations)))

if name == "main": main() ```

Exercises

Exercise 1. A cash-or-nothing call pays \(K\) if \(S_T > K\) and zero otherwise. Write its discounted payoff and the Black-Scholes closed-form price.

Solution to Exercise 1

Discounted payoff: \(Ke^{-rT}\mathbf{1}_{S_T > K}\). The BS price is \(V = Ke^{-rT}N(d_2)\) where \(d_2 = \frac{\ln(S_0/K) + (r - \sigma^2/2)T}{\sigma\sqrt{T}}\). This differs from a standard call because there is no \((S_T - K)\) factor---the payoff is a fixed amount \(K\) conditional on finishing ITM.


Exercise 2. In the COS method, the cash-or-nothing coefficients use only \(\Psi_k\) (not \(\text{Chi}_k\)). Explain why the exponential coefficient is absent.

Solution to Exercise 2

The cash-or-nothing payoff is \(K \cdot \mathbf{1}_{x > 0}\) (in log-space), which does not contain the \(e^x\) factor present in vanilla options. The Fourier-cosine expansion of this indicator function involves only \(\Psi_k = \int \cos(k\pi(x-a)/(b-a))dx\) over the ITM region, without the exponential weighting. The Chi coefficients, which capture \(\int e^x \cos(\cdot)dx\), are needed only for payoffs proportional to \(S_T\).


Exercise 3. The cash-or-nothing put pays \(K\) if \(S_T < K\). Using put-call parity for digitals, express its price in terms of the cash-or-nothing call price.

Solution to Exercise 3

Since the events \(\{S_T > K\}\) and \(\{S_T < K\}\) are complementary (ignoring the zero-probability event \(S_T = K\)): \(V_{\text{put}} + V_{\text{call}} = Ke^{-rT}\). Therefore \(V_{\text{put}} = Ke^{-rT} - V_{\text{call}} = Ke^{-rT}(1 - N(d_2)) = Ke^{-rT}N(-d_2)\).


Exercise 4. For \(N = 40\) COS terms, the cash-or-nothing error is larger than for vanilla options with the same \(N\). Explain why discontinuous payoffs require more terms.

Solution to Exercise 4

The cash-or-nothing payoff has a discontinuity at \(S = K\) (jump from 0 to \(K\)). The Fourier cosine series converges slowly for discontinuous functions (Gibbs phenomenon), with error \(O(1/N)\) rather than the exponential convergence seen for smooth payoffs. The vanilla payoff has a kink (continuous but not differentiable), giving \(O(1/N^2)\) convergence. For \(N = 40\), the digital error can be \(\sim 2.5\%\) versus \(< 0.01\%\) for vanilla options.