*! version 0.8.4 21may2024 program xt2treatments, eclass version 18.0 syntax varname(numeric) [if], treatment(varname numeric) control(varname numeric) [, pre(integer 1) post(integer 3) baseline(string) weighting(string) graph] if ("`baseline'" == "") { local baseline "-1" } if ("`weighting'" == "") { local weighting "equal" } local T1 = `pre'-1 local K = `pre'+`post'+1 marksample touse * read panel structure xtset local group = r(panelvar) local time = r(timevar) local y `varlist' tempvar yg evert everc time_g dy eventtime n_g n_gt tempname w W0 bad_coef bad_Var b V Wcum Wsum D quietly egen `evert' = max(cond(`touse', `treatment', 0)), by(`group') quietly egen `everc' = max(cond(`touse', `control', 0)), by(`group') * no two treatment can happen to the same group capture assert !(`evert' & `everc') if `touse' if (_rc) { display in red "Some groups receive both treatments" inspect `group' if `evert' & `everc' & `touse' error(459) } quietly egen `time_g' = min(cond(`treatment' | `control', `time', .)) if `touse', by(`group') * everyone receives treatment capture assert !missing(`time_g') if `touse' if (_rc) { display in red "Some groups do not receive any treatment" inspect `group' if !`evert' & !`everc' & `touse' error(459) } quietly generate `eventtime' = `time' - `time_g' if `touse' quietly egen `n_gt' = count(1) if `touse', by(`time_g' `time') quietly egen `n_g' = max(`n_gt') if `touse', by(`time_g') quietly levelsof `time_g' if `touse', local(gs) quietly levelsof `time' if `touse', local(ts) local G : word count `gs' local T : word count `ts' local N = `G' * (`T' - 1) tempname n1 n0 matrix `w' = J(`G', `T', 0.0) forvalues g = 1/`G' { forvalues t = 2/`T' { local time_t : word `t' of `ts' local cohort : word `g' of `gs' local e = `time_t' - `cohort' if inrange(`e', -`pre', `post') { quietly count if `time_g' == `cohort' & (`touse') & `everc' & `time_t' == `time' scalar `n0' = r(N) quietly count if `time_g' == `cohort' & (`touse') & `evert' & `time_t' == `time' scalar `n1' = r(N) if ("`weighting'" == "equal") { * if either of the groups has no observations, we cannot estimate a treatment effect matrix `w'[`g', `t'] = cond(`n0'*`n1'==0, 0.0, 1.0) } if ("`weighting'" == "proportional") { matrix `w'[`g', `t'] = cond(`n0'*`n1'==0, 0.0, `n0' + `n1') } if ("`weighting'" == "optimal") { matrix `w'[`g', `t'] = cond(`n0'*`n1'==0, 0.0, `n0' * `n1' / (`n0' + `n1')) } } } } quietly egen `yg' = mean(cond(`eventtime' == -1, `y', .)) if `touse', by(`group') quietly generate `dy' = `y' - `yg' if `touse' capture drop _att_* forvalues g = 1/`G' { forvalues t = 2/`T' { local running_time : word `t' of `ts' local treatment_time : word `g' of `gs' quietly generate byte _att_`g'_`t' = cond(`time_g' == `treatment_time' & `time' == `running_time', `evert', 0) if `touse' } } ***** This is the actual estimation quietly reghdfe `dy' _att_*_* if `touse', a(`time_g'##`time') cluster(`group') nocons matrix `bad_coef' = e(b) matrix `bad_Var' = e(V) local GT = colsof(`bad_coef') assert `GT' == `G' * `=`T'-1' assert colsof(`bad_Var') == `GT' matrix `Wcum' = J(`GT', `K', 0) local i = 1 forvalues g = 1/`G' { forvalues t = 2/`T' { local time_t : word `t' of `ts' local start : word `g' of `gs' local e = `time_t' - `start' if inrange(`e', -`pre', `post') { matrix `Wcum'[`i', `e' + `pre' + 1] = `w'[`g', `t'] } local i = `i' + 1 } } matrix `Wsum' = J(1, `GT', 1) * `Wcum' matrix `D' = diag(`Wsum') matrix `Wcum' = `Wcum' * inv(`D') tempvar esample * exclude observations outside of the event window quietly generate `esample' = e(sample) quietly replace `esample' = 0 if !`touse' quietly count if `esample' local Nobs = r(N) ****** capture drop _att_* if ("`baseline'" == "average") { matrix `W0' = I(`K') - (J(`K', `pre', 1/`pre'), J(`K', `post'+1, 0)) } else if ("`baseline'" == "atet") { matrix `W0' = (J(1, `pre', -1/`pre'), J(1, `post'+1, 1/(`post'+1))) } else { if (!inrange(`baseline', -`pre', -1)) { display in red "Baseline must be between -`pre' and -1" error 198 } matrix `W0' = I(`K') local bl = `pre' + `baseline' + 1 forvalues i = 1/`K' { matrix `W0'[`i', `bl'] = `W0'[`i', `bl'] - 1.0 } } matrix `b' = `bad_coef' * `Wcum' * `W0'' matrix `V' = `W0' * `Wcum'' * `bad_Var' * `Wcum' * `W0'' if ("`baseline'" == "atet") { local colnames "ATET" } else { * label coefficients forvalues t = -`pre'/`post' { local colnames `colnames' `t' } } matrix colname `b' = `colnames' matrix colname `V' = `colnames' matrix rowname `V' = `colnames' local level 95 tempname coefplot matrix `coefplot' = J(`K', 4, .) matrix colname `coefplot' = xvar b ll ul local tlabels "" forvalues t = -`pre'/`post' { local tlabels `tlabels' `t' local i = `t' + `pre' + 1 matrix `coefplot'[`i', 1] = `t'' matrix `coefplot'[`i', 2] = `b'[1, `i'] matrix `coefplot'[`i', 3] = `b'[1, `i'] + invnormal((100-`level')/200) * sqrt(`V'[`i', `i']) matrix `coefplot'[`i', 4] = `b'[1, `i'] - invnormal((100-`level')/200) * sqrt(`V'[`i', `i']) } ereturn post `b' `V', obs(`Nobs') esample(`esample') ereturn local depvar `y' ereturn local cmd xt2treatments ereturn local cmdline xt2treatments `0' _coef_table_header, title(Event study relative to `baseline') width(62) display _coef_table, bmat(e(b)) vmat(e(V)) level(`level') /// depname(`depvar') coeftitle(ATET) if ("`graph'" == "graph") { hetdid_coefplot, mat(`coefplot') title(Event study relative to `baseline') /// ylb(`y') xlb("Length of exposure to the treatment") /// yline(0) legend(off) level(`level') yline(0, extend) ytick(0, add) ylabel(0, add) xlabel(`tlabels') } end