stochvolmodels 1.0.28__tar.gz → 1.0.30__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 (50) hide show
  1. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/PKG-INFO +1 -1
  2. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/pyproject.toml +1 -1
  3. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/__init__.py +3 -3
  4. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/data/fetch_option_chain.py +1 -1
  5. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/examples/run_lognormal_sv_pricer.py +114 -5
  6. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/logsv/logsv_params.py +14 -0
  7. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/logsv_pricer.py +118 -6
  8. stochvolmodels-1.0.30/stochvolmodels/pricers/rough_logsv/RoughKernel.py +1210 -0
  9. stochvolmodels-1.0.30/stochvolmodels/pricers/rough_logsv/split_simulation.py +159 -0
  10. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/LICENSE.txt +0 -0
  11. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/README.md +0 -0
  12. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/data/__init__.py +0 -0
  13. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/data/option_chain.py +0 -0
  14. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/data/test_option_chain.py +0 -0
  15. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/examples/quick_run_lognormal_sv_pricer.py +0 -0
  16. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/examples/run_heston.py +0 -0
  17. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/examples/run_heston_sv_pricer.py +0 -0
  18. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/examples/run_pricing_options_on_qvar.py +0 -0
  19. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/__init__.py +0 -0
  20. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/analytic/__init__.py +0 -0
  21. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/analytic/bachelier.py +0 -0
  22. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/analytic/bsm.py +0 -0
  23. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/analytic/tdist.py +0 -0
  24. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/factor_hjm/double_exp_pricer.py +0 -0
  25. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/factor_hjm/factor_hjm_pricer.py +0 -0
  26. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/factor_hjm/rate_affine_expansion.py +0 -0
  27. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/factor_hjm/rate_core.py +0 -0
  28. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/factor_hjm/rate_evaluate.py +0 -0
  29. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/factor_hjm/rate_factor_basis.py +0 -0
  30. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/factor_hjm/rate_logsv_ivols.py +0 -0
  31. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/factor_hjm/rate_logsv_params.py +0 -0
  32. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/factor_hjm/rate_logsv_pricer.py +0 -0
  33. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/gmm_pricer.py +0 -0
  34. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/hawkes_jd_pricer.py +0 -0
  35. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/heston_pricer.py +0 -0
  36. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/logsv/__init__.py +0 -0
  37. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/logsv/affine_expansion.py +0 -0
  38. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/logsv/vol_moments_ode.py +0 -0
  39. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/model_pricer.py +0 -0
  40. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/pricers/tdist_pricer.py +0 -0
  41. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/tests/__init__.py +0 -0
  42. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/tests/bsm_mgf_pricer.py +0 -0
  43. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/tests/qv_pricer.py +0 -0
  44. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/utils/__init__.py +0 -0
  45. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/utils/config.py +0 -0
  46. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/utils/funcs.py +0 -0
  47. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/utils/mc_payoffs.py +0 -0
  48. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/utils/mgf_pricer.py +0 -0
  49. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/utils/plots.py +0 -0
  50. {stochvolmodels-1.0.28 → stochvolmodels-1.0.30}/stochvolmodels/utils/var_swap_pricer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: stochvolmodels
3
- Version: 1.0.28
3
+ Version: 1.0.30
4
4
  Summary: Implementation of stochastic volatility models for option pricing
5
5
  License: LICENSE.txt
6
6
  Keywords: volatility,options,Black-Scholes,Heston,Monte-Carlo
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "stochvolmodels"
3
- version = "1.0.28"
3
+ version = "1.0.30"
4
4
  description = "Implementation of stochastic volatility models for option pricing"
5
5
  license = "LICENSE.txt"
6
6
  authors = ["Artur Sepp <artursepp@gmail.com>"]
@@ -109,7 +109,9 @@ from stochvolmodels.pricers.logsv_pricer import (
109
109
  ConstraintsType,
110
110
  CalibrationEngine,
111
111
  get_randoms_for_chain_valuation,
112
- logsv_mc_chain_pricer_fixed_randoms
112
+ get_randoms_for_rough_vol_chain_valuation,
113
+ logsv_mc_chain_pricer_fixed_randoms,
114
+ rough_logsv_mc_chain_pricer_fixed_randoms
113
115
  )
114
116
  from stochvolmodels.pricers.logsv.logsv_params import LogSvParams
115
117
 
@@ -159,5 +161,3 @@ from stochvolmodels.utils.plots import (
159
161
 
160
162
 
161
163
  from stochvolmodels.pricers.logsv.vol_moments_ode import compute_analytic_qvar
162
-
163
- from stochvolmodels.data.option_chain import generate_vol_chain_np
@@ -105,7 +105,7 @@ def sample_option_chain_at_times(options_data_dfs: OptionsDataDFs,
105
105
 
106
106
  def load_price_data(options_data_dfs: OptionsDataDFs,
107
107
  time_period: TimePeriod = None,
108
- data: Literal['spot', 'perp', 'funding_rate'] = 'spot',
108
+ data: Literal['close', 'perp', 'funding_rate'] = 'close',
109
109
  freq: Optional[str] = 'D' # to do
110
110
  ) -> pd.Series:
111
111
  #options_data_dfs = OptionsDataDFs(**ts_data_loader_wrapper(ticker=ticker, freq='D', hour_offset=8))
@@ -5,7 +5,6 @@ run few unit test to illustrate implementation of log-normal sv model analytics
5
5
  import numpy as np
6
6
  import matplotlib.pyplot as plt
7
7
  from enum import Enum
8
-
9
8
  import stochvolmodels as sv
10
9
  from stochvolmodels import LogSVPricer, LogSvParams, OptionChain, LogsvModelCalibrationType
11
10
 
@@ -19,6 +18,8 @@ class UnitTests(Enum):
19
18
  CALIBRATE_MODEL_TO_BTC_OPTIONS = 6
20
19
  MC_WITH_FIXED_RANDOMS = 7
21
20
  CALIBRATE_MODEL_TO_BTC_OPTIONS_WITH_MC = 8
21
+ ROUGH_MC_WITH_FIXED_RANDOMS = 9
22
+ BENCHM_ROUGH_PRICER = 10
22
23
 
23
24
 
24
25
  def run_unit_test(unit_test: UnitTests):
@@ -124,9 +125,115 @@ def run_unit_test(unit_test: UnitTests):
124
125
  option_prices_ttm, option_std_ttm = sv.logsv_mc_chain_pricer_fixed_randoms(**args)
125
126
  print(option_prices_ttm)
126
127
 
127
- option_prices_ttm, option_std_ttm = sv.logsv_mc_chain_pricer_fixed_randoms(**args)
128
+
129
+ elif unit_test == UnitTests.ROUGH_MC_WITH_FIXED_RANDOMS:
130
+ btc_option_chain = sv.get_btc_test_chain_data()
131
+ Z0, Z1, grid_ttms = sv.get_randoms_for_rough_vol_chain_valuation(ttms=btc_option_chain.ttms,
132
+ nb_path=10000,
133
+ nb_steps_per_year=360,
134
+ seed=10)
135
+ params0 = LogSvParams(sigma0=0.8, theta=1.0, kappa1=2.21, kappa2=0.0, beta=0.15, volvol=2.0)
136
+ params0.H = 0.3
137
+ params0.approximate_kernel(T=btc_option_chain.ttms[-1], N=3)
138
+
139
+ option_prices_ttm, option_std_ttm = sv.rough_logsv_mc_chain_pricer_fixed_randoms(ttms=btc_option_chain.ttms,
140
+ forwards=btc_option_chain.forwards,
141
+ discfactors=btc_option_chain.discfactors,
142
+ strikes_ttms=btc_option_chain.strikes_ttms,
143
+ optiontypes_ttms=btc_option_chain.optiontypes_ttms,
144
+ Z0=Z0,
145
+ Z1=Z1,
146
+ sigma0=params0.sigma0,
147
+ theta=params0.theta,
148
+ kappa1=params0.kappa1,
149
+ kappa2=params0.kappa2,
150
+ beta=params0.beta,
151
+ orthog_vol=params0.volvol,
152
+ weights=params0.weights,
153
+ nodes=params0.nodes,
154
+ timegrids=grid_ttms)
128
155
  print(option_prices_ttm)
129
156
 
157
+ elif unit_test == UnitTests.BENCHM_ROUGH_PRICER:
158
+ btc_option_chain = OptionChain.get_uniform_chain(ttms=np.array([0.083, 0.25]),
159
+ ids=np.array(['1m', '3m']),
160
+ strikes=np.linspace(0.5, 1.5, 21))
161
+ params0 = LogSvParams(sigma0=0.8, theta=1.0, kappa1=2.21, kappa2=0.0, beta=0.15, volvol=2.0)
162
+ nb_path = 100000
163
+ H = 0.3
164
+ N = 3
165
+
166
+ def rough_vol():
167
+ params1 = LogSvParams.copy(params0)
168
+ params1.H = 0.3
169
+ params1.approximate_kernel(T=btc_option_chain.ttms[-1], N=N)
170
+
171
+ Z0, Z1, grid_ttms = sv.get_randoms_for_rough_vol_chain_valuation(ttms=btc_option_chain.ttms,
172
+ nb_path=nb_path,
173
+ nb_steps_per_year=360,
174
+ seed=10)
175
+
176
+
177
+ option_prices_ttm, option_std_ttm = sv.rough_logsv_mc_chain_pricer_fixed_randoms(ttms=btc_option_chain.ttms,
178
+ forwards=btc_option_chain.forwards,
179
+ discfactors=btc_option_chain.discfactors,
180
+ strikes_ttms=btc_option_chain.strikes_ttms,
181
+ optiontypes_ttms=btc_option_chain.optiontypes_ttms,
182
+ Z0=Z0,
183
+ Z1=Z1,
184
+ sigma0=params0.sigma0,
185
+ theta=params0.theta,
186
+ kappa1=params0.kappa1,
187
+ kappa2=params0.kappa2,
188
+ beta=params0.beta,
189
+ orthog_vol=params0.volvol,
190
+ weights=params1.weights,
191
+ nodes=params1.nodes,
192
+ timegrids=grid_ttms)
193
+ model_ivols_ttms = btc_option_chain.compute_model_ivols_from_chain_data(option_prices_ttm)
194
+ return model_ivols_ttms
195
+
196
+ def regular_vol():
197
+ W0s, W1s, dts = sv.get_randoms_for_chain_valuation(ttms=btc_option_chain.ttms,
198
+ nb_path=nb_path,
199
+ nb_steps_per_year=360,
200
+ seed=10)
201
+ vol_backbone_etas = params.get_vol_backbone_etas(ttms=btc_option_chain.ttms)
202
+ args = dict(ttms=btc_option_chain.ttms,
203
+ forwards=btc_option_chain.forwards,
204
+ discfactors=btc_option_chain.discfactors,
205
+ strikes_ttms=btc_option_chain.strikes_ttms,
206
+ optiontypes_ttms=btc_option_chain.optiontypes_ttms,
207
+ W0s=W0s,
208
+ W1s=W1s,
209
+ dts=dts,
210
+ v0=params0.sigma0,
211
+ theta=params0.theta,
212
+ kappa1=params0.kappa1,
213
+ kappa2=params0.kappa2,
214
+ beta=params0.beta,
215
+ volvol=params0.volvol,
216
+ vol_backbone_etas=vol_backbone_etas)
217
+ option_prices_ttm, option_std_ttm = sv.logsv_mc_chain_pricer_fixed_randoms(**args)
218
+ model_ivols_ttms = btc_option_chain.compute_model_ivols_from_chain_data(option_prices_ttm)
219
+
220
+ return model_ivols_ttms
221
+
222
+ ivols_rough_logsv = rough_vol()
223
+ ivols_logsv = regular_vol()
224
+
225
+ nb_slices = btc_option_chain.ttms.size
226
+ fig, axs = plt.subplots(1, nb_slices, figsize=(4*nb_slices, 3), tight_layout=True)
227
+
228
+ for i in range(nb_slices):
229
+ ax = axs[i] if nb_slices>1 else axs
230
+ ax.plot(btc_option_chain.strikes_ttms[i], ivols_logsv[i], label="LOG_SV", marker="*")
231
+ ax.plot(btc_option_chain.strikes_ttms[i], ivols_rough_logsv[i], label="ROUGH_LOG_SV", marker="o")
232
+ ax.set_title("Expiry: " + btc_option_chain.ids[i])
233
+ ax.legend()
234
+ fig.suptitle(f"Conventional LogSV model vs Rough LogSV, H={H:.2f} via {N}f Markovian approximation",
235
+ color="darkblue")
236
+
130
237
  elif unit_test == UnitTests.CALIBRATE_MODEL_TO_BTC_OPTIONS:
131
238
  btc_option_chain = sv.get_btc_test_chain_data()
132
239
  params0 = LogSvParams(sigma0=0.8, theta=1.0, kappa1=2.21, kappa2=2.18, beta=0.15, volvol=2.0)
@@ -140,12 +247,14 @@ def run_unit_test(unit_test: UnitTests):
140
247
 
141
248
  elif unit_test == UnitTests.CALIBRATE_MODEL_TO_BTC_OPTIONS_WITH_MC:
142
249
  btc_option_chain = sv.get_btc_test_chain_data()
143
- params0 = LogSvParams(sigma0=0.8, theta=1.0, kappa1=2.21, kappa2=2.18, beta=0.15, volvol=2.0)
250
+ params0 = LogSvParams(sigma0=0.8, theta=1.0, kappa1=2.21, kappa2=0.0, beta=0.15, volvol=2.0)
251
+ params0.H = 0.3
252
+ params0.approximate_kernel(T=btc_option_chain.ttms[-1], N=3)
144
253
  btc_calibrated_params = logsv_pricer.calibrate_model_params_to_chain(option_chain=btc_option_chain,
145
254
  params0=params0,
146
255
  model_calibration_type=LogsvModelCalibrationType.PARAMS4,
147
256
  constraints_type=sv.ConstraintsType.INVERSE_MARTINGALE,
148
- calibration_engine=sv.CalibrationEngine.MC,
257
+ calibration_engine=sv.CalibrationEngine.ROUGH_MC,
149
258
  nb_path=100000,
150
259
  seed=7)
151
260
  print(btc_calibrated_params)
@@ -157,7 +266,7 @@ def run_unit_test(unit_test: UnitTests):
157
266
 
158
267
  if __name__ == '__main__':
159
268
 
160
- unit_test = UnitTests.CALIBRATE_MODEL_TO_BTC_OPTIONS
269
+ unit_test = UnitTests.BENCHM_ROUGH_PRICER
161
270
 
162
271
  is_run_all_tests = False
163
272
  if is_run_all_tests:
@@ -9,6 +9,7 @@ from typing import Optional, Dict, Any
9
9
 
10
10
  from stochvolmodels import VariableType, find_nearest
11
11
  from stochvolmodels.pricers.model_pricer import ModelParams
12
+ from stochvolmodels.pricers.rough_logsv.RoughKernel import european_rule
12
13
 
13
14
 
14
15
  @dataclass
@@ -23,10 +24,23 @@ class LogSvParams(ModelParams):
23
24
  beta: float = -1.0
24
25
  volvol: float = 1.0
25
26
  vol_backbone: pd.Series = None
27
+ H: float = 0.5
28
+ weights: np.ndarray = None
29
+ nodes: np.ndarray = None
26
30
 
27
31
  def __post_init__(self):
28
32
  if self.kappa2 is None:
29
33
  self.kappa2 = self.kappa1 / self.theta
34
+ assert 1e-4 < self.H <= 0.5
35
+
36
+ def approximate_kernel(self, T: float, N: int):
37
+ assert 1 <= N <= 5 # not keen to use large N
38
+ if N > 1 and self.H<0.49:
39
+ self.nodes, self.weights = european_rule(self.H, N, T)
40
+ else:
41
+ self.weights = np.array([1.0])
42
+ self.nodes = np.array([1e-3])
43
+
30
44
 
31
45
  def to_dict(self) -> Dict[str, Any]:
32
46
  return asdict(self)
@@ -23,6 +23,7 @@ import stochvolmodels.pricers.logsv.affine_expansion as afe
23
23
  from stochvolmodels.pricers.model_pricer import ModelPricer
24
24
  from stochvolmodels.pricers.logsv.affine_expansion import ExpansionOrder
25
25
  from stochvolmodels.pricers.logsv.vol_moments_ode import fit_model_vol_backbone_to_varswaps
26
+ from stochvolmodels.pricers.rough_logsv.split_simulation import log_spot_full_combined
26
27
 
27
28
  # data
28
29
  from stochvolmodels.data.option_chain import OptionChain
@@ -47,6 +48,7 @@ class ConstraintsType(Enum):
47
48
  class CalibrationEngine(Enum):
48
49
  ANALYTIC = 1
49
50
  MC = 2
51
+ ROUGH_MC = 3
50
52
 
51
53
 
52
54
  LOGSV_BTC_PARAMS = LogSvParams(sigma0=0.8376, theta=1.0413, kappa1=3.1844, kappa2=3.058, beta=0.1514, volvol=1.8458)
@@ -154,21 +156,30 @@ class LogSVPricer(ModelPricer):
154
156
  kappa1=params0.kappa1,
155
157
  kappa2=params0.kappa2,
156
158
  beta=pars[2],
157
- volvol=pars[3])
159
+ volvol=pars[3],
160
+ H=params0.H,
161
+ nodes=params0.nodes,
162
+ weights=params0.weights)
158
163
  elif model_calibration_type == LogsvModelCalibrationType.PARAMS5:
159
164
  fit_params = LogSvParams(sigma0=pars[0],
160
165
  theta=pars[1],
161
166
  kappa1=pars[2],
162
167
  kappa2=None,
163
168
  beta=pars[3],
164
- volvol=pars[4])
169
+ volvol=pars[4],
170
+ H=params0.H,
171
+ nodes=params0.nodes,
172
+ weights=params0.weights)
165
173
  elif model_calibration_type == LogsvModelCalibrationType.PARAMS_WITH_VARSWAP_FIT:
166
174
  fit_params = LogSvParams(sigma0=params0.sigma0,
167
175
  theta=params0.theta,
168
176
  kappa1=params0.kappa1,
169
177
  kappa2=params0.kappa2,
170
178
  beta=pars[0],
171
- volvol=pars[1])
179
+ volvol=pars[1],
180
+ H=params0.H,
181
+ nodes=params0.nodes,
182
+ weights=params0.weights)
172
183
  # set model backbone
173
184
  vol_backbone = fit_model_vol_backbone_to_varswaps(log_sv_params=fit_params,
174
185
  varswap_strikes=varswap_strikes)
@@ -180,7 +191,9 @@ class LogSVPricer(ModelPricer):
180
191
 
181
192
  if calibration_engine == CalibrationEngine.MC:
182
193
  W0s, W1s, dts = get_randoms_for_chain_valuation(ttms=option_chain.ttms, nb_path=nb_path, nb_steps_per_year=nb_steps, seed=seed)
183
-
194
+ if calibration_engine == CalibrationEngine.ROUGH_MC:
195
+ Z0, Z1, grid_ttms = get_randoms_for_rough_vol_chain_valuation(ttms=option_chain.ttms, nb_path=nb_path,
196
+ nb_steps_per_year=nb_steps, seed=seed)
184
197
  def objective(pars: np.ndarray, args: np.ndarray) -> float:
185
198
  params = parse_model_params(pars=pars)
186
199
 
@@ -207,6 +220,25 @@ class LogSVPricer(ModelPricer):
207
220
  # print(f"option_prices_ttm\n{option_prices_ttm}")
208
221
  # print(f"model_vols\n{model_vols}")
209
222
 
223
+ elif calibration_engine == CalibrationEngine.ROUGH_MC:
224
+ option_prices_ttm, option_std_ttm = rough_logsv_mc_chain_pricer_fixed_randoms(ttms=option_chain.ttms,
225
+ forwards=option_chain.forwards,
226
+ discfactors=option_chain.discfactors,
227
+ strikes_ttms=option_chain.strikes_ttms,
228
+ optiontypes_ttms=option_chain.optiontypes_ttms,
229
+ Z0=Z0,
230
+ Z1=Z1,
231
+ sigma0=params.sigma0,
232
+ theta=params.theta,
233
+ kappa1=params.kappa1,
234
+ kappa2=params.kappa2,
235
+ beta=params.beta,
236
+ orthog_vol=params.volvol,
237
+ weights=params.weights,
238
+ nodes=params.nodes,
239
+ timegrids=grid_ttms)
240
+ model_vols = option_chain.compute_model_ivols_from_chain_data(model_prices=option_prices_ttm)
241
+
210
242
  else:
211
243
  raise NotImplementedError(f"{calibration_engine}")
212
244
 
@@ -698,13 +730,14 @@ def simulate_logsv_x_vol_terminal(ttm: float,
698
730
  return x0, sigma0, qvar0
699
731
 
700
732
 
733
+
701
734
  def get_randoms_for_chain_valuation(ttms: np.ndarray,
702
735
  nb_path: int = 100000,
703
736
  nb_steps_per_year: int = 360,
704
737
  seed: int = 10
705
738
  ) -> Tuple[List[np.ndarray], List[np.ndarray], List[np.ndarray]]:
706
739
  """
707
- we need to fix random normals for suesequent evaluation using mc slices
740
+ we need to fix random normals for subsequent evaluation using mc slices
708
741
  outputs as numpy lists
709
742
  """
710
743
  #
@@ -722,6 +755,23 @@ def get_randoms_for_chain_valuation(ttms: np.ndarray,
722
755
  ttm0 = ttm
723
756
  return W0s, W1s, dts
724
757
 
758
+ def get_randoms_for_rough_vol_chain_valuation(ttms: np.ndarray,
759
+ nb_path: int = 100000,
760
+ nb_steps_per_year: int = 360,
761
+ seed: int = 10
762
+ ) -> Tuple[np.ndarray, np.ndarray, List[np.ndarray]]:
763
+ set_seed(seed)
764
+ grid_ttms = List()
765
+ nb_steps_ttms = np.zeros_like(ttms).astype(int)
766
+ for i, ttm in enumerate(ttms):
767
+ nb_steps, dt, grid_t = set_time_grid(ttm, nb_steps_per_year)
768
+ nb_steps_ttms[i] = nb_steps
769
+ grid_ttms.append(grid_t)
770
+ Z0 = np.random.normal(0, 1, size=(nb_steps_ttms[-1], nb_path))
771
+ Z1 = np.random.normal(0, 1, size=(nb_steps_ttms[-1], nb_path))
772
+
773
+ return Z0, Z1, grid_ttms
774
+
725
775
 
726
776
  @njit(cache=False, fastmath=True)
727
777
  def logsv_mc_chain_pricer_fixed_randoms(ttms: np.ndarray,
@@ -787,6 +837,68 @@ def logsv_mc_chain_pricer_fixed_randoms(ttms: np.ndarray,
787
837
 
788
838
  return option_prices_ttm, option_std_ttm
789
839
 
840
+ def rough_logsv_mc_chain_pricer_fixed_randoms(ttms: np.ndarray,
841
+ forwards: np.ndarray,
842
+ discfactors: np.ndarray,
843
+ strikes_ttms: Tuple[np.ndarray, ...],
844
+ optiontypes_ttms: Tuple[np.ndarray, ...],
845
+ Z0: np.ndarray,
846
+ Z1: np.ndarray,
847
+ sigma0: float,
848
+ theta: float,
849
+ kappa1: float,
850
+ kappa2: float,
851
+ beta: float,
852
+ orthog_vol: float,
853
+ weights: np.ndarray,
854
+ nodes: np.ndarray,
855
+ timegrids: List[np.ndarray],
856
+ variable_type: VariableType = VariableType.LOG_RETURN
857
+ ) -> Tuple[List[np.ndarray], List[np.ndarray]]:
858
+ assert weights.shape == nodes.shape and weights.ndim == 1
859
+ assert kappa2 == 0.0
860
+ N = nodes.size
861
+ v0 = sigma0 / np.sum(weights) * np.ones((N,))
862
+
863
+ # need to redenote coefficients
864
+ lamda = kappa1
865
+ theta = kappa1 * theta
866
+ volvol = np.sqrt(beta ** 2 + orthog_vol ** 2)
867
+ rho = beta / volvol
868
+
869
+ nb_path = Z0.shape[1]
870
+ v0_vec = np.repeat(v0[:, None], nb_path, axis=1)
871
+ v_init = v0_vec.copy()
872
+ log_s0 = 0.0
873
+
874
+ # outputs as numpy lists
875
+ option_prices_ttm = List()
876
+ option_std_ttm = List()
877
+ for ttm, forward, discfactor, strikes_ttm, optiontypes_ttm, timegrid in zip(ttms, forwards,
878
+ discfactors,
879
+ strikes_ttms,
880
+ optiontypes_ttms,
881
+ timegrids):
882
+ nb_steps = timegrid.size - 1
883
+ Z0_ = Z0[:nb_steps]
884
+ Z1_ = Z1[:nb_steps]
885
+ log_spot_str, vol_str, qv_str = log_spot_full_combined(nodes, weights, v0_vec, theta, lamda, log_s0, v_init,
886
+ rho, volvol, timegrid, nb_path, Z0_, Z1_)
887
+ print(f"Number of paths with negative vol: {np.sum(weights @ vol_str < 0.0)}")
888
+ print(f"Mean spot Strand: {np.mean(np.exp(log_spot_str))}")
889
+
890
+ option_prices, option_std = compute_mc_vars_payoff(x0=log_spot_str, sigma0=vol_str, qvar0=qv_str,
891
+ ttm=ttm,
892
+ forward=forward,
893
+ strikes_ttm=strikes_ttm,
894
+ optiontypes_ttm=optiontypes_ttm,
895
+ discfactor=discfactor,
896
+ variable_type=variable_type)
897
+ option_prices_ttm.append(option_prices)
898
+ option_std_ttm.append(option_std)
899
+
900
+ return option_prices_ttm, option_std_ttm
901
+
790
902
 
791
903
  class UnitTests(Enum):
792
904
  CHAIN_PRICER = 1
@@ -865,7 +977,7 @@ def run_unit_test(unit_test: UnitTests):
865
977
  fig = logsv_pricer.plot_model_ivols_vs_mc(option_chain=option_chain,
866
978
  params=LOGSV_BTC_PARAMS,
867
979
  variable_type=VariableType.Q_VAR)
868
-
980
+
869
981
  elif unit_test == UnitTests.VOL_PATHS:
870
982
  logsv_pricer = LogSVPricer()
871
983
  nb_path = 10