stochvolmodels 1.0.19__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.
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/PKG-INFO +5 -2
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/pyproject.toml +3 -2
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/__init__.py +1 -1
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/data/option_chain.py +23 -0
- stochvolmodels-1.0.21/stochvolmodels/examples/quick_run_lognormal_sv_pricer.py +34 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/examples/run_lognormal_sv_pricer.py +1 -3
- stochvolmodels-1.0.21/stochvolmodels/my_papers/forward_var/calibrate_forward_var.py +115 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/logsv_model_wtih_quadratic_drift/calibrations.py +11 -3
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/logsv_model_wtih_quadratic_drift/model_fit_to_options_timeseries.py +7 -6
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/logsv_model_wtih_quadratic_drift/moments_vol_qvar.py +2 -1
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/logsv_model_wtih_quadratic_drift/ode_sol_in_time.py +1 -1
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/logsv_model_wtih_quadratic_drift/steady_state_pdf.py +1 -1
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/logsv_model_wtih_quadratic_drift/vol_drift.py +1 -1
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/volatility_models/article_figures.py +1 -1
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/volatility_models/autocorr_fit.py +2 -1
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/pricers/heston_pricer.py +1 -1
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/pricers/logsv/affine_expansion.py +24 -16
- stochvolmodels-1.0.21/stochvolmodels/pricers/logsv/logsv_params.py +176 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/pricers/logsv/vol_moments_ode.py +49 -6
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/pricers/logsv_pricer.py +47 -159
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/pricers/model_pricer.py +3 -3
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/tests/qv_pricer.py +2 -1
- stochvolmodels-1.0.21/stochvolmodels/utils/var_swap_pricer.py +26 -0
- stochvolmodels-1.0.19/stochvolmodels/my_papers/volatility_models/__init__.py +0 -0
- stochvolmodels-1.0.19/stochvolmodels/pricers/__init__.py +0 -0
- stochvolmodels-1.0.19/stochvolmodels/pricers/analytic/__init__.py +0 -0
- stochvolmodels-1.0.19/stochvolmodels/pricers/logsv/__init__.py +0 -0
- stochvolmodels-1.0.19/stochvolmodels/tests/__init__.py +0 -0
- stochvolmodels-1.0.19/stochvolmodels/utils/__init__.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/LICENSE.txt +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/README.md +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/data/__init__.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/data/fetch_option_chain.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/data/test_option_chain.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/examples/run_gmm_fit.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/examples/run_heston.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/examples/run_heston_sv_pricer.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/examples/run_pricing_options_on_qvar.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/__init__.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/il_hedging/README.md +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/il_hedging/logsv_figures.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/il_hedging/run_logsv_for_il_payoff.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/inverse_options/README.md +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/inverse_options/compare_net_delta.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/logsv_model_wtih_quadratic_drift/README.md +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/logsv_model_wtih_quadratic_drift/article_figures.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/logsv_model_wtih_quadratic_drift/compare_admis_reg.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/risk_premia/check_kernel.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/risk_premia/gmm_slides.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/risk_premia/q_kernel.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/t_distribution/illustrations.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/t_distribution/market_data_fit.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/t_distribution/mc_pricer_with_kernel.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/volatility_models/README.md +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/volatility_models/load_data.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/volatility_models/ss_distribution_fit.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/my_papers/volatility_models/vol_beta.py +0 -0
- {stochvolmodels-1.0.19/stochvolmodels/my_papers/il_hedging → stochvolmodels-1.0.21/stochvolmodels/pricers}/__init__.py +0 -0
- {stochvolmodels-1.0.19/stochvolmodels/my_papers/inverse_options → stochvolmodels-1.0.21/stochvolmodels/pricers/analytic}/__init__.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/pricers/analytic/bachelier.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/pricers/analytic/bsm.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/pricers/analytic/tdist.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/pricers/gmm_pricer.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/pricers/hawkes_jd_pricer.py +0 -0
- {stochvolmodels-1.0.19/stochvolmodels/my_papers/logsv_model_wtih_quadratic_drift → stochvolmodels-1.0.21/stochvolmodels/pricers/logsv}/__init__.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/pricers/tdist_pricer.py +0 -0
- {stochvolmodels-1.0.19/stochvolmodels/my_papers/risk_premia → stochvolmodels-1.0.21/stochvolmodels/tests}/__init__.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/tests/bsm_mgf_pricer.py +0 -0
- {stochvolmodels-1.0.19/stochvolmodels/my_papers/t_distribution → stochvolmodels-1.0.21/stochvolmodels/utils}/__init__.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/utils/config.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/utils/funcs.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/utils/mc_payoffs.py +0 -0
- {stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/utils/mgf_pricer.py +0 -0
- {stochvolmodels-1.0.19 → 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.
|
|
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
|
|
@@ -9,7 +9,7 @@ Author: Artur Sepp
|
|
|
9
9
|
Author-email: artursepp@gmail.com
|
|
10
10
|
Maintainer: Artur Sepp
|
|
11
11
|
Maintainer-email: artursepp@gmail.com
|
|
12
|
-
Requires-Python: >=3.8
|
|
12
|
+
Requires-Python: >=3.8
|
|
13
13
|
Classifier: Development Status :: 4 - Beta
|
|
14
14
|
Classifier: Environment :: Console
|
|
15
15
|
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
@@ -22,6 +22,9 @@ Classifier: Programming Language :: Python :: 3
|
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.8
|
|
23
23
|
Classifier: Programming Language :: Python :: 3.9
|
|
24
24
|
Classifier: Programming Language :: Python :: 3.10
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
27
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
28
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
26
29
|
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
27
30
|
Requires-Dist: matplotlib (>=3.5.2)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "stochvolmodels"
|
|
3
|
-
version = "1.0.
|
|
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>"]
|
|
@@ -23,6 +23,7 @@ classifiers=[
|
|
|
23
23
|
"Programming Language :: Python :: 3 :: Only",
|
|
24
24
|
"Topic :: Office/Business :: Financial :: Investment",
|
|
25
25
|
]
|
|
26
|
+
|
|
26
27
|
packages = [ {include = "stochvolmodels"}]
|
|
27
28
|
|
|
28
29
|
exclude = ["stochvolmodel/my_papers/"]
|
|
@@ -32,7 +33,7 @@ exclude = ["stochvolmodel/my_papers/"]
|
|
|
32
33
|
"Personal website" = "https://artursepp.com"
|
|
33
34
|
|
|
34
35
|
[tool.poetry.dependencies]
|
|
35
|
-
python = ">=3.8
|
|
36
|
+
python = ">=3.8"
|
|
36
37
|
numba = ">=0.55"
|
|
37
38
|
numpy = ">=1.22.4"
|
|
38
39
|
scipy = ">=1.3"
|
|
@@ -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()
|
{stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/examples/run_lognormal_sv_pricer.py
RENAMED
|
@@ -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.
|
|
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
|
|
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.
|
|
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.
|
|
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('
|
|
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 = '
|
|
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//
|
|
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
|
|
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
|
|
19
|
+
from stochvolmodels import LogSvParams
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def plot_ode_sol_in_t(params: LogSvParams,
|
|
@@ -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
|
|
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
|
|
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()
|
{stochvolmodels-1.0.19 → stochvolmodels-1.0.21}/stochvolmodels/pricers/logsv/affine_expansion.py
RENAMED
|
@@ -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 *
|
|
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:
|