1
$\begingroup$

I am writing my Master Thesis on momentum strategies including price momentum (UMD/MOM) and two fundamental momentum strategies (SUE and CAR3). Right now I'm trying to create the three momentum factors in order to compare their performance from 1975-2012. The UMD/MOM factor is a factor already constructed by Fama-French. The documentation can be seen on French' website: https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/Data_Library/det_mom_factor.html

I follow the method of Fama-French when constructing the MOM factor and then I try to follow the exact same procedure when creating the SUE and CAR3 factors, just like Robert Novy-Marx in his article "Fundamentally, Momentum is Fundamental Momentum" from 2015: http://rnm.simon.rochester.edu/research/FMFM.pdf. My data is collected directly from WRDS to R by following this code: https://drive.google.com/file/d/0BxvBvE2V-dFTVnZuLUFhZWNuazA/view?fbclid=IwAR0-uPDl1-kzRyugIVePTtIFxMiM5jQbbWqE9VxIdMftrs4adJ_zDGuh4Go

Novy-Marx (2015) creates a figure of the factors performance on p. 9 which is the figure I aim to replicate. My results of MOM and CAR3 are very similar to Fama-French and Robert. The figure bellow is my results, where the UMD factor is the one I have constructed and the MOM factor is the one I downloaded from Fama-French. This clearly showed that I have been able to replicate the UMD factor almost 1:1 and CAR3 is also very similar to Novy-Marx (2015).

enter image description here

However, when I include SUE to the graph it results in this:

enter image description here

But as you see SUE is unrealistically high. Since the procedure of constructing the performance of the factors are exactly the same as for UMD and CAR3 it implies that the problem must be in relation to my calculation of SUE itself. I therefore need some help with examining where exactly the problem occurs.

When calculating SUE I follow Novy-Marx (2015). He defines SUE as "the most recent year-over-year change in earnings per share, scaled by the standard deviation of the these earnings innovations over the last eight announcements, subject to a requirement of at least six observed announcements over the two year window.". I'll try to write step by step what exactly I do:

Step 1: I download Quarterly data from Compustat, including the variables DATADATE, GVKEY and EPSPXQ (Earnings Per Share (Basic) / Excluding Extraordinary Items).

res <- dbSendQuery(wrds,"select gvkey, datadate, epspxq from comp.fundq 
           where INDFMT='INDL' and DATAFMT='STD' and CONSOL='C' and POPSRC='D'")  
 
comp.fundq <- dbFetch(res, n = -1) 

Step 2: I arrange the data by datadate and gvkey to make sure the data is arranged in the right order before lagging EPSPXQ by 4 quarters.

comp.data <-  
  comp.data %>% 
  arrange(datadate, gvkey) %>% 
  group_by(gvkey) %>% 
  mutate(lag.eps = dplyr::lag(epspxq, n = 4, default = NA))

Step 3: I calculate Earnings Innovations by

comp.data$ei <- comp.data$epspxq-comp.data$lag.eps

Step 4: I calculate the rolling std. deviation of the earnings innovations calculated in step 3 over the last 8 announcements (width = 8) with a criteria where at least 6 observations of non NAs are required (Partial = 6)

FUN <- function(x) if (length(na.omit(x)) >= 6) sd(x, na.rm = TRUE) else NA 
roll1 <- function(z) rollapplyr(z, width = 8, FUN = FUN, fill = NA, partial = 6)   
comp.data <-  
  comp.data %>% 
  arrange(datadate, gvkey) %>% 
  transform(comp.data, roll1 = ave(ei, gvkey, FUN = roll1))

Step 5: I calculate SUE by

comp.data$sue <- comp.data$ei/comp.data$roll1 

Step 6: I download CCM data from WRDS as a step to merge Compustat Data with CRSP Data:

res <- dbSendQuery(wrds,"select GVKEY, LPERMNO, LINKDT, LINKENDDT, LINKTYPE, LINKPRIM 
                    from crsp.ccmxpf_lnkhist") 
 
ccm <- dbFetch(res, n = -1) 

Step 7: I merge my Compustat dataset comp.data (incl. my new SUE variable) with CCM:

ccm.comp <-   
  ccm %>% 
  filter(linktype %in% c("LU", "LC", "LS")) %>% 
  filter(linkprim %in% c("P", "C", "J")) %>% 
  merge(comp.data, by="gvkey") %>%  
  mutate(datadate = as.Date(datadate),  
         permno = as.factor(lpermno), 
         linkdt = as.Date(linkdt), 
         linkenddt = as.Date(linkenddt), 
         linktype = factor(linktype, levels=c("LC", "LU", "LS")), 
         linkprim = factor(linkprim, levels=c("P", "C", "J"))) %>% 
  filter(datadate >= linkdt & (datadate <= linkenddt | is.na(linkenddt))) %>% 
  arrange(datadate, permno, linktype, linkprim) %>% 
  distinct(datadate, permno, .keep_all = TRUE)

Step 8: I clean my data after the merge:

ccm.comp.cln <-  
  ccm.comp %>% 
  arrange(datadate, permno) %>% 
  select(datadate, permno, sue) %>% 
  mutate_if(is.numeric, funs(ifelse(is.infinite(.), NA, .))) %>%  
  mutate_if(is.numeric, funs(round(., 5)))
 
ccm.comp.cln$datadate <- strptime(as.character(ccm.comp.cln$datadate), "%Y-%m-%d") 
ccm.comp.cln$datadate <- format(ccm.comp.cln$datadate,"%Y%m") 

Step 9: I load in CRSP data and clean it the same way as Fama-French: " "... all CRSP firms incorporated in the US and listed on the NYSE, AMEX, or NASDAQ that have a CRSP share code of 10 or 11 at the beginning of month t, good shares and price data at the beginning of t, and good return data for t". This includes the handling of delist returns as well.

Step 10: I merge my CCM-Compustat data with my CRSP data:

na_locf_until = function(x, n) { 
  l <- cumsum(! is.na(x)) 
  c(NA, x[! is.na(x)])[replace(l, ave(l, l, FUN=seq_along) > (n+1), 0) + 1] 
} 
 
crsp.comp <- merge(x = crsp.data, y = ccm.comp.cln, by.x = c("permno", "date"), by.y = c("permno", "datadate"), all.x = TRUE) 

Can anyone help me identify where a problem could occur?

$\endgroup$

2 Answers 2

0
$\begingroup$

Apologies for sounding patronising as hell if this isn't the problem... but do the dates associated with your EPS data refer to (1) the accounting period in question, or (2) the dates these were reported?

So if (say) GE or JPM was reporting calendar Q4 2020 on Valentine's Day, would this be 31/12/2020 or 14/02/2021?

If the former, then using this date against prices then effectively overweights your portfolio to positive earnings beats and vice versa avoids negative misses... which would generate exactly the kind of spurious alpha your initial run seems to be showing. In which case, if you take the second last reported EPS versus the 5th, do you get a positive but substantially more realistic result?

$\endgroup$
1
  • $\begingroup$ When I posted this question the dates associated with my EPS data referred to the accounting period. However, I think your point is my actual problem here, so I need to use the dates EPS were reported instead which is the variable RDQ from Compustat. Do you have any suggestions on how I can do that? $\endgroup$ Commented Feb 22, 2021 at 9:23
0
$\begingroup$

It will be extremely hard for anyone to follow your computations without the data you have used. But to venture a guess: In the figure in the Novy-Marx paper, the series are scaled to a vol of 10%. What is the volatility of your series?

$\endgroup$
1
  • $\begingroup$ It is also very hard to give you all the details on the data I have used with the amount of observations and considerations I have had. However, I've added the code I have my inspiration from, when it comes to importing the data from WRDS into R. Regarding the scaling - yes I have scaled SUE to a vol of 10%. I've done that to both CAR3 and UMD as well which seems to work for them. $\endgroup$ Commented Feb 22, 2021 at 8:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.