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.
- {sdevpy-1.0.3/src/sdevpy.egg-info → sdevpy-1.0.4}/PKG-INFO +1 -2
- {sdevpy-1.0.3 → sdevpy-1.0.4}/pyproject.toml +2 -2
- sdevpy-1.0.4/src/sdevpy/__init__.py +1 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/analytics/bachelier.py +15 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/analytics/black.py +2 -1
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/maths/optimization.py +8 -4
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/projects/stovol/stovolgen.py +3 -3
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/projects/stovol/stovolplot.py +4 -3
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/projects/stovol/stovoltrain.py +9 -8
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/projects/stovolinverse/stovolinvgen.py +10 -4
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/projects/stovolinverse/stovolinvtrain.py +33 -28
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_lets_be_rational/__init__.py +48 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_lets_be_rational/constants.py +64 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_lets_be_rational/erf_cody.py +447 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_lets_be_rational/exceptions.py +67 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_lets_be_rational/lets_be_rational.py +800 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_lets_be_rational/normaldistribution.py +193 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_lets_be_rational/numba_helper.py +13 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_lets_be_rational/rationalcubic.py +271 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/__init__.py +31 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black/__init__.py +179 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black/greeks/__init__.py +0 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black/greeks/analytical.py +272 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black/greeks/numerical.py +250 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black/implied_volatility.py +291 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black_scholes/__init__.py +83 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black_scholes/greeks/__init__.py +0 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black_scholes/greeks/analytical.py +277 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black_scholes/greeks/numerical.py +317 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black_scholes/implied_volatility.py +101 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black_scholes_merton/__init__.py +89 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/__init__.py +0 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/analytical.py +308 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/numerical.py +265 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/black_scholes_merton/implied_volatility.py +115 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/helpers/__init__.py +113 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/helpers/constants.py +51 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/helpers/distributions.py +225 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/helpers/doctest_helper.py +61 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/helpers/exceptions.py +58 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/helpers/numerical_greeks.py +216 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/__init__.py +31 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black/__init__.py +235 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/__init__.py +0 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/analytical.py +276 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/numerical.py +221 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black/implied_volatility.py +116 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/__init__.py +156 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/__init__.py +0 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/analytical.py +278 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/numerical.py +289 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/implied_volatility.py +109 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/__init__.py +230 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/__init__.py +0 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/analytical.py +310 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/numerical.py +222 -0
- sdevpy-1.0.4/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/implied_volatility.py +103 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/tools/filemanager.py +1 -2
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/volsurfacegen/sabrgenerator.py +28 -18
- {sdevpy-1.0.3 → sdevpy-1.0.4/src/sdevpy.egg-info}/PKG-INFO +1 -2
- sdevpy-1.0.4/src/sdevpy.egg-info/SOURCES.txt +97 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy.egg-info/requires.txt +0 -1
- sdevpy-1.0.3/src/sdevpy/__init__.py +0 -1
- sdevpy-1.0.3/src/sdevpy.egg-info/SOURCES.txt +0 -51
- {sdevpy-1.0.3 → sdevpy-1.0.4}/LICENSE +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/README.md +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/setup.cfg +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/analytics/fbsabr.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/analytics/mcheston.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/analytics/mcsabr.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/analytics/mczabr.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/analytics/sabr.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/machinelearning/callbacks.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/machinelearning/datasets.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/machinelearning/learningmodel.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/machinelearning/learningschedules.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/machinelearning/topology.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/maths/interpolations.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/maths/metrics.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/maths/rand.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/montecarlo/smoothers.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/projects/aad/aad_mc.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/projects/aad/aad_mc_nd.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/projects/datafiles.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/settings.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/test.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/tools/clipboard.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/tools/constants.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/tools/jsonmanager.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/tools/timegrids.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/tools/timer.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/tools/utils.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/volsurfacegen/fbsabrgenerator.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/volsurfacegen/mchestongenerator.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/volsurfacegen/mcsabrgenerator.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/volsurfacegen/mczabrgenerator.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/volsurfacegen/smilegenerator.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy/volsurfacegen/stovolfactory.py +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy.egg-info/dependency_links.txt +0 -0
- {sdevpy-1.0.3 → sdevpy-1.0.4}/src/sdevpy.egg-info/top_level.txt +0 -0
- {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
|
+
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.
|
|
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","
|
|
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("
|
|
97
|
+
print("Good enough!")
|
|
96
98
|
break
|
|
99
|
+
elif i < len(self.methods_) - 1:
|
|
100
|
+
print("Continuing")
|
|
97
101
|
else:
|
|
98
|
-
print("
|
|
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
|
-
|
|
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 *
|
|
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=
|
|
31
|
-
axs[i, j].plot(strikes[k], mod_disp[k], color='red', label=
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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-
|
|
144
|
+
INIT_LR = 1.0e-3
|
|
145
145
|
FINAL_LR = 1.0e-4
|
|
146
|
-
|
|
147
|
-
|
|
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 =
|
|
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,
|
|
42
|
-
|
|
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
|
-
#
|
|
28
|
-
#
|
|
29
|
-
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
#
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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-
|
|
171
|
-
FINAL_LR = 1.0e-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
251
|
-
|
|
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
|
-
|
|
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
|