NumPy and Cython

Cython has built-in support to provide faster access to NumPy arrays. These facilities make Cython an ideal candidate to optimize NumPy code. For this section, we will study code that calculates the price of the European option, a financial instrument using the Monte-Carlo technique. Knowledge of finance is not expected; however, we assume you have a basic understanding of Monte-Carlo simulations:

defprice_european(strike = 100, S0 = 100, time = 1.0,  
rate = 0.5, mu = 0.2, steps = 50,  
N = 10000, option = "call"): 
 
dt = time / steps 
rand = np.random.standard_normal((steps + 1, N)) 
S = np.zeros((steps+1, N)); 
S[0] = S0 
 
for t in range(1,steps+1): 
S[t] = S[t-1] * np.exp((rate-0.5 * mu ** 2) * dt 
+ mu * np.sqrt(dt) * rand[t]) 
price_call = (np.exp(-rate * time) 
* np.sum(np.maximum(S[-1] - strike, 0))/N) 
price_put = (np.exp(-rate * time) 
* np.sum(np.maximum(strike - S[-1], 0))/N) 
 
returnprice_call if option.upper() == "CALL" else price_put 

The following is the Cythonized code for the preceding example:

import numpy as np 
def price_european_cython(double strike = 100,doubleS0 = 100, 
                          double time = 1.0, double rate = 0.5, 
                          double mu = 0.2, int steps = 50,  
                   long N = 10000, char* option = "call"): 
   cdef double dt = time / steps 
   cdefnp.ndarray rand = np.random.standard_normal((steps + 1, N)) 
   cdefnp.ndarray S = np.zeros([steps+1, N], dtype=np.float) 
       #cdefnp.ndarrayprice_call = np.zeroes([steps+1,N],     dtype=np.float) 
       S[0] = S0 
 
   for t in xrange(1,steps+1): 
           S[t] = S[t-1] * np.exp((rate-0.5 * mu ** 2) * dt 
                               + mu * np.sqrt(dt) * rand[t]) 
           price_call = (np.exp(-rate * time) 
                  * np.sum(np.maximum(S[-1] - strike, 0))/N) 
           price_put = (np.exp(-rate * time) 
                  * np.sum(np.maximum(strike - S[-1], 0))/N) 
 
   return price_call if option.upper() == "CALL" else price_put 

And the setup file for this looks like:

from distutils.core import setup, Extension 
from Cython.Build import cythonize 
from Cython.Distutils import build_ext 
import numpy.distutils.misc_util 
 
include_dirs = numpy.distutils.misc_util.get_numpy_include_dirs() 
 
 
setup( 
 
name="numpy_first", 
    version="0.1", 
ext_modules=[Extension('dynamic_BS_MC', 
                           ['dynamic_BS_MC.pyx'], 
include_dirs = include_dirs)], 
cmdclass={'build_ext': build_ext} 
) 

While the speed-up gained from Cythonizing the code is great and you may be tempted to write most of the code in Cython, it is recommended to convert only the performance-critical parts to Cython. NumPy has done a great job in optimizing access to arrays and performing faster calculations. This code can be taken as an ideal candidate for depicting the same. The preceding code has a lot of "loose ends" and can be treated as an exercise for you to fix performance issues in Python and using NumPy optimally first before going the Cython way. The speed enhancement from blindly Cythonizing the NumPy code might not be as huge as that for an optimally written code with genuine problems due to the dynamic nature of Python.

To conclude, we present the following that you should follow while developing modules in Cython:

  1. Write code in pure Python and test it.
  2. Run profilers and identify key areas to focus on.
  3. Create a new module to hold Cython code (<module_name>.pyx).
  4. Convert all variables and loop indices in these areas to their C counterparts.
  5. Test using your previous test setups.
  6. Add the extensions into setup files.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.219.246.211