Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupport News AboutSign UpSign In
| Download

Week7_Demo

Views: 53
Kernel: Python 3 (Ubuntu Linux)

[FE800] Group 4: Phase 1:3 - Total and Residual Momentum Spillover

import pandas as pd import numpy as np from datetime import datetime, timedelta from dateutil import relativedelta import calendar import matplotlib.pyplot as plt from matplotlib import style import math

Functions

Ranking Equity Return function

def rank_port_decile(equity_data , formation_date, num_month, strat_type = 0): form_date = datetime.strptime(formation_date, '%Y-%m-%d') avg_date = form_date - relativedelta.relativedelta(months = num_month) check_last_day = calendar.monthrange(avg_date.year, avg_date.month) avg_date = datetime(avg_date.year, avg_date.month, check_last_day[-1]) equity_data_ = equity_data[(equity_data.date >= avg_date) & (equity_data.date <= form_date)] equity_data_ = equity_data_.drop_duplicates() try: equity_data_ = equity_data_[equity_data_.RETX != 'C'] except: print('There is no C') equity_data_.RETX = equity_data_.RETX.astype('float64') equity_data_.RET = equity_data_.RET.astype('float64') #residual if strat_type != 0: equity_data_.RETX = equity_data_.RETX - equity_data_.RF equity_data_.RETX = equity_data_.RETX.add(1) #cumulative #equity_group_mean_ret = equity_data_.groupby('TICKER').agg({'RETX': 'mean'}) equity_group_mean_ret = equity_data_.groupby('TICKER').agg({'RETX': 'prod'}) #cumulative equity_group_mean_ret = equity_group_mean_ret.subtract(1) #cumulative equity_group_mean_ret_sort = equity_group_mean_ret.sort_values(by=['RETX']) equity_group_mean_ret_sort['GROUP'] = 0 frac = math.floor(equity_group_mean_ret_sort.shape[0]/10) num_line = equity_group_mean_ret_sort.shape[0] left = num_line%frac group_num = [frac+1]*(left)+[frac]*(10-left) group = ['P10', 'P09', 'P08', 'P07', 'P06', 'P05', 'P04', 'P03', 'P02', 'P01'] i, j = 0, group_num[0] while j <= num_line: equity_group_mean_ret_sort.ix[(j-group_num[i]):j , 'GROUP'] = group[i] i +=1 if(i == 10): break j = j + group_num[i] return(equity_group_mean_ret_sort)

Generating Ranking Summary table function

def rank_table(rank_port, universe, strat_type = 0): rank_port_sort = rank_port rank_port_sort['TICKER'] = rank_port_sort.index rank_port_sort = rank_port.sort_values(by=['GROUP', 'TICKER']) rank_port_sort_sum = rank_port_sort.groupby('GROUP').agg({'TICKER':'count', 'RETX':'mean'}) rank_port_sort_sum.loc['Total'] = rank_port_sort_sum.sum() strat_name = np.where(strat_type == 0, 'Total', 'Residual') fig, ax = plt.subplots() fig.set_figheight(5) fig.set_figwidth(8) plt.subplots_adjust(left=0.5, top=0.8) ax.set_title('Ranking {0} Equity Returns \n From Winner to Loser Portfolios - {1}'.format(strat_name, universe), fontsize=15, weight='bold') ax.xaxis.set_visible(False) ax.yaxis.set_visible(False) ax.axis('off') ax.axis('tight') data = rank_port_sort_sum.round(6).values columns = ['Company numbers', 'Average returns'] rows = rank_port_sort_sum.index rcolors = plt.cm.BuPu(np.linspace(0, 0.5, len(rows))) ccolors = rcolors[::-1] clcolors = [] for i in range(0, 10): clcolors.append(["w","w"]) #clcolors.append(["#92a4cd","#92a4cd"]) clcolors.append(["#8c95c6","#8c95c6"]) plt.table(cellText = data, cellColours=clcolors, rowLabels=rows, colLabels=columns,rowColours=rcolors, colColours=ccolors, loc='center') fig.tight_layout()

Running Total Momentum Spillover Strategy function

def momentum_strategy(bond_data, rank_port, start_month, end_month, TMT = 2): bond_data = pd.merge(bond_data, rank_port[['GROUP']], how = 'left', left_on = 'company_symbol', right_index=True) bond_data = bond_data[bond_data.TMT >= TMT] bond_data = bond_data.dropna(subset=['RET_EOM', 'DURATION']) bond_average_month = bond_data.groupby(['month', 'company_symbol', 'GROUP']).agg({'PRICE_EOM':'mean','RET_EOM':'mean', 'RATING_NUM':'mean', 'DURATION':'mean'}) bond_month = pd.DataFrame(columns = ['month', 'ticker', 'port_group', 'price', 'return', 'avg_rating', 'duration']) bond_month['month'] = [i[0] for i in bond_average_month.index] bond_month['ticker'] = [i[1] for i in bond_average_month.index] bond_month['port_group'] = [i[2] for i in bond_average_month.index] bond_month['price'] = bond_average_month['PRICE_EOM'].values bond_month['return'] = bond_average_month['RET_EOM'].values bond_month['avg_rating'] = bond_average_month['RATING_NUM'].values bond_month['duration'] = bond_average_month['DURATION'].values bond_hold = bond_month[(bond_month.month >= start_month) & (bond_month.month <= end_month)] bond_sum_price = bond_hold.groupby(['port_group', 'month']).agg({'price':'sum'}) bond_sum_price['port_group'] = [i[0] for i in bond_sum_price.index] bond_sum_price['month'] = [i[1] for i in bond_sum_price.index] bond_hold_value = pd.merge(bond_hold, bond_sum_price, how = 'left', left_on = ['port_group', 'month'], right_on=['port_group', 'month']) bond_hold_value.columns = ['month', 'ticker', 'port_group', 'price', 'return', 'avg_rating', 'duration','port_total_weight'] bond_hold_value['port_weight'] = bond_hold_value['price'].divide(bond_hold_value['port_total_weight']) bond_hold_value['value_return'] = bond_hold_value['return'].mul(bond_hold_value['port_weight']) bond_hold_value['value_price'] = bond_hold_value['price'].mul(bond_hold_value['port_weight']) bond_hold_value['value_rating'] = bond_hold_value['avg_rating'].mul(bond_hold_value['port_weight']) bond_hold_value['value_duration'] = bond_hold_value['duration'].mul(bond_hold_value['port_weight']) # Value portfolio bond_hold_value_ = bond_hold_value.groupby(['port_group', 'month', 'ticker']).agg({'value_return':'sum', 'value_price':'sum', 'value_duration':'sum', 'value_rating':'sum'}) bond_hold_value_['port_group'] = [i[0] for i in bond_hold_value_.index] bond_hold_value_['month'] = [i[1] for i in bond_hold_value_.index] bond_hold_value_['ticker'] = [i[2] for i in bond_hold_value_.index] bond_hold_value_['value_return'] = bond_hold_value_['value_return'].add(1) bond_hold_value__ = bond_hold_value_.groupby(['port_group', 'ticker']).agg({'value_return':'prod', 'value_price':lambda x: x.iloc[-1],'value_duration':lambda x: x.iloc[-1], 'value_rating':lambda x: x.iloc[-1]}) bond_hold_value__['port_group'] = [i[0] for i in bond_hold_value__.index] bond_hold_value__['ticker'] = [i[1] for i in bond_hold_value__.index] bond_hold_value__['value_return'] = bond_hold_value__['value_return'].subtract(1) bond_hold_valueW = bond_hold_value__.groupby(['port_group']).agg({'value_return':'sum', 'value_price':'sum','value_duration':'sum', 'value_rating':'sum'}) bond_hold_valueW.loc['P01-P10'] = bond_hold_valueW.ix[0,0] - bond_hold_valueW.ix[-1,0] bond_hold_valueW.loc['P01-P05'] = bond_hold_valueW.ix[0,0] - bond_hold_valueW.ix[4,0] #Equal portfolio bond_hold_value___ = bond_hold_value bond_hold_value___['return'] = bond_hold_value___['return'].add(1) bond_hold_equalW_ = bond_hold_value___.groupby(['port_group', 'ticker']).agg({'return':'prod', 'price':lambda x: x.iloc[-1], 'avg_rating':lambda x: x.iloc[-1], 'duration': lambda x: x.iloc[-1]}) bond_hold_equalW_['port_group'] = [i[0] for i in bond_hold_equalW_.index] bond_hold_equalW_['ticker'] = [i[1] for i in bond_hold_equalW_.index] bond_hold_equalW_['return'] = bond_hold_equalW_['return'].subtract(1) bond_hold_equalW = bond_hold_equalW_.groupby('port_group').agg({'return':'mean', 'price':'mean', 'avg_rating':'mean', 'duration': 'mean', 'ticker': lambda x: x.nunique()}) bond_hold_equalW.loc['P01-P10'] = bond_hold_equalW.ix[0,0] - bond_hold_equalW.ix[-1,0] bond_hold_equalW.loc['P01-P05'] = bond_hold_equalW.ix[0,0] - bond_hold_equalW.ix[4,0] result_ = bond_hold_valueW.merge(bond_hold_equalW, left_index=True, right_index=True) text1 = 'value_wight'+'('+str(start_month)+','+str(end_month)+')' text2 = 'equal_weight'+'('+str(start_month)+','+str(end_month)+')' result__ = pd.DataFrame(columns = [text1, text2, 'value_price', 'equal_price', 'value_rating', 'equal_rating', 'value_duration', 'equal_duration', 'com_num']) result__[text1] = result_.iloc[:, 0] result__[text2] = result_.iloc[:, 4] result__['value_price'] = result_.iloc[:, 1] result__['equal_price'] = result_.iloc[:, 5] result__['value_rating'] = result_.iloc[:, 3] result__['equal_rating'] = result_.iloc[:, 6] result__['value_duration'] = result_.iloc[:, 2] result__['equal_duration'] = result_.iloc[:, 7] result__['com_num'] = result_.iloc[:, 8] result__.ix[10:,2:] = 0 result = pd.DataFrame(data = 0, columns = result__.columns, index = ['P01', 'P02', 'P03', 'P04', 'P05', 'P06', 'P07', 'P08', 'P09', 'P10']) for i in result__.index: result.loc[i] = result__.loc[i] return(result)

Generating Total Momentum Spillover Stategy Summary table function

def m_performance_table(perf, universe, strat_type = 0): strat_name = np.where(strat_type == 0, 'Total', 'Residual') fig, ax = plt.subplots() fig.set_figheight(5) fig.set_figwidth(14) plt.subplots_adjust(left=0.5, top=0.8) ax.set_title('{0} Momentum Spillover Performance Table - {1}'.format(strat_name, universe), fontsize=15, weight='bold') ax.xaxis.set_visible(False) ax.yaxis.set_visible(False) ax.axis('off') ax.axis('tight') data = perf.round(4).values columns = perf.columns rows = perf.index rcolors = plt.cm.BuPu(np.linspace(0, 0.5, len(rows))) ccolors = rcolors[::-1] clcolors = [] for i in range(0, 10): clcolors.append(["w","w","w","w","w","w","w","w","w"]) clcolors.append(["#92a4cd","#92a4cd","#92a4cd","#92a4cd","#92a4cd","#92a4cd", "#92a4cd", "#92a4cd", "#92a4cd"]) clcolors.append(["#8c95c6","#8c95c6","#8c95c6","#8c95c6","#8c95c6","#8c95c6", "#8c95c6", "#8c95c6", "#8c95c6"]) plt.table(cellText = data, cellColours=clcolors, rowLabels=rows, colLabels=columns,rowColours=rcolors, colColours=ccolors, loc='center') fig.tight_layout()

Momentum Spillover Strategy Performance Visualization function

def performance_plot(perf, avg_period, start, end, tmt, universe, strat_type = 0): style.use('seaborn') fig = plt.figure(figsize=(18, 15)) strat_name = np.where(strat_type == 0, 'Total', 'Residual') fig.suptitle('{0} Momentum Spillover Performance - {1}'.format(strat_name, universe), fontsize=20, fontweight='bold') plt.figtext(0.5,0.95, "Average {0}-month equity returns \n Holding bond portfolios from {1} to {2} \n Time-to-maturity over {3}".format(avg_period, start, end, tmt), ha="center", va="top", fontsize=14, color="black") ax1 = plt.subplot2grid((30, 22), (0, 0), rowspan=10, colspan=13) ax1.plot(perf.iloc[0:10, 0], label="{0}".format(perf.columns[0]), color='SteelBlue') ax1.annotate("{0:0.5f}".format(perf.iloc[0,0]), xy=(0, perf.iloc[0,0]), xytext=(8, 6), xycoords=('axes fraction', 'data'), textcoords='offset points') ax1.annotate("{0:0.5f}".format(perf.iloc[-3,0]), xy=(1, perf.iloc[-3,0]), xytext=(8, 6), xycoords=('axes fraction', 'data'), textcoords='offset points') ax1.plot(perf.iloc[0:10, 1], label="{0}".format(perf.columns[1]), color='IndianRed') ax1.annotate("{0:0.5f}".format(perf.iloc[0,1]), xy=(0, perf.iloc[0,1]), xytext=(8, 6), xycoords=('axes fraction', 'data'), textcoords='offset points') ax1.annotate("{0:0.5f}".format(perf.iloc[-3,1]), xy=(1, perf.iloc[-3,1]), xytext=(8, 6), xycoords=('axes fraction', 'data'), textcoords='offset points') ax1.set_title('{0} momemtum Spillover Curves from Winner to Loser Portfolios'.format(strat_name), fontsize=15, weight='bold') ax1.set_ylabel('Cumulative return') ax1.legend() ax2 = plt.subplot2grid((30, 22), (0, 15), rowspan=10, colspan=6) index = np.arange(2) bar_width = 0.35 opacity = 0.8 rect1 = ax2.bar(index - bar_width/2, perf.iloc[10:12,0].values, bar_width, color='SkyBlue', label="{0}".format(perf.columns[0])) for rect in rect1: height1 = rect.get_height() ax2.text(rect.get_x() + rect.get_width()/2., 1.01*height1, "{0:0.5f}".format(height1), ha='center', va='bottom') rect2 = ax2.bar(index + bar_width/2, perf.iloc[10:12,1].values, bar_width, color='IndianRed', alpha=opacity, label="{0}".format(perf.columns[1])) for rect in rect2: height2 = rect.get_height() ax2.text(rect.get_x() + rect.get_width()/2., 1.01*height2, "{0:0.5f}".format(height2), ha='center', va='bottom') ax2.set_xticks(index) ax2.set_xticklabels((perf.index[-2], perf.index[-1])) ax2.set_ylabel('Long-short Cumulative return') ax2.set_title('Long-short Performance', fontsize=15, weight='bold') ax2.legend() ax3 = plt.subplot2grid((30, 22), (12, 0), rowspan=6, colspan=10) ax3.plot(perf.iloc[0:10, 2], 'o-',label="Value price", color='orchid') ax3.plot(perf.iloc[0:10, 3], 'o-',label="Equal price", color='turquoise') ax3.set_title('Bond Prices from Winner to Loser Portfolios', fontsize=15, weight='bold') for i in range(0,10): ax3.text(i,1.001*perf.iloc[i, 2], '{0:.2f}'.format(perf.iloc[i, 2]), ha='center', va='bottom') ax3.text(i,1.001*perf.iloc[i, 3], '{0:.2f}'.format(perf.iloc[i, 3]), ha='center', va='bottom') ax3.legend() ax4 = plt.subplot2grid((30, 22), (12, 11), rowspan=6, colspan=10) ax4.plot(perf.iloc[0:10, 4], 'o-', label="Value credit rating", color='orchid') ax4.plot(perf.iloc[0:10, 5], 'o-', label="Equal credit rating", color='turquoise') ax4.set_title('Credit Rating from Winner to Loser Portfolios', fontsize=15, weight='bold') for i in range(0,10): ax4.text(i,1.001*perf.iloc[i, 4], '{0:.2f}'.format(perf.iloc[i, 4]), ha='center', va='bottom') ax4.text(i,1.001*perf.iloc[i, 5], '{0:.2f}'.format(perf.iloc[i, 5]), ha='center', va='bottom') ax4.legend() ax5 = plt.subplot2grid((30, 22), (20, 0), rowspan=6, colspan=10) ax5.plot(perf.iloc[0:10, 6], 'o-', label="Value durations", color='orchid') ax5.plot(perf.iloc[0:10, 7], 'o-', label="Equal durations", color='turquoise') ax5.set_title('Durations from Winner to Loser Portfolios', fontsize=15, weight='bold') for i in range(0,10): ax5.text(i,1.001*perf.iloc[i, 6], '{0:.2f}'.format(perf.iloc[i, 6]), ha='center', va='bottom') ax5.text(i,1.001*perf.iloc[i, 7], '{0:.2f}'.format(perf.iloc[i, 7]), ha='center', va='bottom') ax5.legend() ax6 = plt.subplot2grid((30, 22), (20, 11), rowspan=6, colspan=10) rect3 = ax6.bar(perf.index[0:10].values, perf.iloc[0:10, 8], bar_width, color='deepskyblue', label="Number of companies") for rect in rect3: height3 = rect.get_height() ax6.text(rect.get_x() + rect.get_width()/2., 1.01*height3, "{0}".format(int(height3)), ha='center', va='bottom') ax6.set_title('Total Company Numbers from Winner to Loser Portfolios', fontsize=15, weight='bold')

Performance comparison function

def comparison_performance(equity_data, bond_data, holding_range, formation_date, equity_range, TMT=2, strat_type = 0): rank1_port = rank_port_decile(equity_data, formation_date, equity_range[0], strat_type=strat_type) rank2_port = rank_port_decile(equity_data, formation_date, equity_range[1], strat_type=strat_type) rank3_port = rank_port_decile(equity_data, formation_date, equity_range[2], strat_type=strat_type) avg_period = (equity_range[0], equity_range[1], equity_range[2]) perf1_1 = momentum_strategy(bond_data, rank1_port, holding_range[0][0], holding_range[0][1], TMT=TMT) perf1_1_ = perf1_1.iloc[:, 0:2].add(1).pow(12/(holding_range[0][1] - holding_range[0][0] + 1)).subtract(1) perf1_2 = momentum_strategy(bond_data, rank1_port, holding_range[1][0], holding_range[1][1], TMT=TMT) perf1_2_ = perf1_2.iloc[:, 0:2].add(1).pow(12/(holding_range[1][1] - holding_range[1][0] + 1)).subtract(1) perf1_3 = momentum_strategy(bond_data, rank1_port, holding_range[2][0], holding_range[2][1], TMT=TMT) perf1_3_ = perf1_3.iloc[:, 0:2].add(1).pow(12/(holding_range[2][1] - holding_range[2][0] + 1)).subtract(1) perf1_4 = momentum_strategy(bond_data, rank1_port, holding_range[3][0], holding_range[3][1], TMT=TMT) perf1_4_ = perf1_4.iloc[:, 0:2].add(1).pow(12/(holding_range[3][1] - holding_range[3][0] + 1)).subtract(1) perf11 = [perf1_1_.iloc[-2, 0], perf1_1_.iloc[-2, 1], perf1_2_.iloc[-2, 0], perf1_2_.iloc[-2, 1], perf1_3_.iloc[-2, 0], perf1_3_.iloc[-2, 1], perf1_4_.iloc[-2, 0], perf1_4_.iloc[-2, 1]] perf12 = [perf1_1_.iloc[-1, 0], perf1_1_.iloc[-1, 1], perf1_2_.iloc[-1, 0], perf1_2_.iloc[-1, 1], perf1_3_.iloc[-1, 0], perf1_3_.iloc[-1, 1], perf1_4_.iloc[-1, 0], perf1_4_.iloc[-1, 1]] perf2_1 = momentum_strategy(bond_data, rank2_port, holding_range[0][0], holding_range[0][1], TMT=TMT) perf2_1_ = perf2_1.iloc[:, 0:2].add(1).pow(12/(holding_range[0][1] - holding_range[0][0] + 1)).subtract(1) perf2_2 = momentum_strategy(bond_data, rank2_port, holding_range[1][0], holding_range[1][1], TMT=TMT) perf2_2_ = perf2_2.iloc[:, 0:2].add(1).pow(12/(holding_range[1][1] - holding_range[1][0] + 1)).subtract(1) perf2_3 = momentum_strategy(bond_data, rank2_port, holding_range[2][0], holding_range[2][1], TMT=TMT) perf2_3_ = perf2_3.iloc[:, 0:2].add(1).pow(12/(holding_range[2][1] - holding_range[2][0] + 1)).subtract(1) perf2_4 = momentum_strategy(bond_data, rank2_port, holding_range[3][0], holding_range[3][1], TMT=TMT) perf2_4_ = perf2_4.iloc[:, 0:2].add(1).pow(12/(holding_range[3][1] - holding_range[3][0] + 1)).subtract(1) perf21 = [perf2_1_.iloc[-2, 0], perf2_1_.iloc[-2, 1], perf2_2_.iloc[-2, 0], perf2_2_.iloc[-2, 1], perf2_3_.iloc[-2, 0], perf2_3_.iloc[-2, 1], perf2_4_.iloc[-2, 0], perf2_4_.iloc[-2, 1]] perf22 = [perf2_1_.iloc[-1, 0], perf2_1_.iloc[-1, 1], perf2_2_.iloc[-1, 0], perf2_2_.iloc[-1, 1], perf2_3_.iloc[-1, 0], perf2_3_.iloc[-1, 1], perf2_4_.iloc[-1, 0], perf2_4_.iloc[-1, 1]] perf3_1 = momentum_strategy(bond_data, rank3_port, holding_range[0][0], holding_range[0][1], TMT=TMT) perf3_1_ = perf3_1.iloc[:, 0:2].add(1).pow(12/(holding_range[0][1] - holding_range[0][0] + 1)).subtract(1) perf3_2 = momentum_strategy(bond_data, rank3_port, holding_range[1][0], holding_range[1][1], TMT=TMT) perf3_2_ = perf3_2.iloc[:, 0:2].add(1).pow(12/(holding_range[1][1] - holding_range[1][0] + 1)).subtract(1) perf3_3 = momentum_strategy(bond_data, rank3_port, holding_range[2][0], holding_range[2][1], TMT=TMT) perf3_3_ = perf3_3.iloc[:, 0:2].add(1).pow(12/(holding_range[2][1] - holding_range[2][0] + 1)).subtract(1) perf3_4 = momentum_strategy(bond_data, rank3_port, holding_range[3][0], holding_range[3][1], TMT=TMT) perf3_4_ = perf3_4.iloc[:, 0:2].add(1).pow(12/(holding_range[3][1] - holding_range[3][0] + 1)).subtract(1) perf31 = [perf3_1_.iloc[-2, 0], perf3_1_.iloc[-2, 1], perf3_2_.iloc[-2, 0], perf3_2_.iloc[-2, 1], perf3_3_.iloc[-2, 0], perf3_3_.iloc[-2, 1], perf3_4_.iloc[-2, 0], perf3_4_.iloc[-2, 1]] perf32 = [perf3_1_.iloc[-1, 0], perf3_1_.iloc[-1, 1], perf3_2_.iloc[-1, 0], perf3_2_.iloc[-1, 1], perf3_3_.iloc[-1, 0], perf3_3_.iloc[-1, 1], perf3_4_.iloc[-1, 0], perf3_4_.iloc[-1, 1]] perf = [perf11, perf12, perf21, perf22, perf31, perf32] table_index = [] for i in avg_period: for j in ['P01-P10', 'P01-P05']: table_index.append('Average {0}-month return:{1}'.format(i, j)) column_name = ['Value {0}'.format(holding_range[0]), 'Equal {0}'.format(holding_range[0]), 'Value {0}'.format(holding_range[1]) , 'Equal {0}'.format(holding_range[1]), 'Value {0}'.format(holding_range[2]), 'Equal {0}'.format(holding_range[2]), 'Value {0}'.format(holding_range[3]), 'Equal {0}'.format(holding_range[3])] comparison_table = pd.DataFrame(data = perf,index = table_index, columns = column_name) return(comparison_table)

Generating performance comparison table function

def comparison_table(com_perf, universe, strat_type = 0): fig, ax = plt.subplots() fig.set_figheight(5) fig.set_figwidth(12) plt.subplots_adjust(left=0.5, top=0.8) strat_name = np.where(strat_type == 0, 'Total', 'Residual') ax.set_title('{0} Momentum Spillover Annualized Return Comparison Performance Table - {1}'.format(strat_name, universe), fontsize=15, weight='bold') ax.xaxis.set_visible(False) ax.yaxis.set_visible(False) ax.axis('off') ax.axis('tight') data = com_perf.round(5).values columns = com_perf.columns rows = com_perf.index rcolors = plt.cm.BuPu(np.linspace(0, 0.5, len(rows))) ccolors = plt.cm.BuPu(np.linspace(0, 0.5, len(columns)))[::-1] clcolors = [] for i in range(0, 6): maxval = max(data[i]) maxidx = [index for index, val in enumerate(data[i]) if val == maxval] clist = ["w","w","w","w","w","w","w","w"] clist[maxidx[0]] = "tomato" clcolors.append(clist) plt.table(cellText = data, cellColours=clcolors, rowLabels=rows, colLabels=columns, rowColours=rcolors, colColours=ccolors, loc='center') fig.tight_layout()

Yearly holding comparision function

def comparison_holding(equity_data, bond_data, form_date, num_month, strat_type = 0, TMT = 2): start_month = 1 end_month = 12 rank_port = rank_port_decile(equity_data, form_date, num_month, strat_type) bond_data = pd.merge(bond_data, rank_port[['GROUP']], how = 'left', left_on = 'company_symbol', right_index=True) bond_data = bond_data[bond_data.TMT >= TMT] bond_data = bond_data.dropna(subset=['RET_EOM', 'DURATION']) bond_average_month = bond_data.groupby(['month', 'company_symbol', 'GROUP']).agg({'PRICE_EOM':'mean','RET_EOM':'mean', 'RATING_NUM':'mean', 'DURATION':'mean'}) bond_month = pd.DataFrame(columns = ['month', 'ticker', 'port_group', 'price', 'return', 'avg_rating', 'duration']) bond_month['month'] = [i[0] for i in bond_average_month.index] bond_month['ticker'] = [i[1] for i in bond_average_month.index] bond_month['port_group'] = [i[2] for i in bond_average_month.index] bond_month['price'] = bond_average_month['PRICE_EOM'].values bond_month['return'] = bond_average_month['RET_EOM'].values bond_month['avg_rating'] = bond_average_month['RATING_NUM'].values bond_month['duration'] = bond_average_month['DURATION'].values bond_hold = bond_month[(bond_month.month >= start_month) & (bond_month.month <= end_month)] bond_sum_price = bond_hold.groupby(['port_group', 'month']).agg({'price':'sum'}) bond_sum_price['port_group'] = [i[0] for i in bond_sum_price.index] bond_sum_price['month'] = [i[1] for i in bond_sum_price.index] bond_hold_value = pd.merge(bond_hold, bond_sum_price, how = 'left', left_on = ['port_group', 'month'], right_on=['port_group', 'month']) bond_hold_value.columns = ['month', 'ticker', 'port_group', 'price', 'return', 'avg_rating', 'duration','port_total_weight'] bond_hold_value['port_weight'] = bond_hold_value['price'].divide(bond_hold_value['port_total_weight']) bond_hold_value['value_return'] = bond_hold_value['return'].mul(bond_hold_value['port_weight']) bond_hold_value['value_price'] = bond_hold_value['price'].mul(bond_hold_value['port_weight']) bond_hold_value['value_rating'] = bond_hold_value['avg_rating'].mul(bond_hold_value['port_weight']) bond_hold_value['value_duration'] = bond_hold_value['duration'].mul(bond_hold_value['port_weight']) # Value portfolio bond_hold_value_ = bond_hold_value.groupby(['port_group', 'month']).agg({'value_return':'sum', 'value_price':'sum', 'value_duration':'sum', 'value_rating':'sum'}) bond_hold_value_['port_group'] = [i[0] for i in bond_hold_value_.index] bond_hold_value_['month'] = [i[1] for i in bond_hold_value_.index] bond_hold_value_['value_return'] = bond_hold_value_['value_return'].add(1) bond_hold_value_['cumulative_return'] = bond_hold_value_.groupby(['port_group']).cumprod()['value_return'] bond_hold_value_['cumulative_return'] = bond_hold_value_['cumulative_return'] .subtract(1) bond_hold_value_['value_return'] = bond_hold_value_['value_return'] .subtract(1) #Equal portfolio bond_hold_value___ = bond_hold_value #bond_hold_value___['return'] = bond_hold_value___['return'].add(1) bond_hold_equal = bond_hold_value___.groupby(['port_group', 'month']).agg({'return':'mean', 'price':'mean', 'duration':'mean', 'avg_rating':'mean'}) bond_hold_equal['port_group'] = [i[0] for i in bond_hold_equal.index] bond_hold_equal['month'] = [i[1] for i in bond_hold_equal.index] bond_hold_equal['return'] = bond_hold_equal['return'].add(1) bond_hold_equal['cumulative_return'] = bond_hold_equal.groupby(['port_group']).cumprod()['return'] bond_hold_equal['cumulative_return'] = bond_hold_equal['cumulative_return'] .subtract(1) bond_hold_equal['return'] = bond_hold_equal['return'] .subtract(1) col_name = [] port = ['P01', 'P02', 'P03', 'P04', 'P05', 'P06', 'P07', 'P08', 'P09', 'P10'] for i in ['value', 'equal']: for j in port: col_name.append('{0}_{1}'.format(i, j)) result = pd.DataFrame(data = 0, columns = col_name, index = [i for i in range(1, 13)]) for i in port: result.ix[:, '{0}_{1}'.format('value', i)] = bond_hold_value_[bond_hold_value_['port_group'] == i]['cumulative_return'].values for i in port: result.ix[:, '{0}_{1}'.format('equal', i)] = bond_hold_equal[ bond_hold_equal['port_group'] == i]['cumulative_return'].values return(result)

Yearly holding comparision visualization function

def comparison_holding_plot(com_hold, universe, avg_period, TMT, strat_type = 0): style.use('seaborn') fig = plt.figure(figsize=(15, 15)) strat_name = np.where(strat_type == 0, 'Total', 'Residual') fig.suptitle('{0} Momentum Spillover Holding Comparison - {1}'.format(strat_name, universe), fontsize=20, fontweight='bold') plt.figtext(0.5,0.95, "Average {0}-month equity returns \n Time-to-maturity over {1}".format(avg_period, TMT), ha="center", va="top", fontsize=14, color="black") ax1 = plt.subplot2grid((30, 22), (0, 0), rowspan=12, colspan=22) ax1.plot(com_hold.iloc[:, 0], 'o-', label="{0}".format(com_hold.columns[0]), color='lightsalmon') ax1.plot(com_hold.iloc[:, 4], 'o-', label="{0}".format(com_hold.columns[4]), color='greenyellow') ax1.plot(com_hold.iloc[:, 9], 'o-', label="{0}".format(com_hold.columns[9]), color='skyblue') for i in range(0, 12): ax1.text(com_hold.index[i],1.02*com_hold.iloc[i, 0], '{0:.5f}'.format(com_hold.iloc[i, 0]), ha='center', va='bottom') ax1.text(com_hold.index[i],1.02*com_hold.iloc[i, 4], '{0:.5f}'.format(com_hold.iloc[i, 4]), ha='center', va='bottom') ax1.text(com_hold.index[i],1.02*com_hold.iloc[i, 9], '{0:.5f}'.format(com_hold.iloc[i, 9]), ha='center', va='bottom') ax1.set_title('Cumulative Returns of Value Wighted Portfolios Per Year', fontsize=15, weight='bold') ax1.set_ylabel('Cumulative return') ax1.legend() ax2 = plt.subplot2grid((30, 22), (14, 0), rowspan=12, colspan=22, sharex=ax1) ax2.plot(com_hold.iloc[:, 10], 'o-', label="{0}".format(com_hold.columns[10]), color='lightsalmon') ax2.plot(com_hold.iloc[:, 14], 'o-', label="{0}".format(com_hold.columns[14]), color='greenyellow') ax2.plot(com_hold.iloc[:, 19], 'o-', label="{0}".format(com_hold.columns[19]), color='skyblue') for i in range(0, 12): ax2.text(com_hold.index[i],1.02*com_hold.iloc[i, 10], '{0:.5f}'.format(com_hold.iloc[i, 10]), ha='center', va='bottom') ax2.text(com_hold.index[i],1.02*com_hold.iloc[i, 14], '{0:.5f}'.format(com_hold.iloc[i, 14]), ha='center', va='bottom') ax2.text(com_hold.index[i],1.02*com_hold.iloc[i, 19], '{0:.5f}'.format(com_hold.iloc[i, 19]), ha='center', va='bottom') ax2.set_title('Cumulative Returns of Equal Wighted Portfolios Per Year', fontsize=15, weight='bold') ax2.set_ylabel('Cumulative return') ax2.set_xlabel('Months') ax2.legend()

Backtesting

Import equity data step

equity_data_IG = pd.read_csv('CRSP 2012-2013 - IG.csv') equity_data_IG.date = pd.to_datetime(equity_data_IG.date, format = '%Y%m%d') equity_data_BBB = pd.read_csv('CRSP 2012-2013 - BBB.csv') equity_data_BBB.date = pd.to_datetime(equity_data_BBB.date, format = '%Y%m%d') equity_data_HY = pd.read_csv('CRSP 2012-2013 - HY.csv') equity_data_HY.date = pd.to_datetime(equity_data_HY.date, format = '%Y%m%d')

Import bond data step

bond_data_IG = pd.read_csv('WRDS bond return 2013 - IG.csv') bond_data_IG.DATE = pd.to_datetime(bond_data_IG.DATE, format = '%d-%b-%y') bond_data_IG['month'] = [i.month for i in bond_data_IG.DATE] bond_data_IG.YIELD = bond_data_IG.YIELD.str.replace('%', '').astype('float').divide(100.0) bond_data_IG.RET_EOM = bond_data_IG.RET_EOM.str.replace('%', '').astype('float').divide(100.0) bond_data_BBB = pd.read_csv('WRDS bond return 2013 - BBB.csv') bond_data_BBB.DATE = pd.to_datetime(bond_data_BBB.DATE, format = '%d-%b-%y') bond_data_BBB['month'] = [i.month for i in bond_data_BBB.DATE] bond_data_BBB.YIELD = bond_data_BBB.YIELD.str.replace('%', '').astype('float').divide(100.0) bond_data_BBB.RET_EOM = bond_data_BBB.RET_EOM.str.replace('%', '').astype('float').divide(100.0) bond_data_HY = pd.read_csv('WRDS bond return 2013 - HY.csv') bond_data_HY.DATE = pd.to_datetime(bond_data_HY.DATE, format = '%d-%b-%y') bond_data_HY['month'] = [i.month for i in bond_data_HY.DATE] bond_data_HY.YIELD = bond_data_HY.YIELD.str.replace('%', '').astype('float').divide(100.0) bond_data_HY.RET_EOM = bond_data_HY.RET_EOM.str.replace('%', '').astype('float').divide(100.0)

Select formation date of identifying systematic strategy

formation_date = '2012-12-31'

Backtesting on IG

Select time-to-maturity of bonds and data universe

TMT_IG = 28 universe_IG = 'IG'

Execute Cumulative Return Holding Per Year Comparison function on IG universe

avg_period_IG = 6 com_hold_IG = comparison_holding(equity_data_IG, bond_data_IG, formation_date, avg_period_IG, TMT = TMT_IG) comparison_holding_plot(com_hold_IG, universe_IG, avg_period_IG, TMT = TMT_IG)
Image in a Jupyter notebook

Observe that the systematic pattern could be in the first quarter after formation date. Hence, select the systematic ranges of bond holding to test as below.

holding_range_IG = [(1, 1), (1, 2), (2, 4), (6, 12)] equity_range = (3, 6, 12)
s_type_IG = 0 com_perf_IG = comparison_performance(equity_data_IG, bond_data_IG, holding_range_IG, formation_date, equity_range, TMT=TMT_IG, strat_type = s_type_IG) comparison_table(com_perf_IG, universe_IG, s_type_IG)
Image in a Jupyter notebook

Using 6-month equity return and holding bond porfolios from 1 to 1 with time-to-maturity of bonds over 28 years shows the highest annualized return and momentum effect. We will see more detail of this systematic strategy.

avg_period_IG = 6 rank_port_IG = rank_port_decile(equity_data_IG, formation_date, avg_period_IG, s_type_IG) rank_table(rank_port_IG, universe_IG, s_type_IG) hfrom_IG = 1 hend_IG = 1 tmt_IG = 28 perf_IG = momentum_strategy(bond_data_IG, rank_port_IG, hfrom_IG, hend_IG, TMT=TMT_IG) m_performance_table(perf_IG, universe_IG, s_type_IG) performance_plot(perf_IG, avg_period_IG, hfrom_IG, hend_IG, tmt_IG, universe_IG, s_type_IG)
Image in a Jupyter notebookImage in a Jupyter notebookImage in a Jupyter notebook

The performance of this systematic strategy on IG universe shows fair momentum with gaining around 2% return.

Backtesting on BBB

Select time-to-maturity of bonds and data universe

TMT_BBB = 5 universe_BBB = 'BBB'

Execute Cumulative Return Holding Per Year Comparison function on BBB universe

avg_period_BBB = 3 com_hold_BBB = comparison_holding(equity_data_BBB, bond_data_BBB, formation_date, avg_period_BBB, strat_type = 1, TMT = TMT_BBB) comparison_holding_plot(com_hold_BBB, universe_BBB, avg_period_BBB, TMT = TMT_BBB, strat_type = 1)
Image in a Jupyter notebook

Observe that the systematic ranges could be in the first quarter after formation date. Hence, select the systematic ranges of bond holding to test as below.

holding_range_BBB = [(1, 1), (2, 2), (1, 3), (12, 12)]
s_type_BBB = 1 com_perf_BBB = comparison_performance(equity_data_BBB, bond_data_BBB, holding_range_BBB, formation_date, equity_range, TMT=TMT_BBB, strat_type = s_type_BBB) comparison_table(com_perf_BBB, universe_BBB, s_type_BBB)
Image in a Jupyter notebook

Using 3-month equity return and holding bond porfolios from 1 to 1 with time-to-maturity of bonds over 5 years shows the highest annualized return and momentum effect. We will see more detail of this systematic strategy.

avg_period_BBB = 3 rank_port_BBB = rank_port_decile(equity_data_BBB, formation_date, avg_period_BBB, s_type_BBB) rank_table(rank_port_BBB, universe_BBB, s_type_BBB) hfrom_BBB = 1 hend_BBB = 1 tmt_BBB = 5 perf_BBB = momentum_strategy(bond_data_BBB, rank_port_BBB, hfrom_BBB, hend_BBB, TMT=TMT_BBB) m_performance_table(perf_BBB, universe_BBB, s_type_BBB) performance_plot(perf_BBB, avg_period_BBB, hfrom_BBB, hend_BBB, tmt_BBB, universe_BBB, s_type_BBB)
Image in a Jupyter notebookImage in a Jupyter notebookImage in a Jupyter notebook

The performance of this systematic strategy on BBB universe shows good momentum with gaining around 1.2% return.

Backtesting on HY

Select time-to-maturity of bonds and data universe

TMT_HY = 1 universe_HY = 'HY'

Execute Cumulative Return Holding Per Year Comparison function on HY universe

avg_period_HY = 3 com_hold_HY = comparison_holding(equity_data_HY, bond_data_HY, formation_date, avg_period_HY, strat_type = 1, TMT = TMT_HY) comparison_holding_plot(com_hold_HY, universe_HY, avg_period_HY,TMT = TMT_HY, strat_type = 1)
Image in a Jupyter notebook

Observe that the systematic ranges could be in the first,second and after sixth month of formation date. Hence, select the systematic ranges of bond holding to test as below.

holding_range_HY = [(1, 1), (1, 2), (6, 12), (12, 12)] equity_range_HY = (1, 3, 6)
s_type_HY = 1 com_perf_HY = comparison_performance(equity_data_HY, bond_data_HY, holding_range_HY, formation_date, equity_range_HY, TMT=TMT_HY, strat_type = s_type_HY) comparison_table(com_perf_HY, universe_HY, s_type_HY)
Image in a Jupyter notebook

Using 1-month equity return and holding bond porfolios from 6 to 12 with time-to-maturity of bonds over 1 year shows the highest annualized return and momentum effect. We will see more detail of this systematic strategy.

avg_period_HY = 1 rank_port_HY = rank_port_decile(equity_data_HY, formation_date, avg_period_HY, s_type_HY) rank_table(rank_port_BBB, universe_HY, s_type_HY) hfrom_HY = 6 hend_HY = 12 tmt_HY = 1 perf_HY = momentum_strategy(bond_data_HY, rank_port_HY, hfrom_HY, hend_HY, TMT=TMT_HY) m_performance_table(perf_HY, universe_HY, s_type_HY) performance_plot(perf_HY, avg_period_HY, hfrom_HY, hend_HY, tmt_HY, universe_HY, s_type_HY)
Image in a Jupyter notebookImage in a Jupyter notebookImage in a Jupyter notebook

The performance of this systematic strategy on HY universe shows good momentum with earning around 3.5%-4.2% return.