I am trying to price a call option on a zero coupon under the Vasicek Model using Monte Carlo method:
$$C_0 = B(0,\theta) \ \mathbb{E}^{\mathbb{Q}_T}[(B(\theta,T)-K)^{+}]$$
The problem is that the code I have written for this is yielding a price too different from the expected answer which is 0.095 based on the closed form formulas.
What am I possibly doing wrong?
The underlying assumptions and equations I am using:
Let $(Ω, (\mathcal{F}_t)_t, \mathbb{Q})$ such that $\mathbb{Q}$ is risk neutral probability.
Vasicek model: $dr_t = a(b - r_t ) dt + \sigma dW_t^{\mathbb{Q}}$
Price of zero coupon bond: $B(T,S) = \tau(T,S) \ exp(-\eta(T,S)\ r_t)$
Since $r_t$ is gaussian under $\mathbb{Q}_t$ (forward measure): $$r(t) \sim \mathcal{N}(r_0e^{-at} + b(1-e^{-at}) + \frac{\sigma(1 - e^{-at})}{a^2} - \frac{\sigma(1 - e^{-2at})}{2a^2} \ , \ \sigma^{2} \frac{1 - e^{-2at}}{2a}) $$
And my code based on the above: (one can simply copy and paste and then run the code below assuming the requisite external Python libraries are already installed.)
import numpy as np
import pylab as plt
from random import gauss
from math import exp, sqrt, log
from scipy.stats import norm
a = 0.2
b = 0.1
sigma = 0.02
r = 0.1
t = 0
theta = 0.5
T = 1
K = 0.9
#Distribution of rt
r_0 = r
m_rt = r * exp(-a*theta) + b * (1 - exp(-a*theta)) + sigma * ((1 - exp(-a*theta)) / a**2 ) - sigma * ((1 - exp(-2*a*theta))/2*(a**2))
v_rt = (sigma**2) * (1 - exp(2*a*theta))/ 2*a
eta = (1 - exp(-a*(T-theta)))/a
tau = np.exp( (b - (sigma**2)/(2*(a**2))) * (eta - T + theta) - (0.25 * (sigma**2) * (eta**2) / a) )
def VasicekZCPrice(t,T,a,b,sigma,r):
B = ( 1- exp( -a*(T-t) ) )/ a
A = exp( ( (b-(0.5*sigma**2)) / a**2 )*( B - T + t ) - ( 0.25*sigma**2 / a ) * B**2)
ZC = A*exp(-B*r)
return ZC
def monte_carlo_call(theta, T, a, b, sigma, r, K, nbSimul = 10000, steps = 100):
dt = (T-theta)/steps
r__T = r * np.exp(-a*dt) + b* (1-np.exp(-a*dt)) + (sigma*(1-exp(-a*dt))/a**2) - sigma * ((1-np.exp(-2*a*dt))/2*a**2) + sigma*np.sqrt((1-np.exp(-2*a*dt))/2*a) * np.random.normal(size=(steps,nbSimul))
B__T = tau * np.exp(-eta*r__T)
payoffs = np.maximum( B__T - K, 0 )
option_price = VasicekZCPrice(t, T,a,b,sigma,r) * np.mean(payoffs)
return option_price
monte_carlo_call(theta, T, a, b, sigma, r,K , nbSimul = 10000, steps = 100)
which gives me:
0.03686737433679907
An expanded version of the code and further explanations are accessible through this link on Google Colab.
Thanks very much!