diff options
Diffstat (limited to 'python')
| -rw-r--r-- | python/swaption.py | 161 | ||||
| -rw-r--r-- | python/test_option.py | 73 |
2 files changed, 181 insertions, 53 deletions
diff --git a/python/swaption.py b/python/swaption.py new file mode 100644 index 00000000..4e81acb2 --- /dev/null +++ b/python/swaption.py @@ -0,0 +1,161 @@ +from pyisda.legs import ContingentLeg, FeeLeg +from pyisda.flat_hazard import strike_vec +from pyisda.curve import YieldCurve, BadDay, SpreadCurve +import math +from scipy.optimize import brentq +from scipy.integrate import simps +import datetime +from tranche_functions import GHquad + +class Index(): + """ minimal class to represent a credit index """ + def __init__(self, start_date, end_date, recovery, fixed_rate): + self.fixed_rate = fixed_rate + self.notional = 1 + self._start_date = start_date + self._end_date = end_date + self.recovery = recovery + + self._fee_leg = FeeLeg(start_date, end_date, True, 1, 1) + self._default_leg = ContingentLeg(start_date, end_date, 1) + + @property + def start_date(self): + return self._start_date + + @property + def end_date(self): + return self._end_date + + @start_date.setter + def start_date(self, d): + self._fee_leg = FeeLeg(d, self.end_date, True, 1, 1) + self._default_leg = ContingentLeg(d, self.end_date, 1) + self._start_date = d + + @end_date.setter + def end_date(self, d): + self._fee_leg = FeeLeg(self.start_date, d, True, 1, 1) + self._default_leg = ContingentLeg(self.start_date, d, 1) + self._end_date = d + + def pv(self, trade_date, exercise_date, yc, sc, h = None): + step_in_date = exercise_date + datetime.timedelta(days=1) + a = self._fee_leg.pv(trade_date, step_in_date, trade_date, yc, sc, False) + Delta = self._fee_leg.accrued(step_in_date) + if exercise_date > trade_date: + Delta *= math.exp(-h * year_frac(trade_date, exercise_date)) + clean_forward_annuity = a - Delta + + dl_pv = self._default_leg.pv( + trade_date, step_in_date, trade_date, yc, sc, self.recovery) + return dl_pv - clean_forward_annuity * self.fixed_rate, clean_forward_annuity + +def year_frac(d1, d2, day_count_conv = "Actual/365"): + """ compute the year fraction between two dates """ + if day_count_conv.lower() in ["actual/365", "act/365"]: + return (d2-d1).days/365 + elif day_count_conv.lower() in ["actual/360", "act/360"]: + return (d2-d1).days/360 + +def DAforward_price(spread, trade_date, exercise_date, yield_curve, index): + cash_settle_date = (pd.Timestamp(trade_date) + 3* BDay()).date() + h, sc = flat_hazard(spread, yc, trade_date, cash_settle_date, + start_date = trade_date, + end_date = index.end_date, + recovery_rate = index.recovery) + exercise_date_settle = (pd.Timestamp(exercise_date) + 3* BDay()).date() + forward_price = index.pv(trade_date, exercise_date, yield_curve, sc, h)[0] + fep = (1 - index.recovery) * (1-math.exp(-h*year_frac(trade_date, exercise_date))) + df = yc.discount_factor(exercise_date_settle) + price = fep + 1/df * forward_price + return price + +def flat_hazard(spread, yc, trade_date=datetime.date.today(), + cash_settle_date = None, + start_date = datetime.date.today(), + end_date = datetime.date(2021, 6, 20), + recovery_rate = 0.4): + step_in_date = start_date + datetime.timedelta(days=1) + if cash_settle_date is None: + cash_settle_date = (pd.Timestamp(trade_date) + 3* BDay()).date() + sc = SpreadCurve(trade_date, yc, trade_date, step_in_date, + cash_settle_date, + [end_date], array.array('d', [spread]), recovery_rate) + sc_data = sc.inspect()['data'] + ## conversion to continuous compounding + hazard_rate = math.log(1 + sc_data[0][1]) + return (hazard_rate, SpreadCurve.from_flat_hazard(trade_date, hazard_rate)) + +def calib(S0, fp, forward_yield_curve, exercise_date_settle, index, tilt, w): + S = S0 * tilt + a, b = strike_vec(S, forward_yield_curve, exercise_date_settle, + index.start_date, index.end_date, index.recovery) + vec = a - index.fixed_rate * b + df = forward_yield_curve.discount_factor(exercise_date_settle) + return 1/df*np.inner(vec - fp, w) + +def g(spread, forward_yield_curve, index): + """ computes the strike price using the expected forward yield curve """ + exercise_date = forward_yield_curve.base_date + step_in_date = exercise_date + datetime.timedelta(days=1) + exercise_date_settle = (pd.Timestamp(exercise_date) + 3* BDay()).date() + sc = SpreadCurve(exercise_date, forward_yield_curve, exercise_date, step_in_date, + exercise_date_settle, + [index.end_date], array.array('d', [spread]), index.recovery) + a = index._fee_leg.pv(exercise_date, step_in_date, exercise_date, + forward_yield_curve, sc, True) + dl_pv = index._default_leg.pv( + exercise_date, step_in_date, exercise_date, forward_yield_curve, + sc, index.recovery) + df = forward_yield_curve.discount_factor(exercise_date_settle) + return 1/df * (dl_pv - a * index.fixed_rate) + +def ATMstrike(spread, trade_date, exercise_date, yield_curve, index): + fp = DAforward_price(spread, trade_date, exercise_date, yc, index) + yc_forward = yc.expected_forward_curve(exercise_date) + closure = lambda S: g(S, yc_forward, index) - fp + eta = 1.1 + a = spread + b = spread * eta + while True: + if closure(b) > 0: + break + return brentq(closure, a, b) + +def option(index, ref, trade_date, exercise_date, yield_curve, sigma, K, option_type="payer"): + """ computes the pv of an option using Pedersen's model """ + fp = DAforward_price(ref, trade_date, exercise_date, yield_curve, index) + forward_yc = yield_curve.expected_forward_curve(exercise_date) + #expiry is end of day (not sure if this is right) + T = year_frac(trade_date, exercise_date) + Z, w = GHquad(50) + tilt = np.exp(-sigma**2/2 * T + sigma * Z * math.sqrt(T)) + exercise_date_settle = (pd.Timestamp(exercise_date) + 3* BDay()).date() + args = (fp, forward_yc, exercise_date_settle, index, tilt, w) + ## atm forward is greater than spread + eta = 1.1 + a = ref + b = ref * eta + while True: + if calib(*((b,) + args)) > 0: + break + b *= eta + S0 = brentq(calib, a, b, args) + S = S0 * tilt + G = g(K, forward_yc, index) + handle = lambda Z: g(S0 * math.exp(-sigma**2/2 * T + sigma * Z * math.sqrt(T)), + forward_yc, index) - G + Zstar = brentq(handle, -3, 3) + if option_type.lower() == "payer": + Z = Zstar + np.logspace(0, 1.1, 300) - 1 + elif option_type.lower() == "receiver": + Z = Zstar - np.logspace(0, 1.1, 300) + 1 + else: + raise ValueError("option_type needs to be either 'payer' or 'receiver'") + S = S0 * np.exp(-sigma**2/2 * T + sigma * Z * math.sqrt(T)) + df = forward_yc.discount_factor(exercise_date_settle) + a, b = strike_vec(S, forward_yc, exercise_date_settle, + index.start_date, index.end_date, index.recovery) + val = ((a - b * index.fixed_rate)/df - G) * 1/math.sqrt(2*math.pi) * np.exp(-Z**2/2) + return simps(val, Z) * yield_curve.discount_factor(exercise_date_settle) diff --git a/python/test_option.py b/python/test_option.py index 061be7c8..daa52d81 100644 --- a/python/test_option.py +++ b/python/test_option.py @@ -1,24 +1,22 @@ -from pyisda.curve import YieldCurve, BadDay, SpreadCurve from pyisda.utils import build_yc -from pyisda.cdsone import upfront_charge -from pyisda.legs import ContingentLeg, FeeLeg + from quantlib.settings import Settings from quantlib.time.api import ( Date, today, Period, Years, WeekendsOnly, Quarterly, CDS, Actual360, - ModifiedFollowing, Actual365Fixed) + ModifiedFollowing, Actual365Fixed, Months) from quantlib.quotes import SimpleQuote from quantlib.termstructures.credit.api import ( SpreadCdsHelper, Interpolator, ProbabilityTrait, PiecewiseDefaultCurve ) -from quantlib.termstructures.yields.api import YieldTermStructure +from quantlib.termstructures.yields.api import YieldTermStructure, DepositRateHelper from quantlib.pricingengines.credit.isda_cds_engine import IsdaCdsEngine import datetime -import array import math import numpy as np import pandas as pd +from pandas.tseries.offsets import BusinessDay as BDay from db import dbengine from yieldcurve import YC -import pdb +from swaption import Index def get_spread(index, series): engine = dbengine("serenitasdb") @@ -34,6 +32,7 @@ def get_spread(index, series): def test_sc(df): settings = Settings() + settings.evaluation_date = today() tenors = [Period(3, Years), Period(5, Years), Period(7, Years), Period(10, Years)] quotes = [] for i in range(4): @@ -47,16 +46,17 @@ def test_sc(df): pdc = PiecewiseDefaultCurve(ProbabilityTrait.SurvivalProbability, Interpolator.LogLinear, 0, WeekendsOnly(), helpers, Actual365Fixed()) + ts = YC() + yts.link_to(ts) for r in df.itertuples(index=True, name = None): settings.evaluation_date = Date.from_datetime(r[0]) ts = YC() yts.link_to(ts) for i in range(4): quotes[i].value = r[i+1] *1e-4 - return helpers + print(pdc.dates) + print(pdc.data) -def FEP(sc, recovery): - pass if __name__ == "__main__": settings = Settings() @@ -64,47 +64,14 @@ if __name__ == "__main__": settings.evaluation_date = today() start_date = datetime.date(2016, 6, 20) term_date = datetime.date(2021, 6, 20) - - dc = YC() - isda_engine = "po" - value_date = trade_date + recovery = 0.4 + fixed_rate = 0.01 + ig26 = Index(start_date, term_date, recovery, fixed_rate) yc = build_yc(trade_date, True) - spread = 0.007 - spread = array.array('d', [spread]) - value_date = datetime.date(2016, 7, 29) - step_in_date = trade_date + datetime.timedelta(days=1) - sc = SpreadCurve(trade_date, yc, start_date, step_in_date, - value_date, [term_date], spread, 0.4, True) - sc_data = sc.inspect()['data'] - hazard_rate = math.log(1 + sc_data[0][1]) - expiration_date = datetime.date(2016, 9, 21) - fee_leg = FeeLeg(start_date, term_date, True, 1, 1) - default_leg = ContingentLeg(start_date, term_date, 1) - fee_leg_expiry = FeeLeg(start_date, expiration_date, True, 1, 1) - default_leg_expiry = ContingentLeg(start_date, expiration_date, 1) - - fee_leg_pv = fee_leg.pv(trade_date, step_in_date, trade_date, - yc, sc, True) - default_leg_pv = default_leg.pv(trade_date, step_in_date, trade_date, - yc, sc, 0.4) - V1 = default_leg_pv - fee_leg_pv - - fee_leg_expiry_pv = fee_leg_expiry.pv(trade_date, step_in_date, trade_date, - yc, sc, True) - default_leg_expiry_pv = default_leg_expiry.pv(trade_date, step_in_date, trade_date, - yc, sc, 0.4) - V2 = default_leg_expiry_pv - fee_leg_expiry_pv - - forward_annuity = fee_leg.pv(trade_date, expiration_date+datetime.timedelta(days=1), trade_date, - yc, sc, True) - exercise_settlement = datetime.date(2016, 9, 25) - print((0.007-0.01) * forward_annuity) - - forward_price = forward_annuity*(0.007-0.01) / ( - yc.discount_factor(exercise_settlement)*math.exp(-hazard_rate * (expiration_date-trade_date).days/365 - )) - print(fee_leg.pv(trade_date, step_in_date, trade_date, - yc, sc, True)) - - print(fee_leg.pv(trade_date, expiration_date + datetime.timedelta(days=1), trade_date, - yc, sc, True)) + exercise_date = datetime.date(2016, 10, 19) + exercise_date_settle = pd.Timestamp(exercise_date + 3* BDay()).date() + spread = np.array([65, 70, 72.5, 75, 77.5, 80, 85, 90, 95, 100, 110, 120, 130, 140]) + tailprob = np.array([64.2, 51.7, 45.8, 40.2, 35, 30.4, 23.2, 18, 14.3, 11.6, 7.9, 5.6, 4.1, 3]) + cumprob = 1-tailprob/100 + hist = cumprob/spread + hist = np.hstack((0, hist)) |
