sdevpy 1.0.8__tar.gz → 1.1__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 (259) hide show
  1. sdevpy-1.1/LICENSE +21 -0
  2. {sdevpy-1.0.8 → sdevpy-1.1}/PKG-INFO +25 -2
  3. {sdevpy-1.0.8 → sdevpy-1.1}/pyproject.toml +25 -5
  4. sdevpy-1.1/sdevpy/__init__.py +26 -0
  5. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/analytics/bachelier.py +7 -19
  6. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/analytics/black.py +26 -25
  7. sdevpy-1.1/sdevpy/instruments/constants.py +20 -0
  8. sdevpy-1.1/sdevpy/logger.py +42 -0
  9. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/keras/learningschedules.py +1 -1
  10. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/market/correlations.py +4 -2
  11. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/market/eqforward.py +14 -21
  12. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/market/eqvolsurface.py +52 -36
  13. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/market/yieldcurve.py +14 -16
  14. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/constants.py +3 -0
  15. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/integration.py +0 -1
  16. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/interpolation.py +14 -7
  17. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/optimization.py +28 -7
  18. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/rand/pathconstruction.py +2 -1
  19. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/specialfunctions.py +3 -3
  20. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/models/assetmodels.py +10 -14
  21. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/models/multiasset_heston.py +1 -1
  22. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/montecarlo/mcpricer.py +59 -11
  23. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/montecarlo/payoffs/basic.py +1 -1
  24. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/montecarlo/payoffs/vanillas.py +10 -29
  25. sdevpy-1.1/sdevpy/montecarlo/smoothers.py +23 -0
  26. sdevpy-1.1/sdevpy/pde/forwardpde.py +303 -0
  27. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/pde/pdeschemes.py +61 -57
  28. sdevpy-1.1/sdevpy/tensorflow/tf_analytics.py +19 -0
  29. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tensorflow/tf_metrics.py +6 -4
  30. sdevpy-1.1/sdevpy/tests/test_algos.py +112 -0
  31. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tests/test_analytics.py +24 -9
  32. sdevpy-1.1/sdevpy/tests/test_backtesting.py +58 -0
  33. sdevpy-1.1/sdevpy/tests/test_cointegration.py +96 -0
  34. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tests/test_dates.py +10 -11
  35. sdevpy-1.1/sdevpy/tests/test_files.py +93 -0
  36. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tests/test_impliedvol.py +114 -32
  37. sdevpy-1.1/sdevpy/tests/test_impliedvol_ml.py +135 -0
  38. sdevpy-1.1/sdevpy/tests/test_integration.py +24 -0
  39. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tests/test_interpolation.py +4 -4
  40. sdevpy-1.1/sdevpy/tests/test_localvol.py +313 -0
  41. sdevpy-1.1/sdevpy/tests/test_localvol_calib.py +200 -0
  42. sdevpy-1.1/sdevpy/tests/test_marketdata.py +90 -0
  43. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tests/test_mc.py +11 -6
  44. sdevpy-1.1/sdevpy/tests/test_meanreversion.py +161 -0
  45. sdevpy-1.1/sdevpy/tests/test_models.py +49 -0
  46. sdevpy-1.1/sdevpy/tests/test_pde.py +186 -0
  47. sdevpy-1.1/sdevpy/tests/test_restapi.py +77 -0
  48. sdevpy-1.1/sdevpy/tests/test_specialfunctions.py +43 -0
  49. sdevpy-1.1/sdevpy/tests/test_timegrids.py +98 -0
  50. sdevpy-1.1/sdevpy/tests/test_tree.py +77 -0
  51. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tests/test_yieldcurves.py +36 -3
  52. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/timeseries/backtesting.py +3 -2
  53. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/timeseries/cointegration.py +11 -41
  54. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/timeseries/meanreversion.py +11 -0
  55. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/timeseries/timeseriestools.py +11 -10
  56. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tree/trees.py +9 -9
  57. sdevpy-1.1/sdevpy/utilities/algos.py +81 -0
  58. sdevpy-1.1/sdevpy/utilities/constants.py +2 -0
  59. sdevpy-1.1/sdevpy/utilities/dates.py +54 -0
  60. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/utilities/filemanager.py +0 -12
  61. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/utilities/jsonmanager.py +6 -7
  62. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/utilities/pydotnet.py +1 -0
  63. sdevpy-1.1/sdevpy/utilities/restapi.py +42 -0
  64. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/utilities/scalendar.py +9 -15
  65. sdevpy-1.1/sdevpy/utilities/timegrids.py +217 -0
  66. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/utilities/tools.py +5 -2
  67. sdevpy-1.1/sdevpy/utilities/xmlmanager.py +29 -0
  68. sdevpy-1.1/sdevpy/volatility/impliedvol/impliedvol.py +218 -0
  69. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/impliedvol_calib.py +20 -32
  70. sdevpy-1.1/sdevpy/volatility/impliedvol/impliedvol_factory.py +66 -0
  71. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/models/biexp.py +17 -7
  72. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/models/cubicvol.py +14 -9
  73. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/models/fbsabr.py +2 -2
  74. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/models/logmix.py +229 -78
  75. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/models/mcheston.py +2 -2
  76. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/models/mcsabr.py +3 -3
  77. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/models/mczabr.py +2 -2
  78. sdevpy-1.1/sdevpy/volatility/impliedvol/models/svi.py +122 -0
  79. sdevpy-1.1/sdevpy/volatility/impliedvol/models/tssvi1.py +276 -0
  80. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/models/tssvi2.py +82 -38
  81. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/models/vsvi.py +46 -34
  82. sdevpy-1.1/sdevpy/volatility/impliedvol/numerical_impliedvol.py +134 -0
  83. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/optionsurface.py +16 -6
  84. sdevpy-1.1/sdevpy/volatility/impliedvol/parametric_impliedvol.py +29 -0
  85. sdevpy-1.1/sdevpy/volatility/localvol/black_calib.py +66 -0
  86. sdevpy-1.1/sdevpy/volatility/localvol/dupire_calib.py +287 -0
  87. sdevpy-1.1/sdevpy/volatility/localvol/localvol.py +336 -0
  88. sdevpy-1.1/sdevpy/volatility/localvol/localvol_factory.py +244 -0
  89. sdevpy-1.1/sdevpy/volatility/localvol/lvsection_calib.py +352 -0
  90. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/mlsurfacegen/sabrgenerator.py +2 -1
  91. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy.egg-info/PKG-INFO +25 -2
  92. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy.egg-info/SOURCES.txt +23 -85
  93. sdevpy-1.0.8/sdevpy/__init__.py +0 -21
  94. sdevpy-1.0.8/sdevpy/cointegration/back_testing.py +0 -224
  95. sdevpy-1.0.8/sdevpy/cointegration/coint_trading.py +0 -677
  96. sdevpy-1.0.8/sdevpy/cointegration/data_io.py +0 -14
  97. sdevpy-1.0.8/sdevpy/cointegration/mean_reversion.py +0 -268
  98. sdevpy-1.0.8/sdevpy/cointegration/model_settings.py +0 -34
  99. sdevpy-1.0.8/sdevpy/cointegration/utils.py +0 -132
  100. sdevpy-1.0.8/sdevpy/montecarlo/mcrun.py +0 -102
  101. sdevpy-1.0.8/sdevpy/montecarlo/smoothers.py +0 -40
  102. sdevpy-1.0.8/sdevpy/pde/forwardpde.py +0 -267
  103. sdevpy-1.0.8/sdevpy/projects/aad/aad_mc.py +0 -282
  104. sdevpy-1.0.8/sdevpy/projects/aad/aad_mc_nd.py +0 -349
  105. sdevpy-1.0.8/sdevpy/projects/chat_gpt2.py +0 -76
  106. sdevpy-1.0.8/sdevpy/projects/datafiles.py +0 -28
  107. sdevpy-1.0.8/sdevpy/projects/raschka/ch2_working_with_text.py +0 -161
  108. sdevpy-1.0.8/sdevpy/projects/raschka/ch3_coding_attention.py +0 -259
  109. sdevpy-1.0.8/sdevpy/projects/raschka/ch4_gpt_model.py +0 -200
  110. sdevpy-1.0.8/sdevpy/projects/raschka/ch5_loadgpt2.py +0 -119
  111. sdevpy-1.0.8/sdevpy/projects/raschka/ch5_pretraining.py +0 -263
  112. sdevpy-1.0.8/sdevpy/projects/raschka/ch7_instruction_finetuning.py +0 -26
  113. sdevpy-1.0.8/sdevpy/projects/raschka/raschka_datasetloader.py +0 -80
  114. sdevpy-1.0.8/sdevpy/projects/raschka/raschka_dnn.py +0 -40
  115. sdevpy-1.0.8/sdevpy/projects/raschka/raschka_gpt_download.py +0 -155
  116. sdevpy-1.0.8/sdevpy/projects/set_limits.py +0 -24
  117. sdevpy-1.0.8/sdevpy/projects/stovol/stovolgen.py +0 -87
  118. sdevpy-1.0.8/sdevpy/projects/stovol/stovolplot.py +0 -81
  119. sdevpy-1.0.8/sdevpy/projects/stovol/stovoltrain.py +0 -269
  120. sdevpy-1.0.8/sdevpy/projects/stovolinverse/stovolinvgen.py +0 -82
  121. sdevpy-1.0.8/sdevpy/projects/stovolinverse/stovolinvtrain.py +0 -374
  122. sdevpy-1.0.8/sdevpy/projects/update_db.py +0 -26
  123. sdevpy-1.0.8/sdevpy/tests/test_algos.py +0 -125
  124. sdevpy-1.0.8/sdevpy/tests/test_localvol.py +0 -0
  125. sdevpy-1.0.8/sdevpy/tests/test_marketdata.py +0 -48
  126. sdevpy-1.0.8/sdevpy/tests/test_pde.py +0 -116
  127. sdevpy-1.0.8/sdevpy/tests/test_timegrids.py +0 -49
  128. sdevpy-1.0.8/sdevpy/thirdparty/py_lets_be_rational/__init__.py +0 -49
  129. sdevpy-1.0.8/sdevpy/thirdparty/py_lets_be_rational/constants.py +0 -65
  130. sdevpy-1.0.8/sdevpy/thirdparty/py_lets_be_rational/erf_cody.py +0 -448
  131. sdevpy-1.0.8/sdevpy/thirdparty/py_lets_be_rational/exceptions.py +0 -68
  132. sdevpy-1.0.8/sdevpy/thirdparty/py_lets_be_rational/lets_be_rational.py +0 -801
  133. sdevpy-1.0.8/sdevpy/thirdparty/py_lets_be_rational/normaldistribution.py +0 -194
  134. sdevpy-1.0.8/sdevpy/thirdparty/py_lets_be_rational/numba_helper.py +0 -13
  135. sdevpy-1.0.8/sdevpy/thirdparty/py_lets_be_rational/rationalcubic.py +0 -272
  136. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/__init__.py +0 -32
  137. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black/__init__.py +0 -180
  138. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black/greeks/analytical.py +0 -273
  139. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black/greeks/numerical.py +0 -251
  140. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black/implied_volatility.py +0 -292
  141. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes/__init__.py +0 -84
  142. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes/greeks/analytical.py +0 -278
  143. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes/greeks/numerical.py +0 -318
  144. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes/implied_volatility.py +0 -102
  145. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes_merton/__init__.py +0 -90
  146. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/__init__.py +0 -0
  147. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/analytical.py +0 -309
  148. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/numerical.py +0 -266
  149. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes_merton/implied_volatility.py +0 -116
  150. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/helpers/__init__.py +0 -114
  151. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/helpers/constants.py +0 -52
  152. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/helpers/distributions.py +0 -226
  153. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/helpers/doctest_helper.py +0 -62
  154. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/helpers/exceptions.py +0 -59
  155. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/helpers/numerical_greeks.py +0 -217
  156. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/__init__.py +0 -32
  157. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black/__init__.py +0 -236
  158. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/__init__.py +0 -0
  159. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/analytical.py +0 -277
  160. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/numerical.py +0 -222
  161. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black/implied_volatility.py +0 -117
  162. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/__init__.py +0 -157
  163. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/__init__.py +0 -0
  164. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/analytical.py +0 -279
  165. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/numerical.py +0 -290
  166. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/implied_volatility.py +0 -110
  167. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/__init__.py +0 -231
  168. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/__init__.py +0 -0
  169. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/analytical.py +0 -311
  170. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/numerical.py +0 -223
  171. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/implied_volatility.py +0 -104
  172. sdevpy-1.0.8/sdevpy/timeseries/__init__.py +0 -0
  173. sdevpy-1.0.8/sdevpy/tree/__init__.py +0 -0
  174. sdevpy-1.0.8/sdevpy/utilities/__init__.py +0 -0
  175. sdevpy-1.0.8/sdevpy/utilities/algos.py +0 -115
  176. sdevpy-1.0.8/sdevpy/utilities/constants.py +0 -6
  177. sdevpy-1.0.8/sdevpy/utilities/dates.py +0 -31
  178. sdevpy-1.0.8/sdevpy/utilities/network.py +0 -95
  179. sdevpy-1.0.8/sdevpy/utilities/speriods.py +0 -25
  180. sdevpy-1.0.8/sdevpy/utilities/timegrids.py +0 -142
  181. sdevpy-1.0.8/sdevpy/volatility/__init__.py +0 -0
  182. sdevpy-1.0.8/sdevpy/volatility/impliedvol/__init__.py +0 -0
  183. sdevpy-1.0.8/sdevpy/volatility/impliedvol/impliedvol.py +0 -45
  184. sdevpy-1.0.8/sdevpy/volatility/impliedvol/models/__init__.py +0 -0
  185. sdevpy-1.0.8/sdevpy/volatility/impliedvol/models/gsvi.py +0 -67
  186. sdevpy-1.0.8/sdevpy/volatility/impliedvol/models/svi.py +0 -95
  187. sdevpy-1.0.8/sdevpy/volatility/impliedvol/models/tssvi1.py +0 -195
  188. sdevpy-1.0.8/sdevpy/volatility/impliedvol/zerosurface.py +0 -216
  189. sdevpy-1.0.8/sdevpy/volatility/localvol/__init__.py +0 -0
  190. sdevpy-1.0.8/sdevpy/volatility/localvol/localvol.py +0 -92
  191. sdevpy-1.0.8/sdevpy/volatility/localvol/localvol_calib.py +0 -303
  192. sdevpy-1.0.8/sdevpy/volatility/localvol/localvol_factory.py +0 -201
  193. sdevpy-1.0.8/sdevpy/volatility/mlsurfacegen/__init__.py +0 -0
  194. {sdevpy-1.0.8 → sdevpy-1.1}/README.md +0 -0
  195. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/analytics/__init__.py +0 -0
  196. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/analytics/americantree.py +0 -0
  197. {sdevpy-1.0.8/sdevpy/cointegration → sdevpy-1.1/sdevpy/machinelearning}/__init__.py +0 -0
  198. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/datasets.py +0 -0
  199. {sdevpy-1.0.8/sdevpy/machinelearning → sdevpy-1.1/sdevpy/machinelearning/keras}/__init__.py +0 -0
  200. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/keras/callbacks.py +0 -0
  201. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/keras/learningmodel.py +0 -0
  202. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/keras/topology.py +0 -0
  203. {sdevpy-1.0.8/sdevpy/machinelearning/keras → sdevpy-1.1/sdevpy/machinelearning/llms}/__init__.py +0 -0
  204. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/llms/attention.py +0 -0
  205. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/llms/chat.py +0 -0
  206. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/llms/datasets.py +0 -0
  207. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/llms/gpt.py +0 -0
  208. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/llms/instructions.py +0 -0
  209. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/llms/modelconverter.py +0 -0
  210. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/llms/textgen.py +0 -0
  211. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/llms/tokenizers.py +0 -0
  212. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/llms/training.py +0 -0
  213. {sdevpy-1.0.8/sdevpy/machinelearning/llms → sdevpy-1.1/sdevpy/market}/__init__.py +0 -0
  214. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/market/fixings.py +0 -0
  215. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/market/spot.py +0 -0
  216. {sdevpy-1.0.8/sdevpy/market → sdevpy-1.1/sdevpy/maths}/__init__.py +0 -0
  217. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/metrics.py +0 -0
  218. {sdevpy-1.0.8/sdevpy/maths → sdevpy-1.1/sdevpy/maths/rand}/__init__.py +0 -0
  219. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/rand/correlations.py +0 -0
  220. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/rand/rng.py +0 -0
  221. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/regression.py +0 -0
  222. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/sets.py +0 -0
  223. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/tridiag.py +0 -0
  224. {sdevpy-1.0.8/sdevpy/maths/rand → sdevpy-1.1/sdevpy/models}/__init__.py +0 -0
  225. {sdevpy-1.0.8/sdevpy/models → sdevpy-1.1/sdevpy/montecarlo}/__init__.py +0 -0
  226. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/montecarlo/pathgenerator.py +0 -0
  227. {sdevpy-1.0.8/sdevpy/montecarlo → sdevpy-1.1/sdevpy/montecarlo/payoffs}/__init__.py +0 -0
  228. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/montecarlo/payoffs/cashflows.py +0 -0
  229. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/montecarlo/payoffs/exotics.py +0 -0
  230. {sdevpy-1.0.8/sdevpy/montecarlo/payoffs → sdevpy-1.1/sdevpy/pde}/__init__.py +0 -0
  231. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/settings.py +0 -0
  232. {sdevpy-1.0.8/sdevpy/pde → sdevpy-1.1/sdevpy/tensorflow}/__init__.py +0 -0
  233. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tensorflow/tf_black.py +0 -0
  234. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tests/__init__.py +0 -0
  235. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tests/test.py +0 -0
  236. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tests/test_utils.py +0 -0
  237. {sdevpy-1.0.8/sdevpy/projects → sdevpy-1.1/sdevpy/thirdparty}/__init__.py +0 -0
  238. {sdevpy-1.0.8/sdevpy/projects/aad → sdevpy-1.1/sdevpy/timeseries}/__init__.py +0 -0
  239. {sdevpy-1.0.8/sdevpy/projects/raschka → sdevpy-1.1/sdevpy/tree}/__init__.py +0 -0
  240. {sdevpy-1.0.8/sdevpy/projects/stovol → sdevpy-1.1/sdevpy/utilities}/__init__.py +0 -0
  241. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/utilities/book.py +0 -0
  242. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/utilities/clipboard.py +0 -0
  243. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/utilities/timer.py +0 -0
  244. {sdevpy-1.0.8/sdevpy/projects/stovolinverse → sdevpy-1.1/sdevpy/volatility}/__init__.py +0 -0
  245. {sdevpy-1.0.8/sdevpy/tensorflow → sdevpy-1.1/sdevpy/volatility/impliedvol}/__init__.py +0 -0
  246. {sdevpy-1.0.8/sdevpy/thirdparty → sdevpy-1.1/sdevpy/volatility/impliedvol/models}/__init__.py +0 -0
  247. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/models/sabr.py +0 -0
  248. {sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black/greeks → sdevpy-1.1/sdevpy/volatility/localvol}/__init__.py +0 -0
  249. {sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes/greeks → sdevpy-1.1/sdevpy/volatility/mlsurfacegen}/__init__.py +0 -0
  250. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/mlsurfacegen/fbsabrgenerator.py +0 -0
  251. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/mlsurfacegen/mchestongenerator.py +0 -0
  252. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/mlsurfacegen/mcsabrgenerator.py +0 -0
  253. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/mlsurfacegen/mczabrgenerator.py +0 -0
  254. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/mlsurfacegen/smilegenerator.py +0 -0
  255. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/mlsurfacegen/stovolfactory.py +0 -0
  256. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy.egg-info/dependency_links.txt +0 -0
  257. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy.egg-info/requires.txt +0 -0
  258. {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy.egg-info/top_level.txt +0 -0
  259. {sdevpy-1.0.8 → sdevpy-1.1}/setup.cfg +0 -0
sdevpy-1.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) [2023] [Sebastien Gurrieri]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,14 +1,36 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sdevpy
3
- Version: 1.0.8
3
+ Version: 1.1
4
4
  Summary: Python package for Finance
5
5
  Author-email: Sebastien Gurrieri <sebgur@gmail.com>
6
- Project-URL: Git page, https://github.com/sebgur/SDev.Python
6
+ License: MIT License
7
+
8
+ Copyright (c) [2023] [Sebastien Gurrieri]
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ Project-URL: Git, https://github.com/sebgur/SDev.Python
7
28
  Project-URL: SDev Finance, http://sdev-finance.com/
8
29
  Classifier: Programming Language :: Python :: 3
9
30
  Classifier: Operating System :: OS Independent
10
31
  Requires-Python: >=3.6
11
32
  Description-Content-Type: text/markdown
33
+ License-File: LICENSE
12
34
  Requires-Dist: pandas
13
35
  Requires-Dist: numpy
14
36
  Requires-Dist: scipy
@@ -18,6 +40,7 @@ Requires-Dist: pandas_market_calendars
18
40
  Requires-Dist: openpyxl
19
41
  Requires-Dist: colorlog
20
42
  Requires-Dist: scikit-learn
43
+ Dynamic: license-file
21
44
 
22
45
  # SDev.Python
23
46
 
@@ -4,8 +4,9 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "sdevpy"
7
- version = "1.0.8"
8
- license-files = []
7
+ version = "1.1"
8
+ license = {file = "LICENSE"}
9
+ #license-files = []
9
10
  authors = [{ name="Sebastien Gurrieri", email="sebgur@gmail.com" }]
10
11
  description = "Python package for Finance"
11
12
  readme = "README.md"
@@ -21,6 +22,10 @@ dependencies = ["pandas", "numpy", "scipy", "matplotlib", "holidays",
21
22
  #dependencies = ["pyperclip", "tensorflow", "scikit-learn",
22
23
  # "tensorflow_probability", "silence_tensorflow"]
23
24
 
25
+ [project.urls]
26
+ "Git" = "https://github.com/sebgur/SDev.Python"
27
+ "SDev Finance" = "http://sdev-finance.com/"
28
+
24
29
  [tool.setuptools.packages.find]
25
30
  where = ["."]
26
31
  include = ["sdevpy*"] # Only include these
@@ -38,6 +43,21 @@ target-version = "py313"
38
43
  select = ["E", "F", "N", "W", "UP", "B"]
39
44
  ignore = ["E401", "I001"]
40
45
 
41
- [project.urls]
42
- "Git page" = "https://github.com/sebgur/SDev.Python"
43
- "SDev Finance" = "http://sdev-finance.com/"
46
+ [tool.coverage.run]
47
+ omit = [
48
+ "sdevpy/**/__init__.py",
49
+ "sdevpy/tests/test.py",
50
+ "sdevpy/logger.py",
51
+ "sdevpy/settings.py",
52
+ "sdevpy/utilities/constants.py",
53
+ "sdevpy/utilities/pydotnet.py",
54
+ "sdevpy/tensorflow/*",
55
+ "sdevpy/machinelearning/*",
56
+ "sdevpy/volatility/mlsurfacegen/*"
57
+ ]
58
+
59
+ [tool.coverage.report]
60
+ exclude_lines = [
61
+ "if __name__ == .__main__.:",
62
+ "pragma: no cov"
63
+ ]
@@ -0,0 +1,26 @@
1
+ __version__ = '1.0.5'
2
+
3
+ import logging
4
+
5
+ logging.getLogger(__name__).addHandler(logging.NullHandler())
6
+
7
+
8
+
9
+ # import colorlog
10
+
11
+ # handler = colorlog.StreamHandler()
12
+ # handler.setFormatter(colorlog.ColoredFormatter(
13
+ # "%(log_color)s%(levelname)-8s%(reset)s %(name)s - %(message)s",
14
+ # log_colors={
15
+ # "DEBUG": "cyan",
16
+ # "INFO": "green",
17
+ # "WARNING": "yellow",
18
+ # "ERROR": "red",
19
+ # "CRITICAL": "bold_red",
20
+ # }
21
+ # ))
22
+
23
+ # root = logging.getLogger()
24
+ # root.addHandler(handler)
25
+ # root.setLevel(logging.WARNING)
26
+ # logging.getLogger("sdevpy").setLevel(logging.DEBUG)
@@ -6,32 +6,20 @@ from scipy.optimize import minimize_scalar
6
6
 
7
7
 
8
8
  def price(expiry: npt.ArrayLike, strike: npt.ArrayLike, is_call: npt.ArrayLike, fwd: npt.ArrayLike,
9
- vol: npt.ArrayLike) -> npt.ArrayLike:
9
+ vol: npt.ArrayLike) -> npt.NDArray[np.float64]:
10
10
  """ Option price under the Bachelier model """
11
- stdev = vol * expiry**0.5
11
+ stdev = vol * np.sqrt(expiry)
12
12
  d = (fwd - strike) / stdev
13
13
  wd = np.where(is_call, d, -d)
14
- # wd = d if is_call else -d
15
14
  return stdev * (wd * norm.cdf(wd) + norm.pdf(d))
16
15
 
17
16
 
18
17
  def price_straddles(expiry: npt.ArrayLike, strike: npt.ArrayLike, fwd: npt.ArrayLike,
19
- vol: npt.ArrayLike) -> npt.ArrayLike:
20
- """ Straddle price under the Bachelier model.
21
- Note: we could improve the speed by writing the code in-line instead of
22
- calling the price() function twice """
23
- expiries_ = np.asarray(expiry).reshape(-1, 1)
24
- prices = []
25
- for i, exp_row in enumerate(expiries_):
26
- k_prices = []
27
- for j, k in enumerate(strike[i]):
28
- iv = vol[i, j]
29
- call_price = price(exp_row, k, True, fwd, iv)
30
- put_price = price(exp_row, k, False, fwd, iv)
31
- k_prices.append(call_price[0] + put_price[0])
32
- prices.append(k_prices)
33
-
34
- return np.asarray(prices)
18
+ vol: npt.ArrayLike) -> npt.NDArray[np.float64]:
19
+ """ Straddle price under the Bachelier model """
20
+ call = price(expiry[:, None], strike, True, fwd, vol)
21
+ put = price(expiry[:, None], strike, False, fwd, vol)
22
+ return call + put
35
23
 
36
24
 
37
25
  def implied_vol_jaeckel(expiry: float, strike: float, is_call: bool, fwd: float, fwd_price: float) -> float:
@@ -3,14 +3,12 @@ import numpy as np
3
3
  import numpy.typing as npt
4
4
  from scipy.stats import norm
5
5
  from scipy.optimize import minimize_scalar
6
- from sdevpy.thirdparty.py_vollib.black import implied_volatility as jaeckel
7
6
  from sdevpy.utilities.tools import isiterable
8
7
 
9
8
 
10
9
  def price(expiry: npt.ArrayLike, strike: npt.ArrayLike, is_call: npt.ArrayLike, fwd: npt.ArrayLike,
11
10
  vol: npt.ArrayLike) -> npt.NDArray[np.float64]:
12
11
  """ Option price under the Black-Scholes model """
13
- # w = 1.0 if is_call else -1.0
14
12
  w = np.where(is_call, 1.0, -1.0)
15
13
  s = vol * np.sqrt(expiry)
16
14
  d1 = np.log(fwd / strike) / s + 0.5 * s
@@ -18,12 +16,25 @@ def price(expiry: npt.ArrayLike, strike: npt.ArrayLike, is_call: npt.ArrayLike,
18
16
  return w * (fwd * norm.cdf(w * d1) - strike * norm.cdf(w * d2))
19
17
 
20
18
 
19
+ def price_straddles(expiry: npt.ArrayLike, strike: npt.ArrayLike, fwd: npt.ArrayLike,
20
+ vol: npt.ArrayLike) -> npt.NDArray[np.float64]:
21
+ """ Straddle price under the Black model """
22
+ call = price(expiry, strike, True, fwd, vol)
23
+ put = price(expiry, strike, False, fwd, vol)
24
+ return call + put
25
+
26
+
21
27
  def implied_vol(expiry: float, strike: float, is_call: bool, fwd: float, fwd_price: float) -> float:
22
28
  """ Direct method by numerical inversion using Brent.
23
29
  Non-vectorized due to solver. """
24
- options = {'xtol': 1e-4, 'maxiter': 100, 'disp': False}
30
+ # Trial config
31
+ options = {'xtol': 1e-6, 'maxiter': 100, 'disp': False}
25
32
  xmin = 1e-6
26
- xmax = 1.0
33
+ xmax = 2.0
34
+ # # Original config
35
+ # options = {'xtol': 1e-4, 'maxiter': 100, 'disp': False}
36
+ # xmin = 1e-6
37
+ # xmax = 1.0
27
38
 
28
39
  def error(vol):
29
40
  premium = price(expiry, strike, is_call, fwd, vol)
@@ -34,7 +45,7 @@ def implied_vol(expiry: float, strike: float, is_call: bool, fwd: float, fwd_pri
34
45
 
35
46
 
36
47
  def implied_vols(expiry: float, strike: npt.ArrayLike, is_call: bool, fwd: float,
37
- fwd_price: npt.ArrayLike) -> npt.ArrayLike:
48
+ fwd_price: npt.ArrayLike) -> npt.NDArray[np.float64]:
38
49
  """ Black implied volatility for vector of strikes/prices """
39
50
  if isiterable(strike) and isiterable(fwd_price):
40
51
  ivs = [implied_vol(expiry, k, is_call, fwd, p) for k, p in zip(strike, fwd_price, strict=True)]
@@ -46,7 +57,7 @@ def implied_vols(expiry: float, strike: npt.ArrayLike, is_call: bool, fwd: float
46
57
 
47
58
 
48
59
  def implied_vol_newton(expiry: float, strike: npt.ArrayLike, is_call: bool, fwd: float,
49
- fwd_price: npt.ArrayLike, tol: float=1e-8, max_iter: int=50) -> npt.ArrayLike:
60
+ fwd_price: npt.ArrayLike, tol: float=1e-8, max_iter: int=50) -> npt.NDArray[np.float64]:
50
61
  """ Using vectorized Newton-Raphson, with faster convergence than Brent.
51
62
  However, this method can struggle for very small vegas, so we may want to switch
52
63
  to another method (maybe Brent above) below a certain vega threshold.
@@ -66,22 +77,12 @@ def implied_vol_newton(expiry: float, strike: npt.ArrayLike, is_call: bool, fwd:
66
77
  if np.all(np.abs(diff) < tol):
67
78
  break
68
79
 
69
- # if len(strike) == 1 and len(fwd_price) == 1 and len(vol) == 1: # Return a scalar if inputs are scalar
70
- # return vol[0]
71
- # else:
72
- # return vol
73
- return (vol.item() if vol.ndim == 0 or vol.size ==1 else vol)
74
-
75
-
76
- def implied_vol_jaeckel(expiry: float, strike: float, is_call: bool, fwd: float, fwd_price: float) -> float:
77
- """ Black-Scholes implied volatility using P. Jaeckel's 'Let's be rational' method,
78
- from package py_vollib. Install with pip install py_vollib or at
79
- https://pypi.org/project/py_vollib/. Unfortunately we found it has instabilities
80
- near ATM. """
81
- flag = 'c' if is_call else 'p'
82
- p = fwd_price
83
- iv = jaeckel.implied_volatility_of_undiscounted_option_price(p, fwd, strike, expiry, flag)
84
- return iv
80
+ # # if len(strike) == 1 and len(fwd_price) == 1 and len(vol) == 1: # Return a scalar if inputs are scalar
81
+ # # return vol[0]
82
+ # # else:
83
+ # # return vol
84
+ # return (vol.item() if vol.ndim == 0 or vol.size ==1 else vol)
85
+ return vol
85
86
 
86
87
 
87
88
  if __name__ == "__main__":
@@ -98,8 +99,8 @@ if __name__ == "__main__":
98
99
  k_space = np.linspace(20, 2180, NUM_POINTS)
99
100
  prices = price(EXPIRY, k_space, IS_CALL, f_space, VOL)
100
101
  # print(prices)
101
- implied_vols = []
102
+ iv_results = []
102
103
  for i, k in enumerate(k_space):
103
- implied_vols.append(implied_vol(EXPIRY, k, IS_CALL, f_space[i], prices[i]))
104
+ iv_results.append(implied_vol(EXPIRY, k, IS_CALL, f_space[i], prices[i]))
104
105
 
105
- # print(implied_vols)
106
+ # print(iv_results)
@@ -0,0 +1,20 @@
1
+ from enum import Enum
2
+
3
+
4
+ class OptionType(Enum):
5
+ CALL = 0
6
+ PUT = 1
7
+ STRADDLE = 2
8
+
9
+
10
+ def string_to_optiontype(s: str) -> OptionType:
11
+ """ Convert string to OptionType """
12
+ match s.lower():
13
+ case 'call':
14
+ return OptionType.CALL
15
+ case 'put':
16
+ return OptionType.PUT
17
+ case 'straddle':
18
+ return OptionType.STRADDLE
19
+ case _:
20
+ raise ValueError(f"Invalid option type: {s}")
@@ -0,0 +1,42 @@
1
+ import logging, colorlog
2
+
3
+
4
+ LOG_COLORS = {"DEBUG": "cyan", "INFO": "green", "WARNING": "yellow", "ERROR": "red",
5
+ "CRITICAL": "bold_red"}
6
+
7
+
8
+ def string_to_logging_level(level_str: str):
9
+ level_str = level_str.lower()
10
+ match level_str:
11
+ case 'debug':
12
+ return logging.DEBUG
13
+ case 'info':
14
+ return logging.INFO
15
+ case 'warning':
16
+ return logging.WARNING
17
+ case 'error':
18
+ return logging.ERROR
19
+ case 'critical':
20
+ return logging.CRITICAL
21
+ case _:
22
+ raise ValueError(f"Unsupported logging level: {level_str}")
23
+
24
+
25
+ def configure(root_level: str='warning', sdevpy_level: str='debug',
26
+ module_display: str='none') -> None:
27
+ """ Configure logger. Levels can be: debug, info, warning, error and critical. """
28
+ handler = colorlog.StreamHandler()
29
+
30
+ match module_display.lower():
31
+ case 'none':
32
+ module_str = "%(log_color)s%(levelname)-1s%(reset)s | %(message)s"
33
+ case 'partial':
34
+ module_str = "%(log_color)s%(levelname)-1s%(reset)s | %(module)s | %(message)s"
35
+ case 'full':
36
+ module_str = "%(log_color)s%(levelname)-1s%(reset)s | %(name)s | %(message)s"
37
+ case _:
38
+ raise ValueError(f"Unsupported module display mode: {module_display}")
39
+
40
+ handler.setFormatter(colorlog.ColoredFormatter(module_str, log_colors=LOG_COLORS))
41
+ logging.basicConfig(level=string_to_logging_level(root_level), handlers=[handler], force=True)
42
+ logging.getLogger("sdevpy").setLevel(string_to_logging_level(sdevpy_level))
@@ -1,7 +1,7 @@
1
1
  """ Custom learning schedules """
2
2
  import numpy as np
3
3
  import tensorflow as tf
4
- from sdevpy.utilities.constants import TWO_PI
4
+ from sdevpy.maths.constants import TWO_PI
5
5
 
6
6
 
7
7
  # Custom learning rate scheduler, exponentially decreases between given values
@@ -57,13 +57,15 @@ def add_correlations(date: dt.datetime, names1: list[str], names2: list[str], va
57
57
  f.write(f"{name1}-{name2},{value}\n")
58
58
 
59
59
 
60
- def data_file(date: dt.datetime, **kwargs):
60
+ def data_file(date: dt.datetime, **kwargs) -> str:
61
+ """ Data file for correlations """
61
62
  folder = kwargs.get('folder', test_data_folder())
62
63
  file = Path(folder) / (date.strftime(dts.DATE_FILE_FORMAT) + ".csv")
63
64
  return file
64
65
 
65
66
 
66
- def test_data_folder():
67
+ def test_data_folder() -> str:
68
+ """ Test data folder for correlations """
67
69
  folder = Path(__file__).parent.parent.parent / "datasets" / "marketdata" / "correlations"
68
70
  folder.mkdir(parents=True, exist_ok=True)
69
71
  return folder
@@ -2,7 +2,8 @@ import os, json
2
2
  import datetime as dt
3
3
  import numpy as np
4
4
  from pathlib import Path
5
- from sdevpy.utilities import dates, timegrids
5
+ from sdevpy.utilities import dates as dts
6
+ from sdevpy.utilities import timegrids
6
7
  from sdevpy.maths import interpolation as itp
7
8
  from sdevpy.market import yieldcurve as ycrv
8
9
  from sdevpy.market.spot import get_spots
@@ -16,17 +17,9 @@ def get_forward_curves(names, valdate, **kwargs):
16
17
  file = data_file(name, valdate, **kwargs)
17
18
  data = eqforwarddata_from_file(file)
18
19
  curve = EqForwardCurve(valdate=valdate, interp_var='forward', interp_type='cubicspline')
19
- # yieldcurve = ycrv.get_yieldcurve('USD.SOFR.1D', valdate)
20
- curve.calibrate(data, spot)#, yieldcurve)
20
+ curve.calibrate(data, spot)
21
21
  fwd_curves.append(curve)
22
22
 
23
- # drifts = np.asarray([0.02, 0.05, 0.04])
24
- # fwd_curves_old = []
25
- # for s, mu in zip(spots, drifts):
26
- # # Use the default variable trick to circumvent late binding in python loops
27
- # # Otherwise, all the lambda functions will effectively be the same
28
- # fwd_curves_old.append(lambda t, s=s, mu=mu: s * np.exp(mu * t))
29
-
30
23
  return fwd_curves
31
24
 
32
25
 
@@ -51,12 +44,12 @@ class EqForwardData:
51
44
  def dump_data(self):
52
45
  pillars = []
53
46
  for expiry, forward in zip(self.expiries, self.forwards, strict=True):
54
- expiry_str = expiry.strftime(dates.DATE_FORMAT)
47
+ expiry_str = expiry.strftime(dts.DATE_FORMAT)
55
48
  pillar = {'expiry': expiry_str, 'forward': forward}
56
49
  pillars.append(pillar)
57
50
 
58
- data = {'name': self.name, 'valdate': self.valdate.strftime(dates.DATE_FORMAT),
59
- 'snapdate': self.snapdate.strftime(dates.DATETIME_FORMAT), 'pillars': pillars}
51
+ data = {'name': self.name, 'valdate': self.valdate.strftime(dts.DATE_FORMAT),
52
+ 'snapdate': self.snapdate.strftime(dts.DATETIME_FORMAT), 'pillars': pillars}
60
53
  return data
61
54
 
62
55
 
@@ -80,7 +73,7 @@ class EqForwardCurve:
80
73
  # Check consistency
81
74
  for d in self.dates:
82
75
  if d <= self.valdate:
83
- raise RuntimeError(f"Expiry before or at valuation date: {d.strftime(dates.DATE_FORMAT)}")
76
+ raise RuntimeError(f"Expiry before or at valuation date: {d.strftime(dts.DATE_FORMAT)}")
84
77
 
85
78
  # Calibrate
86
79
  if self.interp_var == 'yield':
@@ -133,11 +126,11 @@ def eqforwarddata_from_file(file):
133
126
  # Convert date strings into dates
134
127
  for pillar in pillars:
135
128
  date_str = pillar.get('expiry')
136
- date = dt.datetime.strptime(date_str, dates.DATE_FORMAT)
129
+ date = dt.datetime.strptime(date_str, dts.DATE_FORMAT)
137
130
  pillar['expiry'] = date
138
131
 
139
- data = EqForwardData(dt.datetime.strptime(valdate, dates.DATE_FORMAT), pillars,
140
- name=name, snapdate=dt.datetime.strptime(snapdate, dates.DATETIME_FORMAT))
132
+ data = EqForwardData(dt.datetime.strptime(valdate, dts.DATE_FORMAT), pillars,
133
+ name=name, snapdate=dt.datetime.strptime(snapdate, dts.DATETIME_FORMAT))
141
134
  return data
142
135
 
143
136
 
@@ -145,7 +138,7 @@ def data_file(name, date, **kwargs):
145
138
  folder = kwargs.get('folder', test_data_folder())
146
139
  name_folder = os.path.join(folder, name)
147
140
  os.makedirs(name_folder, exist_ok=True)
148
- file = os.path.join(name_folder, date.strftime(dates.DATE_FILE_FORMAT) + ".json")
141
+ file = os.path.join(name_folder, date.strftime(dts.DATE_FILE_FORMAT) + ".json")
149
142
  return file
150
143
 
151
144
 
@@ -161,7 +154,7 @@ if __name__ == "__main__":
161
154
  import matplotlib.pyplot as plt
162
155
 
163
156
  name = "ABC"
164
- valdate = dt.datetime(2026, 2, 15)
157
+ valdate = dt.datetime(2025, 12, 15)
165
158
 
166
159
  # Generate a sample to start from
167
160
  spot = 100.0
@@ -174,7 +167,7 @@ if __name__ == "__main__":
174
167
  pillars = [{'expiry': d, 'forward': f} for d, f in zip(expiries, zfwds, strict=True)]
175
168
  data = EqForwardData(valdate, pillars, name=name)
176
169
  file = data_file(name, valdate)
177
- data.dump(file)
170
+ # data.dump(file)
178
171
 
179
172
  # Get data from existing file
180
173
  test_data = eqforwarddata_from_file(file)
@@ -185,7 +178,7 @@ if __name__ == "__main__":
185
178
  curve.calibrate(test_data, spot, yieldcurve)
186
179
 
187
180
  # Interpolate and display
188
- test_dates = [dates.advance(valdate, months=1*n) for n in range(1, 150)]
181
+ test_dates = [dts.advance(valdate, str(n) + 'm') for n in range(1, 150)]
189
182
  test_fwds = curve.value(test_dates)
190
183
 
191
184
  # Original data
@@ -1,11 +1,13 @@
1
1
  import os, json, logging
2
2
  import datetime as dt
3
3
  import numpy as np
4
+ import numpy.typing as npt
4
5
  from pathlib import Path
5
6
  from sdevpy.utilities import dates
6
7
  from sdevpy.utilities import timegrids
7
8
  from sdevpy.analytics import black
8
- log = logging.getLogger(__name__)
9
+ from sdevpy.market.eqforward import EqForwardCurve, get_forward_curves
10
+ log = logging.getLogger(Path(__file__).stem)
9
11
 
10
12
 
11
13
  class EqVolSurfaceData:
@@ -20,46 +22,58 @@ class EqVolSurfaceData:
20
22
 
21
23
  # Extract
22
24
  self.expiries = np.asarray([s['expiry'] for s in sections])
23
- self.forwards = np.asarray([s['forward'] for s in sections])
24
25
  self.input_strikes = [np.asarray(s['strikes']) for s in sections]
25
26
  self.vols = [np.asarray(s['vols']) for s in sections]
26
27
 
27
28
  # Size checks
28
29
  n_times = len(self.expiries)
29
- if any(len(x) != n_times for x in (self.forwards, self.input_strikes, self.vols)):
30
+ if any(len(x) != n_times for x in (self.input_strikes, self.vols)):
30
31
  raise ValueError("Incompatible size along time direction between expiries, forwards, strikes and vols")
31
32
 
32
- # Calculate missing strike type
33
- if self.strike_input_type == 'absolute':
34
- self.abs_strikes = self.input_strikes
35
- elif self.strike_input_type == 'relative':
36
- # self.abs_strikes = self.forwards * self.input_strikes # Old way assuming numpy matrix
37
- # The code below is a quick non-tested implementation to cater for the non-matrix case.
38
- # To be tested if/when case presents itself.
39
- log.warning("Relative strike conversion should be tested")
40
- self.abs_strikes = []
41
- for i in range(n_times):
42
- self.abs_strikes.append(self.forwards[i] * self.input_strikes[i])
43
- else:
44
- raise ValueError(f"Strike input type not supported yet: {self.strike_input_type}")
45
-
46
- # Calculate prices
47
- self.call_prices = []
33
+ def get_prices(self, fwd_curve: EqForwardCurve, option_type: str='call') -> npt.ArrayLike:
34
+ option_type_lw = option_type.lower()
35
+ prices = []
36
+ abs_strikes = self.get_strikes(fwd_curve, to_type='absolute')
48
37
  for exp_idx, expiry in enumerate(self.expiries):
49
38
  t = timegrids.model_time(self.valdate, expiry)
50
- fwd = self.forwards[exp_idx]
51
- strikes = self.abs_strikes[exp_idx]
39
+ fwd = fwd_curve.value(expiry)
40
+ strikes = abs_strikes[exp_idx]
52
41
  vols = self.vols[exp_idx]
53
- self.call_prices.append(black.price(t, strikes, True, fwd, vols))
54
-
55
- def get_strikes(self, type: str='absolute'):
56
- req_type = type.lower()
57
- if req_type == 'absolute':
58
- return self.abs_strikes
59
- elif req_type == 'relative':
60
- return self.abs_strikes / self.forwards.reshape(-1, 1)
61
- else:
62
- raise ValueError(f"Unknown strike type {req_type}: expected absolute or relative")
42
+ match option_type_lw:
43
+ case 'call':
44
+ price = black.price(t, strikes, True, fwd, vols)
45
+ case 'put':
46
+ price = black.price(t, strikes, False, fwd, vols)
47
+ case 'straddle':
48
+ price = black.price(t, strikes, True, fwd, vols)
49
+ price = price + black.price(t, strikes, False, fwd, vols)
50
+ case _:
51
+ raise ValueError(f"Invalid option type: {option_type}")
52
+
53
+ prices.append(price)
54
+ return prices
55
+
56
+ def get_strikes(self, fwd_curve: EqForwardCurve=None, to_type: str='absolute') -> npt.ArrayLike:
57
+ """ Retrieve strikes, absolute or relative """
58
+ to_type_lw = to_type.lower()
59
+ if to_type_lw == self.strike_input_type:
60
+ return self.input_strikes
61
+ else: # Need conversion
62
+ if fwd_curve is None:
63
+ raise ValueError(f"Forward curve required for strike conversion but None given: {self.name}")
64
+
65
+ # Need to loop over expiries because not all expiries must have the same number of strikes.
66
+ # Therefore we cannot put the strikes into numpy arrays.
67
+ fwds = fwd_curve.value(self.expiries)
68
+ n_times = len(self.expiries)
69
+ if to_type_lw == 'absolute' and self.strike_input_type == 'relative':
70
+ conv_strikes = [self.input_strikes[i] * fwds[i] for i in range(n_times)]
71
+ elif to_type_lw == 'relative' and self.strike_input_type == 'absolute':
72
+ conv_strikes = [self.input_strikes[i] / fwds[i] for i in range(n_times)]
73
+ else:
74
+ raise ValueError(f"Unknown strike type {to_type}: expected absolute or relative")
75
+
76
+ return conv_strikes
63
77
 
64
78
  def dump(self, file, indent=2):
65
79
  data = self.dump_data()
@@ -70,7 +84,7 @@ class EqVolSurfaceData:
70
84
  sections = []
71
85
  for i, expiry in enumerate(self.expiries):
72
86
  expiry_str = expiry.strftime(dates.DATE_FORMAT)
73
- section = {'expiry': expiry_str, 'forward': self.forwards[i], 'strikes': self.input_strikes[i].tolist(),
87
+ section = {'expiry': expiry_str, 'strikes': self.input_strikes[i].tolist(),
74
88
  'vols': self.vols[i].tolist()}
75
89
  sections.append(section)
76
90
 
@@ -93,7 +107,6 @@ class EqVolSurfaceData:
93
107
  for i in range(n_exp):
94
108
  print(sep)
95
109
  print(f"Expiry {i+1}/{n_exp}: {self.expiries[i].strftime(dates.DATE_FORMAT)}")
96
- print(f"Forward: {self.forwards[i]:,.{n_digits}f}")
97
110
  with np.printoptions(precision=n_digits):
98
111
  print("Strikes", self.input_strikes[i])
99
112
  print("Vols", self.vols[i])
@@ -158,9 +171,12 @@ if __name__ == "__main__":
158
171
  # file = data_file(folder, name, valdate)
159
172
  # surface_data.dump(file)
160
173
 
174
+ # Get forward curve
175
+ fwd_curve = get_forward_curves([name], valdate)[0]
176
+
161
177
  # Get data from existing file
162
- file = data_file(folder, name, valdate)
178
+ file = data_file(name, valdate, folder=folder)
163
179
  surface_data = eqvolsurfacedata_from_file(file)
164
- print(surface_data.get_strikes('absolute'))
165
- print(surface_data.get_strikes('relative'))
180
+ print(surface_data.get_strikes(to_type='absolute'))
181
+ print(surface_data.get_strikes(fwd_curve=fwd_curve, to_type='relative'))
166
182
  surface_data.pretty_print(4)