diff options
Diffstat (limited to 'python/analytics/option.py')
| -rw-r--r-- | python/analytics/option.py | 62 |
1 files changed, 55 insertions, 7 deletions
diff --git a/python/analytics/option.py b/python/analytics/option.py index 4db3d1dc..9d7cab0e 100644 --- a/python/analytics/option.py +++ b/python/analytics/option.py @@ -7,7 +7,7 @@ import numpy as np import pandas as pd from db import dbengine -from .black import black +from .black import black, Nx from .utils import GHquad, build_table from .index import g, ForwardIndex, Index, engine from yieldcurve import roll_yc @@ -183,6 +183,19 @@ class BlackSwaption(ForwardIndex): self.sigma, self.option_type == "payer") * self.notional + @property + def tail_prob(self): + """compute exercise probability by pricing it as a binary option""" + strike_tilde = self.index.fixed_rate * 1e-4 + self._G / self.forward_annuity * self.df + if self.sigma == 0: + prob = 1 if strike_tilde > self.forward_spread * 1e-4 else 0 + return prob if self.option_type == 'receiver' else 1 - prob + else: + return Nx(self.forward_spread * 1e-4, + strike_tilde, + self.sigma, + self.T) + @pv.setter def pv(self, val): if np.isnan(val): @@ -415,7 +428,7 @@ def compute_vol(option, strike, mid): except ValueError as e: return None else: - return option.sigma + return np.array([option.sigma, option.tail_prob, option.vega, option.moneyness, option.strike]) class VolatilitySurface(ForwardIndex): def __init__(self, index_type, series, tenor='5yr', trade_date=datetime.date.today()): @@ -438,11 +451,13 @@ class VolatilitySurface(ForwardIndex): dt.tz_localize(None)) self._quotes = self._quotes.sort_values('quotedate') self._surfaces = {} + self._prob_surfaces = {} for k, g in self._quotes.groupby(['quotedate', 'quote_source']): quotedate, source = k for option_type in ["payer", "receiver"]: for model in ["black", "precise"]: self._surfaces[(quotedate, source, option_type, model)] = None + self._prob_surfaces[(quotedate, source, option_type, model)] = None def vol(self, T, moneyness, surface_id): """computes the vol for a given moneyness and term.""" @@ -472,6 +487,22 @@ class VolatilitySurface(ForwardIndex): ax.set_ylabel("Moneyness") ax.set_zlabel("Volatility") + def prob_surf(self, surface_id): + if self._prob_surfaces[surface_id] is None: + self[surface_id] + return self._prob_surfaces[surface_id] + + def prob_plot(self, surface_id): + fig = plt.figure() + ax = fig.gca(projection='3d') + xx, yy = np.meshgrid(np.arange(0.1, 0.5, 0.01), + np.arange(0.8, 1.7, 0.01)) + surf = ax.plot_surface(xx, yy, self.prob_surf(surface_id).ev(xx, yy), + cmap = cm.viridis, vmax = 1) + ax.set_xlabel("Year fraction") + ax.set_ylabel("Moneyness") + ax.set_zlabel("Tail Probability") + def __getitem__(self, surface_id): if self._surfaces[surface_id] is None: quotedate, source, option_type, model = surface_id @@ -490,15 +521,32 @@ class VolatilitySurface(ForwardIndex): quotes = quotes.dropna(subset=['mid']) with Pool(4) as p: for expiry, df in quotes.groupby(['expiry']): - atm_strike = ATMstrike(self._index, expiry.date()) option = swaption_class(self._index, expiry.date(), 100, option_type) - T.append(option.T * np.ones(df.shape[0])) - moneyness.append(df.strike.values / atm_strike) r.append(p.starmap(partial(compute_vol, option), df[['strike', 'mid']].values)) - r = np.fromiter(chain(*r), np.float, quotes.shape[0]) - f = SmoothBivariateSpline(np.hstack(T), np.hstack(moneyness), r) + r = np.concatenate(r) + time = np.hstack(T) + moneyness = np.hstack(r[:,3]) + strike = np.hstack(r[:,4]) + f = SmoothBivariateSpline(time, moneyness, r[:,0]) + skew = SmoothBivariateSpline(time, strike, r[:,0]).ev(time, strike, dy=1) + tail_prob = r[:,1] + (r[:,2] * 100) * (skew * 100) + time, moneyness, tail_prob = fill_the_box(time, moneyness, tail_prob) + g = SmoothBivariateSpline(time, moneyness, tail_prob) self._surfaces[surface_id] = f + self._prob_surfaces[surface_id] = g return f else: return self._surfaces[surface_id] + +def fill_the_box(time, moneyness, tail_prob): + box_ratio = 1.2 + uniques = np.unique(time, return_index = True) + ranges = np.append(uniques[1], time.size).astype(int) + for i, x in enumerate(uniques[0]): + time = np.append(time, [x, x]) + max_money = moneyness[ranges[i]:ranges[i+1]].max() + min_money = moneyness[ranges[i]:ranges[i+1]].min() + moneyness = np.append(moneyness, [max_money*box_ratio, min_money/box_ratio]) + tail_prob = np.append(tail_prob, [0,1]) + return time, moneyness, tail_prob
\ No newline at end of file |
