sdevpy 1.0.6__tar.gz → 1.0.8__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 (243) hide show
  1. {sdevpy-1.0.6 → sdevpy-1.0.8}/PKG-INFO +8 -1
  2. sdevpy-1.0.8/pyproject.toml +43 -0
  3. sdevpy-1.0.8/sdevpy/__init__.py +21 -0
  4. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/analytics/americantree.py +7 -8
  5. sdevpy-1.0.8/sdevpy/analytics/bachelier.py +140 -0
  6. sdevpy-1.0.8/sdevpy/analytics/black.py +105 -0
  7. sdevpy-1.0.8/sdevpy/cointegration/back_testing.py +224 -0
  8. sdevpy-1.0.8/sdevpy/cointegration/coint_trading.py +677 -0
  9. sdevpy-1.0.8/sdevpy/cointegration/data_io.py +14 -0
  10. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/cointegration/mean_reversion.py +117 -128
  11. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/cointegration/model_settings.py +6 -5
  12. sdevpy-1.0.8/sdevpy/cointegration/utils.py +132 -0
  13. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/machinelearning/datasets.py +12 -9
  14. {sdevpy-1.0.6/sdevpy/machinelearning → sdevpy-1.0.8/sdevpy/machinelearning/keras}/callbacks.py +1 -3
  15. {sdevpy-1.0.6/sdevpy/machinelearning → sdevpy-1.0.8/sdevpy/machinelearning/keras}/learningmodel.py +8 -6
  16. {sdevpy-1.0.6/sdevpy/machinelearning → sdevpy-1.0.8/sdevpy/machinelearning/keras}/learningschedules.py +3 -4
  17. {sdevpy-1.0.6/sdevpy → sdevpy-1.0.8/sdevpy/machinelearning}/llms/attention.py +3 -5
  18. {sdevpy-1.0.6/sdevpy → sdevpy-1.0.8/sdevpy/machinelearning}/llms/chat.py +2 -2
  19. {sdevpy-1.0.6/sdevpy → sdevpy-1.0.8/sdevpy/machinelearning}/llms/datasets.py +0 -1
  20. {sdevpy-1.0.6/sdevpy → sdevpy-1.0.8/sdevpy/machinelearning}/llms/gpt.py +7 -6
  21. {sdevpy-1.0.6/sdevpy → sdevpy-1.0.8/sdevpy/machinelearning}/llms/instructions.py +8 -8
  22. sdevpy-1.0.6/sdevpy/llms/ModelConverter.py → sdevpy-1.0.8/sdevpy/machinelearning/llms/modelconverter.py +3 -5
  23. {sdevpy-1.0.6/sdevpy → sdevpy-1.0.8/sdevpy/machinelearning}/llms/training.py +2 -1
  24. sdevpy-1.0.8/sdevpy/market/correlations.py +76 -0
  25. sdevpy-1.0.8/sdevpy/market/eqforward.py +197 -0
  26. sdevpy-1.0.8/sdevpy/market/eqvolsurface.py +166 -0
  27. sdevpy-1.0.8/sdevpy/market/fixings.py +181 -0
  28. sdevpy-1.0.8/sdevpy/market/spot.py +81 -0
  29. sdevpy-1.0.8/sdevpy/market/yieldcurve.py +246 -0
  30. sdevpy-1.0.8/sdevpy/maths/__init__.py +0 -0
  31. sdevpy-1.0.8/sdevpy/maths/constants.py +28 -0
  32. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/maths/interpolation.py +73 -35
  33. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/maths/optimization.py +30 -27
  34. sdevpy-1.0.8/sdevpy/maths/rand/__init__.py +0 -0
  35. sdevpy-1.0.8/sdevpy/maths/rand/correlations.py +44 -0
  36. sdevpy-1.0.8/sdevpy/maths/rand/pathconstruction.py +180 -0
  37. sdevpy-1.0.8/sdevpy/maths/rand/rng.py +148 -0
  38. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/maths/regression.py +1 -1
  39. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/maths/specialfunctions.py +1 -0
  40. sdevpy-1.0.8/sdevpy/models/__init__.py +0 -0
  41. sdevpy-1.0.8/sdevpy/models/assetmodels.py +90 -0
  42. sdevpy-1.0.8/sdevpy/models/multiasset_heston.py +37 -0
  43. sdevpy-1.0.8/sdevpy/montecarlo/__init__.py +0 -0
  44. sdevpy-1.0.8/sdevpy/montecarlo/mcpricer.py +188 -0
  45. sdevpy-1.0.8/sdevpy/montecarlo/mcrun.py +102 -0
  46. sdevpy-1.0.8/sdevpy/montecarlo/pathgenerator.py +31 -0
  47. sdevpy-1.0.8/sdevpy/montecarlo/payoffs/__init__.py +0 -0
  48. sdevpy-1.0.8/sdevpy/montecarlo/payoffs/basic.py +669 -0
  49. sdevpy-1.0.8/sdevpy/montecarlo/payoffs/cashflows.py +32 -0
  50. sdevpy-1.0.8/sdevpy/montecarlo/payoffs/exotics.py +96 -0
  51. sdevpy-1.0.8/sdevpy/montecarlo/payoffs/vanillas.py +76 -0
  52. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/montecarlo/smoothers.py +1 -9
  53. sdevpy-1.0.8/sdevpy/pde/__init__.py +0 -0
  54. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/pde/forwardpde.py +16 -19
  55. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/pde/pdeschemes.py +1 -4
  56. sdevpy-1.0.8/sdevpy/projects/__init__.py +0 -0
  57. sdevpy-1.0.8/sdevpy/projects/aad/__init__.py +0 -0
  58. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/projects/aad/aad_mc.py +2 -1
  59. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/projects/aad/aad_mc_nd.py +4 -4
  60. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/projects/chat_gpt2.py +3 -3
  61. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/projects/datafiles.py +1 -1
  62. sdevpy-1.0.8/sdevpy/projects/raschka/__init__.py +0 -0
  63. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/projects/raschka/ch2_working_with_text.py +3 -3
  64. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/projects/raschka/ch3_coding_attention.py +3 -3
  65. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/projects/raschka/ch4_gpt_model.py +3 -3
  66. sdevpy-1.0.6/sdevpy/projects/raschka/ch5_loadGPT2.py → sdevpy-1.0.8/sdevpy/projects/raschka/ch5_loadgpt2.py +2 -2
  67. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/projects/raschka/ch5_pretraining.py +7 -7
  68. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/projects/raschka/ch7_instruction_finetuning.py +2 -2
  69. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/projects/raschka/raschka_datasetloader.py +2 -2
  70. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/projects/raschka/raschka_dnn.py +1 -1
  71. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/projects/raschka/raschka_gpt_download.py +1 -1
  72. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/projects/set_limits.py +4 -7
  73. sdevpy-1.0.8/sdevpy/projects/stovol/__init__.py +0 -0
  74. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/projects/stovol/stovolgen.py +3 -3
  75. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/projects/stovol/stovolplot.py +1 -2
  76. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/projects/stovol/stovoltrain.py +3 -4
  77. sdevpy-1.0.8/sdevpy/projects/stovolinverse/__init__.py +0 -0
  78. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/projects/stovolinverse/stovolinvgen.py +4 -3
  79. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/projects/stovolinverse/stovolinvtrain.py +8 -8
  80. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/projects/update_db.py +1 -6
  81. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/settings.py +7 -6
  82. sdevpy-1.0.8/sdevpy/tensorflow/__init__.py +0 -0
  83. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/tensorflow/tf_black.py +4 -4
  84. sdevpy-1.0.8/sdevpy/tests/test.py +82 -0
  85. sdevpy-1.0.8/sdevpy/tests/test_algos.py +125 -0
  86. sdevpy-1.0.8/sdevpy/tests/test_analytics.py +132 -0
  87. sdevpy-1.0.8/sdevpy/tests/test_dates.py +58 -0
  88. sdevpy-1.0.8/sdevpy/tests/test_impliedvol.py +172 -0
  89. sdevpy-1.0.8/sdevpy/tests/test_interpolation.py +54 -0
  90. sdevpy-1.0.8/sdevpy/tests/test_localvol.py +0 -0
  91. sdevpy-1.0.8/sdevpy/tests/test_marketdata.py +48 -0
  92. sdevpy-1.0.8/sdevpy/tests/test_mc.py +85 -0
  93. sdevpy-1.0.8/sdevpy/tests/test_pde.py +116 -0
  94. sdevpy-1.0.8/sdevpy/tests/test_timegrids.py +49 -0
  95. sdevpy-1.0.8/sdevpy/tests/test_utils.py +67 -0
  96. sdevpy-1.0.8/sdevpy/tests/test_yieldcurves.py +49 -0
  97. sdevpy-1.0.8/sdevpy/thirdparty/__init__.py +0 -0
  98. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_lets_be_rational/__init__.py +1 -0
  99. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_lets_be_rational/constants.py +1 -0
  100. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_lets_be_rational/erf_cody.py +1 -0
  101. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_lets_be_rational/exceptions.py +1 -0
  102. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_lets_be_rational/lets_be_rational.py +1 -0
  103. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_lets_be_rational/normaldistribution.py +1 -0
  104. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_lets_be_rational/rationalcubic.py +1 -0
  105. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/__init__.py +1 -0
  106. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black/__init__.py +1 -0
  107. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black/greeks/__init__.py +0 -0
  108. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black/greeks/analytical.py +1 -0
  109. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black/greeks/numerical.py +1 -0
  110. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black/implied_volatility.py +1 -0
  111. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black_scholes/__init__.py +1 -0
  112. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes/greeks/__init__.py +0 -0
  113. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black_scholes/greeks/analytical.py +1 -0
  114. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black_scholes/greeks/numerical.py +1 -0
  115. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black_scholes/implied_volatility.py +1 -0
  116. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black_scholes_merton/__init__.py +1 -0
  117. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/__init__.py +0 -0
  118. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/analytical.py +1 -0
  119. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/numerical.py +1 -0
  120. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black_scholes_merton/implied_volatility.py +1 -0
  121. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/helpers/__init__.py +1 -0
  122. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/helpers/constants.py +1 -0
  123. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/helpers/distributions.py +1 -0
  124. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/helpers/doctest_helper.py +1 -0
  125. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/helpers/exceptions.py +1 -0
  126. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/helpers/numerical_greeks.py +1 -0
  127. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/__init__.py +1 -0
  128. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black/__init__.py +1 -0
  129. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/__init__.py +0 -0
  130. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/analytical.py +1 -0
  131. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/numerical.py +1 -0
  132. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black/implied_volatility.py +1 -0
  133. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/__init__.py +1 -0
  134. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/__init__.py +0 -0
  135. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/analytical.py +1 -0
  136. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/numerical.py +1 -0
  137. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/implied_volatility.py +1 -0
  138. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/__init__.py +1 -0
  139. sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/__init__.py +0 -0
  140. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/analytical.py +1 -0
  141. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/numerical.py +1 -0
  142. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/implied_volatility.py +1 -0
  143. sdevpy-1.0.8/sdevpy/timeseries/__init__.py +0 -0
  144. sdevpy-1.0.8/sdevpy/timeseries/backtesting.py +86 -0
  145. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/timeseries/cointegration.py +20 -23
  146. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/timeseries/meanreversion.py +47 -48
  147. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/timeseries/timeseriestools.py +15 -18
  148. sdevpy-1.0.8/sdevpy/tree/__init__.py +0 -0
  149. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/tree/trees.py +1 -16
  150. sdevpy-1.0.8/sdevpy/utilities/__init__.py +0 -0
  151. {sdevpy-1.0.6/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/algos.py +5 -3
  152. sdevpy-1.0.8/sdevpy/utilities/book.py +51 -0
  153. sdevpy-1.0.8/sdevpy/utilities/dates.py +31 -0
  154. {sdevpy-1.0.6/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/network.py +5 -2
  155. {sdevpy-1.0.6/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/pydotnet.py +8 -6
  156. sdevpy-1.0.8/sdevpy/utilities/scalendar.py +286 -0
  157. sdevpy-1.0.8/sdevpy/utilities/speriods.py +25 -0
  158. {sdevpy-1.0.6/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/timegrids.py +35 -6
  159. sdevpy-1.0.8/sdevpy/utilities/tools.py +52 -0
  160. sdevpy-1.0.8/sdevpy/volatility/__init__.py +0 -0
  161. sdevpy-1.0.8/sdevpy/volatility/impliedvol/__init__.py +0 -0
  162. {sdevpy-1.0.6/sdevpy/models → sdevpy-1.0.8/sdevpy/volatility/impliedvol}/impliedvol.py +9 -5
  163. sdevpy-1.0.8/sdevpy/volatility/impliedvol/impliedvol_calib.py +137 -0
  164. sdevpy-1.0.8/sdevpy/volatility/impliedvol/models/__init__.py +0 -0
  165. {sdevpy-1.0.6/sdevpy → sdevpy-1.0.8/sdevpy/volatility/impliedvol}/models/biexp.py +57 -64
  166. sdevpy-1.0.8/sdevpy/volatility/impliedvol/models/cubicvol.py +205 -0
  167. sdevpy-1.0.8/sdevpy/volatility/impliedvol/models/fbsabr.py +182 -0
  168. sdevpy-1.0.8/sdevpy/volatility/impliedvol/models/gsvi.py +67 -0
  169. sdevpy-1.0.8/sdevpy/volatility/impliedvol/models/logmix.py +459 -0
  170. {sdevpy-1.0.6/sdevpy/analytics → sdevpy-1.0.8/sdevpy/volatility/impliedvol/models}/mcheston.py +78 -82
  171. {sdevpy-1.0.6/sdevpy/analytics → sdevpy-1.0.8/sdevpy/volatility/impliedvol/models}/mcsabr.py +96 -99
  172. {sdevpy-1.0.6/sdevpy/analytics → sdevpy-1.0.8/sdevpy/volatility/impliedvol/models}/mczabr.py +83 -86
  173. {sdevpy-1.0.6/sdevpy/analytics → sdevpy-1.0.8/sdevpy/volatility/impliedvol/models}/sabr.py +14 -11
  174. {sdevpy-1.0.6/sdevpy → sdevpy-1.0.8/sdevpy/volatility/impliedvol}/models/svi.py +18 -29
  175. sdevpy-1.0.8/sdevpy/volatility/impliedvol/models/tssvi1.py +195 -0
  176. sdevpy-1.0.8/sdevpy/volatility/impliedvol/models/tssvi2.py +183 -0
  177. sdevpy-1.0.6/sdevpy/models/svivol.py → sdevpy-1.0.8/sdevpy/volatility/impliedvol/models/vsvi.py +51 -50
  178. sdevpy-1.0.8/sdevpy/volatility/impliedvol/optionsurface.py +167 -0
  179. sdevpy-1.0.8/sdevpy/volatility/impliedvol/zerosurface.py +216 -0
  180. sdevpy-1.0.8/sdevpy/volatility/localvol/__init__.py +0 -0
  181. sdevpy-1.0.8/sdevpy/volatility/localvol/localvol.py +92 -0
  182. sdevpy-1.0.8/sdevpy/volatility/localvol/localvol_calib.py +303 -0
  183. sdevpy-1.0.8/sdevpy/volatility/localvol/localvol_factory.py +201 -0
  184. sdevpy-1.0.8/sdevpy/volatility/mlsurfacegen/__init__.py +0 -0
  185. {sdevpy-1.0.6/sdevpy/volsurfacegen → sdevpy-1.0.8/sdevpy/volatility/mlsurfacegen}/fbsabrgenerator.py +4 -4
  186. {sdevpy-1.0.6/sdevpy/volsurfacegen → sdevpy-1.0.8/sdevpy/volatility/mlsurfacegen}/mchestongenerator.py +6 -6
  187. {sdevpy-1.0.6/sdevpy/volsurfacegen → sdevpy-1.0.8/sdevpy/volatility/mlsurfacegen}/mcsabrgenerator.py +4 -4
  188. {sdevpy-1.0.6/sdevpy/volsurfacegen → sdevpy-1.0.8/sdevpy/volatility/mlsurfacegen}/mczabrgenerator.py +5 -5
  189. {sdevpy-1.0.6/sdevpy/volsurfacegen → sdevpy-1.0.8/sdevpy/volatility/mlsurfacegen}/sabrgenerator.py +32 -42
  190. {sdevpy-1.0.6/sdevpy/volsurfacegen → sdevpy-1.0.8/sdevpy/volatility/mlsurfacegen}/smilegenerator.py +4 -5
  191. {sdevpy-1.0.6/sdevpy/volsurfacegen → sdevpy-1.0.8/sdevpy/volatility/mlsurfacegen}/stovolfactory.py +5 -5
  192. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy.egg-info/PKG-INFO +8 -1
  193. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy.egg-info/SOURCES.txt +114 -50
  194. sdevpy-1.0.8/sdevpy.egg-info/requires.txt +9 -0
  195. sdevpy-1.0.8/sdevpy.egg-info/top_level.txt +1 -0
  196. sdevpy-1.0.6/pyproject.toml +0 -30
  197. sdevpy-1.0.6/sdevpy/analytics/bachelier.py +0 -81
  198. sdevpy-1.0.6/sdevpy/analytics/black.py +0 -64
  199. sdevpy-1.0.6/sdevpy/analytics/fbsabr.py +0 -184
  200. sdevpy-1.0.6/sdevpy/cointegration/back_testing.py +0 -233
  201. sdevpy-1.0.6/sdevpy/cointegration/black_analytics.py +0 -45
  202. sdevpy-1.0.6/sdevpy/cointegration/coint_trading.py +0 -839
  203. sdevpy-1.0.6/sdevpy/cointegration/data_io.py +0 -225
  204. sdevpy-1.0.6/sdevpy/cointegration/implied_vol.py +0 -116
  205. sdevpy-1.0.6/sdevpy/cointegration/plotting.py +0 -296
  206. sdevpy-1.0.6/sdevpy/cointegration/run_unit_test.py +0 -572
  207. sdevpy-1.0.6/sdevpy/cointegration/utils.py +0 -477
  208. sdevpy-1.0.6/sdevpy/market/volsurface.py +0 -21
  209. sdevpy-1.0.6/sdevpy/maths/constants.py +0 -7
  210. sdevpy-1.0.6/sdevpy/maths/rand.py +0 -99
  211. sdevpy-1.0.6/sdevpy/models/localvol.py +0 -74
  212. sdevpy-1.0.6/sdevpy/models/localvol_calib.py +0 -301
  213. sdevpy-1.0.6/sdevpy/models/localvol_factory.py +0 -109
  214. sdevpy-1.0.6/sdevpy/montecarlo/singlefactormc.py +0 -11
  215. sdevpy-1.0.6/sdevpy/test.py +0 -66
  216. sdevpy-1.0.6/sdevpy/timeseries/backtesting.py +0 -91
  217. sdevpy-1.0.6/sdevpy/tools/utils.py +0 -42
  218. sdevpy-1.0.6/sdevpy.egg-info/requires.txt +0 -2
  219. sdevpy-1.0.6/sdevpy.egg-info/top_level.txt +0 -2
  220. {sdevpy-1.0.6 → sdevpy-1.0.8}/README.md +0 -0
  221. {sdevpy-1.0.6/sdevpy/thirdparty/py_vollib/black/greeks → sdevpy-1.0.8/sdevpy/analytics}/__init__.py +0 -0
  222. {sdevpy-1.0.6/sdevpy/thirdparty/py_vollib/black_scholes/greeks → sdevpy-1.0.8/sdevpy/cointegration}/__init__.py +0 -0
  223. {sdevpy-1.0.6/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks → sdevpy-1.0.8/sdevpy/machinelearning}/__init__.py +0 -0
  224. {sdevpy-1.0.6/sdevpy/thirdparty/py_vollib/ref_python/black/greeks → sdevpy-1.0.8/sdevpy/machinelearning/keras}/__init__.py +0 -0
  225. {sdevpy-1.0.6/sdevpy/machinelearning → sdevpy-1.0.8/sdevpy/machinelearning/keras}/topology.py +0 -0
  226. {sdevpy-1.0.6/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks → sdevpy-1.0.8/sdevpy/machinelearning/llms}/__init__.py +0 -0
  227. {sdevpy-1.0.6/sdevpy → sdevpy-1.0.8/sdevpy/machinelearning}/llms/textgen.py +0 -0
  228. {sdevpy-1.0.6/sdevpy → sdevpy-1.0.8/sdevpy/machinelearning}/llms/tokenizers.py +0 -0
  229. {sdevpy-1.0.6/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks → sdevpy-1.0.8/sdevpy/market}/__init__.py +0 -0
  230. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/maths/integration.py +0 -0
  231. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/maths/metrics.py +0 -0
  232. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/maths/sets.py +0 -0
  233. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/maths/tridiag.py +0 -0
  234. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/tensorflow/tf_metrics.py +0 -0
  235. {sdevpy-1.0.6/sdevpy → sdevpy-1.0.8/sdevpy/tests}/__init__.py +0 -0
  236. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy/thirdparty/py_lets_be_rational/numba_helper.py +0 -0
  237. {sdevpy-1.0.6/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/clipboard.py +0 -0
  238. {sdevpy-1.0.6/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/constants.py +0 -0
  239. {sdevpy-1.0.6/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/filemanager.py +0 -0
  240. {sdevpy-1.0.6/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/jsonmanager.py +0 -0
  241. {sdevpy-1.0.6/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/timer.py +0 -0
  242. {sdevpy-1.0.6 → sdevpy-1.0.8}/sdevpy.egg-info/dependency_links.txt +0 -0
  243. {sdevpy-1.0.6 → sdevpy-1.0.8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sdevpy
3
- Version: 1.0.6
3
+ Version: 1.0.8
4
4
  Summary: Python package for Finance
5
5
  Author-email: Sebastien Gurrieri <sebgur@gmail.com>
6
6
  Project-URL: Git page, https://github.com/sebgur/SDev.Python
@@ -11,6 +11,13 @@ Requires-Python: >=3.6
11
11
  Description-Content-Type: text/markdown
12
12
  Requires-Dist: pandas
13
13
  Requires-Dist: numpy
14
+ Requires-Dist: scipy
15
+ Requires-Dist: matplotlib
16
+ Requires-Dist: holidays
17
+ Requires-Dist: pandas_market_calendars
18
+ Requires-Dist: openpyxl
19
+ Requires-Dist: colorlog
20
+ Requires-Dist: scikit-learn
14
21
 
15
22
  # SDev.Python
16
23
 
@@ -0,0 +1,43 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "sdevpy"
7
+ version = "1.0.8"
8
+ license-files = []
9
+ authors = [{ name="Sebastien Gurrieri", email="sebgur@gmail.com" }]
10
+ description = "Python package for Finance"
11
+ readme = "README.md"
12
+ requires-python = ">=3.6"
13
+ classifiers = [
14
+ "Programming Language :: Python :: 3",
15
+ "Operating System :: OS Independent",
16
+ ]
17
+ dependencies = ["pandas", "numpy", "scipy", "matplotlib", "holidays",
18
+ "pandas_market_calendars", "openpyxl", "colorlog",
19
+ "scikit-learn"
20
+ ]
21
+ #dependencies = ["pyperclip", "tensorflow", "scikit-learn",
22
+ # "tensorflow_probability", "silence_tensorflow"]
23
+
24
+ [tool.setuptools.packages.find]
25
+ where = ["."]
26
+ include = ["sdevpy*"] # Only include these
27
+ exclude = ["sdevpy/volatility/impliedvol/models/tssvi2*"]
28
+ #exclude = ["notebooks*", "spreadsheets*"] # Exclude these
29
+
30
+ [tool.setuptools.package-data]
31
+ "sdevpy" = ["datasets/**/*", "notebooks/**/*.ipynb"]
32
+
33
+ [tool.ruff]
34
+ line-length = 120
35
+ target-version = "py313"
36
+
37
+ [tool.ruff.lint]
38
+ select = ["E", "F", "N", "W", "UP", "B"]
39
+ ignore = ["E401", "I001"]
40
+
41
+ [project.urls]
42
+ "Git page" = "https://github.com/sebgur/SDev.Python"
43
+ "SDev Finance" = "http://sdev-finance.com/"
@@ -0,0 +1,21 @@
1
+ __version__ = '1.0.5'
2
+
3
+ import logging
4
+ import colorlog
5
+
6
+ handler = colorlog.StreamHandler()
7
+ handler.setFormatter(colorlog.ColoredFormatter(
8
+ "%(log_color)s%(levelname)-8s%(reset)s %(name)s - %(message)s",
9
+ log_colors={
10
+ "DEBUG": "cyan",
11
+ "INFO": "green",
12
+ "WARNING": "yellow",
13
+ "ERROR": "red",
14
+ "CRITICAL": "bold_red",
15
+ }
16
+ ))
17
+
18
+ root = logging.getLogger()
19
+ root.addHandler(handler)
20
+ root.setLevel(logging.WARNING)
21
+ logging.getLogger("sdevpy").setLevel(logging.DEBUG)
@@ -4,17 +4,18 @@ import time
4
4
  import matplotlib.pyplot as plt
5
5
  from sdevpy.analytics import black
6
6
  from sdevpy.tree import trees
7
+ from sdevpy.tree.trees import Payoff
7
8
 
8
9
 
9
- def option_price(ttm, strike, is_call, is_american, spot, vol, rf_rate, div_rate, disc_rate,
10
- method='trinomial', n_steps = 30):
10
+ def option_price(ttm: float, strike: float, is_call: bool, is_american: bool, spot: float, vol: float,
11
+ rf_rate: float, div_rate: float, disc_rate: float, method: str='trinomial', n_steps: int=30):
11
12
  """ Price of an American option using binomial or trinomial trees under Black-Scholes model """
12
13
  payoff = Payoff(ttm, strike, is_call, is_american)
13
14
  return price(payoff, spot, vol, rf_rate, div_rate, disc_rate, method, n_steps)
14
15
 
15
16
 
16
- def price(payoff, spot, vol, rf_rate, div_rate, disc_rate,
17
- method='trinomial', n_steps = 30):
17
+ def price(payoff, spot: float, vol: float, rf_rate: float, div_rate: float, disc_rate: float,
18
+ method: str='trinomial', n_steps: int=30):
18
19
  """ Price of a payoff using binomial or trinomial trees under Black-Scholes model """
19
20
  if method == 'binomial':
20
21
  tree = trees.BinomialTree(n_steps)
@@ -85,7 +86,6 @@ if __name__ == "__main__":
85
86
  bin_p = [100.0 * (x / cf - 1.0) for x in bin_p]
86
87
  tri_p = [100.0 * (x / cf - 1.0) for x in tri_p]
87
88
 
88
-
89
89
  # Plot the results
90
90
  plt.figure(figsize=(10, 6))
91
91
  plt.plot(bin_t, bin_p, label='Binomial Tree Price')
@@ -93,11 +93,10 @@ if __name__ == "__main__":
93
93
  if not payoff.is_american:
94
94
  plt.plot(cf_t, cf_p, label='Vanilla CF Price')
95
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')
96
+ # plt.hlines(bs_price, steps_range[0], steps_range[-1], colors='r', linestyles='dashed',
97
+ # label='Black-Scholes Price')
97
98
  plt.title('Convergence to Black-Scholes Price for Call Options')
98
99
  plt.xlabel('Runtime')
99
100
  plt.ylabel('Option Price')
100
101
  plt.legend()
101
102
  plt.show()
102
-
103
-
@@ -0,0 +1,140 @@
1
+ """ Utilities for Bachelier model """
2
+ import numpy as np
3
+ import numpy.typing as npt
4
+ from scipy.stats import norm
5
+ from scipy.optimize import minimize_scalar
6
+
7
+
8
+ def price(expiry: npt.ArrayLike, strike: npt.ArrayLike, is_call: npt.ArrayLike, fwd: npt.ArrayLike,
9
+ vol: npt.ArrayLike) -> npt.ArrayLike:
10
+ """ Option price under the Bachelier model """
11
+ stdev = vol * expiry**0.5
12
+ d = (fwd - strike) / stdev
13
+ wd = np.where(is_call, d, -d)
14
+ # wd = d if is_call else -d
15
+ return stdev * (wd * norm.cdf(wd) + norm.pdf(d))
16
+
17
+
18
+ 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)
35
+
36
+
37
+ def implied_vol_jaeckel(expiry: float, strike: float, is_call: bool, fwd: float, fwd_price: float) -> float:
38
+ """ P. Jaeckel's method in "Implied Normal Volatility", 6th Jun. 2017.
39
+ This is the original, non-vectorized. """
40
+ m = fwd - strike
41
+ abs_m = np.abs(m)
42
+ # Special case at ATM
43
+ if abs_m < 1e-8:
44
+ return fwd_price * np.sqrt(2.0 * np.pi) / np.sqrt(expiry)
45
+
46
+ # General case
47
+ tilde_phi_star_c = -0.001882039271
48
+ theta = 1.0 if is_call else -1.0
49
+
50
+ tilde_phi_star = -np.abs(fwd_price - np.maximum(theta * m, 0.0)) / abs_m
51
+ em5 = 1e-5
52
+
53
+ if tilde_phi_star < tilde_phi_star_c:
54
+ g = 1.0 / (tilde_phi_star - 0.5)
55
+ g2 = g**2
56
+ em3 = 1e-3
57
+ num = 0.032114372355 - g2 * (0.016969777977 - g2 * (2.6207332461 * em3
58
+ - 9.6066952861 * em5 * g2))
59
+ den = 1.0 - g2 * (0.6635646938 - g2 * (0.14528712196 - 0.010472855461 * g2))
60
+ eta_bar = num / den
61
+ xb = g * (eta_bar * g2 + 1.0 / np.sqrt(2.0 * np.pi))
62
+ else:
63
+ h = np.sqrt(-np.log(-tilde_phi_star))
64
+ num = 9.4883409779 - h * (9.6320903635 - h * (0.58556997323 + 2.1464093351 * h))
65
+ den = 1.0 - h * (0.65174820867 + h * (1.5120247828 + 6.6437847132 * em5 * h))
66
+ xb = num / den
67
+
68
+ q = (norm.cdf(xb) + norm.pdf(xb) / xb - tilde_phi_star) / norm.pdf(xb)
69
+ xb2 = xb**2
70
+ num = 3.0 * q * xb2 * (2.0 - q * xb * (2.0 + xb2))
71
+ den = 6.0 + q * xb * (-12.0 + xb * (6.0 * q + xb * (-6.0 + q * xb * (3.0 + xb2))))
72
+ xs = xb + num / den
73
+ sigma = abs_m / (np.abs(xs) * np.sqrt(expiry))
74
+ return sigma
75
+
76
+
77
+ def implied_vol(expiry: npt.ArrayLike, strike: npt.ArrayLike, is_call: npt.ArrayLike,
78
+ fwd: npt.ArrayLike, fwd_price: npt.ArrayLike) -> npt.NDArray[np.float64]:
79
+ """ P. Jaeckel's method in "Implied Normal Volatility", 6th Jun. 2017.
80
+ Vectorized version (vectorization not in Jaeckel). """
81
+ strike = np.asarray(strike, dtype=float)
82
+ fwd_price = np.asarray(fwd_price, dtype=float)
83
+ is_call = np.asarray(is_call, dtype=bool)
84
+
85
+ #### ATM branch ####
86
+ m = fwd - strike
87
+ abs_m = np.abs(m)
88
+ atm_mask = (abs_m < 1e-8)
89
+ atm_result = fwd_price * np.sqrt(2.0 * np.pi / expiry)
90
+
91
+ #### General case ####
92
+ tilde_phi_star_c = -0.001882039271
93
+ theta = np.where(is_call, 1.0, -1.0)
94
+ tilde_phi_star = -np.abs(fwd_price - np.maximum(theta * m, 0.0)) / np.where(atm_mask, 1.0, abs_m)
95
+ em5 = 1e-5
96
+ mask_low = (tilde_phi_star < tilde_phi_star_c)
97
+
98
+ # Branch = lower
99
+ g = 1.0 / (tilde_phi_star - 0.5)
100
+ g2 = g**2
101
+ em3 = 1e-3
102
+ num_low = 0.032114372355 - g2 * (0.016969777977 - g2 * (2.6207332461 * em3 - 9.6066952861 * em5 * g2))
103
+ den_low = 1.0 - g2 * (0.6635646938 - g2 * (0.14528712196 - 0.010472855461 * g2))
104
+ eta_bar = num_low / den_low
105
+ xb_low = g * (eta_bar * g2 + 1.0 / np.sqrt(2.0 * np.pi))
106
+
107
+ # Branch = higher
108
+ # Note: substitute a safe value where mask_low is True to avoid sqrt(log(large))
109
+ safe_tps = np.where(mask_low, -0.001, tilde_phi_star)
110
+ h = np.sqrt(-np.log(-safe_tps))
111
+ num_high = 9.4883409779 - h * (9.6320903635 - h * (0.58556997323 + 2.1464093351 * h))
112
+ den_high = 1.0 - h * (0.65174820867 + h * (1.5120247828 + 6.6437847132 * em5 * h))
113
+ xb_high = num_high / den_high
114
+
115
+ # Join lower/higher
116
+ xb = np.where(mask_low, xb_low, xb_high)
117
+ q = (norm.cdf(xb) + norm.pdf(xb) / xb - tilde_phi_star) / norm.pdf(xb)
118
+ xb2 = xb**2
119
+ num = 3.0 * q * xb2 * (2.0 - q * xb * (2.0 + xb2))
120
+ den = 6.0 + q * xb * (-12.0 + xb * (6.0 * q + xb * (-6.0 + q * xb * (3.0 + xb2))))
121
+ xs = xb + num / den
122
+ sigma = abs_m / (np.abs(xs) * np.sqrt(expiry))
123
+
124
+ # Join ATM/OTM
125
+ return np.where(atm_mask, atm_result, sigma)
126
+
127
+
128
+ def implied_vol_solve(expiry: float, strike: float, is_call: bool, fwd: float, fwd_price: float) -> float:
129
+ """ Direct method by numerical inversion using Brent """
130
+ options = {'xtol': 1e-4, 'maxiter': 100, 'disp': False}
131
+ xmin = 1e-6
132
+ xmax = 1.0
133
+
134
+ def error(vol):
135
+ premium = price(expiry, strike, is_call, fwd, vol)
136
+ return (premium - fwd_price) ** 2
137
+
138
+ res = minimize_scalar(fun=error, bracket=(xmin, xmax), options=options, method='brent')
139
+
140
+ return res.x
@@ -0,0 +1,105 @@
1
+ """ Utilities for Black-Scholes model """
2
+ import numpy as np
3
+ import numpy.typing as npt
4
+ from scipy.stats import norm
5
+ from scipy.optimize import minimize_scalar
6
+ from sdevpy.thirdparty.py_vollib.black import implied_volatility as jaeckel
7
+ from sdevpy.utilities.tools import isiterable
8
+
9
+
10
+ def price(expiry: npt.ArrayLike, strike: npt.ArrayLike, is_call: npt.ArrayLike, fwd: npt.ArrayLike,
11
+ vol: npt.ArrayLike) -> npt.NDArray[np.float64]:
12
+ """ Option price under the Black-Scholes model """
13
+ # w = 1.0 if is_call else -1.0
14
+ w = np.where(is_call, 1.0, -1.0)
15
+ s = vol * np.sqrt(expiry)
16
+ d1 = np.log(fwd / strike) / s + 0.5 * s
17
+ d2 = d1 - s
18
+ return w * (fwd * norm.cdf(w * d1) - strike * norm.cdf(w * d2))
19
+
20
+
21
+ def implied_vol(expiry: float, strike: float, is_call: bool, fwd: float, fwd_price: float) -> float:
22
+ """ Direct method by numerical inversion using Brent.
23
+ Non-vectorized due to solver. """
24
+ options = {'xtol': 1e-4, 'maxiter': 100, 'disp': False}
25
+ xmin = 1e-6
26
+ xmax = 1.0
27
+
28
+ def error(vol):
29
+ premium = price(expiry, strike, is_call, fwd, vol)
30
+ return (premium - fwd_price) ** 2
31
+
32
+ res = minimize_scalar(fun=error, bracket=(xmin, xmax), options=options, method='brent')
33
+ return res.x
34
+
35
+
36
+ def implied_vols(expiry: float, strike: npt.ArrayLike, is_call: bool, fwd: float,
37
+ fwd_price: npt.ArrayLike) -> npt.ArrayLike:
38
+ """ Black implied volatility for vector of strikes/prices """
39
+ if isiterable(strike) and isiterable(fwd_price):
40
+ ivs = [implied_vol(expiry, k, is_call, fwd, p) for k, p in zip(strike, fwd_price, strict=True)]
41
+ return np.asarray(ivs)
42
+ elif not isiterable(strike) and not isiterable(fwd_price):
43
+ return implied_vol(expiry, strike, is_call, fwd, fwd_price)
44
+ else:
45
+ raise ValueError("Incompatible shapes between strikes and prices")
46
+
47
+
48
+ 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:
50
+ """ Using vectorized Newton-Raphson, with faster convergence than Brent.
51
+ However, this method can struggle for very small vegas, so we may want to switch
52
+ to another method (maybe Brent above) below a certain vega threshold.
53
+ Or to switch to Halley's method.
54
+ To be investigated if speed becomes a bottleneck or Brent's method has issues. """
55
+ strike = np.asarray(strike, dtype=float)
56
+ fwd_price = np.asarray(fwd_price, dtype=float)
57
+ vol = np.full_like(fwd_price, 0.25) # Initial guess: flat 25%
58
+ sqrt_t = np.sqrt(expiry)
59
+ for _ in range(max_iter):
60
+ s = vol * sqrt_t
61
+ d1 = np.log(fwd / strike) / s + 0.5 * s
62
+ vega = fwd * norm.pdf(d1) * sqrt_t
63
+ diff = price(expiry, strike, is_call, fwd, vol) - fwd_price
64
+ vol -= diff / vega
65
+ vol = np.maximum(vol, 1e-8) # Keep vol positive
66
+ if np.all(np.abs(diff) < tol):
67
+ break
68
+
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
85
+
86
+
87
+ if __name__ == "__main__":
88
+ EXPIRY = 1.0
89
+ VOL = 0.25
90
+ IS_CALL = True
91
+ NUM_POINTS = 100
92
+ # FWD = 100
93
+ # K = 100
94
+ # p = price(EXPIRY, K, IS_CALL, FWD, VOL)
95
+ # iv = implied_vol(EXPIRY, K, IS_CALL, FWD, p)
96
+ # print(iv)
97
+ f_space = np.linspace(100, 120, NUM_POINTS)
98
+ k_space = np.linspace(20, 2180, NUM_POINTS)
99
+ prices = price(EXPIRY, k_space, IS_CALL, f_space, VOL)
100
+ # print(prices)
101
+ implied_vols = []
102
+ for i, k in enumerate(k_space):
103
+ implied_vols.append(implied_vol(EXPIRY, k, IS_CALL, f_space[i], prices[i]))
104
+
105
+ # print(implied_vols)
@@ -0,0 +1,224 @@
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,
44
+ stdev_up_to_trade_date).iloc[0]
45
+ basket_5d_rtns_from_trade_date = ut.compute_x_day_historical_returns_in_SD(basket_from_trade_date_to_now, 5,
46
+ stdev_up_to_trade_date).iloc[0]
47
+ basket_10d_rtns_from_trade_date = ut.compute_x_day_historical_returns_in_SD(basket_from_trade_date_to_now, 10,
48
+ stdev_up_to_trade_date).iloc[0]
49
+
50
+ 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]
51
+ min_2d_rtns_in_sd = min_max_res_2d['min']
52
+ max_2d_rtns_in_sd = min_max_res_2d['max']
53
+
54
+ 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]
55
+ min_5d_rtns_in_sd = min_max_res_5d['min']
56
+ max_5d_rtns_in_sd = min_max_res_5d['max']
57
+
58
+ 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]
59
+ min_10d_rtns_in_sd = min_max_res_10d['min']
60
+ max_10d_rtns_in_sd = min_max_res_10d['max']
61
+
62
+ if math.isnan(basket_10d_rtns_from_trade_date):
63
+ raise Exception('Trade DATE is less than 10 days ago.')
64
+
65
+ if zscore_trade_date > 0:
66
+ # flip the return sign if zscore is above 0, because we sell. The basket goes down and we earn
67
+ basket_2d_rtns_from_trade_date = -basket_2d_rtns_from_trade_date
68
+ basket_5d_rtns_from_trade_date = -basket_5d_rtns_from_trade_date
69
+ basket_10d_rtns_from_trade_date = -basket_10d_rtns_from_trade_date
70
+
71
+ # so the max draw down is the negative of the max returns
72
+ max_2d_draw_down_in_sd = -max_2d_rtns_in_sd
73
+ max_5d_draw_down_in_sd = -max_5d_rtns_in_sd
74
+ max_10d_draw_down_in_sd = -max_10d_rtns_in_sd
75
+ else:
76
+ # we buy so that max draw down is teh min returns
77
+ max_2d_draw_down_in_sd = min_2d_rtns_in_sd
78
+ max_5d_draw_down_in_sd = min_5d_rtns_in_sd
79
+ max_10d_draw_down_in_sd = min_10d_rtns_in_sd
80
+
81
+ #if the max draw down is a positive number, we floor it to 0 to show there is no loss
82
+ max_2d_draw_down_in_sd = np.minimum(max_2d_draw_down_in_sd, 0.0)
83
+ max_5d_draw_down_in_sd = np.minimum(max_5d_draw_down_in_sd, 0.0)
84
+ max_10d_draw_down_in_sd = np.minimum(max_10d_draw_down_in_sd, 0.0)
85
+
86
+ res_dict = {'2D Realized Rtns from Trade Date in SD': basket_2d_rtns_from_trade_date,
87
+ '5D Realized Rtns from Trade Date in SD': basket_5d_rtns_from_trade_date,
88
+ '10D Realized Rtns from Trade Date in SD': basket_10d_rtns_from_trade_date,
89
+ '2D max draw down in SD': max_2d_draw_down_in_sd,
90
+ '5D max draw down in SD': max_5d_draw_down_in_sd,
91
+ '10D max draw down in SD': max_10d_draw_down_in_sd,
92
+ 'basket stdev on Trade Date': stdev_up_to_trade_date
93
+ }
94
+
95
+ return res_dict
96
+
97
+ # res_df_filtered - output from coint_trading.filter_cointegration_basket
98
+ def back_test_many_trades(res_df_filtered, now, df_fx_spot):
99
+ back_test_res = []
100
+
101
+ num_rows = len(res_df_filtered)
102
+
103
+ for idx in tqdm(range(num_rows)):
104
+ from_ = res_df_filtered['From'].iloc[idx]
105
+ trade_date = res_df_filtered['Today'].iloc[idx]
106
+ trade_date = pd.to_datetime(trade_date, format='%Y-%m-%d')
107
+
108
+ name_str = res_df_filtered['currency pairs'].iloc[idx]
109
+ name_list = name_list_string_to_name_list(name_str)
110
+
111
+ weights_xxxusd_str = res_df_filtered['unadj weights in xxxusd'].iloc[idx]
112
+ weights_xxxusd = weigths_list_string_to_float_list(weights_xxxusd_str)
113
+
114
+ sharpe_5d = res_df_filtered['5D Sharpe Ratio'].iloc[idx]
115
+ sd_on_trade_date = res_df_filtered['SD Current'].iloc[idx]
116
+
117
+ stop_loss_in_sd = res_df_filtered['Stop Loss in SD'].iloc[idx]
118
+
119
+ half_life_in_days = res_df_filtered['half life in days'].iloc[idx]
120
+
121
+ range_in_sd_current = res_df_filtered['Range in SD current'].iloc[idx]
122
+
123
+ abs_sd_on_trade_date = np.abs(sd_on_trade_date)
124
+
125
+ one_month_trace_5pct = res_df_filtered['+/- 1 month trace (5%)'].iloc[idx]
126
+ one_month_trace_10pct = res_df_filtered['+/- 1 month trace (10%)'].iloc[idx]
127
+ one_month_eigen_5pct = res_df_filtered['+/- 1 month eigen (5%)'].iloc[idx]
128
+ one_month_eigen_10pct = res_df_filtered['+/- 1 month eigen (10%)'].iloc[idx]
129
+
130
+ res_dict = back_test_one_trade(from_, trade_date, now, name_list, weights_xxxusd,
131
+ sd_on_trade_date, df_fx_spot)
132
+
133
+ basket_stdev = res_dict['basket stdev on Trade Date']
134
+
135
+ mean_rev_level = res_df_filtered['mean_rev_level'].iloc[idx]
136
+
137
+ back_test_res.append((from_, trade_date, now, name_list, weights_xxxusd, sharpe_5d,
138
+ sd_on_trade_date, abs_sd_on_trade_date, stop_loss_in_sd,
139
+ res_dict['2D Realized Rtns from Trade Date in SD'],
140
+ res_dict['5D Realized Rtns from Trade Date in SD'],
141
+ res_dict['10D Realized Rtns from Trade Date in SD'],
142
+ res_dict['2D max draw down in SD'],
143
+ res_dict['5D max draw down in SD'],
144
+ res_dict['10D max draw down in SD'],
145
+ basket_stdev, mean_rev_level, half_life_in_days, range_in_sd_current,
146
+ one_month_trace_5pct, one_month_trace_10pct,
147
+ one_month_eigen_5pct, one_month_eigen_10pct))
148
+
149
+ #--- end of for ind in tqdm(res_df_filtered.index):
150
+
151
+ res_df = pd.DataFrame(back_test_res, columns =['from_', 'Trade Date', 'Now', 'currency pairs',
152
+ 'unadj weights in xxxusd', 'Sharpe Ratio on Trade Date',
153
+ 'Z Score on Trade Date', 'Abs Z Score on Trade Date',
154
+ 'Distance to max/min SD', '2D Realized Rtns in SD',
155
+ '5D Realized Rtns in SD', '10D Realized Rtns in SD',
156
+ '2D max DD in SD', '5D max DD in SD', '10D max DD in SD',
157
+ 'basket stdev on Trade Date', 'mean_rev_level on Trade Date',
158
+ 'half life in days', 'Range in SD current',
159
+ '+/- 1 month trace (5%)', '+/- 1 month trace (10%)',
160
+ '+/- 1 month eigen (5%)', '+/- 1 month eigen (10%)'])
161
+
162
+ return res_df
163
+
164
+ # Take the results of back_test_many_trades and then compute the back test diagnostics
165
+ def one_back_test_summary_table(back_test_res_df, start_date, end_date, sharpe_threshold, zscore_threshold):
166
+ upper_sd_condition = back_test_res_df['Z Score on Trade Date'] > zscore_threshold
167
+ lower_sd_condition = back_test_res_df['Z Score on Trade Date'] < -zscore_threshold
168
+ sharpe_condition = back_test_res_df['Sharpe Ratio on Trade Date'] > sharpe_threshold
169
+
170
+ start_date_condition = back_test_res_df['Trade Date'] > pd.Timestamp(start_date)
171
+ end_date_condition = back_test_res_df['Trade Date'] < pd.Timestamp(end_date)
172
+
173
+ # -----------apply conditions
174
+ my_trade_df = back_test_res_df[sharpe_condition]
175
+ my_trade_df = my_trade_df[upper_sd_condition | lower_sd_condition]
176
+ my_trade_df = my_trade_df[start_date_condition]
177
+ my_trade_df = my_trade_df[end_date_condition]
178
+
179
+ total = len(my_trade_df)
180
+
181
+ output_table = []
182
+
183
+ column_list = ['2D Realized Rtns in SD', '5D Realized Rtns in SD', '10D Realized Rtns in SD']
184
+
185
+ for column in column_list:
186
+ positive_return_condition = my_trade_df[column] > 0.0
187
+ num_pos_rtn = len(my_trade_df[positive_return_condition])
188
+ num_neg_rtn = len(my_trade_df[~positive_return_condition])
189
+
190
+ pos_rtn_pct = round(num_pos_rtn/total, 3)
191
+ neg_rtn_pct = round(num_neg_rtn/total, 3)
192
+
193
+ rtns_median = np.median(my_trade_df[column])
194
+ rtns_mean = np.mean(my_trade_df[column])
195
+ rtns_std = np.std(my_trade_df[column])
196
+
197
+ first_3_char = column[:3]
198
+
199
+ output_table.append((start_date,
200
+ end_date,
201
+ sharpe_threshold,
202
+ zscore_threshold,
203
+ first_3_char,
204
+ pos_rtn_pct,
205
+ neg_rtn_pct,
206
+ total,
207
+ rtns_median,
208
+ rtns_mean,
209
+ rtns_std))
210
+
211
+ res_df = pd.DataFrame(output_table, columns =['Period Start',
212
+ 'Period End',
213
+ 'Sharpe threshold',
214
+ 'SD threshold',
215
+ 'Rtns type',
216
+ 'Pos %',
217
+ 'Neg %',
218
+ 'Total',
219
+ 'Rtns median',
220
+ 'Rtns mean',
221
+ 'Rtns stdev'
222
+ ])
223
+
224
+ return res_df, my_trade_df