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/basket_index.py104
2 files changed, 105 insertions, 0 deletions
diff --git a/python/analytics/__init__.py b/python/analytics/__init__.py
index 031df9cb..e23b7e64 100644
--- a/python/analytics/__init__.py
+++ b/python/analytics/__init__.py
@@ -1,3 +1,4 @@
from .index import Index, ForwardIndex
from .option import BlackSwaption, Swaption, VolatilitySurface, ATMstrike
from .portfolio import Portfolio
+from .basket_index import BasketIndex
diff --git a/python/analytics/basket_index.py b/python/analytics/basket_index.py
new file mode 100644
index 00000000..407f93bd
--- /dev/null
+++ b/python/analytics/basket_index.py
@@ -0,0 +1,104 @@
+import index_data as data
+from dateutil.relativedelta import relativedelta
+from pyisda.credit_index import CreditIndex
+from typing import List
+import pandas as pd
+import datetime
+from scipy.optimize import brentq
+
+class BasketIndex(CreditIndex):
+ index_type: str
+ series: int
+ recovery: float
+ step_in_date: pd.Timestamp
+ value_date: pd.Timestamp
+ tweaks: List[float]
+
+ def __init__(self, index_type: str, series: int, tenors: List[str], *args,
+ trade_date: pd.Timestamp = pd.Timestamp.today().normalize()):
+ self.index_type = index_type
+ self.series = series
+ if index_type == 'IG':
+ 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,
+ 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()
+ self.index_desc = self.index_desc.reset_index().set_index('maturity')
+ curves, args = data.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
+ self.value_date = value_date
+ self._trade_date = trade_date
+ self.tweaks = []
+ super().__init__(self.issue_date, maturities, curves)
+
+ @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)
+ _, jp_yc, _, step_in_date, value_date, _ = args
+ self.yc = jp_yc
+ self.step_in_date = step_in_date
+ self.value_date = value_date
+ self._trade_date = d
+ 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)
+
+ def duration(self, maturity):
+ return super().duration(self.step_in_date, self.value_date, maturity, self.yc)
+
+ def theta(self, maturity):
+ 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]
+ return super().theta(self.step_in_date, self.value_date, maturity,
+ self.yc, self.recovery, coupon, index_quote)
+ def tweak(self):
+ """ tweak the singlename curves to match index quotes"""
+ quotes = self.index_quotes.loc[self.trade_date]
+ self.tweaks = []
+ for m, index_quote in quotes.iteritems():
+ coupon = self.index_desc.loc[m, 'coupon']
+ lo, hi = -0.3, 0.3
+ while lo > -1:
+ try:
+ eps = brentq(lambda epsilon: self.pv(m, epsilon) -
+ index_quote, lo, hi)
+ except ValueError:
+ lo *= 1.1
+ hi *= 1.1
+ else:
+ break
+ else:
+ print("couldn't calibrate for date: {} and maturity: {}".
+ format(self.trade_date.date(), m.date()))
+ self.tweaks.append(np.NaN)
+ continue
+ self.tweaks.append(eps)
+ self.tweak_portfolio(eps, m)