diff options
| -rw-r--r-- | python/analytics/index.py | 27 | ||||
| -rw-r--r-- | python/cds_curve.py | 10 | ||||
| -rw-r--r-- | python/yieldcurve.py | 68 |
3 files changed, 60 insertions, 45 deletions
diff --git a/python/analytics/index.py b/python/analytics/index.py index a9b6ab6a..87f5471d 100644 --- a/python/analytics/index.py +++ b/python/analytics/index.py @@ -8,8 +8,6 @@ import warnings from dateutil.relativedelta import relativedelta from pyisda.legs import ContingentLeg, FeeLeg -from quantlib.settings import Settings -from quantlib.time.api import Date, Actual365Fixed from termcolor import colored from pandas.tseries.offsets import BDay from sqlalchemy import exc @@ -21,6 +19,7 @@ from bbg_helpers import BBG_IP, retrieve_data, init_bbg_session from yieldcurve import get_curve, rate_helpers, YC, ql_to_jp from weakref import WeakSet + def g(index, spread, exercise_date, pv=None): """computes the strike clean price using the expected forward yield curve. """ step_in_date = exercise_date + datetime.timedelta(days=1) @@ -173,7 +172,7 @@ class Index(object): @property def flat_hazard(self): sc_data = self._sc.inspect()['data'] - ## conversion to continuous compounding + # conversion to continuous compounding return sc_data[0][1] @property @@ -210,7 +209,7 @@ class Index(object): self._sc = SpreadCurve( self.value_date, self._yc, self.start_date, self._step_in_date, self._cash_settle_date, - [self.end_date], array.array('d',[self.fixed_rate*1e-4]), + [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( @@ -269,7 +268,7 @@ class Index(object): rh.quote += 1e-4 ql_yc = YC(helpers) self._yc = ql_to_jp(ql_yc) - self._update() ## to force recomputation + self._update() # to force recomputation new_pv = self.pv # for r in self._helpers: # r.quote -= 1e-4 @@ -279,7 +278,7 @@ class Index(object): @property def rec_risk(self): - old_pv, old_recovery = self.pv, self.recovery + old_recovery = self.recovery self.recovery = old_recovery - 0.01 self._update() pv_minus = self.pv @@ -315,7 +314,7 @@ class Index(object): self._value_date = d 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() + self._cash_settle_date = pd.Timestamp(self._value_date) + 3 * BDay() if self._spread is not None: self._update() self.notify() @@ -329,7 +328,7 @@ class Index(object): if self._original_clean_pv is None: raise ValueError("original pv not set") else: - ## TODO: handle factor change + # TODO: handle factor change days_accrued = (self.value_date - self._trade_date).days / 360 return - self._direction * self.notional * self.factor * \ (self._clean_pv - self._original_clean_pv - @@ -397,7 +396,7 @@ class Index(object): coupon = df.coupon[0] if tenor is None: tenor = df.tenor[0] - index_type = index.upper() if index else df.loc[0,'index'] + index_type = index.upper() if index else df.loc[0, 'index'] series = series if series else df.series.iat[0] df.loc[df.lastdate.isnull(), 'lastdate'] = maturity except exc.DataError as e: @@ -408,7 +407,7 @@ class Index(object): instance = cls(value_date, maturity, recovery, coupon, notional, index_type == "HY", df.issue_date[0]) instance._version = tuple((ld.date(), factor / 100, version) for ld, factor, version in \ - df[['lastdate', 'indexfactor', 'version']].itertuples(index=False)) + df[['lastdate', 'indexfactor', 'version']].itertuples(index=False)) instance.index_type = index_type instance.series = series instance.tenor = tenor @@ -428,7 +427,7 @@ class Index(object): @classmethod def from_tradeid(cls, trade_id): - engine = dbengine('dawndb') + engine = dbengine('dawndb') r = engine.execute(""" SELECT * FROM cds LEFT JOIN index_desc @@ -465,7 +464,7 @@ class Index(object): accrued_str = "Accrued ({} Day)".format(self.days_accrued) s = ["{:<20}\tNotional {:>5}MM {}\tFactor {:>28}".format("Buy Protection"\ - if self._direction == -1 \ + if self._direction == -1 else "Sell Protection", self.notional/1_000_000, self.currency, @@ -482,7 +481,7 @@ class Index(object): [None, None, None, None]] s += build_table(rows, format_strings, "{:<20}{:>19}\t\t{:<20}{:>15}") s += ["", - colored("Calculator", attrs = ['bold'])] + colored("Calculator", attrs=['bold'])] rows = [["Valuation Date", self.value_date], ["Cash Settled On", self._cash_settle_date]] format_strings = [[None, '{:%m/%d/%y}'], @@ -511,7 +510,7 @@ class ForwardIndex(object): self.forward_date = forward_date.date() else: self.forward_date = forward_date - self.exercise_date_settle = pd.Timestamp(forward_date) + 3* BDay() + self.exercise_date_settle = pd.Timestamp(forward_date) + 3 * BDay() self.df = index._yc.discount_factor(self.exercise_date_settle) self._update() if observer: diff --git a/python/cds_curve.py b/python/cds_curve.py index 235f8fa0..ab13a9ef 100644 --- a/python/cds_curve.py +++ b/python/cds_curve.py @@ -2,12 +2,11 @@ from analytics.basket_index import MarkitBasketIndex from pyisda.legs import FeeLeg, ContingentLeg from pyisda.logging import enable_logging -import datetime -import numpy as np import pandas as pd from db import dbconn + def all_curves_pv(curves, today_date, jp_yc, start_date, step_in_date, value_date, maturities): r = {} for d in maturities: @@ -26,7 +25,7 @@ def all_curves_pv(curves, today_date, jp_yc, start_date, step_in_date, value_dat r[pd.Timestamp(d)] = pd.DataFrame.from_records(data, index=tickers, columns=['duration', 'protection_pv']) - return pd.concat(r, axis=1).swaplevel(axis=1).sort_index(axis=1,level=0) + return pd.concat(r, axis=1).swaplevel(axis=1).sort_index(axis=1, level=0) def calibrate_portfolio(index_type, series, tenors=['3yr', '5yr', '7yr', '10yr'], @@ -43,7 +42,8 @@ def calibrate_portfolio(index_type, series, tenors=['3yr', '5yr', '7yr', '10yr'] 'tweak': index.tweaks}, index=tenors) return pd.concat(r) -if __name__=="__main__": + +if __name__ == "__main__": enable_logging() import argparse import sys @@ -70,7 +70,7 @@ if __name__=="__main__": with conn.cursor() as c: for k, s in df.iterrows(): - c.execute("UPDATE index_quotes SET duration2=%s, theta2=%s "\ + c.execute("UPDATE index_quotes SET duration2=%s, theta2=%s " "WHERE date=%s AND tenor=%s AND index=%s AND series=%s", (s.duration, s.theta, k[0], k[1], index, series)) conn.commit() diff --git a/python/yieldcurve.py b/python/yieldcurve.py index 7ef24a59..6e0cc3bf 100644 --- a/python/yieldcurve.py +++ b/python/yieldcurve.py @@ -23,12 +23,13 @@ from pyisda.curve import YieldCurve from pyisda.date import BadDay import warnings + def load_curves(currency="USD", date=None): """load the prebuilt curves from the database""" + sql_str = f"SELECT * FROM {currency}_curves" if date: - sql_str = "SELECT curve FROM {}_curves WHERE effective_date=%s".format(currency) - else: - sql_str = "SELECT * FROM {}_curves".format(currency) + sql_str += " WHERE effective_date=%s" + with closing(dbconn('serenitasdb')) as conn: with conn.cursor() as c: if date: @@ -42,6 +43,7 @@ def load_curves(currency="USD", date=None): (d, YieldCurve.from_bytes(lz4.block.decompress(curve))) for d, curve in c]) + def get_curve(effective_date, currency="USD"): if f'_{currency}_curves' in globals(): curves = globals()[f'_{currency}_curves'] @@ -64,6 +66,7 @@ def get_curve(effective_date, currency="USD"): curves[effective_date] = jp_yc return jp_yc + def getMarkitIRData(effective_date=datetime.date.today(), currency="USD"): conn = dbconn("serenitasdb") @@ -80,6 +83,7 @@ def getMarkitIRData(effective_date=datetime.date.today(), if rate is not None]} return MarkitData + def get_futures_data(date=datetime.date.today()): futures_file = os.path.join(os.environ['DATA_DIR'], "Yield Curves", "futures-{0:%Y-%m-%d}.csv".format(date)) @@ -87,6 +91,7 @@ def get_futures_data(date=datetime.date.today()): quotes = [float(line.split(",")[1]) for line in fh] return quotes + def rate_helpers(currency="USD", MarkitData=None, evaluation_date=None): """Utility function to build a list of RateHelpers @@ -108,7 +113,7 @@ def rate_helpers(currency="USD", MarkitData=None, evaluation_date=None): if not MarkitData: MarkitData = getMarkitIRData(evaluation_date, currency) if MarkitData['effectiveasof'] != evaluation_date: - warnings.warn("Yield curve effective date: {0} doesn't " \ + warnings.warn("Yield curve effective date: {0} doesn't " "match the evaluation date: {1}".format( MarkitData['effectiveasof'], evaluation_date), @@ -116,21 +121,23 @@ def rate_helpers(currency="USD", MarkitData=None, evaluation_date=None): settings.evaluation_date = Date.from_datetime(MarkitData['effectiveasof']) calendar = WeekendsOnly() if currency == "USD": - isda_ibor = IborIndex("IsdaIbor", Period(3, Months), 2, USDCurrency(), calendar, - ModifiedFollowing, False, Actual360()) + isda_ibor = IborIndex("IsdaIbor", Period(3, Months), 2, USDCurrency(), + calendar, ModifiedFollowing, False, Actual360()) fix_freq = Semiannual elif currency == "EUR": - isda_ibor = IborIndex("IsdaIbor", Period(6, Months), 2, EURCurrency(), calendar, - ModifiedFollowing, False, Actual360()) + isda_ibor = IborIndex("IsdaIbor", Period(6, Months), 2, EURCurrency(), + calendar, ModifiedFollowing, False, Actual360()) fix_freq = Annual # we use SimpleQuotes, rather than just float to make it updateable - deps = [DepositRateHelper(SimpleQuote(q), Period(t), 2, calendar, ModifiedFollowing, False, Actual360()) + deps = [DepositRateHelper(SimpleQuote(q), Period(t), 2, calendar, + ModifiedFollowing, False, Actual360()) for t, q in MarkitData['deposits']] # this matches with bloomberg, but according to Markit, maturity should be unadjusted swaps = [SwapRateHelper.from_tenor(SimpleQuote(q), Period(t), calendar, fix_freq, ModifiedFollowing, Thirty360(), isda_ibor) for t, q in MarkitData['swaps']] return deps + swaps + def get_dates(date, currency="USD"): """computes the list of curve dates on a given date""" if currency == "USD": @@ -139,20 +146,22 @@ def get_dates(date, currency="USD"): calendar = WeekendsOnly() settle_date = calendar.advance(Date.from_datetime(date), 2, 0) deposit_dates = [calendar.advance(settle_date, period=Period(m, Months), - convention=ModifiedFollowing) \ + convention=ModifiedFollowing) for m in month_periods] swap_dates = [calendar.advance(settle_date, period=Period(y, Years), - convention=ModifiedFollowing) \ - for y in year_periods] + convention=ModifiedFollowing) + for y in year_periods] dates = deposit_dates + swap_dates return [pydate_from_qldate(d) for d in dates] + def roll_yc(yc, forward_date): """returns the expected forward yield cuve on a forward_date""" dates = [d for d in yc.dates if d >= forward_date] dfs = np.array([yc.discount_factor(d, forward_date) for d in dates]) return YieldCurve.from_discount_factors(forward_date, dates, dfs, 'ACT/365F') + def YC(helpers=None, currency="USD", MarkitData=None, evaluation_date=None): if helpers is None: helpers = rate_helpers(currency, MarkitData, evaluation_date) @@ -163,25 +172,27 @@ def YC(helpers=None, currency="USD", MarkitData=None, evaluation_date=None): return PiecewiseYieldCurve(BootstrapTrait.Discount, Interpolator.LogLinear, 0, calendar, helpers, Actual365Fixed()) + def jpYC(effective_date, currency="USD", MarkitData=None): if MarkitData is None: markit_data = getMarkitIRData(effective_date, currency) periods, rates = zip(*markit_data['deposits']) - periods_swaps, rate_swaps = zip(*markit_data['swaps']) + periods_swaps, rates_swaps = zip(*markit_data['swaps']) types = 'M'*len(periods) + 'S'*len(periods_swaps) rates = np.array(rates + rates_swaps) - periods = list(period + periods_swaps) + periods = list(periods + periods_swaps) if currency == "USD": fixed_period = '6M' float_period = '3M' elif currency == 'EUR': fixed_period = '12M' float_period = '6M' - return YieldCurve(effective_date, types, periods, rates, 'ACT/360',\ + return YieldCurve(effective_date, types, periods, rates, 'ACT/360', fixed_period, float_period, '30/360', 'ACT/360', BadDay.MODIFIED) + def ql_to_jp(ql_yc): """convert a QuantLib yield curve to a JP's one""" if ql_yc._trait == BootstrapTrait.Discount: @@ -190,21 +201,23 @@ def ql_to_jp(ql_yc): trade_date = pydate_from_qldate(ql_yc.dates[0]) return YieldCurve.from_discount_factors(trade_date, dates, dfs, 'ACT/365F') else: - raise RuntimeErrror('QuantLib curve needs to use Discount trait') + raise RuntimeError('QuantLib curve needs to use Discount trait') + def build_curves(currency="USD"): settings = Settings() calendar = WeekendsOnly() if currency == "USD": - isda_ibor = IborIndex("IsdaIbor", Period(3, Months), 2, USDCurrency(), calendar, - ModifiedFollowing, False, Actual360()) + isda_ibor = IborIndex("IsdaIbor", Period(3, Months), 2, USDCurrency(), + calendar, ModifiedFollowing, False, Actual360()) fix_freq = Semiannual elif currency == "EUR": - isda_ibor = IborIndex("IsdaIbor", Period(6, Months), 2, EURCurrency(), calendar, - ModifiedFollowing, False, Actual360()) + isda_ibor = IborIndex("IsdaIbor", Period(6, Months), 2, EURCurrency(), + calendar, ModifiedFollowing, False, Actual360()) fix_freq = Annual engine = dbengine('serenitasdb') - rates = pd.read_sql_table('{}_rates'.format(currency.lower()), engine, index_col='effective_date') + rates = pd.read_sql_table('{}_rates'.format(currency.lower()), engine, + index_col='effective_date') quotes = [SimpleQuote() for c in rates.columns] gen = zip(quotes, rates.columns) deps = [DepositRateHelper(q, Period(t), 2, calendar, ModifiedFollowing, @@ -212,7 +225,7 @@ def build_curves(currency="USD"): swaps = [SwapRateHelper.from_tenor(q, Period(t), calendar, fix_freq, ModifiedFollowing, Thirty360(), isda_ibor) for q, t in gen] - sql_str = "INSERT INTO {}_curves VALUES(%s, %s) ON CONFLICT DO NOTHING".format(currency) + sql_str = f"INSERT INTO {currency}_curves VALUES(%s, %s) ON CONFLICT DO NOTHING" conn = dbconn('serenitasdb') for effective_date, curve_data in rates.iterrows(): print(effective_date) @@ -221,15 +234,18 @@ def build_curves(currency="USD"): q.value = val valid_deps = [d for d in deps if not np.isnan(d.quote)] valid_swaps = [s for s in swaps if not np.isnan(s.quote)] - ql_yc = PiecewiseYieldCurve(BootstrapTrait.Discount, Interpolator.LogLinear, - 0, calendar, valid_deps + valid_swaps, Actual365Fixed()) + ql_yc = PiecewiseYieldCurve(BootstrapTrait.Discount, + Interpolator.LogLinear, + 0, calendar, valid_deps + valid_swaps, + Actual365Fixed()) jp_yc = ql_to_jp(ql_yc) with conn.cursor() as c: c.execute(sql_str, (effective_date, lz4.block.compress(jp_yc.__getstate__()))) conn.commit() -if __name__=="__main__": - #evaluation_date = Date(29, 4, 2014) + +if __name__ == "__main__": + # evaluation_date = Date(29, 4, 2014) Settings.instance().evaluation_date = today() import matplotlib.pyplot as plt |
