aboutsummaryrefslogtreecommitdiffstats
path: root/python
diff options
context:
space:
mode:
Diffstat (limited to 'python')
-rw-r--r--python/analytics/__init__.py1
-rw-r--r--python/analytics/black.py15
-rw-r--r--python/analytics/index.py313
-rw-r--r--python/analytics/option.py120
-rw-r--r--python/analytics/utils.py7
-rw-r--r--python/swaption.py312
6 files changed, 467 insertions, 301 deletions
diff --git a/python/analytics/__init__.py b/python/analytics/__init__.py
new file mode 100644
index 00000000..90d1b1fe
--- /dev/null
+++ b/python/analytics/__init__.py
@@ -0,0 +1 @@
+from .index import Index
diff --git a/python/analytics/black.py b/python/analytics/black.py
new file mode 100644
index 00000000..647c92b2
--- /dev/null
+++ b/python/analytics/black.py
@@ -0,0 +1,15 @@
+import numpy as np
+from scipy.stats import norm
+
+def d1(F, K, sigma, T):
+ return (np.log(F / K) + sigma**2 * T / 2) / (sigma * np.sqrt(T))
+
+def d2(F, K, sigma, T):
+ return d1(F, K, sigma, T) - sigma * np.sqrt(T)
+
+def black(F, K, T, sigma, option_type = "payer"):
+ chi = 1 if option_type == "payer" else -1
+ if option_type == "payer":
+ return F * norm.cdf(d1(F, K, sigma, T)) - K * norm.cdf(d2(F, K, sigma, T))
+ else:
+ return K * norm.cdf(- d2(F, K, sigma, T)) - F * norm.cdf(- d1(F, K, sigma, T))
diff --git a/python/analytics/index.py b/python/analytics/index.py
new file mode 100644
index 00000000..85ba3474
--- /dev/null
+++ b/python/analytics/index.py
@@ -0,0 +1,313 @@
+import array
+import datetime
+import math
+import pandas as pd
+
+from pyisda.legs import ContingentLeg, FeeLeg
+from quantlib.settings import Settings
+from quantlib.time.api import (
+ Date, Schedule, WeekendsOnly, CDS, Following,
+ Unadjusted, Period, pydate_from_qldate )
+from termcolor import colored
+from pandas.tseries.offsets import BDay
+from dates import prev_immdate
+from db import dbconn
+from psycopg2 import DataError
+from pyisda.curve import SpreadCurve
+from yieldcurve import YC, ql_to_jp, roll_yc, rate_helpers
+from quantlib.time.api import Actual365Fixed
+
+serenitasdb = dbconn('serenitasdb')
+
+class Index():
+ """ minimal class to represent a credit index """
+ def __init__(self, start_date, end_date, recovery, fixed_rate,
+ notional = 10e6):
+ """
+ start_date : :class:`datetime.date`
+ index start_date (Could be issue date, or last imm date)
+ end_date : :class:`datetime.date`
+ index last date
+ recovery :
+ recovery rate (between 0 and 1)
+ fixed_rate :
+ fixed coupon (in bps)
+ """
+ self.fixed_rate = fixed_rate
+ self.notional = notional
+ self._sched = Schedule(Date.from_datetime(start_date),
+ Date.from_datetime(end_date),
+ Period("3M"),
+ WeekendsOnly(),
+ Following,
+ Unadjusted,
+ CDS)
+ self._start_date = start_date
+ self._end_date = end_date
+ self.recovery = recovery
+
+ self._fee_leg = FeeLeg(self._start_date, end_date, True, 1, 1)
+ self._default_leg = ContingentLeg(self._start_date, end_date, 1)
+ self._trade_date = None
+ self._yc = None
+ self._risky_annuity = None
+ self._spread = None
+ self.name = None
+
+ @property
+ def start_date(self):
+ return self._start_date
+
+ @property
+ def end_date(self):
+ return self._end_date
+
+ @start_date.setter
+ def start_date(self, d):
+ self._fee_leg = FeeLeg(d, self.end_date, True, 1, 1)
+ self._default_leg = ContingentLeg(d, self.end_date, 1)
+ self._start_date = d
+ self._sched = Schedule(Date.from_datetime(d),
+ Date.from_datetime(self.end_date),
+ Period("3M"),
+ WeekendsOnly(),
+ Following,
+ Unadjusted,
+ CDS)
+
+ @end_date.setter
+ def end_date(self, d):
+ self._fee_leg = FeeLeg(self.start_date, d, True, 1, 1)
+ self._default_leg = ContingentLeg(self.start_date, d, 1)
+ self._end_date = d
+ self._sched = Schedule(self.start_date,
+ d,
+ Period("3M"),
+ WeekendsOnly(),
+ Following,
+ Unadjusted,
+ CDS)
+
+ def survival_probability(self, d):
+ if d > self.trade_date:
+ return 1
+ else:
+ return math.exp( - self.flat_hazard * (d - self.trade_date)/365)
+
+ def forward_pv(self, exercise_date):
+ """This is default adjusted forward price at time exercise_date"""
+ step_in_date = exercise_date + datetime.timedelta(days=1)
+ a = self._fee_leg.pv(self.trade_date, step_in_date, self._value_date,
+ self._yc, self._sc, False)
+ Delta = self._fee_leg.accrued(step_in_date)
+ value_date = (pd.Timestamp(exercise_date) + 3* BDay()).date()
+ df = self._yc.discount_factor(value_date)
+ q = self.survival_probability(exercise_date)
+ clean_forward_annuity = a - Delta * df * q
+ dl_pv = self._default_leg.pv(
+ self.trade_date, step_in_date, self._value_date,
+ self._yc, self._sc, self.recovery)
+ forward_price = self.notional * (dl_pv - clean_forward_annuity * self.fixed_rate*1e-4)
+ fep = self.notional * (1 - self.recovery) * (1 - q)
+ return forward_price * self._yc.discount_factor(self._value_date) / df + fep
+
+ @property
+ def spread(self):
+ return self._spread * 1e4
+
+ def _update(self):
+ 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._spread]),
+ 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._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)
+
+ @spread.setter
+ def spread(self, s: float):
+ """ s: spread in bps """
+ self._spread = s * 1e-4
+ self._update()
+
+ @property
+ def flat_hazard(self):
+ sc_data = self._sc.inspect()['data']
+ ## conversion to continuous compounding
+ return math.log(1 + sc_data[0][1])
+
+ @property
+ def pv(self):
+ return self.notional * self._pv
+
+ @property
+ def accrued(self):
+ return - self.notional * self._accrued * self.fixed_rate * 1e-4
+
+ @property
+ def days_accrued(self):
+ return int(self._accrued * 360)
+
+ @property
+ def clean_pv(self):
+ return self.notional * self._clean_pv
+
+ @property
+ def price(self):
+ return self._price
+
+ @price.setter
+ def price(self, val):
+ pass
+
+ @property
+ def DV01(self):
+ old_pv = self.pv
+ self.spread += 1
+ dv01 = self.pv - old_pv
+ self.spread -= 1
+ return dv01
+
+ @property
+ def IRDV01(self):
+ old_pv = self.pv
+ old_yc = self._yc
+ for rh in self._helpers:
+ rh.quote += 1e-4
+ self._yc = ql_to_jp(self._ql_yc)
+ self._update() ## to force recomputation
+ new_pv = self.pv
+ for r in self._helpers:
+ r.quote -= 1e-4
+ self._yc = old_yc
+ self._update()
+ return new_pv - old_pv
+
+ @property
+ def rec_risk(self):
+ old_pv = self.pv
+ old_recovery = self.recovery
+ self.recovery = old_recovery - 0.01
+ self._update()
+ pv_minus = self.pv
+ self.recovery = old_recovery + 0.01
+ self._update()
+ pv_plus = self.pv
+ self.recovery = old_recovery
+ self._update()
+ return (pv_plus - pv_minus)/2
+
+ @property
+ def jump_to_default(self):
+ return self.notional * (1 - self.recovery) - self.clean_pv
+
+ @property
+ def risky_annuity(self):
+ return self._risky_annuity - self._accrued
+
+ @property
+ def trade_date(self):
+ if self._trade_date is None:
+ raise AttributeError('Please set trade_date first')
+ else:
+ return self._trade_date
+
+ @trade_date.setter
+ def trade_date(self, d):
+ settings = Settings()
+ settings.evaluation_date = Date.from_datetime(d)
+ self.start_date = pydate_from_qldate(
+ self._sched.previous_date(settings.evaluation_date))
+ self._helpers = rate_helpers(self.currency)
+ self._ql_yc = YC(self._helpers)
+ self._yc = ql_to_jp(self._ql_yc)
+ self._trade_date = d
+ self._step_in_date = self.trade_date + datetime.timedelta(days=1)
+ self._accrued = self._fee_leg.accrued(self._step_in_date)
+ self._value_date = (pd.Timestamp(self._trade_date) + 3* BDay()).date()
+ if self._spread is not None:
+ self._update()
+
+ @classmethod
+ def from_name(cls, index, series, tenor, trade_date = datetime.date.today(),
+ notional = 10e6):
+ try:
+ with serenitasdb.cursor() as c:
+ c.execute("SELECT maturity, coupon FROM index_maturity " \
+ "WHERE index=%s AND series=%s AND tenor = %s",
+ (index.upper(), series, tenor))
+ maturity, coupon = next(c)
+ except DataError as e:
+ raise
+ else:
+ recovery = 0.4 if index.lower() == "ig" else 0.3
+ instance = cls(trade_date, maturity, recovery, coupon)
+ instance.name = "MARKIT CDX.NA.{}.{} {:%m/%y} ".format(
+ index.upper(),
+ series,
+ maturity)
+ if index.upper() in ["IG", "HY"]:
+ instance.currency = "USD"
+ else:
+ instance.currency = "EUR"
+ instance.notional = notional
+ instance.trade_date = trade_date
+ return instance
+
+ def __repr__(self):
+ if self.days_accrued > 1:
+ accrued_str = "Accrued ({} Days)".format(self.days_accrued)
+ else:
+ accrued_str = "Accrued ({} Day)".format(self.days_accrued)
+ s = ["{:<20}\t{:>15}".format("CDS Index", colored(self.name, attrs = ['bold'])),
+ "",
+ "{:<20}\t{:>15}".format("Trade Date", ('{:%m/%d/%y}'.
+ format(self.trade_date))),
+ "{:<20}\t{:>15.2f}\t\t{:<20}\t{:>10,.2f}".format("Trd Sprd (bp)",
+ self.spread,
+ "Coupon (bp)",
+ self.fixed_rate),
+ "{:<20}\t{:>15.2f}\t\t{:<20}\t{:>10}".format("1st Accr Start",
+ self.spread,
+ "Payment Freq",
+ "Quarterly"),
+ "{:<20}\t{:>15}\t\t{:<20}\t{:>10.2f}".format("Maturity Date",
+ ('{:%m/%d/%y}'.
+ format(self.end_date)),
+ "Rec Rate",
+ self.recovery),
+ "{:<20}\t{:>15}\t\t{:<20}\t{:>10}".format("Bus Day Adj",
+ "Following",
+ "Day Count",
+ "ACT/360"),
+ "",
+ colored("Calculator", attrs = ['bold']),
+ "{:<20}\t{:>15}".format("Valuation Date", ('{:%m/%d/%y}'.
+ format(self.trade_date))),
+ "{:<20}\t{:>15}".format("Cash Settled On", ('{:%m/%d/%y}'.
+ format(self._value_date))),
+ "",
+ "{:<20}\t{:>15.8f}\t\t{:<20}\t{:>10,.2f}".format("Price",
+ self.price,
+ "Spread DV01",
+ self.DV01),
+ "{:<20}\t{:>15,.0f}\t\t{:<20}\t{:>10,.2f}".format("Principal",
+ self.clean_pv,
+ "IR DV01",
+ self.IRDV01),
+ "{:<20}\t{:>15,.0f}\t\t{:<20}\t{:>10,.2f}".format(accrued_str,
+ self.accrued,
+ "Rec Risk (1%)",
+ self.rec_risk),
+ "{:<20}\t{:>15,.0f}\t\t{:<20}\t{:>10,.0f}".format("Cash Amount",
+ self.pv,
+ "Def Exposure",
+ self.jump_to_default)
+ ]
+ return "\n".join(s)
diff --git a/python/analytics/option.py b/python/analytics/option.py
new file mode 100644
index 00000000..32f4f947
--- /dev/null
+++ b/python/analytics/option.py
@@ -0,0 +1,120 @@
+from .black import black
+from .utils import GHquad
+from yieldcurve import roll_yc
+from pandas.tseries.offsets import BDay
+
+class Option:
+ def __init__(self, index, exercise_date, strike, option_type="payer"):
+ self.index = index
+ self._exercise_date = exercise_date
+ self._forward_yc = roll_yc(self.index._yc, self.exercise_date)
+ self.exercise_date_settle = (pd.Timestamp(self.exercise_date) + 3* BDay()).date()
+ self._T = None
+ self.strike = strike
+ self.option_type = option_type.lower()
+ self._Z, self._w = GHquad(50)
+ self.notional = 1
+
+ @property
+ def exercise_date(self):
+ return self._exercise_date
+
+ @exercise_date.setter
+ def exercise_date(self, d : datetime.date):
+ self._exercise_date = d
+ self.exercise_date_settle = (pd.Timestamp(d) + 3* BDay()).date()
+ self._forward_yc = roll_yc(self.index._yc, self.exercise_date)
+
+ @property
+ def pv(self):
+ fp = self.index.forward_pv(self.exercise_date) / self.index.notional
+ T = self.T
+ tilt = np.exp(-self.sigma**2/2 * T + self.sigma * self._Z * math.sqrt(T))
+ rolled_curve = roll_yc(self.index._yc, self.exercise_date)
+ args = (fp, self.exercise_date, self.exercise_date_settle,
+ self.index, self._forward_yc, tilt, self._w)
+ eta = 1.1
+ a = self.index.spread
+ b = self.index.spread * eta
+ while True:
+ if calib(*((b,) + args)) > 0:
+ break
+ b *= eta
+
+ S0 = brentq(calib, a, b, args)
+
+ G = g(self.index, self.strike, self.exercise_date)
+ if T == 0:
+ pv = self.notional * (g(self.index, self.index.spread, self.exercise_date) - G)
+ if self.option_type == "payer":
+ return pv if self.index.spread > self.strike else 0
+ else:
+ return - pv if self.index.spread < self.strike else 0
+
+ Zstar = (math.log(self.strike/S0) + self.sigma**2/2 * T) / \
+ (self.sigma * math.sqrt(T))
+
+ if self.option_type == "payer":
+ Z = Zstar + np.logspace(0, 1.5, 300) - 1
+ elif self.option_type == "receiver":
+ Z = Zstar - np.logspace(0, 1.5, 300) + 1
+ else:
+ raise ValueError("option_type needs to be either 'payer' or 'receiver'")
+ S = S0 * np.exp(-self.sigma**2/2 * T + self.sigma * Z * math.sqrt(T))
+ a, b = strike_vec(S * 1e-4, rolled_curve, self.exercise_date,
+ self.exercise_date_settle,
+ self.index.start_date, self.index.end_date, self.index.recovery)
+ val = ((a - b * self.index.fixed_rate*1e-4) - G) * 1/math.sqrt(2*math.pi) * np.exp(-Z**2/2)
+ df_scale = self.index._yc.discount_factor(self.exercise_date_settle)
+ return self.notional * simps(val, Z) * df_scale
+
+ @property
+ def pv2(self):
+ G = g(self.index, self.strike, self.exercise_date)
+ fp = self.index.forward_pv(self.exercise_date) / self.index.notional
+ forward_annuity = self.index.forward_annuity(self.exercise_date)
+ DA_forward_spread = fp / forward_annuity + self.index.fixed_rate * 1e-4
+ strike_tilde = self.index.fixed_rate * 1e-4 + G / forward_annuity
+ return forward_annuity * black(DA_forward_spread,
+ strike_tilde,
+ self.T,
+ self.sigma,
+ self.option_type) * self.notional
+
+ @property
+ def delta(self):
+ old_index_pv = self.index.pv
+ old_pv = self.pv
+ self.index.spread += 0.1
+ notional_ratio = self.index.notional/self.notional
+ delta = (self.pv - old_pv)/(self.index.pv - old_index_pv) * notional_ratio
+ self.index.spread -= 0.1
+ return delta
+
+
+ @property
+ def T(self):
+ if self._T:
+ return self._T
+ else:
+ return year_frac(self.index.trade_date, self.exercise_date) + 1/365
+
+ @property
+ def gamma(self):
+ pass
+
+ @property
+ def theta(self):
+ old_pv = self.pv
+ self._T = self.T - 1/365
+ theta = self.pv - old_pv
+ self._T = None
+ return theta
+
+ @property
+ def vega(self):
+ old_pv = self.pv
+ self.sigma += 0.01
+ vega = self.pv - old_pv
+ self.sigma -= 0.01
+ return vega
diff --git a/python/analytics/utils.py b/python/analytics/utils.py
new file mode 100644
index 00000000..a67d6f32
--- /dev/null
+++ b/python/analytics/utils.py
@@ -0,0 +1,7 @@
+import numpy as np
+from scipy.special import h_roots
+
+def GHquad(n : int):
+ """Gauss-Hermite quadrature weights"""
+ Z, w = h_roots(n)
+ return Z*np.sqrt(2), w/np.sqrt(np.pi)
diff --git a/python/swaption.py b/python/swaption.py
index c85a9b12..68486cc8 100644
--- a/python/swaption.py
+++ b/python/swaption.py
@@ -1,308 +1,15 @@
-from pyisda.legs import ContingentLeg, FeeLeg
from pyisda.flat_hazard import strike_vec
-from pyisda.curve import YieldCurve, BadDay, SpreadCurve
-from yieldcurve import YC, ql_to_jp, roll_yc, rate_helpers
-from pyisda.cdsone import upfront_charge
-from quantlib.settings import Settings
from quantlib.time.api import Date
import array
-import math
from scipy.optimize import brentq
from scipy.integrate import simps
-import numpy as np
import datetime
-from tranche_functions import GHquad
+import numpy as np
import pandas as pd
from pandas.tseries.offsets import BDay
-from db import dbconn
-from psycopg2 import DataError
-from dates import prev_immdate
-
+from tranche_functions import GHquad
+from yieldcurve import roll_yc
from scipy.stats import norm
-from termcolor import colored
-serenitasdb = dbconn('serenitasdb')
-
-class Index():
- """ minimal class to represent a credit index """
- def __init__(self, start_date, end_date, recovery, fixed_rate,
- notional = 10e6):
- """
- start_date : :class:`datetime.date`
- index start_date (Could be issue date, or last imm date)
- end_date : :class:`datetime.date`
- index last date
- recovery :
- recovery rate (between 0 and 1)
- fixed_rate :
- fixed coupon (in bps)
- """
- self.fixed_rate = fixed_rate
- self.notional = notional
- self._start_date = start_date
- self._end_date = end_date
- self.recovery = recovery
-
- self._fee_leg = FeeLeg(start_date, end_date, True, 1, 1)
- self._default_leg = ContingentLeg(start_date, end_date, 1)
- self._trade_date = None
- self._yc = None
- self._risky_annuity = None
- self._spread = None
- self.name = None
-
- @property
- def start_date(self):
- return self._start_date
-
- @property
- def end_date(self):
- return self._end_date
-
- @start_date.setter
- def start_date(self, d):
- self._fee_leg = FeeLeg(d, self.end_date, True, 1, 1)
- self._default_leg = ContingentLeg(d, self.end_date, 1)
- self._start_date = d
-
- @end_date.setter
- def end_date(self, d):
- self._fee_leg = FeeLeg(self.start_date, d, True, 1, 1)
- self._default_leg = ContingentLeg(self.start_date, d, 1)
- self._end_date = d
-
- def forward_annuity(self, exercise_date):
- step_in_date = exercise_date + datetime.timedelta(days=1)
- value_date = (pd.Timestamp(exercise_date) + 3* BDay()).date()
- a = self._fee_leg.pv(self.trade_date, step_in_date, self._value_date,
- self._yc, self._sc, False)
- Delta = self._fee_leg.accrued(step_in_date)
- df = self._yc.discount_factor(value_date)
- if exercise_date > self.trade_date:
- q = math.exp(-self.flat_hazard * year_frac(self._step_in_date, exercise_date))
- else:
- q = 1
- return a - Delta * df * q
-
- def forward_pv(self, exercise_date):
- """This is default adjusted forward price at time exercise_date"""
- step_in_date = exercise_date + datetime.timedelta(days=1)
- value_date = (pd.Timestamp(exercise_date) + 3* BDay()).date()
- a = self._fee_leg.pv(self.trade_date, step_in_date, self._value_date,
- self._yc, self._sc, False)
- Delta = self._fee_leg.accrued(step_in_date)
- df = self._yc.discount_factor(value_date)
- if exercise_date > self.trade_date:
- q = math.exp(-self.flat_hazard * (year_frac(self.trade_date, exercise_date)))
- else:
- q = 1
- clean_forward_annuity = a - Delta * df * q
- dl_pv = self._default_leg.pv(
- self.trade_date, step_in_date, self._value_date,
- self._yc, self._sc, self.recovery)
- forward_price = self.notional * (dl_pv - clean_forward_annuity * self.fixed_rate*1e-4)
- fep = self.notional * (1 - self.recovery) * (1 - q)
- return forward_price * self._yc.discount_factor(self._value_date) / df + fep
-
- @property
- def spread(self):
- return self._spread * 1e4
-
- def _update(self):
- 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._spread]),
- 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._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)
-
- @spread.setter
- def spread(self, s: float):
- """ s: spread in bps """
- self._spread = s * 1e-4
- self._update()
-
- @property
- def flat_hazard(self):
- sc_data = self._sc.inspect()['data']
- ## conversion to continuous compounding
- return math.log(1 + sc_data[0][1])
-
- @property
- def pv(self):
- return self.notional * self._pv
-
- @property
- def accrued(self):
- return - self.notional * self._accrued * self.fixed_rate * 1e-4
-
- @property
- def days_accrued(self):
- return int(self._accrued * 360)
-
- @property
- def clean_pv(self):
- return self.notional * self._clean_pv
-
- @property
- def price(self):
- return self._price
-
- @price.setter
- def price(self, val):
- pass
-
- @property
- def DV01(self):
- old_pv = self.pv
- self.spread += 1
- dv01 = self.pv - old_pv
- self.spread -= 1
- return dv01
-
- @property
- def IRDV01(self):
- old_pv = self.pv
- old_yc = self._yc
- for rh in self._helpers:
- rh.quote += 1e-4
- self._yc = ql_to_jp(self._ql_yc)
- self._update() ## to force recomputation
- new_pv = self.pv
- for r in self._helpers:
- r.quote -= 1e-4
- self._yc = old_yc
- self._update()
- return new_pv - old_pv
-
- @property
- def rec_risk(self):
- old_pv = self.pv
- old_recovery = self.recovery
- self.recovery = old_recovery - 0.01
- self._update()
- pv_minus = self.pv
- self.recovery = old_recovery + 0.01
- self._update()
- pv_plus = self.pv
- self.recovery = old_recovery
- self._update()
- return (pv_plus - pv_minus)/2
-
- @property
- def jump_to_default(self):
- return self.notional * (1 - self.recovery) - self.clean_pv
-
- @property
- def risky_annuity(self):
- return self._risky_annuity - self._accrued
-
- @property
- def trade_date(self):
- if self._trade_date is None:
- raise AttributeError('Please set trade_date first')
- else:
- return self._trade_date
-
- @trade_date.setter
- def trade_date(self, d):
- self.start_date = prev_immdate(pd.Timestamp(d)).date()
- settings = Settings()
- settings.evaluation_date = Date.from_datetime(d)
- self._helpers = rate_helpers(self.currency)
- self._ql_yc = YC(self._helpers)
- self._yc = ql_to_jp(self._ql_yc)
- self._trade_date = d
- self._step_in_date = self.trade_date + datetime.timedelta(days=1)
- self._accrued = self._fee_leg.accrued(self._step_in_date)
- self._value_date = (pd.Timestamp(self._trade_date) + 3* BDay()).date()
- if self._spread is not None:
- self._update()
-
- @classmethod
- def from_name(cls, index, series, tenor, trade_date = datetime.date.today(),
- notional = 10e6):
- try:
- with serenitasdb.cursor() as c:
- c.execute("SELECT maturity, coupon FROM index_maturity " \
- "WHERE index=%s AND series=%s AND tenor = %s",
- (index.upper(), series, tenor))
- maturity, coupon = next(c)
- except DataError as e:
- raise
- else:
- recovery = 0.4 if index.lower() == "ig" else 0.3
- start_date = prev_immdate(pd.Timestamp(trade_date)).date()
- instance = cls(start_date, maturity, recovery, coupon)
- instance.name = "MARKIT CDX.NA.{}.{} {:%m/%y} ".format(
- index.upper(),
- series,
- maturity)
- if index.upper() in ["IG", "HY"]:
- instance.currency = "USD"
- else:
- instance.currency = "EUR"
- instance.notional = notional
- instance.trade_date = trade_date
- return instance
-
- def __repr__(self):
- if self.days_accrued > 1:
- accrued_str = "Accrued ({} Days)".format(self.days_accrued)
- else:
- accrued_str = "Accrued ({} Day)".format(self.days_accrued)
- s = ["{:<20}\t{:>15}".format("CDS Index", colored(self.name, attrs = ['bold'])),
- "",
- "{:<20}\t{:>15}".format("Trade Date", ('{:%m/%d/%y}'.
- format(self.trade_date))),
- "{:<20}\t{:>15.2f}\t\t{:<20}\t{:>10,.2f}".format("Trd Sprd (bp)",
- self.spread,
- "Coupon (bp)",
- self.fixed_rate),
- "{:<20}\t{:>15.2f}\t\t{:<20}\t{:>10}".format("1st Accr Start",
- self.spread,
- "Payment Freq",
- "Quarterly"),
- "{:<20}\t{:>15}\t\t{:<20}\t{:>10.2f}".format("Maturity Date",
- ('{:%m/%d/%y}'.
- format(self.end_date)),
- "Rec Rate",
- self.recovery),
- "{:<20}\t{:>15}\t\t{:<20}\t{:>10}".format("Bus Day Adj",
- "Following",
- "Day Count",
- "ACT/360"),
- "",
- colored("Calculator", attrs = ['bold']),
- "{:<20}\t{:>15}".format("Valuation Date", ('{:%m/%d/%y}'.
- format(self.trade_date))),
- "{:<20}\t{:>15}".format("Cash Settled On", ('{:%m/%d/%y}'.
- format(self._value_date))),
- "",
- "{:<20}\t{:>15.8f}\t\t{:<20}\t{:>10,.2f}".format("Price",
- self.price,
- "Spread DV01",
- self.DV01),
- "{:<20}\t{:>15,.0f}\t\t{:<20}\t{:>10,.2f}".format("Principal",
- self.clean_pv,
- "IR DV01",
- self.IRDV01),
- "{:<20}\t{:>15,.0f}\t\t{:<20}\t{:>10,.2f}".format(accrued_str,
- self.accrued,
- "Rec Risk (1%)",
- self.rec_risk),
- "{:<20}\t{:>15,.0f}\t\t{:<20}\t{:>10,.0f}".format("Cash Amount",
- self.pv,
- "Def Exposure",
- self.jump_to_default)
- ]
- return "\n".join(s)
def year_frac(d1, d2, day_count_conv = "Actual/365"):
""" compute the year fraction between two dates """
@@ -311,7 +18,6 @@ def year_frac(d1, d2, day_count_conv = "Actual/365"):
elif day_count_conv.lower() in ["actual/360", "act/360"]:
return (d2-d1).days/360
-
def calib(S0, fp, exercise_date, exercise_date_settle, index,
rolled_curve, tilt, w):
S = S0 * tilt * 1e-4
@@ -388,6 +94,7 @@ class Option:
b *= eta
S0 = brentq(calib, a, b, args)
+
G = g(self.index, self.strike, self.exercise_date)
if T == 0:
pv = self.notional * (g(self.index, self.index.spread, self.exercise_date) - G)
@@ -397,7 +104,8 @@ class Option:
return - pv if self.index.spread < self.strike else 0
Zstar = (math.log(self.strike/S0) + self.sigma**2/2 * T) / \
- (self.sigma * math.sqrt(T))
+ (self.sigma * math.sqrt(T))
+
if self.option_type == "payer":
Z = Zstar + np.logspace(0, 1.5, 300) - 1
elif self.option_type == "receiver":
@@ -514,9 +222,11 @@ def option(index, exercise_date, sigma, K, option_type="payer"):
if __name__ == "__main__":
import datetime
- from swaption import Index, Option
+ from analytics import Index
+ from swaption import Option
+
ig27_5yr = Index.from_name('ig', 27, '5yr', datetime.date(2016, 10, 24))
ig27_5yr.spread = 74
- payer = Option(ig27_5yr, datetime.date(2017, 3, 15), 75)
- payer.sigma = 0.4847
+ payer = Option(ig27_5yr, datetime.date(2016, 12, 21), 65)
+ payer.sigma = 0.428
payer.notional = 10e6