stochvolmodels 1.1.2__tar.gz → 1.1.3__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.1.2 → stochvolmodels-1.1.3}/.gitignore +2 -0
- {stochvolmodels-1.1.2/stochvolmodels.egg-info → stochvolmodels-1.1.3}/PKG-INFO +15 -1
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/README.md +15 -1
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/pyproject.toml +1 -1
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/examples/run_lognormal_sv_pricer.py +32 -15
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/logsv/logsv_params.py +4 -1
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/logsv_pricer.py +7 -7
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/rough_logsv/split_simulation.py +57 -38
- stochvolmodels-1.1.3/stochvolmodels/pricers/rough_logsv/test_kernel_approx.py +16 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3/stochvolmodels.egg-info}/PKG-INFO +15 -1
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels.egg-info/SOURCES.txt +1 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/LICENSE.txt +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/MANIFEST.in +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/forward_var/calibrate_forward_var.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/il_hedging/README.md +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/il_hedging/logsv_figures.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/il_hedging/run_logsv_for_il_payoff.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/inverse_options/README.md +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/inverse_options/compare_net_delta.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/logsv_model_wtih_quadratic_drift/README.md +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/logsv_model_wtih_quadratic_drift/article_figures.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/logsv_model_wtih_quadratic_drift/calibrations.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/logsv_model_wtih_quadratic_drift/compare_admis_reg.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/logsv_model_wtih_quadratic_drift/model_fit_to_options_timeseries.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/logsv_model_wtih_quadratic_drift/moments_vol_qvar.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/logsv_model_wtih_quadratic_drift/ode_sol_in_time.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/logsv_model_wtih_quadratic_drift/steady_state_pdf.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/logsv_model_wtih_quadratic_drift/vol_drift.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/risk_premia_gmm/check_kernel.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/risk_premia_gmm/gmm_slides.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/risk_premia_gmm/plot_gmm.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/risk_premia_gmm/q_kernel.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/risk_premia_gmm/run_gmm_fit.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/sv_for_factor_hjm/README.md +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/sv_for_factor_hjm/calibration_fig_5_6_7.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/sv_for_factor_hjm/calibration_fig_8_9.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/t_distribution/illustrations.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/t_distribution/market_data_fit.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/t_distribution/mc_pricer_with_kernel.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/volatility_models/README.md +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/volatility_models/article_figures.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/volatility_models/autocorr_fit.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/volatility_models/load_data.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/volatility_models/ss_distribution_fit.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/volatility_models/vol_beta.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/requirements.txt +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/setup.cfg +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/__init__.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/data/__init__.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/data/fetch_option_chain.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/data/option_chain.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/data/test_option_chain.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/examples/quick_run_lognormal_sv_pricer.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/examples/run_heston.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/examples/run_heston_sv_pricer.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/examples/run_pricing_options_on_qvar.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/__init__.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/analytic/__init__.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/analytic/bachelier.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/analytic/bsm.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/analytic/tdist.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/factor_hjm/double_exp_pricer.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/factor_hjm/factor_hjm_pricer.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/factor_hjm/rate_affine_expansion.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/factor_hjm/rate_core.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/factor_hjm/rate_evaluate.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/factor_hjm/rate_factor_basis.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/factor_hjm/rate_logsv_ivols.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/factor_hjm/rate_logsv_params.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/factor_hjm/rate_logsv_pricer.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/gmm_pricer.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/hawkes_jd_pricer.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/heston_pricer.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/logsv/__init__.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/logsv/affine_expansion.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/logsv/vol_moments_ode.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/model_pricer.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/rough_logsv/RoughKernel.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/tdist_pricer.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/tests/__init__.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/tests/bsm_mgf_pricer.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/tests/qv_pricer.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/utils/__init__.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/utils/config.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/utils/funcs.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/utils/mc_payoffs.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/utils/mgf_pricer.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/utils/plots.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/utils/var_swap_pricer.py +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels.egg-info/dependency_links.txt +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels.egg-info/requires.txt +0 -0
- {stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stochvolmodels
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.3
|
|
4
4
|
Summary: Python implementation of pricing analytics and Monte Carlo simulations for stochastic volatility models including log-normal SV model, Heston
|
|
5
5
|
Author-email: Artur Sepp <artursepp@gmail.com>
|
|
6
6
|
Maintainer-email: Artur Sepp <artursepp@gmail.com>, Parviz Rakhmonov <ParvizRZ@gmail.com>
|
|
@@ -1144,3 +1144,17 @@ Special thanks to co-authors and collaborators:
|
|
|
1144
1144
|
- Alexander Lipton
|
|
1145
1145
|
|
|
1146
1146
|
For additional research and advanced analytics, see the companion modules and papers included in this package.
|
|
1147
|
+
|
|
1148
|
+
## BibTeX Citations for StochVolModels (Stochastic Volatility Models) Package
|
|
1149
|
+
|
|
1150
|
+
If you use StochVolModels in your research, please cite it as:
|
|
1151
|
+
|
|
1152
|
+
```bibtex
|
|
1153
|
+
@software{stochvolmodels2024,
|
|
1154
|
+
author={Sepp, Artur},
|
|
1155
|
+
title={StochVolModels: Python implementation of pricing analytics and Monte Carlo simulations for stochastic volatility models},
|
|
1156
|
+
year={2024},
|
|
1157
|
+
url={https://github.com/ArturSepp/StochVolModels},
|
|
1158
|
+
}
|
|
1159
|
+
```
|
|
1160
|
+
|
|
@@ -391,4 +391,18 @@ Special thanks to co-authors and collaborators:
|
|
|
391
391
|
- Vladimir Lucic
|
|
392
392
|
- Alexander Lipton
|
|
393
393
|
|
|
394
|
-
For additional research and advanced analytics, see the companion modules and papers included in this package.
|
|
394
|
+
For additional research and advanced analytics, see the companion modules and papers included in this package.
|
|
395
|
+
|
|
396
|
+
## BibTeX Citations for StochVolModels (Stochastic Volatility Models) Package
|
|
397
|
+
|
|
398
|
+
If you use StochVolModels in your research, please cite it as:
|
|
399
|
+
|
|
400
|
+
```bibtex
|
|
401
|
+
@software{stochvolmodels2024,
|
|
402
|
+
author={Sepp, Artur},
|
|
403
|
+
title={StochVolModels: Python implementation of pricing analytics and Monte Carlo simulations for stochastic volatility models},
|
|
404
|
+
year={2024},
|
|
405
|
+
url={https://github.com/ArturSepp/StochVolModels},
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
|
|
7
7
|
|
|
8
8
|
[project]
|
|
9
9
|
name = "stochvolmodels"
|
|
10
|
-
version = "1.1.
|
|
10
|
+
version = "1.1.3"
|
|
11
11
|
description = "Python implementation of pricing analytics and Monte Carlo simulations for stochastic volatility models including log-normal SV model, Heston"
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
license = {file = "LICENSE.txt"}
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/examples/run_lognormal_sv_pricer.py
RENAMED
|
@@ -4,8 +4,12 @@ run few unit test to illustrate implementation of log-normal sv model analytics
|
|
|
4
4
|
|
|
5
5
|
import numpy as np
|
|
6
6
|
import matplotlib.pyplot as plt
|
|
7
|
+
import seaborn as sns
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import matplotlib.ticker as mticker
|
|
7
10
|
from enum import Enum
|
|
8
11
|
import stochvolmodels as sv
|
|
12
|
+
from stochvolmodels.utils import plots as plot
|
|
9
13
|
from stochvolmodels import LogSVPricer, LogSvParams, OptionChain, LogsvModelCalibrationType
|
|
10
14
|
|
|
11
15
|
|
|
@@ -22,6 +26,7 @@ class LocalTests(Enum):
|
|
|
22
26
|
BENCHM_ROUGH_PRICER = 10
|
|
23
27
|
|
|
24
28
|
|
|
29
|
+
|
|
25
30
|
def run_local_test(local_test: LocalTests):
|
|
26
31
|
"""Run local tests for development and debugging purposes.
|
|
27
32
|
|
|
@@ -160,23 +165,23 @@ def run_local_test(local_test: LocalTests):
|
|
|
160
165
|
print(option_prices_ttm)
|
|
161
166
|
|
|
162
167
|
elif local_test == LocalTests.BENCHM_ROUGH_PRICER:
|
|
163
|
-
btc_option_chain =
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
btc_option_chain = sv.get_btc_test_chain_data()
|
|
169
|
+
# params0 = LogSvParams(sigma0=0.8, theta=1.0, kappa1=2.21, kappa2=1.0, beta=0.15, volvol=1.0)
|
|
170
|
+
params0 = LogSvParams(sigma0=1.32, theta=0.47, kappa1=9.98, kappa2=2.0, beta=0.45, volvol=0.83)
|
|
171
|
+
nb_path = 1000000
|
|
172
|
+
H = 0.4
|
|
173
|
+
N = 2
|
|
174
|
+
seed = 1
|
|
170
175
|
|
|
171
176
|
def rough_vol():
|
|
172
177
|
params1 = LogSvParams.copy(params0)
|
|
173
|
-
params1.H =
|
|
178
|
+
params1.H = H
|
|
174
179
|
params1.approximate_kernel(T=btc_option_chain.ttms[-1], N=N)
|
|
175
180
|
|
|
176
181
|
Z0, Z1, grid_ttms = sv.get_randoms_for_rough_vol_chain_valuation(ttms=btc_option_chain.ttms,
|
|
177
182
|
nb_path=nb_path,
|
|
178
183
|
nb_steps_per_year=360,
|
|
179
|
-
seed=
|
|
184
|
+
seed=seed)
|
|
180
185
|
|
|
181
186
|
|
|
182
187
|
option_prices_ttm, option_std_ttm = sv.rough_logsv_mc_chain_pricer_fixed_randoms(ttms=btc_option_chain.ttms,
|
|
@@ -228,16 +233,28 @@ def run_local_test(local_test: LocalTests):
|
|
|
228
233
|
ivols_logsv = regular_vol()
|
|
229
234
|
|
|
230
235
|
nb_slices = btc_option_chain.ttms.size
|
|
231
|
-
|
|
236
|
+
assert nb_slices == 4
|
|
237
|
+
|
|
238
|
+
with sns.axes_style('darkgrid'):
|
|
239
|
+
fig, axs = plt.subplots(2, 2, figsize=(15, 9), tight_layout=True)
|
|
240
|
+
axs = plot.to_flat_list(axs)
|
|
232
241
|
|
|
233
242
|
for i in range(nb_slices):
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
243
|
+
data = pd.DataFrame({"Rough Log-SV": ivols_rough_logsv[i],
|
|
244
|
+
"Log-SV": ivols_logsv[i]},
|
|
245
|
+
index=np.log(
|
|
246
|
+
btc_option_chain.strikes_ttms[i] / btc_option_chain.forwards[i]))
|
|
247
|
+
ax = axs[i]
|
|
248
|
+
sns.lineplot(data, ax=ax, markers=["o"])
|
|
237
249
|
ax.set_title("Expiry: " + btc_option_chain.ids[i])
|
|
250
|
+
ax.yaxis.set_major_formatter(mticker.FuncFormatter(lambda z, _: '{:.0%}'.format(z)))
|
|
251
|
+
ax.xaxis.set_major_formatter(mticker.FuncFormatter(lambda z, _: '{:.2f}'.format(z)))
|
|
238
252
|
ax.legend()
|
|
239
|
-
fig.suptitle(f"Conventional LogSV model vs Rough LogSV, H={H:.2f} via {N}f Markovian approximation"
|
|
240
|
-
|
|
253
|
+
fig.suptitle(f"Conventional LogSV model vs Rough LogSV, H={H:.2f} via {N}f Markovian approximation\n"
|
|
254
|
+
f"{params0.to_str()}",
|
|
255
|
+
color = "darkblue", fontsize = 14)
|
|
256
|
+
|
|
257
|
+
# fig.savefig("c:/temp/rough_vs_conven_vol.pdf")
|
|
241
258
|
|
|
242
259
|
elif local_test == LocalTests.CALIBRATE_MODEL_TO_BTC_OPTIONS:
|
|
243
260
|
btc_option_chain = sv.get_btc_test_chain_data()
|
|
@@ -35,7 +35,10 @@ class LogSvParams(ModelParams):
|
|
|
35
35
|
|
|
36
36
|
def approximate_kernel(self, T: float, N: int):
|
|
37
37
|
assert 1 <= N <= 5 # not keen to use large N
|
|
38
|
-
if
|
|
38
|
+
if self.H >= 0.4:
|
|
39
|
+
N = N if N<=2 else 2
|
|
40
|
+
self.nodes, self.weights = european_rule(self.H, N, T)
|
|
41
|
+
elif N > 1 and self.H<0.49:
|
|
39
42
|
self.nodes, self.weights = european_rule(self.H, N, T)
|
|
40
43
|
else:
|
|
41
44
|
self.weights = np.array([1.0])
|
|
@@ -703,7 +703,7 @@ def simulate_logsv_x_vol_terminal(ttm: float,
|
|
|
703
703
|
assert sigma0.shape[0] == nb_path
|
|
704
704
|
if W0 is None and W1 is None:
|
|
705
705
|
nb_steps1, dt, grid_t = set_time_grid(ttm=ttm, nb_steps_per_year=nb_steps_per_year)
|
|
706
|
-
print(f"nb_steps1={nb_steps1}, dt={dt}")
|
|
706
|
+
# print(f"nb_steps1={nb_steps1}, dt={dt}")
|
|
707
707
|
sdt = np.sqrt(dt)
|
|
708
708
|
W0_ = sdt * np.random.normal(0, 1, size=(nb_steps1, nb_path))
|
|
709
709
|
W1_ = sdt * np.random.normal(0, 1, size=(nb_steps1, nb_path))
|
|
@@ -760,7 +760,7 @@ def get_randoms_for_rough_vol_chain_valuation(ttms: np.ndarray,
|
|
|
760
760
|
nb_steps_per_year: int = 360,
|
|
761
761
|
seed: int = 10
|
|
762
762
|
) -> Tuple[np.ndarray, np.ndarray, List[np.ndarray]]:
|
|
763
|
-
|
|
763
|
+
np.random.seed(seed)
|
|
764
764
|
grid_ttms = List()
|
|
765
765
|
nb_steps_ttms = np.zeros_like(ttms).astype(int)
|
|
766
766
|
for i, ttm in enumerate(ttms):
|
|
@@ -856,13 +856,11 @@ def rough_logsv_mc_chain_pricer_fixed_randoms(ttms: np.ndarray,
|
|
|
856
856
|
variable_type: VariableType = VariableType.LOG_RETURN
|
|
857
857
|
) -> Tuple[List[np.ndarray], List[np.ndarray]]:
|
|
858
858
|
assert weights.shape == nodes.shape and weights.ndim == 1
|
|
859
|
-
assert kappa2 == 0.0
|
|
859
|
+
# assert kappa2 == 0.0
|
|
860
860
|
N = nodes.size
|
|
861
861
|
v0 = sigma0 / np.sum(weights) * np.ones((N,))
|
|
862
862
|
|
|
863
863
|
# need to redenote coefficients
|
|
864
|
-
lamda = kappa1
|
|
865
|
-
theta = kappa1 * theta
|
|
866
864
|
volvol = np.sqrt(beta ** 2 + orthog_vol ** 2)
|
|
867
865
|
rho = beta / volvol
|
|
868
866
|
|
|
@@ -882,8 +880,10 @@ def rough_logsv_mc_chain_pricer_fixed_randoms(ttms: np.ndarray,
|
|
|
882
880
|
nb_steps = timegrid.size - 1
|
|
883
881
|
Z0_ = Z0[:nb_steps]
|
|
884
882
|
Z1_ = Z1[:nb_steps]
|
|
885
|
-
|
|
886
|
-
|
|
883
|
+
weight_vec = np.repeat(weights[:, None], nb_path,axis=1)
|
|
884
|
+
nodes_vec = np.repeat(nodes[:, None], nb_path,axis=1)
|
|
885
|
+
log_spot_str, vol_str, qv_str = log_spot_full_combined(nodes_vec, weight_vec, v0_vec, theta, kappa1, kappa2, log_s0,
|
|
886
|
+
v_init, rho, volvol, timegrid, nb_path, Z0_, Z1_)
|
|
887
887
|
print(f"Number of paths with negative vol: {np.sum(weights @ vol_str < 0.0)}")
|
|
888
888
|
print(f"Mean spot Strand: {np.mean(np.exp(log_spot_str))}")
|
|
889
889
|
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/rough_logsv/split_simulation.py
RENAMED
|
@@ -14,7 +14,7 @@ from numba import njit, objmode
|
|
|
14
14
|
# from stochvolmodels.pricers.logsv_pricer import LogSvParams
|
|
15
15
|
|
|
16
16
|
@njit(cache=False, fastmath=True)
|
|
17
|
-
def drift_ode_solve(nodes: np.ndarray, v0: np.ndarray, theta: float,
|
|
17
|
+
def drift_ode_solve(nodes: np.ndarray, v0: np.ndarray, theta: float, kappa1: float, kappa2: float,
|
|
18
18
|
z0: np.ndarray, weight: np.ndarray, h: float):
|
|
19
19
|
"""
|
|
20
20
|
|
|
@@ -23,7 +23,8 @@ def drift_ode_solve(nodes: np.ndarray, v0: np.ndarray, theta: float, lamda: floa
|
|
|
23
23
|
nodes : (fixed argument) exponents x_i, array of size (n,)
|
|
24
24
|
v0 : (fixed argument) array of size (n, nb_path)
|
|
25
25
|
theta : long-run level, scaled by lambda, scalar
|
|
26
|
-
|
|
26
|
+
kappa1 : linear mean-reversion speed, scalar
|
|
27
|
+
kappa2 : quadratic mean-reversion speed, scalar
|
|
27
28
|
z0 : initial values, array of size (n, nb_path)
|
|
28
29
|
weight : wieghts, array of size (n, )
|
|
29
30
|
h : step size
|
|
@@ -32,30 +33,39 @@ def drift_ode_solve(nodes: np.ndarray, v0: np.ndarray, theta: float, lamda: floa
|
|
|
32
33
|
-------
|
|
33
34
|
Array of size (n, nb_path)
|
|
34
35
|
"""
|
|
35
|
-
assert nodes.shape == v0.shape
|
|
36
|
-
n = weight.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Dzh =
|
|
36
|
+
assert nodes.shape == v0.shape == z0.shape == weight.shape
|
|
37
|
+
n = weight.shape[0]
|
|
38
|
+
z0w = np.sum(weight * z0, axis=0)
|
|
39
|
+
|
|
40
|
+
# s1 = -nodes[:, None] * (z0 - v0) * h + (kappa1 + kappa2 * z0w) * (theta - z0w) * h
|
|
41
|
+
# s2 = -nodes[:, None] * (z0 + 0.5 * s1 - v0) * h + (kappa1 + kappa2 * (z0w + 0.5 * s1)) * (theta - (z0w + 0.5 * s1)) * h
|
|
42
|
+
# Dzh = z0 + s2
|
|
43
|
+
|
|
44
|
+
z1 = -nodes * (z0 - v0) * h + (kappa1 + kappa2 * z0w) * (theta - z0w) * h
|
|
45
|
+
z2 = -nodes * (z0 + 0.5 * z1 - v0) * h + (kappa1 + kappa2 * (z0w + 0.5 * z1)) * (theta - (z0w + 0.5 * z1)) * h
|
|
46
|
+
Dzh = z0 + z2
|
|
47
|
+
|
|
48
|
+
# lamda_vec = np.repeat(lamda, n)
|
|
49
|
+
# theta_vec = np.repeat(theta, n)[:, None]
|
|
50
|
+
# diag_x = np.diag(nodes)
|
|
51
|
+
# I = np.identity(n)
|
|
52
|
+
# A = -np.outer(lamda_vec, weight) - diag_x # matrix of size (n, n)
|
|
53
|
+
# b = theta_vec + diag_x @ v0 # vector of size (n, nb_path)
|
|
54
|
+
# # with objmode(eAh='float64[:, ::1]'):
|
|
55
|
+
# eAh = expm(A * h)
|
|
56
|
+
# Dzh = eAh @ z0 + (np.linalg.inv(A) @ (eAh - I)) @ b # vector of size (n, nb_path)
|
|
46
57
|
|
|
47
58
|
return Dzh
|
|
48
59
|
|
|
49
60
|
@njit(cache=False, fastmath=True)
|
|
50
61
|
def diffus_sde_solve(y0: np.ndarray, weight: np.ndarray, volvol: float, h: float, nb_path: int,
|
|
51
62
|
z_rand: np.ndarray):
|
|
52
|
-
assert y0.shape
|
|
63
|
+
assert y0.shape == weight.shape and y0.shape[-1] == nb_path
|
|
53
64
|
assert z_rand.shape == (nb_path,)
|
|
54
|
-
|
|
55
|
-
weight_sum = np.sum(weight)
|
|
65
|
+
weight_sum = np.sum(weight, axis=0)
|
|
56
66
|
volvol_ = volvol * weight_sum
|
|
57
67
|
|
|
58
|
-
yw = np.
|
|
68
|
+
yw = np.sum(weight * y0, axis=0)
|
|
59
69
|
|
|
60
70
|
dW = z_rand * np.sqrt(h)
|
|
61
71
|
Yh = yw * np.exp(-0.5 * volvol_ ** 2 * h + volvol_ * dW)
|
|
@@ -68,7 +78,7 @@ def diffus_sde_solve(y0: np.ndarray, weight: np.ndarray, volvol: float, h: float
|
|
|
68
78
|
return Yh_vec
|
|
69
79
|
|
|
70
80
|
@njit(cache=False, fastmath=True)
|
|
71
|
-
def drift_diffus_strand(nodes: np.ndarray, v0: np.ndarray, theta: float,
|
|
81
|
+
def drift_diffus_strand(nodes: np.ndarray, v0: np.ndarray, theta: float, kappa1: float, kappa2: float,
|
|
72
82
|
volvol: float, v_init: np.ndarray, weight: np.ndarray, h: float,
|
|
73
83
|
nb_path: int, z_rand: np.ndarray):
|
|
74
84
|
"""
|
|
@@ -78,7 +88,8 @@ def drift_diffus_strand(nodes: np.ndarray, v0: np.ndarray, theta: float, lamda:
|
|
|
78
88
|
nodes : exponents x_i, array of size (n,)
|
|
79
89
|
v0 : (fixed argument) array of size (n, nb_path)
|
|
80
90
|
theta : long-run level, scaled by lambda, scalar
|
|
81
|
-
|
|
91
|
+
kappa1 : linear mean-reversion speed, scalar
|
|
92
|
+
kappa2 : quadratic mean-reversion speed, scalar
|
|
82
93
|
volvol : total volatility. in other words, vartheta. scalar
|
|
83
94
|
v_init : initial values, array of size (n, nb_path)
|
|
84
95
|
weight : (fixed argument) wieghts, array of size (n, )
|
|
@@ -90,9 +101,9 @@ def drift_diffus_strand(nodes: np.ndarray, v0: np.ndarray, theta: float, lamda:
|
|
|
90
101
|
-------
|
|
91
102
|
|
|
92
103
|
"""
|
|
93
|
-
D_inn = drift_ode_solve(nodes, v0, theta,
|
|
104
|
+
D_inn = drift_ode_solve(nodes, v0, theta, kappa1, kappa2, v_init, weight, 0.5 * h)
|
|
94
105
|
S_inn = diffus_sde_solve(D_inn, weight, volvol, h, nb_path, z_rand)
|
|
95
|
-
sol = drift_ode_solve(nodes, v0, theta,
|
|
106
|
+
sol = drift_ode_solve(nodes, v0, theta, kappa1, kappa2, S_inn, weight, 0.5 * h)
|
|
96
107
|
|
|
97
108
|
return sol
|
|
98
109
|
|
|
@@ -100,33 +111,41 @@ def drift_diffus_strand(nodes: np.ndarray, v0: np.ndarray, theta: float, lamda:
|
|
|
100
111
|
@njit(cache=False, fastmath=True)
|
|
101
112
|
def log_spot_full_solve2(nodes: np.ndarray, weight: np.ndarray,
|
|
102
113
|
v0: np.ndarray, y0: np.ndarray,
|
|
103
|
-
theta: float,
|
|
114
|
+
theta: float, kappa1: float, kappa2: float, log_s: np.ndarray, v: np.ndarray, y: np.ndarray,
|
|
104
115
|
rho: float, volvol: float, h: float, nb_path: int,
|
|
105
116
|
z0: np.ndarray, z1: np.ndarray):
|
|
106
117
|
# raise ValueError
|
|
107
|
-
assert nodes.shape == weight.shape and weight.ndim ==
|
|
108
|
-
assert v.shape
|
|
118
|
+
assert nodes.shape == weight.shape and weight.ndim == 2
|
|
119
|
+
assert v.shape == weight.shape and v.shape[-1] == nb_path
|
|
109
120
|
assert y.shape == (1, nb_path)
|
|
110
121
|
assert log_s.shape == (1, nb_path)
|
|
111
|
-
assert v0.shape
|
|
122
|
+
assert v0.shape == weight.shape and v0.shape[-1] == nb_path
|
|
112
123
|
assert y0.shape == (1, nb_path)
|
|
113
124
|
assert z0.shape == (nb_path,) and z1.shape == (nb_path,)
|
|
114
125
|
|
|
115
|
-
vol_h = drift_diffus_strand(nodes, v0, theta,
|
|
126
|
+
vol_h = drift_diffus_strand(nodes, v0, theta, kappa1, kappa2, volvol, v, weight, h, nb_path, z0)
|
|
116
127
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
w_inv = 1.0 / np.sum(weight)
|
|
128
|
+
wlam = weight * nodes
|
|
129
|
+
vw = np.sum(weight * v, axis=0)
|
|
130
|
+
volw_h = np.sum(weight * vol_h, axis=0)
|
|
131
|
+
w_inv = 1.0 / np.sum(weight, axis=0)
|
|
122
132
|
|
|
123
133
|
c1 = 0.5
|
|
124
134
|
c2 = 0.5
|
|
125
135
|
rho_comp = np.sqrt(1.0 - rho * rho)
|
|
126
136
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
137
|
+
sq_vw = np.square(vw)
|
|
138
|
+
sq_vhw = np.square(volw_h)
|
|
139
|
+
|
|
140
|
+
w_lam_vol = np.sum(wlam * v, axis=0)
|
|
141
|
+
w_lam_vol_h = np.sum(wlam * vol_h, axis=0)
|
|
142
|
+
w_lam_v0 = np.sum(wlam * v0, axis=0)
|
|
143
|
+
|
|
144
|
+
term1 = 1.0 / volvol * (((volw_h - vw) / h + c1 * w_lam_vol + c2 * w_lam_vol_h - w_lam_v0) * w_inv
|
|
145
|
+
- kappa1 * theta + (kappa1 - kappa2 * theta) * (c1 * vw + c2 * volw_h)
|
|
146
|
+
+ kappa2 * (c1 * sq_vw + c2 * sq_vhw)) * h
|
|
147
|
+
|
|
148
|
+
term2 = c1 * h * sq_vw + c2 * h * sq_vhw
|
|
130
149
|
log_spot_h = log_s - 0.5 * term2 + rho * term1 + rho_comp * np.sqrt(term2) * z1
|
|
131
150
|
|
|
132
151
|
y_h = y + 0.5 * h * (vw * vw + volw_h * volw_h) # don't need it but keep for plotting
|
|
@@ -136,12 +155,12 @@ def log_spot_full_solve2(nodes: np.ndarray, weight: np.ndarray,
|
|
|
136
155
|
@njit(cache=False, fastmath=True)
|
|
137
156
|
def log_spot_full_combined(nodes: np.ndarray, weight: np.ndarray,
|
|
138
157
|
v0: np.ndarray,
|
|
139
|
-
theta: float,
|
|
158
|
+
theta: float, kappa1: float, kappa2: float, log_s0: float, v_init: np.ndarray,
|
|
140
159
|
rho: float, volvol: float, timegrid: np.ndarray, nb_path: int,
|
|
141
160
|
Z0: np.ndarray, Z1: np.ndarray):
|
|
142
161
|
h = timegrid[1] - timegrid[0]
|
|
143
|
-
assert np.all(np.isclose(np.diff(timegrid)[1:], h)) and timegrid[0] == 0.0
|
|
144
|
-
assert Z0.shape == (timegrid.size - 1, nb_path) and Z1.shape == (timegrid.size - 1, nb_path)
|
|
162
|
+
# assert np.all(np.isclose(np.diff(timegrid)[1:], h)) and timegrid[0] == 0.0
|
|
163
|
+
# assert Z0.shape == (timegrid.size - 1, nb_path) and Z1.shape == (timegrid.size - 1, nb_path)
|
|
145
164
|
|
|
146
165
|
y0 = np.zeros((1, nb_path))
|
|
147
166
|
|
|
@@ -152,7 +171,7 @@ def log_spot_full_combined(nodes: np.ndarray, weight: np.ndarray,
|
|
|
152
171
|
# a, b, c = solve_coeffs(nodes, weight, v0[:, 0], theta, lamda, rho, volvol)
|
|
153
172
|
|
|
154
173
|
for idx, _ in enumerate(timegrid[:-1]):
|
|
155
|
-
vol_h, y_h, log_spot_h = log_spot_full_solve2(nodes, weight, v0, y0, theta,
|
|
174
|
+
vol_h, y_h, log_spot_h = log_spot_full_solve2(nodes, weight, v0, y0, theta, kappa1, kappa2,
|
|
156
175
|
log_spot_h, vol_h, y_h, rho, volvol, h, nb_path,
|
|
157
176
|
Z0[idx], Z1[idx])
|
|
158
177
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from stochvolmodels import LogSvParams
|
|
3
|
+
|
|
4
|
+
if __name__ == "__main__":
|
|
5
|
+
H = 0.4464700054758044
|
|
6
|
+
N = 2
|
|
7
|
+
T = 1.
|
|
8
|
+
|
|
9
|
+
params0 = LogSvParams(sigma0 = 1.32, theta=0.47, kappa1=4.0, kappa2=2.0, beta=0.45, volvol=0.83)
|
|
10
|
+
for N in [1, 2, 3]:
|
|
11
|
+
for H in np.linspace(0.3, 0.49, 150):
|
|
12
|
+
params0.H = H
|
|
13
|
+
print(f" *** N={N}, H={H} *** ")
|
|
14
|
+
params0.approximate_kernel(T=T, N=N)
|
|
15
|
+
print(params0.weights)
|
|
16
|
+
print(params0.nodes)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stochvolmodels
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.3
|
|
4
4
|
Summary: Python implementation of pricing analytics and Monte Carlo simulations for stochastic volatility models including log-normal SV model, Heston
|
|
5
5
|
Author-email: Artur Sepp <artursepp@gmail.com>
|
|
6
6
|
Maintainer-email: Artur Sepp <artursepp@gmail.com>, Parviz Rakhmonov <ParvizRZ@gmail.com>
|
|
@@ -1144,3 +1144,17 @@ Special thanks to co-authors and collaborators:
|
|
|
1144
1144
|
- Alexander Lipton
|
|
1145
1145
|
|
|
1146
1146
|
For additional research and advanced analytics, see the companion modules and papers included in this package.
|
|
1147
|
+
|
|
1148
|
+
## BibTeX Citations for StochVolModels (Stochastic Volatility Models) Package
|
|
1149
|
+
|
|
1150
|
+
If you use StochVolModels in your research, please cite it as:
|
|
1151
|
+
|
|
1152
|
+
```bibtex
|
|
1153
|
+
@software{stochvolmodels2024,
|
|
1154
|
+
author={Sepp, Artur},
|
|
1155
|
+
title={StochVolModels: Python implementation of pricing analytics and Monte Carlo simulations for stochastic volatility models},
|
|
1156
|
+
year={2024},
|
|
1157
|
+
url={https://github.com/ArturSepp/StochVolModels},
|
|
1158
|
+
}
|
|
1159
|
+
```
|
|
1160
|
+
|
|
@@ -77,6 +77,7 @@ stochvolmodels/pricers/logsv/logsv_params.py
|
|
|
77
77
|
stochvolmodels/pricers/logsv/vol_moments_ode.py
|
|
78
78
|
stochvolmodels/pricers/rough_logsv/RoughKernel.py
|
|
79
79
|
stochvolmodels/pricers/rough_logsv/split_simulation.py
|
|
80
|
+
stochvolmodels/pricers/rough_logsv/test_kernel_approx.py
|
|
80
81
|
stochvolmodels/tests/__init__.py
|
|
81
82
|
stochvolmodels/tests/bsm_mgf_pricer.py
|
|
82
83
|
stochvolmodels/tests/qv_pricer.py
|
|
File without changes
|
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/forward_var/calibrate_forward_var.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/il_hedging/run_logsv_for_il_payoff.py
RENAMED
|
File without changes
|
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/inverse_options/compare_net_delta.py
RENAMED
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/logsv_model_wtih_quadratic_drift/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/sv_for_factor_hjm/calibration_fig_5_6_7.py
RENAMED
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/sv_for_factor_hjm/calibration_fig_8_9.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/t_distribution/mc_pricer_with_kernel.py
RENAMED
|
File without changes
|
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/volatility_models/article_figures.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/my_papers/volatility_models/ss_distribution_fit.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/examples/run_heston_sv_pricer.py
RENAMED
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/examples/run_pricing_options_on_qvar.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/factor_hjm/double_exp_pricer.py
RENAMED
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/factor_hjm/factor_hjm_pricer.py
RENAMED
|
File without changes
|
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/factor_hjm/rate_core.py
RENAMED
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/factor_hjm/rate_evaluate.py
RENAMED
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/factor_hjm/rate_factor_basis.py
RENAMED
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/factor_hjm/rate_logsv_ivols.py
RENAMED
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/factor_hjm/rate_logsv_params.py
RENAMED
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/factor_hjm/rate_logsv_pricer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/logsv/affine_expansion.py
RENAMED
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/logsv/vol_moments_ode.py
RENAMED
|
File without changes
|
|
File without changes
|
{stochvolmodels-1.1.2 → stochvolmodels-1.1.3}/stochvolmodels/pricers/rough_logsv/RoughKernel.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|