aboutsummaryrefslogtreecommitdiffstats
path: root/python/notebooks/tranche and swaption portfolio strategy.ipynb
diff options
context:
space:
mode:
Diffstat (limited to 'python/notebooks/tranche and swaption portfolio strategy.ipynb')
-rw-r--r--python/notebooks/tranche and swaption portfolio strategy.ipynb287
1 files changed, 201 insertions, 86 deletions
diff --git a/python/notebooks/tranche and swaption portfolio strategy.ipynb b/python/notebooks/tranche and swaption portfolio strategy.ipynb
index 1b0e0a08..c0355fb0 100644
--- a/python/notebooks/tranche and swaption portfolio strategy.ipynb
+++ b/python/notebooks/tranche and swaption portfolio strategy.ipynb
@@ -18,7 +18,7 @@
"from datetime import date\n",
"from graphics import plot_color_map\n",
"\n",
- "value_date = (pd.datetime.today() - pd.offsets.BDay(2)).date()"
+ "value_date = (pd.datetime.today() - pd.offsets.BDay(1)).date()"
]
},
{
@@ -27,7 +27,50 @@
"metadata": {},
"outputs": [],
"source": [
- "#Construct IG Swaption Portfolio\n",
+ "def color_plots(portf, scens, options_names):\n",
+ " sort_order = [True, False]\n",
+ " scens_pnl = scens.xs('pnl', axis=1, level=1)\n",
+ " just_spread = scens_pnl.xs((0,0), level=['corr_shock', 'vol_shock'])\n",
+ " combined = just_spread.sum(axis=1)\n",
+ " combined.name = 'combined_pnl'\n",
+ " plot_color_map(combined, sort_order)\n",
+ "\n",
+ " swaptions_only = just_spread[options_names].sum(axis=1)\n",
+ " swaptions_only.name = 'swaptions_pnl'\n",
+ " plot_color_map(swaptions_only, sort_order)\n",
+ "\n",
+ " tranches_only = just_spread[[x for x in portf.trade_ids if x not in options_names]].sum(axis=1)\n",
+ " tranches_only.name = 'tranches_pnl'\n",
+ " plot_color_map(tranches_only, sort_order)\n",
+ "\n",
+ " #Plot delta, swaption delta is in protection terms: switch to risk terms\n",
+ " sort_order = [True, False]\n",
+ " scens_delta = scens.xs('delta', axis=1, level=1)\n",
+ " scens_delta = scens_delta.mul(pd.Series(portf.notionals))\n",
+ " if 'delta' in portf.trade_ids:\n",
+ " scens_delta['delta'] = portf.notionals['delta']\n",
+ " scens_delta = scens_delta.xs((0,0), level=['corr_shock', 'vol_shock'])\n",
+ "\n",
+ " combined = scens_delta.sum(axis=1)\n",
+ " combined.name = 'Combined Delta'\n",
+ " plot_color_map(combined, sort_order)\n",
+ "\n",
+ " swaptions_only = scens_delta[options_names].sum(axis=1)\n",
+ " swaptions_only.name = 'Swaptions Only Delta'\n",
+ " plot_color_map(swaptions_only, sort_order)\n",
+ "\n",
+ " tranches_only = scens_delta[[x for x in portf.trade_ids if x not in options_names]].sum(axis=1)\n",
+ " tranches_only.name = 'Tranches Only Delta'\n",
+ " plot_color_map(tranches_only, sort_order)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#Package 1\n",
"index = 'IG'\n",
"series = 30\n",
"option_delta = CreditIndex(index, series, '5yr', value_date=value_date)\n",
@@ -42,22 +85,17 @@
"option2.notional = 300_000_000\n",
"option_delta.notional = option1.notional * option1.delta + option2.notional * option2.delta\n",
"\n",
- "#Get current Tranche positions\n",
- "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",
- " \"FROM cds WHERE swap_type='CD_INDEX_TRANCHE' AND termination_cp IS NULL\")\n",
- "conn = dbconn('dawndb')\n",
- "with conn.cursor() as c:\n",
- " c.execute(sql_string)\n",
- " trade_ids = [dealid for dealid, ntl in c if ntl != 0]\n",
- "portf = Portfolio([DualCorrTranche.from_tradeid(dealid) for dealid in trade_ids],\n",
- " trade_ids)\n",
+ "equity = DualCorrTranche('IG', 29, '5yr', attach=0, detach=3, corr_attach=np.nan, \n",
+ " corr_detach=.35, tranche_running=100, notional=-40000000, use_trunc=True)\n",
+ "mezz = DualCorrTranche('IG', 29, '5yr', attach=7, detach=15, corr_attach=.45, \n",
+ " corr_detach=.55, tranche_running=100, notional=240000000, use_trunc=True)\n",
+ "portf = Portfolio([equity, mezz], ['equity', 'mezz'])\n",
"portf.trades.extend([option1, option2, option_delta])\n",
"portf.trade_ids.extend(['opt1', 'opt2', 'delta'])\n",
"\n",
- "spread_shock = np.arange(-.3, 1.1, .1)\n",
+ "spread_shock = np.round(np.arange(-.3, 1, .1),2)\n",
"corr_shock = np.arange(0, .1, 0.1)\n",
- "vol_shock = np.arange(-.1, .3, 0.1)\n",
+ "vol_shock = np.arange(0, 0.1, 0.1)\n",
"earliest_expiry = min(portf.swaptions, key=lambda x: x.exercise_date).exercise_date\n",
"date_range = pd.date_range(value_date, earliest_expiry, periods=5)\n",
"vs = BlackSwaptionVolSurface(index, series, value_date=value_date)\n",
@@ -67,12 +105,14 @@
"portf.mark()\n",
"portf.reset_pv()\n",
"\n",
- "scens = run_portfolio_scenarios(portf, date_range, params=[\"pnl\"],\n",
+ "scens = run_portfolio_scenarios(portf, date_range, params=[\"pnl\", 'delta'],\n",
" spread_shock=spread_shock,\n",
" corr_shock=corr_shock,\n",
" vol_shock=vol_shock,\n",
" vol_surface=vol_surface)\n",
- "scens = round(scens,2)"
+ "scens = round(scens,2)\n",
+ "\n",
+ "color_plots(portf, scens, ['opt1', 'opt2', 'delta'])"
]
},
{
@@ -81,11 +121,41 @@
"metadata": {},
"outputs": [],
"source": [
- "sort_order = [True, False]\n",
- "output = scens.xs((0,0), level=['corr_shock', 'vol_shock']).sum(axis=1)\n",
- "(1+output.index.get_level_values(1)) * portf.swaptions[0].ref\n",
- "output.name = 'pnl'\n",
- "plot_color_map(output, sort_order)"
+ "#simple IG package: sell OTM swaption vs. short 3-7 delta neutral at start\n",
+ "index = 'IG'\n",
+ "series = 30\n",
+ "option_delta = CreditIndex(index, series, '5yr', value_date=value_date)\n",
+ "option_delta.spread = 60\n",
+ "option2 = BlackSwaption(option_delta, date(2018, 11, 21), 85, option_type=\"payer\")\n",
+ "option2.sigma = .588\n",
+ "option2.direction = 'Short'\n",
+ "option2.notional = 500_000_000\n",
+ "option_delta.notional = 1\n",
+ "\n",
+ "mezz = DualCorrTranche('IG', 29, '5yr', attach=7, detach=15, corr_attach=.45, \n",
+ " corr_detach=.55, tranche_running=100, notional=50000000, use_trunc=True)\n",
+ "portf = Portfolio([option2, mezz], ['opt2', 'mezz'])\n",
+ "\n",
+ "spread_shock = np.round(np.arange(-.3, 1, .1),2)\n",
+ "corr_shock = np.arange(0, .1, 0.1)\n",
+ "vol_shock = np.arange(0, 0.1, 0.1)\n",
+ "earliest_expiry = min(portf.swaptions, key=lambda x: x.exercise_date).exercise_date\n",
+ "date_range = pd.date_range(value_date, earliest_expiry, periods=5)\n",
+ "vs = BlackSwaptionVolSurface(index, series, value_date=value_date)\n",
+ "ps = ProbSurface(index, series, value_date=value_date)\n",
+ "vol_surface = vs[vs.list(option_type='payer')[-1]]\n",
+ "portf.value_date = value_date\n",
+ "portf.mark()\n",
+ "portf.reset_pv()\n",
+ "\n",
+ "scens = run_portfolio_scenarios(portf, date_range, params=[\"pnl\", 'delta'],\n",
+ " spread_shock=spread_shock,\n",
+ " corr_shock=corr_shock,\n",
+ " vol_shock=vol_shock,\n",
+ " vol_surface=vol_surface)\n",
+ "scens = round(scens,2)\n",
+ "\n",
+ "color_plots(portf, scens, ['opt2'])"
]
},
{
@@ -94,6 +164,48 @@
"metadata": {},
"outputs": [],
"source": [
+ "#simple HY package: sell OTM swaption vs. short 3-7 delta neutral at start\n",
+ "index = 'HY'\n",
+ "series = 30\n",
+ "option_delta = CreditIndex(index, series, '5yr', value_date=value_date)\n",
+ "option_delta.price = 106.75\n",
+ "option2 = BlackSwaption(option_delta, date(2018, 11, 21), 102, option_type=\"payer\")\n",
+ "option2.sigma = .469\n",
+ "option2.direction = 'Short'\n",
+ "option2.notional = 200_000_000\n",
+ "\n",
+ "mezz = DualCorrTranche('HY', 29, '5yr', attach=15, detach=25, corr_attach=.35, \n",
+ " corr_detach=.45, tranche_running=100, notional=16000000, use_trunc=True)\n",
+ "portf = Portfolio([option2, mezz], ['opt2', 'mezz'])\n",
+ "\n",
+ "spread_shock = np.round(np.arange(-.3, 1, .1),2)\n",
+ "corr_shock = np.arange(0, .1, 0.1)\n",
+ "vol_shock = np.arange(0, 0.1, 0.1)\n",
+ "earliest_expiry = min(portf.swaptions, key=lambda x: x.exercise_date).exercise_date\n",
+ "date_range = pd.date_range(value_date, earliest_expiry, periods=5)\n",
+ "vs = BlackSwaptionVolSurface(index, series, value_date=value_date)\n",
+ "ps = ProbSurface(index, series, value_date=value_date)\n",
+ "vol_surface = vs[vs.list(option_type='payer')[-1]]\n",
+ "portf.value_date = value_date\n",
+ "portf.mark()\n",
+ "portf.reset_pv()\n",
+ "\n",
+ "scens = run_portfolio_scenarios(portf, date_range, params=[\"pnl\", 'delta'],\n",
+ " spread_shock=spread_shock,\n",
+ " corr_shock=corr_shock,\n",
+ " vol_shock=vol_shock,\n",
+ " vol_surface=vol_surface)\n",
+ "scens = round(scens,2)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "(1+output.index.get_level_values(1)) * portf.swaptions[0].ref\n",
+ "\n",
"#negative notional == sell protection\n",
"hy_tranche = DualCorrTranche('HY', 29, '5yr', attach=0, detach=15, corr_attach=np.nan, \n",
" corr_detach=.35, tranche_running=500, notional=-10000000)\n",
@@ -112,15 +224,60 @@
"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",
+ " \"FROM cds WHERE swap_type='CD_INDEX_TRANCHE' AND termination_cp IS NULL\")\n",
+ "swaption_sql_string = (\"select id, security_desc from swaptions where date(expiration_date) \"\n",
+ " \"> %s and swap_type = 'CD_INDEX_OPTION'\")\n",
+ "index_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",
+ " \"FROM cds WHERE swap_type='CD_INDEX' AND termination_cp IS null and folder = 'IGOPTDEL'\")\n",
+ "conn = dbconn('dawndb')\n",
+ "with conn.cursor() as c:\n",
+ " c.execute(t_sql_string)\n",
+ " t_trade_ids = [dealid for dealid, ntl in c if ntl != 0]\n",
+ " c.execute(swaption_sql_string, (value_date,))\n",
+ " swaption_trades = c.fetchall()\n",
+ " c.execute(index_sql_string)\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', value_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",
+ " portf.add_trade(CreditIndex.from_tradeid(index_id), 'index_' + str(index_id))\n",
+ " \n",
+ "spread_shock = np.round(np.arange(-.3, 1, .1),2)\n",
+ "corr_shock = np.arange(0, .1, 0.1)\n",
+ "vol_shock = np.arange(0, 0.1, 0.1)\n",
+ "#date_range = pd.date_range(value_date, date(2018,12,31), periods=5)\n",
+ "earliest_expiry = min(portf.swaptions, key=lambda x: x.exercise_date).exercise_date\n",
+ "date_range = pd.date_range(value_date, earliest_expiry, periods=5)\n",
+ "portf.swaptions[0]\n",
+ "vs = BlackSwaptionVolSurface(portf.swaptions[0].index.index_type, portf.swaptions[0].index.series, value_date=value_date)\n",
+ "vol_surface = vs[vs.list(option_type='payer')[-1]]\n",
+ "portf.value_date = value_date\n",
+ "portf.mark()\n",
+ "portf.reset_pv()\n",
"\n",
- "##\n",
- "scens_more = run_portfolio_scenarios(portf, date_range, params=['pnl', 'delta'],\n",
+ "scens = run_portfolio_scenarios(portf, date_range, params=[\"pnl\", 'delta'],\n",
" spread_shock=spread_shock,\n",
" corr_shock=corr_shock,\n",
" vol_shock=vol_shock,\n",
" vol_surface=vol_surface)\n",
- "#swaption delta is in protection terms: switch to risk terms\n",
- "swaption_scens.delta = -swaption_scens.delta"
+ "\n",
+ "color_plots(portf, scens, ['opt_20', 'opt_21', 'index_954'])\n",
+ "\n",
+ "#sort_order = [True, False]\n",
+ "#scens_pnl = scens.xs('pnl', axis=1, level=1)\n",
+ "#just_spread = scens_pnl.xs(0, level=1)\n",
+ "#combined = just_spread.sum(axis=1)\n",
+ "#combined.name = 'tranche_pnl'\n",
+ "#plot_color_map(combined, sort_order)"
]
},
{
@@ -263,7 +420,7 @@
"swaption_scens.delta = -swaption_scens.delta\n",
"\n",
"notional = 30_000_000_000\n",
- "t = bkt.TrancheBasket('IG', '29', '3yr')\n",
+ "t = bkt.TrancheBasket('IG', '29', '5yr')\n",
"t.build_skew()\n",
"#get back to 17bps, .36 delta\n",
"port_spread = 67\n",
@@ -280,70 +437,28 @@
"metadata": {},
"outputs": [],
"source": [
- "#IG Bullish Risk Reversal vs. shorting IG 7-15 risk\n",
"index = 'IG'\n",
- "series = 30\n",
- "option_delta = Index.from_name(index, series, '5yr', value_date)\n",
- "option_delta.spread = 62\n",
- "option1 = BlackSwaption(option_delta, date(2018, 9, 19), 60, option_type=\"receiver\")\n",
- "option2 = BlackSwaption(option_delta, date(2018, 9, 19), 90, option_type=\"payer\")\n",
- "option1.sigma = .344\n",
- "option2.sigma = .585\n",
- "option1.notional = 200_000_000\n",
- "option2.notional = 400_000_000\n",
- "option1.direction = 'Long'\n",
- "option2.direction = 'Short'\n",
- "option_delta.notional = 1\n",
- "option_delta.direction = 'Seller' if option_delta.notional > 0 else 'Buyer'\n",
- "option_delta.notional = abs(option_delta.notional)\n",
- "portf = Portfolio([option1, option2, option_delta])\n",
- "#Plot Scenarios Inputs: Portfolio, spread shock tightening%, spread shock widening%, snapshot period)\n",
- "portf\n",
+ "series = 29\n",
+ "ss = DualCorrTranche('IG', 29, '5yr', attach=15, detach=100, corr_attach=.59685, \n",
+ " corr_detach=.7, tranche_running=100, notional=-230000000)\n",
+ "mezz = DualCorrTranche('IG', 29, '5yr', attach=7, detach=15, corr_attach=.46984, \n",
+ " corr_detach=.59685, tranche_running=100, notional=50000000)\n",
+ "portf = Portfolio([ss, mezz], ['ss', 'mezz'])\n",
"\n",
+ "spread_shock = np.round(np.arange(-.3, 1, .1), 2)\n",
+ "corr_shock = np.arange(0, .1, 0.1)\n",
+ "vol_shock = np.arange(0, 0.1, 0.1)\n",
+ "date_range = pd.date_range(value_date, date(2018,12,31), periods=5)\n",
+ "#portf.value_date = value_date\n",
+ "#portf.mark()\n",
"portf.reset_pv()\n",
- "#Run Swaption sensitivities\n",
- "#Set Shock range\n",
- "shock_min = -.5\n",
- "shock_max = 1\n",
- "spread_shock = np.arange(shock_min, shock_max, 0.1)\n",
- "#Set Date range\n",
- "earliest_expiry = min(portf.swaptions, key=lambda x: x.exercise_date).exercise_date\n",
- "date_range = pd.bdate_range(value_date, earliest_expiry - pd.offsets.BDay(), freq='10B')\n",
- "#Setup Vol Surface\n",
- "vs = BlackSwaptionVolSurface(index,series, value_date=value_date)\n",
- "ps = ProbSurface(index,series, value_date=value_date)\n",
- "vol_surface = vs[vs.list(option_type='payer')[-1]]\n",
- "swaption_scens = run_portfolio_scenarios(portf, date_range, spread_shock, np.array([0]),\n",
- " vol_surface, params=[\"pnl\", \"delta\"])\n",
- "#swaption delta is in protection terms: switch to risk terms\n",
- "swaption_scens.delta = -swaption_scens.delta\n",
- "\n",
- "notional = -100_000_000\n",
- "t = bkt.TrancheBasket('IG', '29', '5yr')\n",
- "t.build_skew()\n",
- "spread_range = (1+ spread_shock) * option_delta.spread\n",
- "tranches_scens = run_tranche_scenarios_rolldown(t, spread_range, date_range, corr_map=False)\n",
- "tranches_scens = notional*tranches_scens.xs('7-15', axis=1, level=1)\n",
"\n",
- "#Create snapshot of the the first scenario date\n",
- "total_scens = swaption_scens.reset_index().merge(tranches_scens.reset_index(), \n",
- " left_on=['date', 'spread'], \n",
- " right_on=['date', 'spread_range'], \n",
- " suffixes=['_s', '_t'])\n",
- "total_scens['pnl'] = total_scens['pnl_s'] + total_scens['pnl_t']\n",
- "total_scens['delta'] = total_scens['delta_s'] + total_scens['delta_t']\n",
- "total_scens_single_date = total_scens.set_index('date').xs(date_range[0])\n",
- "total_scens_single_date = total_scens_single_date.set_index('spread', drop=True)\n",
- "\n",
- "#tranche positions delta at different spreads\n",
- "ax = total_scens_single_date.delta_t.plot(title = 'delta vs. spread levels')\n",
- "ax.ticklabel_format(style='plain')\n",
- "plt.tight_layout()\n",
+ "scens = run_portfolio_scenarios(portf, date_range, params=[\"pnl\", 'delta'],\n",
+ " spread_shock=spread_shock,\n",
+ " corr_shock=corr_shock)\n",
+ "scens = round(scens,2)\n",
"\n",
- "#Tranche + Swaptions positions delta at different spreads\n",
- "ax1 = total_scens_single_date.delta.plot()\n",
- "ax1.ticklabel_format(style='plain')\n",
- "plt.tight_layout()"
+ "color_plots(portf, scens, ['opt1', 'opt2', 'delta'])"
]
},
{
@@ -379,7 +494,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.0"
+ "version": "3.7.1"
}
},
"nbformat": 4,