aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--python/analytics/basket_index.py84
-rw-r--r--python/tranche_functions.py31
2 files changed, 78 insertions, 37 deletions
diff --git a/python/analytics/basket_index.py b/python/analytics/basket_index.py
index 261197cc..f41494fc 100644
--- a/python/analytics/basket_index.py
+++ b/python/analytics/basket_index.py
@@ -1,4 +1,6 @@
-import index_data as data
+from index_data import (get_index_quotes, get_singlenames_curves,
+ get_tranche_quotes, _serenitas_engine)
+from tranche_functions import credit_schedule
from dateutil.relativedelta import relativedelta
from pyisda.credit_index import CreditIndex
from typing import List
@@ -7,6 +9,7 @@ import pandas as pd
import datetime
from scipy.optimize import brentq
from pandas.tseries.offsets import BDay
+from pyisda.cdsone import upfront_charge
class BasketIndex(CreditIndex):
index_type: str
@@ -24,25 +27,18 @@ class BasketIndex(CreditIndex):
self.recovery = 0.4
else:
self.recovery = 0.3
- self.index_quotes = (data.get_index_quotes(index_type, series,
- tenors, years=None)['closeprice'].
- unstack().
- groupby(level='date', as_index=False).nth(0).
- reset_index(['index', 'series'], drop=True))
self.index_desc = pd.read_sql_query("SELECT tenor, maturity, coupon * 1e-4 AS coupon, " \
"issue_date "\
"FROM index_maturity " \
"WHERE index=%s AND series=%s",
- data._serenitas_engine,
+ _serenitas_engine,
index_col='tenor',
params=(index_type, series),
parse_dates=['maturity', 'issue_date'])
- self.index_quotes.columns = self.index_desc.loc[self.index_quotes.columns, "maturity"]
- self.index_quotes = 1 - self.index_quotes / 100
self.issue_date = self.index_desc.issue_date[0]
- maturities = self.index_quotes.columns.sort_values().to_pydatetime()
+ maturities = self.index_desc.maturity.sort_values().dt.to_pydatetime()
self.index_desc = self.index_desc.reset_index().set_index('maturity')
- curves, args = data.get_singlenames_curves(index_type, series, trade_date)
+ curves, args = get_singlenames_curves(index_type, series, trade_date)
_, jp_yc, _, step_in_date, value_date, _ = args
self.yc = jp_yc
self.step_in_date = step_in_date
@@ -51,13 +47,16 @@ class BasketIndex(CreditIndex):
self.tweaks = []
super().__init__(self.issue_date, maturities, curves)
+ def _get_quotes(self):
+ pass
+
@property
def trade_date(self):
return self._trade_date
@trade_date.setter
def trade_date(self, d: pd.Timestamp):
- curves, args = data.get_singlenames_curves(self.index_type, self.series, d)
+ curves, args = get_singlenames_curves(self.index_type, self.series, d)
_, jp_yc, _, step_in_date, value_date, _ = args
self.yc = jp_yc
self.step_in_date = step_in_date
@@ -66,9 +65,8 @@ class BasketIndex(CreditIndex):
self.curves = curves
def pv(self, maturity: pd.Timestamp, epsilon=0.):
- coupon = self.index_desc.loc[maturity, 'coupon']
return super().pv(self.step_in_date, self.value_date, maturity, self.yc,
- self.recovery, coupon, epsilon)
+ self.recovery, self.coupon(maturity), epsilon)
def duration(self, maturity):
return super().duration(self.step_in_date, self.value_date, maturity, self.yc)
@@ -77,16 +75,17 @@ class BasketIndex(CreditIndex):
if self.step_in_date.date() > maturity - relativedelta(years=1):
return np.NaN
else:
- coupon = self.index_desc.loc[maturity, 'coupon']
- index_quote = self.index_quotes.loc[self.trade_date,maturity]
+ index_quote = self.index_quotes.loc[self.trade_date, maturity]
return super().theta(self.step_in_date, self.value_date, maturity,
- self.yc, self.recovery, coupon, index_quote)
+ self.yc, self.recovery, self.coupon(maturity), index_quote)
+ def coupon(self, maturity):
+ return self.index_desc.loc[maturity, 'coupon']
+
def tweak(self):
""" tweak the singlename curves to match index quotes"""
- quotes = self.index_quotes.loc[self.trade_date]
+ quotes = self._get_quotes()
self.tweaks = []
- for m, index_quote in quotes.iteritems():
- coupon = self.index_desc.loc[m, 'coupon']
+ for m, index_quote in quotes.items():
lo, hi = -0.3, 0.3
while lo > -1:
try:
@@ -105,6 +104,49 @@ class BasketIndex(CreditIndex):
self.tweaks.append(eps)
self.tweak_portfolio(eps, m)
+class MarkitBasketIndex(BasketIndex):
+ def __init__(self, index_type: str, series: int, tenors: List[str], *args,
+ trade_date: pd.Timestamp=pd.Timestamp.today().normalize() - BDay()):
+ super().__init__(index_type, series, tenors, *args, trade_date)
+ self.index_quotes = (get_index_quotes(index_type, series,
+ tenors, years=None)['closeprice'].
+ unstack().
+ groupby(level='date', as_index=False).nth(0).
+ reset_index(['index', 'series', 'version'], drop=True))
+ self.index_quotes.columns = self.index_desc.loc[self.index_quotes.columns, "maturity"]
+ self.index_quotes = 1 - self.index_quotes / 100
+
+ def _get_quotes(self):
+ return self.index_quotes.loc[self.trade_date]
+
+class TrancheBasket(BasketIndex):
+ def __init__(self, index_type: str, series: int, tenor: str, *args,
+ trade_date: pd.Timestamp=pd.Timestamp.today().normalize() - BDay()):
+ super().__init__(index_type, series, [tenor], *args, trade_date)
+ self.tranche_quotes = get_tranche_quotes(index_type, series, tenor, trade_date.date())
+ index_desc = self.index_desc.reset_index('maturity').set_index('tenor')
+ self.maturity = index_desc.loc[tenor].maturity
+ self.start_date, self.cs = credit_schedule(trade_date, tenor[:-1], 1, self.yc)
+ self.K_orig = [0] + [q['detach'] for q in self.tranche_quotes]
+ self.K = adjust_attachments(self.K_orig, self
+
+ def _get_quotes(self):
+ refprice = self.tranche_quotes[0]['indexrefprice']
+ refspread = self.tranche_quotes[0]['indexrefspread']
+ if refprice is not None:
+ return {self.maturity: 1 - refprice / 100}
+ if refspread is not None:
+ return {self.maturity:
+ upfront_charge(self.trade_date, self.value_date, self.start_date,
+ self.step_in_date, self.start_date, self.maturity,
+ self.coupon(self.maturity), self.yc,
+ refspread * 1e-4, self.recovery)}
+ raise ValueError("ref is missing")
+
+ @property
+ def survival_matrix(self):
+ return super().survival_matrix(self.cs.index.values.astype('M8[D]').view('int') + 134774)
+
if __name__ == "__main__":
ig28 = BasketIndex("IG", 28, ["3yr", "5yr", "7yr", "10yr"])
from quantlib.time.api import Schedule, Rule, Date, Period, WeekendsOnly
@@ -112,5 +154,5 @@ if __name__ == "__main__":
settings = Settings()
cds_schedule = Schedule.from_rule(settings.evaluation_date, Date.from_datetime(ig28.maturities[-1]),
- Period('3M'), WeekendsOnly(), date_generation_rule=Rule.CDS2015)
+ Period('3M'), WeekendsOnly(), date_generation_rule=Rule.CDS2015)
sp = ig28.survival_matrix(cds_schedule.to_npdates().view('int') + 134774)
diff --git a/python/tranche_functions.py b/python/tranche_functions.py
index 28481130..a92044e3 100644
--- a/python/tranche_functions.py
+++ b/python/tranche_functions.py
@@ -1,8 +1,8 @@
import numpy as np
from ctypes import *
from numpy.ctypeslib import ndpointer
-from quantlib.time.schedule import Schedule, CDS
-from quantlib.time.api import Actual360, Period, UnitedStates, Following, today
+from quantlib.time.schedule import Schedule, CDS2015
+from quantlib.time.api import Actual360, Period, WeekendsOnly, ModifiedFollowing, Unadjusted, today
from quantlib.util.converter import qldate_to_pydate, pydate_to_qldate
import pandas as pd
from scipy.special import h_roots
@@ -284,33 +284,32 @@ def tranche_pl(L, cs, K1, K2, scaled=False):
def tranche_pv(L, R, cs, K1, K2):
return tranche_pl(L, cs, K1, K2) + tranche_cl(L, R, cs, K2, K2)
-def creditSchedule(tradedate, tenor, coupon, yc, enddate = None):
+def credit_schedule(tradedate, tenor, coupon, yc, enddate=None):
tradedate = pydate_to_qldate(tradedate)
- start = tradedate - Period('4Mo')
- enddate = pydate_to_qldate(enddate)
if enddate is None:
enddate = tradedate + Period(tenor)
- cal = UnitedStates()
+ else:
+ enddate = pydate_to_qldate(enddate)
+ cal = WeekendsOnly()
DC = Actual360()
- sched = Schedule(start, enddate, Period('3Mo'), cal, date_generation_rule=CDS)
- prevpaydate = sched.previous_date(tradedate)
- sched = [d for d in sched if d>=prevpaydate]
- df = [yc.discount(d) for d in sched if d > tradedate]
-
- dates = pd.to_datetime([str(d) for d in sched if d > tradedate], "%m/%d/%Y")
- coupons = np.diff([DC.year_fraction(prevpaydate, d) * coupon for d in sched])
+ sched = Schedule.from_rule(tradedate, enddate, Period('3M'), cal,
+ ModifiedFollowing, Unadjusted, CDS2015)
+ dates = sched.to_npdates()
+ pydates = dates.astype('O')
+ df = [yc.discount_factor(d) for d in pydates if d > tradedate]
+ coupons = [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)
- return pd.DataFrame({"df":df, "coupons":coupons}, dates)
+ return pydates[0], pd.DataFrame({"df": df, "coupons": coupons}, dates[1:])
def cdsAccrued(tradedate, coupon):
tradedate = pydate_to_qldate(tradedate)
- start = tradedate - Period('3Mo')
end = tradedate + Period('3Mo')
start_protection = tradedate + 1
DC = Actual360()
cal = UnitedStates()
- sched = Schedule(start, end, Period('3Mo'), cal, date_generation_rule=CDS)
+ sched = Schedule(start, end, Period('3Mo'), cal, date_generation_rule=CDS2015)
prevpaydate = sched.previous_date(tradedate)
return DC.year_fraction(prevpaydate, start_protection) * coupon