sdevpy 0.1.2__tar.gz → 0.1.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-0.1.2 → sdevpy-0.1.4}/PKG-INFO +1 -1
- {sdevpy-0.1.2 → sdevpy-0.1.4}/pyproject.toml +3 -2
- sdevpy-0.1.4/src/sdevpy/__init__.py +1 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/analytics/fbsabr.py +7 -7
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/analytics/mcheston.py +1 -0
- sdevpy-0.1.4/src/sdevpy/machinelearning/datasets.py +78 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/maths/metrics.py +9 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/projects/stovol/stovolgen.py +27 -12
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/projects/stovol/stovoltrain.py +55 -41
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/test.py +111 -11
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/tools/filemanager.py +21 -16
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/tools/timegrids.py +20 -4
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/volsurfacegen/fbsabrgenerator.py +7 -12
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/volsurfacegen/mchestongenerator.py +43 -34
- sdevpy-0.1.4/src/sdevpy/volsurfacegen/mcsabrgenerator.py +63 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/volsurfacegen/mczabrgenerator.py +26 -43
- sdevpy-0.1.2/src/sdevpy/volsurfacegen/mcsabrgenerator.py → sdevpy-0.1.4/src/sdevpy/volsurfacegen/sabrgenerator.py +78 -81
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/volsurfacegen/smilegenerator.py +31 -12
- sdevpy-0.1.4/src/sdevpy/volsurfacegen/stovolfactory.py +38 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy.egg-info/PKG-INFO +1 -1
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy.egg-info/SOURCES.txt +0 -1
- sdevpy-0.1.4/src/sdevpy.egg-info/requires.txt +6 -0
- sdevpy-0.1.2/src/sdevpy/__init__.py +0 -1
- sdevpy-0.1.2/src/sdevpy/machinelearning/datasets.py +0 -32
- sdevpy-0.1.2/src/sdevpy/projects/stovol/xsabrfit.py +0 -255
- sdevpy-0.1.2/src/sdevpy/volsurfacegen/sabrgenerator.py +0 -282
- sdevpy-0.1.2/src/sdevpy/volsurfacegen/stovolfactory.py +0 -44
- sdevpy-0.1.2/src/sdevpy.egg-info/requires.txt +0 -3
- {sdevpy-0.1.2 → sdevpy-0.1.4}/LICENSE +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/README.md +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/setup.cfg +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/analytics/bachelier.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/analytics/black.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/analytics/mcsabr.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/analytics/mczabr.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/analytics/sabr.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/machinelearning/callbacks.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/machinelearning/learningmodel.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/machinelearning/learningschedules.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/machinelearning/topology.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/maths/interpolations.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/maths/optimization.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/maths/rand.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/projects/datafiles.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/projects/pinns/ernst_pinns.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/projects/pinns/pinns.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/projects/pinns/pinns_worst_of.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/projects/stovol/stovolplot.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/settings.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/tools/clipboard.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/tools/constants.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/tools/jsonmanager.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy/tools/timer.py +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy.egg-info/dependency_links.txt +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/src/sdevpy.egg-info/top_level.txt +0 -0
- {sdevpy-0.1.2 → sdevpy-0.1.4}/tests/test.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sdevpy"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.4"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name="Sebastien Gurrieri", email="sebgur@gmail.com" },
|
|
10
10
|
]
|
|
@@ -17,7 +17,8 @@ classifiers = [
|
|
|
17
17
|
"Operating System :: OS Independent",
|
|
18
18
|
]
|
|
19
19
|
dependencies = [
|
|
20
|
-
"pandas","pyperclip","py_vollib"
|
|
20
|
+
"pandas","pyperclip","py_vollib","numpy","tensorflow",
|
|
21
|
+
"scikit-learn"
|
|
21
22
|
]
|
|
22
23
|
|
|
23
24
|
[project.urls]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.1.4'
|
|
@@ -109,15 +109,15 @@ def calculate_fbsabr_alpha(ln_vol, fwd, beta):
|
|
|
109
109
|
return ln_vol * abs_f ** (1.0 - beta)
|
|
110
110
|
|
|
111
111
|
if __name__ == "__main__":
|
|
112
|
-
EXPIRIES = [0
|
|
112
|
+
EXPIRIES = [3.0, 8.0, 13, 19, 22, 31, 34]
|
|
113
113
|
NSTRIKES = 50
|
|
114
|
-
FWD =
|
|
115
|
-
SHIFT = 0.
|
|
114
|
+
FWD = 0.016132
|
|
115
|
+
SHIFT = 0.00
|
|
116
116
|
SFWD = FWD + SHIFT
|
|
117
117
|
IS_CALL = False
|
|
118
118
|
ARE_CALLS = [IS_CALL] * NSTRIKES
|
|
119
119
|
ARE_CALLS = [ARE_CALLS] * len(EXPIRIES)
|
|
120
|
-
LNVOL = 0.
|
|
120
|
+
LNVOL = 0.48
|
|
121
121
|
# Spread method
|
|
122
122
|
# SPREADS = np.linspace(-200, 200, NSTRIKES)
|
|
123
123
|
# SPREADS = np.asarray([SPREADS] * len(EXPIRIES))
|
|
@@ -134,11 +134,11 @@ if __name__ == "__main__":
|
|
|
134
134
|
STRIKES = SSTRIKES - SHIFT
|
|
135
135
|
XAXIS = STRIKES
|
|
136
136
|
|
|
137
|
-
PARAMETERS = {'LnVol': LNVOL, 'Beta': 0.
|
|
137
|
+
PARAMETERS = {'LnVol': LNVOL, 'Beta': 0.5, 'Nu': 0.66, 'Rho': 0.48}
|
|
138
138
|
NUM_MC = 100 * 1000
|
|
139
139
|
POINTS_PER_YEAR = 25
|
|
140
|
-
|
|
141
|
-
SCHEME = 'Euler'
|
|
140
|
+
SCHEME = 'Andersen'
|
|
141
|
+
# SCHEME = 'Euler'
|
|
142
142
|
|
|
143
143
|
# Calculate MC prices
|
|
144
144
|
mc_timer = timer.Stopwatch("MC")
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
""" Dataset preparation for training """
|
|
2
|
+
import os
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
from sdevpy.tools import filemanager
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def prepare_sets(inputs, outputs, train_percent):
|
|
9
|
+
""" Split input and output datasets into training and test datasets split
|
|
10
|
+
according to specified percentage to put in the training set """
|
|
11
|
+
data_size = inputs.shape[0]
|
|
12
|
+
if outputs.shape[0] != data_size:
|
|
13
|
+
raise RuntimeError("Incompatible sizes between inputs and outputs")
|
|
14
|
+
|
|
15
|
+
train_size = int(data_size * train_percent)
|
|
16
|
+
|
|
17
|
+
train_inputs = inputs[0:train_size]
|
|
18
|
+
train_outputs = outputs[0:train_size]
|
|
19
|
+
test_inputs = inputs[train_size:data_size]
|
|
20
|
+
test_outputs = outputs[train_size:data_size]
|
|
21
|
+
|
|
22
|
+
return train_inputs, train_outputs, test_inputs, test_outputs
|
|
23
|
+
|
|
24
|
+
def retrieve_data(folder, num_samples, shuffle=True, sep='\t', export_file=""):
|
|
25
|
+
""" Use all files in folder to create a dataset, shuffle its data,
|
|
26
|
+
extract num_samples from it and return dataframe """
|
|
27
|
+
|
|
28
|
+
# Set extension
|
|
29
|
+
if sep == '\t':
|
|
30
|
+
extension = ".tsv"
|
|
31
|
+
elif sep == ',':
|
|
32
|
+
extension = ".csv"
|
|
33
|
+
else:
|
|
34
|
+
raise RuntimeError("Unknown text file separation")
|
|
35
|
+
|
|
36
|
+
# Merge content of folder in single dataframe
|
|
37
|
+
files = filemanager.list_files(folder, [extension])
|
|
38
|
+
df = pd.DataFrame()
|
|
39
|
+
for f in files:
|
|
40
|
+
new_df = pd.read_csv(os.path.join(folder, f), sep=sep)
|
|
41
|
+
df = pd.concat([df, new_df])
|
|
42
|
+
|
|
43
|
+
if shuffle:
|
|
44
|
+
df = df.sample(frac=1)
|
|
45
|
+
|
|
46
|
+
# Clip num_samples
|
|
47
|
+
df = clip_dataframe(df, num_samples)
|
|
48
|
+
|
|
49
|
+
# If export_file is not empty, export to file
|
|
50
|
+
if export_file != "":
|
|
51
|
+
df.to_csv(export_file, sep=sep, index=False)
|
|
52
|
+
|
|
53
|
+
return df
|
|
54
|
+
|
|
55
|
+
def clip_dataframe(df, size):
|
|
56
|
+
""" Clip dataframe beyond specified size"""
|
|
57
|
+
df_size = len(df.index)
|
|
58
|
+
if df_size <= size:
|
|
59
|
+
return df
|
|
60
|
+
else:
|
|
61
|
+
return df.iloc[range(0, size)]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
if __name__ == "__main__":
|
|
65
|
+
INPUTS = np.asarray([[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]])
|
|
66
|
+
OUTPUTS = np.asarray([[1], [2], [3], [4], [5]])
|
|
67
|
+
train_x, train_y, test_x, test_y = prepare_sets(INPUTS, OUTPUTS, 0.80)
|
|
68
|
+
print(train_x)
|
|
69
|
+
print(train_y)
|
|
70
|
+
print(test_x)
|
|
71
|
+
print(test_y)
|
|
72
|
+
|
|
73
|
+
# Test merging
|
|
74
|
+
FOLDER = r"C:\temp\sdevpy\stovol\samples\test"
|
|
75
|
+
NUM_SAMPLES = 4
|
|
76
|
+
DATA_FILE = r"C:\temp\sdevpy\stovol\samples\test.tsv"
|
|
77
|
+
DATA_DF = retrieve_data(FOLDER, NUM_SAMPLES, export_file=DATA_FILE, shuffle=True)
|
|
78
|
+
print(DATA_DF)
|
|
@@ -8,6 +8,15 @@ def rmse(set1, set2):
|
|
|
8
8
|
""" Root Mean Squared Error """
|
|
9
9
|
return np.sqrt(mean_squared_error(set1, set2))
|
|
10
10
|
|
|
11
|
+
def bps_rmse(y_true, y_ref):
|
|
12
|
+
""" RMSE in bps """
|
|
13
|
+
return 10000.0 * rmse(y_true, y_ref)
|
|
14
|
+
|
|
15
|
+
# Tensorflow versions
|
|
11
16
|
def tf_rmse(y_true, y_pred):
|
|
12
17
|
""" Root Mean Squared Error in tensorflow """
|
|
13
18
|
return tf.sqrt(tf.math.reduce_mean(tf.square(y_true - y_pred)))
|
|
19
|
+
|
|
20
|
+
def tf_bps_rmse(y_true, y_ref):
|
|
21
|
+
""" RMSE in bps in tensorflow """
|
|
22
|
+
return 10000.0 * tf_rmse(y_true, y_ref)
|
|
@@ -4,21 +4,21 @@
|
|
|
4
4
|
import os
|
|
5
5
|
from sdevpy.volsurfacegen import stovolfactory
|
|
6
6
|
from sdevpy import settings
|
|
7
|
-
from sdevpy.tools
|
|
7
|
+
from sdevpy.tools import filemanager
|
|
8
8
|
from sdevpy.tools.timer import Stopwatch
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
# ################ Runtime configuration ##########################################################
|
|
12
12
|
# MODEL_TYPE = "SABR"
|
|
13
|
-
# MODEL_TYPE = "
|
|
14
|
-
# MODEL_TYPE = "
|
|
15
|
-
MODEL_TYPE = "
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
NUM_SAMPLES =
|
|
13
|
+
# MODEL_TYPE = "McSABR"
|
|
14
|
+
# MODEL_TYPE = "FbSABR"
|
|
15
|
+
# MODEL_TYPE = "McZABR"
|
|
16
|
+
MODEL_TYPE = "McHeston"
|
|
17
|
+
SHIFT = 0.03
|
|
18
|
+
NUM_SAMPLES = 1 * 1000
|
|
19
19
|
# The 4 parameters below are only relevant for models whose reference is calculated by MC
|
|
20
20
|
NUM_EXPIRIES = 10
|
|
21
|
-
|
|
21
|
+
NUM_STRIKES = 5
|
|
22
22
|
NUM_MC = 100 * 1000 # 100 * 1000
|
|
23
23
|
POINTS_PER_YEAR = 25 # 25
|
|
24
24
|
SEED = 42# [123456789, 6789, 9191, 888, 4321, 100, 4444, 72, 1234, 42]
|
|
@@ -28,21 +28,36 @@ project_folder = os.path.join(settings.WORKFOLDER, "stovol")
|
|
|
28
28
|
print("> Project folder: " + project_folder)
|
|
29
29
|
data_folder = os.path.join(project_folder, "samples")
|
|
30
30
|
print("> Data folder: " + data_folder)
|
|
31
|
-
check_directory(data_folder)
|
|
31
|
+
filemanager.check_directory(data_folder)
|
|
32
32
|
print("> Chosen model: " + MODEL_TYPE)
|
|
33
33
|
data_file = os.path.join(data_folder, MODEL_TYPE + "_samples.tsv")
|
|
34
34
|
|
|
35
|
-
# ################ Select model
|
|
36
|
-
generator = stovolfactory.set_generator(MODEL_TYPE, NUM_EXPIRIES,
|
|
35
|
+
# ################ Select model ###################################################################
|
|
36
|
+
generator = stovolfactory.set_generator(MODEL_TYPE, SHIFT, NUM_EXPIRIES, NUM_STRIKES, NUM_MC,
|
|
37
37
|
POINTS_PER_YEAR, SEED)
|
|
38
38
|
|
|
39
|
+
# ################ Select training ranges #########################################################
|
|
40
|
+
# # SABR
|
|
41
|
+
# RANGES = {'Ttm': [1.0 / 12.0, 35.0], 'K': [0.01, 0.99], '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]}
|
|
43
|
+
# # ZABR
|
|
44
|
+
# RANGES = {'Ttm': [1.0 / 12.0, 35.0], 'K': [0.01, 0.99], 'F': [-0.009, 0.041], 'LnVol': [0.05, 0.25],
|
|
45
|
+
# 'Beta': [0.49, 0.51], 'Nu': [0.20, 0.80], 'Rho': [-0.4, 0.4],
|
|
46
|
+
# 'Gamma': [0.10, 0.9]}
|
|
47
|
+
# Heston
|
|
48
|
+
RANGES = {'Ttm': [1.0 / 12.0, 35.0], 'K': [0.01, 0.99], 'F': [-0.009, 0.041], 'LnVol': [0.05, 0.25],
|
|
49
|
+
'Kappa': [0.25, 4.00], 'Theta': [0.05**2, 0.25**2], 'Xi': [0.10, 0.50],
|
|
50
|
+
'Rho': [-0.40, 0.40]}
|
|
51
|
+
|
|
39
52
|
# ################ Generate dataset ###############################################################
|
|
40
53
|
print(">> Generate dataset")
|
|
41
54
|
|
|
42
55
|
print(f"> Generate {NUM_SAMPLES:,} price samples")
|
|
43
56
|
timer_gen = Stopwatch("Generating Samples")
|
|
44
57
|
timer_gen.trigger()
|
|
45
|
-
data_df = generator.generate_samples(NUM_SAMPLES)
|
|
58
|
+
data_df = generator.generate_samples(NUM_SAMPLES, RANGES)
|
|
59
|
+
# full_data_file = os.path.join(data_folder, MODEL_TYPE + "_samples_full.tsv")
|
|
60
|
+
# generator.to_file(data_df, full_data_file)
|
|
46
61
|
timer_gen.stop()
|
|
47
62
|
|
|
48
63
|
print("> Convert to normal vol and cleanse data")
|
|
@@ -13,31 +13,36 @@ from sdevpy.machinelearning.topology import compose_model
|
|
|
13
13
|
from sdevpy.machinelearning.learningmodel import LearningModel, load_learning_model
|
|
14
14
|
from sdevpy.machinelearning.learningschedules import FlooredExponentialDecay
|
|
15
15
|
from sdevpy.machinelearning.callbacks import RefCallback
|
|
16
|
-
from sdevpy.machinelearning
|
|
17
|
-
from sdevpy.tools
|
|
16
|
+
from sdevpy.machinelearning import datasets
|
|
17
|
+
from sdevpy.tools import filemanager
|
|
18
18
|
from sdevpy.tools.timer import Stopwatch
|
|
19
19
|
from sdevpy.tools import clipboard
|
|
20
|
-
from sdevpy.maths.metrics import
|
|
20
|
+
from sdevpy.maths.metrics import bps_rmse, tf_bps_rmse
|
|
21
21
|
from sdevpy.volsurfacegen.stovolfactory import set_generator
|
|
22
22
|
from sdevpy.projects.stovol import stovolplot as xplt
|
|
23
23
|
|
|
24
|
+
|
|
25
|
+
# Implement DOWNLOAD_DATASET for TRAIN = True
|
|
26
|
+
# Create Colab to generate, Colab to train
|
|
24
27
|
# Fine-train models
|
|
25
|
-
# Use type in json to know which type to instantiate
|
|
26
|
-
# Lazy instantiation from remote/name code
|
|
27
28
|
# Store data in Kaggle
|
|
28
29
|
|
|
29
30
|
# ################ Runtime configuration ##########################################################
|
|
30
|
-
|
|
31
|
-
# MODEL_TYPE = "
|
|
32
|
-
# MODEL_TYPE = "
|
|
33
|
-
MODEL_TYPE = "
|
|
34
|
-
# MODEL_TYPE = "
|
|
35
|
-
|
|
31
|
+
MODEL_TYPE = "SABR"
|
|
32
|
+
# MODEL_TYPE = "McSABR"
|
|
33
|
+
# MODEL_TYPE = "FbSABR"
|
|
34
|
+
# MODEL_TYPE = "McZABR"
|
|
35
|
+
# MODEL_TYPE = "McHeston"
|
|
36
|
+
MODEL_ID = "SABR_3L_64n" # Pre-trained model ID (we can pre-train several versions)
|
|
37
|
+
SHIFT = 0.03
|
|
36
38
|
USE_TRAINED = True
|
|
39
|
+
DOWNLOAD_MODELS = True # Only used when USE_TRAINED is True
|
|
40
|
+
DOWNLOAD_DATASETS = True # Use when already created/downloaded
|
|
37
41
|
TRAIN = False
|
|
38
42
|
if USE_TRAINED is False and TRAIN is False:
|
|
39
43
|
raise RuntimeError("When not using pre-trained models, a new model must be trained")
|
|
40
44
|
|
|
45
|
+
NUM_SAMPLES = 500 * 1000 # Number of samples to read from sample files
|
|
41
46
|
TRAIN_PERCENT = 0.90 # Proportion of dataset used for training (rest used for test)
|
|
42
47
|
EPOCHS = 100
|
|
43
48
|
BATCH_SIZE = 1000
|
|
@@ -47,35 +52,40 @@ NUM_MC = 100 * 1000 # 100 * 1000
|
|
|
47
52
|
POINTS_PER_YEAR = 25 # 25
|
|
48
53
|
|
|
49
54
|
print(">> Set up runtime configuration")
|
|
55
|
+
print("> Chosen model type: " + MODEL_TYPE)
|
|
56
|
+
if USE_TRAINED:
|
|
57
|
+
print("> Pre-trained model ID: " + MODEL_ID)
|
|
58
|
+
|
|
50
59
|
project_folder = os.path.join(settings.WORKFOLDER, "stovol")
|
|
51
60
|
print("> Project folder: " + project_folder)
|
|
52
|
-
|
|
61
|
+
sample_folder = os.path.join(project_folder, "samples")
|
|
62
|
+
data_folder = os.path.join(sample_folder, MODEL_TYPE)
|
|
53
63
|
print("> Data folder: " + data_folder)
|
|
54
|
-
|
|
55
|
-
print(">
|
|
56
|
-
data_file = os.path.join(data_folder, MODEL_TYPE + "_samples.tsv")
|
|
64
|
+
data_file = os.path.join(sample_folder, MODEL_TYPE + "_samples.tsv")
|
|
65
|
+
print("> Data file: " + data_file)
|
|
57
66
|
model_folder = os.path.join(project_folder, "models")
|
|
58
67
|
print("> Model folder: " + model_folder)
|
|
59
68
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
"
|
|
63
|
-
|
|
69
|
+
if USE_TRAINED and DOWNLOAD_MODELS:
|
|
70
|
+
url = 'https://github.com/sebgur/SDev.Python/raw/main/models/stovol/stovol.zip'
|
|
71
|
+
print("> Downloading and unzipping models from: " + url)
|
|
72
|
+
filemanager.download_unzip(url, model_folder)
|
|
64
73
|
|
|
65
|
-
|
|
66
|
-
"
|
|
67
|
-
return 10000.0 * tf_rmse(y_true, y_ref)
|
|
74
|
+
if DOWNLOAD_DATASETS:
|
|
75
|
+
print("> Downloading datasets from: ")
|
|
68
76
|
|
|
69
77
|
# ################ Select generator ###############################################################
|
|
70
78
|
# Select generator. The number of expiries and surface size are irrelevant as here we do not
|
|
71
79
|
# generate sample data but read it from files. Number of MC and points per year are required
|
|
72
80
|
# to calculate the reference values against which we can validate the model.
|
|
73
|
-
generator = set_generator(MODEL_TYPE, num_mc=NUM_MC, points_per_year=POINTS_PER_YEAR)
|
|
81
|
+
generator = set_generator(MODEL_TYPE, shift=SHIFT, num_mc=NUM_MC, points_per_year=POINTS_PER_YEAR)
|
|
74
82
|
|
|
75
83
|
# ################ Prepare datasets ###############################################################
|
|
76
84
|
# Datasets are always read, as even if we don't train, we're still going to evaluate the
|
|
77
85
|
# performance of the pre-trained model
|
|
78
86
|
print(">> Preparing datasets")
|
|
87
|
+
# Retrieve data from dataset folder
|
|
88
|
+
datasets.retrieve_data(data_folder, NUM_SAMPLES, shuffle=True, export_file=data_file)
|
|
79
89
|
# Retrieve dataset
|
|
80
90
|
print("> Reading dataset from file: " + data_file)
|
|
81
91
|
x_set, y_set, data_df = generator.retrieve_datasets(data_file, shuffle=True)
|
|
@@ -88,13 +98,15 @@ print(data_df.head())
|
|
|
88
98
|
# Split into training and test sets
|
|
89
99
|
TRS = TRAIN_PERCENT * 100
|
|
90
100
|
print(f"> Splitting between training set ({TRS:.2f}%) and test set ({100 - TRS:.2f}%)")
|
|
91
|
-
x_train, y_train, x_test, y_test = prepare_sets(x_set, y_set, TRAIN_PERCENT)
|
|
101
|
+
x_train, y_train, x_test, y_test = datasets.prepare_sets(x_set, y_set, TRAIN_PERCENT)
|
|
102
|
+
print(f"> Training set size: {x_train.shape[0]:,}")
|
|
103
|
+
print(f"> Testing set size: {x_test.shape[0]:,}")
|
|
92
104
|
|
|
93
105
|
# ################ Compose/Load the model #########################################################
|
|
94
106
|
# Compose new model or load pre-trained one
|
|
95
107
|
if USE_TRAINED:
|
|
96
108
|
print(">> Loading pre-trained model")
|
|
97
|
-
model_folder_name = os.path.join(model_folder,
|
|
109
|
+
model_folder_name = os.path.join(model_folder, MODEL_ID)
|
|
98
110
|
print("> Loading pre-trained model from: " + model_folder_name)
|
|
99
111
|
model = load_learning_model(model_folder_name)
|
|
100
112
|
keras_model = model.model
|
|
@@ -138,26 +150,19 @@ if TRAIN:
|
|
|
138
150
|
for field, value in optim_fields.items():
|
|
139
151
|
print("> ", field, ":", value)
|
|
140
152
|
|
|
141
|
-
|
|
142
153
|
# Compile
|
|
143
154
|
print("> Compile model")
|
|
144
155
|
keras_model.compile(loss=tf_bps_rmse, optimizer=optimizer)
|
|
145
156
|
|
|
146
|
-
|
|
147
157
|
# Callbacks
|
|
148
158
|
EPOCH_SAMPLING = 5
|
|
149
159
|
callback = RefCallback(x_test, y_test, bps_rmse, optimizer=optimizer,
|
|
150
160
|
epoch_sampling=EPOCH_SAMPLING)
|
|
151
|
-
# callback = None
|
|
152
|
-
# callback = SDevPyCallback(optimizer=optimizer, epoch_sampling=EPOCH_SAMPLING)
|
|
153
161
|
|
|
154
162
|
# Train the network
|
|
155
163
|
print(">> Training ANN model")
|
|
156
164
|
trn_timer = Stopwatch("Training")
|
|
157
165
|
trn_timer.trigger()
|
|
158
|
-
# shuffled_indices = np.random.permutation(x_train.shape[0])
|
|
159
|
-
# x_train = x_train[shuffled_indices]
|
|
160
|
-
# y_train = y_train[shuffled_indices]
|
|
161
166
|
model.train(x_train, y_train, EPOCHS, BATCH_SIZE, callback)
|
|
162
167
|
trn_timer.stop()
|
|
163
168
|
trn_timer.print()
|
|
@@ -177,22 +182,23 @@ print(">> Analyse results")
|
|
|
177
182
|
# Check performance
|
|
178
183
|
train_pred = model.predict(x_train)
|
|
179
184
|
train_rmse = bps_rmse(train_pred, y_train)
|
|
180
|
-
print(f"RMSE(nvol) on training set: {train_rmse:,.2f}")
|
|
185
|
+
print(f"> RMSE(nvol) on training set: {train_rmse:,.2f}")
|
|
181
186
|
|
|
182
187
|
test_pred = model.predict(x_test)
|
|
183
188
|
test_rmse = bps_rmse(test_pred, y_test)
|
|
184
|
-
print(f"RMSE(nvol) on test set: {test_rmse:,.2f}")
|
|
189
|
+
print(f"> RMSE(nvol) on test set: {test_rmse:,.2f}")
|
|
185
190
|
|
|
186
191
|
# Generate strike spread axis
|
|
187
192
|
if SHOW_VOL_CHARTS:
|
|
193
|
+
print("> Choosing a sample parameter set to display chart")
|
|
188
194
|
NUM_STRIKES = 100
|
|
189
195
|
PARAMS = { 'LnVol': 0.20, 'Beta': 0.5, 'Nu': 0.55, 'Rho': -0.25, 'Gamma': 0.7, 'Kappa': 1.0,
|
|
190
196
|
'Theta': 0.05, 'Xi': 0.50 }
|
|
191
197
|
FWD = 0.028
|
|
192
198
|
|
|
193
199
|
# Any number of expiries can be calculated, but for optimum display choose no more than 6
|
|
194
|
-
EXPIRIES = np.asarray([0.125, 0.250, 0.5, 1.00, 2.0, 5.0]).reshape(-1, 1)
|
|
195
|
-
|
|
200
|
+
# EXPIRIES = np.asarray([0.125, 0.250, 0.5, 1.00, 2.0, 5.0]).reshape(-1, 1)
|
|
201
|
+
EXPIRIES = np.asarray([0.25, 0.50, 1.0, 5.00, 10.0, 30.0]).reshape(-1, 1)
|
|
196
202
|
NUM_EXPIRIES = EXPIRIES.shape[0]
|
|
197
203
|
METHOD = 'Percentiles'
|
|
198
204
|
PERCENTS = np.linspace(0.01, 0.99, num=NUM_STRIKES)
|
|
@@ -204,15 +210,23 @@ if SHOW_VOL_CHARTS:
|
|
|
204
210
|
# ARE_CALLS = [[False if s < FWD else True for s in expks] for expks in strikes] # Puts/calls
|
|
205
211
|
# print(ARE_CALLS)
|
|
206
212
|
|
|
207
|
-
print("Calculating chart surface with reference model")
|
|
213
|
+
print("> Calculating chart surface with reference model")
|
|
214
|
+
timer_ref = Stopwatch("Reference surface calculation")
|
|
215
|
+
timer_ref.trigger()
|
|
208
216
|
ref_prices = generator.price_surface_ref(EXPIRIES, strikes, ARE_CALLS, FWD, PARAMS)
|
|
209
|
-
|
|
217
|
+
timer_ref.stop()
|
|
210
218
|
# clipboard.export2d(ref_prices)
|
|
211
|
-
print("Calculating chart surface with trained model")
|
|
219
|
+
print("> Calculating chart surface with trained model")
|
|
220
|
+
timer_mod = Stopwatch("Model surface calculation")
|
|
221
|
+
timer_mod.trigger()
|
|
212
222
|
mod_prices = generator.price_surface_mod(model, EXPIRIES, strikes, ARE_CALLS, FWD, PARAMS)
|
|
213
|
-
|
|
223
|
+
timer_mod.stop()
|
|
214
224
|
# clipboard.export2d(mod_prices)
|
|
215
|
-
print(f"Ref-Mod RMSE(price): {bps_rmse(ref_prices, mod_prices):.2f}")
|
|
225
|
+
print(f"> Ref-Mod RMSE(price): {bps_rmse(ref_prices, mod_prices):.2f}")
|
|
226
|
+
|
|
227
|
+
# Display timers
|
|
228
|
+
timer_ref.print()
|
|
229
|
+
timer_mod.print()
|
|
216
230
|
|
|
217
231
|
# Available tranforms: Price, ShiftedBlackScholes, Bachelier
|
|
218
232
|
TITLE = f"{MODEL_TYPE} smile sections, forward={FWD*100:.2f}"#,%\n parameters={PARAMS}"
|
|
@@ -1,16 +1,116 @@
|
|
|
1
1
|
""" Just to test things """
|
|
2
2
|
# import tensorflow as tf
|
|
3
|
-
import numpy as np
|
|
4
|
-
import scipy.stats as sp
|
|
5
|
-
import json
|
|
6
|
-
import matplotlib.pyplot as plt
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
# import numpy as np
|
|
4
|
+
# import scipy.stats as sp
|
|
5
|
+
# import json
|
|
6
|
+
# import matplotlib.pyplot as plt
|
|
7
|
+
import requests, zipfile
|
|
8
|
+
import io
|
|
9
|
+
import pandas as pd
|
|
10
|
+
from io import BytesIO
|
|
11
|
+
|
|
12
|
+
# URL = 'https://drive.google.com/file/d/10dKi82fW2arlKnOahNv9i5igfiydwMnc/view?usp=sharing'
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# def download_file_from_google_drive(id, destination):
|
|
16
|
+
# # URL = "https://docs.google.com/uc?export=download"
|
|
17
|
+
|
|
18
|
+
# session = requests.Session()
|
|
19
|
+
|
|
20
|
+
# print(URL)
|
|
21
|
+
# print(id)
|
|
22
|
+
# response = session.get(URL, params = { 'id' : id }, stream = True)
|
|
23
|
+
# print(response)
|
|
24
|
+
# token = get_confirm_token(response)
|
|
25
|
+
# print(token)
|
|
26
|
+
|
|
27
|
+
# if token:
|
|
28
|
+
# params = { 'id' : id, 'confirm' : token }
|
|
29
|
+
# response = session.get(URL, params = params, stream = True)
|
|
30
|
+
|
|
31
|
+
# save_response_content(response, destination)
|
|
32
|
+
|
|
33
|
+
# def get_confirm_token(response):
|
|
34
|
+
# for key, value in response.cookies.items():
|
|
35
|
+
# if key.startswith('download_warning'):
|
|
36
|
+
# return value
|
|
37
|
+
|
|
38
|
+
# return None
|
|
39
|
+
|
|
40
|
+
# def save_response_content(response, destination):
|
|
41
|
+
# CHUNK_SIZE = 32768
|
|
42
|
+
|
|
43
|
+
# with open(destination, "wb") as f:
|
|
44
|
+
# for chunk in response.iter_content(CHUNK_SIZE):
|
|
45
|
+
# if chunk: # filter out keep-alive new chunks
|
|
46
|
+
# f.write(chunk)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# id = '10dKi82fW2arlKnOahNv9i5igfiydwMnc'
|
|
51
|
+
|
|
52
|
+
# destination_ = ''
|
|
53
|
+
|
|
54
|
+
# download_file_from_google_drive(id, destination_)
|
|
55
|
+
|
|
56
|
+
# sample_url = 'https://1drv.ms/u/s!AivreF7B9rL4kK8Hx1vT4PRjtbE1iA?e=fxM7Kx' # OneDrive
|
|
57
|
+
|
|
58
|
+
# req = requests.get(sample_url)
|
|
59
|
+
|
|
60
|
+
# # Extract
|
|
61
|
+
# zipfile = zipfile.ZipFile(BytesIO(req.content))
|
|
62
|
+
# zipfile.extractall('New Folder')
|
|
63
|
+
|
|
64
|
+
# url = 'https://github.com/sebgur/SDev.Python/raw/main/models/stovol/SABR.zip'
|
|
65
|
+
|
|
66
|
+
# MODEL_NAME = 'SABR'
|
|
67
|
+
# OUPUT_ROOT = r'C:\temp\sdevpy\stovol\models'
|
|
68
|
+
|
|
69
|
+
# base_url = 'https://github.com/sebgur/SDev.Python/raw/main/models/stovol/'
|
|
70
|
+
# model_url = base_url + MODEL_NAME + ".zip"
|
|
71
|
+
|
|
72
|
+
# req = requests.get(model_url)
|
|
73
|
+
|
|
74
|
+
# filename = url.split('/')[-1]
|
|
75
|
+
# print("Downloading: " + filename)
|
|
76
|
+
|
|
77
|
+
# # Download
|
|
78
|
+
# with open(filename,'wb') as output_file:
|
|
79
|
+
# output_file.write(req.content)
|
|
80
|
+
# print('Downloading Completed')
|
|
81
|
+
|
|
82
|
+
# # Extract
|
|
83
|
+
# zipfile = zipfile.ZipFile(BytesIO(req.content))
|
|
84
|
+
# zipfile.extractall(OUPUT_ROOT)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# url = "https://raw.githubusercontent.com/sebgur/SDev.Python/main/samples/McHeston_samples.tsv"
|
|
88
|
+
|
|
89
|
+
# https://github.com/sebgur/SDev.Python/blob/main/models/stovol/SABR/config.json
|
|
90
|
+
# https://raw.githubusercontent.com/sebgur/SDev.Python/main/models/stovol/SABR/config.json
|
|
91
|
+
|
|
92
|
+
# https://github.com/sebgur/SDev.Python/tree/main/models/stovol
|
|
93
|
+
# https://raw.githubusercontent.com/sebgur/SDev.Python/main/models/stovol
|
|
94
|
+
|
|
95
|
+
# download = requests.get(url).content
|
|
96
|
+
|
|
97
|
+
# df = pd.read_csv(io.StringIO(download.decode('utf-8')), sep='\t')
|
|
98
|
+
# print(df.head())
|
|
99
|
+
|
|
100
|
+
# parameters = { 'LnVol2': 0.2, 'Beta': 0.5}
|
|
101
|
+
|
|
102
|
+
# # print(parameters['LnVol'])
|
|
103
|
+
|
|
104
|
+
# if 'LnVol' in parameters:
|
|
105
|
+
# print("found it")
|
|
106
|
+
# else:
|
|
107
|
+
# print("boooo")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# strikes = [1, 2, 3, 4, 5]
|
|
111
|
+
# fwd = 2.5
|
|
112
|
+
# are_calls = [False if s < fwd else True for s in strikes]
|
|
113
|
+
# print(are_calls)
|
|
14
114
|
|
|
15
115
|
# num_expiries = 2
|
|
16
116
|
# num_strikes = 3
|
|
@@ -1,9 +1,27 @@
|
|
|
1
1
|
""" File management utilities """
|
|
2
2
|
import os
|
|
3
3
|
import csv
|
|
4
|
-
import
|
|
5
|
-
import datetime as dt
|
|
4
|
+
# import datetime as dt
|
|
6
5
|
import pathlib
|
|
6
|
+
from io import BytesIO
|
|
7
|
+
import zipfile as zf
|
|
8
|
+
import requests
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def download_unzip(zip_url, extract_folder, save_file=False):
|
|
12
|
+
""" Download zip file from url and unzip """
|
|
13
|
+
req = requests.get(zip_url, timeout=10)
|
|
14
|
+
|
|
15
|
+
if save_file:
|
|
16
|
+
down_filename = zip_url.split('/')[-1]
|
|
17
|
+
with open(down_filename,'wb') as output_file:
|
|
18
|
+
output_file.write(req.content)
|
|
19
|
+
|
|
20
|
+
with zf.ZipFile(BytesIO(req.content)) as zip_file:
|
|
21
|
+
zip_file.extractall(extract_folder)
|
|
22
|
+
|
|
23
|
+
# zipfile = zf.ZipFile(BytesIO(req.content))
|
|
24
|
+
# zipfile.extractall(extract_folder)
|
|
7
25
|
|
|
8
26
|
|
|
9
27
|
def check_directory(path):
|
|
@@ -31,6 +49,7 @@ def write_csv(file):
|
|
|
31
49
|
|
|
32
50
|
# return files
|
|
33
51
|
|
|
52
|
+
|
|
34
53
|
def list_files(path, extensions=None):
|
|
35
54
|
""" List all files in a path that have the extensions """
|
|
36
55
|
all_files = os.listdir(path)
|
|
@@ -43,17 +62,3 @@ def list_files(path, extensions=None):
|
|
|
43
62
|
files.append(f)
|
|
44
63
|
|
|
45
64
|
return files
|
|
46
|
-
|
|
47
|
-
def make_a_noise(beep=True):
|
|
48
|
-
""" Make a noise """
|
|
49
|
-
if beep:
|
|
50
|
-
f1 = 500
|
|
51
|
-
f2 = 1000
|
|
52
|
-
duration = 300
|
|
53
|
-
winsound.Beep(f1, duration)
|
|
54
|
-
winsound.Beep(f2, duration)
|
|
55
|
-
winsound.Beep(f1, duration)
|
|
56
|
-
|
|
57
|
-
now = dt.datetime.now()
|
|
58
|
-
dt_string = now.strftime("%H:%M:%S, %d/%m/%Y")
|
|
59
|
-
print("Closing at ", dt_string)
|
|
@@ -24,7 +24,7 @@ class TimeGridBuilder(ABC):
|
|
|
24
24
|
|
|
25
25
|
def add_grid(self, times):
|
|
26
26
|
""" Add vector of times """
|
|
27
|
-
self.time_grid_.extend(times)
|
|
27
|
+
self.time_grid_.extend(times.reshape(-1))
|
|
28
28
|
|
|
29
29
|
def refine(self):
|
|
30
30
|
""" Add a fine grid to the current grid """
|
|
@@ -82,8 +82,24 @@ if __name__ == "__main__":
|
|
|
82
82
|
settlement = date(2026, 1, 24)
|
|
83
83
|
builder = SimpleTimeGridBuilder(5)
|
|
84
84
|
builder.add_dates(base, [fixing, settlement, expiry, monitor, settlement])
|
|
85
|
-
print(builder.time_grid_)
|
|
85
|
+
# print(builder.time_grid_)
|
|
86
86
|
builder.refine()
|
|
87
|
-
print(builder.time_grid_)
|
|
87
|
+
# print(builder.time_grid_)
|
|
88
88
|
builder.clean()
|
|
89
|
-
print(builder.time_grid_)
|
|
89
|
+
# print(builder.time_grid_)
|
|
90
|
+
|
|
91
|
+
# Test MC situation
|
|
92
|
+
time_grid_builder = SimpleTimeGridBuilder(points_per_year=5)
|
|
93
|
+
EXPIRIES = np.asarray([5.0, 1.0, 0.125, 0.250, 0.5]).reshape(-1, 1)
|
|
94
|
+
print(EXPIRIES)
|
|
95
|
+
time_grid_builder.add_grid(EXPIRIES)
|
|
96
|
+
print(time_grid_builder.time_grid_)
|
|
97
|
+
print("refine")
|
|
98
|
+
time_grid_builder.refine()
|
|
99
|
+
print(time_grid_builder.time_grid_)
|
|
100
|
+
print("clean")
|
|
101
|
+
time_grid_builder.clean()
|
|
102
|
+
tg = time_grid_builder.time_grid_
|
|
103
|
+
print(tg)
|
|
104
|
+
# time_grid = time_grid_builder.complete_grid()
|
|
105
|
+
|