aboutsummaryrefslogtreecommitdiffstats
path: root/python/analytics
diff options
context:
space:
mode:
Diffstat (limited to 'python/analytics')
-rw-r--r--python/analytics/index.py107
1 files changed, 78 insertions, 29 deletions
diff --git a/python/analytics/index.py b/python/analytics/index.py
index bc4f9299..1aa2ccc8 100644
--- a/python/analytics/index.py
+++ b/python/analytics/index.py
@@ -14,7 +14,6 @@ from db import dbengine
from sqlalchemy import exc
from pyisda.curve import SpreadCurve
from .utils import previous_twentieth
-from scipy.optimize import brentq
from yieldcurve import YC, ql_to_jp, roll_yc, rate_helpers
engine = dbengine('serenitasdb')
@@ -35,7 +34,7 @@ def g(index, spread, exercise_date, forward_yc=None, pv=0.):
if pv != 0.:
return 1e4 * pv / a + spread
else:
- return (spread - index.fixed_rate) * a *1e-4
+ return (spread - index.fixed_rate) * a * 1e-4
class Index(object):
""" minimal class to represent a credit index """
@@ -56,6 +55,7 @@ class Index(object):
self._start_date = start_date
self._end_date = end_date
self.recovery = recovery
+ self.factor = 1
self._fee_leg = FeeLeg(self._start_date, end_date, True, 1., 1.)
self._default_leg = ContingentLeg(self._start_date, end_date, True)
@@ -94,6 +94,21 @@ class Index(object):
return self._spread * 1e4
else:
return None
+ @property
+ def direction(self):
+ if self._direction == -1.:
+ return "Buyer"
+ else:
+ return "Seller"
+
+ @direction.setter
+ def direction(self, d):
+ if d == "Buyer":
+ self._direction = -1.
+ elif d == "Seller":
+ self._direction = 1.
+ else:
+ raise ValueError("Direction needs to be either 'Buyer' or 'Seller'")
def _update(self):
self._sc = SpreadCurve(self.trade_date, self._yc, self.start_date,
@@ -122,15 +137,22 @@ class Index(object):
def flat_hazard(self):
sc_data = self._sc.inspect()['data']
## conversion to continuous compounding
- return math.log1p(sc_data[0][1])
+ return sc_data[0][1]
@property
def pv(self):
- return self.notional * self._pv
+ return - self._direction * self.notional * self.factor * self._pv
+
+ @pv.setter
+ def pv(self, val):
+ self._pv = val / (self.notional * self.factor)
+ self._clean_pv = self._pv + self._accrued * self.fixed_rate * 1e-4
+ self.price = 100 * (1- self._clean_pv)
@property
def accrued(self):
- return - self.notional * self._accrued * self.fixed_rate * 1e-4
+ return self._direction * self.notional * self.factor * self._accrued * \
+ self.fixed_rate * 1e-4
@property
def days_accrued(self):
@@ -138,7 +160,7 @@ class Index(object):
@property
def clean_pv(self):
- return self.notional * self._clean_pv
+ return - self._direction * self.notional * self.factor * self._clean_pv
@property
def price(self):
@@ -147,19 +169,23 @@ class Index(object):
@price.setter
def price(self, val):
if self._price is None or math.fabs(val-self._price) > 1e-6:
- def handle(x, self, val):
- self._spread = x
- self._update()
- return val - self.price
- eta = 1.2
- a = self.fixed_rate*1e-4 * 0.5
- b = a * eta
- while True:
- if handle(b, self, val) > 0:
- break
- b *= eta
- self._spread = brentq(handle, a, b, args = (self, val))
- self._update()
+ self._clean_pv = (100 - val) / 100
+ self._sc = SpreadCurve(
+ self.trade_date, self._yc, self.start_date,
+ self._step_in_date, self._value_date,
+ [self.end_date], array.array('d',[self.fixed_rate*1e-4]),
+ array.array('d', [self._clean_pv]),
+ array.array('d', [self.recovery]))
+ self._risky_annuity = self._fee_leg.pv(
+ self.trade_date, self._step_in_date, self._value_date,
+ self._yc, self._sc, False)
+ self._dl_pv = self._default_leg.pv(
+ self.trade_date, self._step_in_date, self._value_date,
+ self._yc, self._sc, self.recovery)
+ self._pv = self._clean_pv - self._accrued * self.fixed_rate * 1e-4
+ self._spread = self._clean_pv / (self._risky_annuity - self._accrued) \
+ + self.fixed_rate * 1e-4
+ self._price = val
@property
def ref(self):
@@ -219,7 +245,8 @@ class Index(object):
@property
def jump_to_default(self):
- return self.notional * (1 - self.recovery) - self.clean_pv
+ return self.notional * self.factor * self._direction * \
+ (self.recovery + self._clean_pv - 1)
@property
def risky_annuity(self):
@@ -254,20 +281,28 @@ class Index(object):
@classmethod
def from_name(cls, index, series, tenor, trade_date=datetime.date.today(),
- notional=10e6):
+ notional=10_000_000):
try:
- r = engine.execute("SELECT maturity, coupon FROM index_maturity " \
- "WHERE index=%s AND series=%s AND tenor = %s",
- (index.upper(), series, tenor))
- maturity, coupon = r.fetchone()
+ df = pd.read_sql_query(
+ "SELECT indexfactor, lastdate, maturity, coupon " \
+ "FROM index_desc WHERE index=%s AND series=%s AND tenor = %s "
+ "ORDER BY lastdate ASC",
+ engine, parse_dates=['lastdate'],
+ params=(index.upper(), series, tenor))
+ maturity = df.maturity[0]
+ coupon = df.coupon[0]
+ df.loc[df.lastdate.isnull(),'lastdate'] = maturity
+ factor = df.loc[df.lastdate >= pd.Timestamp(trade_date),
+ 'indexfactor'].iat[0]/100
except exc.DataError as e:
- raise
- except TypeError:
- raise ValueError("Index not found")
+ print(e)
+ return None
else:
recovery = 0.4 if index.lower() == "ig" else 0.3
instance = cls(trade_date, maturity, recovery, coupon, notional,
index.upper()=="HY")
+ instance.factor = factor
+ instance.direction = "Buyer"
instance.name = "MARKIT CDX.NA.{}.{} {:%m/%y} ".format(
index.upper(),
series,
@@ -276,10 +311,24 @@ class Index(object):
instance.currency = "USD"
else:
instance.currency = "EUR"
- instance.notional = notional
instance.trade_date = trade_date
return instance
+ @classmethod
+ def from_tradeid(cls, trade_id):
+ engine = dbengine('dawndb')
+ r = engine.execute("SELECT * from cds WHERE id=%s", (trade_id,))
+ rec = r.fetchone()
+ recovery = 0.4 if "IG" in rec.security_desc else 0.3
+ instance = cls(rec.trade_date, rec.maturity, recovery, rec.fixed_rate * 100,
+ rec.notional, recovery==0.3)
+ instance.name = rec.security_desc
+ instance.currency = rec.currency
+ instance.direction = rec.protection
+ instance.trade_date = rec.trade_date
+ instance.pv = rec.upfront
+ return instance
+
def __repr__(self):
if self.days_accrued > 1:
accrued_str = "Accrued ({} Days)".format(self.days_accrued)