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.
Files changed (143) hide show
  1. {sdevpy-1.0.4/src/sdevpy.egg-info → sdevpy-1.0.5}/PKG-INFO +2 -4
  2. {sdevpy-1.0.4 → sdevpy-1.0.5}/pyproject.toml +2 -2
  3. sdevpy-1.0.5/src/sdevpy/__init__.py +1 -0
  4. sdevpy-1.0.5/src/sdevpy/analytics/americantree.py +103 -0
  5. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/analytics/black.py +45 -45
  6. sdevpy-1.0.5/src/sdevpy/analytics/tf_black.py +52 -0
  7. sdevpy-1.0.5/src/sdevpy/cointegration/back_testing.py +233 -0
  8. sdevpy-1.0.5/src/sdevpy/cointegration/black_analytics.py +45 -0
  9. sdevpy-1.0.5/src/sdevpy/cointegration/coint_trading.py +839 -0
  10. sdevpy-1.0.5/src/sdevpy/cointegration/data_io.py +225 -0
  11. sdevpy-1.0.5/src/sdevpy/cointegration/implied_vol.py +116 -0
  12. sdevpy-1.0.5/src/sdevpy/cointegration/mean_reversion.py +279 -0
  13. sdevpy-1.0.5/src/sdevpy/cointegration/model_settings.py +33 -0
  14. sdevpy-1.0.5/src/sdevpy/cointegration/plotting.py +296 -0
  15. sdevpy-1.0.5/src/sdevpy/cointegration/run_unit_test.py +572 -0
  16. sdevpy-1.0.5/src/sdevpy/cointegration/utils.py +477 -0
  17. sdevpy-1.0.5/src/sdevpy/llms/ModelConverter.py +268 -0
  18. sdevpy-1.0.5/src/sdevpy/llms/attention.py +139 -0
  19. sdevpy-1.0.5/src/sdevpy/llms/chat.py +35 -0
  20. sdevpy-1.0.5/src/sdevpy/llms/datasets.py +30 -0
  21. sdevpy-1.0.5/src/sdevpy/llms/gpt.py +333 -0
  22. sdevpy-1.0.5/src/sdevpy/llms/instructions.py +145 -0
  23. sdevpy-1.0.5/src/sdevpy/llms/textgen.py +167 -0
  24. sdevpy-1.0.5/src/sdevpy/llms/tokenizers.py +37 -0
  25. sdevpy-1.0.5/src/sdevpy/llms/training.py +100 -0
  26. sdevpy-1.0.5/src/sdevpy/maths/sets.py +16 -0
  27. sdevpy-1.0.5/src/sdevpy/projects/chat_gpt2.py +76 -0
  28. sdevpy-1.0.5/src/sdevpy/projects/raschka/ch2_working_with_text.py +161 -0
  29. sdevpy-1.0.5/src/sdevpy/projects/raschka/ch3_coding_attention.py +259 -0
  30. sdevpy-1.0.5/src/sdevpy/projects/raschka/ch4_gpt_model.py +200 -0
  31. sdevpy-1.0.5/src/sdevpy/projects/raschka/ch5_loadGPT2.py +119 -0
  32. sdevpy-1.0.5/src/sdevpy/projects/raschka/ch5_pretraining.py +263 -0
  33. sdevpy-1.0.5/src/sdevpy/projects/raschka/ch7_instruction_finetuning.py +26 -0
  34. sdevpy-1.0.5/src/sdevpy/projects/raschka/raschka_datasetloader.py +80 -0
  35. sdevpy-1.0.5/src/sdevpy/projects/raschka/raschka_dnn.py +40 -0
  36. sdevpy-1.0.5/src/sdevpy/projects/raschka/raschka_gpt_download.py +155 -0
  37. sdevpy-1.0.5/src/sdevpy/projects/set_limits.py +27 -0
  38. sdevpy-1.0.5/src/sdevpy/projects/update_db.py +31 -0
  39. sdevpy-1.0.5/src/sdevpy/settings.py +57 -0
  40. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/test.py +118 -17
  41. sdevpy-1.0.5/src/sdevpy/timeseries/backtesting.py +91 -0
  42. sdevpy-1.0.5/src/sdevpy/timeseries/cointegration.py +253 -0
  43. sdevpy-1.0.5/src/sdevpy/timeseries/meanreversion.py +196 -0
  44. sdevpy-1.0.5/src/sdevpy/timeseries/timeseriestools.py +140 -0
  45. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/tools/jsonmanager.py +14 -0
  46. sdevpy-1.0.5/src/sdevpy/tools/network.py +92 -0
  47. sdevpy-1.0.5/src/sdevpy/tools/pydotnet.py +70 -0
  48. sdevpy-1.0.5/src/sdevpy/tools/utils.py +42 -0
  49. sdevpy-1.0.5/src/sdevpy/tree/trees.py +162 -0
  50. {sdevpy-1.0.4 → sdevpy-1.0.5/src/sdevpy.egg-info}/PKG-INFO +2 -4
  51. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy.egg-info/SOURCES.txt +41 -1
  52. sdevpy-1.0.4/LICENSE +0 -21
  53. sdevpy-1.0.4/src/sdevpy/__init__.py +0 -1
  54. sdevpy-1.0.4/src/sdevpy/settings.py +0 -22
  55. sdevpy-1.0.4/src/sdevpy/tools/utils.py +0 -7
  56. {sdevpy-1.0.4 → sdevpy-1.0.5}/README.md +0 -0
  57. {sdevpy-1.0.4 → sdevpy-1.0.5}/setup.cfg +0 -0
  58. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/analytics/bachelier.py +0 -0
  59. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/analytics/fbsabr.py +0 -0
  60. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/analytics/mcheston.py +0 -0
  61. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/analytics/mcsabr.py +0 -0
  62. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/analytics/mczabr.py +0 -0
  63. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/analytics/sabr.py +0 -0
  64. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/machinelearning/callbacks.py +0 -0
  65. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/machinelearning/datasets.py +0 -0
  66. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/machinelearning/learningmodel.py +0 -0
  67. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/machinelearning/learningschedules.py +0 -0
  68. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/machinelearning/topology.py +0 -0
  69. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/maths/interpolations.py +0 -0
  70. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/maths/metrics.py +0 -0
  71. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/maths/optimization.py +0 -0
  72. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/maths/rand.py +0 -0
  73. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/montecarlo/smoothers.py +0 -0
  74. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/projects/aad/aad_mc.py +0 -0
  75. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/projects/aad/aad_mc_nd.py +0 -0
  76. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/projects/datafiles.py +0 -0
  77. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/projects/stovol/stovolgen.py +0 -0
  78. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/projects/stovol/stovolplot.py +0 -0
  79. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/projects/stovol/stovoltrain.py +0 -0
  80. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/projects/stovolinverse/stovolinvgen.py +0 -0
  81. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/projects/stovolinverse/stovolinvtrain.py +0 -0
  82. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_lets_be_rational/__init__.py +0 -0
  83. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_lets_be_rational/constants.py +0 -0
  84. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_lets_be_rational/erf_cody.py +0 -0
  85. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_lets_be_rational/exceptions.py +0 -0
  86. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_lets_be_rational/lets_be_rational.py +0 -0
  87. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_lets_be_rational/normaldistribution.py +0 -0
  88. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_lets_be_rational/numba_helper.py +0 -0
  89. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_lets_be_rational/rationalcubic.py +0 -0
  90. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/__init__.py +0 -0
  91. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black/__init__.py +0 -0
  92. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black/greeks/__init__.py +0 -0
  93. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black/greeks/analytical.py +0 -0
  94. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black/greeks/numerical.py +0 -0
  95. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black/implied_volatility.py +0 -0
  96. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black_scholes/__init__.py +0 -0
  97. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black_scholes/greeks/__init__.py +0 -0
  98. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black_scholes/greeks/analytical.py +0 -0
  99. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black_scholes/greeks/numerical.py +0 -0
  100. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black_scholes/implied_volatility.py +0 -0
  101. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black_scholes_merton/__init__.py +0 -0
  102. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/__init__.py +0 -0
  103. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/analytical.py +0 -0
  104. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/numerical.py +0 -0
  105. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/black_scholes_merton/implied_volatility.py +0 -0
  106. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/helpers/__init__.py +0 -0
  107. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/helpers/constants.py +0 -0
  108. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/helpers/distributions.py +0 -0
  109. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/helpers/doctest_helper.py +0 -0
  110. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/helpers/exceptions.py +0 -0
  111. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/helpers/numerical_greeks.py +0 -0
  112. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/__init__.py +0 -0
  113. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black/__init__.py +0 -0
  114. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/__init__.py +0 -0
  115. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/analytical.py +0 -0
  116. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/numerical.py +0 -0
  117. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black/implied_volatility.py +0 -0
  118. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/__init__.py +0 -0
  119. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/__init__.py +0 -0
  120. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/analytical.py +0 -0
  121. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/numerical.py +0 -0
  122. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/implied_volatility.py +0 -0
  123. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/__init__.py +0 -0
  124. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/__init__.py +0 -0
  125. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/analytical.py +0 -0
  126. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/numerical.py +0 -0
  127. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/implied_volatility.py +0 -0
  128. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/tools/clipboard.py +0 -0
  129. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/tools/constants.py +0 -0
  130. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/tools/filemanager.py +0 -0
  131. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/tools/timegrids.py +0 -0
  132. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/tools/timer.py +0 -0
  133. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/volsurfacegen/fbsabrgenerator.py +0 -0
  134. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/volsurfacegen/mchestongenerator.py +0 -0
  135. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/volsurfacegen/mcsabrgenerator.py +0 -0
  136. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/volsurfacegen/mczabrgenerator.py +0 -0
  137. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/volsurfacegen/sabrgenerator.py +0 -0
  138. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/volsurfacegen/smilegenerator.py +0 -0
  139. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy/volsurfacegen/stovolfactory.py +0 -0
  140. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy.egg-info/dependency_links.txt +0 -0
  141. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy.egg-info/requires.txt +0 -0
  142. {sdevpy-1.0.4 → sdevpy-1.0.5}/src/sdevpy.egg-info/top_level.txt +0 -0
  143. {sdevpy-1.0.4 → sdevpy-1.0.5}/tests/test.py +0 -0
@@ -1,16 +1,14 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: sdevpy
3
- Version: 1.0.4
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.4"
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
- """ 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]
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