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:
<module_name>.pyx
).18.219.246.211