├── .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 |
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 |
--------------------------------------------------------------------------------