@@ -308,7 +308,7 @@ def price_barrier(self, strike, barrier, spot, texp, cp=1, io=-1):
308
308
return p
309
309
310
310
311
- class BsmDisp (smile .OptSmileABC ):
311
+ class BsmDisp (smile .OptSmileABC , Bsm ):
312
312
"""
313
313
Displaced Black-Scholes-Merton model for option pricing. Displace price,
314
314
@@ -321,17 +321,19 @@ class BsmDisp(smile.OptSmileABC):
321
321
>>> import pyfeng as pf
322
322
>>> m = pf.BsmDisp(sigma=0.2, beta=0.5, pivot=100, intr=0.05, divr=0.1)
323
323
>>> m.price(np.arange(80, 121, 10), 100, 1.2)
324
- >>> sigma = np.array([0.2, 0.3, 0.5])[:, None]
325
- >>> m = pf.BsmDisp(sigma, beta=0.5, pivot=100, intr=0.05, divr=0.1) # sigma in axis=0
326
- >>> m.price(np.array([90, 100, 110]), 100, 1.2, cp=np.array([-1,1,1]))
327
- array([[ 5.75927238, 5.52948546, 2.94558338],
328
- [ 9.4592961 , 9.3881245 , 6.45745004],
329
- [16.812035 , 17.10541288, 14.10354768]])
324
+ array([15.9543935 , 9.7886658 , 5.4274197 , 2.71430505, 1.22740381])
325
+ >>> beta = np.array([0.2, 0.6, 1])[:, None]
326
+ >>> m = pf.BsmDisp(0.2, beta=beta, pivot=100) # beta in axis=0
327
+ >>> m.vol_smile(np.arange(80, 121, 10), 100, 1.2)
328
+ array([[0.21915778, 0.20904587, 0.20038559, 0.19286293, 0.18625174],
329
+ [0.20977955, 0.20461468, 0.20025691, 0.19652101, 0.19327567],
330
+ [0.2 , 0.2 , 0.2 , 0.2 , 0.2 ]])
330
331
"""
331
332
332
- pivot = None
333
333
beta = 1 # equivalent to Black-Scholes
334
- bsm_model = None
334
+ pivot = 0
335
+ sigma_disp = None
336
+ #_m_bsm = None
335
337
336
338
def __init__ (self , sigma , beta = 1 , pivot = 0 , * args , ** kwargs ):
337
339
"""
@@ -343,53 +345,80 @@ def __init__(self, sigma, beta=1, pivot=0, *args, **kwargs):
343
345
divr: dividend/convenience yield (foreign interest rate)
344
346
is_fwd: if True, treat `spot` as forward price. False by default.
345
347
"""
346
- super ().__init__ (sigma , * args , ** kwargs )
347
348
self .pivot = pivot
348
349
self .beta = beta
349
- self .bsm_model = Bsm (sigma = beta * sigma )
350
+ #self._m_bsm = Bsm(sigma=beta*sigma, *args, **kwargs)
351
+ super ().__init__ (sigma , * args , ** kwargs )
350
352
351
- '''
352
353
@property
353
354
def sigma (self ):
354
- return self.sigma
355
+ return self .sigma_disp * self . beta
355
356
356
357
@sigma .setter
357
358
def sigma (self , sigma ):
358
- self.sigma = sigma
359
- self.bsm_model.sigma = self.beta*sigma
360
- '''
359
+ self .sigma_disp = sigma
361
360
362
- def disp (self , x ):
363
- return self .beta * x + (1 - self .beta )* self .pivot
361
+ def disp_spot (self , spot ):
362
+ """
363
+ Displaced spot
364
364
365
- def price (self , strike , spot , * args , ** kwargs ):
366
- return (1 / self .beta )* self .bsm_model .price (
367
- self .disp (strike ), self .disp (spot ), * args , ** kwargs )
365
+ Args:
366
+ spot: spot (or forward) price
368
367
369
- def delta (self , strike , spot , * args , ** kwargs ):
370
- return self .bsm_model .delta (
371
- self .disp (strike ), self .disp (spot ), * args , ** kwargs )
368
+ Returns:
369
+ Displaces spot
370
+ """
371
+ return self .beta * spot + (1 - self .beta )* self .pivot
372
372
373
- def cdf (self , strike , spot , * args , ** kwargs ):
374
- return self . bsm_model . cdf (
375
- self . disp ( strike ), self . disp ( spot ), * args , ** kwargs )
373
+ def disp_strike (self , strike , texp ):
374
+ """
375
+ Displaced strike
376
376
377
- def vega ( self , strike , spot , * args , ** kwargs ) :
378
- return self . bsm_model . vega (
379
- self . disp ( strike ), self . disp ( spot ), * args , ** kwargs )
377
+ Args :
378
+ strike: strike price
379
+ texp: time to expiry
380
380
381
- def gamma (self , strike , spot , * args , ** kwargs ):
381
+ Returns:
382
+ Displace strike
383
+ """
384
+ return self .beta * strike + (1 - self .beta )* self .forward (self .pivot , texp )
385
+
386
+ def price (self , strike , spot , texp , cp = 1 ):
387
+ spot = self .disp_spot (spot )
388
+ strike = self .disp_strike (strike , texp )
389
+ return (1 / self .beta ) * super ().price (strike , spot , texp , cp = cp )
390
+
391
+ def delta (self , strike , spot , texp , cp = 1 ):
392
+ spot = self .disp_spot (spot )
393
+ strike = self .disp_strike (strike , texp )
394
+ return super ().delta (strike , spot , texp , cp = cp )
395
+
396
+ def cdf (self , strike , spot , texp , cp = 1 ):
397
+ spot = self .disp_spot (spot )
398
+ strike = self .disp_strike (strike , texp )
399
+ return super ().cdf (strike , spot , texp , cp = cp )
400
+
401
+ def vega (self , strike , spot , texp , cp = 1 ):
402
+ spot = self .disp_spot (spot )
403
+ strike = self .disp_strike (strike , texp )
404
+ return super ().vega (strike , spot , texp , cp = cp )
405
+
406
+ def gamma (self , strike , spot , texp , cp = 1 ):
382
407
# need to mutiply beta because of (beta*sigma) appearing in the denominator of the bsm gamma
383
- return self .beta * self .bsm_model .gamma (
384
- self .disp (strike ), self .disp (spot ), * args , ** kwargs )
408
+ spot = self .disp_spot (spot )
409
+ strike = self .disp_strike (strike , texp )
410
+ return self .beta * super ().gamma (strike , spot , texp , cp = cp )
385
411
386
- def theta (self , strike , spot , * args , ** kwargs ):
387
- return (1 / self .beta )* self .bsm_model .theta (
388
- self .disp (strike ), self .disp (spot ), * args , ** kwargs )
412
+ def theta (self , strike , spot , texp , cp = 1 ):
413
+ spot = self .disp_spot (spot )
414
+ strike = self .disp_strike (strike , texp )
415
+ return (1 / self .beta )* super ().theta (
416
+ strike , spot , texp , cp = cp )
389
417
390
418
def impvol (self , price_in , strike , spot , texp , cp = 1 , setval = False ):
391
- sigma = (1 / self .beta )* self .bsm_model .impvol (
392
- self .beta * price_in , self .disp (strike ), self .disp (spot ), texp , cp = cp , setval = setval )
419
+ spot = self .disp_spot (spot )
420
+ strike = self .disp_strike (strike , texp )
421
+ sigma = (1 / self .beta )* super ().impvol (self .beta * price_in , strike , spot , texp , cp = cp , setval = False )
393
422
if setval :
394
423
self .sigma = sigma
395
424
return sigma
@@ -409,28 +438,29 @@ def vol_smile(self, strike, spot, texp, cp=1, model='bsm'):
409
438
volatility smile under the specified model
410
439
"""
411
440
if model .lower () == 'norm-approx' :
412
- fwd , _ , _ = self ._fwd_factor ( spot , texp )
413
- kkd = self .disp (strike ) / self . disp ( fwd )
441
+ fwdd = self .forward ( self . disp_spot ( spot ) , texp )
442
+ kkd = self .disp_strike (strike , texp ) / fwdd
414
443
lnkd = np .log (kkd )
415
- sig_beta = self .beta * self .sigma
416
- vol = self .sigma * self .disp (fwd ) * np .sqrt (kkd ) * (1 + lnkd ** 2 / 24 ) / \
417
- (1 + sig_beta ** 2 * texp / 24 )
444
+ # self.sigma actually means self.beta * self._sigma
445
+ vol = self .sigma_disp * fwdd * np .sqrt (kkd ) * (1 + lnkd ** 2 / 24 ) / (1 + self .sigma ** 2 * texp / 24 )
418
446
elif model .lower () == 'bsm-approx' :
419
- fwd , _ , _ = self ._fwd_factor (spot , texp )
447
+ fwd = self .forward (spot , texp )
420
448
kk = strike / fwd
421
449
lnk = np .log (kk )
422
- fwdd = self .disp (fwd )
423
- kkd = self .disp (strike ) / fwdd
450
+
451
+ fwdd = self .forward (self .disp_spot (spot ), texp )
452
+ kkd = self .disp_strike (strike , texp ) / fwdd
424
453
lnkd = np .log (kkd )
425
- sig_beta = self . beta * self . sigma
426
- vol = self .sigma * ( fwdd / fwd ) * np . sqrt ( kkd / kk )
427
- vol *= ( 1 + lnkd ** 2 / 24 ) / ( 1 + lnk ** 2 / 24 ) * ( 1 + vol ** 2 * texp / 24 ) / \
428
- (1 + sig_beta ** 2 * texp / 24 )
454
+
455
+ # self.sigma actually means self.beta * self.sigma_disp
456
+ vol = self . sigma_disp * ( fwdd / fwd ) * np . sqrt ( kkd / kk )
457
+ vol *= ( 1 + lnkd ** 2 / 24 ) / (1 + lnk ** 2 / 24 ) * ( 1 + vol ** 2 * texp / 24 ) / ( 1 + self . sigma ** 2 * texp / 24 )
429
458
else :
430
459
vol = super ().vol_smile (strike , spot , texp , model = model , cp = cp )
431
460
432
461
return vol
433
462
434
- def price_barrier (self , strike , barrier , spot , * args , ** kwargs ):
435
- return (1 / self .beta )* self .bsm_model .price_barrier (
436
- self .disp (strike ), self .disp (barrier ), self .disp (spot ), * args , ** kwargs )
463
+ def price_barrier (self , strike , barrier , spot , texp , cp = 1 , io = - 1 ):
464
+ fwd = self .forward (spot , texp )
465
+ return (1 / self .beta )* super ().price_barrier (
466
+ self .disp_spot (strike ), self .disp_strike (barrier , strike ), self .disp_spot (fwd ), texp , cp = cp , io = io )
0 commit comments