# Recipe: Using Binomial Option Pricing Code as Representative Pricing Derivative Method

By shuo-li, published on June 15, 2016

## Introduction

The Binomial Options Pricing Model (BOPM) is a generalized numerical method used to value options in the quantitative financial services industry. Specifically, it is a lattice-based approach that uses a discrete-time model of the varying price over time of the underlying financial instrument. For historical reasons, it’s also known as a tree model because it has a root and leaf-nodes. In this paper, we continue to follow tree-analogy convention knowing that it is more truly a lattice approach.

Binomial option pricing is a simple but powerful technique that can be used to solve many complex option-pricing problems. The binomial model was first proposed by Cox, Ross, and Rubenstein in 1979^{1}. The name was derived from the construction of a binomial tree that models different possible paths that might be followed by the underlying asset price over the time span of the option.

This article presents the Binomial Option Pricing Code to provide a representative way of pricing derivatives using lattice methods.

## Code Access

Binomial Option Pricing Code is maintained by Shuo Li and is available under the BSD 3-Clause Licensing Agreement. The code supports the asynchronous offload of the Intel® Xeon® processor (referred to as “host” in this document) with the Intel® Xeon Phi™ processor in a single node environment.

To get access to the code and test workloads, download the Binomial.tar file.

## Build Directions

Here are the steps you need to follow in order to rebuild the program:

- Install Intel® Parallel Studio XE 2015 SP3 in your system
- Source the environment variable script file compilervars.sh under /pkg_bin
- Untar the binomial.tar and type
`make`

to build the executables files

You should expect to see binomial.knl and binomial.bdw - Make sure the host machine is powered by Intel Xeon Phi processors
[sli@ortce-knl7 ~]$ lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 272 On-line CPU(s) list: 0-271 Thread(s) per core: 4 Core(s) per socket: 68 Socket(s): 1 NUMA node(s): 8 Vendor ID: GenuineIntel CPU family: 6 Model: 87 Model name: Intel(R) Xeon Phi(TM) CPU 7250 @ 1.40GHz Stepping: 1 CPU MHz: 1400.273 BogoMIPS: 2793.61 Virtualization: VT-x L1d cache: 32K L1i cache: 32K L2 cache: 1024K NUMA node0 CPU(s): 5,6,11,12,17,18,23-25,73-86,135-148,197-210,259-271 NUMA node1 CPU(s): 1,2,7,8,13,14,19,20,43-58,105-120,167-182,229-244 NUMA node2 CPU(s): 3,4,9,10,15,16,21,22,59-72,121-134,183-196,245-258 NUMA node3 CPU(s): 0,26-42,87-104,149-166,211-228For Double Precision processing: BlackScholesDP.kn

- Run binomial.knl

## Running the Binomial

At the prompt, enter:

`./binomial.knl [verbose] [precision]`

Values:

`verbose`

`0: turn off workload feedback and result validation (default)`

`1: turn on workload feedback and result validation`

`precision`

`0: single precision (default)`

`1: double precision`

[command prompt]$ ./binomial [sli@ortce-knl8 clean]$ ./binomial.knl Binomial Single Precision. Compiler Version = 16 Release Update = 3 Build Time = May 27 2016 16:08:12 Input Dataset = 557056 Binomial Option Pricing in single precision: Pricing 557056 Option with time step of 2048.

Binomial Option Pricing using Pragma SIMD Vectorization. Completed in 2.39326 seconds. Options per second: 232760.03965 [sli@ortce-knl8 clean]$ ./binomial.knl 0 1 Binomial Single Precision. Compiler Version = 16 Release Update = 3 Build Time = May 27 2016 16:08:12 Input Dataset = 557056 Binomial Option Pricing in double precision: Pricing 557056 Option with time step of 2048.

Binomial Option Pricing using Pragma SIMD Vectorization. Completed in 6.84975 seconds. Options per second: 81325.03577 TEST PASSED

The program created and priced 36,600,000 sets of option data. The program was vectorized and parallelized to run on both the Intel Xeon processors and Intel Xeon Phi processor. The products involved are Intel E2697V2 at 2.7 Ghz and Intel Xeon Phi processor 7120P. Intel® Parallel Studio XE 2013 SP1 is used to build the final executable program.

This program calculates the European call option price for each input data set. This benchmark runs on single node with or without an Intel Xeon Phi processor. It can also be modified to run in a cluster environment. The program reports a latency measure in seconds spent in pricing activities and also a throughput measure using 36.6 million options. Verbose mode provides additional information and also validates the results using a Black-Scholes-Merton^{2} formula.

## Background

Binomial option pricing is a simple but powerful technique used to solve complex option-pricing problems. Unlike the Black-Scholes-Merton and other complex option pricing models, it does not require solutions to stochastic differential equations; the binomial option-pricing model is mathematically trivial. Anyone with a good understanding of the time value of money and risk-neutral valuation should be able to understand the binomial model.

Figure 1.

Consider a stock with the initial value of **S _{0}** and an option

**f**with lifetime

**T**. During the life of the option, the stock price can either move up from

**S**to a new level,

_{0}**S**where

_{0}u**u > 1**(going up by

**u-1**percent), or it can move down from

**S**to a new level

_{0}**S**where

_{0}d**d < 1**(going down by

**1-d**percent).

Let’s suppose that when the stock price moves to **S _{0}u**, the payoff from option is

**f**; when the stock price moves down to

_{u}**S**, the payoff from the option is

_{0}d**f**. Binomial valuation starts by creating a riskless portfolio consisting of a long option in Δ unit shares of the underlying stocks and a short position in one unit of option. When the stock moves up, the value of the portfolio becomes

_{d}**S**Δ

_{0}u**-f**; when the stock moves down, the value of the portfolio becomes

_{u}**S**Δ

_{0}d**–f**. Using no arbitrage argument, these two cases should be the equal value or:

_{d}**S _{0}u Δ -f_{u} = S_{0}d Δ –f_{d}**

Therefore:

The risk-neutral argument also dictates that all portfolios shall earn the risk-free interest rate. Since we know the risk-free interest * r*, the current value of the portfolio when the stock goes up can be written as follows:

*S _{0}u* –

*f*)

_{u}*e*

^{-rT}This must equal the cost of setting up the portfolio:

*S _{0}Δ - f = (S_{0}u –f_{u})e^{-rT}*

Thus:

*f = S _{0}Δ - (S_{0}u –f_{u})e-^{rT}*

Since we know **Δ**, we can further reduce this equation as follows:

*f = e ^{-rT }[pf_{u} + (1-p)f_{d} ]*

where

With the last two equations, we can naturally interpret the variable * p* as the probability of an upward movement in the underlying stock price. Thus,

**1**is the probability of a downward movement. We can further state that the value of the option today is the expected future payoff discounted at the risk-free rate.

*-p*The last challenge in creating a binomial option-pricing algorithm is to integrate the stock price movement process into the binomial tree parameters * u* and

*. Using the same assumption as the Black-Scholes model, which Hull*

**d**^{3}has elaborated on in Chapter 14 of his book, we can construct a binomial tree by using the underlying stock price volatility

**σ**. Cox, Ross, and Rubinstein (1979) proposed the following set of

**u**and

**d**:

These values not only match the volatility with the up and down movement of stock price but also make the binomial tree recombinant in the sense that the nodes that represent a stock moving up then down and the stock price moving down then up, will be merged or recombined as a single node. This property also spares us from the elaborated process to propagate the internal node values to the leaf nodes; instead it gives a closed-end formula for each leaf node.

Now we can easily extend the one-step binomial tree into a two-step binomial tree, three-step binomial tree, and N-step binomial tree. The previous picture shows a perfectly balanced N-step binomial tree, where each initial node has exactly two child nodes representing an upward movement and a downward movement of the stock price, respectively.

The probability of an upward movement is * p* and the probability of downward movement is

**1-**

*. The N-Step binomial tree has*

**p****N+1**level; each level has one less node than its previous level and one more node than its next level. The number of leaf nodes for N-Step binomial tree is

**N+1**.

## Implementation of Binomial Tree Options Pricing

Binomial option pricing can be conceptually described as a three-step process:

- Binomial tree representation and generation
- Calculation of option values at the leaf nodes
- Backward inductive calculation of option value at internal nodes; the value at the root node is the current price of the option

The next step is to determine a data structure that can represent all the tree nodes. Since the binomial tree model involves calculations on adjacent levels, the minimum data structure required is the one that holds all the node values in one level, including the leaf nodes level, which has the number of nodes equal to the number of time steps **+1**. Depending on the application precision requirement, we can choose a one-dimensional, single-precision, floating-point array to accomplish this purpose.

`static float Call[NUM_STEPS+1];`

## Binomial Option Pricing Implementation - European Options

The following code gives the implementation of Binomial Tree European Option Pricing in C/C++.

#include <mathimf.h> #include <omp.h> #include <stdio.h> #define NUM_STEPS 2048 #define OPT_N 18300000 const float RISKFREE = 0.06f; const float VOLATILITY = 0.10f; __forceinline float Exp( float x) { return expf( x); } __forceinline double Exp( double x) { return exp( x); } __forceinline float Sqrt( float x) { return sqrtf( x); } __forceinline double Sqrt( double x) { return sqrt( x); } template <class Basetype> void BinomialTemplate( Basetype *CallResult, Basetype *S, Basetype *X, Basetype *T) { #pragma omp parallel for for(int opt = 0; opt < OPT_N; opt++) { __attribute__((align(4096)))Basetype Call[NUM_STEPS + 1]; const Basetype Sx = S[opt]; const Basetype Xx = X[opt]; const Basetype Tx = T[opt]; const Basetype dt = Tx / static_cast<Basetype>(NUM_STEPS); const Basetype vDt = VOLATILITY * Sqrt(dt); const Basetype rDt = RISKFREE * dt; const Basetype If = Exp(rDt); const Basetype Df = Exp(-rDt); const Basetype u = Exp(vDt); const Basetype d = Exp(-vDt); const Basetype pu = (If - d) / (u - d); const Basetype pd = 1.0f - pu; const Basetype puByDf = pu * Df; const Basetype pdByDf = pd * Df; #pragma simd for(int i = 0; i <= NUM_STEPS; i++) { Basetype d = Sx * Exp(vDt * (2.0 * i - NUM_STEPS)) - Xx; Call[i] = (d > 0) ? d : 0; } for(int i = NUM_STEPS; i > 0; i--) #pragma simd #pragma unroll(4) for(int j = 0; j <= i - 1; j++) Call[j] = puByDf * Call[j + 1] + pdByDf * Call[j]; CallResult[opt] = (Basetype)Call[0]; } }

## About the Author

Shuo Li works at Software and Services Group at Intel Corporation. His main interest is parallel programming, and application software performance. In his role as a software performance engineer covering financial service industry, Shuo works closely with software developers and modelers and help them achieve best possible performance with their software solutions.

Shuo holds a Master's degree in Computer Science from the University of Oregon and an MBA degree from Duke University.

## References and Resources

1 Cox, John C., Ross, Stephen A., and Rubenstein, Mark. *Option Pricing: A Simplified Approach* (1979).

2 Black, Fischer, and Scholes, Myron. *The Pricing of Options and Corporate Liabilities* (May-Jun 1973).

3 Hull, John C, *Options, Futures, and other Derivatives,10 ^{th} Edition* Prentice-Hull, 2014

4 Intel Xeon processor: http://www.intel.com/content/www/us/en/processors/xeon/xeon-processor-e7-family.html