{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\"The" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Fourier-based Option Pricing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For several reasons, it is beneficial to have available alternative valuation and pricing approaches to the Monte Carlo simulation approach. One application area is to **benchmark Monte Carlo-based valuation results** against other (potentially more accurate) results. Another area is **model calibration to liquidly traded vanilla instruments** where generally faster numerial methods can be applied.\n", "\n", "This part introduces **Fouried-based valuation functions** and benchmarks valuation results from the \"standard\", simulation-based DX Analytics modeling approach to output of those functions. " ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import dx\n", "import datetime as dt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Risk Factors" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The examples and benchmarks to follow rely on four different models:\n", "\n", "* geometric Brownian motion (Black-Scholes-Merton 1973)\n", "* jump diffusion (Merton 1976)\n", "* stochastic volatility (Heston 1993)\n", "* stochastic volatility jump diffusion (Bates 1996)\n", "\n", "For details on these models and the Fourier-based option pricing approach refer to Hilpisch (2015) (cf. http://eu.wiley.com/WileyCDA/WileyTitle/productCd-1119037999.html).\n", "\n", "We first define the single **market and valuation environments**." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# constant short rate\n", "r = dx.constant_short_rate('r', 0.01)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# geometric Brownian motion\n", "me = dx.market_environment('me', dt.datetime(2015, 1, 1))\n", "me.add_constant('initial_value', 100.)\n", "me.add_constant('volatility', 0.2)\n", "me.add_constant('final_date', dt.datetime(2015, 12, 31))\n", "me.add_constant('currency', 'EUR')" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "# jump component\n", "me.add_constant('lambda', 0.4)\n", "me.add_constant('mu', -0.6)\n", "me.add_constant('delta', 0.2)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "# stochastic volatiltiy component\n", "me.add_constant('rho', -.5)\n", "me.add_constant('kappa', 5.0)\n", "me.add_constant('theta', 0.02)\n", "me.add_constant('vol_vol', 0.3)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "# valuation environment\n", "val_env = dx.market_environment('val_env', dt.datetime(2015, 1, 1))\n", "val_env.add_constant('paths', 55000)\n", " # 25,000 paths\n", "val_env.add_constant('frequency', 'D')\n", " # weekly frequency\n", "val_env.add_curve('discount_curve', r)\n", "val_env.add_constant('starting_date', dt.datetime(2015, 1, 1))\n", "val_env.add_constant('final_date', dt.datetime(2015, 12, 31))" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "# add valuation environment to market environment\n", "me.add_environment(val_env)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Equipped with the single market environments and the valuation environment, we can instantiate the **simulation model objects**." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "gbm = dx.geometric_brownian_motion('gbm', me)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "jd = dx.jump_diffusion('jd', me)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "sv = dx.stochastic_volatility('sv', me)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "svjd = dx.stoch_vol_jump_diffusion('svjd', me)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plain Vanilla Put and Call Options" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Based on the just defined risk factors, we define 8 diffent options---a **European put and call option per risk factor**, respectively." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "# market environment for the options\n", "me_option = dx.market_environment('option', dt.datetime(2015, 1, 1))\n", "me_option.add_constant('maturity', dt.datetime(2015, 12, 31))\n", "me_option.add_constant('strike', 100.)\n", "me_option.add_constant('currency', 'EUR')\n", "me_option.add_environment(me)\n", "me_option.add_environment(val_env)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "euro_put_gbm = dx.valuation_mcs_european_single('euro_put', gbm, me_option,\n", " 'np.maximum(strike - maturity_value, 0)')\n", "euro_call_gbm = dx.valuation_mcs_european_single('euro_call', gbm, me_option,\n", " 'np.maximum(maturity_value - strike, 0)')" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "euro_put_jd = dx.valuation_mcs_european_single('euro_put', jd, me_option,\n", " 'np.maximum(strike - maturity_value, 0)')\n", "euro_call_jd = dx.valuation_mcs_european_single('euro_call', jd, me_option,\n", " 'np.maximum(maturity_value - strike, 0)')" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "euro_put_sv = dx.valuation_mcs_european_single('euro_put', sv, me_option,\n", " 'np.maximum(strike - maturity_value, 0)')\n", "euro_call_sv = dx.valuation_mcs_european_single('euro_call', sv, me_option,\n", " 'np.maximum(maturity_value - strike, 0)')" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "euro_put_svjd = dx.valuation_mcs_european_single('euro_put', svjd, me_option,\n", " 'np.maximum(strike - maturity_value, 0)')\n", "euro_call_svjd = dx.valuation_mcs_european_single('euro_call', svjd, me_option,\n", " 'np.maximum(maturity_value - strike, 0)')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Valuation Benchmarking" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this sub-section, we benchmark the **Monte Carlo value estimates** against the **Fourier-based pricing results**." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We first define some parameters used throughout." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "freq = '2m' # used for maturity definitions\n", "periods = 3 # number of intervals for maturity grid\n", "strikes = 5 # number of strikes per maturity\n", "initial_value = 100 # initial value for all risk factors\n", "start = 0.8 # lowest strike in percent of spot\n", "end = 1.2 # highest strike in percent of spot\n", "start_date = '2015/3/1' # start date for simulation/pricing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Geometric Brownian Motion" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We need to initialize the valuation object first." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "7.401144" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "euro_put_gbm.present_value()\n", " # method call needed for initialization" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is a **valuation class for European put and call options in the Black-Scholes-Merton model** available called `BSM_european_option`. It is based on the analytical pricing formula for that model and is instantiated as follows:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "bsm_option = dx.BSM_european_option('bsm_opt', me_option)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The following routine benchmarks the Monte Carlo value estimates for the **European put option** against the output from the valuation object based on the analytical pricing formula. The results are quite good since this model is quite easy to discretize exactly and therefore generally shows good convergence of the Monte Carlo estimates." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " T | strike | mcs | fou | dif | rel\n", "0.244 | 80.000 | 0.0327 | 0.0338 | -0.0011 | -3.22 \n", "0.244 | 90.000 | 0.6532 | 0.6524 | 0.0008 | 0.12 \n", "0.244 | 100.000 | 3.7811 | 3.8130 | -0.0318 | -0.84 \n", "0.244 | 110.000 | 10.6205 | 10.6957 | -0.0752 | -0.70 \n", "0.244 | 120.000 | 19.7026 | 19.8537 | -0.1512 | -0.76 \n", "0.411 | 80.000 | 0.1755 | 0.1748 | 0.0007 | 0.42 \n", "0.411 | 90.000 | 1.3393 | 1.3241 | 0.0152 | 1.15 \n", "0.411 | 100.000 | 4.8421 | 4.8985 | -0.0564 | -1.15 \n", "0.411 | 110.000 | 11.3570 | 11.4275 | -0.0705 | -0.62 \n", "0.411 | 120.000 | 19.9190 | 20.0325 | -0.1135 | -0.57 \n", "0.578 | 80.000 | 0.3820 | 0.3917 | -0.0097 | -2.47 \n", "0.578 | 90.000 | 1.9342 | 1.9466 | -0.0124 | -0.64 \n", "0.578 | 100.000 | 5.7446 | 5.7593 | -0.0147 | -0.25 \n", "0.578 | 110.000 | 12.0049 | 12.0934 | -0.0885 | -0.73 \n", "0.578 | 120.000 | 20.2267 | 20.3153 | -0.0886 | -0.44 \n", "CPU times: user 38.5 s, sys: 6.24 s, total: 44.7 s\n", "Wall time: 9.89 s\n" ] } ], "source": [ "%%time\n", "# European put\n", "print('%4s | %7s | %7s | %7s | %7s | %7s' % ('T', 'strike', 'mcs', 'fou', 'dif', 'rel'))\n", "for maturity in pd.date_range(start=start_date, freq=freq, periods=periods):\n", " bsm_option.maturity = maturity\n", " euro_put_gbm.update(maturity=maturity)\n", " for strike in np.linspace(start, end, strikes) * initial_value:\n", " T = (maturity - me_option.pricing_date).days / 365.\n", " euro_put_gbm.update(strike=strike)\n", " mcs = euro_put_gbm.present_value()\n", " bsm_option.strike = strike\n", " ana = bsm_option.put_value()\n", " print('%4.3f | %7.3f | %7.4f | %7.4f | %7.4f | %7.2f '\n", " % (T, strike, mcs, ana, mcs - ana, (mcs - ana) / ana * 100))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The same now for the **European call option**." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8.426388" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "euro_call_gbm.present_value()\n", " # method call needed for initialization" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " T | strike | mcs | fou | dif | rel\n", "0.244 | 80.000 | 20.0731 | 20.2286 | -0.1556 | -0.77 \n", "0.244 | 90.000 | 10.7908 | 10.8716 | -0.0808 | -0.74 \n", "0.244 | 100.000 | 4.0200 | 4.0565 | -0.0365 | -0.90 \n", "0.244 | 110.000 | 0.9631 | 0.9636 | -0.0005 | -0.05 \n", "0.244 | 120.000 | 0.1356 | 0.1460 | -0.0104 | -7.14 \n", "0.411 | 80.000 | 20.3867 | 20.5029 | -0.1162 | -0.57 \n", "0.411 | 90.000 | 11.6556 | 11.6932 | -0.0376 | -0.32 \n", "0.411 | 100.000 | 5.3106 | 5.3086 | 0.0020 | 0.04 \n", "0.411 | 110.000 | 1.8540 | 1.8787 | -0.0247 | -1.31 \n", "0.411 | 120.000 | 0.5041 | 0.5246 | -0.0205 | -3.91 \n", "0.578 | 80.000 | 20.7445 | 20.8528 | -0.1083 | -0.52 \n", "0.578 | 90.000 | 12.3760 | 12.4654 | -0.0894 | -0.72 \n", "0.578 | 100.000 | 6.3499 | 6.3357 | 0.0142 | 0.22 \n", "0.578 | 110.000 | 2.7110 | 2.7274 | -0.0165 | -0.60 \n", "0.578 | 120.000 | 1.0002 | 1.0070 | -0.0068 | -0.67 \n", "CPU times: user 36.9 s, sys: 5.8 s, total: 42.7 s\n", "Wall time: 9.39 s\n" ] } ], "source": [ "%%time\n", "# European calls\n", "print('%4s | %7s | %7s | %7s | %7s | %7s' % ('T', 'strike', 'mcs', 'fou', 'dif', 'rel'))\n", "for maturity in pd.date_range(start=start_date, freq=freq, periods=periods):\n", " euro_call_gbm.update(maturity=maturity)\n", " for strike in np.linspace(start, end, strikes) * initial_value:\n", " T = (maturity - me_option.pricing_date).days / 365.\n", " euro_call_gbm.update(strike=strike)\n", " mcs = euro_call_gbm.present_value()\n", " bsm_option.strike = strike\n", " bsm_option.maturity = maturity\n", " ana = bsm_option.call_value()\n", " print('%4.3f | %7.3f | %7.4f | %7.4f | %7.4f | %7.2f ' \\\n", " % (T, strike, mcs, ana, mcs - ana, (mcs - ana) / ana * 100))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Benchmarking Function" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All other valuation benchmarks are generated with **Fourier-based pricing functions** for which the handling is identical. We therefore use the following function for the benchmarks from now on:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "def valuation_benchmarking(valuation_object, fourier_function):\n", " print('%4s | %7s | %7s | %7s | %7s | %7s' % ('T', 'strike', 'mcs', 'fou', 'dif', 'rel'))\n", " for maturity in pd.date_range(start=start_date, freq=freq, periods=periods):\n", " valuation_object.update(maturity=maturity)\n", " me_option.add_constant('maturity', maturity)\n", " for strike in np.linspace(start, end, strikes) * initial_value:\n", " T = (maturity - me_option.pricing_date).days / 365.\n", " valuation_object.update(strike=strike)\n", " mcs = valuation_object.present_value()\n", " me_option.add_constant('strike', strike)\n", " fou = fourier_function(me_option)\n", " print('%4.3f | %7.3f | %7.4f | %7.4f | %7.4f | %7.3f '\n", " % (T, strike, mcs, fou, mcs - fou, (mcs - fou) / fou * 100))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Jump Diffusion" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The next model is the jump diffusion as proposed by **Merton (1976)**." ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "14.106036" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "euro_put_jd.present_value()\n", " # method call needed for initialization" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is a Fourier-based pricing function available which is called `M76_put_value` and which is used for the benchmarking for the **European put options** that follows." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " T | strike | mcs | fou | dif | rel\n", "0.244 | 80.000 | 2.1771 | 2.1594 | 0.0177 | 0.819 \n", "0.244 | 90.000 | 3.2142 | 3.2826 | -0.0685 | -2.086 \n", "0.244 | 100.000 | 5.8588 | 5.8842 | -0.0254 | -0.431 \n", "0.244 | 110.000 | 11.4707 | 11.6115 | -0.1408 | -1.213 \n", "0.244 | 120.000 | 19.9428 | 20.0857 | -0.1429 | -0.712 \n", "0.411 | 80.000 | 3.4273 | 3.4505 | -0.0233 | -0.674 \n", "0.411 | 90.000 | 5.1224 | 5.2162 | -0.0938 | -1.798 \n", "0.411 | 100.000 | 8.2061 | 8.2266 | -0.0205 | -0.249 \n", "0.411 | 110.000 | 13.3971 | 13.4430 | -0.0458 | -0.341 \n", "0.411 | 120.000 | 20.8313 | 20.9238 | -0.0925 | -0.442 \n", "0.578 | 80.000 | 4.5777 | 4.6090 | -0.0313 | -0.679 \n", "0.578 | 90.000 | 6.8477 | 6.8782 | -0.0304 | -0.443 \n", "0.578 | 100.000 | 10.2062 | 10.2077 | -0.0015 | -0.014 \n", "0.578 | 110.000 | 15.0388 | 15.2251 | -0.1862 | -1.223 \n", "0.578 | 120.000 | 21.8634 | 22.0886 | -0.2252 | -1.020 \n", "CPU times: user 1min 29s, sys: 11.5 s, total: 1min 40s\n", "Wall time: 22.6 s\n" ] } ], "source": [ "%time valuation_benchmarking(euro_put_jd, dx.M76_put_value)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Accordingly, the benchmarking for the **European call options** based on the Fourier-based `M76_call_value` function." ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "15.076098" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "euro_call_jd.present_value()\n", " # method call needed for initialization" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " T | strike | mcs | fou | dif | rel\n", "0.244 | 80.000 | 22.1538 | 22.3543 | -0.2005 | -0.897 \n", "0.244 | 90.000 | 13.3560 | 13.5018 | -0.1458 | -1.080 \n", "0.244 | 100.000 | 6.0947 | 6.1277 | -0.0330 | -0.539 \n", "0.244 | 110.000 | 1.8695 | 1.8794 | -0.0098 | -0.524 \n", "0.244 | 120.000 | 0.3701 | 0.3780 | -0.0079 | -2.086 \n", "0.411 | 80.000 | 23.6404 | 23.7786 | -0.1383 | -0.582 \n", "0.411 | 90.000 | 15.4757 | 15.5853 | -0.1096 | -0.703 \n", "0.411 | 100.000 | 8.6258 | 8.6367 | -0.0109 | -0.126 \n", "0.411 | 110.000 | 3.8863 | 3.8941 | -0.0078 | -0.200 \n", "0.411 | 120.000 | 1.4004 | 1.4160 | -0.0156 | -1.101 \n", "0.578 | 80.000 | 25.0494 | 25.0701 | -0.0207 | -0.083 \n", "0.578 | 90.000 | 17.3956 | 17.3970 | -0.0014 | -0.008 \n", "0.578 | 100.000 | 10.7217 | 10.7841 | -0.0624 | -0.579 \n", "0.578 | 110.000 | 5.8156 | 5.8591 | -0.0435 | -0.743 \n", "0.578 | 120.000 | 2.7383 | 2.7803 | -0.0420 | -1.511 \n", "CPU times: user 1min 30s, sys: 11.9 s, total: 1min 42s\n", "Wall time: 23 s\n" ] } ], "source": [ "%time valuation_benchmarking(euro_call_jd, dx.M76_call_value)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Stochastic Volatility" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Stochastic volatility models like the one of **Heston (1993)** are popular to reproduce implied volatility smiles observed in markets. First, the benchmarking for the **European put options** using the Fourier-based `H93_put_value` function." ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5.3329" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "euro_put_sv.present_value()\n", " # method call needed for initialization" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " T | strike | mcs | fou | dif | rel\n", "0.244 | 80.000 | 0.0504 | 0.0504 | -0.0000 | -0.011 \n", "0.244 | 90.000 | 0.5512 | 0.5741 | -0.0229 | -3.984 \n", "0.244 | 100.000 | 3.2609 | 3.3204 | -0.0595 | -1.791 \n", "0.244 | 110.000 | 10.1399 | 10.2404 | -0.1005 | -0.981 \n", "0.244 | 120.000 | 19.5842 | 19.7354 | -0.1512 | -0.766 \n", "0.411 | 80.000 | 0.1444 | 0.1603 | -0.0159 | -9.910 \n", "0.411 | 90.000 | 0.9420 | 1.0063 | -0.0643 | -6.386 \n", "0.411 | 100.000 | 3.9185 | 4.0234 | -0.1049 | -2.607 \n", "0.411 | 110.000 | 10.4422 | 10.5487 | -0.1065 | -1.010 \n", "0.411 | 120.000 | 19.5004 | 19.6293 | -0.1288 | -0.656 \n", "0.578 | 80.000 | 0.2502 | 0.2838 | -0.0336 | -11.847 \n", "0.578 | 90.000 | 1.2868 | 1.3631 | -0.0763 | -5.596 \n", "0.578 | 100.000 | 4.4023 | 4.5467 | -0.1444 | -3.176 \n", "0.578 | 110.000 | 10.6897 | 10.8389 | -0.1492 | -1.376 \n", "0.578 | 120.000 | 19.4625 | 19.5767 | -0.1142 | -0.583 \n", "CPU times: user 1min 43s, sys: 16.2 s, total: 1min 59s\n", "Wall time: 26.4 s\n" ] } ], "source": [ "%time valuation_benchmarking(euro_put_sv, dx.H93_put_value)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Second, the benchmarking for the **European call options** based on the Fourier-based `H93_call_value` function." ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6.307286" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "euro_call_sv.present_value()\n", " # method call needed for initialization" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " T | strike | mcs | fou | dif | rel\n", "0.244 | 80.000 | 20.0909 | 20.2453 | -0.1544 | -0.763 \n", "0.244 | 90.000 | 10.6943 | 10.7933 | -0.0989 | -0.917 \n", "0.244 | 100.000 | 3.5110 | 3.5639 | -0.0529 | -1.483 \n", "0.244 | 110.000 | 0.4705 | 0.5083 | -0.0378 | -7.441 \n", "0.244 | 120.000 | 0.0274 | 0.0276 | -0.0002 | -0.549 \n", "0.411 | 80.000 | 20.3465 | 20.4884 | -0.1419 | -0.693 \n", "0.411 | 90.000 | 11.2741 | 11.3754 | -0.1012 | -0.890 \n", "0.411 | 100.000 | 4.3208 | 4.4335 | -0.1127 | -2.543 \n", "0.411 | 110.000 | 0.9430 | 0.9998 | -0.0568 | -5.685 \n", "0.411 | 120.000 | 0.1072 | 0.1214 | -0.0142 | -11.702 \n", "0.578 | 80.000 | 20.6329 | 20.7450 | -0.1121 | -0.540 \n", "0.578 | 90.000 | 11.7564 | 11.8818 | -0.1254 | -1.056 \n", "0.578 | 100.000 | 5.0226 | 5.1231 | -0.1005 | -1.961 \n", "0.578 | 110.000 | 1.3453 | 1.4729 | -0.1276 | -8.665 \n", "0.578 | 120.000 | 0.2373 | 0.2684 | -0.0311 | -11.598 \n", "CPU times: user 1min 44s, sys: 16.5 s, total: 2min\n", "Wall time: 26.6 s\n" ] } ], "source": [ "%time valuation_benchmarking(euro_call_sv, dx.H93_call_value)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Stochastic Volatility Jump-Diffusion" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, we consider the combination of the stochastic volatility and jump diffusion models from before as proposed by **Bates (1996)**. The Fourier-based pricing function for **European put options** is called `B96_put_value`." ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "12.938869" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "euro_put_svjd.present_value()\n", " # method call needed for initialization" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " T | strike | mcs | fou | dif | rel\n", "0.244 | 80.000 | 2.1374 | 2.1638 | -0.0265 | -1.224 \n", "0.244 | 90.000 | 3.1814 | 3.2761 | -0.0947 | -2.891 \n", "0.244 | 100.000 | 5.4111 | 5.5889 | -0.1778 | -3.182 \n", "0.244 | 110.000 | 11.0267 | 11.0733 | -0.0466 | -0.421 \n", "0.244 | 120.000 | 19.5912 | 19.8344 | -0.2431 | -1.226 \n", "0.411 | 80.000 | 3.3949 | 3.4366 | -0.0417 | -1.213 \n", "0.411 | 90.000 | 5.0453 | 5.1339 | -0.0887 | -1.727 \n", "0.411 | 100.000 | 7.5677 | 7.7747 | -0.2069 | -2.662 \n", "0.411 | 110.000 | 12.5075 | 12.5744 | -0.0669 | -0.532 \n", "0.411 | 120.000 | 19.8595 | 20.1827 | -0.3233 | -1.602 \n", "0.578 | 80.000 | 4.3390 | 4.5480 | -0.2090 | -4.595 \n", "0.578 | 90.000 | 6.4859 | 6.7163 | -0.2304 | -3.431 \n", "0.578 | 100.000 | 9.2934 | 9.6585 | -0.3651 | -3.780 \n", "0.578 | 110.000 | 13.9231 | 14.1776 | -0.2545 | -1.795 \n", "0.578 | 120.000 | 20.6726 | 20.9423 | -0.2697 | -1.288 \n", "CPU times: user 2min 42s, sys: 22.1 s, total: 3min 4s\n", "Wall time: 39.8 s\n" ] } ], "source": [ "%time valuation_benchmarking(euro_put_svjd, dx.B96_put_value)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Fourier-based counterpart function for **European call options** is called `B96_call_value`." ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "13.836211" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "euro_call_svjd.present_value()\n", " # method call needed for initialization" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " T | strike | mcs | fou | dif | rel\n", "0.244 | 80.000 | 22.1335 | 22.3587 | -0.2252 | -1.007 \n", "0.244 | 90.000 | 13.3312 | 13.4953 | -0.1640 | -1.216 \n", "0.244 | 100.000 | 5.6897 | 5.8325 | -0.1428 | -2.448 \n", "0.244 | 110.000 | 1.2988 | 1.3411 | -0.0423 | -3.156 \n", "0.244 | 120.000 | 0.1163 | 0.1266 | -0.0103 | -8.110 \n", "0.411 | 80.000 | 23.5008 | 23.7647 | -0.2639 | -1.110 \n", "0.411 | 90.000 | 15.3156 | 15.5030 | -0.1875 | -1.209 \n", "0.411 | 100.000 | 7.9903 | 8.1848 | -0.1944 | -2.376 \n", "0.411 | 110.000 | 2.8889 | 3.0255 | -0.1366 | -4.514 \n", "0.411 | 120.000 | 0.6102 | 0.6749 | -0.0647 | -9.583 \n", "0.578 | 80.000 | 24.7946 | 25.0091 | -0.2145 | -0.858 \n", "0.578 | 90.000 | 16.9820 | 17.2351 | -0.2531 | -1.469 \n", "0.578 | 100.000 | 9.9994 | 10.2349 | -0.2355 | -2.300 \n", "0.578 | 110.000 | 4.5780 | 4.8117 | -0.2337 | -4.857 \n", "0.578 | 120.000 | 1.4891 | 1.6340 | -0.1448 | -8.864 \n", "CPU times: user 2min 46s, sys: 22.9 s, total: 3min 8s\n", "Wall time: 40.9 s\n" ] } ], "source": [ "%time valuation_benchmarking(euro_call_svjd, dx.B96_call_value)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Sources of Errors" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Numerical methods like Monte Carlo simulation might suffer from different **sources of errors**, like for example:\n", "\n", "* **discretization error**: every **discretization of a continuous time interval**---or a continuous state space to this end---leads to a so-called discretization error\n", "* **approximation errors**: DX Analytics uses in several places approximative, **Euler-based discretization schemes** (e.g. for performance reasons and to allow for consistent correlation modeling) which are known to be biased\n", "* **numerical errors**: the approximation of a continuous probability distribution by a **finite, discrete set of (pseudo-) random numbers** introduces also errors" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Copyright, License & Disclaimer**\n", "\n", "© Dr. Yves J. Hilpisch | The Python Quants GmbH\n", "\n", "DX Analytics (the \"dx library\" or \"dx package\") is licensed under the GNU Affero General\n", "Public License version 3 or later (see http://www.gnu.org/licenses/).\n", "\n", "DX Analytics comes with no representations or warranties, to the extent\n", "permitted by applicable law.\n", "\n", "http://tpq.io | [dx@tpq.io](mailto:team@tpq.io) |\n", "http://twitter.com/dyjh\n", "\n", "\"The
\n", "\n", "**Quant Platform** | http://pqp.io\n", "\n", "**Python for Finance Training** | http://training.tpq.io\n", "\n", "**Certificate in Computational Finance** | http://compfinance.tpq.io\n", "\n", "**Derivatives Analytics with Python (Wiley Finance)** |\n", "http://dawp.tpq.io\n", "\n", "**Python for Finance (2nd ed., O'Reilly)** |\n", "http://py4fi.tpq.io" ] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.8" } }, "nbformat": 4, "nbformat_minor": 1 }