diff options
Diffstat (limited to 'python/swaption.py')
| -rw-r--r-- | python/swaption.py | 73 |
1 files changed, 60 insertions, 13 deletions
diff --git a/python/swaption.py b/python/swaption.py index 20cd2d03..5d00c9c8 100644 --- a/python/swaption.py +++ b/python/swaption.py @@ -320,7 +320,7 @@ def calib(S0, fp, exercise_date, exercise_date_settle, index, tilt, w): return np.inner(vec, w) - fp def g(index, spread, exercise_date): - """ computes the strike price using the expected forward yield curve """ + """ computes the strike clean price using the expected forward yield curve """ rolled_curve = roll_yc(index._yc, exercise_date) step_in_date = exercise_date + datetime.timedelta(days=1) exercise_date_settle = (pd.Timestamp(exercise_date) + 3* BDay()).date() @@ -332,10 +332,23 @@ def g(index, spread, exercise_date): rolled_curve, sc, True) return index.notional * (spread - index.fixed_rate) * a *1e-4 +def g2(index, spread, exercise_date): + """ computes the strike price using the expected forward yield curve """ + step_in_date = exercise_date + datetime.timedelta(days=1) + exercise_date_settle = (pd.Timestamp(exercise_date) + 3* BDay()).date() + sc = SpreadCurve(exercise_date, index._yc, index.start_date, + step_in_date, exercise_date_settle, + [index.end_date], array.array('d', [spread * 1e-4]), + index.recovery) + a = index._fee_leg.pv(exercise_date, step_in_date, exercise_date_settle, + index._yc, sc, True) + dl_pv = index._default_leg.pv( + exercise_date, step_in_date, exercise_date_settle, index._yc, + sc, index.recovery) + return index.notional * (dl_pv - a * index.fixed_rate * 1e-4) + def ATMstrike(index, exercise_date): exercise_date_settle = (pd.Timestamp(exercise_date) + 3* BDay()).date() - # df = index._yc.discount_factor(exercise_date_settle) / \ - # index._yc.discount_factor(index._value_date) fp = index.forward_pv(exercise_date) closure = lambda S: g(index, S, exercise_date) - fp eta = 1.1 @@ -350,15 +363,23 @@ def ATMstrike(index, exercise_date): class Option: def __init__(self, index, exercise_date, strike, option_type="payer"): self.index = index - self.exercise_date = exercise_date - self._T = None + self._exercise_date = exercise_date self.exercise_date_settle = (pd.Timestamp(self.exercise_date) + 3* BDay()).date() + self._T = None self.strike = strike self.option_type = option_type.lower() self._Z, self._w = GHquad(50) self.notional = 1 @property + def exercise_date(self): + return self._exercise_date + @exercise_date.setter + def exercise_date(self, d : datetime.date): + self._exercise_date = d + self.exercise_date_settle = (pd.Timestamp(d) + 3* BDay()).date() + + @property def pv(self): fp = self.index.forward_pv(self.exercise_date)/self.index.notional T = self.T @@ -374,15 +395,20 @@ class Option: b *= eta S0 = brentq(calib, a, b, args) - G = g(self.index, self.strike, self.exercise_date) - print(S0) + if T == 0: + pv = self.notional * (g(self.index, self.index.spread, self.exercise_date) - G) + if self.option_type == "payer": + return pv if self.index.spread > self.strike else 0 + else: + return - pv if self.index.spread < self.strike else 0 + 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, 1.1, 100) - 1 + Z = Zstar + np.logspace(0, 2, 300) - 1 elif self.option_type == "receiver": - Z = Zstar - np.logspace(0, 1.1, 100) + 1 + Z = Zstar - np.logspace(0, 2, 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)) @@ -390,9 +416,17 @@ class Option: self.exercise_date_settle, self.index.start_date, self.index.end_date, self.index.recovery) val = ((a - b * self.index.fixed_rate*1e-4) - G) * 1/math.sqrt(2*math.pi) * np.exp(-Z**2/2) - df_scale = self.index._yc.discount_factor(self.exercise_date_settle) / \ - self.index._yc.discount_factor(self.index._value_date) - return self.notional * (simps(val, Z) * df_scale) + df_scale = self.index._yc.discount_factor(self.exercise_date_settle) + return self.notional * simps(val, Z) * df_scale + + @property + def pv2(self): + G = g(self.index, self.strike, self.exercise_date) + fp = self.index.forward_pv(self.exercise_date) + forward_annuity = self.index.forward_annuity(self.exercise_date) + DA_forward_spread = fp / forward_annuity + self.index.fixed_rate * 1e-4 + strike_tilde = self.index.fixed_rate * 1e-4 + G / forward_annuity + return forward_annuity * black(DA_forward_spread, strike_tilde, self.T, self.sigma, self.option_type) @property def delta(self): @@ -410,7 +444,7 @@ class Option: if self._T: return self._T else: - return year_frac(self.index.trade_date, self.exercise_date) + return year_frac(self.index.trade_date, self.exercise_date) + 1/365 @property def gamma(self): @@ -432,6 +466,19 @@ class Option: self.sigma -= 0.01 return vega +def d1(F, K, sigma, T): + return (np.log(F / K) + sigma**2 * T / 2) / (sigma * np.sqrt(T)) + +def d2(F, K, sigma, T): + return d1(F, K, sigma, T) - sigma * np.sqrt(T) + +def black(F, K, T, sigma, option_type = "payer"): + chi = 1 if option_type == "payer" else -1 + if option_type == "payer": + return F * norm.cdf(d1(F, K, sigma, T)) - K * norm.cdf(d2(F, K, sigma, T)) + else: + return K * norm.cdf(- d2(F, K, sigma, T)) - F * norm.cdf(- d1(F, K, sigma, T)) + def option(index, exercise_date, sigma, K, option_type="payer"): """ computes the pv of an option using Pedersen's model """ fp = index.forward_pv(exercise_date)/index.notional |
