Usage


Toolkit usage

Toolkit usage

This page contains a tutorial on how to implement various basic asset pricing techniques using the Toolkit. These include univariate sorts, bivariate sorts, Fama-MacBeth regressions, and accounting for transaction costs.
Let’s start by setting the path, assuming we have just opened a new instance of MATLAB:
clear
clc
 
restoredefaultpath; % Start with the default path
fileName = matlab.desktop.editor.getActiveFilename; % Path to the script
dirSepIndex = strfind(fileName, filesep); % Index of directory separators
fullPath = fileName(1:dirSepIndex(end)); % Path to the package
 
% Add the relevant folders (with subfolders) to the path
addpath(genpath([fullPath, ‘Data’]))
addpath(genpath([fullPath, ‘Functions’]))
cd(fullPath)
 
% Check if Scratch exists, make it if not
if ~exist([fullPath, ‘Scratch’], ‘dir’)
mkdir([fullPath, ‘Scratch’]);
addpath([fullPath, ‘Scratch’]);
else
addpath([fullPath, ‘Scratch’]);
end

Univariate sorts

Univariate sorts are the bread and butter in the anomalies literature. They are a non-parametric way to look at the relation between a predictive signal and future returns. To show how we do these using the Toolkit, let’s start by loading a few variables:
clear
clc
 
load ret
load me
load dates
load pdates
load ff
load NYSE
load exchcd
load tcosts
We run univariate sorts using two functions:
  1. makeUnivSortInd – creates a portfolio index martix
  2. runUnivSort – does everything else
Both of these functions have plenty of optional features, which we’ll showcase next.

Assigning stocks to portfolio

First, let’s take a look at the help file for makeUnivSortInd():
The function takes two required arguments – signal matrix and number of portfolios/breakpoints. The optional arguments allow for determining the breakpoints in various ways (e.g., using only NYSE stocks, ensuring same market cap across all portfolios, etc.).
Let’s start with the most basic case. We’ll do a quintile size sort, that is, in each month, we’ll sort stocks into five portfolios based on the negative of their market capitalization (so that we go long small caps and short large caps).
% Quintile sort on size, name breaks by default
ind = makeUnivSortInd(-me, 5);
The runUnivSort() function with no optional arguments will run the portfolio sort using value-weighting, print the results, and plot a figure with useful diagnostics (more on that function below). We can observe that the average returns increase with the negative of lagged market capitalization, which is the famed size effect. Small caps outperform large caps. The long/short (L/S) portfolio row shows the performance of a portfolio long stocks in portfolio five and short stocks in portfolio one.
We can also see that the loadings on the SMB factor below increase monotonically. That is, portfolio one (which consists of the largest quintile of stocks in each month) has a negative and very significant loading on SMB, and the loading increase monotonically up to portfolio five.
res1 = runUnivSort(ret, ind, dates, me);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.675 0.014 0.988 -0.092 -0.026 0.004
[ 4.43] [ 2.80] [975.03] [-57.27] [-17.39] [ 3.22]
2 0.850 0.015 1.061 0.553 0.058 -0.045
[ 4.30] [ 0.39] [134.84] [44.21] [ 4.89] [-4.92]
3 0.856 -0.027 1.036 0.842 0.164 -0.093
[ 3.95] [-0.62] [117.63] [60.10] [12.35] [-9.07]
4 0.867 -0.044 0.992 1.078 0.333 -0.160
[ 3.63] [-0.73] [81.67] [55.82] [18.22] [-11.29]
5 1.185 0.250 0.934 1.427 0.528 -0.265
[ 4.20] [ 2.33] [43.30] [41.63] [16.27] [-10.50]
L/S 0.503 0.236 -0.054 1.520 0.554 -0.269
[ 2.52] [ 2.19] [-2.49] [44.07] [16.99] [-10.59]
—————————————————————————-
Instead of the number of portoflios, the second argument of makeUnivSortInd() can also be a vector of numbers between 0 and 100, in which case it should indicate the percentile breakpoints used to determine the portfolios. For example, passing on [30 70] would create a Fama-French-style tertile sort where the first portfolio will include stocks with market capitalizations below the 30th percentile, the second portoflio will include stocks with market capitalizations greater than or equal to the 30th percentile, but smaller than the 70th percentile, and the third portfolio will include stocks with market capitalizations greater than or equal to the 70th percentile.
% Tertile Fama-French-style sort, name breaks by default
ind = makeUnivSortInd(-me, [30 70]);
res2 = runUnivSort(ret, ind, dates, me);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.683 0.012 0.993 -0.063 -0.019 0.003
[ 4.43] [ 3.61] [1502.31] [-60.15] [-19.51] [ 3.39]
2 0.852 -0.024 1.042 0.799 0.139 -0.080
[ 4.02] [-0.66] [143.36] [69.15] [12.69] [-9.43]
3 1.012 0.076 0.963 1.272 0.461 -0.217
[ 3.86] [ 0.91] [57.43] [47.72] [18.28] [-11.07]
L/S 0.318 0.064 -0.030 1.335 0.480 -0.219
[ 1.87] [ 0.75] [-1.79] [49.40] [18.79] [-11.05]
—————————————————————————-
The makeUnivSortInd() also takes an optional ‘breaksFilter’ argument, which specifies the stock universe which is to be used for the determination of the portfolio thresholds. For example, if we want a decile sort using NYSE breakpoints as in Table 3 of Novy-Marx and Velikov (2016):
% Decile sort, NYSE breaks
ind = makeUnivSortInd(-me, 10, ‘breaksFilter’, NYSE);
res3 = runUnivSort(ret, ind, dates, me);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.645 0.003 0.979 -0.194 -0.034 0.018
[ 4.34] [ 0.23] [326.12] [-40.58] [-7.52] [ 5.24]
2 0.744 0.025 1.015 0.088 0.078 -0.041
[ 4.39] [ 0.82] [168.71] [ 9.23] [ 8.59] [-5.89]
3 0.841 0.056 1.071 0.209 0.086 -0.040
[ 4.57] [ 1.48] [139.69] [17.16] [ 7.42] [-4.45]
4 0.820 -0.040 1.069 0.425 0.140 -0.015
[ 4.24] [-1.04] [137.12] [34.29] [11.98] [-1.60]
5 0.862 -0.029 1.077 0.524 0.176 -0.028
[ 4.29] [-0.76] [141.50] [43.31] [15.34] [-3.20]
6 0.904 0.006 1.070 0.729 0.172 -0.078
[ 4.24] [ 0.17] [154.06] [66.04] [16.51] [-9.56]
7 0.957 0.005 1.077 0.888 0.249 -0.081
[ 4.23] [ 0.15] [146.07] [75.81] [22.44] [-9.40]
8 0.979 -0.033 1.093 1.065 0.356 -0.122
[ 3.94] [-0.65] [105.52] [64.67] [22.86] [-10.06]
9 1.012 -0.012 1.056 1.251 0.448 -0.160
[ 3.85] [-0.20] [89.14] [66.41] [25.11] [-11.57]
10 1.342 0.259 1.008 1.564 0.714 -0.259
[ 4.31] [ 2.21] [42.76] [41.75] [20.16] [-9.41]
L/S 0.687 0.256 0.029 1.758 0.748 -0.277
[ 2.93] [ 2.25] [ 1.26] [48.25] [21.71] [-10.36]
—————————————————————————-
The first optional argument (‘breaksFilter’) works without specifying the ‘Name’ component too:
% We don’t need to specify the ‘Name’ component for the first optional
% argument (‘breaksFilter’), so the following would also work
ind = makeUnivSortInd(-me, 10, NYSE);
res4 = runUnivSort(ret, ind, dates, me);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.645 0.003 0.979 -0.194 -0.034 0.018
[ 4.34] [ 0.23] [326.12] [-40.58] [-7.52] [ 5.24]
2 0.744 0.025 1.015 0.088 0.078 -0.041
[ 4.39] [ 0.82] [168.71] [ 9.23] [ 8.59] [-5.89]
3 0.841 0.056 1.071 0.209 0.086 -0.040
[ 4.57] [ 1.48] [139.69] [17.16] [ 7.42] [-4.45]
4 0.820 -0.040 1.069 0.425 0.140 -0.015
[ 4.24] [-1.04] [137.12] [34.29] [11.98] [-1.60]
5 0.862 -0.029 1.077 0.524 0.176 -0.028
[ 4.29] [-0.76] [141.50] [43.31] [15.34] [-3.20]
6 0.904 0.006 1.070 0.729 0.172 -0.078
[ 4.24] [ 0.17] [154.06] [66.04] [16.51] [-9.56]
7 0.957 0.005 1.077 0.888 0.249 -0.081
[ 4.23] [ 0.15] [146.07] [75.81] [22.44] [-9.40]
8 0.979 -0.033 1.093 1.065 0.356 -0.122
[ 3.94] [-0.65] [105.52] [64.67] [22.86] [-10.06]
9 1.012 -0.012 1.056 1.251 0.448 -0.160
[ 3.85] [-0.20] [89.14] [66.41] [25.11] [-11.57]
10 1.342 0.259 1.008 1.564 0.714 -0.259
[ 4.31] [ 2.21] [42.76] [41.75] [20.16] [-9.41]
L/S 0.687 0.256 0.029 1.758 0.748 -0.277
[ 2.93] [ 2.25] [ 1.26] [48.25] [21.71] [-10.36]
—————————————————————————-
As another example, we can do NASDAQ breaks (or any other filter really):
% Decile sort, NASDAQ breaks (NASDAQ exchcd is 3, starts in 1973)
ind = makeUnivSortInd(-me, 10, (exchcd==3));
res5 = runUnivSort(ret, ind, dates, me);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.618 0.014 0.994 -0.075 -0.017 0.006
[ 3.32] [ 4.08] [1237.31] [-64.46] [-14.36] [ 7.68]
2 0.769 -0.025 1.067 0.794 0.154 -0.068
[ 3.07] [-0.60] [111.15] [57.39] [10.76] [-6.98]
3 0.756 -0.021 1.039 0.960 0.163 -0.115
[ 2.90] [-0.45] [97.24] [62.35] [10.25] [-10.63]
4 0.680 -0.102 1.012 1.061 0.241 -0.141
[ 2.53] [-1.56] [67.52] [49.13] [10.79] [-9.33]
5 0.722 -0.062 0.992 1.078 0.310 -0.154
[ 2.67] [-0.77] [53.57] [40.42] [11.24] [-8.22]
6 0.627 -0.093 0.929 1.085 0.287 -0.186
[ 2.33] [-0.94] [40.94] [33.17] [ 8.47] [-8.09]
7 0.609 -0.026 0.872 1.068 0.240 -0.242
[ 2.27] [-0.23] [33.27] [28.29] [ 6.15] [-9.10]
8 0.589 0.013 0.818 1.028 0.199 -0.255
[ 2.18] [ 0.10] [25.57] [22.30] [ 4.16] [-7.86]
9 0.736 0.195 0.782 1.127 0.199 -0.303
[ 2.51] [ 1.12] [19.71] [19.69] [ 3.36] [-7.54]
10 1.758 1.321 0.728 1.238 0.138 -0.419
[ 5.13] [ 5.48] [13.23] [15.62] [ 1.68] [-7.53]
L/S 1.140 1.307 -0.267 1.312 0.155 -0.425
[ 3.90] [ 5.40] [-4.83] [16.50] [ 1.88] [-7.61]
—————————————————————————-
The next optional argument allows us to do market capitalization breaks. That is, this ensures we have the same amount of market capitalization across each portfolio:
% Quintile sort, market-capitalization breaks
ind = makeUnivSortInd(-me, 5,‘portfolioMassInd’, me);
res6 = runUnivSort(ret, ind, dates, me);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.675 0.102 0.927 -0.236 -0.138 0.035
[ 4.59] [ 1.81] [81.27] [-13.03] [-8.06] [ 2.62]
2 0.625 -0.043 0.998 -0.256 -0.008 0.041
[ 4.07] [-1.22] [139.16] [-22.48] [-0.75] [ 4.89]
3 0.618 -0.036 1.000 -0.121 -0.031 -0.006
[ 3.95] [-1.25] [173.99] [-13.19] [-3.61] [-0.96]
4 0.726 0.007 1.009 0.091 0.054 -0.024
[ 4.35] [ 0.24] [174.29] [ 9.92] [ 6.21] [-3.57]
5 0.833 -0.010 1.052 0.526 0.114 -0.045
[ 4.28] [-0.35] [194.16] [61.06] [14.02] [-7.04]
L/S 0.147 -0.112 0.125 0.762 0.253 -0.080
[ 1.26] [-1.52] [ 8.45] [32.32] [11.32] [-4.59]
—————————————————————————-
In this case, it is somewhat extreme, because we are also sorting on size, so we’ll end up with very few stocks in the large-cap portfolio (portfolio 1). To see the number of stocks in each portfolio:
% Skip the long/short portfolio
ptfNumStocks = res6.ptfNumStocks(:,1:end-1);
 
% Plot the number of stocks
figure;
plot(pdates, res6.ptfNumStocks(:, 1:end-1));
legend(‘1’,‘2’,‘3’,‘4’,‘5’);
% Print the average number of stocks
round(mean(res6.ptfNumStocks(:,1:end-1), 1, ‘omitnan’))
ans = 1×5
9 30 82 222 2808

Running the univariate sort

Next, we’ll look at the function that runs everything else for the univariate sort, runUnivSort(). Here is its help file:
The function has four required arguments:
  • return matrix (ret)
  • portfolio index (ind)
  • dates vector (dates)
  • market capitalization matrix (me)
It also has multiple optional arguments. By default, if none of the optional arguments are specified, the function does:
  • value-weighting
  • 1-month holding period
  • Fama-French-Carhart 4-factor model
  • prints the results
  • plots a figure with results
  • does the sort on the full sample
  • includes the long/short portfolio
Here is again the quintile sort on the negative of market capitalization using name breaks and value-weighting:
% Start with just the required arguments (same as res1 above)
ind = makeUnivSortInd(-me, 5);
res7 = runUnivSort(ret, ind, dates, me);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.675 0.014 0.988 -0.092 -0.027 0.004
[ 4.43] [ 2.80] [975.01] [-57.24] [-17.45] [ 3.22]
2 0.850 0.015 1.062 0.553 0.059 -0.045
[ 4.30] [ 0.39] [134.78] [44.15] [ 4.93] [-4.92]
3 0.856 -0.027 1.037 0.841 0.164 -0.093
[ 3.95] [-0.62] [117.44] [59.94] [12.36] [-9.06]
4 0.867 -0.044 0.993 1.078 0.334 -0.160
[ 3.63] [-0.73] [81.68] [55.79] [18.24] [-11.29]
5 1.185 0.251 0.934 1.427 0.527 -0.265
[ 4.20] [ 2.34] [43.30] [41.61] [16.24] [-10.51]
L/S 0.503 0.237 -0.054 1.519 0.554 -0.269
[ 2.52] [ 2.20] [-2.48] [44.05] [16.96] [-10.61]
—————————————————————————-
The output of runUnivSort is a structure with multiple results from the univariate sort. It contains the average returns, alphas, their associated t-stats, Sharpe and information ratios for the five portfolios plus the long/short portfolio. It also has time-series of the portfolio returns, number of stocks, and market capitalizatoins, a structure with factor loadings for all portfolios, as well as identifying information for the type of sort:
res7
res7 = struct with fields:
xret: [6×1 double]
txret: [6×1 double]
alpha: [6×1 double]
talpha: [6×1 double]
sharpe: [6×1 double]
info: [6×1 double]
pret: [1153×6 double]
factorModel: 4
nFactors: 4
factorLoadings: [4×1 struct]
r2: [6×1 double]
resid: [1153×6 double]
w: ‘v’
dates: [1153×1 double]
hperiod: 1
ptfMarketCap: [1153×5 double]
ptfNumStocks: [1153×6 double]
Each of the optional arguments above can be tweaked. For example, the ‘weighting’ optional argument can be set to ‘e’ or ‘E’ to use equal-weighted when calculating the portfolio returns:
% Equal-weighting (we’ll just keep using the ind from above)
res8 = runUnivSort(ret, ind, dates, me, ‘weighting’, ‘e’);
—————————————————————————-
Equally-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.737 0.021 1.013 0.146 0.013 -0.031
[ 4.39] [ 0.84] [200.58] [18.13] [ 1.76] [-5.17]
2 0.849 0.006 1.059 0.596 0.062 -0.046
[ 4.25] [ 0.16] [137.90] [48.85] [ 5.40] [-5.12]
3 0.848 -0.036 1.032 0.856 0.172 -0.097
[ 3.90] [-0.81] [116.00] [60.52] [12.84] [-9.32]
4 0.860 -0.054 0.991 1.092 0.348 -0.167
[ 3.57] [-0.85] [78.06] [54.13] [18.22] [-11.26]
5 1.617 0.682 0.912 1.566 0.606 -0.324
[ 5.31] [ 5.20] [34.55] [37.34] [15.25] [-10.49]
L/S 0.871 0.661 -0.101 1.421 0.592 -0.293
[ 4.11] [ 4.69] [-3.56] [31.49] [13.86] [-8.83]
—————————————————————————-
The ‘holdingPeriod’ optional argument can be set to a longer holding period than one month:
% 2-month holding period
res9 = runUnivSort(ret, ind, dates, me, ‘holdingPeriod’, 2);
—————————————————————————-
Value-weighted portfolio sort, 2-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.676 0.015 0.989 -0.091 -0.027 0.003
[ 4.43] [ 3.07] [1011.07] [-58.52] [-18.05] [ 2.95]
2 0.829 -0.007 1.063 0.566 0.054 -0.046
[ 4.18] [-0.20] [139.43] [46.71] [ 4.72] [-5.20]
3 0.828 -0.056 1.041 0.859 0.150 -0.095
[ 3.80] [-1.33] [121.94] [63.28] [11.66] [-9.54]
4 0.811 -0.104 0.995 1.086 0.314 -0.149
[ 3.41] [-1.78] [84.31] [57.92] [17.67] [-10.80]
5 0.994 0.070 0.936 1.382 0.498 -0.251
[ 3.61] [ 0.69] [45.90] [42.65] [16.22] [-10.54]
L/S 0.311 0.055 -0.053 1.473 0.524 -0.254
[ 1.63] [ 0.54] [-2.60] [45.14] [16.96] [-10.60]
—————————————————————————-
The ‘factorModel’ optional argument can be set to one of the preset scalar values:
  • 1 – CAPM
  • 3 – Fama-French three-factor model
  • 4 – Fama-French-Carhart four-factor model
  • 5 – Fama-French five-factor model
  • 6 – Fama-French six-factor model (five-factor model + UMD)
Here is an example of using the six-factor model:
% 6-factor model alphas and loadings
res10 = runUnivSort(ret, ind, dates, me, ‘factorModel’, 6);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 6-factor model
—————————————————————————-
xret alpha mkt smb hml rmw cma umd
1 0.675 0.009 0.994 -0.093 -0.009 0.000 0.009 0.006
[ 4.43] [ 2.16] [976.26] [-64.92] [-4.89] [ 0.00] [ 3.17] [ 5.71]
2 0.850 0.035 1.033 0.757 0.047 -0.064 -0.078 -0.072
[ 4.30] [ 1.10] [133.16] [69.26] [ 3.15] [-4.29] [-3.52] [-9.47]
3 0.856 0.009 0.990 1.005 0.108 -0.132 -0.069 -0.132
[ 3.95] [ 0.19] [82.33] [59.25] [ 4.73] [-5.70] [-1.99] [-11.27]
4 0.867 -0.008 0.903 1.093 0.098 -0.118 -0.005 -0.190
[ 3.63] [-0.09] [42.68] [36.63] [ 2.44] [-2.89] [-0.08] [-9.20]
5 1.185 0.412 0.761 1.158 0.054 -0.301 -0.035 -0.285
[ 4.20] [ 2.76] [20.95] [22.62] [ 0.78] [-4.29] [-0.34] [-8.06]
L/S 0.503 0.403 -0.233 1.251 0.063 -0.301 -0.045 -0.291
[ 2.52] [ 2.67] [-6.37] [24.21] [ 0.90] [-4.25] [-0.43] [-8.14]
—————————————————————————-
We can also pass a matrix with user-defained factors to ‘factorModel’ argument. This would would replicate the six-factor model results from above:
% User defined factor model. In this example, it is still FF6
res11 = runUnivSort(ret, ind, dates, me, ‘factorModel’, ff6(:,2:end));
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
User-defined 6-factor model
—————————————————————————-
xret alpha reg 1 reg 2 reg 3 reg 4 reg 5 reg 6
1 0.675 0.009 0.994 -0.093 -0.009 0.000 0.009 0.006
[ 4.43] [ 2.16] [976.26] [-64.92] [-4.89] [ 0.00] [ 3.17] [ 5.71]
2 0.850 0.035 1.033 0.757 0.047 -0.064 -0.078 -0.072
[ 4.30] [ 1.10] [133.16] [69.26] [ 3.15] [-4.29] [-3.52] [-9.47]
3 0.856 0.009 0.990 1.005 0.108 -0.132 -0.069 -0.132
[ 3.95] [ 0.19] [82.33] [59.25] [ 4.73] [-5.70] [-1.99] [-11.27]
4 0.867 -0.008 0.903 1.093 0.098 -0.118 -0.005 -0.190
[ 3.63] [-0.09] [42.68] [36.63] [ 2.44] [-2.89] [-0.08] [-9.20]
5 1.185 0.412 0.761 1.158 0.054 -0.301 -0.035 -0.285
[ 4.20] [ 2.76] [20.95] [22.62] [ 0.78] [-4.29] [-0.34] [-8.06]
L/S 0.503 0.403 -0.233 1.251 0.063 -0.301 -0.045 -0.291
[ 2.52] [ 2.67] [-6.37] [24.21] [ 0.90] [-4.25] [-0.43] [-8.14]
—————————————————————————-
We can supress printing of the results (e.g., when running sorts on many anomalies):
% Don’t print the results
res12 = runUnivSort(ret, ind, dates, me, ‘printResults’, 0);
and plotting the figure:
% Don’t plot the figure
res13 = runUnivSort(ret, ind, dates, me, ‘plotFigure’, 0);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.675 0.014 0.988 -0.092 -0.027 0.004
[ 4.43] [ 2.80] [975.01] [-57.24] [-17.45] [ 3.22]
2 0.850 0.015 1.062 0.553 0.059 -0.045
[ 4.30] [ 0.39] [134.78] [44.15] [ 4.93] [-4.92]
3 0.856 -0.027 1.037 0.841 0.164 -0.093
[ 3.95] [-0.62] [117.44] [59.94] [12.36] [-9.06]
4 0.867 -0.044 0.993 1.078 0.334 -0.160
[ 3.63] [-0.73] [81.68] [55.79] [18.24] [-11.29]
5 1.185 0.251 0.934 1.427 0.527 -0.265
[ 4.20] [ 2.34] [43.30] [41.61] [16.24] [-10.51]
L/S 0.503 0.237 -0.054 1.519 0.554 -0.269
[ 2.52] [ 2.20] [-2.48] [44.05] [16.96] [-10.61]
—————————————————————————-
The ‘timePeriod’ optional argument allows us to use subsamples. We can either pass it the start date:
% Start sample in 196307
res14 = runUnivSort(ret, ind, dates, me, ‘timePeriod’, 196307);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.578 0.012 0.992 -0.092 -0.020 0.006
[ 3.52] [ 2.68] [914.52] [-59.86] [-12.09] [ 5.33]
2 0.749 -0.013 1.053 0.767 0.127 -0.076
[ 3.38] [-0.37] [129.85] [66.87] [10.44] [-9.19]
3 0.717 -0.064 1.014 1.033 0.230 -0.139
[ 2.99] [-1.25] [83.44] [60.04] [12.58] [-11.19]
4 0.650 -0.068 0.920 1.119 0.262 -0.194
[ 2.65] [-0.79] [45.18] [38.83] [ 8.55] [-9.32]
5 0.870 0.286 0.788 1.236 0.210 -0.298
[ 3.24] [ 1.94] [22.67] [25.16] [ 4.03] [-8.38]
L/S 0.292 0.274 -0.204 1.328 0.230 -0.303
[ 1.38] [ 1.83] [-5.82] [26.75] [ 4.36] [-8.46]
—————————————————————————-
or the start and end dates:
% Start sample in 192601 and end in 196306
res15 = runUnivSort(ret, ind, dates, me, ‘timePeriod’, [192601 196306]);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.829 0.013 0.986 -0.091 -0.031 0.000
[ 2.80] [ 1.25] [492.90] [-27.13] [-9.47] [ 0.08]
2 1.015 -0.003 1.079 0.260 0.128 -0.012
[ 2.73] [-0.05] [97.74] [13.98] [ 7.06] [-0.88]
3 1.080 -0.026 1.075 0.592 0.210 -0.040
[ 2.63] [-0.47] [103.82] [33.92] [12.38] [-3.15]
4 1.215 0.029 1.044 1.016 0.378 -0.109
[ 2.53] [ 0.42] [81.56] [47.11] [17.99] [-6.91]
5 1.686 0.373 0.990 1.637 0.630 -0.186
[ 2.85] [ 3.15] [44.93] [44.07] [17.42] [-6.90]
L/S 0.815 0.359 0.004 1.728 0.661 -0.187
[ 2.09] [ 3.10] [ 0.20] [47.58] [18.69] [-7.06]
—————————————————————————-
The ‘addLongShort’ option arguments allows us to suppress adding the long/short portfolio and is used when we do bivariate sorts:
% Do not add long/short portfolio
res16 = runUnivSort(ret, ind, dates, me, ‘addLongShort’, 0);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.675 0.014 0.988 -0.092 -0.027 0.004
[ 4.43] [ 2.80] [975.01] [-57.24] [-17.45] [ 3.22]
2 0.850 0.015 1.062 0.553 0.059 -0.045
[ 4.30] [ 0.39] [134.78] [44.15] [ 4.93] [-4.92]
3 0.856 -0.027 1.037 0.841 0.164 -0.093
[ 3.95] [-0.62] [117.44] [59.94] [12.36] [-9.06]
4 0.867 -0.044 0.993 1.078 0.334 -0.160
[ 3.63] [-0.73] [81.68] [55.79] [18.24] [-11.29]
5 1.185 0.251 0.934 1.427 0.527 -0.265
[ 4.20] [ 2.34] [43.30] [41.61] [16.24] [-10.51]
—————————————————————————-
The last optional argument, tcosts allows us to calculate net returns by subtracting an effective bid-ask spread measure each time a position is entered or existed. See Novy-Marx and Velikov (2016) and Appendix A in Detzel, Novy-Marx, and Velikov (2023) for more details. The resulting structure here also includes fields for the trading costs, turnover, and net return to the long/short portfolio,
% Calculate tcosts, net returns, and turnover usging the tcosts matrix
res17 = runUnivSort(ret, ind, dates, me, ‘tcosts’, tcosts);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.675 0.014 0.988 -0.092 -0.027 0.004
[ 4.43] [ 2.80] [975.01] [-57.24] [-17.45] [ 3.22]
2 0.850 0.015 1.062 0.553 0.059 -0.045
[ 4.30] [ 0.39] [134.78] [44.15] [ 4.93] [-4.92]
3 0.856 -0.027 1.037 0.841 0.164 -0.093
[ 3.95] [-0.62] [117.44] [59.94] [12.36] [-9.06]
4 0.867 -0.044 0.993 1.078 0.334 -0.160
[ 3.63] [-0.73] [81.68] [55.79] [18.24] [-11.29]
5 1.185 0.251 0.934 1.427 0.527 -0.265
[ 4.20] [ 2.34] [43.30] [41.61] [16.24] [-10.51]
L/S 0.503 0.237 -0.054 1.519 0.554 -0.269
[ 2.52] [ 2.20] [-2.48] [44.05] [16.96] [-10.61]
—————————————————————————-
The order in which the optional arguments are specified doesn’t matter:
% Order doesn’t matter
res18 = runUnivSort(ret, ind, dates, me, ‘factorModel’, 4,
‘weighting’, ‘v’,
‘timePeriod’, 192512,
‘holdingPeriod’, 2,
‘plotFigure’, 0);
—————————————————————————-
Value-weighted portfolio sort, 2-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.676 0.015 0.989 -0.091 -0.027 0.003
[ 4.43] [ 3.07] [1011.07] [-58.52] [-18.05] [ 2.95]
2 0.829 -0.007 1.063 0.566 0.054 -0.046
[ 4.18] [-0.20] [139.43] [46.71] [ 4.72] [-5.20]
3 0.828 -0.056 1.041 0.859 0.150 -0.095
[ 3.80] [-1.33] [121.94] [63.28] [11.66] [-9.54]
4 0.811 -0.104 0.995 1.086 0.314 -0.149
[ 3.41] [-1.78] [84.31] [57.92] [17.67] [-10.80]
5 0.994 0.070 0.936 1.382 0.498 -0.251
[ 3.61] [ 0.69] [45.90] [42.65] [16.22] [-10.54]
L/S 0.311 0.055 -0.053 1.473 0.524 -0.254
[ 1.63] [ 0.54] [-2.60] [45.14] [16.96] [-10.60]
—————————————————————————-
% Specify all optional inputs
res19 = runUnivSort(ret, ind, dates, me, ‘weighting’, ‘v’,
‘holdingPeriod’, 1,
‘factorModel’, 4,
‘printResults’, 1,
‘plotFigure’, 0,
‘timePeriod’, 192512,
‘addLongShort’, 1,
‘tcosts’, 0);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.675 0.014 0.988 -0.092 -0.027 0.004
[ 4.43] [ 2.80] [975.01] [-57.24] [-17.45] [ 3.22]
2 0.850 0.015 1.062 0.553 0.059 -0.045
[ 4.30] [ 0.39] [134.78] [44.15] [ 4.93] [-4.92]
3 0.856 -0.027 1.037 0.841 0.164 -0.093
[ 3.95] [-0.62] [117.44] [59.94] [12.36] [-9.06]
4 0.867 -0.044 0.993 1.078 0.334 -0.160
[ 3.63] [-0.73] [81.68] [55.79] [18.24] [-11.29]
5 1.185 0.251 0.934 1.427 0.527 -0.265
[ 4.20] [ 2.34] [43.30] [41.61] [16.24] [-10.51]
L/S 0.503 0.237 -0.054 1.519 0.554 -0.269
[ 2.52] [ 2.20] [-2.48] [44.05] [16.96] [-10.61]
—————————————————————————-
It even works without specifying the ‘Name’ part of the arguments, though then the inputs have to be in the correct order:
% Works without specifying the ‘Name’ part of the arguments too, although
% they have to be in this specific order (as specified in the function)
res20 = runUnivSort(ret, ind, dates, me, ‘v’, 1, 4, 1, 0, 192512, 1, 0);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.675 0.014 0.988 -0.092 -0.027 0.004
[ 4.43] [ 2.80] [975.01] [-57.24] [-17.45] [ 3.22]
2 0.850 0.015 1.062 0.553 0.059 -0.045
[ 4.30] [ 0.39] [134.78] [44.15] [ 4.93] [-4.92]
3 0.856 -0.027 1.037 0.841 0.164 -0.093
[ 3.95] [-0.62] [117.44] [59.94] [12.36] [-9.06]
4 0.867 -0.044 0.993 1.078 0.334 -0.160
[ 3.63] [-0.73] [81.68] [55.79] [18.24] [-11.29]
5 1.185 0.251 0.934 1.427 0.527 -0.265
[ 4.20] [ 2.34] [43.30] [41.61] [16.24] [-10.51]
L/S 0.503 0.237 -0.054 1.519 0.554 -0.269
[ 2.52] [ 2.20] [-2.48] [44.05] [16.96] [-10.61]
—————————————————————————-
% Works even if you only specify the inputs partially, as long as they are
% in correct order
res21 = runUnivSort(ret, ind, dates, me, ‘v’, 1, 5, 1, 0);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 5-factor model
—————————————————————————-
xret alpha mkt smb hml rmw cma
1 0.675 0.013 0.993 -0.093 -0.012 0.001 0.011
[ 4.43] [ 3.10] [965.78] [-63.37] [-6.51] [ 0.55] [ 3.71]
2 0.850 -0.016 1.044 0.755 0.084 -0.078 -0.100
[ 4.30] [-0.48] [128.39] [65.02] [ 5.55] [-4.93] [-4.28]
3 0.856 -0.085 1.012 1.000 0.177 -0.158 -0.110
[ 3.95] [-1.61] [78.33] [54.26] [ 7.38] [-6.28] [-2.95]
4 0.867 -0.144 0.933 1.085 0.197 -0.155 -0.064
[ 3.63] [-1.58] [42.20] [34.40] [ 4.80] [-3.59] [-1.01]
5 1.185 0.208 0.807 1.147 0.202 -0.355 -0.124
[ 4.20] [ 1.35] [21.52] [21.45] [ 2.90] [-4.88] [-1.15]
L/S 0.503 0.195 -0.187 1.240 0.215 -0.357 -0.135
[ 2.52] [ 1.25] [-4.93] [22.95] [ 3.05] [-4.84] [-1.24]
—————————————————————————-
Here are several examples of errors:
% This causes an error – weighting has to be one of ‘V’, ‘v’ (default), ‘E’, ‘e’
% [~] = runUnivSort(ret, ind, dates, me, ‘text’);
 
% This causes an error – weighting has to be one of ‘V’, ‘v’ (default), ‘E’, ‘e’
% [~] = runUnivSort(ret, ind, dates, me, ‘weighting’, ‘equal’);
 
% This causes an error – factorModel has to be one of 1, 3, 4, 5, 6 or a user-defined matrix
% [~] = runUnivSort(ret, ind, dates, me, ‘factorModel’, 2);
 
% This causes an error – timePeriod has to be YYYYMM or [YYYYMM YYYYMM]
% [~] = runUnivSort(ret, ind, dates, me, ‘timePeriod’, 1963);

Bivariate sorts

Next, we’ll look at how we run bivariate sorts. These are useful tests to examine how two signals are related to each other. We’ll do this again with two functions:
  1. makeBivSortInd – creates a portfolio index martix
  2. runBivSort – does everything else
Both of these functions have plenty of optional features, which we’ll showcase next.
Let’s start by loading some variables:
% Load some variables
clear
clc
 
load me
load ff
load bm
load R
load dates
load NYSE
load ret

Running the bivariate sort

First, let’s take a look at the help file for makeBivSortInd():
The function takes four required arguments – a pair of (signal matrix, number of portfolios/breakpoints) arguments.
Let’s start with the most basic case – a five-by-five sort on size and momentum. The default is to do an unconditional sort and use name breaks:
% Notes on makeBivSortInd:
% 1. Takes four required arguments (2 signals and 2 numbers of portfolios)
% 2. Does name breaks by default
 
% Notes on runBivSort:
% 1. Default is unconditional sort using name breaks.
% 2. runBivSort carries over all {‘Name’,’Value’} optional inputs from
% runUnivSort without ‘addLongShort’.
% 3. Number of portfolios passed to runBivSort should match numbers in
% makeBivSortInd
 
% 5×5 sort on size and momentum.
ind = makeBivSortInd(me, 5, R, 5);

Assigning stocks to portfolio

The portfolio index is then passed on to runBivSort(), which does everything else for the bivariate sort. Here is its (partial) help file:
[res1, cond_res1] = runBivSort(ret, ind, 5, 5, dates, me);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.821 0.116 0.970 1.622 0.340 -0.646
[ 2.43] [ 0.75] [30.95] [32.53] [ 7.21] [-17.61]
2 1.178 0.277 0.899 1.316 0.617 -0.311
[ 4.16] [ 2.37] [38.18] [35.15] [17.44] [-11.30]
3 1.430 0.431 0.845 1.272 0.658 -0.117
[ 5.41] [ 3.66] [35.65] [33.73] [18.46] [-4.24]
4 1.518 0.325 0.902 1.281 0.667 0.106
[ 5.77] [ 2.63] [36.30] [32.41] [17.85] [ 3.65]
5 1.581 0.311 0.935 1.329 0.499 0.302
[ 6.06] [ 2.32] [34.63] [30.96] [12.30] [ 9.56]
6 0.138 -0.488 1.103 1.264 0.164 -0.701
[ 0.44] [-4.63] [51.91] [37.40] [ 5.14] [-28.23]
7 0.802 0.003 0.950 1.035 0.371 -0.303
[ 3.27] [ 0.04] [66.34] [45.47] [17.24] [-18.07]
8 0.965 0.068 0.890 0.891 0.407 -0.071
[ 4.49] [ 0.96] [62.52] [39.34] [19.02] [-4.25]
9 1.237 0.174 0.901 1.019 0.378 0.139
[ 5.78] [ 2.35] [60.43] [42.99] [16.87] [ 7.99]
10 1.477 0.210 1.038 1.134 0.284 0.343
[ 6.37] [ 2.59] [63.68] [43.76] [11.57] [17.99]
11 0.083 -0.449 1.172 0.967 0.037 -0.765
[ 0.28] [-5.08] [65.77] [34.16] [ 1.40] [-36.74]
12 0.686 -0.055 1.030 0.816 0.227 -0.334
[ 2.90] [-0.92] [85.43] [42.60] [12.50] [-23.71]
13 0.900 0.046 0.969 0.725 0.268 -0.093
[ 4.34] [ 0.86] [89.29] [42.05] [16.41] [-7.30]
14 1.084 0.090 0.939 0.764 0.233 0.153
[ 5.66] [ 1.72] [88.86] [45.47] [14.68] [12.42]
15 1.364 0.121 1.079 0.942 0.080 0.440
[ 6.35] [ 1.80] [79.72] [43.80] [ 3.95] [27.82]
16 0.160 -0.286 1.248 0.643 -0.076 -0.819
[ 0.54] [-3.02] [65.58] [21.24] [-2.67] [-36.82]
17 0.618 0.024 1.072 0.481 0.042 -0.407
[ 2.76] [ 0.41] [89.70] [25.31] [ 2.34] [-29.11]
18 0.819 0.048 0.987 0.488 0.132 -0.100
[ 4.26] [ 0.95] [96.45] [29.95] [ 8.57] [-8.32]
19 0.998 0.038 1.008 0.477 0.175 0.150
[ 5.42] [ 0.74] [97.13] [28.89] [11.21] [12.36]
20 1.303 0.112 1.119 0.700 -0.035 0.452
[ 6.42] [ 1.88] [93.10] [36.62] [-1.93] [32.14]
21 0.199 -0.028 1.182 0.116 -0.169 -0.860
[ 0.74] [-0.28] [58.64] [ 3.62] [-5.60] [-36.54]
22 0.445 0.126 1.006 -0.090 -0.053 -0.529
[ 2.24] [ 2.47] [97.47] [-5.47] [-3.42] [-43.85]
23 0.635 0.095 0.979 -0.126 0.047 -0.205
[ 3.74] [ 2.27] [116.86] [-9.45] [ 3.76] [-20.97]
24 0.826 0.046 0.999 -0.125 0.045 0.141
[ 5.31] [ 1.11] [120.49] [-9.46] [ 3.60] [14.58]
25 0.999 -0.084 1.103 0.114 -0.037 0.483
[ 5.72] [-1.59] [103.21] [ 6.73] [-2.28] [38.63]
—————————————————————————-
GRS test results: p-value F-stat df1 df2
full test – 1.148e-13 4.8355 25 1112
partial test – 0 7.8071 25 1116
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 -0.615 -0.145 0.214 -1.507 -0.509 -0.212
[-2.72] [-0.81] [ 5.88] [-26.13] [-9.33] [-5.00]
2 -0.733 -0.150 0.107 -1.405 -0.670 -0.218
[-3.75] [-1.11] [ 3.93] [-32.41] [-16.35] [-6.84]
3 -0.795 -0.336 0.134 -1.398 -0.611 -0.088
[-4.22] [-2.67] [ 5.27] [-34.65] [-16.01] [-2.96]
4 -0.692 -0.279 0.097 -1.406 -0.622 0.035
[-3.52] [-2.13] [ 3.69] [-33.63] [-15.74] [ 1.15]
5 -0.582 -0.395 0.168 -1.215 -0.536 0.181
[-2.99] [-2.71] [ 5.75] [-26.06] [-12.16] [ 5.28]
—————————————————————————-
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.761 0.195 -0.035 -0.293 0.160 0.947
[ 3.81] [ 1.27] [-1.14] [-5.97] [ 3.44] [26.29]
2 1.339 0.698 -0.065 -0.129 0.119 1.044
[ 7.61] [ 6.94] [-3.22] [-4.02] [ 3.92] [44.09]
3 1.281 0.570 -0.093 -0.025 0.043 1.205
[ 6.65] [ 6.35] [-5.16] [-0.88] [ 1.58] [57.06]
4 1.143 0.398 -0.129 0.057 0.042 1.271
[ 5.64] [ 4.31] [-6.94] [ 1.93] [ 1.48] [58.44]
5 0.785 -0.056 -0.079 -0.001 0.132 1.342
[ 3.70] [-0.53] [-3.73] [-0.03] [ 4.17] [54.27]
—————————————————————————-
Portfolio Average Excess Returns (%/month)
0.8205 1.1778 1.4301 1.5177 1.5813
0.1383 0.8019 0.9649 1.2370 1.4770
0.0829 0.6858 0.9000 1.0839 1.3638
0.1602 0.6180 0.8190 0.9984 1.3032
0.1987 0.4452 0.6350 0.8257 0.9989
Portfolio Average Number of Firms
247 130 92 77 82
151 135 117 108 118
110 131 125 124 140
74 123 138 145 152
45 113 160 177 140
Portfolio Average Firm Size ($10^6)
10 12 13 13 13
44 46 46 47 47
131 136 138 139 138
406 429 438 443 435
3454 5205 6137 6279 5238
Both functions (makeBivSortInd and runBivSort) pass on most of their inputs to their univariate equivalents (makeUnivSortInd and runUnivSort), so most of the functionality of the univariate sort functions applies here too:
% 2×3 (FF-style tertiles) sort on size and momentum
ind = makeBivSortInd(me, 2, R, [30 70]);
[res2, cond_res2] = runBivSort(ret, ind, 2, 3, dates, me);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.420 -0.231 1.051 1.183 0.205 -0.603
[ 1.48] [-3.01] [67.85] [48.05] [ 8.79] [-33.31]
2 0.994 0.082 0.928 0.923 0.392 -0.090
[ 4.58] [ 1.66] [93.20] [58.27] [26.19] [-7.75]
3 1.353 0.144 1.000 1.060 0.275 0.326
[ 6.27] [ 2.45] [84.69] [56.45] [15.51] [23.62]
4 0.247 -0.033 1.101 0.109 -0.091 -0.740
[ 1.05] [-0.69] [115.04] [ 7.19] [-6.35] [-66.17]
5 0.663 0.084 0.980 -0.080 0.034 -0.155
[ 4.02] [ 2.80] [161.35] [-8.25] [ 3.73] [-21.76]
6 0.975 -0.031 1.067 0.083 0.013 0.385
[ 5.94] [-0.96] [163.99] [ 8.02] [ 1.32] [50.65]
—————————————————————————-
GRS test results: p-value F-stat df1 df2
full test – 2.2949e-07 7.0346 6 1131
partial test – 0 20.2793 6 1135
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 -0.173 0.198 0.050 -1.074 -0.296 -0.137
[-1.27] [ 2.17] [ 2.70] [-36.64] [-10.68] [-6.36]
2 -0.330 0.002 0.052 -1.002 -0.358 -0.064
[-2.90] [ 0.04] [ 4.75] [-57.10] [-21.57] [-4.99]
3 -0.378 -0.175 0.067 -0.977 -0.263 0.059
[-3.21] [-2.57] [ 4.90] [-44.95] [-12.77] [ 3.71]
—————————————————————————-
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.933 0.375 -0.051 -0.123 0.071 0.929
[ 6.34] [ 5.52] [-3.71] [-5.67] [ 3.43] [58.09]
2 0.727 0.002 -0.034 -0.026 0.104 1.126
[ 4.47] [ 0.04] [-3.21] [-1.59] [ 6.63] [92.03]
—————————————————————————-
Portfolio Average Excess Returns (%/month)
0.4203 0.9936 1.3530
0.2472 0.6634 0.9747
Portfolio Average Number of Firms
633 550 389
310 715 558
Portfolio Average Firm Size ($10^6)
36 47 50
1452 2881 2583
% 5×5 sort on size and momentum. Specify conditional sort
ind = makeBivSortInd(me, 5, R, 5, ‘sortType’, ‘conditional’);
[res3, cond_res3] = runBivSort(ret, ind, 5, 5, dates, me);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.674 0.040 0.968 1.828 0.219 -0.756
[ 1.79] [ 0.20] [23.24] [27.61] [ 3.50] [-15.52]
2 1.033 0.230 0.978 1.390 0.470 -0.526
[ 3.29] [ 1.67] [35.20] [31.46] [11.24] [-16.19]
3 1.285 0.341 0.920 1.374 0.654 -0.330
[ 4.37] [ 2.82] [37.73] [35.45] [17.84] [-11.58]
4 1.444 0.368 0.883 1.335 0.666 -0.074
[ 5.37] [ 3.22] [38.46] [36.57] [19.28] [-2.77]
5 1.493 0.264 0.935 1.329 0.547 0.207
[ 5.83] [ 2.34] [41.26] [36.90] [16.06] [ 7.82]
6 0.040 -0.555 1.118 1.292 0.161 -0.775
[ 0.12] [-4.90] [49.04] [35.64] [ 4.69] [-29.07]
7 0.702 -0.090 0.989 1.053 0.350 -0.348
[ 2.76] [-1.21] [66.02] [44.21] [15.52] [-19.87]
8 0.959 0.033 0.926 0.956 0.444 -0.115
[ 4.21] [ 0.47] [65.03] [42.22] [20.76] [-6.91]
9 1.247 0.217 0.904 0.988 0.421 0.073
[ 5.76] [ 3.00] [62.13] [42.69] [19.23] [ 4.29]
10 1.401 0.155 1.028 1.108 0.296 0.323
[ 6.11] [ 1.95] [64.35] [43.62] [12.31] [17.29]
11 0.171 -0.374 1.153 0.961 0.035 -0.722
[ 0.59] [-4.53] [69.42] [36.38] [ 1.39] [-37.16]
12 0.703 -0.052 1.017 0.821 0.220 -0.296
[ 3.02] [-0.82] [80.44] [40.84] [11.55] [-19.99]
13 0.924 0.059 0.975 0.719 0.267 -0.081
[ 4.46] [ 1.09] [88.46] [41.00] [16.11] [-6.26]
14 1.113 0.104 0.955 0.753 0.236 0.162
[ 5.76] [ 1.96] [89.16] [44.22] [14.62] [12.97]
15 1.366 0.121 1.082 0.953 0.073 0.439
[ 6.33] [ 1.80] [80.06] [44.36] [ 3.58] [27.80]
16 0.318 -0.184 1.190 0.576 -0.037 -0.667
[ 1.19] [-2.55] [82.04] [25.00] [-1.69] [-39.36]
17 0.687 0.038 1.004 0.479 0.106 -0.280
[ 3.35] [ 0.73] [96.07] [28.80] [ 6.71] [-22.90]
18 0.888 0.064 0.977 0.500 0.130 -0.006
[ 4.77] [ 1.24] [93.82] [30.18] [ 8.28] [-0.52]
19 1.005 0.015 1.004 0.500 0.157 0.222
[ 5.54] [ 0.30] [100.12] [31.34] [10.45] [18.96]
20 1.326 0.123 1.129 0.723 -0.078 0.476
[ 6.42] [ 1.91] [86.73] [34.94] [-4.00] [31.26]
21 0.411 0.122 1.024 -0.048 -0.063 -0.608
[ 1.98] [ 2.73] [113.42] [-3.33] [-4.66] [-57.58]
22 0.574 0.119 0.936 -0.150 0.028 -0.268
[ 3.45] [ 3.00] [116.99] [-11.76] [ 2.30] [-28.69]
23 0.638 0.004 0.940 -0.162 0.036 0.007
[ 4.25] [ 0.09] [119.18] [-12.90] [ 3.00] [ 0.79]
24 0.751 -0.073 0.994 -0.105 0.010 0.244
[ 4.97] [-1.87] [126.77] [-8.40] [ 0.86] [26.58]
25 1.004 -0.077 1.089 0.158 -0.083 0.504
[ 5.77] [-1.43] [101.07] [ 9.22] [-5.13] [40.01]
—————————————————————————-
GRS test results: p-value F-stat df1 df2
full test – 1.5818e-12 4.5485 25 1112
partial test – 0 7.8509 25 1116
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 -0.264 0.082 0.057 -1.876 -0.282 0.148
[-0.94] [ 0.38] [ 1.29] [-26.89] [-4.28] [ 2.88]
2 -0.447 -0.109 -0.042 -1.540 -0.441 0.258
[-1.99] [-0.73] [-1.40] [-32.35] [-9.80] [ 7.36]
3 -0.638 -0.337 0.021 -1.536 -0.618 0.337
[-2.88] [-2.64] [ 0.80] [-37.69] [-16.04] [11.25]
4 -0.684 -0.440 0.111 -1.440 -0.655 0.318
[-3.32] [-3.71] [ 4.63] [-37.93] [-18.25] [11.40]
5 -0.488 -0.340 0.155 -1.171 -0.630 0.297
[-2.56] [-2.67] [ 6.02] [-28.69] [-16.33] [ 9.89]
—————————————————————————-
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.818 0.223 -0.033 -0.499 0.328 0.963
[ 3.54] [ 1.17] [-0.86] [-8.16] [ 5.67] [21.41]
2 1.361 0.709 -0.090 -0.184 0.135 1.098
[ 7.24] [ 6.59] [-4.14] [-5.33] [ 4.14] [43.29]
3 1.195 0.495 -0.071 -0.008 0.038 1.161
[ 6.51] [ 5.83] [-4.17] [-0.29] [ 1.48] [58.11]
4 1.007 0.307 -0.061 0.147 -0.041 1.143
[ 5.64] [ 4.05] [-4.00] [ 6.04] [-1.80] [64.04]
5 0.593 -0.199 0.065 0.206 -0.020 1.112
[ 3.58] [-2.79] [ 4.52] [ 9.02] [-0.92] [66.25]
—————————————————————————-
Portfolio Average Excess Returns (%/month)
0.6744 1.0326 1.2850 1.4444 1.4925
0.0400 0.7024 0.9593 1.2470 1.4009
0.1712 0.7026 0.9237 1.1128 1.3659
0.3184 0.6875 0.8880 1.0054 1.3258
0.4109 0.5742 0.6381 0.7515 1.0043
Portfolio Average Number of Firms
125 126 126 126 127
126 125 126 126 126
126 126 126 126 127
126 126 126 127 126
127 127 127 127 127
Portfolio Average Firm Size ($10^6)
10 11 12 13 13
44 46 46 47 47
132 136 138 139 138
415 435 440 443 434
4680 6273 6594 6506 5148
% 5×5 sort on size and momentum. Specify NYSE breaks
ind = makeBivSortInd(me, 5, R, 5, ‘breaksFilterInd’, NYSE);
[res4, cond_res4] = runBivSort(ret, ind, 5, 5, dates, me);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.656 -0.111 1.054 1.483 0.376 -0.613
[ 2.06] [-1.11] [52.14] [46.14] [12.35] [-25.93]
2 1.077 0.090 0.975 1.241 0.652 -0.254
[ 3.92] [ 1.07] [57.56] [46.09] [25.59] [-12.83]
3 1.290 0.240 0.932 1.212 0.659 -0.101
[ 4.94] [ 2.55] [49.27] [40.28] [23.16] [-4.59]
4 1.325 0.099 0.995 1.217 0.629 0.122
[ 5.13] [ 0.98] [49.16] [37.81] [20.66] [ 5.17]
5 1.544 0.189 1.055 1.298 0.404 0.365
[ 6.03] [ 1.79] [49.43] [38.25] [12.58] [14.63]
6 0.418 -0.249 1.168 1.070 0.162 -0.644
[ 1.42] [-3.59] [83.51] [48.12] [ 7.71] [-39.38]
7 0.880 0.061 1.013 0.894 0.355 -0.285
[ 3.68] [ 1.11] [91.33] [50.69] [21.29] [-21.99]
8 0.974 0.049 0.968 0.777 0.371 -0.043
[ 4.59] [ 0.84] [82.87] [41.87] [21.14] [-3.19]
9 1.162 0.082 0.999 0.888 0.348 0.140
[ 5.44] [ 1.38] [83.84] [46.89] [19.41] [10.04]
10 1.401 0.096 1.133 1.011 0.212 0.377
[ 6.15] [ 1.64] [96.11] [53.95] [11.98] [27.34]
11 0.387 -0.140 1.182 0.675 0.087 -0.718
[ 1.39] [-2.03] [85.24] [30.59] [ 4.16] [-44.28]
12 0.773 0.041 1.055 0.539 0.254 -0.302
[ 3.46] [ 0.77] [97.48] [31.33] [15.61] [-23.84]
13 0.868 0.042 1.001 0.506 0.273 -0.095
[ 4.34] [ 0.84] [98.32] [31.27] [17.85] [-8.01]
14 0.926 -0.042 0.980 0.517 0.252 0.157
[ 5.05] [-0.87] [101.54] [33.70] [17.39] [13.89]
15 1.263 0.039 1.121 0.701 0.063 0.449
[ 6.22] [ 0.74] [103.72] [40.81] [ 3.88] [35.55]
16 0.419 -0.021 1.229 0.303 0.083 -0.783
[ 1.50] [-0.25] [72.48] [11.22] [ 3.25] [-39.47]
17 0.670 0.061 1.079 0.172 0.148 -0.349
[ 3.18] [ 1.13] [99.12] [ 9.93] [ 9.04] [-27.40]
18 0.810 0.080 1.002 0.219 0.176 -0.108
[ 4.38] [ 1.54] [94.97] [13.08] [11.11] [-8.75]
19 0.928 0.019 1.027 0.207 0.196 0.142
[ 5.26] [ 0.36] [98.38] [12.48] [12.51] [11.67]
20 1.228 0.067 1.113 0.429 0.029 0.465
[ 6.50] [ 1.19] [97.92] [23.75] [ 1.68] [34.96]
21 0.335 0.057 1.141 -0.039 -0.021 -0.766
[ 1.33] [ 0.64] [63.07] [-1.36] [-0.76] [-36.28]
22 0.530 0.155 0.979 -0.173 0.048 -0.438
[ 2.80] [ 2.87] [90.16] [-10.03] [ 2.93] [-34.55]
23 0.624 0.054 0.977 -0.177 0.091 -0.157
[ 3.72] [ 1.17] [104.58] [-11.92] [ 6.46] [-14.41]
24 0.778 0.013 0.990 -0.195 0.075 0.148
[ 5.06] [ 0.30] [111.37] [-13.79] [ 5.65] [14.23]
25 0.958 -0.091 1.092 -0.016 0.004 0.461
[ 5.66] [-1.75] [104.67] [-0.94] [ 0.23] [37.81]
—————————————————————————-
GRS test results: p-value F-stat df1 df2
full test – 0.0016751 2.0593 25 1112
partial test – 8.8818e-16 5.3647 25 1116
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 -0.314 0.168 0.088 -1.523 -0.396 -0.151
[-1.58] [ 1.20] [ 3.09] [-33.92] [-9.32] [-4.57]
2 -0.547 0.065 0.004 -1.414 -0.604 -0.184
[-3.07] [ 0.63] [ 0.19] [-43.02] [-19.42] [-7.63]
3 -0.666 -0.185 0.045 -1.389 -0.568 -0.056
[-3.79] [-1.82] [ 2.18] [-42.62] [-18.44] [-2.33]
4 -0.547 -0.085 -0.005 -1.412 -0.554 0.025
[-2.97] [-0.80] [-0.25] [-41.37] [-17.15] [ 1.01]
5 -0.586 -0.280 0.037 -1.313 -0.400 0.096
[-3.27] [-2.35] [ 1.54] [-34.38] [-11.07] [ 3.43]
—————————————————————————-
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.889 0.301 0.001 -0.186 0.028 0.978
[ 4.83] [ 2.40] [ 0.03] [-4.63] [ 0.74] [33.20]
2 0.983 0.345 -0.035 -0.059 0.050 1.021
[ 6.10] [ 4.48] [-2.25] [-2.38] [ 2.16] [56.31]
3 0.876 0.179 -0.062 0.026 -0.024 1.167
[ 4.76] [ 2.23] [-3.81] [ 1.03] [-0.97] [61.71]
4 0.809 0.088 -0.116 0.127 -0.054 1.247
[ 4.02] [ 0.98] [-6.43] [ 4.40] [-1.99] [58.96]
5 0.608 -0.148 -0.049 0.024 0.024 1.227
[ 3.08] [-1.49] [-2.43] [ 0.74] [ 0.81] [52.37]
—————————————————————————-
Portfolio Average Excess Returns (%/month)
0.6558 1.0767 1.2895 1.3252 1.5444
0.4182 0.8803 0.9741 1.1622 1.4013
0.3868 0.7728 0.8675 0.9259 1.2627
0.4195 0.6704 0.8099 0.9282 1.2283
0.3351 0.5298 0.6238 0.7782 0.9582
Portfolio Average Number of Firms
657 300 234 221 348
100 90 85 86 123
56 67 69 71 88
40 56 62 65 70
28 51 62 66 58
Portfolio Average Firm Size ($10^6)
38 50 53 55 57
250 257 260 260 258
600 614 623 623 615
1506 1543 1539 1544 1538
8893 11261 12043 11938 11134
% 5×5 sort on size and momentum. Specify cap breaks
ind = makeBivSortInd(me, 5, R, 5, ‘portfolioMassInd’, me);
[res5, cond_res5] = runBivSort(ret, ind, 5, 5, dates, me);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.417 -0.165 1.104 0.567 0.091 -0.513
[ 1.74] [-3.70] [122.97] [39.72] [ 6.72] [-48.88]
2 0.728 0.013 0.967 0.379 0.157 -0.131
[ 3.95] [ 0.31] [116.35] [28.71] [12.55] [-13.47]
3 0.867 0.031 0.958 0.401 0.171 0.051
[ 4.93] [ 0.69] [104.56] [27.53] [12.38] [ 4.80]
4 0.983 0.030 0.981 0.441 0.144 0.213
[ 5.68] [ 0.74] [119.05] [33.64] [11.64] [22.07]
5 1.319 0.133 1.106 0.673 -0.027 0.462
[ 6.66] [ 2.56] [105.96] [40.56] [-1.69] [37.89]
6 0.417 0.031 1.035 0.056 0.001 -0.541
[ 1.96] [ 0.52] [86.72] [ 2.95] [ 0.03] [-38.78]
7 0.669 0.089 0.962 0.008 0.145 -0.214
[ 3.76] [ 1.66] [89.30] [ 0.49] [ 8.93] [-16.99]
8 0.711 0.009 0.943 -0.033 0.091 0.039
[ 4.53] [ 0.18] [90.19] [-1.99] [ 5.80] [ 3.20]
9 0.796 -0.042 1.010 0.070 0.037 0.180
[ 4.80] [-0.72] [85.34] [ 3.74] [ 2.07] [12.99]
10 1.082 0.001 1.079 0.317 -0.118 0.491
[ 5.94] [ 0.02] [79.53] [14.71] [-5.77] [30.94]
11 0.467 0.134 1.013 -0.137 -0.053 -0.499
[ 2.30] [ 1.79] [67.18] [-5.70] [-2.34] [-28.34]
12 0.528 0.073 0.957 -0.176 -0.007 -0.257
[ 3.04] [ 1.14] [74.67] [-8.61] [-0.38] [-17.15]
13 0.527 -0.068 0.940 -0.159 -0.027 -0.006
[ 3.38] [-1.09] [75.17] [-8.00] [-1.41] [-0.40]
14 0.681 -0.153 1.032 -0.126 0.075 0.187
[ 4.12] [-2.53] [84.65] [-6.48] [ 4.09] [13.10]
15 0.936 -0.116 1.097 0.055 -0.093 0.488
[ 5.19] [-1.50] [70.31] [ 2.21] [-3.98] [26.77]
16 0.357 0.049 0.992 -0.230 -0.066 -0.548
[ 1.64] [ 0.44] [43.10] [-6.41] [-1.95] [-20.47]
17 0.518 -0.008 0.961 -0.282 0.027 -0.142
[ 2.95] [-0.10] [55.51] [-10.27] [ 1.02] [-7.06]
18 0.609 -0.005 0.949 -0.347 -0.017 0.063
[ 3.87] [-0.07] [64.16] [-14.79] [-0.77] [ 3.64]
19 0.656 -0.133 1.007 -0.240 -0.030 0.191
[ 3.96] [-1.63] [60.87] [-9.14] [-1.19] [ 9.92]
20 1.000 -0.109 1.127 -0.034 0.020 0.538
[ 4.91] [-0.91] [46.29] [-0.87] [ 0.55] [18.83]
21 0.463 0.316 0.995 -0.196 -0.076 -0.568
[ 1.65] [ 1.73] [26.45] [-3.01] [-1.32] [-13.06]
22 0.407 0.115 0.891 -0.271 -0.056 -0.241
[ 2.24] [ 0.98] [35.77] [-7.13] [-1.44] [-8.41]
23 0.689 0.118 0.914 -0.312 0.019 0.030
[ 3.85] [ 1.03] [38.52] [-8.66] [ 0.56] [ 1.13]
24 0.683 -0.077 0.948 -0.313 -0.002 0.233
[ 3.81] [-0.67] [40.25] [-8.49] [-0.06] [ 8.54]
25 1.038 0.012 1.104 -0.176 -0.074 0.454
[ 4.59] [ 0.08] [37.89] [-3.66] [-1.60] [12.77]
—————————————————————————-
GRS test results: p-value F-stat df1 df2
full test – 2.9975e-05 2.6022 25 1112
partial test – 0 6.7517 25 1116
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.206 0.489 -0.133 -0.833 -0.136 -0.042
[ 0.94] [ 2.42] [-3.20] [-11.61] [-2.14] [-0.88]
2 -0.243 0.070 -0.078 -0.659 -0.231 -0.085
[-1.68] [ 0.55] [-2.86] [-15.80] [-5.46] [-2.69]
3 -0.161 0.087 -0.038 -0.716 -0.145 -0.021
[-1.08] [ 0.66] [-1.40] [-17.29] [-3.67] [-0.68]
4 -0.296 -0.103 -0.027 -0.773 -0.134 0.021
[-1.96] [-0.79] [-0.99] [-18.44] [-3.29] [ 0.66]
5 -0.237 -0.144 -0.012 -0.915 -0.042 -0.014
[-1.30] [-0.88] [-0.36] [-16.94] [-0.81] [-0.34]
—————————————————————————-
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.902 0.297 0.002 0.106 -0.117 0.975
[ 5.90] [ 4.71] [ 0.18] [ 5.26] [-6.14] [65.65]
2 0.666 -0.030 0.044 0.261 -0.118 1.032
[ 3.90] [-0.32] [ 2.36] [ 8.87] [-4.25] [47.66]
3 0.470 -0.250 0.084 0.191 -0.040 0.987
[ 2.75] [-2.27] [ 3.80] [ 5.43] [-1.21] [38.12]
4 0.764 -0.143 0.133 0.220 0.114 1.085
[ 3.49] [-0.82] [ 3.70] [ 3.88] [ 2.12] [25.87]
5 0.529 -0.238 0.249 -0.011 0.080 1.056
[ 1.60] [-0.84] [ 4.24] [-0.11] [ 0.86] [15.33]
—————————————————————————-
Portfolio Average Excess Returns (%/month)
0.4173 0.7280 0.8667 0.9825 1.3189
0.4166 0.6691 0.7109 0.7960 1.0824
0.4666 0.5282 0.5271 0.6807 0.9362
0.3574 0.5183 0.6086 0.6563 1.0001
0.4629 0.4065 0.6894 0.6831 1.0378
Portfolio Average Number of Firms
1180 384 308 316 619
50 43 40 41 49
16 17 17 17 16
5 6 7 7 6
2 3 3 3 2
Portfolio Average Firm Size ($10^6)
197 342 384 402 347
4031 4179 4238 4204 4060
12312 12441 12523 12585 12144
32714 33460 33582 33321 33464
90201 87180 100582 101603 121557
% 5×5 sort on size and momentum. Specify NYSE breaks and conditional sort
ind = makeBivSortInd(me, 5, R, 5, ‘sortType’, ‘unconditional’,
‘breaksFilterInd’, NYSE);
[res6, cond_res6] = runBivSort(ret, ind, 5, 5, dates, me);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.656 -0.111 1.054 1.483 0.376 -0.613
[ 2.06] [-1.11] [52.14] [46.14] [12.35] [-25.93]
2 1.077 0.090 0.975 1.241 0.652 -0.254
[ 3.92] [ 1.07] [57.56] [46.09] [25.59] [-12.83]
3 1.290 0.240 0.932 1.212 0.659 -0.101
[ 4.94] [ 2.55] [49.27] [40.28] [23.16] [-4.59]
4 1.325 0.099 0.995 1.217 0.629 0.122
[ 5.13] [ 0.98] [49.16] [37.81] [20.66] [ 5.17]
5 1.544 0.189 1.055 1.298 0.404 0.365
[ 6.03] [ 1.79] [49.43] [38.25] [12.58] [14.63]
6 0.418 -0.249 1.168 1.070 0.162 -0.644
[ 1.42] [-3.59] [83.51] [48.12] [ 7.71] [-39.38]
7 0.880 0.061 1.013 0.894 0.355 -0.285
[ 3.68] [ 1.11] [91.33] [50.69] [21.29] [-21.99]
8 0.974 0.049 0.968 0.777 0.371 -0.043
[ 4.59] [ 0.84] [82.87] [41.87] [21.14] [-3.19]
9 1.162 0.082 0.999 0.888 0.348 0.140
[ 5.44] [ 1.38] [83.84] [46.89] [19.41] [10.04]
10 1.401 0.096 1.133 1.011 0.212 0.377
[ 6.15] [ 1.64] [96.11] [53.95] [11.98] [27.34]
11 0.387 -0.140 1.182 0.675 0.087 -0.718
[ 1.39] [-2.03] [85.24] [30.59] [ 4.16] [-44.28]
12 0.773 0.041 1.055 0.539 0.254 -0.302
[ 3.46] [ 0.77] [97.48] [31.33] [15.61] [-23.84]
13 0.868 0.042 1.001 0.506 0.273 -0.095
[ 4.34] [ 0.84] [98.32] [31.27] [17.85] [-8.01]
14 0.926 -0.042 0.980 0.517 0.252 0.157
[ 5.05] [-0.87] [101.54] [33.70] [17.39] [13.89]
15 1.263 0.039 1.121 0.701 0.063 0.449
[ 6.22] [ 0.74] [103.72] [40.81] [ 3.88] [35.55]
16 0.419 -0.021 1.229 0.303 0.083 -0.783
[ 1.50] [-0.25] [72.48] [11.22] [ 3.25] [-39.47]
17 0.670 0.061 1.079 0.172 0.148 -0.349
[ 3.18] [ 1.13] [99.12] [ 9.93] [ 9.04] [-27.40]
18 0.810 0.080 1.002 0.219 0.176 -0.108
[ 4.38] [ 1.54] [94.97] [13.08] [11.11] [-8.75]
19 0.928 0.019 1.027 0.207 0.196 0.142
[ 5.26] [ 0.36] [98.38] [12.48] [12.51] [11.67]
20 1.228 0.067 1.113 0.429 0.029 0.465
[ 6.50] [ 1.19] [97.92] [23.75] [ 1.68] [34.96]
21 0.335 0.057 1.141 -0.039 -0.021 -0.766
[ 1.33] [ 0.64] [63.07] [-1.36] [-0.76] [-36.28]
22 0.530 0.155 0.979 -0.173 0.048 -0.438
[ 2.80] [ 2.87] [90.16] [-10.03] [ 2.93] [-34.55]
23 0.624 0.054 0.977 -0.177 0.091 -0.157
[ 3.72] [ 1.17] [104.58] [-11.92] [ 6.46] [-14.41]
24 0.778 0.013 0.990 -0.195 0.075 0.148
[ 5.06] [ 0.30] [111.37] [-13.79] [ 5.65] [14.23]
25 0.958 -0.091 1.092 -0.016 0.004 0.461
[ 5.66] [-1.75] [104.67] [-0.94] [ 0.23] [37.81]
—————————————————————————-
GRS test results: p-value F-stat df1 df2
full test – 0.0016751 2.0593 25 1112
partial test – 8.8818e-16 5.3647 25 1116
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 -0.314 0.168 0.088 -1.523 -0.396 -0.151
[-1.58] [ 1.20] [ 3.09] [-33.92] [-9.32] [-4.57]
2 -0.547 0.065 0.004 -1.414 -0.604 -0.184
[-3.07] [ 0.63] [ 0.19] [-43.02] [-19.42] [-7.63]
3 -0.666 -0.185 0.045 -1.389 -0.568 -0.056
[-3.79] [-1.82] [ 2.18] [-42.62] [-18.44] [-2.33]
4 -0.547 -0.085 -0.005 -1.412 -0.554 0.025
[-2.97] [-0.80] [-0.25] [-41.37] [-17.15] [ 1.01]
5 -0.586 -0.280 0.037 -1.313 -0.400 0.096
[-3.27] [-2.35] [ 1.54] [-34.38] [-11.07] [ 3.43]
—————————————————————————-
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.889 0.301 0.001 -0.186 0.028 0.978
[ 4.83] [ 2.40] [ 0.03] [-4.63] [ 0.74] [33.20]
2 0.983 0.345 -0.035 -0.059 0.050 1.021
[ 6.10] [ 4.48] [-2.25] [-2.38] [ 2.16] [56.31]
3 0.876 0.179 -0.062 0.026 -0.024 1.167
[ 4.76] [ 2.23] [-3.81] [ 1.03] [-0.97] [61.71]
4 0.809 0.088 -0.116 0.127 -0.054 1.247
[ 4.02] [ 0.98] [-6.43] [ 4.40] [-1.99] [58.96]
5 0.608 -0.148 -0.049 0.024 0.024 1.227
[ 3.08] [-1.49] [-2.43] [ 0.74] [ 0.81] [52.37]
—————————————————————————-
Portfolio Average Excess Returns (%/month)
0.6558 1.0767 1.2895 1.3252 1.5444
0.4182 0.8803 0.9741 1.1622 1.4013
0.3868 0.7728 0.8675 0.9259 1.2627
0.4195 0.6704 0.8099 0.9282 1.2283
0.3351 0.5298 0.6238 0.7782 0.9582
Portfolio Average Number of Firms
657 300 234 221 348
100 90 85 86 123
56 67 69 71 88
40 56 62 65 70
28 51 62 66 58
Portfolio Average Firm Size ($10^6)
38 50 53 55 57
250 257 260 260 258
600 614 623 623 615
1506 1543 1539 1544 1538
8893 11261 12043 11938 11134
% 5×5 sort on size and momentum. Specify NYSE breaks and conditional sort
ind = makeBivSortInd(me, 5, R, 5, ‘unconditional’, NYSE);
[res7, cond_res7] = runBivSort(ret, ind, 5, 5, dates, me);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.656 -0.111 1.054 1.483 0.376 -0.613
[ 2.06] [-1.11] [52.14] [46.14] [12.35] [-25.93]
2 1.077 0.090 0.975 1.241 0.652 -0.254
[ 3.92] [ 1.07] [57.56] [46.09] [25.59] [-12.83]
3 1.290 0.240 0.932 1.212 0.659 -0.101
[ 4.94] [ 2.55] [49.27] [40.28] [23.16] [-4.59]
4 1.325 0.099 0.995 1.217 0.629 0.122
[ 5.13] [ 0.98] [49.16] [37.81] [20.66] [ 5.17]
5 1.544 0.189 1.055 1.298 0.404 0.365
[ 6.03] [ 1.79] [49.43] [38.25] [12.58] [14.63]
6 0.418 -0.249 1.168 1.070 0.162 -0.644
[ 1.42] [-3.59] [83.51] [48.12] [ 7.71] [-39.38]
7 0.880 0.061 1.013 0.894 0.355 -0.285
[ 3.68] [ 1.11] [91.33] [50.69] [21.29] [-21.99]
8 0.974 0.049 0.968 0.777 0.371 -0.043
[ 4.59] [ 0.84] [82.87] [41.87] [21.14] [-3.19]
9 1.162 0.082 0.999 0.888 0.348 0.140
[ 5.44] [ 1.38] [83.84] [46.89] [19.41] [10.04]
10 1.401 0.096 1.133 1.011 0.212 0.377
[ 6.15] [ 1.64] [96.11] [53.95] [11.98] [27.34]
11 0.387 -0.140 1.182 0.675 0.087 -0.718
[ 1.39] [-2.03] [85.24] [30.59] [ 4.16] [-44.28]
12 0.773 0.041 1.055 0.539 0.254 -0.302
[ 3.46] [ 0.77] [97.48] [31.33] [15.61] [-23.84]
13 0.868 0.042 1.001 0.506 0.273 -0.095
[ 4.34] [ 0.84] [98.32] [31.27] [17.85] [-8.01]
14 0.926 -0.042 0.980 0.517 0.252 0.157
[ 5.05] [-0.87] [101.54] [33.70] [17.39] [13.89]
15 1.263 0.039 1.121 0.701 0.063 0.449
[ 6.22] [ 0.74] [103.72] [40.81] [ 3.88] [35.55]
16 0.419 -0.021 1.229 0.303 0.083 -0.783
[ 1.50] [-0.25] [72.48] [11.22] [ 3.25] [-39.47]
17 0.670 0.061 1.079 0.172 0.148 -0.349
[ 3.18] [ 1.13] [99.12] [ 9.93] [ 9.04] [-27.40]
18 0.810 0.080 1.002 0.219 0.176 -0.108
[ 4.38] [ 1.54] [94.97] [13.08] [11.11] [-8.75]
19 0.928 0.019 1.027 0.207 0.196 0.142
[ 5.26] [ 0.36] [98.38] [12.48] [12.51] [11.67]
20 1.228 0.067 1.113 0.429 0.029 0.465
[ 6.50] [ 1.19] [97.92] [23.75] [ 1.68] [34.96]
21 0.335 0.057 1.141 -0.039 -0.021 -0.766
[ 1.33] [ 0.64] [63.07] [-1.36] [-0.76] [-36.28]
22 0.530 0.155 0.979 -0.173 0.048 -0.438
[ 2.80] [ 2.87] [90.16] [-10.03] [ 2.93] [-34.55]
23 0.624 0.054 0.977 -0.177 0.091 -0.157
[ 3.72] [ 1.17] [104.58] [-11.92] [ 6.46] [-14.41]
24 0.778 0.013 0.990 -0.195 0.075 0.148
[ 5.06] [ 0.30] [111.37] [-13.79] [ 5.65] [14.23]
25 0.958 -0.091 1.092 -0.016 0.004 0.461
[ 5.66] [-1.75] [104.67] [-0.94] [ 0.23] [37.81]
—————————————————————————-
GRS test results: p-value F-stat df1 df2
full test – 0.0016751 2.0593 25 1112
partial test – 8.8818e-16 5.3647 25 1116
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 -0.314 0.168 0.088 -1.523 -0.396 -0.151
[-1.58] [ 1.20] [ 3.09] [-33.92] [-9.32] [-4.57]
2 -0.547 0.065 0.004 -1.414 -0.604 -0.184
[-3.07] [ 0.63] [ 0.19] [-43.02] [-19.42] [-7.63]
3 -0.666 -0.185 0.045 -1.389 -0.568 -0.056
[-3.79] [-1.82] [ 2.18] [-42.62] [-18.44] [-2.33]
4 -0.547 -0.085 -0.005 -1.412 -0.554 0.025
[-2.97] [-0.80] [-0.25] [-41.37] [-17.15] [ 1.01]
5 -0.586 -0.280 0.037 -1.313 -0.400 0.096
[-3.27] [-2.35] [ 1.54] [-34.38] [-11.07] [ 3.43]
—————————————————————————-
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.889 0.301 0.001 -0.186 0.028 0.978
[ 4.83] [ 2.40] [ 0.03] [-4.63] [ 0.74] [33.20]
2 0.983 0.345 -0.035 -0.059 0.050 1.021
[ 6.10] [ 4.48] [-2.25] [-2.38] [ 2.16] [56.31]
3 0.876 0.179 -0.062 0.026 -0.024 1.167
[ 4.76] [ 2.23] [-3.81] [ 1.03] [-0.97] [61.71]
4 0.809 0.088 -0.116 0.127 -0.054 1.247
[ 4.02] [ 0.98] [-6.43] [ 4.40] [-1.99] [58.96]
5 0.608 -0.148 -0.049 0.024 0.024 1.227
[ 3.08] [-1.49] [-2.43] [ 0.74] [ 0.81] [52.37]
—————————————————————————-
Portfolio Average Excess Returns (%/month)
0.6558 1.0767 1.2895 1.3252 1.5444
0.4182 0.8803 0.9741 1.1622 1.4013
0.3868 0.7728 0.8675 0.9259 1.2627
0.4195 0.6704 0.8099 0.9282 1.2283
0.3351 0.5298 0.6238 0.7782 0.9582
Portfolio Average Number of Firms
657 300 234 221 348
100 90 85 86 123
56 67 69 71 88
40 56 62 65 70
28 51 62 66 58
Portfolio Average Firm Size ($10^6)
38 50 53 55 57
250 257 260 260 258
600 614 623 623 615
1506 1543 1539 1544 1538
8893 11261 12043 11938 11134

Replicating Fama and French (1993)

Now that we are familiar with the functionality for bivariate sorts, we can easily replicate the main results in Fama and French (1993) using only a few lines of code. We’ll run an unconditional 5×5 double sort on size and book-to-market using NYSE breakpoints. The runBivSort() function will print out, among other results, the average returns, number of stocks, and average market capitalizations for the twenty-five portfolios.
There will be some small differences in the results due to backfilling bias, etc., but the general trends should hold. Our book-to-market variable also includes the Davis, Fama, and French (2000) book equity values, which could further result in some differences.
% Load some variables
clear
clc
 
load ret
load dates
load me
load NYSE
load bm
 
% Fama-French get rid of negative BE firms
bm(bm<0) = nan;
 
% Do the double sort
ind = makeBivSortInd(me, 5, bm, 5, ‘unconditional’, NYSE);
[res8, cond_res8] = runBivSort(ret, ind, 5, 5, dates, me, ‘timePeriod’, [196306 199112]);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.331 -0.394 1.040 1.369 -0.256 0.041
[ 0.80] [-3.45] [38.67] [33.80] [-5.56] [ 1.28]
2 0.650 -0.061 0.977 1.229 0.066 -0.046
[ 1.77] [-0.72] [48.94] [40.87] [ 1.93] [-1.95]
3 0.722 -0.064 0.942 1.146 0.272 -0.008
[ 2.13] [-0.93] [57.91] [46.79] [ 9.78] [-0.42]
4 0.847 0.062 0.900 1.084 0.382 -0.018
[ 2.65] [ 0.99] [61.37] [49.08] [15.22] [-1.06]
5 0.964 0.003 0.955 1.191 0.636 0.015
[ 2.82] [ 0.04] [57.96] [48.02] [22.56] [ 0.76]
6 0.420 -0.146 1.112 0.990 -0.432 0.015
[ 1.06] [-1.64] [53.15] [31.44] [-12.07] [ 0.61]
7 0.637 0.028 1.023 0.915 0.006 -0.063
[ 1.87] [ 0.38] [59.20] [35.16] [ 0.19] [-3.07]
8 0.880 0.190 0.973 0.831 0.245 -0.025
[ 2.83] [ 2.80] [60.84] [34.49] [ 8.96] [-1.34]
9 0.916 0.152 0.972 0.691 0.454 0.012
[ 3.15] [ 2.28] [61.58] [29.09] [16.83] [ 0.62]
10 0.992 0.052 1.065 0.859 0.693 0.016
[ 3.04] [ 0.72] [62.89] [33.70] [23.94] [ 0.79]
11 0.422 -0.018 1.090 0.675 -0.426 -0.026
[ 1.18] [-0.23] [58.64] [24.13] [-13.38] [-1.20]
12 0.655 0.083 1.013 0.612 0.024 -0.015
[ 2.13] [ 1.09] [56.09] [22.50] [ 0.78] [-0.72]
13 0.642 0.021 0.975 0.541 0.304 -0.043
[ 2.28] [ 0.28] [54.86] [20.23] [10.01] [-2.08]
14 0.864 0.188 0.980 0.434 0.492 -0.031
[ 3.23] [ 2.76] [61.05] [17.96] [17.91] [-1.64]
15 0.908 0.004 1.065 0.639 0.707 0.035
[ 2.95] [ 0.05] [51.43] [20.51] [19.94] [ 1.44]
16 0.457 0.112 1.055 0.319 -0.449 0.000
[ 1.43] [ 1.43] [56.83] [11.43] [-14.14] [ 0.02]
17 0.344 -0.098 1.072 0.261 0.004 -0.078
[ 1.17] [-1.18] [54.50] [ 8.83] [ 0.11] [-3.36]
18 0.604 0.053 1.046 0.226 0.283 -0.052
[ 2.19] [ 0.62] [51.77] [ 7.42] [ 8.19] [-2.20]
19 0.772 0.116 1.029 0.235 0.547 -0.042
[ 2.90] [ 1.40] [52.37] [ 7.95] [16.28] [-1.82]
20 0.892 0.071 1.143 0.343 0.733 -0.019
[ 2.94] [ 0.69] [46.78] [ 9.31] [17.53] [-0.67]
21 0.364 0.203 0.963 -0.202 -0.438 -0.014
[ 1.37] [ 2.91] [58.50] [-8.15] [-15.57] [-0.75]
22 0.339 -0.018 1.022 -0.188 -0.017 -0.006
[ 1.33] [-0.23] [57.57] [-7.02] [-0.57] [-0.28]
23 0.332 -0.135 0.966 -0.248 0.204 0.071
[ 1.40] [-1.44] [43.59] [-7.45] [ 5.38] [ 2.71]
24 0.518 -0.028 1.009 -0.204 0.563 -0.032
[ 2.20] [-0.36] [56.01] [-7.51] [18.27] [-1.50]
25 0.486 -0.241 1.023 -0.037 0.774 0.029
[ 1.89] [-2.12] [38.15] [-0.90] [16.88] [ 0.91]
—————————————————————————-
GRS test results: p-value F-stat df1 df2
full test – 0.024919 1.6724 25 313
partial test – 0.00061999 2.2778 25 317
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.032 0.597 -0.077 -1.571 -0.182 -0.055
[ 0.11] [ 4.07] [-2.23] [-30.19] [-3.09] [-1.35]
2 -0.311 0.044 0.045 -1.417 -0.083 0.040
[-1.30] [ 0.40] [ 1.74] [-36.34] [-1.88] [ 1.31]
3 -0.390 -0.071 0.024 -1.395 -0.068 0.079
[-1.63] [-0.65] [ 0.92] [-35.83] [-1.55] [ 2.59]
4 -0.329 -0.090 0.109 -1.287 0.181 -0.014
[-1.54] [-0.93] [ 4.76] [-37.50] [ 4.64] [-0.50]
5 -0.477 -0.244 0.068 -1.228 0.138 0.014
[-2.08] [-1.73] [ 2.04] [-24.51] [ 2.43] [ 0.35]
—————————————————————————-
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 4-factor model
—————————————————————————-
xret alpha mkt smb hml umd
1 0.632 0.397 -0.085 -0.178 0.892 -0.026
[ 3.45] [ 3.13] [-2.85] [-3.95] [17.43] [-0.73]
2 0.572 0.197 -0.048 -0.131 1.126 0.001
[ 2.92] [ 1.71] [-1.74] [-3.20] [24.13] [ 0.02]
3 0.486 0.022 -0.025 -0.036 1.132 0.061
[ 2.61] [ 0.21] [-0.98] [-0.95] [26.35] [ 2.07]
4 0.434 -0.041 0.089 0.023 1.182 -0.020
[ 2.26] [-0.35] [ 3.16] [ 0.55] [24.70] [-0.59]
5 0.123 -0.444 0.059 0.165 1.212 0.043
[ 0.59] [-3.21] [ 1.82] [ 3.37] [21.75] [ 1.13]
—————————————————————————-
Portfolio Average Excess Returns (%/month)
0.3314 0.6500 0.7220 0.8468 0.9636
0.4198 0.6374 0.8798 0.9156 0.9920
0.4217 0.6553 0.6420 0.8641 0.9079
0.4573 0.3442 0.6037 0.7719 0.8917
0.3639 0.3386 0.3317 0.5175 0.4864
Portfolio Average Number of Firms
424 275 262 291 507
123 94 92 86 74
100 77 73 65 46
86 67 59 51 34
91 63 51 42 25
Portfolio Average Firm Size ($10^6)
22 23 22 21 17
99 98 100 99 97
231 234 234 238 233
575 578 583 592 594
3715 2897 2882 2782 2401
Let’s compare our 25 portfolios to the ones from the Ken French data library:
% Download the 25 portfolios from Ken French’s data library
fileURL = ‘https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/ftp/25_Portfolios_5x5_CSV.zip’;
ff25FileName = unzip(fileURL);
 
% Read in the the 25 portfolios
opts = detectImportOptions(char(ff25FileName));
FF25factors = readtable(char(ff25FileName), opts);
% Clean them up
e = find(isnan(FF25factors.Var1), 1, ‘first’);
FF25factors = FF25factors(1:e-1,:);
[~, ia, ib] = intersect(dates, FF25factors.Var1);
ff25 = nan(length(dates), 25);
ff25(ia,:) = table2array(FF25factors(ib, 2:end))/100;
 
% Plot a scatter plot with the average returns against ours
s = find(dates==196307);
e = find(dates==199112);
figure;
scatter(mean(res8.pret,1,‘omitnan’), mean(ff25(s:e,:), 1, ‘omitnan’));
xlabel(‘Replication’);
ylabel(‘Ken French Data Library’);
title(‘Average Returns to 25 size/btm portfolios’);
refline

Replicating Ken French’s momentum

Similarly, we can also replicate the UMD factor from Ken French data library:
% load a few variables
clear
clc
 
load ret
load me
load dates
load NYSE
load R
load ff
 
% Start by making the 6 (= 2×3) base portfolios
ind = makeBivSortInd(me, 2, R, [30 70], ‘unconditional’, NYSE);
[res, ~] = runBivSort(ret, ind, 2, 3, dates, me, ‘weighting’, ‘v’,
‘holdingPeriod’, 1,
‘factorModel’, 4,
‘printResults’, 0,
‘plotFigure’,0);
 
% UMD is made from the “corner” portfolios
umdrep = (res.pret(:,3) + res.pret(:,6) –
(res.pret(:,1) + res.pret(:,4)) )/2;
Now that we have the replicated UMD, let’s compare it with the one we downloaded from Ken French’s website during the Toolkit setup:
% Look at their correlations
fprintf(
Look at the correlation between UMD from Ken French and replicated UMD:
);
Look at the correlation between UMD from Ken French and replicated UMD:
indFin = isfinite(umdrep+umd);
corrcoef([umdrep(indFin) umd(indFin)])
ans = 2×2
1.0000 0.9961
0.9961 1.0000
% Look at their average returns
fprintf(
Compare the average return UMD from Ken French and replicated UMD:
);
Compare the average return UMD from Ken French and replicated UMD:
prt(nanols(umd(indFin), const(indFin)))
Ordinary Least-squares Estimates
R-squared = 0.0000
Rbar-squared = 0.0000
sigma^2 = 0.0022
Durbin-Watson = 1.8492
Nobs, Nvars = 1140, 1
***************************************************************
Variable Coefficient t-statistic t-probability
variable 1 0.638500 4.579802 0.000005
prt(nanols(umdrep(indFin), const(indFin)))
Ordinary Least-squares Estimates
R-squared = 0.0000
Rbar-squared = 0.0000
sigma^2 = 0.0022
Durbin-Watson = 1.8225
Nobs, Nvars = 1140, 1
***************************************************************
Variable Coefficient t-statistic t-probability
variable 1 0.639785 4.630946 0.000004
% Regress the two on each other
fprintf(
Regress the two on each other:
);
Regress the two on each other:
prt(nanols(umd, [const umdrep]))
Ordinary Least-squares Estimates
R-squared = 0.9922
Rbar-squared = 0.9922
sigma^2 = 0.0000
Durbin-Watson = 2.2448
Nobs, Nvars = 1140, 2
***************************************************************
Variable Coefficient t-statistic t-probability
variable 1 -0.004605 -0.370182 0.711316
variable 2 1.005189 380.286469 0.000000
prt(nanols(umdrep, [const umd]))
Ordinary Least-squares Estimates
R-squared = 0.9922
Rbar-squared = 0.9922
sigma^2 = 0.0000
Durbin-Watson = 2.2181
Nobs, Nvars = 1140, 2
***************************************************************
Variable Coefficient t-statistic t-probability
variable 1 0.009541 0.774107 0.439028
variable 2 0.987070 380.286469 0.000000
% Plot the two series
ibbots([umd umdrep], dates, ‘timePeriod’, 192801,
‘legendLabels’, {‘UMD’,‘UMDrep’});

Fama-MacBeth regressions

Next, we’ll show how to run Fama-MacBeth regressions using the Toolkit. Let’s start by loading a few variables:
clear
clc
 
load me
load bm
load R
load dates
load ret
To run a Fama-MacBeth regression, we use the runFamaMacBeth function. Let’s take a look at its help file:
The function has four required arguments:
  • dependent variable, y (nDates x nStocks)
  • indepedent variables (x) – horizontally concatenated regressors (nDates x nStocks*nRegressors)
  • dates vector, dates – nDates x 1
It also has multiple optional arguments. By default, if none of the optional arguments are specified, the function:
  • lags the independent variables by one period
  • requires at least 100 observations for each period to be included in the procedure
  • uses OLS regressions for the period-by-period cross-sectional regressions
  • uses OLS standard errors for the time-series regressions of the coefficients on a constant
  • prints the results
  • uses the full sample
  • winsorizes the independent variables
  • uses one percent for the winsorization
  • includes a constant
  • suppreses the warnings
  • does not include labels for the independent variables when printing the results
Let’s start with a basic example, where we estimate a Fama-MacBeth regression of returns on the log of market capitalization and the momentum signal:
runFamaMacBeth(100*ret, [log(me) R], dates);
———————————————————————-
Results from Fama – MacBeth regressions
———————————————————————-
Coeff t-stat
var 0 0.884 2.392
var 1 -0.199 -5.261
var 2 0.776 3.969
———————————————————————-
Then, let’s examine the various optional features:
% High mininimum number of observations
runFamaMacBeth(100*ret, [log(me) R], dates, ‘minObs’, 1000);
———————————————————————-
Results from Fama – MacBeth regressions
———————————————————————-
Coeff t-stat
var 0 0.649 1.914
var 1 -0.131 -4.164
var 2 0.900 5.949
———————————————————————-
% Use WLS for cross-sectional regressions with market caps as the weights
runFamaMacBeth(100*ret, [log(me) R], dates, ‘weightMatrix’, me);
———————————————————————-
Results from Fama – MacBeth regressions
———————————————————————-
Coeff t-stat
var 0 0.153 0.407
var 1 -0.058 -2.121
var 2 0.894 4.125
———————————————————————-
% Use Newey-West standard errors
runFamaMacBeth(100*ret, [log(me) R], dates, ‘neweyWestLags’, 12);
———————————————————————-
Results from Fama – MacBeth regressions
———————————————————————-
Coeff t-stat
var 0 0.884 2.148
var 1 -0.199 -4.280
var 2 0.776 4.302
———————————————————————-
% Use a different starting point for the sample
runFamaMacBeth(100*ret, [log(me) R], dates, ‘timePeriod’, 196307);
———————————————————————-
Results from Fama – MacBeth regressions
———————————————————————-
Coeff t-stat
var 0 0.931 2.420
var 1 -0.155 -4.260
var 2 0.733 4.485
———————————————————————-
% Use a different sample
runFamaMacBeth(100*ret, [log(me) R], dates, ‘timePeriod’, [192807 196306]);
———————————————————————-
Results from Fama – MacBeth regressions
———————————————————————-
Coeff t-stat
var 0 0.745 0.971
var 1 -0.266 -3.247
var 2 0.835 1.836
———————————————————————-
% Trim the independent variables instead of winsorizing them
runFamaMacBeth(100*ret, [log(me) R], dates, ‘trimIndicator’, 1);
———————————————————————-
Results from Fama – MacBeth regressions
———————————————————————-
Coeff t-stat
var 0 0.630 1.713
var 1 -0.169 -4.631
var 2 0.854 4.054
———————————————————————-
% Winsorize at the five percent level
runFamaMacBeth(100*ret, [log(me) R], dates, ‘winsorTrimPctg’, 5);
———————————————————————-
Results from Fama – MacBeth regressions
———————————————————————-
Coeff t-stat
var 0 0.664 1.697
var 1 -0.202 -5.122
var 2 0.943 3.980
———————————————————————-
% Suppres the constant
runFamaMacBeth(100*ret, [log(me) R], dates, ‘noConst’, 1);
———————————————————————-
Results from Fama – MacBeth regressions
———————————————————————-
Coeff t-stat
var 1 -0.122 -4.199
var 2 1.317 5.700
———————————————————————-
% Annually-updated variables (e.g., bm) are automatically filled in
runFamaMacBeth(100*ret, [log(me) log(bm) R], dates);
———————————————————————-
Results from Fama – MacBeth regressions
———————————————————————-
Coeff t-stat
var 0 0.951 2.749
var 1 -0.163 -4.864
var 2 0.246 4.885
var 3 0.699 3.610
———————————————————————-
% Keep any warnings
runFamaMacBeth(100*ret, [log(me) log(bm) R], dates, ‘keepWarnings’, 1);
———————————————————————-
Results from Fama – MacBeth regressions
———————————————————————-
Coeff t-stat
var 0 0.951 2.749
var 1 -0.163 -4.864
var 2 0.246 4.885
var 3 0.699 3.610
———————————————————————-
% Include labels when printing the results
runFamaMacBeth(100*ret, [log(me) log(bm) R], dates, ‘labels’, {‘Const’,‘log(me)’,‘log(bm)’,‘Momentum’});
———————————————————————-
Results from Fama – MacBeth regressions
———————————————————————-
Coeff t-stat
Const 0.951 2.749
log(me) -0.163 -4.864
log(bm) 0.246 4.885
Momentum 0.699 3.610
———————————————————————-

Transaction costs

The code also allow accounting for transaction costs. While we recommend using the high-frequency composite effective spread measure from Chen and Velikov (2022), the Toolkit setup code stores the effective bid-ask spread measure from Hasbrouck (2009) that was used in Novy-Marx and Velikov (2016). Thus, we can easily replicate some of the net return results. The following snippet replicates the momentum net returns in Table 3 of Novy-Marx and Velikov (2016):
% Load some variables
clear
clc
 
load gibbs_filled
load ret
load dates
load me
load nyse
load R
 
% Sort first
ind = makeUnivSortInd(R, 10, NYSE);
 
% Speed it up as the tcost calculation for all 10 ptfs takes a while (this will only calculate it for the short (=1) and long (=10) sides)
ind = 1 * (ind==1) +
2 * (ind==10);
res = runUnivSort(ret, ind, dates, me, ‘timePeriod’, [196306 201212],
‘tcosts’, tcosts,
‘plotFigure’, 0,
‘printResults’, 0);
 
% Print it
fprintf(
Gross return (t-stat) on momentum from Novy-Marx and Velikov (RFS, 2016), Table 3: %2.2f%%/mo (%2.2f).
, res.xret(end), res.txret(end));
Gross return (t-stat) on momentum from Novy-Marx and Velikov (RFS, 2016), Table 3: 1.37%/mo (4.83).
fprintf(
Net return (t-stat) on momentum from Novy-Marx and Velikov (RFS, 2016), Table 3: %2.2f%%/mo (%2.2f).
, res.netxret(end), res.tnetxret(end));
Net return (t-stat) on momentum from Novy-Marx and Velikov (RFS, 2016), Table 3: 0.70%/mo (2.48).

Anomalies

The Toolkit also allows us to work with large anomaly datasets, though currently that requires significant memory for some applications. The Toolkit setup creates and stores in novyMarxVelikovAnomalies.csv the twenty-three signals from Novy-Marx and Velikov (2016), which users can readily use for basic applications.

Price vs fundamental momentum

For example, this snippet of code replicates the main results in Novy-Marx (2018), which shows that fundamental momentum explains price momentum. The getAnomalySignals() has a nice feature which allows the user to read specific columns. In this case we’ll only read in the momentum and the two PEAD signals from the file that contains the twenty-three anomaly signals from Novy-Marx and Velikov (2016) and we’ll run some basic asset pricing tests (Fama-MacBeth regressions and spanning tests)
% Load a few variables
clear
clc
 
load ret
load me
load dates
load nyse
load ff
 
% Read in the momentum and two PEAD signals from the novyMarxVelikovAnomalies.csv
[anoms, ~] = getAnomalySignals(‘novyMarxVelikovAnomalies.csv’, ‘permno’, ‘dates’,
‘anomalyNames’, {‘momentum’,‘peadSUE’,‘peadCAR3’});
 
% Assign the three anomaly signals
mom = anoms(:,:,1);
peadSUE = anoms(:,:,2);
peadCAR3 = anoms(:,:,3);
 
 
% Show this using Fama-MacBeth regressions
load bm
load r
load GP
load AT
load FinFirms
 
gp = GP./AT;
 
% Table 1, column 1
runFamaMacBeth(100*ret, [R log(me) log(bm) gp ret], dates, ‘trimIndicator’, 1,
‘timePeriod’, [197501 201212],
‘labels’, {‘Const’,‘r_{2,12}’,‘ln(ME)’,‘ln(B/M)’,‘GP/A’,‘r_{0,1}’});
———————————————————————-
Results from Fama – MacBeth regressions
———————————————————————-
Coeff t-stat
Const 0.689 1.660
r_{2,12} 0.592 2.858
ln(ME) -0.055 -1.320
ln(B/M) 0.431 6.051
GP/A 0.880 6.617
r_{0,1} -4.688 -10.259
———————————————————————-
% Table 1, column 2
runFamaMacBeth(100*ret, [peadSUE peadCAR3 log(me) log(bm) gp ret], dates, ‘trimIndicator’, 1,
‘timePeriod’, [197501 201212],
‘labels’, {‘Const’,‘SUE’,‘CAR3’,‘ln(ME)’,‘ln(B/M)’,‘GP/A’,‘r_{0,1}’});
———————————————————————-
Results from Fama – MacBeth regressions
———————————————————————-
Coeff t-stat
Const 1.598 4.174
SUE 0.288 17.121
CAR3 5.200 18.514
ln(ME) -0.084 -1.862
ln(B/M) 0.378 4.853
GP/A 0.834 6.337
r_{0,1} -5.817 -11.836
———————————————————————-
% Table 1, column 3
runFamaMacBeth(100*ret, [R peadSUE peadCAR3 log(me) log(bm) gp ret], dates, ‘trimIndicator’, 1,
‘timePeriod’, [197501 201212],
‘labels’, {‘Const’,‘r_{2,12}’,‘SUE’,‘CAR3’,‘ln(ME)’,‘ln(B/M)’,‘GP/A’,‘r_{0,1}’});
———————————————————————-
Results from Fama – MacBeth regressions
———————————————————————-
Coeff t-stat
Const 1.378 3.187
r_{2,12} 0.119 0.567
SUE 0.274 20.551
CAR3 5.179 18.906
ln(ME) -0.089 -2.092
ln(B/M) 0.403 5.613
GP/A 0.842 6.488
r_{0,1} -5.988 -12.745
———————————————————————-
% Now let’s show it using spanning tests
% Start by making the 6 (= 2×3) base portfolios
ind = makeBivSortInd(me, 2, peadSUE, [30 70], ‘unconditional’, NYSE);
[resSUE, ~] = runBivSort(ret, ind, 2, 3, dates, me, ‘weighting’, ‘v’,
‘holdingPeriod’, 1,
‘factorModel’, 4,
‘printResults’, 0,
‘plotFigure’,0);
 
% SUE is made from the “corner” portfolios
sue = (resSUE.pret(:,3) + resSUE.pret(:,6) –
(resSUE.pret(:,1) + resSUE.pret(:,4)) )/2;
 
% Start by making the 6 (= 2×3) base portfolios
ind = makeBivSortInd(me, 2, peadCAR3, [30 70], ‘unconditional’, NYSE);
[resCAR3, ~] = runBivSort(ret, ind, 2, 3, dates, me, ‘weighting’, ‘v’,
‘holdingPeriod’, 1,
‘factorModel’, 4,
‘printResults’, 0,
‘plotFigure’,0);
 
% CAR3 is made from the “corner” portfolios
car3 = (resCAR3.pret(:,3) + resCAR3.pret(:,6) –
(resCAR3.pret(:,1) + resCAR3.pret(:,4)) )/2;
 
% Match the time period
s = find(dates==197501);
e = find(dates==201212);
 
% Table 2, column 1
prt(nanols(umd(s:e),[const(s:e) mkt(s:e) smb(s:e) hml(s:e)]))
Ordinary Least-squares Estimates
R-squared = 0.0700
Rbar-squared = 0.0639
sigma^2 = 0.0019
Durbin-Watson = 1.9073
Nobs, Nvars = 456, 4
***************************************************************
Variable Coefficient t-statistic t-probability
variable 1 0.866551 4.139653 0.000042
variable 2 -0.176499 -3.728773 0.000217
variable 3 0.073974 1.066326 0.286846
variable 4 -0.361094 -4.968704 0.000001
% Table 2, column 3
prt(nanols(umd(s:e),[const(s:e) mkt(s:e) smb(s:e) hml(s:e) sue(s:e) car3(s:e)]))
Ordinary Least-squares Estimates
R-squared = 0.4764
Rbar-squared = 0.4706
sigma^2 = 0.0011
Durbin-Watson = 1.8714
Nobs, Nvars = 456, 6
***************************************************************
Variable Coefficient t-statistic t-probability
variable 1 -0.564669 -3.199074 0.001476
variable 2 -0.104051 -2.879241 0.004176
variable 3 0.294116 5.444966 0.000000
variable 4 -0.055685 -0.971970 0.331588
variable 5 1.169757 13.619634 0.000000
variable 6 0.709313 5.384275 0.000000

Trading cost taxonomy from Novy-Marx and Velikov (2016)

This snippet of code replicates the full Table 3 from Novy-Marx and Velikov (2016):
% Load some variables
clear
clc
 
load dates
load me
load ret
load NYSE
load gibbs_filled
 
% Read in the 23 anomalies
[anoms23, labels23] = getAnomalySignals(‘novyMarxVelikovAnomalies.csv’, 1, 2);
 
% Store the number of anomalies
nAnoms = size(anoms23, 3);
 
% Get the starting dates. These are hard-coded here – bad old habits.
startDate = [1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 2 2 1 1 1 1 1 1]; % 1 – 196306, 2 – 197306
startDate(startDate==1) = max(196306, dates(1));
startDate(startDate==2) = max(197306, dates(1));
 
 
% Loop through the anomalies
for i = 1:nAnoms
% Run a univariate sort for each (value-weighted, decile-sort, NYSE breaks)
sortVar = anoms23(:, :, i);
ind = makeUnivSortInd(sortVar, 10, NYSE);
ind = 1 * (ind==1) +
2 * (ind==10);
res = runUnivSort(ret, ind, dates, me, ‘tcosts’, tcosts,
‘timePeriod’, [startDate(i) 201212],
‘plotFigure’, 0,
‘printResults’, 0);
 
% Store the results
coefs(i,:) = [res.xret(end) res.alpha(end) res.turnover 100*res.tcosts res.netxret];
tstats(i,:) = [res.txret(end) res.talpha(end) nan nan res.tnetxret];
end
 
% Print latex-style output
mat2Tex(coefs, tstats, labels23, 2)
size & 0.31 & -0.14 & 0.02 & 0.06 & 0.25\[-1pt]
& [1.55] & [-1.77] & & & [1.25]\[2pt]
value & 0.47 & -0.16 & 0.03 & 0.06 & 0.42\[-1pt]
& [2.62] & [-1.57] & & & [2.30]\[2pt]
grossProfitability & 0.39 & 0.50 & 0.02 & 0.03 & 0.36\[-1pt]
& [2.84] & [3.67] & & & [2.60]\[2pt]
valProf & 0.82 & 0.46 & 0.04 & 0.07 & 0.75\[-1pt]
& [4.92] & [3.67] & & & [4.50]\[2pt]
accruals & 0.34 & 0.38 & 0.06 & 0.10 & 0.24\[-1pt]
& [2.23] & [2.58] & & & [1.57]\[2pt]
assetGrowth & 0.40 & 0.11 & 0.07 & 0.11 & 0.28\[-1pt]
& [2.73] & [0.86] & & & [1.92]\[2pt]
investment & 0.52 & 0.32 & 0.07 & 0.11 & 0.42\[-1pt]
& [4.19] & [2.64] & & & [3.30]\[2pt]
piotroski & 0.34 & 0.40 & 0.06 & 0.09 & 0.23\[-1pt]
& [1.89] & [2.48] & & & [1.30]\[2pt]
issuance & 0.60 & 0.60 & 0.15 & 0.20 & 0.39\[-1pt]
& [3.88] & [4.17] & & & [2.55]\[2pt]
roe & 0.75 & 0.89 & 0.23 & 0.39 & 0.36\[-1pt]
& [3.21] & [4.81] & & & [1.54]\[2pt]
distress & 0.85 & 0.90 & 0.33 & 0.82 & 0.03\[-1pt]
& [2.54] & [4.84] & & & [0.10]\[2pt]
valMomProf & 1.34 & 0.43 & 0.28 & 0.46 & 0.88\[-1pt]
& [6.54] & [3.69] & & & [4.30]\[2pt]
valMom & 0.96 & -0.15 & 0.28 & 0.42 & 0.54\[-1pt]
& [4.64] & [-1.63] & & & [2.59]\[2pt]
idiosyncraticVolatility & 0.64 & 0.82 & 0.25 & 0.54 & 0.11\[-1pt]
& [2.14] & [4.99] & & & [0.36]\[2pt]
momentum & 1.37 & 0.37 & 0.35 & 0.67 & 0.70\[-1pt]
& [4.83] & [3.24] & & & [2.48]\[2pt]
peadSUE & 0.72 & 0.58 & 0.35 & 0.47 & 0.25\[-1pt]
& [4.43] & [4.18] & & & [1.52]\[2pt]
peadCAR3 & 0.94 & 0.90 & 0.36 & 0.58 & 0.36\[-1pt]
& [6.58] & [6.44] & & & [2.47]\[2pt]
industryMomentum & 0.83 & 0.77 & 0.87 & 1.13 & -0.31\[-1pt]
& [3.61] & [3.31] & & & [-1.31]\[2pt]
industryRelativeReversal & 1.02 & 1.09 & 0.91 & 1.80 & -0.79\[-1pt]
& [5.84] & [6.82] & & & [-4.60]\[2pt]
highFrequencyCombo & 1.61 & 1.49 & 0.91 & 1.47 & 0.14\[-1pt]
& [11.10] & [9.89] & & & [0.99]\[2pt]
reversals & 0.38 & 0.48 & 0.91 & 1.67 & -1.29\[-1pt]
& [1.74] & [2.34] & & & [-5.96]\[2pt]
seasonality & 0.86 & 0.85 & 0.91 & 1.47 & -0.60\[-1pt]
& [5.31] & [5.18] & & & [-3.75]\[2pt]
industryRelativeReversalLowVol & 1.35 & 1.30 & 0.94 & 1.03 & 0.31\[-1pt]
& [9.78] & [9.65] & & & [2.29]\[2pt]

Combination strategy – LASSO

Here we’ll showcase another nice feature of the Toolkit. We’ll combine anomalies to get an expected return signal and sort stocks based on that signal. The Toolkit has functionality that accomodates various combination techniques to get the signal including simple and value-weighted average rank, Fama-MacBeth fitted values, Partial Least Squares (PLS) filter, Instrumented Principal Component Analysis (IPCA) fitted expected retrns, and Least Absolute Shrinkage and Selection Operator (LASSO). The following snippet uses LASSO on the twenty-three anomalies from Novy-Marx and Velikov (2016) with rolling ten years of data and five-fold cross-validation to get an expected return signal.
% Load some variables
clear
clc
 
load dates
load me
load ret
load NYSE
load tcosts
 
% Determine the time period
timePeriod = [198001 202112];
 
% Read in the 23 anomalies
[anoms23, labels23] = getAnomalySignals(‘novyMarxVelikovAnomalies.csv’, 1, 2);
 
% Check & fill them in
[filledAnoms, keepAnoms, keepRollAnoms] = checkFillAnomalies(anoms23, me, dates, timePeriod);
Now working on checking the anomalies. Run started at 23-Jan-2023 23:55:56.
Done with 10/23 anomalies @ 23-Jan-2023 23:56:26.
Done with 20/23 anomalies @ 23-Jan-2023 23:56:54.
Done with checking the anomalies at 23-Jan-2023 23:57:03.
% Get the expected return signal
expRet = makeLassoRes(filledAnoms(:,:,keepAnoms), ret, dates, timePeriod);
Now working on LASSO with 22 anomales. Run started at 23-Jan-2023 23:57:05.
Done until 199511 @ 23-Jan-2023 23:59:20.
Done until 200511 @ 24-Jan-2023 00:03:22.
Done until 201511 @ 24-Jan-2023 00:06:35.
Done with LASSO at 24-Jan-2023 00:08:08.
% Let’s sort and see how a decile, value-weighted, NYSE-breaks strategy does
ind = makeUnivSortInd(expRet, 10, NYSE);
resVW = runUnivSort(ret, ind, dates, me, ‘weighting’, ‘v’,
‘factorModel’, 6,
‘tcosts’, tcosts,
‘plotFigure’, 0);
—————————————————————————-
Value-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 6-factor model
—————————————————————————-
xret alpha mkt smb hml rmw cma umd
1 0.107 -0.437 1.100 0.121 0.064 -0.270 -0.311 -0.355
[ 0.33] [-3.95] [38.67] [ 3.14] [ 1.34] [-5.44] [-4.40] [-14.87]
2 0.526 -0.252 1.068 0.168 0.017 -0.052 -0.045 -0.093
[ 1.93] [-2.51] [41.53] [ 4.81] [ 0.39] [-1.16] [-0.71] [-4.32]
3 0.749 -0.012 1.022 0.029 0.122 -0.059 0.009 -0.048
[ 3.06] [-0.15] [47.88] [ 0.99] [ 3.43] [-1.58] [ 0.17] [-2.69]
4 0.742 -0.012 0.972 -0.013 0.068 0.042 -0.019 -0.026
[ 3.27] [-0.16] [49.37] [-0.49] [ 2.06] [ 1.22] [-0.39] [-1.58]
5 0.838 0.044 0.964 0.019 0.019 0.108 0.064 -0.012
[ 3.83] [ 0.61] [52.79] [ 0.75] [ 0.62] [ 3.39] [ 1.42] [-0.81]
6 0.825 -0.077 1.027 0.033 0.044 0.101 0.102 0.092
[ 3.64] [-1.00] [52.30] [ 1.23] [ 1.33] [ 2.93] [ 2.09] [ 5.60]
7 1.001 0.179 0.959 0.021 0.006 0.059 0.123 0.070
[ 4.66] [ 2.32] [48.39] [ 0.80] [ 0.19] [ 1.71] [ 2.49] [ 4.18]
8 0.916 0.082 1.002 -0.026 0.041 0.069 0.021 0.064
[ 4.07] [ 1.05] [49.92] [-0.97] [ 1.23] [ 1.97] [ 0.43] [ 3.83]
9 0.987 0.091 0.985 0.042 -0.050 0.151 0.100 0.127
[ 4.47] [ 1.01] [42.44] [ 1.34] [-1.30] [ 3.72] [ 1.73] [ 6.51]
10 1.118 0.197 1.033 0.050 0.017 0.026 0.064 0.196
[ 4.67] [ 1.85] [37.67] [ 1.35] [ 0.37] [ 0.55] [ 0.94] [ 8.52]
L/S 1.011 0.634 -0.067 -0.071 -0.047 0.296 0.375 0.551
[ 4.52] [ 3.71] [-1.52] [-1.19] [-0.64] [ 3.86] [ 3.44] [14.95]
—————————————————————————-
% Run an equal-weighted one to compare
resEW = runUnivSort(ret, ind, dates, me, ‘weighting’ , ‘e’,
‘factorModel’, 6,
‘tcosts’, tcosts,
‘plotFigure’, 0);
—————————————————————————-
Equally-weighted portfolio sort, 1-month holding period
Excess returns, alphas, and loadings on:
Fama and French 6-factor model
—————————————————————————-
xret alpha mkt smb hml rmw cma umd
1 -0.411 -0.828 0.963 0.875 -0.096 -0.514 -0.076 -0.496
[-1.03] [-5.14] [23.26] [15.57] [-1.39] [-7.10] [-0.74] [-14.25]
2 0.373 -0.166 0.921 0.842 -0.023 -0.293 -0.053 -0.360
[ 1.10] [-1.42] [30.83] [20.77] [-0.46] [-5.61] [-0.71] [-14.35]
3 0.583 -0.014 0.904 0.774 0.056 -0.232 -0.012 -0.270
[ 1.88] [-0.15] [35.85] [22.60] [ 1.33] [-5.27] [-0.19] [-12.74]
4 0.775 0.126 0.914 0.766 0.092 -0.172 -0.045 -0.216
[ 2.58] [ 1.36] [38.37] [23.67] [ 2.31] [-4.13] [-0.77] [-10.80]
5 0.937 0.277 0.892 0.745 0.044 -0.139 -0.015 -0.179
[ 3.26] [ 3.16] [39.51] [24.32] [ 1.17] [-3.54] [-0.26] [-9.47]
6 1.126 0.442 0.899 0.705 0.087 -0.135 -0.006 -0.144
[ 3.99] [ 4.79] [37.91] [21.91] [ 2.19] [-3.25] [-0.11] [-7.22]
7 1.184 0.470 0.893 0.691 0.109 -0.075 0.006 -0.122
[ 4.35] [ 5.43] [40.18] [22.90] [ 2.94] [-1.93] [ 0.10] [-6.54]
8 1.358 0.623 0.908 0.702 0.108 -0.132 0.069 -0.086
[ 4.89] [ 6.54] [37.11] [21.13] [ 2.65] [-3.09] [ 1.14] [-4.20]
9 1.616 0.893 0.889 0.707 0.131 -0.129 0.066 -0.086
[ 5.88] [ 9.20] [35.65] [20.89] [ 3.14] [-2.96] [ 1.06] [-4.12]
10 2.203 1.462 0.894 0.746 0.124 -0.180 0.082 -0.035
[ 7.65] [11.69] [27.82] [17.09] [ 2.31] [-3.20] [ 1.02] [-1.31]
L/S 2.614 2.291 -0.069 -0.130 0.220 0.334 0.158 0.460
[12.21] [13.25] [-1.56] [-2.15] [ 2.97] [ 4.31] [ 1.44] [12.35]
—————————————————————————-
% Compare the performance of these strategies with and without trading
% costs
figure;
rets = [resVW.pret(:,end) resVW.netpret
resEW.pret(:,end) resEW.netpret];
ibbots(rets, dates, ‘timePeriod’, 199001,
‘legendLabels’, {‘VWgross’,‘VWnet’,‘EWgross’,‘EWnet’});

Anomaly decay results (McLean and Pontiff, 2016; Chen and Velikov, 2022)

The protocol for identifying new anomalies proposed by Novy-Marx and Velikov (2022) uses the anomalies from Chen and Zimmerman’s (2022) open source asset pricing library for benchmarking. Thus, the Toolkit includes a function, getChenZimmermanAnomalies(), that downloads (if necessary) and reads in the 200+ anomalies from their google drive. The code snippet here replicates the anomaly decay results from Chen and Velikov (2022), which is just an extension of the main results in McLean and Pontiff (2016).
clear
clc
 
fprintf(‘Start at %s.
,char(datetime(‘now’)));
Start at 24-Jan-2023 00:10:23.
load dates
load ret
load tcosts
load NYSE
load prc
load exchcd
load me
 
% Read the Chen and Zimmerman anomalies
[anoms, labels, anomaly_summary] = getChenZimmermanAnomalies();
File signed_predictors_dl_wide.zip with id 1OK1bYpaq8xUSLElgJiVDdqizP5YJOzHa was successfully downloaded from Google drive at 24-Jan-2023 00:12:08.
File SignalDoc.csv with id 1PDFl3pKwbY8DH5S9PWH_Op16HPo2wZL1 was successfully downloaded from Google drive at 24-Jan-2023 00:12:10.
Done getting signals at at 24-Jan-2023 00:42:46.
Warning: Column headers from the file were modified to make them valid MATLAB identifiers before creating variable names for the table. The original column headers are saved in the VariableDescriptions property.
Set ‘VariableNamingRule’ to ‘preserve’ to use the original column headers as table variable names.
 
% Store a few monthly indicators
mths = dates – 100*floor(dates/100);
JuneIndicator = (mths==6);
JuneDecemberIndicator = (mths==6 | mths==12);
quarterEndIndicator = (mths==3 | mths==6 | mths==9 | mths==12);
 
% Store some dimensions
[nMonths, nStocks, nAnoms] = size(anoms);
 
% Loop through the anomalies
for i=1:nAnoms
% Store the current anomaly
var = anoms(:,:,i);
% Finds the row in the summary file
r = find(strcmp(anomaly_summary.Acronym, labels(i)));
% Figure out the rebalancing period
if anomaly_summary.PortfolioPeriod(r) == 12 ||
anomaly_summary.PortfolioPeriod(r) == 36
var(~JuneIndicator,:) = nan;
elseif anomaly_summary.PortfolioPeriod(r) == 6
var(~JuneDecemberIndicator,:) = nan;
elseif anomaly_summary.PortfolioPeriod(r) == 3
var(~quarterEndIndicator,:) = nan;
end
% Check for potential filters
if ~strcmp(anomaly_summary.Filter(r), )
filterString = regexprep(anomaly_summary.Filter(r), ‘ ‘, );
if contains(filterString, ‘abs(prc)>1’)
var(abs(prc)<=1) = nan;
end
if contains(filterString, ‘abs(prc)>5’)
var(abs(prc)<=5) = nan;
end
if contains(filterString, ‘exchcd%in%c(1,2)’)
var(exchcd>2) = nan;
end
if contains(filterString, ‘exchcd==1’)
var(exchcd>1) = nan;
end
if contains(filterString, ‘me>me_nyse20’)
indME5 = makeUnivSortInd(me, 5, NYSE);
var(indME5==1) = nan;
end
end
% Determine the sample
if isnan(anomaly_summary.StartMonth(r))
sampleStart = 100*anomaly_summary.SampleStartYear(r) + 6;
else
sampleStart = 100*anomaly_summary.SampleStartYear(r) + anomaly_summary.StartMonth(r);
end
lastHoldDate = find(sum(isfinite(var),2)>0,1,‘last’) + anomaly_summary.PortfolioPeriod(r);
sampleEnd = dates(min(lastHoldDate, nMonths));
dts = [sampleStart sampleEnd];
% Determine the number of portfolios
if strcmp(anomaly_summary.Acronym(r), ‘ChangeInRecommendation’)
bpts = prctile(var,[20 80],2);
ind = 1 * (var<=repmat(bpts(:,1), 1, nStocks)) +
2 * (var>=repmat(bpts(:,2), 1, nStocks));
elseif ismember(anomaly_summary.Acronym(r), {‘NumEarnIncrease’,‘RDcap’})
ind = 1 * (var==0) +
2 * (var>0);
elseif strcmp(anomaly_summary.Cat_Form(r), ‘discrete’)
uVals = unique(var);
uVals(isnan(uVals)) = [];
ind = 1 * (var==min(uVals)) +
2 * (var==max(uVals));
else
if isnan(anomaly_summary.LSQuantile(r))
nptfs = 5;
else
nptfs = round(1/anomaly_summary.LSQuantile(r));
end
ind = makeUnivSortInd(var,nptfs);
if strcmp(anomaly_summary.QuantileFilter(r),‘NYSE’)
ind = makeUnivSortInd(var,nptfs,NYSE);
end
ind = 1 * (ind==1) +
2 * (ind==nptfs);
end
 
% Run the univariate regression
if strcmp(anomaly_summary.StockWeight(i), ‘VW’)
res(i,1) = runUnivSort(ret, ind, dates, me, ‘weighting’, ‘v’,
‘timePeriod’, dts,
‘plotFigure’, 0,
‘printResults’, 0);
else
res(i,1) = runUnivSort(ret, ind, dates, me, ‘weighting’, ‘e’,
‘timePeriod’, dts,
‘plotFigure’, 0,
‘printResults’, 0);
end
end
 
% Timekeeping
fprintf(‘Done running sorts at at %s.
, char(datetime(‘now’)));
Done running sorts at at 24-Jan-2023 00:50:29.
% Check how much memory is used
memory
Maximum possible array: 123870 MB (1.299e+11 bytes) *
Memory available for all arrays: 123870 MB (1.299e+11 bytes) *
Memory used by MATLAB: 70051 MB (7.345e+10 bytes)
Physical Memory (RAM): 195232 MB (2.047e+11 bytes)
* Limited by System Memory (physical + swap file) available.
% Make anomaly returns relative to publication
relativeRets = nan(2*nMonths+1, nAnoms);
 
% Loop through the anomalies
for i=1:nAnoms
% Find the row for this anomaly in the summary table
r = find(strcmp(anomaly_summary.Acronym,labels(i)));
 
% Store the publication date (assume December)
pubDate = 100*anomaly_summary.Year(r) + 12;
 
% Find the publication date in the univariate sort structure
e = find(res(i).dates==pubDate);
 
% Store the returns in the relative returns matrix
prePubInd = nMonths-e+1:nMonths;
relativeRets(prePubInd,i) = res(i).pret(1:e,end);
postPubInd = (nMonths+1):(nMonths+length(res(i).dates)-e);
relativeRets(postPubInd,i) = res(i).pret(e+1:end,end);
end
 
% Now let’s plot the figure
figure;
x = (-nMonths:nMonths)/12;
y = 100 * mean(relativeRets, 2, ‘omitnan’);
plot(x, y, ‘Color’, [0.8 0.8 0.8]);
hold on;
smoothY = 100 * moving(mean(relativeRets, 2, ‘omitnan’), 60);
plot(x, smoothY);
ylim([-0.5 1]);
xlim([-30 20]);
xlabel(‘Years relative to publication’);
title(‘Average returns across anomalies’);
legend(‘Average’,‘5-year moving average’);

Data download

Downloading additional data directly from WRDS

Let’s get Tesla’s daily stock returns and accounting information
clear
clc
 
Params.username = usernameUI(); % Input your WRDS username
Params.pass = passwordUI(); % Input your WRDS password
 
% Call WRDS connection
WRDS = callWRDSConnection(Params.username,Params.pass);
 
% Download Tesla’s stock price
ticker = ‘TSLA’;
qry = [‘select date, prc from CRSP.DSF left join CRSP.DSFHDR on dsf.permno=dsfhdr.permno where htick=”’, ticker, ””];
prcTable = fetch(WRDS, qry);
 
% Convert the dates to a datetime & sort
prcTable.date = datetime(prcTable.date);
prcTable = sortrows(prcTable,‘date’);
 
% Plot the price
figure;
plot(prcTable.date,prcTable.prc);
 
% Now let’s get the cumulative factor to adjust price
qry = [‘select date, prc, cfacpr from CRSP.DSF left join CRSP.DSFHDR on dsf.permno=dsfhdr.permno where htick=”’, ticker, ””];
newPrcTable = fetch(WRDS, qry);
 
% Convert the dates to a datetime & sort
newPrcTable.date = datetime(newPrcTable.date);
newPrcTable = sortrows(newPrcTable,‘date’);
 
% Calculate the adjusted price
newPrcTable.adjPrc = newPrcTable.prc ./ newPrcTable.cfacpr;
 
% Plot the adjusted price
hold on;
plot(newPrcTable.date, newPrcTable.adjPrc);
legend(‘Raw price’,‘Adjusted price’);
hold off;
Now let’s get Tesla’s quarterly revenue and assets:
qry = [‘select fdateq, rdq, atq, revtq from COMP.FUNDQ where tic=”’, ticker, ””];
acctTable = fetch(WRDS, qry);
 
% Convert the dates
acctTable.fdateq = datetime(acctTable.fdateq);
acctTable.rdq = datetime(acctTable.rdq);
 
% Plot their assets against the fiscal quarter end dates
figure;
plot(acctTable.fdateq, acctTable.atq/1000);
ylabel(‘Quarterly total assets ($ bln)’);
plot(acctTable.fdateq, acctTable.revtq);
ylabel(‘Quarterly total assets ($ mln)’);