@@ -22,7 +22,6 @@ class Cev(opt.OptAnalyticABC, smile.OptSmileABC, smile.MassZeroABC):
2222
2323 sigma = None
2424 beta = 0.5
25- is_bsm_sigma = False
2625
2726 def __init__ (self , sigma , beta = 0.5 , intr = 0.0 , divr = 0.0 , is_fwd = False ):
2827 """
@@ -45,20 +44,15 @@ def mass_zero(self, spot, texp, log=False):
4544 fwd = self .forward (spot , texp )
4645
4746 betac = 1.0 - self .beta
48- a = 0.5 / betac
47+ a = 0.5 / betac
4948 sigma_std = np .maximum (
50- self .sigma / np .power (fwd , betac ) * np .sqrt (texp ), np .finfo (float ).eps
49+ self .sigma / np .power (fwd , betac )* np .sqrt (texp ), np .finfo (float ).eps
5150 )
52- x = 0.5 / np .square (betac * sigma_std )
51+ x = 0.5 / np .square (betac * sigma_std )
5352
5453 if log :
55- log_mass = (a - 1 ) * np .log (x ) - x - np .log (spsp .gamma (a ))
56- log_mass += np .log (
57- 1
58- + (a - 1 )
59- / x
60- * (1 + (a - 2 ) / x * (1 + (a - 3 ) / x * (1 + (a - 4 ) / x )))
61- )
54+ log_mass = (a - 1 )* np .log (x ) - x - np .log (spsp .gamma (a ))
55+ log_mass += np .log (1 + (a - 1 )/ x * (1 + (a - 2 )/ x * (1 + (a - 3 )/ x * (1 + (a - 4 )/ x ))))
6256 with np .errstate (divide = "ignore" ):
6357 log_mass = np .where (x > 100 , log_mass , np .log (spst .gamma .sf (x = x , a = a )))
6458 return log_mass
@@ -77,41 +71,40 @@ def mass_zero_t0(self, spot, texp):
7771 """
7872 fwd = self .forward (spot , texp )
7973 betac = 1.0 - self .beta
80- alpha = self .sigma / np .power (fwd , betac )
81- t0 = 0.5 / (betac * alpha ) ** 2
74+ alpha = self .sigma / np .power (fwd , betac )
75+ t0 = 0.5 / (betac * alpha )** 2
8276 return t0
8377
8478 @staticmethod
85- def price_formula (
86- strike , spot , texp , sigma = None , cp = 1 , beta = 0.5 , intr = 0.0 , divr = 0.0 , is_fwd = False
87- ):
79+ def price_formula (strike , spot , texp , sigma = None , cp = 1 , beta = 0.5 , intr = 0.0 , divr = 0.0 , is_fwd = False ):
8880 """
81+ CEV model call/put option pricing formula (static method)
8982
9083 Args:
91- strike:
92- spot:
93- texp:
94- cp:
95- sigma:
96- beta:
97- intr:
98- divr:
99- is_fwd:
84+ strike: strike price
85+ spot: spot (or forward)
86+ sigma: model volatility
87+ texp: time to expiry
88+ cp: 1/-1 for call/put option
89+ beta: elasticity parameter
90+ intr: interest rate (domestic interest rate)
91+ divr: dividend/convenience yield (foreign interest rate)
92+ is_fwd: if True, treat `spot` as forward price. False by default.
10093
10194 Returns:
102-
95+ Vanilla option price
10396 """
10497
105- disc_fac = np .exp (- texp * intr )
106- fwd = spot * (1.0 if is_fwd else np .exp (- texp * divr ) / disc_fac )
98+ disc_fac = np .exp (- texp * intr )
99+ fwd = spot * (1.0 if is_fwd else np .exp (- texp * divr )/ disc_fac )
107100
108101 betac = 1.0 - beta
109- betac_inv = 1.0 / betac
110- alpha = sigma / np .power (fwd , betac )
111- sigma_std = np .maximum (alpha * np .sqrt (texp ), np .finfo (float ).eps )
112- kk = strike / fwd
113- x = 1.0 / np .square (betac * sigma_std )
114- y = np .power (kk , 2 * betac ) * x
102+ betac_inv = 1.0 / betac
103+ alpha = sigma / np .power (fwd , betac )
104+ sigma_std = np .maximum (alpha * np .sqrt (texp ), np .finfo (float ).eps )
105+ kk = strike / fwd
106+ x = 1.0 / np .square (betac * sigma_std )
107+ y = np .power (kk , 2 * betac )* x
115108
116109 # Need to clean up the case beta > 0
117110 if beta > 1.0 :
@@ -123,43 +116,25 @@ def price_formula(
123116 # Computing call and put is a bit of computtion waste, but do this for vectorization.
124117 price = np .where (
125118 cp > 0 ,
126- fwd * ncx2_sf (y , 2 + betac_inv , x ) - strike * ncx2_cdf (x , betac_inv , y ),
127- strike * ncx2_sf (x , betac_inv , y ) - fwd * ncx2_cdf (y , 2 + betac_inv , x ),
119+ fwd * ncx2_sf (y , 2 + betac_inv , x ) - strike * ncx2_cdf (x , betac_inv , y ),
120+ strike * ncx2_sf (x , betac_inv , y ) - fwd * ncx2_cdf (y , 2 + betac_inv , x ),
128121 )
129- return disc_fac * price
122+ return disc_fac * price
130123
131124 def delta (self , strike , spot , texp , cp = 1 ):
132125 fwd , df , divf = self ._fwd_factor (spot , texp )
133- betac_inv = 1 / (1 - self .beta )
126+ betac_inv = 1 / (1 - self .beta )
134127
135- k_star = 1.0 / np .square (self .sigma / betac_inv ) / texp
136- x = k_star * np .power (fwd , 2 / betac_inv )
137- y = k_star * np .power (strike , 2 / betac_inv )
128+ k_star = 1.0 / np .square (self .sigma / betac_inv )/ texp
129+ x = k_star * np .power (fwd , 2 / betac_inv )
130+ y = k_star * np .power (strike , 2 / betac_inv )
138131
139132 if self .beta < 1.0 :
140- delta = (
141- 0.5 * (cp - 1 )
142- + spst .ncx2 .sf (y , 2 + betac_inv , x )
143- + 2
144- * x
145- / betac_inv
146- * (
147- spst .ncx2 .pdf (y , 4 + betac_inv , x )
148- - strike / fwd * spst .ncx2 .pdf (x , betac_inv , y )
149- )
150- )
133+ delta = 0.5 * (cp - 1 ) + spst .ncx2 .sf (y , 2 + betac_inv , x )\
134+ + 2 * x / betac_inv * (spst .ncx2 .pdf (y , 4 + betac_inv , x ) - strike / fwd * spst .ncx2 .pdf (x , betac_inv , y ))
151135 else :
152- delta = (
153- 0.5 * (cp - 1 )
154- + spst .ncx2 .sf (x , - betac_inv , y )
155- - 2
156- * x
157- / betac_inv
158- * (
159- spst .ncx2 .pdf (x , - betac_inv , y )
160- - strike / fwd * spst .ncx2 .pdf (y , 4 - betac_inv , x )
161- )
162- )
136+ delta = 0.5 * (cp - 1 ) + spst .ncx2 .sf (x , - betac_inv , y )\
137+ - 2 * x / betac_inv * (spst .ncx2 .pdf (x , - betac_inv , y ) - strike / fwd * spst .ncx2 .pdf (y , 4 - betac_inv , x ))
163138
164139 delta *= df if self .is_fwd else divf
165140 return delta
@@ -168,75 +143,57 @@ def cdf(self, strike, spot, texp, cp=1):
168143 fwd = self .forward (spot , texp )
169144
170145 betac = 1.0 - self .beta
171- betac_inv = 1.0 / betac
172- alpha = self .sigma / np .power (fwd , betac )
173- sigma_std = np .maximum (alpha * np .sqrt (texp ), np .finfo (float ).eps )
174- kk = strike / fwd
175- x = 1.0 / np .square (betac * sigma_std )
176- y = np .power (kk , 2 * betac ) * x
177-
178- cdf = np .where (
179- cp > 0 , spst .ncx2 .cdf (x , betac_inv , y ), spst .ncx2 .sf (x , betac_inv , y )
180- )
146+ betac_inv = 1.0 / betac
147+ alpha = self .sigma / np .power (fwd , betac )
148+ sigma_std = np .maximum (alpha * np .sqrt (texp ), np .finfo (float ).eps )
149+ kk = strike / fwd
150+ x = 1.0 / np .square (betac * sigma_std )
151+ y = np .power (kk , 2 * betac )* x
152+
153+ cdf = np .where (cp > 0 , spst .ncx2 .cdf (x , betac_inv , y ), spst .ncx2 .sf (x , betac_inv , y ))
181154 return cdf
182155
183156 def gamma (self , strike , spot , texp , cp = 1 ):
184157 fwd , df , divf = self ._fwd_factor (spot , texp )
185- betac_inv = 1 / (1 - self .beta )
158+ betac_inv = 1 / (1 - self .beta )
186159
187- k_star = 1.0 / np .square (self .sigma / betac_inv ) / texp
188- x = k_star * np .power (fwd , 2 / betac_inv )
189- y = k_star * np .power (strike , 2 / betac_inv )
160+ k_star = 1.0 / np .square (self .sigma / betac_inv )/ texp
161+ x = k_star * np .power (fwd , 2 / betac_inv )
162+ y = k_star * np .power (strike , 2 / betac_inv )
190163
191164 if self .beta < 1.0 :
192- gamma = (
193- (2 + betac_inv - x ) * spst .ncx2 .pdf (y , 4 + betac_inv , x )
194- + x * spst .ncx2 .pdf (y , 6 + betac_inv , x )
195- + strike
196- / fwd
197- * (
198- x * spst .ncx2 .pdf (x , betac_inv , y )
199- - y * spst .ncx2 .pdf (x , 2 + betac_inv , y )
200- )
201- )
165+ gamma = (2 + betac_inv - x )* spst .ncx2 .pdf (y , 4 + betac_inv , x ) \
166+ + x * spst .ncx2 .pdf (y , 6 + betac_inv , x ) \
167+ + strike / fwd * (x * spst .ncx2 .pdf (x , betac_inv , y ) - y * spst .ncx2 .pdf (x , 2 + betac_inv , y ))
202168 else :
203- gamma = (
204- x * spst .ncx2 .pdf (x , - betac_inv , y )
205- - y * spst .ncx2 .pdf (x , 2 - betac_inv , y )
206- ) + strike / fwd * (
207- (2 - betac_inv - x ) * spst .ncx2 .pdf (y , 4 - betac_inv , x )
208- + x * spst .ncx2 .pdf (y , 6 - betac_inv , x )
209- )
169+ gamma = (x * spst .ncx2 .pdf (x , - betac_inv , y ) - y * spst .ncx2 .pdf (x , 2 - betac_inv , y )) \
170+ + strike / fwd * ((2 - betac_inv - x )* spst .ncx2 .pdf (y , 4 - betac_inv , x )
171+ + x * spst .ncx2 .pdf (y , 6 - betac_inv , x ))
210172
211- gamma *= 2 * (divf / betac_inv ) ** 2 / df * x / fwd
173+ gamma *= 2 * (divf / betac_inv )** 2 / df * x / fwd
212174
213175 if self .is_fwd :
214- gamma *= (df / divf ) ** 2
176+ gamma *= (df / divf )** 2
215177
216178 return gamma
217179
218180 def vega (self , strike , spot , texp , cp = 1 ):
219181 fwd , df , divf = self ._fwd_factor (spot , texp )
220- spot = fwd * df / divf
182+ spot = fwd * df / divf
221183
222- betac_inv = 1 / (1 - self .beta )
184+ betac_inv = 1 / (1 - self .beta )
223185
224- k_star = 1.0 / np .square (self .sigma / betac_inv ) / texp
225- x = k_star * np .power (fwd , 2 / betac_inv )
226- y = k_star * np .power (strike , 2 / betac_inv )
186+ k_star = 1.0 / np .square (self .sigma / betac_inv )/ texp
187+ x = k_star * np .power (fwd , 2 / betac_inv )
188+ y = k_star * np .power (strike , 2 / betac_inv )
227189
228190 if self .beta < 1.0 :
229- vega = - fwd * spst .ncx2 .pdf (y , 4 + betac_inv , x ) + strike * spst .ncx2 .pdf (
230- x , betac_inv , y
231- )
191+ vega = - fwd * spst .ncx2 .pdf (y , 4 + betac_inv , x ) + strike * spst .ncx2 .pdf (x , betac_inv , y )
232192 else :
233- vega = fwd * spst .ncx2 .pdf (x , - betac_inv , y ) - strike * spst .ncx2 .pdf (
234- y , 4 - betac_inv , x
235- )
236-
237- sigma = self .sigma * spot ** (self .beta - 1 )
193+ vega = fwd * spst .ncx2 .pdf (x , - betac_inv , y ) - strike * spst .ncx2 .pdf (y , 4 - betac_inv , x )
238194
239- vega *= df * 2 * x / sigma
195+ sigma = self .sigma * np .power (spot , self .beta - 1 )
196+ vega *= df * 2 * x / sigma
240197 return vega
241198
242199 def theta (self , strike , spot , texp , cp = 1 ):
0 commit comments