aboutsummaryrefslogtreecommitdiffstats
path: root/python/analytics
diff options
context:
space:
mode:
Diffstat (limited to 'python/analytics')
-rw-r--r--python/analytics/__init__.py1
-rw-r--r--python/analytics/credit_default_swap.py46
-rw-r--r--python/analytics/index_data.py43
-rw-r--r--python/analytics/singlename_cds.py51
-rw-r--r--python/analytics/utils.py7
5 files changed, 132 insertions, 16 deletions
diff --git a/python/analytics/__init__.py b/python/analytics/__init__.py
index 62933b5b..8caeec55 100644
--- a/python/analytics/__init__.py
+++ b/python/analytics/__init__.py
@@ -15,6 +15,7 @@ from .option import (
)
from .portfolio import Portfolio
from .basket_index import MarkitBasketIndex
+from .singlename_cds import SingleNameCds
from .tranche_basket import DualCorrTranche, TrancheBasket
from .ir_swaption import IRSwaption
diff --git a/python/analytics/credit_default_swap.py b/python/analytics/credit_default_swap.py
index 5bddbf73..a2464f94 100644
--- a/python/analytics/credit_default_swap.py
+++ b/python/analytics/credit_default_swap.py
@@ -13,6 +13,7 @@ from pyisda.date import previous_twentieth
from pyisda.legs import ContingentLeg, FeeLeg
from termcolor import colored
from .utils import build_table, get_fx
+from typing import Union
from weakref import WeakSet
from yieldcurve import get_curve, rate_helpers, YC, ql_to_jp
@@ -51,7 +52,13 @@ class CreditDefaultSwap:
)
def __init__(
- self, start_date, end_date, recovery, fixed_rate, notional=10e6, issue_date=None
+ self,
+ start_date: datetime.date,
+ end_date: datetime.date,
+ recovery: float,
+ fixed_rate: float,
+ notional: float = 10e6,
+ issue_date: Union[datetime.date, None] = None,
):
"""
start_date : :class:`datetime.date`
@@ -134,6 +141,15 @@ class CreditDefaultSwap:
def spread(self):
if self._spread is not None:
return self._spread * 1e4
+ elif self._sc is not None:
+ return self._sc.par_spread(
+ self.value_date,
+ self._step_in_date,
+ self.start_date,
+ [self.end_date],
+ np.array([self.recovery]),
+ self._yc,
+ )
else:
return None
@@ -154,17 +170,18 @@ class CreditDefaultSwap:
raise ValueError("Direction needs to be either 'Buyer' or 'Seller'")
def _update(self):
- self._sc = SpreadCurve(
- self._yc.base_date,
- self._yc,
- self.start_date,
- self._step_in_date,
- self._cash_settle_date,
- [self.end_date],
- np.array([self._spread]),
- np.zeros(1),
- np.array([self.recovery]),
- )
+ if self._spread is not None:
+ self._sc = SpreadCurve(
+ self._yc.base_date,
+ self._yc,
+ self.start_date,
+ self._step_in_date,
+ self._cash_settle_date,
+ [self.end_date],
+ np.array([self._spread]),
+ np.zeros(1),
+ np.array([self.recovery]),
+ )
self._risky_annuity = self._fee_leg.pv(
self.value_date,
@@ -189,7 +206,7 @@ class CreditDefaultSwap:
@spread.setter
def spread(self, s):
""" s: spread in bps """
- if self.spread is None or s != self.spread:
+ if self._spread is None or s != self.spread:
self._spread = s * 1e-4
self._update()
self.notify()
@@ -361,8 +378,7 @@ class CreditDefaultSwap:
self._step_in_date = d + datetime.timedelta(days=1)
self._accrued = self._fee_leg.accrued(self._step_in_date)
self._cash_settle_date = pd.Timestamp(self._value_date) + 3 * BDay()
- if self._spread is not None:
- self._update()
+ self._update()
self.notify()
def reset_pv(self):
diff --git a/python/analytics/index_data.py b/python/analytics/index_data.py
index 29d50cbd..57ad45cc 100644
--- a/python/analytics/index_data.py
+++ b/python/analytics/index_data.py
@@ -4,7 +4,7 @@ import numpy as np
from .utils import tenor_t
from functools import lru_cache
-from pyisda.curve import SpreadCurve, Seniority, DocClause
+from pyisda.curve import SpreadCurve, Seniority, DocClause, YieldCurve
from multiprocessing import Pool
from yieldcurve import get_curve
@@ -244,3 +244,44 @@ def get_tranche_quotes(index_type, series, tenor, date=datetime.date.today()):
df = pd.DataFrame.from_records(dict(d) for d in c)
serenitas_pool.putconn(conn)
return df
+
+
+def get_singlename_curve(
+ ticker: str,
+ seniority: str,
+ doc_clause: str,
+ value_date: datetime.date,
+ yieldcurve: YieldCurve,
+ source: str = "MKIT",
+):
+ conn = serenitas_pool.getconn()
+ with conn.cursor() as c:
+ c.execute(
+ "SELECT * FROM cds_quotes "
+ "JOIN (SELECT UNNEST(cds_curve) AS curve_ticker, "
+ " UNNEST(ARRAY[0.5, 1., 2., 3., 4., 5., 7., 10.]::float[]) AS tenor"
+ " FROM bbg_issuers"
+ " JOIN bbg_markit_mapping USING (company_id, seniority)"
+ " WHERE markit_ticker=%s and seniority=%s) a "
+ "USING (curve_ticker) WHERE date=%s AND source=%s ORDER BY tenor",
+ (ticker, seniority, value_date, source),
+ )
+ df = pd.DataFrame(c, columns=[col.name for col in c.description])
+ serenitas_pool.putconn(conn)
+ spread_curve = 0.5 * (df.runningbid + df.runningask).values * 1e-4
+ upfront_curve = 0.5 * (df.upfrontbid + df.upfrontask).values * 1e-2
+ return SpreadCurve(
+ value_date,
+ yieldcurve,
+ None,
+ None,
+ None,
+ df.tenor.values,
+ spread_curve,
+ upfront_curve,
+ df.recovery.values,
+ ticker=ticker,
+ seniority=Seniority[seniority],
+ doc_clause=DocClause[doc_clause],
+ defaulted=None,
+ )
diff --git a/python/analytics/singlename_cds.py b/python/analytics/singlename_cds.py
new file mode 100644
index 00000000..3c59f039
--- /dev/null
+++ b/python/analytics/singlename_cds.py
@@ -0,0 +1,51 @@
+from .credit_default_swap import CreditDefaultSwap
+from .index_data import get_singlename_curve
+from pyisda.date import previous_twentieth, roll_date
+from .utils import tenor_to_float
+from typing import Union
+from yieldcurve import get_curve
+
+import datetime
+
+
+class SingleNameCds(CreditDefaultSwap):
+ __slots__ = ("ticker", "seniority", "doc_clause", "tenor")
+
+ def __init__(
+ self,
+ ticker: str,
+ seniority: str = "Senior",
+ doc_clause: str = "XR14",
+ tenor: str = "5yr",
+ *,
+ end_date: Union[datetime.date, None] = None,
+ recovery: float = 0.4,
+ fixed_rate: float = 100.0,
+ notional: float = 10e6,
+ currency: str = "USD",
+ value_date: datetime.date = datetime.date.today()
+ ):
+
+ if end_date is None:
+ end_date = roll_date(value_date, tenor_to_float(tenor))
+
+ super().__init__(
+ previous_twentieth(value_date), end_date, recovery, fixed_rate, notional
+ )
+
+ self.ticker = ticker
+ self.seniority = seniority
+ self.doc_clause = doc_clause
+ self.tenor = tenor
+ self.currency = currency
+ self.value_date = value_date
+
+ value_date = property(CreditDefaultSwap.value_date.__get__)
+
+ @value_date.setter
+ def value_date(self, d: datetime.date):
+ self._yc = get_curve(d, self.currency)
+ self._sc = get_singlename_curve(
+ self.ticker, self.seniority, self.doc_clause, d, self._yc
+ )
+ CreditDefaultSwap.value_date.__set__(self, d)
diff --git a/python/analytics/utils.py b/python/analytics/utils.py
index 3d5c0da4..c0da47f6 100644
--- a/python/analytics/utils.py
+++ b/python/analytics/utils.py
@@ -69,6 +69,13 @@ def next_third_wed(d):
return y
+def tenor_to_float(t: str):
+ if t == "6m":
+ return 0.5
+ else:
+ return float(t.rstrip("yr"))
+
+
def roll_date(d, tenor, nd_array=False):
""" roll date d to the next CDS maturity"""
cutoff = pd.Timestamp("2015-09-20")