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.
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/PKG-INFO +1 -1
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/pyproject.toml +1 -1
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/__init__.py +4 -1
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/examples/run_lognormal_sv_pricer.py +48 -1
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/factor_hjm/rate_logsv_pricer.py +2 -2
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/hawkes_jd_pricer.py +1 -1
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/heston_pricer.py +2 -2
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/logsv_pricer.py +152 -15
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/utils/funcs.py +4 -2
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/LICENSE.txt +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/README.md +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/data/__init__.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/data/option_chain.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/data/test_option_chain.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/examples/quick_run_lognormal_sv_pricer.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/examples/run_heston.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/examples/run_heston_sv_pricer.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/examples/run_pricing_options_on_qvar.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/__init__.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/analytic/__init__.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/analytic/bachelier.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/analytic/bsm.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/analytic/tdist.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/factor_hjm/double_exp_pricer.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/factor_hjm/factor_hjm_pricer.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/factor_hjm/rate_affine_expansion.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/factor_hjm/rate_core.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/factor_hjm/rate_evaluate.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/factor_hjm/rate_factor_basis.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/factor_hjm/rate_logsv_ivols.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/factor_hjm/rate_logsv_params.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/gmm_pricer.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/logsv/__init__.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/logsv/affine_expansion.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/logsv/logsv_params.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/logsv/vol_moments_ode.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/model_pricer.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/tdist_pricer.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/tests/__init__.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/tests/bsm_mgf_pricer.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/tests/qv_pricer.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/utils/__init__.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/utils/config.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/utils/mc_payoffs.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/utils/mgf_pricer.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/utils/plots.py +0 -0
- {stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/utils/var_swap_pricer.py +0 -0
|
@@ -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
|
|
{stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/examples/run_lognormal_sv_pricer.py
RENAMED
|
@@ -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.
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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((
|
|
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
|
-
|
|
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
|
-
|
|
633
|
-
|
|
634
|
-
|
|
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(
|
|
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,
|
|
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
|
|
107
|
+
find closest element
|
|
106
108
|
https://stackoverflow.com/questions/2566412/find-nearest-value-in-numpy-array
|
|
107
109
|
"""
|
|
108
110
|
if is_sorted:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/examples/run_heston_sv_pricer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/analytic/bachelier.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/factor_hjm/rate_core.py
RENAMED
|
File without changes
|
{stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/factor_hjm/rate_evaluate.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/logsv/affine_expansion.py
RENAMED
|
File without changes
|
{stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/logsv/logsv_params.py
RENAMED
|
File without changes
|
{stochvolmodels-1.0.26 → stochvolmodels-1.0.27}/stochvolmodels/pricers/logsv/vol_moments_ode.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
|