This is not yet another tutorial on binomial trees. I expect my reader to be familiar with them already. If not, an article by Goddard Consulting is rather readable. Here I would like to show how to program the binomial trees in R and how to generate the graph description which an external program like graphviz can turn into a pretty picture.

Why keep the tree?

It is true that in many cases it is not necessary to store the whole tree just to get the final value of the option. I do, however, find it useful to see how the option value changes with the asset price and time. It is also useful if one wishes to calculate greeks, for example.

Some examples

The following example was produced by the programs discussed later in this post. The parameters (and, thankfully, results) match those of the Example 11.11 in Hull.

The index level is 810, strike price is 800, risk-free rate is 5%, volatility is 20%, and dividend yield is 2%. We are pricing a 6-month European call option.

A Binomial Tree Example 1

For a more involved example, here is a valuation of an American put with more steps. The circumscribed noded at the bottom right are the nodes where an early exercise is preferred.

A Binomial Tree Example 2

By comparing with the following screenshot from DerivaGem one can see that the values make sense, indeed.

A DerivaGem screenshot

That’s the desired outcome, let’s get it done.

Previously… in 2012

The two functions written by Rory Winston will provide scaffolding for achieving the aims of this post.

The genlattice function generates the recombining binomial tree, or binary lattice for the asset price only.

The dotlattice function takes this lattice and generates the graph specification in graphviz format from it.

These two functions are our starting point.

Fast forward to 2016

Rory’s post was written in 2012, and I had to make some trivial changes to make it work with my version of graphviz. Specifically, I had to remove references to samehead and sametail in node specification.

I changed the line

cat("node[shape=",shape,", samehead, sametail];","\n", sep="")

to

cat("node[shape=",shape,"];","\n", sep="")

and this was enough to be able to produce the image using R

> x <- capture.output(dotlattice(genlattice(N=8, u=1.1, d=0.9), labels=TRUE))
> cat(x, file="lattice.dot")

and the terminal

$ dot -Tpng -o lattice-1.png lattice.dot

A Binomial Tree generated with genlattice and dotlattice.

These functions are a good starting point but we need more. The genlattice function must value the option and store the option price, as well as the asset price. It must accept the parameters which describe the option valuation scenarios, such as volatility, interest rates, payoff function and so on. The dotlattice function needs to render both asset and option prices to a desired level of precision. It must work with both European and American trees (the latter is different from the former by having an early exercise flag for each node).

The Vanilla Payoff Functions

We must have the payoff functions in hand before we proceed. These provide the boundary conditions for the equations and they will be useful for pricing both European and American options. Note, that these are vanilla options only for now.

European Option Valuation

I make a number of significant changes to genlattice function. Previously, we’ve been storing the asset value only at each node using a vector named X. Now we are going to store two values per node – the asset price and the option price. This can be achieved by using a data frame. To create a data frame, we first compute the number of nodes in the tree and pre-populate the frame with NA values. This is to make sure that our pricing algorithm does not leave any nodes untouched. This provides an invariant – after the valuation has finished, there should be no NA values in the frame.

We also need to match the up and down movement parameters with volatility since the latter is the thing we can at least hope to know. For this, the oldest and simplest Cox-Ross-Rubinstein method is used (CRR, 1979), where the is volatility and is a time step.

Where is the synthetic probability (or Q-measure).

Aplying the discussed changes results in the following function.

For convenience, I also define two functions which will serve as the interface to it.

American Option Valuation

For the American option, the early exercise flag must to be stored at every node as well. While it’s possible to have a unified implementation which values both European and American options, the amount of branching resulting in the code is not worth it, in my experience. The extra flags and branching sabotage clarity and ease of making changes. I take the view that any computer program that handles money must be as straightforward in its implementation as possible. So, without further ado, I present my American vanilla option valuator.

And just like before, there are two functions which will serve as the interface to it.

Plotting the tree

Thankfully, as far as plotting is concerned, the differences between a European and American trees are negligible – the latter needs some way of highlighting the nodes where early exercise happens. So I chose to keep a single dotlattice implementation which checks which type of tree it is dealing with. The facility to round the prices to a given number of digits after a decimal point has been introduced. I have also removed some unnecessary functionality such as printing the points instead of full nodes.

The results

Now the time has come to try some examples. Here is the complete, up-to-date code in a single file.

The first example is the European Call that we have seen in the introduction albeit with an increased number of steps.

> x <- genlattice.vanilla.european.call(Asset=810, Volatility=0.2, IntRate=0.05, DividendRate=0.02, Strike=800, Expiry=0.5, NoSteps=5)
> y <- capture.output(dotlattice(x, digits=4))
> cat(y, file="lattice.dot")

And through the graphviz.

$ dot -Tpng -o binomial-tree-european-call-2.png lattice.dot

The output is

A Binomial Tree Example 3

Another example is for a foreign currency, which can be thought of as an asset providing a yield at the foreign risk-free rate of interest.

The following Example 11.2 is from Hull 7th ed., p. 254

The Australian dollar is currently worth 0.6100 U.S. dollards and this exchange rate has a volatility of 12%. The Australian risk-free rate is 7% and the U.S. risk-free rate is 5%. […] 3-month American call option with a strike price of 0.6000 using a three-step tree.

We can value this using our framework

> x <- genlattice.vanilla.american.call(Asset = 0.6100, Volatility = 0.12, IntRate = 0.05, DividendRate = 0.07, Strike = 0.6, Expiry = 3/12, NoSteps = 3)
> y <- capture.output(dotlattice(x, digits=3))
> cat(y, file="lattice.dot")

And through the graphviz.

$ dot -Tpng -o binomial-tree-american-call-1.png lattice.dot

We get

A Binomial Tree Example 4

This matches the Figure 11.12 in Hull. What a relief!

What now?

The code that we now have opens up a number of interesting directions. The other related things which I would like to try:

  • Computing the Greeks in binomial tree
  • Binomial trees with skewness and curtosis
  • Matching volatility with methods other than Cox-Ross-Rubinstein
  • Trinomial trees
  • Non-recombining trees, path-dependent options
  • Power, cap options

I’ll post a write-up if I ever have the chance to look into them.

References

The books listed below all discuss binomial trees to some depth and have pictures like the ones I have created for this post.

  • Haug, E.G. (2007) The Complete Guide to Option Pricing Formulas. 2nd ed. Mc-Graw Hill
  • Hull, J.C. (2007) Options, Futures, and Other Derivatives. 7th ed. Pearson Education
  • Neftci, S.N. and Hirsa A. (2014) An Introduction to the Mathematics of Financial Derivatives. 3rd ed. Elsevier
  • Wilmott, P. (2006) Paul Wilmott on Quantitative Finance: Volume 1. 2nd ed. Wiley