@@ -22,7 +22,6 @@ class Cev(opt.OptAnalyticABC, smile.OptSmileABC, smile.MassZeroABC):
22
22
23
23
sigma = None
24
24
beta = 0.5
25
- is_bsm_sigma = False
26
25
27
26
def __init__ (self , sigma , beta = 0.5 , intr = 0.0 , divr = 0.0 , is_fwd = False ):
28
27
"""
@@ -45,20 +44,15 @@ def mass_zero(self, spot, texp, log=False):
45
44
fwd = self .forward (spot , texp )
46
45
47
46
betac = 1.0 - self .beta
48
- a = 0.5 / betac
47
+ a = 0.5 / betac
49
48
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
51
50
)
52
- x = 0.5 / np .square (betac * sigma_std )
51
+ x = 0.5 / np .square (betac * sigma_std )
53
52
54
53
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 ))))
62
56
with np .errstate (divide = "ignore" ):
63
57
log_mass = np .where (x > 100 , log_mass , np .log (spst .gamma .sf (x = x , a = a )))
64
58
return log_mass
@@ -77,41 +71,40 @@ def mass_zero_t0(self, spot, texp):
77
71
"""
78
72
fwd = self .forward (spot , texp )
79
73
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
82
76
return t0
83
77
84
78
@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 ):
88
80
"""
81
+ CEV model call/put option pricing formula (static method)
89
82
90
83
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.
100
93
101
94
Returns:
102
-
95
+ Vanilla option price
103
96
"""
104
97
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 )
107
100
108
101
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
115
108
116
109
# Need to clean up the case beta > 0
117
110
if beta > 1.0 :
@@ -123,43 +116,25 @@ def price_formula(
123
116
# Computing call and put is a bit of computtion waste, but do this for vectorization.
124
117
price = np .where (
125
118
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 ),
128
121
)
129
- return disc_fac * price
122
+ return disc_fac * price
130
123
131
124
def delta (self , strike , spot , texp , cp = 1 ):
132
125
fwd , df , divf = self ._fwd_factor (spot , texp )
133
- betac_inv = 1 / (1 - self .beta )
126
+ betac_inv = 1 / (1 - self .beta )
134
127
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 )
138
131
139
132
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 ))
151
135
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 ))
163
138
164
139
delta *= df if self .is_fwd else divf
165
140
return delta
@@ -168,75 +143,57 @@ def cdf(self, strike, spot, texp, cp=1):
168
143
fwd = self .forward (spot , texp )
169
144
170
145
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 ))
181
154
return cdf
182
155
183
156
def gamma (self , strike , spot , texp , cp = 1 ):
184
157
fwd , df , divf = self ._fwd_factor (spot , texp )
185
- betac_inv = 1 / (1 - self .beta )
158
+ betac_inv = 1 / (1 - self .beta )
186
159
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 )
190
163
191
164
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 ))
202
168
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 ))
210
172
211
- gamma *= 2 * (divf / betac_inv ) ** 2 / df * x / fwd
173
+ gamma *= 2 * (divf / betac_inv )** 2 / df * x / fwd
212
174
213
175
if self .is_fwd :
214
- gamma *= (df / divf ) ** 2
176
+ gamma *= (df / divf )** 2
215
177
216
178
return gamma
217
179
218
180
def vega (self , strike , spot , texp , cp = 1 ):
219
181
fwd , df , divf = self ._fwd_factor (spot , texp )
220
- spot = fwd * df / divf
182
+ spot = fwd * df / divf
221
183
222
- betac_inv = 1 / (1 - self .beta )
184
+ betac_inv = 1 / (1 - self .beta )
223
185
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 )
227
189
228
190
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 )
232
192
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 )
238
194
239
- vega *= df * 2 * x / sigma
195
+ sigma = self .sigma * np .power (spot , self .beta - 1 )
196
+ vega *= df * 2 * x / sigma
240
197
return vega
241
198
242
199
def theta (self , strike , spot , texp , cp = 1 ):
0 commit comments