sdevpy 1.0.7__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.
- {sdevpy-1.0.7 → sdevpy-1.0.8}/PKG-INFO +2 -1
- {sdevpy-1.0.7 → sdevpy-1.0.8}/pyproject.toml +9 -4
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/analytics/americantree.py +4 -5
- sdevpy-1.0.8/sdevpy/analytics/bachelier.py +140 -0
- sdevpy-1.0.8/sdevpy/analytics/black.py +105 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/machinelearning/datasets.py +1 -1
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/machinelearning/keras/learningmodel.py +2 -2
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/machinelearning/keras/learningschedules.py +2 -1
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/market/correlations.py +1 -1
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/market/eqforward.py +1 -1
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/market/eqvolsurface.py +14 -3
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/market/fixings.py +4 -4
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/market/spot.py +1 -1
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/market/yieldcurve.py +2 -2
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/maths/rand/pathconstruction.py +1 -1
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/montecarlo/mcpricer.py +1 -1
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/montecarlo/mcrun.py +2 -2
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/montecarlo/payoffs/basic.py +2 -2
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/montecarlo/payoffs/exotics.py +1 -1
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/pde/forwardpde.py +1 -1
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/aad/aad_mc.py +2 -1
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/aad/aad_mc_nd.py +1 -1
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/datafiles.py +1 -1
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/set_limits.py +2 -4
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/stovol/stovolgen.py +3 -3
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/stovol/stovolplot.py +1 -2
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/stovol/stovoltrain.py +3 -3
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/stovolinverse/stovolinvgen.py +4 -3
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/stovolinverse/stovolinvtrain.py +3 -3
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/update_db.py +1 -6
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/tests/test.py +2 -2
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/tests/test_algos.py +1 -1
- sdevpy-1.0.8/sdevpy/tests/test_analytics.py +132 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/tests/test_dates.py +3 -3
- sdevpy-1.0.8/sdevpy/tests/test_impliedvol.py +172 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/tests/test_interpolation.py +0 -1
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/tests/test_mc.py +1 -1
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/tests/test_pde.py +1 -1
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/tests/test_timegrids.py +1 -1
- sdevpy-1.0.8/sdevpy/tests/test_utils.py +67 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/tests/test_yieldcurves.py +1 -1
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/timeseries/cointegration.py +1 -4
- {sdevpy-1.0.7/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/book.py +1 -1
- {sdevpy-1.0.7/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/dates.py +1 -1
- {sdevpy-1.0.7/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/scalendar.py +2 -3
- {sdevpy-1.0.7/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/timegrids.py +2 -2
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/volatility/impliedvol/impliedvol_calib.py +36 -24
- {sdevpy-1.0.7/sdevpy/analytics → sdevpy-1.0.8/sdevpy/volatility/impliedvol/models}/fbsabr.py +3 -3
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/volatility/impliedvol/models/gsvi.py +7 -3
- sdevpy-1.0.8/sdevpy/volatility/impliedvol/models/logmix.py +459 -0
- {sdevpy-1.0.7/sdevpy/analytics → sdevpy-1.0.8/sdevpy/volatility/impliedvol/models}/mcheston.py +2 -2
- {sdevpy-1.0.7/sdevpy/analytics → sdevpy-1.0.8/sdevpy/volatility/impliedvol/models}/mcsabr.py +5 -5
- {sdevpy-1.0.7/sdevpy/analytics → sdevpy-1.0.8/sdevpy/volatility/impliedvol/models}/mczabr.py +4 -4
- {sdevpy-1.0.7/sdevpy/analytics → sdevpy-1.0.8/sdevpy/volatility/impliedvol/models}/sabr.py +14 -11
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/volatility/impliedvol/models/tssvi1.py +71 -19
- sdevpy-1.0.8/sdevpy/volatility/impliedvol/models/tssvi2.py +183 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/volatility/impliedvol/optionsurface.py +2 -2
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/volatility/impliedvol/zerosurface.py +24 -59
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/volatility/localvol/localvol.py +1 -1
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/volatility/localvol/localvol_calib.py +1 -1
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/volatility/localvol/localvol_factory.py +1 -1
- {sdevpy-1.0.7/sdevpy/volsurfacegen → sdevpy-1.0.8/sdevpy/volatility/mlsurfacegen}/fbsabrgenerator.py +4 -4
- {sdevpy-1.0.7/sdevpy/volsurfacegen → sdevpy-1.0.8/sdevpy/volatility/mlsurfacegen}/mchestongenerator.py +4 -4
- {sdevpy-1.0.7/sdevpy/volsurfacegen → sdevpy-1.0.8/sdevpy/volatility/mlsurfacegen}/mcsabrgenerator.py +4 -4
- {sdevpy-1.0.7/sdevpy/volsurfacegen → sdevpy-1.0.8/sdevpy/volatility/mlsurfacegen}/mczabrgenerator.py +4 -4
- {sdevpy-1.0.7/sdevpy/volsurfacegen → sdevpy-1.0.8/sdevpy/volatility/mlsurfacegen}/sabrgenerator.py +8 -8
- {sdevpy-1.0.7/sdevpy/volsurfacegen → sdevpy-1.0.8/sdevpy/volatility/mlsurfacegen}/smilegenerator.py +1 -1
- {sdevpy-1.0.7/sdevpy/volsurfacegen → sdevpy-1.0.8/sdevpy/volatility/mlsurfacegen}/stovolfactory.py +5 -5
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy.egg-info/PKG-INFO +2 -1
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy.egg-info/SOURCES.txt +31 -28
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy.egg-info/requires.txt +1 -0
- sdevpy-1.0.8/sdevpy.egg-info/top_level.txt +1 -0
- sdevpy-1.0.7/sdevpy/analytics/bachelier.py +0 -82
- sdevpy-1.0.7/sdevpy/analytics/black.py +0 -64
- sdevpy-1.0.7/sdevpy/tests/test_impliedvol.py +0 -100
- sdevpy-1.0.7/sdevpy/tests/test_utils.py +0 -67
- sdevpy-1.0.7/sdevpy.egg-info/top_level.txt +0 -2
- {sdevpy-1.0.7 → sdevpy-1.0.8}/README.md +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/analytics/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/cointegration/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/cointegration/back_testing.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/cointegration/coint_trading.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/cointegration/data_io.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/cointegration/mean_reversion.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/cointegration/model_settings.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/cointegration/utils.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/machinelearning/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/machinelearning/keras/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/machinelearning/keras/callbacks.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/machinelearning/keras/topology.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/machinelearning/llms/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/machinelearning/llms/attention.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/machinelearning/llms/chat.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/machinelearning/llms/datasets.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/machinelearning/llms/gpt.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/machinelearning/llms/instructions.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/machinelearning/llms/modelconverter.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/machinelearning/llms/textgen.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/machinelearning/llms/tokenizers.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/machinelearning/llms/training.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/market/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/maths/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/maths/constants.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/maths/integration.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/maths/interpolation.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/maths/metrics.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/maths/optimization.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/maths/rand/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/maths/rand/correlations.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/maths/rand/rng.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/maths/regression.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/maths/sets.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/maths/specialfunctions.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/maths/tridiag.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/models/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/models/assetmodels.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/models/multiasset_heston.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/montecarlo/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/montecarlo/pathgenerator.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/montecarlo/payoffs/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/montecarlo/payoffs/cashflows.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/montecarlo/payoffs/vanillas.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/montecarlo/smoothers.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/pde/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/pde/pdeschemes.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/aad/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/chat_gpt2.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/raschka/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/raschka/ch2_working_with_text.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/raschka/ch3_coding_attention.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/raschka/ch4_gpt_model.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/raschka/ch5_loadgpt2.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/raschka/ch5_pretraining.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/raschka/ch7_instruction_finetuning.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/raschka/raschka_datasetloader.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/raschka/raschka_dnn.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/raschka/raschka_gpt_download.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/stovol/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/projects/stovolinverse/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/settings.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/tensorflow/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/tensorflow/tf_black.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/tensorflow/tf_metrics.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/tests/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/tests/test_localvol.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/tests/test_marketdata.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_lets_be_rational/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_lets_be_rational/constants.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_lets_be_rational/erf_cody.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_lets_be_rational/exceptions.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_lets_be_rational/lets_be_rational.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_lets_be_rational/normaldistribution.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_lets_be_rational/numba_helper.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_lets_be_rational/rationalcubic.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black/greeks/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black/greeks/analytical.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black/greeks/numerical.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black/implied_volatility.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black_scholes/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black_scholes/greeks/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black_scholes/greeks/analytical.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black_scholes/greeks/numerical.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black_scholes/implied_volatility.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black_scholes_merton/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/analytical.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black_scholes_merton/greeks/numerical.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/black_scholes_merton/implied_volatility.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/helpers/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/helpers/constants.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/helpers/distributions.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/helpers/doctest_helper.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/helpers/exceptions.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/helpers/numerical_greeks.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/analytical.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black/greeks/numerical.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black/implied_volatility.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/analytical.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/greeks/numerical.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black_scholes/implied_volatility.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/analytical.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/greeks/numerical.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/thirdparty/py_vollib/ref_python/black_scholes_merton/implied_volatility.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/timeseries/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/timeseries/backtesting.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/timeseries/meanreversion.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/timeseries/timeseriestools.py +0 -0
- {sdevpy-1.0.7/sdevpy/tools → sdevpy-1.0.8/sdevpy/tree}/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/tree/trees.py +0 -0
- {sdevpy-1.0.7/sdevpy/tree → sdevpy-1.0.8/sdevpy/utilities}/__init__.py +0 -0
- {sdevpy-1.0.7/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/algos.py +0 -0
- {sdevpy-1.0.7/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/clipboard.py +0 -0
- {sdevpy-1.0.7/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/constants.py +0 -0
- {sdevpy-1.0.7/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/filemanager.py +0 -0
- {sdevpy-1.0.7/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/jsonmanager.py +0 -0
- {sdevpy-1.0.7/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/network.py +0 -0
- {sdevpy-1.0.7/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/pydotnet.py +0 -0
- {sdevpy-1.0.7/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/speriods.py +0 -0
- {sdevpy-1.0.7/sdevpy/tools → sdevpy-1.0.8/sdevpy/utilities}/timer.py +0 -0
- /sdevpy-1.0.7/sdevpy/tools/utils.py → /sdevpy-1.0.8/sdevpy/utilities/tools.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/volatility/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/volatility/impliedvol/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/volatility/impliedvol/impliedvol.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/volatility/impliedvol/models/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/volatility/impliedvol/models/biexp.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/volatility/impliedvol/models/cubicvol.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/volatility/impliedvol/models/svi.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/volatility/impliedvol/models/vsvi.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy/volatility/localvol/__init__.py +0 -0
- {sdevpy-1.0.7/sdevpy/volsurfacegen → sdevpy-1.0.8/sdevpy/volatility/mlsurfacegen}/__init__.py +0 -0
- {sdevpy-1.0.7 → sdevpy-1.0.8}/sdevpy.egg-info/dependency_links.txt +0 -0
- {sdevpy-1.0.7 → 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.
|
|
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
|
|
@@ -17,6 +17,7 @@ Requires-Dist: holidays
|
|
|
17
17
|
Requires-Dist: pandas_market_calendars
|
|
18
18
|
Requires-Dist: openpyxl
|
|
19
19
|
Requires-Dist: colorlog
|
|
20
|
+
Requires-Dist: scikit-learn
|
|
20
21
|
|
|
21
22
|
# SDev.Python
|
|
22
23
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sdevpy"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.8"
|
|
8
8
|
license-files = []
|
|
9
9
|
authors = [{ name="Sebastien Gurrieri", email="sebgur@gmail.com" }]
|
|
10
10
|
description = "Python package for Finance"
|
|
@@ -15,15 +15,20 @@ classifiers = [
|
|
|
15
15
|
"Operating System :: OS Independent",
|
|
16
16
|
]
|
|
17
17
|
dependencies = ["pandas", "numpy", "scipy", "matplotlib", "holidays",
|
|
18
|
-
"pandas_market_calendars", "openpyxl", "colorlog"
|
|
18
|
+
"pandas_market_calendars", "openpyxl", "colorlog",
|
|
19
|
+
"scikit-learn"
|
|
19
20
|
]
|
|
20
21
|
#dependencies = ["pyperclip", "tensorflow", "scikit-learn",
|
|
21
22
|
# "tensorflow_probability", "silence_tensorflow"]
|
|
22
23
|
|
|
23
24
|
[tool.setuptools.packages.find]
|
|
24
25
|
where = ["."]
|
|
25
|
-
include = ["sdevpy*"
|
|
26
|
-
|
|
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"]
|
|
27
32
|
|
|
28
33
|
[tool.ruff]
|
|
29
34
|
line-length = 120
|
|
@@ -7,15 +7,15 @@ from sdevpy.tree import trees
|
|
|
7
7
|
from sdevpy.tree.trees import Payoff
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
def option_price(ttm, strike, is_call, is_american, spot, vol
|
|
11
|
-
method='trinomial', n_steps =
|
|
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):
|
|
12
12
|
""" Price of an American option using binomial or trinomial trees under Black-Scholes model """
|
|
13
13
|
payoff = Payoff(ttm, strike, is_call, is_american)
|
|
14
14
|
return price(payoff, spot, vol, rf_rate, div_rate, disc_rate, method, n_steps)
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
def price(payoff, spot, vol, rf_rate, div_rate, disc_rate,
|
|
18
|
-
method='trinomial', n_steps =
|
|
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):
|
|
19
19
|
""" Price of a payoff using binomial or trinomial trees under Black-Scholes model """
|
|
20
20
|
if method == 'binomial':
|
|
21
21
|
tree = trees.BinomialTree(n_steps)
|
|
@@ -86,7 +86,6 @@ if __name__ == "__main__":
|
|
|
86
86
|
bin_p = [100.0 * (x / cf - 1.0) for x in bin_p]
|
|
87
87
|
tri_p = [100.0 * (x / cf - 1.0) for x in tri_p]
|
|
88
88
|
|
|
89
|
-
|
|
90
89
|
# Plot the results
|
|
91
90
|
plt.figure(figsize=(10, 6))
|
|
92
91
|
plt.plot(bin_t, bin_p, label='Binomial Tree Price')
|
|
@@ -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)
|
|
@@ -3,7 +3,7 @@ import os
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
import numpy.typing as npt
|
|
5
5
|
import pandas as pd
|
|
6
|
-
from sdevpy.
|
|
6
|
+
from sdevpy.utilities import filemanager
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def prepare_sets(inputs: npt.ArrayLike, outputs: npt.ArrayLike, train_percent: float) -> npt.ArrayLike:
|
|
@@ -5,8 +5,8 @@ from sklearn.preprocessing import StandardScaler
|
|
|
5
5
|
import tensorflow as tf
|
|
6
6
|
import joblib
|
|
7
7
|
import absl.logging
|
|
8
|
-
from sdevpy.
|
|
9
|
-
from sdevpy.
|
|
8
|
+
from sdevpy.utilities import jsonmanager
|
|
9
|
+
from sdevpy.utilities import filemanager
|
|
10
10
|
|
|
11
11
|
class LearningModel:
|
|
12
12
|
""" Wrapper class for machine learning models, including scalers, and simplifying
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
""" Custom learning schedules """
|
|
2
2
|
import numpy as np
|
|
3
3
|
import tensorflow as tf
|
|
4
|
-
from sdevpy.
|
|
4
|
+
from sdevpy.utilities.constants import TWO_PI
|
|
5
|
+
|
|
5
6
|
|
|
6
7
|
# Custom learning rate scheduler, exponentially decreases between given values
|
|
7
8
|
class FlooredExponentialDecay(tf.keras.optimizers.schedules.LearningRateSchedule):
|
|
@@ -2,7 +2,7 @@ import os, json
|
|
|
2
2
|
import datetime as dt
|
|
3
3
|
import numpy as np
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from sdevpy.
|
|
5
|
+
from sdevpy.utilities import dates, timegrids
|
|
6
6
|
from sdevpy.maths import interpolation as itp
|
|
7
7
|
from sdevpy.market import yieldcurve as ycrv
|
|
8
8
|
from sdevpy.market.spot import get_spots
|
|
@@ -2,7 +2,9 @@ import os, json, logging
|
|
|
2
2
|
import datetime as dt
|
|
3
3
|
import numpy as np
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from sdevpy.
|
|
5
|
+
from sdevpy.utilities import dates
|
|
6
|
+
from sdevpy.utilities import timegrids
|
|
7
|
+
from sdevpy.analytics import black
|
|
6
8
|
log = logging.getLogger(__name__)
|
|
7
9
|
|
|
8
10
|
|
|
@@ -21,8 +23,6 @@ class EqVolSurfaceData:
|
|
|
21
23
|
self.forwards = np.asarray([s['forward'] for s in sections])
|
|
22
24
|
self.input_strikes = [np.asarray(s['strikes']) for s in sections]
|
|
23
25
|
self.vols = [np.asarray(s['vols']) for s in sections]
|
|
24
|
-
# self.input_strikes = np.asarray([s['strikes'] for s in sections])
|
|
25
|
-
# self.vols = np.asarray([s['vols'] for s in sections])
|
|
26
26
|
|
|
27
27
|
# Size checks
|
|
28
28
|
n_times = len(self.expiries)
|
|
@@ -43,12 +43,23 @@ class EqVolSurfaceData:
|
|
|
43
43
|
else:
|
|
44
44
|
raise ValueError(f"Strike input type not supported yet: {self.strike_input_type}")
|
|
45
45
|
|
|
46
|
+
# Calculate prices
|
|
47
|
+
self.call_prices = []
|
|
48
|
+
for exp_idx, expiry in enumerate(self.expiries):
|
|
49
|
+
t = timegrids.model_time(self.valdate, expiry)
|
|
50
|
+
fwd = self.forwards[exp_idx]
|
|
51
|
+
strikes = self.abs_strikes[exp_idx]
|
|
52
|
+
vols = self.vols[exp_idx]
|
|
53
|
+
self.call_prices.append(black.price(t, strikes, True, fwd, vols))
|
|
54
|
+
|
|
46
55
|
def get_strikes(self, type: str='absolute'):
|
|
47
56
|
req_type = type.lower()
|
|
48
57
|
if req_type == 'absolute':
|
|
49
58
|
return self.abs_strikes
|
|
50
59
|
elif req_type == 'relative':
|
|
51
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")
|
|
52
63
|
|
|
53
64
|
def dump(self, file, indent=2):
|
|
54
65
|
data = self.dump_data()
|
|
@@ -2,8 +2,8 @@ import os
|
|
|
2
2
|
import datetime as dt
|
|
3
3
|
import pandas as pd
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from sdevpy.
|
|
6
|
-
from sdevpy.tools
|
|
5
|
+
from sdevpy.utilities import dates as dts
|
|
6
|
+
from sdevpy.utilities.tools import isiterable
|
|
7
7
|
from sdevpy.maths import interpolation as itp
|
|
8
8
|
|
|
9
9
|
|
|
@@ -131,8 +131,8 @@ def test_data_folder():
|
|
|
131
131
|
|
|
132
132
|
if __name__ == "__main__":
|
|
133
133
|
# import numpy as np
|
|
134
|
-
# from sdevpy.
|
|
135
|
-
# from sdevpy.
|
|
134
|
+
# from sdevpy.utilities.scalendar import make_schedule
|
|
135
|
+
# from sdevpy.utilities.timegrids import model_time
|
|
136
136
|
# from sdevpy.maths.rand.rng import get_rng
|
|
137
137
|
name = "ABC"
|
|
138
138
|
|
|
@@ -4,7 +4,7 @@ import datetime as dt
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
6
|
from enum import Enum
|
|
7
|
-
from sdevpy.
|
|
7
|
+
from sdevpy.utilities import timegrids, dates
|
|
8
8
|
from sdevpy.maths import interpolation as itp
|
|
9
9
|
|
|
10
10
|
|
|
@@ -201,7 +201,7 @@ def test_data_folder():
|
|
|
201
201
|
|
|
202
202
|
if __name__ == "__main__":
|
|
203
203
|
import matplotlib.pyplot as plt
|
|
204
|
-
from sdevpy.
|
|
204
|
+
from sdevpy.utilities import dates
|
|
205
205
|
|
|
206
206
|
valdate = dt.datetime(2026, 2, 15)
|
|
207
207
|
|
|
@@ -131,7 +131,7 @@ if __name__ == "__main__":
|
|
|
131
131
|
# Sobol with scrambling in incremental construction thatn Sobol without scrambling
|
|
132
132
|
# in the Brownian bridge construction.
|
|
133
133
|
# from sdevpy.maths.rand import rng
|
|
134
|
-
# from sdevpy.
|
|
134
|
+
# from sdevpy.utilities import timer
|
|
135
135
|
|
|
136
136
|
# Make time grid
|
|
137
137
|
n_steps = 5
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
multi-d vector. This way we can get those paths from an independent engine. """
|
|
3
3
|
import numpy as np
|
|
4
4
|
import datetime as dt
|
|
5
|
-
from sdevpy.
|
|
5
|
+
from sdevpy.utilities import timegrids, timer
|
|
6
6
|
from sdevpy.volatility.localvol import localvol_factory as lvf
|
|
7
7
|
from sdevpy.models.assetmodels import MultiAssetGBM
|
|
8
8
|
from sdevpy.montecarlo.pathgenerator import PathGenerator
|
|
@@ -7,8 +7,8 @@ from sdevpy.montecarlo.payoffs.exotics import WorstOfBarrier, make_basket_option
|
|
|
7
7
|
from sdevpy.montecarlo.mcpricer import price_book
|
|
8
8
|
from sdevpy.models.localvol_factory import get_local_vols
|
|
9
9
|
from sdevpy.analytics import black
|
|
10
|
-
from sdevpy.
|
|
11
|
-
from sdevpy.
|
|
10
|
+
from sdevpy.utilities import timegrids
|
|
11
|
+
from sdevpy.utilities import book as bk
|
|
12
12
|
from sdevpy.market.yieldcurve import get_yieldcurve
|
|
13
13
|
from sdevpy.market.eqforward import get_forward_curves
|
|
14
14
|
from sdevpy.montecarlo.payoffs import cashflows as cfl
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
import datetime as dt
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
|
-
from sdevpy.
|
|
5
|
-
from sdevpy.tools
|
|
4
|
+
from sdevpy.utilities.scalendar import make_schedule
|
|
5
|
+
from sdevpy.utilities.tools import rand_str
|
|
6
6
|
from sdevpy.market import fixings as fxgs
|
|
7
7
|
|
|
8
8
|
|
|
@@ -60,7 +60,7 @@ def make_asian_option(name: str, strike: float, optiontype: str, start: dt.datet
|
|
|
60
60
|
return payoff
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
def make_basket_option(names: list[str], weights: list[float], strike: float, optiontype: str, expiry: dt.
|
|
63
|
+
def make_basket_option(names: list[str], weights: list[float], strike: float, optiontype: str, expiry: dt.datetime):
|
|
64
64
|
""" Create Basket option payoff """
|
|
65
65
|
spots = [Terminal(name, expiry) for name in names]
|
|
66
66
|
basket = Basket(spots, weights)
|
|
@@ -5,7 +5,7 @@ import matplotlib.pyplot as plt
|
|
|
5
5
|
from sdevpy.analytics import black
|
|
6
6
|
from sdevpy.maths import metrics
|
|
7
7
|
from sdevpy.pde import pdeschemes
|
|
8
|
-
from sdevpy.
|
|
8
|
+
from sdevpy.utilities import timegrids
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def density_step(old_p, old_x, old_dx, t_grid, local_vol, config):
|
|
@@ -6,7 +6,8 @@ import matplotlib.pyplot as plt
|
|
|
6
6
|
from sdevpy.montecarlo import smoothers
|
|
7
7
|
from sdevpy import settings
|
|
8
8
|
from sdevpy.analytics import black
|
|
9
|
-
from sdevpy.
|
|
9
|
+
from sdevpy.utilities.timer import Stopwatch
|
|
10
|
+
|
|
10
11
|
|
|
11
12
|
# ################ Runtime configuration ##########################################################
|
|
12
13
|
# Parameters
|
|
@@ -6,7 +6,7 @@ from scipy.stats import norm
|
|
|
6
6
|
import matplotlib.pyplot as plt
|
|
7
7
|
from sdevpy.analytics import black
|
|
8
8
|
from sdevpy.montecarlo import smoothers
|
|
9
|
-
from sdevpy.
|
|
9
|
+
from sdevpy.utilities.timer import Stopwatch
|
|
10
10
|
|
|
11
11
|
# In this script we calculate PV, Delta and Gamma for a hypothetical product
|
|
12
12
|
# (an option on the multiplicated spots) using Monte-Carlo with bumps, closed-form
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
# Import packages
|
|
2
1
|
import datetime as dt
|
|
3
|
-
|
|
4
|
-
# from dateutil.relativedelta import relativedelta
|
|
5
|
-
from sdevpy.tools import dates
|
|
2
|
+
from sdevpy.utilities import dates
|
|
6
3
|
from openbb import obb
|
|
7
4
|
obb.user.preferences.output_type = "dataframe"
|
|
8
5
|
|
|
6
|
+
|
|
9
7
|
names = ['SPY', 'VTV', 'VUG', 'VBR', 'VBK', 'VGK', 'VPL']
|
|
10
8
|
colors = ['blue', 'red', 'green', 'brown', 'orange', 'yellow']
|
|
11
9
|
today = dt.date.today()
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
a network that learns the so-called 'direct' calculation, i.e. prices from parameter. """
|
|
4
4
|
import os
|
|
5
5
|
from datetime import datetime
|
|
6
|
-
from sdevpy.
|
|
6
|
+
from sdevpy.volatility.mlsurfacegen import stovolfactory
|
|
7
7
|
from sdevpy import settings
|
|
8
|
-
from sdevpy.
|
|
9
|
-
from sdevpy.
|
|
8
|
+
from sdevpy.utilities import filemanager
|
|
9
|
+
from sdevpy.utilities.timer import Stopwatch
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
# ################ Runtime configuration ##########################################################
|
|
@@ -4,7 +4,6 @@ import matplotlib.pyplot as plt
|
|
|
4
4
|
import matplotlib.ticker as mtick
|
|
5
5
|
from sdevpy.analytics import bachelier
|
|
6
6
|
from sdevpy.analytics import black
|
|
7
|
-
# from sdevpy.tools import clipboard
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
def plot_transform_surface(expiries, strikes, are_calls, fwd, ref_prices, mod_prices, title_,
|
|
@@ -70,7 +69,7 @@ def transform_surface(expiries, strikes, are_calls, fwd, prices, transform='Shif
|
|
|
70
69
|
are_calls_ = are_calls[i]
|
|
71
70
|
# trans_prices_ = []
|
|
72
71
|
for j, strike in enumerate(strikes_):
|
|
73
|
-
trans_prices[i, j] = bachelier.
|
|
72
|
+
trans_prices[i, j] = bachelier.implied_vol_jaeckel(expiry, strike, are_calls_[j], fwd,
|
|
74
73
|
prices[i, j])
|
|
75
74
|
# trans_prices_.append(bachelier.implied_vol(expiry, strike, are_calls_[j], fwd,
|
|
76
75
|
# prices[i, j]))
|
|
@@ -14,10 +14,10 @@ from sdevpy.machinelearning.learningmodel import LearningModel, load_learning_mo
|
|
|
14
14
|
from sdevpy.machinelearning.learningschedules import FlooredExponentialDecay
|
|
15
15
|
from sdevpy.machinelearning.callbacks import RefCallback
|
|
16
16
|
from sdevpy.machinelearning import datasets
|
|
17
|
-
from sdevpy.
|
|
18
|
-
from sdevpy.
|
|
17
|
+
from sdevpy.utilities import filemanager
|
|
18
|
+
from sdevpy.utilities.timer import Stopwatch
|
|
19
19
|
from sdevpy.maths.metrics import bps_rmse, tf_bps_rmse
|
|
20
|
-
from sdevpy.
|
|
20
|
+
from sdevpy.volatility.mlsurfacegen.stovolfactory import set_generator
|
|
21
21
|
from sdevpy.projects.stovol import stovolplot as xplt
|
|
22
22
|
|
|
23
23
|
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
a network that learns the so-called 'inverse' calculation, i.e. parameters from prices. """
|
|
4
4
|
import os
|
|
5
5
|
from datetime import datetime
|
|
6
|
-
from sdevpy.
|
|
6
|
+
from sdevpy.volatility.mlsurfacegen import stovolfactory
|
|
7
7
|
from sdevpy import settings
|
|
8
|
-
from sdevpy.
|
|
9
|
-
from sdevpy.
|
|
8
|
+
from sdevpy.utilities import filemanager
|
|
9
|
+
from sdevpy.utilities.timer import Stopwatch
|
|
10
|
+
|
|
10
11
|
|
|
11
12
|
# ################ Runtime configuration ##########################################################
|
|
12
13
|
MODEL_TYPE = "SABR"
|