aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--python/analytics/option.py44
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))