Hier ist das schwarze Preismodell (Black Scholes abzüglich der Dividende) für Optionen auf Futures, geschrieben in Cython mit tatsächlichem Multi-Threading, aber ich kann es nicht ausführen. (JETZT FESTGELEGT, SIEHE SPÄTER NACH UNTEN ZUR ANTWORT). Ich verwende Python 3.5 mit Microsoft Visual Studio 2015 Compiler. Hier ist die Serienversion, die 3.5s für 10M Optionen nimmt: Cython program is slower than plain Python (10M options 3.5s vs 3.25s Black Scholes) - what am I missing?Cython parallel OpenMP für Black Scholes mit NumPy integriertem, seriellem Code 10M Optionen 3.5s, parallel?
ich versuchte, diese parallel zu machen, indem nogil
verwenden, aber nach dem Kompilieren, kann ich nicht Zugriff auf die interne Funktion CyBlackP
. Es gibt mehrere Probleme (zumindest unter Windows). 1) Cython geht bei der Generierung des OpenMP-Codes davon aus, dass Sie über V2.0 hinausgehen, aber Microsoft Visual Studio 2015 hängt an der alten Version fest, für die signierte Iteratoren erforderlich sind. Die Problemumgehung, die ich habe, ist nach dem ersten Versuch, den Code zu erstellen, wird es aus, öffnen Sie dann die Ausgabe CyBlackP.cpp
Datei in Microsoft Visual Studio 2015, suchen Sie nach size_t __pyx_t_2
(Zeile 1430), ändern Sie es dann in ssize_t __pyx_t_2
, und ändern Sie die nächste Zeile aus size_t __pyx_t_3
zu ssize_t __pyx_t_3
, um signierte/unsignierte Fehler zu entfernen und erneut zu kompilieren. 2) Sie können nicht direkt von NumPy-Arrays in die Funktion gehen, da nogil
nur auf reinen C/C++ - Funktionen funktioniert, also habe ich mehrere Hilfsfunktionen, um die NumPy-Array-Eingaben in C++ vector
-Format zu konvertieren, dann an eine C++ - Funktion übergeben wandle die zurückgegebene vector
zurück in ein NumPy-Array um. Ich poste den parallelen Code hier für andere, und ich bin mir sicher, dass jemand da draußen herausfinden kann, warum ich nicht auf die parallele Funktion von Python zugreifen kann - auf die nicht-parallele Version wurde so zugegriffen from CyBlackP.CyBlackP import CyBlackP
.
Code ist hier mit Schritten zum Erstellen. Erste Datei speichern unter CyBlackP.pyx
[beachten Sie die exposed-Funktion zu Python hier ist CyBlackP
, die die NumPy Eingabe Arrays in C-Vektoren durch die Hilfsfunktionen konvertiert, dann übergibt die C-Vektoren CyBlackParallel
, die mit nogil
und OpenMP läuft. Die Ergebnisse werden dann wieder zu einem NumPy Array umgewandelt und kehrten von CyBlackP
zu Python zurück]:
import numpy as np
cimport cython
from cython.parallel cimport prange
from libcpp.vector cimport vector
cdef extern from "math.h" nogil:
double exp(double)
double log(double)
double erf(double)
double sqrt(double)
cdef double std_norm_cdf(double x) nogil:
return 0.5*(1+erf(x/sqrt(2.0)))
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.cdivision(True)
cdef CyBlackParallel(vector[double] Black_PnL, vector[double] Black_S, vector[double] Black_Texpiry, vector[double] Black_strike, vector[double] Black_volatility, vector[double] Black_IR, vector[int] Black_callput):
cdef int i
N = Black_PnL.size()
cdef double d1, d2
for i in prange(N, nogil=True, num_threads=4, schedule='static'):
d1 = ((log(Black_S[i]/Black_strike[i]) + Black_Texpiry[i] * (Black_volatility[i] * Black_volatility[i])/2))/(Black_volatility[i] * sqrt(Black_Texpiry[i]))
d2 = d1 - Black_volatility[i] * sqrt(Black_Texpiry[i])
Black_PnL[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 Black_PnL
cdef vector[double] arrayToVector(np.ndarray[np.float64_t,ndim=1] array):
cdef long size = array.size
cdef vector[double] vec
cdef long i
for i in range(size):
vec.push_back(array[i])
return vec
cdef vector[int] INTarrayToVector(np.ndarray[np.int64_t,ndim=1] array):
cdef long size = array.size
cdef vector[int] vec
cdef long i
for i in range(size):
vec.push_back(array[i])
return vec
cdef np.ndarray[np.float64_t, ndim=1] vectorToArray(vector[double] vec):
cdef np.ndarray[np.float64_t, ndim=1] arr = np.zeros(vec.size())
cdef long i
for i in range(vec.size()):
arr[i] = vec[i]
return arr
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.cdivision(True)
cpdef CyBlackP(ndarray[np.float64_t, ndim=1] PnL, ndarray[np.float64_t, ndim=1] S0, ndarray[np.float64_t, ndim=1] Texpiry, ndarray[np.float64_t, ndim=1] strike, ndarray [np.float64_t, ndim=1] volatility, ndarray[np.float64_t, ndim=1] IR, ndarray[np.int64_t, ndim=1] callput):
cdef vector[double] Black_PnL, Black_S, Black_Texpiry, Black_strike, Black_volatility, Black_IR
cdef ndarray[np.float64_t, ndim=1] Results
cdef vector[int] Black_callput
Black_PnL = arrayToVector(PnL)
Black_S = arrayToVector(S0)
Black_Texpiry = arrayToVector(Texpiry)
Black_strike = arrayToVector(strike)
Black_volatility = arrayToVector(volatility)
Black_IR = arrayToVector(IR)
Black_callput = INTarrayToVector(callput)
Black_PnL = CyBlackParallel (Black_PnL, Black_S, Black_Texpiry, Black_strike, Black_volatility, Black_IR, Black_callput)
Results = vectorToArray(Black_PnL)
return Results
nächstes Codestück Speicher als setup.py
zur Verwendung durch Cython
:
try:
from setuptools import setup
from setuptools import Extension
except ImportError:
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
import numpy as np
ext_modules = [Extension("CyBlackP",sources=["CyBlackP.pyx"],
extra_compile_args=['/Ot', '/openmp', '/favor:INTEL64', '/EHsc', '/GA'],
language='c++')]
setup(
name= 'Generic model class',
cmdclass = {'build_ext': build_ext},
include_dirs = [np.get_include()],
ext_modules = ext_modules)
dann aus einer Eingabeaufforderung Typ: python setup.py build_ext --inplace --compiler=msvc
zu bauen.
Jede Hilfe beim Zugriff auf diese Funktion ist willkommen, ich bin mir nicht sicher, warum ich sie nach dem Kompilieren nicht finden kann. Ich kann import CyBlackP
oder from CyBlackP import *
aber ich kann nicht auf die eigentliche Funktion, um die Option Werte zu berechnen.
ist hier ein realistisches NumPy Testskript zu verwenden, wenn Sie diese Cython Funktion testen wollen:
BlackPnL = np.zeros(10000000)
Black_S=np.random.randint(200, 10000, 10000000)*0.01
Black_Texpiry=np.random.randint(1,500,10000000)*0.01
Black_strike=np.random.randint(1,100,10000000)*0.1
Black_volatility=np.random.rand(10000000)*1.2
Black_IR=np.random.rand(10000000)*0.1
Black_callput=np.sign(np.random.randn(10000000))
Black_callput=Black_callput.astype(np.int64)
* "Sie können nicht direkt von NumPy Arrays in die Funktion gehen, wie' nogil' funktioniert nur auf reine C/C++ - Funktionen "* - Sie können existierende numpy Arrays entweder als typisierte memoryviews (zB' double [:] array'') oder mit der alten Syntax '' np.darray [...] array'' übergeben, ohne die GIL zu erwerben, Sie können sie nur nicht instanziieren oder auf ihre Python-Methoden innerhalb eines 'nogil'-Blocks zugreifen. –
Obwohl die Funktion die NumPy-Arrays in C++ - Vektoren konvertiert, bevor die C++ - Funktion nur mit 'nogil' verarbeitet wird, heißt es, dass die Funktion cpdef überhaupt nicht Python zur Verfügung gestellt wird? Der Compiler kompiliert keinen 'nogil'-Block, wenn er ein Python-Objekt berührt, während dies kompiliert wird. – Matt
Ein NumPy-Array ist im Wesentlichen ein Python-Wrapper um einen Speicherpuffer, der direkt mit C/C++ - Funktionen in Verbindung gebracht werden kann - es ist keine Konvertierung erforderlich. Werfen Sie einen Blick auf die [Dokumentation hier] (http://docs.cython.org/src/userguide/memoryviews.html). Ich bin mir nicht sicher, ob dies direkt mit dem beschriebenen Problem zusammenhängt, aber die Beseitigung all dieser unnötigen Hilfsfunktionen würde Ihren Code erheblich vereinfachen und das Debugging vereinfachen. –