Option Pricer: Binomial Trees
When pricing options, we don’t know whether the price will go up or down. This simple model captures that uncertainty.
Parameters
Consider an option with the strike price \(X = 120\) maturing in \(T = 3\) months on a stock worth \(S = 115\) having volatility \(\sigma = 30\%\) and interest rate \(r = 15\%\).
u & d
The above stock price will either go up or down. In this model, these up/down movements are defined as:
\[u = e^{\sigma\sqrt{\Delta t}}\\ d = e^{-\sigma\sqrt{\Delta t}} = \frac{1}{u}\\\]where \(\sigma\) is the volatility and \(\Delta t\) is the time step.
Time Step
We shall use a three-step bionimal model, with each time step representing one month.
\[\Delta t = 1/12 = 0.8333\]Building a Tree of Stock Prices
Applying our parameters, we get:
\[u = e^{0.3\sqrt{0.08333}} = 1.0905\\ d = 1/1.0905 = 0.9170\\\]To get the stock price at the next time step we compute both \(S_0 \cdot u\) and \(S_0 \cdot d\).
That is:
\[S_0 \cdot u = 115 \times 1.0905 = 125.4074\\ S_0 \cdot d = 115 \times 0.9170 = 105.455\]We can represent this fledgling tree using a matrix.
\[\begin{array}{|c|c|} \hline 115 & 125.4074 \\ & 105.455 \\ \hline \end{array}\]Doing this for every timestep, we build our tree iteratively until \(t = T\).
\[\begin{array}{|c|c|c|c|} \hline 115 & 125.4074 & 136.7569 & 149.1334 \\ & 105.455 & 115 & 125.4075 \\ & & 96.7022 & 105.455 \\ & & & 88.6759 \\ \hline \end{array}\]We have now \(n = T+1\) predictions of \(S_T\).
Implementation
We can populate this tree in Java using a two-dimensional array.
private double[][] stockPrices(double S0, double u, double d, int T) {
double[][] stockPrice = new double[T][T];
stockPrice[0][0] = S0;
for (int hori = 1; hori < T; hori++) {
// compute increase by u on the extreme side only
stockPrice[0][hori] = stockPrice[0][hori - 1] * u;
for (int vert = 1; vert < T; vert++)
// compute all decrease by d
stockPrice[vert][hori] = stockPrice[vert - 1][hori - 1] * d;
}
return stockPrice;
}
This method will return a matrix of stock prices of size \(T \times T\)
Building a Tree of Option Prices
We now construct a second matrix of option prices.
Computing Payoff
Once again we seek to populate a matrix of size \(T \times T\) iteratively. Only this time we move from right to left.
In the first step, we look at the stock prices at maturity and compute the Pay-Off.
\[\begin{array}{|c|c|} \hline \text{Call} & \text{Put} \\ \hline MAX(S_T - X,0) & MAX(X - S_T, 0) \\ \hline \end{array}\]We compute an option price for each value of \(S_T\) we computed earlier, which, if the option is a Call, returns the following:
\[\begin{array}{|c|c|c|c|} \hline ? & ? & ? & 29.1334 \\ & ? & ? & 5.4075 \\ & & ? & 0 \\ & & & 0 \\ \hline \end{array}\]And the following if the option is a Put:
\[\begin{array}{|c|c|c|c|} \hline ? & ? & ? & 0 \\ & ? & ? & 0 \\ & & ? & 14.5402 \\ & & & 31.3120 \\ \hline \end{array}\]Applying Discounting
Next we look at all stock prices prior to maturity \(t < T\) and use the following formulas to iteratively compute option prices.
\[f = e^{r\Delta t}(pf_u+(1-p)f_d)\]where:
\[p = \frac{e^{r\Delta t} - d}{u-d}\]which in this example gives us:
\[p = \frac{1.0126-0.9170}{1.0905-0.9170} = 0.5509\]But note that for American Puts, we must consider early exercise such that our option price is:
\[f = MAX(e^{r\Delta t}(pf_u+(1-p)f_d), X - S_t)\]Either way, we take the option price at the root of the tree, \(f_{t=0}\).
Below are the option trees as fully developed for each type of derivative.
European & American Call
\[\begin{array}{|c|c|c|c|} \hline 6.8171 & 11.2264 & 18.2383 & 29.1334 \\ & 1.5993 & 2.9400 & 5.4075 \\ & & 0 & 0 \\ & & & 0 \\ \hline \end{array}\] \[f = 6.8171\]European Put
\[\begin{array}{|c|c|c|c|} \hline 8.0051 & 2.8603 & 0 & 0 \\ & 14.5402 & 6.4490 & 0 \\ & & 23.2890 & 14.5402 \\ & & & 31.3120 \\ \hline \end{array}\] \[f = 8.0051\]American Put
\[\begin{array}{|c|c|c|c|} \hline 7.4003 & 2.8603 & 0 & 0 \\ & 13.1767 & 6.4490 & 0 \\ & & 21.7983 & 14.5402 \\ & & & 31.3120 \\ \hline \end{array}\] \[f = 7.4003\]Implementing Call Pricing
As the implementation of pricing a European Call the same as that for an American Put, we need only write one implementation.
Therefore we write the following method in TreeAbstract.java
.
private double computeCall(double[][] stockPrice, double strike, double interest, double p, double dt, int T) {
double[][] optionPrice = new double[T][T];
for(int hori = T-1; hori >= 0; hori--)
for(int vert = hori; vert >= 0; vert--){
// compute option prices at maturity
if (hori == T -1)
optionPrice[vert][hori] = Math.max(stockPrice[vert][hori]-strike, 0.0);
// compute option prices prior to maturity
else
optionPrice[vert][hori] = Math.exp(-interest*dt)*((p*optionPrice[vert][hori+1])+(1-p)*optionPrice[vert+1][hori+1]);
}
return optionPrice[0][0];
}
Implementing Put Pricing
As the implmentation for a European Put differs from the American Put, we place an abstract method in TreeAbstract.java
.
abstract public double computePut(double[][] stockPrice, double strike, double interest, double p, double dt, int T);
Then we let the concrete implementation of this method be defined in the concrete classes TreeEuropean.java
and TreeAmerican.java
which both extend the abstract class.
TreeEuropean.java
public class TreeEuropean extends TreeAbstract {
public TreeEuropean(HashMap<String, Double> hashMap){
super(hashMap);
}
@Override
public double computePut(double[][] stockPrice, double strike, double interest, double p, double dt, int T) {
double[][] optionPrice = new double[T][T];
for(int hori = T-1; hori >= 0; hori--)
for(int vert = hori; vert >= 0; vert--){
// compute option prices at maturity
if (hori == T -1)
optionPrice[vert][hori] = Math.max(strike-stockPrice[vert][hori],0.0);
// compute option prices prior to maturity
else
optionPrice[vert][hori] = Math.exp(-interest*dt)*((p*optionPrice[vert][hori+1])+(1-p)*optionPrice[vert+1][hori+1]);
}
for (int i = 0; i < T; i++)
System.out.println(Arrays.toString(optionPrice[i]));
return optionPrice[0][0];
}
}
American
public class TreeAmerican extends TreeAbstract {
public TreeAmerican(HashMap<String, Double> hashMap){
super(hashMap);
}
@Override
public double computePut(double[][] stockPrice, double strike, double interest, double p, double dt, int T) {
double[][] optionPrice = new double[T][T];
for(int hori = T-1; hori >= 0; hori--)
for(int vert = hori; vert >= 0; vert--){
// compute option prices at maturity
if (hori == T -1)
optionPrice[vert][hori] = Math.max(strike-stockPrice[vert][hori],0.0);
// compute option prices prior to maturity
else
optionPrice[vert][hori] = Math.max(
Math.exp(-interest*dt)*((p*optionPrice[vert][hori+1])+(1-p)*optionPrice[vert+1][hori+1])
, strike-stockPrice[vert][hori]);
}
for (int i = 0; i < T; i++)
System.out.println(Arrays.toString(optionPrice[i]));
return optionPrice[0][0];
}
}
Returning Option Prices
TreeAbstract.java
is an abstract class implementing the interface PricingType.java
which is written as follows:
public interface PricingType {
double getCall();
double getPut();
}
This allows OptionPricer.java
to remain boilerplate. Now the code to return a call or put is the same no matter the underlying pricing method being used.
In TreeAbstract.java
, we define the body of the above methods:
public double getCall(){
double fCall = computeCall(stockPrice, strike, interest, p, dt, T);
return fCall;
}
public double getPut(){
double fPut = computePut(stockPrice, strike, interest, p, dt, T);
return fPut;
}
That is, we simply invoke the methods computeCall
and computePut
.