From 12e76d76a5434572e5a80ee5eed8e9a17e5d7c57 2014-04-08 17:21:58 From: Erik Tollerud Date: 2014-04-08 17:21:58 Subject: [PATCH] Add a pure-python version of the Black-Scholes function for comparison with the Cython equivalent --- diff --git a/examples/Builtin Extensions/Cython Magics.ipynb b/examples/Builtin Extensions/Cython Magics.ipynb index f847641..4a9cd51 100644 --- a/examples/Builtin Extensions/Cython Magics.ipynb +++ b/examples/Builtin Extensions/Cython Magics.ipynb @@ -1,6 +1,7 @@ { "metadata": { - "name": "Cython Magics" + "name": "", + "signature": "sha256:c357b93e9480d6347c6677862bf43750745cef4b30129c5bc53cb879a19d4074" }, "nbformat": 3, "nbformat_minor": 0, @@ -79,6 +80,7 @@ "metadata": {}, "outputs": [ { + "metadata": {}, "output_type": "pyout", "prompt_number": 3, "text": [ @@ -126,6 +128,7 @@ "metadata": {}, "outputs": [ { + "metadata": {}, "output_type": "pyout", "prompt_number": 5, "text": [ @@ -161,12 +164,12 @@ "from libc.math cimport exp, sqrt, pow, log, erf\n", "\n", "@cython.cdivision(True)\n", - "cdef double std_norm_cdf(double x) nogil:\n", + "cdef double std_norm_cdf_cy(double x) nogil:\n", " return 0.5*(1+erf(x/sqrt(2.0)))\n", "\n", "@cython.cdivision(True)\n", - "def black_scholes(double s, double k, double t, double v,\n", - " double rf, double div, double cp):\n", + "def black_scholes_cy(double s, double k, double t, double v,\n", + " double rf, double div, double cp):\n", " \"\"\"Price an option using the Black-Scholes model.\n", " \n", " s : initial stock price\n", @@ -181,8 +184,8 @@ " with nogil:\n", " d1 = (log(s/k)+(rf-div+0.5*pow(v,2))*t)/(v*sqrt(t))\n", " d2 = d1 - v*sqrt(t)\n", - " optprice = cp*s*exp(-div*t)*std_norm_cdf(cp*d1) - \\\n", - " cp*k*exp(-rf*t)*std_norm_cdf(cp*d2)\n", + " optprice = cp*s*exp(-div*t)*std_norm_cdf_cy(cp*d1) - \\\n", + " cp*k*exp(-rf*t)*std_norm_cdf_cy(cp*d2)\n", " return optprice" ], "language": "python", @@ -194,12 +197,13 @@ "cell_type": "code", "collapsed": false, "input": [ - "black_scholes(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)" + "black_scholes_cy(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)" ], "language": "python", "metadata": {}, "outputs": [ { + "metadata": {}, "output_type": "pyout", "prompt_number": 7, "text": [ @@ -210,10 +214,75 @@ "prompt_number": 7 }, { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For comparison, the same code is implemented here in pure python." + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "from math import exp, sqrt, pow, log, erf\n", + "\n", + "def std_norm_cdf_py(x):\n", + " return 0.5*(1+erf(x/sqrt(2.0)))\n", + "\n", + "def black_scholes_py(s, k, t, v, rf, div, cp):\n", + " \"\"\"Price an option using the Black-Scholes model.\n", + " \n", + " s : initial stock price\n", + " k : strike price\n", + " t : expiration time\n", + " v : volatility\n", + " rf : risk-free rate\n", + " div : dividend\n", + " cp : +1/-1 for call/put\n", + " \"\"\"\n", + " d1 = (log(s/k)+(rf-div+0.5*pow(v,2))*t)/(v*sqrt(t))\n", + " d2 = d1 - v*sqrt(t)\n", + " optprice = cp*s*exp(-div*t)*std_norm_cdf_py(cp*d1) - \\\n", + " cp*k*exp(-rf*t)*std_norm_cdf_py(cp*d2)\n", + " return optprice" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 8 + }, + { "cell_type": "code", "collapsed": false, "input": [ - "%timeit black_scholes(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)" + "black_scholes_py(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 9, + "text": [ + "10.327861752731728" + ] + } + ], + "prompt_number": 9 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Below we see the runtime of the two functions: the Cython version is nearly a factor of 10 faster." + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "%timeit black_scholes_cy(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)" ], "language": "python", "metadata": {}, @@ -222,11 +291,30 @@ "output_type": "stream", "stream": "stdout", "text": [ - "1000000 loops, best of 3: 821 ns per loop\n" + "1000000 loops, best of 3: 319 ns per loop\n" ] } ], - "prompt_number": 8 + "prompt_number": 10 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "%timeit black_scholes_py(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "100000 loops, best of 3: 2.28 \u00b5s per loop\n" + ] + } + ], + "prompt_number": 11 }, { "cell_type": "heading", @@ -262,7 +350,7 @@ ] } ], - "prompt_number": 9 + "prompt_number": 12 }, { "cell_type": "markdown", @@ -275,4 +363,4 @@ "metadata": {} } ] -} +} \ No newline at end of file