Lecture 0: Review of Binomial Option Pricing
A Simple Binomial Option Pricing Program
Philip H. Dybvig
Washington University
Saint Louis, Missouri
The option pricing model of Black and Scholes revolutionized a literature previously characterized by clever but unreliable rules of thumb. The Black-Scholes model uses continuous-time stochastic process methods that interfere with understanding the simple intuition underlying these models. We will use instead the binomial option pricing model of Cox, Ross, and Rubinstein, which captures all of the economics of the continuous time model but is simple to understand and program. For option pricing problems not appropriately handled by Black-Scholes, some variant of the binomial model is the usual choice of practitioners since it is relatively easy to program, fast, and flexible.
Cox, John C., Stephen A. Ross, and Mark Rubinstein (1979) ``Option Pricing: A Simplified Approach'' Journal of Financial Economics 7, 229--263
Black, Fischer, and Myron Scholes (1973) ``The Pricing of Options and Corporate Liabilities'' Journal of Political Economy 81, 637--654
Riskless bond:
2 3 1 --- r --- r --- r
Stock (u > r > d):
|- uuuS |- uuS -| |- uS -| |- uudS S -| |- udS -| |- dS -| |- uddS |- ddS -| |- dddS
Derivative security (option or whatever):
|- V1 |- ? -| |- ? -| |- V2 ? -| |- ? -| |- ? -| |- V3 |- ? -| |- V4
What is the price of the derivative security?
Riskless bond (interest rate is 0):
100 --- 100
Stock:
|- 100 50 -| |- 25
Derivative security (on-the-money call option):
|- 50 ? -| |- 0
To duplicate the call option with alpha_S shares of stock and alpha_B bonds:
50 = 100 alpha_S + 100 alpha_B
0 = 25 alpha_S + 100 alpha_B
Therefore alpha_S = 2/3, alpha_B = -1/6, and the duplicating portfolio is worth 50 alpha_S + 100 alpha_B = 100/6 = 16 2/3. By absence of arbitrage, this must also be the price of the call option.
Compute the duplicating portfolio and the price of the general derivative security below. Assume u > r > d > 0.
Riskless bond:
1 --- r
Stock:
|- uS S -| |- dSDerivative security:
|- V_u ? -| |- V_d
In general, exactly the same valuation procedure is used many times, taking as given the value at maturity and solving back one period of time until the beginning. This valuation can be viewed in terms of state prices p_u and p_d or risk-neutral probabilities pi*_u and pi*_d, which give the same answer (which is the only one consistent with no arbitrage):
Value = p_u V_u + p_d V_d = (1/r)(pi*_u V_u + pi*_d V_d)
where
p_u = (r-d)/(u-d)/r,
p_d = (u-r)/(u-d)/r,
pi_u = (r-d)/(u-d),
and
pi_d = (u-r)/(u-d).
In the binomial model (with parameters u, d, and r), show that the stock and the bond have the same one-period expected return computed using the artificial probabilities.
Consider the binomial model with u=2, d=1/2, and r=1. What are the risk-neutral probabilities? Assuming the stock price is initially 100, what is the price of a call option with a 90 strike price maturing in two periods? Do the valuation two ways, using the risk neutral probabilities to solve backwards through the tree, and directly using the two-period risk neutral probabilities.
d = 1 + r * Delta t - sigma * sqrt{Delta t}
with pi*_u=pi*_d=1/2 and Delta t the time increment. This has the two essential features: it equates expected stock and bond returns, and it has the right standard deviation. In addition, it has a continuous stock price (like Black-Scholes) as a limit.
One alternative is to choose the positive solution of ud=1 and u-d=2 sigma sqrt{Delta t} (good for option price as a function of time) or a recombining trinomial (good for including some dependence of variance on the stock price).
The file crr.h is a header file containing type declarations for the class of binomial option pricing objects including their member functions and variables. This is a description of what the compiler needs to know about the way a binomial option pricing object of type crr is accessed. This file is included at the top of each of the other files.
The file crr.cc contains the code that does the actual option pricing. The member crr::crr is a constructor that initializes the member parameters when a an object of type crr is declared. The function crr::eurcall is a member function that computes the price of a European call option. The function crr::binopt is a member function that computes the price of a binary option (also called a digital option---an even worse term) that pays off in a given price range.
The file crrtest.cc has the code that is the interface between the user and the pricing program in crr.cc. Note that only the public parts of an object of type crr can be accessed here.
// crr.h // // Simple binomial option pricing model: declarations // #define MAXTERNODES 400 class crr { public: crr(double ttm=.25,int npers=4,double r=.05,double sigma=.3); double eurcall(double S0,double X); double binopt(double S0,double lower,double upper); private: int nper; double tinc,r1per,disc,up,down,prup,prdn,Sprice; double val[MAXTERNODES];};
// crrtest.cc // // Binomial option pricing model: test // #include <iostream.h> #include "crr.h" main() { crr c1; double stockP=0.0,strikeP; cout << "\nType the stock price, a space, the strike" << " price, and then ENTER." << "\n" << "Make the stock price negative to terminate." << "\n\n"; while(1) { cout << "stock-price strike-price: "; cin >> stockP >> strikeP; if(stockP < 0.0){ cout << "\nBye!\n" << endl; return(0);} if(!cin){ cout << "\nError: enter stock-price strike-price\n" << "Terminating\n" << endl; return(1);} cout << "call option value = " << c1.eurcall(stockP,strikeP) << "\n\n";}}
// crr.cc // // Simple binomial option pricing model: implementation // (following Cox, Ross, and Rubinstein) // #include <math.h> #include <iostream.h> #include "crr.h" #define MAX(a,b) (((a) > (b)) ? (a) : (b))
crr::crr(double ttm,int npers,double r,double sigma) { nper = npers; tinc = ttm/(double) nper; r1per = 1.0 + r * tinc; disc = 1.0/r1per; up = r1per + sigma * sqrt(tinc); down = r1per - sigma * sqrt(tinc); prup = disc * 0.5; prdn = disc * 0.5;}
double crr::eurcall(double S0,double X) { int i,j; // initialize terminal payoffs // i is the number of up moves over the whole life for(i=0;i<=nper;i++) { Sprice = S0*pow(up,(double) i)*pow(down,(double) (nper-i)); val[i] = MAX(Sprice - X,0);} // compute prices back through the tree // j+1 is the number of periods from the end // i is the number of up moves from the start for(j=0;j<nper;j++) { for(i=0;i<nper-j;i++) { val[i] = prdn * val[i] + prup * val[i+1];}} return(val[0]);}
double crr::binopt(double S0,double lower,double upper) { int i,j; // initialize terminal payoffs // i is the number of up moves over the whole life for(i=0;i<=nper;i++) { Sprice = S0*pow(up,(double) i)*pow(down,(double) (nper-i)); val[i] = (((Sprice>=lower) && (Sprice<=upper)) ? 1.0 : 0.0);} // compute prices back through the tree // j+1 is the number of periods from the end // i is the number of up moves from the start for(j=0;j<nper;j++) { for(i=0;i<nper-j;i++) { val[i] = prdn * val[i] + prup * val[i+1];}} return(val[0]);}