sdevpy 1.0.4__tar.gz → 1.0.5__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.4/src/sdevpy.egg-info → sdevpy-1.0.5}/PKG-INFO +2 -4
- {sdevpy-1.0.4 → sdevpy-1.0.5}/pyproject.toml +2 -2
- sdevpy-1.0.5/src/sdevpy/__init__.py +1 -0
- sdevpy-1.0.5/src/sdevpy/analytics/americantree.py +103 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/analytics/black.py +45 -45
- sdevpy-1.0.5/src/sdevpy/analytics/tf_black.py +52 -0
- sdevpy-1.0.5/src/sdevpy/cointegration/back_testing.py +233 -0
- sdevpy-1.0.5/src/sdevpy/cointegration/black_analytics.py +45 -0
- sdevpy-1.0.5/src/sdevpy/cointegration/coint_trading.py +839 -0
- sdevpy-1.0.5/src/sdevpy/cointegration/data_io.py +225 -0
- sdevpy-1.0.5/src/sdevpy/cointegration/implied_vol.py +116 -0
- sdevpy-1.0.5/src/sdevpy/cointegration/mean_reversion.py +279 -0
- sdevpy-1.0.5/src/sdevpy/cointegration/model_settings.py +33 -0
- sdevpy-1.0.5/src/sdevpy/cointegration/plotting.py +296 -0
- sdevpy-1.0.5/src/sdevpy/cointegration/run_unit_test.py +572 -0
- sdevpy-1.0.5/src/sdevpy/cointegration/utils.py +477 -0
- sdevpy-1.0.5/src/sdevpy/llms/ModelConverter.py +268 -0
- sdevpy-1.0.5/src/sdevpy/llms/attention.py +139 -0
- sdevpy-1.0.5/src/sdevpy/llms/chat.py +35 -0
- sdevpy-1.0.5/src/sdevpy/llms/datasets.py +30 -0
- sdevpy-1.0.5/src/sdevpy/llms/gpt.py +333 -0
- sdevpy-1.0.5/src/sdevpy/llms/instructions.py +145 -0
- sdevpy-1.0.5/src/sdevpy/llms/textgen.py +167 -0
- sdevpy-1.0.5/src/sdevpy/llms/tokenizers.py +37 -0
- sdevpy-1.0.5/src/sdevpy/llms/training.py +100 -0
- sdevpy-1.0.5/src/sdevpy/maths/sets.py +16 -0
- sdevpy-1.0.5/src/sdevpy/projects/chat_gpt2.py +76 -0
- sdevpy-1.0.5/src/sdevpy/projects/raschka/ch2_working_with_text.py +161 -0
- sdevpy-1.0.5/src/sdevpy/projects/raschka/ch3_coding_attention.py +259 -0
- sdevpy-1.0.5/src/sdevpy/projects/raschka/ch4_gpt_model.py +200 -0
- sdevpy-1.0.5/src/sdevpy/projects/raschka/ch5_loadGPT2.py +119 -0
- sdevpy-1.0.5/src/sdevpy/projects/raschka/ch5_pretraining.py +263 -0
- sdevpy-1.0.5/src/sdevpy/projects/raschka/ch7_instruction_finetuning.py +26 -0
- sdevpy-1.0.5/src/sdevpy/projects/raschka/raschka_datasetloader.py +80 -0
- sdevpy-1.0.5/src/sdevpy/projects/raschka/raschka_dnn.py +40 -0
- sdevpy-1.0.5/src/sdevpy/projects/raschka/raschka_gpt_download.py +155 -0
- sdevpy-1.0.5/src/sdevpy/projects/set_limits.py +27 -0
- sdevpy-1.0.5/src/sdevpy/projects/update_db.py +31 -0
- sdevpy-1.0.5/src/sdevpy/settings.py +57 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/test.py +118 -17
- sdevpy-1.0.5/src/sdevpy/timeseries/backtesting.py +91 -0
- sdevpy-1.0.5/src/sdevpy/timeseries/cointegration.py +253 -0
- sdevpy-1.0.5/src/sdevpy/timeseries/meanreversion.py +196 -0
- sdevpy-1.0.5/src/sdevpy/timeseries/timeseriestools.py +140 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/tools/jsonmanager.py +14 -0
- sdevpy-1.0.5/src/sdevpy/tools/network.py +92 -0
- sdevpy-1.0.5/src/sdevpy/tools/pydotnet.py +70 -0
- sdevpy-1.0.5/src/sdevpy/tools/utils.py +42 -0
- sdevpy-1.0.5/src/sdevpy/tree/trees.py +162 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5/src/sdevpy.egg-info}/PKG-INFO +2 -4
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy.egg-info/SOURCES.txt +41 -1
- sdevpy-1.0.4/LICENSE +0 -21
- sdevpy-1.0.4/src/sdevpy/__init__.py +0 -1
- sdevpy-1.0.4/src/sdevpy/settings.py +0 -22
- sdevpy-1.0.4/src/sdevpy/tools/utils.py +0 -7
- {sdevpy-1.0.4 → sdevpy-1.0.5}/README.md +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/setup.cfg +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/analytics/bachelier.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/analytics/fbsabr.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/analytics/mcheston.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/analytics/mcsabr.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/analytics/mczabr.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/analytics/sabr.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/machinelearning/callbacks.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/machinelearning/datasets.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/machinelearning/learningmodel.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/machinelearning/learningschedules.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/machinelearning/topology.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/maths/interpolations.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/maths/metrics.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/maths/optimization.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/maths/rand.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/montecarlo/smoothers.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/projects/aad/aad_mc.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/projects/aad/aad_mc_nd.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/projects/datafiles.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/projects/stovol/stovolgen.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/projects/stovol/stovolplot.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/projects/stovol/stovoltrain.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/projects/stovolinverse/stovolinvgen.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/projects/stovolinverse/stovolinvtrain.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_lets_be_rational/__init__.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_lets_be_rational/constants.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_lets_be_rational/erf_cody.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_lets_be_rational/exceptions.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_lets_be_rational/lets_be_rational.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_lets_be_rational/normaldistribution.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_lets_be_rational/numba_helper.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_lets_be_rational/rationalcubic.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/__init__.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black/__init__.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black/greeks/__init__.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black/greeks/analytical.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black/greeks/numerical.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black/implied_volatility.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black_scholes/__init__.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black_scholes/greeks/__init__.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black_scholes/greeks/analytical.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black_scholes/greeks/numerical.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black_scholes/implied_volatility.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black_scholes_merton/__init__.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/__init__.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/analytical.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/numerical.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black_scholes_merton/implied_volatility.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/helpers/__init__.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/helpers/constants.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/helpers/distributions.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/helpers/doctest_helper.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/helpers/exceptions.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/helpers/numerical_greeks.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/__init__.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black/__init__.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/__init__.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/analytical.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/numerical.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black/implied_volatility.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/__init__.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/__init__.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/analytical.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/numerical.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/implied_volatility.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/__init__.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/__init__.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/analytical.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/numerical.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/implied_volatility.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/tools/clipboard.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/tools/constants.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/tools/filemanager.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/tools/timegrids.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/tools/timer.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/volsurfacegen/fbsabrgenerator.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/volsurfacegen/mchestongenerator.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/volsurfacegen/mcsabrgenerator.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/volsurfacegen/mczabrgenerator.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/volsurfacegen/sabrgenerator.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/volsurfacegen/smilegenerator.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/volsurfacegen/stovolfactory.py +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy.egg-info/dependency_links.txt +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy.egg-info/requires.txt +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy.egg-info/top_level.txt +0 -0
- {sdevpy-1.0.4 → sdevpy-1.0.5}/tests/test.py +0 -0
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: sdevpy
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.5
|
|
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
|
|
7
7
|
Project-URL: SDev Finance, http://sdev-finance.com/
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
10
9
|
Classifier: Operating System :: OS Independent
|
|
11
10
|
Requires-Python: >=3.6
|
|
12
11
|
Description-Content-Type: text/markdown
|
|
13
|
-
License-File: LICENSE
|
|
14
12
|
Requires-Dist: pandas
|
|
15
13
|
Requires-Dist: pyperclip
|
|
16
14
|
Requires-Dist: numpy
|
|
@@ -4,7 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sdevpy"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.5"
|
|
8
|
+
license-files = []
|
|
8
9
|
authors = [
|
|
9
10
|
{ name="Sebastien Gurrieri", email="sebgur@gmail.com" },
|
|
10
11
|
]
|
|
@@ -13,7 +14,6 @@ readme = "README.md"
|
|
|
13
14
|
requires-python = ">=3.6"
|
|
14
15
|
classifiers = [
|
|
15
16
|
"Programming Language :: Python :: 3",
|
|
16
|
-
"License :: OSI Approved :: MIT License",
|
|
17
17
|
"Operating System :: OS Independent",
|
|
18
18
|
]
|
|
19
19
|
dependencies = [
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '1.0.5'
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
""" Simple example of binomial tree to value American options in Black-Scholes """
|
|
2
|
+
import numpy as np
|
|
3
|
+
import time
|
|
4
|
+
import matplotlib.pyplot as plt
|
|
5
|
+
from sdevpy.analytics import black
|
|
6
|
+
from sdevpy.tree import trees
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def option_price(ttm, strike, is_call, is_american, spot, vol, rf_rate, div_rate, disc_rate,
|
|
10
|
+
method='trinomial', n_steps = 30):
|
|
11
|
+
""" Price of an American option using binomial or trinomial trees under Black-Scholes model """
|
|
12
|
+
payoff = Payoff(ttm, strike, is_call, is_american)
|
|
13
|
+
return price(payoff, spot, vol, rf_rate, div_rate, disc_rate, method, n_steps)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def price(payoff, spot, vol, rf_rate, div_rate, disc_rate,
|
|
17
|
+
method='trinomial', n_steps = 30):
|
|
18
|
+
""" Price of a payoff using binomial or trinomial trees under Black-Scholes model """
|
|
19
|
+
if method == 'binomial':
|
|
20
|
+
tree = trees.BinomialTree(n_steps)
|
|
21
|
+
else:
|
|
22
|
+
tree = trees.TrinomialTree(n_steps)
|
|
23
|
+
|
|
24
|
+
pv = tree.price(payoff, spot, vol, rf_rate, div_rate, disc_rate)
|
|
25
|
+
|
|
26
|
+
return pv
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
if __name__ == "__main__":
|
|
30
|
+
ttm = 2.5
|
|
31
|
+
strike = 106.0
|
|
32
|
+
is_call = False
|
|
33
|
+
is_american = False
|
|
34
|
+
vol = 0.20
|
|
35
|
+
spot = 100.0
|
|
36
|
+
rf_rate = 0.05
|
|
37
|
+
div_rate = 0.02
|
|
38
|
+
disc_rate = 0.03
|
|
39
|
+
payoff = trees.Payoff(ttm, strike, is_call, is_american)
|
|
40
|
+
|
|
41
|
+
repeat = 10
|
|
42
|
+
# bin_range = range(50, 54, 2)
|
|
43
|
+
# tri_range = range(20, 24, 2)
|
|
44
|
+
bin_range = range(200, 2000, 50)
|
|
45
|
+
tri_range = range(100, 1000, 25)
|
|
46
|
+
|
|
47
|
+
# Binomial
|
|
48
|
+
print("\nCalculating Binomial")
|
|
49
|
+
bin_p = []
|
|
50
|
+
bin_t = []
|
|
51
|
+
for n_steps in bin_range:
|
|
52
|
+
start = time.time()
|
|
53
|
+
for _ in range(repeat):
|
|
54
|
+
p = price(payoff, spot, vol, rf_rate, div_rate, disc_rate, 'binomial', n_steps)
|
|
55
|
+
bin_t.append(time.time() - start)
|
|
56
|
+
print(p)
|
|
57
|
+
bin_p.append(p)
|
|
58
|
+
|
|
59
|
+
# Trinomial
|
|
60
|
+
print("\nCalculating Trinomial")
|
|
61
|
+
tri_p = []
|
|
62
|
+
tri_t = []
|
|
63
|
+
for n_steps in tri_range:
|
|
64
|
+
start = time.time()
|
|
65
|
+
for _ in range(repeat):
|
|
66
|
+
p = price(payoff, spot, vol, rf_rate, div_rate, disc_rate, 'trinomial', n_steps)
|
|
67
|
+
tri_t.append(time.time() - start)
|
|
68
|
+
print(p)
|
|
69
|
+
tri_p.append(p)
|
|
70
|
+
|
|
71
|
+
print(f"Binomial(price): {bin_p[-1]:.4f}")
|
|
72
|
+
print(f"Binomial(time): {bin_t[-1]:.4f}")
|
|
73
|
+
print(f"Trinomial(price): {tri_p[-1]:.4f}")
|
|
74
|
+
print(f"Trinomial(time): {tri_t[-1]:.4f}")
|
|
75
|
+
|
|
76
|
+
# Closed-Form
|
|
77
|
+
if not payoff.is_american:
|
|
78
|
+
fwd = spot * np.exp((rf_rate - div_rate) * ttm)
|
|
79
|
+
cf = np.exp(-disc_rate * ttm) * black.price(ttm, strike, is_call, fwd, vol)
|
|
80
|
+
print(f"Vanilla CF: {cf:.4f}")
|
|
81
|
+
cf_t = np.linspace(0, tri_t[-1], 100)
|
|
82
|
+
cf_p = [0.0] * 100 # Error 0 for vanilla
|
|
83
|
+
|
|
84
|
+
# Reframe as relative errors
|
|
85
|
+
bin_p = [100.0 * (x / cf - 1.0) for x in bin_p]
|
|
86
|
+
tri_p = [100.0 * (x / cf - 1.0) for x in tri_p]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# Plot the results
|
|
90
|
+
plt.figure(figsize=(10, 6))
|
|
91
|
+
plt.plot(bin_t, bin_p, label='Binomial Tree Price')
|
|
92
|
+
plt.plot(tri_t, tri_p, label='Trinomial Tree Price')
|
|
93
|
+
if not payoff.is_american:
|
|
94
|
+
plt.plot(cf_t, cf_p, label='Vanilla CF Price')
|
|
95
|
+
# # plt.plot(steps_range, trinomial_prices, label='Trinomial Tree Price')
|
|
96
|
+
# plt.hlines(bs_price, steps_range[0], steps_range[-1], colors='r', linestyles='dashed', label='Black-Scholes Price')
|
|
97
|
+
plt.title('Convergence to Black-Scholes Price for Call Options')
|
|
98
|
+
plt.xlabel('Runtime')
|
|
99
|
+
plt.ylabel('Option Price')
|
|
100
|
+
plt.legend()
|
|
101
|
+
plt.show()
|
|
102
|
+
|
|
103
|
+
|
|
@@ -2,15 +2,13 @@
|
|
|
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
|
|
6
5
|
from sdevpy.thirdparty.py_vollib.black import implied_volatility as jaeckel
|
|
7
|
-
import tensorflow as tf
|
|
8
|
-
import tensorflow_probability as tfp
|
|
6
|
+
# import tensorflow as tf
|
|
7
|
+
# import tensorflow_probability as tfp
|
|
9
8
|
from sdevpy import settings
|
|
10
9
|
|
|
11
10
|
N = scipy.stats.norm.cdf
|
|
12
11
|
# Ninv = scipy.stats.norm.ppf
|
|
13
|
-
tf_N = tfp.distributions.Normal(0.0, 1.0)
|
|
14
12
|
|
|
15
13
|
|
|
16
14
|
def price(expiry, strike, is_call, fwd, vol):
|
|
@@ -21,6 +19,7 @@ def price(expiry, strike, is_call, fwd, vol):
|
|
|
21
19
|
d2 = d1 - s
|
|
22
20
|
return w * (fwd * N(w * d1) - strike * N(w * d2))
|
|
23
21
|
|
|
22
|
+
|
|
24
23
|
def implied_vol_jaeckel(expiry, strike, is_call, fwd, fwd_price):
|
|
25
24
|
""" Black-Scholes implied volatility using P. Jaeckel's 'Let's be rational' method,
|
|
26
25
|
from package py_vollib. Install with pip install py_vollib or at
|
|
@@ -31,6 +30,7 @@ def implied_vol_jaeckel(expiry, strike, is_call, fwd, fwd_price):
|
|
|
31
30
|
iv = jaeckel.implied_volatility_of_undiscounted_option_price(p, fwd, strike, expiry, flag)
|
|
32
31
|
return iv
|
|
33
32
|
|
|
33
|
+
|
|
34
34
|
def implied_vol(expiry, strike, is_call, fwd, fwd_price):
|
|
35
35
|
""" Direct method by numerical inversion using Brent """
|
|
36
36
|
options = {'xtol': 1e-4, 'maxiter': 100, 'disp': False}
|
|
@@ -45,47 +45,47 @@ def implied_vol(expiry, strike, is_call, fwd, fwd_price):
|
|
|
45
45
|
|
|
46
46
|
return res.x
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
def price_and_greeks(expiry, strike, spot, vol, rate, div):
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
48
|
+
# Moved to tf_black to avoid slow loading of tf when not needed
|
|
49
|
+
# def price_and_greeks(expiry, strike, spot, vol, rate, div):
|
|
50
|
+
# """ Calculate call PV and sensitivities by AAD on Black-Scholes CF """
|
|
51
|
+
# tf_spot = tf.convert_to_tensor(spot, dtype='float32')
|
|
52
|
+
# tf_vol = tf.convert_to_tensor(vol)
|
|
53
|
+
# tf_time = tf.convert_to_tensor(expiry, dtype='float32')
|
|
54
|
+
# tf_rate = tf.convert_to_tensor(rate, dtype='float32')
|
|
55
|
+
# tf_div = tf.constant(div)
|
|
56
|
+
|
|
57
|
+
# with tf.GradientTape(persistent=True) as tape:
|
|
58
|
+
# tape.watch([tf_spot, tf_vol, tf_time, tf_rate])
|
|
59
|
+
# with tf.GradientTape(persistent=True) as tape2nd:
|
|
60
|
+
# tape2nd.watch([tf_spot, tf_vol])
|
|
61
|
+
|
|
62
|
+
# fwd = tf_spot * tf.math.exp((tf_rate - tf_div) * tf_time)
|
|
63
|
+
# stdev = tf_vol * tf.math.sqrt(tf_time)
|
|
64
|
+
# d1 = tf.math.log(fwd / strike) / stdev + 0.5 * stdev
|
|
65
|
+
# d2 = d1 - stdev
|
|
66
|
+
# df = tf.math.exp(-tf_rate * tf_time)
|
|
67
|
+
# pv = df * (fwd * tf_N.cdf(d1) - strike * tf_N.cdf(d2))
|
|
68
|
+
|
|
69
|
+
# # Calculate delta and vega
|
|
70
|
+
# g_delta = tape2nd.gradient(pv, tf_spot)
|
|
71
|
+
# g_vega = tape2nd.gradient(pv, tf_vol)
|
|
72
|
+
|
|
73
|
+
# delta = tape.gradient(pv, tf_spot)
|
|
74
|
+
# gamma = tape.gradient(g_delta, tf_spot)
|
|
75
|
+
# vega = tape.gradient(pv, tf_vol)
|
|
76
|
+
# theta = tape.gradient(pv, tf_time)
|
|
77
|
+
# dv01 = tape.gradient(pv, tf_rate)
|
|
78
|
+
# volga = tape.gradient(g_vega, tf_vol)
|
|
79
|
+
# vanna = tape.gradient(g_delta, tf_vol)
|
|
80
|
+
|
|
81
|
+
# # Scale
|
|
82
|
+
# vega = vega.numpy() * settings.VEGA_SCALING
|
|
83
|
+
# theta = -theta.numpy() * settings.THETA_SCALING
|
|
84
|
+
# dv01 = dv01.numpy() * settings.DV01_SCALING
|
|
85
|
+
# volga = volga.numpy() * settings.VOLGA_SCALING
|
|
86
|
+
# vanna = vanna.numpy() * settings.VANNA_SCALING
|
|
87
|
+
|
|
88
|
+
# return [pv.numpy(), delta.numpy(), gamma.numpy(), vega, theta, dv01, volga, vanna]
|
|
89
89
|
|
|
90
90
|
# def performance(spot_vol, repo_rate, div_rate, expiry, strike, fixings):
|
|
91
91
|
# shape = spot_vol.shape
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
""" Utilities for Black-Scholes model """
|
|
2
|
+
import numpy as np
|
|
3
|
+
import scipy.stats
|
|
4
|
+
from scipy.optimize import minimize_scalar
|
|
5
|
+
import tensorflow as tf
|
|
6
|
+
import tensorflow_probability as tfp
|
|
7
|
+
from sdevpy import settings
|
|
8
|
+
|
|
9
|
+
N = scipy.stats.norm.cdf
|
|
10
|
+
tf_N = tfp.distributions.Normal(0.0, 1.0)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def price_and_greeks(expiry, strike, spot, vol, rate, div):
|
|
14
|
+
""" Calculate call PV and sensitivities by AAD on Black-Scholes CF """
|
|
15
|
+
tf_spot = tf.convert_to_tensor(spot, dtype='float32')
|
|
16
|
+
tf_vol = tf.convert_to_tensor(vol)
|
|
17
|
+
tf_time = tf.convert_to_tensor(expiry, dtype='float32')
|
|
18
|
+
tf_rate = tf.convert_to_tensor(rate, dtype='float32')
|
|
19
|
+
tf_div = tf.constant(div)
|
|
20
|
+
|
|
21
|
+
with tf.GradientTape(persistent=True) as tape:
|
|
22
|
+
tape.watch([tf_spot, tf_vol, tf_time, tf_rate])
|
|
23
|
+
with tf.GradientTape(persistent=True) as tape2nd:
|
|
24
|
+
tape2nd.watch([tf_spot, tf_vol])
|
|
25
|
+
|
|
26
|
+
fwd = tf_spot * tf.math.exp((tf_rate - tf_div) * tf_time)
|
|
27
|
+
stdev = tf_vol * tf.math.sqrt(tf_time)
|
|
28
|
+
d1 = tf.math.log(fwd / strike) / stdev + 0.5 * stdev
|
|
29
|
+
d2 = d1 - stdev
|
|
30
|
+
df = tf.math.exp(-tf_rate * tf_time)
|
|
31
|
+
pv = df * (fwd * tf_N.cdf(d1) - strike * tf_N.cdf(d2))
|
|
32
|
+
|
|
33
|
+
# Calculate delta and vega
|
|
34
|
+
g_delta = tape2nd.gradient(pv, tf_spot)
|
|
35
|
+
g_vega = tape2nd.gradient(pv, tf_vol)
|
|
36
|
+
|
|
37
|
+
delta = tape.gradient(pv, tf_spot)
|
|
38
|
+
gamma = tape.gradient(g_delta, tf_spot)
|
|
39
|
+
vega = tape.gradient(pv, tf_vol)
|
|
40
|
+
theta = tape.gradient(pv, tf_time)
|
|
41
|
+
dv01 = tape.gradient(pv, tf_rate)
|
|
42
|
+
volga = tape.gradient(g_vega, tf_vol)
|
|
43
|
+
vanna = tape.gradient(g_delta, tf_vol)
|
|
44
|
+
|
|
45
|
+
# Scale
|
|
46
|
+
vega = vega.numpy() * settings.VEGA_SCALING
|
|
47
|
+
theta = -theta.numpy() * settings.THETA_SCALING
|
|
48
|
+
dv01 = dv01.numpy() * settings.DV01_SCALING
|
|
49
|
+
volga = volga.numpy() * settings.VOLGA_SCALING
|
|
50
|
+
vanna = vanna.numpy() * settings.VANNA_SCALING
|
|
51
|
+
|
|
52
|
+
return [pv.numpy(), delta.numpy(), gamma.numpy(), vega, theta, dv01, volga, vanna]
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import statsmodels.api as sm
|
|
4
|
+
from sdevpy.cointegration import coint_trading as ct
|
|
5
|
+
from sdevpy.cointegration import utils as ut
|
|
6
|
+
from sdevpy.cointegration import data_io as myio
|
|
7
|
+
from sdevpy.cointegration import mean_reversion as my_mean_rev
|
|
8
|
+
import math
|
|
9
|
+
from tqdm import tqdm # for console progress bar
|
|
10
|
+
|
|
11
|
+
# input string
|
|
12
|
+
# "['EURUSD Curncy', 'GBPUSD Curncy', 'JPYUSD Curncy', 'SGDUSD Curncy', 'CNHUSD Curncy']"
|
|
13
|
+
# we need to remove all "[", "]" and "'", then split by "," and then create a list
|
|
14
|
+
def name_list_string_to_name_list(name_list_str):
|
|
15
|
+
res_str = name_list_str.replace(" '", "")
|
|
16
|
+
res_str = res_str.replace("'", "")
|
|
17
|
+
res_str = res_str.replace('[', '')
|
|
18
|
+
res_str = res_str.replace(']', '')
|
|
19
|
+
name_list = list(res_str.split(','))
|
|
20
|
+
return name_list
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def weigths_list_string_to_float_list(weigths_list_str):
|
|
24
|
+
res_str = weigths_list_str.replace('[', '')
|
|
25
|
+
res_str = res_str.replace(']', '')
|
|
26
|
+
float_list = [float(idx) for idx in res_str.split(',')]
|
|
27
|
+
return float_list
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# Given a fixed basket, from date, trade date, now date
|
|
31
|
+
# compute the sharpe ratio, z_score, 5D realized return and 10D realized return from trade date
|
|
32
|
+
def back_test_one_trade(FROM, TRADE_DATE, NOW, name_list, weights_XXXUSD, zscore_TRADE_DATE, df_fx_spot):
|
|
33
|
+
df_fx_name_list_XXXUSD = df_fx_spot[name_list]
|
|
34
|
+
|
|
35
|
+
# compute the basket up to NOW, because we need to compute the realized 5D and 10D returns
|
|
36
|
+
basket_up_to_NOW = ut.compute_basket(df_fx_name_list_XXXUSD.loc[FROM:NOW], weights_XXXUSD)
|
|
37
|
+
|
|
38
|
+
# compute the stdev as if we were on the TRADE DATE, this is used for computing the return in terms of ZScore
|
|
39
|
+
stdev_up_to_trade_date = np.std(basket_up_to_NOW.loc[:TRADE_DATE])
|
|
40
|
+
|
|
41
|
+
basket_from_trade_date_to_NOW = basket_up_to_NOW.loc[TRADE_DATE:NOW]
|
|
42
|
+
|
|
43
|
+
basket_2D_rtns_from_TRADE_DATE = ut.compute_x_day_historical_returns_in_SD(basket_from_trade_date_to_NOW, 2, stdev_up_to_trade_date).iloc[0]
|
|
44
|
+
basket_5D_rtns_from_TRADE_DATE = ut.compute_x_day_historical_returns_in_SD(basket_from_trade_date_to_NOW, 5, stdev_up_to_trade_date).iloc[0]
|
|
45
|
+
basket_10D_rtns_from_TRADE_DATE = ut.compute_x_day_historical_returns_in_SD(basket_from_trade_date_to_NOW, 10, stdev_up_to_trade_date).iloc[0]
|
|
46
|
+
|
|
47
|
+
min_max_res_2D = ut.min_max_return_x_days_in_SD(basket_from_trade_date_to_NOW, 2, stdev_up_to_trade_date).iloc[0]
|
|
48
|
+
min_2D_rtns_in_SD = min_max_res_2D['min']
|
|
49
|
+
max_2D_rtns_in_SD = min_max_res_2D['max']
|
|
50
|
+
|
|
51
|
+
min_max_res_5D = ut.min_max_return_x_days_in_SD(basket_from_trade_date_to_NOW, 5, stdev_up_to_trade_date).iloc[0]
|
|
52
|
+
min_5D_rtns_in_SD = min_max_res_5D['min']
|
|
53
|
+
max_5D_rtns_in_SD = min_max_res_5D['max']
|
|
54
|
+
|
|
55
|
+
min_max_res_10D = ut.min_max_return_x_days_in_SD(basket_from_trade_date_to_NOW, 10, stdev_up_to_trade_date).iloc[0]
|
|
56
|
+
min_10D_rtns_in_SD = min_max_res_10D['min']
|
|
57
|
+
max_10D_rtns_in_SD = min_max_res_10D['max']
|
|
58
|
+
|
|
59
|
+
if math.isnan(basket_10D_rtns_from_TRADE_DATE):
|
|
60
|
+
raise Exception('Trade DATE is less than 10 days ago.')
|
|
61
|
+
|
|
62
|
+
if zscore_TRADE_DATE > 0:
|
|
63
|
+
# flip the return sign if zscore is above 0, because we sell. The basket goes down and we earn
|
|
64
|
+
basket_2D_rtns_from_TRADE_DATE = -basket_2D_rtns_from_TRADE_DATE
|
|
65
|
+
basket_5D_rtns_from_TRADE_DATE = -basket_5D_rtns_from_TRADE_DATE
|
|
66
|
+
basket_10D_rtns_from_TRADE_DATE = -basket_10D_rtns_from_TRADE_DATE
|
|
67
|
+
|
|
68
|
+
# so the max draw down is the negative of the max returns
|
|
69
|
+
max_2D_draw_down_in_SD = -max_2D_rtns_in_SD
|
|
70
|
+
max_5D_draw_down_in_SD = -max_5D_rtns_in_SD
|
|
71
|
+
max_10D_draw_down_in_SD = -max_10D_rtns_in_SD
|
|
72
|
+
else:
|
|
73
|
+
# we buy so that max draw down is teh min returns
|
|
74
|
+
max_2D_draw_down_in_SD = min_2D_rtns_in_SD
|
|
75
|
+
max_5D_draw_down_in_SD = min_5D_rtns_in_SD
|
|
76
|
+
max_10D_draw_down_in_SD = min_10D_rtns_in_SD
|
|
77
|
+
|
|
78
|
+
#if the max draw down is a positive number, we floor it to 0 to show there is no loss
|
|
79
|
+
max_2D_draw_down_in_SD = np.minimum(max_2D_draw_down_in_SD, 0.0)
|
|
80
|
+
max_5D_draw_down_in_SD = np.minimum(max_5D_draw_down_in_SD, 0.0)
|
|
81
|
+
max_10D_draw_down_in_SD = np.minimum(max_10D_draw_down_in_SD, 0.0)
|
|
82
|
+
|
|
83
|
+
res_dict = {'2D Realized Rtns from Trade Date in SD': basket_2D_rtns_from_TRADE_DATE,
|
|
84
|
+
'5D Realized Rtns from Trade Date in SD': basket_5D_rtns_from_TRADE_DATE,
|
|
85
|
+
'10D Realized Rtns from Trade Date in SD': basket_10D_rtns_from_TRADE_DATE,
|
|
86
|
+
'2D max draw down in SD': max_2D_draw_down_in_SD,
|
|
87
|
+
'5D max draw down in SD': max_5D_draw_down_in_SD,
|
|
88
|
+
'10D max draw down in SD': max_10D_draw_down_in_SD,
|
|
89
|
+
'basket stdev on Trade Date': stdev_up_to_trade_date
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return res_dict
|
|
93
|
+
|
|
94
|
+
# res_df_filtered - output from coint_trading.filter_cointegration_basket
|
|
95
|
+
def back_test_many_trades(res_df_filtered, NOW, df_fx_spot):
|
|
96
|
+
back_test_res = []
|
|
97
|
+
|
|
98
|
+
num_rows = len(res_df_filtered)
|
|
99
|
+
|
|
100
|
+
for idx in tqdm(range(num_rows)):
|
|
101
|
+
FROM = res_df_filtered['From'].iloc[idx]
|
|
102
|
+
TRADE_DATE = res_df_filtered['Today'].iloc[idx]
|
|
103
|
+
TRADE_DATE = pd.to_datetime(TRADE_DATE, format='%Y-%m-%d')
|
|
104
|
+
|
|
105
|
+
name_str = res_df_filtered['currency pairs'].iloc[idx]
|
|
106
|
+
name_list = name_list_string_to_name_list(name_str)
|
|
107
|
+
|
|
108
|
+
weights_XXXUSD_str = res_df_filtered['unadj weights in XXXUSD'].iloc[idx]
|
|
109
|
+
weights_XXXUSD = weigths_list_string_to_float_list(weights_XXXUSD_str)
|
|
110
|
+
|
|
111
|
+
Sharpe_5D = res_df_filtered['5D Sharpe Ratio'].iloc[idx]
|
|
112
|
+
SD_on_Trade_Date = res_df_filtered['SD Current'].iloc[idx]
|
|
113
|
+
|
|
114
|
+
Stop_Loss_in_SD = res_df_filtered['Stop Loss in SD'].iloc[idx]
|
|
115
|
+
|
|
116
|
+
half_life_in_days = res_df_filtered['half life in days'].iloc[idx]
|
|
117
|
+
|
|
118
|
+
range_in_SD_current = res_df_filtered['Range in SD current'].iloc[idx]
|
|
119
|
+
|
|
120
|
+
abs_SD_on_Trade_Date = np.abs(SD_on_Trade_Date)
|
|
121
|
+
|
|
122
|
+
one_month_trace_5pct = res_df_filtered['+/- 1 month trace (5%)'].iloc[idx]
|
|
123
|
+
one_month_trace_10pct = res_df_filtered['+/- 1 month trace (10%)'].iloc[idx]
|
|
124
|
+
one_month_eigen_5pct = res_df_filtered['+/- 1 month eigen (5%)'].iloc[idx]
|
|
125
|
+
one_month_eigen_10pct = res_df_filtered['+/- 1 month eigen (10%)'].iloc[idx]
|
|
126
|
+
|
|
127
|
+
res_dict = back_test_one_trade(FROM, TRADE_DATE, NOW, name_list, weights_XXXUSD,
|
|
128
|
+
SD_on_Trade_Date, df_fx_spot)
|
|
129
|
+
|
|
130
|
+
basket_stdev = res_dict['basket stdev on Trade Date']
|
|
131
|
+
|
|
132
|
+
mean_rev_level = res_df_filtered['mean_rev_level'].iloc[idx]
|
|
133
|
+
|
|
134
|
+
back_test_res.append((FROM, TRADE_DATE, NOW, name_list, weights_XXXUSD, Sharpe_5D,
|
|
135
|
+
SD_on_Trade_Date, abs_SD_on_Trade_Date, Stop_Loss_in_SD,
|
|
136
|
+
res_dict['2D Realized Rtns from Trade Date in SD'],
|
|
137
|
+
res_dict['5D Realized Rtns from Trade Date in SD'],
|
|
138
|
+
res_dict['10D Realized Rtns from Trade Date in SD'],
|
|
139
|
+
res_dict['2D max draw down in SD'],
|
|
140
|
+
res_dict['5D max draw down in SD'],
|
|
141
|
+
res_dict['10D max draw down in SD'],
|
|
142
|
+
basket_stdev, mean_rev_level, half_life_in_days, range_in_SD_current,
|
|
143
|
+
one_month_trace_5pct, one_month_trace_10pct,
|
|
144
|
+
one_month_eigen_5pct, one_month_eigen_10pct))
|
|
145
|
+
|
|
146
|
+
#--- end of for ind in tqdm(res_df_filtered.index):
|
|
147
|
+
|
|
148
|
+
res_df = pd.DataFrame(back_test_res, columns =['FROM', 'Trade Date', 'Now', 'currency pairs',
|
|
149
|
+
'unadj weights in XXXUSD', 'Sharpe Ratio on Trade Date',
|
|
150
|
+
'Z Score on Trade Date', 'Abs Z Score on Trade Date',
|
|
151
|
+
'Distance to max/min SD', '2D Realized Rtns in SD',
|
|
152
|
+
'5D Realized Rtns in SD', '10D Realized Rtns in SD',
|
|
153
|
+
'2D max DD in SD', '5D max DD in SD', '10D max DD in SD',
|
|
154
|
+
'basket stdev on Trade Date', 'mean_rev_level on Trade Date',
|
|
155
|
+
'half life in days', 'Range in SD current',
|
|
156
|
+
'+/- 1 month trace (5%)', '+/- 1 month trace (10%)',
|
|
157
|
+
'+/- 1 month eigen (5%)', '+/- 1 month eigen (10%)'])
|
|
158
|
+
|
|
159
|
+
return res_df
|
|
160
|
+
|
|
161
|
+
# Take the results of back_test_many_trades and then compute the back test diagnostics
|
|
162
|
+
def one_back_test_summary_table(back_test_res_df, start_date, end_date, Sharpe_threshold, ZScore_threshold):
|
|
163
|
+
|
|
164
|
+
upper_SD_condition = back_test_res_df['Z Score on Trade Date'] > ZScore_threshold
|
|
165
|
+
lower_SD_condition = back_test_res_df['Z Score on Trade Date'] < -ZScore_threshold
|
|
166
|
+
Sharpe_condition = back_test_res_df['Sharpe Ratio on Trade Date'] > Sharpe_threshold
|
|
167
|
+
|
|
168
|
+
start_date_condition = back_test_res_df['Trade Date'] > pd.Timestamp(start_date)
|
|
169
|
+
end_date_condition = back_test_res_df['Trade Date'] < pd.Timestamp(end_date)
|
|
170
|
+
|
|
171
|
+
# -----------apply conditions
|
|
172
|
+
my_trade_df = back_test_res_df[Sharpe_condition]
|
|
173
|
+
my_trade_df = my_trade_df[upper_SD_condition | lower_SD_condition]
|
|
174
|
+
my_trade_df = my_trade_df[start_date_condition]
|
|
175
|
+
my_trade_df = my_trade_df[end_date_condition]
|
|
176
|
+
|
|
177
|
+
total = len(my_trade_df)
|
|
178
|
+
|
|
179
|
+
output_table = []
|
|
180
|
+
|
|
181
|
+
column_list = ['2D Realized Rtns in SD', '5D Realized Rtns in SD', '10D Realized Rtns in SD']
|
|
182
|
+
|
|
183
|
+
for column in column_list:
|
|
184
|
+
positive_return_condition = my_trade_df[column] > 0.0
|
|
185
|
+
num_pos_rtn = len(my_trade_df[positive_return_condition])
|
|
186
|
+
num_neg_rtn = len(my_trade_df[~positive_return_condition])
|
|
187
|
+
|
|
188
|
+
pos_rtn_pct = round(num_pos_rtn/total, 3)
|
|
189
|
+
neg_rtn_pct = round(num_neg_rtn/total, 3)
|
|
190
|
+
|
|
191
|
+
rtns_median = np.median(my_trade_df[column])
|
|
192
|
+
rtns_mean = np.mean(my_trade_df[column])
|
|
193
|
+
rtns_std = np.std(my_trade_df[column])
|
|
194
|
+
|
|
195
|
+
first_3_char = column[:3]
|
|
196
|
+
|
|
197
|
+
output_table.append((start_date,
|
|
198
|
+
end_date,
|
|
199
|
+
Sharpe_threshold,
|
|
200
|
+
ZScore_threshold,
|
|
201
|
+
first_3_char,
|
|
202
|
+
pos_rtn_pct,
|
|
203
|
+
neg_rtn_pct,
|
|
204
|
+
total,
|
|
205
|
+
rtns_median,
|
|
206
|
+
rtns_mean,
|
|
207
|
+
rtns_std))
|
|
208
|
+
|
|
209
|
+
res_df = pd.DataFrame(output_table, columns =['Period Start',
|
|
210
|
+
'Period End',
|
|
211
|
+
'Sharpe threshold',
|
|
212
|
+
'SD threshold',
|
|
213
|
+
'Rtns type',
|
|
214
|
+
'Pos %',
|
|
215
|
+
'Neg %',
|
|
216
|
+
'Total',
|
|
217
|
+
'Rtns median',
|
|
218
|
+
'Rtns mean',
|
|
219
|
+
'Rtns stdev'
|
|
220
|
+
])
|
|
221
|
+
|
|
222
|
+
return res_df, my_trade_df
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def several_back_test_summary_tables(back_test_res_df, start_dates, end_dates, Sharpe_thresholds, ZScore_thresholds):
|
|
226
|
+
output_df = pd.DataFrame()
|
|
227
|
+
for start, end in zip(start_dates, end_dates):
|
|
228
|
+
for Sharpe in Sharpe_thresholds:
|
|
229
|
+
for zscore in ZScore_thresholds:
|
|
230
|
+
df_one_table, my_trade_df = one_back_test_summary_table(back_test_res_df, start, end, Sharpe, zscore)
|
|
231
|
+
output_df = pd.concat([output_df, df_one_table], axis=0)
|
|
232
|
+
|
|
233
|
+
return output_df
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from scipy.stats import norm
|
|
3
|
+
from scipy import optimize
|
|
4
|
+
|
|
5
|
+
# undiscounted black option price
|
|
6
|
+
def black_option_price(f, # forward, double
|
|
7
|
+
k, # strike, double
|
|
8
|
+
t, # time to maturity, double
|
|
9
|
+
v, # implied volatility, double
|
|
10
|
+
c_or_p # call (1) or put (-1), integer
|
|
11
|
+
):
|
|
12
|
+
|
|
13
|
+
d_1 = (np.log(f/k)+0.5*v*v*t)/(v*np.sqrt(t))
|
|
14
|
+
d_2 = d_1 - v*np.sqrt(t)
|
|
15
|
+
if c_or_p == 1 :
|
|
16
|
+
return f * norm.cdf(d_1) - k * norm.cdf(d_2)
|
|
17
|
+
elif c_or_p == -1 :
|
|
18
|
+
return k * norm.cdf(-d_2) - f * norm.cdf(-d_1)
|
|
19
|
+
else:
|
|
20
|
+
raise ValueError('c_or_p is expected to be 1 for call or -1 for put.')
|
|
21
|
+
|
|
22
|
+
#undiscounted black option vega
|
|
23
|
+
def black_option_vega(f, # forward, double
|
|
24
|
+
k, # strike, double
|
|
25
|
+
t, # time to maturity, double
|
|
26
|
+
v # implied volatility, double
|
|
27
|
+
):
|
|
28
|
+
|
|
29
|
+
d_1 = (np.log(f / k) + 0.5 * v * v * t) / (v * np.sqrt(t))
|
|
30
|
+
return f * norm.pdf(d_1) * np.sqrt(t)
|
|
31
|
+
|
|
32
|
+
#compute black implied volatility
|
|
33
|
+
def black_implied_vol(p, # option price, double
|
|
34
|
+
f, # forward, double
|
|
35
|
+
k, # strike, double
|
|
36
|
+
t, # time to maturity, double
|
|
37
|
+
c_or_p, # call (1) or put (-1), integer
|
|
38
|
+
init_guess = 0.2 # initial guess
|
|
39
|
+
):
|
|
40
|
+
|
|
41
|
+
f_ivol = lambda x: black_option_price(f, k, t, x, c_or_p) - p
|
|
42
|
+
f_vega = lambda x: black_option_vega(f, k, t, x)
|
|
43
|
+
black_implied_vol = optimize.newton(f_ivol, init_guess, f_vega)
|
|
44
|
+
|
|
45
|
+
return black_implied_vol
|