aboutsummaryrefslogtreecommitdiffstats
path: root/python
diff options
context:
space:
mode:
Diffstat (limited to 'python')
-rw-r--r--python/swaption.py161
-rw-r--r--python/test_option.py73
2 files changed, 181 insertions, 53 deletions
diff --git a/python/swaption.py b/python/swaption.py
new file mode 100644
index 00000000..4e81acb2
--- /dev/null
+++ b/python/swaption.py
@@ -0,0 +1,161 @@
+from pyisda.legs import ContingentLeg, FeeLeg
+from pyisda.flat_hazard import strike_vec
+from pyisda.curve import YieldCurve, BadDay, SpreadCurve
+import math
+from scipy.optimize import brentq
+from scipy.integrate import simps
+import datetime
+from tranche_functions import GHquad
+
+class Index():
+ """ minimal class to represent a credit index """
+ def __init__(self, start_date, end_date, recovery, fixed_rate):
+ self.fixed_rate = fixed_rate
+ self.notional = 1
+ 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)
+
+ @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 pv(self, trade_date, exercise_date, yc, sc, h = None):
+ step_in_date = exercise_date + datetime.timedelta(days=1)
+ a = self._fee_leg.pv(trade_date, step_in_date, trade_date, yc, sc, False)
+ Delta = self._fee_leg.accrued(step_in_date)
+ if exercise_date > trade_date:
+ Delta *= math.exp(-h * year_frac(trade_date, exercise_date))
+ clean_forward_annuity = a - Delta
+
+ dl_pv = self._default_leg.pv(
+ trade_date, step_in_date, trade_date, yc, sc, self.recovery)
+ return dl_pv - clean_forward_annuity * self.fixed_rate, clean_forward_annuity
+
+def year_frac(d1, d2, day_count_conv = "Actual/365"):
+ """ compute the year fraction between two dates """
+ if day_count_conv.lower() in ["actual/365", "act/365"]:
+ return (d2-d1).days/365
+ elif day_count_conv.lower() in ["actual/360", "act/360"]:
+ return (d2-d1).days/360
+
+def DAforward_price(spread, trade_date, exercise_date, yield_curve, index):
+ cash_settle_date = (pd.Timestamp(trade_date) + 3* BDay()).date()
+ h, sc = flat_hazard(spread, yc, trade_date, cash_settle_date,
+ start_date = trade_date,
+ end_date = index.end_date,
+ recovery_rate = index.recovery)
+ exercise_date_settle = (pd.Timestamp(exercise_date) + 3* BDay()).date()
+ forward_price = index.pv(trade_date, exercise_date, yield_curve, sc, h)[0]
+ fep = (1 - index.recovery) * (1-math.exp(-h*year_frac(trade_date, exercise_date)))
+ df = yc.discount_factor(exercise_date_settle)
+ price = fep + 1/df * forward_price
+ return price
+
+def flat_hazard(spread, yc, trade_date=datetime.date.today(),
+ cash_settle_date = None,
+ start_date = datetime.date.today(),
+ end_date = datetime.date(2021, 6, 20),
+ recovery_rate = 0.4):
+ step_in_date = start_date + datetime.timedelta(days=1)
+ if cash_settle_date is None:
+ cash_settle_date = (pd.Timestamp(trade_date) + 3* BDay()).date()
+ sc = SpreadCurve(trade_date, yc, trade_date, step_in_date,
+ cash_settle_date,
+ [end_date], array.array('d', [spread]), recovery_rate)
+ sc_data = sc.inspect()['data']
+ ## conversion to continuous compounding
+ hazard_rate = math.log(1 + sc_data[0][1])
+ return (hazard_rate, SpreadCurve.from_flat_hazard(trade_date, hazard_rate))
+
+def calib(S0, fp, forward_yield_curve, exercise_date_settle, index, tilt, w):
+ S = S0 * tilt
+ a, b = strike_vec(S, forward_yield_curve, exercise_date_settle,
+ index.start_date, index.end_date, index.recovery)
+ vec = a - index.fixed_rate * b
+ df = forward_yield_curve.discount_factor(exercise_date_settle)
+ return 1/df*np.inner(vec - fp, w)
+
+def g(spread, forward_yield_curve, index):
+ """ computes the strike price using the expected forward yield curve """
+ exercise_date = forward_yield_curve.base_date
+ step_in_date = exercise_date + datetime.timedelta(days=1)
+ exercise_date_settle = (pd.Timestamp(exercise_date) + 3* BDay()).date()
+ sc = SpreadCurve(exercise_date, forward_yield_curve, exercise_date, step_in_date,
+ exercise_date_settle,
+ [index.end_date], array.array('d', [spread]), index.recovery)
+ a = index._fee_leg.pv(exercise_date, step_in_date, exercise_date,
+ forward_yield_curve, sc, True)
+ dl_pv = index._default_leg.pv(
+ exercise_date, step_in_date, exercise_date, forward_yield_curve,
+ sc, index.recovery)
+ df = forward_yield_curve.discount_factor(exercise_date_settle)
+ return 1/df * (dl_pv - a * index.fixed_rate)
+
+def ATMstrike(spread, trade_date, exercise_date, yield_curve, index):
+ fp = DAforward_price(spread, trade_date, exercise_date, yc, index)
+ yc_forward = yc.expected_forward_curve(exercise_date)
+ closure = lambda S: g(S, yc_forward, index) - fp
+ eta = 1.1
+ a = spread
+ b = spread * eta
+ while True:
+ if closure(b) > 0:
+ break
+ return brentq(closure, a, b)
+
+def option(index, ref, trade_date, exercise_date, yield_curve, sigma, K, option_type="payer"):
+ """ computes the pv of an option using Pedersen's model """
+ fp = DAforward_price(ref, trade_date, exercise_date, yield_curve, index)
+ forward_yc = yield_curve.expected_forward_curve(exercise_date)
+ #expiry is end of day (not sure if this is right)
+ T = year_frac(trade_date, exercise_date)
+ Z, w = GHquad(50)
+ tilt = np.exp(-sigma**2/2 * T + sigma * Z * math.sqrt(T))
+ exercise_date_settle = (pd.Timestamp(exercise_date) + 3* BDay()).date()
+ args = (fp, forward_yc, exercise_date_settle, index, tilt, w)
+ ## atm forward is greater than spread
+ eta = 1.1
+ a = ref
+ b = ref * eta
+ while True:
+ if calib(*((b,) + args)) > 0:
+ break
+ b *= eta
+ S0 = brentq(calib, a, b, args)
+ S = S0 * tilt
+ G = g(K, forward_yc, index)
+ handle = lambda Z: g(S0 * math.exp(-sigma**2/2 * T + sigma * Z * math.sqrt(T)),
+ forward_yc, index) - G
+ Zstar = brentq(handle, -3, 3)
+ if option_type.lower() == "payer":
+ Z = Zstar + np.logspace(0, 1.1, 300) - 1
+ elif option_type.lower() == "receiver":
+ Z = Zstar - np.logspace(0, 1.1, 300) + 1
+ else:
+ raise ValueError("option_type needs to be either 'payer' or 'receiver'")
+ S = S0 * np.exp(-sigma**2/2 * T + sigma * Z * math.sqrt(T))
+ df = forward_yc.discount_factor(exercise_date_settle)
+ a, b = strike_vec(S, forward_yc, exercise_date_settle,
+ index.start_date, index.end_date, index.recovery)
+ val = ((a - b * index.fixed_rate)/df - G) * 1/math.sqrt(2*math.pi) * np.exp(-Z**2/2)
+ return simps(val, Z) * yield_curve.discount_factor(exercise_date_settle)
diff --git a/python/test_option.py b/python/test_option.py
index 061be7c8..daa52d81 100644
--- a/python/test_option.py
+++ b/python/test_option.py
@@ -1,24 +1,22 @@
-from pyisda.curve import YieldCurve, BadDay, SpreadCurve
from pyisda.utils import build_yc
-from pyisda.cdsone import upfront_charge
-from pyisda.legs import ContingentLeg, FeeLeg
+
from quantlib.settings import Settings
from quantlib.time.api import (
Date, today, Period, Years, WeekendsOnly, Quarterly, CDS, Actual360,
- ModifiedFollowing, Actual365Fixed)
+ ModifiedFollowing, Actual365Fixed, Months)
from quantlib.quotes import SimpleQuote
from quantlib.termstructures.credit.api import (
SpreadCdsHelper, Interpolator, ProbabilityTrait, PiecewiseDefaultCurve )
-from quantlib.termstructures.yields.api import YieldTermStructure
+from quantlib.termstructures.yields.api import YieldTermStructure, DepositRateHelper
from quantlib.pricingengines.credit.isda_cds_engine import IsdaCdsEngine
import datetime
-import array
import math
import numpy as np
import pandas as pd
+from pandas.tseries.offsets import BusinessDay as BDay
from db import dbengine
from yieldcurve import YC
-import pdb
+from swaption import Index
def get_spread(index, series):
engine = dbengine("serenitasdb")
@@ -34,6 +32,7 @@ def get_spread(index, series):
def test_sc(df):
settings = Settings()
+ settings.evaluation_date = today()
tenors = [Period(3, Years), Period(5, Years), Period(7, Years), Period(10, Years)]
quotes = []
for i in range(4):
@@ -47,16 +46,17 @@ def test_sc(df):
pdc = PiecewiseDefaultCurve(ProbabilityTrait.SurvivalProbability,
Interpolator.LogLinear,
0, WeekendsOnly(), helpers, Actual365Fixed())
+ ts = YC()
+ yts.link_to(ts)
for r in df.itertuples(index=True, name = None):
settings.evaluation_date = Date.from_datetime(r[0])
ts = YC()
yts.link_to(ts)
for i in range(4):
quotes[i].value = r[i+1] *1e-4
- return helpers
+ print(pdc.dates)
+ print(pdc.data)
-def FEP(sc, recovery):
- pass
if __name__ == "__main__":
settings = Settings()
@@ -64,47 +64,14 @@ if __name__ == "__main__":
settings.evaluation_date = today()
start_date = datetime.date(2016, 6, 20)
term_date = datetime.date(2021, 6, 20)
-
- dc = YC()
- isda_engine = "po"
- value_date = trade_date
+ recovery = 0.4
+ fixed_rate = 0.01
+ ig26 = Index(start_date, term_date, recovery, fixed_rate)
yc = build_yc(trade_date, True)
- spread = 0.007
- spread = array.array('d', [spread])
- value_date = datetime.date(2016, 7, 29)
- step_in_date = trade_date + datetime.timedelta(days=1)
- sc = SpreadCurve(trade_date, yc, start_date, step_in_date,
- value_date, [term_date], spread, 0.4, True)
- sc_data = sc.inspect()['data']
- hazard_rate = math.log(1 + sc_data[0][1])
- expiration_date = datetime.date(2016, 9, 21)
- fee_leg = FeeLeg(start_date, term_date, True, 1, 1)
- default_leg = ContingentLeg(start_date, term_date, 1)
- fee_leg_expiry = FeeLeg(start_date, expiration_date, True, 1, 1)
- default_leg_expiry = ContingentLeg(start_date, expiration_date, 1)
-
- fee_leg_pv = fee_leg.pv(trade_date, step_in_date, trade_date,
- yc, sc, True)
- default_leg_pv = default_leg.pv(trade_date, step_in_date, trade_date,
- yc, sc, 0.4)
- V1 = default_leg_pv - fee_leg_pv
-
- fee_leg_expiry_pv = fee_leg_expiry.pv(trade_date, step_in_date, trade_date,
- yc, sc, True)
- default_leg_expiry_pv = default_leg_expiry.pv(trade_date, step_in_date, trade_date,
- yc, sc, 0.4)
- V2 = default_leg_expiry_pv - fee_leg_expiry_pv
-
- forward_annuity = fee_leg.pv(trade_date, expiration_date+datetime.timedelta(days=1), trade_date,
- yc, sc, True)
- exercise_settlement = datetime.date(2016, 9, 25)
- print((0.007-0.01) * forward_annuity)
-
- forward_price = forward_annuity*(0.007-0.01) / (
- yc.discount_factor(exercise_settlement)*math.exp(-hazard_rate * (expiration_date-trade_date).days/365
- ))
- print(fee_leg.pv(trade_date, step_in_date, trade_date,
- yc, sc, True))
-
- print(fee_leg.pv(trade_date, expiration_date + datetime.timedelta(days=1), trade_date,
- yc, sc, True))
+ exercise_date = datetime.date(2016, 10, 19)
+ exercise_date_settle = pd.Timestamp(exercise_date + 3* BDay()).date()
+ spread = np.array([65, 70, 72.5, 75, 77.5, 80, 85, 90, 95, 100, 110, 120, 130, 140])
+ tailprob = np.array([64.2, 51.7, 45.8, 40.2, 35, 30.4, 23.2, 18, 14.3, 11.6, 7.9, 5.6, 4.1, 3])
+ cumprob = 1-tailprob/100
+ hist = cumprob/spread
+ hist = np.hstack((0, hist))