C++ For Finance
C++ For Finance
5.1
5.2
55
5.2.1
Base class . . . . . . . . . . . . . . . . . .
55
5.2.2
57
5.3
Contents
5.4
Linear Interpolation. . . . . . . . . . . . .
59
5.3.2
61
64
67
6.1
Setup
. . . . . . . . . . . . . . . . . . . . . . . .
67
69
6.2
6.3
. . . . . . . . .
69
1.1
6.4
72
72
6.5
Efficient portfolios . . . . . . . . . . . . . . . . .
1.2.1
Types . . . . . . . . . . . . . . . . . . . .
6.6
73
1.2.2
Operations
. . . . . . . . . . . . . . . . .
6.7
73
1.2.3
6.8
74
1.2.4
. . . . . . . . . .
6.9
Short-sale constraints . . . . . . . . . . . . . . . .
75
1.2.5
Flow control . . . . . . . . . . . . . . . . .
75
1.2.6
Input Output . . . . . . . . . . . . . . . .
76
1.2.7
Splitting up a program . . . . . . . . . . .
6.11.1 Treynor . . . . . . . . . . . . . . . . . . .
76
1.2.8
Namespaces . . . . . . . . . . . . . . . . .
6.11.2 Jensen
. . . . . . . . . . . . . . . . . . .
76
76
77
10
1.4
Const references . . . . . . . . . . . . . . . . . . .
16
1.5
16
Futures algoritms.
7.1
81
. . . . . . . . . . . .
81
Matrix Tools
17
2.1
18
82
2.2
Linear algebra . . . . . . . . . . . . . . . . . . . .
18
8.1
Options . . . . . . . . . . . . . . . . . . . . . . .
82
2.2.1
18
8.2
Pricing . . . . . . . . . . . . . . . . . . . . . . . .
82
2.2.2
19
8.3
85
2.3
22
2.4
24
2.5
Function definitions
. . . . . . . . . . . . . . . .
24
2.6
m files . . . . . . . . . . . . . . . . . . . . . . . .
24
2.7
Flow control . . . . . . . . . . . . . . . . . . . . .
24
2.8
Plotting . . . . . . . . . . . . . . . . . . . . . . .
24
2.9
Libraries . . . . . . . . . . . . . . . . . . . . . . .
25
2.10 References . . . . . . . . . . . . . . . . . . . . . .
25
. . . . . . . . . . .
89
9.1
The formula . . . . . . . . . . . . . . . . . . . . .
90
92
9.2
9.3
9.2.1
93
9.2.2
93
9.2.3
93
. . .
Partial derivatives. . . . . . . . . . . . . . . . . .
93
9.3.1
Delta . . . . . . . . . . . . . . . . . . . . .
93
26
9.3.2
Other Derivatives . . . . . . . . . . . . . .
94
3.1
26
9.3.3
Implied Volatility.
. . . . . . . . . . . . .
96
References . . . . . . . . . . . . . . . . . . . . . .
98
3.2
3.3
3.4
4
58
5.3.1
1.3
. . . . . . . . . . . .
52
Present value . . . . . . . . . . . . . . . . . . . .
One interest rate with annual compounding . . .
27
3.2.1
30
34
3.3.1
35
Present value . . . . . . . . . . . . . . . .
Further readings
. . . . . . . . . . . . . . . . . .
9.4
10 Warrants
35
99
99
100
36
102
37
4.1.1
Bond Price
. . . . . . . . . . . . . . . . .
37
4.1.2
Yield to maturity . . . . . . . . . . . . . .
38
4.1.3
Duration . . . . . . . . . . . . . . . . . . .
41
4.1.4
43
4.2
47
4.3
Further readings
50
. . . . . . . . . . . . . . . . . .
51
177
112
183
. . . . . . . . . . . . . 114
. . . . . . . . . . . . . 114
. . . . . . . . . . . . 120
. . . . 123
191
. . . . . . . . . . 124
186
21 Credit risk
195
. . . . . . . . . . . . . 130
13 Finite Differences
132
197
22.5 Vasicek
. . . . . . . . . . . . . . . . . 137
. . . . . . . . . . . . . . . . . 140
198
. . . . . . . . . . . . . . . . . . . . 202
. . . . . . . . . . . . . . . . . . . . . . . 208
211
142
24 Interest rate trees
214
. . . . . . 147
221
153
227
15.4 An alternative approximation to american options due to Bjerksund and Stensland (1993) . . 162
229
231
166
. . . . . . . . . . . . . . . . . 166
16.4 Monte Carlo Pricing of options whose payoff depend on the whole price path . . . . . . . . . . . 172
. . . . . . . . . . . . . . . . 232
. . . . . . . 235
C++ concepts
237
239
241
. . . . . . . . . . . . . . . . . . . . . . . . 239
E
. . . . . . . . . . . . . . . . . . . . . . . . 239
Installation
E.1 Source availability
251
. . . . . . . . . . . . . . . . . 251
The evaluation of
N3
. . . . . . . . . . . . 239
Acknowledgements.
256
This book is a a discussion of the calculation of specific formulas in finance. The field of finance has seen a
rapid development in recent years, with increasing mathematical sophistication. While the formalization
of the field can be traced back to the work of Markowitz (1952) on investors mean-variance decisions
and Modigliani and Miller (1958) on the capital structure problem, it was the solution for the price of
a call option by Black and Scholes (1973); Merton (1973) which really was the starting point for the
mathematicalization of finance. The fields of derivatives and fixed income have since then been the main
fields where complicated formulas are used. This book is intended to be of use for people who want to
both understand and use these formulas, which explains why most of the algorithms presented later are
derivatives prices.
This project started when I was teaching a course in derivatives at the University of British Columbia, in
the course of which I sat down and wrote code for calculating the formulas I was teaching. I have always
found that implementation helps understanding these things. For teaching such complicated material it
is often useful to actually look at the implementation of how the calculation is done in practice. The
purpose of the book is therefore primarily pedagogical, although I believe all the routines presented are
correct and reasonably efficient, and I know they are also used by people to price real options.
To implement the algorithms in a computer language I choose C++. My students keep asking why anybody
would want to use such a backwoods computer language, they think a spreadsheet can solve all the worlds
problems. I have some experience with alternative systems for computing, and no matter what, in the
end you end up being frustrated with higher end languages, such as Matlab og R (Not to mention the
straitjacket which is is a spreadsheet.) and going back to implementation in a standard language. In my
experience with empirical finance I have come to realize that nothing beats knowledge a real computer
language. This used to be FORTRAN, then C, and now it is C++. All example algorithms are therefore
coded in C++. I do acknowledge that matrix tools like Matlab are very good for rapid prototyping and
compact calculations, and will in addition to C++ in places also illustrate the use of Matlab, as well as
other (public domain) tools.
The manuscript has been sitting on the internet a few of years, during which it has been visited by a
large number of people, to judge by the number of mails I have received about the routines. The present
(2014) version mainly expands on the background discussion of the routines, it is more extensive, (but
it does not replace a real textbook). I have also added some introductory material on how to program
in C++, since a number of questions make it obvious this manuscript is used by a number of people
who know finance but not C++. All the routines have been made to confirm to the new ISO/ANSI C++
standard, using such concepts as namespaces and the standard template library. The latest (2011) C++
standard introduced a few useful simplifications, which is incorporated in places.
The current manscript therefore has various intented audiences. Primarily it is for students of finance
who desires to see a complete discussion and implementation of some formula. But the manuscript is
also useful for students of finance who wants to learn C++, and for computer scientists who want to
understand about the finance algorithms they are asked to implent and embed into their programs.
In doing the implementation I have tried to be as generic as possible in terms of the C++ used, but I
have taken advantage of a some of the possibilities the language provides in terms of abstraction and
modularization. This will also serve as a lesson in why a real computer language is useful. For example
I have encapsulated the term structure of interest rate as an example of the use of classes.
This is not a textbook in the underlying theory, for that there are many good alternatives. For much of
the material the best textbooks to refer to are Hull (2011) and McDonald (2013), which I have used as
references. The notation of the present manuscipt is also similar to these books.
Chapter 1
5
6
1.2.1
1.2.2
Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
6
1.2.3
1.2.4
1.2.5
7
8
1.2.6
1.2.7
Input Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Splitting up a program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8
8
1.2.8 Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3 Extending the language, the class concept. . . . . . . . . . . . . . . . . . . . . . . . . . . .
9
9
10
16
16
In this chapter I introduce C++ and discuss how to run programs written in C++. This is by no means
a complete reference to programming in C++, it is designed to give enough information to understand
the rest of the book. This chapter also only discusses a subset of C++, it concentrates on the parts of
the language used in the remainder of this book. For really learning C++ a textbook is necessary. I have
found Lippman and Lajoie (1998) an excellent introduction to the language.1 The authorative source
on the language is Stroustrup (1997b).
1.2.1 Types
The types we will work with in this book are bool, int, long, double and string.
Here are some example definitions
bool this_is_true=true;
int i = 0;
long j = 123456789;
double pi = 3.141592653589793238462643;
string s("this is a string");
The most important part of C++ comes from the fact that these basic types can be expanded by use of
classes, of which more later.
1.2.2 Operations
To these basic types the common mathematical operations can be applied, such as addition, subtraction,
multiplication and division:
int
int
int
int
i
j
n
m
=
=
=
=
100
100
100
100
+
*
/
50;
50;
2;
2;
These operations are defined for all the common datatypes, with exception of the string type. Such
operations can be defined by the programmer for other datatypes as well.
Increment and decrement In addition to these basic operations there are some additional operations with
their own shorthand. An example we will be using often is incrementing and decrementing a variable.
When we want to increase the value of one item by one, in most languages this is written:
int i=0;
i = i+1;
i = i-1;
In C++ this operation has its own shorthand
int i=0;
i++;
i--;
While this does not seem intuitive, and it is excusable to think that this operation is not really necessary,
it does come in handy for more abstract data constructs. For example, as we will see later, if one defines
a date class with the necessary operations, to get the next date will simply be a matter of
date d(1,1,1995);
d++;
These two statements will result in the date in d being 2jan95.
6
M =4
1
2
3
3
5
Note some pecularities here. When first defining the vector with the statement
vector<double> M(2);
we defined an array of 2 elements of type double, which we then proceeded to fill with the values 1 and
2. When filling the array we addressed each element directly. Note that in the statement
M[0]=1.0;
lies one of the prime traps for programmers coming to C or C++ from another language. Indexing of
arrays starts at zero, not at one. M[0] really means the first element of the array.
The last statement,
7
M.push_back(3);
shows the ability of the programmer of changing the size of the array after it has been defined. push_back
is a standard operation on arrays which pushes the element onto the back of the array, extending the
size of the array by one element. Most programming languages do not allow the programmer to specify
variable-sized arrays on the fly. In FORTRAN or Pascal we would usually have to set a maximum length
for each array, and hope that we would not need to exceed that length. The vector<> template of C++
gets rid of the programmers need for bookkeeping in such array manipulations.
=
=
=
=
=
2
4
8
16
32
1.2.8 Namespaces
To help in building large programs, the concept of a namespace was introduced. Namespaces are a means
of keeping the variables and functions defined local to the context in which they are used. For now it
is necessary to know that any function in the standard C++ library lies in its own namespace, called the
standard namespace. To actually access these library functons it is necessary to explicitly specify that
one wants to access the standard namespace, by the statement
using namespace std;
Instead of such a general approach, one can also specify the namespace on an element by element basis,
but this is more a topic for specialized C++ texts, for the current purposes we will allow all routines access
to the whole standard namespace.
A C++ programmer will proceed to use a class that embodies these uses of the concept of a date. Typically
one will look around for an extant class which has already implemented this, but we will show a trivial
such date class as an example of how one can create a class.
class date {
protected:
int year ;
int month ;
int day ;
public:
date();
date(const int& d, const int& m, const int& y);
bool valid() const;
int day() const;
int month() const;
int year() const;
void set day (const int& day );
void set month (const int& month );
void set year (const int& year );
date
date
date
date
};
bool
bool
bool
bool
bool
bool
Create a date variable: date(const int& d, const int& m, const int& y);
Functions outputting the date by the three integer functions day(), month() and year().
Functions setting the date set_day(int), set_month(int) and set_year(int), which are used
by providing an integer as arguments to the function.
Increment and decrement functions ++ and
Comparison functions <, <=, >, >=, == and !-.
After including this header file, programmers using such a class will then treat an object of type date
just like any other.
For exmple,
date d(1,1,2001);
++d;
would result in the date object d containing the date 2 january 2001.
Any C++ programmer who want to use this date object will only need to look at the header file to know
what are the possible functions one can use with a date object, and be happy about not needing to know
anything about how these functions are implemented. This is the encapsulation part of object oriented
programming, all relevant information about the date object is specified by the header file. This is the
only point of interaction, all details about implementation of the class objects and its functions is not
used in code using this object. In fact, the user of the class can safely ignore the class privates, which
is only good manners, anyway.
Let us look at the implementation of this.
C++ Code 1.2 defines the basic operations, initialization, setting the date, and checking whether a date is
valid.
11
#include "date.h"
date::date(){ year = 0; month = 0; day = 0;};
date::date(const int& day, const int& month, const int& year){
day = day;
month = month;
year = year;
};
int date::day() const { return day ; };
int date::month() const { return month ; };
int date::year() const { return year ; };
void date::set day (const int& day)
{ date::day = day; };
void date::set month(const int& month) { date::month = month; };
void date::set year (const int& year) { date::year = year; };
bool date::valid() const {
// This function will check the given date is valid or not.
// If the date is not valid then it will return the value false.
// Need some more checks on the year, though
return false;
if (year <0)
if (month >12
month <1) return false;
if (day >31
day <1) return false;
if ((day ==31 && ( month ==2
month ==4
month ==6
month ==9
return false;
if ( day ==30 && month ==2) return false;
// should also check for leap years, but for now allow for feb 29 in any year
return true;
};
jj
jj
jj
jj
jj
j j month
12
==11) ) )
For many abstract types it can be possible to define an ordering. For dates there is the natural ordering.
C++ Code 1.3 shows how such comparison operations is defined.
#include "date.h"
bool operator == (const date& d1,const date& d2){ // check for equality
if (! (d1.valid() && (d2.valid())) ) { return false; }; /* if dates not valid, not clear what to do.
alternative: throw exception */
return ((d1.day()==d2.day()) && (d1.month()==d2.month()) && (d1.year()==d2.year()));
};
bool operator < (const date& d1, const date& d2){
if (! (d1.valid() && (d2.valid())) ) { return false; }; // see above remark
if (d1.year()==d2.year()) { // same year
if (d1.month()==d2.month()) { // same month
return (d1.day()<d2.day());
}
else {
return (d1.month()<d2.month());
};
}
else { // different year
return (d1.year()<d2.year());
};
};
// remaining operators defined in terms of the above
bool operator <=(const date& d1, const date& d2){
if (d1==d2) { return true; }
return (d1<d2);
}
bool operator >=(const date& d1, const date& d2) {
if (d1==d2) { return true;};
return (d1>d2);
};
bool operator
bool operator !=(const date& d1, const date& d2){ return !(d1==d2);}
13
C++ Code 1.4 shows operations for finding previous and next date, called an iteration operator.
#include "date.h"
date next date(const date& d){
if (!d.valid()) { return date(); }; //
date ndat=date((d.day()+1),d.month(),d.year()); // first try adding a day
if (ndat.valid()) return ndat;
ndat=date(1,(d.month()+1),d.year()); // then try adding a month
if (ndat.valid()) return ndat;
ndat = date(1,1,(d.year()+1));
// must be next year
return ndat;
}
date previous date(const date& d){
if (!d.valid()) { return date(); }; // return the default date
date pdat = date((d.day() 1),d.month(),d.year()); if (pdat.valid()) return pdat; // try same month
pdat = date(31,(d.month() 1),d.year()); if (pdat.valid()) return pdat; // try previous month
pdat = date(30,(d.month() 1),d.year()); if (pdat.valid()) return pdat;
pdat = date(29,(d.month() 1),d.year()); if (pdat.valid()) return pdat;
pdat = date(28,(d.month() 1),d.year()); if (pdat.valid()) return pdat;
pdat = date(31,12,(d.year() 1));
// try previous year
return pdat;
};
date date::operator ++(int){ // postfix operator
date d = *this;
*this = next date(d);
return d;
}
date date::operator ++(){ // prefix operator
*this = next date(*this);
return *this;
}
date date::operator
(int){ // postfix operator, return current value
date d = *this;
*this = previous date(*this);
return d;
}
date date::operator
(){ // prefix operator, return new value
*this = previous date(*this);
return *this;
};
14
Exercise 1.1.
The function valid() in the date class accepts february 29th in every year, but this should ideally only
happen for leap years. Modify the function to return a false if the year is not a leap year.
Exercise 1.2.
A typical operating system has functions for dealing with dates, which your typical C++ implementation can
call. Find the relevant functions in your implementation, and
1. Implement a function querying the operating system for the current date, and return this date.
2. Implement a function querying the operating system for the weekday of a given date, and return a
representation of the weekday as a member of the set:
{"mon","tue","wed","thu","fri","sat","sun"}
3. Reimplement the valid() function using a system call.
Exercise 1.3.
Once the date class is available, a number of obvious functions begs to be implemented. How would you
1. Add a given number of days to a date?
2. Go to the end or beginning of a month?
3. Find the distance betwen two dates (in days or in years)?
4. Extract a date from a string? (Here one need to make some assumptions about the format)
Exercise 1.4.
Take a look at how dates are dealt with in various computing environments, such as the operating system
(Unix, Windows), applications (Spreadsheets), programming languages, etc. At what level of abstraction is
the interface? Do you need to know how dates are implemented? For those with access to both Matlab and
Windows, why would you say that Matlab has an off-by-one problem relative to Windows?
15
16
Chapter 2
Matrix Tools
Being computer literate entails being aware of a number of computer tools and being able to choose the
most suitable tool for the problem at hand. Way to many people turns this around, and want to fit
any problem to the computer tool they know. The tool that very often is the tool for business school
students is a spreadsheet like Excel. Of course, a spreadsheet is very useful for very many business
applications. However, it is not the best tool for more computationally intensive tasks.
While the bulk of the present book concerns itself with C++, in many applications in finance a very
handy tool is a language for manipulating vectors and matrices using linear algebra. There are a lot
of different possible programs that behaves very similarly, with a syntax taken from the mathematical
formulation of linear algebra. An early tool of this sort was matlab, with a large number of programs
copying much of the syntax of this program. As a result of this there is a proliferation of programs with
similar syntax to Matlab doing similar analysis. General tools include the commercial package Matlab
sold by Mathworks, the public domain programs octave and scilab. Tools that are similar, but more
geared towards econometrics, include Gauss, Ox and S with its public domain clone R. As for what
program to install, there is no right answer. For the basic learning of how these tools work, any of the
mentioned packages will do the job. For students on a limited budget the public domain tools octave,
scilab and R are obvious candidates. All of them perform the basic operations done by the commercial
Matlab package, and good for learning the basics of such a matrix tool.
All of these tools are programs that lets the user manipulate vectors and matrices using very compact
notation. While compact notation is always prone to tense, making programs using it unreadable, this
is not such a large problem in Matlab, the notation tends to be so close how a mathematician would
write them that programs can be relatively easy to follow. There are some pitfalls for the unwary user,
in particular it is easy to miss the difference between a command operating on a whole matrix and
the corresponding element by element operation. For example, consider the following short example,
where the operator means that the matrix A is taken to the power 2 (multiplied with itself), and the
operator . means that each element of the matrix A is taken to the power 2. The two commands give
very different answers.
>> A = [1 1 ; 1 1]
A =
1 1
1 1
>> A^2
ans =
2 2
2 2
>> A.^2
ans =
17
1
1
1
1
A = [1, 2, 3;
4, 5, 6]
This particular command defines a matrix A, the matrix tool will respond to this command by printing
the matrix that was just defined:
A =
1 2 3
4 5 6
x]
2 3
[A;x]
3
6
3
[A y]
number of rows must match
evaluating assignment expression near line 22, column 3
If the dimensioning is wrong, you get an error message, and the variable is not defined.
To see what is in a variable, tell Matlab to print the value by giving the name:
>> a
a = 1
>> A
A =
1 2
4 5
3
6
>> x=[1 2 3 4]
x =
1
2
3
4
>> y=[4 3 2 1]
y =
4
3
2
1
>> x+y
ans =
5
5
5
5
>> y-x
ans =
3
1 -1 -3
>> a*x+b*y
ans =
9
8
7
6
similarly, for matrices:
A=[1 2 3; 4 5 6]
A =
1
2
3
4
5
6
>> B=[6 5 4; 3 2 1]
B =
6
5
4
3
2
1
>> A+B
ans =
7
7
7
7
7
7
>>A-B
ans =
-5 -3 -1
1
3
5
>> a*A+b*B
ans =
13
12
11
10
9
8
> A*B
ans =
28
10
73
28
In linear algebra, you need to be aware that matrix multiplication is not element by element multiplication, it is a much more complex operation, where all possible vector combinations are multiplied with
each other. When multiplying matrices, you need to be more careful about dimensions, but in terms of
notation it is just like vector multiplication.
>> A=[1 2 3;4 5 6]
A =
1 2 3
4 5 6
>> B = [1 2;3 4; 5 6]
20
B =
1 2
3 4
5 6
>> A*B
ans =
22 28
49 64
>> B*A
ans =
9 12
19 26
29 40
15
33
51
For these matrices, both AB and BA are defined operations, but note that the results are different, in
fact, even the dimension of the product is different.
If we let B be a
3
6
The rank of a matrix is is the number of independent rows or columns in the matrix, and calculated as
>> Ahi
A =
1 2 3
4 5 6
>> rank(A)
ans = 2
The inverse of a square matrix A is the matrix inv(A) such that A*inv(A) equals the identity matrix,
or in mathematical notation AA 1 = I.
>> D=[1 2;1 4]
D =
1
2
1
4
>> inv(D)
ans =
2.00000 -1.00000
-0.50000
0.50000
>> D^-1
ans =
2.00000 -1.00000
-0.50000
0.50000
To make sure that this is the inverse, multiply D and inv(D):
>> D * inv(D)
ans =
1 0
0 1
Determinant
>> B
B =
1 2
3 4
>> det(B)
ans = -2
b
22
3x1 + 4x2 = 5
4x1 + 6x2 = 8
Write this in matrix form by defining
A=
b=
3 4
4 6
5
8
>> inverse(A)
ans =
3.0000 -2.0000
-2.0000
1.5000
>> x = inverse(A) * b
x =
-1
2
The solution to the system of equations is
x=
1
2
In this case we calculated the solution by finding the inverse. But you should be aware that solving
the system of equations by calculation of the inverse is not the numerically most stable way of doing
the calculation. Matlab has built in a direct linear matrix solver, which is invoked by the left division
operator
>> x = A\b
x =
-1
2
23
This solves the system of equations directly, and it is usually the preferred way to do this operation,
unless the inverse is needed for other purposes.
2.9 Libraries
2.10 References
You need the manual for your chosen package.
25
Chapter 3
26
27
30
34
35
35
Finance as a field of study is sometimes somewhat flippantly said to deal with the value of two things:
time and risk. While this is not the whole story, there is a deal of truth in it. These are the two issues
which is always present. We start our discussion by ignoring risk and only considering the implications
of the fact that anybody prefers to get something earlier rather than later, or the value of time.
C1
C2
CN
t1
t2
tN
Ct
time
To find the present value of these future cash flows one need a set of prices of future cash flows. Suppose
dt is the price one would pay today for the right to receive one dollar at a future date t. Such a price
is also called a discount factor. To complicate matters further such prices will differ depending on the
riskiness of the future cash flows. For now we concentrate on one particular set of prices, the prices of
riskless future cash flows. We will return to how one would adjust the prices for risky cash flows.
If one knows the set of prices for future claims of one dollar, d1 ; d2 ; : : : ;, one would calculate the present
value as the sum of the present values of the different elements.
PV =
N
X
i=1
dti Cti
26
dt1 Ct1
dt2 Ct2
dtN CtN
Ct 1
Ct 2
Ct N
t1
t2
tN
time
However, knowing this set of current prices for cash flows at all future dates is not always feasible, and
some way has to be found to simplify the data need inherent in such general present value calculations.
dt =
1
(1 + rt )t
where rt is the interest rate (usually termed the spot rate) relevant for a t-period investment. To further
simplify this calculation one can impose that this interest rate r is constant for all periods. This is
termed a flat term structure. We will in the next chapter relax this simplifying assumption. The prices
for valuing the future payments dt is calculated from this interest rate:
dt =
1
;
(1 + r)t
In this case one would calculate the present value of a stream of cash flows paid at discrete dates
t = 1; 2; : : : N as
PV =
N
X
Ct
(1 + r)t
t=1
An investment project has an investment cost of 100 today, and produces cash flows of 75 each of the next
two years. What is the Net Present Value of the project?
Matlab program:
C=[ 100 75 75]
t=[0 1 2]
r = 0.1
d=(1/(1+r)).^t
NPV=C*d
1. Show that the present value of this sequence of cash flows is calculated simply as
PV =
1
X
1+r
t=1
X
r
Exercise 3.2.
A growing perpetuity is again an infinite sequence of cashflows, where the payment the first year is X and
each consequent payment grows by a constant rate g , i.e, the time 2 payment is X (1+ g ), the time 3 payment
is X (1 + g )2 , and so on.
1. Show that the present value of this perpetuity simplifies to
PV =
1
X
X
X (1 + g)t 1
= 1
t
(1
+
r
)
r
g
t=1
28
Exercise 3.3.
An annuity is a sequence of cashflows for a given number of years, say T periods into the future. Consider
an annuity paying a fixed amount X each period. The interest rate is r.
1. Show that the present value of this sequence of cash flows can be simplified as
PV =
T
X
t=1
(1 + r)t
=X
1
r (1 + r)T
Exercise 3.4.
An growing annuity is a sequence of cashflows for a given number of years, say T periods into the future,
where each payment grows by a given factor each year. Consider a T -period annuity that pays X the first
period. After that, the payments grows at a rate of g per year, i.e. the second year the cash flow is X (1 + g ),
the third X (1 + g )2 , and so on.
1. Show that the present value of this growing annuity can be simplified as
"
X (1 + g)(t 1)
1
PV =
=X
t
(1 + r)
r g
t=1
T
X
1+g
1+r
T
r g
Exercise 3.5.
Rank the following cash flows in terms of present value. Use an interest rate of 5%.
1. A perpetuity with an annual payment of $100.
2. A growing perpetuity, where the first payment is $75, and each subsequent payment grows by 2%.
3. A 10-year annuity with an annual payment of $90.
4. A 10 year growing annuity, where the first payment is $85, and each subsequent payment grows by 5%.
29
Ct
(1 + y )t
t=1
C0 ; C1 ; C2 ; : : : CT .
C0 = 0
Note that this is a polynomial equation, and as T becomes large, there in no way to find an explicit
solution to the equation. It therefore needs to be solved numerically. For well behaved cash flows, where
we know that there is one IRR, the method implemented in C++ Code 3.2 is suitable, it is an iterative
process called bisection. It is an adaption of the bracketing approach discussed in (Press, Teukolsky,
Vetterling, and Flannery, 1992, Chapter9),
30
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
#include "fin_recipes.h"
const double ERROR= 1e30;
double cash flow irr discrete(const vector<double>& cflow times,
const vector<double>& cflow amounts) {
// simple minded irr function. Will find one root (if it exists.)
// adapted from routine in Numerical Recipes in C.
if (cflow times.size()!=cflow amounts.size()) return ERROR;
const double ACCURACY = 1.0e 5;
const int MAX ITERATIONS = 50;
double x1 = 0.0;
double x2 = 0.2;
// create an initial bracket, with a root somewhere between bot,top
double f1 = cash flow pv discrete(cflow times, cflow amounts, x1);
double f2 = cash flow pv discrete(cflow times, cflow amounts, x2);
int i;
for (i=0;i<MAX ITERATIONS;i++) {
if ( (f1*f2) < 0.0) { break; }; //
if (fabs(f1)<fabs(f2)) {
f1 = cash flow pv discrete(cflow times,cflow amounts, x1+=1.6*(x1 x2));
}
else {
f2 = cash flow pv discrete(cflow times,cflow amounts, x2+=1.6*(x2 x1));
};
};
if (f2*f1>0.0) { return ERROR; };
double f = cash flow pv discrete(cflow times,cflow amounts, x1);
double rtb;
double dx=0;
if (f <0.0) {
rtb = x1;
dx=x2 x1;
}
else {
rtb = x2;
dx = x1 x2;
};
for (i=0;i<MAX ITERATIONS;i++){
dx *= 0.5;
double x mid = rtb+dx;
double f mid = cash flow pv discrete(cflow times,cflow amounts, x mid);
if (f mid<=0.0) { rtb = x mid; }
if ( (fabs(f mid)<ACCURACY)
(fabs(dx)<ACCURACY) ) return x mid;
};
return ERROR; // error.
jj
};
31
Example
We are considering an investment with the following cash flows at dates 0,
1 and 2:
32
In addition to the above economic qualifications to interpretations of the internal rate of return, we also
have to deal with technical problem stemming from the fact that any polynomial equation has potentially
several solutions, some of which may be imaginary. By imaginary here we mean that we move away from
the real line to the set of complex numbers. In economics we prefer the real solutions, complex interest
rates are not something we have much intuition about... To see whether we are likely to have problems
in identifying a single meaningful IRR, the code shown in code 3.3 implements a simple check. It is only
a necessary condition for a unique IRR, not sufficient, so you may still have a well-defined IRR even
if this returns false. The first test is just to count the number of sign changes in the cash flow. From
Descartes rule we know that the number of real roots is one if there is only one sign change. If there is
more than one change in the sign of cash flows, we can go further and check the aggregated cash flows
for sign changes (See Norstrom (1972)).
#include <cmath>
#include <vector>
using namespace std;
inline int sgn(const double& r){ if (r>=0) {return 1;} else {return
1;}; };
2
pv(r)
0
pv(r)
0
1
5
0
-1
-2
-5
-3
-10
-4
-15
0
0.02
0.04
0.06
0.08
0.1
0.12
-5
-0.2
-0.15
-0.1
-0.05
0.05
0.1
0.15
33
0.2
Exercise 3.6.
An alternative way of estimating the IRR is to use an external subroutine that finds the root of a polynomial
equation. Search for a suitable general subroutine for root finding and replace the IRR estimation with a call
to this subroutine.
dt = e
rt ;
Formula 3.1 summarizes some rules for translating between continously compounded and discretly compounded interest rates.
r = n ln 1 +
rn
n
rn = n e n
Future value
= ert
Present value
=e
rt
Notation: rn : interest rate with discrete compounding, n: compounding periods per year. r: interest rate with
continuous compounding, t: time to maturity.
1.
:15 = 14:91%
r12 = 15%, r = 12 ln 1 + 012
2.
r = 12% , r4 = n e nr
1 =4 e
0:12
4
1 = 12:18%
PV =
n
X
i=1
t1 ; t2 ; : : : ; tn ,
rti C
ti
C++ Code 3.4: Present value calculation with continously compounded interest
In much of what follows we will work with the case of continously compounded interest. There is a
number of reasons why, but a prime reason is actually that it is easier to use continously compounded
interest than discretely compounded, because it is easier to deal with uneven time periods. Discretely
compounded interest is easy to use with evenly spaced cash flows (such as annual cash flows), but harder
otherwise.
35
Chapter 4
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
37
37
38
41
43
47
50
In this section we use the present value framework of the previous chapter to price bonds and other fixed
income securities. What distinguishes bonds is that the future payments are set when the security is
issued. The simplest, and most typical bond, is a fixed interest, constant maturity bond with no default
risk. There is however a large number of alternative contractual features of bonds. The bond could for
example ba an annuity bond, paying a fixed amount each period. For such a bond the principal amount
outstanding is paid gradually during the life of the bond. The interest rate the bond pays need not be
fixed, it could be a floating rate, the interest rate paid could be a function of some market rate. Many
bonds are issued by corporations, and in such cases there is a risk that the company issued the bond
defaults, and the bond does not pay the complete promised amount. Another thing that makes bond
pricing difficult in practice, is that interest rates tend to change over time.
We start by assuming that all the promised payments are certain.
Then the bond current price B0 is found as the present value of these payments. The first step of pricing
is to use the terms of the bond to find the promised payments. We start by considering a fixed interest
bond with no default risk. Such bonds are typically bonds issued by governments. The bond is a promise
to pay a face value F at the maturity date T periods from now. Each period the bond pays a fixed
percentage amount of the face value as coupon C . The cash flows from the bond thus look as follows.
t=
Coupon
Face value
Total cash flows
T
C
F
CT = C + F
C
C1 = C C2 = C
B0 = d1 C1 + d2 C2 + + dT CT =
T
X
t=1
dt C t
where dt is the discount factor, or the time 0 price of a payment of 1 at time t. To fully specify the
36
problem it is necessary to find all discount factors dt . In this chapter we will work with a specially simple
specifiction of the term structure, namely that it is flat, and specified by the interest rate r.
dt =
1
1+r
t
1
(1 + r)t
B0 =
T
X
T
X
1
Ct
C
=
t
t
(1
+
r
)
(1
+
r)t
t=1
t=1
(4.1)
Ct = C
when
t<T
and
Example
A 3 year bond with a face value of $100 makes annual coupon payments of 10%. The current interest rate
(with annual compounding) is 9%.
1. Determine the current bond price.
The current bond price:
= 102.531
37
The general code in C++ for calculating the bond price with discrete annual compounding is shown in
C++ Code 4.1.
#include <cmath>
#include <vector>
using namespace std;
double bonds price discrete(const vector<double>& times,
const vector<double>& cashflows,
const double& r) {
double p=0;
for (int i=0;i<times.size();i++) {
p += cashflows[i]/(pow((1+r),times[i]));
};
return p;
};
C++ Code 4.1: Bond price calculation with discrete, annual compounding.
B0 =
T
X
Ct
(1 + y )t
t=1
(4.2)
This calculation therefore has the same qualifications as discussed earlier calculating IRR, it supposes
reinvestment of coupon at the bond yield (the IRR).
There is much less likelihood well have technical problems with multiple solutions when doing this yield
estimation for bonds, since the structure of cash flows is such that there exist only one solution to the
equation. The algorithm for finding a bonds yield to maturity shown in C++ Code 4.2 is thus simple
bisection. We know that the bond yield is above zero and set zero as a lower bound on the bond yield.
We then find an upper bound on the yield by increasing the interest rate until the bond price with this
interest rate is negative. We then bisect the interval between the upper and lower until we are close
enough. C++ Code 4.2 implements this idea.
38
#include <cmath>
using namespace std;
#include "fin_recipes.h"
double bonds yield to maturity discrete( const vector<double>& times,
const vector<double>& cashflows,
const double& bondprice) {
const double ACCURACY = 1e 5;
const int MAX ITERATIONS = 200;
double bot=0, top=1.0;
while (bonds price discrete(times, cashflows, top) > bondprice) { top = top*2; };
double r = 0.5 * (top+bot);
for (int i=0;i<MAX ITERATIONS;i++){
double diff = bonds price discrete(times, cashflows,r)
bondprice;
if (fabs(diff)<ACCURACY) return r;
if (diff >0.0) { bot=r;}
else
{ top=r; };
r = 0.5 * (top+bot);
};
return r;
};
C++ Code 4.2: Bond yield calculation with discrete, annual compounding
39
Example
A 3 year bond with a face value of $100 makes annual coupon payments of 10%. The current interest rate
(with annual compounding) is 9%.
1. Find the bonds current price.
2. Find the bonds yield to maturity.
Matlab program:
C = [ 10 10 110 ];
t = 1:3;
r=0.09;
B = C * (1./((1+r).^t))
y = irr([ B C ])
40
<< endl;
4.1.3 Duration
When holding a bond one would like to know how sensitive the value of the bond is to changes in
economic environment. The most relevent piece of the economic environment is the current interest
rate. An important component of such calculation is the duration of a bond. The duration of a bond
should be interpreted as the weighted average maturity of the bond, and is calculated as
P
Duration =
Ct
t t (1+r)t
Bond Price
(4.3)
D=
tC
(1+rt)t
Ct
t (1+r)t
t
(4.4)
C++ Code 4.3: Bond duration using discrete, annual compounding and a flat term structure
An alternative approach to calculating duration is calculate the yield to maturity y for the bond, and
use that in estimating the bond price. This is called Macaulay Duration. First one calculates y , the
yield to maturity, from
Bond price =
and then use this
T
X
Ct
(1 + y )t
t=1
Macaulay duration = P
tC
(1+yt)t
Ct
t (1+y)t
t
(4.5)
#include "fin_recipes.h"
double bonds duration macaulay discrete(const vector<double>& times,
const vector<double>& cashflows,
const double& bond price) {
double y = bonds yield to maturity discrete(times, cashflows, bond price);
return bonds duration discrete(times, cashflows, y); // use YTM in duration calculation
};
Matlab program:
C =[10,10,110];
t = 1:3;
r = 0.09;
B= C * (1./(1+r).^t)
D= (1/B)*t.* C * (1./(1+r).^t)
y = irr([ B C ])
DM= (1/B)*t.* C * (1./(1+y).^t)
= 102.531
= 2.73895
= 2.73895
42
B0
B0
1+r
r
where D is the bonds duration. For simplicity one often calculates the term in front of the
D directly and terms it the bonds modified duration.
above, 1+
y
Modified Duration = D
y in the
1+r
B0
B0
D r
The modified duration is also written in terms of the bonds yield to maturity y , and is then
D =
1+y
bond price
@
@
@
@
@
@
@
@
@
@
43
Duration measures
angle of tangent.
@ yield
@ -
The modified duration measures the angle of the tangent at the current bond yield. Approximating
the change in bond price with duration is thus only a first order approximation. To improve on this
approximation we also need to account for the curvature in the relationship between bond price and
interest rate. To quantify this curvature we calculate the convexity of a bond.
Convexity = Cx =
T
1 X
Ct
(t + t2 )
B0 (1 + r)2 t=1
(1 + r)t
(4.6)
#include <cmath>
#include "fin_recipes.h"
double bonds convexity discrete(const vector<double>& times,
const vector<double>& cashflows,
const double& r) {
double Cx=0;
for (int i=0;i<times.size();i++){
Cx+= cashflows[i]*times[i]*(times[i]+1)/(pow((1+r),times[i]));
};
double B=bonds price discrete(times, cashflows, r);
return (Cx/(pow(1+r,2)))/B;
};
C++ Code 4.6: Bond convexity with a flat term structure and annual compounding
change when the interest rates changes you will then calculate
B0
D y + 12 Cx (y)2
B0
B0 =
(B0 )
T
X
Duration
Ct
D =
(1 + r)t
t=1
Yield to maturity
B0 =
Modified duration
y solves
Convexity
T
X
Ct
(1 + y )t
t=1
D=
T
X
tCt
B0 t=1 (1 + r)t
1
T
X
=1 2 3
B0
D y
B0
D y + 21 Cx (y)2
B0
tCt
B0 t=1 (1 + y)t
1
T
Ct
1 X
(t + t2 )
B0 (1 + r)2 t=1
(1 + r)t
Macaulay duration
D=
(Cx)
Cx =
(D )
1+y
y:
B0 :
B0
Formula 4.1: Bond pricing formulas with a flat term structure and discrete, annual compounding
44
Example
A 3 year bond with a face value of $100 makes annual coupon payments of 10%. The current interest rate
(with annual compounding) is 9%.
1. Determine the current bond price.
2. Suppose the interest rate changes to 10%, determine the new price of the bond by direct calculation.
3. Use duration to estimate the new price and compare it to the correct price.
4. Use convexity to improve on the estimate using duration only.
Need to calculate the following:
110 3 = 102:531.
The current bond price: B0 = (1+010:09)1 + (1+010:09)2 + (1+0
:09)
1
1
10
2
10
3
110
The bonds duration: D = 102:531 1:09 + 1:092 + 1:093 = 2:74
D = 2:74 = 2:51.
The modified duration: D = 1+
r
1:09
The convexity:
10 + (22 +2)10 + (3+32 )110 = 8:93.
Cx = (1+01:09)2 1021:531 (1+1)
2
3
1:09
1:09
1:09
45
<< endl;
Using these numbers to answer the questions, let us see what happens when the interest rate increases to 10%.
This means the bond will be selling at par, equal to 100, which can be confirmed with direct computation:
10
10
110
+
+
= 100
B0 =
(1 + 0:1)1 (1 + 0:1)2 (1 + 0:1)3
Using duration to estimate the change in the bond price for a unit change in the interest rate:
B0
B0
Using this duration based number to estimate the new bond price.
B0
B^ = B0 +
B0 = 102:531 0:0251 102:531 = 99:957
B0
B0
B0
1
1
= D y + Cx y 2 = 2:51 0:01 + 8:93(0:01)2 = 0:0251 + 0:00044 = 0:02465
2
2
B0
B^ = 102:531 1 +
B0 = 102:531(1 0:02465) = 100:0036
B0
Exercise 4.1.
Perpetual duration [4]
The term structure is flat with annual compounding. Consider the pricing of a perpetual bond. Let
per period cash flow
B0 =
1
X
(1 + r)t
t=1
C be the
C
r
1. Determine the first derivative of the price with respect to the interest rate.
2. Find the duration of the bond.
Exercise 4.2.
[5]
Consider an equally weighted portfolio of two bonds, A and B. Bond A is a zero coupon bond with 1 year to
maturity. Bond B is a zero coupon bond with 3 years to maturity. Both bonds have face values of 100. The
current interest rate is 5%.
1. Determine the bond prices.
2. Your portfolio is currently worth 2000. Find the number of each bond invested.
3. Determine the duration of the portfolio.
4. Determine the convexity of your position.
46
B0 :
B0 = e
Bond Price
X
i
Convexity
rti ) C
ti
Cx =
y solves:
X
B0 = Cti e yti
Yield to maturity
Duration
D:
D=
B0
X
i
ti Cti e
Cx:
1
B0
X
i
Cti t2i e
rti
rti
B0
D y
yti
B0
Dy + 21 Cx (y)2
B0
Macaulay duration
D=
B0
X
i
ti Cti e
Notation:
B0 :
B0
e:
natural exponent.
Formula 4.2: Bond pricing formulas with continously compounded interest and a flat term structure
Some important differences is worth pointing out. When using continously compounded interest, one
does not need the concept of modified duration. In the continously compounded case one uses the
calculated duration directly to approximate bond changes, as seen in the formulas describing the approximation of bond price changes. Note also the difference in the convexity calculation, one does not
divide by (1 + y )2 in the continously compounded formula, as was done in the discrete case.
C++ Code 4.7, C++ Code 4.8, C++ Code 4.9 and C++ Code 4.10 show continously compounded analogs of the
earlier codes for the discretely compounded case.
#include <cmath>
#include <vector>
using namespace std;
double bonds price(const vector<double>& cashflow times,
const vector<double>& cashflows,
const double& r) {
double p=0;
for (int i=0;i<cashflow times.size();i++) {
p += exp( r*cashflow times[i])*cashflows[i];
};
return p;
};
C++ Code 4.7: Bond price calculation with continously compounded interest and a flat term structure
47
#include <cmath>
#include <vector>
using namespace std;
double bonds duration(const vector<double>& cashflow times,
const vector<double>& cashflows,
const double& r) {
double S=0;
double D1=0;
for (int i=0;i<cashflow times.size();i++){
S +=
cashflows[i] * exp( r*cashflow times[i]);
D1 += cashflow times[i] * cashflows[i] * exp( r*cashflow times[i]);
};
return D1 / S;
};
C++ Code 4.8: Bond duration calculation with continously compounded interest and a flat term structure
#include "fin_recipes.h"
double bonds duration macaulay(const vector<double>& cashflow times,
const vector<double>& cashflows,
const double& bond price) {
double y = bonds yield to maturity(cashflow times, cashflows, bond price);
return bonds duration(cashflow times, cashflows, y); // use YTM in duration
};
C++ Code 4.9: Calculating the Macaulay duration of a bond with continously compounded interest and a
flat term structure
#include <cmath>
#include "fin_recipes.h"
double bonds convexity(const vector<double>& times,
const vector<double>& cashflows,
const double& r ) {
double C=0;
for (int i=0;i<times.size();i++){
C += cashflows[i] * pow(times[i],2) * exp( r*times[i]);
};
double B=bonds price(times, cashflows,r);
return C/B;
};
C++ Code 4.10: Bond convexity calculation with continously compounded interest and a flat term structure
48
Example
A 3 year bond with a face value of $100 makes annual coupon payments of 10%. The current interest rate
(with continous compounding) is 9%.
1. Calculate the bonds price, yield to maturity, duration and convexity.
2. Suppose the interest rate falls to 8%. Estimate the new bond price using duration, and compare with
the actual bond price using the correct interest rate.
Calculations:
Matlab program:
C =[10,10,110]
t = 1:3
r = 0.09
d=(1./(1+r).^t)
B= C * d
D=1/B*C.*t*d
Dadj=D/(1+r)
r=0.1
actualB = C*(1./(1+r).^t)
= 101.464
= 2.73753
=7.86779
= 104.282
Exercise 4.3.
49
The term structure is flat, and compounding is continous. Consider two definitions of duration, the usual
P
P
definition D = B10 i ti Cti e rti and the Macaulay defintion: D = B10 i ti Cti e yti , where B0 is the current
bond price, Cti is the coupon payment at date ti , r is the current interest rate and y is the bonds yield to
maturity.
1. Show that these two definitions will produce the same number if the bond is correctly priced.
50
Chapter 5
52
55
55
57
58
59
61
64
In this chapter we expand on the analysis of the previous chapter by relaxing the one interest rate
assumption used there and allow the spot rates to change as you change the time you are discounting
over.
Recall that we said that the present value of a set of cash flows is calculated as
PV =
N
X
i=1
dti Cti
dt1 Ct1
dt2 Ct2
dtN CtN
Ct 1
Ct 2
Ct N
t1
t2
tN
time
To make this applicable to cash flows received at any future date t we potentially need an infinite
number of discount factors dt . This is not feasible, so some lower dimensional way needs to be found to
approximate dt , but with more flexibility than the extremely strong assumption that there is one fixed
interest rate r, and that the discount factor for any time t is calculated as either dt = 1=(1 + r)t (discrete
compounding), or dt = e rt (continuous compounding), which we used in the previous chapter.
51
In this chapter we first show that this approximation of the discount factors can be done in either terms
of discount factors directly, interest rates, or forward rates. Either of these are useful ways of formulating
a term structure, and either of them can be used, since there are one to one transformations between
either of these three. We then go on to demonstrate how a feature of C++, the ability to create an abstract
datatype as an object, or class, is very useful for the particular application of defining and using a term
structure. It is in fact this particular application, to create a term structure class, which really illustrates
the power of C++, and why you want to use an object oriented language instead of classical langues like
FORTRAN and C, or matrix languages like Gauss or Matlab for many financial calculations.
5.1 The interchangeability of discount factors, spot interest rates and forward
interest rates
The term structure can be specified in terms of either discount factors, spot interest rates or forward
interest rates. A discount factor is the current price for a future (time t) payment of one dollar. To
find the current value P V of a cash flow Ct , we calculate P V = dt Ct . This discount factor can also be
specified in terms of interest rates, where we let rt be the relevant interest rate (spot rate) for discounting
a t-period cashflow. Then we know that the present value P V = e rt t Ct . Since these two methods of
calculating the present value must be consistent,
P V = dt C t = e
rt t C
and hence
dt = e
rt t
Note that this equation calculates dt given rt . Rearranging this equation we find the spot rate rt in
terms of discount factors
rt =
ln(dt )
An alternative concept that is very useful is a forward interest rate, the yield on borrowing at some
future date t1 and repaying it at a later date t2 . Let ft1 ;t2 be this interest rate. If we invest one dollar
today, at the current spot rate spot rate till period t1 and the forward rate for the period from t1 to t2
(which is what you would have to do to make an actual investment), you would get the following future
value
t1 )
dt2 F V = 1
These considerations are enough to calculate the relevant transforms. The forward rate for borrowing at
time t1 for delivery at time t2 is calculated as
ft1 ;t2 =
ln
dt2
d t1
t2 t1
ln
d t1
d t2
t2 t1
t2
t2 t1
rt1
t1
t2 t1
52
dt = e
rt =
ft1 ;t2 =
rt t
ln(dt )
ln
d t1
d t2
t2 t1
t
ft1 ;t2 = rt2 2
t2 t1
rt1
t1
t2 t1
Notation: dt discount factor for payment at time t, rt : spot rate applying to cash flows at time t.
time t1 and t2 , i.e. the interest rate you would agree on today on the future transactions.
ft1 ;t2
#include <cmath>
using namespace std;
double term structure yield from discount factor(const double& d t, const double& t) {
return ( log(d t)/t);
}
double term structure discount factor from yield(const double& r, const double& t) {
return exp( r*t);
};
double term structure forward rate from discount factors(const double& d t1, const double& d t2,
const double& time) {
return (log (d t1/d t2))/time;
};
double term structure forward rate from yields(const double& r t1, const double& r t2,
const double& t1, const double& t2) {
return r t2*t2/(t2 t1) r t1*t1/(t2 t1);
};
53
d2 = 0:9.
Calculate the
C++ program:
double t1=1; double r t1=0.05; double d t1=term structure discount factor from yield(r t1,t1);
cout << " a " << t1 << " period spot rate of " << r t1
<< " corresponds to a discount factor of " << d t1 << endl;
double t2=2; double d t2 = 0.9;
double r t2 = term structure yield from discount factor(d t2,t2);
cout << " a " << t2 << " period discount factor of " << d t2
<< " corresponds to a spot rate of " << r t2 << endl;
cout << " the forward rate between " << t1 << " and " << t2
<< " is " << term structure forward rate from discount factors(d t1,d t2,t2 t1)
<< " using discount factors " << endl;
cout << " and is " << term structure forward rate from yields(r t1,r t2,t1,t2)
<< " using yields " << endl;
54
discount factors
spot rates
forward rates
for any future maturity t. The user of a term structure will not need to know how the term structure is
implemented, all that is needed is an interface that specifies the above three functions.
This is tailor made for being implemented as a C++ class. A class in C++ terms is a collection of data
structures and functions that operate on these data structures. In the present context it is a way of
specifying the three functions.
r(t)
d(t)
f(t)
Header file 5.1: Header file describing the term_structure base class
The code for these functions uses algorithms that are described earlier in this chapter for transforming
between various views of the term structure. The term structure class merely provide a convenient
interface to these algorithms. The code is shown in C++ Code 5.2
Note that the definitions of calculations are circular. Any given specific type of term structure has to
over-ride at least one of the functions r (yield), d (discount factor) or f (forward rate).
We next consider two examples of specific term structures.
55
#include "fin_recipes.h"
term structure class::term structure class(){};
double term structure class::f(const double& t1, const double& t2) const{
double d1 = d(t1);
double d2 = d(t2);
return term structure forward rate from discount factors(d1,d2,t2 t1);
};
double term structure class::r(const double& t) const{
return term structure yield from discount factor(d(t),t);
};
double term structure class::d(const double& t) const {
return term structure discount factor from yield(r(t),t);
};
C++ Code 5.2: Default code for transformations between discount factors, spot rates and forward rates in
a term structure class
56
Header file 5.2: Header file for term structure class using a flat term structure
#include "fin_recipes.h"
term structure class flat::term structure class flat(const double& r){ R = r; };
term structure class flat::term structure class flat(){};
double term structure class flat::r(const double& T) const { if (T>=0) return R ; return 0; };
void term structure class flat::set int rate(const double& r) { R = r; };
C++ Code 5.3: Implementing term structure class using a flat term structure
Example
The term structure is flat with
rate between 1 and 2.
r = 5%.
Determine the discount factors for years 1 and 2 and the forward
C++ program:
term structure class flat ts(0.05);
double t1=1;
cout << "discount factor t1 = " << t1 << ":" << ts.d(t1) << endl;
double t2=2;
cout << "discount factor t2 = " << t2 << ":" << ts.d(t2) << endl;
cout << "spot rate t = " << t1 << ":" << ts.r(t1) << endl;
cout << "spot rate t = " << t2 << ":" << ts.r(t2) << endl;
cout << "forward rate from t1= " << t1 << " to t2= " << t2 << ":"
<< ts.f(t1,t2) << endl;
B1
B2
B3
=4
d1
d2
d3
32
54
3
5
d1
d2
d3
=4
12
B1
B2
B3
3
5
or
d=C
1B
B=4
B1
B2
B3
d=4
d1
d2
d3
C=4
3
5
Example
The following set of bond and bond prices is observed:
Bond
1
2
2
3
4
5
Bond
Price
98
96
92
118
109
112
110
8
9
108
9
109
100
100
10
8
9
10
8
9
100
10
8
9
The bonds are treasury secrurities, which can be viewed as nominally riskless.
1. For what maturities is it possible to infer discount factors?
2. Determine the implicit discount factors in the market.
3. What is the interest rates implied in this set of discount rates?
Here we can find the discount factors for maturities of 0.5,1,1.5,2,3 and 4.
58
Matlab program:
C=[100 0 0 0 0 0;0 100 0 0 0 0;0 0 100 0 0 0;10 10 10 110 0 0;8 8 8 8 108 0;9 9 9 9 9 109]
B=[96 94 92 118 109 112]
d=B*inv(C)
t=[0.5 1 1.5 2 3 4]
r=d.^( 1./t) 1
r
0.1
0.2
0.3
0.4
0.5
Interpolate spot rates (zero rates) at times 0.1, 0.5, 1, 3, 5 and 10.
59
#include <vector>
using namespace std;
#include "fin_recipes.h"
double term structure yield linearly interpolated(const double& time,
const vector<double>& obs times,
const vector<double>& obs yields) {
// assume the yields are in increasing time to maturity order.
int no obs = int(obs times.size());
if (no obs<1) return 0;
double t min = obs times[0];
if (time <= t min) return obs yields[0]; // earlier than lowest obs.
double t max = obs times[no obs 1];
if (time >= t max) return obs yields[no obs 1]; // later than latest obs
int t=1; // find which two observations we are between
while ( (t<no obs) && (time>obs times[t])) { ++t; };
double lambda = (obs times[t] time)/(obs times[t] obs times[t 1]);
// by ordering assumption, time is between t-1,t
double r = obs yields[t 1] * lambda + obs yields[t] * (1.0 lambda);
return r;
};
60
Header file 5.3: Header file describing a term structure class using linear interpolation between spot rates
61
#include "fin_recipes.h"
void term structure class interpolated::clear(){
times .erase(times .begin(), times .end());
yields .erase(yields .begin(), yields .end());
};
term structure class interpolated::term structure class interpolated():term structure class(){clear();};
term structure class interpolated::term structure class interpolated(const vector<double>& in times,
const vector<double>& in yields) {
clear();
if (in times.size()!=in yields.size()) return;
times = vector<double>(in times.size());
yields = vector<double>(in yields.size());
for (int i=0;i<in times.size();i++) {
times [i]=in times[i];
yields [i]=in yields[i];
};
};
term structure class interpolated::term structure class interpolated(){ clear();};
term structure class interpolated::term structure class interpolated(const term structure class interpolated& term) {
times
= vector<double> (term.no observations());
yields
= vector<double> (term.no observations());
for (int i=0;i<term.no observations();i++){
times [i]
= term.times [i];
yields [i] = term.yields [i];
};
};
term structure class interpolated
term structure class interpolated::operator= (const term structure class interpolated& term) {
times
= vector<double> (term.no observations());
yields
= vector<double> (term.no observations());
for (int i=0;i<term.no observations();i++){
times [i]
= term.times [i];
yields [i] = term.yields [i];
};
return (*this);
};
double term structure class interpolated::r(const double& T) const {
return term structure yield linearly interpolated(T, times , yields );
};
void
term structure class interpolated::set interpolated observations(vector<double>& in times,
vector<double>& in yields) {
clear();
if (in times.size()!=in yields.size()) return;
times = vector<double>(in times.size());
yields = vector<double>(in yields.size());
for (int i=0;i<in times.size();i++) {
times [i]=in times[i];
yields [i]=in yields[i];
};
};
C++ Code 5.5: Term structure class using linear interpolation between spot rates
62
Example
Time
0.1
1
5
r
0.05
0.07
0.08
Determine discount factors and spot rates at times 1 and 2, and forward rate between 1 and 2.
C++ program:
vector<double> times; times.push back(0.1);
vector<double> spotrates; spotrates.push back(0.05);
times.push back(5);
times.push back(1);
spotrates.push back(0.07);spotrates.push back(0.08);
term structure class interpolated ts(times,spotrates);
double t1=1;
cout << "discount factor t1 = " << t1 << ":" << ts.d(t1) << endl;
double t2=2;
cout << "discount factor t2 = " << t2 << ":" << ts.d(t2) << endl;
cout << "spot rate t = " << t1 << ":" << ts.r(t1) << endl;
cout << "spot rate t = " << t2 << ":" << ts.r(t2) << endl;
cout << "forward rate from t1= " << t1 << " to t2= " << t2 << ":"
<< ts.f(t1,t2) << endl;
63
5.4 Bond calculations with a general term structure and continous compounding
Coupon bond paying coupons at dates t1 ; t2 ; : : ::
Bond Price
B0 =
Duration
B0 :
X
i
D:
D=
D=
D=
dti Cti =
B0
B0
B0
X
i
Yield to maturity
B0 =
rti ti C
ti
Convexity
y solves:
Cti e
yti
Cx:
Cx =
ti dti Cti
ti e
rti ti C
ti
Cx =
ti e
yti C
ti
Cx =
B0
B0
B0
rti ti C
t2i e
yti C
ti
ti
64
#include "fin_recipes.h"
double bonds duration(const vector<double>& cashflow times,
const vector<double>& cashflow amounts,
const term structure class& d ) {
double S=0;
double D1=0;
for (unsigned i=0;i<cashflow times.size();i++){
S += cashflow amounts[i] * d.d(cashflow times[i]);
D1 += cashflow times[i] * cashflow amounts[i] * d.d(cashflow times[i]);
};
return D1/S;
};
C++ Code 5.7: Calculating a bonds duration with a term structure class
#include "fin_recipes.h"
#include <cmath>
double bonds convexity(const vector<double>& cashflow times,
const vector<double>& cashflow amounts,
const term structure class& d ) {
double B=0;
double Cx=0;
for (unsigned i=0;i<cashflow times.size();i++){
B += cashflow amounts[i] * d.d(cashflow times[i]);
Cx += pow(cashflow times[i],2) * cashflow amounts[i] * d.d(cashflow times[i]);
};
return Cx/B;
};
C++ Code 5.8: Calculating a bonds convexity with a term structure class
65
Example
The term structure is flat with r
convexity of a 10%, 2 year bond.
C++ program:
vector <double> times; times.push back(1);
times.push back(2);
vector <double> cashflows; cashflows.push back(10); cashflows.push back(110);
term structure class flat tsflat(0.1);
cout << " price = " << bonds price (times, cashflows, tsflat) << endl;
cout << " duration = " << bonds duration(times, cashflows, tsflat) << endl;
cout << " convexity = " << bonds convexity(times, cashflows, tsflat) << endl;
66
Chapter 6
67
69
69
72
72
73
73
74
75
75
76
6.11.1 Treynor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.11.2 Jensen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.12 Working with Mean Variance and CAPM . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.13 Mean variance analysis using matrix libraries . . . . . . . . . . . . . . . . . . . . . . . . . . .
76
76
76
77
We now discuss a classical topic in finance, mean variance analysis. This leads to ways of accounting for
the riskiness of cashflows.
Mean variance analysis concerns investors choices between portfolios of risky assets, and how an investor
chooses portfolio weights. Let rp be a portfolio return. We assume that investors preferences over
portfolios p satisfy a mean variance utility representation, u(p) = u(E [rp ]; (rp )), with utility increasing
in expected return (@u=@E [rp ] > 0) and decreasing in variance (@u=@ var(rp ) < 0). In this part we
consider the representation of the portfolio opportunity set of such decision makers. There are a number
of useful properties of this opportunity set which follows purely from the mathematical formulation of
the optimization problem. It is these properties we focus on here.
6.1 Setup
We assume there exists
2
6
6
4
e=6
E [r1 ]
E [r2 ]
..
.
3
7
7
7
5
E [rn ]
67
V=6
(r1 ; r1 ) (r1 ; r2 ) : : :
(r2 ; r1 ) (r2 ; r2 ) : : :
..
.
(rn ; r1 )
7
7
7
5
:::
(rn ; rn )
3
7
7
w = 6 . 7;
4 .. 5
!n
where wi is the fraction of the investors wealth invested in asset i. Note that the weights sum to one.
The expected return on a portfolio is calculated as
E [rp ] = w0 e
and the variance of the portfolio is
2 (rp ) = w0 Vw
Example
An investor can invest in three assets with expected returns and variances as specified in the following table.
Asset
1
2
3
E [r]
2 (r)
10%
11.5%
8%
0.20
0.10
0.15
68
1
= arg min w0 Vw
2
w
wp
subject to:
w0 e = E [~
rp ]
w0 1 = 1
The set of all frontier portfolios is called the minimum variance frontier.
= g + hE [rp ]
wp
where
g=
h=
(B 10
Ae0 ) V 1
(C e0
A10 ) V 1
D
D
A = 10 V 1 e
B = e0 V 1 e
C = 10 V 1 1
B A
A=
A C
D = BC A2 = jAj
Proof
Any minimum variance portfolio solves the program
wp
= arg min
w
1
2
w0 Vw
subject to
w0 e = E [~
rp ]
w0 1 = 1
Set up the Lagrangian corresponding to this problem
L(w; ;
je; V) = w0 Vw
1
2
E [~
rp ]
w0 e
(1
69
w0 1)
Differentiate
@L
@w
w0 V
e0
10
@L
@
E [rp ]
w0 e = 0
@L
@
=1
=0
w0 1 = 0
e 0 V
10 V
(6.1)
w0 e = E [~
rp ]
(6.2)
w0 1 = 1
(6.3)
Post-multiply equation (6.1) with e and recognise the expression for E [rp ] in the second equation
w0 e = E [rp ] = e0 V
e + 10 V
Similarly post-multiply equation (6.1) with 1 and recognise the expression for
w 0 1 = 1 = e 0 V
1 + 10 V
With the definitions of A, B , C and D above, this becomes the following system of equations
E [rp ]
1
=
=
B +
A
A +
C
=
AE [rp ]
D
CE [rp ]
D
Plug in expressions for and
into equation (6.1) above, and get
w0
B 10
Ae0 V
A10 V
C e0
E [rp ] = g + hE [rp ]
The portfolio defined by weights g is a portfolio with expected return 0. The portfolio defined by weights
(g + h) is a portfolio with expected return 1. This implies the useful property that g10 = 1, and h10 = 0.
Example
An investor can invest in three assets with expected returns and variances as specified in the following table.
Asset
1
2
3
E [r]
2 (r)
10%
11.5%
8%
0.20
0.10
0.15
70
Matlab program:
e=[0.1 0.11 0.08]
V=[ 0.2 0 0; 0 0.1 0 ; 0 0 0.15]
r=0.09
n = length(e)
a = ones(1,n)*inv(V)*e
b = e*inv(V)*e
c = ones(1,n)*inv(V)*ones(n,1)
A = [b a;a c]
d = det(A)
g = 1/d*(b*ones(1,n)
a*e)*inv(V)
h = h = 1/d*(c*e - a*ones(1,n))*inv(V)
w=g+h*r
Matlab Code 6.1: Calculation of minimum variance portfolio for given return
71
Calculating the weights of the minimum variance portfolio given an interest rate
wp
= g + hE [rp ]
where
g=
h=
(B 10
Ae0 ) V 1
(C e0
A10 ) V 1
D
D
A = 10 V 1 e
B = e0 V 1 e
C = 10 V 1 1
D = BC A2 =
Notation:
rp
desired portfolio returns.V: covariance matrix of asset returns. e: vector of expected asset returns.
0
wmvp
1
= 10 V 1 1 1 10 V 1 = 10 V 1 ;
expected return
E [rmvp ] =
A
C and variance var(rmvp )
= C1 .
E [r]
6
A
C
(r )
E [r]
6
E [rmvp ]
(r )
(rmvp )
72
E [rmvp ] are
cov(rzc(p) ; rp ) = 0:
This portfolio is called the zero beta portfolio relative to p. The zero beta portfolio
E [rzc(p) ] =
A
C
D
C2
E [rp ]
A
C
s mvp
E [rzc(p) ]
zc(p)
-
zc(p) is inefficient.
E [r] 6
sp
s mvp
s zc(p)
(r)
return risky
+ weight riskless
rf
E [rp ] = w0 e + (1
w0 1)rf
Proposition 4 An efficient portfolio in the presence of a riskless asset has the weights
wp
E [r ] rf
= V 1 (e 1rf ) p
H
where
H = (e
1rf )0 V
1 (e
1rf )
73
Conversely,
2 (rp ) =
(E [rp ] rf )2
E [rp ].
E [r] 6
se
A
C
rf
s mvp
@
@
@
@
p@
1
C@
Suppose rf
>
(r)
A
C . Then the efficient set is the two half-lines starting from
(0; rf ).
E [r] 6
rf
A@
C @
s mvp
@
@
@ s
@e
p
(r)
A , the weight in the risk free asset is one. The risky portfolio is an zero investment portfolio.
If rf = C
The efficient set consists of two asymptotes toward the efficient set of risky assets.
74
E [r] 6
A
C@
s mvp
@
@
@
@
p
(r)
p solves
1
= arg min w0 Vw
2
w
subject to
w0 e = E [~
rp ]
w0 1 = 1
w0
0
Such short sale resctricted minimum variance portfolio portfolios are much harder to deal with analytically, since they do not admit a general solution, one rather has to investigate the Kuhn-Tucker
conditions for corner solutions etc. To deal with this problem in practice one will use a subroutine for
solving constrained optimization problems.
Sp =
p is defined as
E [rp ] rf
(rp )
The Sharpe ratio Sp of a portfolio p is the slope of the line in mean-standard deviations space from the
risk free rate through p. Note that in the case with a risk free asset, the tangency portfolio has the
maximal Sharpe Ratio on the efficient frontier.
75
E [ri ] = rf + i (E [rm ] rf )
where ri is the return on asset i, rf the return on the risk free asset,
6.11.1 Treynor
Tp =
rp rf
p
6.11.2 Jensen
p = rp (rf + p (rm rf )
Readings and Sources The classical sources for this material are Merton (1972) and Roll (1977a). (Huang
and Litzenberger, 1988, Ch 3) has a good textbook discussion of it.
76
V=
0:05
0:1
1:0 0:0
0:0 1:0
0:5
0:5
77
#include <cmath>
using namespace std;
#include "newmat.h"
using namespace NEWMAT;
double mv calculate mean(const Matrix& e, const Matrix& w){
Matrix tmp = e.t()*w;
return tmp(1,1);
};
double mv calculate variance(const Matrix& V, const Matrix& w){
Matrix tmp = w.t()*V*w;
return tmp(1,1);
};
double mv calculate st dev(const Matrix& V, const Matrix& w){
double var = mv calculate variance(V,w);
return sqrt(var);
};
78
In C++ Code 6.4 and C++ Code 6.3 we show how to calculate the mean variance optimal portfolio for a given
required return. This is the case where there are no constraints on the weight, and we use the analytical
solution directly.
#include "newmat.h"
using namespace NEWMAT;
ReturnMatrix mv calculate portfolio given mean unconstrained(const Matrix& e,
const Matrix& V,
const double& r){
int no assets=e.Nrows();
Matrix ones = Matrix(no assets,1); for (int i=0;i<no assets;++i){ ones.element(i,0) = 1; };
Matrix Vinv = V.i(); // inverse of V
Matrix A = (ones.t()*Vinv*e); double a = A.element(0,0);
Matrix B = e.t()*Vinv*e; double b = B.element(0,0);
Matrix C = ones.t()*Vinv*ones; double c = C.element(0,0);
double d = b*c
a*a;
Matrix Vinv1=Vinv*ones;
Matrix Vinve=Vinv*e;
Matrix g = (Vinv1*b
Vinve*a)*(1.0/d);
Matrix h = (Vinve*c
Vinv1*a)*(1.0/d);
Matrix w = g + h*r;
w.Release();
return w;
};
C++ Code 6.3: Calculating the unconstrained frontier portfolio given an expected return using Newmat
Example
Mean variance calculations.
e=
V=
0:05
0:1
1:0 0:0
0:0 1:0
r = 0:075.
C++ program:
cout << "Testing portfolio calculation " << endl;
Matrix e(2,1);
e(1,1)=0.05; e(2,1)=0.1;
Matrix V(2,2);
V(1,1)=1.0; V(2,1)=0.0;
V(1,2)=0.0; V(2,2)=1.0;
double r=0.075;
Matrix w = mv calculate portfolio given mean unconstrained(e,V,r);
cout << " suggested portfolio: ";
cout << " w1 = " << w(1,1) << " w2 = " << w(2,1) << endl;
79
#include <itpp/itbase.h>
using namespace itpp;
mat mv calculate portfolio given mean unconstrained(const vec& e,
const mat& V,
const double& r){
int no assets=e.size();
vec one = ones(no assets);
mat Vinv = inv(V);
// inverse of V
mat A = one.transpose()*Vinv*e;
double a = A(0,0);
mat B = e.transpose()*Vinv*e;
double b = B(0,0);
mat C = one.transpose()*Vinv*one;
double c = C(0,0);
double d = b*c a*a;
mat Vinv1=Vinv*one;
mat Vinve=Vinv*e;
mat g = (Vinv1*b
Vinve*a)*(1.0/d);
mat h = (Vinve*c
Vinv1*a)*(1.0/d);
mat w = g + h*r;
return w;
};
C++ Code 6.4: Calculating the unconstrained frontier portfolio given an expected return using IT++
80
Chapter 7
Futures algoritms.
Contents
7.1 Pricing of futures contract. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
81
ft = er(T t) St
#include <cmath>
using namespace std;
// current price of underlying asset
double futures price(const double& S,
const double& r,
// risk free interest rate
const double& time to maturity) {
return exp(r*time to maturity)*S;
};
What is the futures price for a contract with time to maturity of half a year?
C++ program:
double S=100; double r=0.10; double time=0.5;
cout << " futures price = " << futures price(S,r, time)
81
<< endl;
Chapter 8
82
82
85
8.1 Options
Option and other derivative pricing is one of the prime success stories of modern finance. An option is
a derivative security, the cash flows from the security is a function of the price of some other security,
typically called the underlying security. A call option is a right, but not obligation, to buy a given
quantity of the underlying security at a given price, called the exercise price K , within a certain time
interval. A put option is the right, but not obligation, to sell a given quantity of the underlying security
to an agreed excercise price within a given time interval. If an option can only be exercised (used) at a
given date (the time interval is one day), the option is called an European Option. If the option can be
used in a whole time period up to a given date, the option is called American.
An option will only be used if it is valuable to the option holder. In the case of a call option, this is
when the exercise price K is lower than the price one alternatively could buy the underlying security
for, which is the current price of the underlying security. Hence, options have never negative cash flows
at maturity. Thus, for anybody to be willing to offer an option, they must have a cost when entered
into. This cost, or price, is typically called an option premium. As notation, let C signify the price of
a call option, P the price of a put option and S the price of the underlying security. All of these prices
are indexed by time. We typically let 0 be now and T the final maturity date of the option. From the
definition of the options, it is clear that at their last possible exercise date, the maturity date, they have
cash flows.
CT = max(0; ST
K)
PT = max(0; K ST )
The challenge of option pricing is to determine the option premia
8.2 Pricing
All pricing uses that the cashflows from the derivative is a direct function of the price of the underlying
security. Pricing can therefore be done relative to the price of the underlying security. To price options
82
it is necessary to make assumptions about the probability distribution of movements of the underlying
security. We start by considering this in a particularly simple framework, the binomial assumption. The
price of the underlying is currently S0 . The price can next period only take on two values, Su and Sd .
S0
H
H
HH
*
HH
j
H
Su
Sd
If one can find all possible future states, an enumeration of all possibilities, one can value a security
by constructing artificial probabilities, called state price probabilities, which one use to find an
artificial expected value of the underlying security, which is then discounted at the risk free interest
rate. The binomial framework is particularly simple, since there are only two possible states. If we
find the probability q of one state, we also find the probability of the other as (1 q ). Equation 8.1
demonstrates this calculation for the underlying security.
S0 = e r (qSu + (1 q)Sd )
(8.1)
Now, any derivative security based on this underlying security can be priced using the same probability
q. The contribution of binomial option pricing is in actually calculating the number q. To do valuation,
start by introducing constants u and d implicitly defined by Su = uS0 and Sd = dS0 , and you get a
price process as illustrated in figure 8.1.
S0
*
H
HH
HH
j
uS0
dS0
q=
q as
er d
u d
The price of a one-period call option in a binomial framework is shown in Formula 8.1 and implemented
in C++ Code 8.1.
The state price probability q is found by an assumption of no arbitrage opportunities. If one has the
possibility of trading in the underlying security and a risk free bond, it is possible to create a portfolio of
these two assets that exactly duplicates the future payoffs of the derivative security. Since this portfolio
has the same future payoff as the derivative, the price of the derivative has to equal the cost of the
duplicating portfolio. Working out the algebra of this, one can find the expression for q as the function
of the up and down movements u and d.
Exercise 8.1.
The price of the underlying security follows the binomial process
83
Cu = max(0; Su K )
Cd = max(0; Sd K )
C0 = e r (qCu + (1 q)Cd )
er d
q=
u d
Su = uS0 and Sd = dS0 are the possible values for the underlying security next period, u and d are constants, r is the (continously
compounded) risk free interest rate and
double&
double&
double&
double&
double&
S, // spot price
X,
// exercice price
r,
// interest rate (per period)
u,
// up movement
d){ // down movement
S0
*
HH
HH
j
H
Su
Sd
C0
*
H
HH
HH
j
Cu = max(0; Su K )
Cd = max(0; Sd K )
1. Show how one can combine a position in the underlying security with a position in risk free bonds to
create a portfolio which exactly duplicates the payoffs from the call.
2. Use this result to show the one period pricing formula for a call option shown in formula 8.1.
84
St
H
H
HH
t
* HuS
H
HH
* u(uSt ) = uuSt
H
HH
j d(uS ) = u(dS ) = udS
H
t
t
t
*
HH
j dS
H
HHt
H
HH
H
H
j d(dSt ) = ddSt
H
Iterating this idea a few times more, the number of different terminal states increases markedly, and we
get closer to a realistic distribution of future prices of the underlying at the terminal date. Note that a
crucial assumption to get a picture like this is that the factors u and d are the same on each date.
H
H
H
HH
H
HH
HH H
H
H
H
H
H
H
H
HH
H
HH
H
H
H
HH HH
H
H
HH HH
H
HH
H
H
Pricing in a setting like this is done by working backwards, starting at the terminal date. Here we know
all the possible values of the underlying security. For each of these, we calculate the payoffs from the
derivative, and find what the set of possible derivative prices is one period before. Given these, we can
find the option one period before this again, and so on. Working ones way down to the root of the tree,
the option price is found as the derivative price in the first node.
For example, suppose we have two periods, and price a two period call option with exercise price
85
K.
S0
H
H
HH
0
* HuS
H
H
H
* uuS0
H
HH
j udS
H
0
*
HH
j dS0
H
HH
HH
H
HH
j ddS0
H
K)
* HH
HH
H
HH
j C = max(0; duS K )
H
HH
* du
HH
H
HH
j
H
HH
HH
H
HH
j Cdd max(0; ddS K )
H
Next step: Find the two possible call prices at time 1:
Cu = e r (qCuu + (1 q)Cud )
Cd = e r (qCud + (1 q)Cdd )
C0
*
HH
HH
j
H
Cu
Cd
C0 = e r (qCu + (1 q)Cd )
Thus, binomial pricing really concerns rolling backward in a binomial tree, and programming therefore
concerns an efficient way of traversing such a tree. The obvious data structure for describing such a tree
is shown in C++ Code 8.2, where the value in each node is calculated from finding out the number of up
and down steps are used to get to the particular node.
Exercise 8.2.
86
#include <vector>
#include <cmath>
using namespace std;
vector< vector<double>
<< endl;
3.64342
5.44255
Exercise 8.3.
Implement pricing of single and multi period binomial put options.
Further reading The derivation of the single period binomial is shown in for example Bossaerts and
degaard (2001), Hull (2011) or McDonald (2013).
87
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
double option price call european binomial multi period given ud(const double& S, // spot price
const double& K, // exercice price
const double& r, // interest rate (per period)
const double& u, // up movement
const double& d, // down movement
const int& no periods){ // no steps in binomial tree
double Rinv = exp( r);
// inverse of interest rate
double uu = u*u;
double p up = (exp(r) d)/(u d);
double p down = 1.0 p up;
vector<double> prices(no periods+1); // price of underlying
prices[0] = S*pow(d, no periods); // fill in the endnodes.
for (int i=1; i<=no periods; ++i) prices[i] = uu*prices[i 1];
vector<double> call values(no periods+1); // value of corresponding call
for (int i=0; i<=no periods; ++i) { call values[i] = max(0.0, (prices[i] K));}; // call payoffs at maturity
step) {
for (int step=no periods 1; step>=0;
for (int i=0; i<=step; ++i) {
call values[i] = (p up*call values[i+1]+p down*call values[i])*Rinv;
};
};
return call values[0];
};
88
Chapter 9
90
92
93
93
93
93
9.3.1
9.3.2
Delta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Other Derivatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
93
94
9.3.3
Implied Volatility. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
96
9.4 References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
98
The pricing of options and related instruments has been a major breakthrough for the use of financial
theory in practical application. Since the original papers of Black and Scholes (1973) and Merton (1973),
there has been a wealth of practical and theoretical applications. We will now consider the orginal Black
Scholes formula for pricing options, how it is calculated and used. For the basic intuition about option
pricing the reader should first read the discussion of the binomial model in the previous chapter, as that
is a much better environment for understanding what is actually calculated.
An option is a derivative security, its value depends on the value, or price, of some other underlying
security, called the underlying security.. Let S denote the value, or price, of this underlying security.
We need to keep track of what time this price is observed at, so let St denote that the price is observed
at time t. A call (put) option gives the holder the right, but not the obligation, to buy (sell) some
underlying asset at a given price K , called the exercise price, on or before some given date T . If the
option is a so called European option, it can only be used (exercised) at the maturity date. If the
option is of the so called American type, it can be used (exercised) at any date up to and including the
maturity date T . If exercised at time T , a call option provides payoff
CT = max(0; ST
K)
PT = max(0; K ST )
The Black Scholes formulas provides analytical solutions for European put and call options, options
which can only be exercised at the options maturity date. Black and Scholes showed that the additional
89
information needed to price the option is the (continously compounded) risk free interest rate r, the
variability of the underlying asset, measured by the standard deviation of (log) price changes, and
the time to maturity (T t) of the option, measured in years. The original formula was derived under
the assumption that there are no payouts, such as stock dividends, coming from the underlying security
during the life of the option. Such payouts will affection option values, as will become apparent later.
d1 =
ln
and
S
K + (r +
1 2
p 2 )(T t)
T
d2 = d1 T
d1 =
d2 =
d1 and d2 as
ln
S
K + r (T
t)
1 p
+ T
2
ln
S
K + r (T
t)
1 p
T
2
T
T
t
t
S is the price of the underlying security, K the exercise price, r the (continously compounded) risk free interest rate, the
standard deviation of the underlying asset, t the current date, T the maturity date, T t the time to maturity for the option and
N the cumulative normal distribution.
()
#include <cmath>
#include "normdist.h"
// mathematical C library
// the calculation of the cumularive normal distribution
double option price call black scholes(const double& S, // spot (underlying) price
const double& K,
// strike (exercise) price,
const double& r,
// interest rate
const double& sigma, // volatility
const double& time) { // time to maturity
double time sqrt = sqrt(time);
double d1 = (log(S/K)+r*time)/(sigma*time sqrt)+0.5*sigma*time sqrt;
double d2 = d1 (sigma*time sqrt);
return S*N(d1)
K*exp( r*time)*N(d2);
};
C++ Code 9.1: Price of European call option using the Black Scholes formula
90
Matlab Code 9.1: Price of European call option using the Black Scholes formula
91
Example
Stock in company XYZ is currently trading at 50. Consider a call option on XYZ stock with an exercise price
of K = 50 and time to maturity of 6 months. The volatility of XYZ stock has been estimated to be = 30%.
The current risk free interest rate (with continous compounding) for six month borrowing is 10%.
1. Determine the option price.
To calculate the price of this option we use the Black Scholes formula with inputs S
= 0:3 and (T t) = 0:5.
Matlab program:
S = 100;
K = 100;
r = 0.1;
sigma = 0.1;
time = 1;
c = black scholes call(S,K,r,sigma,time)
<< endl;
p = Ke r(T t) N ( d2 ) SN ( d1 )
where
d2 = d1 T
t;
dS = Sdt + SdZ
or
dS
= dt + dZ
S
where and are constants, and Z
is Brownian motion.
Using Itos lemma, the assumption of no arbitrage, and the ability to trade continuously, Black and
Scholes showed that the price of any contingent claim written on the underlying must solve the partial
differential equation (9.1).
@f
@f 1 @ 2 f 2 2
S = rf
rS + +
@S
@t 2 @S 2
(9.1)
For any particular contingent claim, the terms of the claim will give a number of boundary conditions
that determines the form of the pricing formula.
The pde given in equation (9.1), with the boundary condition cT = max(0; ST K ) was shown by Black
and Scholes to have an analytical solution of functional form shown in the Black Scoles formula 9.1.
9.3.1 Delta
The first derivative of the option price with respect to the price of the underlying security is called the
delta of the option price. It is the derivative most people will run into, since it is important in hedging
of options.
@c
= N (d1 )
@S
C++ Code 9.2 shows the calculation of the delta for a call option.
93
#include <cmath>
#include "normdist.h"
double option price delta call black scholes(const double& S, // spot price
const double& K, // Strike (exercise) price,
const double& r,
// interest rate
const double& sigma, // volatility
const double& time){ // time to maturity
double time sqrt = sqrt(time);
double d1 = (log(S/K)+r*time)/(sigma*time sqrt) + 0.5*sigma*time sqrt;
double delta = N(d1);
return delta;
};
C++ Code 9.2: Calculating the delta of the Black Scholes call option price
=
@c
= N (d1 )
@S
Gamma ( )
@2c
n(d )
= p1
2
@S
S T t
Theta () (careful about which of these you want)
1
1
@c
= Sn(d1 ) p
+ rKe r(T t) N (d2 )
@ (T t)
2 T t
@c
1
1
= Sn(d1 ) p
rKe r(T t) N (d2 )
@t
2 T t
Vega
p
@c
= S T tn(d1 )
@
Rho ()
@c
= K (T t)e r(T t) N (d2 )
@r
S is the price of the underlying security, K the exercise price, r the (continously compounded) risk free interest rate, the
standard deviation of the underlying asset,
for the option.
t the current date,
T the maturity date and T t the time to maturity
n() is the normal distribution function n(z ) = p12 e
1 2
2z
and
Rz
1 n(t)dt
Formula 9.2: Partial derivatives of the Black Scholes call option formula
The calculation of all of these partial derivatives for a call option is shown in C++ Code 9.3.
Example
Consider again six month call options on XYZ stock. The option matures 6 months from now, at which time
the holder of the option can recive one unit of the underlying security by paying the exercise price of K = 50.
The current price of the underlying security is S = 50. The volatility of the underlying security is given as
= 30%. The current risk free interest rate (with continous compounding) for six month borrowing is 10%.
94
#include <cmath>
#include "normdist.h"
using namespace std;
void option price partials call black scholes( const double& S, // spot price
const double& K, // Strike (exercise) price,
const double& r,
// interest rate
const double& sigma, // volatility
const double& time, // time to maturity
double& Delta, // partial wrt S
double& Gamma, // second prt wrt S
double& Theta, // partial wrt time
double& Vega, // partial wrt sigma
double& Rho){ // partial wrt r
double time sqrt = sqrt(time);
double d1 = (log(S/K)+r*time)/(sigma*time sqrt) + 0.5*sigma*time sqrt;
double d2 = d1 (sigma*time sqrt);
Delta = N(d1);
Gamma = n(d1)/(S*sigma*time sqrt);
Theta = (S*sigma*n(d1))/(2*time sqrt)
r*K*exp( r*time)*N(d2);
Vega = S * time sqrt*n(d1);
Rho = K*time*exp( r*time)*N(d2);
};
C++ Code 9.3: Calculating the partial derivatives of a Black Scholes call option
1. Calculate the partial derivatives (Delta, Gamma, Theta, Vega and Rho) for this option.
To calculate the partial derivatives we use inputs
C++ program:
cout << " Black Scholes call partial derivatives " << endl;
double S = 50; double K = 50; double r = 0.10;
double sigma = 0.30; double time=0.50;
double Delta, Gamma, Theta, Vega, Rho;
option price partials call black scholes(S,K,r,sigma, time, Delta, Gamma, Theta, Vega, Rho);
cout << " Delta = " << Delta << endl;
cout << " Gamma = " << Gamma << endl;
cout << " Theta = " << Theta << endl;
cout << " Vega = " << Vega << endl;
cout << " Rho = " << Rho << endl;
95
t) = 0:5.
c0 = c(S; K; r; ; T
t)
Unfortunately, this equation has no closed form solution, which means the equation must be numerically
solved to find . What is probably the algorithmic simplest way to solve this is to use a binomial search
algorithm, which is implemented in C++ Code 9.4. We start by bracketing the sigma by finding a high
sigma that makes the BS price higher than the observed price, and then, given the bracketing interval,
we search for the volatility in a systematic way. shows such a calculation.
#include <cmath>
#include "fin_recipes.h"
double option price implied volatility call black scholes bisections(const double& S,
const double& K,
const double& r,
const double& time,
const double& option price){
if (option price<0.99*(S K*exp( time*r))) { // check for arbitrage violations.
return 0.0;
// Option price is too low if this happens
};
// simple binomial search for the implied volatility.
// relies on the value of the option increasing in volatility
const double ACCURACY = 1.0e 5; // make this smaller for higher accuracy
const int MAX ITERATIONS = 100;
const double HIGH VALUE = 1e10;
const double ERROR = 1e40;
// want to bracket sigma. first find a maximum sigma by finding a sigma
// with a estimated price higher than the actual price.
double sigma low=1e 5;
double sigma high=0.3;
double price = option price call black scholes(S,K,r,sigma high,time);
while (price < option price) {
sigma high = 2.0 * sigma high; // keep doubling.
price = option price call black scholes(S,K,r,sigma high,time);
if (sigma high>HIGH VALUE) return ERROR; // panic, something wrong.
};
for (int i=0;i<MAX ITERATIONS;i++){
double sigma = (sigma low+sigma high)*0.5;
price = option price call black scholes(S,K,r,sigma,time);
double test = (price option price);
if (fabs(test)<ACCURACY) { return sigma; };
if (test < 0.0) { sigma low = sigma; }
else { sigma high = sigma; }
};
return ERROR;
};
C++ Code 9.4: Calculation of implied volatility of Black Scholes using bisections
Instead of this simple bracketing, which is actually pretty fast, and will (almost) always find the solution,
we can use the NewtonRaphson formula for finding the root of an equation in a single variable. The
general description of this method starts with a function f () for which we want to find a root.
f (x) = 0:
96
The function
f () needs to be differentiable.
xi+1 = xi
x0 , iterate by
f (xi )
f 0 (xi )
until
jf (xi )j <
where
In our case
i+1 = i +
C++ Code 9.5 shows the calculation of implied volatility using Newton-Raphson.
#include "fin_recipes.h"
#include "normdist.h"
#include <cmath>
#include <iostream>
double option price implied volatility call black scholes newton(const double& S,
const double& K,
const double& r,
const double& time,
const double& option price) {
if (option price<0.99*(S K*exp( time*r))) { // check for arbitrage violations. Option price is too low if this happens
return 0.0;
};
const int MAX ITERATIONS = 100;
const double ACCURACY = 1.0e 5;
double t sqrt = sqrt(time);
double sigma = (option price/S)/(0.398*t sqrt); // find initial value
for (int i=0;i<MAX ITERATIONS;i++){
double price = option price call black scholes(S,K,r,sigma,time);
double diff = option price price;
if (fabs(diff)<ACCURACY) return sigma;
double d1 = (log(S/K)+r*time)/(sigma*t sqrt) + 0.5*sigma*t sqrt;
double vega = S * t sqrt * n(d1);
sigma = sigma + diff/vega;
};
return 99e10; // something screwy happened, should throw exception
};
C++ Code 9.5: Calculation of implied volatility of Black Scholes using Newton-Raphson
Note that to use Newton-Raphson we need the derivative of the option price. For the Black-Scholes
formula this is known, and we can use this. But for pricing formulas like the binomial, where the partial
derivatives are not that easy to calculate, simple bisection is the preferred algorithm.
Example
Consider again six month call options on XYZ stock. The option matures 6 months from now, at which time
the holder of the option can recive one unit of the underlying security by paying the exercise price of K = 50.
1 For
further discussion of the Newton-Raphson formula and bracketing, a good source is chapter 9 of Press et al. (1992)
97
C = 2:5.
The implied volatility is the which, input in the Black Scholes formula with these other inputs, will produce
an option price of C = 2:5.
To calculate we use inputs
t) = 0:5.
C++ program:
double S = 50; double K = 50; double r = 0.10; double time=0.50;
double C=2.5;
cout << " Black Scholes implied volatility using Newton search = ";
cout << option price implied volatility call black scholes newton(S,K,r,time,C) << endl;
cout << " Black Scholes implied volatility using bisections = ";
cout << option price implied volatility call black scholes bisections(S,K,r,time,C) << endl;
9.4 References
Black and Scholes (1973) Merton (1973)
Gray and Gray (2001) has a simple proof of the formula.
98
Chapter 10
Warrants
Contents
10.1 Warrant value in terms of assets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10.2 Valuing warrants when observing the stock value . . . . . . . . . . . . . . . . . . . . . . . . .
10.3 Readings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
99
100
101
A warrant is an option-like security on equity, but it is issued by the same company which has issued
the equity, and when a warrant is exercised, a new stock is issued. This new stock is issued at a the
warrant strike price, which is lower than the current stock price (If it wasnt the warrant would not be
exercised.) Since the new stock is a a fractional right to all cashflows, this stock issue waters out, or
dilutes, the equity in a company. The degree of dilution is a function of how many warrants are issued.
At + mK;
but this new asset value is spread over more shares, since each exercised warrant is now an equity. The
assets of the firm is spread over all shares, hence each new share is worth:
At + mK
m+n
making each exercised warrant worth:
At + mK
m+n
At
K=
m+n n
If we knew the current value of assets in the company, we could value the warrant in two steps:
1. Value the option using the Black Scholes formula and Ant as the current stock price.
2. Multiply the resulting call price with mn+n .
If we let
99
where
At = nSt + mWt
Using the stock price, one would value the warrant as
Wt =
n+m
CBS
nSt + mWt
; K; ; r; (T
n
t)
or
Wt =
n+m
CBS St +
m
W ; K; ; r; (T
n t
t)
Wt as a function of Wt .
g(Wt ) = Wt
Starting with an initial guess for the warrant value Wto , the Newton-Rhapson method is that one iterates
as follows
Wti = Wti 1
g(Wti
g0 (Wti
1)
1) ;
g0 (Wt ) = 1
In this
m
N (d )
m+n 1
where
d1 =
ln
St + m
n Wt
K
+ (r + 12 2 )(T
T
t)
An obvious starting value is to set calculate the Black Scholes value using the current stock price, and
multiply it with mm
+n .
C++ Code 10.1 implements this calculation.
Example
A stock is currently priced at S = 48. Consider warrants on the same company with exercise price K = 40 and
time to maturity of six months. The company has n = 10000 shares outstanding, and has issued m = 1000
warrants. The current (continously compounded) risk free interest rate is 8%. Determine the current warrant
price.
100
#include "fin_recipes.h"
#include "normdist.h"
#include <cmath>
const double EPSILON=0.00001;
double warrant price adjusted black scholes(const double& S,
const double& K,
const double& r,
const double& sigma,
const double& time,
const double& m, // number of warrants outstanding
const double& n){ // number of shares outstanding
double time sqrt = sqrt(time);
double w = (n/(n+m))*option price call black scholes(S,K,r,sigma,time);
double g = w (n/(n+m))*option price call black scholes(S+(m/n)*w,K,r,sigma,time);
while (fabs(g)>EPSILON) {
double d1 = (log((S+(m/n))/K)+r*time)/(sigma*time sqrt)+0.5*sigma*time sqrt;
double gprime = 1 (m/n)*N(d1);
w=w g/gprime;
g = w (n/(n+m))*option price call black scholes(S+(m/n)*w,K,r,sigma,time);
};
return w;
};
10.3 Readings
McDonald (2013) and Hull (2011) are general references. A problem with warrants is that exercise of all
warrants simultaneously is not necessarily optimal.
Press et al. (1992) discusses the Newton-Rhapson method for root finding.
101
Chapter 11
. . . . . . . . 105
109
110
11.6 Readings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
111
p = Ke r(T t) N ( d2 ) Se q(T t) N ( d1 )
1. Implement this formula.
102
q is:
d1 =
ln
S
K + (r
1 2
p q + 2 )(T t)
T
d2 = d1 T
is the price of the underlying secrutity, K the exercise price, r the risk free interest rate, q the (continous) payout and the
standard deviation of the underlying asset, t the current date, T the maturity date, T t the time to maturity for the option and
N the cumulative normal distribution.
()
Formula 11.1: Analytical prices for European call option on underlying security having a payout of
#include <cmath>
#include "normdist.h"
using namespace std;
// mathematical library
// this defines the normal distribution
double option price european call payout( const double& S, // spot price
const double& X, // Strike (exercise) price,
const double& r, // interest rate
const double& q, // yield on underlying
const double& sigma, // volatility
const double& time) { // time to maturity
double sigma sqr = pow(sigma,2);
double time sqrt = sqrt(time);
double d1 = (log(S/X) + (r q + 0.5*sigma sqr)*time)/(sigma*time sqrt);
double d2 = d1 (sigma*time sqrt);
double call price = S * exp( q*time)* N(d1)
X * exp( r*time) * N(d2);
return call price;
};
11.1.2 Dividends.
A special case of payouts from the underlying security is stock options when the stock pays dividends.
When the stock pays dividends, the pricing formula is adjusted, because the dividend changes the value
of the underlying.
The case of continuous dividends is easiest to deal with. It corresponds to the continuous payouts we
have looked at previously. The problem is the fact that most dividends are paid at discrete dates.
European Options on dividend-paying stock.
To adjust the price of an European option for known dividends, we merely subtract the present value of
the dividends from the current price of the underlying asset in calculating the Black Scholes value.
Example
Consider a stock option with the following data given:S = 100, K = 100, r = 0:1,
is one year. dividend yield = 5%. Dividend payments at times 0:25 and 0:75.
Determine the option price
103
#include <cmath>
#include <vector>
#include "fin_recipes.h"
// mathematical library
// define the black scholes price
<< endl;
104
11.2.1 Exact american call formula when stock is paying one dividend.
When a stock pays dividend, a call option on the stock may be optimally exercised just before the stock
goes ex-dividend. While the general dividend problem is usually approximated somehow, for the special
case of one dividend payment during the life of an option an analytical solution is available, due to
RollGeskeWhaley.
If we let S be the stock price, K the exercise price, D1 the amount of dividend paid, t1 the time of
dividend payment, T the maturity date of option, we denote the time to dividend payment 1 = T t1
and the time to maturity = T t.
A first check of early exercise is:
D1 K 1 e r(T
t1 )
If this inequality is fulfilled, early exercise is not optimal, and the value of the option is
c(S e r(t1 t) D1 ; K; r; ; (T
where
t))
If the inequality is not fulfilled, one performs the calculation shown in Formula 11.2 and implemented in
C++ Code 11.3.
=
a1 =
(t1
t
t
ln
S D1 e
K
a2 = a1 T
b1 =
ln
+ (r + 12 2 )
t
S D1 e r(t1
S
b2 = b1 T
and
q (1
t)
+ (r + 21 2 )(t1
t)
(t1 t)
S solves
t1 ) = S + D1 K
c(S;
S is the price of the underlying secrutity, K the exercise price, r the risk free interest rate, D1 is the dividend amount and the
standard deviation of the underlying asset, t the current date, T the maturity date, T
t the time to maturity for the option
and N
the cumulative normal distribution. N with one argument is the univariate normal cumulative distribution. N with
three arguments is the bivariate normal distribution with the correlation between the two normals given as the third arguement.
()
()
()
Formula 11.2: RollGeskeWhaley price of american call option paying one fixed dividend
Example
Consider an option on a stock paying one dividend. The relevant data is
= 1:0, 1 = 0:5 and D1 = 10.
Price the option.
105
C++ program:
double S = 100.0; double K = 100.0;
double r = 0.1;
double sigma = 0.25;
double tau = 1.0; double tau1 = 0.5;
double D1 = 10.0;
cout << " american call price with one dividend = "
<< option price american call one dividend(S,K,r,sigma,tau,D1, tau1)<< endl;
106
#include <cmath>
#include "normdist.h" // define the normal distribution functions
#include "fin_recipes.h" // the regular black sholes formula
double option price american call one dividend(const double& S,
const double& K,
const double& r,
const double& sigma,
const double& tau,
const double& D1,
const double& tau1){
if (D1 <= K* (1.0 exp( r*(tau tau1)))) // check for no exercise
return option price call black scholes(S exp( r*tau1)*D1,K,r,sigma,tau);
const double ACCURACY = 1e 6;
// decrease this for more accuracy
double sigma sqr = sigma*sigma;
double tau sqrt = sqrt(tau);
double tau1 sqrt = sqrt(tau1);
double rho =
sqrt(tau1/tau);
double S bar = 0; // first find the S bar that solves c=S bar+D1-K
double S low=0; // the simplest: binomial search
double S high=S; // start by finding a very high S above S bar
double c = option price call black scholes(S high,K,r,sigma,tau tau1);
double test = c S high D1+K;
while ( (test>0.0) && (S high<=1e10) ) {
S high *= 2.0;
c = option price call black scholes(S high,K,r,sigma,tau tau1);
test = c S high D1+K;
};
if (S high>1e10) { // early exercise never optimal, find BS value
return option price call black scholes(S D1*exp( r*tau1),K,r,sigma,tau);
};
S bar = 0.5 * S high; // now find S bar that solves c=S bar-D+K
c = option price call black scholes(S bar,K,r,sigma,tau tau1);
test = c S bar D1+K;
while ( (fabs(test)>ACCURACY) && ((S high S low)>ACCURACY) ) {
if (test<0.0) { S high = S bar; }
else { S low = S bar; };
S bar = 0.5 * (S high + S low);
c = option price call black scholes(S bar,K,r,sigma,tau tau1);
test = c S bar D1+K;
};
double a1 = (log((S D1*exp( r*tau1))/K) +( r+0.5*sigma sqr)*tau) / (sigma*tau sqrt);
double a2 = a1
sigma*tau sqrt;
double b1 = (log((S D1*exp( r*tau1))/S bar)+(r+0.5*sigma sqr)*tau1)/(sigma*tau1 sqrt);
double b2 = b1
sigma * tau1 sqrt;
double C = (S D1*exp( r*tau1)) * N(b1) + (S D1*exp( r*tau1)) * N(a1, b1,rho)
(K*exp( r*tau))*N(a2, b2,rho)
(K D1)*exp( r*tau1)*N(b2);
return C;
};
C++ Code 11.3: Option price, RollGeskeWhaley call formula for dividend paying stock
107
d1 =
ln
F
K +
1 2
p 2 (T t)
T t
p
d2 = d1 T
is the futures price, K is the exercise price, r the risk free interest rate, the volatility of the futures
price, and T t is the time to maturity of the option (in years).
Formula 11.3: Blacks formula for the price of an European Call option with a futures contract as the
underlying security
#include <cmath>
#include "normdist.h"
using namespace std;
// mathematics library
// normal distribution
double futures option price call european black( const double& F, // futures price
const double& K, // exercise price
const double& r,
// interest rate
const double& sigma, // volatility
const double& time){ // time to maturity
double sigma sqr = sigma*sigma;
double time sqrt = sqrt(time);
double d1 = (log (F/K) + 0.5 * sigma sqr * time) / (sigma * time sqrt);
double d2 = d1
sigma * time sqrt;
return exp( r*time)*(F * N(d1)
K * N(d2));
};
C++ program:
double F = 50.0; double K = 45.0;
double r = 0.08; double sigma = 0.2;
double time=0.5;
cout << " european futures call option = "
<< futures option price put european black(F,K,r,sigma,time)
<< endl;
p = e r(T t) (KN ( d2 ) F N ( d1 ))
where the varibles are as defined for the call option.
1. Implement the put option price.
c = Se
where
d1 =
ln
rf (T t) N (d
S
K +
d2 = d1 T
1 ) Ke r(T t) N (d2 )
r rf + 12 2 (T
p
T t
t)
is the spot exchange rate and K the exercise price. r is the domestic interest rate and rf the foreign interest rate.
volatility of changes in the exchange rate. T
t is the time to maturity for the option.
is the
#include <cmath>
#include "normdist.h"
double currency option price call european( const double& S, // exchange rate,
const double& X,
// exercise,
const double& r,
// r domestic,
const double& r f, // r foreign,
const double& sigma, // volatility,
const double& time){ // time to maturity
double sigma sqr = sigma*sigma;
double time sqrt = sqrt(time);
double d1 = (log(S/X) + (r r f+ (0.5*sigma sqr)) * time)/(sigma*time sqrt);
double d2 = d1
sigma * time sqrt;
return S * exp( r f*time) * N(d1)
X * exp( r*time) * N(d2);
};
109
C++ program:
double S = 50.0;
double K = 52.0;
double r = 0.08;
double rf=0.05;
double sigma = 0.2; double time=0.5;
cout << " european currency call option = "
<< currency option price call european(S,K,r,rf,sigma,time)
<< endl;
p = Ke r(T t) N ( d2 ) Se
rf (T t) N (
d1 )
Cp =
K
h1 1 S
h1 1
h1 K
where
1
h1 =
2
r q
+
2
s
h1
r q
2
1 2 2r
+ 2
2
#include <cmath>
using namespace std;
double option price american perpetual call(const double& S,
const double& K,
const double& r,
const double& q,
const double& sigma){
double sigma sqr=pow(sigma,2);
double h1 = 0.5
((r q)/sigma sqr);
h1 += sqrt(pow(((r q)/sigma sqr 0.5),2)+2.0*r/sigma sqr);
double pric=(K/(h1 1.0))*pow(((h1 1.0)/h1)*(S/K),h1);
return pric;
};
options would be like the classical April fools present, a perpetual zero coupon bond. . .
110
Example
Price a perpetual call, given the following informtation
C++ program:
double S=50.0; double K=40.0;
double r=0.05; double q=0.02;
double sigma=0.05;
double price = option price american perpetual call(S,K,r,q,sigma);
cout << " perpetual call price = " << price << endl;
Pp =
K
h2 1 S
1 h2
h2 K
h2
where
1
h2 =
2
r q
2
s
r q
2
1 2 2r
+ 2
2
11.6 Readings
Hull (2011) and McDonald (2013) are general references. A first formulation of an analytical call price
with dividends was in Roll (1977b). This had some errors, that were partially corrected in Geske (1979),
before Whaley (1981) gave a final, correct formula. See Hull (2011) for a textbook summary. Black
(1976) is the original development of the futures option. The original formulations of European foreign
currency option prices are in Garman and Kohlhagen (1983) and Grabbe (1983). The price of a perpetual
put was first shown in Merton (1973). For a perpetual call see McDonald and Siegel (1986). The notation
for perpetual puts and calls follows the summary in (McDonald, 2013, pg. 393).
111
Chapter 12
112
113
123
124
130
12.8 References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
131
12.1 Introduction
We have shown binomial calculations given an up and down movement in chapter 8. However, binomial
option pricing can also be viewed as an approximation to a continuous time distribution by judicious
choice of the constants u and d. To do so one has to ask: Is it possible to find a parametrization (choice
of u and d) of a binomial process
112
S
H
H
HH
* HuS
H
HH
* uuS
H
HH
j udS
H
*
HH
j dS
H
HH
HH
H
H
H
j ddS
H
which has the same time series properties as a (continous time) process with the same mean and volatility? There is actually any number of ways of constructing this, hence one uses one degree of freedom on
imposing that the nodes reconnect, by imposing u = d1 .
To value an option using this approach, we specify the number n of periods to split the time to maturity
(T t) into, and then calculate the option using a binomial tree with that number of steps.
S; X; r; ; T
T t
t =
n
p
u = e t
Given
d=e
p t
R = ert
R d
q=
u d
To find the option price, will roll backwards: At node t, calculate the call price as a function of the
two possible outcomes at time t + 1. For example, if there is one step,
C0
H
H
HH
*
HH
j
H
Cu = max(0; Su X )
Cd = max(0; Sd X )
C0 = e r (qCu + (1 q)Cd )
With more periods one will roll backwards as discussed in chapter 8
double option price call european binomial( const double& S, // spot price
const double& K, // exercice price
const double& r,
// interest rate
const double& sigma, // volatility
const double& t,
// time to maturity
const int& steps){ // no steps in binomial tree
double R = exp(r*(t/steps));
// interest rate for each step
double Rinv = 1.0/R;
// inverse of interest rate
double u = exp(sigma*sqrt(t/steps)); // up movement
double uu = u*u;
double d = 1.0/u;
double p up = (R d)/(u d);
double p down = 1.0 p up;
vector<double> prices(steps+1); // price of underlying
prices[0] = S*pow(d, steps); // fill in the endnodes.
for (int i=1; i<=steps; ++i) prices[i] = uu*prices[i 1];
vector<double> call values(steps+1); // value of corresponding call
for (int i=0; i<=steps; ++i) call values[i] = max(0.0, (prices[i] K)); // call payoffs at maturity
for (int step=steps 1; step>=0;
step) {
for (int i=0; i<=step; ++i) {
call values[i] = (p up*call values[i+1]+p down*call values[i])*Rinv;
};
};
return call values[0];
};
114
#include <cmath>
#include <vector>
using namespace std;
double option price call american binomial( const
const
const
const
const
const
double R = exp(r*(t/steps));
double Rinv = 1.0/R;
double u = exp(sigma*sqrt(t/steps));
double d = 1.0/u;
double p up = (R d)/(u d);
double p down = 1.0 p up;
double& S,
double& K,
double& r,
double& sigma,
double& t,
int& steps) {
C++ Code 12.2: Price of American call option using a binomial approximation
Actually, for this particular case, the american price will equal the european.
115
116
117
Example
You are given the following information about an option: S = 100, K = 100, r = 0:1, = 0:25 and time to
maturity is 1 year. Price American calls and puts using binomial approximations with 100 steps.
C++ program:
double S = 100.0; double K = 100.0;
double r = 0.1;
double sigma = 0.25;
double time=1.0; int no steps = 100;
cout << " european call= " << option price call european binomial(S,K,r,sigma,time,no steps) << endl;
cout << " american call= " << option price call american binomial(S,K,r,sigma,time,no steps) << endl;
cout << " american put = " << option price put american binomial(S,K,r,sigma,time,no steps) << endl;
118
16.5
16
15.5
15
14.5
14
13.5
0
10
20
30
40
50
n
binomial
60
Black Scholes
119
70
80
90
100
double&
double&
double&
double&
double&
int& no
S,
K,
r,
sigma,
t,
steps){ // steps in binomial
R = exp(r*(t/no steps));
Rinv = 1.0/R;
u = exp(sigma*sqrt(t/no steps));
d = 1.0/u;
uu= u*u;
pUp = (R d)/(u d);
pDown = 1.0
pUp;
120
#include <cmath>
#include <algorithm>
#include "fin_recipes.h"
void option price partials american call binomial(const double& S, // spot price
const double& K, // Exercise price,
const double& r,
// interest rate
const double& sigma, // volatility
const double& time, // time to maturity
const int& no steps, // steps in binomial
double& delta, // partial wrt S
double& gamma, // second prt wrt S
double& theta, // partial wrt time
double& vega, // partial wrt sigma
double& rho){ // partial wrt r
vector<double> prices(no steps+1);
vector<double> call values(no steps+1);
double delta t =(time/no steps);
double R = exp(r*delta t);
double Rinv = 1.0/R;
double u = exp(sigma*sqrt(delta t));
double d = 1.0/u;
double uu= u*u;
double pUp = (R d)/(u d);
double pDown = 1.0
pUp;
prices[0] = S*pow(d, no steps);
for (int i=1; i<=no steps; ++i) prices[i] = uu*prices[i 1];
for (int i=0; i<=no steps; ++i) call values[i] = max(0.0, (prices[i] K));
CurrStep) {
for (int CurrStep=no steps 1; CurrStep>=2;
for (int i=0; i<=CurrStep; ++i) {
prices[i] = d*prices[i+1];
call values[i] = (pDown*call values[i]+pUp*call values[i+1])*Rinv;
call values[i] = max(call values[i], prices[i] K);
// check for exercise
};
};
double f22 = call values[2];
double f21 = call values[1];
double f20 = call values[0];
for (int i=0;i<=1;i++) {
prices[i] = d*prices[i+1];
call values[i] = (pDown*call values[i]+pUp*call values[i+1])*Rinv;
// check for exercise
call values[i] = max(call values[i], prices[i] K);
};
double f11 = call values[1];
double f10 = call values[0];
prices[0] = d*prices[1];
call values[0] = (pDown*call values[0]+pUp*call values[1])*Rinv;
call values[0] = max(call values[0], S K); // check for exercise on first date
double f00 = call values[0];
delta = (f11 f10)/(S*u S*d);
double h = 0.5 * S * ( uu
d*d);
gamma = ( (f22 f21)/(S*(uu 1))
(f21 f20)/(S*(1 d*d)) ) / h;
theta = (f21 f00) / (2*delta t);
double diff = 0.02;
double tmp sigma = sigma+diff;
double tmp prices = option price call american binomial(S,K,r,tmp sigma,time,no steps);
vega = (tmp prices f00)/diff;
diff = 0.05;
double tmp r = r+diff;
tmp prices = option price call american binomial(S,K,tmp r,sigma,time,no steps);
rho = (tmp prices f00)/diff;
};
121
Example
Given the following information: S = 100,
100 steps in the binomial approximation.
1. Estimate all the greeks for the option: delta(), gamma, theta, vega and rho.
C++ program:
double S = 100.0; double K = 100.0;
double r = 0.1;
double sigma = 0.25;
double time=1.0; int no steps = 100;
double delta, gamma, theta, vega, rho;
option price partials american call binomial(S,K,r, sigma, time, no steps,
delta, gamma, theta, vega, rho);
cout << " Call price partials " << endl;
cout << " delta = " << delta << endl;
cout << " gamma = " << gamma << endl;
cout << " theta = " << theta << endl;
cout << " vega = " << vega << endl;
cout << " rho = " << rho << endl;
122
(T
t =
t)=n
u = et
d=
pu =
e(r y)t d
u d
pd = 1 pu
For example, the last node
double option price call american binomial( const double& S, // spot price
const double& K, // exercice price
const double& r,
// interest rate
const double& y,
// continous payout
const double& sigma, // volatility
const double& t,
// time to maturity
const int& steps) { // no steps in binomial tree
double R = exp(r*(t/steps));
// interest rate for each step
double Rinv = 1.0/R;
// inverse of interest rate
double u = exp(sigma*sqrt(t/steps)); // up movement
double uu = u*u;
double d = 1.0/u;
double p up = (exp((r y)*(t/steps)) d)/(u d);
double p down = 1.0 p up;
vector<double> prices(steps+1); // price of underlying
prices[0] = S*pow(d, steps);
for (int i=1; i<=steps; ++i) prices[i] = uu*prices[i 1]; // fill in the endnodes.
vector<double> call values(steps+1); // value of corresponding call
for (int i=0; i<=steps; ++i) call values[i] = max(0.0, (prices[i] K)); // call payoffs at maturity
for (int step=steps 1; step>=0;
step) {
for (int i=0; i<=step; ++i) {
call values[i] = (p up*call values[i+1]+p down*call values[i])*Rinv;
prices[i] = d*prices[i+1];
call values[i] = max(call values[i],prices[i] K);
// check for exercise
};
};
return call values[0];
};
123
124
#include <cmath>
#include <algorithm>
#include <vector>
#include "fin_recipes.h"
#include <iostream>
double option price call american proportional dividends binomial(const double& S,
const double& K,
const double& r,
const double& sigma,
const double& time,
const int& no steps,
const vector<double>& dividend times,
const vector<double>& dividend yields) {
// note that the last dividend date should be before the expiry date, problems if dividend at terminal node
int no dividends = int(dividend times.size());
if (no dividends == 0) {
return option price call american binomial(S,K,r,sigma,time,no steps); // price w/o dividends
};
double delta t = time/no steps;
double R = exp(r*delta t);
double Rinv = 1.0/R;
double u = exp(sigma*sqrt(delta t));
double uu= u*u;
double d = 1.0/u;
double pUp = (R d)/(u d);
double pDown = 1.0
pUp;
vector<int> dividend steps(no dividends); // when dividends are paid
for (int i=0; i<no dividends; ++i) {
dividend steps[i] = (int)(dividend times[i]/time*no steps);
};
vector<double> prices(no steps+1);
vector<double> call prices(no steps+1);
prices[0] = S*pow(d, no steps); // adjust downward terminal prices by dividends
for (int i=0; i<no dividends; ++i) { prices[0]*=(1.0 dividend yields[i]); };
for (int i=1; i<=no steps; ++i) {
prices[i] = uu*prices[i 1]; };
for (int i=0; i<=no steps; ++i) call prices[i] = max(0.0, (prices[i] K));
for (int step=no steps 1; step>=0;
step) {
for (int i=0;i<no dividends;++i) { // check whether dividend paid
if (step==dividend steps[i]) {
for (int j=0;j<=(step+1);++j) {
prices[j]*=(1.0/(1.0 dividend yields[i]));
};
};
};
for (int i=0; i<=step; ++i) {
call prices[i] = (pDown*call prices[i]+pUp*call prices[i+1])*Rinv;
prices[i] = d*prices[i+1];
call prices[i] = max(call prices[i], prices[i] K);
// check for exercise
};
};
return call prices[0];
};
C++ Code 12.6: Binomial option price of stock option where stock pays proportional dividends
125
126
#include <cmath>
#include <vector>
#include "fin_recipes.h"
#include <iostream>
double option price call american discrete dividends binomial(const double& S,
const double& K,
const double& r,
const double& sigma,
const double& t,
const int& steps,
const vector<double>& dividend times,
const vector<double>& dividend amounts) {
int no dividends = int(dividend times.size());
if (no dividends==0) return option price call american binomial(S,K,r,sigma,t,steps);// just do regular
int steps before dividend = (int)(dividend times[0]/t*steps);
const double R = exp(r*(t/steps));
const double Rinv = 1.0/R;
const double u = exp(sigma*sqrt(t/steps));
const double d = 1.0/u;
const double pUp = (R d)/(u d);
const double pDown = 1.0
pUp;
double dividend amount = dividend amounts[0];
vector<double> tmp dividend times(no dividends 1); // temporaries with
vector<double> tmp dividend amounts(no dividends 1); // one less dividend
for (int i=0;i<(no dividends 1);++i){
tmp dividend amounts[i] = dividend amounts[i+1];
tmp dividend times[i] = dividend times[i+1]
dividend times[0];
};
vector<double> prices(steps before dividend+1);
vector<double> call values(steps before dividend+1);
prices[0] = S*pow(d, steps before dividend);
for (int i=1; i<=steps before dividend; ++i) prices[i] = u*u*prices[i 1];
for (int i=0; i<=steps before dividend; ++i){
double value alive
= option price call american discrete dividends binomial(prices[i] dividend amount,K, r, sigma,
t dividend times[0],// time after first dividend
steps steps before dividend,
tmp dividend times,
tmp dividend amounts);
call values[i] = max(value alive,(prices[i] K)); // compare to exercising now
};
for (int step=steps before dividend 1; step>=0;
step) {
for (int i=0; i<=step; ++i) {
prices[i] = d*prices[i+1];
call values[i] = (pDown*call values[i]+pUp*call values[i+1])*Rinv;
call values[i] = max(call values[i], prices[i] K);
};
};
return call values[0];
};
C++ Code 12.7: Binomial option price of stock option where stock pays discrete dividends
127
Example
Given the following information S = 100, K = 100, r = 0:1, = 0:25 and time to maturity is 1 year,
Calculate option price with two different assumptions about dividends:
1. Continuous payout
2. Discrete payout,
d = 0:02.
C++ program:
double S = 100.0; double K = 100.0;
double r = 0.10;
double sigma = 0.25;
double time=1.0;
int no steps = 100;
double d=0.02;
cout << " call price with continuous dividend payout = "
<< option price call american binomial(S,K,r,d,sigma,time,no steps) << endl;
vector<double> dividend times; vector<double> dividend yields;
dividend times.push back(0.25); dividend yields.push back(0.025);
dividend times.push back(0.75); dividend yields.push back(0.025);
cout << " call price with proportial dividend yields at discrete dates = "
<< option price call american proportional dividends binomial(S,K,r,sigma,time,no steps,
dividend times, dividend yields)
<< endl;
vector<double> dividend amounts; dividend amounts.push back(2.5); dividend amounts.push back(2.5);
cout << " call price with proportial dividend amounts at discrete dates = "
<< option price call american discrete dividends binomial(S,K,r,sigma,time,no steps,
dividend times, dividend amounts)
<< endl;
F = 50:0; K = 45:0; r = 0:08; sigma = 0:2, time=0.5, no steps=100; Price the futures option
C++ program:
double F = 50.0; double K = 45.0;
double r = 0.08; double sigma = 0.2;
double time=0.5;
int no steps=100;
cout << " european futures call option = "
<< futures option price call american binomial(F,K,r,sigma,time,no steps)
128
<< endl;
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
double futures option price call american binomial(const double& F, // price futures contract
const double& K, // exercise price
const double& r, // interest rate
const double& sigma, // volatility
const double& time, // time to maturity
const int& no steps) { // number of steps
vector<double> futures prices(no steps+1);
vector<double> call values (no steps+1);
double t delta= time/no steps;
double Rinv = exp( r*(t delta));
double u = exp(sigma*sqrt(t delta));
double d = 1.0/u;
double uu= u*u;
double pUp = (1 d)/(u d); // note how probability is calculated
double pDown = 1.0
pUp;
futures prices[0] = F*pow(d, no steps);
int i;
for (i=1; i<=no steps; ++i) futures prices[i] = uu*futures prices[i 1]; // terminal tree nodes
for (i=0; i<=no steps; ++i) call values[i] = max(0.0, (futures prices[i] K));
for (int step=no steps 1; step>=0;
step) {
for (i=0; i<=step; ++i) {
futures prices[i] = d*futures prices[i+1];
call values[i] = (pDown*call values[i]+pUp*call values[i+1])*Rinv;
call values[i] = max(call values[i], futures prices[i] K); // check for exercise
};
};
return call values[0];
};
C++ Code 12.8: Pricing an american call on an option on futures using a binomial approximation
129
C++ Code 12.9: Pricing an american call on an option on currency using a binomial approximation
Example
Price a futures currency option with the following information:
time=0.5, number of steps = 100.
C++ program:
double S = 50.0;
double K = 52.0;
double r = 0.08;
double rf=0.05;
double sigma = 0.2; double time=0.5;
int no steps = 100;
cout << " european currency option call = "
<< currency option price call american binomial(S,K,r,rf,sigma,time,no steps)
130
<< endl;
12.8 References
The original source for binomial option pricing was the paper by Cox et al. (1979). Textbook discussions
are in Cox and Rubinstein (1985), Bossaerts and degaard (2001) and Hull (2011).
131
Chapter 13
Finite Differences
Contents
13.1 Explicit Finite differences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
132
132
134
137
137
137
137
140
13.9 References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
141
132
#include <cmath>
#include <vector>
using namespace std;
double option price put european finite diff explicit(const double& S,
const double& X,
const double& r,
const double& sigma,
const double& time,
const int& no S steps,
const int& no t steps) {
double sigma sqr = pow(sigma,2);
int M=no S steps; if ((no S steps%2)==1) { ++M; } // need no S steps to be even:
double delta S = 2.0*S/M;
vector<double> S values(M+1);
for (unsigned m=0;m<=M;m++) { S values[m] = m*delta S; };
int N=no t steps;
double delta t = time/N;
vector<double> a(M);
vector<double> b(M);
vector<double> c(M);
double r1=1.0/(1.0+r*delta t);
double r2=delta t/(1.0+r*delta t);
for (unsigned int j=1;j<M;j++){
a[j] = r2*0.5*j*( r+sigma sqr*j);
b[j] = r1*(1.0 sigma sqr*j*j*delta t);
c[j] = r2*0.5*j*(r+sigma sqr*j);
};
vector<double> f next(M+1);
for (unsigned m=0;m<=M;++m) { f next[m]=max(0.0,X S values[m]); };
double f[M+1];
for (int t=N 1;t>=0;
t) {
f[0]=X;
for (unsigned m=1;m<M;++m) {
f[m]=a[m]*f next[m 1]+b[m]*f next[m]+c[m]*f next[m+1];
};
f[M] = 0;
for (unsigned m=0;m<=M;++m) { f next[m] = f[m]; };
};
return f[M/2];
};
C++ Code 13.1: Explicit finite differences calculation of european put option
133
C++ Code 13.2: Explicit finite differences calculation of american put option
134
Matlab Code 13.1: Explicit finite differences calculation of American put option
135
Example
Given the following parameters:
Price European and American put option prices using finite differences with 20 steps in the
11 steps in the time dimenstion.
S dimension and
C++ program:
double S = 50.0;
double K = 50.0;
double r = 0.1;
double sigma = 0.4;
double time=0.4167;
int no S steps=20;
int no t steps=11;
cout << " explicit finite differences, european put price = ";
cout << option price put european finite diff explicit(S,K,r,sigma,time,no S steps,no t steps)
<< endl;
cout << " explicit finite differences, american put price = ";
cout << option price put american finite diff explicit(S,K,r,sigma,time,no S steps,no t steps)
<< endl;
136
Matlab Code 13.2: Calculation of price of American put using implicit finite differences
137
#include <cmath>
#include "newmat.h" // definitions for newmat matrix library
using namespace NEWMAT;
#include <vector>
#include <algorithm>
using namespace std;
double option price put american finite diff implicit(const double& S,
const double& K,
const double& r,
const double& sigma,
const double& time,
const int& no S steps,
const int& no t steps) {
double sigma sqr = sigma*sigma;
int M=no S steps + (no S steps%2); // need no S steps to be even:
// int M=no S steps; if ((no S steps%2)==1) { ++M; }; // need no S steps to be even:
double delta S = 2.0*S/double(M);
double S values[M+1];
for (int m=0;m<=M;m++) { S values[m] = m*delta S; };
int N=no t steps;
double delta t = time/N;
BandMatrix A(M+1,1,1); A=0.0;
A.element(0,0) = 1.0;
for (int j=1;j<M;++j) {
A.element(j,j 1) = 0.5*j*delta t*(r sigma sqr*j); // a[j]
A.element(j,j) = 1.0 + delta t*(r+sigma sqr*j*j); // b[j];
A.element(j,j+1) = 0.5*j*delta t*( r sigma sqr*j); // c[j];
};
A.element(M,M)=1.0;
ColumnVector B(M+1);
for (int m=0;m<=M;++m){ B.element(m) = max(0.0,K S values[m]); };
ColumnVector F=A.i()*B;
for(int t=N 1;t>0;
t) {
B = F;
F = A.i()*B;
for (int m=1;m<M;++m) { // now check for exercise
F.element(m) = max(F.element(m), K S values[m]);
};
};
return F.element(M/2);
};
C++ Code 13.3: Calculation of price of American put using implicit finite differences with the Newmat
matrix library
138
#include <cmath>
//#include <vector>
//#include <algorithm>
using namespace std;
#include
#include
#include
<itpp/base/vec.h>
<itpp/base/mat.h>
<itpp/base/matfunc.h>
C++ Code 13.4: Calculation of price of American put using implicit finite differences with the IT++ matrix
library
139
C++ Code 13.5: Calculation of price of European put using implicit finite differences
140
Exercise 13.1.
The routines above uses direct multiplication with the inverse of A. Are there alternative, numerically more
stable ways of doing it?
Example
Given the parameters
1. Price European put options using both Black Scholes and implicit finite differences.
2. Price the American put option using implicit finite differences.
C++ program:
double S = 50.0; double K = 50.0;
double r = 0.1;
double sigma = 0.4; double time=0.5;
int no S steps=200; int no t steps=200;
cout << " black scholes put price = " << option price put black scholes(S,K,r,sigma,time)<< endl;
cout << " implicit Euro put price = ";
cout << option price put european finite diff implicit(S,K,r,sigma,time,no S steps,no t steps) << endl;
cout << " implicit American put price = ";
cout << option price put american finite diff implicit(S,K,r,sigma,time,no S steps,no t steps) << endl;
13.9 References
Hull (2011). See the natbib documentation.
141
Chapter 14
143
143
144
146
147
151
152
We now consider using Monte Carlo methods to estimate the price of an European option, and let us
first consider the case of the usual European Call, which can priced by the Black Scholes equation.
Since there is already a closed form solution for this case, it is not really necessary to use simulations,
but we use the case of the standard call for illustrative purposes.
At maturity, a call option is worth
cT = max(0; ST
X)
At an earlier date t, the option value will be the expected present value of this.
ct = E [P V (max(0; ST
X )]
Now, an important simplifying feature of option pricing is the risk neutral result, which implies that
we can treat the (suitably transformed) problem as the decision of a risk neutral decision maker, if we
also modify the expected return of the underlying asset such that this earns the risk free rate.
ct = e r(T t) E [max(0; ST
X )];
where E [] is a transformation of the original expectation. One way to estimate the value of the call is
to simulate a large number of sample values of ST according to the assumed price process, and find the
estimated call price as the average of the simulated values. By appealing to a law of large numbers, this
average will converge to the actual call value, where the rate of convergence will depend on how many
simulations we perform.
142
St+1 = St e(r
1
2
2 )+x~ ;
or more generally, if the current time is t and terminal date is T , with a time between t and T of (T
ST = St e(r
1
2
t),
ct = e r(T t) E [max(0; ST
X )];
note that here one merely need to simulate the terminal price of the underlying, ST , the price of the
underlying at any time between t and T is not relevant for pricing. We proceed by simulating lognormally
distributed random variables, which gives us a set of observations of the terminal price ST . If we let
ST;1 ; ST;2 ; ST;3 ; : : : ST;n denote the n simulated values, we will estimate E [max(0; ST X )] as the average
of option payoffs at maturity, discounted at the risk free rate.
c^t = e r(T t)
n
X
i=1
X)
C++ Code 14.2 shows the implementation of a Monte Carlo estimation of an European call option.
Example
Given S = 100, K = 100, r = 0:1,
Black Scholes and simulation.
= 0:25, time=1.
143
f 0 (x) = hlim
!0
f (x + h) f (x)
h
Thinking of f (S ) as the option price formula ct = f (S ; X; r; ; (T t)), we see that we can evaluate the
option price at two different values of the underlying, S and S + q , where q is a small quantity, and
estimate the option delta as
b =
f (S + q) f (S )
q
144
In the case of Monte Carlo estimation, it is very important that this is done by using the same sequence
of random variables to estimate the two option prices with prices of the underlying S and S + q .
C++ Code 14.3 implements this estimation of the option delta.
#include <cmath> // standard mathematical functions
#include <algorithm> // define the max() function
using namespace std;
#include "normdist.h" // definition of random number generator
double option price delta call european simulated(const double&
const double&
const double&
const double&
const double&
const int& no
double R = (r
0.5 * pow(sigma,2))*time;
double SD = sigma * sqrt(time);
double sum payoffs = 0.0;
double sum payoffs q = 0.0;
double q = S*0.01;
for (int n=1; n<=no sims; n++) {
double Z = random normal();
double S T = S* exp(R + SD * Z);
sum payoffs += max(0.0, S T K);
double S T q = (S+q)* exp(R + SD * Z);
sum payoffs q += max(0.0, S T q K);
};
double c = exp( r*time) * (sum payoffs/no sims);
double c q = exp( r*time) * (sum payoffs q/no sims);
return (c q c)/q;
};
S,
K,
r,
sigma,
time,
sims){
C++ Code 14.3: Estimate Delta of European Call option priced by Monte Carlo
One can estimate other hedge parameters in a simular way.
Example
Given S = 100, K = 100, r = 0:1, = 0:25, time=1. Use 5000 simulations. Calculate deltas of put and call
option using Black Scholes and simulation.
C++ program:
double S=100.0; double K=100.0; double r=0.1; double sigma=0.25;
double time=1.0; int no sims=5000;
cout << " call: bs delta = " << option price delta call black scholes(S,K,r,sigma,time)
<< "
sim delta = " << option price delta call european simulated(S,K,r,sigma,time,no sims)
<< endl;
cout << " put: bs delta = " << option price delta put black scholes(S,K,r,sigma,time)
<< " sim delta = " << option price delta put european simulated(S,K,r,sigma,time,no sims)
<< endl;
145
CT = max(ST
X; 0)
PT = max(X ST ; 0)
C++ Code 14.4 shows two C++ functions which calculates this.
#include <algorithm>
using namespace std;
double payoff call(const double& S,
const double& K){
return max(0.0,S K);
};
double payoff put (const double& S,
const double& K) {
return max(0.0,K S);
};
C++ program:
double S = 100; double K = 100; double r = 0.1;
double sigma = 0.25; double time = 1.0; int no sims = 50000;
cout << "Black Scholes call option price = " << option price call black scholes(S,K,r,sigma,time) << endl;
cout << "Simulated call option price = "
<< derivative price simulate european option generic(S,K,r,sigma,time,payoff call,no sims)
<< endl;
cout << "Black Scholes put option price = " << option price put black scholes(S,K,r,sigma,time) << endl;
cout << "Simulated put option price = "
<< derivative price simulate european option generic(S,K,r,sigma,time,payoff put,no sims)
<< endl;
=
=
=
=
14.9758
14.995
5.45954
5.5599
As we see, even with as many as 50,000 simuations, the option prices estimated using Monte Carlo still
differs substantially from the true values.
p^t = e
r(T t)
n
X
i=1
max (0; X
ST;i )
147
c^t = e
r(T t)
n
X
i=1
X)
cv
We calculate the Black Scholes value of the call c^bs
t , and calculate pt , the estimate of the put price with
a control variate adjustment, as follows
bs c^ )
p^cv
t
t = p^t + (ct
One can use other derivatives than the at-the-money call as the control variate, the only limitation being
that it has a tractable analytical solution.
C++ Code 14.6 shows the implementation of a Monte Carlo estimation using an at-the-money European
call as the control variate.
#include <cmath>
using namespace std;
#include "fin_recipes.h"
double
derivative price simulate european option generic with control variate(const double& S,
const double& X,
const double& r,
const double& sigma,
const double& time,
double payoff(const double& S,
const double& X),
const int& no sims) {
double c bs = option price call black scholes(S,S,r,sigma,time);// price an at the money Black Scholes call
double sum payoffs=0;
double sum payoffs bs=0;
for (int n=0; n<no sims; n++) {
double S T= simulate lognormal random variable(S,r,sigma,time);
sum payoffs += payoff(S T,X);
sum payoffs bs += payoff call(S T,S); // simulate at the money Black Scholes price
};
double c sim = exp( r*time) * (sum payoffs/no sims);
double c bs sim = exp( r*time) * (sum payoffs bs/no sims);
c sim += (c bs c bs sim);
return c sim;
};
148
#include "fin_recipes.h"
#include "normdist.h"
#include <cmath>
using namespace std;
double
derivative price simulate european option generic with antithetic variate(const double& S,
const double& K,
const double& r,
const double& sigma,
const double& time,
double payoff(const double& S,
const double& X),
const int& no sims) {
double R = (r
0.5 * pow(sigma,2) )*time;
double SD = sigma * sqrt(time);
double sum payoffs=0;
for (int n=0; n<no sims; n++) {
double x=random normal();
double S1 = S * exp(R + SD * x);
sum payoffs += payoff(S1,K);
double S2 = S * exp(R + SD * ( x));
sum payoffs += payoff(S2,K);
};
return exp( r*time) * (sum payoffs/(2*no sims));
};
149
Example
Given S = 100, K = 100, r = 0:1, = 0:25, time=1, no sims=50000. Price put and call option using Black
Scholes and simulation. The simulation is to use both control variates and anthitetic methods.
C++ program:
double S = 100; double K = 100; double r = 0.1;
double sigma = 0.25; double time = 1; int no sims = 50000;
cout << "Black Scholes call option price = "
<< option price call black scholes(S,K,r,sigma,time) << endl;
cout << "Simulated call option price = "
<< derivative price simulate european option generic(S,K,r,sigma,time, payoff call,no sims)
<< endl;
cout << "Simulated call option price, CV = "
<< derivative price simulate european option generic with control variate(S,K,r,sigma,time,
payoff call,no sims)
<< endl;
cout << "Simulated call option price, AV = "
<< derivative price simulate european option generic with antithetic variate(S,K,r,sigma,time,
payoff call,no sims)
<< endl;
cout << "Black Scholes put option price = " << option price put black scholes(S,K,r,sigma,time) << endl;
cout << "Simulated put option price = "
<< derivative price simulate european option generic(S,K,r,sigma,time,payoff put,no sims) << endl;
cout << "Simulated put option price, CV = "
<< derivative price simulate european option generic with control variate(S,K,r,sigma,time,
payoff put,no sims)
<< endl;
cout << "Simulated put option price, AV = "
<< derivative price simulate european option generic with antithetic variate(S,K,r,sigma,time,
payoff put,no sims)
<< endl;
=
=
=
=
=
=
=
=
14.9758
14.995
14.9758
14.9919
5.45954
5.41861
5.42541
5.46043
150
151
C++ program:
double S=100.0; double K=100.0; double r=0.1; double sigma=0.25;
double time=1.0; int no sims=5000;
cout << " cash or nothing, Q=1: "
<< derivative price simulate european option generic(S,K,r,sigma,time,
payoff cash or nothing call,
no sims)
<< endl;
cout << " control variate "
<< derivative price simulate european option generic with control variate(S,K,r,sigma,time,
payoff cash or nothing call,
no sims)
<< endl;
cout << " antithetic variate "
<< derivative price simulate european option generic with antithetic variate(S,K,r,sigma,time,
payoff cash or nothing call,
no sims)
<< endl;
cout << " asset or nothing: "
<< derivative price simulate european option generic(S,K,r,sigma,time,
payoff asset or nothing call,
no sims)
<< endl;
cout << " control variate "
<< derivative price simulate european option generic with control variate(S,K,r,sigma,time,
payoff asset or nothing call,
no sims)
<< endl;
cout << " antithetic variate "
<< derivative price simulate european option generic with antithetic variate(S,K,r,sigma,time,
payoff asset or nothing call,
no sims)
<< endl;
14.7 References
Boyle (1977) is a good early source on the use of the Monte Carlo technique for pricing derivatives.
Simulation is also covered in Hull (2011).
152
Chapter 15
153
156
159
162
15.5 Readings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
165
There has been developed some useful approximations to various specific options. It is of course American options that are approximated. We will look at a number of approximations, both for their intrinsic
usefulness, but also as examples of different approaches to generating an approximated value. The first
we will look at is the Johnson (1983) approximation to an American put. This is a purely empirical
approach to estimating the functional forms, with parameters estimated using regressions. The Geske
and Johnson (1984) approach is to view the American option as a the limit of a sequence of Bermudan
options with increasing number of exercise dates. The American value is found by interpolating forward
the Bermudan values. The Barone-Adesi and Whaley (1987) approximation decomposes the American
option into two, the European value, and the early exercise premium. The early exercise premium is
relatively small, and is easier to approximate. Finally, the Bjerksund and Stensland (1993) is an example
of bounding the option value by finding a lower bound on its value. A typical approach to doing so is
to specify some (suboptimal) exercise strategy. As soon as the exercise strategy is known, the option is
easily valued.
Approximations are useful, but they should be used with caution. Typically, an approximation works
well within a range of parameter values, but can go seriously wrong for some unfortunate combination
of parameters. The user is well advised to to consider alternative approximations, and at a minimum
use the price of the corresponding European option as a sanity check in the calculations. (In fact, in
several of the following routines values are checked against Black Scholes values.)
153
P (X ) = p Xer(T t) + (1 )p (X )
r(T t)
=
a0 r(T t) + a1
Sc = X
1+
m
l
where
where
m=
l=
ln(S=Sc )
ln(X=Sc )
2 (T t)
= 2r=2
b0 2 (T t) + b1
The constants
b0 = 1:040803 b1 = 0:00963
S:
(T
X:
:
Volatility of underlying.
r:
Formula 15.1: The functional form of the Johnson (1983) approximation to the value of an American put
C++ program:
double r=0.125; double S=1.1; double X=1;
double sigma=0.5; double time = 1;
cout << " American put price using Johnson approximation = "
<< option price american put approximated johnson(S, X, r, sigma, time)
<< endl;
154
#include <cmath>
#include "fin_recipes.h"
double option price american put approximated johnson( const double& S,
const double& X,
const double& r,
const double& sigma,
const double& time ){
double sigma sqr=pow(sigma,2);
double a0= 3.9649 ;
double a1 = 0.032325;
double b0 = 1.040803;
double b1 = 0.00963;
double gamma = 2*r/sigma sqr;
double m = (sigma sqr*time)/(b0*sigma sqr*time+b1);
double Sc = X * pow (((gamma)/(1+gamma)),m);
double l = (log(S/Sc))/(log(X/Sc) );
double alpha = pow( ( (r*time)/(a0*r*time+a1) ), l );
double P = alpha*option price put black scholes(S,X*exp(r*time),r,sigma,time)
+ (1 alpha)*option price put black scholes(S,X,r,sigma,time);
double p=option price put black scholes(S,X,r,sigma,time); // for safety use the Black Scholes as lower bound
return max(p,P);
};
C++ Code 15.1: The Johnson (1983) approximation to an american put price
155
15.2 An approximation to the American Put due to Geske and Johnson (1984)
Geske and Johnson (1984) develop an approximation for the American put problem. The solution
technique is to view the American put as an sequence of Bermudan options, with the number of exercise
dates increasing. The correct value is the limit of this sequence.
Define Pi to be the price of the put option with i dates of exercise left. P1 is then the price of an
european option, with the one exercise date the expiry date. P2 is the price of an option that can be
exercised twice, once halfway between now and expiry, the other at expiry. Geske-Johnson shows how
these options may be priced, and then develops a sequence of approximate prices that converges to the
correct price. An approximation involving 3 option evaluations is
P^ = P3 + (P3 P2 )
2
1
(P
2 2
P1 )
ln
d1 (q; ) =
S +
q
r + 12 2
p
;
d2 (q; ) = d1
and
In this notation,
P1 = Xe
To calculate
1
13 = p ;
3
1
12 = p ;
2
23 =
2
3
rT N
P2 :
T
T
SN1 d1 S T2 ;
1 d2 S T2 ; 2
2
T
T
; d2 (X; T ); 12
; d1 (X; T ) ; 12
+ Xe rT N2 d2 S T2 ;
SN2 d1 S T2 ;
2
2
P2 = Xe
and
S T2
r T2 N
solves
T
S = X p S; X; ; r; = S T2
2
To calculate
P3 :
T
T
SN1 d1 S T3 ;
1 d2 S T3 ; 3
3
2T
T
2T
+ Xe r 3 N2 d2 S T3 ;
; d2 S 23T ;
; 12
3
3
P3 = Xe
r T3 N
T
2T
SN2 d1 S T3 ;
; d1 S 23T ;
; 12
3
3
T
2T
+ Xe rT N3 d1 S T3 ;
; d1 S 23T ;
; d1 (X; T ); 12 ; 13 ; 23
3
3
T
2T
; d2 S 23T ;
; d2 (X; T ); 12 ; 13 ; 23
SN3 d2 S T3 ;
3
3
156
2T
S = X P2 S; X; ; r; = S T=3
3
T
S = X p S; X; ; r; = S 2T=3
3
The main issue in implementation of this formula (besides keeping the notation straight) is the evalutation of P3 , since it involves the evaluation of a trivariate normal cumulative distribution. The evaluation
of N3 () is described later, since it involves calls to external routines for numerical intergration.
To solve for
T
S = X p S; X; ; r; = S T2
2
T
g(S ) = S X + p S; X; ; r;
Iterate on
Si = Si
until
g
g0
157
#include <cmath>
#include "normdist.h"
#include "fin_recipes.h"
#include <iostream>
const double ACCURACY=1e 6;
inline double d1(const double& S,const double& X, const double& r, const double& sigma, const double& tau){
return (log(S/X) + (r+0.5*pow(sigma,2))*tau)/(sigma*sqrt(tau));
};
inline double d2(const double& S, const double& X, const double& r, const double& sigma, const double& tau){
return d1(S,X,r,sigma,tau) sigma*sqrt(tau);
};
inline double calcP2(const double& S, const double& X, const double& r, const double& sigma, const double& time,
const double& t2, const double& S2 bar, const double& rho12){
double P2 = X*exp( r*t2)*N( d2(S,S2 bar,r,sigma,t2));
P2 = S*N( d1(S,S2 bar,r,sigma,t2));
P2 += X*exp( r*time)*N(d2(S,S2 bar,r,sigma,t2), d2(S,X,r,sigma,time), rho12);
P2 = S*N(d1(S,S2 bar,r,sigma,t2), d1(S,X,r,sigma,time), rho12);
return P2;
};
double option price american put approximated geske johnson( const double& S, const double& X, const double& r,
const double& sigma, const double& time ){
double P1 = option price put black scholes(S,X,r,sigma,time);
double rho12=1.0/sqrt(2.0); double rho13=1.0/sqrt(3.0); double rho23=sqrt(2.0/3.0);
double t2 = time/2.0; double t23 = time*2.0/3.0; double t3 = time/3.0;
double Si=S;
double S2 bar=S;
double g=1;
double gprime=1;
while (fabs(g)>ACCURACY){
g=Si X+option price put black scholes(Si,X,r,sigma,t2);
gprime=1.0+option price delta put black scholes(Si,X,r,sigma,t2);
S2 bar=Si;
Si=Si g/gprime;
};
double P2 = calcP2(S,X,r,sigma,time,t2,S2 bar,rho12);
P2=max(P1,P2); // for safety, use one less step as lower bound
double S23 bar=S2 bar;
g=1;
while (fabs(g)>ACCURACY){
g=Si X+option price put black scholes(Si,X,r,sigma,t23);
gprime=1.0+option price delta put black scholes(Si,X,r,sigma,t23);
S23 bar=Si;
Si=Si g/gprime;
};
double S3 bar=S23 bar;
g=1;
while (fabs(g)>ACCURACY){
g=Si X+option price put black scholes(Si,X,r,sigma,t3);
gprime=1.0+option price delta put black scholes(Si,X,r,sigma,t3);
S3 bar=Si;
Si=Si g/gprime;
};
double P3 = X * exp( r*t3) * N( d2(S,S3 bar,r,sigma,t3));
P3 = S * N( d1(S,S3 bar,r,sigma,t3));
P3 += X*exp( r*time)*N(d2(S,S3 bar,r,sigma,t3), d2(S,S23 bar,r,sigma,t23), rho12);
P3 = S*N(d1(S,S3 bar,r,sigma,t3), d1(S,S23 bar,r,sigma,t23), rho12);
P3 += X*exp( r*t23)*N3(d1(S,S3 bar,r,sigma,t3),d1(S,S23 bar,r,sigma,t23), d1(S,X,r,sigma,time),rho12, rho13, rho23);
P3 = S*N3(d2(S,S3 bar,r,sigma,t3),d2(S,S23 bar,r,sigma,t23), d2(S,X,r,sigma,time),rho12, rho13, rho23);
P3=max(P2,P3); // for safety, use one less step as lower bound
return P3+3.5*(P3 P2) 0.5*(P2 P1);
};
1 2 2
S VS S + bSVS
2
rV + Vt = 0
(15.1)
Here V is the (unknown) formula that determines the price of the contingent claim. For an European
option the value of V has a known solution, the adjusted Black Scholes formula. For American options,
which may be exercised early, there is no known analytical solution.
To do their approximation, BAW decomposes the American price into the European price and the early
exercise premium
C (S; T ) =
c(S; T ) + A2
S X
where
1
q2 =
2
S q2
S
(N
if
if
S < S
S S
4M
1)2 +
K
1) + (N
S
A2 =
1 e(b r)(T t) N (d1 (S ))
q2
2r
2b
M = 2 ; N = 2 ; K (T ) = 1 e r(T t)
and
S solves
S X = c (S ; T ) +
Notation:
Stock price.
X:
S
1 e(b r)(T t) N (d1 (S ))
q2
Exercise price.
Formula 15.2: The functional form of the Barone Adesi Whaley approximation to the value of an American
call
In implementing this formula, the only problem is finding the critical value
problem of finding a root of the equation
g(S ) = S X c(S )
1 The
S
1 e(b r)(T t) N (d1 (S )) = 0
q2
159
S.
This is solved using Newtons algorithm for finding the root. We start by finding a first seed value
The next estimate of Si is found by:
S0 .
g()
g0
Si+1 = Si
g(S ) = S X c(S )
g0 (S ) = (1
q2
q2
1
) 1 e(b r)(T t) N (d1 ) + (e(b r)(T t) n(d1 ))
q2
p1
T
where c(S ) is the Black Scholes value for commodities. Code 15.3 shows the implementation of this
formula for the price of a call option.
Example
Consider the following set of parameters, used as an example in the Barone-Adesi and Whaley (1987) paper:
S = 100, X = 100, = 0:20, r = 0:08, b = 0:04.
1. Price a call option with time to maturity of 3 months.
C++ program:
double S = 100; double X = 100; double sigma = 0.20;
double r = 0.08; double b = 0.04; double time = 0.25;
cout << " Call price using Barone-Adesi Whaley approximation = "
<< option price american call approximated baw(S,X,r,b,sigma,time)
<< endl;
P (S ) = p(S; T ) + A1
X S
A1 =
S q1
S
if
if
S > S
S S
S
(1 e(b r)(T t) N ( d1 (S ))
q1
g(S ) = X S p(S ) +
g 0 (S ) = (
q1
1
1
1) 1 e(b r)(T t) N ( d1 ) + e(b r)(T t) p
q1
T
n( d 1 )
1. Implement the calculation of the price of an American put option using the BAW approach.
160
#include <cmath>
#include <algorithm>
using namespace std;
#include "normdist.h"
#include "fin_recipes.h"
// normal distribution
// define other option pricing formulas
C++ Code 15.3: Barone Adesi quadratic approximation to the price of a call option
161
P (S; X; T; r; b; ) = C (X; S; T; r b; b; )
162
C (S; K; T; r; b; ; X ) = (X )S
(X ) = (X K )X
1
=
2
b
+
2
'(S; T j
; H; X ) =
=
s
1 2
r
+2 2
2
b
2
e S
N (d1 )
ln(X 2 =SH ) + b + (
T
N (d2 )
1 )2 T
2
2b
= 2 + (2
1)
XT = B0 + B1 B0 )(1 eh(T )
1 2
2 ) T
T
h(T ) =
1) 2 T
ln(S=H ) + b + (
d2 =
X
S
1
r +
b +
(
2
d1 =
bT + 2 T
B0
B1 B0
h(T ) =
B1 =
bT + 2 T
B1 B0
K
1
r
B0 = max K;
K
r b
S:
K2
K:
Exercise price.
Formula 15.3: The Bjerksund and Stensland (1993) lower bound approximation to the value of an American Call
163
#include "fin_recipes.h"
#include <cmath>
#include "normdist.h"
inline double phi(double S, double T, double gamma, double H, double X, double r, double b, double sigma){
double sigma sqr=pow(sigma,2);
double kappa = 2.0*b/sigma sqr + 2.0*gamma
1.0;
double lambda = ( r + gamma * b + 0.5*gamma*(gamma 1.0)*sigma sqr)*T; // check this, says lambda in text
double d1=
(log(S/H)+(b+(gamma 0.5)*sigma sqr)*T)/(sigma*sqrt(T));
double d2=
(log((X*X)/(S*H))+(b+(gamma 0.5)*sigma sqr)*T)/(sigma*sqrt(T));
double phi = exp(lambda) * pow(S,gamma) * (N(d1)
pow((X/S),kappa) * N(d2));
return phi;
};
double option price american call approximated bjerksund stensland( const double& S,
const double& K,
const double& r,
const double& b,
const double& sigma,
const double& T ){
double sigma sqr=pow(sigma,2);
double B0=max(K,(r/(r b)*K));
double beta = (0.5
b/sigma sqr) + sqrt( pow((b/sigma sqr 0.5),2) + 2.0 * r/sigma sqr);
double Binf = beta/(beta 1.0)*K;
double hT=
(b*T + 2.0*sigma*sqrt(T))*((K*K)/(Binf B0));
double XT = B0+(Binf B0)*(1.0 exp(hT));
double alpha = (XT K)*pow(XT, beta);
double C=alpha*pow(S,beta);
C = alpha*phi(S,T,beta,XT,XT,r,b,sigma);
C += phi(S,T,1,XT,XT,r,b,sigma);
C = phi(S,T,1,K,XT,r,b,sigma) ;
C = K*phi(S,T,0,XT,XT,r,b,sigma);
C += K*phi(S,T,0,K,XT,r,b,sigma);
double c=option price european call payout(S,K,r,b,sigma,T); // for safety use the Black Scholes as lower bound
return max(c,C);
};
C++ Code 15.4: Approximation of American Call due to Bjerksund and Stensland (1993)
#include "fin_recipes.h"
double option price american put approximated bjerksund stensland( const double& S,
const double& X,
const double& r,
const double& q,
const double& sigma,
const double& T ){
return option price american call approximated bjerksund stensland(X,S,r (r q),r q,sigma,T);
};
C++ Code 15.5: Approximation of American put due to Bjerksund and Stensland (1993)
164
Exercise 15.2.
An option is At the Money Forward if the forward price
1. Show that a reasonable approximation to the Black Scholes value of a call option on a non-dividend
paying asset that is At the Money Forward is
C = 0:4 T
tP V (F )
15.5 Readings
See Broadie and Detemple (1996) for some comparisions of various approximations. A survey of the
pricing of options, including American options, is Broadie and Detemple (2004). Barone-Adesi (2005)
summarizes the history of approximating the American put.
165
Chapter 16
166
169
170
172
175
176
We now look at a type of options that has received a lot of attention in later years. The distinguishing
factor of these options is that they depend on the whole price path of the underlying security between
today and the option maturity.
166
C++ program:
double S=80;
double K=100;
double r = 0.20;
double time = 1.0; double sigma = 0.25;
int steps = 500;
double q=0.0;
vector<double> potential exercise times; potential exercise times.push back(0.25);
potential exercise times.push back(0.5); potential exercise times.push back(0.75);
cout << " Bermudan put price = "
<< option price put bermudan binomial(S,K,r,q,sigma,time,potential exercise times,steps)
<< endl;
167
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
168
CT = max(0; S X );
where
CT = max(0; ST
S)
There are different types of Asians depending on how the average S is calculated. For the case of S being
lognormal and the average S being a geometric average, there is an analytic formula due to Kemna and
Vorst (1990). Hull (2011) also discusses this case. It turns out p
that one can calculate this option using
the regular Black Scholes formula adjusting the volatility to = 3 and the dividend yield to
1
1
r + q + 2
2
6
C++ Code 16.2: Analytical price of an Asian geometric average price call
Example
Relevant parameters: S = 100, K = 100,
an Asian geometric average price call.
C++ program:
double S=100; double K=100; double q=0;
double r=0.06; double sigma=0.25; double time=1.0;
cout << " Analytical geometric average = "
<< option price asian geometric average price call(S,K,r,q,sigma,time)
<< endl;
Price
CT = max(0; ST
min S )
2[t;T ]
For this particular option an analytical solution has been found, due to Goldman, Sosin, and Gatto
(1979), which is shown in Formula 16.1 and implemented in C++ Code 16.3.
2
C = Se q(T t) N (a1 ) Se q(T t)
N ( a1 ) Smin e r(T t) N (a2 )
2(r q )
a1 =
ln
S
Smin
q + 12 2 )(T
p
T t
+ (r
a2 = a1 T
a3 =
Y1 =
ln
2 r
S
Smin
2 Y1
e N ( a3)
2(r q )
t)
t
+
r + q + 12 2 (T
p
T t
1 2 ln
2
2
t)
Smin
#include <cmath>
using namespace std;
#include "normdist.h"
double option price european lookback call(const double& S,
const double& Smin,
const double& r,
const double& q,
const double& sigma,
const double& time){
if (r==q) return 0;
double sigma sqr=sigma*sigma;
double time sqrt = sqrt(time);
double a1 = (log(S/Smin) + (r q+sigma sqr/2.0)*time)/(sigma*time sqrt);
double a2 = a1 sigma*time sqrt;
double a3 = (log(S/Smin) + ( r+q+sigma sqr/2.0)*time)/(sigma*time sqrt);
double Y1 = 2.0 * (r q sigma sqr/2.0)*log(S/Smin)/sigma sqr;
return S * exp( q*time)*N(a1) S*exp( q*time)*(sigma sqr/(2.0*(r q)))*N( a1)
Smin * exp( r*time)*(N(a2) (sigma sqr/(2*(r q)))*exp(Y1)*N( a3));
};
S = 100, Smin = S q = 0, r = 0:06, = 0:346, time = 1.0, Price an European lookback call.
170
C++ program:
double S=100; double Smin=S; double q = 0; double r = 0.06;
double sigma = 0.346; double time = 1.0;
cout << " Lookback call price = "
<< option price european lookback call(S,Smin,r,q,sigma,time)
171
<< endl;
16.4 Monte Carlo Pricing of options whose payoff depend on the whole price
path
Monte Carlo simulation can be used to price a lot of different options. The limitation is that the options
should be European. American options can not be priced by simulation methods. There is (at least) two
reasons for this. First, the optimal exercise policy would have to be known. But if the exercise policy
was known there would be an analytical solution. Second, approximations using Monte Carlo relies on
a law of large numbers. But if some of the sample paths were removed, the LLN would be invalidated.
In chapter 14 we looked at a general simulation case where we wrote a generic routine which we passed
a payoff function to, and the payoff function was all that was necessary to define an option value. The
payoff function in that case was a function of the terminal price of the underlying security. The only
difference to the previous case is that we now have to generate a price sequence and write the terminal
payoff of the derivative in terms of that, instead of just generating the terminal value of the underlying
security from the lognormal assumption.
ST = St e(r
1
2
t =
T.
t + t t + 2t t + 3t
Time
St+t = St e(r
1
2
2 )+
ptx~
C++ Code 16.4 shows how one would simulate a sequence of lognormally distributed variables.
This code is then used in the generic routine to do calculations, as shown in C++ Code 16.5.
To price an option we are then only in need of a definition of a payoff function. We consider a couple of
examples. One is the case of an Asian option, shown in C++ Code 16.6.2
Another is the payoff for a lookback, shown in C++ Code 16.7.3
2 Note
3 Note
the use of the accumulate() function, which is part of the C++ standard.
the use of the min_element() and max_element functions, which are part of the C++ standard.
172
#include <cmath>
#include <vector>
using namespace std;
#include "normdist.h"
vector<double>
simulate lognormally distributed sequence(const double& S,
const double& r,
const double& sigma,
const double& time, // time to final date
const int& no steps){ // number of steps
vector<double> prices(no steps);
double delta t = time/no steps;
double R = (r 0.5*pow(sigma,2))*delta t;
double SD = sigma * sqrt(delta t);
double S t = S;
// initialize at current price
for (int i=0; i<no steps; ++i) {
S t = S t * exp(R + SD * random normal());
prices[i]=S t;
};
return prices;
};
#include <cmath>
using namespace std;
#include "fin_recipes.h"
double
derivative price simulate european option generic(const double& S,
const double& K,
const double& r,
const double& sigma,
const double& time,
double payoff(const vector<double>& prices, const double& X),
const int& no steps,
const int& no sims) {
double sum payoffs=0;
for (int n=0; n<no sims; n++) {
vector<double>prices = simulate lognormally distributed sequence(S,r,sigma,time,no steps);
sum payoffs += payoff(prices,K);
};
return exp( r*time) * (sum payoffs/no sims);
};
173
#include <cmath>
#include <numeric>
#include <vector>
using namespace std;
double payoff arithmetric average call(const vector<double>& prices, const double& K) {
double sum=accumulate(prices.begin(), prices.end(),0.0);
double avg = sum/double(prices.size());
return max(0.0,avg K);
};
double payoff geometric average call(const vector<double>& prices, const double& K) {
double logsum=log(prices[0]);
for (unsigned i=1;i<prices.size();++i){ logsum+=log(prices[i]); };
double avg = exp(logsum/double(prices.size()));
return max(0.0,avg K);
};
#include <vector>
#include <algorithm>
using namespace std;
double payoff lookback call(const vector<double>& prices, const double& unused variable) {
double m = *min element(prices.begin(),prices.end());
return prices.back() m; // always positive or zero
};
double payoff lookback put(const vector<double>& prices, const double& unused variable) {
double m = *max element(prices.begin(),prices.end());
return m prices.back(); // max is always larger or equal.
};
174
#include "fin_recipes.h"
#include <cmath>
using namespace std;
double
derivative price simulate european option generic with control variate(const double& S,
const double& K,
const double& r,
const double& sigma,
const double& time,
double payoff(const vector<double>& prices,
const double& X),
const int& no steps,
const int& no sims) {
double c bs = option price call black scholes(S,S,r,sigma,time);// price an at the money Black Scholes call
double sum payoffs=0;
double sum payoffs bs=0;
for (int n=0; n<no sims; n++) {
vector<double> prices = simulate lognormally distributed sequence(S,r,sigma,time, no steps);
double S1= prices.back();
sum payoffs += payoff(prices,K);
sum payoffs bs += payoff call(S1,S); // simulate at the money Black Scholes price
};
double c sim = exp( r*time) * (sum payoffs/no sims);
double c bs sim = exp( r*time) * (sum payoffs bs/no sims);
c sim += (c bs c bs sim);
return c sim;
};
175
C++ program:
cout << "Testing general simulation of European options " << endl;
double S=100; double K=120; double r = 0.10;
double time = 1.0; double sigma = 0.25; int no sims = 10000; int no steps = 250;
double q=0;
cout << " simulated arithmetric average "
<< " S= " << S << " r= " << r << " price="
<< derivative price simulate european option generic(S,K,r,sigma,time,
payoff arithmetric average call,
no steps,no sims)
<< endl;
cout << " simulated geometric average = "
<< derivative price simulate european option generic(S,K,r,sigma,time,
payoff geometric average call,
no steps,no sims)
<< endl;
cout << " analytical lookback put = "
<< option price european lookback put(S,S,r,q,sigma,time)
<< endl;
cout << " simulated lookback put = "
<< derivative price simulate european option generic(S,0,r,sigma,time,
payoff lookback put,
no steps,no sims)
<< endl;
cout << " analytical lookback call = "
<< option price european lookback call(S,S,r,q,sigma,time)
<< endl;
cout << " simulated lookback call = "
<< derivative price simulate european option generic(S,0,r,sigma,time,
payoff lookback call,
no steps,no sims)
<< endl;
cout << " simulated lookback call using control variates = "
<< derivative price simulate european option generic with control variate(S,0,r,sigma,time,
payoff lookback call,
no steps,no sims)
<< endl;
16.6 References
Exotic options are covered in Hull (2011). Rubinstein (1993) has an extensive discussion of analytical
solutions to various exotic options. Gray and Gray (2001) also looks at some analytical solutions.
176
Chapter 17
177
182
17.1 Introduction
In earlier chapters we have seen a large number of different versions of the binomial pricing formula. In
this chapter we see how we can build a framework for binomial pricing that lets us write a single generic
routine that can be used for a binomial approximation of all sorts of derivatives. The important feature
that lets us write such a generic routine is that the only place the terms of the derivative appears in
the calculation is the calculation of the value at each node. Consider the binomial approximation of an
American call in Chapter 12s C++ Code 12.2, repeated below as C++ Code 17.1 for convenience.
The terms of the derivative only appears when calculating the value at the nodes, where the calculation
max(prices[]-K,0) appears. The key to building a generic binomial routine lies in replacing this
calculation with a generic routine. C++ Code 17.2 shows how the generic ruotine is implemented.
177
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
double option price call american binomial( const double& S, // spot price
const double& X,
// exercice price
const double& r,
// interest rate
const double& sigma, // volatility
const double& t,
// time to maturity
const int& steps) { // no steps in binomial tree
double R = exp(r*(t/steps));
// interest rate for each step
double Rinv = 1.0/R;
// inverse of interest rate
double u = exp(sigma*sqrt(t/steps)); // up movement
double d = 1.0/u;
double p up = (R d)/(u d);
double p down = 1.0 p up;
vector<double> prices(steps+1); // price of underlying
prices[0] = S*pow(d, steps); // fill in the endnodes.
double uu = u*u;
for (int i=1; i<=steps; ++i) prices[i] = uu*prices[i 1];
vector<double> call values(steps+1); // value of corresponding call
for (int i=0; i<=steps; ++i) call values[i] = max(0.0, (prices[i] X)); // call payoffs at maturity
for (int step=steps 1; step>=0;
step) {
for (int i=0; i<=step; ++i) {
call values[i] = (p up*call values[i+1]+p down*call values[i])*Rinv;
prices[i] = d*prices[i+1];
// check for exercise
call values[i] = max(call values[i],prices[i] X);
};
};
return call values[0];
};
178
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
179
Using this routine is then merely a matter of providing a definition of the derivative payoff. C++ Code 17.3
shows how the payoffs are defined for standard put and call options. Pricing American put and call
options is then merely a matter of supplying these payoff definitions to the generic binomial routine.
#include <algorithm>
using namespace std;
double payoff call(const double& S, const double& K){
return max(0.0,S K);
};
double payoff put (const double& S, const double& K) {
return max(0.0,K S);
};
C++ Code 17.3: Payoff definitions for put and call options
Example
Consider American call and put option on non-dividend paying stock, where
(T t) = 1 and r = 0:1.
180
181
182
Chapter 18
Trinomial trees
Contents
18.1 Intro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
18.2 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
18.3 Further reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
183
183
185
18.1 Intro
A trinomial tree is similar to a binomial tree, just with one more branch. At each point of time there
are three possible future values of the underlying S .
S0
*
HH
H
HH
H
H
j
H
Su = uS0
Sm = S0
Sd = dS0
18.2 Implementation
A trinomial tree can be implemented with various parameterizations. We will show the calulation using
the following parameterization:
p
u = e 3t
e=
pu =
pm =
2
1
6
2
3
pu =
t
r
12 2
t
r
12 2
2
2
1
6
183
We calculate the option price using the usual roll back procedure with the continuation value
e rt (pu fu + pm fm + pd fd )
#include <vector>
#include <cmath>
using namespace std;
double option price put american trinomial( const double& S,
const double& K,
const double& r,
const double& q,
const double& sigma,
const double& t,
const int& steps) {
double delta t = t/steps;
double Rinv = exp( r*(delta t));
double sigma sqr=pow(sigma,2);
double
double
double
double
double
u
d
p
p
p
= exp(sigma*sqrt(3.0*delta t));
= 1.0/u;
u = 1.0/6.0 + sqrt(delta t/(12.0*sigma sqr)) * (r q 0.5*sigma sqr);
m = 2.0/3.0;
d = 1.0/6.0
sqrt(delta t/(12.0*sigma sqr)) * (r q 0.5*sigma sqr);
184
<< endl;
185
Chapter 19
186
188
A large number of alternative formulations to the Black Scholes analysis has been proposed. Few of
them have seen any widespread use, but we will look at some of these alternatives.
c=
1 e0 (0 )n
X
n!
n=0
t)
where
=T
0 = (1 + )
CBS () is the Black Scholes formula, and
n 2
n2 = 2 +
n ln(1 + )
rn = r +
Formula 19.1: The option pricing formula of the Merton (1976) model
In implementing this formula, we need to terminate the infinite sum at some point. But since the
186
factorial function is growing at a much higher rate than any other, that is no problem, terminating at
nabout n = 50 should be on the conservative side. To avoid numerical difficulties, use the following
method for calculation of
e (0 )n
e (0 )n
= exp ln
n!
n!
!!
= exp
0 + n ln(0 )
n
X
i=1
ln i
#include <cmath>
#include "fin_recipes.h"
double option price call merton jump diffusion( const double& S,
const double& X,
const double& r,
const double& sigma,
const double& time to maturity,
const double& lambda,
const double& kappa,
const double& delta) {
const int MAXN=50;
double tau=time to maturity;
double sigma sqr = sigma*sigma;
double delta sqr = delta*delta;
double lambdaprime = lambda * (1+kappa);
double gamma = log(1+kappa);
double c = exp( lambdaprime*tau)*option price call black scholes(S,X,r lambda*kappa,sigma,tau);
double log n = 0;
for (int n=1;n<=MAXN; ++n) {
log n += log(double(n));
double sigma n = sqrt( sigma sqr+n*delta sqr/tau );
double r n = r lambda*kappa+n*gamma/tau;
c += exp( lambdaprime*tau+n*log(lambdaprime*tau) log n)*
option price call black scholes(S,X,r n,sigma n,tau);
};
return c;
};
187
v the volatility.
dSp(t) = Sdt p
+ v (t)Sdz1 (t)
d v(t) = v(t)dt + v(t)Sdz2 (t)
K.
1 1 1
e
Pj (x; v; T; ln(K )) = +
Re
2 0
Z
i ln(K ) f
j (x; v; T; )
i
d
fj (x; v; T; ) = eC (;)+D(;)v+ix
a
C (; ) = ri + 2 (bj
D(; ) =
1
2
u1 = ;
bj
i + d)
i + d 1 ed
2
1 ged
u2 =
2 ln
1 ged
1 g
1
2
a =
b1 = + b2 = +
x = ln(S )
g=
bj
bj
i + d
i d
d = (i bj )2 2 (2uj i 2 )
Notation:
S:
K:
exercise price.
The implementation of this pricing formula has some instructive C++ features. First, it illustrates calculations of of complex variables. Complex numbers is part of the C++ standard, and are accessed by
including the
#include <complex>
188
statement. Complex numbers are templated, it is necessary to specify what type of floating point type
to use, such as complex<double> or complex<double double>.
To evaluate the price it is also necessary to do a numerical integration. In the calculation this is solved
by a call to an external routine. We use a routine provided by the Gnu GSL project.1
Example
Given the following set of parameters: S = 100, K = 100, r = 0:01, v
= 0:01 and = 0:01, price a call option using the Heston formula
= 0:01, = 0:5, = 0, = 2, = 0,
C++ program:
double S=100; double K=100; double r=0.01; double v=0.01;
double tau=0.5; double rho=0; double kappa=2; double lambda=0.0;
double theta=0.01; double sigma=0.01;
cout << "heston call price "
<< heston call option price( S, K, r, v, tau, rho, kappa, lambda, theta, sigma)
1 An
189
<< endl;
#include <iostream>
#include <cmath>
#include <complex>
using namespace std;
#include "gsl/gsl_integration.h"
struct heston parms {double K; double x; double r; double v; double tau; double kappa; double theta;
double rho; double sigma; double lambda; int j;};
extern "C"{
double heston integrand j(double phi, void *p){
struct heston parms* parms = (struct heston parms*)p;
double K = (parms >K); double x = (parms >x);
double v = (parms >v); double r = (parms >r);
double kappa = (parms >kappa);
double theta = (parms >theta);
double rho = (parms >rho);
double sigma = (parms >sigma);
double lambda = (parms >lambda);
double tau = (parms >tau);
double j = (parms >j);
double sigma sqr = pow(sigma,2);
double uj;
double bj;
if (j==1){ uj=0.5; bj=kappa+lambda rho*sigma; }
else {
uj= 0.5; bj=kappa+lambda; };
complex <double> i(0,1);
double a = kappa*theta;
complex<double> d = sqrt( pow(rho*sigma*phi*i bj,2)
sigma sqr*(2*uj*phi*i pow(phi,2)) );
complex<double> g = (bj
rho*sigma*phi*i+d)/(bj rho*sigma*phi*i d);
complex<double> C = r*phi*i*tau+(a/sigma sqr)*((bj rho*sigma*phi*i+d)*tau 2.0*log((1.0 g*exp(d*tau))/(1.0 g)));
complex<double> D = (bj rho*sigma*phi*i+d)/sigma sqr * ( (1.0 exp(d*tau))/(1.0 g*exp(d*tau)) );
complex<double> f1 = exp(C+D*v+i*phi*x);
complex<double> F = exp( phi*i*log(K))*f1/(i*phi);
return real(F);
};};
inline double heston Pj(double S, double K, double r, double v, double tau, double sigma,
double kappa, double lambda, double rho, double theta, int j){
double x=log(S);
struct heston parms parms = { K, x, r, v, tau, kappa, theta, rho, sigma, lambda, j};
size t n=10000;
gsl integration workspace* w = gsl integration workspace alloc(n);
gsl function F;
F.function = &heston integrand j;
F.params=&parms;
double result, error;
gsl integration qagiu(&F,0,1e 7,1e 7,n,w,&result,&error); // integral to infinity starting at zero
return 0.5 + result/M PI;
};
double heston call option price(const double& S, const double& K, const double& r, const double& v,
const double& tau, const double& rho, const double& kappa,
const double& lambda, const double& theta, const double& sigma){
double P1 = heston Pj(S,K,r,v,tau,sigma,kappa,lambda,rho,theta,1);
double P2 = heston Pj(S,K,r,v,tau,sigma,kappa,lambda,rho,theta,2);
double C=S*P1 K*exp( r*tau)*P2;
return C;
};
C++ Code 19.2: Hestons pricing formula for a stochastic volatility model
190
Chapter 20
191
193
The area of fixed income securities is one where a lot of work is being done in creating advanced
mathematical models for pricing of financial securities, in particular fixed income derivatives. The focus
of the modelling in this area is on modelling the term structure of interest rates and its evolution over
time, which is then used to price both bonds and fixed income derivatives. However, in some cases one
does not need the machinery of term structure modelling which well look at in later chapters, and price
derivatives by modelling the evolution of the bond price directly.
Specifically, suppose that the price of a Bond follows a Geometric Brownian Motion process, just like the
case we have studied before. This is not a particularly realistic assumption for the long term behaviour
of bond prices, since any bond price converges to the bond face value at the maturity of the bond. The
Geometric Brownian motion may be OK for the case of short term options on long term bonds.
C++ Code 20.1: Black scholes price for European put option on zero coupon bond
191
#include <cmath>
#include <vector>
using namespace std;
#include "normdist.h"
#include "fin_recipes.h"
double bond option price put coupon bond black scholes( const double& B,
const double& X,
const double& r,
const double& sigma,
const double& time,
const vector<double> coupon times,
const vector<double> coupon amounts){
double adjusted B=B;
for (unsigned int i=0;i<coupon times.size();i++) {
if (coupon times[i]<=time) {
adjusted B = coupon amounts[i] * exp( r*coupon times[i]);
};
};
return bond option price put zero black scholes(adjusted B,X,r,sigma,time);
};
C++ Code 20.2: Black scholes price for European put option on coupon bond
192
double bond option price put american binomial( const double& B, // Bond price
const double& K, // exercise price
const double& r,
// interest rate
const double& sigma, // volatility
const double& t,
// time to maturity
const int& steps){ // no steps in binomial tree
double R = exp(r*(t/steps));
// interest rate for each step
double Rinv = 1.0/R;
// inverse of interest rate
double u = exp(sigma*sqrt(t/steps)); // up movement
double uu = u*u;
double d = 1.0/u;
double p up = (R d)/(u d);
double p down = 1.0 p up;
vector<double> prices(steps+1); // price of underlying
vector<double> put values(steps+1); // value of corresponding put
prices[0] = B*pow(d, steps); // fill in the endnodes.
for (int i=1; i<=steps; ++i) prices[i] = uu*prices[i 1];
for (int i=0; i<=steps; ++i) put values[i] = max(0.0, (K prices[i])); // put payoffs at maturity
for (int step=steps 1; step>=0;
step) {
for (int i=0; i<=step; ++i) {
put values[i] = (p up*put values[i+1]+p down*put values[i])*Rinv;
prices[i] = d*prices[i+1];
put values[i] = max(put values[i],(K prices[i])); // check for exercise
};
};
return put values[0];
};
C++ Code 20.3: Binomial approximation to american put bond option price
193
Example
Parameters:
There is also a coupon bond with the the same bond price, but paying coupon of
0:5 at date 1.
1. Price a an European put option on the zero coupon bond using Black Scholes.
2. Price a an European put option on the coupon coupon bond using Black Scholes.
3. Price a an European put option on the zero coupon bond using binomial approximation with 100 steps.
C++ program:
double B=100;
double K=100;
double r=0.05;
double sigma=0.1;
double time=1;
cout << " zero coupon put option price = "
<< bond option price put zero black scholes(B,K,r,sigma,time)
<< endl;
<< endl;
194
Chapter 21
Credit risk
Contents
21.1 The Merton Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
21.2 Issues in implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
195
196
Assuming the firm value V follows the usual Brownian motion proces, debt is found as a closed form
solution, similar in structure to the Black Scholes equation for a call option.
Easiest seen from the interpretation of firm debt as the price of risk free debt, minus the value of a put
option.
Price debt by the price
Scholes formula.
of risk free debt, and then subtract the price of the put, using the Black
d1 =
ln
S
K + (r +
1
p 2 )(T t)
T
d2 = d1 T
N () =
p = Ke r(T t) N ( d2 ) SN ( d1 )
In the context here, reinterpret
S as V , firm value.
p = Ke r(T t) N ( d2 ) Vt N ( d1 )
195
where
d1 =
ln
Vt + (r +
K
1
p 2 )(T t)
T
Note on interpretation: The spread between risky and risk free debt determined solely by the price of
the put option.
Example
The current value of the firm V = 100. The firm has issued one bond with face value 90, which is due to
be paid one year from now. The risk free interest rate is 5% and the volatility of the firms value is 25%.
Determine the value of the debt.
C++ program:
cout << " Credit Risk Calculation " << endl;
double V=100; double F=90; double r=0.05; double T=1; double sigma=0.25;
double p = option price put black scholes(V,F,r,sigma,T);
cout << " Debt value = " << exp( r*T)*F
p << endl;
196
Chapter 22
198
200
202
205
22.5 Vasicek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
22.6 Readings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
208
210
We now expand on the analysis of the term structure in chapter 5. As shown there, the term structure
is best viewed as an abstract class providing, as functions of term to maturity, the prices of zero coupon
bonds (discount factors), yield on zero coupon bonds (spot rates) or forward rates. In the earlier case
we considered two particular implementations of the term structure: A flat term structure or a term
structure estimated by linear interpolations of spot rates. We now consider a number of alternative term
structure models. The focus of this chapter is empirical, we consider ways in which on one can specify a
term structure in a lower dimensional way. Essentially we are looking at ways of doing curve-fitting, of
estimating a nonlinear relationship between time and discount factors, or between time and spot rates.
Since the relationship is nonlinear, this is a nontrivial problem. One has to choose a functional form to
estimate, which allows enough flexibility to fit the term structure, but not so flexible that it violates
the economic restrictions on the term structure. Here are some considerations.
> 0).
Again, this is to
Both discount factors and interest rates must be smooth functions of time.
The value of a payment today is the payment today.
d0 = 1.
A number of alternative ways of estimating the term structure has been considered. Some are purely
used as interpolation functions, while others are fully specified, dynamic term structure models. Of
the models that follow, the approximating function proposed in Nelson and Siegel (1987) and the cubic
spline used by e.g. McCulloch (1971) are examples of the first kind, and the term structure models of
Cox, Ingersoll, and Ross (1985) and Vasicek (1977) are examples of the second kind.
197
What is the typical use of the functions we consider here? One starts with a set of fixed income securities,
typically a set of treasury bonds. Observing the prices of these bonds, one asks: What set of discount
factors is most likely to have generated the observed prices. Or: What term structure approximations
provides the best fit to this set of observed bond prices.
r(t) = 0 + (1 + 2 )
Notation: t: Time to maturity.
1 e
t
t
+ 2 e
t
0 ; 1 ; 2
and
:
constants.
C++ Code 22.1: Calculation of the Nelson and Siegel (1987) term structure model
This is wrapped in a term structure class as shown in Header File 22.1 and C++ Code 22.2.
class term structure class nelson siegel : public term structure class {
private:
double beta0 , beta1 , beta2 , lambda ;
public:
term structure class nelson siegel(const double& beta0,
const double& beta1,
const double& beta2,
const double& lambda);
virtual double yield(const double& T) const;
};
Header file 22.1: Header file defining a term structure class wrapper for the Nelson Siegel approximation
198
#include "fin_recipes.h"
term structure class nelson siegel::term structure class nelson siegel( const double& b0,
const double& b1,
const double& b2,
const double& l) {
beta0 =b0; beta1 =b1; beta2 =b2; lambda =l;
};
double term structure class nelson siegel::r(const double& t) const {
if (t<=0.0) return beta0 ;
return term structure yield nelson siegel(t,beta0 ,beta1 ,beta2 ,lambda );
};
C++ Code 22.2: Defining a term structure class wrapper for the Nelson Siegel approximation
199
Example
Using the parameters 0 = 0:01, 1 = 0:01, 2 = 0:01, = 5:0 and t = 1 in the Nelson Siegel approximation,
find the 1 year discount factor and spot rate, and the forward rate between years 1 and 2.
C++ program:
double beta0=0.01; double beta1=0.01; double beta2=0.01; double lambda=5.0;
double t=1.0;
cout << "Example calculations using the Nelson Siegel term structure approximation"
<< endl;
cout << " direct calculation, yield = "
<< term structure yield nelson siegel(t,beta0,beta1,beta2,lambda) << endl;
term structure class nelson siegel ns(beta0,beta1,beta2,lambda);
cout << " using a term structure class" << endl;
cout << " yield (t=1) = " << ns.r(t) << endl;
cout << " discount factor (t=1) = " << ns.d(t) << endl;
cout << " forward rate (t1=1, t2=2) = " << ns.f(1,2) << endl;
r(t) = 0 + 1
1 e
t
t
1
+ 2
1 e
t
1
t
1
0 ; 1 ; 2
e
and
:
t
1
+ 3
1 e
t
2
t
2
t
2
constants.
Formula 22.2: Svenssons extension of the Nelson and Siegel (1987) parameterization of term structure
This is wrapped in a term structure class as shown in Header File 22.2 and C++ Code 22.4.
200
#include <cmath>
using namespace std;
double term structure yield svensson(const double& t,
const double& beta0,
const double& beta1,
const double& beta2,
const double& beta3,
const double& tau1,
const double& tau2){
if (t==0.0) return beta0;
double r = beta0;
r += beta1* ((1 exp( t/tau1))/(t/tau1)) ;
r += beta2 * ( ((1 exp( t/tau1))/(t/tau1))
exp( t/tau1) );
r += beta3 * ( ((1 exp( t/tau2))/(t/tau2))
exp( t/tau2) );
return r;
};
C++ Code 22.3: Calculation of Svenssons extended Nelson and Siegel (1987) term structure model
structure class {
, tau2 ;
beta0,
beta1,
beta2,
beta3,
tau1,
tau2);
Header file 22.2: Header file defining a term structure class wrapper for the Svensson model
#include "fin_recipes.h"
term structure class svensson::term structure class svensson( const double& b0, const double& b1, const double& b2, const double& b3,
const double& tau1, const double& tau2) {
beta0 =b0; beta1 =b1; beta2 =b2; beta3 =b3; tau1 =tau1; tau2 =tau2;
};
double term structure class svensson::r(const double& t) const {
if (t<=0.0) return beta0 ;
return term structure yield svensson(t,beta0 ,beta1 ,beta2 ,beta3 ,tau1 ,tau2 );
};
C++ Code 22.4: Defining a term structure class wrapper for the Svensson model
201
X
d(t) = 1 + b1 t + c1 t2 + d1 t3 + Fj (t tj )3 1ft<tj g
j =1
Here
fb1 ; c1 ; d1 ; F1 ; ; FK g
If the spline knots are known, this is a simple linear regression. C++ Code 22.5 shows the calculation using
this approximation.
#include <cmath>
#include <vector>
using namespace std;
double term structure discount factor cubic spline(const double& t,
const double& b1,
const double& c1,
const double& d1,
const vector<double>& f,
const vector<double>& knots){
double d = 1.0 + b1*t + c1*(pow(t,2)) + d1*(pow(t,3));
for (int i=0;i<knots.size();i++) {
if (t >= knots[i]) { d += f[i] * (pow((t knots[i]),3)); }
else { break; };
};
return d;
};
#include "fin_recipes.h"
#include <vector>
using namespace std;
class term structure class cubic spline : public term structure class {
private:
double b ; double c ; double d ;
vector<double> f ; vector<double> knots ;
public:
term structure class cubic spline(const double& b, const double& c, const double& d,
const vector<double>& f, const vector<double> & knots);
virtual term structure class cubic spline();
virtual double d(const double& T) const;
};
Header file 22.3: Term structure class wrapping the cubic spline approximation
202
#include "fin_recipes.h"
term structure class cubic spline::
term structure class cubic spline ( const double& b, const double& c, const double& d,
const vector<double>& f, const vector<double>& knots) {
b = b; c = c; d = d; f .clear(); knots .clear();
if (f.size()!=knots.size()){ return; };
for (int i=0;i<f.size();++i) {
f .push back(f[i]);
knots .push back(knots[i]);
};
};
double term structure class cubic spline::d(const double& T) const {
return term structure discount factor cubic spline(T,b ,c ,d ,f ,knots );
};
C++ Code 22.6: Term structure class wrapping the cubic spline approximation
203
Example
Using the parameters
2
=4
0:01
0:01
0:01
2
knots = 4
3
5
2
7
12
3
5
Find short rates and discount factors for 1 year, and the forward rate between 1 and 2 years.
C++ program:
cout << "Example term structure calculations using a cubic spline " << endl;
double b=0.1; double c=0.1; double d= 0.1;
vector<double> f; f.push back(0.01); f.push back(0.01); f.push back( 0.01);
vector<double> knots; knots.push back(2); knots.push back(7); knots.push back(12);
cout << " direct calculation, discount factor (t=1) "
<< term structure discount factor cubic spline(1,b,c,d,f,knots) << endl;
cout << " Using a term structure class " << endl;
term structure class cubic spline cs(b,c,d,f,knots);
cout << " yield (t=1) = " << cs.r(1) << endl;
cout << " discount factor (t=1) = " << cs.d(1) << endl;
cout << " forward (t1=1, t2=2) = " << cs.f(1,2) << endl;
204
1
2
e 2 (++
)(T t)
A(t; T ) =
(
+ + )(e(T t) 1) + 2
# 2
2
and
B (t; T ) =
2e
(T t) 1
(
+ + )(e(T t) 1) + 2
Five parameters: r, the short term interest rate, , the mean reversion parameter, , the market risk
parameter, the longrun mean of the process and , the variance rate of the process.
#include <cmath>
using namespace std;
double term structure discount factor cir(const double& t,
const double& r,
const double& kappa,
const double& lambda,
const double& theta,
const double& sigma){
double sigma sqr=pow(sigma,2);
double gamma = sqrt(pow((kappa+lambda),2)+2.0*sigma sqr);
double denum = (gamma+kappa+lambda)*(exp(gamma*t) 1)+2*gamma;
double p=2*kappa*theta/sigma sqr;
double enum1= 2*gamma*exp(0.5*(kappa+lambda+gamma)*t);
double A = pow((enum1/denum),p);
double B = (2*(exp(gamma*t) 1))/denum;
double dfact=A*exp( B*r);
return dfact;
};
C++ Code 22.7: Calculation of the discount factor using the Cox et al. (1985) model
205
#include "fin_recipes.h"
class term structure class cir : public term structure class {
private:
double r ;
// interest rate
double kappa ;
// mean reversion parameter
// risk aversion
double lambda ;
double theta ;
// long run mean
// volatility
double sigma ;
public:
term structure class cir();
term structure class cir(const double& r, const double& k, const double& l,
const double& th, const double& sigma);
virtual double d(const double& T) const;
};
Header file 22.4: Class definition, Cox et al. (1985) model, header file
#include "fin_recipes.h"
//term structure class cir::term structure class cir(){;};
term structure class cir::term structure class cir(const double& r, const double& k, const double& l,
const double& th, const double& sigma) {
r =r; kappa =k; lambda =l; theta =th; sigma =sigma;
};
double term structure class cir::d(const double& T) const{
return term structure discount factor cir(T,r ,kappa ,lambda ,theta ,sigma );
};
206
Example
Parameters: r = 0:05, = 0:01,
short rate and discount factor for
= 0:1, = 0:08 and = 0:0. Use the CIR term structure model.
t = 1, and forward rate between years 1 and 2.
C++ program:
cout << "Example calculations using the Cox Ingersoll Ross term structure model " << endl;
double r = 0.05; double kappa=0.01; double sigma=0.1; double theta=0.08; double lambda=0.0;
cout << " direct calculation, discount factor (t=1): "
<< term structure discount factor cir(1, r, kappa, lambda, theta, sigma) << endl;
cout << " using a class " << endl;
term structure class cir cir(r,kappa,lambda,theta,sigma);
cout << " yield (t=1) = " << cir.r(1) << endl;
cout << " discount factor (t=1) = " << cir.d(1) << endl;
cout << " forward (t1=1, t2=2) = " << cir.f(1,2) << endl;
207
Find
22.5 Vasicek
#include <cmath>
using namespace std;
double term structure discount factor vasicek(const double& time,
const double& r,
const double& a,
const double& b,
const double& sigma){
double A,B;
double sigma sqr = sigma*sigma;
double aa = a*a;
if (a==0.0){
B = time;
A = exp(sigma sqr*pow(time,3))/6.0;
}
else {
B = (1.0
exp( a*time))/a;
A = exp( ((B time)*(aa*b 0.5*sigma sqr))/aa ((sigma sqr*B*B)/(4*a)));
};
double dfact = A*exp( B*r);
return dfact;
}
C++ Code 22.9: Calculating a discount factor using the Vasicek functional form
#include "fin_recipes.h"
class term structure class vasicek : public term structure class {
private:
double r ; double a ; double b ; double sigma ;
public:
term structure class vasicek(const double& r, const double& a, const double& b, const double& sigma);
virtual double discount factor(const double& T) const;
};
208
#include "fin_recipes.h"
term structure class vasicek::term structure class vasicek(const double& r, const double& a,
const double& b, const double& sigma) {
r =r; a =a; b =b; sigma =sigma;
};
double term structure class vasicek::d(const double& T) const{
return term structure discount factor vasicek(T,r ,a ,b ,sigma );
};
209
Example
Parameters r = 0:05, a = 0:1, b = 0:1, = 0:1 Use the Vasicek term structure model. Find short rate and
discount factor for t = 1, and forward rate between years 1 and 2.
C++ program:
cout
<< "Example term structure calculation using the Vasicek term structure model"
<< endl;
22.6 Readings
The methods in this chapter I first studied in my dissertation at Carnegie Mellon University in 1992,
which lead to the paper published as Green and degaard (1997). A textbook treatment of estimation
and fitting of term structure models can be found in (Martinelli, Priaulet, and Priaulet, 2003, Ch 4)
210
Chapter 23
211
23.2 Readings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
213
Pricing bond options with the Black Scholes model, or its binomial approximation, as done in chapter 20,
does not always get it right. For example, it ignores the fact that at the maturity of the bond, the bond
volatility is zero. The bond volatility decreases as one gets closer to the bond maturity. This behaviour
is not captured by the assumptions underlying the Black Scholes assumption. We therefore look at more
complicated term structure models, the unifying theme of which is that they are built by building trees
of the interest rate.
211
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
double bond option price call zero american rendleman bartter(const double& X,
const double& option maturity,
const double& S,
const double& M, // term structure paramters
const double& interest, // current short interest rate
const double& bond maturity, // time to maturity for underlying bond
const double& maturity payment,
const int& no steps) {
double delta t = bond maturity/no steps;
double
double
double
double
u=exp(S*sqrt(delta t));
d=1/u;
p up = (exp(M*delta t) d)/(u d);
p down = 1.0 p up;
C++ Code 23.1: RB binomial model for European call on zero coupon bond
212
Example
Parameters:K = 950, S = 0:15 and M = 0:05 The interest rate is 10%, The option matures in 4 years, the
bond matures in year 5, with a bond maturity payment of 1000. Price the option on the zero coupon bond
using a RandlemanBartter approximation with 100 steps.
C++ program:
double K=950; double S=0.15; double M=0.05; double interest=0.10;
double option maturity=4; double bond maturity=5; double bond maturity payment=1000;
int no steps=100;
cout << " Rendleman Bartter price of option on zero coupon bond: ";
cout << bond option price call zero american rendleman bartter( K, option maturity, S, M,
interest, bond maturity,
bond maturity payment, no steps);
23.2 Readings
General references include Sundaresan (2001).
Rendleman and Bartter (1979) and Rendleman and Bartter (1980) are the original references for building
standard binomial interest rate trees.
213
Chapter 24
214
216
216
218
24.5 Readings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
220
In this chapter we show a way of building interest rate trees and apply it to the pricing of various fixed
income securities. The first need in such a procedure is to specify the future evolution of interest rates.
dr = rdt + rdZ
This is the same assumption which was used for stock prices in the Black Scholes case. This leads to
the same binomial approximation, for one period:
r0
* ru
H
HH
HH
j rd
or several periods:
214
r0
*
ru
* HH
H
HH
j
H
*
HH
r
HH
d
j
HH
HH
j
H
ruu
rud
rdd
When using this approach it turns out to be useful to keep the whole tree around, we therefore separate
the building of the tree in one routine, as shown in C++ Code 24.1.
#include <vector>
#include <cmath>
using namespace std;
vector<vector<double>
215
C++ program:
vector< vector<double> > tree = interest rate trees gbm build(0.1,1.02,0.99,3);
cout << " Interest rate tree: " << endl;
cout << " Time 0: " << tree[0][0] << endl;
cout << " Time 1: " << tree[1][0] << " " << tree[1][1] << endl;
cout << " Time 2: " << tree[2][0] << " " << tree[2][1] << " " << tree[2][2] << endl;
0.10404
*
d(0; 2)
HH
H
HH
H
H
j
H
du (1; 2)
dd (1; 2)
In constructing trees of discount we need one additional piece of information, q , which are used as follows:
d(0; 2) = e
The parameter
r0 (qd
q serves the same purpose as the state price probability, but it is found differently.
Exercise 24.1.
Given the prices of two discount bonds, with maturities 1 and 2, how would you back out q ?
Exercise 24.2.
Suppose you have
q = 0:5.
216
#include <vector>
#include <cmath>
using namespace std;
double interest rate trees gbm value of cashflows(const vector<double>& cflow,
const vector< vector<double> >& r tree,
const double& q){
int n = int(cflow.size());
vector< vector<double> > values(n);
vector<double> value(n);
for (int i=0;i<n;i++){ value[i]=cflow[n 1]; };
values[n 1]=value;
for (int t=n 1;t>0;
t){
vector<double> value(t,0.0);
for (int i=0;i<t;++i){
value[i]=cflow[t 1]+exp( r tree[t 1][i])*(q*values[t][i]+(1 q)*values[t][i+1]);
};
values[t 1]=value;
};
return values[0][0];
};
217
6.0
7.2
5.4
8.6
6.5
4.9
10.4
7.8
5.8
4.4
12.4
9.3
7.0
5.2
3.9
14.9
11.2
8.4
6.3
4.7
3.5
17.9
13.4
10.1
7.6
5.7
4.3
3.2
21.5
16.1
12.1
9.1
6.8
5.1
3.8
2.9
25.8
19.3
14.5
10.9
8.2
6.1
4.6
3.4
2.6
218
9
31.0
23.2
17.4
13.1
9.8
7.3
5.5
4.1
3.1
2.3
step:
nodes:
6
6
89.90
6
6
6
6
6
6
6
6
6
6
6
6
1
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
88.89
102.03
82.57
95.59
107.13
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
77.07
89.88
101.29
111.05
10
106
106
106
106
106
106
106
106
106
106
106
4
72.59
85.09
96.24
105.79
113.70
69.38
81.45
92.19
101.37
108.96
115.09
67.84
79.32
89.45
98.04
105.10
110.78
115.26
68.67
79.28
88.46
96.14
102.39
107.37
111.29
114.32
73.14
82.27
89.93
96.19
101.20
105.15
108.21
110.57
112.38
83.78
90.04
95.06
99.02
102.11
104.49
106.32
107.71
108.77
109.56
10
106.00
106.00
106.00
106.00
106.00
106.00
106.00
106.00
106.00
106.00
106.00
B = 89:90.
2. The callable will be called when the value of the bond is above par, because the issuing company can
issue a bond at a lower interest rate.
The following set of values is calculated
step:
nodes:
89.35
88.70
101.05
82.53
95.21
105.44
77.07
89.80
100.56
108.22
72.59
85.08
96.08
104.39
109.19
B = 89:35.
219
69.38
81.45
92.18
101.03
106.36
108.31
67.84
79.32
89.45
98.01
104.42
106.00
106.00
68.67
79.28
88.46
96.14
102.32
106.00
106.00
106.00
73.14
82.27
89.93
96.19
101.20
105.00
106.00
106.00
106.00
83.78
90.04
95.06
99.02
102.11
104.49
106.00
106.00
106.00
106.00
10
106.00
106.00
106.00
106.00
106.00
106.00
106.00
106.00
106.00
106.00
106.00
C++ program:
double r0=0.06;
double u=1.2; double d=0.9;
int n=10;
double q=0.5;
vector< vector<double> > tree = interest rate trees gbm build(r0,u,d,n);
vector<double> cashflows;
cashflows.push back(0);
for (int t=1;t<=9;++t){ cashflows.push back(6); };
cashflows.push back(106);
cout << "Straight bond price = " << interest rate trees gbm value of cashflows(cashflows,tree,q) << endl;
int first call time = 6;
double call price = 106;
cout << "Callable bond price = "
<< interest rate trees gbm value of callable bond(cashflows,tree,q, first call time, call price) << endl;
24.5 Readings
General references include Sundaresan (2001).
Rendleman and Bartter (1979) and Rendleman and Bartter (1980) are the original references for building
standard binomial interest rate trees.
220
Chapter 25
221
#include "fin_recipes.h"
class term structure class ho lee : public term structure class {
private:
term structure class* initial term ;
int n ;
int i ;
double delta ;
double pi ;
public:
term structure class ho lee(term structure class* fitted term,
const int & n,
const int & i,
const double& lambda,
const double& pi);
double d(const double& T) const;
};
vector< vector<term structure class ho lee> >
term structure ho lee build term structure tree(term structure class* initial,
const int& no steps,
const double& delta,
const double& pi);
double price european call option on bond using ho lee(term structure class* initial,
const double& delta,
const double& pi,
const vector<double>& underlying bond cflow times,
const vector<double>& underlying bond cflows,
const double& K,
const double& option time to maturity);
#include "fin_recipes.h"
term structure class ho lee::term structure class ho lee(term structure class* fitted term,
const int & n,
const int & i,
const double& delta,
const double& pi){
initial term =fitted term;
n =n;
i =i;
delta =delta;
pi =pi;
};
222
#include "fin_recipes.h"
//#include term structure class ho lee.h
inline double hT(const double& T, const double& delta, const double& pi){
return (1.0/(pi+(1 pi)*pow(delta,T)));
};
double term structure class ho lee::d(const double& T) const{
double d=(*initial term ).d(T+n )/(*initial term ).d(n );
for (int j=1;j<n ;++j){
d *= hT(T+(n j),delta ,pi ) / hT(n j,delta ,pi ) ;
};
d *= hT(T,delta ,pi )*pow(delta ,T*(n i ));
return d;
};
C++ Code 25.2: Term structure class for Ho-Lee, calculation of discount function
#include "fin_recipes.h"
vector< vector<term structure class ho lee> >
term structure ho lee build term structure tree(term structure class* initial,
const int& no steps,
const double& delta,
const double& pi){
vector< vector<term structure class ho lee> > hl tree;
for (int t=0;t<5;++t){
hl tree.push back(vector<term structure class ho lee>());
for (int j=0;j<=t;++j){
term structure class ho lee hl(initial,t,j,delta,pi);
hl tree[t].push back(hl);
};
};
return hl tree;
};
223
What we are pricing are typically state and time-contingent claims to cash flows. Let us illustrate pricing
of an (European) call option on some underlying bond. Suppose this bond is risk free. Its cash flows
at each future date does not depend on the state, but the timing of cash flows changes as you move
in the tree. It is therefore necessary to some way figure out at date t: What are the future cash flows
when you want to price the underlying bond at that date. We build a small class that contains this
information, and use it, together with the term structures in the individual nodes, to find the bond price
at each node. The value of the bond at the different nodes change because the term structure you use
for discounting is changing. This bond price is then used to find the option value at each node.
224
#include "fin_recipes.h"
class time contingent cash flows{
public:
vector<double> times;
vector<double> cash flows;
time contingent cash flows(const vector<double>& in times, const vector<double>& in cflows){
times=in times; cash flows=in cflows;
};
int no cflows(){ return int(times.size()); };
};
vector<time contingent cash flows>
build time series of bond time contingent cash flows(const vector<double>& initial times,
const vector<double>& initial cflows){
vector<time contingent cash flows> vec cf;
vector<double> times = initial times;
vector<double> cflows = initial cflows;
while (times.size()>0){
vec cf.push back(time contingent cash flows(times,cflows));
vector<double> tmp times;
vector<double> tmp cflows;
for (int i=0;i<times.size();++i){
if (times[i] 1.0>=0.0) {
tmp times.push back(times[i] 1);
tmp cflows.push back(cflows[i]);
};
};
times = tmp times; cflows = tmp cflows;
};
return vec cf;
};
double price european call option on bond using ho lee(term structure class* initial, const double& delta, const double& pi,
const vector<double>& underlying bond cflow times,
const vector<double>& underlying bond cflows,
const double& K, const double& time to maturity){
int T = int(time to maturity+0.0001);
vector<vector<term structure class ho lee> > hl tree
= term structure ho lee build term structure tree(initial,T+1,delta,pi);
vector<time contingent cash flows> vec cf
= build time series of bond time contingent cash flows(underlying bond cflow times, underlying bond cflows);
vector<double> values(T+1);
for (int i=0;i<=T;++i){
values[i]=max(0.0,bonds price(vec cf[T+1].times, vec cf[T+1].cash flows, hl tree[T+1][i])
K);
};
for (int t=T;t>=0;
t){
vector<double> values this(t+1);
for (int i=0;i<=t;++i){ values this[i]=(pi*values[i+1]+(1.0 pi)*values[i])*hl tree[t][i].d(1); };
values=values this;
};
return values[0];
};
C++ Code 25.4: Pricing of European call option on straight bond using Ho-Lee
225
Example
You are pricing options on a 5 year zero coupon risk free bond. The options are European calls with a time
to maturity of 3 years.
You will price the options using a Ho-Lee approach with parameters
Price the option using two different assumptions about the current term structure:
1. The term structure is flat with an interest rate of 10% (continously compounded).
2. The current term structure has been estimated using a Nelson Siegel parameterization with parameters
0 = 0:09, 1 = 0:01, 2 = 0:01 and = 5:0.
C++ program:
double delta=0.98;
double pi=0.5;
double r=0.1;
term structure class* initial=new term structure class flat(r);
vector<double> times; times.push back(5.0);
vector<double> cflows; cflows.push back(100);
double K=80;
double time to maturity=3;
cout << " Flat term structure " << endl;
cout << " c= " << price european call option on bond using ho lee(initial,delta, pi, times,cflows,K,time to maturity);
cout << endl;
delete (initial);
double beta0=0.09; double beta1=0.01; double beta2=0.01; double lambda=5.0;
initial = new term structure class nelson siegel(beta0,beta1,beta2,lambda);
cout << " Nelson Siegel term structure " << endl;
cout << " c= " << price european call option on bond using ho lee(initial,delta, pi, times,cflows,K,time to maturity);
cout << endl;
25.5 References
The discussion in this chapter follows closely the original paper Ho and Lee (1986)
226
Chapter 26
227
h=
P
ln
1
P (t; s)
+ P
P (t; T )X 2
P = v(t; T )B (T; s)
1 e a(T t)
B (t; T ) =
a
2 (1 e a(T t) )
v(t; T )2 =
2a
In the case of a = 0,
v(t; T ) = T
P = (s T ) T
Example
Parameters:
#include "normdist.h"
#include "fin_recipes.h"
#include <cmath>
using namespace std;
double bond option price call zero vasicek(const double& X, // exercise price
const double& r, // current interest rate
const double& option time to maturity,
const double& bond time to maturity,
const double& a, // parameters
const double& b,
const double& sigma){
double T t = option time to maturity;
double s t = bond time to maturity;
double T s = s t T t;
double v t T;
double sigma P;
if (a==0.0) {
v t T = sigma * sqrt ( T t ) ;
sigma P = sigma*T s*sqrt(T t);
}
else {
v t T = sqrt (sigma*sigma*(1 exp( 2*a*T t))/(2*a));
double B T s = (1 exp( a*T s))/a;
sigma P = v t T*B T s;
};
double h = (1.0/sigma P) * log (term structure discount factor vasicek(s t,r,a,b,sigma)/
(term structure discount factor vasicek(T t,r,a,b,sigma)*X) )
+ sigma P/2.0;
double c = term structure discount factor vasicek(s t,r,a,b,sigma)*N(h)
X*term structure discount factor vasicek(T t,r,a,b,sigma)*N(h sigma P);
return c;
};
C++ Code 26.1: Bond option pricing using the Vasicek model
C++ program:
double a = 0.1; double b = 0.1; double sigma = 0.02; double r = 0.05; double X=0.9;
cout << " Vasicek call option price "
<< bond option price call zero vasicek(X,r,1,5,a,b,sigma) << endl;
228
Chapter 27
229
C++ program:
#include "boost/date_time/gregorian/gregorian.hpp"
#include "boost/date_time/posix_time/posix_time.hpp"
#include <iostream>
using namespace std;
using namespace boost::gregorian;
using namespace boost::posix time;
void ex date time(){
boost::gregorian::date d(2014,4,1); // creating a date
cout << " date: " << d << endl;
boost::posix time::ptime dt;
stringstream ss("2014-mar-01 10:01:01.01");
ss >> dt;
cout << " date time just created: " << dt
<< endl;
ptime now = second clock::local time(); //get the current time from the clock
date today = now.date(); //Get the date part out of the time
cout << " today: " << today << endl;
date tommorrow = today + days(1); // add one date
ptime tommorrow start(tommorrow); //midnight
time duration remaining = tommorrow start
now;
std::cout << "Time left till midnight: " << to simple string(remaining) << std::endl;
}
27.1 References
The Boost homepage is at http://www.boost.org/
230
Appendix A
231
232
232
233
235
236
236
A.8 References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
236
We will in general not go into detail about more standard numerical problems not connected to finance,
there are a number of well known sources for such, but we show the example of calculations involving
the normal distribution.
n(x) = e
x2
2
is calculated as
#include <cmath> // c library of math functions
using namespace std; // which is part of the standard namespace
// most C compilers define PI, but just in case it doesnt
#ifndef PI
#define PI 3.141592653589793238462643
#endif
double n(const double& z) { // normal distribution function
return (1.0/sqrt(2.0*PI))*exp( 0.5*z*z);
};
231
Prob(x z ) = N (z ) =
Z z
n(x)dx =
Z z
x2
2
dx
There is no explicit closed form solution for calculation of this integral, but a large number of well known
approximations exists. Abramowiz and Stegun (1964) is a good source for these approximations. The
following is probably the most used such approximation, it being pretty accurate and relatively fast.
The arguments to the function are assumed normalized to a (0,1) distribution.
#include <cmath> // math functions.
using namespace std;
double N(const double& z) {
if (z > 6.0) { return 1.0; }; // this guards against overflow
if (z < 6.0) { return 0.0; };
double
double
double
double
double
double
double
b1 = 0.31938153;
b2 = 0.356563782;
b3 = 1.781477937;
b4 = 1.821255978;
b5 = 1.330274429;
p = 0.2316419;
c2 = 0.3989423;
double a=fabs(z);
double t = 1.0/(1.0+a*p);
double b = c2*exp(( z)*(z/2.0));
double n = ((((b5*t+b4)*t+b3)*t+b2)*t+b1)*t;
n = 1.0 b*n;
if ( z < 0.0 ) n = 1.0
n;
return n;
};
x1
x2
3
7
7
X=6 . 7
4 .. 5
xn
A probability statement about this vector is a joint statement about all elements of the vector.
232
Z a Z b
1
p
exp
1 2 1 2
1 x2 2xy + y 2
dxdy
2
1 2
There are several approximations to this integral. We pick one such, discussed in (Hull, 1993, Ch 10),
shown in C++ Code A.3.
If one has more than two correlated variables, the calculation of cumulative probabilites is a nontrivial
problem. One common method involves Monte Carlo estimation of the definite integral. We will consider
this, but then it is necessary to first consider simulation of random normal variables.
Example
Calculate N(0) and N(0,0,0)
C++ program:
cout
cout
233
234
[0; 1) distribution
Exercise A.1.
Replace the random_uniform function here by an alternative of higher quality, by looking into what numerical
libraries is available on your computing platform, or by downloading a high quality random number generator
from such places as mathlib or statlib.
These uniformly distributed distributed random variates are used as a basis for the polar method for
normal densities discussed in Knuth (1997) and inplemented as shown in C++ Code A.5.
#include <cmath>
#include <cstdlib>
using namespace std;
double random uniform 0 1(void);
double random normal(void){
double U1, U2, V1, V2;
double S=2;
while (S>=1) {
U1 = random uniform 0 1();
U2 = random uniform 0 1();
V1 = 2.0*U1 1.0;
V2 = 2.0*U2 1.0;
S = pow(V1,2)+pow(V2,2);
};
double X1=V1*sqrt(( 2.0*log(S))/S);
return X1;
};
235
(0; 1) distribution
C++ program:
cout << " 5 random uniform numbers between 0 and 1: " << endl;
cout << " ";
for (int i=0;i<5;++i){ cout << " " << random uniform 0 1(); }; cout << endl;
cout << " 5 random normal(0,1) numbers: " << endl;
cout << " ";
for (int i=0;i<5;++i){ cout << " " << random normal() ; }; cout << endl;
A.8 References
Tong (1990) discusses the multivariate normal distribution, and is a good reference. For the ultimate
source, see Knuth (1997).
236
Appendix B
C++ concepts
This chapter contains a listing of various C/C++ concepts and some notes on each of them.
accumulate() Function accumulating the elements of a sequence.
bool Boolean variable, taking on the two values true and false. For historical reasons one can also
use the values zero and one for false and true.
class (C++ keyword).
const (qualifyer to variable in C++ function call).
double (basic type). A floating point number with high accuracy.
exp(x) (C function). Defined in
<cmath>.
fabs
float (basic type). A floating point number with limited accuracy.
for Loop
header file
if
Indexation (in vectors and matrices). To access element number i in an array A, use A[i-1]. Well
known trap for people coming to C from other languages. Present in C for historical efficiency
reasons. Arrays in C were implemented using pointers. Indexing was done by finding the first
element of the array, and then adding pointers to find the indexed element. The first element is of
course found by adding nothing to the first elment, hence the first element was indexed by zero.
include
inline (qualifyer to C++ function name). Hint to the optimizer that this function is most efficiently
implemented by inlining it, or putting the full code of the function into each instance of its
calling. Has the side effect of making the function local to the file in which it is defined.
int
<cmath>.
<vector>
while
238
Appendix C
Boost . . . . . . . . . .
Newmat . . . . . . . . .
IT++ . . . . . . . . . .
GSL . . . . . . . . . . .
C.4.1 The evaluation
C.5 Internet links . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
of N3 . . . . . .
. . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
239
239
239
239
239
240
In various places we have used routines from other public domain packages. In this appendix there are
some notes about the libraries which are referenced, and some comments about linking to them.
C.1 Boost
Large collection of peer-reviewed code. Particularly useful: the date_time library.
C.2 Newmat
Newmat is a matrix library that attempts to do matlab-like operations in C++, in that one can define
matrices and vectors, and then multiply them and so on without needing to explicitly call subroutines,
instead writing the operations in a more mathematically oriented way.
C.3 IT++
IT++ is a large library with various different
C.4 GSL
C.4.1 The evaluation of
In the calculation of the American Put approximation of Geske and Johnson (1984), a trivariate normal
needs to be evaluated. Following the discussion in the paper, this is evaluated as
Z j
k z h z
n(z )N2 p 232 ; p 132 ; p 12 2 p13 23 2
1 23 1 13 1 13 1 23
1
239
240
Appendix D
241
242
double&
double&
double&
double&
double&
S, const double& K,
r, const double& sigma,
time,
no warrants outstanding,
no shares outstanding);
double&
double&
double&
double&
S, const double& K,
r, const double& q,
sigma, const double& time,
no warrants outstanding,
243
244
245
246
double option price american put approximated bjerksund stensland( const double& S,
const double& X,
const double& r,
const double& q,
const double& sigma,
const double& T );
////////////// path dependent and other exotic options ////////////////////////////////
double option price call bermudan binomial(const double& S, const double& K, const double& r,
const double& q, const double& sigma, const double& time,
const vector<double>& potential exercise times,
const int& steps);
double option price put bermudan binomial( const double& S, const double& K, const double& r,
const double& q, const double& sigma, const double& time,
const vector<double>& potential exercise times,
const int& steps);
double option price european lookback call(const double& S, const double& Smin, const double& r,
const double& q, const double& sigma, const double& time);
double option price european lookback put(const double& S, const double& Smin, const double& r,
const double& q, const double& sigma, const double& time);
double
option price asian geometric average price call(const double& S, const double& K, const double& r,
const double& q, const double& sigma, const double& time);
vector<double> simulate lognormally distributed sequence(const double& S, const double& r,
const double& sigma, const double& time, const int& no steps);
double
derivative price simulate european option generic( const double& S, const double& K, const double& r,
const double& sigma, const double& time,
double payoff(const vector<double>& S,
const double& K),
const int& no steps, const int& no sims);
double
derivative price simulate european option generic with control variate(const double& S, const double& K,
const double& r, const double& sigma,
const double& time,
double payoff(const vector<double>& S,
const double& K),
const int& nosteps, const int& nosims);
/////////////////////////////
// payoffs of various options, to be used as function arguments in above simulations
double
double
double
double
payoff
payoff
payoff
payoff
/////////////////////////////////////
// generic binomial trees
double option price generic binomial( const double& S, const double& K,
double generic payoff(const double& S, const double& K),
const double& r, const double& sigma, const double& t, const int& steps);
double payoff binary call(const double& S, const double& K);
double payoff binary put(const double& S, const double& K);
247
////////////////////////////////////////
// trinomial trees
double option price call american trinomial( const double& S, const double& K, const double& r, const double& q,
const double& sigma, const double& t, const int& steps) ;
double option price put american trinomial( const double& S, const double& K, const double& r, const double& q,
const double& sigma, const double& t, const int& steps) ;
248
structure class {
, tau2 ;
beta0, const double& beta1, const double& beta2, const double& beta3,
tau1, const double& tau2);
class term structure class cubic spline : public term structure class {
private:
double b ; double c ; double d ; vector<double> f ; vector<double> knots ;
public:
term structure class cubic spline(const double& b, const double& c, const double& d,
const vector<double>& f, const vector<double> & knots);
virtual double d(const double& t) const; // discount factor
};
class term structure class cir : public term structure class {
private:
double r ; double kappa ; double lambda ; double theta ; double sigma ;
public:
term structure class cir(const double& r, const double& k, const double& l,
const double& th,const double& sigma);
virtual double d(const double& t) const; // discount factor
};
class term structure class vasicek : public term structure class {
private:
double r ; double a ; double b ; double sigma ;
public:
term structure class vasicek(const double& r, const double& a, const double& b, const double& sigma);
virtual double d(const double& T) const;
};
/////////////////
/// binomial term structure models
/// bond option, rendlemann bartter (binomial)
double
bond option price call zero american rendleman bartter(const double& K, const double& option maturity,
const double& S, const double& M,
const double& interest,
const double& bond maturity,
const double& maturity payment,
const int& no steps);
vector< vector<double>
249
>& r tree,
vector<double>& cflows,
vector< vector<double> >& r tree,
double& q,
int& first call time,
double& call price);
double price european call option on bond using ho lee(term structure class* initial,
const double& delta,
const double& pi,
const vector<double>& underlying bond cflow times,
const vector<double>& underlying bond cflows,
const double& K,
const double& option time to maturity);
///////////////////////////////////////////////
// ho and lee modelling
class term structure class ho lee : public term structure class {
private:
term structure class* initial term ;
int n ;
int i ;
double delta ;
double pi ;
public:
term structure class ho lee(term structure class* fitted term,
const int & n,
const int & i,
const double& lambda,
const double& pi);
double d(const double& T) const;
};
vector< vector<term structure class ho lee> >
term structure ho lee build term structure tree(term structure class* initial,
const int& no steps,
const double& delta,
const double& pi);
double price european call option on bond using ho lee(term structure class* initial,
const double& delta,
const double& pi,
const vector<double>& underlying bond cflow times,
const vector<double>& underlying bond cflows,
const double& K,
const double& option time to maturity);
/////////////////////////////////
// term structure derivatives, analytical solutions
double bond option price call zero vasicek(const double& X, const double& r,
const double& option time to maturity,
const double& bond time to maturity,
const double& a, const double& b, const double& sigma);
double bond option price put zero vasicek(const double& X, const double& r,
const double& option time to maturity,
const double& bond time to maturity,
const double& a, const double& b, const double& sigma);
#endif
250
Appendix E
Installation
The routines discussed in the book are available for download.
251
A complete program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Basic operations for the date class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Comparison operators for the date class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Iterative operators for the date class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Present value with discrete compounding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Estimation of the internal rate of return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Test for uniqueness of IRR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Present value calculation with continously compounded interest . . . . . . . . . . . . . . . . . . .
Bond price calculation with discrete, annual compounding. . . . . . . . . . . . . . . . . . . . . .
Bond yield calculation with discrete, annual compounding . . . . . . . . . . . . . . . . . . . . . .
Bond duration using discrete, annual compounding and a flat term structure . . . . . . . . . . .
Calculating the Macaulay duration of a bond . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Modified duration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Bond convexity with a flat term structure and annual compounding . . . . . . . . . . . . . . . .
Bond price calculation with continously compounded interest and a flat term structure . . . . .
Bond duration calculation with continously compounded interest and a flat term structure . . .
Calculating the Macaulay duration of a bond with continously compounded interest and a flat
term structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Bond convexity calculation with continously compounded interest and a flat term structure . . .
Term structure transformations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Default code for transformations between discount factors, spot rates and forward rates in a term
structure class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Implementing term structure class using a flat term structure . . . . . . . . . . . . . . . . . . . .
Interpolated term structure from spot rates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Term structure class using linear interpolation between spot rates . . . . . . . . . . . . . . . . .
Pricing a bond with a term structure class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Calculating a bonds duration with a term structure class . . . . . . . . . . . . . . . . . . . . . .
Calculating a bonds convexity with a term structure class . . . . . . . . . . . . . . . . . . . . . .
Mean variance calculations using IT++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Mean variance calculations using Newmat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Calculating the unconstrained frontier portfolio given an expected return using Newmat . . . . . .
Calculating the unconstrained frontier portfolio given an expected return using IT++ . . . . . . .
Futures price . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Binomial European, one period . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Building a binomial tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Binomial multiperiod pricing of European call option . . . . . . . . . . . . . . . . . . . . . . . . .
Price of European call option using the Black Scholes formula . . . . . . . . . . . . . . . . . . . .
Calculating the delta of the Black Scholes call option price . . . . . . . . . . . . . . . . . . . . .
Calculating the partial derivatives of a Black Scholes call option . . . . . . . . . . . . . . . . . .
Calculation of implied volatility of Black Scholes using bisections . . . . . . . . . . . . . . . . . .
Calculation of implied volatility of Black Scholes using Newton-Raphson . . . . . . . . . . . . . .
Adjusted Black Scholes value for a Warrant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Option price, continous payout from underlying . . . . . . . . . . . . . . . . . . . . . . . . . . . .
European option price, dividend paying stock . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Option price, RollGeskeWhaley call formula for dividend paying stock . . . . . . . . . . . . . .
Price of European Call option on Futures contract . . . . . . . . . . . . . . . . . . . . . . . . . .
252
9
12
13
14
27
31
33
35
38
39
41
42
43
44
47
48
48
48
53
56
57
60
62
64
65
65
77
78
79
80
81
84
87
88
90
94
95
96
97
101
103
104
107
108
253
23.1
24.1
24.2
24.3
25.1
25.2
25.3
25.4
26.1
A.1
A.2
A.3
A.4
A.5
C.1
254
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
212
215
217
218
222
223
223
225
228
231
232
234
235
235
240
255
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
71
76
76
76
91
116
117
135
137
185
215
Appendix F
Acknowledgements.
After this paper was put up on the net, Ive had quite a few emails about them. Some of them has
pointed out bugs and other inaccuracies.
Among the ones I want to say thanks to for making improving suggestions and pointing out bugs are
Ariel Almedal
Andrei Bejenari
Steve Bellantoni
Jean-Paul Beveraggi
Lars Gregori
Daniel Herlemont
Lorenzo Isella
Jens Larsson
Garrick Lau
Steven Leadbeater
Michael L Locher
Lotti Luca, Milano, Italy
Tuan Nguyen
Michael R Wayne
256
Index
, 94
, 94
, 94
A, 137
a, 135
antithetic variates, 148
asset or nothing call, 151
build_time_series_of_bond_time_contingent_cash_flows,
225
calcP2, 158
call option, 82
cash flow, 26
cash or nothing call, 151
cash_flow_irr_discrete, 31
cash_flow_pv, 35
cash_flow_pv_discrete, 27, 28, 32, 241
cash_flow_unique_irr, 33
class, 9
cmath, 7
Complex numbers
in C++, 189
control variates, 147
convexity
of bond, 44
Cox Ingersoll Ross term structure model, 205
currency
option, 109
currency option
American, 130
European, 109
currency_option_price_call_american_binomial, 130
currency_option_price_call_european, 109, 110
257
Macaulay, 41
modified, 43
e, 78, 79
early exercise premium, 159
ex_date_time, 230
exp, 196
exp() (C++ statement), 7
explicit finite differences, 132
Monte Carlo
antithetic variates, 148
monte carlo
control variates, 147
mv_calculate_mean, 77, 78
mv_calculate_portfolio_given_mean_unconstrained, 79,
80
mv_calculate_st_dev, 77, 78
mv_calculate_variance, 77, 78
f, 234
N, 232234
f3, 240
n, 231
finite differences, 132, 137
N3, 240
explicit, 132
next_date, 14
for (C++ statement), 8
no_observations, 61, 242
function prototypes (C++ concept), 146
Normal distribution
futures
Simulation, 235
option, 108
normal distribution
futures_option_price_call_american_binomial, 128, 129
approximations, 231
futures_option_price_call_european_black, 108
option
futures_option_price_put_european_black, 108
call, 82
futures_price, 81
currency, 109
gamma, 94
futures, 108
geometric Brownian motion, 93
lookback, 170
Geske and Johnson, 156
put, 82
Option price
hedging parameters
Black Scholes, 89
Black Scholes, 93
option price
heston_call_option_price, 189, 190
binomial, 112
heston_integrand_j, 190
simulated, 142
heston_Pj, 190
option_price_american_call_approximated_baw, 160,
hT, 223
161
option_price_american_call_approximated_bjerksund_stensland,
if, 13
164
implied volatility
option_price_american_call_one_dividend, 106, 107
calculation, 96
option_price_american_perpetual_call, 110
include (C++ statement), 7
option_price_american_put_approximated_bjerksund_stensland,
int (C++ type), 6
164
interest_rate_trees_gbm_build, 215
option_price_american_put_approximated_geske_johnson,
interest_rate_trees_gbm_value_of_callable_bond, 218
158
interest_rate_trees_gbm_value_of_cashflows, 217, 220 option_price_american_put_approximated_johnson,
internal rate of return, 30
154, 155
Internet links, 240
option_price_asian_geometric_average_price_call, 169
irr, 30
option_price_call_american_binomial, 115, 123, 128,
iteration operator
178
definition of (C++ concept), 14
option_price_call_american_discrete_dividends_binomial,
127
Jump Diffusion, 186
option_price_call_american_proportional_dividends_binomial,
125
Links
option_price_call_black_scholes, 90, 92, 144, 147, 150
Internet, 240
option_price_call_european_binomial, 114, 118
long (C++ type), 6
option_price_call_european_binomial_multi_period_given_ud,
lookback option, 170
88
option_price_call_european_binomial_single_period,
main, 9
84, 87
Merton
option_price_call_european_simulated, 144
Jump Diffusion, 186
option_price_call_merton_jump_diffusion, 187
modified duration, 43
258
259
term_structure_yield_from_discount_factor, 53
term_structure_yield_linearly_interpolated, 60
term_structure_yield_nelson_siegel, 198, 200
term_structure_yield_svensson, 201
theta, 94
time
value of, 26
time_contingent_cash_flows, 225
underlying security, 89
valid, 10
Vasicek, 208
vega, 94
volatility
implied, 96
warrant_price_adjusted_black_scholes, 101
260
John C Cox, Stephen A Ross, and Mark Rubinstein. Option pricing: A simplified approach. Journal of Financial
Economics, 7:229263, 1979.
John C Cox, Jonathan E Ingersoll, and Stephen A Ross. A
theory of the term structure of interest rates. Econometrica, 53:385408, 1985.
Bibliography
Milton Abramowiz and Irene A Stegun. Handbook of Mathematical Functions. National Bureau of Standards, 1964.
Giovanni Barone-Adesi. The saga of the American put. Journal of Banking and Finance, 29:29092918, 2005.
Giovanni Barone-Adesi and Robert E Whaley. Efficient analytic approximation of American option values. Journal
of Finance, 42(2):30120, June 1987.
Petter Bjerksund and Gunnar Stensland. Closed form approximations of american options. Scandinavian Journal
of Management, 20(5):761764, 1993.
Petter Bjerksund and Gunnar Stensland. Closed form valuation of american options. Working Paper, NHH, October
2002.
Fisher Black. The pricing of commodity contracts. Journal
of Financial Economics, 3:16779, 1976.
Fisher Black and Myron S Scholes. The pricing of options
and corporate liabilities. Journal of Political Economy,
7:63754, 1973.
Zvi Bodie, Alex Kane, and Alan J Marcus. Investments.
McGraw Hill/Irwin, Seventh edition, 2007.
Peter L Bossaerts and Bernt Arne degaard. Lectures on
Corporate Finance. World Scientific Press, Singapore,
2001.
Phelim P Boyle. Options: A Monte Carlo approach. Journal
of Financial Economics, 4:32338, 1977.
Richard A Brealey, Stewart C Myers, and Franklin Allen.
Principles of Corporate Finance. McGrawHill/Irwin,
tenth edition, 2010.
Michael Brennan and Eduardo Schwartz. Finite difference
methods and jump processes arising in the pricing of contingent claims: A synthesis. Journal of Financial and
Quantitative Analysis, 13:46174, 1978.
Mark Broadie and Jerome Detemple. American option valuation: New bounds, approximations, and a comparison
of existing methods. Review of Financial Studies, 9(4):
12111250, Winter 1996.
Mark Broadie and Jerome Detemple. Option pricing: Valuation models and applications. Management Science, 50
(9):11451177, September 2004.
261
C++ primer.
Richard J Rendleman and Brit J Bartter. The pricing of options on debt securities. Journal of Financial and Quantitative Analysis, 15(1):1124, March 1980.
Richard Roll. A critique of the asset pricing theorys tests
Part I: On past and potential testability of the theory.
Journal of Financial Economics, 4:129176, 1977a.
Richard Roll. An analytical formula for unprotected American call options on stocks with known dividends. Journal
of Financial Economics, 5:25158, 1977b.
Stephen A Ross, Randolph Westerfield, and Jeffrey F Jaffe.
Corporate Finance. McGraw-Hill/Irwin, ninth edition,
2009.
Mark Rubinstein.
The valuation of uncertain income
streams and the valuation of options. Bell Journal, 7:
40725, 1976.
Mark Rubinstein. Exotic options. University of California,
Berkeley, working paper, 1993.
William F Sharpe, Gordon J Alexander, and Jeffery V Bailey. Investments. Prentice Hall, sixth edition, 1999.
Robert J. Shiller. The term structure of interest rates. In
B M Friedman and F H Hahn, editors, Handbook of Monetary Economics, volume 2, chapter 13, pages 627722.
Elsevier, 1990.
Bjarne Stroustrup.
The C++ Programming language.
AddisonWesley, fourth edition, 1997a.
Bjarne Stroustrup.
The C++ Programming language.
AddisonWesley, third edition, 1997b.
Suresh Sundaresan. Fixed Income Markets and their Derivatives. South-Western, 2 edition, 2001.
Charles R Nelson and Andrew F Siegel. Parsimonious modelling of yield curves. Journal of Business, 60(4):47389,
1987.
Carl J Norstrom. A sufficient conditions for a unique nonnegative internal rate of return. Journal of Financial and
Quantitative Analysis, 7(3):183539, 1972.
262
263