*! version 1.2.2 15may2018

Title

csestudy -- Efficient Inference for Cross-Sectional Event Studies

Syntax

csestudy
depvar [indepvars]
[if]
, eventstartdate(csestudy##eventdate:date) firstpreeventdate(csestudy##firstpreeventdate:date) lastpreeventdate(csestudy##lastpreeventdate:date) [options]

Main
* eventstartdate(csestudy##eventstartdate:date) The start date of the event.

* firstpreeventdate(csestudy##firstpreeventdate:date) The first (i.e. earliest) date in the pre-event period.

* lastpreeventdate(csestudy##lastpreeventdate:date) The last (i.e. latest) date in the pre-event period.

gls Calculate GLS estimates.

npc(integer) Number of principal components. Defaults to 100

woodbury Use the Woodbury matrix identity for GLS instead of Cholesky decomposition. Faster but slightly less numerically precise. Requires gls.

coefsonly Calculates only the coefficients, skipping the significance tests. Programmer option only.

* eventstartdate(csestudy##eventstartdate:date)}, firstpreeventdate(csestudy##firstpreeventdate:date)}, and lastpreeventdate(csestudy##lastpreeventdate:date)} are required.

Description

csestudy calculates robust inference for cross-sectional event studies as described in Cohn, Johnson, Liu, and Wardlaw (2026) "Past is Prologue: Inference from the Cross Section of Returns Around an Event," Journal of Financial Economics 180, 104278.

The estimation uses a time-series adjusted portfolio approach to inference about standard errors in which the coefficients are compared against a pre-event window of daily returns and adjusted rejection criteria are computed in the form of a parameterized z-score and a p-value estimated from the empirical distribution (the preferred metric in this approach.)

Options

Main

eventstartdate(date) The start date of the event. This date refers to the time variable set by tsset.

lastpreeventdate() The last date in the pre-event period. This must be earlier than the eventstartdate later than lastpreeventdate.

firstpreeventdate(date) The first date in the pre-event period. This must be earlier than the eventstartdate and lastpreeventdate.

gls Calculate GLS estimates.

npc() Number of principal components. Defaults to 100.

woodbury Use the Woodbury matrix identity to compute the GLS transformation instead of a
Cholesky decomposition of the full covariance matrix. This inverts only a k x k matrix
(k = npc) rather than factoring the N x N covariance matrix, yielding a ~50-66% speedup
per iteration. The tradeoff is slightly reduced numerical precision due to the wide range
of idiosyncratic variances. Requires gls.

Remarks

The GLS estimation requires a strongly balanced panel in the pre-period in order to work, so any ids which do not have a full set of available returns in the pre-period will be dropped. This is done for the user, and the observations which satisfy this condition are stored in e(sample). This is usually not a major issue in daily stock market data, but if your sample is significantly cut down by this operation, you may have an unusual set of pre-period observations.

Calculating significance with the estimates also require that there is a sufficiently long window of available data prior to the firstpreeventdate. (Effectively a window equal to eventstartdate - firstpreeventdate prior to firstpreeventdate). The user should check that the data is at least mostly balanced before proceeding.

Event Date Input

The date options accept integer values or Stata expressions evaluated at runtime. For example:

. csestudy ret lag_LNMV, eventstartdate(100) ...

. csestudy ret lag_LNMV, eventstartdate(bofd("mycal",mdy(9,19,2011))) ...

It is important that the time variable tracks trading days rather than calendar days.
The simplest way to achieve this is with bcal create, which constructs a
business calendar from the dates in your data and generates a sequential trading-day
variable:

. bcal create trading, from(date) gen(trading_date) center(20081006) replace

. tsset permno trading_date

Alternatively, many users have historically used a simple sequential integer for the time
variable:

. bysort permno (date): gen time = _n

. tsset permno time

This works as long as the panel is strongly balanced (i.e. all observations begin at the
same date and exist on all trading days). The bcal approach is more robust to missing
data and non-trading days, while the sequential integer approach can be more convenient if
your data is already structured that way.

Sample Data

A synthetic dataset with realistic CRSP-like properties is included for testing. It contains
300 firms over 461 S&P 500 trading days (Jan 2007 - Nov 2008), with a size-dependent
abnormal return (0.15% per sd of lag_LNMV) injected on 2008-10-06. The panel is long
enough to support GLS estimation with a 200-day pre-event window.

Load directly from GitHub:

. use "https://raw.githubusercontent.com/MalcolmWardlaw/csestudy/release/examples/sample_data.dta", clear

Examples

Load the sample data and set up the panel:

. use "https://raw.githubusercontent.com/MalcolmWardlaw/csestudy/release/examples/sample_data.dta", clear

. bcal create trading, from(date) gen(trading_date) center(20081006) replace

. tsset permno trading_date

OLS with time-series corrected errors:

. csestudy ret lag_LNMV if abs(prc)>5, eventstartdate(0) firstpreeventdate(-200) lastpreeventdate(-1)

GLS with 100 principal components (Cholesky, default):

. csestudy ret lag_LNMV if abs(prc)>5, eventstartdate(0) firstpreeventdate(-200) lastpreeventdate(-1) gls npc(100)

GLS with Woodbury identity (faster, slightly less precise):

. csestudy ret lag_LNMV if abs(prc)>5, eventstartdate(0) firstpreeventdate(-200) lastpreeventdate(-1) gls npc(100) woodbury

Multi-day event window using cumulative returns:

. gen ret5 = ret + f1.ret + f2.ret + f3.ret + f4.ret

. csestudy ret5 lag_LNMV if abs(prc)>5, eventstartdate(0) firstpreeventdate(-204) lastpreeventdate(-5)