diff options
Diffstat (limited to 'python/analytics/option.py')
| -rw-r--r-- | python/analytics/option.py | 67 |
1 files changed, 65 insertions, 2 deletions
diff --git a/python/analytics/option.py b/python/analytics/option.py index c8167f53..26af228c 100644 --- a/python/analytics/option.py +++ b/python/analytics/option.py @@ -1,6 +1,7 @@ from __future__ import division import array +import bottleneck as bn import datetime import math import numpy as np @@ -20,12 +21,12 @@ 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 +from scipy.interpolate import SmoothBivariateSpline, interp2d from matplotlib import cm from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt from multiprocessing import Pool -from functools import partial +from functools import partial, lru_cache from itertools import chain from scipy.optimize import least_squares @@ -524,6 +525,68 @@ class VolSurface(QuoteSurface): ax.set_ylabel("Moneyness") ax.set_zlabel("Volatility") +@lru_cache(maxsize=8) +def _forward_annuity(expiry, index): + step_in_date = expiry + datetime.timedelta(days=1) + expiry_settle = pd.Timestamp(expiry) + 3* BDay() + df = index._yc.discount_factor(expiry_settle) + a = index._fee_leg.pv(index.trade_date, step_in_date, + index.trade_date, index._yc, index._sc, False) + Delta = index._fee_leg.accrued(step_in_date) + q = index._sc.survival_probability(expiry) + return a - Delta * df * q + +class ProbSurface(QuoteSurface): + + def __init__(self, index_type, series, tenor='5yr', trade_date=datetime.date.today()): + super().__init__(index_type, series, tenor, trade_date) + self._surfaces = {} + self._index = Index.from_name(index_type, series, tenor, trade_date) + + 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)] + self._index.ref = quotes.ref[0] + quotes = quotes.assign(time=((quotes.expiry - self.trade_date).dt.days + 0.25) / 365, + pay_mid=quotes[['pay_bid','pay_offer']].mean(1), + rec_mid=quotes[['rec_bid','rec_offer']].mean(1), + forward_annuity=quotes.expiry.apply(_forward_annuity, + args=(self._index,))) + quotes.pay_mid /= quotes.forward_annuity + quotes.rec_mid /= quotes.forward_annuity + quotes = quotes.sort_values(['expiry', 'strike']) + prob_pay = np.concatenate([-np.gradient(df.pay_mid, df.strike) + for _, df in quotes.groupby('expiry')]) + prob_rec = np.concatenate([1-np.gradient(df.rec_mid, df.strike) + for _, df in quotes.groupby('expiry')]) + prob = bn.nanmean(np.stack([prob_pay, prob_rec]), axis=0) + f = SmoothBivariateSpline(quotes.time.values[~np.isnan(prob)], + quotes.strike.values[~np.isnan(prob)], + logit(prob[~np.isnan(prob)])) + self._surfaces[surface_id] = f + return self._surfaces[surface_id] + else: + return self._surfaces[surface_id] + + def tail_prob(self, T, strike, surface_id): + """computes the vol for a given moneyness and term.""" + return expit(self[surface_id](T, strike)) + + def plot(self, surface_id): + fig = plt.figure() + ax = fig.gca(projection='3d') + surf = self[surface_id] + time, strike = surf.get_knots() + xx, yy = np.meshgrid(np.arange(time[0], time[-1], 0.01), + np.arange(strike[0], strike[-1], 0.01)) + surf = ax.plot_surface(xx, yy, expit(surf.ev(xx, yy)), + cmap=cm.viridis, vmax=1) + ax.set_xlabel("Year fraction") + ax.set_ylabel("Strike") + ax.set_zlabel("Tail Probability") + class MyInterp: def __init__(self, T, f): self.T = T |
