@@ -118,6 +118,8 @@ class SabrMcExactCai2017(sabr.SabrABC, sv.CondMcBsmABC):
118
118
"""
119
119
m_inv = 20
120
120
n_inv = 35
121
+ comb_coef = None
122
+ nn = None
121
123
122
124
def set_mc_params (self , n_path = 10000 , m_inv = 20 , n_inv = 35 , rn_seed = None , antithetic = True ):
123
125
"""
@@ -138,6 +140,9 @@ def set_mc_params(self, n_path=10000, m_inv=20, n_inv=35, rn_seed=None, antithet
138
140
self .antithetic = antithetic
139
141
self .rn_seed = rn_seed
140
142
self .rng = np .random .default_rng (rn_seed )
143
+ self .comb_coef = spsp .comb (self .m_inv , np .arange (0 , self .m_inv + 0.1 )) * np .power (0.5 , self .m_inv )
144
+ assert abs (self .comb_coef .sum ()- 1 ) < 1e-8
145
+ self .nn = np .arange (0 , self .m_inv + self .n_inv + 0.1 )
141
146
142
147
def vol_paths (self , tobs , mu = 0 ):
143
148
return None
@@ -173,14 +178,12 @@ def cond_laplace(self, theta, vovn, sigma_T):
173
178
(Laplace transform function)
174
179
"""
175
180
176
- lam = theta * vovn ** 2
177
181
x = np .log (sigma_T )
178
- z = lam / sigma_T + 0.5 * (sigma_T + 1 / sigma_T )
179
- phi_value = np .log (z + np .sqrt (z ** 2 - 1 ))
182
+ lam = theta * vovn ** 2
183
+ z = 0.5 * sigma_T + (0.5 + lam )/ sigma_T
184
+ phi = np .log (z + np .sqrt (z ** 2 - 1 ))
180
185
181
- numerator = phi_value ** 2 - x ** 2
182
- denominator = 2 * vovn ** 2
183
- return 1 / theta * np .exp (- numerator / denominator )
186
+ return np .exp ((x ** 2 - phi ** 2 ) / (2 * vovn ** 2 )) / theta
184
187
185
188
def inv_laplace (self , u , vovn , sigma_T ):
186
189
'''
@@ -197,14 +200,11 @@ def inv_laplace(self, u, vovn, sigma_T):
197
200
'''
198
201
199
202
## index from 0 to m + n
200
- nn = np .arange (0 , self .m_inv + self .n_inv + 0.1 )
201
- ss_j = self .cond_laplace ((self .m_inv - 2j * np .pi * nn ) / u , vovn , sigma_T ).real
203
+ ss_j = self .cond_laplace ((self .m_inv - 2j * np .pi * self .nn ) / (2 * u ), vovn , sigma_T ).real
202
204
term1 = 0.5 * ss_j [0 ]
203
205
ss_j [1 ::2 ] *= - 1
204
206
np .cumsum (ss_j , out = ss_j )
205
-
206
- comb_coef = spsp .comb (self .m_inv , np .arange (0 , self .m_inv + 0.1 )) * np .power (0.5 , self .m_inv )
207
- term2 = np .sum (comb_coef * ss_j [self .n_inv :])
207
+ term2 = np .sum (self .comb_coef * ss_j [self .n_inv :])
208
208
209
209
origin_L = np .exp (self .m_inv / 2 ) / u * (- term1 + term2 )
210
210
@@ -223,14 +223,22 @@ def conditional_state(self, vovn):
223
223
(sigma at the time of expiration, Exact integrated variance)
224
224
"""
225
225
226
- u_rn = self .rng .uniform (size = self .n_path )
226
+ u_rn = self .rng .uniform (size = self .n_path // 2 )
227
+ u_rn = np .hstack ([u_rn , 1 - u_rn ])
228
+
227
229
int_var = np .zeros (self .n_path )
228
230
sigma_final = self .sigma_final (vovn )
229
231
230
232
for i in range (self .n_path ):
231
233
obj_func = lambda x : self .inv_laplace (x , vovn , sigma_final [i ]) - u_rn [i ]
232
- sol = spop .root (obj_func , 1 / vovn ** 3 )
233
- int_var [i ] = 1 / sol .x
234
+
235
+ if sigma_final [i ] > 1 :
236
+ x0 = 1 / sigma_final [i ]
237
+ else :
238
+ x0 = 2 / (1 + sigma_final [i ])
239
+
240
+ sol = spop .brentq (obj_func , 0.000001 , 100 )
241
+ int_var [i ] = 1 / sol
234
242
return sigma_final , int_var
235
243
236
244
def cond_spot_sigma (self , texp ):
@@ -249,7 +257,6 @@ def cond_spot_sigma(self, texp):
249
257
)
250
258
return fwd_cond , vol_cond
251
259
252
-
253
260
# The algorithem below is about pricing when 0<=beta<1
254
261
def simu_ST (self , beta , VT , spot ):
255
262
'''
@@ -398,7 +405,7 @@ def theta_func(mu2, yita, s):
398
405
cdf = spst .norm .cdf (z )
399
406
return cdf
400
407
401
- def price (self , strike , spot , texp , cp_sign = 1 ):
408
+ def price (self , strike , spot , texp , cp = 1 ):
402
409
"""
403
410
over ride the price method in parent class to incorporate 0< beta < 1
404
411
if beta = 1 or 0, use bsm model
@@ -407,7 +414,7 @@ def price(self, strike, spot, texp, cp_sign=1):
407
414
strike: 1d array, an array of strike prices
408
415
spot: float, spot prices
409
416
texp: time to expiry
410
- cp_sign :
417
+ cp :
411
418
Returns:
412
419
1d array of option prices
413
420
when x < 500 and l < 500:
@@ -429,8 +436,8 @@ def price(self, strike, spot, texp, cp_sign=1):
429
436
strike = np .array ([strike ])
430
437
if self .beta == 1 :
431
438
fwd_cond , vol_cond = self .cond_spot_sigma (texp )
432
- base_model = self .base_model (vol_cond [:, None ])
433
- price_grid = base_model .price (strike , spot * fwd_cond [:, None ], texp , cp_sign )
439
+ base_model = self .base_model (self . sigma * vol_cond [:, None ])
440
+ price_grid = base_model .price (strike , spot * fwd_cond [:, None ], texp , cp )
434
441
price = np .mean (price_grid , axis = 0 )
435
442
return price [0 ] if price .size == 1 else price
436
443
else : # 0 < beta < 1
0 commit comments