@@ -116,6 +116,28 @@ class SabrMcExactCai2017(sabr.SabrABC, sv.CondMcBsmABC):
116
116
References:
117
117
- Cai N, Song Y, Chen N (2017) Exact Simulation of the SABR Model. Oper Res 65:931–951. https://doi.org/10.1287/opre.2017.1617
118
118
"""
119
+ m_inv = 20
120
+ n_inv = 35
121
+
122
+ def set_mc_params (self , n_path = 10000 , m_inv = 20 , n_inv = 35 , rn_seed = None , antithetic = True ):
123
+ """
124
+ Set MC parameters
125
+
126
+ Args:
127
+ n_path: number of paths
128
+ m_inv: parameter M used in Laplace inversion
129
+ n_inv: parameter n used in Laplace inversion
130
+ rn_seed: random number seed
131
+ antithetic: antithetic
132
+ """
133
+ self .n_path = int (n_path )
134
+ self .m_inv = m_inv
135
+ self .n_inv = n_inv
136
+ self .dt = None
137
+ self .rn_seed = rn_seed
138
+ self .antithetic = antithetic
139
+ self .rn_seed = rn_seed
140
+ self .rng = np .random .default_rng (rn_seed )
119
141
120
142
def vol_paths (self , tobs , mu = 0 ):
121
143
return None
@@ -131,7 +153,9 @@ def sigma_final(self, vovn):
131
153
-------
132
154
vol at maturity
133
155
"""
134
- zz = self .rng .normal (size = self .n_path )
156
+ zz = self .rng .normal (size = self .n_path // 2 )
157
+ zz = np .hstack ([zz , - zz ])
158
+
135
159
sigma_T = np .exp (vovn * (zz - vovn / 2 ))
136
160
return sigma_T
137
161
@@ -158,31 +182,31 @@ def cond_laplace(self, theta, vovn, sigma_T):
158
182
denominator = 2 * vovn ** 2
159
183
return 1 / theta * np .exp (- numerator / denominator )
160
184
161
- def inv_laplace (self , texp , sigma_T , u , m = 20 , n = 35 ):
185
+ def inv_laplace (self , u , vovn , sigma_T ):
162
186
'''
163
187
Eq. (16) in the article
164
188
Return the original function from transform function
165
189
Parameters
166
190
----------
167
- texp: time to expiration
191
+ u: variable
192
+ vovn: vov * sqrt(texp)
168
193
sigma_T: simulated sigma at time of expiration
169
- u: the u of original function
170
194
Returns
171
195
-------
172
196
original functions with parameter u
173
197
'''
174
198
175
- sum_term = np .zeros (m + 1 )
176
- comb_vec = np .frompyfunc (spsp .comb , 2 , 1 )
177
- comb_term = comb_vec (m , np .arange (0 , m + 1 )).astype (int )
178
- for i in range (0 , m + 1 ):
179
- sum_term [i ] = np .sum (
180
- (- 1 ) ** np .arange (0 , n + i + 1 ) * self .cond_laplace ((m - 2j * np .pi * np .arange (0 , n + i + 1 )) / u ,
181
- texp , sigma_T ).real )
182
- Euler_term = np .sum (comb_term * sum_term * 2 ** (- m ))
199
+ ## 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
202
+ term1 = 0.5 * ss_j [0 ]
203
+ ss_j [1 ::2 ] *= - 1
204
+ 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 :])
183
208
184
- origin_L = 1 / (2 * u ) * self .cond_laplace (m / (2 * u ), texp , sigma_T ).real \
185
- * np .exp (m / 2 ) + Euler_term / u * np .exp (m / 2 ) - np .exp (- m )
209
+ origin_L = np .exp (self .m_inv / 2 ) / u * (- term1 + term2 )
186
210
187
211
return origin_L
188
212
@@ -199,13 +223,12 @@ def conditional_state(self, vovn):
199
223
(sigma at the time of expiration, Exact integrated variance)
200
224
"""
201
225
202
- _u = self .rng .uniform (size = self .n_path )
226
+ u_rn = self .rng .uniform (size = self .n_path )
203
227
int_var = np .zeros (self .n_path )
204
228
sigma_final = self .sigma_final (vovn )
205
229
206
230
for i in range (self .n_path ):
207
- Lh_func = partial (self .inv_laplace , vovn , sigma_final [i ])
208
- obj_func = lambda x : Lh_func (x ) - _u [i ]
231
+ obj_func = lambda x : self .inv_laplace (x , vovn , sigma_final [i ]) - u_rn [i ]
209
232
sol = spop .root (obj_func , 1 / vovn ** 3 )
210
233
int_var [i ] = 1 / sol .x
211
234
return sigma_final , int_var
0 commit comments