stochvolmodels 1.0.20__tar.gz → 1.0.21__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 (74) hide show
  1. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/PKG-INFO +2 -1
  2. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/pyproject.toml +1 -1
  3. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/__init__.py +1 -1
  4. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/data/option_chain.py +23 -0
  5. stochvolmodels-1.0.21/stochvolmodels/examples/quick_run_lognormal_sv_pricer.py +34 -0
  6. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/examples/run_lognormal_sv_pricer.py +1 -3
  7. stochvolmodels-1.0.21/stochvolmodels/my_papers/forward_var/calibrate_forward_var.py +115 -0
  8. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/logsv_model_wtih_quadratic_drift/calibrations.py +11 -3
  9. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/logsv_model_wtih_quadratic_drift/model_fit_to_options_timeseries.py +7 -6
  10. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/logsv_model_wtih_quadratic_drift/moments_vol_qvar.py +2 -1
  11. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/logsv_model_wtih_quadratic_drift/ode_sol_in_time.py +1 -1
  12. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/logsv_model_wtih_quadratic_drift/steady_state_pdf.py +1 -1
  13. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/logsv_model_wtih_quadratic_drift/vol_drift.py +1 -1
  14. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/volatility_models/article_figures.py +1 -1
  15. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/volatility_models/autocorr_fit.py +2 -1
  16. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/pricers/heston_pricer.py +1 -1
  17. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/pricers/logsv/affine_expansion.py +24 -16
  18. stochvolmodels-1.0.21/stochvolmodels/pricers/logsv/logsv_params.py +176 -0
  19. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/pricers/logsv/vol_moments_ode.py +49 -6
  20. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/pricers/logsv_pricer.py +47 -159
  21. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/pricers/model_pricer.py +3 -3
  22. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/tests/qv_pricer.py +2 -1
  23. stochvolmodels-1.0.21/stochvolmodels/utils/var_swap_pricer.py +26 -0
  24. stochvolmodels-1.0.20/stochvolmodels/my_papers/volatility_models/__init__.py +0 -0
  25. stochvolmodels-1.0.20/stochvolmodels/pricers/__init__.py +0 -0
  26. stochvolmodels-1.0.20/stochvolmodels/pricers/analytic/__init__.py +0 -0
  27. stochvolmodels-1.0.20/stochvolmodels/pricers/logsv/__init__.py +0 -0
  28. stochvolmodels-1.0.20/stochvolmodels/tests/__init__.py +0 -0
  29. stochvolmodels-1.0.20/stochvolmodels/utils/__init__.py +0 -0
  30. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/LICENSE.txt +0 -0
  31. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/README.md +0 -0
  32. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/data/__init__.py +0 -0
  33. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/data/fetch_option_chain.py +0 -0
  34. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/data/test_option_chain.py +0 -0
  35. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/examples/run_gmm_fit.py +0 -0
  36. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/examples/run_heston.py +0 -0
  37. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/examples/run_heston_sv_pricer.py +0 -0
  38. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/examples/run_pricing_options_on_qvar.py +0 -0
  39. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/__init__.py +0 -0
  40. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/il_hedging/README.md +0 -0
  41. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/il_hedging/logsv_figures.py +0 -0
  42. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/il_hedging/run_logsv_for_il_payoff.py +0 -0
  43. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/inverse_options/README.md +0 -0
  44. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/inverse_options/compare_net_delta.py +0 -0
  45. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/logsv_model_wtih_quadratic_drift/README.md +0 -0
  46. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/logsv_model_wtih_quadratic_drift/article_figures.py +0 -0
  47. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/logsv_model_wtih_quadratic_drift/compare_admis_reg.py +0 -0
  48. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/risk_premia/check_kernel.py +0 -0
  49. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/risk_premia/gmm_slides.py +0 -0
  50. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/risk_premia/q_kernel.py +0 -0
  51. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/t_distribution/illustrations.py +0 -0
  52. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/t_distribution/market_data_fit.py +0 -0
  53. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/t_distribution/mc_pricer_with_kernel.py +0 -0
  54. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/volatility_models/README.md +0 -0
  55. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/volatility_models/load_data.py +0 -0
  56. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/volatility_models/ss_distribution_fit.py +0 -0
  57. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/volatility_models/vol_beta.py +0 -0
  58. {stochvolmodels-1.0.20/stochvolmodels/my_papers/il_hedging → stochvolmodels-1.0.21/stochvolmodels/pricers}/__init__.py +0 -0
  59. {stochvolmodels-1.0.20/stochvolmodels/my_papers/inverse_options → stochvolmodels-1.0.21/stochvolmodels/pricers/analytic}/__init__.py +0 -0
  60. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/pricers/analytic/bachelier.py +0 -0
  61. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/pricers/analytic/bsm.py +0 -0
  62. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/pricers/analytic/tdist.py +0 -0
  63. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/pricers/gmm_pricer.py +0 -0
  64. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/pricers/hawkes_jd_pricer.py +0 -0
  65. {stochvolmodels-1.0.20/stochvolmodels/my_papers/logsv_model_wtih_quadratic_drift → stochvolmodels-1.0.21/stochvolmodels/pricers/logsv}/__init__.py +0 -0
  66. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/pricers/tdist_pricer.py +0 -0
  67. {stochvolmodels-1.0.20/stochvolmodels/my_papers/risk_premia → stochvolmodels-1.0.21/stochvolmodels/tests}/__init__.py +0 -0
  68. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/tests/bsm_mgf_pricer.py +0 -0
  69. {stochvolmodels-1.0.20/stochvolmodels/my_papers/t_distribution → stochvolmodels-1.0.21/stochvolmodels/utils}/__init__.py +0 -0
  70. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/utils/config.py +0 -0
  71. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/utils/funcs.py +0 -0
  72. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/utils/mc_payoffs.py +0 -0
  73. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/utils/mgf_pricer.py +0 -0
  74. {stochvolmodels-1.0.20 → stochvolmodels-1.0.21}/stochvolmodels/utils/plots.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: stochvolmodels
3
- Version: 1.0.20
3
+ Version: 1.0.21
4
4
  Summary: Implementation of stochastic volatility models for option pricing
5
5
  Home-page: https://github.com/ArturSepp/StochVolModels
6
6
  License: LICENSE.txt
@@ -24,6 +24,7 @@ Classifier: Programming Language :: Python :: 3.9
24
24
  Classifier: Programming Language :: Python :: 3.10
25
25
  Classifier: Programming Language :: Python :: 3.11
26
26
  Classifier: Programming Language :: Python :: 3.12
27
+ Classifier: Programming Language :: Python :: 3.13
27
28
  Classifier: Programming Language :: Python :: 3 :: Only
28
29
  Classifier: Topic :: Office/Business :: Financial :: Investment
29
30
  Requires-Dist: matplotlib (>=3.5.2)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "stochvolmodels"
3
- version = "1.0.20"
3
+ version = "1.0.21"
4
4
  description = "Implementation of stochastic volatility models for option pricing"
5
5
  license = "LICENSE.txt"
6
6
  authors = ["Artur Sepp <artursepp@gmail.com>"]
@@ -104,10 +104,10 @@ from stochvolmodels.pricers.heston_pricer import (
104
104
  from stochvolmodels.pricers.logsv_pricer import (
105
105
  LOGSV_BTC_PARAMS,
106
106
  LogSVPricer,
107
- LogSvParams,
108
107
  LogsvModelCalibrationType,
109
108
  ConstraintsType
110
109
  )
110
+ from stochvolmodels.pricers.logsv.logsv_params import LogSvParams
111
111
 
112
112
  from stochvolmodels.pricers.gmm_pricer import (
113
113
  GmmParams,
@@ -10,9 +10,12 @@ from __future__ import annotations
10
10
  import numpy as np
11
11
  from dataclasses import dataclass
12
12
  from typing import Tuple, Optional
13
+
14
+ import pandas as pd
13
15
  from numba.typed import List
14
16
 
15
17
  import stochvolmodels.pricers.analytic.bsm as bsm
18
+ from stochvolmodels.utils.var_swap_pricer import compute_var_swap_strike
16
19
 
17
20
 
18
21
  @dataclass
@@ -227,6 +230,26 @@ class OptionChain:
227
230
  ask_prices=None if self.ask_prices is None else self.ask_prices[idx])
228
231
  return option_slice
229
232
 
233
+ def get_slice_varswap_strikes(self, floor_with_atm_vols: bool = True) -> pd.Series:
234
+ varswap_strikes = np.zeros_like(self.ttms)
235
+ vols_ttms = self.get_mid_vols()
236
+ for idx, ttm in enumerate(self.ttms):
237
+ mid_prices = bsm.compute_bsm_vanilla_slice_prices(ttm=ttm,
238
+ forward=self.forwards[idx],
239
+ strikes=self.strikes_ttms[idx],
240
+ vols=vols_ttms[idx],
241
+ optiontypes=self.optiontypes_ttms[idx])
242
+ strikes = self.strikes_ttms[idx]
243
+ puts_cond = self.optiontypes_ttms[idx] == 'P'
244
+ puts = pd.Series(mid_prices[puts_cond], index=strikes[puts_cond])
245
+ calls = pd.Series(mid_prices[puts_cond == False], index=strikes[puts_cond == False])
246
+ varswap_strikes[idx] = compute_var_swap_strike(puts=puts, calls=calls, forward=self.forwards[idx], ttm=ttm)
247
+
248
+ if floor_with_atm_vols:
249
+ varswap_strikes = np.maximum(self.get_chain_atm_vols(), varswap_strikes)
250
+
251
+ return pd.Series(varswap_strikes, index=self.ttms)
252
+
230
253
  @classmethod
231
254
  def get_slices_as_chain(cls, option_chain: OptionChain, ids: List[str]) -> OptionChain:
232
255
  """
@@ -0,0 +1,34 @@
1
+ """
2
+ run few unit test to illustrate implementation of log-normal sv model analytics
3
+ """
4
+ import numpy as np
5
+ import matplotlib.pyplot as plt
6
+ from stochvolmodels import LogSVPricer, LogSvParams, LogsvModelCalibrationType, ConstraintsType, get_btc_test_chain_data
7
+
8
+ # 1. create instance of pricer
9
+ logsv_pricer = LogSVPricer()
10
+
11
+ # 2. define model params
12
+ params = LogSvParams(sigma0=1.0, theta=1.0, kappa1=5.0, kappa2=5.0, beta=0.2, volvol=2.0)
13
+
14
+ # 3. compute model prices for option slices
15
+ model_prices, vols = logsv_pricer.price_slice(params=params,
16
+ ttm=0.25,
17
+ forward=1.0,
18
+ strikes=np.array([0.8, 0.9, 1.0, 1.1]),
19
+ optiontypes=np.array(['P', 'P', 'C', 'C']))
20
+ print([f"{p:0.4f}, implied vol={v: 0.2%}" for p, v in zip(model_prices, vols)])
21
+
22
+ # 4. calibrate model to test option chain data
23
+ btc_option_chain = get_btc_test_chain_data()
24
+ params0 = LogSvParams(sigma0=1.0, theta=1.0, kappa1=2.21, kappa2=2.18, beta=0.15, volvol=2.0)
25
+ btc_calibrated_params = logsv_pricer.calibrate_model_params_to_chain(option_chain=btc_option_chain,
26
+ params0=params0,
27
+ model_calibration_type=LogsvModelCalibrationType.PARAMS4,
28
+ constraints_type=ConstraintsType.INVERSE_MARTINGALE)
29
+ print(btc_calibrated_params)
30
+
31
+ # 5. plot model implied vols
32
+ logsv_pricer.plot_model_ivols_vs_bid_ask(option_chain=btc_option_chain,
33
+ params=btc_calibrated_params)
34
+ plt.show()
@@ -90,7 +90,6 @@ def run_unit_test(unit_test: UnitTests):
90
90
  nb_path=100000)
91
91
 
92
92
  elif unit_test == UnitTests.PLOT_FIT_TO_BITCOIN_OPTION_CHAIN:
93
-
94
93
  btc_option_chain = sv.get_btc_test_chain_data()
95
94
  btc_calibrated_params = LogSvParams(sigma0=0.8327, theta=1.0139, kappa1=4.8609, kappa2=4.7940, beta=0.1988, volvol=2.3694)
96
95
  logsv_pricer.plot_model_ivols_vs_bid_ask(option_chain=btc_option_chain,
@@ -102,7 +101,6 @@ def run_unit_test(unit_test: UnitTests):
102
101
  btc_calibrated_params = logsv_pricer.calibrate_model_params_to_chain(option_chain=btc_option_chain,
103
102
  params0=params0,
104
103
  model_calibration_type=LogsvModelCalibrationType.PARAMS4,
105
-
106
104
  constraints_type=sv.ConstraintsType.INVERSE_MARTINGALE)
107
105
  print(btc_calibrated_params)
108
106
  logsv_pricer.plot_model_ivols_vs_bid_ask(option_chain=btc_option_chain,
@@ -112,7 +110,7 @@ def run_unit_test(unit_test: UnitTests):
112
110
 
113
111
  if __name__ == '__main__':
114
112
 
115
- unit_test = UnitTests.COMPARE_MODEL_VOLS_TO_MC
113
+ unit_test = UnitTests.COMPUTE_MODEL_PRICES
116
114
 
117
115
  is_run_all_tests = False
118
116
  if is_run_all_tests:
@@ -0,0 +1,115 @@
1
+ # packages
2
+ import numpy as np
3
+ import pandas as pd
4
+ import matplotlib.pyplot as plt
5
+ from enum import Enum
6
+
7
+ import qis
8
+
9
+ # project
10
+ import stochvolmodels as sv
11
+ from stochvolmodels.pricers.logsv_pricer import LogSVPricer, LogsvModelCalibrationType
12
+ from stochvolmodels.pricers.logsv.vol_moments_ode import fit_model_vol_backbone_to_varswaps
13
+ from stochvolmodels import LogSvParams
14
+ from stochvolmodels.utils.funcs import set_seed
15
+
16
+
17
+ class UnitTests(Enum):
18
+ VARSWAP_FIT = 1
19
+ CALIBRATE_4PARAM_MODEL = 2
20
+ CALIBRATE_VARSWAP_PARAM_MODEL = 3
21
+ COMPARE_MODEL_VOLS_TO_MC = 4
22
+
23
+
24
+ def run_unit_test(unit_test: UnitTests):
25
+
26
+ set_seed(24)
27
+
28
+ logsv_pricer = LogSVPricer()
29
+ option_chain = sv.get_btc_test_chain_data()
30
+
31
+ local_path = "C://Users//artur//OneDrive//My Papers//MyPresentations//Crypto Vols Tartu. Zurich. Aug 2022//figures//"
32
+
33
+ if unit_test == UnitTests.VARSWAP_FIT:
34
+ btc_log_params = LogSvParams(sigma0=0.7118361434192538, theta=0.7118361434192538,
35
+ kappa1=2.214702576955766, kappa2=2.18028273418397, beta=0.0,
36
+ volvol=0.921487415907961)
37
+ btc_log_params = LogSvParams(sigma0=0.88, theta=0.88,
38
+ kappa1=2.214702576955766, kappa2=2.18028273418397, beta=0.0,
39
+ volvol=0.921487415907961)
40
+
41
+ vars_swaps = option_chain.get_slice_varswap_strikes()
42
+ vars_swaps1 = pd.Series(np.square(option_chain.get_chain_atm_vols()), index=option_chain.ttms)
43
+ vars_swaps = np.maximum(vars_swaps, vars_swaps1)
44
+
45
+ vol_backbone = fit_model_vol_backbone_to_varswaps(log_sv_params=btc_log_params,
46
+ varswap_strikes=vars_swaps,
47
+ verbose=True)
48
+ btc_log_params.set_vol_backbone(vol_backbone=vol_backbone)
49
+
50
+ logsv_pricer = LogSVPricer()
51
+ logsv_pricer.plot_model_ivols_vs_bid_ask(option_chain=option_chain,
52
+ params=btc_log_params)
53
+
54
+ elif unit_test == UnitTests.CALIBRATE_4PARAM_MODEL:
55
+ params0 = LogSvParams(sigma0=0.8, theta=1.0, kappa1=2.21, kappa2=2.18, beta=0.15, volvol=2.0)
56
+ fitted_params = LogSvParams(sigma0=0.8626, theta=1.0417, kappa1=2.21, kappa2=2.18, beta=0.13, volvol=1.6286)
57
+ btc_calibrated_params = fitted_params
58
+ """
59
+ btc_calibrated_params = logsv_pricer.calibrate_model_params_to_chain(option_chain=option_chain,
60
+ params0=params0,
61
+ model_calibration_type=LogsvModelCalibrationType.PARAMS4,
62
+ constraints_type=sv.ConstraintsType.INVERSE_MARTINGALE)
63
+ """
64
+ print(btc_calibrated_params)
65
+ fig = logsv_pricer.plot_model_ivols_vs_bid_ask(option_chain=option_chain,
66
+ params=btc_calibrated_params)
67
+ qis.save_fig(fig=fig, file_name='four_param_model_fit', local_path=local_path)
68
+
69
+ elif unit_test == UnitTests.CALIBRATE_VARSWAP_PARAM_MODEL:
70
+ params0 = LogSvParams(sigma0=0.85, theta=0.85, kappa1=2.21, kappa2=2.18, beta=0.15, volvol=1.5)
71
+ fitted_params = LogSvParams(sigma0=0.85, theta=1.0, kappa1=2.21, kappa2=2.18, beta=0.24, volvol=1.14)
72
+ btc_calibrated_params = logsv_pricer.calibrate_model_params_to_chain(
73
+ option_chain=option_chain,
74
+ params0=params0,
75
+ params_min=LogSvParams(sigma0=0.1, theta=0.1, kappa1=0.25, kappa2=0.25, beta=0.0, volvol=1.5),
76
+ model_calibration_type=LogsvModelCalibrationType.PARAMS_WITH_VARSWAP_FIT,
77
+ constraints_type=sv.ConstraintsType.INVERSE_MARTINGALE)
78
+ print(btc_calibrated_params)
79
+
80
+ fig = logsv_pricer.plot_model_ivols_vs_bid_ask(option_chain=option_chain,
81
+ params=btc_calibrated_params)
82
+ qis.save_fig(fig=fig, file_name='backbone_model_fit', local_path=local_path)
83
+
84
+ elif unit_test == UnitTests.COMPARE_MODEL_VOLS_TO_MC:
85
+ uniform_chain_data = sv.OptionChain.to_uniform_strikes(obj=option_chain, num_strikes=31)
86
+ is_varswap = True
87
+ if is_varswap:
88
+ fitted_params = LogSvParams(sigma0=0.85, theta=.85, kappa1=2.21, kappa2=2.18, beta=0.24, volvol=1.14)
89
+ varswap_strikes = option_chain.get_slice_varswap_strikes(floor_with_atm_vols=True)
90
+ fitted_params.set_vol_backbone(vol_backbone=fit_model_vol_backbone_to_varswaps(log_sv_params=fitted_params,
91
+ varswap_strikes=varswap_strikes))
92
+ else:
93
+ fitted_params = LogSvParams(sigma0=0.8626, theta=1.0417, kappa1=2.21, kappa2=2.18, beta=0.13, volvol=1.6286)
94
+
95
+ logsv_pricer.plot_model_ivols_vs_mc(option_chain=uniform_chain_data,
96
+ params=fitted_params,
97
+ nb_path=100000)
98
+
99
+ logsv_pricer.plot_comp_mma_inverse_options_with_mc(option_chain=uniform_chain_data,
100
+ params=fitted_params,
101
+ nb_path=100000)
102
+
103
+ plt.show()
104
+
105
+
106
+ if __name__ == '__main__':
107
+
108
+ unit_test = UnitTests.CALIBRATE_VARSWAP_PARAM_MODEL
109
+
110
+ is_run_all_tests = False
111
+ if is_run_all_tests:
112
+ for unit_test in UnitTests:
113
+ run_unit_test(unit_test=unit_test)
114
+ else:
115
+ run_unit_test(unit_test=unit_test)
@@ -10,7 +10,8 @@ from enum import Enum
10
10
 
11
11
  from stochvolmodels.utils.config import VariableType
12
12
  from stochvolmodels.data.option_chain import OptionChain
13
- from stochvolmodels.pricers.logsv_pricer import LogSVPricer, LogsvModelCalibrationType, ConstraintsType, LogSvParams
13
+ from stochvolmodels.pricers.logsv_pricer import LogSVPricer, LogsvModelCalibrationType, ConstraintsType
14
+ from stochvolmodels import LogSvParams
14
15
  from stochvolmodels.pricers.logsv.vol_moments_ode import compute_analytic_qvar
15
16
  from stochvolmodels.utils.funcs import set_seed
16
17
  import stochvolmodels.utils.plots as plot
@@ -89,6 +90,7 @@ def calibrate_logsv_model(asset: Assets = Assets.BTC,
89
90
 
90
91
 
91
92
  class UnitTests(Enum):
93
+ CHAIN_DATA = 0
92
94
  CALIBRATION = 1
93
95
  MODEL_COMPARISION_WITH_MC = 2
94
96
  ALL_PARAMS_TABLE = 3
@@ -100,7 +102,13 @@ def run_unit_test(unit_test: UnitTests):
100
102
 
101
103
  set_seed(24)
102
104
 
103
- if unit_test == UnitTests.CALIBRATION:
105
+ if unit_test == UnitTests.CHAIN_DATA:
106
+ option_chain = get_asset_chain_data(asset=Assets.BTC)
107
+ print(option_chain)
108
+ atm_vols = option_chain.get_chain_atm_vols()
109
+ print(atm_vols)
110
+
111
+ elif unit_test == UnitTests.CALIBRATION:
104
112
  asset = Assets.BTC
105
113
  fig = calibrate_logsv_model(asset=asset)
106
114
  plot.save_fig(fig=fig, local_path='../../../docs/figures//', file_name=f"calibration_{asset.value}")
@@ -180,7 +188,7 @@ def run_unit_test(unit_test: UnitTests):
180
188
 
181
189
  if __name__ == '__main__':
182
190
 
183
- unit_test = UnitTests.PLOT_QVAR_FIGURE_FOR_ARTICLE
191
+ unit_test = UnitTests.CHAIN_DATA
184
192
 
185
193
  is_run_all_tests = False
186
194
  if is_run_all_tests:
@@ -184,6 +184,7 @@ def run_unit_test(unit_test: UnitTests):
184
184
 
185
185
  # chain data here
186
186
  options_data_dfs = OptionsDataDFs(**ts_data_loader_wrapper(ticker=ticker))
187
+ options_data_dfs.get_start_end_date().print()
187
188
  chain = create_chain_from_from_options_dfs(options_data_dfs=options_data_dfs, value_time=value_time)
188
189
 
189
190
  option_chain = generate_vol_chain_np(chain=chain,
@@ -217,25 +218,25 @@ def run_unit_test(unit_test: UnitTests):
217
218
  qis.save_figs_to_pdf(figs=figs_dict, file_name='btc_calibration', local_path=f"C://Users//artur//OneDrive//analytics//outputs//")
218
219
 
219
220
  elif unit_test == UnitTests.RUN_CALIBRATION_TIMESERIES:
220
- time_period = qis.TimePeriod('01Jan2023', '31Jan2023', tz='UTC')
221
- time_period = qis.TimePeriod('2019-03-30 00:00:00+00:00', '2023-09-01 00:00:00+00:00', tz='UTC')
221
+ time_period = qis.TimePeriod('2019-03-30 00:00:00+00:00', '2024-05-06 00:00:00+00:00', tz='UTC')
222
222
 
223
223
  output_df, figs_dict = run_calibration_time_series(options_data_dfs=options_data_dfs, time_period=time_period,
224
224
  freq='W-FRI',
225
225
  **kappas)
226
226
  print(output_df)
227
227
 
228
- file_name = 'eth_calibration_w_fri'
228
+ file_name = 'btc_calibration_w_fri_may_2024'
229
229
  qis.save_df_to_excel(data=output_df, file_name=file_name, local_path=f"C://Users//artur//OneDrive//analytics//outputs//",
230
230
  add_current_date=True)
231
231
  qis.save_figs_to_pdf(figs=figs_dict, file_name=file_name, local_path=f"C://Users//artur//OneDrive//analytics//outputs//")
232
232
 
233
233
  elif unit_test == UnitTests.REPORT_CALIBRATION_TIMESERIES:
234
-
235
- df = qis.load_df_from_excel(file_name='btc_calibration_w_fri_20231209_1504', local_path=f"C://Users//artur//OneDrive//analytics//resources//")
234
+ # df = qis.load_df_from_excel(file_name='btc_calibration_w_fri_20231209_1504', local_path=f"C://Users//artur//OneDrive//analytics//resources//")
236
235
  # df = qis.load_df_from_excel(file_name='eth_calibration_w_fri_20231210_1124', local_path=f"C://Users//artur//OneDrive//analytics//resources//")
236
+ df = qis.load_df_from_excel(file_name='btc_calibration_w_fri_may_2024_20241018_1147', local_path=f"C://Users//artur//OneDrive//analytics//resources//")
237
+
237
238
  fig = report_calibration_timeseries(df=df)
238
- qis.save_fig(fig=fig, file_name='btc_calibration_w', local_path=f"C://Users//artur//OneDrive//My Papers//Working Papers//LogNormal Stochastic Volatility. London. Oct 2013//figures//")
239
+ qis.save_fig(fig=fig, file_name='btc_calibration_w', local_path=f"C://Users//artur//OneDrive//analytics//outputs//")
239
240
 
240
241
  plt.show()
241
242
 
@@ -14,7 +14,8 @@ from enum import Enum
14
14
  import stochvolmodels.pricers.logsv.vol_moments_ode as vmo
15
15
  import stochvolmodels.utils.plots as plot
16
16
  from stochvolmodels.utils.funcs import set_seed
17
- from stochvolmodels.pricers.logsv_pricer import LogSVPricer, LogSvParams
17
+ from stochvolmodels.pricers.logsv_pricer import LogSVPricer
18
+ from stochvolmodels import LogSvParams
18
19
 
19
20
  VOLVOL = 1.5
20
21
  SIGMA0P = 1.5
@@ -16,7 +16,7 @@ from typing import Tuple, List
16
16
 
17
17
  import stochvolmodels.utils.plots as plot
18
18
  from stochvolmodels.pricers.logsv.affine_expansion import solve_ode_for_a, ExpansionOrder, func_a_ode_quadratic_terms, get_expansion_n
19
- from stochvolmodels.pricers.logsv_pricer import LogSvParams
19
+ from stochvolmodels import LogSvParams
20
20
 
21
21
 
22
22
  def plot_ode_sol_in_t(params: LogSvParams,
@@ -13,7 +13,7 @@ from numba import njit
13
13
  from enum import Enum
14
14
  from typing import Dict
15
15
 
16
- from stochvolmodels.pricers.logsv_pricer import LogSvParams
16
+ from stochvolmodels import LogSvParams
17
17
  import stochvolmodels.utils.plots as plot
18
18
 
19
19
  VOLVOL = 1.5
@@ -11,7 +11,7 @@ from typing import Dict, List
11
11
  from enum import Enum
12
12
 
13
13
  import stochvolmodels.utils.plots as plot
14
- from stochvolmodels.pricers.logsv_pricer import LogSvParams
14
+ from stochvolmodels import LogSvParams
15
15
 
16
16
  VOLVOL = 1.75
17
17
 
@@ -8,7 +8,7 @@ from typing import Dict, List
8
8
  from enum import Enum
9
9
 
10
10
  # package
11
- from stochvolmodels.pricers.logsv_pricer import LogSvParams
11
+ from stochvolmodels import LogSvParams
12
12
  from stochvolmodels.utils.funcs import set_seed
13
13
 
14
14
  # project
@@ -8,7 +8,8 @@ from enum import Enum
8
8
 
9
9
  import stochvolmodels.my_papers.volatility_models.ss_distribution_fit as ssd
10
10
  from stochvolmodels.my_papers.volatility_models.load_data import fetch_ohlc_vol
11
- from stochvolmodels.pricers.logsv_pricer import LogSvParams, LogSVPricer
11
+ from stochvolmodels.pricers.logsv_pricer import LogSVPricer
12
+ from stochvolmodels import LogSvParams
12
13
  from stochvolmodels.utils.funcs import set_seed
13
14
 
14
15
 
@@ -412,7 +412,7 @@ def run_unit_test(unit_test: UnitTests):
412
412
 
413
413
  elif unit_test == UnitTests.MC_COMPARISION_QVAR:
414
414
  from stochvolmodels.pricers.logsv.vol_moments_ode import compute_analytic_qvar
415
- from stochvolmodels.pricers.logsv_pricer import LogSvParams
415
+ from stochvolmodels import LogSvParams
416
416
  heston_pricer = HestonPricer()
417
417
  ttms = {'1m': 1.0/12.0, '6m': 0.5}
418
418
  option_chain = chains.get_qv_options_test_chain_data()
@@ -36,7 +36,8 @@ def func_a_ode_quadratic_terms(theta: float,
36
36
  phi: np.complex128,
37
37
  psi: np.complex128,
38
38
  is_spot_measure: bool = True,
39
- expansion_order: ExpansionOrder = ExpansionOrder.FIRST
39
+ expansion_order: ExpansionOrder = ExpansionOrder.FIRST,
40
+ vol_backbone_eta: float = 1.0
40
41
  ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
41
42
  """
42
43
  Matrices for the quadratic form A_t = A.T@M@A + L@A + H
@@ -45,14 +46,15 @@ def func_a_ode_quadratic_terms(theta: float,
45
46
  vartheta2 = beta * beta + volvol * volvol
46
47
  qv = theta * vartheta2
47
48
  qv2 = theta2 * vartheta2
49
+ vol_backbone_eta2 = vol_backbone_eta * vol_backbone_eta
48
50
  if is_spot_measure:
49
51
  lamda = 0
50
52
  kappa2_p = kappa2
51
53
  kappa_p = kappa1 + kappa2 * theta
52
54
  else:
53
- lamda = beta*theta2
54
- kappa2_p = kappa2-beta
55
- kappa_p = kappa1 + kappa2 * theta - 2*beta*theta
55
+ lamda = beta*theta2*vol_backbone_eta
56
+ kappa2_p = kappa2-beta*vol_backbone_eta
57
+ kappa_p = kappa1 + kappa2 * theta - 2*beta*theta*vol_backbone_eta
56
58
 
57
59
  # fill Ms: M should be of same type as L and H for numba, eventhough they are real
58
60
  # utilize that M is symmetric
@@ -83,15 +85,15 @@ def func_a_ode_quadratic_terms(theta: float,
83
85
 
84
86
  # fills Ls
85
87
  L = np.zeros((n, n), dtype=np.complex128)
86
- L[0, 1], L[0, 2] = lamda - theta2 * beta * phi, qv2
87
- L[1, 1], L[1, 2] = -kappa_p - 2.0 * theta * beta * phi, 2.0 * (lamda + qv - theta2 * beta * phi)
88
- L[2, 1], L[2, 2] = -kappa2_p - beta * phi, vartheta2 - 2.0 * kappa_p - 4.0 * theta * beta * phi
88
+ L[0, 1], L[0, 2] = lamda - theta2 * beta * vol_backbone_eta * phi, qv2
89
+ L[1, 1], L[1, 2] = -kappa_p - 2.0 * theta * beta * vol_backbone_eta * phi, 2.0 * (lamda + qv - theta2 * beta * vol_backbone_eta * phi)
90
+ L[2, 1], L[2, 2] = -kappa2_p - beta * vol_backbone_eta * phi, vartheta2 - 2.0 * kappa_p - 4.0 * theta * beta * vol_backbone_eta * phi
89
91
 
90
92
  if expansion_order == ExpansionOrder.SECOND:
91
93
  L[1, 3] = 3.0*qv2
92
- L[2, 3], L[2, 4] = 3.0 * (2.0 * qv - theta2 * beta * phi), 6.0 * qv2
93
- L[3, 2], L[3, 3], L[3, 4] = -2.0 * (kappa2_p + beta * phi), 3.0 * (vartheta2 - kappa_p - 2.0 * theta * beta * phi), 4.0 * (3.0 * qv - theta2 * beta * phi)
94
- L[4, 3], L[4, 4] = -3.0 * (kappa2_p + beta * phi), 2.0 * (vartheta2 - 2.0 * kappa_p - 4.0 * theta * beta * phi)
94
+ L[2, 3], L[2, 4] = 3.0 * (2.0 * qv - theta2 * beta * vol_backbone_eta * phi), 6.0 * qv2
95
+ L[3, 2], L[3, 3], L[3, 4] = -2.0 * (kappa2_p + beta * vol_backbone_eta * phi), 3.0 * (vartheta2 - kappa_p - 2.0 * theta * beta * vol_backbone_eta * phi), 4.0 * (3.0 * qv - theta2 * beta * vol_backbone_eta * phi)
96
+ L[4, 3], L[4, 4] = -3.0 * (kappa2_p + beta * vol_backbone_eta * phi), 2.0 * (vartheta2 - 2.0 * kappa_p - 4.0 * theta * beta * vol_backbone_eta * phi)
95
97
 
96
98
  # fill Hs
97
99
  H = np.zeros(n, dtype=np.complex128)
@@ -99,7 +101,7 @@ def func_a_ode_quadratic_terms(theta: float,
99
101
  rhs = (phi * (phi + 1.0) - 2.0 * psi)
100
102
  else:
101
103
  rhs = (phi * (phi - 1.0) - 2.0 * psi)
102
- H[0], H[1], H[2] = 0.5 * theta2 * rhs, theta * rhs, 0.5 * rhs
104
+ H[0], H[1], H[2] = 0.5*theta2 * vol_backbone_eta2 * rhs, theta * vol_backbone_eta2 * rhs, 0.5*vol_backbone_eta2 * rhs
103
105
 
104
106
  return M, L, H
105
107
 
@@ -153,7 +155,8 @@ def solve_ode_for_a(ttm: float,
153
155
  a_t0: Optional[np.ndarray] = None,
154
156
  expansion_order: ExpansionOrder = ExpansionOrder.FIRST,
155
157
  is_stiff_solver: bool = False,
156
- dense_output: bool = False
158
+ dense_output: bool = False,
159
+ vol_backbone_eta: float = 1.0
157
160
  ) -> OdeResult:
158
161
  """
159
162
  solve ode for given phi
@@ -167,7 +170,8 @@ def solve_ode_for_a(ttm: float,
167
170
  phi=phi,
168
171
  psi=psi,
169
172
  expansion_order=expansion_order,
170
- is_spot_measure=is_spot_measure)
173
+ is_spot_measure=is_spot_measure,
174
+ vol_backbone_eta=vol_backbone_eta)
171
175
 
172
176
  if a_t0 is None:
173
177
  a_t0 = np.zeros_like(H, dtype=np.complex128)
@@ -361,7 +365,8 @@ def solve_a_ode_grid(phi_grid: np.ndarray,
361
365
  is_spot_measure: bool = True,
362
366
  a_t0: Optional[np.ndarray] = None,
363
367
  is_stiff_solver: bool = False,
364
- expansion_order: ExpansionOrder = ExpansionOrder.FIRST
368
+ expansion_order: ExpansionOrder = ExpansionOrder.FIRST,
369
+ vol_backbone_eta: float = 1.0
365
370
  ) -> np.ndarray:
366
371
  """
367
372
  solve ode for range phi
@@ -382,7 +387,8 @@ def solve_a_ode_grid(phi_grid: np.ndarray,
382
387
  is_stiff_solver=is_stiff_solver,
383
388
  dense_output=False,
384
389
  expansion_order=expansion_order,
385
- is_spot_measure=is_spot_measure)
390
+ is_spot_measure=is_spot_measure,
391
+ vol_backbone_eta=vol_backbone_eta)
386
392
 
387
393
  a_t1 = np.zeros((phi_grid.shape[0], get_expansion_n(expansion_order)), dtype=np.complex128)
388
394
  for idx, (phi, psi) in enumerate(zip(phi_grid, psi_grid)):
@@ -429,6 +435,7 @@ def compute_logsv_a_mgf_grid(ttm: float,
429
435
  is_stiff_solver: bool = False,
430
436
  is_analytic: bool = False,
431
437
  is_spot_measure: bool = True,
438
+ vol_backbone_eta: float = 1.0,
432
439
  **kwargs
433
440
  ) -> Tuple[np.ndarray, np.ndarray]:
434
441
  """
@@ -471,7 +478,8 @@ def compute_logsv_a_mgf_grid(ttm: float,
471
478
  a_t0=a_t0,
472
479
  is_stiff_solver=is_stiff_solver,
473
480
  expansion_order=expansion_order,
474
- is_spot_measure=is_spot_measure)
481
+ is_spot_measure=is_spot_measure,
482
+ vol_backbone_eta=vol_backbone_eta)
475
483
 
476
484
  y = sigma0 - theta
477
485
  if expansion_order == ExpansionOrder.FIRST: