This program is designed to calculate earnings surprises (SUE) and form portfolios based on SUE. These portfolios continue to display return drifts after the earnings announcement dates, and hence are referred to as post-earnings announcement drift (PEAD) portfolios. The logic of the program is based on the paper by Livnat and Mendenhall (2006). The sample universe of this code is set to be S&P 500 index constituents.

Notes before attempting the code

This code relies on a linking table before IBES and CRSP (ICLINK), which is invoked in Step 0. The static table "iclink.pkl" was the output of a separate python program ICLINK. Please make sure to run the ICLINK python code before running this PEAD code.

Unlike SAS, Python is not known for handling large dataset efficiently in memory. As a result of this constraint, one might not be able to run the entire sample period (1980-now) in python one shot. Instead, you can break up the sample period into batches by changing start and end date (begdate and enddate) of the program, before finally piecing the outputs together.

We start by importing proper packages and setting macro variables for the sample date. Notice the two sets of date variables: CRSP related date ranges are set to be a bit longer than the IBES date range to make sure earlier sample months have valid records.

Then we load the linking table between IBES and CRSP, stored from the ICLINK python code.

Step 1 below extracts index constituents information from Compustat, linking table between CRSP and Compustat, and security identifier information from IBES.

In the original SAS datasets, if a company's link is still effective, the variable link end date (linkenddt) will be marked as '.E', which in postgres table is converted into missing. To reverse this, I use today's date to fill in the missing linkenddt.

The block of code below extracts estimate related information from IBES.

As forecasts can be made based on either primary (P) or diluted (D) basis, the next step is to assign a dominant reporting basis for each security.

Step 3 handles the linkage between the analysts' forecasts and the actual earnings.

The section of code below adjusts all estimate and earnings announcement dates to the closest preceding trading date in CRSP to ensure that adjustment factors won't be missing after the merge.

We then merge the CRSP adjustment factors for all estimate and report dates:

One caveat in dealing with the comparison between forecasts and actuals is that companies may experience corporate actions during the time period in between, and hence comparing the raw figures can lead to artificial surprises! Therefore, we first need to adjust the estimates using adjustment factors from CRSP, to make sure they are based on the same underlying shares as the actuals.

Now the "new_value" variable reflects the adjusted estimates by analysts. The median estimates is calculated below.

Extract fundamental data from Compustat.

We now filter the sample based on the methodology described in Livnat & Mendenhall (2006):

  • earnings announcement date is not missing in Compustat

  • the price per share is missing from Compustat at fiscal quarter end

  • price is greater than $1

  • market cap is greater than $5 million

  • with valid market and book value of equity at fiscal quarter end

  • Earnings announcement dates obtained from Compustat and from IBES (if available) should not differ by more than one calendar day

Now that we have calculated the SUEs, the rest of the code sorts stocks into portfolios based on the SUEs, and analyzes the portfolio drifts post the formation date.

To estimate the drift, sum daily returns over the period from 1 day after the earnings announcement through the day of the following quarterly earnings announcement.

In this part of the code, I use SUE3 as the main measure of earnings surprises and form portfolios based on it.

Last but not least, if you want to visualize the portfolio performance over time: