aboutsummaryrefslogtreecommitdiffstats
path: root/python
diff options
context:
space:
mode:
Diffstat (limited to 'python')
-rw-r--r--python/notebooks/Valuation Backtest.ipynb108
1 files changed, 89 insertions, 19 deletions
diff --git a/python/notebooks/Valuation Backtest.ipynb b/python/notebooks/Valuation Backtest.ipynb
index dfddbf86..5e64c8e0 100644
--- a/python/notebooks/Valuation Backtest.ipynb
+++ b/python/notebooks/Valuation Backtest.ipynb
@@ -6,15 +6,15 @@
"metadata": {},
"outputs": [],
"source": [
- "from utils.db import dbengine\n",
+ "from utils.db import dbconn, dbengine, serenitas_engine, dawn_engine\n",
"\n",
"import datetime\n",
"import mark_backtest_underpar as mark\n",
"import globeop_reports as ops\n",
"import pandas as pd\n",
"import matplotlib.pyplot as plt\n",
- "\n",
- "engine = dbengine('dawndb')"
+ "import numpy as np\n",
+ "import math"
]
},
{
@@ -236,20 +236,30 @@
"metadata": {},
"outputs": [],
"source": [
- "#Portfolio MTM Gains/Loss/Net\n",
- "df_pnl = ops.get_monthly_pnl()[:date][['mtdbookunrealmtm', 'mtdbookrealmtm']].sum(axis=1)\n",
- "df_pnl.name = 'mtm'\n",
- "r=[]\n",
- "for d, g in df_pnl.reset_index('identifier').groupby(pd.Grouper(freq='M')):\n",
- " sql_string = \"SELECT * FROM risk_positions(%s, 'Subprime') WHERE notional > 0\"\n",
- " pos = pd.read_sql_query(sql_string, engine, params=[g.index[-1].date()])\n",
- " pos.identifier = pos.identifier.str[:9]\n",
- " pos = pos.join(df_pnl.groupby('identifier').cumsum().loc[g.index[-1]],\n",
- " on='identifier')['mtm'] / nav.loc[d]\n",
- " r.append([g.index[-1], pos[pos>=0].sum(), pos[pos<0].sum()])\n",
- "summary = pd.DataFrame.from_records(r, index='date', columns=['date','gains','loss'])\n",
- "summary['Net'] = summary.gains + summary.loss\n",
- "summary.plot()"
+ "#Portfolio MTM Gains/Loss/Net each month\n",
+ "df_pnl = ops.get_monthly_pnl()\n",
+ "df_pnl_temp = df_pnl[:date][['mtdbookunrealmtm', 'mtdbookrealmtm']].sum(axis=1)\n",
+ "df_pnl_temp.name = 'mtm'\n",
+ "r={}\n",
+ "for d, g in df_pnl_temp.reset_index('identifier').groupby(pd.Grouper(freq='M')):\n",
+ " p = []\n",
+ " for a in ['Subprime', 'CLO', 'CSO', 'CRT']:\n",
+ " sql_string = \"SELECT * FROM risk_positions(%s, %s) WHERE notional > 0\"\n",
+ " p.append(pd.read_sql_query(sql_string, dawn_engine, params=[d.date(), a], index_col = ['identifier']))\n",
+ " r[d]= pd.concat(p).merge(g.groupby('identifier').sum(), left_index=True, right_index=True)\n",
+ "pos = pd.concat(r, names=['date','identifier'])\n",
+ "pos = pos.groupby('date').sum().merge(navs, left_index=True, right_index=True)\n",
+ "pos['mtm_ret'] = pos['mtm']/pos['endbooknav']"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#MTM in each portfolio\n",
+ "df_pnl.groupby(['identifier']).cumsum()"
]
},
{
@@ -258,7 +268,67 @@
"metadata": {},
"outputs": [],
"source": [
- "summary.iloc[-1]"
+ "################################## How much unrealized gains in bonds?\n",
+ "#1) sum up all the (paid-down adjusted proceeds) of all buy trades up to the point of where current face = sum of all traded notionals (FIFO)\n",
+ "#2) (paid down adjusted proceeds) of each trade = principal_paid - paydown since purchase of that bond to the date\n",
+ "#3) cost basis = sum of all (paid down adjusted proceeds)\n",
+ "#4) Doesn't work with CSOs - their cashflows are not correctly recorded in bloomberg\n",
+ "fund = 'SERCGMAST'\n",
+ "globeop_val_rep = ops.get_portfolio()\n",
+ "navs = ops.get_net_navs()\n",
+ "all_cf = pd.read_sql_query(\"SELECT * FROM cashflow_history\", dawn_engine,\n",
+ " parse_dates=['date'],\n",
+ " index_col=['date']).sort_index()\n",
+ "all_trades = pd.read_sql_query(\"SELECT * FROM bonds where fund = %s order by trade_date desc\", dawn_engine,\n",
+ " parse_dates={'lastupdate':{'utc':True}, 'trade_date': {}, 'settle_date':{}},\n",
+ " index_col = 'trade_date', params=[fund])\n",
+ "all_trades['curr_notional'] = all_trades['principal_payment']/all_trades['price'] *100\n",
+ "\n",
+ "bonds = {}\n",
+ "monthend_val_rep = globeop_val_rep.groupby(pd.Grouper(freq=\"M\"), group_keys=False).apply(lambda df: df.loc[df.index[-1]])\n",
+ "for position_date, go_val in monthend_val_rep.groupby('periodenddate'):\n",
+ " for a in [\"Subprime\", \"CRT\", \"CLO\"]:\n",
+ " bonds[position_date, a] = pd.read_sql_query(\"select * from risk_positions(%s, %s, %s)\", dawn_engine,\n",
+ " params=[position_date.date(), a, fund], index_col = 'identifier').sort_index()\n",
+ "\n",
+ "bonds = pd.concat(bonds, names=['periodenddate','asset_class', 'identifier'])\n",
+ "bonds['curr_notional'] = bonds['notional'] * bonds['factor']\n",
+ "monthend_val_rep = monthend_val_rep.groupby(['periodenddate', 'identifier'])['endbookmv'].sum()\n",
+ "bonds = bonds.reset_index().merge(monthend_val_rep, \n",
+ " left_on=['periodenddate','identifier'], \n",
+ " right_on=['periodenddate','identifier'],\n",
+ " validate='1:1',\n",
+ " how='left')\n",
+ "\n",
+ "#can also use endbookmv in field to use official globeop values, but having a Repo looks like a loss \n",
+ "def gains_calc(pos, trades, cf, field='usd_market_value'):\n",
+ " bond_trade = trades[(trades.identifier == pos.identifier) & (trades.buysell==True)]\n",
+ " cost_basis = 0\n",
+ " for i, bt in bond_trade.iterrows():\n",
+ " bond_cf_hist = cf[cf.identifier==pos.identifier]\n",
+ " bond_cf_hist['beg_principal_bal'] = bond_cf_hist['principal_bal'].shift(1)\n",
+ " bond_cf_hist = bond_cf_hist[bt.settle_date:pos.periodenddate]\n",
+ " paydown = 0\n",
+ " if ~bond_cf_hist.empty:\n",
+ " bt.orig_curr_notional = bt.curr_notional\n",
+ " for i, bond_cf in bond_cf_hist.iterrows():\n",
+ " paydown = bt.curr_notional/bond_cf.beg_principal_bal * bond_cf.principal if bond_cf.beg_principal_bal > 0 else 0\n",
+ " bt.curr_notional -= paydown\n",
+ " bt.curr_notional = bt.orig_curr_notional\n",
+ " if len(pos.identifier) == 11:\n",
+ " ratio = np.max([0,np.min([1.0, pos.notional/bt.faceamount])])\n",
+ " pos.notional -= bt.faceamount\n",
+ " else:\n",
+ " ratio = np.max([0,np.min([1.0, pos.curr_notional/bt.curr_notional])]) if bt.curr_notional > 0 else 0\n",
+ " pos.curr_notional -= bt.curr_notional\n",
+ " adj_principal_payment = np.max([0,bt.principal_payment - paydown])\n",
+ " cost_basis += adj_principal_payment * ratio\n",
+ " return pos[field] - cost_basis\n",
+ " \n",
+ "bonds['gains'] = bonds.apply(lambda position: gains_calc(position, all_trades, all_cf), axis=1)\n",
+ "navs = navs.merge(bonds.groupby('periodenddate')['gains'].sum(), left_index=True, right_index=True)\n",
+ "navs['mtm_in_portf'] = navs['gains']/navs['begbooknav']\n",
+ "navs['mtm_in_portf'].plot()"
]
},
{
@@ -299,7 +369,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.8.1"
+ "version": "3.8.5"
}
},
"nbformat": 4,