diff options
| -rw-r--r-- | python/analytics/tranche_functions.py | 66 |
1 files changed, 50 insertions, 16 deletions
diff --git a/python/analytics/tranche_functions.py b/python/analytics/tranche_functions.py index 00fd0ec1..606457cd 100644 --- a/python/analytics/tranche_functions.py +++ b/python/analytics/tranche_functions.py @@ -1,17 +1,21 @@ import numpy as np from ctypes import POINTER, c_int, c_double, byref from numpy.ctypeslib import ndpointer -from quantlib.time.schedule import Schedule, CDS2015 +from pyisda.legs import FeeLeg +from pyisda.date import previous_twentieth +from quantlib.time.schedule import Schedule, CDS2015, OldCDS from quantlib.time.api import ( Actual360, + Date, Period, WeekendsOnly, ModifiedFollowing, Unadjusted, + pydate_from_qldate, ) -from quantlib.util.converter import pydate_to_qldate import pandas as pd from scipy.special import h_roots +from .utils import next_twentieth import os @@ -441,32 +445,62 @@ def tranche_pv(L, R, cs, K1, K2): return tranche_pl(L, cs, K1, K2) + tranche_cl(L, R, cs, K2, K2) -def credit_schedule(tradedate, tenor, coupon, yc, enddate=None): - tradedate = pydate_to_qldate(tradedate) +def credit_schedule(tradedate, coupon, yc, enddate=None, tenor=None, rule=CDS2015): + tradedate = Date.from_datetime(tradedate) if enddate is None: enddate = tradedate + Period(tenor) else: - enddate = pydate_to_qldate(enddate) + enddate = Date.from_datetime(enddate) cal = WeekendsOnly() DC = Actual360() start_date = tradedate + 1 sched = Schedule.from_rule( - tradedate, enddate, Period("3M"), cal, ModifiedFollowing, Unadjusted, CDS2015 + start_date, enddate, Period("3M"), cal, ModifiedFollowing, Unadjusted, rule ) - dates = sched.to_npdates() - pydates = dates.astype("O") - df = [yc.discount_factor(d) for d in pydates if d > start_date] + payment_dates = [pydate_from_qldate(cal.adjust(d)) for d in sched[1:]] + df = [yc.discount_factor(d) for d in payment_dates] coupons = [ - DC.year_fraction(d1, d2) * coupon - for d1, d2 in zip(sched[:-2], sched[1:-1]) - if d2 > start_date + DC.year_fraction(d1, d2) * coupon for d1, d2 in zip(sched[:-2], sched[1:-1]) ] coupons.append(Actual360(True).year_fraction(sched[-2], sched[-1]) * coupon) - if dates[1] <= start_date: - dates = dates[2:] + dates = sched.to_npdates() + start_dates = dates[:-1] + end_dates = dates[1:] + return pd.DataFrame( + { + "df": df, + "coupons": coupons, + "start_dates": start_dates, + "payment_dates": payment_dates, + }, + index=end_dates, + ) + + +def credit_schedule_pyisda( + tradedate, coupon, yc, enddate=None, tenor=None, rule=CDS2015 +): + tradedate = Date.from_datetime(tradedate) + if enddate is None: + enddate = tradedate + Period(tenor) else: - dates = dates[1:] - return pd.DataFrame({"df": df, "coupons": coupons}, index=dates) + enddate = Date.from_datetime(enddate) + start_date = pydate_from_qldate(tradedate + 1) + if (next_twentieth(start_date) - start_date).days < 30: + stub = "f/l" + else: + stub = "f/s" + print(stub) + if rule is CDS2015: + start_date = previous_twentieth(start_date) + fl = FeeLeg(start_date, pydate_from_qldate(enddate), True, 1.0, coupon, stub=stub) + df = pd.DataFrame({"coupons": [t[1] for t in fl.cashflows], **fl.inspect()}) + df["df"] = [yc.discount_factor(d) for d in df.pay_dates] + + df = df.rename( + columns={"acc_start_dates": "start_dates", "pay_dates": "payment_dates"} + ).set_index("acc_end_dates") + return df def cds_accrued(tradedate, coupon): |
