The Event Study Macro calculates Cumulative Abnormal Returns and various t-statistics (Patells t, Sign t, etc) for an input dataset (Inset) containing (permno,evtdate) combinations. Outputs mean cumulative abnormal return within the event window (set by the variables start and end) and different statistics into the output dataset (Outset). Abnormal returns are computed using either Market, Fama-French or Carhart 4-factor models during the estimation period. The program draws on some of the ideas outlined in the event study macro in Chapter 6 of Boehmer, Broussard and Kallunki (2002)
Macro call with an example
The following code performs an event study for the input dataset (INSET) with PERMNO as identifier (ID), date variable DATE (EVTDATE) with estimation window length of 110 trading days (ESTPER). The event window is set to be (-1,+1) using START and END parameters. The pre-event window’s length is set to 5 trading days (GAP). The group variable is identified by SUBGROUP field in INSET table. The benchmark risk model is specified as Fama-French 3-factor model. The cross-sectional output is DATA2 (OUTSET parameter), test statistics are in STATS (OUTSTATS parameter)
proc sql;
create table inset
as select distinct permno, MbrStartdt as date format date9., 1 as subgroup
from crsp.dsp500list_v2 where not missing(MbrStartDt) and
MbrStartDt between '01JAN1980'd and '31DEC2020'd;
quit;
%EVTSTUDY(INSET=inset, OUTSET=DATA2, OUTSTATS=STATS, ID=PERMNO, DATA=CRSP, EVTDATE=DATE, ESTPER=110, START=-1,END=+1,GAP=5,GROUP=SUBGROUP,MODEL=FF);
SAS macro
/* ********************************************************************************* */
/* ******************** W R D S R E S E A R C H M A C R O S ******************** */
/* ********************************************************************************* */
/* WRDS Macro: EVTSTUDY */
/* Summary : Performs an event study */
/* Author : Denys Glushkov, WRDS */
/* Update : November 2024 to reflect CRSP CIZ data format */
/* Modified : May 30, 2012 */
/* Parameters: */
/* - ID : Name of security identifier in INSET: PERMNO or CUSIP */
/* CUSIP should be at least 8 (eight) characters */
/* - INSET : Input dataset containg security IDs and event dates */
/* - OUTSET : Name of the output dataset to store mean CAR and t-stats */
/* - OUTSTATS:Name of the output dataset to store test statistics (Patell Z, etc) */
/* - EVTDATE: Name of the event date variable in INSET dataset */
/* - DATA : Name of CRSP library to use. CRSP and CRSPQ for annual and */
/* quarterly updates, respectively */
/* - ESTPER : Length of the estimation period in trading days over which */
/* the risk model is run, e.g., 110; */
/* - START : Beginning of the event window (relative to the event date, eg. -2) */
/* - END : End of the event window (relative to the event date, e.g., +1) */
/* - GAP : Length of pre-event window, i.e., number of trading days between */
/* the end of estimation period and the beginning of the event window */
/* -GROUP: Defines an subgroup (can be more than 2) */
/* -MODEL: Risk model to be used for risk-adjustment */
/* madj - Market-Adjusted Model (assumes stock beta=1) */
/* m - Standard Market Model (CRSP value-weighted index as the market) */
/* ff - Fama-French three factor model */
/* ffm - Carhart model that includes FF factors plus momentum */
/* ********************************************************************************* */
%MACRO EVTSTUDY (INSET=, OUTSET=, OUTSTATS=, ID=permno, EVTDATE=, DATA=CRSP,
ESTPER=, START=,END=,GAP=,GROUP=,MODEL=);
%local evtwin factors abret newvars;
%local oldoptions errors;
%let oldoptions=%sysfunc(getoption(mprint)) %sysfunc(getoption(notes))
%sysfunc(getoption(source));
%let errors=%sysfunc(getoption(errors));
options nonotes nomprint nosource errors=0;
%let evtwin=%eval(&end-&start+1); *length of event window in trading days;
/*depending on the model, define the model for abnormal returns*/
%if %lowcase(&model)=madj %then %do; %let factors=vwretd;
%let abret=ret-vwretd;
%let newvars=(intercept=alpha);
%end;%else
%if %lowcase(&model)=m %then %do; %let factors=vwretd;
%let abret=ret-alpha-beta*vwretd;
%let newvars=(intercept=alpha vwretd=beta);
%end;%else
%if %lowcase(&model)=ff %then %do;
%let factors=vwretd smb hml;
%let abret=ret-alpha-beta*vwretd-sminb*smb-hminl*hml;
%let newvars=(intercept=alpha vwretd=beta smb=sminb hml=hminl);
%end;%else
%if %lowcase(&model)=ffm %then %do;
%let factors=vwretd smb hml mom;
%let abret=ret-alpha-beta*vwretd-sminb*smb-hminl*hml-wminl*mom;
%let newvars=(intercept=alpha vwretd=beta smb=sminb hml=hminl mom=wminl);
%end;
%put; %put ### CREATING TRADING DAY CALENDAR...;
data _caldates;
merge &data..Wrds_dailyindexret_query (keep=dlycaldt rename=(dlycaldt=estper_beg))
&data..Wrds_dailyindexret_query (keep=dlycaldt firstobs=%eval(&estper) rename=(dlycaldt=estper_end))
&data..Wrds_dailyindexret_query (keep=dlycaldt firstobs=%eval(&estper+&gap+1) rename=(dlycaldt=evtwin_beg))
&data..Wrds_dailyindexret_query (keep=dlycaldt firstobs=%eval(&estper+&gap-&start+1) rename=(dlycaldt=&evtdate))
&data..Wrds_dailyindexret_query (keep=dlycaldt firstobs=%eval(&estper+&gap+&evtwin) rename=(dlycaldt=evtwin_end));
format estper_beg estper_end evtwin_beg &evtdate evtwin_end date9.;
if nmiss(estper_beg,estper_end,evtwin_beg,evtwin_end,&evtdate)=0;
time+1;
run;
%put ### DONE!;
/*If primary identifier is Cusip, then link in permno*/
%if %lowcase(&id)=cusip %then %do;
proc sql;
create view _link
as select permno, cusip,
min(SecInfoStartDt) as fdate format=date9., max(SecInfoEndDt) as ldate format=date9.
from &data..stkSecurityInfoHist
group by permno, cusip;
quit;
proc sql;
create table _temp
as select distinct b.permno, a.*
from &inset a left join _link b
on a.cusip=b.cusip and b.fdate<=a.&evtdate<=b.ldate
order by a.&evtdate;
quit;%end;
%else %do;
/*pre-sort the input dataset in case it is not sorted yet*/
proc sort data=&inset out=_temp;
by &evtdate;
run;
%end;
/*If event date is a non-trading day, select the closest */
/*trading day that follows the event day */
/*Merge in relevant dates from the trading calendar */
proc printto log=junk;run;
proc sql;
create table _temp (drop=&evtdate)
as select a.*, a.&evtdate as _edate format date9., b.*
from _temp a left join _caldates (drop=time) b
on b.&evtdate-a.&evtdate>=0
group by a.&evtdate
having (b.&evtdate-a.&evtdate)=min(b.&evtdate-a.&evtdate);
quit;
proc printto;run;
%put ; %put ### PREPARING BENCHMARK FACTORS... ;
proc sql;create table _factors
as select a.dlycaldt as date, a.vwretd, b.smb, b.hml, b.umd as mom
from &data..Wrds_dailyindexret_query (keep=dlycaldt vwretd) a left join ff.factors_daily b
on a.dlycaldt=b.date;
quit;
%put ### DONE! ;
%put; %put ### RETRIEVING RETURNS DATA FROM CRSP...;
proc printto log=junk;run;
proc sql;
create table _evtrets_temp
as select a.permno, a.dlycaldt as date format date9., a.DlyRet as ret1, b.*
from &data..stkDlySecurityData a, _temp b
where a.permno=b.permno and b.estper_beg<=a.DlyCalDt<=b.evtwin_end;
quit;
proc printto;run;
%put ### DONE!;
%put; %put ### MERGING IN BECHMARK FACTORS...;
proc printto log=junk;run;
proc sql;
create table _evtrets1
as select a.*, b.*, (c.time-d.time) as evttime
from _evtrets_temp a
left join _factors (keep=date &factors) b
on a.date=b.date
left join _caldates c
on a.date=c.&evtdate
left join _caldates d
on a._edate=d.&evtdate;
quit;
* Delisting return already incorporated in the new CIZ format;
data _evtrets;
set _evtrets1;
exret = ret1 - vwretd;
ret = ret1;
label exret = 'Market-adjusted total ret'
ret = "Ret adjusted for delisting";
run;
proc sort data = _evtrets;
by permno _edate date evttime;
run;
proc printto;run;
%put ### DONE!;
%put; %put ### ESTIMATING FACTOR EXPOSURES OVER THE ESTIMATION PERIOD...;
%if %lowcase(&model) ne madj %then %do;
/*estimate risk factor exposures during the estimation period*/
proc reg data=_evtrets edf outest=_params (rename=&newvars
keep=permno _edate intercept &factors _rmse_ _p_ _edf_) noprint;
where estper_beg<=date<=estper_end;
by permno _edate;
model ret=&factors;
quit;%end;
%else %do;
proc reg data=_evtrets edf outest=_params (rename=&newvars
keep=permno _edate intercept _rmse_ _p_ _edf_) noprint;
where estper_beg<=date<=estper_end;
by permno _edate;
model ret=;
quit;%end;
%put ### DONE!;
%put; %put ### CALCULATING ONE-DAY ABNORMAL RETURN IN THE EVENT WINDOW...;
data _abrets/view=_abrets;
merge _evtrets (where=(evtwin_beg<=date<=evtwin_end) in=a) _params;
by permno _edate;
abret=&abret;
logret=log(1+ret);
var_estp=_rmse_*_rmse_;
nobs=_p_+_edf_;
label var_estp='Estimation Period Variance'
abret= 'One-day Abnormal Return (AR)'
ret= 'Raw Return'
_edate= 'Event Date'
evttime= "Trading day within (&start,&end) event window";
drop &factors _p_ _edf_ estper_beg estper_end;
if a;
run;
%put ### DONE!;
%put; %put ### CALCULATING CARS AND VARIOUS STATISTICS...;
proc means data=_abrets noprint;
by permno _edate;
id &group var_estp;
output out=_car sum(logret)=cret sum(abret)=car n(abret)=nrets;
/*calculate Standardized Cumulative Abnormal Returns*/
data _car; set _car;
poscar=car>0;
scar=car/(&evtwin*var_estp)**0.5;
cret=exp(cret)-1;
label poscar='Positive Abnormal Return Dummy'
scar= 'Standardized Cumulative Abnormal Return (SCAR)'
car= 'Cumulative Abnormal Return (CAR)'
cret= 'Cumulative Raw Return'
nrets= 'Number of non-missing abnormal returns within event window';
/*compute stats across all events (i.e., permno-event date combinations*/
proc means data=_car noprint;
var cret car scar poscar;
class &group;
output out=_test
mean= n= t=/autoname;
/*calculate different stats for assessing */
/*statistical signficance of abnormal returns*/
data &outstats; set _test;
tpatell=scar_mean*((scar_n)**0.5);
tsign=(poscar_mean-0.5)/sqrt(0.25/poscar_n);
format cret_mean car_mean percent7.5;
label tpatell= "Patell's t-stat"
car_mean= 'Mean Cumulative Abnormal Return'
cret_mean= 'Mean Cumulative Raw Return'
scar_mean= 'Mean Cumulative Standardized Abnormal Return'
car_t= 'Cross-sectional t-stat'
scar_t= "Boehmer's et al. (1991) t-stat"
car_n= 'Number of events in the portfolio'
poscar_mean= 'Percent of positive abnormal returns'
tsign= 'Sign-test statistic';
drop cret_N scar_N poscar_N cret_t poscar_t;
run;
%put ### DONE!;
proc print label u;
title1 "Output for dataset &inset for a
(&start,&end) event window using &model model";
id &group;
var cret_mean car_mean scar_mean poscar_mean
car_n tsign tpatell car_t scar_t;
%if "&group" ne "" %then %do;
title2 "Test for Equality of CARs among groups defined by &group";
/*To find out the results of the hypothesis test for comparing groups */
/*find the row of output labeled 'Model' and look at the column labeled */
/*F-value for the Fisher statistic and Pr>F for the associated p-value */
/*HOVTEST tests for whether variances of two groups are the same */
proc glm data=_car;
class &group;
model scar=&group;
means &group /hovtest;
proc npar1way data=_car wilcoxon;
var scar;
class &group;
%end;
run;
/*create the final output dataset*/
data &outset;
merge &inset (in=a rename=(&evtdate=_edate))
_abrets(keep=permno _edate date evttime ret abret var_estp)
_car (keep=permno _edate cret car scar nrets);
by permno _edate;
rename _edate=evtdate;
label _edate='Event date'
date='Trading date in event window';
format _edate date9. date date9.;
if a;
run;
/*house cleaning*/
proc sql; drop table _caldates, _car, _factors, _test,
_params, _temp, _evtrets,_evtrets1, _evtrets_temp;
drop view _abrets; quit;
options errors=&errors &oldoptions;
%put ;%put ### OUTPUT IN THE DATASET &outset;
%put ;%put ### TEST STATISTICS IN THE DATASET &outstats;
%MEND;
/* ********************************************************************************* */
/* ************* Material Copyright Wharton Research Data Services *************** */
/* ****************************** All Rights Reserved ****************************** */
/* ********************************************************************************* */
Usage Notes
A note on the calculated stats:
Patell (1976) statistic assumes cross-sectional independence in abnormal returns and no event-induced change in variance of the event window returns
Cross-sectional test still assumes the cross-sectional independence, but accounts for event-induced variance changes
Boehmer's statistic accounts for both event-induced variance changes as well as potential cross-sectional dependence of CARs
The Sign statistic relies on the assumption of no cross-correlation of abnormal returns
To find out the results of the hypothesis test for comparing groups , find the row of output labeled 'Model' and look at the column labeled ‘F-value’ for the Fisher statistic and Pr>F for the associated p-value . HOVTEST tests for whether variances of two groups are the same