diff options
| -rw-r--r-- | python/analytics/__init__.py | 2 | ||||
| -rw-r--r-- | python/analytics/credit_default_swap.py | 103 | ||||
| -rw-r--r-- | python/analytics/index.py | 10 |
3 files changed, 62 insertions, 53 deletions
diff --git a/python/analytics/__init__.py b/python/analytics/__init__.py index e5a378b1..62cd168c 100644 --- a/python/analytics/__init__.py +++ b/python/analytics/__init__.py @@ -1,6 +1,8 @@ import sys sys.path.append("..") +# indicates whether we use local pricing +_local = True from utils.db import serenitas_engine, dawn_engine, dbconn, DataError, serenitas_pool from functools import lru_cache from .index import CreditIndex, ForwardIndex diff --git a/python/analytics/credit_default_swap.py b/python/analytics/credit_default_swap.py index e863de12..b6b2a16c 100644 --- a/python/analytics/credit_default_swap.py +++ b/python/analytics/credit_default_swap.py @@ -1,3 +1,4 @@ +import analytics import array import datetime import math @@ -11,8 +12,7 @@ from pandas.tseries.offsets import BDay from pyisda.curve import SpreadCurve from pyisda.date import previous_twentieth from pyisda.legs import ContingentLeg, FeeLeg -from termcolor import colored -from .utils import build_table, get_fx +from .utils import get_fx from typing import Union from weakref import WeakSet from yieldcurve import get_curve, rate_helpers, YC, ql_to_jp @@ -129,9 +129,10 @@ class CreditDefaultSwap: @start_date.setter def start_date(self, d): - 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 + if d != self._start_date: + 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): @@ -171,10 +172,10 @@ class CreditDefaultSwap: else: raise ValueError("Direction needs to be either 'Buyer' or 'Seller'") - def _update(self): + def _update_spread_curve(self): if self._spread is not None: self._sc = SpreadCurve( - self._yc.base_date, + self.value_date, self._yc, self.start_date, self._step_in_date, @@ -213,7 +214,7 @@ class CreditDefaultSwap: """ s: spread in bps """ if self._spread is None or s != self.spread: self._spread = s * 1e-4 - self._update() + self._update_spread_curve() self.notify() @property @@ -224,7 +225,15 @@ class CreditDefaultSwap: @property def pv(self): - return self.notional * self._factor * self._pv + if not analytics._local: + return ( + self.notional + * self._factor + * self._pv + * get_fx(self.currency, self.value_date) + ) + else: + return self.notional * self._factor * self._pv @pv.setter def pv(self, val): @@ -233,15 +242,11 @@ class CreditDefaultSwap: self.price = 100 * (1 - self._clean_pv) @property - def local_pv(self): - if self.currency == "USD": - return self.pv - else: - return self.pv * get_fx(self.currency, self.value_date) - - @property def accrued(self): - return -self.notional * self._factor * self._accrued * self.fixed_rate * 1e-4 + r = -self.notional * self._factor * self._accrued * self.fixed_rate * 1e-4 + if not analytics._local: + r *= get_fx(self.currency, self.value_date) + return r @property def days_accrued(self): @@ -249,22 +254,17 @@ class CreditDefaultSwap: @property def clean_pv(self): - return self.notional * self._factor * self._clean_pv - - @property - def local_clean_pv(self): - return self.clean_pv * get_fx(self.currency, self.value_date) + r = self.notional * self._factor * self._clean_pv + if not analytics._local: + r *= get_fx(self.currency, self.value_date) + return r @property def price(self): - return self._price - - @property - def local_price(self): - if self.currency == "USD": - return self._price + if not analytics._local: + return 100 + (self._price - 100) / get_fx(self.currency, self.value_date) else: - return 100 + (self.price - 100) / get_fx(self.currency, self.value_date) + return self._price @price.setter def price(self, val): @@ -318,10 +318,12 @@ class CreditDefaultSwap: old_pv, old_value_date = self.clean_pv, self.value_date with warnings.catch_warnings(): warnings.simplefilter("ignore") - self.value_date = self.value_date + relativedelta(days=1) + self._update_dates(self.value_date + relativedelta(days=1)) carry = -self.notional * self._factor * self.fixed_rate * 1e-4 / 360 + if not analytics._local: + carry *= get_fx(self.currency, old_value_date) roll_down = self.clean_pv - old_pv - self.value_date = old_value_date + self._update_dates(old_value_date) return carry + roll_down @property @@ -335,25 +337,25 @@ class CreditDefaultSwap: rh.quote.value += 1e-4 ql_yc = YC(helpers) self._yc = ql_to_jp(ql_yc) - self._update() # to force recomputation + self._update_spread_curve() # to force recomputation new_pv = self.pv # for r in self._helpers: # r.quote -= 1e-4 self._yc = old_yc - self._update() + self._update_spread_curve() return new_pv - old_pv @property def rec_risk(self): old_recovery = self.recovery self.recovery = old_recovery - 0.01 - self._update() + self._update_spread_curve() pv_minus = self.pv self.recovery = old_recovery + 0.01 - self._update() + self._update_spread_curve() pv_plus = self.pv self.recovery = old_recovery - self._update() + self._update_spread_curve() return (pv_plus - pv_minus) / 2 @property @@ -375,52 +377,49 @@ class CreditDefaultSwap: def value_date(self, d): if self._value_date and d == self.value_date: return + self._update_dates(d) + self._yc = get_curve(self.value_date, self.currency) + self._update_spread_curve() + self.notify() + + def _update_dates(self, d): if isinstance(d, datetime.datetime): d = d.date() self.start_date = previous_twentieth(d) - self._yc = get_curve(d, self.currency) 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._update() - self.notify() def reset_pv(self): - self._original_clean_pv = self._clean_pv - self._original_local_clean_pv = self._clean_pv * get_fx( + self._original_clean_pv = self._clean_pv * get_fx( self.currency, self.value_date ) + self._original_local_clean_pv = self._clean_pv self._trade_date = self._value_date @property def pnl(self): if self._original_clean_pv is None: raise ValueError("original pv not set") - else: - days_accrued = (self.value_date - self._trade_date).days / 360 + + days_accrued = (self.value_date - self._trade_date).days / 360 + if not analytics._local: return ( self.notional * self._factor * ( - self._clean_pv + self._clean_pv * get_fx(self.currency, self.value_date) - self._original_clean_pv - days_accrued * self.fixed_rate * 1e-4 ) ) - - @property - def local_pnl(self): - if self.currency == "USD": - return self.pnl else: - days_accrued = (self.value_date - self._trade_date).days / 360 return ( self.notional * self._factor * ( - (self._clean_pv - days_accrued * self.fixed_rate * 1e-4) - * get_fx(self.currency, self.value_date) + self._clean_pv - self._original_local_clean_pv - days_accrued * self.fixed_rate * 1e-4 ) diff --git a/python/analytics/index.py b/python/analytics/index.py index 9504451a..b4805df2 100644 --- a/python/analytics/index.py +++ b/python/analytics/index.py @@ -12,6 +12,9 @@ except ModuleNotFoundError: pass from pandas.tseries.offsets import BDay from pyisda.curve import SpreadCurve +from pyisda.date import previous_twentieth +from termcolor import colored +from .utils import build_table def g(index, spread, exercise_date, pv=None): @@ -114,7 +117,12 @@ class CreditIndex(CreditDefaultSwap): else: recovery = 0.3 if index_type == "HY" else 0.4 super().__init__( - value_date, maturity, recovery, coupon, notional, df.issue_date[0] + previous_twentieth(value_date), + maturity, + recovery, + coupon, + notional, + df.issue_date[0], ) self._quote_is_price = index_type == "HY" self._indic = tuple( |
