diff options
Diffstat (limited to 'python')
| -rw-r--r-- | python/analytics/option.py | 50 | ||||
| -rw-r--r-- | python/tests/test_index.py | 12 | ||||
| -rw-r--r-- | python/tests/test_scenarios.py | 4 | ||||
| -rw-r--r-- | python/tests/test_swaption.py | 2 |
4 files changed, 27 insertions, 41 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 diff --git a/python/tests/test_index.py b/python/tests/test_index.py index d9dbf868..3b15cce8 100644 --- a/python/tests/test_index.py +++ b/python/tests/test_index.py @@ -3,7 +3,6 @@ import datetime import numpy as np from pyisda.cdsone import upfront_charge -from pyisda.flat_hazard import pv_vec from analytics import CreditIndex, ForwardIndex from analytics.index import g @@ -40,17 +39,6 @@ class TestStrike(unittest.TestCase): self.index._update() self.assertAlmostEqual(self.index.clean_pv, strike) - def test_strike_vec(self): - self.index.value_date = datetime.date(2016, 8, 19) - r = pv_vec(np.array([70, 75])*1e-4, self.index._yc, self.index.value_date, - self.index._cash_settle_date, self.index.start_date, self.index.end_date, - self.index.recovery, self.index.fixed_rate * 1e-4) - self.index.notional = 1 - self.index.spread = 70 - self.assertAlmostEqual(self.index.clean_pv, r[0]) - self.index.spread = 75 - self.assertAlmostEqual(self.index.clean_pv, r[1]) - def test_price_setting(self): self.index.price = self.index.price self.assertAlmostEqual(self.index.spread, 75) diff --git a/python/tests/test_scenarios.py b/python/tests/test_scenarios.py index 6536c97e..1f1f6bf3 100644 --- a/python/tests/test_scenarios.py +++ b/python/tests/test_scenarios.py @@ -2,7 +2,8 @@ import unittest import numpy as np import pandas as pd -from analytics import CreditIndex, BlackSwaption, Portfolio, BlackSwaptionVolSurface +from analytics import (CreditIndex, BlackSwaption, Portfolio, + BlackSwaptionVolSurface) from pandas.tseries.offsets import BDay from analytics.scenarios import (run_portfolio_scenarios, run_swaption_scenarios, run_index_scenarios) @@ -37,5 +38,6 @@ class TestSenarios(unittest.TestCase): df_orig = df_orig.set_index('vol_shock', append=True) self.assertFalse(np.any((df.sum(axis=1).values-df_orig['pnl'].values))) + if __name__ == "__main__": unittest.main() diff --git a/python/tests/test_swaption.py b/python/tests/test_swaption.py index 211d0e49..40237b79 100644 --- a/python/tests/test_swaption.py +++ b/python/tests/test_swaption.py @@ -89,7 +89,7 @@ class TestBreakeven(unittest.TestCase): self.igstrike, "receiver") receiver.sigma = .4 receiver.notional = 1e7 - self.assertAlmostEqual(receiver.breakeven, 73.715942707132982) + self.assertAlmostEqual(receiver.breakeven, 73.71594264020226) if __name__ == "__main__": unittest.main() |
