diff options
Diffstat (limited to 'python/analytics')
| -rw-r--r-- | python/analytics/option.py | 50 |
1 files changed, 23 insertions, 27 deletions
diff --git a/python/analytics/option.py b/python/analytics/option.py index bd1803b0..065de803 100644 --- a/python/analytics/option.py +++ b/python/analytics/option.py @@ -11,10 +11,10 @@ from .utils import GHquad, build_table from .index import g, ForwardIndex, CreditIndex from .db import _engine from .utils import memoize -from yieldcurve import roll_yc from pandas.tseries.offsets import BDay -from pyisda.flat_hazard import pv_vec +from pyisda.optim import init_context, update_context, expected_pv +import pyisda.optim from scipy.optimize import brentq from scipy.integrate import simps from scipy.interpolate import SmoothBivariateSpline, interp1d, CubicSpline @@ -25,16 +25,13 @@ from multiprocessing import Pool from functools import partial, lru_cache from itertools import chain from scipy.optimize import least_squares +from scipy import LowLevelCallable +from scipy.integrate import quad from scipy.special import logit, expit -def calib(S0, fp, exercise_date, exercise_date_settle, - index, tilt, w): - S = S0 * tilt * 1e-4 - pv = pv_vec(S, index._yc, exercise_date, exercise_date_settle, - index.start_date, index.end_date, index.recovery, - index.fixed_rate * 1e-4) - return np.inner(pv, w) - fp +def calib(S0, fp, tilt, w, ctx): + return expected_pv(tilt, w, S0, ctx) - fp def ATMstrike(index, exercise_date): """computes the at-the-money strike. @@ -390,8 +387,8 @@ class Swaption(BlackSwaption): def __init__(self, index, exercise_date, strike, option_type="payer", direction="Long"): super().__init__(index, exercise_date, strike, option_type, direction) - self._Z, self._w = GHquad(50) self._cache = {} + self._Z, self._w = GHquad(30) def __hash__(self): return super().__hash__() @@ -400,9 +397,14 @@ class Swaption(BlackSwaption): @memoize def pv(self): T = self.T - tilt = np.exp(-self.sigma**2/2 * T + self.sigma * self._Z * math.sqrt(T)) - args = (self.forward_pv, self.exercise_date, self.exercise_date_settle, - self.index, tilt, self._w) + if T == 0.: + return self.notional * self.intrinsic_value + sigmaT = self.sigma * math.sqrt(T) + tilt = np.exp(-0.5 * sigmaT**2 + sigmaT * self._Z) + ctx = init_context(self.index._yc, self.exercise_date, self.exercise_date_settle, + self.index.start_date, self.index.end_date, self.index.recovery, + self.index.fixed_rate * 1e-4, self._G, sigmaT, 0.01) + args = (self.forward_pv, tilt, self._w, ctx) eta = 1.05 a = self.index.spread * 0.99 b = a * eta @@ -412,29 +414,23 @@ class Swaption(BlackSwaption): b *= eta S0 = brentq(calib, a, b, args) - if T == 0: - return self.notional * self.intrinsic_value + update_context(ctx, S0) + my_pv = LowLevelCallable.from_cython(pyisda.optim, "pv", ctx) ## Zstar solves S_0 exp(-\sigma^2/2 * T + sigma * Z^\star\sqrt{T}) = strike - Zstar = (math.log(self._strike/S0) + self.sigma**2/2 * T) / \ - (self.sigma * math.sqrt(T)) + Zstar = (math.log(self._strike / S0) + 0.5 * sigmaT**2) / sigmaT if self.option_type == "payer": - Z = Zstar + np.logspace(0, math.log(4 / (self.sigma * math.sqrt(T)), 10), 300) - 1 + val, err = quad(my_pv, Zstar, 12) elif self.option_type == "receiver": - Z = Zstar - np.logspace(0, math.log(4 / (self.sigma * math.sqrt(T)), 10), 300) + 1 - else: - raise ValueError("option_type needs to be either 'payer' or 'receiver'") - S = S0 * np.exp(-self.sigma**2/2 * T + self.sigma * Z * math.sqrt(T)) - r = pv_vec(S * 1e-4, self.index._yc, self.exercise_date, - self.exercise_date_settle, self.index.start_date, - self.index.end_date, self.index.recovery, self.index.fixed_rate * 1e-4) - val = (r - self._G) * 1/math.sqrt(2*math.pi) * np.exp(-Z**2/2) - return self._direction * self.notional * simps(val, Z) * self.df + val, err = quad(my_pv, Zstar, -12) + return self._direction * self.notional * val * self.df @pv.setter def pv(self, val): # use sigma_black as a starting point self.pv_black = val + if self.sigma == 0.: + self.sigma = 1e-6 def handle(x): self.sigma = x |
