Skip to content

Commit 8160ed6

Browse files
committed
bugs fixed
* corrected 2 term in inverse Lap transform
1 parent a6179e0 commit 8160ed6

File tree

1 file changed

+26
-19
lines changed

1 file changed

+26
-19
lines changed

pyfeng/sabr_mc.py

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ class SabrMcExactCai2017(sabr.SabrABC, sv.CondMcBsmABC):
118118
"""
119119
m_inv = 20
120120
n_inv = 35
121+
comb_coef = None
122+
nn = None
121123

122124
def set_mc_params(self, n_path=10000, m_inv=20, n_inv=35, rn_seed=None, antithetic=True):
123125
"""
@@ -138,6 +140,9 @@ def set_mc_params(self, n_path=10000, m_inv=20, n_inv=35, rn_seed=None, antithet
138140
self.antithetic = antithetic
139141
self.rn_seed = rn_seed
140142
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)
141146

142147
def vol_paths(self, tobs, mu=0):
143148
return None
@@ -173,14 +178,12 @@ def cond_laplace(self, theta, vovn, sigma_T):
173178
(Laplace transform function)
174179
"""
175180

176-
lam = theta * vovn**2
177181
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))
180185

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
184187

185188
def inv_laplace(self, u, vovn, sigma_T):
186189
'''
@@ -197,14 +200,11 @@ def inv_laplace(self, u, vovn, sigma_T):
197200
'''
198201

199202
## 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
202204
term1 = 0.5 * ss_j[0]
203205
ss_j[1::2] *= -1
204206
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:])
208208

209209
origin_L = np.exp(self.m_inv/2) / u * (-term1 + term2)
210210

@@ -223,14 +223,22 @@ def conditional_state(self, vovn):
223223
(sigma at the time of expiration, Exact integrated variance)
224224
"""
225225

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+
227229
int_var = np.zeros(self.n_path)
228230
sigma_final = self.sigma_final(vovn)
229231

230232
for i in range(self.n_path):
231233
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
234242
return sigma_final, int_var
235243

236244
def cond_spot_sigma(self, texp):
@@ -249,7 +257,6 @@ def cond_spot_sigma(self, texp):
249257
)
250258
return fwd_cond, vol_cond
251259

252-
253260
# The algorithem below is about pricing when 0<=beta<1
254261
def simu_ST(self, beta, VT, spot):
255262
'''
@@ -398,7 +405,7 @@ def theta_func(mu2, yita, s):
398405
cdf = spst.norm.cdf(z)
399406
return cdf
400407

401-
def price(self, strike, spot, texp, cp_sign=1):
408+
def price(self, strike, spot, texp, cp=1):
402409
"""
403410
over ride the price method in parent class to incorporate 0< beta < 1
404411
if beta = 1 or 0, use bsm model
@@ -407,7 +414,7 @@ def price(self, strike, spot, texp, cp_sign=1):
407414
strike: 1d array, an array of strike prices
408415
spot: float, spot prices
409416
texp: time to expiry
410-
cp_sign:
417+
cp:
411418
Returns:
412419
1d array of option prices
413420
when x < 500 and l < 500:
@@ -429,8 +436,8 @@ def price(self, strike, spot, texp, cp_sign=1):
429436
strike = np.array([strike])
430437
if self.beta == 1:
431438
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)
434441
price = np.mean(price_grid, axis=0)
435442
return price[0] if price.size == 1 else price
436443
else: # 0 < beta < 1

0 commit comments

Comments
 (0)