sdevpy 1.0.3__tar.gz → 1.0.4__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 (101) hide show
  1. {sdevpy-1.0.3/src/sdevpy.egg-info → sdevpy-1.0.4}/PKG-INFO +1 -2
  2. {sdevpy-1.0.3 → sdevpy-1.0.4}/pyproject.toml +2 -2
  3. sdevpy-1.0.4/src/sdevpy/__init__.py +1 -0
  4. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/analytics/bachelier.py +15 -0
  5. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/analytics/black.py +2 -1
  6. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/maths/optimization.py +8 -4
  7. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/projects/stovol/stovolgen.py +3 -3
  8. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/projects/stovol/stovolplot.py +4 -3
  9. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/projects/stovol/stovoltrain.py +9 -8
  10. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/projects/stovolinverse/stovolinvgen.py +10 -4
  11. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/projects/stovolinverse/stovolinvtrain.py +33 -28
  12. sdevpy-1.0.4/src/sdevpy/thirdparty/py_lets_be_rational/__init__.py +48 -0
  13. sdevpy-1.0.4/src/sdevpy/thirdparty/py_lets_be_rational/constants.py +64 -0
  14. sdevpy-1.0.4/src/sdevpy/thirdparty/py_lets_be_rational/erf_cody.py +447 -0
  15. sdevpy-1.0.4/src/sdevpy/thirdparty/py_lets_be_rational/exceptions.py +67 -0
  16. sdevpy-1.0.4/src/sdevpy/thirdparty/py_lets_be_rational/lets_be_rational.py +800 -0
  17. sdevpy-1.0.4/src/sdevpy/thirdparty/py_lets_be_rational/normaldistribution.py +193 -0
  18. sdevpy-1.0.4/src/sdevpy/thirdparty/py_lets_be_rational/numba_helper.py +13 -0
  19. sdevpy-1.0.4/src/sdevpy/thirdparty/py_lets_be_rational/rationalcubic.py +271 -0
  20. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/__init__.py +31 -0
  21. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black/__init__.py +179 -0
  22. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black/greeks/__init__.py +0 -0
  23. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black/greeks/analytical.py +272 -0
  24. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black/greeks/numerical.py +250 -0
  25. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black/implied_volatility.py +291 -0
  26. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black_scholes/__init__.py +83 -0
  27. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black_scholes/greeks/__init__.py +0 -0
  28. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black_scholes/greeks/analytical.py +277 -0
  29. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black_scholes/greeks/numerical.py +317 -0
  30. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black_scholes/implied_volatility.py +101 -0
  31. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black_scholes_merton/__init__.py +89 -0
  32. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/__init__.py +0 -0
  33. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/analytical.py +308 -0
  34. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/numerical.py +265 -0
  35. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black_scholes_merton/implied_volatility.py +115 -0
  36. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/helpers/__init__.py +113 -0
  37. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/helpers/constants.py +51 -0
  38. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/helpers/distributions.py +225 -0
  39. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/helpers/doctest_helper.py +61 -0
  40. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/helpers/exceptions.py +58 -0
  41. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/helpers/numerical_greeks.py +216 -0
  42. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/__init__.py +31 -0
  43. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black/__init__.py +235 -0
  44. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/__init__.py +0 -0
  45. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/analytical.py +276 -0
  46. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/numerical.py +221 -0
  47. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black/implied_volatility.py +116 -0
  48. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/__init__.py +156 -0
  49. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/__init__.py +0 -0
  50. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/analytical.py +278 -0
  51. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/numerical.py +289 -0
  52. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/implied_volatility.py +109 -0
  53. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/__init__.py +230 -0
  54. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/__init__.py +0 -0
  55. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/analytical.py +310 -0
  56. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/numerical.py +222 -0
  57. sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/implied_volatility.py +103 -0
  58. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/tools/filemanager.py +1 -2
  59. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/volsurfacegen/sabrgenerator.py +28 -18
  60. {sdevpy-1.0.3 → sdevpy-1.0.4/src/sdevpy.egg-info}/PKG-INFO +1 -2
  61. sdevpy-1.0.4/src/sdevpy.egg-info/SOURCES.txt +97 -0
  62. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy.egg-info/requires.txt +0 -1
  63. sdevpy-1.0.3/src/sdevpy/__init__.py +0 -1
  64. sdevpy-1.0.3/src/sdevpy.egg-info/SOURCES.txt +0 -51
  65. {sdevpy-1.0.3 → sdevpy-1.0.4}/LICENSE +0 -0
  66. {sdevpy-1.0.3 → sdevpy-1.0.4}/README.md +0 -0
  67. {sdevpy-1.0.3 → sdevpy-1.0.4}/setup.cfg +0 -0
  68. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/analytics/fbsabr.py +0 -0
  69. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/analytics/mcheston.py +0 -0
  70. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/analytics/mcsabr.py +0 -0
  71. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/analytics/mczabr.py +0 -0
  72. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/analytics/sabr.py +0 -0
  73. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/machinelearning/callbacks.py +0 -0
  74. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/machinelearning/datasets.py +0 -0
  75. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/machinelearning/learningmodel.py +0 -0
  76. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/machinelearning/learningschedules.py +0 -0
  77. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/machinelearning/topology.py +0 -0
  78. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/maths/interpolations.py +0 -0
  79. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/maths/metrics.py +0 -0
  80. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/maths/rand.py +0 -0
  81. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/montecarlo/smoothers.py +0 -0
  82. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/projects/aad/aad_mc.py +0 -0
  83. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/projects/aad/aad_mc_nd.py +0 -0
  84. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/projects/datafiles.py +0 -0
  85. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/settings.py +0 -0
  86. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/test.py +0 -0
  87. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/tools/clipboard.py +0 -0
  88. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/tools/constants.py +0 -0
  89. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/tools/jsonmanager.py +0 -0
  90. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/tools/timegrids.py +0 -0
  91. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/tools/timer.py +0 -0
  92. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/tools/utils.py +0 -0
  93. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/volsurfacegen/fbsabrgenerator.py +0 -0
  94. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/volsurfacegen/mchestongenerator.py +0 -0
  95. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/volsurfacegen/mcsabrgenerator.py +0 -0
  96. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/volsurfacegen/mczabrgenerator.py +0 -0
  97. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/volsurfacegen/smilegenerator.py +0 -0
  98. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/volsurfacegen/stovolfactory.py +0 -0
  99. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy.egg-info/dependency_links.txt +0 -0
  100. {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy.egg-info/top_level.txt +0 -0
  101. {sdevpy-1.0.3 → sdevpy-1.0.4}/tests/test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sdevpy
3
- Version: 1.0.3
3
+ Version: 1.0.4
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
@@ -13,7 +13,6 @@ Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
14
  Requires-Dist: pandas
15
15
  Requires-Dist: pyperclip
16
- Requires-Dist: py_vollib
17
16
  Requires-Dist: numpy
18
17
  Requires-Dist: tensorflow
19
18
  Requires-Dist: scikit-learn
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "sdevpy"
7
- version = "1.0.3"
7
+ version = "1.0.4"
8
8
  authors = [
9
9
  { name="Sebastien Gurrieri", email="sebgur@gmail.com" },
10
10
  ]
@@ -17,7 +17,7 @@ classifiers = [
17
17
  "Operating System :: OS Independent",
18
18
  ]
19
19
  dependencies = [
20
- "pandas","pyperclip","py_vollib","numpy","tensorflow",
20
+ "pandas","pyperclip","numpy","tensorflow",
21
21
  "scikit-learn", "tensorflow_probability", "silence_tensorflow"
22
22
  ]
23
23
 
@@ -0,0 +1 @@
1
+ __version__ = '1.0.4'
@@ -12,6 +12,21 @@ def price(expiry, strike, is_call, fwd, vol):
12
12
  return stdev * (wd * norm.cdf(wd) + norm.pdf(d))
13
13
 
14
14
 
15
+ def price_straddles(expiries, strikes, fwd, vols):
16
+ expiries_ = np.asarray(expiries).reshape(-1, 1)
17
+ prices = []
18
+ for i, expiry in enumerate(expiries_):
19
+ k_prices = []
20
+ for j, k in enumerate(strikes[i]):
21
+ iv = vols[i, j]
22
+ call_price = price(expiry, k, True, fwd, iv)
23
+ put_price = price(expiry, k, False, fwd, iv)
24
+ k_prices.append(call_price[0] + put_price[0])
25
+ prices.append(k_prices)
26
+
27
+ return np.asarray(prices)
28
+
29
+
15
30
  def implied_vol(expiry, strike, is_call, fwd, fwd_price):
16
31
  """ P. Jaeckel's method in "Implied Normal Volatility", 6th Jun. 2017 """
17
32
  m = fwd - strike
@@ -2,7 +2,8 @@
2
2
  import numpy as np
3
3
  import scipy.stats
4
4
  from scipy.optimize import minimize_scalar
5
- import py_vollib.black.implied_volatility as jaeckel
5
+ # import py_vollib.black.implied_volatility as jaeckel
6
+ from sdevpy.thirdparty.py_vollib.black import implied_volatility as jaeckel
6
7
  import tensorflow as tf
7
8
  import tensorflow_probability as tfp
8
9
  from sdevpy import settings
@@ -66,7 +66,7 @@ class SciPyOptimizer(Optimizer):
66
66
  popsize = self.kwargs.get('popsize', 15)
67
67
  strategy = self.kwargs.get('strategy', 'best1bin')
68
68
  recombination = self.kwargs.get('recombination', 0.7)
69
- mutation = self.kwargs.get('mutation', (0.5, 1.0))
69
+ mutation = self.kwargs.get('mutation', (0.5, 1.0)) # ToDo: parameter not used
70
70
  result = opt.differential_evolution(f, x0=x0, args=args, bounds=bounds, atol=atol,
71
71
  popsize=popsize, strategy=strategy,
72
72
  recombination=recombination)
@@ -88,16 +88,20 @@ class MultiOptimizer(Optimizer):
88
88
 
89
89
  def minimize(self, f, x0=None, args=(), bounds=None):
90
90
  result = None
91
+ nfev = 0
91
92
  for i, optimizer in enumerate(self.optimizers_):
92
93
  print("Trying optimization using " + self.methods_[i] + ": ", end='')
93
94
  result = optimizer.minimize(f, x0, args, bounds)
95
+ nfev = nfev + result.nfev
94
96
  if result.fun < self.mtol_:
95
- print("SUCCESS!")
97
+ print("Good enough!")
96
98
  break
99
+ elif i < len(self.methods_) - 1:
100
+ print("Continuing")
97
101
  else:
98
- print("FAILURE")
102
+ print("Stopping")
99
103
 
100
- return result
104
+ return result, nfev
101
105
 
102
106
 
103
107
  if __name__ == "__main__":
@@ -10,13 +10,13 @@ from sdevpy.tools.timer import Stopwatch
10
10
 
11
11
 
12
12
  # ################ Runtime configuration ##########################################################
13
- # MODEL_TYPE = "SABR"
14
- MODEL_TYPE = "McSABR"
13
+ MODEL_TYPE = "SABR"
14
+ # MODEL_TYPE = "McSABR"
15
15
  # MODEL_TYPE = "FbSABR"
16
16
  # MODEL_TYPE = "McZABR"
17
17
  # MODEL_TYPE = "McHeston"
18
18
  SHIFT = 0.03
19
- NUM_SAMPLES = 35 * 1000
19
+ NUM_SAMPLES = 35 * 100
20
20
  # The 4 parameters below are only relevant for models whose reference is calculated by MC
21
21
  NUM_EXPIRIES = 10
22
22
  NUM_STRIKES = 5
@@ -8,7 +8,8 @@ from sdevpy.tools import clipboard
8
8
 
9
9
 
10
10
  def plot_transform_surface(expiries, strikes, are_calls, fwd, ref_prices, mod_prices, title_,
11
- transform='ShiftedBlackScholes'):
11
+ transform='ShiftedBlackScholes', ref_name='Reference',
12
+ mod_name='Model'):
12
13
  """ Calculate quantities to display for the surface and display them in charts. Transformed
13
14
  quantities available are: Price, ShiftedBlackScholes (3%) and Bachelier (normal vols). """
14
15
  # Transform prices
@@ -27,8 +28,8 @@ def plot_transform_surface(expiries, strikes, are_calls, fwd, ref_prices, mod_pr
27
28
  for i in range(num_rows):
28
29
  for j in range(num_cols):
29
30
  k = num_cols * i + j
30
- axs[i, j].plot(strikes[k], ref_disp[k], color='blue', label='Reference')
31
- axs[i, j].plot(strikes[k], mod_disp[k], color='red', label='Model')
31
+ axs[i, j].plot(strikes[k], ref_disp[k], color='blue', label=ref_name)
32
+ axs[i, j].plot(strikes[k], mod_disp[k], color='red', label=mod_name)
32
33
  axs[i, j].xaxis.set_major_formatter(mtick.PercentFormatter(xmax=1, decimals=1))
33
34
  axs[i, j].yaxis.set_major_formatter(mtick.PercentFormatter(xmax=1, decimals=0))
34
35
  axs[i, j].set_xlabel('Strike')
@@ -23,8 +23,8 @@ from sdevpy.projects.stovol import stovolplot as xplt
23
23
 
24
24
 
25
25
  # ################ Runtime configuration ##########################################################
26
- # MODEL_TYPE = "SABR"
27
- MODEL_TYPE = "McSABR"
26
+ MODEL_TYPE = "SABR"
27
+ # MODEL_TYPE = "McSABR"
28
28
  # MODEL_TYPE = "FbSABR"
29
29
  # MODEL_TYPE = "McZABR"
30
30
  # MODEL_TYPE = "McHeston"
@@ -38,9 +38,9 @@ TRAIN = True
38
38
  if USE_TRAINED is False and TRAIN is False:
39
39
  raise RuntimeError("When not using pre-trained models, a new model must be trained")
40
40
 
41
- NUM_SAMPLES = 500 * 1000 # Number of samples to read from sample files
41
+ NUM_SAMPLES = 2 * 1000 * 1000 # Number of samples to read from sample files
42
42
  TRAIN_PERCENT = 0.90 # Proportion of dataset used for training (rest used for test)
43
- EPOCHS = 200
43
+ EPOCHS = 100
44
44
  BATCH_SIZE = 1000
45
45
  SHOW_VOL_CHARTS = True # Show smile section charts
46
46
  # For comparison to reference values (accuracy of reference)
@@ -141,11 +141,12 @@ print(f"> Drop-out rate: {DROP_OUT:.2f}")
141
141
  # ################ Train the model ################################################################
142
142
  if TRAIN:
143
143
  # Learning rate scheduler
144
- INIT_LR = 1.0e-2
144
+ INIT_LR = 1.0e-3
145
145
  FINAL_LR = 1.0e-4
146
- DECAY = 0.97
147
- STEPS = 250
148
- lr_schedule = FlooredExponentialDecay(INIT_LR, FINAL_LR, DECAY, STEPS)
146
+ TARGET_EPOCH = EPOCHS * 0.90 # Epoch by which we plan to be down to 110% of final LR
147
+ PERIODS = 10 # Number of oscillation periods until target epoch
148
+ # lr_schedule = FlooredExponentialDecay(INIT_LR, FINAL_LR, DECAY, STEPS)
149
+ lr_schedule = FlooredExponentialDecay(NUM_SAMPLES, BATCH_SIZE, TARGET_EPOCH, INIT_LR, FINAL_LR)
149
150
 
150
151
  # Optimizer
151
152
  optimizer = tf.keras.optimizers.Adam(learning_rate=lr_schedule)
@@ -20,9 +20,10 @@ NUM_SAMPLES = 1000 * 1000
20
20
  NUM_EXPIRIES = 15
21
21
  NUM_MC = 100 * 1000 # 100 * 1000
22
22
  POINTS_PER_YEAR = 25 # 25
23
- SEED = 1234 # [1357, 8642, 1000, 8888, 4444, 2222, 1111, 4321, 1234, 42]
23
+ SEED = 8888 # [1357, 8642, 1000, 8888, 4444, 2222, 1111, 4321, 1234, 42]
24
24
  SPREADS = [-200, -100, -75, -50, -25, -10, 0, 10, 25, 50, 75, 100, 200]
25
25
  USE_NVOL = True
26
+ NOISE = 0.01 # Relative size of noise
26
27
 
27
28
  print(">> Set up runtime configuration")
28
29
  project_folder = os.path.join(settings.WORKFOLDER, "stovolinv")
@@ -38,8 +39,13 @@ generator = stovolfactory.set_generator(MODEL_TYPE, SHIFT, NUM_EXPIRIES, num_mc=
38
39
 
39
40
  # ################ Select training ranges #########################################################
40
41
  # SABR
41
- RANGES = {'Ttm': [1.0 / 12.0, 35.0], 'F': [-0.009, 0.041], 'LnVol': [0.05, 0.5],
42
- 'Beta': [0.1, 0.9], 'Nu': [0.1, 1.0], 'Rho': [-0.6, 0.6]}
42
+ # RANGES = {'Ttm': [1.0 / 12.0, 6.0], 'F': [0.05, 0.06], 'LnVol': [0.20, 0.40],
43
+ # 'Beta': [0.1, 0.9], 'Nu': [0.2, 1.0], 'Rho': [-0.5, 0.2]} # 6y
44
+ RANGES = {'Ttm': [1.0 / 12.0, 35.0], 'F': [0.05, 0.06], 'LnVol': [0.20, 0.40],
45
+ 'Beta': [0.1, 0.9], 'Nu': [0.2, 1.0], 'Rho': [-0.5, 0.2]} # All times
46
+
47
+ # RANGES = {'Ttm': [1.0 / 12.0, 35.0], 'F': [-0.009, 0.041], 'LnVol': [0.05, 0.5],
48
+ # 'Beta': [0.1, 0.9], 'Nu': [0.1, 1.0], 'Rho': [-0.6, 0.6]}
43
49
  # # FBSABR
44
50
  # RANGES = {'Ttm': [1.0 / 12.0, 5.0], 'F': [-0.009, 0.041], 'LnVol': [0.05, 0.5],
45
51
  # 'Beta': [0.25, 0.75], 'Nu': [0.1, 1.0], 'Rho': [-0.6, 0.6]}
@@ -58,7 +64,7 @@ print(">> Generate dataset")
58
64
  print(f"> Generate {NUM_SAMPLES:,} price samples")
59
65
  timer_gen = Stopwatch("Generating Samples")
60
66
  timer_gen.trigger()
61
- data_df = generator.generate_samples_inverse(NUM_SAMPLES, RANGES, SPREADS, USE_NVOL)
67
+ data_df = generator.generate_samples_inverse(NUM_SAMPLES, RANGES, SPREADS, USE_NVOL, rel_noise=NOISE)
62
68
  timer_gen.stop()
63
69
 
64
70
  timer_out = Stopwatch("File Output")
@@ -17,28 +17,28 @@ from sdevpy.machinelearning.callbacks import RefCallback
17
17
  from sdevpy.machinelearning import datasets
18
18
  from sdevpy.tools import filemanager
19
19
  from sdevpy.tools.timer import Stopwatch
20
- # from sdevpy.tools import clipboard
21
20
  from sdevpy.maths.metrics import bps_rmse, tf_bps_rmse, tf_mse, mse, tf_rmse, rmse
22
21
  from sdevpy.volsurfacegen.stovolfactory import set_generator
23
22
  from sdevpy.projects.stovol import stovolplot as xplt
23
+ from sdevpy.analytics import bachelier
24
24
 
25
25
 
26
26
  # ################ ToDo ###########################################################################
27
- # Start additional script with simplified function starting from trained model only, to test
28
- # against classic calibration, shifting parameters, sensies, etc.
29
-
30
- # We could generate market points from SABR, then generate a new set from a slightly different
31
- # set of SABR parameters such as a rho move, a nu move, etc. Then re-calibrate by optimization
32
- # vs using the network and see if they produce expected move. For instance if the second
33
- # market is a rho move of +10%, do the standard optimization and the network produce a set
34
- # of parameters that correspond to +10% in rho?
35
- # Then we can create a new market that corresponds to an infinitesimal move in the generating
36
- # SABR parameters, and see if that translates into an expected infinitesimal move in the result.
37
-
38
- # This method should allow us to calculate the sensitivity to market by AAD. That's quite an
39
- # advantage, as for the regular method we can't AAD through calibration/optimization.
40
-
41
- # Compare vegas
27
+ # At the comparison check between model and calibration, we tried adding random noise to the normal
28
+ # vols used for comparison, on top of their values that come from a chosen SABR model. This way the
29
+ # vols we apply the model/calibration to are not longer exactly SABR.
30
+ # The issue was that the model was going off track very quickly with increasing size of noise.
31
+ # Incidentally, the optimization says "FAILURE" but this is likely to be because the tolerance is
32
+ # very small. We should try to make the tolerance larger and see how it goes.
33
+
34
+ # So the idea would be to include non-SABR points in the training dataset. This could be done by the
35
+ # same noise technique in reverse. Generate random SABR parameters, calculate the normal vols, then
36
+ # add noise on the normal vols, then calculate prices. But then in principle we should calibrate
37
+ # SABR to those prices again, to lear the truly optimium SABR parameters. However, isn't it possible
38
+ # to simply consider the original SABR (before the noise) as the likely optimum? At least, we could
39
+ # start the optimization there. It sounds likely that over a big range of data, these original SABRs
40
+ # could overall be near-optimum enough. Which would then avoid us the trouble of going through the
41
+ # calibration during the training.
42
42
  # ################ Module versions ################################################################
43
43
  print("TensorFlow version: " + tf.__version__)
44
44
  # print("Keras version: " + tf.keras.__version__)
@@ -53,18 +53,18 @@ MODEL_TYPE = "SABR"
53
53
  # MODEL_TYPE = "McZABR"
54
54
  # MODEL_TYPE = "McHeston"
55
55
  # MODEL_ID = "SABR_3L_64n" # For pre-trained model ID (we can pre-train several versions)
56
- MODEL_ID = MODEL_TYPE # For pre-trained model ID (we can pre-train several versions)
56
+ MODEL_ID = "SABR" # MODEL_TYPE # For pre-trained model ID (we can pre-train several versions)
57
57
  SHIFT = 0.03
58
58
  USE_TRAINED = True
59
59
  DOWNLOAD_MODELS = False # Only used when USE_TRAINED is True
60
60
  DOWNLOAD_DATASETS = False # Use when already created/downloaded
61
- TRAIN = True
61
+ TRAIN = False
62
62
  if USE_TRAINED is False and TRAIN is False:
63
63
  raise RuntimeError("When not using pre-trained models, a new model must be trained")
64
64
 
65
- NUM_SAMPLES = 200 * 1000#2 * 1000 * 1000 # Number of samples to read from sample files
65
+ NUM_SAMPLES = 1000 * 1000#2 * 1000 * 1000 # Number of samples to read from sample files
66
66
  TRAIN_PERCENT = 0.90 # Proportion of dataset used for training (rest used for test)
67
- EPOCHS = 50
67
+ EPOCHS = 300
68
68
  BATCH_SIZE = 1000
69
69
  SHOW_VOL_CHARTS = True # Show smile section charts
70
70
  # For comparison to reference values (accuracy of reference)
@@ -151,7 +151,7 @@ else:
151
151
  # Initialize the model
152
152
  HIDDEN_LAYERS = ['softplus', 'softplus', 'softplus']
153
153
  # NUM_NEURONS = 128
154
- NUM_NEURONS = 64
154
+ NUM_NEURONS = 128
155
155
  DROP_OUT = 0.0
156
156
  keras_model = compose_model(input_dim, output_dim, HIDDEN_LAYERS, NUM_NEURONS, DROP_OUT)
157
157
  topology = { 'layers': HIDDEN_LAYERS, 'neurons': NUM_NEURONS, 'dropout': DROP_OUT}
@@ -167,8 +167,8 @@ print(f"> Drop-out rate: {DROP_OUT:.2f}")
167
167
  # ################ Train the model ################################################################
168
168
  if TRAIN:
169
169
  # Learning rate scheduler
170
- INIT_LR = 1.0e-2#1.0e-2
171
- FINAL_LR = 1.0e-3#1.0e-4
170
+ INIT_LR = 1.0e-3#1.0e-2
171
+ FINAL_LR = 1.0e-4#1.0e-4
172
172
  TARGET_EPOCH = EPOCHS * 0.90 # Epoch by which we plan to be down to 110% of final LR
173
173
  PERIODS = 10 # Number of oscillation periods until target epoch
174
174
 
@@ -227,15 +227,16 @@ print(f"> RMSE on test set: {test_rmse:,.2f}")
227
227
  if SHOW_VOL_CHARTS:
228
228
  print("> Choosing a sample parameter set to display chart")
229
229
  NUM_STRIKES = 100
230
- PARAMS = { 'LnVol': 0.20, 'Beta': 0.5, 'Nu': 0.55, 'Rho': -0.25, 'Gamma': 0.7, 'Kappa': 1.0,
230
+ PARAMS = { 'LnVol': 0.30, 'Beta': 0.5, 'Nu': 0.50, 'Rho': -0.10, 'Gamma': 0.7, 'Kappa': 1.0,
231
231
  'Theta': 0.03, 'Xi': 0.35 }
232
- FWD = 0.028
232
+ FWD = 0.055
233
233
 
234
234
  # Any number of expiries can be calculated, but for optimum display choose no more than 6
235
235
  if MODEL_TYPE == "FbSABR":
236
236
  EXPIRIES = np.asarray([0.25, 0.50, 1.0, 2.0, 5.0, 10.0]).reshape(-1, 1) # Only trained up to 5y
237
237
  else:
238
238
  EXPIRIES = np.asarray([0.25, 0.50, 1.0, 5.0, 10.0, 30.0]).reshape(-1, 1)
239
+ # EXPIRIES = np.asarray([0.25, 0.50, 1.0, 2.0, 3.0, 5.0]).reshape(-1, 1)
239
240
  NUM_EXPIRIES = EXPIRIES.shape[0]
240
241
 
241
242
  # Calculate market strikes and prices on the training spreads
@@ -247,12 +248,16 @@ if SHOW_VOL_CHARTS:
247
248
  mkt_strikes = TRAINING_SPREADS / 10000.0 + FWD
248
249
 
249
250
  # Calculate market prices and vols
250
- mkt_vols = generator.price_straddles_ref(EXPIRIES, mkt_strikes, FWD, PARAMS, True)
251
- mkt_prices = generator.price_straddles_ref(EXPIRIES, mkt_strikes, FWD, PARAMS, False)
251
+ rel_noise = 0.02
252
+ noise_thresh = 0.9
253
+ mkt_vols = generator.price_straddles_ref(EXPIRIES, mkt_strikes, FWD, PARAMS, True,
254
+ rel_noise=rel_noise, noise_thresh=noise_thresh)
255
+ mkt_prices = bachelier.price_straddles(EXPIRIES, mkt_strikes, FWD, mkt_vols)
256
+ # mkt_prices = generator.price_straddles_ref(EXPIRIES, mkt_strikes, FWD, PARAMS, False)
252
257
 
253
258
  # Use model to get parameters at each expiry, then calculate parameters and then prices
254
259
  mod_params, mod_vols = generator.price_straddles_mod(model, EXPIRIES, mkt_strikes, FWD,
255
- mkt_vols, True)
260
+ mkt_vols, True)
256
261
  # mod_vols = generator.price_straddles_ref(EXPIRIES, mkt_strikes, FWD, mod_params, True)
257
262
 
258
263
  # mkt_prices = generator.price_straddles_ref(EXPIRIES, mkt_strikes, FWD, PARAMS, False)
@@ -0,0 +1,48 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ """
4
+ py_lets_be_rational
5
+ ~~~~~~~~~~~~~~~~~~~
6
+
7
+ Pure python implementation of Peter Jaeckel's LetsBeRational.
8
+
9
+ :copyright: © 2017 Gammon Capital LLC
10
+ :license: MIT, see LICENSE for more details.
11
+
12
+ About LetsBeRational:
13
+ ~~~~~~~~~~~~~~~~~~~~~
14
+
15
+ The source code of LetsBeRational resides at www.jaeckel.org/LetsBeRational.7z .
16
+
17
+ ======================================================================================
18
+ Copyright © 2013-2014 Peter Jäckel.
19
+
20
+ Permission to use, copy, modify, and distribute this software is freely granted,
21
+ provided that this notice is preserved.
22
+
23
+ WARRANTY DISCLAIMER
24
+ The Software is provided "as is" without warranty of any kind, either express or implied,
25
+ including without limitation any implied warranties of condition, uninterrupted use,
26
+ merchantability, fitness for a particular purpose, or non-infringement.
27
+ ======================================================================================
28
+ """
29
+
30
+ from sdevpy.thirdparty.py_lets_be_rational.lets_be_rational import black
31
+ from sdevpy.thirdparty.py_lets_be_rational.lets_be_rational import normalised_black
32
+ from sdevpy.thirdparty.py_lets_be_rational.lets_be_rational import normalised_black_call
33
+ from sdevpy.thirdparty.py_lets_be_rational.lets_be_rational import implied_volatility_from_a_transformed_rational_guess
34
+ from sdevpy.thirdparty.py_lets_be_rational.lets_be_rational import implied_volatility_from_a_transformed_rational_guess_with_limited_iterations
35
+ from sdevpy.thirdparty.py_lets_be_rational.lets_be_rational import normalised_implied_volatility_from_a_transformed_rational_guess
36
+ from sdevpy.thirdparty.py_lets_be_rational.lets_be_rational import normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations
37
+ from sdevpy.thirdparty.py_lets_be_rational.lets_be_rational import normalised_vega
38
+ from sdevpy.thirdparty.py_lets_be_rational.normaldistribution import norm_cdf
39
+
40
+ # from py_lets_be_rational.lets_be_rational import black
41
+ # from py_lets_be_rational.lets_be_rational import normalised_black
42
+ # from py_lets_be_rational.lets_be_rational import normalised_black_call
43
+ # from py_lets_be_rational.lets_be_rational import implied_volatility_from_a_transformed_rational_guess
44
+ # from py_lets_be_rational.lets_be_rational import implied_volatility_from_a_transformed_rational_guess_with_limited_iterations
45
+ # from py_lets_be_rational.lets_be_rational import normalised_implied_volatility_from_a_transformed_rational_guess
46
+ # from py_lets_be_rational.lets_be_rational import normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations
47
+ # from py_lets_be_rational.lets_be_rational import normalised_vega
48
+ # from py_lets_be_rational.normaldistribution import norm_cdf
@@ -0,0 +1,64 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ """
4
+ py_lets_be_rational.constants
5
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6
+
7
+ Pure python implementation of Peter Jaeckel's LetsBeRational.
8
+
9
+ :copyright: © 2017 Gammon Capital LLC
10
+ :license: MIT, see LICENSE for more details.
11
+
12
+ About LetsBeRational:
13
+ ~~~~~~~~~~~~~~~~~~~~~
14
+
15
+ The source code of LetsBeRational resides at www.jaeckel.org/LetsBeRational.7z .
16
+
17
+ ======================================================================================
18
+ Copyright © 2013-2014 Peter Jäckel.
19
+
20
+ Permission to use, copy, modify, and distribute this software is freely granted,
21
+ provided that this notice is preserved.
22
+
23
+ WARRANTY DISCLAIMER
24
+ The Software is provided "as is" without warranty of any kind, either express or implied,
25
+ including without limitation any implied warranties of condition, uninterrupted use,
26
+ merchantability, fitness for a particular purpose, or non-infringement.
27
+ ======================================================================================
28
+ """
29
+
30
+ from __future__ import absolute_import
31
+
32
+ from sys import float_info
33
+ DBL_MIN, DBL_MAX = float_info.min, float_info.max
34
+ # from _testcapi import DBL_MIN, DBL_MAX
35
+
36
+ import sys
37
+ from math import sqrt
38
+
39
+ DBL_EPSILON = sys.float_info.epsilon
40
+
41
+ SQRT_DBL_EPSILON = sqrt(DBL_EPSILON)
42
+ FOURTH_ROOT_DBL_EPSILON = sqrt(SQRT_DBL_EPSILON)
43
+ EIGHTH_ROOT_DBL_EPSILON = sqrt(FOURTH_ROOT_DBL_EPSILON)
44
+ SIXTEENTH_ROOT_DBL_EPSILON = sqrt(EIGHTH_ROOT_DBL_EPSILON)
45
+ SQRT_DBL_MIN = sqrt(DBL_MIN)
46
+ SQRT_DBL_MAX = sqrt(DBL_MAX)
47
+
48
+ # Set this to 0 if you want positive results for (positive) denormalized inputs, else to DBL_MIN.
49
+ # Note that you cannot achieve full machine accuracy from denormalized inputs!
50
+ DENORMALIZATION_CUTOFF = 0
51
+
52
+ VOLATILITY_VALUE_TO_SIGNAL_PRICE_IS_BELOW_INTRINSIC = -DBL_MAX
53
+ VOLATILITY_VALUE_TO_SIGNAL_PRICE_IS_ABOVE_MAXIMUM = DBL_MAX
54
+
55
+ ONE_OVER_SQRT_TWO = 0.7071067811865475244008443621048490392848359376887
56
+ ONE_OVER_SQRT_TWO_PI = 0.3989422804014326779399460599343818684758586311649
57
+ SQRT_TWO_PI = 2.506628274631000502415765284811045253006986740610
58
+
59
+ TWO_PI = 6.283185307179586476925286766559005768394338798750
60
+ SQRT_PI_OVER_TWO = 1.253314137315500251207882642405522626503493370305 # sqrt(pi/2) to avoid misinterpretation.
61
+ SQRT_THREE = 1.732050807568877293527446341505872366942805253810
62
+ SQRT_ONE_OVER_THREE = 0.577350269189625764509148780501957455647601751270
63
+ TWO_PI_OVER_SQRT_TWENTY_SEVEN = 1.209199576156145233729385505094770488189377498728 # 2*pi/sqrt(27)
64
+ PI_OVER_SIX = 0.523598775598298873077107230546583814032861566563