How to properly solve the Black Scholes PDE numerically

error-propagationnumerical methodspartial differential equationsprogramming

I have a problem numerically solving the following PDE with boundary conditions:
$$
u_t + \frac{x^2\sigma^2}2u_{xx} + rxu_x – ru = 0 \quad (x,t) \in (0,N) \times (0,T)
$$

with
$$
u(x,T) = \max\{0,x-K\}˛ \quad u(0,t) = 0, \quad u(N,t) = N – K.
$$

(This is the Black Scholes PDE to determine the fair price of an European call option.) Originally $x$ should vary over $\mathbb{R}^+$. However to numerically solve the problem, I should choose $N$ sufficiently large. The given parameters are $K = 12, r = 0.04, \sigma = 0.4$ and $T = 2$. I am supposed to use the explicit Euler method.

First, I discretise the underlying space using $m,n \in \mathbb{N}$, I define $x_i = h i$ and $t_j = \tau j$ with $h = \frac Nn, \tau = \frac Tm$ and $i \in \{0,\dots,n\}, j \in \{0,\dots,m\}$. I use the recursion, using $u_i^j := u(x_i,t_j)$,
$$
\frac1\tau(u_i^j – u_i^{j-1}) + \frac{x_i^2\sigma^2}2\frac1{h^2}(u_{i+1}^{j} – 2u_{i}^{j} + u_{i-1}^{j}) + rx_i \frac1{2h}(u_{i+1}^{j} – u_{i-1}^{j}) – r u_{i}^{j-1} = 0.
$$
(Notice that I use $w_i^{j-1}$ as the last term and not $w_i^j$). Rearranging, I get
the recursion
$$
w_i^{j-1} = \frac1{1+r\tau}\left(u_i^j + \frac{x_i^2\sigma^2}2\frac{\tau}{h^2}(u_{i+1}^{j} – 2u_{i}^{j} + u_{i-1}^{j}) + rx_i \frac{\tau}{2h}(u_{i+1}^{j} – u_{i-1}^{j})\right).
$$

Translating my boundary conditions yields
$$
w_0^j = 0, \quad w_n^j = N – K, \quad w_i^m = \max\{0, x_i – K\}.
$$

Now my Problem
When implementing this procedure (using R) (I use e.g. $N = 30$, $m = 5000$ and $n = 200$), I get a really unstable behaviour.

Here you can see 3 pictures, where you can see the (in that order) time steps $t_j$ for $j = 2962, 2959, 2956$
enter image description here

enter image description here

enter image description here

The solution seems to oscillate more and more and I am not exactly sure why. Maybe just due to numerical errors? This behaviour gets worse when I consider even smaller $t_j$, up until a point where it is unrecognisable.

For $n = 100$ the solutions looks fine, with no unstable behaviour at all. I would suspect that I get more errors when $n$ is too small, but here it is the other way around.

Unfortunately I have basically no experience for numerically solving a PDE and am not sure what I can try next and why I see this behaviour.

If you need more information (code, etc.) let me know.

Thanks in advance!

Best Answer

Edit:

Sorry, I just realise that the smooth graph below was obtained with a vol of $$ \sigma=0.2 $$ instead of your $\sigma=0.4\,.$ So it seems that the large vol is the reason for the instability, not the space step/time step configuration which seems totally OK. (end of edit)

I have experimented with a bit of python code myself and also consulted the classic book P. Willmot, S. Howison, J. Dewynne, The Mathematics of Financial Derivatives (1995). The authors write on p. 143 that an explicit scheme (yours) is stable if and only if (in your notation) $$ 0< \alpha:=\frac{\tau}{h^2}=\frac{T\,n^2}{N^2\,m}\le \frac{1}{2}\,. $$ In other words, when you have a stable configuration and double the number of space steps $n$ you have to quadruple the number $m$ of time steps to keep stability.

In your example we have $N=30,T=2,n=200,m=5000$ which gives $$ \alpha=0.01777 $$ which should give a stable result, and indeed I am getting a smooth graph which agrees very well with the closed form BS solution, unless the underlying is far ITM:

enter image description here

  • With $\sigma=0.2$ that I used the probability that the underlying is above $N=30$ in two years when it is at $13$ today is $$ \mathbb P(X_T>N)\approx 0.06\% $$ which is I think small enough. With your $\sigma=0.4$ this would be $2\%$ which I think is too large. Then also, my code fails.

  • I believe that the discrepancy of the graphs near $N=30$ shows that for large vols you have to take a far larger $N$ and ensure again that the stability parameter $\alpha$ is small.

Code:

from numpy import log, exp, sqrt, linspace
from scipy.stats import norm
import matplotlib.pyplot as plt
def option( S, K, T, r, sig, eps ):
    d1 = eps*(log(S/K) + r*T + sig**2*T/2)/sig/sqrt(T)
    d2 = eps*(log(S/K) + r*T - sig**2*T/2)/sig/sqrt(T)
    Nd1 = norm.cdf(d1)
    Nd2 = norm.cdf(d2)
    return( eps*S*Nd1 - eps*exp(-r*T)*K*Nd2 )  
r = 0.04 
K = 13  
sig = 0.2
T = 2
N = 30
n = 200
m = 5000
h = N/n
tau = T/m
alpha = tau/h**2
print( "alpha  {:10.6f}".format( alpha ) )
d2 = (log(K/N) - sig**2*T)/sig/sqrt(T)
print( "P(S>N) {:10.6f}".format( norm.cdf(d2) ) )
X = linspace(0.001,N,n)  
U = linspace(0,N,n)
W = linspace(0,N,n)
for i in range(n):
    U[i] = max( X[i]-K, 0 )
t = T
while( t > tau+1e-6 ):
   t -= tau
   for i in range(1,n-1):
       df = 1/(1+tau*r)
       D2 = (X[i]*sig)**2*tau*(U[i+1] - 2*U[i] + U[i-1])/2/h**2
       W[i] = df*( U[i] + D2 + r*X[i]*tau*(U[i+1]-U[i-1])/2/h )
   for i in range(1,n-1):
       U[i] = W[i]
O = linspace(0,N,n)
for i in range(n):
    O[i] = option( X[i], K, T, r, sig, 1 )
plt.plot( X, U, label="PDE" )
plt.plot( X, O, label="BS" )
plt.legend()
plt.show()