├── .gitattributes ├── README.md ├── handbook ├── 01-merton-jdm.ipynb └── img │ ├── merton-jdm.png │ └── sample-path-jdm.png └── python-modules └── jump_diffusion.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Prevent Jupyter Notebook from showing up as main repository language 2 | *.ipynb linguist-vendored 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Financial Engineering 2 | Python projects in financial engineering. 3 | 4 | ## [Merton's Jump Diffusion Model](https://nbviewer.jupyter.org/github/federicomariamassari/financial-engineering/blob/master/handbook/01-merton-jdm.ipynb) (1976) 5 | This is an application of **Monte Carlo methods** [1] to the pricing of options on stocks when the underlying asset has occasional jumps in the trajectories. Merton [2] describes such jumps as _"idiosynchratic shocks affecting an individual company but not the market as a whole"_. The jump component makes the distribution of prices _leptokurtic_ (high peak, heavy tails), a feature typical of market data. 6 | 7 | The model builds on the _standard Brownian motion_, which can also be generated using the [willow tree](https://github.com/federicomariamassari/willow-tree). 8 | 9 | [Link to Python module](/python-modules/jump_diffusion.py) 10 | 11 | merton-jump-diffusion-model 12 | 13 | [1] Glasserman, P. (2003) _Monte Carlo Methods in Financial Engineering_, Springer Applications of Mathematics, Vol. 53 14 | 15 | [2] Merton, R.C. (1976) _Option pricing when underlying stock returns are discontinuous_, Journal of Financial Economics, 3:125-144 16 | 17 | ## Dependencies 18 | `financial-engineering` requires Python 3.5+, and is built on top of the following libraries: 19 | - **NumPy**: v. 1.13+ 20 | - **SciPy**: v. 0.19+ 21 | - **Matplotlib**: v. 2.0+ 22 | - **Seaborn**: v. 0.8+ 23 | 24 | ## Installation 25 | The source code is currently hosted on GitHub at: https://github.com/federicomariamassari/financial-engineering. 26 | Either clone or download the git repository. To clone the repository, on either Terminal (macOS) or Command Prompt (Windows) enter the folder inside which you want the repository to be, possibly changing directory with `cd `, and execute: 27 | ```shell 28 | $ git clone https://github.com/federicomariamassari/financial-engineering.git 29 | ``` 30 | 31 | ## Contributing 32 | This is a small but continuously evolving project open to anyone willing to contribute—simply fork the repository and modify its content. Any improvement, in terms of code speed and readability, or inclusion of new models (such as those from Glasserman's book), is more than welcome. For git commits, it is desirable to follow [Udacity's Git Commit Message Style Guide](https://udacity.github.io/git-styleguide/). 33 | 34 | Feel free to bookmark, or "star", the repository if you find this project interesting. Thank you for your support! 35 | -------------------------------------------------------------------------------- /handbook/img/merton-jdm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicomariamassari/financial-engineering/a62c130f6050426b27c9f4b1d020473140e6d51b/handbook/img/merton-jdm.png -------------------------------------------------------------------------------- /handbook/img/sample-path-jdm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicomariamassari/financial-engineering/a62c130f6050426b27c9f4b1d020473140e6d51b/handbook/img/sample-path-jdm.png -------------------------------------------------------------------------------- /python-modules/jump_diffusion.py: -------------------------------------------------------------------------------- 1 | def jump_diffusion(S=1, X=0.5, T=1, mu=0.12, sigma=0.3, Lambda=0.25, 2 | a=0.2, b=0.2, Nsteps=252, Nsim=100, alpha=0.05, seed=None): 3 | ''' 4 | Monte Carlo simulation [1] of Merton's Jump Diffusion Model [2]. 5 | The model is specified through the stochastic differential equation (SDE): 6 | 7 | dS(t) 8 | ----- = mu*dt + sigma*dW(t) + dJ(t) 9 | S(t-) 10 | 11 | with: 12 | 13 | mu, sigma: constants, the drift and volatility coefficients of the stock 14 | price process; 15 | W: a standard one-dimensional Brownian motion; 16 | J: a jump process, independent of W, with piecewise constant sample paths. 17 | It is defined as the sum of multiplicative jumps Y(j). 18 | 19 | Input 20 | --------------------------------------------------------------------------- 21 | S: float. The current asset price. 22 | X: float. The strike price, i.e. the price at which the asset may be bought 23 | (call) or sold (put) in an option contract [3]. 24 | T: int or float. The maturity of the option contract, i.e. the final 25 | monitoring date. 26 | mu, sigma: float. Respectively, the drift and volatility coefficients of 27 | the asset price process. 28 | Lambda: float. The intensity of the Poisson process in the jump diffusion 29 | model ('lambda' is a protected keyword in Python). 30 | a, b: float. Parameters required to calculate, respectively, the mean and 31 | variance of a standard lognormal distribution, log(x) ~ N(a, b**2). 32 | (see code). 33 | Nsteps: int. The number of monitoring dates, i.e. the time steps. 34 | Nsim: int. The number of Monte Carlo simulations (at least 10,000 required 35 | to generate stable results). 36 | alpha: float. The confidence interval significance level, in [0, 1]. 37 | seed: int. Set random seed, for reproducibility of the results. Default 38 | value is None (the best seed available is used, but outcome will vary 39 | in each experiment). 40 | 41 | References 42 | --------------------------------------------------------------------------- 43 | [1] Glasserman, P. (2003): 'Monte Carlo Methods in Financial Engineering', 44 | Springer Applications of Mathematics, Vol. 53 45 | [2] Merton, R.C. (1976): 'Option Pricing when Underlying Stock Returns are 46 | Discontinuous', Journal of Financial Economics, 3:125-144. 47 | [3] Hull, J.C. (2017): 'Options, Futures, and Other Derivatives', 10th 48 | Edition, Pearson. 49 | ''' 50 | 51 | # Import required libraries 52 | import time 53 | import numpy as np 54 | from scipy import stats 55 | import matplotlib.pyplot as plt 56 | import seaborn as sns 57 | 58 | # Set random seed 59 | np.random.seed(seed) 60 | 61 | ''' 62 | Time the whole path-generating process, using a tic-toc method familiar 63 | to MATLAB users 64 | ''' 65 | tic = time.time() 66 | 67 | # Calculate the length of the time step 68 | Delta_t = T/Nsteps 69 | 70 | ''' 71 | Compute mean and variance of a standard lognormal distribution from user 72 | defined parameters a and b. The latter are useful to simulate the jump 73 | component in Monte Carlo. 74 | a and b are chosen such that log(Y(j)) ~ N(a, b**2). This implies that the 75 | mean and variance of the multiplicative jumps will be: 76 | 77 | * mean_Y = np.exp(a + 0.5*(b**2)) 78 | * variance_Y = np.exp(2*a + b**2) * (np.exp(b**2)-1) 79 | 80 | ''' 81 | mean_Y = np.exp(a + 0.5*(b**2)) 82 | variance_Y = np.exp(2*a + b**2) * (np.exp(b**2)-1) 83 | 84 | ''' 85 | Calculate the theoretical drift (M) and volatility (V) of the stock price 86 | process under Merton's jump diffusion model. These values can be used to 87 | monitor the rate of convergence of Monte Carlo estimates as the number of 88 | simulated experiments increases, and can help spot errors, if any, in 89 | implementing the model. 90 | ''' 91 | M = S * np.exp(mu*T + Lambda*T*(mean_Y-1)) 92 | V = S**2 * (np.exp((2*mu + sigma**2)*T \ 93 | + Lambda*T*(variance_Y + mean_Y**2 - 1)) \ 94 | - np.exp(2*mu*T + 2*Lambda*T*(mean_Y - 1))) 95 | 96 | ''' 97 | Generate an Nsim x (Nsteps+1) array of zeros to preallocate the simulated 98 | paths of the Monte Carlo simulation. Each row of the matrix represents a 99 | full, possible path for the stock, each column all values of the asset at 100 | a particular instant in time. 101 | ''' 102 | simulated_paths = np.zeros([Nsim, Nsteps+1]) 103 | 104 | # Replace the first column of the array with the vector of initial price S 105 | simulated_paths[:,0] = S 106 | 107 | ''' 108 | To account for the multiple sources of uncertainty in the jump diffusion 109 | process, generate three arrays of random variables. 110 | 111 | - The first one is related to the standard Brownian motion, the component 112 | epsilon(0,1) in epsilon(0,1) * np.sqrt(dt); 113 | - The second and third ones model the jump, a compound Poisson process: 114 | the former (a Poisson process with intensity Lambda) causes the asset 115 | price to jump randomly (random timing); the latter (a Gaussian variable) 116 | defines both the direction (sign) and intensity (magnitude) of the jump. 117 | ''' 118 | Z_1 = np.random.normal(size=[Nsim, Nsteps]) 119 | Z_2 = np.random.normal(size=[Nsim, Nsteps]) 120 | Poisson = np.random.poisson(Lambda*Delta_t, [Nsim, Nsteps]) 121 | 122 | # Populate the matrix with Nsim randomly generated paths of length Nsteps 123 | for i in range(Nsteps): 124 | simulated_paths[:,i+1] = simulated_paths[:,i]*np.exp((mu 125 | - sigma**2/2)*Delta_t + sigma*np.sqrt(Delta_t) \ 126 | * Z_1[:,i] + a*Poisson[:,i] \ 127 | + np.sqrt(b**2) * np.sqrt(Poisson[:,i]) \ 128 | * Z_2[:,i]) 129 | 130 | # Single out array of simulated prices at maturity T 131 | final_prices = simulated_paths[:,-1] 132 | 133 | # Compute mean, variance, standard deviation, skewness, excess kurtosis 134 | mean_jump = np.mean(final_prices) 135 | var_jump = np.var(final_prices) 136 | std_jump = np.std(final_prices) 137 | skew_jump = stats.skew(final_prices) 138 | kurt_jump = stats.kurtosis(final_prices) 139 | 140 | # Calculate confidence interval for the mean 141 | ci_low = mean_jump - std_jump/np.sqrt(Nsim)*stats.norm.ppf(1-0.5*alpha) 142 | ci_high = mean_jump + std_jump/np.sqrt(Nsim)*stats.norm.ppf(1-0.5*alpha) 143 | 144 | # Print statistics, align results 145 | print("Merton's Jump Diffusion Model") 146 | print('-----------------------------') 147 | print('Theoretical Moments') 148 | print('-----------------------------') 149 | print('Mean (M){:>21.4f}'.format(M)) 150 | print('Variance (V){:>17.4f}'.format(V)) 151 | print('\nMonte Carlo Estimates') 152 | print('-----------------------------') 153 | print('Mean {:>24.4f}'.format(mean_jump)) 154 | print('Variance {:>20.4f}'.format(var_jump)) 155 | print('Standard deviation {:>10.4f}'.format(std_jump)) 156 | print('Skewness {:>20.4f}'.format(skew_jump)) 157 | print('Excess kurtosis {:>13.4f}'.format(kurt_jump)) 158 | print('\nConfidence interval, Mean') 159 | print('-----------------------------') 160 | print('Alpha {:>23.2f}'.format(alpha)) 161 | print('Lower bound {:>17.4f}'.format(ci_low)) 162 | print('Upper bound {:>17.4f}'.format(ci_high)) 163 | 164 | # Choose palette, figure size, and define figure axes 165 | sns.set(palette='viridis') 166 | plt.figure(figsize=(10,8)) 167 | ax = plt.axes() 168 | 169 | # Generate t, the time variable on the abscissae 170 | t = np.linspace(0, T, Nsteps+1) * Nsteps 171 | 172 | # Plot the Monte Carlo simulated stock price paths 173 | jump_diffusion = ax.plot(t, simulated_paths.transpose()); 174 | 175 | # Make drawn paths thinner by decreasing line width 176 | plt.setp(jump_diffusion, linewidth=1); 177 | 178 | # Set title (LaTeX notation) and x- and y- labels 179 | ax.set(title="Monte Carlo simulated stock price paths in Merton's jump \ 180 | diffusion model\n$S_0$ = {}, $\mu$ = {}, $\sigma$ = {}, $a$ = {}, $b$ = {}, \ 181 | $\lambda$ = {}, $T$ = {}, Nsteps = {}, Nsim = {}"\ 182 | .format(S, mu, sigma, a, b, Lambda, T, Nsteps, Nsim), \ 183 | xlabel='Time (days)', ylabel='Stock price') 184 | 185 | # Display figure in a Python environment 186 | plt.show() 187 | 188 | # Time and print the elapsed time 189 | toc = time.time() 190 | elapsed_time = toc - tic 191 | print('Total running time: {:.2f} ms'.format(elapsed_time*1000)) 192 | --------------------------------------------------------------------------------