stochvolmodels 1.0.26__tar.gz → 1.0.27__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 (47) hide show
  1. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/PKG-INFO +1 -1
  2. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/pyproject.toml +1 -1
  3. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/__init__.py +4 -1
  4. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/examples/run_lognormal_sv_pricer.py +48 -1
  5. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/factor_hjm/rate_logsv_pricer.py +2 -2
  6. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/hawkes_jd_pricer.py +1 -1
  7. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/heston_pricer.py +2 -2
  8. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/logsv_pricer.py +152 -15
  9. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/utils/funcs.py +4 -2
  10. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/LICENSE.txt +0 -0
  11. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/README.md +0 -0
  12. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/data/__init__.py +0 -0
  13. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/data/option_chain.py +0 -0
  14. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/data/test_option_chain.py +0 -0
  15. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/examples/quick_run_lognormal_sv_pricer.py +0 -0
  16. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/examples/run_heston.py +0 -0
  17. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/examples/run_heston_sv_pricer.py +0 -0
  18. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/examples/run_pricing_options_on_qvar.py +0 -0
  19. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/__init__.py +0 -0
  20. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/analytic/__init__.py +0 -0
  21. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/analytic/bachelier.py +0 -0
  22. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/analytic/bsm.py +0 -0
  23. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/analytic/tdist.py +0 -0
  24. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/factor_hjm/double_exp_pricer.py +0 -0
  25. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/factor_hjm/factor_hjm_pricer.py +0 -0
  26. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/factor_hjm/rate_affine_expansion.py +0 -0
  27. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/factor_hjm/rate_core.py +0 -0
  28. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/factor_hjm/rate_evaluate.py +0 -0
  29. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/factor_hjm/rate_factor_basis.py +0 -0
  30. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/factor_hjm/rate_logsv_ivols.py +0 -0
  31. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/factor_hjm/rate_logsv_params.py +0 -0
  32. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/gmm_pricer.py +0 -0
  33. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/logsv/__init__.py +0 -0
  34. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/logsv/affine_expansion.py +0 -0
  35. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/logsv/logsv_params.py +0 -0
  36. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/logsv/vol_moments_ode.py +0 -0
  37. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/model_pricer.py +0 -0
  38. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/tdist_pricer.py +0 -0
  39. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/tests/__init__.py +0 -0
  40. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/tests/bsm_mgf_pricer.py +0 -0
  41. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/tests/qv_pricer.py +0 -0
  42. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/utils/__init__.py +0 -0
  43. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/utils/config.py +0 -0
  44. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/utils/mc_payoffs.py +0 -0
  45. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/utils/mgf_pricer.py +0 -0
  46. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/utils/plots.py +0 -0
  47. {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/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.26
3
+ Version: 1.0.27
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.26"
3
+ version = "1.0.27"
4
4
  description = "Implementation of stochastic volatility models for option pricing"
5
5
  license = "LICENSE.txt"
6
6
  authors = ["Artur Sepp <artursepp@gmail.com>"]
@@ -106,7 +106,10 @@ from stochvolmodels.pricers.logsv_pricer import (
106
106
  LOGSV_BTC_PARAMS,
107
107
  LogSVPricer,
108
108
  LogsvModelCalibrationType,
109
- ConstraintsType
109
+ ConstraintsType,
110
+ CalibrationEngine,
111
+ get_randoms_for_chain_valuation,
112
+ logsv_mc_chain_pricer_fixed_randoms
110
113
  )
111
114
  from stochvolmodels.pricers.logsv.logsv_params import LogSvParams
112
115
 
@@ -17,6 +17,8 @@ class UnitTests(Enum):
17
17
  COMPARE_MODEL_VOLS_TO_MC = 4
18
18
  PLOT_FIT_TO_BITCOIN_OPTION_CHAIN = 5
19
19
  CALIBRATE_MODEL_TO_BTC_OPTIONS = 6
20
+ MC_WITH_FIXED_RANDOMS = 7
21
+ CALIBRATE_MODEL_TO_BTC_OPTIONS_WITH_MC = 8
20
22
 
21
23
 
22
24
  def run_unit_test(unit_test: UnitTests):
@@ -105,12 +107,57 @@ def run_unit_test(unit_test: UnitTests):
105
107
  print(btc_calibrated_params)
106
108
  logsv_pricer.plot_model_ivols_vs_bid_ask(option_chain=btc_option_chain,
107
109
  params=btc_calibrated_params)
110
+
111
+ elif unit_test == UnitTests.MC_WITH_FIXED_RANDOMS:
112
+ btc_option_chain = sv.get_btc_test_chain_data()
113
+ W0s, W1s, dts = sv.get_randoms_for_chain_valuation(ttms=btc_option_chain.ttms,
114
+ nb_path=10000,
115
+ nb_steps_per_year=360,
116
+ seed=10)
117
+ print(dts)
118
+ params0 = LogSvParams(sigma0=0.8, theta=1.0, kappa1=2.21, kappa2=2.18, beta=0.15, volvol=2.0)
119
+ vol_backbone_etas = params.get_vol_backbone_etas(ttms=btc_option_chain.ttms)
120
+ args = dict(ttms=btc_option_chain.ttms,
121
+ forwards=btc_option_chain.forwards,
122
+ discfactors=btc_option_chain.discfactors,
123
+ strikes_ttms=btc_option_chain.strikes_ttms,
124
+ optiontypes_ttms=btc_option_chain.optiontypes_ttms,
125
+ W0s=W0s,
126
+ W1s=W1s,
127
+ dts=dts,
128
+ v0=params0.sigma0,
129
+ theta=params0.theta,
130
+ kappa1=params0.kappa1,
131
+ kappa2=params0.kappa2,
132
+ beta=params0.beta,
133
+ volvol=params0.volvol,
134
+ vol_backbone_etas=vol_backbone_etas)
135
+ option_prices_ttm, option_std_ttm = sv.logsv_mc_chain_pricer_fixed_randoms(**args)
136
+ print(option_prices_ttm)
137
+
138
+ option_prices_ttm, option_std_ttm = sv.logsv_mc_chain_pricer_fixed_randoms(**args)
139
+ print(option_prices_ttm)
140
+
141
+ elif unit_test == UnitTests.CALIBRATE_MODEL_TO_BTC_OPTIONS_WITH_MC:
142
+ 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)
144
+ btc_calibrated_params = logsv_pricer.calibrate_model_params_to_chain(option_chain=btc_option_chain,
145
+ params0=params0,
146
+ model_calibration_type=LogsvModelCalibrationType.PARAMS4,
147
+ constraints_type=sv.ConstraintsType.INVERSE_MARTINGALE,
148
+ calibration_engine=sv.CalibrationEngine.MC,
149
+ nb_path=100000,
150
+ seed=7)
151
+ print(btc_calibrated_params)
152
+ logsv_pricer.plot_model_ivols_vs_bid_ask(option_chain=btc_option_chain,
153
+ params=btc_calibrated_params)
154
+
108
155
  plt.show()
109
156
 
110
157
 
111
158
  if __name__ == '__main__':
112
159
 
113
- unit_test = UnitTests.COMPUTE_MODEL_PRICES
160
+ unit_test = UnitTests.CALIBRATE_MODEL_TO_BTC_OPTIONS_WITH_MC
114
161
 
115
162
  is_run_all_tests = False
116
163
  if is_run_all_tests:
@@ -877,7 +877,7 @@ def simulate_logsv_MF(ttms: np.ndarray,
877
877
  if seed is None:
878
878
  seed = 16
879
879
  np.random.seed(seed) # fix seed
880
- nb_steps, dt, grid_t = set_time_grid(ttm=ttm, nb_steps=int(np.ceil(year_days*ttm)))
880
+ nb_steps, dt, grid_t = set_time_grid(ttm=ttm, nb_steps_per_year=360)
881
881
  if W is None:
882
882
  W0 = np.sqrt(dt) * np.random.normal(0, 1, size=(nb_steps, nb_path, basis.get_nb_factors())) # TODO: undo
883
883
  W1 = np.sqrt(dt) * np.random.normal(0, 1, size=(nb_steps, nb_path)) # TODO: undo
@@ -1017,7 +1017,7 @@ def simulate_logsv_futures_MF2(params: MultiFactRateLogSvParams,
1017
1017
  if seed is None:
1018
1018
  seed = 16
1019
1019
  np.random.seed(seed) # fix seed
1020
- nb_steps, dt, grid_t = set_time_grid(ttm=ttm, year_days=720)
1020
+ nb_steps, dt, grid_t = set_time_grid(ttm=ttm, nb_steps_per_year=720)
1021
1021
  if W is None:
1022
1022
  W0 = np.sqrt(dt) * np.random.normal(0, 1, size=(nb_steps, nb_path, basis.get_nb_factors())) # TODO: undo
1023
1023
  W1 = np.sqrt(dt) * np.random.normal(0, 1, size=(nb_steps, nb_path)) # TODO: undo
@@ -696,7 +696,7 @@ def simulate_hawkesjd_terminal(ttm: float,
696
696
  assert lambda_m0.shape[0] == nb_path
697
697
 
698
698
  # vars
699
- nb_steps, dt, grid_t = set_time_grid(ttm=ttm, nb_steps=int(5*360)) # need small dt step for large intensities
699
+ nb_steps, dt, grid_t = set_time_grid(ttm=ttm, nb_steps_per_year=5*360) # need small dt step for large intensities
700
700
  W0 = np.sqrt(dt) * np.random.normal(0, 1, size=(nb_steps, nb_path))
701
701
  U_P = -np.log(np.random.uniform(low=1e-16, high=1.0, size=(nb_steps, nb_path)))/dt
702
702
  U_M = -np.log(np.random.uniform(low=1e-16, high=1.0, size=(nb_steps, nb_path)))/dt
@@ -298,7 +298,7 @@ def simulate_heston_x_vol_terminal(ttm: float,
298
298
  rho: float,
299
299
  volvol: float,
300
300
  nb_path: int = 100000,
301
- nb_steps: int = 360
301
+ nb_steps_per_year: int = 360
302
302
  ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
303
303
 
304
304
  if x0.shape[0] == 1: # initial value
@@ -316,7 +316,7 @@ def simulate_heston_x_vol_terminal(ttm: float,
316
316
  else:
317
317
  assert qvar0.shape[0] == nb_path
318
318
 
319
- nb_steps, dt, grid_t = set_time_grid(ttm=ttm, nb_steps=nb_steps)
319
+ nb_steps, dt, grid_t = set_time_grid(ttm=ttm, nb_steps_per_year=nb_steps_per_year)
320
320
  w0 = np.sqrt(dt) * np.random.normal(0, 1, size=(nb_steps, nb_path))
321
321
  w1 = np.sqrt(dt) * np.random.normal(0, 1, size=(nb_steps, nb_path))
322
322
 
@@ -15,7 +15,7 @@ from enum import Enum
15
15
  from stochvolmodels.utils.config import VariableType
16
16
  import stochvolmodels.utils.mgf_pricer as mgfp
17
17
  from stochvolmodels.utils.mc_payoffs import compute_mc_vars_payoff
18
- from stochvolmodels.utils.funcs import to_flat_np_array, set_time_grid, timer, compute_histogram_data
18
+ from stochvolmodels.utils.funcs import to_flat_np_array, set_time_grid, timer, compute_histogram_data, set_seed
19
19
 
20
20
  # stochvolmodels pricers
21
21
  from stochvolmodels.pricers.logsv.logsv_params import LogSvParams
@@ -44,6 +44,11 @@ class ConstraintsType(Enum):
44
44
  INVERSE_MARTINGALE_MOMENT4 = 5 # kappa_2 >= 2.0*beta
45
45
 
46
46
 
47
+ class CalibrationEngine(Enum):
48
+ ANALYTIC = 1
49
+ MC = 2
50
+
51
+
47
52
  LOGSV_BTC_PARAMS = LogSvParams(sigma0=0.8376, theta=1.0413, kappa1=3.1844, kappa2=3.058, beta=0.1514, volvol=1.8458)
48
53
 
49
54
 
@@ -95,7 +100,7 @@ class LogSVPricer(ModelPricer):
95
100
  is_spot_measure=is_spot_measure,
96
101
  variable_type=variable_type,
97
102
  nb_path=nb_path,
98
- nb_steps=nb_steps or int(360*np.max(option_chain.ttms))+1)
103
+ nb_steps_per_year=nb_steps or int(360 * np.max(option_chain.ttms)) + 1)
99
104
 
100
105
  def set_vol_scaler(self, option_chain: OptionChain) -> float:
101
106
  """
@@ -115,6 +120,10 @@ class LogSVPricer(ModelPricer):
115
120
  is_unit_ttm_vega: bool = False,
116
121
  model_calibration_type: LogsvModelCalibrationType = LogsvModelCalibrationType.PARAMS5,
117
122
  constraints_type: ConstraintsType = ConstraintsType.UNCONSTRAINT,
123
+ calibration_engine: CalibrationEngine = CalibrationEngine.ANALYTIC,
124
+ nb_path: int = 100000,
125
+ nb_steps: int = 360,
126
+ seed: int = 10,
118
127
  **kwargs
119
128
  ) -> LogSvParams:
120
129
  """
@@ -169,9 +178,38 @@ class LogSVPricer(ModelPricer):
169
178
  raise NotImplementedError(f"{model_calibration_type}")
170
179
  return fit_params
171
180
 
181
+ if calibration_engine == CalibrationEngine.MC:
182
+ 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
+
172
184
  def objective(pars: np.ndarray, args: np.ndarray) -> float:
173
185
  params = parse_model_params(pars=pars)
174
- model_vols = self.compute_model_ivols_for_chain(option_chain=option_chain, params=params, vol_scaler=vol_scaler)
186
+
187
+ if calibration_engine == CalibrationEngine.ANALYTIC:
188
+ model_vols = self.compute_model_ivols_for_chain(option_chain=option_chain, params=params, vol_scaler=vol_scaler)
189
+
190
+ elif calibration_engine == CalibrationEngine.MC:
191
+ option_prices_ttm, option_std_ttm = logsv_mc_chain_pricer_fixed_randoms(ttms=option_chain.ttms,
192
+ forwards=option_chain.forwards,
193
+ discfactors=option_chain.discfactors,
194
+ strikes_ttms=option_chain.strikes_ttms,
195
+ optiontypes_ttms=option_chain.optiontypes_ttms,
196
+ W0s=W0s,
197
+ W1s=W1s,
198
+ dts=dts,
199
+ v0=params.sigma0,
200
+ theta=params.theta,
201
+ kappa1=params.kappa1,
202
+ kappa2=params.kappa2,
203
+ beta=params.beta,
204
+ volvol=params.volvol,
205
+ vol_backbone_etas=params.get_vol_backbone_etas(ttms=option_chain.ttms))
206
+ model_vols = option_chain.compute_model_ivols_from_chain_data(model_prices=option_prices_ttm)
207
+ print(f"option_prices_ttm\n{option_prices_ttm}")
208
+ print(f"model_vols\n{model_vols}")
209
+
210
+ else:
211
+ raise NotImplementedError(f"{calibration_engine}")
212
+
175
213
  resid = np.nansum(weights * np.square(to_flat_np_array(model_vols) - market_vols))
176
214
  return resid
177
215
 
@@ -278,7 +316,7 @@ class LogSVPricer(ModelPricer):
278
316
  volvol=params.volvol,
279
317
  nb_path=nb_path,
280
318
  is_spot_measure=is_spot_measure,
281
- nb_steps=nb_steps,
319
+ nb_steps_per_year=nb_steps,
282
320
  brownians=brownians,
283
321
  **kwargs)
284
322
  return sigma_t, grid_t
@@ -512,7 +550,7 @@ def logsv_mc_chain_pricer(ttms: np.ndarray,
512
550
  vol_backbone_etas: np.ndarray,
513
551
  is_spot_measure: bool = True,
514
552
  nb_path: int = 100000,
515
- nb_steps: int = 360,
553
+ nb_steps_per_year: int = 360,
516
554
  variable_type: VariableType = VariableType.LOG_RETURN
517
555
  ) -> Tuple[List[np.ndarray], List[np.ndarray]]:
518
556
  # starting values
@@ -538,7 +576,7 @@ def logsv_mc_chain_pricer(ttms: np.ndarray,
538
576
  volvol=volvol,
539
577
  vol_backbone_eta=vol_backbone_eta,
540
578
  nb_path=nb_path,
541
- nb_steps=nb_steps,
579
+ nb_steps_per_year=nb_steps_per_year,
542
580
  is_spot_measure=is_spot_measure)
543
581
  ttm0 = ttm
544
582
  option_prices, option_std = compute_mc_vars_payoff(x0=x0, sigma0=sigma0, qvar0=qvar0,
@@ -564,7 +602,7 @@ def simulate_vol_paths(ttm: float,
564
602
  volvol: float,
565
603
  is_spot_measure: bool = True,
566
604
  nb_path: int = 100000,
567
- nb_steps: int = 360,
605
+ nb_steps_per_year: int = 360,
568
606
  brownians: np.ndarray = None,
569
607
  **kwargs
570
608
  ) -> Tuple[np.ndarray, np.ndarray]:
@@ -573,7 +611,7 @@ def simulate_vol_paths(ttm: float,
573
611
  """
574
612
  sigma0 = v0 * np.ones(nb_path)
575
613
 
576
- nb_steps, dt, grid_t = set_time_grid(ttm=ttm, nb_steps=nb_steps)
614
+ nb_steps, dt, grid_t = set_time_grid(ttm=ttm, nb_steps_per_year=nb_steps_per_year)
577
615
 
578
616
  if brownians is None:
579
617
  brownians = np.sqrt(dt) * np.random.normal(0, 1, size=(nb_steps, nb_path))
@@ -586,7 +624,7 @@ def simulate_vol_paths(ttm: float,
586
624
  vartheta2 = beta*beta + volvol*volvol
587
625
  vartheta = np.sqrt(vartheta2)
588
626
  vol_var = np.log(sigma0)
589
- sigma_t = np.zeros((nb_steps+1, nb_path)) # sigma grid will increase to include the sigma_0 at t0 = 0
627
+ sigma_t = np.zeros((nb_steps_per_year + 1, nb_path)) # sigma grid will increase to include the sigma_0 at t0 = 0
590
628
  sigma_t[0, :] = sigma0 # keep first value
591
629
  for t_, w1_ in enumerate(brownians):
592
630
  vol_var = vol_var + ((kappa1 * theta / sigma0 - kappa1) + kappa2*(theta-sigma0) + adj*sigma0 - 0.5*vartheta2) * dt + vartheta*w1_
@@ -609,7 +647,10 @@ def simulate_logsv_x_vol_terminal(ttm: float,
609
647
  vol_backbone_eta: float = 1.0,
610
648
  is_spot_measure: bool = True,
611
649
  nb_path: int = 100000,
612
- nb_steps: int = 360
650
+ nb_steps_per_year: int = 360,
651
+ W0: Optional[np.ndarray] = None,
652
+ W1: Optional[np.ndarray] = None,
653
+ dt: Optional[float] = None
613
654
  ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
614
655
  """
615
656
  mc simulator for terminal values of log-return, vol sigma0, and qvar for log sv model
@@ -628,10 +669,16 @@ def simulate_logsv_x_vol_terminal(ttm: float,
628
669
  sigma0 = sigma0 * np.ones(nb_path)
629
670
  else:
630
671
  assert sigma0.shape[0] == nb_path
631
-
632
- nb_steps, dt, grid_t = set_time_grid(ttm=ttm, nb_steps=nb_steps)
633
- W0 = np.sqrt(dt) * np.random.normal(0, 1, size=(nb_steps, nb_path))
634
- W1 = np.sqrt(dt) * np.random.normal(0, 1, size=(nb_steps, nb_path))
672
+ if W0 is None and W1 is None:
673
+ nb_steps1, dt, grid_t = set_time_grid(ttm=ttm, nb_steps_per_year=nb_steps_per_year)
674
+ print(f"nb_steps1={nb_steps1}, dt={dt}")
675
+ sdt = np.sqrt(dt)
676
+ W0_ = sdt * np.random.normal(0, 1, size=(nb_steps1, nb_path))
677
+ W1_ = sdt * np.random.normal(0, 1, size=(nb_steps1, nb_path))
678
+ else:
679
+ sdt = np.sqrt(dt)
680
+ W0_ = sdt * W0
681
+ W1_ = sdt * W1
635
682
 
636
683
  if is_spot_measure:
637
684
  alpha, adj = -1.0, 0.0
@@ -641,7 +688,7 @@ def simulate_logsv_x_vol_terminal(ttm: float,
641
688
  vartheta2 = beta*beta + volvol*volvol
642
689
  vol_backbone_eta2 = vol_backbone_eta * vol_backbone_eta
643
690
  vol_var = np.log(sigma0)
644
- for t_, (w0, w1) in enumerate(zip(W0, W1)):
691
+ for t_, (w0, w1) in enumerate(zip(W0_, W1_)):
645
692
  sigma0_2dt = vol_backbone_eta2 * sigma0 * sigma0 * dt
646
693
  x0 = x0 + alpha * 0.5 * sigma0_2dt + vol_backbone_eta * sigma0 * w0
647
694
  vol_var = vol_var + ((kappa1 * theta / sigma0 - kappa1) + kappa2*(theta-sigma0) + adj*sigma0 - 0.5*vartheta2) * dt + beta*w0+volvol*w1
@@ -651,6 +698,96 @@ def simulate_logsv_x_vol_terminal(ttm: float,
651
698
  return x0, sigma0, qvar0
652
699
 
653
700
 
701
+ def get_randoms_for_chain_valuation(ttms: np.ndarray,
702
+ nb_path: int = 100000,
703
+ nb_steps_per_year: int = 360,
704
+ seed: int = 10
705
+ ) -> Tuple[List[np.ndarray], List[np.ndarray], List[np.ndarray]]:
706
+ """
707
+ we need to fix random normals for suesequent evaluation using mc slices
708
+ outputs as numpy lists
709
+ """
710
+ #
711
+ set_seed(seed)
712
+ W0s = List()
713
+ W1s = List()
714
+ dts = List()
715
+ ttm0 = 0.0
716
+ for ttm in ttms:
717
+ # qqq
718
+ nb_steps_, dt, grid_t = set_time_grid(ttm=ttm - ttm0, nb_steps_per_year=nb_steps_per_year)
719
+ W0s.append(np.random.normal(0, 1, size=(nb_steps_, nb_path)))
720
+ W1s.append(np.random.normal(0, 1, size=(nb_steps_, nb_path)))
721
+ dts.append(dt)
722
+ ttm0 = ttm
723
+ return W0s, W1s, dts
724
+
725
+
726
+ @njit(cache=False, fastmath=True)
727
+ def logsv_mc_chain_pricer_fixed_randoms(ttms: np.ndarray,
728
+ forwards: np.ndarray,
729
+ discfactors: np.ndarray,
730
+ strikes_ttms: Tuple[np.ndarray,...],
731
+ optiontypes_ttms: Tuple[np.ndarray, ...],
732
+ W0s: Tuple[np.ndarray, ...],
733
+ W1s: Tuple[np.ndarray, ...],
734
+ dts: Tuple[np.ndarray, ...],
735
+ v0: float,
736
+ theta: float,
737
+ kappa1: float,
738
+ kappa2: float,
739
+ beta: float,
740
+ volvol: float,
741
+ vol_backbone_etas: np.ndarray,
742
+ is_spot_measure: bool = True,
743
+ variable_type: VariableType = VariableType.LOG_RETURN
744
+ ) -> Tuple[List[np.ndarray], List[np.ndarray]]:
745
+ """
746
+ chain valuation using fixed randoms
747
+ """
748
+ # starting values
749
+ nb_path = W0s[0].shape[1]
750
+ x0 = np.zeros(nb_path)
751
+ qvar0 = np.zeros(nb_path)
752
+ sigma0 = v0*np.ones(nb_path)
753
+ ttm0 = 0.0
754
+
755
+ # outputs as numpy lists
756
+ option_prices_ttm = List()
757
+ option_std_ttm = List()
758
+ for ttm, forward, discfactor, strikes_ttm, optiontypes_ttm, vol_backbone_eta, W0, W1, dt in zip(ttms, forwards, discfactors,
759
+ strikes_ttms, optiontypes_ttms,
760
+ vol_backbone_etas,
761
+ W0s, W1s, dts):
762
+ x0, sigma0, qvar0 = simulate_logsv_x_vol_terminal(ttm=ttm - ttm0,
763
+ x0=x0,
764
+ sigma0=sigma0,
765
+ qvar0=qvar0,
766
+ theta=theta,
767
+ kappa1=kappa1,
768
+ kappa2=kappa2,
769
+ beta=beta,
770
+ volvol=volvol,
771
+ vol_backbone_eta=vol_backbone_eta,
772
+ nb_path=nb_path,
773
+ dt=dt,
774
+ is_spot_measure=is_spot_measure,
775
+ W0=W0,
776
+ W1=W1)
777
+ ttm0 = ttm
778
+ option_prices, option_std = compute_mc_vars_payoff(x0=x0, sigma0=sigma0, qvar0=qvar0,
779
+ ttm=ttm,
780
+ forward=forward,
781
+ strikes_ttm=strikes_ttm,
782
+ optiontypes_ttm=optiontypes_ttm,
783
+ discfactor=discfactor,
784
+ variable_type=variable_type)
785
+ option_prices_ttm.append(option_prices)
786
+ option_std_ttm.append(option_std)
787
+
788
+ return option_prices_ttm, option_std_ttm
789
+
790
+
654
791
  class UnitTests(Enum):
655
792
  CHAIN_PRICER = 1
656
793
  SLICE_PRICER = 2
@@ -15,11 +15,13 @@ def to_flat_np_array(input_list: List[np.ndarray]) -> np.ndarray:
15
15
 
16
16
 
17
17
  @njit(cache=False, fastmath=False)
18
- def set_time_grid(ttm: float, nb_steps: int = 360) -> Tuple[int, float, np.ndarray]:
18
+ def set_time_grid(ttm: float, nb_steps_per_year: int = 360) -> Tuple[int, float, np.ndarray]:
19
19
  """
20
20
  set daily steps
21
21
  """
22
+ nb_steps = int(ttm * nb_steps_per_year) + 1
22
23
  grid_t = np.linspace(0.0, ttm, nb_steps + 1)
24
+ # dt = ttm / nb_steps
23
25
  dt = grid_t[1] - grid_t[0]
24
26
  return nb_steps, dt, grid_t
25
27
 
@@ -102,7 +104,7 @@ def find_nearest(a: np.ndarray,
102
104
  is_equal_or_largest: bool = False
103
105
  ) -> float:
104
106
  """
105
- find closes element
107
+ find closest element
106
108
  https://stackoverflow.com/questions/2566412/find-nearest-value-in-numpy-array
107
109
  """
108
110
  if is_sorted: