##// END OF EJS Templates
Add a pure-python version of the Black-Scholes function for comparison with the Cython equivalent
Erik Tollerud -
Show More
@@ -1,278 +1,366 b''
1 1 {
2 2 "metadata": {
3 "name": "Cython Magics"
3 "name": "",
4 "signature": "sha256:c357b93e9480d6347c6677862bf43750745cef4b30129c5bc53cb879a19d4074"
4 5 },
5 6 "nbformat": 3,
6 7 "nbformat_minor": 0,
7 8 "worksheets": [
8 9 {
9 10 "cells": [
10 11 {
11 12 "cell_type": "heading",
12 13 "level": 1,
13 14 "metadata": {},
14 15 "source": [
15 16 "Cython Magic Functions"
16 17 ]
17 18 },
18 19 {
19 20 "cell_type": "heading",
20 21 "level": 2,
21 22 "metadata": {},
22 23 "source": [
23 24 "Loading the extension"
24 25 ]
25 26 },
26 27 {
27 28 "cell_type": "markdown",
28 29 "metadata": {},
29 30 "source": [
30 31 "IPython has a `cythonmagic` extension that contains a number of magic functions for working with Cython code. This extension can be loaded using the `%load_ext` magic as follows:"
31 32 ]
32 33 },
33 34 {
34 35 "cell_type": "code",
35 36 "collapsed": false,
36 37 "input": [
37 38 "%load_ext cythonmagic"
38 39 ],
39 40 "language": "python",
40 41 "metadata": {},
41 42 "outputs": [],
42 43 "prompt_number": 1
43 44 },
44 45 {
45 46 "cell_type": "heading",
46 47 "level": 2,
47 48 "metadata": {},
48 49 "source": [
49 50 "The %cython_inline magic"
50 51 ]
51 52 },
52 53 {
53 54 "cell_type": "markdown",
54 55 "metadata": {},
55 56 "source": [
56 57 "The `%%cython_inline` magic uses `Cython.inline` to compile a Cython expression. This allows you to enter and run a function body with Cython code. Use a bare `return` statement to return values. "
57 58 ]
58 59 },
59 60 {
60 61 "cell_type": "code",
61 62 "collapsed": false,
62 63 "input": [
63 64 "a = 10\n",
64 65 "b = 20"
65 66 ],
66 67 "language": "python",
67 68 "metadata": {},
68 69 "outputs": [],
69 70 "prompt_number": 2
70 71 },
71 72 {
72 73 "cell_type": "code",
73 74 "collapsed": false,
74 75 "input": [
75 76 "%%cython_inline\n",
76 77 "return a+b"
77 78 ],
78 79 "language": "python",
79 80 "metadata": {},
80 81 "outputs": [
81 82 {
83 "metadata": {},
82 84 "output_type": "pyout",
83 85 "prompt_number": 3,
84 86 "text": [
85 87 "30"
86 88 ]
87 89 }
88 90 ],
89 91 "prompt_number": 3
90 92 },
91 93 {
92 94 "cell_type": "heading",
93 95 "level": 2,
94 96 "metadata": {},
95 97 "source": [
96 98 "The %cython_pyximport magic"
97 99 ]
98 100 },
99 101 {
100 102 "cell_type": "markdown",
101 103 "metadata": {},
102 104 "source": [
103 105 "The `%%cython_pyximport` magic allows you to enter arbitrary Cython code into a cell. That Cython code is written as a `.pyx` file in the current working directory and then imported using `pyximport`. You have the specify the name of the module that the Code will appear in. All symbols from the module are imported automatically by the magic function."
104 106 ]
105 107 },
106 108 {
107 109 "cell_type": "code",
108 110 "collapsed": false,
109 111 "input": [
110 112 "%%cython_pyximport foo\n",
111 113 "def f(x):\n",
112 114 " return 4.0*x"
113 115 ],
114 116 "language": "python",
115 117 "metadata": {},
116 118 "outputs": [],
117 119 "prompt_number": 4
118 120 },
119 121 {
120 122 "cell_type": "code",
121 123 "collapsed": false,
122 124 "input": [
123 125 "f(10)"
124 126 ],
125 127 "language": "python",
126 128 "metadata": {},
127 129 "outputs": [
128 130 {
131 "metadata": {},
129 132 "output_type": "pyout",
130 133 "prompt_number": 5,
131 134 "text": [
132 135 "40.0"
133 136 ]
134 137 }
135 138 ],
136 139 "prompt_number": 5
137 140 },
138 141 {
139 142 "cell_type": "heading",
140 143 "level": 2,
141 144 "metadata": {},
142 145 "source": [
143 146 "The %cython magic"
144 147 ]
145 148 },
146 149 {
147 150 "cell_type": "markdown",
148 151 "metadata": {},
149 152 "source": [
150 153 "Probably the most important magic is the `%cython` magic. This is similar to the `%%cython_pyximport` magic, but doesn't require you to specify a module name. Instead, the `%%cython` magic uses manages everything using temporary files in the `~/.cython/magic` directory. All of the symbols in the Cython module are imported automatically by the magic.\n",
151 154 "\n",
152 155 "Here is a simple example of a Black-Scholes options pricing algorithm written in Cython. Please note that this example might not compile on non-POSIX systems (e.g., Windows) because of a missing `erf` symbol."
153 156 ]
154 157 },
155 158 {
156 159 "cell_type": "code",
157 160 "collapsed": false,
158 161 "input": [
159 162 "%%cython\n",
160 163 "cimport cython\n",
161 164 "from libc.math cimport exp, sqrt, pow, log, erf\n",
162 165 "\n",
163 166 "@cython.cdivision(True)\n",
164 "cdef double std_norm_cdf(double x) nogil:\n",
167 "cdef double std_norm_cdf_cy(double x) nogil:\n",
165 168 " return 0.5*(1+erf(x/sqrt(2.0)))\n",
166 169 "\n",
167 170 "@cython.cdivision(True)\n",
168 "def black_scholes(double s, double k, double t, double v,\n",
169 " double rf, double div, double cp):\n",
171 "def black_scholes_cy(double s, double k, double t, double v,\n",
172 " double rf, double div, double cp):\n",
170 173 " \"\"\"Price an option using the Black-Scholes model.\n",
171 174 " \n",
172 175 " s : initial stock price\n",
173 176 " k : strike price\n",
174 177 " t : expiration time\n",
175 178 " v : volatility\n",
176 179 " rf : risk-free rate\n",
177 180 " div : dividend\n",
178 181 " cp : +1/-1 for call/put\n",
179 182 " \"\"\"\n",
180 183 " cdef double d1, d2, optprice\n",
181 184 " with nogil:\n",
182 185 " d1 = (log(s/k)+(rf-div+0.5*pow(v,2))*t)/(v*sqrt(t))\n",
183 186 " d2 = d1 - v*sqrt(t)\n",
184 " optprice = cp*s*exp(-div*t)*std_norm_cdf(cp*d1) - \\\n",
185 " cp*k*exp(-rf*t)*std_norm_cdf(cp*d2)\n",
187 " optprice = cp*s*exp(-div*t)*std_norm_cdf_cy(cp*d1) - \\\n",
188 " cp*k*exp(-rf*t)*std_norm_cdf_cy(cp*d2)\n",
186 189 " return optprice"
187 190 ],
188 191 "language": "python",
189 192 "metadata": {},
190 193 "outputs": [],
191 194 "prompt_number": 6
192 195 },
193 196 {
194 197 "cell_type": "code",
195 198 "collapsed": false,
196 199 "input": [
197 "black_scholes(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)"
200 "black_scholes_cy(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)"
198 201 ],
199 202 "language": "python",
200 203 "metadata": {},
201 204 "outputs": [
202 205 {
206 "metadata": {},
203 207 "output_type": "pyout",
204 208 "prompt_number": 7,
205 209 "text": [
206 210 "10.327861752731728"
207 211 ]
208 212 }
209 213 ],
210 214 "prompt_number": 7
211 215 },
212 216 {
217 "cell_type": "markdown",
218 "metadata": {},
219 "source": [
220 "For comparison, the same code is implemented here in pure python."
221 ]
222 },
223 {
224 "cell_type": "code",
225 "collapsed": false,
226 "input": [
227 "from math import exp, sqrt, pow, log, erf\n",
228 "\n",
229 "def std_norm_cdf_py(x):\n",
230 " return 0.5*(1+erf(x/sqrt(2.0)))\n",
231 "\n",
232 "def black_scholes_py(s, k, t, v, rf, div, cp):\n",
233 " \"\"\"Price an option using the Black-Scholes model.\n",
234 " \n",
235 " s : initial stock price\n",
236 " k : strike price\n",
237 " t : expiration time\n",
238 " v : volatility\n",
239 " rf : risk-free rate\n",
240 " div : dividend\n",
241 " cp : +1/-1 for call/put\n",
242 " \"\"\"\n",
243 " d1 = (log(s/k)+(rf-div+0.5*pow(v,2))*t)/(v*sqrt(t))\n",
244 " d2 = d1 - v*sqrt(t)\n",
245 " optprice = cp*s*exp(-div*t)*std_norm_cdf_py(cp*d1) - \\\n",
246 " cp*k*exp(-rf*t)*std_norm_cdf_py(cp*d2)\n",
247 " return optprice"
248 ],
249 "language": "python",
250 "metadata": {},
251 "outputs": [],
252 "prompt_number": 8
253 },
254 {
213 255 "cell_type": "code",
214 256 "collapsed": false,
215 257 "input": [
216 "%timeit black_scholes(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)"
258 "black_scholes_py(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)"
259 ],
260 "language": "python",
261 "metadata": {},
262 "outputs": [
263 {
264 "metadata": {},
265 "output_type": "pyout",
266 "prompt_number": 9,
267 "text": [
268 "10.327861752731728"
269 ]
270 }
271 ],
272 "prompt_number": 9
273 },
274 {
275 "cell_type": "markdown",
276 "metadata": {},
277 "source": [
278 "Below we see the runtime of the two functions: the Cython version is nearly a factor of 10 faster."
279 ]
280 },
281 {
282 "cell_type": "code",
283 "collapsed": false,
284 "input": [
285 "%timeit black_scholes_cy(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)"
217 286 ],
218 287 "language": "python",
219 288 "metadata": {},
220 289 "outputs": [
221 290 {
222 291 "output_type": "stream",
223 292 "stream": "stdout",
224 293 "text": [
225 "1000000 loops, best of 3: 821 ns per loop\n"
294 "1000000 loops, best of 3: 319 ns per loop\n"
226 295 ]
227 296 }
228 297 ],
229 "prompt_number": 8
298 "prompt_number": 10
299 },
300 {
301 "cell_type": "code",
302 "collapsed": false,
303 "input": [
304 "%timeit black_scholes_py(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)"
305 ],
306 "language": "python",
307 "metadata": {},
308 "outputs": [
309 {
310 "output_type": "stream",
311 "stream": "stdout",
312 "text": [
313 "100000 loops, best of 3: 2.28 \u00b5s per loop\n"
314 ]
315 }
316 ],
317 "prompt_number": 11
230 318 },
231 319 {
232 320 "cell_type": "heading",
233 321 "level": 2,
234 322 "metadata": {},
235 323 "source": [
236 324 "External libraries"
237 325 ]
238 326 },
239 327 {
240 328 "cell_type": "markdown",
241 329 "metadata": {},
242 330 "source": [
243 331 "Cython allows you to specify additional libraries to be linked with your extension, you can do so with the `-l` flag (also spelled `--lib`). Note that this flag can be passed more than once to specify multiple libraries, such as `-lm -llib2 --lib lib3`. Here's a simple example of how to access the system math library:"
244 332 ]
245 333 },
246 334 {
247 335 "cell_type": "code",
248 336 "collapsed": false,
249 337 "input": [
250 338 "%%cython -lm\n",
251 339 "from libc.math cimport sin\n",
252 340 "print 'sin(1)=', sin(1)"
253 341 ],
254 342 "language": "python",
255 343 "metadata": {},
256 344 "outputs": [
257 345 {
258 346 "output_type": "stream",
259 347 "stream": "stdout",
260 348 "text": [
261 349 "sin(1)= 0.841470984808\n"
262 350 ]
263 351 }
264 352 ],
265 "prompt_number": 9
353 "prompt_number": 12
266 354 },
267 355 {
268 356 "cell_type": "markdown",
269 357 "metadata": {},
270 358 "source": [
271 359 "You can similarly use the `-I/--include` flag to add include directories to the search path, and `-c/--compile-args` to add extra flags that are passed to Cython via the `extra_compile_args` of the distutils `Extension` class. Please see [the Cython docs on C library usage](http://docs.cython.org/src/tutorial/clibraries.html) for more details on the use of these flags."
272 360 ]
273 361 }
274 362 ],
275 363 "metadata": {}
276 364 }
277 365 ]
278 }
366 } No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now