## Crank-Nicolson Finite Difference Method - A MATLAB Implementation

This tutorial presents MATLAB code that implements the Crank-Nicolson finite difference method for option pricing as discussed in the The Crank-Nicolson Finite Difference Method tutorial.

The code may be used to price vanilla European Put or Call options. Note that the primary purpose of the code is to show how to implement the Crank-Nicolson method. The option under consideration could easily be priced using the standard Black-Scholes analytical solution, however knowing the actual option price allows the accuracy of the code here to be verified. The code contains no error checking and is not optimized for speed or memory use. As such it is not suitable for inclusion into a larger application without modifications.

### A Pricing Example

Consider pricing a European Call and Put option with the following parameters, X = \$60, S0 = \$50, r = 5%, σ = 0.2 and T = 1.

The Black-Scholes price for the Call option is \$1.624, and the Put option is \$8.697

A MATLAB function called finDiffCN is given below. The following shows an example of executing finDiffCN (and pricing the above option) in MATLAB,

```>> cPrice = finDiffCN(60,50,0.05,0.2,0:1:100,0:0.01:1,'CALL')
cPrice =
1.6216
>> pPrice = finDiffCN(60,50,0.05,0.2,0:1:100,0:0.01:1,'PUT')
pPrice =
8.6952
```

### MATLAB Function: finDiffCN

```function oPrice = finDiffCN(X,S0,r,sig,Svec,tvec,oType)
% Function to calculate the price of a vanilla European
% Put or Call option using the Crank-Nicolson finite difference method
%
% oPrice = finDiffCN(X,r,sig,Svec,tvec,oType)
%
% Inputs: X - strike
%       : S0 - stock price
%       : r - risk free interest rate
%       : sig - volatility
%       : Svec - Vector of stock prices (i.e. grid points)
%       : tvec - Vector of times (i.e. grid points)
%       : oType - must be 'PUT' or 'CALL'.
%
% Output: oPrice - the option price
%
% Notes: This code focuses on details of the implementation of the
%        Crank-Nicolson finite difference scheme.
%        It does not contain any programatic essentials such as error
%        checking.
%        It does not allow for optional/default input arguments.
%        It is not optimized for memory efficiency, speed or
%        use of sparse matrces.

% Author: Phil Goddard (phil@goddardconsulting.ca)
% Date  : Q4, 2007

% Get the number of grid points
M = length(Svec)-1;
N = length(tvec)-1;
% Get the grid sizes (assuming equi-spaced points)
dt = tvec(2)-tvec(1);

% Calculate the coefficients
% To do this we need a vector of j points
j = 0:M;
sig2 = sig*sig;
aj = (dt/4)*(sig2*(j.^2) - r*j);
bj = -(dt/2)*(sig2*(j.^2) + r);
cj = (dt/4)*(sig2*(j.^2) + r*j);

% Pre-allocate the output
price(1:M+1,1:N+1) = nan;

% Specify the boundary conditions
switch oType
case 'CALL'
% Specify the expiry time boundary condition
price(:,end) = max(Svec-X,0);
% Put in the minimum and maximum price boundary conditions
% assuming that the largest value in the Svec is
% chosen so that the following is true for all time
price(1,:) = 0;
price(end,:) = (Svec(end)-X)*exp(-r*tvec(end:-1:1));
case 'PUT'
% Specify the expiry time boundary condition
price(:,end) = max(X-Svec,0);
% Put in the minimum and maximum price boundary conditions
% assuming that the largest value in the Svec is
% chosen so that the following is true for all time
price(1,:) = (X-Svec(1))*exp(-r*tvec(end:-1:1));
price(end,:) = 0;
end

% Form the tridiagonal matrix
C = -diag(aj(3:M),-1) + diag(1-bj(2:M)) - diag(cj(2:M-1),1);
[L,U] = lu(C);
D = diag(aj(3:M),-1) + diag(1+bj(2:M)) + diag(cj(2:M-1),1);

% Solve at each node
offset = zeros(size(D,2),1);
for idx = N:-1:1
if length(offset)==1
offset = aj(2)*(price(1,idx)+price(1,idx+1)) + ...
cj(end)*(price(end,idx)+price(end,idx+1));
else
offset(1) = aj(2)*(price(1,idx)+price(1,idx+1));
offset(end) = cj(end)*(price(end,idx)+price(end,idx+1));
end
price(2:M,idx) = U\(L\(D*price(2:M,idx+1) + offset));
end

% Calculate the option price
oPrice = interp1(Svec,price(:,1),S0);
```
{"found_math": false, "script_math_tex": 0, "script_math_asciimath": 0, "math_annotations": 0, "math_alttext": 0, "mathml": 0, "mathjax_tag": 0, "mathjax_inline_tex": 0, "mathjax_display_tex": 0, "mathjax_asciimath": 0, "img_math": 0, "codecogs_latex": 0, "wp_latex": 0, "mimetex.cgi": 0, "/images/math/codecogs": 0, "mathtex.cgi": 0, "katex": 0, "math-container": 0, "wp-katex-eq": 0, "align": 0, "equation": 0, "x-ck12": 0, "texerror": 0, "url": "http://goddardconsulting.ca/matlab-finite-diff-crank-nicolson.html", "fetch_time": 1722944430000000000, "content_mime_type": "text/html", "warc_filename": "crawl-data/CC-MAIN-2024-33/segments/1722640484318.27/warc/CC-MAIN-20240806095414-20240806125414-00735.warc.gz", "warc_record_offset": 9868930, "warc_record_length": 3025, "token_count": 1200, "char_count": 4026, "score": 3.515625, "int_score": 4, "crawl": "CC-MAIN-2024-33", "snapshot_type": "latest", "language": "en", "language_score": 0.7405763268470764, "file_path": "/sailhome/yjruan/project/latent-lm/data/processed/finemath-4plus/finemath-4plus/train-00046-of-00064.parquet"}