FIXED: see updated code below.
This is my first Cython attempt and have a working build but it doesn't allow numpy array (vectors) as inputs, which is my real purpose here. It is the Black model (Black Scholes for European option pricing without a dividend). It will only accept length 1 arrays as inputs (if I try to calculate with length 2 arrays it errors: TypeError: CyBlack() takes exactly 7 positional arguments (14 given)
, if I pass length 10 arrays, (70 given)
, etc.). I'm not sure why as I have defined numpy arrays within the Cython code. For reference you can compile it with the below code then use it as such: from CyBlack.CyBlack import CyBlack
then calling CyBlack(BlackPnL, Black_S, Black_Texpiry, Black_strike, Black_volatility, Black_IR, Black_callput)
So here's the code (save as CyBlack.pyx
file to compile):
from numpy cimport ndarray
cimport numpy as np
cimport cython
cdef extern from "math.h":
double exp(double)
double sqrt(double)
double log(double)
double erf(double)
cdef double std_norm_cdf(double x):
return 0.5*(1+erf(x/sqrt(2.0)))
@cython.boundscheck(False)
cpdef CyBlack(ndarray[np.float64_t, ndim=1] BlackPnL, ndarray[np.float64_t, ndim=1] Black_S, ndarray[np.float64_t, ndim=1] Black_Texpiry, ndarray[np.float64_t, ndim=1] Black_strike, ndarray [np.float64_t, ndim=1] Black_volatility, ndarray[np.float64_t, ndim=1] Black_IR, ndarray[np.int64_t, ndim=1] Black_callput):
cdef Py_ssize_t i
cdef Py_ssize_t N = BlackPnL.shape[0]
cdef double d1, d2
for i in range(N):
d1 = ((log(Black_S[i] / Black_strike[i]) + Black_Texpiry[i] * Black_volatility[i] **2 / 2)) / (Black_volatility[i] * sqrt(Black_Texpiry[i]))
d2 = d1 - Black_volatility[i] * sqrt(Black_Texpiry[i])
BlackPnL[i] = exp(-Black_IR[i] * Black_Texpiry[i]) * (Black_callput[i] * Black_S[i] * std_norm_cdf(Black_callput[i] * d1) - Black_callput[i] * Black_strike[i] * std_norm_cdf(Black_callput[i] * d2))
return BlackPnL
Here is the setup.py
so others can build this typing: python setup.py build_ext --inplace
built with VS2015 for Python 3.5 64bit Windows.
from distutils.core import setup
from Cython.Build import cythonize
import numpy
extra_compile_args = ['/EHsc', '/openmp', '/favor:INTEL64']
setup(
ext_modules=cythonize("CyBlack.pyx"),
include_dirs=['.', numpy.get_include()],
extra_compile_args=extra_compile_args)
The above compiles and works after using the fix on the variable type (Black_callput was actually an int64
) and you have to pass the numpy arrays as they are (no star) and it works correctly.