sdevpy 0.9.0__tar.gz → 1.0.0__tar.gz

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.
Files changed (50) hide show
  1. {sdevpy-0.9.0/src/sdevpy.egg-info → sdevpy-1.0.0}/PKG-INFO +1 -1
  2. {sdevpy-0.9.0 → sdevpy-1.0.0}/pyproject.toml +2 -2
  3. sdevpy-1.0.0/src/sdevpy/__init__.py +1 -0
  4. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/analytics/black.py +46 -0
  5. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/machinelearning/datasets.py +5 -0
  6. sdevpy-1.0.0/src/sdevpy/montecarlo/smoothers.py +48 -0
  7. sdevpy-1.0.0/src/sdevpy/projects/aad/aad_mc.py +281 -0
  8. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/settings.py +6 -0
  9. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/test.py +5 -3
  10. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/volsurfacegen/mchestongenerator.py +2 -4
  11. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/volsurfacegen/mczabrgenerator.py +2 -4
  12. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/volsurfacegen/sabrgenerator.py +3 -4
  13. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/volsurfacegen/smilegenerator.py +14 -27
  14. {sdevpy-0.9.0 → sdevpy-1.0.0/src/sdevpy.egg-info}/PKG-INFO +1 -1
  15. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy.egg-info/SOURCES.txt +2 -0
  16. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy.egg-info/requires.txt +1 -0
  17. sdevpy-0.9.0/src/sdevpy/__init__.py +0 -1
  18. {sdevpy-0.9.0 → sdevpy-1.0.0}/LICENSE +0 -0
  19. {sdevpy-0.9.0 → sdevpy-1.0.0}/README.md +0 -0
  20. {sdevpy-0.9.0 → sdevpy-1.0.0}/setup.cfg +0 -0
  21. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/analytics/bachelier.py +0 -0
  22. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/analytics/fbsabr.py +0 -0
  23. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/analytics/mcheston.py +0 -0
  24. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/analytics/mcsabr.py +0 -0
  25. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/analytics/mczabr.py +0 -0
  26. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/analytics/sabr.py +0 -0
  27. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/machinelearning/callbacks.py +0 -0
  28. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/machinelearning/learningmodel.py +0 -0
  29. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/machinelearning/learningschedules.py +0 -0
  30. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/machinelearning/topology.py +0 -0
  31. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/maths/interpolations.py +0 -0
  32. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/maths/metrics.py +0 -0
  33. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/maths/optimization.py +0 -0
  34. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/maths/rand.py +0 -0
  35. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/projects/datafiles.py +0 -0
  36. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/projects/stovol/stovolgen.py +0 -0
  37. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/projects/stovol/stovolplot.py +0 -0
  38. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/projects/stovol/stovoltrain.py +0 -0
  39. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/tools/clipboard.py +0 -0
  40. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/tools/constants.py +0 -0
  41. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/tools/filemanager.py +0 -0
  42. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/tools/jsonmanager.py +0 -0
  43. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/tools/timegrids.py +0 -0
  44. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/tools/timer.py +0 -0
  45. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/volsurfacegen/fbsabrgenerator.py +0 -0
  46. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/volsurfacegen/mcsabrgenerator.py +0 -0
  47. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy/volsurfacegen/stovolfactory.py +0 -0
  48. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy.egg-info/dependency_links.txt +0 -0
  49. {sdevpy-0.9.0 → sdevpy-1.0.0}/src/sdevpy.egg-info/top_level.txt +0 -0
  50. {sdevpy-0.9.0 → sdevpy-1.0.0}/tests/test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sdevpy
3
- Version: 0.9.0
3
+ Version: 1.0.0
4
4
  Summary: Python package for Machine Learning in Finance
5
5
  Author-email: Sebastien Gurrieri <sebgur@gmail.com>
6
6
  Project-URL: Git page, https://github.com/sebgur/SDev.Python
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "sdevpy"
7
- version = "0.9.0"
7
+ version = "1.0.0"
8
8
  authors = [
9
9
  { name="Sebastien Gurrieri", email="sebgur@gmail.com" },
10
10
  ]
@@ -18,7 +18,7 @@ classifiers = [
18
18
  ]
19
19
  dependencies = [
20
20
  "pandas","pyperclip","py_vollib","numpy","tensorflow",
21
- "scikit-learn"
21
+ "scikit-learn", "tensorflow_probability"
22
22
  ]
23
23
 
24
24
  [project.urls]
@@ -0,0 +1 @@
1
+ __version__ = '1.0.0'
@@ -3,9 +3,13 @@ import numpy as np
3
3
  import scipy.stats
4
4
  from scipy.optimize import minimize_scalar
5
5
  import py_vollib.black.implied_volatility as jaeckel
6
+ import tensorflow as tf
7
+ import tensorflow_probability as tfp
8
+ from sdevpy import settings
6
9
 
7
10
  N = scipy.stats.norm.cdf
8
11
  # Ninv = scipy.stats.norm.ppf
12
+ tf_N = tfp.distributions.Normal(0.0, 1.0)
9
13
 
10
14
 
11
15
  def price(expiry, strike, is_call, fwd, vol):
@@ -40,6 +44,48 @@ def implied_vol(expiry, strike, is_call, fwd, fwd_price):
40
44
 
41
45
  return res.x
42
46
 
47
+
48
+ def price_and_greeks(expiry, strike, spot, vol, rate, div):
49
+ """ Calculate call PV and sensitivities by AAD on Black-Scholes CF """
50
+ tf_spot = tf.convert_to_tensor(spot, dtype='float32')
51
+ tf_vol = tf.convert_to_tensor(vol)
52
+ tf_time = tf.convert_to_tensor(expiry, dtype='float32')
53
+ tf_rate = tf.convert_to_tensor(rate, dtype='float32')
54
+ tf_div = tf.constant(div)
55
+
56
+ with tf.GradientTape(persistent=True) as tape:
57
+ tape.watch([tf_spot, tf_vol, tf_time, tf_rate])
58
+ with tf.GradientTape(persistent=True) as tape2nd:
59
+ tape2nd.watch([tf_spot, tf_vol])
60
+
61
+ fwd = tf_spot * tf.math.exp((tf_rate - tf_div) * tf_time)
62
+ stdev = tf_vol * tf.math.sqrt(tf_time)
63
+ d1 = tf.math.log(fwd / strike) / stdev + 0.5 * stdev
64
+ d2 = d1 - stdev
65
+ df = tf.math.exp(-tf_rate * tf_time)
66
+ pv = df * (fwd * tf_N.cdf(d1) - strike * tf_N.cdf(d2))
67
+
68
+ # Calculate delta and vega
69
+ g_delta = tape2nd.gradient(pv, tf_spot)
70
+ g_vega = tape2nd.gradient(pv, tf_vol)
71
+
72
+ delta = tape.gradient(pv, tf_spot)
73
+ gamma = tape.gradient(g_delta, tf_spot)
74
+ vega = tape.gradient(pv, tf_vol)
75
+ theta = tape.gradient(pv, tf_time)
76
+ dv01 = tape.gradient(pv, tf_rate)
77
+ volga = tape.gradient(g_vega, tf_vol)
78
+ vanna = tape.gradient(g_delta, tf_vol)
79
+
80
+ # Scale
81
+ vega = vega.numpy() * settings.VEGA_SCALING
82
+ theta = -theta.numpy() * settings.THETA_SCALING
83
+ dv01 = dv01.numpy() * settings.DV01_SCALING
84
+ volga = volga.numpy() * settings.VOLGA_SCALING
85
+ vanna = vanna.numpy() * settings.VANNA_SCALING
86
+
87
+ return [pv.numpy(), delta.numpy(), gamma.numpy(), vega, theta, dv01, volga, vanna]
88
+
43
89
  # def performance(spot_vol, repo_rate, div_rate, expiry, strike, fixings):
44
90
  # shape = spot_vol.shape
45
91
  # num_underlyings = int(shape[0] / 2)
@@ -59,6 +59,11 @@ def clip_dataframe(df, size):
59
59
  return df
60
60
  else:
61
61
  return df.iloc[range(0, size)]
62
+
63
+ def shuffle_dataframe(df, frac=1):
64
+ """ Shuffle dataframe """
65
+ df = df.sample(frac=frac)
66
+ return df
62
67
 
63
68
 
64
69
  if __name__ == "__main__":
@@ -0,0 +1,48 @@
1
+ """ Payoff smoothers for numerical methods """
2
+ import numpy as np
3
+ import tensorflow as tf
4
+ # import scipy.stats
5
+ # import tensorflow_probability as tfp
6
+
7
+
8
+ # Smoothing parameters (for the max function)
9
+ SMOOTH_VOL = 0.40
10
+ SMOOTH_TIME = 10.0 / 365.0
11
+ SMOOTH_STDEV = SMOOTH_VOL * np.sqrt(SMOOTH_TIME)
12
+
13
+
14
+ # Numpy versions (non-AAD)
15
+ # N = scipy.stats.norm
16
+
17
+ def approx_cdf(x):
18
+ """ Simple approximation of CDF """
19
+ return 1.0 / (1.0 + np.exp(-x / 0.5879))
20
+
21
+
22
+ def smooth_call(spot, strike):
23
+ """ Smoothing of call payoff """
24
+ d1 = np.log(spot / strike) / SMOOTH_STDEV + 0.5 * SMOOTH_STDEV
25
+ d2 = d1 - SMOOTH_STDEV
26
+ n1 = approx_cdf(d1)
27
+ n2 = approx_cdf(d2)
28
+ return spot * n1 - 0.5 * strike * (n1 + n2) # Average
29
+ # return spot * N.cdf(d1) - strike * N.cdf(d2) # BS, overestimates
30
+ # return (spot - strike) * N.cdf(d1) # Underestimates
31
+
32
+
33
+ # Tensorflow versions (AAD)
34
+ # tf_N = tfp.distributions.Normal(0.0, 1.0)
35
+
36
+ def tf_approx_cdf(x):
37
+ """ Simple approximation of CDF """
38
+ return 1.0 / (1.0 + tf.math.exp(-x / 0.5879))
39
+
40
+ def tf_smooth_call(spot, strike):
41
+ """ Smoothing of call payoff """
42
+ d1 = tf.math.log(spot / strike) / SMOOTH_STDEV + 0.5 * SMOOTH_STDEV
43
+ d2 = d1 - SMOOTH_STDEV
44
+ n1 = tf_approx_cdf(d1)
45
+ n2 = tf_approx_cdf(d2)
46
+ return spot * n1 - 0.5 * strike * (n1 + n2) # Average
47
+ # return spot * tf_N.cdf(d1) - strike * tf_N.cdf(d2) # BS, overestimates
48
+ # return (spot - strike) * tf_N.cdf(d1) # Underestimates
@@ -0,0 +1,281 @@
1
+ """ Calculate PV and Greeks of a call option on a single Black-Scholes underlying. We compare
2
+ standard MC with Greeks obtained by bump and reprice vs AAD MC vs Closed-Form. """
3
+ import numpy as np
4
+ import tensorflow as tf
5
+ import matplotlib.pyplot as plt
6
+ from sdevpy.montecarlo import smoothers
7
+ from sdevpy import settings
8
+ from sdevpy.analytics import black
9
+ from sdevpy.tools.timer import Stopwatch
10
+
11
+ # ################ Runtime configuration ##########################################################
12
+ # Parameters
13
+ EXPIRY = 2
14
+ SPOT = 100.0
15
+ VOL = 0.20
16
+ RATE = 0.04
17
+ DIV = 0.01
18
+ STRIKE = 100.0
19
+
20
+ # Bumps for differentials
21
+ SPOT_BUMP = 0.01 # Percentage of spot
22
+ VOL_BUMP = 0.01 # Percentage of vol
23
+ TIME_BUMP = 1.0 / 365.0
24
+ RATE_BUMP = 1.0 / 10000.0 # In bps
25
+
26
+ # Scalings
27
+ VEGA_SCL = settings.VEGA_SCALING
28
+ THETA_SCL = settings.THETA_SCALING
29
+ DV01_SCL = settings.DV01_SCALING
30
+ VOLGA_SCL = settings.VOLGA_SCALING
31
+ VANNA_SCL = settings.VANNA_SCALING
32
+
33
+ # Random generator seed
34
+ SEED = 42
35
+
36
+ # Use smoother in standard MC (always used in AAD)
37
+ USE_SMOOTHER_STD = False
38
+
39
+ # ################ Helper functions ###############################################################
40
+ def print_val(values_, is_pv_vector=False):
41
+ """ Simple screen printing of valuation results """
42
+ if is_pv_vector:
43
+ print(f"PV: {values_[0][0]:,.4f}")
44
+ else:
45
+ print(f"PV: {values_[0]:,.4f}")
46
+ print(f"Delta: {values_[1]:,.4f}")
47
+ print(f"Gamma: {values_[2]:,.4f}")
48
+ print(f"Vega: {values_[3]:,.4f}")
49
+ print(f"Theta: {values_[4]:,.4f}")
50
+ print(f"DV01: {values_[5]:,.4f}")
51
+ print(f"Volga: {values_[6]:,.4f}")
52
+ print(f"Vanna: {values_[7]:,.4f}")
53
+
54
+ # ################ Standard Monte-Carlo #########################################################
55
+ # Standard simulator
56
+ def simulate_std(spot_, vol_, time_, rate_, gaussians):
57
+ """ Standard MC simulation for PV """
58
+ # Calculate deterministic forward
59
+ fwd = spot_ * np.exp((rate_ - DIV) * time_)
60
+
61
+ # Calculate final spot paths
62
+ stdev = vol_ * np.sqrt(time_)
63
+ future_spot = fwd * np.exp(-0.5 * stdev * stdev + stdev * gaussians)
64
+
65
+ # Calculate discounted payoff
66
+ df = np.exp(-rate_ * time_)
67
+ if USE_SMOOTHER_STD:
68
+ payoff = df * smoothers.smooth_call(future_spot, STRIKE) # Use smoothing
69
+ else:
70
+ payoff = df * np.maximum(future_spot - STRIKE, 0)
71
+
72
+ # Reduce
73
+ pv = np.mean(payoff, axis=0)
74
+
75
+ return pv
76
+
77
+ # Sensitivities by bumps
78
+ def calculate_std(spot_, vol_, time_, rate_, num_mc):
79
+ """ Calculate PV and sensitivities by bumps on MC """
80
+ rng = np.random.RandomState(SEED)
81
+ # Calculate gaussians only once
82
+ gaussians = rng.normal(0.0, 1.0, (num_mc, 1))
83
+
84
+ pv = simulate_std(spot_, vol_, EXPIRY, rate_, gaussians)
85
+
86
+ # Delta-Gamma
87
+ spot_u = spot_ * (1.0 + SPOT_BUMP)
88
+ pv_spot_up = simulate_std(spot_u, vol_, time_, rate_, gaussians)
89
+
90
+ spot_d = spot_ * (1.0 - SPOT_BUMP)
91
+ pv_spot_down = simulate_std(spot_d, vol_, time_, rate_, gaussians)
92
+
93
+ # Vega-Volga
94
+ vol_u = vol_ * (1.0 + VOL_BUMP)
95
+ pv_vol_up = simulate_std(spot_, vol_u, time_, rate_, gaussians)
96
+
97
+ vol_d = vol_ * (1.0 - VOL_BUMP)
98
+ pv_vol_down = simulate_std(spot_, vol_d, time_, rate_, gaussians)
99
+
100
+ # Theta
101
+ pv_expiry_down = simulate_std(spot_, vol_, time_ - TIME_BUMP, rate_, gaussians)
102
+
103
+ # DV01
104
+ pv_rate_up = simulate_std(spot_, vol_, time_, rate_ + RATE_BUMP, gaussians)
105
+ pv_rate_down = simulate_std(spot_, vol_, time_, rate_ - RATE_BUMP, gaussians)
106
+
107
+ # Vanna
108
+ pv_uu = simulate_std(spot_u, vol_u, time_, rate_, gaussians)
109
+ pv_ud = simulate_std(spot_u, vol_d, time_, rate_, gaussians)
110
+ pv_du = simulate_std(spot_d, vol_u, time_, rate_, gaussians)
111
+ pv_dd = simulate_std(spot_d, vol_d, time_, rate_, gaussians)
112
+
113
+ # FDM
114
+ delta = (pv_spot_up - pv_spot_down) / (2.0 * SPOT_BUMP * spot_)
115
+ gamma = (pv_spot_up + pv_spot_down - 2.0 * pv) / np.power(SPOT_BUMP * spot_, 2)
116
+ vega = (pv_vol_up - pv_vol_down) / (2.0 * VOL_BUMP * vol_) * VEGA_SCL
117
+ theta = (pv_expiry_down - pv) / TIME_BUMP * THETA_SCL
118
+ dv01 = (pv_rate_up - pv_rate_down) / (2.0 * RATE_BUMP) * DV01_SCL
119
+ volga_size = np.power(VOL_BUMP * vol_, 2)
120
+ volga = (pv_vol_up + pv_vol_down - 2.0 * pv) / volga_size * VOLGA_SCL
121
+ vanna_size = 4.0 * SPOT_BUMP * spot_ * VOL_BUMP * vol_
122
+ vanna = (pv_uu - pv_ud - pv_du + pv_dd) / vanna_size * VANNA_SCL
123
+
124
+ return [pv, delta, gamma, vega, theta, dv01, volga, vanna]
125
+
126
+ # Test bumps
127
+ NUM_MC = 100 * 1000
128
+ print(f">> Running {NUM_MC:,} Standard MC Simulations")
129
+ values = calculate_std(SPOT, VOL, EXPIRY, RATE, NUM_MC)
130
+ print_val([v[0] for v in values])
131
+
132
+ # ################ AAD Monte-Carlo ###############################################################
133
+ # AAD 2nd order simulator with smoothing
134
+ def calculate_aad(spot_, vol_, time_, rate_, num_mc):
135
+ """ Calculate PV and sensitivities by AAD MC """
136
+ rng = np.random.RandomState(SEED)
137
+ gaussians = rng.normal(0.0, 1.0, (num_mc, 1))
138
+
139
+ tf_spot = tf.convert_to_tensor(spot_, dtype='float32')
140
+ tf_vol = tf.convert_to_tensor(vol_)
141
+ tf_time = tf.convert_to_tensor(time_, dtype='float32')
142
+ tf_rate = tf.convert_to_tensor(rate_, dtype='float32')
143
+ tf_div = tf.constant(DIV)
144
+
145
+ with tf.GradientTape(persistent=True) as tape:
146
+ tape.watch([tf_spot, tf_vol, tf_time, tf_rate])
147
+ with tf.GradientTape(persistent=True) as tape2nd:
148
+ tape2nd.watch([tf_spot, tf_vol])
149
+
150
+ # Calculate deterministic forward
151
+ fwd = tf_spot * tf.math.exp((tf_rate - tf_div) * tf_time)
152
+
153
+ # Calculate final spot paths
154
+ stdev = tf_vol * tf.math.sqrt(tf_time)
155
+ future_spot = fwd * tf.math.exp(-0.5 * stdev * stdev + stdev * gaussians)
156
+
157
+ # Calculate discounted payoff
158
+ df = tf.math.exp(-tf_rate * tf_time)
159
+ payoff = df * smoothers.tf_smooth_call(future_spot, STRIKE)
160
+
161
+ # Reduce
162
+ pv = tf.reduce_mean(payoff, axis=0)
163
+
164
+ # Calculate delta and vega
165
+ g_delta = tape2nd.gradient(pv, tf_spot)
166
+ g_vega = tape2nd.gradient(pv, tf_vol)
167
+
168
+ delta = tape.gradient(pv, tf_spot)
169
+ gamma = tape.gradient(g_delta, tf_spot)
170
+ vega = tape.gradient(pv, tf_vol)
171
+ theta = tape.gradient(pv, tf_time)
172
+ dv01 = tape.gradient(pv, tf_rate)
173
+ volga = tape.gradient(g_vega, tf_vol)
174
+ vanna = tape.gradient(g_delta, tf_vol)
175
+
176
+ # Scale
177
+ vega = vega.numpy() * VEGA_SCL
178
+ theta = -theta.numpy() * THETA_SCL
179
+ dv01 = dv01.numpy() * DV01_SCL
180
+ volga = volga.numpy() * VOLGA_SCL
181
+ vanna = vanna.numpy() * VANNA_SCL
182
+
183
+ return [pv.numpy(), delta.numpy(), gamma.numpy(), vega, theta, dv01, volga, vanna]
184
+
185
+ # Test AAD with smoothing
186
+ NUM_MC = 100 * 1000
187
+ print(f">> Running {NUM_MC:,} AAD MC Simulations")
188
+ values = calculate_aad(SPOT, VOL, EXPIRY, RATE, NUM_MC)
189
+
190
+ print_val(values, is_pv_vector=True)
191
+
192
+ ############# Test against CF #####################################################################
193
+ # Calculate with closed-form
194
+ print(">> Calculating closed-form (CF)")
195
+ values = black.price_and_greeks(EXPIRY, STRIKE, SPOT, VOL, RATE, DIV)
196
+ print_val(values)
197
+
198
+ ######### Numerical Tests #########################################################################
199
+ # Ladder tests
200
+ NUM_POINTS = 100 # Number of loops/points in the charts
201
+ MIN_S = 20.0
202
+ MAX_S = 200.0
203
+ NUM_MC = 10 * 1000 # Number of simulations
204
+ spots = np.linspace(MIN_S, MAX_S, NUM_POINTS, dtype='double') # spot ladder
205
+ print(f">> Calculating {NUM_POINTS:,} points with {NUM_MC:,} simulations over spot range " +
206
+ f"[{MIN_S:,}, {MAX_S:,}]")
207
+
208
+ # Vectorize for broadcasting
209
+ vols = np.ones(NUM_POINTS, dtype='float32') * VOL
210
+ times = np.ones(NUM_POINTS, dtype='float32') * EXPIRY
211
+ rates = np.ones(NUM_POINTS, dtype='float32') * RATE
212
+
213
+ # Bump method
214
+ print("> Calculating with bumps...")
215
+ print(f"Number of simulations: {NUM_MC:,}")
216
+ timer_bmp = Stopwatch("Bump-MC")
217
+ timer_bmp.trigger()
218
+ results_bmp = calculate_std(spots, vols, times, rates, NUM_MC)
219
+ timer_bmp.stop()
220
+
221
+ # AAD
222
+ print("Calculating with AAD...")
223
+ print(f"Number of simulations: {NUM_MC:,}")
224
+ timer_aad = Stopwatch("AAD-MC")
225
+ timer_aad.trigger()
226
+ results_aad = calculate_aad(spots, vols, times, rates, NUM_MC)
227
+ timer_aad.stop()
228
+
229
+ # AD closed-form
230
+ print("Calculating with closed-form...")
231
+ timer_cf = Stopwatch("CF")
232
+ timer_cf.trigger()
233
+ results_adcf = black.price_and_greeks(times, STRIKE, spots, vols, rates, DIV)
234
+ timer_cf.stop()
235
+
236
+ print("Calculation complete!")
237
+ timer_bmp.print()
238
+ timer_aad.print()
239
+ timer_cf.print()
240
+
241
+ #### Charts
242
+ results_bmp = np.array(results_bmp)
243
+ results_aad = np.array(results_aad)
244
+ results_adcf = np.array(results_adcf)
245
+
246
+ # Select viewing range
247
+ VIEW_MIN = MIN_S # Choose larger to zoom in
248
+ VIEW_MAX = MAX_S # Choose smaller to zoom in
249
+
250
+ view_range = [i for i in range(NUM_POINTS) if spots[i] >= VIEW_MIN and spots[i] <= VIEW_MAX]
251
+ start = view_range[0]
252
+ end = view_range[-1]
253
+
254
+ def plot_value(plt_idx, name, result_idx, legend_location):
255
+ """ Plot 3-way comparison between standard MC, AAD MC and closed-form """
256
+ plt.subplot(4, 2, plt_idx)
257
+ plt.title(name)
258
+ plt.xlabel('Spot')
259
+ plt.plot(spots[start:end], results_bmp[result_idx][start:end], 'green', alpha=0.7,
260
+ label='Bump-MC')
261
+ plt.plot(spots[start:end], results_adcf[result_idx][start:end], color='blue', alpha=0.8,
262
+ label='CF')
263
+ plt.plot(spots[start:end], results_aad[result_idx][start:end], color='red',
264
+ label='AAD-MC')
265
+ plt.legend(loc=legend_location)
266
+
267
+ # Plot results
268
+ plt.ioff()
269
+ plt.figure(figsize=(15, 16))
270
+ plt.subplots_adjust(hspace=0.40, wspace=0.20)
271
+
272
+ plot_value(1, "PV", 0, 'upper left')
273
+ plot_value(2, "Delta", 1, 'upper left')
274
+ plot_value(3, "Vega", 3, 'upper right')
275
+ plot_value(4, "Gamma", 2, 'upper right')
276
+ plot_value(5, "Volga", 6, 'upper left')
277
+ plot_value(6, "Vanna", 7, 'upper right')
278
+ plot_value(7, "Theta", 4, 'upper right')
279
+ plot_value(8, "DV01", 5, 'lower right')
280
+
281
+ plt.show()
@@ -4,6 +4,12 @@ import os
4
4
  # Global variables
5
5
  WORKFOLDER = r"C:\temp\sdevpy"
6
6
 
7
+ VEGA_SCALING = 0.01 # Vega shown for 1% absolute moves
8
+ THETA_SCALING = 1.0 / 365.0 # Theta shown for 1d moves
9
+ DV01_SCALING = 1.0 / 10000.0 # DV01 shown for 1bp moves
10
+ VOLGA_SCALING = VEGA_SCALING**2
11
+ VANNA_SCALING = VEGA_SCALING
12
+
7
13
  # Disable debug warnings in tensorflow
8
14
  os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
9
15
 
@@ -4,10 +4,12 @@
4
4
  # import scipy.stats as sp
5
5
  # import json
6
6
  # import matplotlib.pyplot as plt
7
- import requests, zipfile
8
- import io
7
+ # import requests, zipfile
8
+ # import io
9
9
  import pandas as pd
10
- from io import BytesIO
10
+ # from io import BytesIO
11
+
12
+
11
13
 
12
14
  # URL = 'https://drive.google.com/file/d/10dKi82fW2arlKnOahNv9i5igfiydwMnc/view?usp=sharing'
13
15
 
@@ -108,9 +108,7 @@ class McHestonGenerator(SmileGenerator):
108
108
 
109
109
  return prices
110
110
 
111
- def retrieve_datasets(self, data_file, shuffle=False):
112
- data_df = SmileGenerator.from_file(data_file, shuffle)
113
-
111
+ def retrieve_datasets_no_shuffle(self, data_df):
114
112
  # Retrieve suitable data
115
113
  t = data_df.Ttm
116
114
  strike = data_df.K
@@ -128,7 +126,7 @@ class McHestonGenerator(SmileGenerator):
128
126
  y_set = np.asarray(nvol)
129
127
  y_set = np.reshape(y_set, (num_samples, 1))
130
128
 
131
- return x_set, y_set, data_df
129
+ return x_set, y_set
132
130
 
133
131
  def price_surface_mod(self, model, expiries, strikes, are_calls, fwd, parameters):
134
132
  # Retrieve parameters
@@ -108,9 +108,7 @@ class McZabrGenerator(SmileGenerator):
108
108
 
109
109
  return prices
110
110
 
111
- def retrieve_datasets(self, data_file, shuffle=False):
112
- data_df = SmileGenerator.from_file(data_file, shuffle)
113
-
111
+ def retrieve_datasets_no_shuffle(self, data_df):
114
112
  # Retrieve suitable data
115
113
  t = data_df.Ttm
116
114
  strike = data_df.K
@@ -128,7 +126,7 @@ class McZabrGenerator(SmileGenerator):
128
126
  y_set = np.asarray(nvol)
129
127
  y_set = np.reshape(y_set, (num_samples, 1))
130
128
 
131
- return x_set, y_set, data_df
129
+ return x_set, y_set
132
130
 
133
131
  def price_surface_mod(self, model, expiries, strikes, are_calls, fwd, parameters):
134
132
  # Retrieve parameters
@@ -109,9 +109,7 @@ class SabrGenerator(SmileGenerator):
109
109
 
110
110
  return np.asarray(prices)
111
111
 
112
- def retrieve_datasets(self, data_file, shuffle=False):
113
- data_df = SmileGenerator.from_file(data_file, shuffle)
114
-
112
+ def retrieve_datasets_no_shuffle(self, data_df):
115
113
  # Retrieve suitable data
116
114
  t = data_df.Ttm
117
115
  strike = data_df.K
@@ -128,7 +126,8 @@ class SabrGenerator(SmileGenerator):
128
126
  y_set = np.asarray(nvol)
129
127
  y_set = np.reshape(y_set, (num_samples, 1))
130
128
 
131
- return x_set, y_set, data_df
129
+ return x_set, y_set
130
+
132
131
 
133
132
  def price_surface_mod(self, model, expiries, strikes, are_calls, fwd, parameters):
134
133
  # Retrieve parameters
@@ -4,14 +4,12 @@ import numpy as np
4
4
  import pandas as pd
5
5
  import scipy.stats as sp
6
6
  from sdevpy.analytics import bachelier
7
- # from sdevpy.analytics import black
7
+ from sdevpy.machinelearning import datasets
8
8
 
9
9
 
10
10
  class SmileGenerator(ABC):
11
11
  """ Base class for smile generation """
12
12
  def __init__(self, shift=0.0, num_expiries=15, num_strikes=10, seed=42):
13
- # self.num_curve_parameters = 0
14
- # self.num_vol_parameters = 0
15
13
  self.is_call = False # Use put options by default
16
14
  self.shift = shift
17
15
  self.num_strikes = num_strikes
@@ -28,13 +26,22 @@ class SmileGenerator(ABC):
28
26
  def price(self, expiries, strikes, are_calls, fwd, parameters):
29
27
  """ Calculate option price under the specified model and its parameters """
30
28
 
31
- @abstractmethod
32
29
  def retrieve_datasets(self, data_file, shuffle=False):
33
30
  """ Retrieve dataset stored in tsv file """
31
+ data_df = SmileGenerator.from_file(data_file, shuffle)
32
+ x_set, y_set = self.retrieve_datasets_from_df(data_df, False)
33
+ return x_set, y_set, data_df
34
+
35
+ def retrieve_datasets_from_df(self, data_df, shuffle=False):
36
+ """ Retrieve dataset from dataframe """
37
+ if shuffle:
38
+ data_df = datasets.shuffle_dataframe(data_df)
39
+
40
+ return self.retrieve_datasets_no_shuffle(data_df)
34
41
 
35
- # @abstractmethod
36
- # def price_surface_ref(self, expiries, strikes, are_calls, fwd, parameters):
37
- # """ Calculate a surface of prices for given parameters using the generating model """
42
+ @abstractmethod
43
+ def retrieve_datasets_no_shuffle(self, data_df):
44
+ """ Retrieve dataset from dataframe without shuffling """
38
45
 
39
46
  def price_surface_ref(self, expiries, strikes, are_calls, fwd, parameters):
40
47
  """ Calculate a surface of prices for given parameters using the generating model """
@@ -65,10 +72,6 @@ class SmileGenerator(ABC):
65
72
 
66
73
  return strikes
67
74
 
68
- # def num_parameters(self):
69
- # """ Total number of parameters (curve + vol) """
70
- # return self.num_curve_parameters + self.num_vol_parameters
71
-
72
75
  def to_nvol(self, data_df, cleanse=True, min_vol=0.0001, max_vol=0.1):
73
76
  """ Calculate normal implied vol and remove errors. Further remove points that are not
74
77
  in the given min/max range """
@@ -92,20 +95,6 @@ class SmileGenerator(ABC):
92
95
  except (Exception,):
93
96
  nvol.append(-9999)
94
97
 
95
- # Add test on Shifted BS
96
- # shift = 0.03
97
- # bsvol = []
98
- # for i in range(num_samples):
99
- # if i % num_print == 0:
100
- # print(f"Converting to lognormal vol, batch {int(i/num_print)+1:,} out of
101
- # {num_batches:,}")
102
- # try:
103
- # bsvol.append(black.implied_vol(t[i], strike[i] + shift, self.is_call,
104
- # fwd[i] + shift, price[i]))
105
- # except (Exception,):
106
- # bsvol.append(-9999)
107
-
108
-
109
98
  np.seterr(divide='warn') # Set back to warning
110
99
 
111
100
  data_df['NVol'] = nvol
@@ -115,8 +104,6 @@ class SmileGenerator(ABC):
115
104
  if cleanse:
116
105
  data_df = data_df.drop(data_df[data_df.NVol > max_vol].index)
117
106
  data_df = data_df.drop(data_df[data_df.NVol < min_vol].index)
118
- # data_df = data_df.drop(data_df[data_df.BSVol < 0.01].index)
119
- # data_df = data_df.drop(data_df[data_df.BSVol > 0.99].index)
120
107
 
121
108
  return data_df
122
109
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sdevpy
3
- Version: 0.9.0
3
+ Version: 1.0.0
4
4
  Summary: Python package for Machine Learning in Finance
5
5
  Author-email: Sebastien Gurrieri <sebgur@gmail.com>
6
6
  Project-URL: Git page, https://github.com/sebgur/SDev.Python
@@ -25,7 +25,9 @@ src/sdevpy/maths/interpolations.py
25
25
  src/sdevpy/maths/metrics.py
26
26
  src/sdevpy/maths/optimization.py
27
27
  src/sdevpy/maths/rand.py
28
+ src/sdevpy/montecarlo/smoothers.py
28
29
  src/sdevpy/projects/datafiles.py
30
+ src/sdevpy/projects/aad/aad_mc.py
29
31
  src/sdevpy/projects/stovol/stovolgen.py
30
32
  src/sdevpy/projects/stovol/stovolplot.py
31
33
  src/sdevpy/projects/stovol/stovoltrain.py
@@ -4,3 +4,4 @@ py_vollib
4
4
  numpy
5
5
  tensorflow
6
6
  scikit-learn
7
+ tensorflow_probability
@@ -1 +0,0 @@
1
- __version__ = '0.9.0'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes