IB9JH0 Programming Assignment 1
In this assignment you will write code in the C programming language which uses the CRR binomial
method to value vanilla European/American options for an underlying asset with a fixed annual
dividend yield. You will implement the binomial method using two different algorithms which should
give the same result, allowing you to verify you have implemented them correctly. In addition, you
will verify that your calculated solution approaches the analytical solutions for European options
given by Black-Scholes formalism if the time intervals are sufficiently small.
You may use any standard library data structures/functions in your solution. However, do not use
any third-party libraries which are not part of the standard. Your code should be clearly formatted
and annotated with detailed comments. You have some flexibility in how the code is implemented
and tested, but your solution should contain the file structure and functions specified in this
document so that it can be tested. It is very important that your final submission only contains the
files specified, and these files can be compiled without needing modification. If your code can’t be
compiled in either visual studio or onlineGDB, you are very limited in the number of marks you can
score. Please test it works in at least one of the above. You will be provided with templates of the
files to get you started. Please read the document carefully to make sure your submission is in the
correct format.
Submit the following files:
OptionPricingFunctions.h
OptionPricingFunctions.c
Testing.c
Analysis.c
Black Scholes (European options):
For European options, you should find that using larger trees/more timesteps causes the binomial
algorithm to approach the analytical solution for the value of the option in the risk-free framework
imposed by the Black-Scholes model. The analytical solutions for calls and puts are omitted here as
they are widely known and easy to find. The Black Scholes solution requires one to calculate the
normal cumulative distribution function (). In C this can be done by using the erfc() library
function with the following transform:
Forward Recursion method (European options):
This method starts from timestep zero and accumulates the option value from the next timestep
using the recurrence relation:
is the option value, r is the risk-free interest rate, ▽ is the time increment between each
timestep, p is the risk-neutral probability of an upwards jump, i is the timestep/tree depth of the
current node, and j is height of the current node. This can be visualised in the figure below for a tree
of depth 2:
The recurrence relation can be implemented by utilising recursion in C. Note that you will need to
keep track of i and j in your recursive function calls. The recursion terminates at the base of the
binomial tree where i is equal to the depth of the tree (i = 2 in the above figure). For a tree of depth
n, the values at the base of the tree are the values of the option at the expiry time, and can be
computed as follows:
(2)= max(02 , 0) for call options
(3)= max( 02, 0) for put options
Where 0 is the asset price at time zero (i = 0) and k is the strike price.
Forward Recursion method (American options):
The difference in the calculation for American options in comparison to European options is that
they can be exercised by the contract buyer before the expiry time. This means that the exercise
value
needs to be calculated at each node:
The rest of the algorithm is identical to the version for European options.
Backward Induction method:
The backward induction method starts from the last timestep and computes the option value at all
the base nodes using equation 2 (calls) or 3 (puts). Next, it uses the recurrence relation from
equation 1 (European options) or equation 6 (American options) to compute the value of all the
nodes at the previous timestep. This process is repeated until time zero. You will use a nested loop
with two layers. For a tree of depth n, the outer loop is over timesteps from i = n – 1 down to i = 0.
The inner loop is from node height j = 0 to j = i. You need to use an array to store the values at each
timestep. A single one-dimensional array of size n + 1 is sufficient to price an option with a tree of
depth n. This is because the values calculated at timestep i will replace the values calculated at
timestep i + 1. That is, for all j-values at timestep i,
will replace +1
in the array.
Task 1. Implementing the Algorithms:
The assignment requires you to complete the functions listed below so they work as described. You
are free to add any additional functions you require in OptionPricingFunctions.c. Do NOT modify the
function signatures or names. The input variables must stay the same. Moreover, do NOT modify
the header file OptionPricingFunctions.h.
void price_vanilla_option_european_bs(double S0, double r, double volatility,
double strike_price, double dividend_yield, double expiration_time, double*
call_price, double* put_price)
double price_vanilla_option_european_recursion(unsigned int i, unsigned int j,
unsigned int depth, double delta_t, double S0, double r,
double volatility, double strike_price, double dividend_yield, double
expiration_time, option_fxn exercise_profit_calculator)
double price_vanilla_option_american_recursion(unsigned int i, unsigned int j,
unsigned int depth, double delta_t, double S0, double r,
double volatility, double strike_price, double dividend_yield, double
expiration_time, option_fxn exercise_profit_calculator)
double price_vanilla_option_european_induction(
unsigned int depth, double delta_t, double S0, double r,
double volatility, double strike_price, double dividend_yield,
double expiration_time, option_fxn exercise_profit_calculator)
double price_vanilla_option_american_induction(
unsigned int depth, double delta_t, double S0, double r,
double volatility, double strike_price, double dividend_yield,
double expiration_time, option_fxn exercise_profit_calculator)
The functions should be implemented as follows:
price_vanilla_option_european_bs – calculate call and put prices for European options using
the Black-Scholes analytical solution. The outputs are stored in the call_price and put_price
variables
price_vanilla_option_european_recursion – price a European option using the forward-
recursion method.
price_vanilla_option_american_recursion – price an American option using the forward-
recursion method.
price_vanilla_option_european_induction - price a European option using the backward-
induction method.
price_vanilla_option_american_induction - price an American option using the backward-
induction method.
Task 2. Testing:
Using only the functions exposed in the header file OptionPricingFunctions.h (and any other
standard C library code), demonstrate that the algorithms from task 1 are correctly implemented by
writing a function which tests the outputs under a range of input values (refer to general advice
section for help with this). The tests should print an alert to the console if an error is found. Save all
your testing code in Testing.c and annotate the tests with comments which explain what the code
does.
Task 3. Analysis:
Using only the functions exposed in the header file OptionPricingFunctions.h (and any other
standard C library code), write code to investigate the following questions:
What is the (time) performance difference between the two algorithms (recursion and
induction)
What binomial tree depth is required to be reasonably certain the option values are
accurate to within 5 significant figures?
How can I estimate the accuracy of an American option without an analytical solution?
Save all your analysis code in Analysis.c and annotate the file with comments to explain what the
code does.
General Advice:
When testing/debugging, start with a tree depth of 2 so that you can verify the values in
your code by hand and identify where things are going wrong. Then try with bigger trees.
Start by getting European call options correct for both the forward recursion method and
the backward induction method. Then it is relatively straightforward to extend this to put
options, and finally American call/put options.
A key thing to remember with testing is that you have two different methods (recursion and
induction) to calculate the exact same thing. Given the same inputs, the outputs should be
identical within machine precision. If the two different sources agree exactly for a few cases,
it verifies that you likely implemented the algorithms correctly. The analytical solution is
useful for checking accuracy with respect to the true solution, but less useful for checking
that the code implementation is correct.
Do not take it for granted that your code will work in all four cases, even if it appears to work
in one case. The only way to know for sure that things work is to test them.
It is important to start working on the assignment early, so you have time to ask questions
through the forum or the support classes if you get stuck or need things clarified. You are
encouraged to communicate with others, but do not share actual coded solutions and copy
from each other (it is usually easy to tell if you did so do not risk it).