Finite Difference Methods for Option Pricing › The Crank-Nicolson Scheme
Quantitative FinanceAll chapters Finite Difference Methods for Option Pricing › The Crank-Nicolson SchemeOne section · Sections in this chapter5 / 14 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
The Crank-Nicolson scheme represents a middle ground between explicit and implicit methods. It averages the spatial derivatives at times and , achieving second-order accuracy in time while maintaining unconditional stability. This combination of accuracy and stability makes Crank-Nicolson the method of choice for most practical applications.
The Averaging Approach
Instead of evaluating spatial derivatives entirely at (implicit) or (explicit), Crank-Nicolson uses the average of both evaluations. This averaging can be motivated by the trapezoidal rule for numerical integration: when integrating the time derivative, we approximate the integral of the spatial operator by averaging its values at the beginning and end of the time interval. The scheme uses:
where:
- : values at current time step
- : values at next time step
- : price step size
and similarly for the first derivative. This averaging can be viewed as taking a half-step with the explicit scheme and a half-step with the implicit scheme. The explicit part uses known information from the future time step, while the implicit part ensures stability by coupling values at the current time step.
The Discretized Equation
Substituting the approximations into the Black-Scholes PDE:
where:
- : spatial operator at node , equal to
- : option values at current and next time steps
The spatial operator encapsulates all the terms involving spatial derivatives and the discounting term. Applying this operator at both time levels and averaging creates the characteristic Crank-Nicolson structure. Rearranging terms by time step:
where:
- LHS: implicit terms at current time
- RHS: explicit terms at next time
- : Crank-Nicolson coefficients
The left side involves unknown values at , forming a tridiagonal system just as in the implicit scheme. The right side uses known values at , which we can compute explicitly before solving the system. The coefficients are:
where:
- : time step
- : volatility
- : risk-free rate
- : spatial index
Notice that these coefficients are exactly half of the corresponding implicit scheme coefficients, which reflects the averaging of the spatial operator between two time levels. The left side involves unknown values at , while the right side uses known values at . This again leads to a tridiagonal system at each time step.
Implementation
def crank_nicolson_european_call(S_max, K, T, r, sigma, M, N):
"""
Price a European call option using the Crank-Nicolson finite difference method.
"""
dS = S_max / M
dt = T / N
# Grid of stock prices
S = np.linspace(0, S_max, M + 1)
# Initialize option values at expiration
V = np.maximum(S - K, 0)
# Precompute coefficients for interior points
j = np.arange(1, M)
alpha = 0.25 * dt * (sigma**2 * j**2 - r * j)
beta = 0.5 * dt * (sigma**2 * j**2 + r)
gamma = 0.25 * dt * (sigma**2 * j**2 + r * j)
# Tridiagonal matrix coefficients (left side)
a_left = -alpha[1:] # Sub-diagonal
b_left = 1 + beta # Main diagonal
c_left = -gamma[:-1] # Super-diagonal
# Time stepping
for n in range(N - 1, -1, -1):
# Build right-hand side (explicit part)
d = np.zeros(M - 1)
d = alpha * V[0 : M - 1] + (1 - beta) * V[1:M] + gamma * V[2 : M + 1]
# Boundary condition adjustments
boundary_0 = 0
boundary_M = S_max - K * np.exp(-r * (N - n) * dt)
d[0] += alpha[0] * boundary_0
d[-1] += gamma[-1] * boundary_M
# Solve tridiagonal system
V[1:M] = thomas_algorithm(a_left, b_left, c_left, d)
# Update boundaries
V[0] = boundary_0
V[M] = boundary_M
return S, V# Price with Crank-Nicolson method
S_grid, V_cn = crank_nicolson_european_call(S_max, K, T, r, sigma, M, N=500)
idx = np.argmin(np.abs(S_grid - S_0))
price_cn = V_cn[idx]Crank-Nicolson FD price at S = 100: $10.4407
The Crank-Nicolson price is consistent with the other methods. By averaging the explicit and implicit approaches, it maintains stability while improving accuracy, effectively reducing the bias introduced by time discretization.
Accuracy Analysis
The Crank-Nicolson scheme achieves accuracy in time, compared to for both the explicit and implicit schemes. This means that halving the time step reduces the time discretization error by a factor of four rather than two. The improved accuracy comes from the averaging, which cancels the leading-order error terms in the Taylor expansion. For most applications, Crank-Nicolson provides the best balance of accuracy, stability, and computational efficiency.
