A Simple Binomial Interest Option Pricing Program

Philip H. Dybvig

Washington University

Saint Louis, Missouri

- Riskless Bonds
- Bond Options
- Puts, Calls, Straddles, etc.
- American, European, Down-and-Out, etc.

- Bond Futures and Futures Options
- Caps Floors, Collars
- Riskless Inverse Floaters

- Mortgages and CMOs
- Structured Loans
- Risky Corporate Bonds
- Callable and/or Convertible Bonds
- Foreign Exchange Futures Options
- Hybrid Securities, e.g. a binary option paying off if at maturity 3-mo LIBOR > 12% and the dollar is stronger against the yen than it was at the start of the contract

- The interest rate is not constant.
- The volatility is not constant.
- ``Today's price'' is not an asset price.
- We may want to value claims that are not simple combinations of puts and calls.

|- r + deltaWe choose delta = sigma * sqrt(Delta t).

r -|

|- r - delta

The interest rate is not an asset! Therefore, we can't use the formula from the previous lecture to compute the risk-neutral probabilities or state prices. There are several approaches:

- Make an assumption about the price process for some asset (e.g. a perpetuity).
- Make an assumption about the nature of supply and demand in the economy and compute equilibrium prices.
- Make an assumption about the interest rate process in the
risk-neutral probabilities, e.g.
- Random walk
- Modest mean reversion (my preference)

E[Delta r]=k(r*-r)Delta t,

then

pi*_u delta+(1-pi*_u)(-delta)=k(r*-r) Delta t

or

pi*_u=(1/2)+(k(r^*-r)Delta t)/2 delta.

A random walk (good for short maturities and non-critical applications) corresponds to k=0.

Another issue: use uneven spacing to make interest rate volatility a
function of the interest rate. Fudge factors can fit today's yield
curve. *Stochastic volatility* and additional factors are harder.

**About Intermediate Cash Flows** When a claim includes intermediate
cash flows (as for a coupon bond or a cap), the claim is simply
added in at the appropriate time. For example, if V indicates the
ex-cashflow value and C the cashflow, we have, at some node at time
t,
V(t,r)=(pi*_u(V(t,r+delta)+C(t,r+delta))+pi*_d(V(t,r-delta)+
C(t,r-delta)))/(1+r)

// cap.h // // Fixed-income binomial option pricing include file // #ifndef MAXTERNODES #define MAXTERNODES 200 #endif class f_i_bin { public: f_i_bin(double ttm=.25,int npers=4,double sigma=.02, double rrbar=.07,double k=.125); double bprice(double r0); double cap(double level,double r0); private: int nper; double tinc,up,down,sig,rbar,kappa,prfact; double r[MAXTERNODES],val[MAXTERNODES];};

// captest.cc // // Fixed income binomial option pricing model: test // #include <iostream.h> #include "cap.h" main() { f_i_bin c2; double caprate,rzero; cout << "\nType the cap rate, a space, the initial" << " rate, and then ENTER." << "\n" << "Make the cap rate negative to terminate." << "\n\n"; cout << "State rates in percent per annum (without the " << "percent sign).\n\n"; while(1) { cout << "cap-rate initial-rate: "; cin >> caprate >> rzero; if(!cin) { cout << "\nError: expected cap-rate initial-rate\n" << "Terminating\n" << endl; return(1);} if(caprate<0.0) { cout << endl; return(0);} cout << "cap value = " << c2.cap(caprate/100.0,rzero/100.0) << "\n\n";}}

// cap.cc // // Fixed-income binomial option pricing engine // #include <math.h> #include <iostream.h> #include "cap.h" #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #define MIN(a,b) (((a) < (b)) ? (a) : (b))

f_i_bin::f_i_bin(double ttm,int npers,double sigma,double rrbar,double k) { nper=npers; tinc = ttm/(double) nper; sig = sigma; up = sigma*sqrt(tinc); rbar = rrbar; kappa = k; prfact = kappa*sqrt(tinc)/(2.0*sig);}

double f_i_bin::bprice(double r0) { int i,j; double prup; //initialize terminal payoffs //i is the number of up moves for(i=0;i<=nper;i++) { // r[i] = r0 + up * (double)(2*i-nper); not needed for this claim val[i] = 1.0;} //compute prices back through the tree //j is the number of periods from the end //i is the number of up moves from the start for(j=1;j<=nper;j++) {for(i=0;i<=nper-j;i++) { r[i] = r0 + up * (double) (2*i-nper + j); prup = 0.5 + prfact*(rbar-r[i]); prup = MIN(1.0,MAX(0.0,prup)); val[i] = (prup*val[i+1]+(1.0-prup)*val[i])*exp(-r[i]*tinc);}} return(val[0]);}

double f_i_bin::cap(double level,double r0) { int i,j; double prup; //initialize terminal payoffs //i is the number of up moves for(i=0;i<=nper;i++) { // r[i] = r0 + up * (double)(2*i-nper); not needed for this claim val[i] = 0.0;} //compute prices back through the tree //j is the number of periods from the end //i is the number of up moves from the start for(j=1;j<=nper;j++) {for(i=0;i<=nper-j;i++) { r[i] = r0 + up * (double) (2*i-nper + j); prup = 0.5 + prfact*(rbar-r[i]); prup = MIN(1.0,MAX(0.0,prup)); val[i] = (prup*val[i+1]+(1.0-prup)*val[i])*exp(-r[i]*tinc) + MAX(0.0,(r[i]-level)*tinc);}} return(val[0]);}