{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import analytics.tranche_basket as bkt\n", "import pandas as pd\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "\n", "from analytics.scenarios import run_tranche_scenarios, run_portfolio_scenarios, run_tranche_scenarios_rolldown\n", "from analytics import Swaption, BlackSwaption, CreditIndex, BlackSwaptionVolSurface, Portfolio, ProbSurface\n", "from analytics import DualCorrTranche\n", "from db import dbconn\n", "from datetime import date\n", "from graphics import plot_color_map\n", "\n", "value_date = (pd.datetime.today() - pd.offsets.BDay(1)).date()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "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", "option_delta.spread = 60\n", "option1 = BlackSwaption(option_delta, date(2018, 11, 21), 62.5, option_type=\"payer\")\n", "option1.sigma = .404\n", "option1.direction = 'Long'\n", "option2 = BlackSwaption(option_delta, date(2018, 11, 21), 85, option_type=\"payer\")\n", "option2.sigma = .588\n", "option2.direction = 'Short'\n", "option1.notional = 100_000_000\n", "option2.notional = 300_000_000\n", "option_delta.notional = option1.notional * option1.delta + option2.notional * option2.delta\n", "\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.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, ['opt1', 'opt2', 'delta'])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#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'])" ] }, { "cell_type": "code", "execution_count": null, "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", "portf1 = Portfolio([hy_tranche], [1])\n", "scens = run_portfolio_scenarios(portf1, date_range, params=[\"pnl\"],\n", " spread_shock=spread_shock,\n", " corr_shock=corr_shock,\n", " vol_shock=vol_shock,\n", " vol_surface=vol_surface)\n", "scens.xs((0,0), level=['corr_shock', 'vol_shock'])" ] }, { "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", " \"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", "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", "\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)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pos['basket'] = tranche_port\n", "#Set Shock Range\n", "spread_range = (1+ spread_shock) * option_delta.spread\n", "#Run tranche scenarios\n", "temp = []\n", "for i, r in pos.iterrows():\n", " df = run_tranche_scenarios_rolldown(r.basket, spread_range, date_range, corr_map=False)\n", " temp.append(r.notional*df.xs(str(r.attach) + \"-\" + str(r.detach), axis=1, level=1))\n", "tranches_scens = sum(temp)\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)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#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()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#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()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "total_scens_single_date[['delta', 'delta_t', 'delta_s']].plot()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "total_scens_single_date[['pnl', 'pnl_t', 'pnl_s']].plot()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "total_scens.date = pd.to_datetime(total_scens.date)\n", "total_scens = total_scens.set_index(['date'])\n", "plot_time_color_map(total_scens, spread_range, attr=\"pnl\")\n", "#plot_time_color_map(df, shock_range, attr=\"final_delta\", color_map= 'rainbow', centered = False)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#PNL of just the swaptions\n", "plot_time_color_map(swaption_scens, spread_range, attr=\"pnl\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Construct levered Super senior hedged with swaption\n", "index = 'IG'\n", "series = 30\n", "option_delta = Index.from_name(index, series, '5yr')\n", "option_delta.spread = 62\n", "option_delta.notional = 1\n", "option1 = BlackSwaption(option_delta, date(2018, 7, 19), 75, option_type=\"payer\")\n", "option1.sigma = .52\n", "option1.direction = 'Long'\n", "option1.notional = 2_000_000_000\n", "\n", "#If we have two options instead of just one\n", "option2 = BlackSwaption(option_delta, date(2018, 7, 19), 90, option_type=\"payer\")\n", "option2.sigma = .68\n", "option2.direction = 'Long'\n", "option2.notional = 6_000_000_000\n", "\n", "option3 = BlackSwaption(option_delta, date(2018, 12, 19), 55, option_type=\"receiver\")\n", "option3.sigma = .373\n", "option3.direction = 'Short'\n", "option3.notional = 5_000_000_000\n", "\n", "#portf = Portfolio([option1, option_delta])\n", "portf = Portfolio([option1, option2, option3, option_delta])\n", "portf.value_date = value_date\n", "portf.reset_pv()\n", "#Run Swaption sensitivities\n", "#Set Shock range\n", "shock_min = -.5\n", "shock_max = 1.25\n", "spread_shock = np.arange(shock_min, shock_max, 0.05)\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 = 30_000_000_000\n", "t = bkt.TrancheBasket('IG', '29', '5yr')\n", "t.build_skew()\n", "#get back to 17bps, .36 delta\n", "port_spread = 67\n", "#t.rho = np.array([np.nan, 0.39691196, 0.48904597, 0.8, np.nan])\n", "t.tweak(port_spread)\n", "spread_range = (1+ spread_shock) * port_spread\n", "tranches_scens = run_tranche_scenarios_rolldown(t, spread_range, date_range, corr_map=False)\n", "tranches_scens = notional*tranches_scens.xs('15-100', axis=1, level=1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "index = 'IG'\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", "\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", "color_plots(portf, scens, ['opt1', 'opt2', 'delta'])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dawnengine.dispose()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.1" } }, "nbformat": 4, "nbformat_minor": 2 }