├── .gitignore ├── README.md └── dawp ├── book ├── 02_mbv │ ├── BSM_value_plot.py │ └── inner_value_plot.py ├── 03_stf │ ├── BSM_imp_vol.py │ ├── DAX_imp_vol.py │ ├── DAX_returns.py │ ├── ES50_imp_vol.py │ ├── EURIBOR_analysis.py │ ├── EURIBOR_current.xlsx │ ├── GBM_returns.py │ ├── SX5E.h5 │ ├── bsm_functions.py │ ├── es50_option_data.h5 │ └── es50_option_data.h5r ├── 05_com │ ├── BSM_call_greeks.py │ ├── BSM_option_valuation.py │ └── CRR_option_valuation.py ├── 06_fou │ ├── BSM_option_valuation_FOU.py │ ├── Fourier_series.py │ ├── call_convolution.py │ ├── call_convolution_general.py │ ├── call_fft_pricing.py │ ├── call_fft_speed.py │ ├── convolution.py │ ├── parameters.py │ └── roots_of_unity.py ├── 07_amo │ ├── CRR_american_options.py │ ├── LSM_primal_dual_table.py │ ├── LSM_primal_dual_valuation.py │ ├── LSM_primal_valuation.py │ ├── results_2014-10-13_full.h5 │ ├── results_2014-10-15_table.h5 │ ├── results_2014-10-15_table_old.h5 │ ├── table_1.tex │ └── table_2.tex ├── 08_m76 │ ├── M76_calibration_FFT.py │ ├── M76_calibration_single.py │ ├── M76_valuation_FFT.py │ ├── M76_valuation_INT.py │ ├── M76_valuation_MCS.py │ └── option_data.h5 ├── 09_gmm │ ├── BCC_option_valuation.py │ ├── BSM_option_valuation.py │ └── CIR_zcb_valuation.py ├── 10_mcs │ ├── CIR_zcb_simulation.py │ ├── CIR_zcb_valuation_gen.py │ ├── H93_european_mcs.py │ ├── RNG.py │ ├── SGEN.py │ ├── SVSI_american_mcs.py │ ├── SVSI_american_mcs_10_3.py │ ├── SVSI_american_mcs_10_4.py │ ├── SVSI_american_mcs_10_5.py │ ├── SVSI_american_speed.py │ ├── mcs_american_10_3.h5 │ ├── mcs_american_10_3_lam_1.h5 │ ├── mcs_american_10_4.h5 │ ├── mcs_american_10_4_lam_1.h5 │ ├── mcs_american_10_5.h5 │ ├── mcs_american_10_5_lam_1.h5 │ ├── mcs_european.h5 │ ├── table_10_1.tex │ ├── table_10_2.tex │ ├── table_10_3.tex │ ├── table_10_4.tex │ └── table_10_5.tex ├── 11_cal │ ├── BCC97_calibration_full.py │ ├── BCC97_calibration_iv.py │ ├── BCC97_calibration_short.py │ ├── BCC97_full_results.tex │ ├── BCC97_iv_results.tex │ ├── BCC97_jump_results.tex │ ├── CIR_calibration.py │ ├── H93_calibration.py │ ├── H93_results.tex │ ├── cal_results_full.h5 │ ├── cal_results_full_iv.h5 │ ├── cal_results_sv.h5 │ ├── opt_full.npy │ ├── opt_iv.npy │ ├── opt_jump.npy │ ├── opt_sv.npy │ └── plot_implied_volatilities.py ├── 12_val │ ├── BCC97_american_valuation.py │ ├── BCC97_simulation.py │ └── BCC97_valuation_comparison.py ├── 13_dyh │ ├── BCC97_lsm_hedging_algorithm.py │ ├── BCC97_lsm_hedging_histogram.py │ ├── BSM_lsm_hedging_algorithm.py │ └── BSM_lsm_hedging_histogram.py ├── A_DAWP_Chs_2_3.ipynb ├── A_pyt │ ├── DAX_data.h5 │ ├── DAX_data.xlsx │ ├── Option_Container │ ├── Option_Container_2 │ ├── a_first_program.py │ ├── b_BSM_valuation.py │ ├── c_parameters.py │ ├── d_CRR1979_loop.py │ ├── e_CRR1979_vectorized.py │ ├── f_CRR1979_fft.py │ ├── g_MCS.py │ ├── h_REG.py │ ├── i_SPLINE.py │ ├── j_OPT.py │ ├── k_INT.py │ ├── l_CLASS.py │ ├── m_Excel_read.py │ ├── n_pandas.py │ └── numpy_comp.py ├── B_DAWP_Chs_5_6_7.ipynb ├── C_DAWP_Ch_8.ipynb ├── DAX_data.h5 ├── D_DAWP_Ch_9_10.ipynb ├── E_DAWP_Ch_11.ipynb ├── F_DAWP_Chs_12_13.html ├── F_DAWP_Chs_12_13.ipynb ├── G_Python_Nutshell.ipynb ├── option_container └── option_container_2 └── images ├── 02_mbv ├── call_inner_values.pdf └── call_values.pdf ├── 03_stf ├── DAX_histogram.pdf ├── DAX_qqplot.pdf ├── DAX_quotes_returns.pdf ├── DAX_realized_volatility.pdf ├── DAX_rolling_stats.pdf ├── EBO_histogram.pdf ├── EBO_qqplot.pdf ├── EBO_quotes_returns.pdf ├── EBO_term_structure.pdf ├── ES50_implied_volatilities.pdf ├── GBM_histogram.pdf ├── GBM_qqplot.pdf ├── GBM_quotes_returns.pdf ├── GBM_realized_volatility.pdf └── GBM_rolling_stats.pdf ├── 05_com ├── BSM_call_values.pdf ├── BSM_delta.pdf ├── BSM_gamma.pdf ├── BSM_put_values.pdf ├── BSM_rho.pdf ├── BSM_theta.pdf ├── BSM_vega.pdf ├── CRR_convergence_1.pdf └── CRR_convergence_2.pdf ├── 06_fou ├── BSM_val_accuracy_1.pdf ├── BSM_val_accuracy_2.pdf ├── Fourier_series.pdf └── spokes.pdf ├── 07_amo ├── LSM_primal_dual_APO.pdf └── LSM_primal_dual_SCS.pdf ├── 08_m76 ├── M76_calibration_3_0.pdf ├── M76_calibration_3_1.pdf ├── M76_calibration_3_2.pdf ├── M76_calibration_single.pdf └── M76_value_comparison.pdf ├── 10_mcs ├── CIR_paths.pdf ├── CIR_zcb_mcs_euler.pdf ├── CIR_zcb_mcs_exact.pdf └── box_mm.pdf ├── 11_cal ├── BCC97_calibration_iv_2014-10-17.pdf ├── BCC97_calibration_iv_2014-12-19.pdf ├── BCC97_calibration_iv_2015-03-20.pdf ├── BCC97_full_calibration_quotes.pdf ├── BCC97_iv_calibration_iv_2014-10-17.pdf ├── BCC97_iv_calibration_iv_2014-12-19.pdf ├── BCC97_iv_calibration_iv_2015-03-20.pdf ├── BCC97_iv_calibration_quotes.pdf ├── BCC97_jump_calibration_quotes.pdf ├── CIR_zcb_values.pdf ├── H93_calibration_iv_2014-10-17.pdf ├── H93_calibration_iv_2014-12-19.pdf ├── H93_calibration_iv_2015-03-20.pdf ├── H93_calibration_quotes.pdf ├── frc_calibration.pdf └── term_structure.pdf ├── 12_val ├── ES50_histogram.pdf ├── ES50_paths.pdf ├── rate_paths.pdf └── volatility_paths.pdf ├── 13_dyh ├── BCC_hedge_histo.pdf ├── BCC_hedge_run_1.pdf ├── BCC_hedge_run_2.pdf ├── BCC_hedge_run_3.pdf └── BSM_hedge_histo.pdf └── A_pyt ├── DAX_hist_rets.pdf ├── DAX_history.pdf ├── dots_bars.pdf ├── histogram.pdf ├── line_plot.pdf ├── regression.pdf └── splines.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .ipynb* 3 | *.pyc 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Derivatives Analytics with Python (Wiley Finance) 2 | 3 | This repository provides all Python codes and IPython Notebooks of the book _Derivatives Analytics with Python_ by Yves Hilpisch. 4 | 5 | ![alt text](http://hilpisch.com/images/derivatives_analytics_front.jpg "Book Cover") 6 | 7 | Oder the book here http://eu.wiley.com/WileyCDA/WileyTitle/productCd-1119037999.html or here www.amazon.com/Derivatives-Analytics-Python-Simulation-Calibration/dp/1119037999/. 8 | 9 | ## Python Quant Platform 10 | 11 | You can immediately use all codes and IPython Notebooks by registering for the Python Quant Platform under http://wiley.quant-platform.com. 12 | 13 | 14 | ## Company Information 15 | 16 | © Dr. Yves J. Hilpisch \| The Python Quants GmbH 17 | 18 | The Python Quant Platform and all codes/IPython Notebooks come with no representations or warranties, to the extent permitted by applicable law. 19 | 20 | http://www.pythonquants.com \| analytics@pythonquants.com \| 21 | http://twitter.com/dyjh 22 | 23 | **Python Quant Platform** \| http://wiley.quant-platform.com 24 | 25 | **Python for Finance (O'Reilly)** \| 26 | http://python-for-finance.com 27 | 28 | **Derivatives Analytics with Python (Wiley Finance)** \| 29 | http://derivatives-analytics-with-python.com 30 | -------------------------------------------------------------------------------- /dawp/book/02_mbv/BSM_value_plot.py: -------------------------------------------------------------------------------- 1 | # 2 | # European Call Option Value Plot 3 | # 02_mbv/BSM_value_plot.py 4 | # 5 | # (c) Dr. Yves J. Hilpisch 6 | # Derivatives Analytics with Python 7 | # 8 | import numpy as np 9 | import matplotlib as mpl 10 | import matplotlib.pyplot as plt 11 | mpl.rcParams['font.family'] = 'serif' 12 | 13 | # Import Valuation Function from Chapter 5 14 | import sys 15 | sys.path.append('05_com') 16 | from BSM_option_valuation import BSM_call_value 17 | 18 | # Model and Option Parameters 19 | K = 8000 # strike price 20 | T = 1.0 # time-to-maturity 21 | r = 0.025 # constant, risk-less short rate 22 | vol = 0.2 # constant volatility 23 | 24 | # Sample Data Generation 25 | S = np.linspace(4000, 12000, 150) # vector of index level values 26 | h = np.maximum(S - K, 0) # inner value of option 27 | C = [BSM_call_value(S0, K, 0, T, r, vol) for S0 in S] 28 | # calculate call option values 29 | 30 | # Graphical Output 31 | plt.figure() 32 | plt.plot(S, h, 'b-.', lw=2.5, label='inner value') 33 | # plot inner value at maturity 34 | plt.plot(S, C, 'r', lw=2.5, label='present value') 35 | # plot option present value 36 | plt.grid(True) 37 | plt.legend(loc=0) 38 | plt.xlabel('index level $S_0$') 39 | plt.ylabel('present value $C(t=0)$') 40 | -------------------------------------------------------------------------------- /dawp/book/02_mbv/inner_value_plot.py: -------------------------------------------------------------------------------- 1 | # 2 | # European Call Option Inner Value Plot 3 | # 02_MBV/inner_value_plot.py 4 | # 5 | # (c) Dr. Yves J. Hilpisch 6 | # Derivatives Analytics with Python 7 | # 8 | import numpy as np 9 | import matplotlib as mpl 10 | import matplotlib.pyplot as plt 11 | mpl.rcParams['font.family'] = 'serif' 12 | 13 | # Option Strike 14 | K = 8000 15 | 16 | # Graphical Output 17 | S = np.linspace(7000, 9000, 100) # index level values 18 | h = np.maximum(S - K, 0) # inner values of call option 19 | 20 | plt.figure() 21 | plt.plot(S, h, lw=2.5) # plot inner values at maturity 22 | plt.xlabel('index level $S_t$ at maturity') 23 | plt.ylabel('inner value of European call option') 24 | plt.grid(True) 25 | -------------------------------------------------------------------------------- /dawp/book/03_stf/BSM_imp_vol.py: -------------------------------------------------------------------------------- 1 | # 2 | # Valuation of European Call Options in BSM Model 3 | # and Numerical Derivation of Implied Volatility 4 | # 03_stf/BSM_imp_vol.py 5 | # 6 | # (c) Dr. Yves J. Hilpisch 7 | # from Hilpisch, Yves (2014): Python for Finance, O'Reilly. 8 | # 9 | from math import log, sqrt, exp 10 | from scipy import stats 11 | from scipy.optimize import fsolve 12 | 13 | class call_option(object): 14 | ''' Class for European call options in BSM Model. 15 | 16 | Attributes 17 | ========== 18 | S0 : float 19 | initial stock/index level 20 | K : float 21 | strike price 22 | t : datetime/Timestamp object 23 | pricing date 24 | M : datetime/Timestamp object 25 | maturity date 26 | r : float 27 | constant risk-free short rate 28 | sigma : float 29 | volatility factor in diffusion term 30 | 31 | Methods 32 | ======= 33 | value : float 34 | return present value of call option 35 | vega : float 36 | return vega of call option 37 | imp_vol : float 38 | return implied volatility given option quote 39 | ''' 40 | 41 | def __init__(self, S0, K, t, M, r, sigma): 42 | self.S0 = float(S0) 43 | self.K = K 44 | self.t = t 45 | self.M = M 46 | self.r = r 47 | self.sigma = sigma 48 | 49 | def update_ttm(self): 50 | ''' Updates time-to-maturity self.T. ''' 51 | if self.t > self.M: 52 | raise ValueError("Pricing date later than maturity.") 53 | self.T = (self.M - self.t).days / 365. 54 | 55 | def d1(self): 56 | ''' Helper function. ''' 57 | d1 = ((log(self.S0 / self.K) 58 | + (self.r + 0.5 * self.sigma ** 2) * self.T) 59 | / (self.sigma * sqrt(self.T))) 60 | return d1 61 | 62 | def value(self): 63 | ''' Return option value. ''' 64 | self.update_ttm() 65 | d1 = self.d1() 66 | d2 = ((log(self.S0 / self.K) 67 | + (self.r - 0.5 * self.sigma ** 2) * self.T) 68 | / (self.sigma * sqrt(self.T))) 69 | value = (self.S0 * stats.norm.cdf(d1, 0.0, 1.0) 70 | - self.K * exp(-self.r * self.T) * stats.norm.cdf(d2, 0.0, 1.0)) 71 | return value 72 | 73 | def vega(self): 74 | ''' Return Vega of option. ''' 75 | self.update_ttm() 76 | d1 = self.d1() 77 | vega = self.S0 * stats.norm.pdf(d1, 0.0, 1.0) * sqrt(self.T) 78 | return vega 79 | 80 | def imp_vol(self, C0, sigma_est=0.2): 81 | ''' Return implied volatility given option price. ''' 82 | option = call_option(self.S0, self.K, self.t, self.M, 83 | self.r, sigma_est) 84 | option.update_ttm() 85 | def difference(sigma): 86 | option.sigma = sigma 87 | return option.value() - C0 88 | iv = fsolve(difference, sigma_est)[0] 89 | return iv 90 | -------------------------------------------------------------------------------- /dawp/book/03_stf/DAX_imp_vol.py: -------------------------------------------------------------------------------- 1 | # 2 | # Black-Scholes-Merton Implied Volatilities of 3 | # Call Options on the DAX 4 | # Quotes from 29 Apr 2011 5 | # Source: www.eurexchange.com 6 | # DAX_Imp_Vol.py 7 | # 8 | # (c) Dr. Yves J. Hilpisch 9 | # Derivatives Analytics with Python 10 | # 11 | from pylab import * 12 | import mpl_toolkits.mplot3d.axes3d as p3 13 | from BSM_Imp_Vol import * 14 | 15 | # 16 | # Option Data 17 | # 18 | S0 = 7514.46 # initial index level 29 Apr 2011 19 | T = array((21., 49., 140., 231., 322.)) / 365. # call option maturities 20 | r = [0.0124, 0.0132, 0.015, 0.019, 0.0214] # approx. short rates 21 | 22 | # May 2011 23 | K = array((7000, 7050, 7100, 7150, 7200, 7250, 7300, 7350, 7400, 7450, 24 | 7500, 7550, 7600, 7650, 7700, 7750, 7800, 7850, 7900, 7950, 8000)) 25 | C1 = array((530.8, 482.9, 435.2, 388.5, 342.5, 297.8, 254.8, 213.7, 175.4, 26 | 140.2, 108.7, 81.6, 59, 41.2, 27.9, 18.5, 12.1, 7.9, 5.1, 3.4, 2.3)) 27 | 28 | # June 2011 29 | C2 = array((568.9, 524.5, 481.1, 438.7, 397.3, 357.5, 318.9, 281.9, 247, 214, 30 | 183.3, 155.1, 129.3, 106.3, 86.1, 68.8, 54.1, 42, 32.2, 24.5, 18.4)) 31 | 32 | # Sep 2011 33 | C3 = array((697.1, 657.9, 619.5, 581.8, 544.9, 509.1, 474.2, 440, 407.2, 375.4, 34 | 344.8, 315.3, 287.5, 260.6, 235.5, 211.5, 189, 168, 148.8, 130.7, 35 | 114.2)) 36 | 37 | # Dec 2011 38 | C4 = array((811.5, 774.4, 737.9, 702.1, 666.9, 632.3, 598.5, 565.6, 533.5, 39 | 502.1, 471.6, 442.1, 413.2, 385.6, 359, 333.2, 308.4, 284.9, 262.4, 40 | 240.9, 220.4)) 41 | 42 | # Mar 2012 43 | C5 = array((921.3, 885.4, 849.8, 814.7, 780.1, 746.6, 713.4, 680.8, 648.9, 44 | 617.7, 587.1, 557.4, 528.6, 500.2, 472.6, 446, 419.9, 395, 370.4, 45 | 347.1, 324.6)) 46 | 47 | # 48 | # BSM Implied Volatilities 49 | # 50 | imv1 = [] 51 | imv2 = [] 52 | imv3 = [] 53 | imv4 = [] 54 | imv5 = [] 55 | for j in range(len(K)): 56 | imv1.append(BSM_Call_ImpVol(S0, K[j], T[0], r[0], C1[j], 0.2)) 57 | imv2.append(BSM_Call_ImpVol(S0, K[j], T[1], r[1], C2[j], 0.2)) 58 | imv3.append(BSM_Call_ImpVol(S0, K[j], T[2], r[2], C3[j], 0.2)) 59 | imv4.append(BSM_Call_ImpVol(S0, K[j], T[3], r[3], C4[j], 0.2)) 60 | imv5.append(BSM_Call_ImpVol(S0, K[j], T[4], r[4], C5[j], 0.2)) 61 | imv1 = array(imv1) 62 | imv2 = array(imv2) 63 | imv3 = array(imv3) 64 | imv4 = array(imv4) 65 | imv5 = array(imv5) 66 | imv = array((imv1, imv2, imv3, imv4, imv5)) 67 | 68 | # 69 | # Graphical Output 70 | # 71 | ## 2d Output 72 | figure() 73 | plot(K, imv[0] * 100, 'ro') 74 | plot(K, imv[1] * 100, 'gx') 75 | plot(K, imv[2] * 100, 'bv') 76 | plot(K, imv[3] * 100, 'yD') 77 | plot(K, imv[4] * 100, 'mh') 78 | grid(True) 79 | xlabel('Strike') 80 | ylabel('Implied Volatility') 81 | 82 | ## 3d Output 83 | k, t = meshgrid(K, T) 84 | fig = figure() 85 | plot = p3.Axes3D(fig) 86 | plot.plot_wireframe(k, t, imv) 87 | plot.set_xlabel('K') 88 | plot.set_ylabel('T') 89 | plot.set_zlabel('Implied Volatility') -------------------------------------------------------------------------------- /dawp/book/03_stf/DAX_returns.py: -------------------------------------------------------------------------------- 1 | # 2 | # Analyzing DAX Index Quotes and Returns 3 | # Source: http://finance.yahoo.com 4 | # 03_stf/DAX_returns.py 5 | # 6 | # (c) Dr. Yves J. Hilpisch 7 | # Derivatives Analytics with Python 8 | # 9 | import pandas.io.data as web 10 | from GBM_returns import * 11 | 12 | # Read Data for DAX from the Web 13 | def read_dax_data(): 14 | ''' Reads historical DAX data from Yahoo! Finance, calculates log returns, 15 | realized variance and volatility.''' 16 | DAX = web.DataReader('^GDAXI', data_source='yahoo', 17 | start='30-09-2004', end='30-09-2014') 18 | DAX.rename(columns={'Adj Close' : 'index'}, inplace=True) 19 | DAX['returns'] = np.log(DAX['index'] / DAX['index'].shift(1)) 20 | DAX['rea_var'] = 252 * np.cumsum(DAX['returns'] ** 2) / np.arange(len(DAX)) 21 | DAX['rea_vol'] = np.sqrt(DAX['rea_var']) 22 | DAX = DAX.dropna() 23 | return DAX 24 | 25 | def count_jumps(data, value): 26 | ''' Counts the number of return jumps as defined in size by value. ''' 27 | jumps = np.sum(np.abs(data['returns']) > value) 28 | return jumps 29 | -------------------------------------------------------------------------------- /dawp/book/03_stf/ES50_imp_vol.py: -------------------------------------------------------------------------------- 1 | # 2 | # Black-Scholes-Merton Implied Volatilities of 3 | # Call Options on the EURO STOXX 50 4 | # Option Quotes from 30. September 2014 5 | # Source: www.eurexchange.com, www.stoxx.com 6 | # 03_stf/ES50_imp_vol.py 7 | # 8 | # (c) Dr. Yves J. Hilpisch 9 | # Derivatives Analytics with Python 10 | # 11 | import numpy as np 12 | import pandas as pd 13 | from BSM_imp_vol import call_option 14 | import matplotlib as mpl 15 | import matplotlib.pyplot as plt 16 | mpl.rcParams['font.family'] = 'serif' 17 | 18 | # Pricing Data 19 | pdate = pd.Timestamp('30-09-2014') 20 | 21 | # 22 | # EURO STOXX 50 index data 23 | # 24 | 25 | # URL of data file 26 | es_url = 'http://www.stoxx.com/download/historical_values/hbrbcpe.txt' 27 | # column names to be used 28 | cols = ['Date', 'SX5P', 'SX5E', 'SXXP', 'SXXE', 29 | 'SXXF', 'SXXA', 'DK5F', 'DKXF', 'DEL'] 30 | # reading the data with pandas 31 | es = pd.read_csv(es_url, # filename 32 | header=None, # ignore column names 33 | index_col=0, # index column (dates) 34 | parse_dates=True, # parse these dates 35 | dayfirst=True, # format of dates 36 | skiprows=4, # ignore these rows 37 | sep=';', # data separator 38 | names=cols) # use these column names 39 | # deleting the helper column 40 | del es['DEL'] 41 | S0 = es['SX5E']['30-09-2014'] 42 | r = -0.05 43 | 44 | # 45 | # Option Data 46 | # 47 | data = pd.HDFStore('./03_stf/es50_option_data.h5', 'r')['data'] 48 | 49 | # 50 | # BSM Implied Volatilities 51 | # 52 | 53 | def calculate_imp_vols(data): 54 | ''' Calculate all implied volatilities for the European call options 55 | given the tolerance level for moneyness of the option.''' 56 | data['Imp_Vol'] = 0.0 57 | tol = 0.30 # tolerance for moneyness 58 | for row in data.index: 59 | t = data['Date'][row] 60 | T = data['Maturity'][row] 61 | ttm = (T - t).days / 365. 62 | forward = np.exp(r * ttm) * S0 63 | if (abs(data['Strike'][row] - forward) / forward) < tol: 64 | call = call_option(S0, data['Strike'][row], t, T, r, 0.2) 65 | data['Imp_Vol'][row] = call.imp_vol(data['Call'][row]) 66 | return data 67 | 68 | # 69 | # Graphical Output 70 | # 71 | markers = ['.', 'o', '^', 'v', 'x', 'D', 'd', '>', '<'] 72 | def plot_imp_vols(data): 73 | ''' Plot the implied volatilites. ''' 74 | maturities = sorted(set(data['Maturity'])) 75 | plt.figure(figsize=(10, 5)) 76 | for i, mat in enumerate(maturities): 77 | dat = data[(data['Maturity'] == mat) & (data['Imp_Vol'] > 0)] 78 | plt.plot(dat['Strike'].values, dat['Imp_Vol'].values, 79 | 'b%s' % markers[i], label=str(mat)[:10]) 80 | plt.grid() 81 | plt.legend() 82 | plt.xlabel('strike') 83 | plt.ylabel('implied volatility') 84 | -------------------------------------------------------------------------------- /dawp/book/03_stf/EURIBOR_analysis.py: -------------------------------------------------------------------------------- 1 | # 2 | # Analyzing Euribor Interest Rate Data 3 | # Source: http://www.emmi-benchmarks.eu/euribor-org/euribor-rates.html 4 | # 03_stf/EURIBOR_analysis.py 5 | # 6 | # (c) Dr. Yves J. Hilpisch 7 | # Derivatives Analytics with Python 8 | # 9 | import pandas as pd 10 | from GBM_returns import * 11 | 12 | # Read Data for Euribor from Excel file 13 | def read_euribor_data(): 14 | ''' Reads historical Euribor data from Excel file, calculates log returns, 15 | realized variance and volatility.''' 16 | EBO = pd.read_excel('./03_stf/EURIBOR_current.xlsx', 17 | index_col=0, parse_dates=True) 18 | EBO['returns'] = np.log(EBO['1w'] / EBO['1w'].shift(1)) 19 | EBO = EBO.dropna() 20 | return EBO 21 | 22 | # Plot the Term Structure 23 | markers = [',', '-.', '--', '-'] 24 | def plot_term_structure(data): 25 | ''' Plot the term structure of Euribor rates. ''' 26 | plt.figure(figsize=(10, 5)) 27 | for i, mat in enumerate(['1w', '1m', '6m', '12m']): 28 | plt.plot(data[mat].index, data[mat].values, 29 | 'b%s' % markers[i], label=mat) 30 | plt.grid() 31 | plt.legend() 32 | plt.xlabel('strike') 33 | plt.ylabel('implied volatility') 34 | plt.ylim(0.0, plt.ylim()[1]) 35 | -------------------------------------------------------------------------------- /dawp/book/03_stf/EURIBOR_current.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/03_stf/EURIBOR_current.xlsx -------------------------------------------------------------------------------- /dawp/book/03_stf/GBM_returns.py: -------------------------------------------------------------------------------- 1 | # 2 | # Analyzing Returns from Geometric Brownian Motion 3 | # 03_stf/GBM_returns.py 4 | # 5 | # (c) Dr. Yves J. Hilpisch 6 | # Derivatives Analytics with Python 7 | # 8 | import math 9 | import numpy as np 10 | import pandas as pd 11 | import scipy.stats as scs 12 | import statsmodels.api as sm 13 | import matplotlib as mpl 14 | import matplotlib.pyplot as plt 15 | mpl.rcParams['font.family'] = 'serif' 16 | 17 | # 18 | # Helper Function 19 | # 20 | 21 | 22 | def dN(x, mu, sigma): 23 | ''' Probability density function of a normal random variable x. 24 | 25 | Parameters 26 | ========== 27 | mu : float 28 | expected value 29 | sigma : float 30 | standard deviation 31 | 32 | Returns 33 | ======= 34 | pdf : float 35 | value of probability density function 36 | ''' 37 | z = (x - mu) / sigma 38 | pdf = np.exp(-0.5 * z ** 2) / math.sqrt(2 * math.pi * sigma ** 2) 39 | return pdf 40 | 41 | 42 | # 43 | # Simulate a Number of Years of Daily Stock Quotes 44 | # 45 | def simulate_gbm(): 46 | # model parameters 47 | S0 = 100.0 # initial index level 48 | T = 10.0 # time horizon 49 | r = 0.05 # risk-less short rate 50 | vol = 0.2 # instantaneous volatility 51 | 52 | # simulation parameters 53 | np.random.seed(250000) 54 | gbm_dates = pd.DatetimeIndex(start='30-09-2004', 55 | end='30-09-2014', 56 | freq='B') 57 | M = len(gbm_dates) # time steps 58 | I = 1 # index level paths 59 | dt = 1 / 252. # fixed for simplicity 60 | df = math.exp(-r * dt) # discount factor 61 | 62 | # stock price paths 63 | rand = np.random.standard_normal((M, I)) # random numbers 64 | S = np.zeros_like(rand) # stock matrix 65 | S[0] = S0 # initial values 66 | for t in range(1, M): # stock price paths 67 | S[t] = S[t - 1] * np.exp((r - vol ** 2 / 2) * dt 68 | + vol * rand[t] * math.sqrt(dt)) 69 | 70 | gbm = pd.DataFrame(S[:, 0], index=gbm_dates, columns=['index']) 71 | gbm['returns'] = np.log(gbm['index'] / gbm['index'].shift(1)) 72 | 73 | 74 | # Realized Volatility (eg. as defined for variance swaps) 75 | gbm['rea_var'] = 252 * np.cumsum(gbm['returns'] ** 2) / np.arange(len(gbm)) 76 | gbm['rea_vol'] = np.sqrt(gbm['rea_var']) 77 | gbm = gbm.dropna() 78 | return gbm 79 | 80 | # Return Sample Statistics and Normality Tests 81 | 82 | def print_statistics(data): 83 | print "RETURN SAMPLE STATISTICS" 84 | print "---------------------------------------------" 85 | print "Mean of Daily Log Returns %9.6f" % np.mean(data['returns']) 86 | print "Std of Daily Log Returns %9.6f" % np.std(data['returns']) 87 | print "Mean of Annua. Log Returns %9.6f" % (np.mean(data['returns']) * 252) 88 | print "Std of Annua. Log Returns %9.6f" % \ 89 | (np.std(data['returns']) * math.sqrt(252)) 90 | print "---------------------------------------------" 91 | print "Skew of Sample Log Returns %9.6f" % scs.skew(data['returns']) 92 | print "Skew Normal Test p-value %9.6f" % scs.skewtest(data['returns'])[1] 93 | print "---------------------------------------------" 94 | print "Kurt of Sample Log Returns %9.6f" % scs.kurtosis(data['returns']) 95 | print "Kurt Normal Test p-value %9.6f" % \ 96 | scs.kurtosistest(data['returns'])[1] 97 | print "---------------------------------------------" 98 | print "Normal Test p-value %9.6f" % \ 99 | scs.normaltest(data['returns'])[1] 100 | print "---------------------------------------------" 101 | print "Realized Volatility %9.6f" % data['rea_vol'].iloc[-1] 102 | print "Realized Variance %9.6f" % data['rea_var'].iloc[-1] 103 | 104 | # 105 | # Graphical Output 106 | # 107 | 108 | # daily quotes and log returns 109 | def quotes_returns(data): 110 | ''' Plots quotes and returns. ''' 111 | plt.figure(figsize=(9, 6)) 112 | plt.subplot(211) 113 | data['index'].plot() 114 | plt.ylabel('daily quotes') 115 | plt.grid(True) 116 | plt.axis('tight') 117 | 118 | plt.subplot(212) 119 | data['returns'].plot() 120 | plt.ylabel('daily log returns') 121 | plt.grid(True) 122 | plt.axis('tight') 123 | 124 | # histogram of annualized daily log returns 125 | def return_histogram(data): 126 | ''' Plots a histogram of the returns. ''' 127 | plt.figure(figsize=(9, 5)) 128 | x = np.linspace(min(data['returns']), max(data['returns']), 100) 129 | plt.hist(np.array(data['returns']), bins=50, normed=True) 130 | y = dN(x, np.mean(data['returns']), np.std(data['returns'])) 131 | plt.plot(x, y, linewidth=2) 132 | plt.xlabel('log returns') 133 | plt.ylabel('frequency/probability') 134 | plt.grid(True) 135 | 136 | # Q-Q plot of annualized daily log returns 137 | def return_qqplot(data): 138 | ''' Generates a Q-Q plot of the returns.''' 139 | plt.figure(figsize=(9, 5)) 140 | sm.qqplot(data['returns'], line='s') 141 | plt.grid(True) 142 | plt.xlabel('theoretical quantiles') 143 | plt.ylabel('sample quantiles') 144 | 145 | 146 | # realized volatility 147 | def realized_volatility(data): 148 | ''' Plots the realized volatility. ''' 149 | plt.figure(figsize=(9, 5)) 150 | data['rea_vol'].plot() 151 | plt.ylabel('realized volatility') 152 | plt.grid(True) 153 | 154 | # mean return, volatility and correlation (252 days moving = 1 year) 155 | def rolling_statistics(data): 156 | ''' Calculates and plots rolling statistics (mean, std, correlation). ''' 157 | plt.figure(figsize=(11, 8)) 158 | 159 | plt.subplot(311) 160 | mr = pd.rolling_mean(data['returns'], 252) * 252 161 | mr.plot() 162 | plt.grid(True) 163 | plt.ylabel('returns (252d)') 164 | plt.axhline(mr.mean(), color='r', ls='dashed', lw=1.5) 165 | 166 | plt.subplot(312) 167 | vo = pd.rolling_std(data['returns'], 252) * math.sqrt(252) 168 | vo.plot() 169 | plt.grid(True) 170 | plt.ylabel('volatility (252d)') 171 | plt.axhline(vo.mean(), color='r', ls='dashed', lw=1.5) 172 | vx = plt.axis() 173 | 174 | plt.subplot(313) 175 | co = pd.rolling_corr(mr, vo, 252) 176 | co.plot() 177 | plt.grid(True) 178 | plt.ylabel('correlation (252d)') 179 | cx = plt.axis() 180 | plt.axis([vx[0], vx[1], cx[2], cx[3]]) 181 | plt.axhline(co.mean(), color='r', ls='dashed', lw=1.5) 182 | -------------------------------------------------------------------------------- /dawp/book/03_stf/SX5E.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/03_stf/SX5E.h5 -------------------------------------------------------------------------------- /dawp/book/03_stf/bsm_functions.py: -------------------------------------------------------------------------------- 1 | # 2 | # Valuation of European call options in Black-Scholes-Merton model 3 | # incl. Vega function and implied volatility estimation 4 | # bsm_functions.py 5 | # 6 | 7 | # Analytical Black-Scholes-Merton (BSM) Formula 8 | 9 | 10 | def bsm_call_value(S0, K, T, r, sigma): 11 | ''' Valuation of European call option in BSM model. 12 | Analytical formula. 13 | 14 | Parameters 15 | ========== 16 | S0 : float 17 | initial stock/index level 18 | K : float 19 | strike price 20 | T : float 21 | maturity date (in year fractions) 22 | r : float 23 | constant risk-free short rate 24 | sigma : float 25 | volatility factor in diffusion term 26 | 27 | Returns 28 | ======= 29 | value : float 30 | present value of the European call option 31 | ''' 32 | from math import log, sqrt, exp 33 | from scipy import stats 34 | 35 | S0 = float(S0) 36 | d1 = (log(S0 / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * sqrt(T)) 37 | d2 = (log(S0 / K) + (r - 0.5 * sigma ** 2) * T) / (sigma * sqrt(T)) 38 | value = (S0 * stats.norm.cdf(d1, 0.0, 1.0) 39 | - K * exp(-r * T) * stats.norm.cdf(d2, 0.0, 1.0)) 40 | # stats.norm.cdf --> cumulative distribution function 41 | # for normal distribution 42 | return value 43 | 44 | # Vega function 45 | 46 | 47 | def bsm_vega(S0, K, T, r, sigma): 48 | ''' Vega of European option in BSM Model. 49 | 50 | Parameters 51 | ========== 52 | S0 : float 53 | initial stock/index level 54 | K : float 55 | strike price 56 | T : float 57 | maturity date (in year fractions) 58 | r : float 59 | constant risk-free short rate 60 | sigma : float 61 | volatility factor in diffusion term 62 | 63 | Returns 64 | ======= 65 | vega : float 66 | partial derivative of BSM formula with respect 67 | to sigma, i.e. Vega 68 | 69 | ''' 70 | from math import log, sqrt 71 | from scipy import stats 72 | 73 | S0 = float(S0) 74 | d1 = (log(S0 / K) + (r + (0.5 * sigma ** 2) * T)) / (sigma * sqrt(T)) 75 | vega = S0 * stats.norm.pdf(d1, 0.0, 1.0) * sqrt(T) 76 | return vega 77 | 78 | # Implied volatility function 79 | 80 | 81 | def bsm_call_imp_vol(S0, K, T, r, C0, sigma_est, it=100): 82 | ''' Implied Volatility of European call option in BSM Model. 83 | 84 | Parameters 85 | ========== 86 | S0 : float 87 | initial stock/index level 88 | K : float 89 | strike price 90 | T : float 91 | maturity date (in year fractions) 92 | r : float 93 | constant risk-free short rate 94 | sigma_est : float 95 | estimate of impl. volatility 96 | it : integer 97 | number of iterations 98 | 99 | Returns 100 | ======= 101 | simga_est : float 102 | numerically estimated implied volatility 103 | ''' 104 | for i in range(it): 105 | sigma_est -= ((bsm_call_value(S0, K, T, r, sigma_est) - C0) 106 | / bsm_vega(S0, K, T, r, sigma_est)) 107 | return sigma_est 108 | -------------------------------------------------------------------------------- /dawp/book/03_stf/es50_option_data.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/03_stf/es50_option_data.h5 -------------------------------------------------------------------------------- /dawp/book/03_stf/es50_option_data.h5r: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/03_stf/es50_option_data.h5r -------------------------------------------------------------------------------- /dawp/book/05_com/BSM_call_greeks.py: -------------------------------------------------------------------------------- 1 | # 2 | # Black-Scholes-Merton (1973) European Call Option Greeks 3 | # 05_com/BSM_call_greeks.py 4 | # 5 | # (c) Dr. Yves J. Hilpisch 6 | # Derivatives Analytics with Python 7 | # 8 | import math 9 | import numpy as np 10 | import matplotlib as mpl 11 | import matplotlib.pyplot as plt 12 | mpl.rcParams['font.family'] = 'serif' 13 | import mpl_toolkits.mplot3d.axes3d as p3 14 | from BSM_option_valuation import d1f, N, dN 15 | 16 | # 17 | # Functions for Greeks 18 | # 19 | 20 | 21 | def BSM_delta(St, K, t, T, r, sigma): 22 | ''' Black-Scholes-Merton DELTA of European call option. 23 | 24 | Parameters 25 | ========== 26 | St : float 27 | stock/index level at time t 28 | K : float 29 | strike price 30 | t : float 31 | valuation date 32 | T : float 33 | date of maturity/time-to-maturity if t = 0; T > t 34 | r : float 35 | constant, risk-less short rate 36 | sigma : float 37 | volatility 38 | 39 | Returns 40 | ======= 41 | delta : float 42 | European call option DELTA 43 | ''' 44 | d1 = d1f(St, K, t, T, r, sigma) 45 | delta = N(d1) 46 | return delta 47 | 48 | 49 | def BSM_gamma(St, K, t, T, r, sigma): 50 | ''' Black-Scholes-Merton GAMMA of European call option. 51 | 52 | Parameters 53 | ========== 54 | St : float 55 | stock/index level at time t 56 | K : float 57 | strike price 58 | t : float 59 | valuation date 60 | T : float 61 | date of maturity/time-to-maturity if t = 0; T > t 62 | r : float 63 | constant, risk-less short rate 64 | sigma : float 65 | volatility 66 | 67 | Returns 68 | ======= 69 | gamma : float 70 | European call option GAMM 71 | ''' 72 | d1 = d1f(St, K, t, T, r, sigma) 73 | gamma = dN(d1) / (St * sigma * math.sqrt(T - t)) 74 | return gamma 75 | 76 | 77 | def BSM_theta(St, K, t, T, r, sigma): 78 | ''' Black-Scholes-Merton THETA of European call option. 79 | 80 | Parameters 81 | ========== 82 | St : float 83 | stock/index level at time t 84 | K : float 85 | strike price 86 | t : float 87 | valuation date 88 | T : float 89 | date of maturity/time-to-maturity if t = 0; T > t 90 | r : float 91 | constant, risk-less short rate 92 | sigma : float 93 | volatility 94 | 95 | Returns 96 | ======= 97 | theta : float 98 | European call option THETA 99 | ''' 100 | d1 = d1f(St, K, t, T, r, sigma) 101 | d2 = d1 - sigma * math.sqrt(T - t) 102 | theta = -(St * dN(d1) * sigma / (2 * math.sqrt(T - t)) 103 | + r * K * math.exp(-r * (T - t)) * N(d2)) 104 | return theta 105 | 106 | 107 | def BSM_rho(St, K, t, T, r, sigma): 108 | ''' Black-Scholes-Merton RHO of European call option. 109 | 110 | Parameters 111 | ========== 112 | St : float 113 | stock/index level at time t 114 | K : float 115 | strike price 116 | t : float 117 | valuation date 118 | T : float 119 | date of maturity/time-to-maturity if t = 0; T > t 120 | r : float 121 | constant, risk-less short rate 122 | sigma : float 123 | volatility 124 | 125 | Returns 126 | ======= 127 | rho : float 128 | European call option RHO 129 | ''' 130 | d1 = d1f(St, K, t, T, r, sigma) 131 | d2 = d1 - sigma * math.sqrt(T - t) 132 | rho = K * (T - t) * math.exp(-r * (T - t)) * N(d2) 133 | return rho 134 | 135 | 136 | def BSM_vega(St, K, t, T, r, sigma): 137 | ''' Black-Scholes-Merton VEGA of European call option. 138 | 139 | Parameters 140 | ========== 141 | St : float 142 | stock/index level at time t 143 | K : float 144 | strike price 145 | t : float 146 | valuation date 147 | T : float 148 | date of maturity/time-to-maturity if t = 0; T > t 149 | r : float 150 | constant, risk-less short rate 151 | sigma : float 152 | volatility 153 | 154 | Returns 155 | ======= 156 | vega : float 157 | European call option VEGA 158 | ''' 159 | d1 = d1f(St, K, t, T, r, sigma) 160 | vega = St * dN(d1) * math.sqrt(T - t) 161 | return vega 162 | 163 | # 164 | # Plotting the Greeks 165 | # 166 | def plot_greeks(function, greek): 167 | # Model Parameters 168 | St = 100.0 # index level 169 | K = 100.0 # option strike 170 | t = 0.0 # valuation date 171 | T = 1.0 # maturity date 172 | r = 0.05 # risk-less short rate 173 | sigma = 0.2 # volatility 174 | 175 | # Greek Calculations 176 | tlist = np.linspace(0.01, 1, 25) 177 | klist = np.linspace(80, 120, 25) 178 | V = np.zeros((len(tlist), len(klist)), dtype=np.float) 179 | for j in range(len(klist)): 180 | for i in range(len(tlist)): 181 | V[i, j] = function(St, klist[j], t, tlist[i], r, sigma) 182 | 183 | # 3D Plotting 184 | x, y = np.meshgrid(klist, tlist) 185 | fig = plt.figure(figsize=(9, 5)) 186 | plot = p3.Axes3D(fig) 187 | plot.plot_wireframe(x, y, V) 188 | plot.set_xlabel('strike $K$') 189 | plot.set_ylabel('maturity $T$') 190 | plot.set_zlabel('%s(K, T)' % greek) 191 | -------------------------------------------------------------------------------- /dawp/book/05_com/BSM_option_valuation.py: -------------------------------------------------------------------------------- 1 | # 2 | # Black-Scholes-Merton (1973) European Call & Put Valuation 3 | # 05_com/BSM_option_valuation.py 4 | # 5 | # (c) Dr. Yves J. Hilpisch 6 | # Derivatives Analytics with Python 7 | # 8 | import math 9 | import numpy as np 10 | import matplotlib as mpl 11 | import matplotlib.pyplot as plt 12 | mpl.rcParams['font.family'] = 'serif' 13 | from scipy.integrate import quad 14 | 15 | # 16 | # Helper Functions 17 | # 18 | 19 | 20 | def dN(x): 21 | ''' Probability density function of standard normal random variable x. ''' 22 | return math.exp(-0.5 * x ** 2) / math.sqrt(2 * math.pi) 23 | 24 | 25 | def N(d): 26 | ''' Cumulative density function of standard normal random variable x. ''' 27 | return quad(lambda x: dN(x), -20, d, limit=50)[0] 28 | 29 | 30 | def d1f(St, K, t, T, r, sigma): 31 | ''' Black-Scholes-Merton d1 function. 32 | Parameters see e.g. BSM_call_value function. ''' 33 | d1 = (math.log(St / K) + (r + 0.5 * sigma ** 2) 34 | * (T - t)) / (sigma * math.sqrt(T - t)) 35 | return d1 36 | 37 | # 38 | # Valuation Functions 39 | # 40 | 41 | 42 | def BSM_call_value(St, K, t, T, r, sigma): 43 | ''' Calculates Black-Scholes-Merton European call option value. 44 | 45 | Parameters 46 | ========== 47 | St : float 48 | stock/index level at time t 49 | K : float 50 | strike price 51 | t : float 52 | valuation date 53 | T : float 54 | date of maturity/time-to-maturity if t = 0; T > t 55 | r : float 56 | constant, risk-less short rate 57 | sigma : float 58 | volatility 59 | 60 | Returns 61 | ======= 62 | call_value : float 63 | European call present value at t 64 | ''' 65 | d1 = d1f(St, K, t, T, r, sigma) 66 | d2 = d1 - sigma * math.sqrt(T - t) 67 | call_value = St * N(d1) - math.exp(-r * (T - t)) * K * N(d2) 68 | return call_value 69 | 70 | 71 | def BSM_put_value(St, K, t, T, r, sigma): 72 | ''' Calculates Black-Scholes-Merton European put option value. 73 | 74 | Parameters 75 | ========== 76 | St : float 77 | stock/index level at time t 78 | K : float 79 | strike price 80 | t : float 81 | valuation date 82 | T : float 83 | date of maturity/time-to-maturity if t = 0; T > t 84 | r : float 85 | constant, risk-less short rate 86 | sigma : float 87 | volatility 88 | 89 | Returns 90 | ======= 91 | put_value : float 92 | European put present value at t 93 | ''' 94 | put_value = BSM_call_value(St, K, t, T, r, sigma) \ 95 | - St + math.exp(-r * (T - t)) * K 96 | return put_value 97 | 98 | 99 | 100 | 101 | # 102 | # Plotting European Option Values 103 | # 104 | 105 | 106 | def plot_values(function): 107 | ''' Plots European option values for different parameters c.p. ''' 108 | plt.figure(figsize=(10, 8.3)) 109 | points = 100 110 | # 111 | # Model Parameters 112 | # 113 | St = 100.0 # index level 114 | K = 100.0 # option strike 115 | t = 0.0 # valuation date 116 | T = 1.0 # maturity date 117 | r = 0.05 # risk-less short rate 118 | sigma = 0.2 # volatility 119 | 120 | # C(K) plot 121 | plt.subplot(221) 122 | klist = np.linspace(80, 120, points) 123 | vlist = [function(St, K, t, T, r, sigma) for K in klist] 124 | plt.plot(klist, vlist) 125 | plt.grid() 126 | plt.xlabel('strike $K$') 127 | plt.ylabel('present value') 128 | 129 | # C(T) plot 130 | plt.subplot(222) 131 | tlist = np.linspace(0.0001, 1, points) 132 | vlist = [function(St, K, t, T, r, sigma) for T in tlist] 133 | plt.plot(tlist, vlist) 134 | plt.grid(True) 135 | plt.xlabel('maturity $T$') 136 | 137 | # C(r) plot 138 | plt.subplot(223) 139 | rlist = np.linspace(0, 0.1, points) 140 | vlist = [function(St, K, t, T, r, sigma) for r in rlist] 141 | plt.plot(tlist, vlist) 142 | plt.grid(True) 143 | plt.xlabel('short rate $r$') 144 | plt.ylabel('present value') 145 | plt.axis('tight') 146 | 147 | # C(sigma) plot 148 | plt.subplot(224) 149 | slist = np.linspace(0.01, 0.5, points) 150 | vlist = [function(St, K, t, T, r, sigma) for sigma in slist] 151 | plt.plot(slist, vlist) 152 | plt.grid(True) 153 | plt.xlabel('volatility $\sigma$') 154 | plt.tight_layout() -------------------------------------------------------------------------------- /dawp/book/05_com/CRR_option_valuation.py: -------------------------------------------------------------------------------- 1 | # 2 | # Cox-Ross-Rubinstein Binomial Model 3 | # European Option Valuation 4 | # 05_com/CRR_option_caluation.py 5 | # 6 | # (c) Dr. Yves J. Hilpisch 7 | # Derivatives Analytics with Python 8 | # 9 | import math 10 | import numpy as np 11 | import matplotlib as mpl 12 | import matplotlib.pyplot as plt 13 | mpl.rcParams['font.family'] = 'serif' 14 | from BSM_option_valuation import BSM_call_value 15 | 16 | # 17 | # Model Parameters 18 | # 19 | S0 = 100.0 # index level 20 | K = 100.0 # option strike 21 | T = 1.0 # maturity date 22 | r = 0.05 # risk-less short rate 23 | sigma = 0.2 # volatility 24 | 25 | 26 | # Valuation Function 27 | def CRR_option_value(S0, K, T, r, sigma, otype, M=4): 28 | ''' Cox-Ross-Rubinstein European option valuation. 29 | 30 | Parameters 31 | ========== 32 | S0 : float 33 | stock/index level at time 0 34 | K : float 35 | strike price 36 | T : float 37 | date of maturity 38 | r : float 39 | constant, risk-less short rate 40 | sigma : float 41 | volatility 42 | otype : string 43 | either 'call' or 'put' 44 | M : int 45 | number of time intervals 46 | ''' 47 | # Time Parameters 48 | dt = T / M # length of time interval 49 | df = math.exp(-r * dt) # discount per interval 50 | 51 | # Binomial Parameters 52 | u = math.exp(sigma * math.sqrt(dt)) # up movement 53 | d = 1 / u # down movement 54 | q = (math.exp(r * dt) - d) / (u - d) # martingale branch probability 55 | 56 | # Array Initialization for Index Levels 57 | mu = np.arange(M + 1) 58 | mu = np.resize(mu, (M + 1, M + 1)) 59 | md = np.transpose(mu) 60 | mu = u ** (mu - md) 61 | md = d ** md 62 | S = S0 * mu * md 63 | 64 | # Inner Values 65 | if otype == 'call': 66 | V = np.maximum(S - K, 0) # inner values for European call option 67 | else: 68 | V = np.maximum(K - S, 0) # inner values for European put option 69 | 70 | z = 0 71 | for t in range(M - 1, -1, -1): # backwards iteration 72 | V[0:M - z, t] = (q * V[0:M - z, t + 1] 73 | + (1 - q) * V[1:M - z + 1, t + 1]) * df 74 | z += 1 75 | return V[0, 0] 76 | 77 | def plot_convergence(mmin, mmax, step_size): 78 | ''' Plots the CRR option values for increasing number of time 79 | intervals M against the Black-Scholes-Merton benchmark value.''' 80 | BSM_benchmark = BSM_call_value(S0, K, 0, T, r, sigma) 81 | m = xrange(mmin, mmax, step_size) 82 | CRR_values = [CRR_option_value(S0, K, T, r, sigma, 'call', M) for M in m] 83 | plt.figure(figsize=(9, 5)) 84 | plt.plot(m, CRR_values, label='CRR values') 85 | plt.axhline(BSM_benchmark, color='r', ls='dashed', lw=1.5, 86 | label='BSM benchmark') 87 | plt.grid() 88 | plt.xlabel('# of binomial steps $M$') 89 | plt.ylabel('European call option value') 90 | plt.legend(loc=4) 91 | plt.xlim(0, mmax) 92 | -------------------------------------------------------------------------------- /dawp/book/06_fou/Fourier_series.py: -------------------------------------------------------------------------------- 1 | # 2 | # Fourier Series for f(x) = abs(x) for -pi <= x <= pi 3 | # 06_fou/Fourier_series.py 4 | # 5 | # (c) Dr. Yves J. Hilpisch 6 | # Derivatives Analytics with Python 7 | # 8 | import numpy as np 9 | import matplotlib.pyplot as plt 10 | 11 | # 12 | # Fourier series function 13 | # 14 | 15 | 16 | def fourier_series(x, n): 17 | ''' Generate Fourier Series from vector x for f(x) = abs(x) 18 | of order n. 19 | 20 | Parameters 21 | ========== 22 | x : float or array of floats 23 | input numbers 24 | n : int 25 | order of Fourier series 26 | 27 | Returns 28 | ======= 29 | fourier_values : float or array of floats 30 | numbers according to Fourier series approximation 31 | ''' 32 | fourier_values = np.pi / 2 33 | for i in range(1, n + 1): 34 | fourier_values += ((2 * ((-1) ** i - 1)) 35 | / (np.pi * i ** 2) * np.cos(i * x)) 36 | return fourier_values 37 | 38 | def plot_fourier_series(): 39 | # Data Generation 40 | x = np.linspace(-np.pi, np.pi, 100) 41 | y1 = fourier_series(x, 1) 42 | y2 = fourier_series(x, 5) 43 | 44 | # Data Plotting 45 | plt.figure(figsize=(10, 5)) 46 | plt.subplot(121) 47 | plt.plot(x, abs(x), 'b', label='$f(x) = |x|$') 48 | plt.plot(x, y1, 'r-.', lw=3.0, label='Fourier series $n=1$') 49 | plt.grid() 50 | plt.legend(loc=9) 51 | plt.subplot(122) 52 | plt.plot(x, abs(x), 'b', label='$f(x) = |x|$') 53 | plt.plot(x, y2, 'r-.', lw=3.0, label='Fourier series $n=5$') 54 | plt.grid() 55 | plt.legend(loc=9) 56 | -------------------------------------------------------------------------------- /dawp/book/06_fou/call_convolution.py: -------------------------------------------------------------------------------- 1 | # 2 | # Call Option Pricing with Circular Convolution (Simple) 3 | # 06_fou/call_convolution.py 4 | # 5 | # (c) Dr. Yves J. Hilpisch 6 | # Derivatives Analytics with Python 7 | # 8 | import math 9 | import numpy as np 10 | from convolution import revnp, convolution 11 | 12 | # Parameter Definitions 13 | M = 4 # number of time steps 14 | dt = 1.0 / M # length of time interval 15 | r = 0.05 # constant short rate 16 | C = [49.18246976, 22.14027582, 0, 0, 0] # call payoff at maturity 17 | q = 0.537808372 # martingale probability 18 | qv = np.array([q, 1 - q, 0, 0, 0]) # probabilitiy vector filled with zeros 19 | 20 | # Calculation 21 | V = np.zeros((M + 1, M + 1), dtype=np.float) 22 | V[M] = C 23 | 24 | for t in range(M - 1, -1, -1): 25 | V[t] = convolution(V[t + 1], revnp(qv)) * math.exp(-r * dt) 26 | 27 | print "Value of the Call Option %8.3f" % V[0, 0] 28 | 29 | -------------------------------------------------------------------------------- /dawp/book/06_fou/call_convolution_general.py: -------------------------------------------------------------------------------- 1 | # 2 | # Call Option Pricing with Circular Convolution (General) 3 | # 06_fou/call_convolution_general.py 4 | # 5 | # (c) Dr. Yves J. Hilpisch 6 | # Derivatives Analytics with Python 7 | # 8 | import numpy as np 9 | from convolution import revnp, convolution 10 | from parameters import * 11 | 12 | # Parmeter Adjustments 13 | M = 3 # number of time steps 14 | dt, df, u, d, q = get_binomial_parameters(M) 15 | 16 | # Array Generation for Stock Prices 17 | mu = np.arange(M + 1) 18 | mu = np.resize(mu, (M + 1, M + 1)) 19 | md = np.transpose(mu) 20 | mu = u ** (mu - md) 21 | md = d ** md 22 | S = S0 * mu * md 23 | 24 | # Valuation 25 | V = np.maximum(S - K, 0) 26 | qv = np.zeros((M + 1), dtype=np.float) 27 | qv[0] = q 28 | qv[1] = 1 - q 29 | for t in range(M - 1, -1, -1): 30 | V[:, t] = convolution(V[:, t + 1], revnp(qv)) * df 31 | 32 | print "Value of the Call Option %8.3f" % V[0, 0] 33 | -------------------------------------------------------------------------------- /dawp/book/06_fou/call_fft_pricing.py: -------------------------------------------------------------------------------- 1 | # 2 | # Call Option Pricing with Discrete Fourier Transforms (DFT/FFT) 3 | # 06_fou/call_fft_pricing.py 4 | # 5 | # (c) Dr. Yves J. Hilpisch 6 | # Derivatives Analytics with Python 7 | # 8 | import math 9 | import numpy as np 10 | from numpy.fft import fft, ifft 11 | from convolution import revnp 12 | from parameters import * 13 | 14 | # Parmeter Adjustments 15 | M = 3 # number of time steps 16 | dt, df, u, d, q = get_binomial_parameters(M) 17 | 18 | # Array Generation for Stock Prices 19 | mu = np.arange(M + 1) 20 | mu = np.resize(mu, (M + 1, M + 1)) 21 | md = np.transpose(mu) 22 | mu = u ** (mu - md) 23 | md = d ** md 24 | S = S0 * mu * md 25 | 26 | # Valuation by fft 27 | CT = np.maximum(S[:, -1] - K, 0) 28 | qv = np.zeros(M + 1, dtype=np.float) 29 | qv[0] = q 30 | qv[1] = 1 - q 31 | C0_a = fft(math.exp(-r * T) * ifft(CT) * ((M + 1) * ifft(revnp(qv))) ** M) 32 | C0_b = fft(math.exp(-r * T) * ifft(CT) * fft(qv) ** M) 33 | C0_c = ifft(math.exp(-r * T) * fft(CT) * fft(revnp(qv)) ** M) 34 | 35 | # Results Outpu 36 | print "Value of European option is %8.3f" % np.real(C0_a[0]) 37 | print "Value of European option is %8.3f" % np.real(C0_b[0]) 38 | print "Value of European option is %8.3f" % np.real(C0_c[0]) 39 | -------------------------------------------------------------------------------- /dawp/book/06_fou/call_fft_speed.py: -------------------------------------------------------------------------------- 1 | # 2 | # Call Option Pricing with DFT/FFT Speed Test 3 | # 06_fou/call_fft_speed.py 4 | # 5 | # (c) Dr. Yves J. Hilpisch 6 | # Derivatives Analytics with Python 7 | # 8 | import math 9 | import numpy as np 10 | from numpy.fft import fft, ifft 11 | from convolution import revnp 12 | from parameters import * 13 | 14 | def call_fft_value(M): 15 | # Parmeter Adjustments 16 | dt, df, u, d, q = get_binomial_parameters(M) 17 | 18 | # Array Generation for Stock Prices 19 | mu = np.arange(M + 1) 20 | mu = np.resize(mu, (M + 1, M + 1)) 21 | md = np.transpose(mu) 22 | mu = u ** (mu - md) 23 | md = d ** md 24 | S = S0 * mu * md 25 | 26 | # Valuation by FFT 27 | CT = np.maximum(S[:, -1] - K, 0) 28 | qv = np.zeros(M + 1, dtype=np.float) 29 | qv[0] = q 30 | qv[1] = 1 - q 31 | C0 = fft(math.exp(-r * T) * ifft(CT) * fft(qv) ** M) 32 | -------------------------------------------------------------------------------- /dawp/book/06_fou/convolution.py: -------------------------------------------------------------------------------- 1 | # 2 | # Circular convolution of two 1-dim vectors 3 | # 06_fou/convolution.py 4 | # 5 | # (c) Dr. Yves J. Hilpisch 6 | # Derivatives Analytics with Python 7 | # 8 | import numpy as np 9 | 10 | # 11 | # Function Definitions 12 | # 13 | 14 | 15 | def revpy(a): 16 | ''' Reversing the order of the vector's numbers (for loop). ''' 17 | a = np.array(a) 18 | n = len(a) 19 | c = np.zeros(n, dtype=np.float) 20 | c[0] = a[0] 21 | for j in range(1, n): 22 | c[j] = a[n - j] 23 | return c 24 | 25 | 26 | def revnp(a): 27 | ''' Reversing the order of the vector's numbers (NumPy version). ''' 28 | b = a.copy() 29 | b[1:] = b[1:][::-1] 30 | return b 31 | 32 | 33 | def convolution(a, b): 34 | ''' Convolution of two vectors. ''' 35 | if len(a) != len(b): 36 | raise ValueError("Lengths of vectors do not match.") 37 | n = len(a) 38 | c = np.zeros(n, dtype=np.float) 39 | for j in range(n): 40 | s = 0 41 | for k in range(n): 42 | if j - k >= 0: 43 | s += a[j - k] * b[k] 44 | else: 45 | s += a[j - k + n] * b[k] 46 | c[j] = s 47 | return c 48 | -------------------------------------------------------------------------------- /dawp/book/06_fou/parameters.py: -------------------------------------------------------------------------------- 1 | # 2 | # Model Parameters for European Call Option 3 | # in Binomial Model 4 | # 06_fou/parameters.py 5 | # 6 | # (c) Dr. Yves J. Hilpisch 7 | # Derivatives Analytics with Python 8 | # 9 | from math import exp, sqrt 10 | 11 | # Model and Option Parameters 12 | S0 = 100.0 # index level 13 | K = 100.0 # option strike 14 | T = 1.0 # maturity date 15 | r = 0.05 # risk-less short rate 16 | sigma = 0.2 # volatility 17 | 18 | def get_binomial_parameters(M=100): 19 | # Time Parameters 20 | dt = T / M # length of time interval 21 | df = exp(-r * dt) # discount per interval 22 | 23 | # Binomial Parameters 24 | u = exp(sigma * sqrt(dt)) # up movement 25 | d = 1 / u # down movement 26 | q = (exp(r * dt) - d) / (u - d) # martingale branch probability 27 | return dt, df, u, d, q 28 | -------------------------------------------------------------------------------- /dawp/book/06_fou/roots_of_unity.py: -------------------------------------------------------------------------------- 1 | # 2 | # Plotting Spokes and Points on a Circle 3 | # with Complex Numbers 4 | # 06_fou/roots_of_unity.py 5 | # 6 | # (c) Dr. Yves J. Hilpisch 7 | # Derivatives Analytics with Python 8 | # 9 | import numpy as np 10 | import matplotlib.pyplot as plt 11 | 12 | def generate_subplot(n): 13 | y = np.exp(1j * 2 * np.pi / n) ** np.arange(1, n + 1) 14 | for l in range(n): 15 | plt.plot(y[l].real, y[l].imag, 'ro') 16 | plt.plot((0, y[l].real), (0.0, y[l].imag), 'b') 17 | plt.axis([-1.1, 1.1, -1.1, 1.1]) 18 | plt.xlabel('$n=%s$' % n) 19 | plt.grid() 20 | 21 | def generate_plot(): 22 | plt.figure(figsize=(10, 7)) 23 | # first sub-plot for n=5 24 | plt.subplot(121) 25 | generate_subplot(n=5) 26 | 27 | # second sub-plot for n=30 28 | plt.subplot(122) 29 | generate_subplot(n=30) 30 | 31 | plt.subplots_adjust(left=0.05, bottom=0.2, top=0.8, right=1.0) -------------------------------------------------------------------------------- /dawp/book/07_amo/CRR_american_options.py: -------------------------------------------------------------------------------- 1 | # 2 | # Valuation of American Options 3 | # with the Cox-Ross-Rubinstein Model 4 | # Primal Algorithm 5 | # Case 1: American Put Option (APO) 6 | # Case 2: Short Condor Spread (SCS) 7 | # 07_amo/CRR_american_options.py 8 | # 9 | # (c) Dr. Yves J. Hilpisch 10 | # Derivatives Analytics with Python 11 | # 12 | import math 13 | import numpy as np 14 | 15 | # General Parameters and Option Values 16 | def set_parameters(otype, M): 17 | ''' Sets parameters depending on valuation case. 18 | 19 | Parameters 20 | ========== 21 | otype : int 22 | option type 23 | 1 = American put option 24 | 2 = Short Condor Spread 25 | ''' 26 | if otype == 1: 27 | # Parameters -- American Put Option 28 | S0 = 36. # initial stock level 29 | T = 1.0 # time-to-maturity 30 | r = 0.06 # short rate 31 | sigma = 0.2 # volatility 32 | 33 | elif otype == 2: 34 | # Parameters -- Short Condor Spread 35 | S0 = 100. # initial stock level 36 | T = 1.0 # time-to-maturity 37 | r = 0.05 # short rate 38 | sigma = 0.5 # volatility 39 | 40 | else: 41 | raise ValueError('Option type not known.') 42 | 43 | # Numerical Parameters 44 | dt = T / M # time interval 45 | df = math.exp(-r * dt) # discount factor 46 | u = math.exp(sigma * math.sqrt(dt)) # up-movement 47 | d = 1 / u # down-movement 48 | q = (math.exp(r * dt) - d) / (u - d) # martingale probability 49 | 50 | return S0, T, r, sigma, M, dt, df, u, d, q 51 | 52 | 53 | def inner_value(S, otype): 54 | ''' Inner value functions for American put option and short condor spread 55 | option with American exercise. 56 | 57 | Parameters 58 | ========== 59 | otype : int 60 | option type 61 | 1 = American put option 62 | 2 = Short Condor Spread 63 | ''' 64 | if otype == 1: 65 | return np.maximum(40. - S, 0) 66 | elif otype == 2: 67 | return np.minimum(40., np.maximum(90. - S, 0) 68 | + np.maximum(S - 110., 0)) 69 | else: 70 | raise ValueError('Option type not known.') 71 | 72 | 73 | def CRR_option_valuation(otype, M=500): 74 | S0, T, r, sigma, M, dt, df, u, d, q = set_parameters(otype, M) 75 | # Array Generation for Stock Prices 76 | mu = np.arange(M + 1) 77 | mu = np.resize(mu, (M + 1, M + 1)) 78 | md = np.transpose(mu) 79 | mu = u ** (mu - md) 80 | md = d ** md 81 | S = S0 * mu * md 82 | 83 | # Valuation by Backwards Induction 84 | h = inner_value(S, otype) # innver value matrix 85 | V = inner_value(S, otype) # value matrix 86 | C = np.zeros((M + 1, M + 1), dtype=np.float) # continuation values 87 | ex = np.zeros((M + 1, M + 1), dtype=np.float) # exercise matrix 88 | 89 | z = 0 90 | for i in range(M - 1, -1, -1): 91 | C[0:M - z, i] = (q * V[0:M - z, i + 1] 92 | + (1 - q) * V[1:M - z + 1, i + 1]) * df 93 | V[0:M - z, i] = np.where(h[0:M - z, i] > C[0:M - z, i], 94 | h[0:M - z, i], C[0:M - z, i]) 95 | ex[0:M - z, i] = np.where(h[0:M - z, i] > C[0:M - z, i], 1, 0) 96 | z += 1 97 | return V[0, 0] 98 | -------------------------------------------------------------------------------- /dawp/book/07_amo/LSM_primal_dual_table.py: -------------------------------------------------------------------------------- 1 | # 2 | # Valuation of American Options 3 | # with Least-Squares Monte Carlo 4 | # Primal and Dual Algorithm 5 | # Case 1: American Put Option (APO) 6 | # Case 2: Short Condor Spread (SCS) 7 | # 07_amo/LSM_primal_dual_valuation.py 8 | # 9 | # (c) Dr. Yves J. Hilpisch 10 | # Derivatives Analytics with Python 11 | # 12 | import math 13 | import numpy as np 14 | import pandas as pd 15 | from time import time 16 | from datetime import datetime 17 | import itertools as it 18 | import warnings 19 | warnings.simplefilter('ignore') 20 | 21 | t0 = time() 22 | np.random.seed(150000) # seed for Python RNG 23 | 24 | ## Simulation Parameters 25 | runs = 25 26 | write = True 27 | otype = [1, 2] # option type 28 | M = [10, 25, 50] # time steps 29 | I1 = np.array([4]) * 4096 # replications for regression 30 | I2 = np.array([1]) * 1024 # replications for valuation 31 | J = [50] # replications for nested MCS 32 | reg = [9] # no of basis functions 33 | AP = [False] # antithetic paths 34 | MM = [True] # moment matching of RN 35 | ITM = [False] # ITM paths for regression 36 | 37 | results = pd.DataFrame() 38 | 39 | # 40 | # Function Definitions 41 | # 42 | 43 | 44 | def generate_random_numbers(I): 45 | ''' Function to generate I pseudo-random numbers. ''' 46 | if AP: 47 | ran = np.random.standard_normal(I / 2) 48 | ran = np.concatenate((ran, -ran)) 49 | else: 50 | ran = np.random.standard_normal(I) 51 | if MM: 52 | ran = ran - np.mean(ran) 53 | ran = ran / np.std(ran) 54 | return ran 55 | 56 | 57 | def generate_paths(I): 58 | ''' Function to generate I stock price paths. ''' 59 | S = np.zeros((M + 1, I), dtype=np.float) # stock matrix 60 | S[0] = S0 # initial values 61 | for t in range(1, M + 1, 1): # stock price paths 62 | ran = generate_random_numbers(I) 63 | S[t] = S[t - 1] * np.exp((r - sigma ** 2 / 2) * dt 64 | + sigma * ran * math.sqrt(dt)) 65 | return S 66 | 67 | 68 | def inner_values(S): 69 | ''' Innver value functions for American put and Short Condor Spread. ''' 70 | if otype == 1: 71 | return np.maximum(40. - S, 0) 72 | else: 73 | return np.minimum(40., np.maximum(90. - S, 0) 74 | + np.maximum(S - 110., 0)) 75 | 76 | 77 | def nested_monte_carlo(St, J): 78 | ''' Function for nested Monte Carlo simulation. 79 | 80 | Parameters 81 | ========== 82 | St : float 83 | start value for S 84 | J : int 85 | number of paths to simulate 86 | 87 | Returns 88 | ======= 89 | paths : array 90 | simulated nested paths 91 | ''' 92 | ran = generate_random_numbers(J) 93 | paths = St * np.exp((r - sigma ** 2 / 2) * dt 94 | + sigma * ran * math.sqrt(dt)) 95 | return paths 96 | 97 | # 98 | # Valuation 99 | # 100 | para = it.product(otype, M, I1, I2, J, reg, AP, MM, ITM) 101 | count = 0 102 | for pa in para: 103 | otype, M, I1, I2, J, reg, AP, MM, ITM = pa 104 | ## General Parameters and Option Values 105 | if otype == 1: 106 | ## Parameters -- American Put Option 107 | S0 = 36. # initial stock level 108 | T = 1.0 # time-to-maturity 109 | r = 0.06 # short rate 110 | sigma = 0.2 # volatility 111 | V0_true = 4.48637 # American Put Option (500 steps bin. model) 112 | else: 113 | ## Parameters -- Short Condor Spread 114 | S0 = 100. # initial stock level 115 | T = 1.0 # time-to-maturity 116 | r = 0.05 # short rate 117 | sigma = 0.5 # volatility 118 | V0_true = 26.97705 # Short Condor Spread (500 steps bin. model) 119 | dt = T / M # length of time interval 120 | df = math.exp(-r * dt) # discount factor per time interval 121 | for j in range(runs): 122 | count += 1 123 | # regression estimation 124 | S = generate_paths(I1) # generate stock price paths 125 | h = inner_values(S) # inner values 126 | V = inner_values(S) # value matrix 127 | rg = np.zeros((M + 1, reg + 1), dtype=np.float) 128 | # regression parameter matrix 129 | 130 | itm = np.greater(h, 0) # ITM paths 131 | for t in xrange(M - 1, 0, -1): 132 | if ITM: 133 | S_itm = np.compress(itm[t] == 1, S[t]) 134 | V_itm = np.compress(itm[t] == 1, V[t + 1]) 135 | if len(V_itm) == 0: 136 | rg[t] = 0.0 137 | else: 138 | rg[t] = np.polyfit(S_itm, V_itm * df, reg) 139 | else: 140 | rg[t] = np.polyfit(S[t], V[t + 1] * df, reg) 141 | # regression at time t 142 | C = np.polyval(rg[t], S[t]) # continuation values 143 | V[t] = np.where(h[t] > C, h[t], V[t + 1] * df) 144 | # exercise decision 145 | 146 | ## Simulation 147 | Q = np.zeros((M + 1, I2), dtype=np.float) # martingale matrix 148 | U = np.zeros((M + 1, I2), dtype=np.float) # upper bound matrix 149 | S = generate_paths(I2) # generate stock price paths 150 | h = inner_values(S) # inner values 151 | V = inner_values(S) # value matrix 152 | 153 | ## Primal Valuation 154 | for t in xrange(M - 1, 0, -1): 155 | C = np.polyval(rg[t], S[t]) # continuation values 156 | V[t] = np.where(h[t] > C, h[t], V[t + 1] * df) 157 | # exercise decision 158 | V0 = df * np.sum(V[1]) / I2 # LSM estimator 159 | 160 | ## Dual Valuation 161 | for t in xrange(1, M + 1): 162 | for i in xrange(I2): 163 | Vt = max(h[t, i], np.polyval(rg[t], S[t, i])) 164 | # estimated value V(t,i) 165 | St = nested_monte_carlo(S[t - 1, i], J) # nested MCS 166 | Ct = np.polyval(rg[t], St) # cv from nested MCS 167 | ht = inner_values(St) # iv from nested MCS 168 | VtJ = np.sum(np.where(ht > Ct, ht, Ct)) / len(St) 169 | # average of V(t,i,j) 170 | Q[t, i] = Q[t - 1, i] / df + (Vt - VtJ) # "optimal" martingale 171 | U[t, i] = max(U[t - 1, i] / df, h[t, i] - Q[t, i]) 172 | # high estimator values 173 | if t == M: 174 | U[t, i] = np.maximum(U[t - 1, i] / df, 175 | np.mean(ht) - Q[t, i]) 176 | U0 = np.sum(U[M]) / I2 * df ** M # DUAL estimator 177 | AV = (V0 + U0) / 2 # average of LSM and DUAL estimator 178 | 179 | # output 180 | print "%4d | %4.1f | %48s " % (count, (time() - t0) / 60, pa), \ 181 | "| %6.3f | %6.3f | %6.3f" % (V0, U0, AV) 182 | # results storage 183 | results = results.append(pd.DataFrame({'otype': otype, 'runs': runs, 184 | 'M': M, 'I1': I1, 'I2': I2, 'J': J, 'reg': reg, 'AP': AP, 185 | 'MM': MM, 'ITM': ITM, 'LSM': V0, 'LSM_se': (V0 - V0_true) ** 2, 186 | 'DUAL': U0, 'DUAL_se': (U0 - V0_true) ** 2, 'AV': AV, 187 | 'AV_se': (AV - V0_true) ** 2}, index=[0,]), ignore_index=True) 188 | 189 | t1 = time() 190 | print "Total time in min %s" % ((t1 - t0) / 60) 191 | 192 | if write: 193 | h5 = pd.HDFStore('results_%s_%s.h5' % (datetime.now().date(), 194 | str(datetime.now().time())[:8]), 'w') 195 | h5['results'] = results 196 | h5.close() -------------------------------------------------------------------------------- /dawp/book/07_amo/LSM_primal_dual_valuation.py: -------------------------------------------------------------------------------- 1 | # 2 | # Valuation of American Options 3 | # with Least-Squares Monte Carlo 4 | # Primal and Dual Algorithm 5 | # Case 1: American Put Option (APO) 6 | # Case 2: Short Condor Spread (SCS) 7 | # 07_amo/LSM_primal_dual_valuation.py 8 | # 9 | # (c) Dr. Yves J. Hilpisch 10 | # Derivatives Analytics with Python 11 | # 12 | import math 13 | import numpy as np 14 | import pandas as pd 15 | from time import time 16 | from datetime import datetime 17 | import itertools as it 18 | import warnings 19 | warnings.simplefilter('ignore') 20 | 21 | t0 = time() 22 | np.random.seed(150000) # seed for Python RNG 23 | 24 | ## Simulation Parameters 25 | runs = 5 26 | write = True 27 | otype = [1, 2] # option type 28 | M = [10, 20] # time steps 29 | I1 = np.array([4, 6]) * 4096 # replications for regression 30 | I2 = np.array([1, 2]) * 1024 # replications for valuation 31 | J = [50, 75] # replications for nested MCS 32 | reg = [5, 9] # no of basis functions 33 | AP = [False, True] # antithetic paths 34 | MM = [False, True] # moment matching of RN 35 | ITM = [True, False] # ITM paths for regression 36 | 37 | results = pd.DataFrame() 38 | 39 | # 40 | # Function Definitions 41 | # 42 | 43 | 44 | def generate_random_numbers(I): 45 | ''' Function to generate I pseudo-random numbers. ''' 46 | if AP: 47 | ran = np.random.standard_normal(I / 2) 48 | ran = np.concatenate((ran, -ran)) 49 | else: 50 | ran = np.random.standard_normal(I) 51 | if MM: 52 | ran = ran - np.mean(ran) 53 | ran = ran / np.std(ran) 54 | return ran 55 | 56 | 57 | def generate_paths(I): 58 | ''' Function to generate I stock price paths. ''' 59 | S = np.zeros((M + 1, I), dtype=np.float) # stock matrix 60 | S[0] = S0 # initial values 61 | for t in range(1, M + 1, 1): # stock price paths 62 | ran = generate_random_numbers(I) 63 | S[t] = S[t - 1] * np.exp((r - sigma ** 2 / 2) * dt 64 | + sigma * ran * math.sqrt(dt)) 65 | return S 66 | 67 | 68 | def inner_values(S): 69 | ''' Innver value functions for American put and Short Condor Spread. ''' 70 | if otype == 1: 71 | return np.maximum(40. - S, 0) 72 | else: 73 | return np.minimum(40., np.maximum(90. - S, 0) 74 | + np.maximum(S - 110., 0)) 75 | 76 | 77 | def nested_monte_carlo(St, J): 78 | ''' Function for nested Monte Carlo simulation. 79 | 80 | Parameters 81 | ========== 82 | St : float 83 | start value for S 84 | J : int 85 | number of paths to simulate 86 | 87 | Returns 88 | ======= 89 | paths : array 90 | simulated nested paths 91 | ''' 92 | ran = generate_random_numbers(J) 93 | paths = St * np.exp((r - sigma ** 2 / 2) * dt 94 | + sigma * ran * math.sqrt(dt)) 95 | return paths 96 | 97 | # 98 | # Valuation 99 | # 100 | para = it.product(otype, M, I1, I2, J, reg, AP, MM, ITM) 101 | count = 0 102 | for pa in para: 103 | otype, M, I1, I2, J, reg, AP, MM, ITM = pa 104 | ## General Parameters and Option Values 105 | if otype == 1: 106 | ## Parameters -- American Put Option 107 | S0 = 36. # initial stock level 108 | T = 1.0 # time-to-maturity 109 | r = 0.06 # short rate 110 | sigma = 0.2 # volatility 111 | V0_true = 4.48637 # American Put Option (500 steps bin. model) 112 | else: 113 | ## Parameters -- Short Condor Spread 114 | S0 = 100. # initial stock level 115 | T = 1.0 # time-to-maturity 116 | r = 0.05 # short rate 117 | sigma = 0.5 # volatility 118 | V0_true = 26.97705 # Short Condor Spread (500 steps bin. model) 119 | dt = T / M # length of time interval 120 | df = math.exp(-r * dt) # discount factor per time interval 121 | for j in range(runs): 122 | count += 1 123 | # regression estimation 124 | S = generate_paths(I1) # generate stock price paths 125 | h = inner_values(S) # inner values 126 | V = inner_values(S) # value matrix 127 | rg = np.zeros((M + 1, reg + 1), dtype=np.float) 128 | # regression parameter matrix 129 | 130 | itm = np.greater(h, 0) # ITM paths 131 | for t in xrange(M - 1, 0, -1): 132 | if ITM: 133 | S_itm = np.compress(itm[t] == 1, S[t]) 134 | V_itm = np.compress(itm[t] == 1, V[t + 1]) 135 | if len(V_itm) == 0: 136 | rg[t] = 0.0 137 | else: 138 | rg[t] = np.polyfit(S_itm, V_itm * df, reg) 139 | else: 140 | rg[t] = np.polyfit(S[t], V[t + 1] * df, reg) 141 | # regression at time t 142 | C = np.polyval(rg[t], S[t]) # continuation values 143 | V[t] = np.where(h[t] > C, h[t], V[t + 1] * df) 144 | # exercise decision 145 | 146 | ## Simulation 147 | Q = np.zeros((M + 1, I2), dtype=np.float) # martingale matrix 148 | U = np.zeros((M + 1, I2), dtype=np.float) # upper bound matrix 149 | S = generate_paths(I2) # generate stock price paths 150 | h = inner_values(S) # inner values 151 | V = inner_values(S) # value matrix 152 | 153 | ## Primal Valuation 154 | for t in xrange(M - 1, 0, -1): 155 | C = np.polyval(rg[t], S[t]) # continuation values 156 | V[t] = np.where(h[t] > C, h[t], V[t + 1] * df) 157 | # exercise decision 158 | V0 = df * np.sum(V[1]) / I2 # LSM estimator 159 | 160 | ## Dual Valuation 161 | for t in xrange(1, M + 1): 162 | for i in xrange(I2): 163 | Vt = max(h[t, i], np.polyval(rg[t], S[t, i])) 164 | # estimated value V(t,i) 165 | St = nested_monte_carlo(S[t - 1, i], J) # nested MCS 166 | Ct = np.polyval(rg[t], St) # cv from nested MCS 167 | ht = inner_values(St) # iv from nested MCS 168 | VtJ = np.sum(np.where(ht > Ct, ht, Ct)) / len(St) 169 | # average of V(t,i,j) 170 | Q[t, i] = Q[t - 1, i] / df + (Vt - VtJ) # "optimal" martingale 171 | U[t, i] = max(U[t - 1, i] / df, h[t, i] - Q[t, i]) 172 | # high estimator values 173 | if t == M: 174 | U[t, i] = np.maximum(U[t - 1, i] / df, 175 | np.mean(ht) - Q[t, i]) 176 | U0 = np.sum(U[M]) / I2 * df ** M # DUAL estimator 177 | AV = (V0 + U0) / 2 # average of LSM and DUAL estimator 178 | 179 | # output 180 | print "%4d | %4.1f | %48s " % (count, (time() - t0) / 60, pa), \ 181 | "| %6.3f | %6.3f | %6.3f" % (V0, U0, AV) 182 | # results storage 183 | results = results.append(pd.DataFrame({'otype': otype, 'runs': runs, 184 | 'M': M, 'I1': I1, 'I2': I2, 'J': J, 'reg': reg, 'AP': AP, 185 | 'MM': MM, 'ITM': ITM, 'LSM': V0, 'LSM_se': (V0 - V0_true) ** 2, 186 | 'DUAL': U0, 'DUAL_se': (U0 - V0_true) ** 2, 'AV': AV, 187 | 'AV_se': (AV - V0_true) ** 2}, index=[0,]), ignore_index=True) 188 | 189 | t1 = time() 190 | print "Total time in min %s" % ((t1 - t0) / 60) 191 | 192 | if write: 193 | h5 = pd.HDFStore('results_%s_%s.h5' % (datetime.now().date(), 194 | str(datetime.now().time())[:8]), 'w') 195 | h5['results'] = results 196 | h5.close() 197 | -------------------------------------------------------------------------------- /dawp/book/07_amo/LSM_primal_valuation.py: -------------------------------------------------------------------------------- 1 | # 2 | # Valuation of American Options 3 | # with Least-Squares Monte Carlo 4 | # Primal Algorithm 5 | # American Put Option 6 | # 07_amo/LSM_primal_valuation.py 7 | # 8 | # (c) Dr. Yves J. Hilpisch 9 | # Derivatives Analytics with Python 10 | # 11 | import math 12 | import numpy as np 13 | np.random.seed(150000) 14 | 15 | # Model Parameters 16 | S0 = 36. # initial stock level 17 | K = 40. # strike price 18 | T = 1.0 # time-to-maturity 19 | r = 0.06 # short rate 20 | sigma = 0.2 # volatility 21 | 22 | # Simulation Parameters 23 | I = 25000 24 | M = 50 25 | dt = T / M 26 | df = math.exp(-r * dt) 27 | 28 | # Stock Price Paths 29 | S = S0 * np.exp(np.cumsum((r - 0.5 * sigma ** 2) * dt 30 | + sigma * math.sqrt(dt) * np.random.standard_normal((M + 1, I)), axis=0)) 31 | S[0] = S0 32 | 33 | # Inner Values 34 | h = np.maximum(K - S, 0) 35 | 36 | # Present Value Vector (Initialization) 37 | V = h[-1] 38 | 39 | # American Option Valuation by Backwards Induction 40 | for t in xrange(M - 1, 0, -1): 41 | rg = np.polyfit(S[t], V * df, 5) 42 | C = np.polyval(rg, S[t]) # continuation values 43 | V = np.where(h[t] > C, h[t], V * df) 44 | # exercise decision 45 | V0 = df * np.sum(V) / I # LSM estimator 46 | 47 | print "American put option value %5.3f" % V0 -------------------------------------------------------------------------------- /dawp/book/07_amo/results_2014-10-13_full.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/07_amo/results_2014-10-13_full.h5 -------------------------------------------------------------------------------- /dawp/book/07_amo/results_2014-10-15_table.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/07_amo/results_2014-10-15_table.h5 -------------------------------------------------------------------------------- /dawp/book/07_amo/results_2014-10-15_table_old.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/07_amo/results_2014-10-15_table_old.h5 -------------------------------------------------------------------------------- /dawp/book/07_amo/table_1.tex: -------------------------------------------------------------------------------- 1 | 2 | \def\TMP{\begin{center}\begin{tabular}{p{2cm} p{2cm} x{2cm} x{2cm} x{2cm}} 3 | \hline\hline 4 | \textbf{Algorithm} & \textbf{Value} & $M=25$ & $M=50$ & $M=75$ \tn [0.5ex] 5 | \hline 6 | LSM & Maximum & 4.526 & 4.529 & 4.544 \tn 7 | & Mean & 4.452 & 4.470 & 4.467 \tn 8 | & Difference & -0.034 & -0.016 & -0.019 \tn 9 | & Median & 4.459 & 4.471 & 4.475 \tn 10 | & Minimum & 4.342 & 4.370 & 4.391 \tn 11 | \hline 12 | DUAL & Maximum & 5.018 & 4.697 & 4.753 \tn 13 | & Mean & 4.651 & 4.595 & 4.598 \tn 14 | & Difference & 0.165 & 0.109 & 0.111 \tn 15 | & Median & 4.632 & 4.585 & 4.574 \tn 16 | & Minimum & 4.610 & 4.570 & 4.552 \tn [0.5ex] 17 | \hline 18 | Spread & Absolute & 0.199 & 0.125 & 0.131 \tn 19 | \hline \hline 20 | \end{tabular}\end{center}} 21 | \newdimen\TMPsize\settowidth{\TMPsize}{\TMP} 22 | -------------------------------------------------------------------------------- /dawp/book/07_amo/table_2.tex: -------------------------------------------------------------------------------- 1 | 2 | \def\TMP{\begin{center}\begin{tabular}{p{2cm} p{2cm} x{2cm} x{2cm} x{2cm}} 3 | \hline\hline 4 | \textbf{Algorithm} & \textbf{Value} & $M=25$ & $M=50$ & $M=75$ \tn [0.5ex] 5 | \hline 6 | LSM & Maximum & 26.683 & 26.710 & 27.063 \tn 7 | & Mean & 25.977 & 26.027 & 26.308 \tn 8 | & Difference & -1.000 & -0.950 & -0.669 \tn 9 | & Median & 25.946 & 25.989 & 26.294 \tn 10 | & Minimum & 25.181 & 25.564 & 25.521 \tn 11 | \hline 12 | DUAL & Maximum & 33.462 & 45.401 & 32.416 \tn 13 | & Mean & 27.885 & 28.177 & 27.749 \tn 14 | & Difference & 0.908 & 1.200 & 0.772 \tn 15 | & Median & 27.493 & 27.479 & 27.569 \tn 16 | & Minimum & 27.221 & 27.230 & 27.282 \tn [0.5ex] 17 | \hline 18 | Spread & Absolute & 1.908 & 2.149 & 1.441 \tn 19 | \hline \hline 20 | \end{tabular}\end{center}} 21 | \newdimen\TMPsize\settowidth{\TMPsize}{\TMP} 22 | -------------------------------------------------------------------------------- /dawp/book/08_m76/M76_calibration_FFT.py: -------------------------------------------------------------------------------- 1 | # 2 | # Calibration of Merton's (1976) 3 | # Jump Diffusion Model 4 | # via Fast Fourier Transform 5 | # 08_m76/M76_calibration_FFT.py 6 | # 7 | # (c) Dr. Yves J. Hilpisch 8 | # Derivatives Analytics with Python 9 | # 10 | import math 11 | import numpy as np 12 | np.set_printoptions(suppress=True, 13 | formatter={'all': lambda x: '%5.3f' % x}) 14 | import pandas as pd 15 | import scipy.optimize as sop 16 | import matplotlib.pyplot as plt 17 | import matplotlib as mpl 18 | mpl.rcParams['font.family'] = 'serif' 19 | from M76_valuation_FFT import M76_value_call_FFT 20 | 21 | # 22 | # Market Data from www.eurexchange.com 23 | # as of 30. September 2014 24 | # 25 | h5 = pd.HDFStore('08_m76/option_data.h5', 'r') 26 | data = h5['data'] # European call & put option data (3 maturities) 27 | h5.close() 28 | S0 = 3225.93 # EURO STOXX 50 level 29 | r = 0.0005 # ECB base rate 30 | 31 | # Option Selection 32 | tol = 0.02 33 | options = data[(np.abs(data['Strike'] - S0) / S0) < tol] 34 | 35 | # 36 | # Error Function 37 | # 38 | 39 | def M76_error_function_FFT(p0): 40 | ''' Error Function for parameter calibration in M76 Model via 41 | Carr-Madan (1999) FFT approach. 42 | 43 | Parameters 44 | ========== 45 | sigma: float 46 | volatility factor in diffusion term 47 | lamb: float 48 | jump intensity 49 | mu: float 50 | expected jump size 51 | delta: float 52 | standard deviation of jump 53 | 54 | Returns 55 | ======= 56 | RMSE: float 57 | root mean squared error 58 | ''' 59 | global i, min_RMSE 60 | sigma, lamb, mu, delta = p0 61 | if sigma < 0.0 or delta < 0.0 or lamb < 0.0: 62 | return 500.0 63 | se = [] 64 | for row, option in options.iterrows(): 65 | T = (option['Maturity'] - option['Date']).days / 365. 66 | model_value = M76_value_call_FFT(S0, option['Strike'], T, 67 | r, sigma, lamb, mu, delta) 68 | se.append((model_value - option['Call']) ** 2) 69 | RMSE = math.sqrt(sum(se) / len(se)) 70 | min_RMSE = min(min_RMSE, RMSE) 71 | if i % 50 == 0: 72 | print '%4d |' % i, np.array(p0), '| %7.3f | %7.3f' % (RMSE, min_RMSE) 73 | i += 1 74 | return RMSE 75 | 76 | def generate_plot(opt, options): 77 | # 78 | # Calculating Model Prices 79 | # 80 | sigma, lamb, mu, delta = opt 81 | options['Model'] = 0.0 82 | for row, option in options.iterrows(): 83 | T = (option['Maturity'] - option['Date']).days / 365. 84 | options.loc[row, 'Model'] = M76_value_call_FFT(S0, option['Strike'], 85 | T, r, sigma, lamb, mu, delta) 86 | 87 | # 88 | # Plotting 89 | # 90 | mats = sorted(set(options['Maturity'])) 91 | options = options.set_index('Strike') 92 | for i, mat in enumerate(mats): 93 | options[options['Maturity'] == mat][['Call', 'Model']].\ 94 | plot(style=['b-', 'ro'], title='%s' % str(mat)[:10], 95 | grid=True) 96 | plt.ylabel('option value') 97 | plt.savefig('../images/08_m76/M76_calibration_3_%s.pdf' % i) 98 | 99 | if __name__ == '__main__': 100 | # 101 | # Calibration 102 | # 103 | i = 0 # counter initialization 104 | min_RMSE = 100 # minimal RMSE initialization 105 | p0 = sop.brute(M76_error_function_FFT, ((0.075, 0.201, 0.025), 106 | (0.10, 0.401, 0.1), (-0.5, 0.01, 0.1), 107 | (0.10, 0.301, 0.1)), finish=None) 108 | 109 | # p0 = [0.15, 0.2, -0.3, 0.2] 110 | opt = sop.fmin(M76_error_function_FFT, p0, 111 | maxiter=500, maxfun=750, 112 | xtol=0.000001, ftol=0.000001) 113 | -------------------------------------------------------------------------------- /dawp/book/08_m76/M76_calibration_single.py: -------------------------------------------------------------------------------- 1 | # 2 | # Calibration of Merton's (1976) 3 | # Jump Diffusion Model 4 | # to Short Maturity Data 5 | # 08_m76/M76_calibration_single.py 6 | # 7 | # (c) Dr. Yves J. Hilpisch 8 | # Derivatives Analytics with Python 9 | # 10 | import math 11 | import numpy as np 12 | np.set_printoptions(suppress=True, 13 | formatter={'all': lambda x: '%5.3f' % x}) 14 | import pandas as pd 15 | import scipy.optimize as sop 16 | import matplotlib.pyplot as plt 17 | import matplotlib as mpl 18 | mpl.rcParams['font.family'] = 'serif' 19 | from M76_valuation_FFT import M76_value_call_FFT 20 | 21 | # 22 | # Market Data from www.eurexchange.com 23 | # as of 30. September 2014 24 | # 25 | h5 = pd.HDFStore('08_m76/option_data.h5', 'r') 26 | data = h5['data'] # European call & put option data (3 maturities) 27 | h5.close() 28 | S0 = 3225.93 # EURO STOXX 50 level 29 | r = 0.005 # assumption 30 | 31 | # Option Selection 32 | tol = 0.05 33 | options = data[(np.abs(data['Strike'] - S0) / S0) < tol] 34 | mats = sorted(set(options['Maturity'])) 35 | options = options[options['Maturity'] == mats[0]] 36 | 37 | # 38 | # Error Function 39 | # 40 | 41 | 42 | def M76_error_function_FFT(p0): 43 | ''' Error function for parameter calibration in M76 Model via 44 | Carr-Madan (1999) FFT approach. 45 | 46 | Parameters 47 | ========== 48 | sigma: float 49 | volatility factor in diffusion term 50 | lamb: float 51 | jump intensity 52 | mu: float 53 | expected jump size 54 | delta: float 55 | standard deviation of jump 56 | 57 | Returns 58 | ======= 59 | RMSE: float 60 | root mean squared error 61 | ''' 62 | global i, min_RMSE 63 | sigma, lamb, mu, delta = p0 64 | if sigma < 0.0 or delta < 0.0 or lamb < 0.0: 65 | return 500.0 66 | se = [] 67 | for row, option in options.iterrows(): 68 | T = (option['Maturity'] - option['Date']).days / 365. 69 | model_value = M76_value_call_FFT(S0, option['Strike'], T, 70 | r, sigma, lamb, mu, delta) 71 | se.append((model_value - option['Call']) ** 2) 72 | RMSE = math.sqrt(sum(se) / len(se)) 73 | min_RMSE = min(min_RMSE, RMSE) 74 | if i % 50 == 0: 75 | print '%4d |' % i, np.array(p0), '| %7.3f | %7.3f' % (RMSE, min_RMSE) 76 | i += 1 77 | return RMSE 78 | 79 | 80 | # 81 | # Graphical Output 82 | # 83 | def generate_plot(opt, options): 84 | # 85 | # Calculating Model Prices 86 | # 87 | sigma, lamb, mu, delta = opt 88 | options['Model'] = 0.0 89 | for row, option in options.iterrows(): 90 | T = (option['Maturity'] - option['Date']).days / 365. 91 | options.loc[row, 'Model'] = M76_value_call_FFT(S0, option['Strike'], 92 | T, r, sigma, lamb, mu, delta) 93 | 94 | # 95 | # Plotting 96 | # 97 | options = options.set_index('Strike') 98 | fig, ax = plt.subplots(2, sharex=True, figsize=(8, 7)) 99 | options[['Call', 'Model']].plot(style=['b-', 'ro'], 100 | title='%s' % str(option['Maturity'])[:10], ax=ax[0]) 101 | ax[0].set_ylabel('option values') 102 | ax[0].grid(True) 103 | xv = options.index.values 104 | ax[1] = plt.bar(xv - 5 / 2., options['Model'] - options['Call'], 105 | width=5) 106 | plt.ylabel('difference') 107 | plt.xlim(min(xv) - 10, max(xv) + 10) 108 | plt.tight_layout() 109 | plt.grid(True) 110 | 111 | 112 | # 113 | # Calibration 114 | # 115 | if __name__ == '__main__': 116 | i = 0 117 | min_RMSE = 100. 118 | p0 = sop.brute(M76_error_function_FFT, ((0.10, 0.201, 0.025), 119 | (0.1, 0.8, 0.1), (-0.4, 0.01, 0.1), 120 | (0.00, 0.121, 0.02)), finish=None) 121 | 122 | opt = sop.fmin(M76_error_function_FFT, p0, xtol=0.00001, 123 | ftol=0.00001, maxiter=750, maxfun=1500) -------------------------------------------------------------------------------- /dawp/book/08_m76/M76_valuation_FFT.py: -------------------------------------------------------------------------------- 1 | # 2 | # Valuation of European Call Options 3 | # in Merton's (1976) Jump Diffusion Model 4 | # via Fast Fourier Transform (FFT) 5 | # 08_m76/M76_valuation_FFT.py 6 | # 7 | # (c) Dr. Yves J. Hilpisch 8 | # Derivatives Analytics with Python 9 | # 10 | import math 11 | import numpy as np 12 | from numpy.fft import * 13 | 14 | # 15 | # Model Parameters 16 | # 17 | S0 = 100.0 # initial index level 18 | K = 100.0 # strike level 19 | T = 1.0 # call option maturity 20 | r = 0.05 # constant short rate 21 | sigma = 0.4 # constant volatility of diffusion 22 | lamb = 1.0 # jump frequency p.a. 23 | mu = -0.2 # expected jump size 24 | delta = 0.1 # jump size volatility 25 | 26 | # 27 | # M76 Characteristic Function 28 | # 29 | 30 | def M76_characteristic_function(u, x0, T, r, sigma, lamb, mu, delta): 31 | ''' Valuation of European call option in M76 model via 32 | Lewis (2001) Fourier-based approach: characteristic function. 33 | 34 | Parameter definitions see function M76_value_call_INT. ''' 35 | omega = x0 / T + r - 0.5 * sigma ** 2 \ 36 | - lamb * (np.exp(mu + 0.5 * delta ** 2) - 1) 37 | value = np.exp((1j * u * omega - 0.5 * u ** 2 * sigma ** 2 + 38 | lamb * (np.exp(1j * u * mu - u ** 2 * delta ** 2 * 0.5) - 1)) * T) 39 | return value 40 | 41 | # 42 | # Valuation by FFT 43 | # 44 | 45 | 46 | def M76_value_call_FFT(S0, K, T, r, sigma, lamb, mu, delta): 47 | ''' Valuation of European call option in M76 model via 48 | Carr-Madan (1999) Fourier-based approach. 49 | 50 | Parameters 51 | ========== 52 | S0: float 53 | initial stock/index level 54 | K: float 55 | strike price 56 | T: float 57 | time-to-maturity (for t=0) 58 | r: float 59 | constant risk-free short rate 60 | sigma: float 61 | volatility factor in diffusion term 62 | lamb: float 63 | jump intensity 64 | mu: float 65 | expected jump size 66 | delta: float 67 | standard deviation of jump 68 | 69 | Returns 70 | ======= 71 | call_value: float 72 | European call option present value 73 | ''' 74 | k = math.log(K / S0) 75 | x0 = math.log(S0 / S0) 76 | g = 2 # factor to increase accuracy 77 | N = g * 4096 78 | eps = (g * 150.) ** -1 79 | eta = 2 * math.pi / (N * eps) 80 | b = 0.5 * N * eps - k 81 | u = np.arange(1, N + 1, 1) 82 | vo = eta * (u - 1) 83 | # Modificatons to Ensure Integrability 84 | if S0 >= 0.95 * K: # ITM case 85 | alpha = 1.5 86 | v = vo - (alpha + 1) * 1j 87 | mod_char_fun = math.exp(-r * T) * M76_characteristic_function( 88 | v, x0, T, r, sigma, lamb, mu, delta) \ 89 | / (alpha ** 2 + alpha - vo ** 2 + 1j * (2 * alpha + 1) * vo) 90 | else: # OTM case 91 | alpha = 1.1 92 | v = (vo - 1j * alpha) - 1j 93 | mod_char_fun_1 = math.exp(-r * T) * (1 / (1 + 1j * (vo - 1j * alpha)) 94 | - math.exp(r * T) / (1j * (vo - 1j * alpha)) 95 | - M76_characteristic_function( 96 | v, x0, T, r, sigma, lamb, mu, delta) 97 | / ((vo - 1j * alpha) ** 2 - 1j * (vo - 1j * alpha))) 98 | v = (vo + 1j * alpha) - 1j 99 | mod_char_fun_2 = math.exp(-r * T) * (1 / (1 + 1j * (vo + 1j * alpha)) 100 | - math.exp(r * T) / (1j * (vo + 1j * alpha)) 101 | - M76_characteristic_function( 102 | v, x0, T, r, sigma, lamb, mu, delta) 103 | / ((vo + 1j * alpha) ** 2 - 1j * (vo + 1j * alpha))) 104 | 105 | # Numerical FFT Routine 106 | delt = np.zeros(N, dtype=np.float) 107 | delt[0] = 1 108 | j = np.arange(1, N + 1, 1) 109 | SimpsonW = (3 + (-1) ** j - delt) / 3 110 | if S0 >= 0.95 * K: 111 | fft_func = np.exp(1j * b * vo) * mod_char_fun * eta * SimpsonW 112 | payoff = (fft(fft_func)).real 113 | call_value_m = np.exp(-alpha * k) / math.pi * payoff 114 | else: 115 | fft_func = (np.exp(1j * b * vo) 116 | * (mod_char_fun_1 - mod_char_fun_2) 117 | * 0.5 * eta * SimpsonW) 118 | payoff = (fft(fft_func)).real 119 | call_value_m = payoff / (np.sinh(alpha * k) * math.pi) 120 | pos = int((k + b) / eps) 121 | call_value = call_value_m[pos] 122 | return call_value * S0 123 | 124 | if __name__ == '__main__': 125 | print "Value of Call Option %8.3f" \ 126 | % M76_value_call_FFT(S0, K, T, r, sigma, lamb, mu, delta) -------------------------------------------------------------------------------- /dawp/book/08_m76/M76_valuation_INT.py: -------------------------------------------------------------------------------- 1 | # 2 | # Valuation of European Call Options 3 | # in Merton's (1976) Jump Diffusion Model 4 | # via Numerical Integration 5 | # 08_m76/M76_valuation_INT.py 6 | # 7 | # (c) Dr. Yves J. Hilpisch 8 | # Derivatives Analytics with Python 9 | # 10 | import math 11 | import numpy as np 12 | from scipy.integrate import quad 13 | 14 | # 15 | # Model Parameters 16 | # 17 | S0 = 100.0 # initial index level 18 | K = 100.0 # strike level 19 | T = 1.0 # call option maturity 20 | r = 0.05 # constant short rate 21 | sigma = 0.4 # constant volatility of diffusion 22 | lamb = 1.0 # jump frequency p.a. 23 | mu = -0.2 # expected jump size 24 | delta = 0.1 # jump size volatility 25 | 26 | # 27 | # Valuation by Integration 28 | # 29 | 30 | 31 | def M76_value_call_INT(S0, K, T, r, sigma, lamb, mu, delta): 32 | ''' Valuation of European call option in M76 model via 33 | Lewis (2001) Fourier-based approach. 34 | 35 | Parameters 36 | ========== 37 | S0: float 38 | initial stock/index level 39 | K: float 40 | strike price 41 | T: float 42 | time-to-maturity (for t=0) 43 | r: float 44 | constant risk-free short rate 45 | sigma: float 46 | volatility factor in diffusion term 47 | lamb: float 48 | jump intensity 49 | mu: float 50 | expected jump size 51 | delta: float 52 | standard deviation of jump 53 | 54 | Returns 55 | ======= 56 | call_value: float 57 | European call option present value 58 | ''' 59 | int_value = quad(lambda u: M76_integration_function(u, S0, K, T, r, 60 | sigma, lamb, mu, delta), 0, 50, limit=250)[0] 61 | call_value = S0 - np.exp(-r * T) * math.sqrt(S0 * K) / math.pi * int_value 62 | return call_value 63 | 64 | 65 | def M76_integration_function(u, S0, K, T, r, sigma, lamb, mu, delta): 66 | ''' Valuation of European call option in M76 model via 67 | Lewis (2001) Fourier-based approach: integration function. 68 | 69 | Parameter definitions see function M76_value_call_INT. ''' 70 | JDCF = M76_characteristic_function(u - 0.5 * 1j, T, r, 71 | sigma, lamb, mu, delta) 72 | value = 1 / (u ** 2 + 0.25) * (np.exp(1j * u * math.log(S0 / K)) 73 | * JDCF).real 74 | return value 75 | 76 | 77 | def M76_characteristic_function(u, T, r, sigma, lamb, mu, delta): 78 | ''' Valuation of European call option in M76 model via 79 | Lewis (2001) Fourier-based approach: characteristic function. 80 | 81 | Parameter definitions see function M76_value_call_INT. ''' 82 | omega = r - 0.5 * sigma ** 2 - lamb * (np.exp(mu + 0.5 * delta ** 2) - 1) 83 | value = np.exp((1j * u * omega - 0.5 * u ** 2 * sigma ** 2 + 84 | lamb * (np.exp(1j * u * mu - u ** 2 * delta ** 2 * 0.5) - 1)) * T) 85 | return value 86 | 87 | if __name__ == '__main__': 88 | print "Value of Call Option %8.3f" \ 89 | % M76_value_call_INT(S0, K, T, r, sigma, lamb, mu, delta) -------------------------------------------------------------------------------- /dawp/book/08_m76/M76_valuation_MCS.py: -------------------------------------------------------------------------------- 1 | # 2 | # Valuation of European Call Options 3 | # in Merton's (1976) Jump Diffusion Model 4 | # via Monte Carlo Simulation 5 | # 08_m76/M76_valuation_MCS.py 6 | # 7 | # (c) Dr. Yves J. Hilpisch 8 | # Derivatives Analytics with Python 9 | # 10 | import math 11 | import numpy as np 12 | import pandas as pd 13 | from M76_valuation_FFT import M76_value_call_FFT 14 | from M76_valuation_INT import M76_value_call_INT 15 | 16 | # 17 | # Model Parameters (from Calibration) 18 | # 19 | S0 = 3225.93 # EURO STOXX 50 level (30.09.2014) 20 | T = 0.22 # shortest maturity 21 | r = 0.005 # assumption 22 | 23 | sigma, lamb, mu, delta = [0.113, 3.559, -0.075, 0.041] 24 | # from calibration 25 | 26 | 27 | # 28 | # Valuation by Simulation 29 | # 30 | seed = 100000 # seed value 31 | M = 50 # time steps 32 | I = 200000 # paths 33 | disc = 2 # 1 = simple Euler; else = log Euler 34 | 35 | 36 | def M76_generate_paths(S0, T, r, sigma, lamb, mu, delta, M, I): 37 | ''' Generate Monte Carlo Paths for M76 Model. 38 | Parameters 39 | ========== 40 | S0: float 41 | initial stock/index level 42 | K: float 43 | strike price 44 | T: float 45 | time-to-maturity (for t=0) 46 | r: float 47 | constant risk-free short rate 48 | sigma: float 49 | volatility factor in diffusion term 50 | lamb: float 51 | jump intensity 52 | mu: float 53 | expected jump size 54 | delta: float 55 | standard deviation of jump 56 | M: int 57 | number of time intervals 58 | I: int 59 | number of paths 60 | 61 | Returns 62 | ======= 63 | S: array 64 | simulated paths 65 | ''' 66 | dt = T / M 67 | rj = lamb * (math.exp(mu + 0.5 * delta ** 2) - 1) 68 | shape = (M + 1, I) 69 | S = np.zeros((M + 1, I), dtype=np.float) 70 | S[0] = S0 71 | 72 | np.random.seed(10000) 73 | rand1 = np.random.standard_normal(shape) 74 | rand2 = np.random.standard_normal(shape) 75 | rand3 = np.random.poisson(lamb * dt, shape) 76 | 77 | for t in xrange(1, M + 1, 1): 78 | if disc == 1: 79 | S[t] = S[t - 1] * ((1 + (r - rj) * dt) + sigma 80 | * math.sqrt(dt) * rand1[t] 81 | + (np.exp(mu + delta * rand2[t]) - 1) 82 | * rand3[t]) 83 | else: 84 | S[t] = S[t - 1] * (np.exp((r - rj - 0.5 * sigma ** 2) * dt 85 | + sigma * math.sqrt(dt) * rand1[t]) 86 | + (np.exp(mu + delta * rand2[t]) - 1) 87 | * rand3[t]) 88 | return S 89 | 90 | def M76_value_call_MCS(K): 91 | ''' Function to calculate the MCS estimator given K. 92 | 93 | Parameters 94 | ========== 95 | K: float 96 | strike price 97 | 98 | Returns 99 | ======= 100 | call_mcs: float 101 | European call option Monte Carlo estimator 102 | ''' 103 | return math.exp(-r * T) * np.sum(np.maximum(S[-1] - K, 0)) / I 104 | 105 | 106 | 107 | if __name__ == '__main__': 108 | 109 | # Single Valuation 110 | S = M76_generate_paths(S0, T, r, sigma, lamb, mu, delta, M, I) 111 | print "Value of Call Option %8.3f" % M76_value_call_MCS(S0) 112 | 113 | # Value Comparisons 114 | strikes = np.arange(3000, 3601, 50) 115 | values = np.zeros((3, len(strikes)), dtype=np.float) 116 | z = 0 117 | for k in strikes: 118 | print "CALL STRIKE %10.3f" % k 119 | print "----------------------------" 120 | values[0, z] = M76_value_call_INT(S0, k, T, r, sigma, 121 | lamb, mu, delta) 122 | print "Call Value by Int %10.3f" % values[0, z] 123 | 124 | values[1, z] = M76_value_call_FFT(S0, k, T, r, sigma, 125 | lamb, mu, delta) 126 | print "Call Value by FFT %10.3f" % values[1, z] 127 | print "Difference FFT/Int%10.3f" % (values[1, z] - values[0, z]) 128 | values[2, z] = M76_value_call_MCS(k) 129 | print "Call Value by MCS %10.3f" % values[2, z] 130 | print "Difference MCS/Int%10.3f" % (values[2, z] - values[0, z]) 131 | print "----------------------------" 132 | z = z + 1 133 | 134 | results = pd.DataFrame(values.T, index=strikes, columns=[ 135 | 'INT', 'FFT', 'MCS']) 136 | results.index.name = 'Strike' 137 | -------------------------------------------------------------------------------- /dawp/book/08_m76/option_data.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/08_m76/option_data.h5 -------------------------------------------------------------------------------- /dawp/book/09_gmm/BSM_option_valuation.py: -------------------------------------------------------------------------------- 1 | # 2 | # Black-Scholes-Merton (1973) European Call & Put Valuation 3 | # 05_com/BSM_option_valuation.py 4 | # 5 | # (c) Dr. Yves J. Hilpisch 6 | # Derivatives Analytics with Python 7 | # 8 | import math 9 | import numpy as np 10 | import matplotlib as mpl 11 | import matplotlib.pyplot as plt 12 | mpl.rcParams['font.family'] = 'serif' 13 | from scipy.integrate import quad 14 | 15 | # 16 | # Helper Functions 17 | # 18 | 19 | 20 | def dN(x): 21 | ''' Probability density function of standard normal random variable x. ''' 22 | return math.exp(-0.5 * x ** 2) / math.sqrt(2 * math.pi) 23 | 24 | 25 | def N(d): 26 | ''' Cumulative density function of standard normal random variable x. ''' 27 | return quad(lambda x: dN(x), -20, d, limit=50)[0] 28 | 29 | 30 | def d1f(St, K, t, T, r, sigma): 31 | ''' Black-Scholes-Merton d1 function. 32 | Parameters see e.g. BSM_Call function. ''' 33 | d1 = (math.log(St / K) + (r + (0.5 * sigma ** 2)) 34 | * (T - t)) / (sigma * math.sqrt(T - t)) 35 | return d1 36 | 37 | # 38 | # Valuation Functions 39 | # 40 | 41 | 42 | def BSM_call_value(St, K, t, T, r, sigma): 43 | ''' Calculates Black-Scholes-Merton European call option value. 44 | 45 | Parameters 46 | ========== 47 | St : float 48 | stock/index level at time t 49 | K : float 50 | strike price 51 | t : float 52 | valuation date 53 | T : float 54 | date of maturity/time-to-maturity if t = 0; T > t 55 | r : float 56 | constant, risk-less short rate 57 | sigma : float 58 | volatility 59 | 60 | Returns 61 | ======= 62 | call : float 63 | European call present value at t 64 | ''' 65 | d1 = d1f(St, K, t, T, r, sigma) 66 | d2 = d1 - sigma * math.sqrt(T - t) 67 | call = St * N(d1) - math.exp(-r * (T - t)) * K * N(d2) 68 | return call 69 | 70 | 71 | def BSM_put_value(St, K, t, T, r, sigma): 72 | ''' Calculates Black-Scholes-Merton European put option value. 73 | 74 | Parameters 75 | ========== 76 | St : float 77 | stock/index level at time t 78 | K : float 79 | strike price 80 | t : float 81 | valuation date 82 | T : float 83 | date of maturity/time-to-maturity if t = 0; T > t 84 | r : float 85 | constant, risk-less short rate 86 | sigma : float 87 | volatility 88 | 89 | Returns 90 | ======= 91 | put : float 92 | European put present value at t 93 | ''' 94 | put = BSM_call_value(St, K, t, T, r, sigma) \ 95 | - St + math.exp(-r * (T - t)) * K 96 | return put 97 | 98 | 99 | 100 | 101 | # 102 | # Plotting European Option Values 103 | # 104 | 105 | 106 | def plot_values(function): 107 | ''' Plots European option values for different parameters c.p. ''' 108 | plt.figure(figsize=(10, 8.3)) 109 | points = 100 110 | # 111 | # Model Parameters 112 | # 113 | St = 100.0 # index level 114 | K = 100.0 # option strike 115 | t = 0.0 # valuation date 116 | T = 1.0 # maturity date 117 | r = 0.05 # risk-less short rate 118 | sigma = 0.2 # volatility 119 | 120 | # C(K) plot 121 | plt.subplot(221) 122 | klist = np.linspace(80, 120, points) 123 | vlist = [function(St, K, t, T, r, sigma) for K in klist] 124 | plt.plot(klist, vlist) 125 | plt.grid() 126 | plt.xlabel('strike $K$') 127 | plt.ylabel('present value') 128 | 129 | # C(T) plot 130 | plt.subplot(222) 131 | tlist = np.linspace(0.0001, 1, points) 132 | vlist = [function(St, K, t, T, r, sigma) for T in tlist] 133 | plt.plot(tlist, vlist) 134 | plt.grid(True) 135 | plt.xlabel('maturity $T$') 136 | 137 | # C(r) plot 138 | plt.subplot(223) 139 | rlist = np.linspace(0, 0.1, points) 140 | vlist = [function(St, K, t, T, r, sigma) for r in rlist] 141 | plt.plot(tlist, vlist) 142 | plt.grid(True) 143 | plt.xlabel('short rate $r$') 144 | plt.ylabel('present value') 145 | plt.axis('tight') 146 | 147 | # C(sigma) plot 148 | plt.subplot(224) 149 | slist = np.linspace(0.01, 0.5, points) 150 | vlist = [function(St, K, t, T, r, sigma) for sigma in slist] 151 | plt.plot(slist, vlist) 152 | plt.grid(True) 153 | plt.xlabel('volatility $\sigma$') 154 | plt.tight_layout() -------------------------------------------------------------------------------- /dawp/book/09_gmm/CIR_zcb_valuation.py: -------------------------------------------------------------------------------- 1 | # 2 | # Valuation of Zero-Coupon Bonds 3 | # in Cox-Ingersoll-Ross (1985) Model 4 | # 09_gmm/CIR_zcb_valuation.py 5 | # 6 | # (c) Dr. Yves J. Hilpisch 7 | # Derivatives Analytics with Python 8 | # 9 | import math 10 | import numpy as np 11 | 12 | # 13 | # Example Parameters CIR85 Model 14 | # 15 | kappa_r, theta_r, sigma_r, r0, T = 0.3, 0.04, 0.1, 0.04, 1.0 16 | 17 | # 18 | # Zero-Coupon Bond Valuation Formula 19 | # 20 | 21 | 22 | def gamma(kappa_r, sigma_r): 23 | ''' Help Function. ''' 24 | return math.sqrt(kappa_r ** 2 + 2 * sigma_r ** 2) 25 | 26 | 27 | def b1(alpha): 28 | ''' Help Function. ''' 29 | kappa_r, theta_r, sigma_r, r0, T = alpha 30 | g = gamma(kappa_r, sigma_r) 31 | return (((2 * g * math.exp((kappa_r + g) * T / 2)) / 32 | (2 * g + (kappa_r + g) * (math.exp(g * T) - 1))) 33 | ** (2 * kappa_r * theta_r / sigma_r ** 2)) 34 | 35 | 36 | def b2(alpha): 37 | ''' Help Function. ''' 38 | kappa_r, theta_r, sigma_r, r0, T = alpha 39 | g = gamma(kappa_r, sigma_r) 40 | return ((2 * (math.exp(g * T) - 1)) / 41 | (2 * g + (kappa_r + g) * (math.exp(g * T) - 1))) 42 | 43 | 44 | def B(alpha): 45 | ''' Function to value unit zero-coupon bonds in Cox-Ingersoll-Ross (1985) 46 | model. 47 | 48 | Parameters 49 | ========== 50 | r0: float 51 | initial short rate 52 | kappa_r: float 53 | mean-reversion factor 54 | theta_r: float 55 | long-run mean of short rate 56 | sigma_r: float 57 | volatility of short rate 58 | T: float 59 | time horizon/interval 60 | 61 | Returns 62 | ======= 63 | zcb_value: float 64 | zero-coupon bond present value 65 | ''' 66 | b_1 = b1(alpha) 67 | b_2 = b2(alpha) 68 | kappa_r, theta_r, sigma_r, r0, T = alpha 69 | return b_1 * math.exp(-b_2 * r0) 70 | 71 | if __name__ == '__main__': 72 | # 73 | # Example Valuation 74 | # 75 | B0T = B([kappa_r, theta_r, sigma_r, r0, T]) 76 | # discount factor, ZCB value 77 | print "ZCB Value %10.4f" % B0T -------------------------------------------------------------------------------- /dawp/book/10_mcs/CIR_zcb_simulation.py: -------------------------------------------------------------------------------- 1 | # 2 | # Valuation of Zero-Coupon Bonds by Monte Carlo Simulation 3 | # in Cox-Ingersoll-Ross (1985) Model 4 | # 10_mcs/CIR_zcb_simulation.py 5 | # 6 | # (c) Dr. Yves J. Hilpisch 7 | # Derivatives Analytics with Python 8 | # 9 | import math 10 | import numpy as np 11 | from CIR_zcb_valuation_gen import B 12 | import matplotlib.pyplot as plt 13 | import matplotlib as mpl 14 | mpl.rcParams['font.family'] = 'serif' 15 | from time import time 16 | 17 | # 18 | # Simulation of Square Root Diffusion 19 | # 20 | 21 | 22 | def CIR_generate_paths(x0, kappa, theta, sigma, T, M, I, x_disc='exact'): 23 | ''' Function to simulate Square-Root Difussion (SRD/CIR) process. 24 | 25 | Parameters 26 | ========== 27 | x0: float 28 | initial value 29 | kappa: float 30 | mean-reversion factor 31 | theta: float 32 | long-run mean 33 | sigma: float 34 | volatility factor 35 | T: float 36 | final date/time horizon 37 | M: int 38 | number of time steps 39 | I: int 40 | number of paths 41 | 42 | Returns 43 | ======= 44 | x: NumPy array 45 | simulated paths 46 | ''' 47 | dt = T / M 48 | x = np.zeros((M + 1, I), dtype=np.float) 49 | x[0] = x0 50 | xh = np.zeros_like(x) 51 | xh[0] = x0 52 | ran = np.random.standard_normal((M + 1, I)) 53 | 54 | if x_disc is 'exact': 55 | # exact discretization 56 | d = 4 * kappa * theta / sigma ** 2 57 | c = (sigma ** 2 * (1 - math.exp(-kappa * dt))) / (4 * kappa) 58 | if d > 1: 59 | for t in xrange(1, M + 1): 60 | l = x[t - 1] * math.exp(-kappa * dt) / c 61 | chi = np.ramdom.chisquare(d - 1, I) 62 | x[t] = c * ((ran[t] + np.sqrt(l)) ** 2 + chi) 63 | else: 64 | for t in xrange(1, M + 1): 65 | l = x[t - 1] * math.exp(-kappa * dt) / c 66 | N = np.random.poisson(l / 2, I) 67 | chi = np.random.chisquare(d + 2 * N, I) 68 | x[t] = c * chi 69 | 70 | else: 71 | # Euler scheme (full truncation) 72 | for t in xrange(1, M + 1): 73 | xh[t] = (xh[t - 1] + kappa * (theta - np.maximum(0, xh[t - 1])) 74 | * dt + np.sqrt(np.maximum(0, xh[t - 1])) 75 | * sigma * ran[t] * math.sqrt(dt)) 76 | x[t] = np.maximum(0, xh[t]) 77 | return x 78 | 79 | 80 | 81 | # 82 | # Graphical Output of Simulated Paths 83 | # 84 | def plot_paths(): 85 | plt.figure(figsize=(9, 5)) 86 | plt.plot(range(len(r)), r[:, :20]) 87 | plt.grid() 88 | plt.xlabel('time step') 89 | plt.ylabel('short rate') 90 | 91 | # 92 | # Valuation of ZCB 93 | # 94 | def zcb_estimator(M=50, x_disc='exact'): 95 | dt = T / M 96 | r = CIR_generate_paths(r0, kappa_r, theta_r, sigma_r, T, M, I, x_disc) 97 | zcb = np.zeros((M + 1, I), dtype=np.float) 98 | zcb[-1] = 1.0 # final value 99 | for t in range(M, 0, -1): 100 | zcb[t - 1] = zcb[t] * np.exp(-(r[t] + r[t - 1]) / 2 * dt) 101 | return np.sum(zcb, axis=1) / I 102 | 103 | # 104 | # Graphical Value Comparison 105 | # 106 | def graphical_comparison(M=50, x_disc='exact'): 107 | MCS_values = zcb_estimator(M, x_disc) 108 | CIR_values = [] 109 | dt = T / M 110 | t_list = np.arange(0.0, T + 0.001, dt) # dates of interest 111 | for t in t_list: 112 | alpha = r0, kappa_r, theta_r, sigma_r, t, T 113 | CIR_values.append(B(alpha)) 114 | # CIR model values given date list 115 | 116 | fig, ax = plt.subplots(2, sharex=True, figsize=(8, 6)) 117 | ax[0].plot(t_list, MCS_values, 'ro', label='MCS values') 118 | ax[0].plot(t_list, CIR_values, 'b', label='CIR values') 119 | ax[0].legend(loc=0) 120 | ax[0].grid() 121 | ax[0].set_ylim(min(CIR_values) - 0.005, max(CIR_values) + 0.005) 122 | ax[0].set_ylabel('option values') 123 | ax[0].set_title('maturity $T=2$') 124 | ax[1].bar(t_list - 0.025 / 2., MCS_values - CIR_values, 125 | width=0.025) 126 | plt.ylabel('difference') 127 | plt.xlim(min(t_list) - 0.1, max(t_list) + 0.1) 128 | plt.xlabel('time $t$') 129 | plt.tight_layout() 130 | plt.grid() 131 | 132 | if __name__ == '__main__': 133 | # 134 | # Model Parameters 135 | # 136 | r0, kappa_r, theta_r, sigma_r = [0.01, 0.1, 0.03, 0.2] 137 | T = 2.0 # time horizon 138 | M = 50 # time steps 139 | dt = T / M 140 | I = 50000 # number of MCS paths 141 | np.random.seed(50000) # seed for RNG 142 | 143 | r = CIR_generate_paths(r0, kappa_r, theta_r, sigma_r, T, M, I) 144 | -------------------------------------------------------------------------------- /dawp/book/10_mcs/CIR_zcb_valuation_gen.py: -------------------------------------------------------------------------------- 1 | # 2 | # Valuation of Zero-Coupon Bonds 3 | # in Cox-Ingersoll-Ross (1985) Model 4 | # 09_gmm/CIR_zcb_valuation_gen.py 5 | # 6 | # (c) Dr. Yves J. Hilpisch 7 | # Derivatives Analytics with Python 8 | # 9 | import math 10 | import numpy as np 11 | 12 | # 13 | # Example Parameters CIR85 Model 14 | # 15 | r0, kappa_r, theta_r, sigma_r, t, T = 0.04, 0.3, 0.04, 0.1, 0.5, 5.0 16 | 17 | # 18 | # Zero-Coupon Bond Valuation Formula 19 | # 20 | 21 | 22 | def gamma(kappa_r, sigma_r): 23 | ''' Help Function. ''' 24 | return np.sqrt(kappa_r ** 2 + 2 * sigma_r ** 2) 25 | 26 | 27 | def b1(alpha): 28 | ''' Help Function. ''' 29 | r0, kappa_r, theta_r, sigma_r, t, T = alpha 30 | g = gamma(kappa_r, sigma_r) 31 | return (((2 * g * np.exp((kappa_r + g) * (T - t) / 2)) / 32 | (2 * g + (kappa_r + g) * (np.exp(g * (T - t)) - 1))) 33 | ** (2 * kappa_r * theta_r / sigma_r ** 2)) 34 | 35 | 36 | def b2(alpha): 37 | ''' Help Function. ''' 38 | r0, kappa_r, theta_r, sigma_r, t, T = alpha 39 | g = gamma(kappa_r, sigma_r) 40 | return ((2 * (np.exp(g * (T - t)) - 1)) / 41 | (2 * g + (kappa_r + g) * (np.exp(g * (T - t)) - 1))) 42 | 43 | 44 | def B(alpha): 45 | ''' Function to value unit zero-coupon bonds in CIR85 Model. 46 | 47 | Parameters 48 | ========== 49 | r0: float 50 | initial short rate 51 | kappa_r: float 52 | mean-reversion factor 53 | theta_r: float 54 | long-run mean of short rate 55 | sigma_r: float 56 | volatility of short rate 57 | t: float 58 | valuation date 59 | T: float 60 | time horizon/interval 61 | 62 | Returns 63 | ======= 64 | zcb_value: float 65 | value of zero-coupon bond 66 | ''' 67 | b_1 = b1(alpha) 68 | b_2 = b2(alpha) 69 | r0, kappa_r, theta_r, sigma_r, t, T = alpha 70 | E_rt = theta_r + np.exp(-kappa_r * t) * (r0 - theta_r) 71 | # expected value of r_t 72 | zcb_value = b_1 * np.exp(-b_2 * E_rt) 73 | return zcb_value 74 | 75 | if __name__ == '__main__': 76 | # 77 | # Example Valuation 78 | # 79 | BtT = B([r0, kappa_r, theta_r, sigma_r, t, T]) 80 | # discount factor, ZCB value for t & T 81 | print "ZCB Value %10.4f" % BtT -------------------------------------------------------------------------------- /dawp/book/10_mcs/RNG.py: -------------------------------------------------------------------------------- 1 | def random_number_generator(M, I): 2 | if antipath: 3 | rand = np.random.standard_normal((2, M + 1, I / 2)) 4 | rand = np.concatenate((rand, -rand), 2) 5 | else: 6 | rand = np.random.standard_normal((2, M + 1, I)) 7 | if momatch: 8 | rand = rand / np.std(rand) 9 | rand = rand - np.mean(rand) 10 | return rand 11 | -------------------------------------------------------------------------------- /dawp/book/10_mcs/SGEN.py: -------------------------------------------------------------------------------- 1 | for t in range(1, M + 1, 1): 2 | ran = np.dot(CM, rand[:, t]) 3 | if momatch: 4 | bias = np.mean(np.sqrt(v[t]) * ran[row] * sdt) 5 | if s_disc == 'Log': 6 | S[t] = S[t - 1] * np.exp((r - 0.5 * v[t]) * dt + 7 | np.sqrt(v[t]) * ran[row] * sdt - bias) 8 | elif s_disc == 'Naive': 9 | S[t] = S[t - 1] * (math.exp(r * dt) + 10 | np.sqrt(v[t]) * ran[row] * sdt - bias) 11 | -------------------------------------------------------------------------------- /dawp/book/10_mcs/mcs_american_10_3.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/10_mcs/mcs_american_10_3.h5 -------------------------------------------------------------------------------- /dawp/book/10_mcs/mcs_american_10_3_lam_1.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/10_mcs/mcs_american_10_3_lam_1.h5 -------------------------------------------------------------------------------- /dawp/book/10_mcs/mcs_american_10_4.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/10_mcs/mcs_american_10_4.h5 -------------------------------------------------------------------------------- /dawp/book/10_mcs/mcs_american_10_4_lam_1.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/10_mcs/mcs_american_10_4_lam_1.h5 -------------------------------------------------------------------------------- /dawp/book/10_mcs/mcs_american_10_5.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/10_mcs/mcs_american_10_5.h5 -------------------------------------------------------------------------------- /dawp/book/10_mcs/mcs_american_10_5_lam_1.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/10_mcs/mcs_american_10_5_lam_1.h5 -------------------------------------------------------------------------------- /dawp/book/10_mcs/mcs_european.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/10_mcs/mcs_european.h5 -------------------------------------------------------------------------------- /dawp/book/10_mcs/table_10_1.tex: -------------------------------------------------------------------------------- 1 | CALL & 5 & 100000 & L & A & False & False & 146 & 180 & 0.07670 & 3.74152 \tn 2 | CALL & 5 & 100000 & L & A & False & True & 146 & 180 & 0.07288 & 3.75574 \tn 3 | CALL & 5 & 100000 & L & A & True & False & 3 & 180 & 0.00633 & 0.00136 \tn 4 | CALL & 5 & 100000 & L & A & True & True & 3 & 180 & 0.00468 & 0.00149 \tn 5 | CALL & 5 & 100000 & L & F & False & False & 146 & 180 & 0.03556 & 3.63164 \tn 6 | CALL & 5 & 100000 & L & F & False & True & 148 & 180 & 0.03462 & 3.62426 \tn 7 | CALL & 5 & 100000 & L & F & True & False & 1 & 180 & -0.01659 & 0.00113 \tn 8 | CALL & 5 & 100000 & L & F & True & True & 1 & 180 & -0.01299 & 0.00090 \tn 9 | CALL & 5 & 100000 & L & P & False & False & 144 & 180 & 0.03942 & 3.68678 \tn 10 | CALL & 5 & 100000 & L & P & False & True & 145 & 180 & 0.04079 & 3.64441 \tn 11 | CALL & 5 & 100000 & L & P & True & False & 1 & 180 & -0.01474 & 0.00108 \tn 12 | CALL & 5 & 100000 & L & P & True & True & 1 & 180 & -0.01128 & 0.00111 \tn 13 | CALL & 5 & 100000 & L & T & False & False & 147 & 180 & 0.07196 & 3.72847 \tn 14 | CALL & 5 & 100000 & L & T & False & True & 145 & 180 & 0.07256 & 3.74803 \tn 15 | CALL & 5 & 100000 & L & T & True & False & 3 & 180 & 0.00340 & 0.00155 \tn 16 | CALL & 5 & 100000 & L & T & True & True & 3 & 180 & 0.01147 & 0.00162 \tn 17 | PUT & 5 & 100000 & L & A & False & False & 143 & 180 & 0.04343 & 0.93155 \tn 18 | PUT & 5 & 100000 & L & A & False & True & 141 & 180 & 0.04284 & 0.93065 \tn 19 | PUT & 5 & 100000 & L & A & True & False & 14 & 180 & 0.00445 & 0.00110 \tn 20 | PUT & 5 & 100000 & L & A & True & True & 20 & 180 & 0.00657 & 0.00149 \tn 21 | PUT & 5 & 100000 & L & F & False & False & 141 & 180 & 0.03198 & 0.94487 \tn 22 | PUT & 5 & 100000 & L & F & False & True & 142 & 180 & 0.03797 & 0.94874 \tn 23 | PUT & 5 & 100000 & L & F & True & False & 9 & 180 & -0.01349 & 0.00068 \tn 24 | PUT & 5 & 100000 & L & F & True & True & 10 & 180 & -0.01379 & 0.00083 \tn 25 | PUT & 5 & 100000 & L & P & False & False & 143 & 180 & 0.03593 & 0.96873 \tn 26 | PUT & 5 & 100000 & L & P & False & True & 141 & 180 & 0.03330 & 0.94941 \tn 27 | PUT & 5 & 100000 & L & P & True & False & 3 & 180 & -0.00881 & 0.00041 \tn 28 | PUT & 5 & 100000 & L & P & True & True & 5 & 180 & -0.00987 & 0.00056 \tn 29 | PUT & 5 & 100000 & L & T & False & False & 143 & 180 & 0.04830 & 0.92206 \tn 30 | PUT & 5 & 100000 & L & T & False & True & 142 & 180 & 0.04231 & 0.92051 \tn 31 | PUT & 5 & 100000 & L & T & True & False & 10 & 180 & 0.00687 & 0.00111 \tn 32 | PUT & 5 & 100000 & L & T & True & True & 10 & 180 & 0.00445 & 0.00117 \tn 33 | -------------------------------------------------------------------------------- /dawp/book/10_mcs/table_10_2.tex: -------------------------------------------------------------------------------- 1 | CALL & 5 & 100000 & L & A & True & True & 3 & 180 & 0.00468 & 0.00149 \tn 2 | CALL & 5 & 100000 & L & F & True & True & 1 & 180 & -0.01299 & 0.00090 \tn 3 | CALL & 5 & 100000 & L & H & True & True & 4 & 180 & 0.01169 & 0.00175 \tn 4 | CALL & 5 & 100000 & L & P & True & True & 1 & 180 & -0.01128 & 0.00111 \tn 5 | CALL & 5 & 100000 & L & R & True & True & 5 & 180 & 0.01208 & 0.00253 \tn 6 | CALL & 5 & 100000 & L & S & True & True & 13 & 180 & 0.02979 & 0.00659 \tn 7 | CALL & 5 & 100000 & L & T & True & True & 3 & 180 & 0.01147 & 0.00162 \tn 8 | CALL & 5 & 100000 & N & A & True & True & 1 & 180 & 0.00805 & 0.00101 \tn 9 | CALL & 5 & 100000 & N & F & True & True & 2 & 180 & -0.01566 & 0.00104 \tn 10 | CALL & 5 & 100000 & N & H & True & True & 4 & 180 & 0.00545 & 0.00226 \tn 11 | CALL & 5 & 100000 & N & P & True & True & 4 & 180 & -0.01526 & 0.00120 \tn 12 | CALL & 5 & 100000 & N & R & True & True & 6 & 180 & 0.01221 & 0.00279 \tn 13 | CALL & 5 & 100000 & N & S & True & True & 11 & 180 & 0.03020 & 0.00549 \tn 14 | CALL & 5 & 100000 & N & T & True & True & 3 & 180 & 0.00692 & 0.00174 \tn 15 | PUT & 5 & 100000 & L & A & True & True & 20 & 180 & 0.00657 & 0.00149 \tn 16 | PUT & 5 & 100000 & L & F & True & True & 10 & 180 & -0.01379 & 0.00083 \tn 17 | PUT & 5 & 100000 & L & H & True & True & 14 & 180 & 0.00808 & 0.00157 \tn 18 | PUT & 5 & 100000 & L & P & True & True & 5 & 180 & -0.00987 & 0.00056 \tn 19 | PUT & 5 & 100000 & L & R & True & True & 18 & 180 & 0.01005 & 0.00207 \tn 20 | PUT & 5 & 100000 & L & S & True & True & 29 & 180 & 0.02747 & 0.00592 \tn 21 | PUT & 5 & 100000 & L & T & True & True & 10 & 180 & 0.00445 & 0.00117 \tn 22 | PUT & 5 & 100000 & N & A & True & True & 16 & 180 & 0.00084 & 0.00123 \tn 23 | PUT & 5 & 100000 & N & F & True & True & 9 & 180 & -0.01466 & 0.00075 \tn 24 | PUT & 5 & 100000 & N & H & True & True & 13 & 180 & 0.00402 & 0.00176 \tn 25 | PUT & 5 & 100000 & N & P & True & True & 7 & 180 & -0.01529 & 0.00070 \tn 26 | PUT & 5 & 100000 & N & R & True & True & 17 & 180 & 0.00987 & 0.00217 \tn 27 | PUT & 5 & 100000 & N & S & True & True & 30 & 180 & 0.02535 & 0.00556 \tn 28 | PUT & 5 & 100000 & N & T & True & True & 11 & 180 & 0.00622 & 0.00106 \tn 29 | -------------------------------------------------------------------------------- /dawp/book/10_mcs/table_10_3.tex: -------------------------------------------------------------------------------- 1 | 5 & 20 & 25000 & A & True & True & True & 1 & 180 & -0.00117 & 0.00064 \tn 2 | 5 & 20 & 25000 & F & True & True & True & 1 & 180 & -0.00105 & 0.00042 \tn 3 | 5 & 20 & 25000 & H & True & True & True & 5 & 180 & 0.00043 & 0.00046 \tn 4 | 5 & 20 & 25000 & P & True & True & True & 4 & 180 & -0.00379 & 0.00047 \tn 5 | 5 & 20 & 25000 & R & True & True & True & 5 & 180 & -0.00187 & 0.00058 \tn 6 | 5 & 20 & 25000 & S & True & True & True & 3 & 180 & -0.00290 & 0.00044 \tn 7 | 5 & 20 & 25000 & T & True & True & True & 2 & 180 & 0.00072 & 0.00062 \tn 8 | 5 & 20 & 35000 & A & True & True & True & 1 & 180 & -0.00836 & 0.00050 \tn 9 | 5 & 20 & 35000 & F & True & True & True & 1 & 180 & -0.00289 & 0.00043 \tn 10 | 5 & 20 & 35000 & H & True & True & True & 5 & 180 & -0.00205 & 0.00057 \tn 11 | 5 & 20 & 35000 & P & True & True & True & 4 & 180 & -0.00328 & 0.00039 \tn 12 | 5 & 20 & 35000 & R & True & True & True & 4 & 180 & -0.00543 & 0.00051 \tn 13 | 5 & 20 & 35000 & S & True & True & True & 2 & 180 & -0.00408 & 0.00044 \tn 14 | 5 & 20 & 35000 & T & True & True & True & 2 & 180 & -0.00283 & 0.00035 \tn 15 | 5 & 25 & 25000 & A & True & True & True & 2 & 180 & -0.00171 & 0.00045 \tn 16 | 5 & 25 & 25000 & F & True & True & True & 1 & 180 & 0.00126 & 0.00039 \tn 17 | 5 & 25 & 25000 & H & True & True & True & 2 & 180 & -0.00177 & 0.00043 \tn 18 | 5 & 25 & 25000 & P & True & True & True & 1 & 180 & -0.00083 & 0.00037 \tn 19 | 5 & 25 & 25000 & R & True & True & True & 2 & 180 & -0.00016 & 0.00059 \tn 20 | 5 & 25 & 25000 & S & True & True & True & 4 & 180 & 0.00044 & 0.00050 \tn 21 | 5 & 25 & 25000 & T & True & True & True & 3 & 180 & -0.00173 & 0.00054 \tn 22 | 5 & 25 & 35000 & A & True & True & True & 2 & 180 & -0.00162 & 0.00043 \tn 23 | 5 & 25 & 35000 & F & True & True & True & 1 & 180 & -0.00135 & 0.00045 \tn 24 | 5 & 25 & 35000 & H & True & True & True & 2 & 180 & -0.00166 & 0.00041 \tn 25 | 5 & 25 & 35000 & P & True & True & True & 1 & 180 & -0.00199 & 0.00036 \tn 26 | 5 & 25 & 35000 & R & True & True & True & 0 & 180 & -0.00436 & 0.00028 \tn 27 | 5 & 25 & 35000 & S & True & True & True & 5 & 180 & -0.00429 & 0.00048 \tn 28 | 5 & 25 & 35000 & T & True & True & True & 1 & 180 & -0.00144 & 0.00045 \tn 29 | -------------------------------------------------------------------------------- /dawp/book/10_mcs/table_10_4.tex: -------------------------------------------------------------------------------- 1 | 5 & 20 & 35000 & A & False & False & False & 54 & 180 & -0.01504 & 0.00412 \tn 2 | 5 & 20 & 35000 & A & False & False & True & 42 & 180 & -0.01279 & 0.00365 \tn 3 | 5 & 20 & 35000 & A & False & True & False & 43 & 180 & -0.01126 & 0.00368 \tn 4 | 5 & 20 & 35000 & A & False & True & True & 42 & 180 & -0.01366 & 0.00351 \tn 5 | 5 & 20 & 35000 & A & True & False & False & 0 & 180 & -0.00319 & 0.00041 \tn 6 | 5 & 20 & 35000 & A & True & False & True & 2 & 180 & -0.00594 & 0.00046 \tn 7 | 5 & 20 & 35000 & A & True & True & False & 1 & 180 & -0.00364 & 0.00037 \tn 8 | 5 & 20 & 35000 & A & True & True & True & 0 & 180 & -0.00499 & 0.00045 \tn 9 | 5 & 20 & 35000 & F & False & False & False & 44 & 180 & -0.00894 & 0.00394 \tn 10 | 5 & 20 & 35000 & F & False & False & True & 43 & 180 & -0.01226 & 0.00379 \tn 11 | 5 & 20 & 35000 & F & False & True & False & 42 & 180 & -0.01283 & 0.00374 \tn 12 | 5 & 20 & 35000 & F & False & True & True & 46 & 180 & -0.01179 & 0.00422 \tn 13 | 5 & 20 & 35000 & F & True & False & False & 4 & 180 & -0.00525 & 0.00059 \tn 14 | 5 & 20 & 35000 & F & True & False & True & 3 & 180 & -0.00366 & 0.00038 \tn 15 | 5 & 20 & 35000 & F & True & True & False & 2 & 180 & -0.00416 & 0.00046 \tn 16 | 5 & 20 & 35000 & F & True & True & True & 2 & 180 & -0.00618 & 0.00042 \tn 17 | 5 & 20 & 35000 & P & False & False & False & 45 & 180 & -0.01272 & 0.00432 \tn 18 | 5 & 20 & 35000 & P & False & False & True & 44 & 180 & -0.01313 & 0.00407 \tn 19 | 5 & 20 & 35000 & P & False & True & False & 42 & 180 & -0.01241 & 0.00371 \tn 20 | 5 & 20 & 35000 & P & False & True & True & 41 & 180 & -0.01339 & 0.00385 \tn 21 | 5 & 20 & 35000 & P & True & False & False & 3 & 180 & -0.00337 & 0.00046 \tn 22 | 5 & 20 & 35000 & P & True & False & True & 4 & 180 & -0.00540 & 0.00047 \tn 23 | 5 & 20 & 35000 & P & True & True & False & 1 & 180 & -0.00322 & 0.00036 \tn 24 | 5 & 20 & 35000 & P & True & True & True & 3 & 180 & -0.00154 & 0.00045 \tn 25 | 5 & 20 & 35000 & T & False & False & False & 53 & 180 & -0.01329 & 0.00431 \tn 26 | 5 & 20 & 35000 & T & False & False & True & 40 & 180 & -0.01140 & 0.00374 \tn 27 | 5 & 20 & 35000 & T & False & True & False & 42 & 180 & -0.01210 & 0.00400 \tn 28 | 5 & 20 & 35000 & T & False & True & True & 42 & 180 & -0.01345 & 0.00367 \tn 29 | 5 & 20 & 35000 & T & True & False & False & 1 & 180 & -0.00513 & 0.00050 \tn 30 | 5 & 20 & 35000 & T & True & False & True & 1 & 180 & -0.00530 & 0.00042 \tn 31 | 5 & 20 & 35000 & T & True & True & False & 3 & 180 & -0.00479 & 0.00042 \tn 32 | 5 & 20 & 35000 & T & True & True & True & 2 & 180 & -0.00446 & 0.00039 \tn 33 | -------------------------------------------------------------------------------- /dawp/book/10_mcs/table_10_5.tex: -------------------------------------------------------------------------------- 1 | 5 & 20 & 35000 & A & True & True & True & 10 & 180 & -0.00417 & 0.00038 \tn 2 | 5 & 20 & 35000 & F & True & True & True & 11 & 180 & -0.00241 & 0.00043 \tn 3 | 5 & 20 & 35000 & P & True & True & True & 10 & 180 & -0.00323 & 0.00051 \tn 4 | 5 & 20 & 35000 & T & True & True & True & 15 & 180 & -0.00318 & 0.00051 \tn 5 | 5 & 20 & 100000 & A & True & True & True & 14 & 180 & -0.00928 & 0.00048 \tn 6 | 5 & 20 & 100000 & F & True & True & True & 15 & 180 & -0.00857 & 0.00033 \tn 7 | 5 & 20 & 100000 & P & True & True & True & 20 & 180 & -0.00855 & 0.00047 \tn 8 | 5 & 20 & 100000 & T & True & True & True & 15 & 180 & -0.00981 & 0.00047 \tn 9 | 5 & 20 & 200000 & A & True & True & True & 17 & 180 & -0.00966 & 0.00040 \tn 10 | 5 & 20 & 200000 & F & True & True & True & 19 & 180 & -0.01116 & 0.00044 \tn 11 | 5 & 20 & 200000 & P & True & True & True & 15 & 180 & -0.01055 & 0.00043 \tn 12 | 5 & 20 & 200000 & T & True & True & True & 16 & 180 & -0.01032 & 0.00041 \tn 13 | 5 & 50 & 35000 & A & True & True & True & 12 & 180 & -0.00211 & 0.00040 \tn 14 | 5 & 50 & 35000 & F & True & True & True & 16 & 180 & -0.00004 & 0.00048 \tn 15 | 5 & 50 & 35000 & P & True & True & True & 14 & 180 & 0.00020 & 0.00043 \tn 16 | 5 & 50 & 35000 & T & True & True & True & 15 & 180 & -0.00258 & 0.00047 \tn 17 | 5 & 50 & 100000 & A & True & True & True & 7 & 180 & -0.00478 & 0.00031 \tn 18 | 5 & 50 & 100000 & F & True & True & True & 6 & 180 & -0.00536 & 0.00028 \tn 19 | 5 & 50 & 100000 & P & True & True & True & 7 & 180 & -0.00657 & 0.00032 \tn 20 | 5 & 50 & 100000 & T & True & True & True & 9 & 180 & -0.00591 & 0.00034 \tn 21 | 5 & 50 & 200000 & A & True & True & True & 7 & 180 & -0.00783 & 0.00034 \tn 22 | 5 & 50 & 200000 & F & True & True & True & 7 & 180 & -0.00720 & 0.00038 \tn 23 | 5 & 50 & 200000 & P & True & True & True & 3 & 180 & -0.00709 & 0.00029 \tn 24 | 5 & 50 & 200000 & T & True & True & True & 9 & 180 & -0.00743 & 0.00032 \tn 25 | -------------------------------------------------------------------------------- /dawp/book/11_cal/BCC97_calibration_full.py: -------------------------------------------------------------------------------- 1 | # 2 | # Calibration of Bakshi, Cao and Chen (1997) 3 | # Stoch Vol Jump Model to EURO STOXX Option Quotes 4 | # Data Source: www.eurexchange.com 5 | # via Numerical Integration 6 | # 11_cal/BCC97_calibration_full.py 7 | # 8 | # (c) Dr. Yves J. Hilpisch 9 | # Derivatives Analytics with Python 10 | # 11 | import sys 12 | sys.path.append('09_gmm') 13 | import math 14 | import numpy as np 15 | np.set_printoptions(suppress=True, 16 | formatter={'all': lambda x: '%5.3f' % x}) 17 | import pandas as pd 18 | from scipy.optimize import brute, fmin, minimize 19 | import matplotlib as mpl 20 | mpl.rcParams['font.family'] = 'serif' 21 | from BCC_option_valuation import BCC_call_value 22 | from CIR_calibration import CIR_calibration, r_list 23 | from CIR_zcb_valuation import B 24 | from H93_calibration import options 25 | 26 | # 27 | # Calibrate Short Rate Model 28 | # 29 | kappa_r, theta_r, sigma_r = CIR_calibration() 30 | 31 | # 32 | # Market Data from www.eurexchange.com 33 | # as of 30. September 2014 34 | # 35 | S0 = 3225.93 # EURO STOXX 50 level 36 | r0 = r_list[0] # initial short rate (Eonia 30.09.2014) 37 | # 38 | # Parameters from H93 & jump calibrations 39 | # 40 | kappa_v, theta_v, sigma_v, rho, v0 = np.load('11_cal/opt_sv.npy') 41 | lamb, mu, delta = np.load('11_cal/opt_jump.npy') 42 | p0 = [kappa_v, theta_v, sigma_v, rho, v0, lamb, mu, delta] 43 | 44 | # 45 | # Calibration Functions 46 | # 47 | i = 0 48 | min_MSE = 5000.0 49 | def BCC_error_function(p0): 50 | ''' Error function for parameter calibration in BCC97 model via 51 | Lewis (2001) Fourier approach. 52 | 53 | Parameters 54 | ========== 55 | kappa_v: float 56 | mean-reversion factor 57 | theta_v: float 58 | long-run mean of variance 59 | sigma_v: float 60 | volatility of variance 61 | rho: float 62 | correlation between variance and stock/index level 63 | v0: float 64 | initial, instantaneous variance 65 | lamb: float 66 | jump intensity 67 | mu: float 68 | expected jump size 69 | delta: float 70 | standard deviation of jump 71 | 72 | Returns 73 | ======= 74 | MSE: float 75 | mean squared error 76 | ''' 77 | global i, min_MSE 78 | kappa_v, theta_v, sigma_v, rho, v0, lamb, mu, delta = p0 79 | if kappa_v < 0.0 or theta_v < 0.005 or sigma_v < 0.0 or \ 80 | rho < -1.0 or rho > 1.0 or v0 < 0.0 or lamb < 0.0 or \ 81 | mu < -.6 or mu > 0.0 or delta < 0.0: 82 | return 5000.0 83 | if 2 * kappa_v * theta_v < sigma_v ** 2: 84 | return 5000.0 85 | se = [] 86 | for row, option in options.iterrows(): 87 | model_value = BCC_call_value(S0, option['Strike'], option['T'], 88 | option['r'], kappa_v, theta_v, sigma_v, rho, v0, 89 | lamb, mu, delta) 90 | se.append((model_value - option['Call']) ** 2) 91 | MSE = sum(se) / len(se) 92 | min_MSE = min(min_MSE, MSE) 93 | if i % 25 == 0: 94 | print '%4d |' % i, np.array(p0), '| %7.3f | %7.3f' % (MSE, min_MSE) 95 | i += 1 96 | return MSE 97 | 98 | def BCC_calibration_full(): 99 | ''' Calibrates complete BCC97 model to market quotes. ''' 100 | # local, convex minimization for all parameters 101 | opt = fmin(BCC_error_function, p0, 102 | xtol=0.000001, ftol=0.000001, 103 | maxiter=450, maxfun=650) 104 | np.save('11_cal/opt_full', np.array(opt)) 105 | return opt 106 | 107 | def BCC_calculate_model_values(p0): 108 | ''' Calculates all model values given parameter vector p0. ''' 109 | kappa_v, theta_v, sigma_v, rho, v0, lamb, mu, delta = p0 110 | values = [] 111 | for row, option in options.iterrows(): 112 | model_value = BCC_call_value(S0, option['Strike'], option['T'], 113 | option['r'], kappa_v, theta_v, sigma_v, rho, v0, 114 | lamb, mu, delta) 115 | values.append(model_value) 116 | return np.array(values) 117 | -------------------------------------------------------------------------------- /dawp/book/11_cal/BCC97_calibration_iv.py: -------------------------------------------------------------------------------- 1 | # 2 | # Calibration of Bakshi, Cao and Chen (1997) 3 | # Stoch Vol Jump Model to EURO STOXX Option Quotes 4 | # Data Source: www.eurexchange.com 5 | # via Numerical Integration 6 | # 11_cal/BCC97_calibration_iv.py 7 | # 8 | # (c) Dr. Yves J. Hilpisch 9 | # Derivatives Analytics with Python 10 | # 11 | import sys 12 | sys.path.append('09_gmm') 13 | import math 14 | import numpy as np 15 | np.set_printoptions(suppress=True, 16 | formatter={'all': lambda x: '%5.3f' % x}) 17 | import pandas as pd 18 | from scipy.optimize import brute, fmin, minimize 19 | import matplotlib as mpl 20 | mpl.rcParams['font.family'] = 'serif' 21 | from BSM_imp_vol import call_option 22 | from BCC_option_valuation import BCC_call_value 23 | from CIR_calibration import CIR_calibration, r_list 24 | from CIR_zcb_valuation import B 25 | from H93_calibration import options 26 | 27 | # 28 | # Calibrate Short Rate Model 29 | # 30 | kappa_r, theta_r, sigma_r = CIR_calibration() 31 | 32 | # 33 | # Market Data from www.eurexchange.com 34 | # as of 30. September 2014 35 | # 36 | S0 = 3225.93 # EURO STOXX 50 level 30.09.2014 37 | r0 = r_list[0] # initial short rate (Eonia 30.09.2014) 38 | 39 | # 40 | # Market Implied Volatilities 41 | # 42 | for row, option in options.iterrows(): 43 | call = call_option(S0, option['Strike'], option['Date'], 44 | option['Maturity'], option['r'], 0.15) 45 | options.ix[row, 'Market_IV'] = call.imp_vol(option['Call'], 0.15) 46 | 47 | # 48 | # Calibration Functions 49 | # 50 | i = 0 51 | min_MSE = 5000.0 52 | def BCC_iv_error_function(p0): 53 | ''' Error function for parameter calibration in BCC97 model via 54 | Lewis (2001) Fourier approach. 55 | 56 | Parameters 57 | ========== 58 | kappa_v: float 59 | mean-reversion factor 60 | theta_v: float 61 | long-run mean of variance 62 | sigma_v: float 63 | volatility of variance 64 | rho: float 65 | correlation between variance and stock/index level 66 | v0: float 67 | initial, instantaneous variance 68 | lamb: float 69 | jump intensity 70 | mu: float 71 | expected jump size 72 | delta: float 73 | standard deviation of jump 74 | 75 | Returns 76 | ======= 77 | MSE: float 78 | mean squared error 79 | ''' 80 | global i, min_MSE 81 | kappa_v, theta_v, sigma_v, rho, v0, lamb, mu, delta = p0 82 | if kappa_v < 0.0 or theta_v < 0.005 or sigma_v < 0.0 or \ 83 | rho < -1.0 or rho > 1.0 or v0 < 0.0 or lamb < 0.0 or \ 84 | mu < -.6 or mu > 0.0 or delta < 0.0: 85 | return 5000.0 86 | if 2 * kappa_v * theta_v < sigma_v ** 2: 87 | return 5000.0 88 | se = [] 89 | for row, option in options.iterrows(): 90 | call = call_option(S0, option['Strike'], option['Date'], 91 | option['Maturity'], option['r'], 92 | option['Market_IV']) 93 | model_value = BCC_call_value(S0, option['Strike'], option['T'], 94 | option['r'], kappa_v, theta_v, sigma_v, rho, v0, 95 | lamb, mu, delta) 96 | model_iv = call.imp_vol(model_value, 0.15) 97 | se.append(((model_iv - option['Market_IV']) * call.vega()) ** 2) 98 | MSE = sum(se) / len(se) 99 | min_MSE = min(min_MSE, MSE) 100 | if i % 25 == 0: 101 | print '%4d |' % i, np.array(p0), '| %7.3f | %7.3f' % (MSE, min_MSE) 102 | i += 1 103 | return MSE 104 | 105 | def BCC_iv_calibration_full(): 106 | ''' Calibrates complete BCC97 model to market implied volatilities. ''' 107 | 108 | p0 = np.load('11_cal/opt_full.npy') 109 | 110 | # local, convex minimization 111 | opt = fmin(BCC_iv_error_function, p0, 112 | xtol=0.000001, ftol=0.000001, 113 | maxiter=450, maxfun=650) 114 | np.save('11_cal/opt_iv', opt) 115 | return opt 116 | 117 | def BCC_calculate_model_values(p0): 118 | ''' Calculates all model values given parameter vector p0. ''' 119 | kappa_v, theta_v, sigma_v, rho, v0, lamb, mu, delta = p0 120 | values = [] 121 | for row, option in options.iterrows(): 122 | model_value = BCC_call_value(S0, option['Strike'], option['T'], 123 | option['r'], kappa_v, theta_v, sigma_v, rho, v0, 124 | lamb, mu, delta) 125 | values.append(model_value) 126 | return np.array(values) 127 | -------------------------------------------------------------------------------- /dawp/book/11_cal/BCC97_calibration_short.py: -------------------------------------------------------------------------------- 1 | # 2 | # Calibration of Bakshi, Cao and Chen (1997) 3 | # Stoch Vol Jump Model to EURO STOXX Option Quotes 4 | # Data Source: www.eurexchange.com 5 | # via Numerical Integration 6 | # 11_cal/BCC97_calibration_short.py 7 | # 8 | # (c) Dr. Yves J. Hilpisch 9 | # Derivatives Analytics with Python 10 | # 11 | import sys 12 | sys.path.append('09_gmm') 13 | import math 14 | import numpy as np 15 | np.set_printoptions(suppress=True, 16 | formatter={'all': lambda x: '%5.3f' % x}) 17 | import pandas as pd 18 | from scipy.optimize import brute, fmin 19 | import matplotlib.pyplot as plt 20 | import matplotlib as mpl 21 | mpl.rcParams['font.family'] = 'serif' 22 | from BCC_option_valuation import BCC_call_value 23 | from CIR_calibration import CIR_calibration, r_list 24 | from CIR_zcb_valuation import B 25 | from H93_calibration import options 26 | 27 | # 28 | # Calibrate Short Rate Model 29 | # 30 | kappa_r, theta_r, sigma_r = CIR_calibration() 31 | 32 | # 33 | # Market Data from www.eurexchange.com 34 | # as of 30. September 2014 35 | # 36 | S0 = 3225.93 # EURO STOXX 50 level 37 | r0 = r_list[0] # initial short rate (Eonia 30.09.2014) 38 | # 39 | # Option Selection 40 | # 41 | mats = sorted(set(options['Maturity'])) 42 | options = options[options['Maturity'] == mats[0]] 43 | # only shortest maturity 44 | 45 | # 46 | # Initial Parameter Guesses 47 | # 48 | kappa_v, theta_v, sigma_v, rho, v0 = np.load('11_cal/opt_sv.npy') 49 | # from H93 model calibration 50 | 51 | 52 | # 53 | # Calibration Functions 54 | # 55 | i = 0 56 | min_MSE = 5000.0 57 | local_opt = False 58 | def BCC_error_function(p0): 59 | ''' Error function for parameter calibration in M76 Model via 60 | Carr-Madan (1999) FFT approach. 61 | 62 | Parameters 63 | ========== 64 | lamb: float 65 | jump intensity 66 | mu: float 67 | expected jump size 68 | delta: float 69 | standard deviation of jump 70 | 71 | Returns 72 | ======= 73 | MSE: float 74 | mean squared error 75 | ''' 76 | global i, min_MSE, local_opt, opt1 77 | lamb, mu, delta = p0 78 | if lamb < 0.0 or mu < -0.6 or mu > 0.0 or delta < 0.0: 79 | return 5000.0 80 | se = [] 81 | for row, option in options.iterrows(): 82 | model_value = BCC_call_value(S0, option['Strike'], option['T'], 83 | option['r'], kappa_v, theta_v, sigma_v, rho, v0, 84 | lamb, mu, delta) 85 | se.append((model_value - option['Call']) ** 2) 86 | MSE = sum(se) / len(se) 87 | min_MSE = min(min_MSE, MSE) 88 | if i % 25 == 0: 89 | print '%4d |' % i, np.array(p0), '| %7.3f | %7.3f' % (MSE, min_MSE) 90 | i += 1 91 | if local_opt: 92 | penalty = np.sqrt(np.sum((p0 - opt1) ** 2)) * 1 93 | return MSE + penalty 94 | return MSE 95 | 96 | # 97 | # Calibration 98 | # 99 | 100 | def BCC_calibration_short(): 101 | ''' Calibrates jump component of BCC97 model to market quotes. ''' 102 | # first run with brute force 103 | # (scan sensible regions) 104 | opt1 = 0.0 105 | opt1 = brute(BCC_error_function, 106 | ((0.0, 0.51, 0.1), # lambda 107 | (-0.5, -0.11, 0.1), # mu 108 | (0.0, 0.51, 0.25)), # delta 109 | finish=None) 110 | 111 | # second run with local, convex minimization 112 | # (dig deeper where promising) 113 | local_opt = True 114 | opt2 = fmin(BCC_error_function, opt1, 115 | xtol=0.0000001, ftol=0.0000001, 116 | maxiter=550, maxfun=750) 117 | np.save('11_cal/opt_jump', np.array(opt2)) 118 | return opt2 119 | 120 | def BCC_jump_calculate_model_values(p0): 121 | ''' Calculates all model values given parameter vector p0. ''' 122 | lamb, mu, delta = p0 123 | values = [] 124 | for row, option in options.iterrows(): 125 | T = (option['Maturity'] - option['Date']).days / 365. 126 | B0T = B([kappa_r, theta_r, sigma_r, r0, T]) 127 | r = -math.log(B0T) / T 128 | model_value = BCC_call_value(S0, option['Strike'], T, r, 129 | kappa_v, theta_v, sigma_v, rho, v0, 130 | lamb, mu, delta) 131 | values.append(model_value) 132 | return np.array(values) 133 | 134 | # 135 | # Graphical Results Output 136 | # 137 | def plot_calibration_results(p0): 138 | options['Model'] = BCC_jump_calculate_model_values(p0) 139 | plt.figure(figsize=(8, 6)) 140 | plt.subplot(211) 141 | plt.grid() 142 | plt.title('Maturity %s' % str(options['Maturity'].iloc[0])[:10]) 143 | plt.ylabel('option values') 144 | plt.plot(options.Strike, options.Call, 'b', label='market') 145 | plt.plot(options.Strike, options.Model, 'ro', label='model') 146 | plt.legend(loc=0) 147 | plt.axis([min(options.Strike) - 10, max(options.Strike) + 10, 148 | min(options.Call) - 10, max(options.Call) + 10]) 149 | plt.subplot(212) 150 | plt.grid(True) 151 | wi = 5.0 152 | diffs = options.Model.values - options.Call.values 153 | plt.bar(options.Strike - wi / 2, diffs, width=wi) 154 | plt.ylabel('difference') 155 | plt.axis([min(options.Strike) - 10, max(options.Strike) + 10, 156 | min(diffs) * 1.1, max(diffs) * 1.1]) 157 | plt.tight_layout() -------------------------------------------------------------------------------- /dawp/book/11_cal/BCC97_full_results.tex: -------------------------------------------------------------------------------- 1 | 2 | \begin{itemize} 3 | \item $\kappa_v = 22.212$ 4 | \item $\theta_v = 0.025$ 5 | \item $\sigma_v = 0.952$ 6 | \item $\rho = -0.999$ 7 | \item $v_0 = 0.036$ 8 | \item $\lambda = 0.008$ 9 | \item $\mu = -0.501$ 10 | \item $\delta = 0.000$ 11 | \end{itemize} 12 | -------------------------------------------------------------------------------- /dawp/book/11_cal/BCC97_iv_results.tex: -------------------------------------------------------------------------------- 1 | 2 | \begin{itemize} 3 | \item $\kappa_v = 28.473$ 4 | \item $\theta_v = 0.025$ 5 | \item $\sigma_v = 1.175$ 6 | \item $\rho = -0.953$ 7 | \item $v_0 = 0.039$ 8 | \item $\lambda = 0.007$ 9 | \item $\mu = -0.600$ 10 | \item $\delta = 0.000$ 11 | \end{itemize} 12 | -------------------------------------------------------------------------------- /dawp/book/11_cal/BCC97_jump_results.tex: -------------------------------------------------------------------------------- 1 | 2 | \begin{itemize} 3 | \item $\lambda = 0.008$ 4 | \item $\mu = -0.600$ 5 | \item $\delta = 0.001$ 6 | \end{itemize} 7 | -------------------------------------------------------------------------------- /dawp/book/11_cal/CIR_calibration.py: -------------------------------------------------------------------------------- 1 | # 2 | # Calibration of CIR85 model 3 | # to Euribor Rates from 30. September 2014 4 | # 11_cal/CIR_calibration.py 5 | # 6 | # (c) Dr. Yves J. Hilpisch 7 | # Derivatives Analytics with Python 8 | # 9 | import sys 10 | sys.path.append('10_mcs') 11 | import math 12 | import numpy as np 13 | np.set_printoptions(suppress=True, 14 | formatter={'all': lambda x: '%7.6f' % x}) 15 | import matplotlib.pyplot as plt 16 | import matplotlib as mpl 17 | mpl.rcParams['font.family'] = 'serif' 18 | import scipy.interpolate as sci 19 | from scipy.optimize import fmin 20 | from CIR_zcb_valuation_gen import B 21 | 22 | # 23 | # Market Data: Eonia rate (01.10.2014) + Euribor rates 24 | # Source: http://www.emmi-benchmarks.eu 25 | # on 30. September 2014 26 | # 27 | t_list = np.array((1, 7, 14, 30, 60, 90, 180, 270, 360)) / 360. 28 | r_list = np.array((-0.032, -0.013, -0.013, 0.007, 0.043, 29 | 0.083, 0.183, 0.251, 0.338)) / 100 30 | 31 | factors = (1 + t_list * r_list) 32 | zero_rates = 1 / t_list * np.log(factors) 33 | 34 | r0 = r_list[0] # 0.0 # set to zero 35 | 36 | # 37 | # Interpolation of Market Data 38 | # 39 | 40 | tck = sci.splrep(t_list, zero_rates, k=3) # cubic splines 41 | tn_list = np.linspace(0.0, 1.0, 24) 42 | ts_list = sci.splev(tn_list, tck, der=0) 43 | de_list = sci.splev(tn_list, tck, der=1) 44 | 45 | f = ts_list + de_list * tn_list 46 | # forward rate transformation 47 | 48 | def plot_term_structure(): 49 | plt.figure(figsize=(8, 5)) 50 | plt.plot(t_list, r_list, 'ro', label='rates') 51 | plt.plot(tn_list, ts_list, 'b', label='interpolation', lw=1.5) 52 | # cubic splines 53 | plt.plot(tn_list, de_list, 'g--', label='1st derivative', lw=1.5) 54 | # first derivative 55 | plt.legend(loc=0) 56 | plt.xlabel('time horizon in years') 57 | plt.ylabel('rate') 58 | plt.grid() 59 | 60 | 61 | # 62 | # Model Forward Rates 63 | # 64 | def CIR_forward_rate(opt): 65 | ''' Function for forward rates in CIR85 model. 66 | 67 | Parameters 68 | ========== 69 | kappa_r: float 70 | mean-reversion factor 71 | theta_r: float 72 | long-run mean 73 | sigma_r: float 74 | volatility factor 75 | 76 | Returns 77 | ======= 78 | forward_rate: float 79 | forward rate 80 | ''' 81 | kappa_r, theta_r, sigma_r = opt 82 | t = tn_list 83 | g = np.sqrt(kappa_r ** 2 + 2 * sigma_r ** 2) 84 | sum1 = ((kappa_r * theta_r * (np.exp(g * t) - 1)) / 85 | (2 * g + (kappa_r + g) * (np.exp(g * t) - 1))) 86 | sum2 = r0 * ((4 * g ** 2 * np.exp(g * t)) / 87 | (2 * g + (kappa_r + g) * (np.exp(g * t) - 1)) ** 2) 88 | forward_rate = sum1 + sum2 89 | return forward_rate 90 | 91 | # 92 | # Error Function 93 | # 94 | def CIR_error_function(opt): 95 | ''' Error function for CIR85 model calibration. ''' 96 | kappa_r, theta_r, sigma_r = opt 97 | if 2 * kappa_r * theta_r < sigma_r ** 2: 98 | return 100 99 | if kappa_r < 0 or theta_r < 0 or sigma_r < 0.001: 100 | return 100 101 | forward_rates = CIR_forward_rate(opt) 102 | MSE = np.sum((f - forward_rates) ** 2) / len(f) 103 | # print opt, MSE 104 | return MSE 105 | 106 | # 107 | # Calibration Procedure 108 | # 109 | def CIR_calibration(): 110 | opt = fmin(CIR_error_function, [1.0, 0.02, 0.1], 111 | xtol=0.00001, ftol=0.00001, 112 | maxiter=300, maxfun=500) 113 | return opt 114 | 115 | # 116 | # Graphical Results Output 117 | # 118 | def plot_calibrated_frc(opt): 119 | ''' Plots market and calibrated forward rate curves. ''' 120 | forward_rates = CIR_forward_rate(opt) 121 | plt.figure(figsize=(8, 7)) 122 | plt.subplot(211) 123 | plt.grid() 124 | plt.ylabel('forward rate $f(0,T)$') 125 | plt.plot(tn_list, f, 'b', label='market') 126 | plt.plot(tn_list, forward_rates, 'ro', label='model') 127 | plt.legend(loc=0) 128 | plt.axis([min(tn_list) - 0.05, max(tn_list) + 0.05, 129 | min(f) - 0.005, max(f) * 1.1]) 130 | plt.subplot(212) 131 | plt.grid(True) 132 | wi = 0.02 133 | plt.bar(tn_list - wi / 2, forward_rates - f, width=wi) 134 | plt.xlabel('time horizon in years') 135 | plt.ylabel('difference') 136 | plt.axis([min(tn_list) - 0.05, max(tn_list) + 0.05, 137 | min(forward_rates - f) * 1.1, max(forward_rates - f) * 1.1]) 138 | plt.tight_layout() 139 | 140 | 141 | def plot_zcb_values(p0, T): 142 | ''' Plots unit zero-coupon bond values (discount factors). ''' 143 | t_list = np.linspace(0.0, T, 20) 144 | r_list = B([r0, p0[0], p0[1], p0[2], t_list, T]) 145 | plt.figure(figsize=(8, 5)) 146 | plt.plot(t_list, r_list, 'b') 147 | plt.plot(t_list, r_list, 'ro') 148 | plt.xlabel('time horizon in years') 149 | plt.ylabel('unit zero-coupon bond value') 150 | plt.grid() 151 | 152 | 153 | -------------------------------------------------------------------------------- /dawp/book/11_cal/H93_calibration.py: -------------------------------------------------------------------------------- 1 | # 2 | # Calibration of Bakshi, Cao and Chen (1997) 3 | # Stoch Vol Jump Model to EURO STOXX Option Quotes 4 | # Data Source: www.eurexchange.com 5 | # via Numerical Integration 6 | # 11_cal/BCC97_calibration_2.py 7 | # 8 | # (c) Dr. Yves J. Hilpisch 9 | # Derivatives Analytics with Python 10 | # 11 | import sys 12 | sys.path.append('09_gmm') 13 | import math 14 | import numpy as np 15 | np.set_printoptions(suppress=True, 16 | formatter={'all': lambda x: '%5.3f' % x}) 17 | import pandas as pd 18 | from scipy.optimize import brute, fmin, minimize 19 | import matplotlib as mpl 20 | mpl.rcParams['font.family'] = 'serif' 21 | from BCC_option_valuation import H93_call_value 22 | from CIR_calibration import CIR_calibration, r_list 23 | from CIR_zcb_valuation import B 24 | 25 | # 26 | # Calibrate Short Rate Model 27 | # 28 | kappa_r, theta_r, sigma_r = CIR_calibration() 29 | 30 | # 31 | # Market Data from www.eurexchange.com 32 | # as of 30. September 2014 33 | # 34 | h5 = pd.HDFStore('08_m76/option_data.h5', 'r') 35 | data = h5['data'] # European call & put option data (3 maturities) 36 | h5.close() 37 | S0 = 3225.93 # EURO STOXX 50 level 30.09.2014 38 | r0 = r_list[0] # initial short rate (Eonia 30.09.2014) 39 | 40 | # 41 | # Option Selection 42 | # 43 | tol = 0.02 # percent ITM/OTM options 44 | options = data[(np.abs(data['Strike'] - S0) / S0) < tol] 45 | 46 | # options = data[data['Strike'].isin([3100, 3150, 3225, 3300, 3350])] 47 | 48 | # 49 | # Adding Time-to-Maturity and Short Rates 50 | # 51 | for row, option in options.iterrows(): 52 | T = (option['Maturity'] - option['Date']).days / 365. 53 | options.ix[row, 'T'] = T 54 | B0T = B([kappa_r, theta_r, sigma_r, r0, T]) 55 | options.ix[row, 'r'] = -math.log(B0T) / T 56 | 57 | # 58 | # Calibration Functions 59 | # 60 | i = 0 61 | min_MSE = 500 62 | def H93_error_function(p0): 63 | ''' Error function for parameter calibration in BCC97 model via 64 | Lewis (2001) Fourier approach. 65 | 66 | Parameters 67 | ========== 68 | kappa_v: float 69 | mean-reversion factor 70 | theta_v: float 71 | long-run mean of variance 72 | sigma_v: float 73 | volatility of variance 74 | rho: float 75 | correlation between variance and stock/index level 76 | v0: float 77 | initial, instantaneous variance 78 | 79 | Returns 80 | ======= 81 | MSE: float 82 | mean squared error 83 | ''' 84 | global i, min_MSE 85 | kappa_v, theta_v, sigma_v, rho, v0 = p0 86 | if kappa_v < 0.0 or theta_v < 0.005 or sigma_v < 0.0 or \ 87 | rho < -1.0 or rho > 1.0: 88 | return 500.0 89 | if 2 * kappa_v * theta_v < sigma_v ** 2: 90 | return 500.0 91 | se = [] 92 | for row, option in options.iterrows(): 93 | model_value = H93_call_value(S0, option['Strike'], option['T'], 94 | option['r'], kappa_v, theta_v, sigma_v, rho, v0) 95 | se.append((model_value - option['Call']) ** 2) 96 | MSE = sum(se) / len(se) 97 | min_MSE = min(min_MSE, MSE) 98 | if i % 25 == 0: 99 | print '%4d |' % i, np.array(p0), '| %7.3f | %7.3f' % (MSE, min_MSE) 100 | i += 1 101 | return MSE 102 | 103 | def H93_calibration_full(): 104 | ''' Calibrates H93 stochastic volatility model to market quotes. ''' 105 | # first run with brute force 106 | # (scan sensible regions) 107 | p0 = brute(H93_error_function, 108 | ((2.5, 10.6, 5.0), # kappa_v 109 | (0.01, 0.041, 0.01), # theta_v 110 | (0.05, 0.251, 0.1), # sigma_v 111 | (-0.75, 0.01, 0.25), # rho 112 | (0.01, 0.031, 0.01)), # v0 113 | finish=None) 114 | 115 | # second run with local, convex minimization 116 | # (dig deeper where promising) 117 | opt = fmin(H93_error_function, p0, 118 | xtol=0.000001, ftol=0.000001, 119 | maxiter=750, maxfun=900) 120 | np.save('11_cal/opt_sv', np.array(opt)) 121 | return opt 122 | 123 | def H93_calculate_model_values(p0): 124 | ''' Calculates all model values given parameter vector p0. ''' 125 | kappa_v, theta_v, sigma_v, rho, v0 = p0 126 | values = [] 127 | for row, option in options.iterrows(): 128 | model_value = H93_call_value(S0, option['Strike'], option['T'], 129 | option['r'], kappa_v, theta_v, sigma_v, rho, v0) 130 | values.append(model_value) 131 | return np.array(values) -------------------------------------------------------------------------------- /dawp/book/11_cal/H93_results.tex: -------------------------------------------------------------------------------- 1 | 2 | \begin{itemize} 3 | \item $\kappa_v = 18.447$ 4 | \item $\theta_v = 0.026$ 5 | \item $\sigma_v = 0.978$ 6 | \item $\rho = -0.821$ 7 | \item $v_0 = 0.035$ 8 | \end{itemize} 9 | -------------------------------------------------------------------------------- /dawp/book/11_cal/cal_results_full.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/11_cal/cal_results_full.h5 -------------------------------------------------------------------------------- /dawp/book/11_cal/cal_results_full_iv.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/11_cal/cal_results_full_iv.h5 -------------------------------------------------------------------------------- /dawp/book/11_cal/cal_results_sv.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/11_cal/cal_results_sv.h5 -------------------------------------------------------------------------------- /dawp/book/11_cal/opt_full.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/11_cal/opt_full.npy -------------------------------------------------------------------------------- /dawp/book/11_cal/opt_iv.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/11_cal/opt_iv.npy -------------------------------------------------------------------------------- /dawp/book/11_cal/opt_jump.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/11_cal/opt_jump.npy -------------------------------------------------------------------------------- /dawp/book/11_cal/opt_sv.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/11_cal/opt_sv.npy -------------------------------------------------------------------------------- /dawp/book/11_cal/plot_implied_volatilities.py: -------------------------------------------------------------------------------- 1 | # 2 | # Black-Scholes-Merton Implied Volatilities of 3 | # of Calibrated BCC97 Model 4 | # Data Source: www.eurexchange.com, 30. September 2014 5 | # 11_cal/plot_implied_volatilities.py 6 | # 7 | # (c) Dr. Yves J. Hilpisch 8 | # Derivatives Analytics with Python 9 | # 10 | import sys 11 | sys.path.extend(['03_stf', '09_gmm']) 12 | import math 13 | import pandas as pd 14 | import matplotlib.pyplot as plt 15 | import matplotlib as mpl 16 | mpl.rcParams['font.family'] = 'serif' 17 | from BSM_imp_vol import call_option 18 | from CIR_zcb_valuation import B 19 | from H93_calibration import S0, r0, kappa_r, theta_r, sigma_r 20 | 21 | # 22 | # Calibration Results 23 | # 24 | 25 | def calculate_implied_volatilities(filename): 26 | ''' Calculates market and model implied volatilities. ''' 27 | h5 = pd.HDFStore(filename, 'r') 28 | options = h5['options'] 29 | h5.close() 30 | for row, option in options.iterrows(): 31 | T = (option['Maturity'] - option['Date']).days / 365. 32 | B0T = B([kappa_r, theta_r, sigma_r, r0, T]) 33 | r = -math.log(B0T) / T 34 | call = call_option(S0, option['Strike'], option['Date'], 35 | option['Maturity'], option['r'], 0.1) 36 | options.ix[row, 'market_iv'] = call.imp_vol(option['Call'], 0.15) 37 | options.ix[row, 'model_iv'] = call.imp_vol(option['Model'], 0.15) 38 | return options 39 | 40 | def plot_implied_volatilities(options, model): 41 | ''' Plots market implied volatilities against model implied ones. ''' 42 | mats = sorted(set(options.Maturity)) 43 | for mat in mats: 44 | opts = options[options.Maturity == mat] 45 | plt.figure(figsize=(8, 6)) 46 | plt.subplot(211) 47 | plt.grid() 48 | plt.ylabel('implied volatility') 49 | plt.plot(opts.Strike, opts.market_iv, 'b', label='market', lw=1.5) 50 | plt.plot(opts.Strike, opts.model_iv, 'ro', label='model') 51 | plt.legend(loc=0) 52 | plt.axis([min(opts.Strike) - 10, max(opts.Strike) + 10, 53 | min(opts.market_iv) - 0.015, max(opts.market_iv) + 0.015]) 54 | plt.title('Maturity %s' % str(mat)[:10]) 55 | plt.subplot(212) 56 | plt.grid(True) 57 | wi = 5.0 58 | diffs = opts.model_iv.values - opts.market_iv.values 59 | plt.bar(opts.Strike - wi / 2, diffs, width=wi) 60 | plt.ylabel('difference') 61 | ymi = min(diffs) - (max(diffs) - min(diffs)) * 0.1 62 | yma = max(diffs) + (max(diffs) - min(diffs)) * 0.1 63 | plt.axis([min(opts.Strike) - 10, max(opts.Strike) + 10, ymi, yma]) 64 | plt.tight_layout() 65 | plt.savefig('../images/11_cal/%s_calibration_iv_%s.pdf' 66 | % (model, str(mat)[:10])) 67 | -------------------------------------------------------------------------------- /dawp/book/12_val/BCC97_american_valuation.py: -------------------------------------------------------------------------------- 1 | # 2 | # Valuation of American Options in BCC97 Model 3 | # by Least-Squares Monte Carlo Algorithm 4 | # 12_val/BCC97_american_valuation.py 5 | # 6 | # (c) Dr. Yves J. Hilpisch 7 | # Derivatives Analytics with Python 8 | # 9 | import sys 10 | sys.path.extend(['09_gmm', '10_mcs']) 11 | import math 12 | from BCC_option_valuation import * 13 | from CIR_zcb_valuation_gen import B 14 | from BCC97_simulation import * 15 | 16 | # 17 | # Additional Parameters 18 | # 19 | D = 10 # number of basis functions 20 | t_list = [1 / 12., 0.5, 1.0, 1.5, 2.0, 3.0] 21 | k_list = [3050, 3225, 3400] 22 | 23 | # 24 | # LSM Valuation Function 25 | # 26 | def BCC97_lsm_valuation(S, r, v, K, T, M, I): 27 | ''' Function to value American put options by LSM algorithm. 28 | 29 | Parameters 30 | ========== 31 | S: NumPy array 32 | simulated index level paths 33 | r: NumPy array 34 | simulated short rate paths 35 | v: NumPy array 36 | simulated variance paths 37 | K: float 38 | strike of the put option 39 | T: float 40 | final date/time horizon 41 | M: int 42 | number of time steps 43 | I: int 44 | number of paths 45 | 46 | Returns 47 | ======= 48 | LSM_value: float 49 | LSM Monte Carlo estimator of American put option value 50 | ''' 51 | dt = T / M 52 | # inner value matrix 53 | h = np.maximum(K - S, 0) 54 | # value/cash flow matrix 55 | V = np.maximum(K - S, 0) 56 | for t in xrange(M - 1, 0, -1): 57 | df = np.exp(-(r[t] + r[t + 1]) / 2 * dt) 58 | # select only ITM paths 59 | itm = np.greater(h[t], 0) 60 | relevant = np.nonzero(itm) 61 | rel_S = np.compress(itm, S[t]) 62 | no_itm = len(rel_S) 63 | if no_itm == 0: 64 | cv = np.zeros((I), dtype=np.float) 65 | else: 66 | rel_v = np.compress(itm, v[t]) 67 | rel_r = np.compress(itm, r[t]) 68 | rel_V = (np.compress(itm, V[t + 1]) 69 | * np.compress(itm, df)) 70 | matrix = np.zeros((D + 1, no_itm), dtype=np.float) 71 | matrix[10] = rel_S * rel_v * rel_r 72 | matrix[9] = rel_S * rel_v 73 | matrix[8] = rel_S * rel_r 74 | matrix[7] = rel_v * rel_r 75 | matrix[6] = rel_S ** 2 76 | matrix[5] = rel_v ** 2 77 | matrix[4] = rel_r ** 2 78 | matrix[3] = rel_S 79 | matrix[2] = rel_v 80 | matrix[1] = rel_r 81 | matrix[0] = 1 82 | reg = np.linalg.lstsq(matrix.transpose(), rel_V) 83 | cv = np.dot(reg[0], matrix) 84 | erg = np.zeros((I), dtype=np.float) 85 | np.put(erg, relevant, cv) 86 | V[t] = np.where(h[t] > erg, h[t], V[t + 1] * df) 87 | # exercise decision 88 | df = np.exp(-((r[0] + r[1]) / 2) * dt) 89 | LSM_value = max(np.sum(V[1, :] * df) / I, h[0, 0]) # LSM estimator 90 | return LSM_value 91 | 92 | # 93 | # Valuation for Different Strikes & Maturities 94 | # 95 | def lsm_compare_values(M0=50, I=50000): 96 | results = [] 97 | for T in t_list: 98 | # 99 | # Simulation 100 | # 101 | M = int(M0 * T) 102 | cho_matrix = generate_cholesky(rho) 103 | rand = random_number_generator(M, I, anti_paths, moment_matching) 104 | r = SRD_generate_paths(r0, kappa_r, theta_r, sigma_r, T, M, I, 105 | rand, 0, cho_matrix) 106 | v = SRD_generate_paths(v0, kappa_v, theta_v, sigma_v, T, M, I, 107 | rand, 2, cho_matrix) 108 | S = B96_generate_paths(S0, r, v, lamb, mu, delta, rand, 1, 3, 109 | cho_matrix, T, M, I, moment_matching) 110 | for K in k_list: 111 | # 112 | # Valuation 113 | # 114 | h = np.maximum(S[-1] - K, 0) 115 | B0T = B([r0, kappa_r, theta_r, sigma_r, 0.0, T]) 116 | V0_lsm = BCC97_lsm_valuation(S, r, v, K, T, M, I) 117 | # LSM estimator 118 | # 119 | # European Call Option via Fourier 120 | # 121 | ra = -math.log(B0T) / T # average short rate/yield 122 | C0 = BCC_call_value(S0, K, T, ra, kappa_v, theta_v, sigma_v, 123 | rho, v0, lamb, mu, delta) 124 | P0 = C0 + K * B0T - S0 125 | 126 | results.append((T, K, P0, V0_lsm, V0_lsm - P0)) 127 | 128 | print " %6s | %6s | %7s | %7s | %7s" % ('T', 'K', 'P0', 'LSM', 'DIFF') 129 | for res in results: 130 | print " %6.3f | %6d | %7.3f | %7.3f | %7.3f" % res 131 | -------------------------------------------------------------------------------- /dawp/book/12_val/BCC97_simulation.py: -------------------------------------------------------------------------------- 1 | # 2 | # Monte Carlo Simulation of BCC97 Model 3 | # 12_val/BCC97_simulation.py 4 | # 5 | # (c) Dr. Yves J. Hilpisch 6 | # Derivatives Analytics with Python 7 | # 8 | import sys 9 | sys.path.append('11_cal') 10 | import math 11 | import numpy as np 12 | import matplotlib as mpl 13 | mpl.rcParams['font.family'] = 'serif' 14 | import matplotlib.pyplot as plt 15 | from H93_calibration import S0, kappa_r, theta_r, sigma_r, r0 16 | 17 | # 18 | # Model Parameters 19 | # 20 | opt = np.load('11_cal/opt_full.npy') 21 | kappa_v, theta_v, sigma_v, rho, v0, lamb, mu, delta = opt 22 | 23 | 24 | # 25 | # Simulation Parameters 26 | # 27 | T = 1.0 # time horizon 28 | M = 25 # time steps 29 | I = 10000 # number of replications per valuation 30 | anti_paths = True # antithetic paths for variance reduction 31 | moment_matching = True # moment matching for variance reduction 32 | np.random.seed(100000) # seed value for random number generator 33 | 34 | # 35 | # Random Number Generation 36 | # 37 | 38 | 39 | def generate_cholesky(rho): 40 | ''' Function to generate Cholesky matrix. 41 | 42 | Parameters 43 | ========== 44 | rho: float 45 | correlation between index level and variance 46 | 47 | Returns 48 | ======= 49 | matrix: NumPy array 50 | Cholesky matrix 51 | ''' 52 | rho_rs = 0 # correlation between index level and short rate 53 | covariance = np.zeros((4, 4), dtype=np.float) 54 | covariance[0] = [1.0, rho_rs, 0.0, 0.0] 55 | covariance[1] = [rho_rs, 1.0, rho, 0.0] 56 | covariance[2] = [0.0, rho, 1.0, 0.0] 57 | covariance[3] = [0.0, 0.0, 0.0, 1.0] 58 | cho_matrix = np.linalg.cholesky(covariance) 59 | return cho_matrix 60 | 61 | def random_number_generator(M, I, anti_paths, moment_matching): 62 | ''' Function to generate pseudo-random numbers. 63 | 64 | Parameters 65 | ========== 66 | M: int 67 | time steps 68 | I: int 69 | number of simulation paths 70 | anti_paths: bool 71 | flag for antithetic paths 72 | moment_matching: bool 73 | flag for moment matching 74 | 75 | Returns 76 | ======= 77 | rand: NumPy array 78 | random number array 79 | ''' 80 | if anti_paths: 81 | rand = np.random.standard_normal((4, M + 1, I / 2)) 82 | rand = np.concatenate((rand, -rand), 2) 83 | else: 84 | rand = np.random.standard_normal((4, M + 1, I)) 85 | if moment_matching: 86 | for a in range(4): 87 | rand[a] = rand[a] / np.std(rand[a]) 88 | rand[a] = rand[a] - np.mean(rand[a]) 89 | return rand 90 | 91 | # 92 | # Function for Short Rate and Volatility Processes 93 | # 94 | 95 | def SRD_generate_paths(x0, kappa, theta, sigma, T, M, I, 96 | rand, row, cho_matrix): 97 | ''' Function to simulate Square-Root Difussion (SRD/CIR) process. 98 | 99 | Parameters 100 | ========== 101 | x0: float 102 | initial value 103 | kappa: float 104 | mean-reversion factor 105 | theta: float 106 | long-run mean 107 | sigma: float 108 | volatility factor 109 | T: float 110 | final date/time horizon 111 | M: int 112 | number of time steps 113 | I: int 114 | number of paths 115 | row: int 116 | row number for random numbers 117 | cho_matrix: NumPy array 118 | cholesky matrix 119 | 120 | Returns 121 | ======= 122 | x: NumPy array 123 | simulated variance paths 124 | ''' 125 | dt = T / M 126 | x = np.zeros((M + 1, I), dtype=np.float) 127 | x[0] = x0 128 | xh = np.zeros_like(x) 129 | xh[0] = x0 130 | sdt = math.sqrt(dt) 131 | for t in range(1, M + 1): 132 | ran = np.dot(cho_matrix, rand[:, t]) 133 | xh[t] = (xh[t - 1] + kappa * (theta - 134 | np.maximum(0, xh[t - 1])) * dt + 135 | np.sqrt(np.maximum(0, xh[t - 1])) * sigma * ran[row] * sdt) 136 | x[t] = np.maximum(0, xh[t]) 137 | return x 138 | 139 | # 140 | # Function for B96 Index Process 141 | # 142 | 143 | def B96_generate_paths(S0, r, v, lamb, mu, delta, rand, row1, row2, 144 | cho_matrix, T, M, I, moment_matching): 145 | ''' Simulation of Bates (1996) index process. 146 | 147 | Parameters 148 | ========== 149 | S0: float 150 | initial value 151 | r: NumPy array 152 | simulated short rate paths 153 | v: NumPy array 154 | simulated variance paths 155 | lamb: float 156 | jump intensity 157 | mu: float 158 | expected jump size 159 | delta: float 160 | standard deviation of jump 161 | rand: NumPy array 162 | random number array 163 | row1, row2: int 164 | rows/matrices of random number array to use 165 | cho_matrix: NumPy array 166 | Cholesky matrix 167 | T: float 168 | time horizon, maturity 169 | M: int 170 | number of time intervals, steps 171 | I: int 172 | number of paths to simulate 173 | moment_matching: bool 174 | flag for moment matching 175 | 176 | Returns 177 | ======= 178 | S: NumPy array 179 | simulated index level paths 180 | ''' 181 | S = np.zeros((M + 1, I), dtype=np.float) 182 | S[0] = S0 183 | dt = T / M 184 | sdt = math.sqrt(dt) 185 | ranp = np.random.poisson(lamb * dt, (M + 1, I)) 186 | rj = lamb * (math.exp(mu + 0.5 * delta ** 2) - 1) 187 | bias = 0.0 188 | for t in xrange(1, M + 1, 1): 189 | ran = np.dot(cho_matrix, rand[:, t, :]) 190 | if moment_matching: 191 | bias = np.mean(np.sqrt(v[t]) * ran[row1] * sdt) 192 | S[t] = S[t - 1] * (np.exp(((r[t] + r[t -1]) / 2 - 0.5 * v[t]) * dt + 193 | np.sqrt(v[t]) * ran[row1] * sdt - bias) 194 | + (np.exp(mu + delta * ran[row2]) - 1) * ranp[t]) 195 | return S 196 | 197 | if __name__ == '__main__': 198 | # 199 | # Simulation 200 | # 201 | cho_matrix = generate_cholesky(rho) 202 | rand = random_number_generator(M, I, anti_paths, moment_matching) 203 | r = SRD_generate_paths(r0, kappa_r, theta_r, sigma_r, T, M, I, 204 | rand, 0, cho_matrix) 205 | v = SRD_generate_paths(v0, kappa_v, theta_v, sigma_v, T, M, I, 206 | rand, 2, cho_matrix) 207 | S = B96_generate_paths(S0, r, v, lamb, mu, delta, rand, 1, 3, 208 | cho_matrix, T, M, I, moment_matching) 209 | 210 | 211 | def plot_rate_paths(r): 212 | plt.figure(figsize=(8, 4)) 213 | plt.plot(r[:, :10]) 214 | plt.xlabel('time step') 215 | plt.ylabel('short rate level') 216 | plt.title('Short Rate Simulated Paths') 217 | plt.grid() 218 | 219 | def plot_volatility_paths(v): 220 | plt.figure(figsize=(8, 4)) 221 | plt.plot(np.sqrt(v[:, :10])) 222 | plt.xlabel('time step') 223 | plt.ylabel('volatility level') 224 | plt.title('Volatility Simulated Paths') 225 | plt.grid() 226 | 227 | def plot_index_paths(S): 228 | plt.figure(figsize=(8, 4)) 229 | plt.plot(S[:, :10]) 230 | plt.xlabel('time step') 231 | plt.ylabel('index level') 232 | plt.title('EURO STOXX 50 Simulated Paths') 233 | plt.grid() 234 | 235 | def plot_index_histogram(S): 236 | plt.figure(figsize=(8, 4)) 237 | plt.hist(S[-1], bins=30) 238 | plt.xlabel('index level') 239 | plt.ylabel('frequency') 240 | plt.title('EURO STOXX 50 Values after 1 Year') 241 | plt.grid() 242 | -------------------------------------------------------------------------------- /dawp/book/12_val/BCC97_valuation_comparison.py: -------------------------------------------------------------------------------- 1 | # 2 | # Valuation of European Options in BCC97 Model 3 | # by Monte Carlo Simulation 4 | # 12_val/BCC97_valuation_comparison.py 5 | # 6 | # (c) Dr. Yves J. Hilpisch 7 | # Derivatives Analytics with Python 8 | # 9 | import sys 10 | sys.path.extend(['09_gmm', '10_mcs']) 11 | import math 12 | from BCC_option_valuation import * 13 | from CIR_zcb_valuation_gen import B 14 | from BCC97_simulation import * 15 | 16 | # 17 | # Parameters 18 | # 19 | t_list = [1 / 12., 0.5, 1.0, 1.5, 2.0, 3.0] 20 | k_list = [3050, 3225, 3400] 21 | 22 | # 23 | # Valuation for Different Strikes & Maturities 24 | # 25 | def compare_values(M0=50, I=50000): 26 | results = [] 27 | for T in t_list: 28 | # 29 | # Simulation 30 | # 31 | M = int(M0 * T) 32 | cho_matrix = generate_cholesky(rho) 33 | rand = random_number_generator(M, I, anti_paths, moment_matching) 34 | r = SRD_generate_paths(r0, kappa_r, theta_r, sigma_r, T, M, I, 35 | rand, 0, cho_matrix) 36 | v = SRD_generate_paths(v0, kappa_v, theta_v, sigma_v, T, M, I, 37 | rand, 2, cho_matrix) 38 | S = B96_generate_paths(S0, r, v, lamb, mu, delta, rand, 1, 3, 39 | cho_matrix, T, M, I, moment_matching) 40 | for K in k_list: 41 | # 42 | # Valuation 43 | # 44 | h = np.maximum(S[-1] - K, 0) 45 | B0T = B([r0, kappa_r, theta_r, sigma_r, 0.0, T]) 46 | V0_mcs = B0T * np.sum(h) / I # MCS estimator 47 | # 48 | # European Call Option via Fourier 49 | # 50 | ra = -math.log(B0T) / T # average short rate/yield 51 | C0 = BCC_call_value(S0, K, T, ra, kappa_v, theta_v, sigma_v, 52 | rho, v0, lamb, mu, delta) 53 | 54 | results.append((T, K, C0, V0_mcs, V0_mcs - C0)) 55 | 56 | print " %6s | %6s | %7s | %7s | %7s" % ('T', 'K', 'C0', 'MCS', 'DIFF') 57 | for res in results: 58 | print " %6.3f | %6d | %7.3f | %7.3f | %7.3f" % res -------------------------------------------------------------------------------- /dawp/book/13_dyh/BCC97_lsm_hedging_histogram.py: -------------------------------------------------------------------------------- 1 | # 2 | # Delta Hedging an American Put Option in BCC97 3 | # via Least Squares Monte Carlo (Multiple Replications) 4 | # 13_dyh/BCC97_lsm_hedging_histogram.py 5 | # 6 | # (c) Dr. Yves s. Hilpisch 7 | # Derivatives Analytics with Python 8 | # 9 | from BCC97_lsm_hedging_algorithm import * 10 | from CIR_zcb_valuation_gen import B 11 | from BSM_lsm_hedging_histogram import plot_hedge_histogram 12 | 13 | # 14 | # Simulation 15 | # 16 | T = 1.0 17 | a = 1.0 # a from the interval [0.0, 2.0] 18 | dis = 0.05 # change of S[t] in percent to estimate derivative 19 | dt = T / M 20 | np.random.seed(50000) 21 | 22 | def BCC97_hedge_simulation(M=50, I=10000): 23 | ''' Monte Carlo simualtion of dynamic hedging paths 24 | for American put option in BSM model. ''' 25 | # 26 | # Initializations 27 | # 28 | po = np.zeros(M + 1, dtype=np.float) # vector for portfolio values 29 | delt = np.zeros(M + 1, dtype=np.float) # vector for deltas 30 | ds = dis * S0 31 | V_1, S, r, v, ex, rg, h, dt = BCC97_lsm_put_value(S0 + (2 - a) * ds, 32 | K, T, M, I) 33 | # 'data basis' for delta hedging 34 | V_2 = BCC97_lsm_put_value(S0 - a * ds, K, T, M, I)[0] 35 | delt[0] = (V_1 - V_2) / (2 * ds) 36 | V0LSM = BCC97_lsm_put_value(S0, K, T, M, I)[0] # initial option value for S0 37 | po[0] = V0LSM # initial portfolio values 38 | 39 | # 40 | # Hedge Runs 41 | # 42 | pl_list = [] 43 | runs = min(I, 10000) 44 | for run in range(runs): 45 | bo = V0LSM - delt[0] * S0 # initial bond position value 46 | p = run 47 | run += 1 48 | for t in range(1, M + 1, 1): 49 | if ex[t, p] == 0: 50 | df = math.exp((r[t, p] + r[t - 1, p]) / 2 * dt) 51 | if t != M: 52 | po[t] = delt[t - 1] * S[t, p] + bo * df # portfolio payoff 53 | ds = dis * S[t, p] 54 | sd = S[t, p] + (2 - a) * ds # disturbed index level 55 | stateV_A = [sd * v[t, p] * r[t, p], 56 | sd * v[t, p], 57 | sd * r[t, p], 58 | v[t, p] * r[t, p], 59 | sd ** 2, 60 | v[t, p] ** 2, 61 | r[t, p] ** 2, 62 | sd, 63 | v[t, p], 64 | r[t, p], 65 | 1] 66 | # state vector for S[t, p] + (2.0 - a) * ds 67 | stateV_A.reverse() 68 | V0A = max(0, np.dot(rg[t], stateV_A)) 69 | # revaluation via regression 70 | sd = S[t, p] - a * ds # disturbed index level 71 | stateV_B = [sd * v[t, p] * r[t, p], 72 | sd * v[t, p], 73 | sd * r[t, p], 74 | v[t, p] * r[t, p], 75 | sd ** 2, 76 | v[t, p] ** 2, 77 | r[t, p] ** 2, 78 | sd, 79 | v[t, p], 80 | r[t, p], 81 | 1] 82 | # state vector for S[t, p] - a * ds 83 | stateV_B.reverse() 84 | V0B = max(0, np.dot(rg[t], stateV_B)) 85 | # revaluation via regression 86 | delt[t] = (V0A - V0B) / (2 * ds) 87 | else: 88 | po[t] = delt[t - 1] * S[t, p] + bo * df 89 | delt[t] = 0.0 90 | bo = po[t] - delt[t] * S[t, p] 91 | else: 92 | po[t] = delt[t - 1] * S[t, p] + bo * df 93 | break 94 | alpha_t = [kappa_r, theta_r, sigma_r, r0, 0.0, t * dt] 95 | pl = (po[t] - h[t, p]) * B(alpha_t) 96 | if run % 1000 == 0: 97 | print "run %5d p/l %8.3f" % (run, pl) 98 | pl_list.append(pl) 99 | pl_list = np.array(pl_list) 100 | 101 | # 102 | # Results Output 103 | # 104 | print "\nSUMMARY STATISTICS FOR P&L" 105 | print "---------------------------------" 106 | print "Dynamic Replications %12d" % runs 107 | print "Time Steps %12d" % M 108 | print "Paths for Valuation %12d" % I 109 | print "Maximum %12.3f" % max(pl_list) 110 | print "Average %12.3f" % np.mean(pl_list) 111 | print "Median %12.3f" % np.median(pl_list) 112 | print "Minimum %12.3f" % min(pl_list) 113 | print "---------------------------------" 114 | 115 | return pl_list -------------------------------------------------------------------------------- /dawp/book/13_dyh/BSM_lsm_hedging_algorithm.py: -------------------------------------------------------------------------------- 1 | # 2 | # Dynamic Hedging of American Put Option in BSM Model 3 | # with Least Squares Monte Carlo 4 | # 13_dyh/BSM_lsm_hedging_algorithm.py 5 | # 6 | # (c) Dr. Yves J. Hilpisch 7 | # Derivatives Analytics with Python 8 | # 9 | import math 10 | import numpy as np 11 | import warnings 12 | warnings.simplefilter('ignore') 13 | import matplotlib as mpl 14 | mpl.rcParams['font.family'] = 'serif' 15 | import matplotlib.pyplot as plt 16 | 17 | # 18 | # Parameters 19 | # 20 | S0 = 36.0 # initial stock value 21 | K = 40.0 # strike price 22 | T = 1.0 # time to maturity 23 | r = 0.06 # risk-less short rate 24 | sigma = 0.20 # volatility of stock value 25 | M = 50 # number of time steps 26 | I = 50000 # number of paths 27 | 28 | 29 | # 30 | # Valuation 31 | # 32 | D = 9 # number of regression functions 33 | 34 | 35 | def BSM_lsm_put_value(S0, M, I): 36 | ''' Function to value an American put option by LSM algorithm. 37 | 38 | Parameters 39 | ========== 40 | S0: float 41 | initial index level 42 | M: int 43 | number of time steps 44 | 45 | Returns 46 | ======= 47 | V0: float 48 | LSM Monte Carlo estimator of American put option value 49 | S: NumPy array 50 | simulated index level paths 51 | ex: NumPy array 52 | exercise matrix 53 | rg: NumPy array 54 | regression coefficients 55 | h: NumPy array 56 | inner value matrix 57 | dt: float 58 | length of time interval 59 | ''' 60 | rand = np.random.standard_normal((M + 1, I)) # random numbers 61 | dt = T / M # length of time interval 62 | df = math.exp(-r * dt) # discount factor 63 | S = np.zeros((M + 1, I), dtype=np.float) # stock price matrix 64 | S[0] = S0 # initial values 65 | for t in xrange(1, M + 1, 1): # stock price at t 66 | S[t] = S[t - 1] * (np.exp((r - sigma ** 2 / 2) * dt 67 | + sigma * math.sqrt(dt) * rand[t])) 68 | h = np.maximum(K - S, 0) # inner values 69 | V = np.maximum(K - S, 0) # value matrix 70 | ex = np.zeros((M + 1, I), dtype=np.float) # exercise matrix 71 | C = np.zeros((M + 1, I), dtype=np.float) # continuation value matrix 72 | rg = np.zeros((M + 1, D + 1), dtype=np.float) 73 | # matrix for reg. coefficients 74 | for t in range(M - 1, 0, -1): 75 | rg[t] = np.polyfit(S[t], V[t + 1] * df, D) 76 | # regression in step i 77 | C[t] = np.polyval(rg[t], S[t]) 78 | # estimated continuation values 79 | C[t] = np.where(C[t] < 0, 0., C[t]) 80 | # correction for neg C 81 | V[t] = np.where(h[t] >= C[t], 82 | h[t], V[t + 1] * df) # exercise decision 83 | ex[t] = np.where(h[t] >= C[t], 1, 0) 84 | # exercise decision (yes=1) 85 | V0 = np.sum(V[1]) / I * df 86 | return V0, S, ex, rg, h, dt 87 | 88 | def BSM_hedge_run(p=0): 89 | ''' Implements delta hedging for a single path. ''' 90 | np.random.seed(50000) 91 | # 92 | # Initial Delta 93 | # 94 | ds = 0.01 95 | V_1, S, ex, rg, h, dt = BSM_lsm_put_value(S0 + ds, M, I) 96 | V_2 = BSM_lsm_put_value(S0, M, I)[0] 97 | del_0 = (V_1 - V_2) / ds 98 | 99 | # 100 | # Dynamic Hedging 101 | # 102 | delt = np.zeros(M + 1, dtype=np.float) # vector for deltas 103 | print 104 | print "APPROXIMATION OF FIRST ORDER " 105 | print "-----------------------------" 106 | print " %7s | %7s | %7s " % ('step', 'S_t', 'Delta') 107 | for t in xrange(1, M, 1): 108 | if ex[t, p] == 0: # if option is alive 109 | St = S[t, p] # relevant index level 110 | diff = (np.polyval(rg[t], St + ds) - 111 | np.polyval(rg[t], St)) 112 | # numerator of difference quotient 113 | delt[t] = diff / ds # delta as difference quotient 114 | print " %7d | %7.2f | %7.2f" % (t, St, delt[t]) 115 | if (S[t, p] - S[t - 1, p]) * (delt[t] - delt[t - 1]) < 0: 116 | print " wrong" 117 | else: 118 | break 119 | 120 | delt[0] = del_0 121 | print 122 | print "DYNAMIC HEDGING OF AMERICAN PUT (BSM)" 123 | print "---------------------------------------" 124 | po = np.zeros(t, dtype=np.float) # vector for portfolio values 125 | vt = np.zeros(t, dtype=np.float) # vector for option values 126 | vt[0] = V_1 127 | po[0] = V_1 128 | bo = V_1 - delt[0] * S0 # bond position value 129 | print "Initial Hedge" 130 | print "Stocks %8.3f" % delt[0] 131 | print "Bonds %8.3f" % bo 132 | print "Cost %8.3f" % (delt[0] * S0 + bo) 133 | 134 | print 135 | print "Regular Rehedges " 136 | print 68 * "-" 137 | print "step|" + 7 * " %7s|" % ('S_t', 'Port', 'Put', 138 | 'Diff', 'Stock', 'Bond', 'Cost') 139 | for j in range(1, t, 1): 140 | vt[j] = BSM_lsm_put_value(S[j, p], M - j, I)[0] 141 | po[j] = delt[j - 1] * S[j, p] + bo * math.exp(r * dt) 142 | bo = po[j] - delt[j] * S[j, p] # bond position value 143 | print "%4d|" % j + 7 * " %7.3f|" % (S[j, p], po[j], vt[j], 144 | (po[j] - vt[j]), delt[j], bo, delt[j] * S[j, p] + bo) 145 | 146 | errs = po - vt # hedge errors 147 | print "MSE %7.3f" % (np.sum(errs ** 2) / len(errs)) 148 | print "Average Error %7.3f" % (np.sum(errs) / len(errs)) 149 | print "Total P&L %7.3f" % np.sum(errs) 150 | return S[:, p], po, vt, errs, t 151 | 152 | def plot_hedge_path(S, po, vt, errs, t): 153 | # 154 | # Graphical Output 155 | # 156 | tl = np.arange(t) 157 | plt.figure(figsize=(8, 6)) 158 | plt.subplot(311) 159 | plt.grid(True) 160 | plt.plot(tl, S[tl], 'r') 161 | plt.ylabel('index level') 162 | plt.subplot(312) 163 | plt.grid(True) 164 | plt.plot(tl, po[tl], 'r-.', label='portfolio value', lw=2) 165 | plt.plot(tl, vt[tl], 'b', label='option value', lw=1) 166 | plt.ylabel('value') 167 | plt.legend(loc=0) 168 | ax = plt.axis() 169 | plt.subplot(313) 170 | plt.grid(True) 171 | wi = 0.3 172 | diffs = po[tl] - vt[tl] 173 | plt.bar(tl - wi / 2, diffs, color='b', width=wi) 174 | plt.ylabel('difference') 175 | plt.xlabel('time step') 176 | plt.axis([ax[0], ax[1], min(diffs) * 1.1, max(diffs) * 1.1]) 177 | plt.tight_layout() -------------------------------------------------------------------------------- /dawp/book/13_dyh/BSM_lsm_hedging_histogram.py: -------------------------------------------------------------------------------- 1 | # 2 | # Dynamic Hedging of American Put Option in BSM Model 3 | # with Least Squares Monte Carlo -- Histogram 4 | # 13_dyh/BSM_lsm_hedging_histogram.py 5 | # 6 | # (c) Dr. Yves s. Hilpisch 7 | # Derivatives Analytics with Python 8 | # 9 | from BSM_lsm_hedging_algorithm import * 10 | 11 | def BSM_dynamic_hedge_mcs(M=50, I=10000): 12 | ''' Monte Carlo simualtion of dynamic hedging paths 13 | for American put option in BSM model. ''' 14 | # 15 | # Initial Delta 16 | # 17 | ds = 0.01 18 | V_1, S, ex, rg, h, dt = BSM_lsm_put_value(S0 + ds, M, I) 19 | V_2 = BSM_lsm_put_value(S0, M, I)[0] 20 | del_0 = (V_1 - V_2) / ds 21 | 22 | print"Value of American Put Option is %8.3f" % V_2 23 | print"Delta t=0 is %8.3f" % del_0 24 | # 25 | # Dynamic Hedging Runs 26 | # 27 | pl_list = [] 28 | run = 0 29 | runs = min(I, 10000) 30 | for run in xrange(runs): 31 | p = run 32 | run += 1 33 | delta = np.zeros(M + 1, dtype=np.float) # vector for deltas 34 | for t in xrange(0, M, 1): 35 | if ex[t - 1, p] == 0: # if option is alive 36 | St = S[t, p] # relevant index level 37 | diff = (np.polyval(rg[t, :], St + ds) 38 | - np.polyval(rg[t, :], St)) 39 | # numerator of difference quotient 40 | delta[t] = diff / ds # delta as difference quotient 41 | else: 42 | break 43 | delta[0] = del_0 44 | po = np.zeros(t, dtype=np.float) # vector for portfolio values 45 | vt = np.zeros(t, dtype=np.float) # vector for option values 46 | vt[0] = V_2 # initial option value 47 | po[0] = V_2 # initial portfolio value 48 | bo = V_2 - delta[0] * S0 # initial bond position value 49 | for s in range(1, t, 1): # for all times up to i-1 50 | po[s] = delta[s - 1] * S[s, p] + bo * math.exp(r * dt) 51 | # portfolio payoff 52 | bo = po[s] - delta[s] * S[s, p] # bond position value 53 | if s == t - 1: # at exercise/expiration date 54 | vt[s] = h[s, p] # option value equals inner value 55 | pl = (po[s] - vt[s]) * math.exp(-r * t * dt) 56 | # discounted difference between option and portfolio value 57 | if run % 1000 == 0: 58 | print "run %5d p/l %8.3f" % (run, pl) 59 | pl_list.append(pl) # collect all differences 60 | pl_list = np.array(pl_list) 61 | 62 | # 63 | # Summary Results Output 64 | # 65 | print "\nSUMMARY STATISTICS FOR P&L" 66 | print "---------------------------------" 67 | print "Dynamic Replications %12d" % runs 68 | print "Time Steps %12d" % M 69 | print "Paths for Valuation %12d" % I 70 | print "Maximum %12.3f" % max(pl_list) 71 | print "Average %12.3f" % np.mean(pl_list) 72 | print "Median %12.3f" % np.median(pl_list) 73 | print "Minimum %12.3f" % min(pl_list) 74 | print "---------------------------------" 75 | 76 | return pl_list 77 | 78 | def plot_hedge_histogram(pl_list): 79 | ''' Plot of P/L histogram. ''' 80 | # 81 | # Graphical Output 82 | # 83 | plt.figure(figsize=(8, 6)) 84 | plt.grid() 85 | plt.hist(pl_list, 75) 86 | plt.xlabel('profit/loss') 87 | plt.ylabel('frequency') -------------------------------------------------------------------------------- /dawp/book/A_pyt/DAX_data.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/A_pyt/DAX_data.h5 -------------------------------------------------------------------------------- /dawp/book/A_pyt/DAX_data.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/A_pyt/DAX_data.xlsx -------------------------------------------------------------------------------- /dawp/book/A_pyt/Option_Container: -------------------------------------------------------------------------------- 1 | (i__main__ 2 | Option 3 | p1 4 | (dp2 5 | S'vola' 6 | p3 7 | F0.25 8 | sS'K' 9 | F100 10 | sS's0' 11 | p4 12 | F105 13 | sS'r' 14 | F0.050000000000000003 15 | sS'T' 16 | F1 17 | sb.(i__main__ 18 | Option_Vega 19 | p1 20 | (dp2 21 | S'vola' 22 | p3 23 | F0.25 24 | sS'K' 25 | F100 26 | sS's0' 27 | p4 28 | F105 29 | sS'r' 30 | F0.050000000000000003 31 | sS'T' 32 | F1 33 | sb. -------------------------------------------------------------------------------- /dawp/book/A_pyt/Option_Container_2: -------------------------------------------------------------------------------- 1 | ((i__main__ 2 | Option 3 | p1 4 | (dp2 5 | S'K' 6 | F100 7 | sS'r' 8 | F0.050000000000000003 9 | sS'vola' 10 | p3 11 | F0.25 12 | sS's0' 13 | p4 14 | F105 15 | sS'T' 16 | F1 17 | sb(i__main__ 18 | Option_Vega 19 | p5 20 | (dp6 21 | S'K' 22 | F100 23 | sS'r' 24 | F0.050000000000000003 25 | sg3 26 | F0.25 27 | sg4 28 | F105 29 | sS'T' 30 | F1 31 | sbt. -------------------------------------------------------------------------------- /dawp/book/A_pyt/a_first_program.py: -------------------------------------------------------------------------------- 1 | # 2 | # First Program with Python 3 | # A_pyt/a_first_program.py 4 | # 5 | import math 6 | 7 | # Variable Definition 8 | a = 3.0 9 | b = 4 10 | 11 | # Function Definition 12 | 13 | 14 | def f(x): 15 | ''' Mathematical Function. ''' 16 | return x ** 3 + x ** 2 - 2 + math.sin(x) 17 | 18 | # Calculation 19 | f_a = f(a) 20 | f_b = f(b) 21 | 22 | # Output 23 | print "f(a) = %6.3f" % f_a 24 | print "f(b) = %6.3f" % f_b 25 | -------------------------------------------------------------------------------- /dawp/book/A_pyt/b_BSM_valuation.py: -------------------------------------------------------------------------------- 1 | # 2 | # Valuation of European Call Option 3 | # in Black-Scholes-Merton Model 4 | # A_pyt/b_BSM_valuation.py 5 | # 6 | from scipy import stats 7 | import math 8 | 9 | # Option Parameters 10 | S0 = 105.00 # initial index level 11 | K = 100.00 # strike price 12 | T = 1. # call option maturity 13 | r = 0.05 # constant short rate 14 | vola = 0.25 # constant volatility factor of diffusion 15 | 16 | # Analytical Formula 17 | 18 | 19 | def BSM_call_value(S0, K, T, r, vola): 20 | ''' Analytical European call option value for Black-Scholes-Merton (1973). 21 | 22 | Parameters 23 | ========== 24 | S0: float 25 | initial index level 26 | K: float 27 | strike price 28 | T: float 29 | time-to-maturity 30 | r: float 31 | constant short rate 32 | vola: float 33 | constant volatility factor 34 | 35 | Returns 36 | ======= 37 | call_value 38 | 39 | ''' 40 | S0 = float(S0) # make sure to have float type 41 | d1 = (math.log(S0 / K) + (r + 0.5 * vola ** 2) * T) / (vola * math.sqrt(T)) 42 | d2 = d1 - vola * math.sqrt(T) 43 | call_value = (S0 * stats.norm.cdf(d1, 0.0, 1.0) 44 | - K * math.exp(-r * T) * stats.norm.cdf(d2, 0.0, 1.0)) 45 | return call_value 46 | 47 | # Output 48 | print "Value of European call option is %8.3f" \ 49 | % BSM_call_value(S0, K, T, r, vola) 50 | -------------------------------------------------------------------------------- /dawp/book/A_pyt/c_parameters.py: -------------------------------------------------------------------------------- 1 | # 2 | # Model Parameters for European Call Option 3 | # and Binomial Model 4 | # A_pyt/c_parameters.py 5 | # 6 | import math 7 | 8 | # Option Parameters 9 | S0 = 105.0 # initial index level 10 | K = 100.0 # strike price 11 | T = 1. # call option maturity 12 | r = 0.05 # constant short rate 13 | vola = 0.25 # constant volatility factor of diffusion 14 | 15 | # Time Parameters 16 | M = 1000 # time steps 17 | dt = T / M # length of time interval 18 | df = math.exp(-r * dt) # discount factor per time interval 19 | 20 | # Binomial Parameters 21 | u = math.exp(vola * math.sqrt(dt)) # up-movement 22 | d = 1 / u # down-movement 23 | q = (math.exp(r * dt) - d) / (u - d) # martingale probability 24 | -------------------------------------------------------------------------------- /dawp/book/A_pyt/d_CRR1979_loop.py: -------------------------------------------------------------------------------- 1 | # 2 | # Valuation of European Call Option in CRR1979 Model 3 | # Loop Version (= C-like Iterations) 4 | # A_pyt/d_CRR1979_loop.py 5 | # 6 | import numpy as np 7 | from c_parameters import * 8 | 9 | # Array Initialization for Index Levels 10 | S = np.zeros((M + 1, M + 1), dtype=np.float) # index level array 11 | S[0, 0] = S0 12 | z = 0 13 | for j in xrange(1, M + 1, 1): 14 | z += 1 15 | for i in xrange(z + 1): 16 | S[i, j] = S[0, 0] * (u ** j) * (d ** (i * 2)) 17 | 18 | # Array Initialization for Inner Values 19 | iv = np.zeros((M + 1, M + 1), dtype=np.float) # inner value array 20 | z = 0 21 | for j in xrange(0, M + 1, 1): 22 | for i in xrange(z + 1): 23 | iv[i, j] = round(max(S[i, j] - K, 0), 8) 24 | z += 1 25 | 26 | # Valuation by Risk-Neutral Discounting 27 | pv = np.zeros((M + 1, M + 1), dtype=np.float) # present value array 28 | pv[:, M] = iv[:, M] # initialize last time step 29 | z = M + 1 30 | for j in xrange(M - 1, -1, -1): 31 | z -= 1 32 | for i in xrange(z): 33 | pv[i, j] = (q * pv[i, j + 1] + (1 - q) * pv[i + 1, j + 1]) * df 34 | 35 | # Result Output 36 | print "Value of European call option is %8.3f" % pv[0, 0] -------------------------------------------------------------------------------- /dawp/book/A_pyt/e_CRR1979_vectorized.py: -------------------------------------------------------------------------------- 1 | # 2 | # Valuation of European Call Option in CRR1979 Model 3 | # Vectorized Version (= NumPy-level Iterations) 4 | # A_pyt/e_CRR1979_vectorized.py 5 | # 6 | import numpy as np 7 | from c_parameters import * 8 | 9 | # Array Initialization for Index Levels 10 | mu = np.arange(M + 1) 11 | mu = np.resize(mu, (M + 1, M + 1)) 12 | md = np.transpose(mu) 13 | mu = u ** (mu - md) 14 | md = d ** md 15 | S = S0 * mu * md 16 | 17 | # Valuation by Risk-Neutral Discounting 18 | pv = np.maximum(S - K, 0) # present value array initialized with inner values 19 | z = 0 20 | for i in xrange(M - 1, -1, -1): # backwards induction 21 | pv[0:M - z, i] = (q * pv[0:M - z, i + 1] 22 | + (1 - q) * pv[1:M - z + 1, i + 1]) * df 23 | z += 1 24 | 25 | # Result Output 26 | print "Value of European call option is %8.3f" % pv[0, 0] 27 | -------------------------------------------------------------------------------- /dawp/book/A_pyt/f_CRR1979_fft.py: -------------------------------------------------------------------------------- 1 | # 2 | # Valuation of European Call Option in CRR1979 Model 3 | # FFT Version 4 | # A_pyt/f_CRR1979_fft.py 5 | # 6 | import numpy as np 7 | from numpy.fft import fft, ifft 8 | from c_parameters import * 9 | 10 | # Array Generation for Index Levels 11 | md = np.arange(M + 1) 12 | mu = np.resize(md[-1], M + 1) 13 | mu = u ** (mu - md) 14 | md = d ** md 15 | S = S0 * mu * md 16 | 17 | # Valuation by FFT 18 | C_T = np.maximum(S - K, 0) 19 | Q = np.zeros(M + 1, 'd') 20 | Q[0] = q 21 | Q[1] = 1 - q 22 | l = np.sqrt(M + 1) 23 | v1 = ifft(C_T) * l 24 | v2 = (np.sqrt(M + 1) * fft(Q) / (l * (1 + r * dt))) ** M 25 | C_0 = fft(v1 * v2) / l 26 | 27 | # Result Output 28 | print "Value of European call option is %8.3f" % np.real(C_0[0]) 29 | -------------------------------------------------------------------------------- /dawp/book/A_pyt/g_MCS.py: -------------------------------------------------------------------------------- 1 | # 2 | # Valuation of European Call Option 3 | # via Monte Carlo Simulation 4 | # A_pyt/g_MCS.py 5 | # 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | from c_parameters import * 9 | 10 | # Valuation via MCS 11 | I = 100000 # number of simulated values for S_T 12 | rand = np.random.standard_normal(I) # generate pseude-random numbers 13 | ST = S0 * np.exp((r - 0.5 * vola ** 2) * T + np.sqrt(T) * vola * rand) 14 | # simulate I values for S_T 15 | pv = np.sum(np.maximum(ST - K, 0) * np.exp(-r * T)) / I # MCS estimator 16 | 17 | # Result Output 18 | print "Value of European call option is %8.3f" % pv 19 | 20 | # Graphical Output 21 | plt.figure() 22 | plt.hist(ST, 100) 23 | plt.xlabel('index level at T') 24 | plt.ylabel('frequency') 25 | plt.grid(True) -------------------------------------------------------------------------------- /dawp/book/A_pyt/h_REG.py: -------------------------------------------------------------------------------- 1 | # 2 | # Ordinary Least Squares Regression 3 | # A_pyt/h_REG.py 4 | # 5 | import numpy as np 6 | import matplotlib.pyplot as plt 7 | 8 | # Regression 9 | x = np.linspace(0.0, np.pi / 2, 20) # x values 10 | y = np.cos(x) # y values, i.e. those values to regress 11 | g1 = np.polyfit(x, y, 0) # OLS of degree 1 12 | g2 = np.polyfit(x, y, 1) # OLS of degree 2 13 | g3 = np.polyfit(x, y, 2) # OLS of degree 3 14 | 15 | g1y = np.polyval(g1, x) # calculate regressed values for x vector 16 | g2y = np.polyval(g2, x) 17 | g3y = np.polyval(g3, x) 18 | 19 | # Graphical Output 20 | plt.figure() # intialize new figure 21 | plt.plot(x, y, 'r', lw=3, label='cosine') # plot original function values 22 | plt.plot(x, g1y, 'mx', label='constant') # plot regression function values 23 | plt.plot(x, g2y, 'bo', label='linear') 24 | plt.plot(x, g3y, 'g>', label='quadratic') 25 | plt.legend(loc=0) 26 | plt.grid(True) 27 | -------------------------------------------------------------------------------- /dawp/book/A_pyt/i_SPLINE.py: -------------------------------------------------------------------------------- 1 | # 2 | # Cubic Spline Interpolation 3 | # A_pyt/i_SPLINE.py 4 | # 5 | import numpy as np 6 | import scipy.interpolate as sci 7 | import matplotlib.pyplot as plt 8 | 9 | # Interpolation 10 | x = np.linspace(0.0, np.pi / 2, 20) # x values 11 | y = np.cos(x) # function values to interpolate 12 | gp = sci.splrep(x, y, k=3) # cubic spline interpolatiln 13 | gy = sci.splev(x, gp, der=0) # calculate interpolated values 14 | 15 | # Graphical Output 16 | plt.figure() 17 | plt.plot(x, y, 'b', label='cosine') # plot original function values 18 | plt.plot(x, gy, 'ro', label='cubic splines') 19 | # plot interpolated function values 20 | plt.legend(loc=0) 21 | plt.grid(True) 22 | -------------------------------------------------------------------------------- /dawp/book/A_pyt/j_OPT.py: -------------------------------------------------------------------------------- 1 | # 2 | # Finding a Minimum of a Function 3 | # A_pyt/j_OPT.py 4 | # 5 | import numpy as np 6 | import scipy.optimize as sco 7 | 8 | # Finding a Minimum 9 | 10 | 11 | def y(x): 12 | ''' Function to Minimize. ''' 13 | if x < -np.pi or x > 0: 14 | return 0.0 15 | return np.sin(x) 16 | 17 | gmin = sco.brute(y, ((-np.pi, 0, 0.01), ), finish=None) # global optimization 18 | lmin = sco.fmin(y, -0.5) # local optimization 19 | 20 | # Result Output 21 | print "Global Minimum is %8.6f" % gmin 22 | print "Local Minimum is %8.6f" % lmin -------------------------------------------------------------------------------- /dawp/book/A_pyt/k_INT.py: -------------------------------------------------------------------------------- 1 | # 2 | # Numerically Integrate a Function 3 | # A_pyt/k_INT.py 4 | # 5 | import numpy as np 6 | from scipy.integrate import quad 7 | 8 | # Numerical Integration 9 | 10 | 11 | def f(x): 12 | ''' Function to Integrate. ''' 13 | return np.exp(x) 14 | 15 | int_value = quad(lambda u: f(u), 0, 1)[0] 16 | 17 | # Output 18 | print "Value of the integral is %10.9f" % int_value -------------------------------------------------------------------------------- /dawp/book/A_pyt/l_CLASS.py: -------------------------------------------------------------------------------- 1 | # 2 | # Two Financial Option Classes 3 | # A_pyt/l_CLASS.py 4 | # 5 | # 6 | import math 7 | import scipy.stats as scs 8 | 9 | # Class Definitions 10 | 11 | 12 | class Option: 13 | ''' Black-Scholes-Merton European call option class. 14 | 15 | Attributes 16 | ========== 17 | S0: float 18 | initial index level 19 | K: float 20 | strike price 21 | T: float 22 | time-to-maturity 23 | r: float 24 | constant short rate 25 | vola: float 26 | constant volatility factor 27 | ''' 28 | 29 | def __init__(self, S0, K, T, r, vola): 30 | ''' Initialization of Object. ''' 31 | self.S0 = float(S0) 32 | self.K = K 33 | self.T = T 34 | self.r = r 35 | self.vola = vola 36 | 37 | def d1(self): 38 | ''' Helper function. ''' 39 | d1 = ((math.log(self.S0 / self.K) + 40 | (self.r + 0.5 * self.vola ** 2) * self.T) 41 | / (self.vola * math.sqrt(self.T))) 42 | return d1 43 | 44 | def value(self): 45 | ''' Method to value option. ''' 46 | d1 = self.d1() 47 | d2 = d1 - self.vola * math.sqrt(self.T) 48 | call_value = (self.S0 * scs.norm.cdf(d1, 0.0, 1.0) 49 | - self.K * math.exp(-self.r * self.T) 50 | * scs.norm.cdf(d2, 0.0, 1.0)) 51 | return call_value 52 | 53 | 54 | class OptionVega(Option): 55 | ''' Black-Scholes-Merton class for Vega of European call option. ''' 56 | 57 | def vega(self): 58 | ''' Method to calculate the Vega of the European call option. ''' 59 | d1 = self.d1() 60 | vega = self.S0 * scs.norm.cdf(d1, 0.0, 1.0) * math.sqrt(self.T) 61 | return vega -------------------------------------------------------------------------------- /dawp/book/A_pyt/m_Excel_read.py: -------------------------------------------------------------------------------- 1 | # 2 | # Reading Data from Excel Spreadsheet Files 3 | # A_pyt/m_Excel_read.py 4 | # 5 | import pandas as pd 6 | import matplotlib.pyplot as plt 7 | 8 | # Open Excel Spreadsheet and Read Date 9 | DAX = pd.read_excel('A_pyt/DAX_data.xlsx', 'sheet1', 10 | index_col=0, parse_dates=True) 11 | 12 | # Print 10 Most Current Daily Data Sets 13 | print DAX.ix[-10:].to_string() 14 | 15 | # Plot Close Levels for Whole Data Set 16 | DAX['Close'].plot(label='DAX Index', grid=True) 17 | plt.legend(loc=0) 18 | -------------------------------------------------------------------------------- /dawp/book/A_pyt/n_pandas.py: -------------------------------------------------------------------------------- 1 | # 2 | # Retrieving Financial Data from the Web and 3 | # Doing Data Analytics with pandas 4 | # A_pyt/n_pandas.py 5 | # 6 | import math 7 | import numpy as np 8 | import pandas as pd 9 | import pandas.io.data as web 10 | import matplotlib.pyplot as plt 11 | 12 | # 13 | # 1. Data Gathering 14 | # 15 | DAX = web.DataReader('^GDAXI', data_source='yahoo', 16 | start='1/1/2005', end='28/11/2014') 17 | # reads DAX data from Yahoo Finance 18 | 19 | # 20 | # 2. Data Analysis 21 | # 22 | DAX['Returns'] = np.log(DAX['Close'] / DAX['Close'].shift(1)) 23 | # daily log returns 24 | 25 | # 26 | # 3. Generating Plots 27 | # 28 | plt.figure(figsize=(7, 5)) 29 | plt.subplot(211) 30 | DAX['Adj Close'].plot(grid=True) 31 | plt.title('DAX Index') 32 | plt.subplot(212) 33 | DAX['Returns'].plot(grid=True) 34 | plt.title('log returns') 35 | plt.tight_layout() 36 | 37 | # 38 | # 4. Numerical Methods 39 | # 40 | # Market Parameters 41 | S0 = DAX['Close'][-1] # start value of DAX for simulation 42 | vol = np.std(DAX['Returns']) * math.sqrt(252) 43 | # historical, annualized volatility of DAX 44 | r = 0.01 # constant risk-free short rate 45 | 46 | # Option Parameters 47 | K = 10000. # strike price of the option to value 48 | T = 1.0 # time-to-maturity of the option 49 | 50 | # Simulation Parameters 51 | M = 50 # number of time steps 52 | dt = T / M # length of time interval 53 | I = 10000 # number of paths to simulate 54 | np.random.seed(5000) # fixed seed value 55 | 56 | # Simulation 57 | S = np.zeros((M + 1, I), dtype=np.float) # array for simulated DAX levels 58 | S[0] = S0 # initial values 59 | for t in xrange(1, M + 1): 60 | ran = np.random.standard_normal(I) # pseudo-random numbers 61 | S[t] = S[t - 1] * np.exp((r - vol ** 2 / 2) * dt 62 | + vol * math.sqrt(dt) * ran) 63 | # difference equation to simulate DAX levels step-by-step 64 | # NumPy vectorization over all simulated paths 65 | 66 | # Valuation 67 | V0 = math.exp(-r * T) * np.sum(np.maximum(S[-1] - K, 0)) / I # MCS estimator 68 | print "MCS call value estimate is %8.3f" % V0 69 | 70 | # 71 | # 5. Data Storage 72 | # 73 | h5file = pd.HDFStore('DAX_data.h5') # open HDF5 file as database 74 | h5file['DAX'] = DAX # write pandas.DataFrame DAX into HDFStore 75 | h5file.close() # close file 76 | -------------------------------------------------------------------------------- /dawp/book/A_pyt/numpy_comp.py: -------------------------------------------------------------------------------- 1 | # 2 | # Loop Version --- Iterations in Python 3 | # 4 | # Array Initialization for Inner Values 5 | iv = np.zeros((M + 1, M + 1), dtype=np.float) 6 | z = 0 7 | for j in xrange(0, M + 1, 1): 8 | for i in xrange(z + 1): 9 | iv[i, j] = max(S[i, j] - K, 0) 10 | z += 1 11 | 12 | # 13 | # Vectorized Version --- Iterations on NumPy Level 14 | # 15 | # Array Initialization for Inner Values 16 | pv = maximum(S - K, 0) 17 | -------------------------------------------------------------------------------- /dawp/book/DAX_data.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/book/DAX_data.h5 -------------------------------------------------------------------------------- /dawp/book/option_container: -------------------------------------------------------------------------------- 1 | (i__main__ 2 | Option 3 | p1 4 | (dp2 5 | S'vola' 6 | p3 7 | F0.25 8 | sS'K' 9 | F100 10 | sS'S0' 11 | p4 12 | F105 13 | sS'r' 14 | F0.050000000000000003 15 | sS'T' 16 | F1 17 | sb.(i__main__ 18 | OptionVega 19 | p1 20 | (dp2 21 | S'vola' 22 | p3 23 | F0.25 24 | sS'K' 25 | F100 26 | sS'S0' 27 | p4 28 | F105 29 | sS'r' 30 | F0.050000000000000003 31 | sS'T' 32 | F1 33 | sb. -------------------------------------------------------------------------------- /dawp/book/option_container_2: -------------------------------------------------------------------------------- 1 | ((i__main__ 2 | Option 3 | p1 4 | (dp2 5 | S'S0' 6 | p3 7 | F105 8 | sS'K' 9 | F100 10 | sS'r' 11 | F0.050000000000000003 12 | sS'vola' 13 | p4 14 | F0.25 15 | sS'T' 16 | F1 17 | sb(i__main__ 18 | OptionVega 19 | p5 20 | (dp6 21 | g3 22 | F105 23 | sS'K' 24 | F100 25 | sS'r' 26 | F0.050000000000000003 27 | sg4 28 | F0.25 29 | sS'T' 30 | F1 31 | sbt. -------------------------------------------------------------------------------- /dawp/images/02_mbv/call_inner_values.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/02_mbv/call_inner_values.pdf -------------------------------------------------------------------------------- /dawp/images/02_mbv/call_values.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/02_mbv/call_values.pdf -------------------------------------------------------------------------------- /dawp/images/03_stf/DAX_histogram.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/03_stf/DAX_histogram.pdf -------------------------------------------------------------------------------- /dawp/images/03_stf/DAX_qqplot.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/03_stf/DAX_qqplot.pdf -------------------------------------------------------------------------------- /dawp/images/03_stf/DAX_quotes_returns.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/03_stf/DAX_quotes_returns.pdf -------------------------------------------------------------------------------- /dawp/images/03_stf/DAX_realized_volatility.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/03_stf/DAX_realized_volatility.pdf -------------------------------------------------------------------------------- /dawp/images/03_stf/DAX_rolling_stats.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/03_stf/DAX_rolling_stats.pdf -------------------------------------------------------------------------------- /dawp/images/03_stf/EBO_histogram.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/03_stf/EBO_histogram.pdf -------------------------------------------------------------------------------- /dawp/images/03_stf/EBO_qqplot.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/03_stf/EBO_qqplot.pdf -------------------------------------------------------------------------------- /dawp/images/03_stf/EBO_quotes_returns.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/03_stf/EBO_quotes_returns.pdf -------------------------------------------------------------------------------- /dawp/images/03_stf/EBO_term_structure.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/03_stf/EBO_term_structure.pdf -------------------------------------------------------------------------------- /dawp/images/03_stf/ES50_implied_volatilities.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/03_stf/ES50_implied_volatilities.pdf -------------------------------------------------------------------------------- /dawp/images/03_stf/GBM_histogram.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/03_stf/GBM_histogram.pdf -------------------------------------------------------------------------------- /dawp/images/03_stf/GBM_qqplot.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/03_stf/GBM_qqplot.pdf -------------------------------------------------------------------------------- /dawp/images/03_stf/GBM_quotes_returns.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/03_stf/GBM_quotes_returns.pdf -------------------------------------------------------------------------------- /dawp/images/03_stf/GBM_realized_volatility.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/03_stf/GBM_realized_volatility.pdf -------------------------------------------------------------------------------- /dawp/images/03_stf/GBM_rolling_stats.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/03_stf/GBM_rolling_stats.pdf -------------------------------------------------------------------------------- /dawp/images/05_com/BSM_call_values.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/05_com/BSM_call_values.pdf -------------------------------------------------------------------------------- /dawp/images/05_com/BSM_delta.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/05_com/BSM_delta.pdf -------------------------------------------------------------------------------- /dawp/images/05_com/BSM_gamma.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/05_com/BSM_gamma.pdf -------------------------------------------------------------------------------- /dawp/images/05_com/BSM_put_values.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/05_com/BSM_put_values.pdf -------------------------------------------------------------------------------- /dawp/images/05_com/BSM_rho.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/05_com/BSM_rho.pdf -------------------------------------------------------------------------------- /dawp/images/05_com/BSM_theta.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/05_com/BSM_theta.pdf -------------------------------------------------------------------------------- /dawp/images/05_com/BSM_vega.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/05_com/BSM_vega.pdf -------------------------------------------------------------------------------- /dawp/images/05_com/CRR_convergence_1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/05_com/CRR_convergence_1.pdf -------------------------------------------------------------------------------- /dawp/images/05_com/CRR_convergence_2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/05_com/CRR_convergence_2.pdf -------------------------------------------------------------------------------- /dawp/images/06_fou/BSM_val_accuracy_1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/06_fou/BSM_val_accuracy_1.pdf -------------------------------------------------------------------------------- /dawp/images/06_fou/BSM_val_accuracy_2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/06_fou/BSM_val_accuracy_2.pdf -------------------------------------------------------------------------------- /dawp/images/06_fou/Fourier_series.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/06_fou/Fourier_series.pdf -------------------------------------------------------------------------------- /dawp/images/06_fou/spokes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/06_fou/spokes.pdf -------------------------------------------------------------------------------- /dawp/images/07_amo/LSM_primal_dual_APO.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/07_amo/LSM_primal_dual_APO.pdf -------------------------------------------------------------------------------- /dawp/images/07_amo/LSM_primal_dual_SCS.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/07_amo/LSM_primal_dual_SCS.pdf -------------------------------------------------------------------------------- /dawp/images/08_m76/M76_calibration_3_0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/08_m76/M76_calibration_3_0.pdf -------------------------------------------------------------------------------- /dawp/images/08_m76/M76_calibration_3_1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/08_m76/M76_calibration_3_1.pdf -------------------------------------------------------------------------------- /dawp/images/08_m76/M76_calibration_3_2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/08_m76/M76_calibration_3_2.pdf -------------------------------------------------------------------------------- /dawp/images/08_m76/M76_calibration_single.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/08_m76/M76_calibration_single.pdf -------------------------------------------------------------------------------- /dawp/images/08_m76/M76_value_comparison.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/08_m76/M76_value_comparison.pdf -------------------------------------------------------------------------------- /dawp/images/10_mcs/CIR_paths.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/10_mcs/CIR_paths.pdf -------------------------------------------------------------------------------- /dawp/images/10_mcs/CIR_zcb_mcs_euler.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/10_mcs/CIR_zcb_mcs_euler.pdf -------------------------------------------------------------------------------- /dawp/images/10_mcs/CIR_zcb_mcs_exact.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/10_mcs/CIR_zcb_mcs_exact.pdf -------------------------------------------------------------------------------- /dawp/images/10_mcs/box_mm.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/10_mcs/box_mm.pdf -------------------------------------------------------------------------------- /dawp/images/11_cal/BCC97_calibration_iv_2014-10-17.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/11_cal/BCC97_calibration_iv_2014-10-17.pdf -------------------------------------------------------------------------------- /dawp/images/11_cal/BCC97_calibration_iv_2014-12-19.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/11_cal/BCC97_calibration_iv_2014-12-19.pdf -------------------------------------------------------------------------------- /dawp/images/11_cal/BCC97_calibration_iv_2015-03-20.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/11_cal/BCC97_calibration_iv_2015-03-20.pdf -------------------------------------------------------------------------------- /dawp/images/11_cal/BCC97_full_calibration_quotes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/11_cal/BCC97_full_calibration_quotes.pdf -------------------------------------------------------------------------------- /dawp/images/11_cal/BCC97_iv_calibration_iv_2014-10-17.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/11_cal/BCC97_iv_calibration_iv_2014-10-17.pdf -------------------------------------------------------------------------------- /dawp/images/11_cal/BCC97_iv_calibration_iv_2014-12-19.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/11_cal/BCC97_iv_calibration_iv_2014-12-19.pdf -------------------------------------------------------------------------------- /dawp/images/11_cal/BCC97_iv_calibration_iv_2015-03-20.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/11_cal/BCC97_iv_calibration_iv_2015-03-20.pdf -------------------------------------------------------------------------------- /dawp/images/11_cal/BCC97_iv_calibration_quotes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/11_cal/BCC97_iv_calibration_quotes.pdf -------------------------------------------------------------------------------- /dawp/images/11_cal/BCC97_jump_calibration_quotes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/11_cal/BCC97_jump_calibration_quotes.pdf -------------------------------------------------------------------------------- /dawp/images/11_cal/CIR_zcb_values.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/11_cal/CIR_zcb_values.pdf -------------------------------------------------------------------------------- /dawp/images/11_cal/H93_calibration_iv_2014-10-17.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/11_cal/H93_calibration_iv_2014-10-17.pdf -------------------------------------------------------------------------------- /dawp/images/11_cal/H93_calibration_iv_2014-12-19.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/11_cal/H93_calibration_iv_2014-12-19.pdf -------------------------------------------------------------------------------- /dawp/images/11_cal/H93_calibration_iv_2015-03-20.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/11_cal/H93_calibration_iv_2015-03-20.pdf -------------------------------------------------------------------------------- /dawp/images/11_cal/H93_calibration_quotes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/11_cal/H93_calibration_quotes.pdf -------------------------------------------------------------------------------- /dawp/images/11_cal/frc_calibration.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/11_cal/frc_calibration.pdf -------------------------------------------------------------------------------- /dawp/images/11_cal/term_structure.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/11_cal/term_structure.pdf -------------------------------------------------------------------------------- /dawp/images/12_val/ES50_histogram.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/12_val/ES50_histogram.pdf -------------------------------------------------------------------------------- /dawp/images/12_val/ES50_paths.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/12_val/ES50_paths.pdf -------------------------------------------------------------------------------- /dawp/images/12_val/rate_paths.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/12_val/rate_paths.pdf -------------------------------------------------------------------------------- /dawp/images/12_val/volatility_paths.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/12_val/volatility_paths.pdf -------------------------------------------------------------------------------- /dawp/images/13_dyh/BCC_hedge_histo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/13_dyh/BCC_hedge_histo.pdf -------------------------------------------------------------------------------- /dawp/images/13_dyh/BCC_hedge_run_1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/13_dyh/BCC_hedge_run_1.pdf -------------------------------------------------------------------------------- /dawp/images/13_dyh/BCC_hedge_run_2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/13_dyh/BCC_hedge_run_2.pdf -------------------------------------------------------------------------------- /dawp/images/13_dyh/BCC_hedge_run_3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/13_dyh/BCC_hedge_run_3.pdf -------------------------------------------------------------------------------- /dawp/images/13_dyh/BSM_hedge_histo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/13_dyh/BSM_hedge_histo.pdf -------------------------------------------------------------------------------- /dawp/images/A_pyt/DAX_hist_rets.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/A_pyt/DAX_hist_rets.pdf -------------------------------------------------------------------------------- /dawp/images/A_pyt/DAX_history.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/A_pyt/DAX_history.pdf -------------------------------------------------------------------------------- /dawp/images/A_pyt/dots_bars.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/A_pyt/dots_bars.pdf -------------------------------------------------------------------------------- /dawp/images/A_pyt/histogram.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/A_pyt/histogram.pdf -------------------------------------------------------------------------------- /dawp/images/A_pyt/line_plot.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/A_pyt/line_plot.pdf -------------------------------------------------------------------------------- /dawp/images/A_pyt/regression.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/A_pyt/regression.pdf -------------------------------------------------------------------------------- /dawp/images/A_pyt/splines.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yatharthn/Derivative-Analytics-with-Python-Yves-Hilpisch/6a81535d6173727362438244d91398babbc654bd/dawp/images/A_pyt/splines.pdf --------------------------------------------------------------------------------