diff options
Diffstat (limited to 'python/analytics/option.py')
| -rw-r--r-- | python/analytics/option.py | 44 |
1 files changed, 27 insertions, 17 deletions
diff --git a/python/analytics/option.py b/python/analytics/option.py index 8acdf121..e2bafee8 100644 --- a/python/analytics/option.py +++ b/python/analytics/option.py @@ -1,6 +1,5 @@ from __future__ import division -import array import bottleneck as bn import datetime import math @@ -17,9 +16,7 @@ from yieldcurve import roll_yc from pandas.tseries.offsets import BDay from functools import wraps -from pyisda.curve import SpreadCurve from pyisda.flat_hazard import pv_vec -import numpy as np from scipy.optimize import brentq from scipy.integrate import simps from scipy.interpolate import SmoothBivariateSpline, interp1d, CubicSpline @@ -73,6 +70,7 @@ def ATMstrike(index, exercise_date): else: return g(index, index.fixed_rate, exercise_date, pv=fp) + class BlackSwaption(ForwardIndex): """Swaption class""" __slots__ = ('_T', '_G', '_strike', 'option_type', @@ -282,8 +280,8 @@ class BlackSwaption(ForwardIndex): notional_ratio = self.index.notional / self.notional dv01 = self.pv - old_pv delta = -self.index._direction * dv01 * notional_ratio / \ - (self.index.pv - old_index_pv) - self.index.spread = old_spread + (self.index.pv - old_index_pv) + self.index.spread = old_spread self._update() return delta @@ -292,7 +290,7 @@ class BlackSwaption(ForwardIndex): if self._T: return self._T else: - return ((self.exercise_date - self.index.value_date).days + 0.25)/365 + return ((self.exercise_date - self.index.value_date).days + 0.25) / 365 @property def gamma(self): @@ -358,7 +356,7 @@ class BlackSwaption(ForwardIndex): self.index.spread = orig_spread * (1 + ss) curr_vol = vol_surface.ev(self.T, self.moneyness) for vs in vol_shock: - self.sigma = curr_vol * ( 1 + vs ) + self.sigma = curr_vol * (1 + vs) r.append([getattr(self, p) for p in actual_params]) self.index.spread = orig_spread self.sigma = orig_sigma @@ -399,6 +397,7 @@ class BlackSwaption(ForwardIndex): def __str__(self): return "{} at 0x{:02x}".format(type(self), id(self)) + class Swaption(BlackSwaption): __slots__ = ("_cache", "_Z", "_w") def __init__(self, index, exercise_date, strike, option_type="payer", @@ -492,12 +491,12 @@ def _get_keys(df, models=["black", "precise"]): class QuoteSurface(): def __init__(self, index_type, series, tenor='5yr', value_date=datetime.date.today()): self._quotes = pd.read_sql_query( - "SELECT quotedate, index, series, ref, fwdspread, fwdprice, expiry, " \ - "swaption_quotes.*, quote_source " \ - "FROM swaption_quotes " \ - "JOIN swaption_ref_quotes USING (ref_id)" \ - "WHERE quotedate::date = %s AND index= %s AND series = %s " \ - "AND quote_source != 'SG' " \ + "SELECT quotedate, index, series, ref, fwdspread, fwdprice, expiry, " + "swaption_quotes.*, quote_source " + "FROM swaption_quotes " + "JOIN swaption_ref_quotes USING (ref_id)" + "WHERE quotedate::date = %s AND index= %s AND series = %s " + "AND quote_source != 'SG' " "ORDER BY quotedate, strike", _engine, parse_dates=['quotedate', 'expiry'], @@ -620,12 +619,14 @@ def _calibrate_model(index, quotes, option_type, option_model, else: raise ValueError("interp_method needs to be one of 'bivariate_spline' or 'bivariate_linear'") + def _calibrate(index, quotes, option_type, **kwargs): if 'option_model' in kwargs: return _calibrate_model(index, quotes, option_type, **kwargs) elif 'beta' in kwargs: return _calibrate_sabr(index, quotes, option_type, kwargs['beta']) + class ModelBasedVolSurface(VolSurface): def __init__(self, index_type, series, tenor='5yr', value_date=datetime.date.today(), interp_method='bivariate_spline'): @@ -676,7 +677,6 @@ class ModelBasedVolSurface(VolSurface): self[surface_id] return self._index_refs[surface_id] - def plot(self, surface_id): fig = plt.figure() ax = fig.gca(projection='3d') @@ -690,19 +690,23 @@ class ModelBasedVolSurface(VolSurface): ax.set_ylabel("Moneyness") ax.set_zlabel("Volatility") + class BlackSwaptionVolSurface(ModelBasedVolSurface): pass + class SwaptionVolSurface(ModelBasedVolSurface): pass + class SABRVolSurface(ModelBasedVolSurface): pass + @lru_cache(maxsize=32) def _forward_annuity(expiry, index): step_in_date = expiry + datetime.timedelta(days=1) - expiry_settle = pd.Timestamp(expiry) + 3* BDay() + expiry_settle = pd.Timestamp(expiry) + 3 * BDay() df = index._yc.discount_factor(expiry_settle) a = index._fee_leg.pv(index.value_date, step_in_date, index.value_date, index._yc, index._sc, False) @@ -710,6 +714,7 @@ def _forward_annuity(expiry, index): q = index._sc.survival_probability(expiry) return a - Delta * df * q + class ProbSurface(QuoteSurface): def __init__(self, index_type, series, tenor='5yr', value_date=datetime.date.today()): @@ -745,6 +750,7 @@ class ProbSurface(QuoteSurface): prob = np.clip(prob, 1e-10, None, out=prob) quotes['prob'] = prob quotes.dropna(subset=['prob'], inplace=True) + def spline(df): x = df.strike y = logit(df.prob) @@ -752,7 +758,7 @@ class ProbSurface(QuoteSurface): y = y[np.hstack([True, np.diff(y) < 0])] return CubicSpline(x, y, bc_type='natural') h = quotes.sort_values('strike').groupby('time').apply(spline) - self._surfaces[surface_id] = MyInterp(h.index.values, h.values) + self._surfaces[surface_id] = BivariateLinearFunction(h.index.values, h.values) return self._surfaces[surface_id] else: return self._surfaces[surface_id] @@ -764,6 +770,7 @@ class ProbSurface(QuoteSurface): def quantile_spread(self, T, prob, surface_id): """computes the spread for a given probability and term.""" l_prob = logit(prob) + def prob_calib(x, T, surface_id): return l_prob - self[surface_id](T, math.log(x)) eta = 1.5 @@ -813,6 +820,7 @@ class ProbSurface(QuoteSurface): ax.set_ylabel("Strike") ax.set_zlabel("Tail Probability") + class BivariateLinearFunction: """Linear interpolation between a set of functions""" def __init__(self, T, f): @@ -829,6 +837,7 @@ class BivariateLinearFunction: 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] + def calib_sabr(x, option, strikes, pv, beta): alpha, rho, nu = x F = option.forward_spread @@ -840,11 +849,12 @@ def calib_sabr(x, option, strikes, pv, beta): r[i] = option.pv - pv[i] return r + def _calibrate_sabr(index, quotes, option_type, beta): T, r = [], [] column = 'pay_mid' if option_type == 'payer' else 'rec_mid' for expiry, df in quotes.groupby(['expiry']): - option = BlackSwaption(self._index, expiry.date(), 100, option_type) + option = BlackSwaption(index, expiry.date(), 100, option_type) prog = least_squares(calib_sabr, (0.01, 0.3, 3.5), bounds=([0, -1, 0], [np.inf, 1, np.inf]), args=(option, df.strike.values, df[column].values, beta)) |
