aboutsummaryrefslogtreecommitdiffstats
path: root/python/analytics/option.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/analytics/option.py')
-rw-r--r--python/analytics/option.py130
1 files changed, 64 insertions, 66 deletions
diff --git a/python/analytics/option.py b/python/analytics/option.py
index 4a61e90f..d3012470 100644
--- a/python/analytics/option.py
+++ b/python/analytics/option.py
@@ -78,7 +78,7 @@ def ATMstrike(index, exercise_date):
b *= eta
return brentq(closure, a, b)
-class Swaption(ForwardIndex):
+class BlackSwaption(ForwardIndex):
"""Swaption class"""
def __init__(self, index, exercise_date, strike, option_type="payer"):
ForwardIndex.__init__(self, index, exercise_date)
@@ -143,68 +143,6 @@ class Swaption(ForwardIndex):
@property
@memoize
def pv(self):
- T = self.T
- tilt = np.exp(-self.sigma**2/2 * T + self.sigma * self._Z * math.sqrt(T))
- args = (self.forward_pv, self.exercise_date, self.exercise_date_settle,
- self.index, self._forward_yc, tilt, self._w)
- eta = 1.05
- a = self.index.spread * 0.99
- b = a * eta
- while True:
- if calib(*((b,) + args)) > 0:
- break
- b *= eta
-
- S0 = brentq(calib, a, b, args)
- if T == 0:
- return self.notional * self.intrinsic
- ## Zstar solves S_0 exp(-\sigma^2/2 * T + sigma * Z^\star\sqrt{T}) = strike
- Zstar = (math.log(self._strike/S0) + self.sigma**2/2 * T) / \
- (self.sigma * math.sqrt(T))
-
- if self.option_type == "payer":
- Z = Zstar + np.logspace(0, math.log(4 / (self.sigma * math.sqrt(T)), 10), 300) - 1
- elif self.option_type == "receiver":
- Z = Zstar - np.logspace(0, math.log(4 / (self.sigma * math.sqrt(T)), 10), 300) + 1
- else:
- raise ValueError("option_type needs to be either 'payer' or 'receiver'")
- S = S0 * np.exp(-self.sigma**2/2 * T + self.sigma * Z * math.sqrt(T))
- r = pv_vec(S * 1e-4, self._forward_yc, self.exercise_date,
- self.exercise_date_settle, self.index.start_date,
- self.index.end_date, self.index.recovery, self.index.fixed_rate * 1e-4)
- val = (r - self._G) * 1/math.sqrt(2*math.pi) * np.exp(-Z**2/2)
- return self.notional * simps(val, Z) * self.df
-
- @pv.setter
- def pv(self, val):
- if np.isnan(val):
- raise ValueError("val is nan")
- if val < self.intrinsic_value:
- raise ValueError("{}: is less than intrinsic value: {}".
- format(val, self.intrinsic_value))
- elif val == self.intrinsic_value:
- self.sigma = 0
- return
- def handle(x):
- self.sigma = x
- return self.pv - val
- # use sigma_black as a starting point
- self.pv_black = val
- eta = 1.1
- a = self.sigma
- while True:
- if handle(a) < 0:
- break
- a /= eta
- b = a * eta
- while True:
- if handle(b) > 0:
- break
- b *= eta
- self.sigma = brentq(handle, a, b)
-
- @property
- def pv_black(self):
"""compute pv using black-scholes formula"""
if self.sigma == 0:
return self.intrinsic_value
@@ -215,13 +153,17 @@ class Swaption(ForwardIndex):
self.T,
self.sigma,
self.option_type) * self.notional
- @pv_black.setter
- def pv_black(self, val):
+ @pv.setter
+ def pv(self, val):
if np.isnan(val):
raise ValueError("val is nan")
if val < self.intrinsic_value:
raise ValueError("{}: is less than intrinsic value: {}".
format(val, self.intrinsic_value))
+ elif val == self.intrinsic_value:
+ self.sigma = 0
+ return
+
def handle(x):
self.sigma = x
return self.pv_black - val
@@ -261,7 +203,7 @@ class Swaption(ForwardIndex):
old_delta = self.delta
self.index.spread -= 10
self._update()
- gamma = abs(self.delta- old_delta)
+ gamma = abs(self.delta - old_delta)
self.index.spread += 5
self._update()
return gamma
@@ -367,6 +309,62 @@ class Swaption(ForwardIndex):
]
return "\n".join(s)
+class Swaption(BlackSwaption):
+ @property
+ @memoize
+ def pv(self):
+ T = self.T
+ tilt = np.exp(-self.sigma**2/2 * T + self.sigma * self._Z * math.sqrt(T))
+ args = (self.forward_pv, self.exercise_date, self.exercise_date_settle,
+ self.index, self._forward_yc, tilt, self._w)
+ eta = 1.05
+ a = self.index.spread * 0.99
+ b = a * eta
+ while True:
+ if calib(*((b,) + args)) > 0:
+ break
+ b *= eta
+
+ S0 = brentq(calib, a, b, args)
+ if T == 0:
+ return self.notional * self.intrinsic
+ ## Zstar solves S_0 exp(-\sigma^2/2 * T + sigma * Z^\star\sqrt{T}) = strike
+ Zstar = (math.log(self._strike/S0) + self.sigma**2/2 * T) / \
+ (self.sigma * math.sqrt(T))
+
+ if self.option_type == "payer":
+ Z = Zstar + np.logspace(0, math.log(4 / (self.sigma * math.sqrt(T)), 10), 300) - 1
+ elif self.option_type == "receiver":
+ Z = Zstar - np.logspace(0, math.log(4 / (self.sigma * math.sqrt(T)), 10), 300) + 1
+ else:
+ raise ValueError("option_type needs to be either 'payer' or 'receiver'")
+ S = S0 * np.exp(-self.sigma**2/2 * T + self.sigma * Z * math.sqrt(T))
+ r = pv_vec(S * 1e-4, self._forward_yc, self.exercise_date,
+ self.exercise_date_settle, self.index.start_date,
+ self.index.end_date, self.index.recovery, self.index.fixed_rate * 1e-4)
+ val = (r - self._G) * 1/math.sqrt(2*math.pi) * np.exp(-Z**2/2)
+ return self.notional * simps(val, Z) * self.df
+
+ @pv.setter
+ def pv(self, val):
+ # use sigma_black as a starting point
+ super().pv = val
+ def handle(x):
+ self.sigma = x
+ return self.pv - val
+ eta = 1.1
+ a = self.sigma
+ while True:
+ if handle(a) < 0:
+ break
+ a /= eta
+ b = a * eta
+ while True:
+ if handle(b) > 0:
+ break
+ b *= eta
+ self.sigma = brentq(handle, a, b)
+
def compute_vol(option, strike, mid, pv_type):
option.strike = strike
try: