Matt Matt - 5 months ago 17x
Python Question

Cython program with numpy arrays does not allow vectorized inputs (only accepts length 1 arrays), how to fix?

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
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)))

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
so others can build this typing:
python 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']

include_dirs=['.', numpy.get_include()],

The above compiles and works after using the fix on the variable type (Black_callput was actually an
) and you have to pass the numpy arrays as they are (no star) and it works correctly.


There were two issues here:

  1. TypeError: CyBlack() takes exactly 7 positional arguments (14 given)

    This was caused by the fact that you were unpacking your input arrays using the * ("splat") operator. You needed to pass them in as 7 separate arguments instead.

  2. ValueError: Buffer dtype mismatch, expected 'float64_t' but got 'long long':

    This error was telling you that one of the input arrays had a C type of long long (which is a 64 bit integer), rather than float64_tas specified in your input type declarations. You needed to cast all of your input arrays to np.float64.