invsqrt = lambda x: 1.0/sqrt(x) def black_scholes ( nopt, price, strike, t, rate, vol, call, put ): mr = -rate sig_sig_two = vol * vol * 2 for i in range(nopt): P = float( price [i] ) S = strike [i] T = t [i] a = log(P / S) b = T * mr z = T * sig_sig_two c = 0.25 * z y = invsqrt(z) w1 = (a - b + c) * y w2 = (a - b - c) * y d1 = 0.5 + 0.5 * erf(w1) d2 = 0.5 + 0.5 * erf(w2) Se = exp(b) * S call [i] = P * d1 - Se * d2 put [i] = call [i] - P + Se base_bs_erf.run("Naive-loop", black_scholes, 4, 8, nparr=False, pass_args=True)
for i in range(nopt): P = price[i] S = strike[i] T = t[i] a = log(P / S) b = T * mr z = T * sig_sig_two c = 0.25 * z y = 1. / sqrt(z) w1 = (a - b + c) * y w2 = (a - b - c) * y d1 = 0.5 + 0.5 * erf(w1) d2 = 0.5 + 0.5 * erf(w2) Se = exp(b) * S r = P * d1 - Se * d2 call[i] = r put[i] = r - P + Se if __name__ == '__main__': base_bs_erf.run("Numba@jit-loop", black_scholes, nparr=True, pass_args=True)
def black_scholes(nopt, price, strike, t, rate, vol, schd=None): mr = -rate sig_sig_two = vol * vol * 2 P = price S = strike T = t a = log(P / S) b = T * mr z = T * sig_sig_two c = 0.25 * z y = da.map_blocks(invsqrt, z) w1 = (a - b + c) * y w2 = (a - b - c) * y d1 = 0.5 + 0.5 * da.map_blocks(erf, w1) d2 = 0.5 + 0.5 * da.map_blocks(erf, w2) Se = exp(b) * S call = P * d1 - Se * d2 put = call - P + Se return da.compute(da.stack((put, call)), get=schd) base_bs_erf.run("Dask", black_scholes, dask=True)
z = T * sig_sig_two c = 0.25 * z y = 1. / sqrt(z) w1 = (a - b + c) * y w2 = (a - b - c) * y d1 = 0.5 + 0.5 * erf(w1) d2 = 0.5 + 0.5 * erf(w2) Se = exp(b) * S r = P * d1 - Se * d2 return complex(r, r - P + Se) black_scholes_numba_opt_vec = nb.vectorize( 'c16(f8,f8,f8,f8,f8)', target="parallel")(black_scholes_numba_opt) @nb.jit def black_scholes(nopt, price, strike, t, rate, vol): sig_sig_two = vol * vol * 2 mr = -rate black_scholes_numba_opt_vec(price, strike, t, mr, sig_sig_two) if __name__ == '__main__': base_bs_erf.run("Numba@vec-par", black_scholes, pass_args=False)
w1 = (a - b + c) * y w2 = (a - b - c) * y d1 = 0.5 + 0.5 * erf(w1) d2 = 0.5 + 0.5 * erf(w2) Se = exp(b) * S res = P * d1 - Se * d2 call[0] = res put[0] = res - P + Se black_scholes_numba_opt_vec = nb.guvectorize( '(f8[::1],f8[::1],f8[::1],f8[:],f8[:],f8[:],f8[::1],f8[::1])', '(),(),(),(),(),()->(),()', nopython=True, target="parallel", fastmath=False)(black_scholes_numba_opt) @nb.jit def black_scholes(nopt, price, strike, t, rate, vol, call, put): sig_sig_two = vol * vol * 2 mr = -rate black_scholes_numba_opt_vec(price, strike, t, mr, sig_sig_two, vol, call, put) base_bs_erf.run("Numba@guvec-par", black_scholes, pass_args=True)
S = strike T = t a = ne.evaluate("log(P / S) ") b = ne.evaluate("T * mr ") z = ne.evaluate("T * sig_sig_two ") c = ne.evaluate("0.25 * z ") y = ne.evaluate("1/sqrt(z) ") w1 = ne.evaluate("(a - b + c) * y ") w2 = ne.evaluate("(a - b - c) * y ") d1 = ne.evaluate("0.5 + 0.5 * erf(w1) ") d2 = ne.evaluate("0.5 + 0.5 * erf(w2) ") Se = ne.evaluate("exp(b) * S ") call = ne.evaluate("P * d1 - Se * d2 ") put = ne.evaluate("call - P + Se ") return call, put ne.set_num_threads(ne.detect_number_of_cores()) ne.set_vml_accuracy_mode('high') if "invsqrt" in numpy_ver: # XXX: find a better way base_bs_erf.run("Numexpr", black_scholes) else: print("Skipping current environment")
subprocess.call(['icc', '--version']) except: print("error building the extension module: Cython and Intel compiler are required") return False else: os.environ['CC'] = "icc" os.environ['LDSHARED'] = "icc -shared" # use high accuracy precision os.environ['CFLAGS'] = "-fimf-precision=high -prec-sqrt -qopt-report=5 -fno-alias -xhost -qopenmp -pthread -fno-strict-aliasing" setup( name = "bs_erf_cython_impl", ext_modules = cythonize("bs_erf_cython_impl.pyx"), include_dirs = [numpy.get_include()], script_args = ["build_ext", "--inplace"] ) import bs_erf_cython_impl as bsc return bsc try: import bs_erf_cython_impl as bsc except: bsc = build_ext() if not bsc: print("Skipping Cython version") else: base_bs_erf.run("Cython-parallel", bsc.black_scholes_par, pass_args=True) base_bs_erf.run("Cython-serial", bsc.black_scholes_ser, pass_args=True)
z = T * sig_sig_two c = 0.25 * z y = invsqrt(z) w1 = (a - b + c) * y w2 = (a - b - c) * y d1 = 0.5 + 0.5 * erf(w1) d2 = 0.5 + 0.5 * erf(w2) Se = exp(b) * S call = P * d1 - Se * d2 put = call - P + Se return np.stack((call, put)) def black_scholes_dask(nopt, price, strike, t, rate, vol, schd=None): return da.map_blocks(black_scholes, nopt, price, strike, t, rate, vol, new_axis=0).compute(get=schd) base_bs_erf.run("Dask-agg", black_scholes_dask, dask=True)
from base_bs_erf import erf, invsqrt def black_scholes ( nopt, price, strike, t, rate, vol, call, put ): mr = -rate sig_sig_two = vol * vol * 2 P = price S = strike T = t a = log(P / S) b = T * mr z = T * sig_sig_two c = 0.25 * z y = invsqrt(z) w1 = (a - b + c) * y w2 = (a - b - c) * y d1 = 0.5 + 0.5 * erf(w1) d2 = 0.5 + 0.5 * erf(w2) Se = exp(b) * S call[:] = P * d1 - Se * d2 put[:] = call - P + Se base_bs_erf.run("Numpy", black_scholes, nparr=True, pass_args=True)
def black_scholes(nopt, price, strike, t, rate, vol): mr = -rate sig_sig_two = vol * vol * 2 P = price S = strike T = t a = log(P / S) b = T * mr z = T * sig_sig_two c = 0.25 * z y = invsqrt(z) w1 = (a - b + c) * y w2 = (a - b - c) * y d1 = 0.5 + 0.5 * erf(w1) d2 = 0.5 + 0.5 * erf(w2) Se = exp(b) * S call = P * d1 - Se * d2 put = call - P + Se return (call, put) base_bs_erf.run("Numpy", black_scholes)
T = t[i] a = log(P / S) b = T * mr z = T * sig_sig_two c = 0.25 * z y = 1.0 / sqrt(z) w1 = (a - b + c) * y w2 = (a - b - c) * y d1 = 0.5 + 0.5 * erf(w1) d2 = 0.5 + 0.5 * erf(w2) Se = exp(b) * S r = P * d1 - Se * d2 call[i] = r put[i] = r - P + Se def black_scholes(nopt, price, strike, t, rate, vol, call, put): # offload blackscholes computation to CPU (toggle level0 or opencl driver). with dpctl.device_context(base_bs_erf.get_device_selector()): black_scholes_kernel(nopt, price, strike, t, rate, vol, call, put) # call the run function to setup input data and performance data infrastructure base_bs_erf.run("Numba@jit-loop-par", black_scholes)