diff options
Diffstat (limited to 'python/notebooks/swaption_scenarios.ipynb')
| -rw-r--r-- | python/notebooks/swaption_scenarios.ipynb | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/python/notebooks/swaption_scenarios.ipynb b/python/notebooks/swaption_scenarios.ipynb new file mode 100644 index 00000000..13639918 --- /dev/null +++ b/python/notebooks/swaption_scenarios.ipynb @@ -0,0 +1,432 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import datetime\n", + "import pandas as pd\n", + "import numpy as np\n", + "import analytics\n", + "import math\n", + "\n", + "from graphics import plot_color_map\n", + "from analytics import Swaption, BlackSwaption, BlackSwaptionVolSurface, CreditIndex, Portfolio\n", + "from analytics.scenarios import run_swaption_scenarios, run_index_scenarios, run_portfolio_scenarios, run_portfolio_scenarios_module\n", + "from scipy.interpolate import SmoothBivariateSpline\n", + "from utils.db import dbconn, dbengine\n", + "from risk.swaptions import get_swaption_portfolio\n", + "\n", + "conn = dbconn('dawndb')\n", + "dawn_engine = dbengine('dawndb')\n", + "conn.autocommit=True\n", + "analytics.init_ontr()\n", + "pd.options.display.float_format = \"{:,.2f}\".format" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "portf = get_swaption_portfolio(datetime.date.today() - pd.offsets.BDay(), conn, source_list=['GS'])\n", + "\n", + "hedges = pd.read_sql_query(\"SELECT security_desc, notional FROM list_cds_positions_by_strat(%s) \"\n", + " \"WHERE folder in ('IGOPTDEL', 'HYOPTDEL')\",\n", + " conn, params=(datetime.date.today(),))\n", + "\n", + "for i, r in hedges.iterrows():\n", + " portf.add_trade(CreditIndex(r['security_desc'].split(\" \")[1],\n", + " r['security_desc'].split(\" \")[3][1:],\n", + " '5yr', value_date=datetime.date.today() - pd.offsets.BDay(),\n", + " notional = r['notional']), ('delta', i))\n", + "\n", + "vol_surface = {}\n", + "for trade in portf.swaptions:\n", + " vs = BlackSwaptionVolSurface(trade.index.index_type, trade.index.series, \n", + " value_date=datetime.date.today(), interp_method = \"bivariate_linear\")\n", + " vol_surface[(trade.index.index_type, trade.index.series, trade.option_type)] = vs[vs.list(source='GS', option_type=trade.option_type)[-1]]\n", + "\n", + "#Set original_pv as of yesterday's EOD levels, don't reset PV after this time\n", + "portf.mark(interp_method=\"bivariate_linear\", source_list=['GS'])\n", + "portf.reset_pv()\n", + "\n", + "#set ref to today's levels\n", + "portf.value_date = datetime.date.today()\n", + "portf.mark(interp_method=\"bivariate_linear\", source_list=['GS'])\n", + "\n", + "spread_shock = np.round(np.arange(-.1, .1, .01), 4)\n", + "scens = run_portfolio_scenarios(portf, [datetime.datetime.now()], params=['pnl', 'hy_equiv', 'sigma'],\n", + " spread_shock=spread_shock,\n", + " vol_shock=[0],\n", + " corr_shock=[0],\n", + " vol_surface=vol_surface)\n", + "pnl = scens.xs('pnl', level = 2, axis=1).sum(axis=1)\n", + "hy_equiv = scens.xs('hy_equiv', level = 2, axis=1).sum(axis=1)\n", + "\n", + "ig = CreditIndex('IG', 32, '5yr')\n", + "ig.mark()\n", + "\n", + "pnl.index = pnl.index.set_levels((1+pnl.index.get_level_values('spread_shock')) * ig.spread, level = 'spread_shock')\n", + "hy_equiv.index = pnl.index" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pnl, hy_equiv" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#Trade Analysis - pre-trade analytics\n", + "index = 'IG'\n", + "series = 32\n", + "option_delta = CreditIndex(index, series, '5yr') \n", + "option_delta.spread = 60\n", + "option1 = BlackSwaption(option_delta, datetime.date(2019, 9, 17), 90, option_type=\"payer\") \n", + "option2 = BlackSwaption(option_delta, datetime.date(2019, 11, 19), 90, option_type=\"payer\") \n", + "option1.sigma = .6\n", + "option2.sigma = .58\n", + "option1.notional = 100_000_000 \n", + "option2.notional = 100_000_000 \n", + "option1.direction = 'Long' \n", + "option2.direction = 'Short' \n", + "option_delta.notional = option1.delta * option1.notional + option2.delta * option2.notional\n", + "portf = Portfolio([option1, option2, option_delta], trade_ids=['opt1', 'opt2', 'delta'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "date_range = pd.bdate_range(portf.value_date, portf.value_date + pd.tseries.offsets.BDay(30), freq='3B')\n", + "vol_shock = np.arange(-.15, .31, 0.01)\n", + "spread_shock = np.arange(-.2, 2, 0.01)\n", + "vol_surface = {}\n", + "for trade in portf.swaptions:\n", + " vs = BlackSwaptionVolSurface(trade.index.index_type, trade.index.series, \n", + " value_date=portf.value_date, interp_method = \"bivariate_linear\")\n", + " vol_surface[(trade.index.index_type, trade.index.series)] = vs[vs.list(option_type='payer')[-1]]\n", + "\n", + "df = run_portfolio_scenarios(portf, date_range, params=[\"pnl\"],\n", + " spread_shock = spread_shock,\n", + " vol_shock = vol_shock,\n", + " vol_surface = vol_surface)\n", + "df = df.reset_index()\n", + "df.vol_shock = df.vol_shock.round(2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def plot_trade_scenarios(portf, shock_min=-.15, shock_max=.2, vol_time_roll=True):\n", + " portf.reset_pv()\n", + " end_date = min(portf.swaptions, key=lambda x: x.exercise_date).exercise_date\n", + " date_range = pd.bdate_range(portf.value_date,\n", + " end_date - pd.tseries.offsets.BDay(), freq='3B')\n", + " vol_shock = np.arange(-.15, .31, 0.01)\n", + " spread_shock = np.arange(shock_min, shock_max, 0.01)\n", + " index = portf.indices[0].index_type\n", + " vol_surface = {}\n", + " for trade in portf.swaptions:\n", + " vs = BlackSwaptionVolSurface(trade.index.index_type, trade.index.series, \n", + " value_date=portf.value_date, interp_method = \"bivariate_linear\")\n", + " vol_surface[(trade.index.index_type, trade.index.series)] = vs[vs.list(option_type='payer')[-1]]\n", + " \n", + " df = run_portfolio_scenarios(portf, date_range, params=[\"pnl\",\"delta\"],\n", + " spread_shock = spread_shock,\n", + " vol_shock = vol_shock,\n", + " vol_surface = vol_surface)\n", + " df = df.reset_index()\n", + " df.vol_shock = df.vol_shock.round(2)\n", + "\n", + " if index == 'HY':\n", + " df['price'] = 100 + (500 - portf.indices[0].spread * (1 + df.spread_shock)) \\\n", + " * abs(portf.indices[0].DV01) / portf.indices[0].notional * 100\n", + " df = df.set_index(['date', 'price', 'vol_shock'])\n", + " sort_order = [True, False]\n", + " else:\n", + " df['spread'] = portf.indices[0].spread * (1 + df.spread_shock)\n", + " df = df.set_index(['date', 'spread', 'vol_shock'])\n", + " sort_order = [True, True]\n", + " \n", + " pnl = df.xs('pnl', axis=1, level=1).sum(axis=1)\n", + " for trade_id, t in portf.items():\n", + " if isinstance(t, BlackSwaption):\n", + " df[(trade_id, 'delta')] *= -t.notional \n", + " delta = df.xs('delta', axis=1, level=1).sum(axis=1).xs(0, level='vol_shock')\n", + " delta += sum([x.notional * -1 if x.direction == 'Buyer' else 1 for x in portf.indices])\n", + "\n", + " pnl.name = 'pnl'\n", + " delta.name = 'delta'\n", + "\n", + " plot_color_map(pnl.xs(0, level='vol_shock'), sort_order)\n", + " plot_color_map(delta, sort_order)\n", + " plot_color_map(pnl.loc[date_range[-1]], sort_order)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def calc_simple_scenario(portf, shock_min=-.15, shock_max=.2):\n", + " portf.reset_pv()\n", + " end_date = min(portf.swaptions, key=lambda x: x.exercise_date).exercise_date\n", + " date_range = pd.bdate_range(portf.value_date,\n", + " end_date - pd.tseries.offsets.BDay(), freq='3B')\n", + " vol_shock = [0]\n", + " spread_shock = np.arange(shock_min, shock_max, 0.01)\n", + " index = portf.indices[0].index_type\n", + " vol_surface = {}\n", + " for trade in portf.swaptions:\n", + " vs = BlackSwaptionVolSurface(trade.index.index_type, trade.index.series, \n", + " value_date=portf.value_date, interp_method = \"bivariate_linear\")\n", + " vol_surface[(trade.index.index_type, trade.index.series)] = vs[vs.list(option_type='payer')[-1]]\n", + "\n", + " df = run_portfolio_scenarios(portf, date_range, params=[\"pnl\"],\n", + " spread_shock = spread_shock,\n", + " vol_shock = vol_shock,\n", + " vol_surface = vol_surface)\n", + "\n", + " return df.xs('pnl', axis=1, level=1).sum(axis=1) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot_trade_scenarios(portf)\n", + "plot_trade_scenarios(portf, -.15, .5, vol_time_roll=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#Dec Jan 2017 Trade\n", + "option_delta = CreditIndex.from_tradeid(864)\n", + "option1 = BlackSwaption.from_tradeid(3, option_delta)\n", + "option2 = BlackSwaption.from_tradeid(4, option_delta)\n", + "portf = Portfolio([option1, option2, option_delta], trade_ids=['opt1', 'opt2', 'delta'])\n", + "#plot_trade_scenarios(portf)\n", + "\n", + "#Feb 2017: Sell May Buy April Calendar Trade\n", + "option_delta = CreditIndex.from_tradeid(870)\n", + "option1 = BlackSwaption.from_tradeid(5, option_delta)\n", + "option2 = BlackSwaption.from_tradeid(6, option_delta)\n", + "portf = Portfolio([option1, option2, option_delta], trade_ids=['opt1', 'opt2', 'delta'])\n", + "#plot_trade_scenarios(portf)\n", + "\n", + "#April 2017: Sell May Buy June Calendar Trade\n", + "option_delta = CreditIndex.from_tradeid(874)\n", + "option1 = BlackSwaption.from_tradeid(7, option_delta)\n", + "option2 = BlackSwaption.from_tradeid(8, option_delta)\n", + "portf = Portfolio([option1, option2, option_delta], trade_ids=['opt1', 'opt2', 'delta'])\n", + "#plot_trade_scenarios(portf)\n", + "\n", + "#June July 2017 Calendar Trade\n", + "option_delta_pf = CreditIndex.from_tradeid(874)\n", + "option_delta2_pf = CreditIndex.from_tradeid(879)\n", + "\n", + "option1_pf = BlackSwaption.from_tradeid(7, option_delta_pf)\n", + "option2_pf = BlackSwaption.from_tradeid(9, option_delta_pf)\n", + "option_delta_pf.notional = 50_335_169\n", + "\n", + "portf = Portfolio([option1_pf, option2_pf, option_delta_pf], trade_ids=['opt1', 'opt2', 'delta'])\n", + "portf.value_date = datetime.date(2017, 5, 17)\n", + "portf.mark()\n", + "#plot_trade_scenarios(portf)\n", + "\n", + "#July 2017: Buy Sept HY payer spread\n", + "option_delta = CreditIndex.from_tradeid(891)\n", + "option1 = BlackSwaption.from_tradeid(10, option_delta)\n", + "option2 = BlackSwaption.from_tradeid(11, option_delta)\n", + "portf = Portfolio([option1, option2, option_delta], trade_ids=['opt1', 'opt2', 'delta'])\n", + "#plot_trade_scenarios(portf)\n", + "\n", + "#March 2019: May Bull Risky\n", + "option_delta = CreditIndex.from_tradeid(1063)\n", + "option1 = BlackSwaption.from_tradeid(41, option_delta)\n", + "option2 = BlackSwaption.from_tradeid(40, option_delta)\n", + "portf = Portfolio([option1, option2, option_delta], trade_ids=['opt1', 'opt2', 'delta'])\n", + "results = calc_simple_scenario(portf, shock_min=-.3, shock_max=.3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#Look at steepness of volatility - 30 days, .85 vs .15 payer deltas on HY\n", + "days = 30\n", + "delta1 = .85\n", + "delta2 = .15\n", + "index = 'HY'\n", + "\n", + "sql_str = \"select b.quotedate, b.ref, b.ref_id, b.expiry, a.delta_pay, a.vol from \" \\\n", + " \"swaption_quotes a join swaption_ref_quotes b on a.ref_id = b.ref_id and index = %s\"\n", + "df = pd.read_sql_query(sql_str, dbengine('serenitasdb'), \n", + " index_col=['quotedate'], parse_dates={'quotedate': {'utc': True}}, params=[index])\n", + "df['days_expiry'] = (df.expiry - df.index.date).dt.days\n", + "r_1 = []\n", + "for i, g in df.groupby(pd.Grouper(freq='D', level='quotedate')):\n", + " r = []\n", + " for i_1, g_1 in g.groupby(['days_expiry', 'delta_pay']):\n", + " r.append([i_1[0], i_1[1], g_1['vol'].mean()])\n", + " if len(r) > 0:\n", + " temp = np.dstack(r)\n", + " f = SmoothBivariateSpline(temp[0][0], temp[0][1], temp[0][2])\n", + " r = (f(days, delta1) - f(days, delta2))[0][0]\n", + " r_1.append([i, r])\n", + " else:\n", + " pass\n", + "df_1 = pd.DataFrame(r_1, columns=['date', 'steepness'])\n", + "df_1.set_index('date').plot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#Set up Portfolio\n", + "from risk.swaptions import get_swaption_portfolio\n", + "from risk.tranches import get_tranche_portfolio\n", + "rundate = datetime.date(2019,6,21)\n", + "\n", + "portf = get_swaption_portfolio(rundate, conn)\n", + "\n", + "#index positions\n", + "df = pd.read_sql_query(\"SELECT * from list_cds_positions_by_strat(%s)\",\n", + " dawn_engine, params=(rundate,))\n", + "df = df[df.folder.str.contains(\"OPT\")]\n", + "for t in df.itertuples(index=False):\n", + " portf.add_trade(CreditIndex(redcode=t.security_id, maturity=t.maturity, notional=t.notional),\n", + " (t.folder, t.security_desc))\n", + "\n", + "portf.value_date = rundate\n", + "portf.mark(interp_method=\"bivariate_linear\")\n", + "portf.reset_pv()\n", + "\n", + "#------------------------Calc Scenarios\n", + "vol_surface = {}\n", + "for trade in portf.swaptions:\n", + " vs = BlackSwaptionVolSurface(trade.index.index_type, trade.index.series, \n", + " value_date=rundate, 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 = np.round(np.arange(-.2, 1, .05), 3)\n", + "scens = run_portfolio_scenarios(portf, [pd.Timestamp(rundate)], params=['pnl', 'delta'],\n", + " spread_shock=spread_shock,\n", + " vol_shock=vol_shock,\n", + " corr_shock=[0],\n", + " vol_surface=vol_surface)\n", + "\n", + "pnl = scens.xs('pnl', axis=1, level=2)\n", + "pnl = pnl.xs(0, level='vol_shock')\n", + "\n", + "scenarios = (pnl.\n", + " reset_index(level=['date'], drop=True).\n", + " groupby(level=0, axis=1).sum())\n", + "\n", + "options = ['HYOPTDEL', 'HYPAYER', 'HYREC', 'IGOPTDEL', 'IGPAYER', 'IGREC']\n", + "scenarios['options'] = scenarios[set(scenarios.columns).intersection(options)].sum(axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#Now say that it widens by X percentage, then rebalance, then do the shock again\n", + "rundate = datetime.date(2019,6,21)\n", + "x = 1.2\n", + "for t in portf.swaptions:\n", + " t.index.spread *= x\n", + " vs = vol_surface[(t.index.index_type, t.index.series)]\n", + " t.sigma = max(0.2, float(vs(t.T, math.log(t.moneyness))))\n", + "for t in portf.indices:\n", + " t.spread *= x\n", + "pnl = portf.pnl\n", + "\n", + "analytics.init_ontr(value_date=rundate)\n", + "rebal = analytics._ontr()\n", + "rebal.notional = portf.hy_equiv\n", + "rebal.direction = 'Seller'\n", + "\n", + "rebal.spread *= x\n", + "portf.add_trade(rebal, ('rebalance', 'HYOPTDEL'))\n", + "portf.reset_pv()\n", + "\n", + "swaptions_scens = portf.swaptions[0].shock(params=['pnl', 'pv'],\n", + " spread_shock=spread_shock,\n", + " vol_shock=vol_shock,\n", + " vol_surface=vol_surface)\n", + "\n", + "#------------------------Calc Scenarios\n", + "scens = run_portfolio_scenarios(portf, [pd.Timestamp(rundate)], params=['pnl', 'pv'],\n", + " spread_shock=spread_shock,\n", + " vol_shock=vol_shock,\n", + " corr_shock=[0],\n", + " vol_surface=vol_surface)\n", + "\n", + "pnl = scens.xs('pnl', axis=1, level=2)\n", + "pnl = pnl.xs(0, level='vol_shock')\n", + "\n", + "scenarios = (pnl.\n", + " reset_index(level=['date'], drop=True).\n", + " groupby(level=0, axis=1).sum())\n", + "\n", + "options = ['HYOPTDEL', 'HYPAYER', 'HYREC', 'IGOPTDEL', 'IGPAYER', 'IGREC', 'rebalance']\n", + "scenarios['options'] = scenarios[set(scenarios.columns).intersection(options)].sum(axis=1)" + ] + } + ], + "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.3" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} |
