diff options
Diffstat (limited to 'python')
| -rw-r--r-- | python/analytics/curve_trades.py | 29 | ||||
| -rw-r--r-- | python/notebooks/Curve Trades.ipynb | 79 |
2 files changed, 98 insertions, 10 deletions
diff --git a/python/analytics/curve_trades.py b/python/analytics/curve_trades.py index 455d1422..f76f6b88 100644 --- a/python/analytics/curve_trades.py +++ b/python/analytics/curve_trades.py @@ -82,12 +82,11 @@ def on_the_run_theta(index='IG', rolling=6): theta_matrix = df.groupby(level=['date', 'tenor']).nth(-1)['theta_per_dur'] theta_matrix.unstack(-1).plot() - -def curve_returns(index='IG', rolling=6): +def curve_returns(index='IG', rolling=6, years=3): # look at returns otr = on_the_run(index) df = index_returns(index=index, series=list(range(otr - rolling, otr + 1)), - tenor=['3yr', '5yr', '7yr', '10yr']) + tenor=['3yr', '5yr', '7yr', '10yr'], years=years) # on-the-run returns df = df.reset_index('index', drop=True) returns = df.price_return.dropna().unstack('tenor').groupby(level='date').nth(-1) @@ -97,7 +96,17 @@ def curve_returns(index='IG', rolling=6): '7-10': 1.33 * returns['7yr'] - returns['10yr'], '3-5-10': -2 * returns['3yr'] + 3 * returns['5yr'] - returns['10yr'], '3-5': returns['5yr'] - 1.56 * returns['3yr'], - '3-7': returns['7yr'] - 2.07 * returns['3yr']}) + '3-7': returns['7yr'] - 2.07 * returns['3yr'], + '5yr long': returns['5yr']}) + + return strategies_return + + +def curve_returns_stats(strategies_return): + + ''' + Takes a curve_return df''' + strategies_return_monthly = (strategies_return. groupby(pd.Grouper(freq='M')). agg(lambda df: (1 + df).prod() - 1)) @@ -108,10 +117,10 @@ def curve_returns(index='IG', rolling=6): else: return df.mean() / df.std() * math.sqrt(12) - results = strategies_return.agg([sharpe, lambda df: df.nsmallest(10).mean()]) + results = strategies_return.agg([sharpe, lambda df: df.nsmallest(10).mean(), lambda df: df.std()]) sharpe_monthly = strategies_return_monthly.agg(sharpe, period="monthly") sharpe_monthly.name = 'Monthly Sharpe' - results.index = ['Sharpe', 'Mean Worst 10 Days DrawDown'] + results.index = ['Sharpe', 'Mean Worst 10 Days DrawDown', 'Standard Deviation'] return results.append(sharpe_monthly) @@ -169,7 +178,11 @@ def curve_model(tenor_1='5yr', tenor_2='10yr'): def curve_model_results(df, model): df = df.dropna() - prstd_ols, df['down_2_stdev'], df['up_2_stdev'] = wls_prediction_std(model) + a, b, c = wls_prediction_std(model) + b.name = 'down_2_stdev' + c.name = 'up_2_stdev' + df = df.join(b) + df = df.join(c) #dr/dspread = exp(k) + spread_coeff * duration ^ dur_coeff * spread ^ (spread_coeff-1) cols = ['ratio', 'closespread', 'down_2_stdev', 'up_2_stdev'] df[cols] = np.exp(df[cols]) @@ -324,7 +337,7 @@ def pos_pnl_abs(portf, value_date, index='IG', rolling=6, years=3): for date, row in df.iterrows(): f = interp1d(np.hstack([0, lookup_table['year_frac']]), np.hstack([row[0]/2, row])) for ind in portf_copy.indices: - ind.spread = f((pd.to_datetime(ind.end_date) - value_date).days/365) + ind.spread = f((ind.end_date - value_date).days/365) r.append([[date, f(5)] + [portf_copy.pnl]]) df = pd.DataFrame.from_records(chain(*r), columns=['date', 'five_yr_spread', 'pnl']) return df.set_index('date') diff --git a/python/notebooks/Curve Trades.ipynb b/python/notebooks/Curve Trades.ipynb index a6ae0b7e..b2cde72e 100644 --- a/python/notebooks/Curve Trades.ipynb +++ b/python/notebooks/Curve Trades.ipynb @@ -16,6 +16,7 @@ "\n", "from ipywidgets import widgets\n", "from analytics.scenarios import run_curve_scenarios\n", + "from scipy.optimize import brentq\n", "from db import dbengine" ] }, @@ -41,7 +42,7 @@ "outputs": [], "source": [ "index = w.value\n", - "report_date = (pd.datetime.today() - pd.offsets.BDay(2)).normalize()" + "report_date = (pd.datetime.today() - pd.offsets.BDay(2)).date()" ] }, { @@ -91,8 +92,82 @@ "metadata": {}, "outputs": [], "source": [ + "rolling = 10\n", + "years = 5\n", + "ret = ct.curve_returns(index, rolling, years)\n", + "if index == 'IG':\n", + " ret1 = ct.curve_returns('HY', rolling, years)\n", + " suf = ' HY'\n", + "else:\n", + " ret1 = ct.curve_returns('IG', rolling, years)\n", + " suf = ' IG'\n", + "ret = ret.join(ret1['5yr long'], rsuffix=suf)\n", + "col_name = '5yr long' + suf" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#Strategy Cumulative Return\n", + "#Margin Requirement: 3% for IG Long only (33.3x), 25bps for curve (400x)\n", + "#Assume Margin Requirement of 10% for IG Long only (10x) and size the curve trade margin to\n", + "lev = 10\n", + "#1) have the same return volatility or \n", + "#curve_lev = ret['5yr long'].std()/ret['3-5-10'].std()\n", + "#2) have the same cumulative return\n", + "ret['5yr long lev'] = lev * ret['5yr long']\n", + "def aux(x, ret, col_a, col_b):\n", + " ret[col_b + ' lev'] = x * ret[col_b]\n", + " cum_ret = (ret+1).cumprod()\n", + " return cum_ret[col_a][-1] - cum_ret[col_b + ' lev'][-1]\n", + "\n", + "curve_lev = brentq(aux, 0.01, 3 * lev, args=(ret, '5yr long lev', '3-5-10'))\n", + "other_lev = brentq(aux, 0.01, 3 * lev, args=(ret, '5yr long lev', col_name))\n", + "\n", + "ret['3-5-10 lev'] = curve_lev * ret['3-5-10']\n", + "ret[col_name + ' lev'] = other_lev * ret[col_name]\n", + "cum_ret = (ret+1).cumprod()\n", + "cum_ret_ax = cum_ret[['5yr long lev', '3-5-10 lev', col_name + ' lev']].plot()\n", + "cum_ret_ax.figure.savefig(\"/home/serenitas/edwin/PythonGraphs/curve_trades_cum_return.png\", bbox_inches='tight')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "curve_lev, other_lev" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "#Curve Trade returns\n", - "ct.curve_returns()" + "ct.curve_returns_stats(ret)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#2016 scenario: max drawdown from the 2015 peak to 2016 trough\n", + "peak = cum_ret['2015'].max()\n", + "trough = cum_ret['2016'].min()\n", + "scenario_2016 = pd.DataFrame({'peak': peak,\n", + " 'trough': trough,\n", + " 'max_drawdown': (peak - trough)/peak,\n", + " 'peak_dates': cum_ret['2015'].idxmax(),\n", + " 'trough_dates': cum_ret['2016'].idxmin()})\n", + "scenario_2016" ] }, { |
