Greetings to my blog readers!
In this post I will share with you a fast Binomial Option Pricer. I’ve built this pricer without using trees or other bulky data structures and this is what makes it very fast. At the moment, the pricer is capable of pricing Vanilla Options. However, it can be easily extended to price barrier or digital options. This model is not suitable for pricing path-dependent (e.g. Asian options) yet. I will work on designing a model for path-dependent options later on. We’ll begin by revisiting the idea of the Binomial Model. Then, I explain how I designed my pricer. The pricer is coded in C# and split into five program files. Each file can be downloaded via links provided below. Please note my disclaimer.
Binomial Pricing Model
The Binomial Pricing Model (BPM) has been around for ages. It is based on the idea of risk-neutral world where the value of a portfolio of derivatives can be replicated with a portfolio of its underlying and bonds. The BPM is a discrete model in a sense that it breaks-up the time-period between now and the maturity date into discrete time-steps. Then, for each time-step the underlying spot price is evolved until maturity. The terminal underlying prices are used to obtain the first sequence of derivative’s values. Then, the BPM works backwards to the starting point. On each time-step the previously-obtained values of the derivative are used to calculated the newer values using the risk-neutral pricing formula (i.e. the discounted expectation). This is repeated until the first time-step is reached.
The BPM requires a tree-like structure to hold simulated prices of the underlying and, finally, to hold intermediate option prices. Let’s look at an example. Suppose I need to price a vanilla call option with the following parameters: current spot = £41.75; strike = £45.0; bond yield/rate = 0.535%; spot volatility = 34%; time to maturity = 2 months. The BPM requires me to simulate the spot price for some period covering 2 months. It is up to me into how many time-steps I divide this 2-months period. Let’s say I divide it into 2 periods. Then I need to simulate something like this:
So, S0 is the current spot, S1 and S2 are simulated for each corresponding time-node. Each node is a result of the spot price moving up or down. There are two BPM types: the multiplicative and the additive. In this post I am only showing you the multiplicative model, which means that we have up and down factors that are multiplied into the current spot price to obtain the new spot. For example, if up factor is 1.103 and down factor is 0.9065 then our simulated two-step tree will look like this:
where 46.05 was obtained by multiplying 41.75 into 1.103, and so on. The spot underlying prices at time-step 2 can be used to price the call option with the following payoff: Max(X-K, 0), where X is a given terminal spot and K is the specified strike (e.g. we have specified £45.0). So, in terms of call prices at time S2 we have £5.80 for up_up, £0.0 for up_down and £0.0 for down_down. We now take these call prices and work back to the starting point. At this point we need the risk-neutral option pricing formula. In the BPM it takes this form:
where r is the bond yield/rate, delta t is the single time-step expressed in times to maturity, p is the risk-neutral probability of the price of the underlying to increase at the next time-step, and Vu and Vd are the option prices we have just calculated. In practice, both, the probability of price increase and the up/down factors are dictated by the SDE model. The supplied code is configured to work for Jarrow&Rudd, Cox&Ross&Rubinstein and the BS cases. Once we have the time S1 option prices, we proceed to calculate the time S0 option price by using the same risk-neutral formula and the time S1 option prices.
Designing Binomial Option Model for Speed
As far as coding the BPM, we need to make decisions about how to represent the simulated spot prices, how to represent the intermediate option prices, and how to glue together components that do the following: simulate price of the underlying for various SDEs, represent option payoff and the workings of the binomial model itself. Ideally, a large number of simulations should be possible (e.g. N=1,000). Taking a binary tree to represent simulated prices is not feasible because it would require a tree with 2^N -1 nodes. A key to a fast BPM is to recognize three things:
- We don’t need to store intermediate spot prices, only the final spot prices.
- The binomial tree is a recombining tree due to its discrete nature.
- Two price paths that are different by a single time outcome (i.e. up or down) are adjacent to each other.
The adopted design can be imagined as the following:
The multiplicative model lends itself to a use of powers. Thus a price path where spot is increasing on each step is calculated as S*u*u*u…*u, etc. Because the paths that differ by a single outcome are adjacent, we don’t need a fancy data structure to store terminal prices of the underlying. A plain array of doubles works just fine. Finally, to implement the binomial model, we utilize only two arrays. The first array is populated with the first option prices. The second array is used to calculate option prices for one step backwards. While the original is now storing the copies of the second array one value at a time.
The full BPM project implemented in C# can be accessed via these links:
An UPDATE: a careful reader noted that the SDE link was wrong. The SDE file now lets you download the correct file. Also, a very useful suggestion was made by the same reader – to replace the inner body of the double for loop in BinomialPricer.GetPrice with the following assignment. Indeed, the time it takes to price the example vanilla option drops from 3,833 to 2,299 milliseconds on my PC in debug release. Thank you!
for (int i = timeSteps; i>=0; i--)
for (int j=0; j<nodes; j++)
//tempOptionPrices[j] = instDRate*(upProb*firstOptionPrices[j] + downProb*firstOptionPrices[j + 1]);
//firstOptionPrices[j] = tempOptionPrices[j]; // reuse firstoptionprices array for next iteration
firstOptionPrices[j] = instDRate * (upProb * firstOptionPrices[j] + downProb * firstOptionPrices[j + 1]);