Solution - Reversals#

Thanks to Anand Nakhate

1. Investigate Reversals#

Data#

Find monthly data on the long-term reversal factor.

  • LT Reversal (excess returns)

  • deciles (total returns)

1.1.#

Report summary statistics for the reversal factor.

1.2.#

Report the summary statistics for the low and high decile from deciles (total returns).

Do you see evidence of differential performance?

Plot the cumulative return of each.

1.3.#

Does your answer to 1.2 change if we focus just on data since Jan 2001?

1.4.#

Staying with the later subsample, do you see evidence that the results are significantly impacted by small stocks?

Use size_sorts (total returns) to compare BIG LoPRIOR to BIG HiPRIOR.


1.#

KEY = 'REV'
TAG_LONG = 'Lo'
TAG_SHORT = 'Hi'

YR_CUTOFF = '2001'
YR_LAST_PRIOR_CUTOFF = '2000'

periods = [['1927', '2025'], ['1927', YR_LAST_PRIOR_CUTOFF], [YR_CUTOFF, '2025']]
FILE_PATH = '../data/reversal_data.xlsx'

SHEET_factors = 'FF factors (excess returns)'
SHEET_momentum = 'LT Reversal (excess returns)'
SHEET_deciles = 'deciles (total returns)'
SHEET_size_sorts = 'size_sorts (total returns)'
SHEET_rf = 'risk-free rate'

ANN_SCALE = 12
from functools import partial
import pandas as pd
import numpy as np
import seaborn as sns
import statsmodels.api as sm
import matplotlib.pyplot as plt

plt.style.use('ggplot')

import sys
if '../cmds/' not in sys.path:
    sys.path.append('../cmds/')
from portfolio_management_helper import *

import warnings
warnings.filterwarnings("ignore")
raw_data = pd.read_excel(FILE_PATH,sheet_name = None)
ff_factors = raw_data[SHEET_factors].set_index('Date')
momentum = raw_data[SHEET_momentum].set_index('Date')
mom_deciles = raw_data[SHEET_deciles].set_index('Date')
tercile_port = raw_data[SHEET_size_sorts].set_index('Date')
rf = raw_data[SHEET_rf].set_index('Date')

ff_factors[KEY] = momentum[KEY]

1.1.#

summary_col_names = ['Annualized Mean','Annualized Vol','Annualized Sharpe','Skewness']
res = []
for period in periods:
    temp = momentum.loc[period[0]:period[1]]
    temp_ff = ff_factors.loc[period[0]:period[1]]
    summary = calc_summary_statistics(temp, annual_factor=ANN_SCALE, provided_excess_returns=True)[summary_col_names]
    summary['mkt_corr'] = temp_ff.corr().loc['MKT',[KEY]]
    summary['val_corr'] = temp_ff.corr().loc['HML',[KEY]]
    summary = summary.T.iloc[:,0].rename(f'{period[0]} - {period[1]}')
    res.append(summary)
summary  = pd.concat(res, axis=1).T
summary.style.format('{:.1%}')
  Annualized Mean Annualized Vol Annualized Sharpe Skewness mkt_corr val_corr
1927 - 2025 3.4% 11.9% 28.1% 289.5% 23.5% 64.8%
1927 - 2000 4.4% 12.5% 34.8% 325.4% 26.8% 63.8%
2001 - 2025 0.5% 10.0% 4.9% 56.9% 9.2% 68.8%

1.2.#

mom_long = (tercile_port[f'BIG {TAG_LONG}PRIOR'] + tercile_port[f'SMALL {TAG_LONG}PRIOR'])/2 - rf['RF']
mom_names = ['long_and_short','long_only']

temp = ff_factors.copy().rename(columns={f'{KEY}':'long_and_short'})
temp['long_only']  = mom_long
summary =calc_summary_statistics(
    temp.loc[YR_CUTOFF:, mom_names], annual_factor=ANN_SCALE, provided_excess_returns=True,
    keep_columns=summary_col_names
)
summary['mkt_corr'] = temp.loc[YR_CUTOFF:].corr().loc['MKT', mom_names]
summary['val_corr'] = temp.loc[YR_CUTOFF:].corr().loc['HML', mom_names]
summary.style.format('{:.1%}')
  Annualized Mean Annualized Vol Annualized Sharpe Skewness mkt_corr val_corr
long_and_short 0.5% 10.0% 4.9% 56.9% 9.2% 68.8%
long_only 10.4% 21.0% 49.4% -31.9% 88.7% 32.9%

1.3.#

fig, ax = plt.subplots(figsize=(8,5))
(100 * ((1 + temp[mom_names + ['MKT']].loc[YR_CUTOFF:]).cumprod() -1 )).plot(ax=ax)
ax.set_ylabel('Cumulative Returns (%)')
ax.set_title('Cumulative Returns')
Text(0.5, 1.0, 'Cumulative Returns')
../_images/f939acd312c44e7a39ecdf4ea09f755b6a8230ee0ed4e7578913cc3b9d33b75b.png

1.4.#

ls_1decile = mom_deciles[f'{TAG_LONG} PRIOR'] - mom_deciles[f'{TAG_SHORT} PRIOR']
ls_3decile = mom_deciles.iloc[:,-3:].mean(axis = 1) - mom_deciles.iloc[:,:3].mean(axis = 1)
ls_5decile = mom_deciles.iloc[:,-5:].mean(axis = 1) - mom_deciles.iloc[:,:5].mean(axis = 1)

ls_names = ['mom_D1','mom_D3','mom_D5']
temp = ff_factors.copy()
temp[ls_names] = pd.concat([ls_1decile, ls_3decile, ls_5decile], axis=1)
summary = calc_summary_statistics(temp.loc[YR_CUTOFF:, ls_names + [KEY]], annual_factor=ANN_SCALE, provided_excess_returns=True)[summary_col_names]
summary['mkt_corr'] = temp.corr().loc['MKT', ls_names + [KEY]]
summary['val_corr'] = temp.corr().loc['HML', ls_names + [KEY]]
summary.style.format('{:.1%}')
  Annualized Mean Annualized Vol Annualized Sharpe Skewness mkt_corr val_corr
mom_D1 -1.2% 20.5% -5.9% 26.8% 20.0% 60.6%
mom_D3 0.1% 12.7% 0.5% -36.0% -24.7% -62.3%
mom_D5 0.7% 8.8% 8.2% -19.7% -24.8% -61.0%
REV 0.5% 10.0% 4.9% 56.9% 23.5% 64.8%
mom_small = tercile_port[f'SMALL {TAG_LONG}PRIOR'] - tercile_port[f'SMALL {TAG_SHORT}PRIOR']
mom_large = tercile_port[f'BIG {TAG_LONG}PRIOR'] - tercile_port[f'BIG {TAG_SHORT}PRIOR']

mom_size_name = ['Small_stocks','Big_stocks']
temp = ff_factors.copy()
temp[mom_size_name] = pd.concat([mom_small, mom_large], axis=1)
summary = calc_summary_statistics(temp.loc[YR_CUTOFF:, [KEY] + mom_size_name], annual_factor=ANN_SCALE, provided_excess_returns=True)[summary_col_names]
summary['mkt_corr'] = temp.corr().loc['MKT', [KEY] + mom_size_name]
summary['val_corr'] = temp.corr().loc['HML', [KEY] + mom_size_name]
summary.rename({'UMD':'All_stocks'}).style.format('{:.1%}')
  Annualized Mean Annualized Vol Annualized Sharpe Skewness mkt_corr val_corr
REV 0.5% 10.0% 4.9% 56.9% 23.5% 64.8%
Small_stocks 1.4% 9.4% 14.8% 66.9% 24.7% 53.5%
Big_stocks -0.4% 12.6% -3.4% 73.7% 18.5% 63.5%
from cmds.plot_tools import plot_corr_matrix
temp = pd.concat([ff_factors[KEY], mom_long, mom_small, mom_large], axis=1)
temp.columns = ['long-short','long','small','big']

display(plot_corr_matrix(ff_factors,triangle='lower'));
display(plot_corr_matrix(temp,triangle='lower'));
(<Figure size 800x800 with 2 Axes>,
 <Axes: title={'center': 'Correlation matrix (lower triangle)'}>)
(<Figure size 800x800 with 2 Axes>,
 <Axes: title={'center': 'Correlation matrix (lower triangle)'}>)
../_images/284f652ef18144a93b22f9dda7df36b7a4d06429eb7d1901bbc8b320278b37f2.png ../_images/7770f03225ef8917960d2a83df4861675af2d21ba8fe8615bb189fce6336cdcd.png
display(plot_corr_matrix(pd.concat([ff_factors,temp.iloc[:,1:]],axis=1),triangle='lower'));
(<Figure size 800x800 with 2 Axes>,
 <Axes: title={'center': 'Correlation matrix (lower triangle)'}>)
../_images/9d35ac653bba57063d854897ba28d8b3dc9b38ff496f96136f0bd75770d38cdc.png