affidiff 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
affidiff/__init__.py ADDED
@@ -0,0 +1,13 @@
1
+ from .param_generic import *
2
+ from .param_gbm import *
3
+ from .param_vasicek import *
4
+ from .param_cir import *
5
+ from .param_heston import *
6
+ from .param_ct import *
7
+
8
+ from .model_generic import *
9
+ from .model_gbm import *
10
+ from .model_vasicek import *
11
+ from .model_cir import *
12
+ from .model_heston import *
13
+ from .model_ct import *
@@ -0,0 +1,395 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Helper functions
5
+
6
+ """
7
+ from __future__ import print_function, division
8
+
9
+ import time
10
+ import contextlib
11
+
12
+ import numpy as np
13
+ import matplotlib.pylab as plt
14
+ import seaborn as sns
15
+ import itertools as it
16
+
17
+ from statsmodels.tsa.tsatools import lagmat
18
+
19
+ __all__ = ['nice_errors', 'ajd_drift', 'ajd_diff',
20
+ 'plot_trajectories', 'plot_final_distr', 'plot_realized',
21
+ 'columnwise_prod', 'rolling_window', 'poly_coef', 'instruments']
22
+
23
+
24
+ def ajd_drift(state, theta):
25
+ """Instantaneous mean.
26
+
27
+ Parameters
28
+ ----------
29
+ state : (nsim, nvars) array_like
30
+ Current value of the process
31
+ theta : parameter instance
32
+ Model parameter
33
+
34
+ Returns
35
+ -------
36
+ (nsim, nvars) array_like
37
+ Value of the drift
38
+
39
+ """
40
+ state = np.atleast_2d(state)
41
+ return theta.mat_k0 + state.dot(np.transpose(theta.mat_k1))
42
+
43
+
44
+ def ajd_diff(state, theta):
45
+ """Instantaneous volatility.
46
+
47
+ Parameters
48
+ ----------
49
+ state : (nsim, nvars) array_like
50
+ Current value of the process
51
+ theta : parameter instance
52
+ Model parameter
53
+
54
+ Returns
55
+ -------
56
+ (nsim, nvars, nvars) array_like
57
+ Value of the diffusion
58
+
59
+ """
60
+ state = np.atleast_2d(state)
61
+ mat_h1 = np.atleast_3d(theta.mat_h1)
62
+ # (nsim, nvars, nvars)
63
+ var = theta.mat_h0 + np.tensordot(state, mat_h1, axes=(1, 0))
64
+ try:
65
+ return np.linalg.cholesky(var)
66
+ except(np.linalg.LinAlgError):
67
+ return np.ones_like(var) * 1e10
68
+
69
+
70
+ def nice_errors(errors, sdim):
71
+ """Normalize the errors and apply antithetic sampling.
72
+
73
+ Parameters
74
+ ----------
75
+ errors : array
76
+ Innovations to be standardized
77
+ sdim : int
78
+ Which dimension corresponds to simulation instances?
79
+
80
+ Returns
81
+ -------
82
+ errors : array
83
+ Standardized innovations
84
+
85
+ """
86
+ if errors.shape[sdim] > 10:
87
+ errors -= errors.mean(sdim, keepdims=True)
88
+ errors /= errors.std(sdim, keepdims=True)
89
+ return np.concatenate((errors, -errors), axis=sdim)
90
+
91
+
92
+ def plot_trajectories(paths, nsub, names):
93
+ """Plot process realizations.
94
+
95
+ Parameters
96
+ ----------
97
+ paths : array
98
+ Process realizations. Shape is either (nobs,) or (nobs, nsim)
99
+ nsub : int
100
+ Number of subintervals inside of unit interval
101
+ names : str or list of strings
102
+ Labels
103
+
104
+ """
105
+ if isinstance(paths, list):
106
+ for path, name in zip(paths, names):
107
+ x = np.arange(0, path.shape[0] / nsub, 1 / nsub)
108
+ plt.plot(x, path, label=name)
109
+ else:
110
+ x = np.arange(0, paths.shape[0] / nsub, 1 / nsub)
111
+ plt.plot(x, paths, label=names)
112
+
113
+ plt.xlabel('$t$')
114
+ plt.ylabel('$x_t$')
115
+ plt.legend()
116
+ plt.show()
117
+
118
+
119
+ def plot_final_distr(paths, names):
120
+ """Plot marginal distribution of the process.
121
+
122
+ Parameters
123
+ ----------
124
+ paths : array
125
+ Process realizations. Shape is (nobs, nsim)
126
+ names : str or list of strings
127
+ Labels
128
+
129
+ """
130
+ if isinstance(paths, list):
131
+ for path, name in zip(paths, names):
132
+ if path.ndim != 2:
133
+ raise ValueError('Simulate more paths!')
134
+ sns.kdeplot(path[-1], label=name)
135
+ else:
136
+ sns.kdeplot(paths[-1], label=names)
137
+
138
+ plt.xlabel('x')
139
+ plt.legend()
140
+ plt.show()
141
+
142
+
143
+ def plot_realized(returns, rvar, suffix=None):
144
+ """Plot realized returns and volatility.
145
+
146
+ Parameters
147
+ ----------
148
+ returns : array
149
+ Returns
150
+ rvar : array
151
+ Realized variance
152
+
153
+ """
154
+ fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(7, 6))
155
+ if isinstance(returns, list):
156
+ returns = np.vstack(returns).T
157
+ rlabel = ['Returns ' + x for x in suffix]
158
+ else:
159
+ rlabel = 'Returns'
160
+ if isinstance(rvar, list):
161
+ rvar = np.vstack(rvar).T
162
+ vlabel = ['Realized volatility ' + x for x in suffix]
163
+ else:
164
+ vlabel = 'Realized volatility'
165
+ axes[0].plot(returns)
166
+ axes[1].plot(rvar**.5)
167
+ axes[0].legend(rlabel)
168
+ axes[1].legend(vlabel)
169
+ plt.show()
170
+
171
+
172
+ def columnwise_prod(left, right):
173
+ """Columnwise kronker product.
174
+
175
+ Parameters
176
+ ----------
177
+ left : (n, m) array
178
+ right : (n, p) array
179
+
180
+ Returns
181
+ -------
182
+ (n, m*p) array
183
+ [left * right[:, 0], ..., left * right[:, -1]]
184
+
185
+ Example
186
+ -------
187
+ .. doctest::
188
+
189
+ >>> left = np.arange(6).reshape((3,2))
190
+ >>> left
191
+ array([[0, 1],
192
+ [2, 3],
193
+ [4, 5]])
194
+ >>> right = np.arange(9).reshape((3,3))
195
+ >>> right
196
+ array([[0, 1, 2],
197
+ [3, 4, 5],
198
+ [6, 7, 8]])
199
+ >>> columnwise_prod(left, right)
200
+ array([[ 0, 0, 0, 1, 0, 2],
201
+ [ 6, 9, 8, 12, 10, 15],
202
+ [24, 30, 28, 35, 32, 40]])
203
+
204
+ """
205
+ prod = left[:, np.newaxis, :] * right[:, :, np.newaxis]
206
+ return prod.reshape((left.shape[0], left.shape[1] * right.shape[1]))
207
+
208
+
209
+ def rolling_window(fun, mat, window=1):
210
+ """Rolling window apply.
211
+
212
+ Source: http://www.rigtorp.se/2011/01/01/rolling-statistics-numpy.html
213
+
214
+ Parameters
215
+ ----------
216
+ fun : function
217
+ Function to apply
218
+ mat : array_like
219
+ Data to transform
220
+ window : int
221
+ Window size
222
+ axis : int
223
+ Which axis to apply to
224
+
225
+ Returns
226
+ -------
227
+ array
228
+
229
+ Examples
230
+ --------
231
+ .. doctest::
232
+
233
+ >>> mat = np.arange(10).reshape((2,5))
234
+ >>> mat
235
+ array([[0, 1, 2, 3, 4],
236
+ [5, 6, 7, 8, 9]])
237
+ >>> rolling_window(np.mean, mat, window=2)
238
+ array([[ 0.5, 1.5, 2.5, 3.5],
239
+ [ 5.5, 6.5, 7.5, 8.5]])
240
+
241
+ """
242
+ shape = mat.shape[:-1] + (mat.shape[-1] - window + 1, window)
243
+ strides = mat.strides + (mat.strides[-1],)
244
+ mat = np.lib.stride_tricks.as_strided(mat, shape=shape, strides=strides)
245
+ return np.apply_along_axis(fun, -1, mat)
246
+
247
+
248
+ def poly_coef(roots):
249
+ """Ploynomial coefficients.
250
+
251
+ Parameters
252
+ ----------
253
+ roots : list of floats
254
+ Roots of the polynomial, i.e. [a0, a1] for (1 - a0 * x) * (1 - a1 * x)
255
+
256
+ Returns
257
+ -------
258
+ coefs : list of floats
259
+ List of all polynomial coefficients, i.e. [p0, p1, p2]
260
+ with p0 = 1, p1 = - a0 - a1, p2 = a0*a1
261
+
262
+ Examples
263
+ --------
264
+ .. doctest::
265
+
266
+ >>> poly_coef([2, 3])
267
+ [1, -5, 6]
268
+
269
+ >>> poly_coef([2, 3, 4])
270
+ [1, -9, 26, -24]
271
+
272
+ """
273
+ roots = np.array(roots)
274
+ nroots = roots.size
275
+ coefs = [1]
276
+ for power in range(nroots):
277
+ comb = it.combinations(range(nroots), power+1)
278
+ temp = [np.prod(roots[[x]]) for x in comb]
279
+ coefs.append((-1)**(power+1) * np.sum(temp))
280
+ return coefs
281
+
282
+
283
+ def instruments(data=None, instrlag=1, nobs=None, instr_choice='const'):
284
+ """Create an array of instruments.
285
+
286
+ Parameters
287
+ ----------
288
+ data : (ninstr, nobs) array
289
+ Returns and realized variance
290
+ instrlag : int
291
+ Number of lags for the instruments
292
+ nobs : int
293
+ Number of observations in the data to match
294
+ instr_choice : str {'const', 'var'}
295
+ Choice of instruments.
296
+ - 'const' : just a constant (unconditional moments)
297
+ - 'var' : lags of instrument data
298
+
299
+ Returns
300
+ -------
301
+ (nobs, ninstr*instrlag + 1) array
302
+ Instrument array
303
+
304
+ Examples
305
+ --------
306
+ .. doctest::
307
+
308
+ >>> instruments(nobs=3)
309
+ array([[ 1.],
310
+ [ 1.],
311
+ [ 1.]])
312
+
313
+ >>> data = np.arange(6).reshape((2,3))
314
+ >>> instruments(data=data)
315
+ array([[ 1.],
316
+ [ 1.],
317
+ [ 1.]])
318
+ >>> instruments(data=data, instr_choice='var')
319
+ array([[ 1., 0., 0.],
320
+ [ 1., 0., 3.],
321
+ [ 1., 1., 4.]])
322
+ >>> instruments(data=data, instr_choice='var', instrlag=2)
323
+ array([[ 1., 0., 0., 0., 0.],
324
+ [ 1., 0., 3., 0., 0.],
325
+ [ 1., 1., 4., 0., 3.]])
326
+
327
+ """
328
+ if data is not None:
329
+ nobs = data.shape[-1]
330
+
331
+ if instr_choice == 'const' or data is None:
332
+ if nobs is None:
333
+ raise ValueError('Specify nobs!')
334
+ return np.ones((nobs, 1))
335
+
336
+ else:
337
+ instr = lagmat(np.atleast_2d(data).T, maxlag=instrlag)
338
+ width = ((0, 0), (1, 0))
339
+ return np.pad(instr, width, mode='constant', constant_values=1)
340
+
341
+
342
+ def format_time(t):
343
+ """Format time for nice printing.
344
+
345
+ Parameters
346
+ ----------
347
+ t : float
348
+ Time in seconds
349
+
350
+ Returns
351
+ -------
352
+ format template
353
+
354
+ """
355
+ if t > 60 or t == 0:
356
+ units = 'min'
357
+ t /= 60
358
+ elif t > 1:
359
+ units = 's'
360
+ elif t > 1e-3:
361
+ units = 'ms'
362
+ t *= 1e3
363
+ elif t > 1e-6:
364
+ units = 'us'
365
+ t *= 1e6
366
+ else:
367
+ units = 'ns'
368
+ t *= 1e9
369
+ return '%.1f %s' % (t, units)
370
+
371
+
372
+ @contextlib.contextmanager
373
+ def take_time(desc):
374
+ """Context manager for timing the code.
375
+
376
+ Parameters
377
+ ----------
378
+ desc : str
379
+ Description of the code
380
+
381
+ Example
382
+ -------
383
+ >>> with take_time('Estimation'):
384
+ >>> estimate()
385
+
386
+ """
387
+ t0 = time.time()
388
+ yield
389
+ dt = time.time() - t0
390
+ print('%s took %s' % (desc, format_time(dt)))
391
+
392
+
393
+ if __name__ == "__main__":
394
+ import doctest
395
+ doctest.testmod()
affidiff/model_cir.py ADDED
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ CIR model class
5
+ ~~~~~~~~~~~~~~~
6
+
7
+ """
8
+ from __future__ import print_function, division
9
+
10
+ from .model_generic import SDE
11
+
12
+ __all__ = ['CIR']
13
+
14
+
15
+ class CIR(SDE):
16
+
17
+ """Cox-Ingersoll-Ross (CIR) model.
18
+
19
+ """
20
+
21
+ def __init__(self, param=None):
22
+ """Initialize the class.
23
+
24
+ Parameters
25
+ ----------
26
+ param : parameter instance
27
+ True parameters used for simulation of the data
28
+
29
+ """
30
+ super(CIR, self).__init__(param)
31
+
32
+ @staticmethod
33
+ def drift(state, theta):
34
+ """Drift function.
35
+
36
+ Parameters
37
+ ----------
38
+ state : (nvars, nsim) array_like
39
+ Current value of the process
40
+ theta : parameter instance
41
+ Model parameter
42
+
43
+ Returns
44
+ -------
45
+ scalar
46
+ Drift value
47
+
48
+ """
49
+ return theta.kappa * (theta.mean - state)
50
+
51
+ @staticmethod
52
+ def diff(state, theta):
53
+ """Diffusion (instantaneous volatility) function.
54
+
55
+ Parameters
56
+ ----------
57
+ state : (nvars, nsim) array_like
58
+ Current value of the process
59
+ theta : parameter instance
60
+ Model parameter
61
+
62
+ Returns
63
+ -------
64
+ scalar
65
+ Diffusion value
66
+
67
+ """
68
+ return theta.eta * state**.5