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

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 19791. 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:

  1. Install Intel® Parallel Studio XE 2015 SP3 in your system
  2. Source the environment variable script file compilervars.sh under /pkg_bin
  3. Untar the binomial.tar and type make to build the executables files
    You should expect to see binomial.knl and binomial.bdw
  4. 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
    		
  5. 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-Merton2 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 S0 and an option f with lifetime T. During the life of the option, the stock price can either move up from S0 to a new level, S0u where u > 1 (going up by u-1 percent), or it can move down from S0 to a new level S0d where d < 1 (going down by 1-d percent).

Let’s suppose that when the stock price moves to S0u, the payoff from option is fu; when the stock price moves down to S0d, the payoff from the option is fd. 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 S0u Δ -fu; when the stock moves down, the value of the portfolio becomes S0d Δ –fd. Using no arbitrage argument, these two cases should be the equal value or:

S0u Δ -fu = S0d Δ –fd

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:

S0ufu) e-rT

This must equal the cost of setting up the portfolio:

S0Δ - f = (S0u –fu)e-rT

Thus:

f = S0Δ - (S0u –fu)e-rT

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

f = e-rT [pfu + (1-p)fd ]

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-p 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.

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 d. Using the same assumption as the Black-Scholes model, which Hull3 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-p. The N-Step binomial tree has 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:

  1. Binomial tree representation and generation
  2. Calculation of option values at the leaf nodes
  3. 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,10th Edition Prentice-Hull, 2014
4 Intel Xeon processor: http://www.intel.com/content/www/us/en/processors/xeon/xeon-processor-e7-family.html

Intel Sample Source Code License Agreement

Para obter informações mais completas sobre otimizações do compilador, consulte nosso aviso de otimização.