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.py62
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