diff options
Diffstat (limited to 'python/analytics')
| -rw-r--r-- | python/analytics/option.py | 89 |
1 files changed, 86 insertions, 3 deletions
diff --git a/python/analytics/option.py b/python/analytics/option.py index 5e764b68..c8167f53 100644 --- a/python/analytics/option.py +++ b/python/analytics/option.py @@ -455,6 +455,90 @@ def _get_keys(df, models=["black", "precise"]): else: yield (quotedate, source, option_type) +class QuoteSurface(): + def __init__(self, index_type, series, tenor='5yr', trade_date=datetime.date.today()): + self._quotes = pd.read_sql_query( + "SELECT swaption_quotes.*, ref, fwdspread FROM swaption_quotes " \ + "JOIN swaption_ref_quotes USING (quotedate, index, series, expiry)" \ + "WHERE quotedate::date = %s AND index= %s AND series = %s " \ + "AND quote_source != 'SG' " \ + "ORDER BY quotedate DESC", + _engine, + parse_dates = ['quotedate', 'expiry'], + params=(trade_date, index_type.upper(), series)) + self._quotes.loc[(self._quotes.quote_source == "GS") & (self._quotes['index'] =="HY"), + ["pay_bid", "pay_offer", "rec_bid", "rec_offer"]] *=100 + if self._quotes.empty: + raise ValueError("No quotes for that day") + self._quotes['quotedate'] = (self._quotes['quotedate']. + dt.tz_convert('America/New_York'). + dt.tz_localize(None)) + self._quotes = self._quotes.sort_values('quotedate') + self.trade_date = trade_date + + def list(self, source=None): + """returns list of vol surfaces""" + r = [] + for quotedate, quotesource in (self._quotes[['quotedate', 'quote_source']]. + drop_duplicates(). + itertuples(index=False)): + if source is None or quotesource == source: + r.append((quotedate, quotesource)) + return r + +class VolSurface(QuoteSurface): + def __init__(self, index_type, series, tenor='5yr', trade_date=datetime.date.today()): + super().__init__(index_type, series, tenor, trade_date) + self._surfaces = {} + + def __getitem__(self, surface_id): + if surface_id not in self._surfaces: + quotedate, source = surface_id + quotes = self._quotes[(self._quotes.quotedate == quotedate) & + (self._quotes.quote_source == source)] + quotes = quotes.assign(moneyness=quotes.strike / quotes.fwdspread, + time=((quotes.expiry - self.trade_date).dt.days + 0.25) / 365) + spline = lambda df: CubicSpline(np.log(df.moneyness), df.vol, bc_type="natural") + h = quotes.sort_values('moneyness').groupby('time').apply(spline) + self._surfaces[surface_id] = MyInterp(h.index.values, h.values) + return self._surfaces[surface_id] + else: + return self._surfaces[surface_id] + + def vol(self, T, moneyness, surface_id): + """computes the vol for a given moneyness and term.""" + return self[surface_id](T, moneyness) + + def plot(self, surface_id): + fig = plt.figure() + ax = fig.gca(projection='3d') + surf = self[surface_id] + time = surf.T + y = np.arange(-0.15, 0.7, 0.01) + x = np.arange(time[0], time[-1], 0.01) + xx, yy = np.meshgrid(x, y) + z = np.vstack([self[surface_id](xx, y) for xx in x]) + surf = ax.plot_surface(xx, yy, z.T, + cmap=cm.viridis) + ax.set_xlabel("Year fraction") + ax.set_ylabel("Moneyness") + ax.set_zlabel("Volatility") + +class MyInterp: + def __init__(self, T, f): + self.T = T + self.f = f + self._dgrid = np.diff(self.T) + + def __call__(self, x, y): + grid_offset = self.T - x + i = np.searchsorted(grid_offset, 0.) + if i == 0: + return self.f[0](y) + else: + return -self.f[i](y) * grid_offset[i-1] / self._dgrid[i-1] + \ + self.f[i-1](y) * grid_offset[i] / self._dgrid[i-1] + class VolatilitySurface(ForwardIndex): def __init__(self, index_type, series, tenor='5yr', trade_date=datetime.date.today()): self._index = Index.from_name(index_type, series, tenor, trade_date, notional=1.) @@ -499,9 +583,8 @@ class VolatilitySurface(ForwardIndex): fig = plt.figure() ax = fig.gca(projection='3d') surf = self[surface_id] - time, moneyness = surf.get_knots() - xx, yy = np.meshgrid(np.arange(time[0], time[-1], 0.01), - np.arange(moneyness[0], moneyness[-1], 0.01)) + xx, yy = np.meshgrid(np.arange(surf.x_min, surf.x_max, 0.01), + np.arange(surf.y_min, surf.y_max, 0.01)) surf = ax.plot_surface(xx, yy, self[surface_id].ev(xx, yy), cmap = cm.viridis) ax.set_xlabel("Year fraction") |
