aboutsummaryrefslogtreecommitdiffstats
path: root/python/analytics/basket_index.py
blob: 261197cc3eb112a515fa9357983b196a1729eb11 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import index_data as data
from dateutil.relativedelta import relativedelta
from pyisda.credit_index import CreditIndex
from typing import List
import numpy as np
import pandas as pd
import datetime
from scipy.optimize import brentq
from pandas.tseries.offsets import BDay

class BasketIndex(CreditIndex):
    index_type: str
    series: int
    recovery: float
    step_in_date: pd.Timestamp
    value_date: pd.Timestamp
    tweaks: List[float]

    def __init__(self, index_type: str, series: int, tenors: List[str], *args,
                 trade_date: pd.Timestamp=pd.Timestamp.today().normalize() - BDay()):
        self.index_type = index_type
        self.series = series
        if index_type == 'IG' or index_type == 'EU':
            self.recovery = 0.4
        else:
            self.recovery = 0.3
        self.index_quotes = (data.get_index_quotes(index_type, series,
                                                   tenors, years=None)['closeprice'].
                             unstack().
                             groupby(level='date', as_index=False).nth(0).
                             reset_index(['index', 'series'], drop=True))
        self.index_desc = pd.read_sql_query("SELECT tenor, maturity, coupon * 1e-4 AS coupon, " \
                                            "issue_date "\
                                            "FROM index_maturity " \
                                            "WHERE index=%s AND series=%s",
                                            data._serenitas_engine,
                                            index_col='tenor',
                                            params=(index_type, series),
                                            parse_dates=['maturity', 'issue_date'])
        self.index_quotes.columns = self.index_desc.loc[self.index_quotes.columns, "maturity"]
        self.index_quotes = 1 - self.index_quotes / 100
        self.issue_date = self.index_desc.issue_date[0]
        maturities = self.index_quotes.columns.sort_values().to_pydatetime()
        self.index_desc = self.index_desc.reset_index().set_index('maturity')
        curves, args = data.get_singlenames_curves(index_type, series, trade_date)
        _, jp_yc, _, step_in_date, value_date, _ = args
        self.yc = jp_yc
        self.step_in_date = step_in_date
        self.value_date = value_date
        self._trade_date = trade_date
        self.tweaks = []
        super().__init__(self.issue_date, maturities, curves)

    @property
    def trade_date(self):
        return self._trade_date

    @trade_date.setter
    def trade_date(self, d: pd.Timestamp):
        curves, args = data.get_singlenames_curves(self.index_type, self.series, d)
        _, jp_yc, _, step_in_date, value_date, _ = args
        self.yc = jp_yc
        self.step_in_date = step_in_date
        self.value_date = value_date
        self._trade_date = d
        self.curves = curves

    def pv(self, maturity: pd.Timestamp, epsilon=0.):
        coupon = self.index_desc.loc[maturity, 'coupon']
        return super().pv(self.step_in_date, self.value_date, maturity, self.yc,
                          self.recovery, coupon, epsilon)

    def duration(self, maturity):
        return super().duration(self.step_in_date, self.value_date, maturity, self.yc)

    def theta(self, maturity):
        if self.step_in_date.date() > maturity - relativedelta(years=1):
            return np.NaN
        else:
            coupon = self.index_desc.loc[maturity, 'coupon']
            index_quote = self.index_quotes.loc[self.trade_date,maturity]
            return super().theta(self.step_in_date, self.value_date, maturity,
                              self.yc, self.recovery, coupon, index_quote)
    def tweak(self):
        """ tweak the singlename curves to match index quotes"""
        quotes = self.index_quotes.loc[self.trade_date]
        self.tweaks = []
        for m, index_quote in quotes.iteritems():
            coupon = self.index_desc.loc[m, 'coupon']
            lo, hi = -0.3, 0.3
            while lo > -1:
                try:
                    eps = brentq(lambda epsilon: self.pv(m, epsilon) -
                                 index_quote, lo, hi)
                except ValueError:
                    lo *= 1.1
                    hi *= 1.1
                else:
                    break
            else:
                print("couldn't calibrate for date: {} and maturity: {}".
                      format(self.trade_date.date(), m.date()))
                self.tweaks.append(np.NaN)
                continue
            self.tweaks.append(eps)
            self.tweak_portfolio(eps, m)

if __name__ == "__main__":
    ig28 = BasketIndex("IG", 28, ["3yr", "5yr", "7yr", "10yr"])
    from quantlib.time.api import Schedule, Rule, Date, Period, WeekendsOnly
    from quantlib.settings import Settings
    settings = Settings()

    cds_schedule = Schedule.from_rule(settings.evaluation_date, Date.from_datetime(ig28.maturities[-1]),
                                     Period('3M'), WeekendsOnly(), date_generation_rule=Rule.CDS2015)
    sp = ig28.survival_matrix(cds_schedule.to_npdates().view('int') + 134774)