Binomial Options Pricing Model Code for Intel® Xeon Phi™ Coprocessor

Introduction

The Binomial Options Pricing Model (BOPM) is a generalized numerical method used to value options in the quantitative Financial Services industry. To be accurate, 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 reason, it’s also known as a tree model because it has a root and the leave-nodes. In this paper, we continue to follow tree-analogy convention knowing that it’s a lattice approach in reality.

The binomial model was first proposed by Cox, Ross, and Rubenstein in 1979 [1]. Binomial option pricing is a simple but powerful technique that can be used to solve many complex option-pricing problems. 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™ coprocessor (referred to as “coprocessor” in this document) in a single node environment.

To get access to the code and test workloads:
Download the Binomialsrc.tar file

Build Directions

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

  1. You have to install Intel Composer XE 2013 SP 3 in your system.
  2. Source the environment variable script file compilervars.csh under /pkg_bin
  3. Untar the binomialsrc.tar, execute build_sh script file with csh/tcsh shell
  4. Invoke executable file ./Binomial

Run Directions


	./Binomial [verbose] [precision] 

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
Binomial Option Pricing in single precision:
Pricing 36600000 Option with time step of 2048.
Benchmarking the reference result in single...
Completed in  256.17960 seconds. Options per second: 142868.51654

[command prompt]$ ./Binomial 1 1
Initializing data...
...allocating memory for options.
...generating input data in mem.
Creating the reference result...
Benchmarking the reference result...
Binomial Option Pricing in double precision:
Pricing 36600000 Option with time step of 2048.
Benchmarking the reference result in double...
Completed in  514.10377 seconds. Options per second: 71191.85352
L1 norm: 2.717881E-05
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™ Coprocessor. The products involved are Intel E2697V2 at 2.7 Ghz and Intel® Xeon Phi™ Coprocessor 7120P. Intel® Composer 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 a Intel Xeon Phi Coprocessor. 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

As previously stated, 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.

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 Hull[3] has elaborated 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];
        }

}

In the hybrid version of the code. The program execute simultaneously on the host Intel Xeon processors and on the Intel Xeon Phi coprocessors. The main program instantiates the above code twice, once to run on the host processor, another to run on MIC coprocessor. It uses asynchronous offload capability from Intel Compiler so that host and coprocessor can work concurrently.

About the Author

Shuo Li works at Software and Service Group at Intel Corporation. His main interest is parallel programming, and application software performance. In his recent 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 university of Oregon and an MBA degree from the Duke University.

References and Resources

[1]Option Pricing: A Simplified Approach (1979) by John C. Cox, Stephen A. Ross, and Mark Rubinstein:
[2]The Pricing of Options and Corporate Liabilities (May-Jun 1973) by Fischer Black and Myron Scholes:
[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
[5]Intel Xeon Phi™ Coprocessor: https://software.intel.com/en-us/articles/quick-start-guide-for-the-intel-xeon-phi-coprocessor-developer

有关编译器优化的更完整信息,请参阅优化通知