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.
- sdevpy-1.1/LICENSE +21 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/PKG-INFO +25 -2
- {sdevpy-1.0.8 → sdevpy-1.1}/pyproject.toml +25 -5
- sdevpy-1.1/sdevpy/__init__.py +26 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/analytics/bachelier.py +7 -19
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/analytics/black.py +26 -25
- sdevpy-1.1/sdevpy/instruments/constants.py +20 -0
- sdevpy-1.1/sdevpy/logger.py +42 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/keras/learningschedules.py +1 -1
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/market/correlations.py +4 -2
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/market/eqforward.py +14 -21
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/market/eqvolsurface.py +52 -36
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/market/yieldcurve.py +14 -16
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/constants.py +3 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/integration.py +0 -1
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/interpolation.py +14 -7
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/optimization.py +28 -7
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/rand/pathconstruction.py +2 -1
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/specialfunctions.py +3 -3
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/models/assetmodels.py +10 -14
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/models/multiasset_heston.py +1 -1
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/montecarlo/mcpricer.py +59 -11
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/montecarlo/payoffs/basic.py +1 -1
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/montecarlo/payoffs/vanillas.py +10 -29
- sdevpy-1.1/sdevpy/montecarlo/smoothers.py +23 -0
- sdevpy-1.1/sdevpy/pde/forwardpde.py +303 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/pde/pdeschemes.py +61 -57
- sdevpy-1.1/sdevpy/tensorflow/tf_analytics.py +19 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tensorflow/tf_metrics.py +6 -4
- sdevpy-1.1/sdevpy/tests/test_algos.py +112 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tests/test_analytics.py +24 -9
- sdevpy-1.1/sdevpy/tests/test_backtesting.py +58 -0
- sdevpy-1.1/sdevpy/tests/test_cointegration.py +96 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tests/test_dates.py +10 -11
- sdevpy-1.1/sdevpy/tests/test_files.py +93 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tests/test_impliedvol.py +114 -32
- sdevpy-1.1/sdevpy/tests/test_impliedvol_ml.py +135 -0
- sdevpy-1.1/sdevpy/tests/test_integration.py +24 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tests/test_interpolation.py +4 -4
- sdevpy-1.1/sdevpy/tests/test_localvol.py +313 -0
- sdevpy-1.1/sdevpy/tests/test_localvol_calib.py +200 -0
- sdevpy-1.1/sdevpy/tests/test_marketdata.py +90 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tests/test_mc.py +11 -6
- sdevpy-1.1/sdevpy/tests/test_meanreversion.py +161 -0
- sdevpy-1.1/sdevpy/tests/test_models.py +49 -0
- sdevpy-1.1/sdevpy/tests/test_pde.py +186 -0
- sdevpy-1.1/sdevpy/tests/test_restapi.py +77 -0
- sdevpy-1.1/sdevpy/tests/test_specialfunctions.py +43 -0
- sdevpy-1.1/sdevpy/tests/test_timegrids.py +98 -0
- sdevpy-1.1/sdevpy/tests/test_tree.py +77 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tests/test_yieldcurves.py +36 -3
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/timeseries/backtesting.py +3 -2
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/timeseries/cointegration.py +11 -41
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/timeseries/meanreversion.py +11 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/timeseries/timeseriestools.py +11 -10
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tree/trees.py +9 -9
- sdevpy-1.1/sdevpy/utilities/algos.py +81 -0
- sdevpy-1.1/sdevpy/utilities/constants.py +2 -0
- sdevpy-1.1/sdevpy/utilities/dates.py +54 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/utilities/filemanager.py +0 -12
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/utilities/jsonmanager.py +6 -7
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/utilities/pydotnet.py +1 -0
- sdevpy-1.1/sdevpy/utilities/restapi.py +42 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/utilities/scalendar.py +9 -15
- sdevpy-1.1/sdevpy/utilities/timegrids.py +217 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/utilities/tools.py +5 -2
- sdevpy-1.1/sdevpy/utilities/xmlmanager.py +29 -0
- sdevpy-1.1/sdevpy/volatility/impliedvol/impliedvol.py +218 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/impliedvol_calib.py +20 -32
- sdevpy-1.1/sdevpy/volatility/impliedvol/impliedvol_factory.py +66 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/models/biexp.py +17 -7
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/models/cubicvol.py +14 -9
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/models/fbsabr.py +2 -2
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/models/logmix.py +229 -78
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/models/mcheston.py +2 -2
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/models/mcsabr.py +3 -3
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/models/mczabr.py +2 -2
- sdevpy-1.1/sdevpy/volatility/impliedvol/models/svi.py +122 -0
- sdevpy-1.1/sdevpy/volatility/impliedvol/models/tssvi1.py +276 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/models/tssvi2.py +82 -38
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/models/vsvi.py +46 -34
- sdevpy-1.1/sdevpy/volatility/impliedvol/numerical_impliedvol.py +134 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/optionsurface.py +16 -6
- sdevpy-1.1/sdevpy/volatility/impliedvol/parametric_impliedvol.py +29 -0
- sdevpy-1.1/sdevpy/volatility/localvol/black_calib.py +66 -0
- sdevpy-1.1/sdevpy/volatility/localvol/dupire_calib.py +287 -0
- sdevpy-1.1/sdevpy/volatility/localvol/localvol.py +336 -0
- sdevpy-1.1/sdevpy/volatility/localvol/localvol_factory.py +244 -0
- sdevpy-1.1/sdevpy/volatility/localvol/lvsection_calib.py +352 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/mlsurfacegen/sabrgenerator.py +2 -1
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy.egg-info/PKG-INFO +25 -2
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy.egg-info/SOURCES.txt +23 -85
- sdevpy-1.0.8/sdevpy/__init__.py +0 -21
- sdevpy-1.0.8/sdevpy/cointegration/back_testing.py +0 -224
- sdevpy-1.0.8/sdevpy/cointegration/coint_trading.py +0 -677
- sdevpy-1.0.8/sdevpy/cointegration/data_io.py +0 -14
- sdevpy-1.0.8/sdevpy/cointegration/mean_reversion.py +0 -268
- sdevpy-1.0.8/sdevpy/cointegration/model_settings.py +0 -34
- sdevpy-1.0.8/sdevpy/cointegration/utils.py +0 -132
- sdevpy-1.0.8/sdevpy/montecarlo/mcrun.py +0 -102
- sdevpy-1.0.8/sdevpy/montecarlo/smoothers.py +0 -40
- sdevpy-1.0.8/sdevpy/pde/forwardpde.py +0 -267
- sdevpy-1.0.8/sdevpy/projects/aad/aad_mc.py +0 -282
- sdevpy-1.0.8/sdevpy/projects/aad/aad_mc_nd.py +0 -349
- sdevpy-1.0.8/sdevpy/projects/chat_gpt2.py +0 -76
- sdevpy-1.0.8/sdevpy/projects/datafiles.py +0 -28
- sdevpy-1.0.8/sdevpy/projects/raschka/ch2_working_with_text.py +0 -161
- sdevpy-1.0.8/sdevpy/projects/raschka/ch3_coding_attention.py +0 -259
- sdevpy-1.0.8/sdevpy/projects/raschka/ch4_gpt_model.py +0 -200
- sdevpy-1.0.8/sdevpy/projects/raschka/ch5_loadgpt2.py +0 -119
- sdevpy-1.0.8/sdevpy/projects/raschka/ch5_pretraining.py +0 -263
- sdevpy-1.0.8/sdevpy/projects/raschka/ch7_instruction_finetuning.py +0 -26
- sdevpy-1.0.8/sdevpy/projects/raschka/raschka_datasetloader.py +0 -80
- sdevpy-1.0.8/sdevpy/projects/raschka/raschka_dnn.py +0 -40
- sdevpy-1.0.8/sdevpy/projects/raschka/raschka_gpt_download.py +0 -155
- sdevpy-1.0.8/sdevpy/projects/set_limits.py +0 -24
- sdevpy-1.0.8/sdevpy/projects/stovol/stovolgen.py +0 -87
- sdevpy-1.0.8/sdevpy/projects/stovol/stovolplot.py +0 -81
- sdevpy-1.0.8/sdevpy/projects/stovol/stovoltrain.py +0 -269
- sdevpy-1.0.8/sdevpy/projects/stovolinverse/stovolinvgen.py +0 -82
- sdevpy-1.0.8/sdevpy/projects/stovolinverse/stovolinvtrain.py +0 -374
- sdevpy-1.0.8/sdevpy/projects/update_db.py +0 -26
- sdevpy-1.0.8/sdevpy/tests/test_algos.py +0 -125
- sdevpy-1.0.8/sdevpy/tests/test_localvol.py +0 -0
- sdevpy-1.0.8/sdevpy/tests/test_marketdata.py +0 -48
- sdevpy-1.0.8/sdevpy/tests/test_pde.py +0 -116
- sdevpy-1.0.8/sdevpy/tests/test_timegrids.py +0 -49
- sdevpy-1.0.8/sdevpy/thirdparty/py_lets_be_rational/__init__.py +0 -49
- sdevpy-1.0.8/sdevpy/thirdparty/py_lets_be_rational/constants.py +0 -65
- sdevpy-1.0.8/sdevpy/thirdparty/py_lets_be_rational/erf_cody.py +0 -448
- sdevpy-1.0.8/sdevpy/thirdparty/py_lets_be_rational/exceptions.py +0 -68
- sdevpy-1.0.8/sdevpy/thirdparty/py_lets_be_rational/lets_be_rational.py +0 -801
- sdevpy-1.0.8/sdevpy/thirdparty/py_lets_be_rational/normaldistribution.py +0 -194
- sdevpy-1.0.8/sdevpy/thirdparty/py_lets_be_rational/numba_helper.py +0 -13
- sdevpy-1.0.8/sdevpy/thirdparty/py_lets_be_rational/rationalcubic.py +0 -272
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/__init__.py +0 -32
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black/__init__.py +0 -180
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black/greeks/analytical.py +0 -273
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black/greeks/numerical.py +0 -251
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black/implied_volatility.py +0 -292
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes/__init__.py +0 -84
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes/greeks/analytical.py +0 -278
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes/greeks/numerical.py +0 -318
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes/implied_volatility.py +0 -102
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes_merton/__init__.py +0 -90
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/__init__.py +0 -0
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/analytical.py +0 -309
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/numerical.py +0 -266
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes_merton/implied_volatility.py +0 -116
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/helpers/__init__.py +0 -114
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/helpers/constants.py +0 -52
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/helpers/distributions.py +0 -226
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/helpers/doctest_helper.py +0 -62
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/helpers/exceptions.py +0 -59
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/helpers/numerical_greeks.py +0 -217
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/__init__.py +0 -32
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black/__init__.py +0 -236
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/__init__.py +0 -0
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/analytical.py +0 -277
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/numerical.py +0 -222
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black/implied_volatility.py +0 -117
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/__init__.py +0 -157
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/__init__.py +0 -0
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/analytical.py +0 -279
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/numerical.py +0 -290
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/implied_volatility.py +0 -110
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/__init__.py +0 -231
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/__init__.py +0 -0
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/analytical.py +0 -311
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/numerical.py +0 -223
- sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/implied_volatility.py +0 -104
- sdevpy-1.0.8/sdevpy/timeseries/__init__.py +0 -0
- sdevpy-1.0.8/sdevpy/tree/__init__.py +0 -0
- sdevpy-1.0.8/sdevpy/utilities/__init__.py +0 -0
- sdevpy-1.0.8/sdevpy/utilities/algos.py +0 -115
- sdevpy-1.0.8/sdevpy/utilities/constants.py +0 -6
- sdevpy-1.0.8/sdevpy/utilities/dates.py +0 -31
- sdevpy-1.0.8/sdevpy/utilities/network.py +0 -95
- sdevpy-1.0.8/sdevpy/utilities/speriods.py +0 -25
- sdevpy-1.0.8/sdevpy/utilities/timegrids.py +0 -142
- sdevpy-1.0.8/sdevpy/volatility/__init__.py +0 -0
- sdevpy-1.0.8/sdevpy/volatility/impliedvol/__init__.py +0 -0
- sdevpy-1.0.8/sdevpy/volatility/impliedvol/impliedvol.py +0 -45
- sdevpy-1.0.8/sdevpy/volatility/impliedvol/models/__init__.py +0 -0
- sdevpy-1.0.8/sdevpy/volatility/impliedvol/models/gsvi.py +0 -67
- sdevpy-1.0.8/sdevpy/volatility/impliedvol/models/svi.py +0 -95
- sdevpy-1.0.8/sdevpy/volatility/impliedvol/models/tssvi1.py +0 -195
- sdevpy-1.0.8/sdevpy/volatility/impliedvol/zerosurface.py +0 -216
- sdevpy-1.0.8/sdevpy/volatility/localvol/__init__.py +0 -0
- sdevpy-1.0.8/sdevpy/volatility/localvol/localvol.py +0 -92
- sdevpy-1.0.8/sdevpy/volatility/localvol/localvol_calib.py +0 -303
- sdevpy-1.0.8/sdevpy/volatility/localvol/localvol_factory.py +0 -201
- sdevpy-1.0.8/sdevpy/volatility/mlsurfacegen/__init__.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/README.md +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/analytics/__init__.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/analytics/americantree.py +0 -0
- {sdevpy-1.0.8/sdevpy/cointegration → sdevpy-1.1/sdevpy/machinelearning}/__init__.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/datasets.py +0 -0
- {sdevpy-1.0.8/sdevpy/machinelearning → sdevpy-1.1/sdevpy/machinelearning/keras}/__init__.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/keras/callbacks.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/keras/learningmodel.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/keras/topology.py +0 -0
- {sdevpy-1.0.8/sdevpy/machinelearning/keras → sdevpy-1.1/sdevpy/machinelearning/llms}/__init__.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/llms/attention.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/llms/chat.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/llms/datasets.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/llms/gpt.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/llms/instructions.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/llms/modelconverter.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/llms/textgen.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/llms/tokenizers.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/machinelearning/llms/training.py +0 -0
- {sdevpy-1.0.8/sdevpy/machinelearning/llms → sdevpy-1.1/sdevpy/market}/__init__.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/market/fixings.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/market/spot.py +0 -0
- {sdevpy-1.0.8/sdevpy/market → sdevpy-1.1/sdevpy/maths}/__init__.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/metrics.py +0 -0
- {sdevpy-1.0.8/sdevpy/maths → sdevpy-1.1/sdevpy/maths/rand}/__init__.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/rand/correlations.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/rand/rng.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/regression.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/sets.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/maths/tridiag.py +0 -0
- {sdevpy-1.0.8/sdevpy/maths/rand → sdevpy-1.1/sdevpy/models}/__init__.py +0 -0
- {sdevpy-1.0.8/sdevpy/models → sdevpy-1.1/sdevpy/montecarlo}/__init__.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/montecarlo/pathgenerator.py +0 -0
- {sdevpy-1.0.8/sdevpy/montecarlo → sdevpy-1.1/sdevpy/montecarlo/payoffs}/__init__.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/montecarlo/payoffs/cashflows.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/montecarlo/payoffs/exotics.py +0 -0
- {sdevpy-1.0.8/sdevpy/montecarlo/payoffs → sdevpy-1.1/sdevpy/pde}/__init__.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/settings.py +0 -0
- {sdevpy-1.0.8/sdevpy/pde → sdevpy-1.1/sdevpy/tensorflow}/__init__.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tensorflow/tf_black.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tests/__init__.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tests/test.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/tests/test_utils.py +0 -0
- {sdevpy-1.0.8/sdevpy/projects → sdevpy-1.1/sdevpy/thirdparty}/__init__.py +0 -0
- {sdevpy-1.0.8/sdevpy/projects/aad → sdevpy-1.1/sdevpy/timeseries}/__init__.py +0 -0
- {sdevpy-1.0.8/sdevpy/projects/raschka → sdevpy-1.1/sdevpy/tree}/__init__.py +0 -0
- {sdevpy-1.0.8/sdevpy/projects/stovol → sdevpy-1.1/sdevpy/utilities}/__init__.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/utilities/book.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/utilities/clipboard.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/utilities/timer.py +0 -0
- {sdevpy-1.0.8/sdevpy/projects/stovolinverse → sdevpy-1.1/sdevpy/volatility}/__init__.py +0 -0
- {sdevpy-1.0.8/sdevpy/tensorflow → sdevpy-1.1/sdevpy/volatility/impliedvol}/__init__.py +0 -0
- {sdevpy-1.0.8/sdevpy/thirdparty → sdevpy-1.1/sdevpy/volatility/impliedvol/models}/__init__.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/impliedvol/models/sabr.py +0 -0
- {sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black/greeks → sdevpy-1.1/sdevpy/volatility/localvol}/__init__.py +0 -0
- {sdevpy-1.0.8/sdevpy/thirdparty/py_vollib/black_scholes/greeks → sdevpy-1.1/sdevpy/volatility/mlsurfacegen}/__init__.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/mlsurfacegen/fbsabrgenerator.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/mlsurfacegen/mchestongenerator.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/mlsurfacegen/mcsabrgenerator.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/mlsurfacegen/mczabrgenerator.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/mlsurfacegen/smilegenerator.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy/volatility/mlsurfacegen/stovolfactory.py +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy.egg-info/dependency_links.txt +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy.egg-info/requires.txt +0 -0
- {sdevpy-1.0.8 → sdevpy-1.1}/sdevpy.egg-info/top_level.txt +0 -0
- {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.
|
|
3
|
+
Version: 1.1
|
|
4
4
|
Summary: Python package for Finance
|
|
5
5
|
Author-email: Sebastien Gurrieri <sebgur@gmail.com>
|
|
6
|
-
|
|
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.
|
|
8
|
-
license
|
|
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
|
-
[
|
|
42
|
-
|
|
43
|
-
"
|
|
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.
|
|
9
|
+
vol: npt.ArrayLike) -> npt.NDArray[np.float64]:
|
|
10
10
|
""" Option price under the Bachelier model """
|
|
11
|
-
stdev = vol * expiry
|
|
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.
|
|
20
|
-
""" Straddle price under the Bachelier model
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
30
|
+
# Trial config
|
|
31
|
+
options = {'xtol': 1e-6, 'maxiter': 100, 'disp': False}
|
|
25
32
|
xmin = 1e-6
|
|
26
|
-
xmax =
|
|
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.
|
|
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.
|
|
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
|
-
|
|
102
|
+
iv_results = []
|
|
102
103
|
for i, k in enumerate(k_space):
|
|
103
|
-
|
|
104
|
+
iv_results.append(implied_vol(EXPIRY, k, IS_CALL, f_space[i], prices[i]))
|
|
104
105
|
|
|
105
|
-
# print(
|
|
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))
|
|
@@ -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
|
|
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
|
-
|
|
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(
|
|
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(
|
|
59
|
-
'snapdate': self.snapdate.strftime(
|
|
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(
|
|
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,
|
|
129
|
+
date = dt.datetime.strptime(date_str, dts.DATE_FORMAT)
|
|
137
130
|
pillar['expiry'] = date
|
|
138
131
|
|
|
139
|
-
data = EqForwardData(dt.datetime.strptime(valdate,
|
|
140
|
-
name=name, snapdate=dt.datetime.strptime(snapdate,
|
|
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(
|
|
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(
|
|
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 = [
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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 =
|
|
51
|
-
strikes =
|
|
39
|
+
fwd = fwd_curve.value(expiry)
|
|
40
|
+
strikes = abs_strikes[exp_idx]
|
|
52
41
|
vols = self.vols[exp_idx]
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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, '
|
|
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(
|
|
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)
|