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