aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--python/analytics/__init__.py4
-rw-r--r--python/analytics/index.py9
-rw-r--r--python/analytics/option.py11
-rw-r--r--python/analytics/portfolio.py12
-rw-r--r--python/analytics/tranche_basket.py24
-rw-r--r--python/notebooks/VaR.ipynb40
6 files changed, 75 insertions, 25 deletions
diff --git a/python/analytics/__init__.py b/python/analytics/__init__.py
index 39c0557b..e499288a 100644
--- a/python/analytics/__init__.py
+++ b/python/analytics/__init__.py
@@ -5,3 +5,7 @@ from .portfolio import Portfolio
from .basket_index import MarkitBasketIndex
from .tranche_basket import DualCorrTranche, TrancheBasket
from .ir_swaption import IRSwaption
+
+_ontr = CreditIndex('HY', 31, '5yr')
+_ontr.mark()
+_beta = {'HY': 1, 'IG': .3}
diff --git a/python/analytics/index.py b/python/analytics/index.py
index 3130398a..4c841a74 100644
--- a/python/analytics/index.py
+++ b/python/analytics/index.py
@@ -1,6 +1,7 @@
import array
import datetime
import pandas as pd
+import analytics
from .credit_default_swap import CreditDefaultSwap
from .db import _engine, dbengine, DataError
@@ -8,7 +9,6 @@ from bbg_helpers import BBG_IP, retrieve_data, init_bbg_session
from pandas.tseries.offsets import BDay
from pyisda.curve import SpreadCurve
-
def g(index, spread, exercise_date, pv=None):
"""computes the strike clean price using the expected forward yield curve. """
step_in_date = exercise_date + datetime.timedelta(days=1)
@@ -113,6 +113,13 @@ class CreditIndex(CreditDefaultSwap):
return instance
@property
+ def hy_equiv(self):
+ risk = self.notional * self.risky_annuity / analytics._ontr.risky_annuity
+ if self.index_type != 'HY':
+ risk *= analytics._beta[self.index_type]
+ return -risk if self.direction == 'Buyer' else risk
+
+ @property
def ref(self):
if self._quote_is_price:
return self.price
diff --git a/python/analytics/option.py b/python/analytics/option.py
index eecb9997..2930796e 100644
--- a/python/analytics/option.py
+++ b/python/analytics/option.py
@@ -3,6 +3,8 @@ import datetime
import math
import numpy as np
import pandas as pd
+import analytics
+
from db import dbengine
from .black import black, Nx
@@ -262,13 +264,20 @@ class BlackSwaption(ForwardIndex):
self._update()
notional_ratio = self.index.notional / self.notional
dv01 = self.pv - old_pv
- delta = -self.index._direction * dv01 * notional_ratio / \
+ delta = self.index._direction * dv01 * notional_ratio / \
(self.index.pv - old_index_pv)
self.index.spread = old_spread
self._update()
return delta
@property
+ def hy_equiv(self):
+ risk = self.delta * abs(self.index.hy_equiv/ \
+ self.index.notional) * self.notional
+ risk *= -1 if self.option_type == 'payer' else 1
+ return -risk if self.direction == 'Short' else risk
+
+ @property
def T(self):
if self._T:
return self._T
diff --git a/python/analytics/portfolio.py b/python/analytics/portfolio.py
index a4ed393d..ef1d5578 100644
--- a/python/analytics/portfolio.py
+++ b/python/analytics/portfolio.py
@@ -166,10 +166,14 @@ class Portfolio:
def theta(self):
return sum(t.theta for t in self.trades)
+ @property
+ def hy_equiv(self):
+ return sum(t.hy_equiv for t in self.trades)
+
def _todf(self):
headers = ["Product", "Index", "Notional", "Ref", "Strike", "Direction",
"Type", "Expiry", "Vol", "PV", "Delta", "Gamma", "Theta",
- "Vega", "attach", "detach", "Attach Rho", "Detach Rho"]
+ "Vega", "attach", "detach", "Attach Rho", "Detach Rho", "HY Equiv"]
rec = []
for t in self.trades:
if isinstance(t, CreditIndex):
@@ -177,19 +181,19 @@ class Portfolio:
r = ("Index", name, t.notional, t.ref, "N/A",
t.direction, "N/A", "N/A", None, t.pv,
1., 0., t.theta, 0.,
- None, None, None, None)
+ None, None, None, None, t.hy_equiv)
elif isinstance(t, BlackSwaption):
name = f"{t.index.index_type}{t.index.series} {t.index.tenor}"
r = ("Swaption", name, t.notional, t.ref, t.strike,
t.direction, t.option_type, t.forward_date, t.sigma, t.pv,
t.delta, t.gamma, t.theta, t.vega,
- None, None, None, None)
+ None, None, None, None, t.hy_equiv)
elif isinstance(t, DualCorrTranche):
name = f"{t.index_type}{t.series} {t.tenor}"
r = ("Tranche", name, t.notional, None, None,
t.direction, None, None, None, t.upfront,
t.delta, t.gamma, None, None,
- t.attach, t.detach, t.rho[0], t.rho[1])
+ t.attach, t.detach, t.rho[0], t.rho[1], t.hy_equiv)
else:
raise TypeError
rec.append(r)
diff --git a/python/analytics/tranche_basket.py b/python/analytics/tranche_basket.py
index dab528c6..1af4d4eb 100644
--- a/python/analytics/tranche_basket.py
+++ b/python/analytics/tranche_basket.py
@@ -13,9 +13,11 @@ from pyisda.date import cds_accrued
from scipy.optimize import brentq
from scipy.interpolate import CubicSpline, PchipInterpolator
from scipy.special import logit, expit
+
import datetime
import pandas as pd
import numpy as np
+import analytics
class DualCorrTranche():
@@ -175,7 +177,8 @@ class DualCorrTranche():
@property
def pv(self):
- return self._pv()
+ pl, cl = self._pv()
+ return float(-pl - cl + self._accrued)
def _pv(self, epsilon=0.):
""" computes coupon leg, protection leg and bond price.
@@ -192,7 +195,7 @@ class DualCorrTranche():
dK = np.diff(self.K)
pl = np.diff(pl) / dK
cl = np.diff(cl) / dK * self.tranche_running * 1e-4
- return float(-pl - cl + self._accrued)
+ return pl, cl
@property
def upfront(self):
@@ -246,7 +249,7 @@ class DualCorrTranche():
self._index.tweak_portfolio(ss, self.maturity, False)
for corrs in corr_shock:
#also need to map skew
- self.rho = np.fmin(1, orig_rho * (1 + corrs))
+ self.rho = orig_rho * (1 + corrs)
r.append([getattr(self, p) for p in actual_params])
self._index.curves = orig_curves
self.rho = orig_rho
@@ -283,6 +286,18 @@ class DualCorrTranche():
self._index.factor
@property
+ def duration(self):
+ return self._pv()[1] - self._accrued
+
+ @property
+ def hy_equiv(self):
+ risk = self.notional * self.delta * float(self._index.duration()) / \
+ analytics._ontr.risky_annuity
+ if self.index_type != 'HY':
+ risk *= analytics._beta[self.index_type]
+ return risk
+
+ @property
def delta(self):
calc = self._greek_calc()
factor = self.tranche_factor / self._index.factor
@@ -306,7 +321,8 @@ class DualCorrTranche():
bp = [self.pv]
for tweak in [eps, -eps, 2*eps]:
indexbp.append(self.tranche_legs(1., None, tweak).bond_price)
- bp.append(self._pv(tweak))
+ pl, cl = self._pv(tweak)
+ bp.append(float(-pl - cl + self._accrued))
return {'indexbp': indexbp, 'bp': bp}
class TrancheBasket(BasketIndex):
diff --git a/python/notebooks/VaR.ipynb b/python/notebooks/VaR.ipynb
index 2fa3ee1d..de2c26a1 100644
--- a/python/notebooks/VaR.ipynb
+++ b/python/notebooks/VaR.ipynb
@@ -28,7 +28,7 @@
"metadata": {},
"outputs": [],
"source": [
- "date = (datetime.date.today() - pd.tseries.offsets.BDay(1)).date()\n",
+ "date = (datetime.date.today() - pd.tseries.offsets.BDay(3)).date()\n",
"report_date = (date + pd.tseries.offsets.BMonthEnd(-1)).date()\n",
"index_type = \"IG\"\n",
"quantile = .025"
@@ -127,6 +127,17 @@
"metadata": {},
"outputs": [],
"source": [
+ "position_date = (datetime.date.today() - pd.tseries.offsets.BDay(1)).date()\n",
+ "shock_date = (datetime.date.today() - pd.tseries.offsets.BDay(1)).date()\n",
+ "(position_date, shock_date)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
"#Current tranche and swaptions positions\n",
"t_sql_string = (\"SELECT id, sum(notional * case when protection='Buyer' then -1 else 1 end) \"\n",
" \"OVER (partition by security_id, attach) AS ntl_agg \"\n",
@@ -142,19 +153,19 @@
" \"AND trade_date <= %s\")\n",
"with conn.cursor() as c:\n",
" #Get Tranche Trade Ids\n",
- " c.execute(t_sql_string, (date,))\n",
+ " c.execute(t_sql_string, (position_date,))\n",
" t_trade_ids = [dealid for dealid, ntl in c if ntl != 0]\n",
" #Get Swaption Trade Ids\n",
- " c.execute(swaption_sql_string, (date, date))\n",
+ " c.execute(swaption_sql_string, (position_date, position_date))\n",
" swaption_trades = c.fetchall()\n",
" #Get Index/deltas Trade Ids\n",
- " c.execute(index_sql_string, (date,))\n",
+ " c.execute(index_sql_string, (position_date,))\n",
" index_trade_ids = [dealid for dealid, ntl in c if ntl != 0]\n",
" \n",
"portf = Portfolio([DualCorrTranche.from_tradeid(dealid) for dealid in t_trade_ids],\n",
" t_trade_ids)\n",
"for row in swaption_trades:\n",
- " option_delta = CreditIndex(row[1].split()[1], row[1].split()[3][1:], '5yr', date)\n",
+ " option_delta = CreditIndex(row[1].split()[1], row[1].split()[3][1:], '5yr', position_date)\n",
" option_delta.mark()\n",
" portf.add_trade(BlackSwaption.from_tradeid(row[0], option_delta), 'opt_' + str(row[0]))\n",
"for index_id in index_trade_ids:\n",
@@ -162,30 +173,29 @@
" \n",
"#Update manually - positive notional = long risk\n",
"non_trancheSwap_risk_notional = 49119912 \n",
- "\n",
- "portf.add_trade(CreditIndex('HY', on_the_run('HY'), '5yr', value_date = date, notional = -non_trancheSwap_risk_notional), 'bond')\n",
+ "portf.add_trade(CreditIndex('HY', on_the_run('HY'), '5yr', value_date = shock_date, notional = -non_trancheSwap_risk_notional), 'bond')\n",
" \n",
- "portf.value_date = date\n",
+ "portf.value_date = shock_date\n",
"portf.mark(interp_method=\"bivariate_linear\")\n",
"portf.reset_pv()\n",
"\n",
"vol_surface = {}\n",
"for trade in portf.swaptions:\n",
" vs = BlackSwaptionVolSurface(trade.index.index_type, trade.index.series, \n",
- " value_date=date, interp_method = \"bivariate_linear\")\n",
+ " value_date=shock_date, interp_method = \"bivariate_linear\")\n",
" vol_surface[trade.index.index_type + trade.index.series] = vs[vs.list(option_type='payer')[-1]]\n",
"vol_shock = [0]\n",
"corr_shock = [0]\n",
- "spread_shock = widen + tighten\n",
- "date_range = [pd.Timestamp(date)]\n",
+ "spread_shock = tighten + [0] + widen\n",
+ "date_range = [pd.Timestamp(shock_date)]\n",
"\n",
- "scens = run_portfolio_scenarios(portf, date_range, params=[\"pnl\"],\n",
+ "scens = run_portfolio_scenarios(portf, date_range, params=[\"pnl\", \"hy_equiv\"],\n",
" spread_shock=spread_shock,\n",
" vol_shock=vol_shock,\n",
" corr_shock=corr_shock,\n",
" vol_surface=vol_surface)\n",
"\n",
- "scens.sum(axis=1)"
+ "scens.xs('pnl', level=1).sum(axis=1)"
]
},
{
@@ -202,8 +212,8 @@
" vol_surface=vol_surface)\n",
"scens.sum(axis=1)\n",
"\n",
- "risk_notional = [t.notional * t._index.duration for t in portf.indices]\n",
- "portf.trades[0]._index.duration()"
+ "#risk_notional = [t.notional * t._index.duration for t in portf.indices]\n",
+ "#portf.trades[0]._index.duration()"
]
},
{