Matt Matt - 6 months ago 48
Python Question

Cython compiled module does not allow access to the function defined 'module' not callable

ORIGINAL ERROR FOUND ->

from CyBlack.CyBlack import CyBlack
then pass
*numpy_value
as inputs. A new issue has arisen so creating a new post for that.

I am new to Cython and tried converting the Black (Black Scholes without a stock dividend) over from Python to Cython. After getting it to compile, it doesn't let me actually use the function. I'm sure someone with more experience can look at this easily and figure out why. The error I get after compiling and importing the function
from CyBlack import CyBlack
and calling
CyBlack(BlackPnL, Black_S, Black_Texpiry, Black_strike, Black_volatility, Black_IR, Black_callput)
is
TypeError: 'module' object is not callable
: So here's the code:

from numpy cimport ndarray
cimport numpy as np
cimport cython

cdef extern from "math.h":
double exp(double)
double sqrt(double)
double pow(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)
cdef 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.float64_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


Thanks for any help here! I can post python fake data if you need something to test off of - although just calling it with any data will expose the error... Something points me to the variables not being exposed to Python from the C code.

Adding my
setup.py
here 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

setup(ext_modules = cythonize("CyBlack.pyx"), include_dirs =["C://Program Files (x86)//Microsoft Visual Studio 14.0//VC//include", "C://Program Files (x86)//Windows Kits//10//Include//10.0.1.0240.0//ucrt", "C://Program Files (x86)//Microsoft Visual Studio 14.0//VC//lib//amd64", "C://Anaconda3//Lib//site-packages//numpy//core//include", "C://Program Files (x86)//Microsoft Visual Studio 14.0//VC//lib//amd64"])

val val
Answer

I managed to get (something like it) working in the following way:

C:/dev/tmp/CyBlack/
                   __init__.py
                   setup.py
                   CyBlack.pyx

Where CyBlack.pyx is just like yours, except with CyBlack function being cpdef'd. setup.py contains:

from distutils.core import setup
import numpy
from Cython.Build import cythonize

extra_compile_args = ['/EHsc', '/openmp', '/favor:INTEL64']

setup(
    ext_modules=cythonize("CyBlack.pyx"),
    include_dirs=['.', numpy.get_include()],
    extra_compile_args=extra_compile_args
)

Running then:

C:\dev\tmp\CyBlack> python .\setup.py build_ext --compiler=msvc --inplace

Will produce C:/dev/tmp/CyBlack/CyBlack.pyd, and I then managed to run the code from Python:

>>> from sys import path
>>> path.insert(0, "C:/dev/tmp")
>>> from CyBlack.CyBlack import CyBlack
>>> CyBlack(*[np.array([1.0]) for _ in xrange(7)]) # I'm too lazy to put proper values here...
array([ 0.14087021])

This was done with Python 2.7, and overall setup might be slightly different, but hopefully, that might help you getting a minimum working example and trace back how to make yours run correctly.