aboutsummaryrefslogtreecommitdiffstats
path: root/python/analytics/option.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/analytics/option.py')
-rw-r--r--python/analytics/option.py89
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")