Commit d716f8a8 authored by Erik Tollerud's avatar Erik Tollerud Committed by Martín Gaitán

Add a pure-python version of the Black-Scholes function for comparison with the Cython equivalent

parent 4e9e533c
{ {
"metadata": { "metadata": {
"name": "", "name": "Cython Magics",
"signature": "sha256:72d92961ad5ed0edc2b71d8b835c31c35d82d67c6f7f2360d56c2c708f1d9767" "signature": "sha256:c357b93e9480d6347c6677862bf43750745cef4b30129c5bc53cb879a19d4074"
}, },
"nbformat": 3, "nbformat": 3,
"nbformat_minor": 0, "nbformat_minor": 0,
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
"cell_type": "code", "cell_type": "code",
"collapsed": false, "collapsed": false,
"input": [ "input": [
"%load_ext cython" "%load_ext cythonmagic"
], ],
"language": "python", "language": "python",
"metadata": {}, "metadata": {},
...@@ -164,12 +164,12 @@ ...@@ -164,12 +164,12 @@
"from libc.math cimport exp, sqrt, pow, log, erf\n", "from libc.math cimport exp, sqrt, pow, log, erf\n",
"\n", "\n",
"@cython.cdivision(True)\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", " return 0.5*(1+erf(x/sqrt(2.0)))\n",
"\n", "\n",
"@cython.cdivision(True)\n", "@cython.cdivision(True)\n",
"def black_scholes(double s, double k, double t, double v,\n", "def black_scholes_cy(double s, double k, double t, double v,\n",
" double rf, double div, double cp):\n", " double rf, double div, double cp):\n",
" \"\"\"Price an option using the Black-Scholes model.\n", " \"\"\"Price an option using the Black-Scholes model.\n",
" \n", " \n",
" s : initial stock price\n", " s : initial stock price\n",
...@@ -184,8 +184,8 @@ ...@@ -184,8 +184,8 @@
" with nogil:\n", " with nogil:\n",
" d1 = (log(s/k)+(rf-div+0.5*pow(v,2))*t)/(v*sqrt(t))\n", " d1 = (log(s/k)+(rf-div+0.5*pow(v,2))*t)/(v*sqrt(t))\n",
" d2 = d1 - v*sqrt(t)\n", " d2 = d1 - v*sqrt(t)\n",
" optprice = cp*s*exp(-div*t)*std_norm_cdf(cp*d1) - \\\n", " optprice = cp*s*exp(-div*t)*std_norm_cdf_cy(cp*d1) - \\\n",
" cp*k*exp(-rf*t)*std_norm_cdf(cp*d2)\n", " cp*k*exp(-rf*t)*std_norm_cdf_cy(cp*d2)\n",
" return optprice" " return optprice"
], ],
"language": "python", "language": "python",
...@@ -197,7 +197,7 @@ ...@@ -197,7 +197,7 @@
"cell_type": "code", "cell_type": "code",
"collapsed": false, "collapsed": false,
"input": [ "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", "language": "python",
"metadata": {}, "metadata": {},
...@@ -213,11 +213,76 @@ ...@@ -213,11 +213,76 @@
], ],
"prompt_number": 7 "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": [
"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", "cell_type": "code",
"collapsed": false, "collapsed": false,
"input": [ "input": [
"%timeit black_scholes(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)" "%timeit black_scholes_cy(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)"
], ],
"language": "python", "language": "python",
"metadata": {}, "metadata": {},
...@@ -226,11 +291,30 @@ ...@@ -226,11 +291,30 @@
"output_type": "stream", "output_type": "stream",
"stream": "stdout", "stream": "stdout",
"text": [ "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", "cell_type": "heading",
...@@ -266,7 +350,7 @@ ...@@ -266,7 +350,7 @@
] ]
} }
], ],
"prompt_number": 9 "prompt_number": 12
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
...@@ -279,4 +363,4 @@ ...@@ -279,4 +363,4 @@
"metadata": {} "metadata": {}
} }
] ]
} }
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment