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.

- Expected Excess Returns
- Common stock indices: 8-10% per year or 8-10%/250 ~ 3 or 4 basis points daily
- Individual common stocks: 50%-150% of the index expected excess return.

- Standard Deviation
- Common stock indices: 20-25% per year or 20-25%/sqrt{250}~1-1.5% per day
- Individual common stocks: 35%-40% per year

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]);}