aboutsummaryrefslogtreecommitdiffstats
path: root/python/analytics/credit_default_swap.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/analytics/credit_default_swap.py')
-rw-r--r--python/analytics/credit_default_swap.py231
1 files changed, 155 insertions, 76 deletions
diff --git a/python/analytics/credit_default_swap.py b/python/analytics/credit_default_swap.py
index 99b4e658..e4998657 100644
--- a/python/analytics/credit_default_swap.py
+++ b/python/analytics/credit_default_swap.py
@@ -17,18 +17,42 @@ from weakref import WeakSet
from yieldcurve import get_curve, rate_helpers, YC, ql_to_jp
-class CreditDefaultSwap():
+class CreditDefaultSwap:
""" minimal class to represent a credit default swap """
- __slots__ = ('_observed', 'fixed_rate', 'notional', '_start_date',
- '_end_date', 'recovery', '_version', '_fee_leg',
- '_default_leg', '_value_date', '_yc', '_sc', '_risky_annuity',
- '_spread', '_price', 'name', 'issue_date',
- 'currency', '_step_in_date', '_accrued',
- '_cash_settle_date', '_dl_pv', '_pv', '_clean_pv',
- '_original_clean_pv', '_trade_date', '_factor')
- def __init__(self, start_date, end_date, recovery, fixed_rate,
- notional=10e6, issue_date=None):
+ __slots__ = (
+ "_observed",
+ "fixed_rate",
+ "notional",
+ "_start_date",
+ "_end_date",
+ "recovery",
+ "_version",
+ "_fee_leg",
+ "_default_leg",
+ "_value_date",
+ "_yc",
+ "_sc",
+ "_risky_annuity",
+ "_spread",
+ "_price",
+ "name",
+ "issue_date",
+ "currency",
+ "_step_in_date",
+ "_accrued",
+ "_cash_settle_date",
+ "_dl_pv",
+ "_pv",
+ "_clean_pv",
+ "_original_clean_pv",
+ "_trade_date",
+ "_factor",
+ )
+
+ def __init__(
+ self, start_date, end_date, recovery, fixed_rate, notional=10e6, issue_date=None
+ ):
"""
start_date : :class:`datetime.date`
index start_date (Could be issue date, or last imm date)
@@ -45,7 +69,7 @@ class CreditDefaultSwap():
self._end_date = end_date
self.recovery = recovery
- self._fee_leg = FeeLeg(self._start_date, end_date, True, 1., 1.)
+ self._fee_leg = FeeLeg(self._start_date, end_date, True, 1.0, 1.0)
self._default_leg = ContingentLeg(self._start_date, end_date, True)
self._value_date = None
self._yc, self._sc = None, None
@@ -54,9 +78,17 @@ class CreditDefaultSwap():
self.name = None
self.issue_date = issue_date
self._factor = 1
- for attr in ['currency', '_step_in_date', '_cash_settle_date',
- '_accrued', '_dl_pv', '_pv', '_clean_pv',
- '_original_clean_pv', '_trade_date']:
+ for attr in [
+ "currency",
+ "_step_in_date",
+ "_cash_settle_date",
+ "_accrued",
+ "_dl_pv",
+ "_pv",
+ "_clean_pv",
+ "_original_clean_pv",
+ "_trade_date",
+ ]:
setattr(self, attr, None)
self._observed = WeakSet()
@@ -65,9 +97,9 @@ class CreditDefaultSwap():
def _getslots(self):
classes = reversed(self.__class__.__mro__)
- next(classes) # skip object
+ next(classes) # skip object
slots = chain.from_iterable(cls.__slots__ for cls in classes)
- next(slots) # skip _observed
+ next(slots) # skip _observed
yield from slots
def __getstate__(self):
@@ -88,13 +120,13 @@ class CreditDefaultSwap():
@start_date.setter
def start_date(self, d):
- self._fee_leg = FeeLeg(d, self.end_date, True, 1., 1.)
+ self._fee_leg = FeeLeg(d, self.end_date, True, 1.0, 1.0)
self._default_leg = ContingentLeg(d, self.end_date, True)
self._start_date = d
@end_date.setter
def end_date(self, d):
- self._fee_leg = FeeLeg(self.start_date, d, True, 1., 1.)
+ self._fee_leg = FeeLeg(self.start_date, d, True, 1.0, 1.0)
self._default_leg = ContingentLeg(self.start_date, d, True)
self._end_date = d
@@ -107,7 +139,7 @@ class CreditDefaultSwap():
@property
def direction(self):
- if self.notional > 0.:
+ if self.notional > 0.0:
return "Buyer"
else:
return "Seller"
@@ -122,17 +154,34 @@ 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]))
+ 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, self._step_in_date,
- self._cash_settle_date, self._yc,
- self._sc, False)
+ self._risky_annuity = self._fee_leg.pv(
+ self.value_date,
+ self._step_in_date,
+ self._cash_settle_date,
+ self._yc,
+ self._sc,
+ False,
+ )
self._dl_pv = self._default_leg.pv(
- self.value_date, self._step_in_date, self._cash_settle_date,
- self._yc, self._sc, self.recovery)
+ self.value_date,
+ self._step_in_date,
+ self._cash_settle_date,
+ self._yc,
+ self._sc,
+ self.recovery,
+ )
self._pv = self._dl_pv - self._risky_annuity * self.fixed_rate * 1e-4
self._clean_pv = self._pv + self._accrued * self.fixed_rate * 1e-4
self._price = 100 * (1 - self._clean_pv)
@@ -147,7 +196,7 @@ class CreditDefaultSwap():
@property
def flat_hazard(self):
- sc_data = self._sc.inspect()['data']
+ sc_data = self._sc.inspect()["data"]
# conversion to continuous compounding
return sc_data[0][1]
@@ -163,8 +212,7 @@ class CreditDefaultSwap():
@property
def accrued(self):
- return -self.notional * self._factor * self._accrued * \
- self.fixed_rate * 1e-4
+ return -self.notional * self._factor * self._accrued * self.fixed_rate * 1e-4
@property
def days_accrued(self):
@@ -180,23 +228,40 @@ class CreditDefaultSwap():
@price.setter
def price(self, val):
- if self._price is None or math.fabs(val-self._price) > 1e-6:
+ if self._price is None or math.fabs(val - self._price) > 1e-6:
self._clean_pv = (100 - val) / 100
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]),
- array.array('d', [self._clean_pv]),
- array.array('d', [self.recovery]))
+ 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]),
+ array.array("d", [self._clean_pv]),
+ array.array("d", [self.recovery]),
+ )
self._risky_annuity = self._fee_leg.pv(
- self.value_date, self._step_in_date, self._cash_settle_date,
- self._yc, self._sc, False)
+ self.value_date,
+ self._step_in_date,
+ self._cash_settle_date,
+ self._yc,
+ self._sc,
+ False,
+ )
self._dl_pv = self._default_leg.pv(
- self.value_date, self._step_in_date, self._cash_settle_date,
- self._yc, self._sc, self.recovery)
+ self.value_date,
+ self._step_in_date,
+ self._cash_settle_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._spread = (
+ self._clean_pv / (self._risky_annuity - self._accrued)
+ self.fixed_rate * 1e-4
+ )
self._price = val
self.notify()
@@ -214,7 +279,7 @@ class CreditDefaultSwap():
with warnings.catch_warnings():
warnings.simplefilter("ignore")
self.value_date = self.value_date + relativedelta(days=1)
- carry = self.notional * self.fixed_rate * 1e-4/360
+ carry = self.notional * self.fixed_rate * 1e-4 / 360
roll_down = self.clean_pv - old_pv
self.value_date = old_value_date
return carry + roll_down
@@ -262,7 +327,7 @@ class CreditDefaultSwap():
@property
def value_date(self):
if self._value_date is None:
- raise AttributeError('Please set value_date first')
+ raise AttributeError("Please set value_date first")
else:
return self._value_date
@@ -290,8 +355,11 @@ class CreditDefaultSwap():
raise ValueError("original pv not set")
else:
days_accrued = (self.value_date - self._trade_date).days / 360
- return self.notional * (self._clean_pv - self._original_clean_pv -
- days_accrued * self.fixed_rate * 1e-4)
+ return self.notional * (
+ self._clean_pv
+ - self._original_clean_pv
+ - days_accrued * self.fixed_rate * 1e-4
+ )
def notify(self):
for obj in self._observed:
@@ -308,7 +376,7 @@ class CreditDefaultSwap():
self.spread = orig_spread * (1 + ss)
r.append([getattr(self, p) for p in actual_params])
self.spread = orig_spread
- ind = pd.Index(spread_shock, name='spread_shock', copy=False)
+ ind = pd.Index(spread_shock, name="spread_shock", copy=False)
return pd.DataFrame(r, index=ind, columns=actual_params)
def __repr__(self):
@@ -319,37 +387,48 @@ class CreditDefaultSwap():
else:
accrued_str = "Accrued ({} Day)".format(self.days_accrued)
- s = ["{:<20}\tNotional {:>5}MM {}\tFactor {:>28}".format(
- "Buy Protection" if self.notional > 0. else "Sell Protection",
- abs(self.notional)/1_000_000,
- self.currency,
- self._factor),
- "{:<20}\t{:>15}".format("CDS Index", colored(self.name, attrs=['bold'])),
- ""]
- rows = [["Trd Sprd (bp)", self.spread, "Coupon (bp)", self.fixed_rate],
- ["1st Accr Start", self.issue_date, "Payment Freq", "Quarterly"],
- ["Maturity Date", self.end_date, "Rec Rate", self.recovery],
- ["Bus Day Adj", "Following", "DayCount", "ACT/360"]]
- format_strings = [[None, '{:.2f}', None, '{:.0f}'],
- [None, '{:%m/%d/%y}', None, None],
- [None, '{:%m/%d/%y}', None, None],
- [None, None, None, None]]
+ s = [
+ "{:<20}\tNotional {:>5}MM {}\tFactor {:>28}".format(
+ "Buy Protection" if self.notional > 0.0 else "Sell Protection",
+ abs(self.notional) / 1_000_000,
+ self.currency,
+ self._factor,
+ ),
+ "{:<20}\t{:>15}".format("CDS Index", colored(self.name, attrs=["bold"])),
+ "",
+ ]
+ rows = [
+ ["Trd Sprd (bp)", self.spread, "Coupon (bp)", self.fixed_rate],
+ ["1st Accr Start", self.issue_date, "Payment Freq", "Quarterly"],
+ ["Maturity Date", self.end_date, "Rec Rate", self.recovery],
+ ["Bus Day Adj", "Following", "DayCount", "ACT/360"],
+ ]
+ format_strings = [
+ [None, "{:.2f}", None, "{:.0f}"],
+ [None, "{:%m/%d/%y}", None, None],
+ [None, "{:%m/%d/%y}", None, None],
+ [None, None, None, None],
+ ]
s += build_table(rows, format_strings, "{:<20}{:>19}\t\t{:<20}{:>15}")
- s += ["",
- colored("Calculator", attrs=['bold'])]
- rows = [["Valuation Date", self.value_date],
- ["Cash Settled On", self._cash_settle_date]]
- format_strings = [[None, '{:%m/%d/%y}'],
- [None, '{:%m/%d/%y}']]
+ s += ["", colored("Calculator", attrs=["bold"])]
+ rows = [
+ ["Valuation Date", self.value_date],
+ ["Cash Settled On", self._cash_settle_date],
+ ]
+ format_strings = [[None, "{:%m/%d/%y}"], [None, "{:%m/%d/%y}"]]
s += build_table(rows, format_strings, "{:<20}\t{:>15}")
s += [""]
- rows = [["Price", self.price, "Spread DV01", self.DV01],
- ["Principal", self.clean_pv, "IR DV01", self.IRDV01],
- [accrued_str, self.accrued, "Rec Risk (1%)", self.rec_risk],
- ["Cash Amount", self.pv, "Def Exposure", self.jump_to_default]]
- format_strings = [[None, '{:.8f}', None, '{:,.2f}'],
- [None, '{:,.0f}', None, '{:,.2f}'],
- [None, '{:,.0f}', None, '{:,.2f}'],
- [None, '{:,.0f}', None, '{:,.0f}']]
+ rows = [
+ ["Price", self.price, "Spread DV01", self.DV01],
+ ["Principal", self.clean_pv, "IR DV01", self.IRDV01],
+ [accrued_str, self.accrued, "Rec Risk (1%)", self.rec_risk],
+ ["Cash Amount", self.pv, "Def Exposure", self.jump_to_default],
+ ]
+ format_strings = [
+ [None, "{:.8f}", None, "{:,.2f}"],
+ [None, "{:,.0f}", None, "{:,.2f}"],
+ [None, "{:,.0f}", None, "{:,.2f}"],
+ [None, "{:,.0f}", None, "{:,.0f}"],
+ ]
s += build_table(rows, format_strings, "{:<20}{:>19}\t\t{:<20}{:>15}")
return "\n".join(s)