├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── dependency.do ├── impute_option_description.html ├── stata.toc ├── test ├── effect_file.dta ├── example31.dta ├── small_repeated_cross_sectional_example31.dta └── test.do ├── xtevent.pkg └── xtevent ├── _eventgenvars.ado ├── _eventiv.ado ├── _eventivstatic.ado ├── _eventols.ado ├── _eventolsstatic.ado ├── get_unit_time_effects.ado ├── get_unit_time_effects.sthlp ├── install.txt ├── release.txt ├── xtevent.ado ├── xtevent.sthlp ├── xteventplot.ado ├── xteventplot.sthlp ├── xteventtest.ado └── xteventtest.sthlp /.gitattributes: -------------------------------------------------------------------------------- 1 | *.dta filter=lfs diff=lfs merge=lfs -text 2 | *.rda filter=lfs diff=lfs merge=lfs -text 3 | *.rds filter=lfs diff=lfs merge=lfs -text 4 | *.pdf filter=lfs diff=lfs merge=lfs -text 5 | *.png filter=lfs diff=lfs merge=lfs -text 6 | *.zip filter=lfs diff=lfs merge=lfs -text 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | external/ 2 | temp/ 3 | scons/ 4 | datastore/ 5 | datastore 6 | temp 7 | *.sconsign.dblite 8 | *.pyc 9 | *.lyx~ 10 | *.lyx# 11 | *.lyx.emergency 12 | *.Rhistory 13 | *.Rapp.history 14 | *.DS_Store 15 | *.aux 16 | *.fls 17 | *.lof 18 | *.lot 19 | *.nav 20 | *.snm 21 | *.toc 22 | *.out 23 | *.svn 24 | *.ipynb 25 | *.synctex.gz 26 | *.bbl 27 | *.blg 28 | *.asv 29 | *converted-to.pdf 30 | *texput.log 31 | *.synctex(busy) 32 | test/test.log 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Simon Freyaldenhoven, Christian Hansen, Jorge Pérez Pérez, Jesse M. Shapiro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xtevent 2 | ![GitHub release (latest by date)](https://img.shields.io/github/v/release/JMSLab/xtevent?label=last%20version) 3 | 4 | ----------- 5 | 6 | ### Description 7 | `xtevent` is a Stata package to estimate linear panel event-study models. It includes three commands: `xtevent` for estimation; `xteventplot` to create event-study plots and; `xteventtest` for post-estimation hypotheses testing. 8 | 9 | 10 | - Last version: 3.1.0 (07jul2024) 11 | - Current SSC version: 3.1.0 (07jul2024) 12 | ----------- 13 | 14 | ### Updates 15 | 16 | * **Version 3.1.0 (07jul2024)**: 17 | - Options for choosing the largest available estimation window, and the largest available balanced estimation window [#170](https://github.com/JMSLab/xtevent/issues/170) 18 | - Simpler syntax for Sun and Abraham (2021) estimation with automatic cohort variables generation [#179](https://github.com/JMSLab/xtevent/issues/179) 19 | - New default graph style for Stata 18 [#214](https://github.com/JMSLab/xtevent/issues/214) 20 | - Fixed bugs present in version 3.0.0 [#181](https://github.com/JMSLab/xtevent/issues/181), [#186](https://github.com/JMSLab/xtevent/issues/186), [#188](https://github.com/JMSLab/xtevent/issues/188), [#189](https://github.com/JMSLab/xtevent/issues/189), [#203](https://github.com/JMSLab/xtevent/issues/203), [#204](https://github.com/JMSLab/xtevent/issues/204), [#217](https://github.com/JMSLab/xtevent/issues/217), [#222](https://github.com/JMSLab/xtevent/issues/222) 21 | - See [here](https://github.com/JMSLab/xtevent/releases/tag/v3.1.0) for the complete update list. 22 | 23 | * **Version 3.0.0 (23feb2024)**: 24 | - Increase default replications for sup-t confidence intervals: [#153](https://github.com/JMSLab/xtevent/issues/153) 25 | - Change the name of the option to omit the label for the value of the dependent variable from `nominus1label` to `nonormlabel`: [#152](https://github.com/JMSLab/xtevent/issues/152) 26 | - Fixed bugs present in version 2.2.0: [#155](https://github.com/JMSLab/xtevent/issues/155), [#159](https://github.com/JMSLab/xtevent/issues/159), [#166](https://github.com/JMSLab/xtevent/issues/166) 27 | - Clarifications in the documentation: [#152](https://github.com/JMSLab/xtevent/issues/152), [#161](https://github.com/JMSLab/xtevent/issues/161), [#163](https://github.com/JMSLab/xtevent/issues/163) 28 | - See [here](https://github.com/JMSLab/xtevent/releases/tag/v3.0.0) for the complete update list. 29 | 30 | * **Version 2.2.0 (15mar2023)**: 31 | - Add `cohort` and `control_cohort` to obtain estimates using [Sun and Abraham's (2021)](https://www.sciencedirect.com/science/article/pii/S030440762030378X) method. 32 | - Add `repeatedcs`option and `get_unit_time_effects`command to estimate event-studies in repeated cross-section settings. 33 | - Add `noestimate` option to `savek()` to generate event-time dummies without estimating the regression model. 34 | - Enable clustered and robust standard errors for IV estimation without `reghdfe`. 35 | - Fixed bugs present in version 2.1.1. 36 | - See [here](https://github.com/JMSLab/xtevent/releases/tag/v2.2.0) for the complete update list. 37 | 38 | * **Version 2.1.1 (12aug2022)**: 39 | - Fixed bugs present in version 2.1.0. 40 | - Updates in the help files and other documentation. 41 | - See [here](https://github.com/JMSLab/xtevent/releases/tag/v2.1.1) for the complete update list. 42 | 43 | * **Version 2.1.0 (1aug2022)**: 44 | - Adds `diffavg` option to `xtevent` to obtain the difference between the average post-event and pre-event coefficient estimates. 45 | - Adds `textboxoption` option to `xteventplot` to specify characteristics for displaying the p-values of the pre-trend and leveling-off tests. 46 | - Fixed bugs present in version 2.0.0 47 | - See [here](https://github.com/JMSLab/xtevent/releases/tag/v2.1.0) for the complete update list. 48 | 49 | * **Version 2.0.0 (24jun2022)**: 50 | - **To produce equivalent results as with xtevent 1.0.0, where the default was to impute the endpoints, the user should use *impute(stag)*.** The **impute** option imputes missing values in the *policyvar* following different rules. For instance, specifying **impute(stag)** indicates the program to check before imputing if the *policyvar* follows staggered adoption. For a detailed explanation of the **impute** option, see this [detailed example](https://rawcdn.githack.com/JMSLab/xtevent/cf16d12f90ddf363df62c397cf0e9dc05bbd9875/impute_option_description.html). 51 | - The option `nonstaggered` has been depreciated. The default option is now not to impute missing values or endpoints. You should now choose any of the imputation rules in the `impute` option. To get results using imputation consistent with staggered adoption, as in version 1.0.0 you should use `impute(stag)`. 52 | - Now the option `trend` allows for trend adjustment by either OLS or GMM. 53 | - Fixed several bugs present in version 1.0.0. 54 | - See [here](https://github.com/JMSLab/xtevent/releases/tag/v2.0.0) for the complete update list. 55 | 56 | ----------- 57 | ### Installation 58 | 59 | #### To install version 3.1.0 from SSC: 60 | ```stata 61 | ssc install xtevent 62 | ``` 63 | 64 | To update from an older version: 65 | ```stata 66 | adoupdate xtevent, update 67 | ``` 68 | 69 | 70 | #### To install the last version in this repository, use the `github` command: 71 | First, install the `github` command: 72 | ```stata 73 | net install github, from("https://haghish.github.io/github/") 74 | ``` 75 | Then execute: 76 | ```stata 77 | cap github uninstall xtevent 78 | ``` 79 | ```stata 80 | github install JMSLab/xtevent 81 | ``` 82 | 83 | The `github` command will also install all the necessary dependencies. 84 | 85 | If you have an older version and want to update: 86 | ```stata 87 | github update xtevent 88 | ``` 89 | 90 | #### To install using `net`: 91 | ```stata 92 | cap ado uninstall xtevent 93 | ``` 94 | ```stata 95 | net install xtevent, from("https://raw.githubusercontent.com/JMSLab/xtevent/master") 96 | ``` 97 | ----------- 98 | 99 | ### To get started 100 | ```stata 101 | help xtevent 102 | ``` 103 | 104 | ----------- 105 | 106 | ### Examples 107 | 108 | Using xtevent 3.1.0 109 | 110 | #### xtevent 111 | ```stata 112 | *** setup 113 | webuse nlswork, clear 114 | * year variable has many missing observations 115 | * Create a time variable that ignores the gaps 116 | by idcode (year): gen time=_n 117 | xtset idcode time 118 | 119 | 120 | *Generate a policy variable that follows staggered adoption 121 | by idcode (time): gen union2=sum(union) 122 | replace union2=1 if union2>1 123 | order time union union2, after(year) 124 | 125 | *** Examples 126 | *Estimate a basic event study with clustered standard errors 127 | *Impute the policy variable assuming no unobserved changes 128 | xtevent ln_w age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure , /// 129 | pol(union2) w(3) cluster(idcode) impute(nuchange) 130 | 131 | *Omit unit and time fixed effects 132 | *Impute the policy variable verifying staggered adoption 133 | xtevent ln_w age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure , /// 134 | pol(union2) w(3) cluster(idcode) nofe note impute(stag) 135 | 136 | * Bring back unit and time fixed effects 137 | *Adjust for a pre-trend by estimating a linear trend by GMM 138 | xtevent ln_w age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure , /// 139 | pol(union2) w(3) cluster(idcode) trend(-2, method(gmm)) /// 140 | impute(stag) 141 | 142 | *Freyaldenhoven, Hansen and Shapiro (2019) estimator with proxy variables 143 | xtevent ln_w age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure , /// 144 | pol(union2) w(3) vce(cluster idcode) proxy(wks_work) /// 145 | impute(stag) 146 | 147 | *reghdfe and two-way clustering 148 | xtevent ln_w age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure , /// 149 | pol(union2) w(3) impute(stag) cluster(idcode year) reghdfe /// 150 | proxy(wks_work) 151 | 152 | *Sun and Abraham (2021) Estimator 153 | xtevent ln_w age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure, /// 154 | policyvar(union2) window(3) impute(stag) vce(cluster idcode) /// 155 | reghdfe sunabraham 156 | ``` 157 | 158 | #### xteventplot 159 | ```stata 160 | 161 | *** Setup 162 | webuse nlswork, clear 163 | * year variable has many missing observations 164 | * Create a time variable that ignores the gaps 165 | by idcode (year): gen time=_n 166 | xtset idcode time 167 | 168 | *** Examples 169 | *Basic event study with clustered standard errors 170 | *Impute the policy variable assuming no unobserved changes 171 | xtevent ln_w age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure , /// 172 | pol(union) w(3) cluster(idcode) impute(nuchange) 173 | 174 | * Simple plot 175 | xteventplot 176 | 177 | *Plot smoothest path in confidence region 178 | xteventplot, smpath(line) 179 | 180 | *Freyaldenhoven, Hansen and Shapiro (2019) estimator with proxy variables 181 | xtevent ln_w age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure , /// 182 | pol(union) w(3) vce(cluster idcode) impute(nuchange) /// 183 | proxy(wks_work) 184 | 185 | *Dependent variable, proxy variable, and overlay plots 186 | xteventplot, y 187 | xteventplot, proxy 188 | xteventplot, overlay(iv) 189 | xteventplot 190 | ``` 191 | 192 | #### xteventtest 193 | ```stata 194 | *** setup 195 | webuse nlswork, clear 196 | xtset idcode year 197 | 198 | *** examples 199 | *Basic event study with clustered standard errors. 200 | *Impute the policy variable assuming no unobserved changes 201 | xtevent ln_w age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure , /// 202 | pol(union) w(3) cluster(idcode) impute(nuchange) 203 | 204 | *Test some coefficients to be equal to 0 jointly 205 | xteventtest, coefs(1 2) 206 | 207 | *Test that the sum of all pre-event coefficients is equal to 0 208 | xteventtest, allpre cumul 209 | 210 | *Test whether the coefficients before the event follow a linear trend 211 | xteventtest, linpretrend 212 | 213 | *Tests that the coefficients for the earliest 2 periods before the event are equal to 0 214 | xteventtest, overidpre(2) 215 | ``` 216 | 217 | ----------- 218 | 219 | ### Video tutorial: 220 | 221 | Our YouTube channel, [Linear Panel Event-Study Design](https://www.youtube.com/watch?v=hOIB3PwntYg), contains a video series discussing `xtevent` and the accompanying paper, Visualization, Identification, and Estimation in the Panel Event-Study Design. 222 | 223 | ----------- 224 | 225 | ### Citation 226 | 227 | Simon Freyaldenhoven, Christian Hansen, Jorge Pérez Pérez, and Jesse M. Shapiro. "Visualization, Identification, and Estimation in the Linear Panel Event-Study Design." [NBER Working Paper No. 29170](https://www.nber.org/papers/w29170), 228 | August 2021; forthcoming in _Advances in Economics and Econometrics: Twelfth World Congress_. 229 | 230 | Simon Freyaldenhoven, Christian Hansen, Jorge Pérez Pérez, Jesse M. Shapiro, and Constantino Carreto. "xtevent: Estimation and Visualization in the Linear Panel Event-Study Design." [Article to accompany Stata package](https://jorgeperezperez.com/files/xtevent_merged.pdf), July 2024. 231 | 232 | -------------------------------------------------------------------------------- /dependency.do: -------------------------------------------------------------------------------- 1 | cap ssc install ftools // v2.49.1 2 | cap ssc install reghdfe // v6.12.3 3 | cap ssc install ivreg2 // v4.1.11 4 | * For ivreghdfe, trying to install the github version 5 | cap net install ivreghdfe, from(https://raw.githubusercontent.com/sergiocorreia/ivreghdfe/master/src/) // v1.1.3 6 | * if installation failed, install the ssc version 7 | if _rc cap ssc install ivreghdfe // v1.0.0 8 | cap ssc install ranktest // v2.0.04 9 | cap ssc install avar // v1.0.07 -------------------------------------------------------------------------------- /stata.toc: -------------------------------------------------------------------------------- 1 | v 3.1.0 2 | d 3 | d Simon Freyaldenhoven, Federal Reserve Bank of Philadelphia 4 | d Christian Hansen, University of Chicago 5 | d Jorge Pérez Pérez (maintainer), Banco de México (jorgepp@banxico.org.mx) 6 | d Jesse M. Shapiro, Harvard University and NBER 7 | d Constantino Carreto, Banco de México 8 | 9 | d https://github.com/JMSLab/xtevent 10 | 11 | d 'xtevent': Estimation and Visualization in the Linear Panel Event-Study Design 12 | 13 | p xtevent 14 | -------------------------------------------------------------------------------- /test/effect_file.dta: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7dc2235eb958a0c6f77bef77fc08ef4e2f9af02a578f6ac7e31c387cb1b6327c 3 | size 13515 4 | -------------------------------------------------------------------------------- /test/example31.dta: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:10679fabe875da6194cc757cd86181f1306a2fcce18540c4b1ffe6d9e06c4b06 3 | size 808613 4 | -------------------------------------------------------------------------------- /test/small_repeated_cross_sectional_example31.dta: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:37994d177781cecc09c29621b0fa35c8b6cbf5c16a1f01e550f521eca2ebf33d 3 | size 1050533 4 | -------------------------------------------------------------------------------- /test/test.do: -------------------------------------------------------------------------------- 1 | cap log close 2 | 3 | cap log using test.log, replace text 4 | 5 | clear all 6 | 7 | set seed 5674896 8 | 9 | /*========================================================================= 10 | 1: Load data 11 | ===========================================================================*/ 12 | use "example31.dta", clear 13 | 14 | 15 | /*========================================================================= 16 | 2: Run tests 17 | ===========================================================================*/ 18 | 19 | graph drop _all 20 | 21 | *------------------------ 2.1: Replicate 2a and test basic funcionality ---------------------------------- 22 | 23 | xtevent y eta , panelvar(i) timevar(t) policyvar(z) window(5) 24 | 25 | * Testing xtset options 26 | 27 | xtevent y eta, policyvar(z) window(5) plot 28 | xtevent y eta, policyvar(z) panelvar(i) window(5) plot 29 | xtevent y eta, policyvar(z) timevar(t) window(5) plot 30 | 31 | 32 | /* Must fail 33 | xtevent y eta, policyvar(z) panelvar(z) window(5) 34 | xtevent y eta, policyvar(z) timevar(z) window(5) 35 | */ 36 | 37 | * Testing noci, nosupt, nozeroline, nonormlabel 38 | xtevent y eta, policyvar(z) timevar(t) window(5) 39 | xteventplot, noci 40 | xteventplot, nosupt 41 | xteventplot, nozeroline 42 | xteventplot, nonormlabel 43 | 44 | * Test combinations 45 | xteventplot, noci nozeroline 46 | xteventplot, noci nonormlabel 47 | xteventplot, nosupt nozeroline 48 | xteventplot, nosupt nonormlabel 49 | 50 | * A common axis plot with labels 51 | gen y2 = y + 5 52 | xtevent y eta, policyvar(z) timevar(t) window(5) 53 | loc lab : di %-9.2f `=e(y1)' 54 | loc lab=strtrim("`lab'") 55 | xteventplot, nonormlabel ylab(-3 0 `"0 (`lab')"' 3) name(g1) /* " */ 56 | xtevent y2 eta, policyvar(z) timevar(t) window(5) 57 | loc lab : di %-9.2f `=e(y1)' 58 | loc lab=strtrim("`lab'") 59 | xteventplot, nonormlabel ylab(-3 0 `"0 (`lab')"' 3) name(g2) /* " */ 60 | graph combine g1 g2 61 | drop y2 62 | 63 | * Test if/in 64 | xtevent y eta if i<100, panelvar(i) timevar(t) policyvar(z) window(5) plot 65 | xtevent y eta in 1/600 , panelvar(i) timevar(t) policyvar(z) window(5) plot 66 | xtevent y eta in 1/600 if i<30 , panelvar(i) timevar(t) policyvar(z) window(5) plot 67 | 68 | * Test nofe, note 69 | xtevent y eta, panelvar(i) timevar(t) policyvar(z) window(5) nofe plot 70 | xtevent y eta, panelvar(i) timevar(t) policyvar(z) window(5) nofe note plot 71 | 72 | * Test smoothest line 73 | xtevent eta, panelvar(i) timevar(t) policyvar(z) window(4) 74 | xteventplot, smpath(scatter) 75 | xteventplot, smpath(line) 76 | xteventplot, smpath(line, technique("dfp")) 77 | 78 | * Test more suptreps 79 | 80 | cap graph drop g1 81 | cap graph drop g2 82 | 83 | xtevent y eta, panelvar(i) timevar(t) policyvar(z) window(3) 84 | xteventplot, suptreps(20) name(g1) 85 | xteventplot, suptreps(1e6) name(g2) 86 | 87 | graph combine g1 g2, rows(1) 88 | 89 | graph drop g1 90 | graph drop g2 91 | 92 | * Test savek 93 | xtevent y eta, panelvar(i) timevar(t) policyvar(z) window(5) savek(a) 94 | des a_eq*, s 95 | des a_evtime, s 96 | drop a* 97 | 98 | * Test savek with suboption noestimate 99 | xtevent y eta, panelvar(i) timevar(t) policyvar(z) window(5) savek(a, noe) 100 | des a_eq*, s 101 | des a_evtime, s 102 | drop a* 103 | 104 | * Test different prefix 105 | xtevent y eta, panelvar(i) timevar(t) policyvar(z) window(5) savek(b) 106 | 107 | *savek + suboption replace 108 | cap noi xtevent y eta, panelvar(i) timevar(t) policyvar(z) window(5) savek(b) 109 | xtevent y eta, panelvar(i) timevar(t) policyvar(z) window(5) savek(b, replace) 110 | 111 | * Test factor variables in varlist 112 | cap gen pois=rpoisson(5) 113 | xtevent y eta i.pois, panelvar(i) timevar(t) policyvar(z) window(5) plot 114 | cap drop pois 115 | 116 | * Test time series variables in varlist 117 | 118 | xtevent y l.eta , panelvar(i) timevar(t) policyvar(z) window(5) plot 119 | xtevent y f.eta , panelvar(i) timevar(t) policyvar(z) window(5) plot 120 | 121 | * Test asymmetric window 122 | xtevent y eta , panelvar(i) timevar(t) policyvar(z) window(-4 2) plot 123 | 124 | * Test finding and estimating with the widest window 125 | xtevent y eta , panelvar(i) timevar(t) policyvar(z) impute(stag) window(max) plot 126 | 127 | * Test finding and estimating with the widest window with balanced time periods for all units 128 | * expect an error message because balanced window is too narrow 129 | cap noi xtevent y eta , panelvar(i) timevar(t) policyvar(z) impute(stag) window(balanced) plot 130 | 131 | * Test normalizations 132 | 133 | xtevent y eta, panelvar(i) timevar(t) policyvar(z) window(5) norm(-1) plot 134 | xtevent y eta, panelvar(i) timevar(t) policyvar(z) window(5) norm(-2) plot 135 | xtevent y eta, panelvar(i) timevar(t) policyvar(z) window(5) norm(-6) plot 136 | * Should fail 137 | * xtevent y eta, panelvar(i) timevar(t) policyvar(z) window(5) norm(-7) plot 138 | xtevent y eta, panelvar(i) timevar(t) policyvar(z) window(5) norm(1) plot 139 | xtevent y eta, panelvar(i) timevar(t) policyvar(z) window(5) norm(5) plot 140 | * Should fail 141 | * xtevent y eta, panelvar(i) timevar(t) policyvar(z) window(5) norm(7) plot 142 | graph drop _all 143 | 144 | * Test exclusion of unbalanced units with ambiguous eventtime 145 | 146 | gen z2 = z 147 | replace z2 = . if i==1 & t==7 148 | xtevent y eta, policyvar(z2) window(5) 149 | drop z2 150 | 151 | * Test reghdfe 152 | xtevent y eta , panelvar(i) timevar(t) policyvar(z) window(3) reghdfe plot 153 | 154 | * Test reghdfe, proxy and absorbing a variable 155 | gen k=round(x) //generate a categorical variable. Use it as a control 156 | xtevent y eta, policyvar(z) window(3) proxy(x) nofe note addabsorb(k) reghdfe 157 | 158 | *Test additional cluster and robust specifications 159 | xtevent y eta, policyvar(z) window(3) proxy(x) nofe note addabsorb(k) reghdfe robust cluster(i) 160 | *equivalent specification (it admits use of vce) 161 | xtevent y eta, policyvar(z) window(3) proxy(x) nofe note addabsorb(k) reghdfe vce(robust cluster i) 162 | 163 | /* 164 | *Test other standard-error specifications (not allowed) 165 | xtevent y eta, policyvar(z) window(3) proxy(x) nofe note addabsorb(k) reghdfe vce(bootstrap) //will show an error message 166 | */ 167 | 168 | *Imputation of policyvar without verifying staggered adoption conditions 169 | xtevent y eta, policyvar(z) timevar(t) window(5) impute(nuchange) 170 | 171 | *outer imputation of policyvar verifying staggered adoption conditions 172 | xtevent y eta, policyvar(z) timevar(t) window(5) impute(stag) 173 | 174 | *outer and inner imputation of policyvar verifying staggered adoption conditions 175 | xtevent y eta, policyvar(z) timevar(t) window(5) impute(instag) 176 | 177 | *outer and inner mputation of policyvar. Adds the imputed policyvar to the database 178 | xtevent y eta, policyvar(z) timevar(t) window(5) impute(instag, saveimp) 179 | drop z_imputed 180 | 181 | *imputation fails if staggered conditions are not satisfied. It reverts to no imputation 182 | replace z=0.5 in 7 183 | xtevent y eta, policyvar(z) timevar(t) window(5) impute(instag) 184 | replace z=1 in 7 185 | 186 | *Difference in averages between the post and pre-period 187 | xtevent y eta , panelvar(i) timevar(t) policyvar(z) window(5) diffavg 188 | xtevent y eta , panelvar(i) timevar(t) policyvar(z) window(4) diff 189 | xtevent y eta , panelvar(i) timevar(t) policyvar(z) window(5) norm(1) diff 190 | xtevent y eta , panelvar(i) timevar(t) policyvar(z) window(5) norm(2) diff 191 | xtevent y eta , panelvar(i) timevar(t) policyvar(z) window(5) norm(-2) diff 192 | 193 | *Trend adjustment. Default method is GMM 194 | xtevent y eta, policyvar(z) timevar(t) window(5) trend(-3) 195 | 196 | *Trend adjustment. Use OLS instead 197 | xtevent y eta, policyvar(z) timevar(t) window(5) trend(-3, method(ols)) 198 | 199 | *Compare: 1) no adjustment; 2) adjustment by GMM and; 3) adjustment by OLS 200 | xtevent y eta, policyvar(z) timevar(t) window(5) 201 | xteventplot, name(g1) 202 | xtevent y eta, policyvar(z) timevar(t) window(5) trend(-3, method(gmm)) 203 | xteventplot, name(g2) 204 | xtevent y eta, policyvar(z) timevar(t) window(5) trend(-3, method(ols)) 205 | xteventplot, name(g3) 206 | 207 | graph combine g1 g2 g3, rows(2) 208 | 209 | graph drop _all 210 | 211 | *Sun and Abraham Estimator (2021) 212 | *Generate cohort indicator 213 | * This works because of staggered adoption 214 | gen timet=t if z==1 215 | by i: egen time_of_treat=min(timet) 216 | *Generate control cohort indicator. We use the never treated units as the control cohort. 217 | gen never_treat=time_of_treat==. 218 | *Could also use last trated as the control group 219 | egen last_treat_time = max(time_of_treat) 220 | gen last_treat = (time_of_treat == last_treat_time) 221 | replace last_treat = . if time_of_treat == . 222 | gen cohort_for_last_treat = time_of_treat 223 | replace cohort_for_last_treat = . if last_treat 224 | 225 | *Estimate the event-time coefficients with the Sun-and-Abraham Estimator. 226 | xtevent y eta , policyvar(z) window(5) vce(cluster i) impute(nuchange) cohort(variable time_of_treat) control_cohort(variable never_treat) 227 | *Use reghdfe as the underlying estimation command 228 | xtevent y eta , policyvar(z) window(5) vce(cluster i) impute(nuchange) cohort(variable time_of_treat) control_cohort(variable never_treat) reghdfe 229 | * Automatic generation of cohort and control_cohort variables 230 | xtevent y eta , policyvar(z) window(5) vce(cluster i) impute(nuchange) cohort(create) reghdfe 231 | 232 | *Overlay trend plot 233 | xtevent y eta, policyvar(z) timevar(t) window(5) trend(-3, method(gmm) saveov) 234 | xteventplot, overlay(trend) 235 | 236 | /* 237 | *Overlay trend plot fails because suboption "saveov" was not specified 238 | xtevent y eta, policyvar(z) timevar(t) window(5) trend(-3, method(gmm)) 239 | xteventplot, overlay(trend) 240 | */ 241 | 242 | * Overlay static plot 243 | xtevent y eta, policyvar(z) timevar(t) window(5) 244 | xteventplot, overlay(static) 245 | 246 | * Test graphic options 247 | gen y2 = y - z 248 | xtevent y2 eta, policyvar(z) timevar(t) window(5) 249 | xteventplot, smplotopts(lcolor(green)) smpath(line) 250 | xteventplot, ciplotopts(lcolor(green)) 251 | xteventplot, suptciplotopts(lcolor(green)) 252 | xteventplot, scatterplotopts(mcolor(green)) 253 | xteventplot, overlay(static) staticovplotopts(lcolor(red)) 254 | 255 | xtevent y2 eta, policyvar(z) timevar(t) window(5) trend(-3, saveov) 256 | xteventplot, overlay(trend) trendplotopts(lcolor(red)) 257 | xtevent y eta, policyvar(z) proxy(x) window(5) 258 | xteventplot, overlay(iv) scatterplotopts(mcolor(green red)) 259 | xtevent y eta , panelvar(i) timevar(t) policyvar(z) window(5) 260 | xteventplot, textboxoption(color(blue) size(large)) 261 | drop y2 262 | 263 | ******** Repeated cross-sectional data 264 | use "small_repeated_cross_sectional_example31.dta", clear 265 | xtset, clear 266 | *OLS, impute and trend adjustment 267 | xtevent y, panelvar(state) t(t) policyvar(z) window(5) trend(-3, method(ols)) impute(instag) repeatedcs 268 | xteventplot 269 | *IV 270 | xtevent y, panelvar(state) t(t) policyvar(z) window(5) impute(stag) proxy(x) repeatedcs 271 | xteventplot 272 | xteventplot, overlay(iv) 273 | 274 | *static ols 275 | xtevent y, panelvar(state) t(t) policyvar(z) impute(stag) static repeatedcs 276 | *static IV 277 | xtevent y, panelvar(state) t(t) policyvar(z) impute(stag) proxy(x) static repeatedcs 278 | 279 | *get unit time effects 280 | get_unit_time_effects y u eta, panelvar(state) timevar(t) saving("effect_file.dta", replace) 281 | 282 | *get_unit_time_effects + xtevent 283 | get_unit_time_effects y u eta, panelvar(state) timevar(t) saving("effect_file.dta", replace) 284 | bysort state t (z): keep if _n==1 285 | keep state t z 286 | merge m:1 state t using "effect_file.dta" 287 | drop _merge 288 | xtevent _unittimeeffects, panelvar(state) t(t) policyvar(z) window(5) 289 | xteventplot 290 | 291 | *------------------------ 2.2: Replicate 2b and test basic funcionality without controls ---------------------------------- 292 | 293 | * load panel dataset 294 | use "example31.dta", clear 295 | 296 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(5) plot 297 | 298 | * Testing xtset options 299 | 300 | xtevent y , policyvar(z) window(5) plot 301 | xtevent y , policyvar(z) panelvar(i) window(5) plot 302 | xtevent y , policyvar(z) timevar(t) window(5) plot 303 | 304 | 305 | /* Must fail 306 | xtevent y eta, policyvar(z) panelvar(z) window(5) 307 | xtevent y eta, policyvar(z) timevar(z) window(5) 308 | */ 309 | 310 | * Testing noci, nosupt, nozeroline, nonormlabel 311 | xtevent y , policyvar(z) timevar(t) window(5) 312 | xteventplot, noci 313 | xteventplot, nosupt 314 | xteventplot, nozeroline 315 | xteventplot, nonormlabel 316 | 317 | * Test if/in 318 | xtevent y if i<100, panelvar(i) timevar(t) policyvar(z) window(5) plot 319 | xtevent y in 1/600 , panelvar(i) timevar(t) policyvar(z) window(5) plot 320 | xtevent y in 1/600 if i<30 , panelvar(i) timevar(t) policyvar(z) window(5) plot 321 | 322 | * Test nofe, note 323 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(5) nofe plot 324 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(5) nofe note plot 325 | 326 | * Test smoothest line 327 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(3) 328 | xteventplot, smpath(scatter) 329 | xteventplot, smpath(line) 330 | /* maximum order allowed is 10 331 | xteventplot, smpath(line, maxorder(25)) 332 | xteventplot, smpath(line, maxorder(30)) 333 | */ 334 | xteventplot, smpath(line, technique("nr 10 bfgs 10")) 335 | 336 | 337 | * Test more suptreps 338 | 339 | cap graph drop g1 340 | cap graph drop g2 341 | 342 | xtevent y, panelvar(i) timevar(t) policyvar(z) window(3) 343 | xteventplot, suptreps(20) name(g1) 344 | xteventplot, suptreps(1e6) name(g2) 345 | 346 | graph combine g1 g2, rows(1) 347 | 348 | graph drop g1 349 | graph drop g2 350 | 351 | * Test savek 352 | xtevent y, panelvar(i) timevar(t) policyvar(z) window(5) savek(a) 353 | des a_eq*, s 354 | des a_evtime, s 355 | drop a* 356 | 357 | * Test factor variables in varlist 358 | cap gen pois=rpoisson(5) 359 | xtevent y i.pois, panelvar(i) timevar(t) policyvar(z) window(5) plot 360 | cap drop pois 361 | 362 | * Test asymmetric window 363 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(-4 2) plot 364 | 365 | * Test normalizations 366 | 367 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(5) norm(-1) plot 368 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(5) norm(-2) plot 369 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(5) norm(-6) plot 370 | * xtevent y eta, panelvar(i) timevar(t) policyvar(z) window(5) norm(-7) plot 371 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(5) norm(1) plot 372 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(5) norm(5) plot 373 | 374 | graph drop _all 375 | 376 | * Test exclusion of unbalanced units with ambiguous eventtime 377 | gen z2 = z 378 | replace z2 = . if i==1 & t==7 379 | xtevent y , policyvar(z2) window(5) 380 | drop z2 381 | 382 | * Overlay static plot 383 | xtevent y , policyvar(z) timevar(t) window(5) 384 | xteventplot, overlay(static) 385 | 386 | *------------------------ 2.3: Replicate 2c ---------------------------------- 387 | 388 | * Replicate 2c 389 | xtevent y x , panelvar(i) timevar(t) policyvar(z) window(5) plot 390 | 391 | * areg y x _k_eq* i.t , absorb(i) cluster(i) 392 | 393 | *------------------------ 2.4: Replicate 2d and test basic funcionality ---------------------------------- 394 | 395 | * Replicate 2d 396 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(5) proxy(x) plot 397 | 398 | * Test alternative ways of specifying iv 399 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(5) proxy(x) proxyiv(1) plot 400 | xteventplot, smpath(scatter) 401 | /* 402 | * This should not work now, for leads write 1 403 | cap gen f1z=f1.z 404 | cap noi xtevent y , panelvar(i) timevar(t) policyvar(z) window(5) proxy(x) proxyiv(f1z) 405 | cap drop f1z 406 | */ 407 | 408 | *Generate an instrument for the proxy. This instrument is collinear with the event-time dummies. 409 | gen lead1=f1.d.z 410 | *expect an error message: instrument is collinear 411 | *xtevent y , panelvar(i) timevar(t) policyvar(z) window(5) proxy(x) proxyiv(lead1) 412 | drop lead1 413 | 414 | * Other leads 415 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(4) proxy(x) proxyiv(2) plot 416 | xteventplot, smpath(scatter) 417 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(4) proxy(x) proxyiv(3) plot 418 | xteventplot, smpath(scatter) 419 | 420 | * Test additional instruments 421 | 422 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(5) proxy(x) proxyiv(1 2) plot 423 | xteventplot, smpath(scatter, technique("nr 10 bfgs 10")) 424 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(4) proxy(x) proxyiv(3 4) plot 425 | xteventplot, smpath(scatter, technique("nr 10 bfgs 10")) 426 | 427 | * Test both normalizations 428 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(5) norm(-3) proxy(x) proxyiv(1) plot 429 | 430 | * Test additional proxys 431 | cap gen x2=rnormal() 432 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(5) proxy(x x2) proxyiv(1 2) plot 433 | * Must fail 434 | * xtevent y , panelvar(i) timevar(t) policyvar(z) window(5) proxy(x x2) proxyiv(1) 435 | cap drop x2 436 | 437 | * Testing xtset options 438 | 439 | xtevent y , policyvar(z) window(5) proxy(x) plot 440 | xteventplot, levels(90 95 99) 441 | xtevent y , policyvar(z) panelvar(i) window(5) proxy(x) 442 | xtevent y , policyvar(z) timevar(t) window(5) proxy(x) 443 | 444 | /* Must fail 445 | xtevent y , policyvar(z) panelvar(z) window(5) proxy(x) 446 | xtevent y , policyvar(z) timevar(z) window(5) proxy(x) 447 | */ 448 | 449 | * Testing noci, nosupt, nozeroline, nonormlabel 450 | xtevent y , policyvar(z) timevar(t) window(5) proxy(x) 451 | xteventplot, nosupt 452 | xteventplot, noci 453 | xteventplot, nozeroline 454 | xteventplot, nonormlabel 455 | 456 | * Test if/in 457 | xtevent y if i<100, panelvar(i) timevar(t) policyvar(z) window(5) proxy(x) plot 458 | xtevent y in 1/600 , panelvar(i) timevar(t) policyvar(z) window(5) proxy(x) plot 459 | xtevent y in 1/600 if i<30 , panelvar(i) timevar(t) policyvar(z) window(5) proxy(x) plot 460 | 461 | * Test nofe, note 462 | xtevent y, panelvar(i) timevar(t) policyvar(z) window(5) nofe proxy(x) 463 | 464 | * Note: Warning for the omitted time dummy comes from ivregress 2sls 465 | 466 | xtevent y, panelvar(i) timevar(t) policyvar(z) window(5) nofe note proxy(x) 467 | 468 | * Test savek 469 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(5) savek(a) proxy(x) 470 | des a_eq*, s 471 | des a_evtime, s 472 | drop a_* 473 | 474 | 475 | * Test factor variables in varlist 476 | cap gen pois=rpoisson(5) 477 | xtevent y i.pois, panelvar(i) timevar(t) policyvar(z) window(5) proxy(x) 478 | cap drop pois 479 | 480 | * Test asymmetric window 481 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(-4 2) proxy(x) plot 482 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(-2 2) proxy(x) plot 483 | 484 | * Test overlay plots 485 | graph drop _all 486 | 487 | xteventplot, y 488 | xteventplot, proxy 489 | xteventplot, overlay(iv) 490 | xteventplot 491 | xteventplot, overlay(static) 492 | 493 | 494 | 495 | 496 | 497 | *------------------------ 2.5: Replicate 2e and test basic funcionality ---------------------------------- 498 | 499 | * Replicate 2e 500 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(5) trend(-3, saveov) plot 501 | 502 | * Test overlay plot 503 | xteventplot, overlay(trend) 504 | 505 | 506 | /* Must fail 507 | xtevent y eta, policyvar(z) panelvar(z) window(5) trend(-3 5) 508 | xtevent y eta, policyvar(z) timevar(z) window(5) trend(-3 5) 509 | */ 510 | 511 | * Testing noci, nosupt, nozeroline, nonormlabel 512 | xteventplot, noci 513 | xteventplot, nosupt 514 | xteventplot, nozeroline 515 | xteventplot, nonormlabel 516 | 517 | * Must fail 518 | * xtevent y eta if i<100, panelvar(i) timevar(t) policyvar(z) window(5) trend(-8 5) 519 | 520 | 521 | * Test nofe, note 522 | xtevent y, panelvar(i) timevar(t) policyvar(z) window(5) nofe trend(-3) plot 523 | xtevent y, panelvar(i) timevar(t) policyvar(z) window(5) nofe note trend(-3) plot 524 | 525 | * Test savek 526 | xtevent y, panelvar(i) timevar(t) policyvar(z) window(5) savek(a) trend(-3) 527 | des a_eq*, s 528 | des a_evtime, s 529 | drop a_* 530 | 531 | * Test factor variables in varlist 532 | cap gen pois=rpoisson(5) 533 | xtevent y i.pois, panelvar(i) timevar(t) policyvar(z) window(5) trend(-3) 534 | cap drop pois 535 | * Test time series variables in varlist 536 | 537 | xtevent y l.eta , panelvar(i) timevar(t) policyvar(z) window(5) trend(-3) 538 | 539 | * Test asymmetric window 540 | * Must fail 541 | * xtevent y eta , panelvar(i) timevar(t) policyvar(z) window(-4 2) trend(-3 5) 542 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(-4 6) trend(-3) plot 543 | 544 | * Test overlay plot 545 | xtevent y , panelvar(i) timevar(t) policyvar(z) window(5) trend(-3, saveov) plot 546 | xteventplot, overlay(trend) 547 | 548 | *------------------------ 2.6: Hypotheses tests ---------------------------------- 549 | 550 | xtevent y eta, panelvar(i) timevar(t) policyvar(z) window(3) 551 | 552 | xteventtest, coefs(1 2) 553 | xteventtest, coefs(1 2) cumul 554 | xteventtest, coefs(-2 -3) 555 | xteventtest, allpre 556 | xteventtest, allpre cumul 557 | xteventtest, allpost 558 | xteventtest, allpost cumul 559 | xteventtest, coefs(1 2) testopts(coef) 560 | xteventtest, linpretrend 561 | xteventtest, overidpre(2) 562 | xteventtest, overidpost(3) 563 | xteventtest, overid 564 | 565 | cap log close -------------------------------------------------------------------------------- /xtevent.pkg: -------------------------------------------------------------------------------- 1 | v 3.1.0 2 | d XTEVENT: Estimation and Visualization in the Linear Panel Event-Study Design 3 | d 4 | d Simon Freyaldenhoven, Federal Reserve Bank of Philadelphia 5 | d Christian Hansen, University of Chicago 6 | d Jorge Pérez Pérez, Banco de México 7 | d Jesse M. Shapiro, Harvard University and NBER 8 | d Constantino Carreto, Banco de México 9 | d 10 | d Support: 11 | d https://github.com/JMSLab/xtevent/issues 12 | d Jorge Pérez Pérez (jorgepp@banxico.org.mx) 13 | d 14 | d Distribution-Date: 20240711 15 | d License: MIT 16 | d 17 | 18 | f ./xtevent/_eventgenvars.ado 19 | f ./xtevent/_eventiv.ado 20 | f ./xtevent/_eventivstatic.ado 21 | f ./xtevent/_eventols.ado 22 | f ./xtevent/_eventolsstatic.ado 23 | f ./xtevent/get_unit_time_effects.ado 24 | f ./xtevent/get_unit_time_effects.sthlp 25 | f ./xtevent/install.txt 26 | f ./xtevent/xtevent.ado 27 | f ./xtevent/xtevent.sthlp 28 | f ./xtevent/xteventplot.ado 29 | f ./xtevent/xteventplot.sthlp 30 | f ./xtevent/xteventtest.ado 31 | f ./xtevent/xteventtest.sthlp 32 | -------------------------------------------------------------------------------- /xtevent/_eventgenvars.ado: -------------------------------------------------------------------------------- 1 | version 13 2 | 3 | program define _eventgenvars, rclass 4 | 5 | #d; 6 | 7 | syntax [anything] [if] [in], 8 | Panelvar(varname) /* Panel variable */ 9 | Timevar(varname) /* Time variable */ 10 | POLicyvar(varname) /* Policy variable */ 11 | [ 12 | LWindow(string) /* Left window */ 13 | RWindow(string) /* Right window */ 14 | w_type(string) /* Window defined by the user (numeric) or define window based on the data time limits (string: max or balanced) */ 15 | norm(numlist) /* Coefficients to normalize */ 16 | 17 | IMPute(string) /*impute policyvar. There are three options: */ 18 | /*nuchange imputes outer missing values of policyvar without verifying staggered adoption*/ 19 | /*stag imputes outer missing values of policyvar verifying staggered adoption*/ 20 | /*instag imputes outer and inner missing values verifying staggered adoption*/ 21 | STatic /* Estimate static model */ 22 | rr(name) /*return imputed policyvar as temporary variable. For use of _eventiv*/ 23 | trcoef(real 0) /*inferior limit to start the trend*/ 24 | methodt(string) /* method for the trend computation*/ 25 | REPeatedcs /*indicate that the input data is a repeated cross-sectional datasets*/ 26 | mkvarlist(name) /* marker of non-missing observations in local varlist */ 27 | 28 | ] 29 | ; 30 | #d cr 31 | 32 | tempvar mz kg touse 33 | 34 | mark `touse' `if' `in' 35 | 36 | * mz maximum of policy outside window 37 | * kg grouped event time variable, grouping dummies outside window 38 | 39 | loc z = "`policyvar'" 40 | 41 | if "`static'"==""{ 42 | * Get span of calendar time 43 | qui su `timevar' if `touse', d 44 | loc tmin=r(min) 45 | loc tmax=r(max) 46 | loc tdiff = `tmax'-`tmin' 47 | 48 | * Using notation from latest draft: https://jorgeperezperez.com/files/EventStudy.pdf 49 | 50 | * Endpoints are left: G+L+1 51 | * Right: M+1 52 | 53 | * Left window is G+L 54 | * Right window is M 55 | 56 | * If you have t=-3 -2 -1 0 1 2 3, tdiff=6 then the maximum window size you can have is 2 on each side 57 | * If you have t=-3 -2 -1 0 1 2 3 4, tdiff=7 then the maximum window size you can have is 2 left, 3 right 58 | 59 | 60 | * Error check for window outside event time range 61 | if "`w_type'"=="numeric" { 62 | if abs(`lwindow') > `=abs(`tdiff')-1' | abs(`rwindow') > `=abs(`tdiff')-1' { 63 | di as err _n "Window outside event-time range." 64 | exit 301 65 | } 66 | } 67 | 68 | * Error check for trend event time range 69 | if "`trcoef'"=="." loc trcoef "" 70 | if "`trcoef'"!="" { 71 | if abs(`trcoef') > `=abs(`tdiff')-1' { 72 | di as err _n "Trend outside time range." 73 | exit 301 74 | } 75 | 76 | * Error check for trend outside window 77 | if "`w_type'"=="numeric" { 78 | if abs(`trcoef')>abs(`lwindow') { 79 | di as err _n "Trend outside window range." 80 | exit 301 81 | } 82 | } 83 | } 84 | 85 | * Check for vars named _k 86 | cap unab oldkvars : _k* 87 | if !_rc { 88 | di as err _n "You have variable names with the _k prefix. _k is reserved for internal -xtevent- variables." 89 | di as err _n "Please rename these variables before proceeding." 90 | exit 110 91 | } 92 | 93 | } 94 | 95 | *parse impute option 96 | if "`impute'"!=""{ 97 | parseimpute `impute' 98 | loc impute = r(impute) 99 | loc saveimp = r(saveimp) 100 | if "`saveimp'"=="." loc saveimp "" 101 | } 102 | 103 | * Check for a variable named as the imputed policyvar 104 | if "`saveimp'"!="" { 105 | cap confirm var `policyvar'_imputed, exact 106 | if !_rc { 107 | di as err _n "You have a variable named `policyvar'_imputed. This name is reserved for the imputed policy variable." 108 | di as err _n "Please drop or rename this variable before proceeding." 109 | exit 110 110 | } 111 | } 112 | 113 | * Check for a valid option in impute 114 | if "`impute'"!=""{ 115 | cap assert "`impute'"=="nuchange" | "`impute'"=="stag" | "`impute'"=="instag" 116 | if _rc { 117 | di as err _n "{bf:`impute'} not allowed in option {bf:impute}." 118 | exit 110 119 | } 120 | } 121 | 122 | *Window(max|balanced) must be specified along with impute(stag|instag) 123 | if "`w_type'"=="string" & !inlist("`impute'","stag","instag") { 124 | di as err _n "Options {bf: window(max)} and {bf: window(balanced)} can be used only if the policyvar follows staggered adoption." 125 | di as err _n "Add {bf:impute(stag)} or {bf:impute(instag)} to check if the policyvar follows staggered adoption and impute it." 126 | exit 197 127 | } 128 | 129 | *** repeated cross section databases 130 | if "`repeatedcs'"!=""{ 131 | 132 | *checks: 133 | *same value of policyvar in a (policyvar, time) cell 134 | tempvar maxpol minpol 135 | qui bysort `panelvar' `timevar': egen `maxpol'=max(`policyvar') if `touse' //it ignores missing values 136 | qui by `panelvar' `timevar': egen `minpol'=min(`policyvar') if `touse' 137 | cap assert `maxpol'==`minpol' 138 | if _rc { 139 | di as err _n "{bf:Policyvar} is not constant within some (`panelvar', `timevar') cells." 140 | exit 110 141 | } 142 | 143 | *missing values 144 | cap assert !missing(`policyvar') if `touse' 145 | if _rc & "`rr'"=="" { //use rr to avoid showing twice the error message in the case of IV 146 | di "{bf:Policyvar} has missing values within some (`panelvar', `timevar') cells. These observations will be ignored." 147 | } 148 | 149 | *proceed with generation of event-time dummies in the repeated cs setting 150 | preserve 151 | qui keep `panelvar' `timevar' `policyvar' `touse' `rr' `mkvarlist' 152 | *sorting by policyvar guarantees not choosing a missing value 153 | qui keep if `touse' 154 | qui bysort `panelvar' `timevar' (`policyvar'): keep if _n==1 //altervative: collapse (min) z if `touse', by(state t) 155 | } 156 | 157 | ********************* find first and last observed values ********************* 158 | 159 | *find minimum valid time (time where there is a no-missing observation) 160 | qui xtset `panelvar' `timevar' 161 | tempvar zmint zmint2 zminv zminv2 zmaxt zmaxt2 zmaxv zmaxv2 162 | qui{ 163 | by `panelvar' (`timevar'): egen long `zmint'=min(`timevar') if !missing(`z') & `touse' 164 | by `panelvar' (`timevar'): egen long `zmint2'=min(`zmint') 165 | *find the corresponding minimum valid value 166 | by `panelvar' (`timevar'): gen double `zminv'=`z' if `timevar'==`zmint2' 167 | by `panelvar' (`timevar'): egen double `zminv2'=min(`zminv') 168 | 169 | *find maximum valid time 170 | by `panelvar' (`timevar'): egen long `zmaxt'=max(`timevar') if !missing(`z') & `touse' 171 | by `panelvar' (`timevar'): egen long `zmaxt2'=max(`zmaxt') 172 | *find the corresponding maximum valid value 173 | by `panelvar' (`timevar'): gen double `zmaxv'=`z' if `timevar'==`zmaxt2' 174 | by `panelvar' (`timevar'): egen double `zmaxv2'=max(`zmaxv') 175 | } 176 | *create a copy of z. If imputation happens, it will be on this copy 177 | tempvar zn2 178 | qui gen double `zn2'=`z' 179 | 180 | 181 | ********** Verify consistency with staggered adoption ******************* 182 | 183 | loc bin 0 184 | loc norever 0 185 | loc bounds 0 186 | * show a warning message if we don't know treatment time for some units due to missing values in policyvar 187 | if ("`impute'"=="stag" | "`impute'"=="instag") { 188 | 189 | tempvar zwd zwu seq 190 | qui gen double `zwd'=`z' if `touse' 191 | qui by `panelvar' (`timevar'): replace `zwd'=`zwd'[_n-1] if missing(`z') & `timevar'>=`zmint2' & `timevar'<=`zmaxt2' & `touse' 192 | qui gen double `zwu'=`z' if `touse' 193 | sort `panelvar' `timevar' 194 | qui by `panelvar': gen int `seq' = -_n if `touse' 195 | sort `panelvar' `seq' 196 | qui by `panelvar': replace `zwu'=`zwu'[_n-1] if missing(`z') & `timevar'>=`zmint2' & `timevar'<=`zmaxt2' & `touse' 197 | sort `panelvar' `timevar' 198 | cap assert `zwd'==`zwu' if missing(`z') & `timevar'>=`zmint2' & `timevar'<=`zmaxt2' & `touse' 199 | if _rc{ 200 | di "Event time is unknown for some units due to missing values in policyvar." 201 | } 202 | } 203 | 204 | ***************** verify whether policyvar is binary ******************* 205 | 206 | ****** Check if z is binary 207 | cap assert inlist(`z',0,1,.) if `touse' 208 | if _rc { 209 | qui su `z' if `touse' 210 | loc rminz=`=r(min)' 211 | loc rmaxz=`=r(max)' 212 | cap assert inlist(`z',`rminz',`rmaxz',.) 213 | if !_rc { 214 | if ("`impute'"=="stag" | "`impute'"=="instag"){ 215 | di "Policyvar is binary, but its values are different from 0 and 1. Assuming `=r(min)' as the unadopted policy state and `=r(max)' as the adopted policy state." 216 | } 217 | loc bin 1 218 | } 219 | else loc bin 0 220 | } 221 | else { 222 | loc rminz=0 223 | loc rmaxz=1 224 | loc bin 1 225 | } 226 | 227 | * If not binary, return to default 228 | if `bin'==0 & ("`impute'"=="stag" | "`impute'"=="instag") { 229 | 230 | di "The policy variable is not binary. Assuming non-staggered adoption (no imputation)." 231 | di "If event dummies and variables are saved, event-time will be missing." 232 | loc impute ="" 233 | } 234 | if `bin'==0 & "`w_type'"=="string" { 235 | di as err _n "Cannot use {bf:window(`lwindow')} if policyvar doesn't follow staggered adoption." 236 | exit 199 237 | } 238 | 239 | *********** verify no reversion **************************** 240 | *(e.g. if binary 0 and 1, once reached 1, never returns to zero) 241 | 242 | tempvar zr zn l1 243 | qui gen double `zr'=`z' 244 | *where there are missings, impute the previous value 245 | qui by `panelvar' (`timevar'): replace `zr'=`zr'[_n-1] if missing(`zr') & `timevar'>=`zmint2' & `timevar'<=`zmaxt2' & `touse' 246 | 247 | qui by `panelvar' (`timevar'): gen byte `l1'= (F1.`zr'>=`zr') if !missing(`zr') & !missing(F1.`zr') & `touse' 248 | cap assert `l1'==1 if !missing(`l1') 249 | if ! _rc{ 250 | loc norever 1 251 | } 252 | 253 | 254 | if `norever'==0 & ("`impute'"=="stag" | "`impute'"=="instag") { 255 | di "Policyvar changes more than once for some units. Assuming non-staggered adoption (no imputation)." 256 | loc impute="" 257 | } 258 | if `norever'==0 & "`w_type'"=="string" { 259 | di as err _n "Cannot use {bf:window(`lwindow')} if policyvar doesn't follow staggered adoption." 260 | exit 199 261 | } 262 | 263 | ****** if no-reversion holds, verify "bounds" condition: e.g. if binary 0 and 1, verify 0 as the first observed value and 1 as the last observed value 264 | if ("`impute'"=="stag" | "`impute'"=="instag") { 265 | tempvar notmiss zt minzt maxzt 266 | qui{ 267 | gen byte `notmiss'=!missing(`z') 268 | 269 | by `panelvar' (`timevar'): gen long `zt'=`timevar' if `notmiss'==1 & `touse' 270 | by `panelvar' (`timevar'): egen long `maxzt'=max(`zt') 271 | by `panelvar' (`timevar'): egen long `minzt'=min(`zt') 272 | } 273 | *first filter: all units satisfy the bounds condition? 274 | if `bin'==1 & `norever'==1 { 275 | *verify the lower-bound value 276 | cap assert `z'==`rminz' if `minzt'==`timevar' & `touse' 277 | * if the lower-bound value is zero, then test the upper-bound 278 | if !_rc{ 279 | cap assert `z'==`rmaxz' if `maxzt'==`timevar' & `touse' 280 | if !_rc loc bounds 1 281 | } 282 | } 283 | 284 | *second filter: if some units didn't satisfy the first filter, then allow those units to have only adopted policy or only unadopted policy (and no missing values inside the observed range) 285 | if `bin'==1 & `norever'==1 & `bounds'==0 { 286 | 287 | tempvar ilb iub sb sbmin 288 | qui{ 289 | by `panelvar' (`timevar'): gen byte `ilb'=(`z'==`rminz') if `minzt'==`timevar' & `touse' 290 | by `panelvar' (`timevar'): gen byte `iub'=(`z'==`rmaxz') if `maxzt'==`timevar' & `touse' 291 | egen byte `sb'=rowtotal(`ilb' `iub') if (`minzt'==`timevar' | `maxzt'==`timevar') 292 | 293 | *sbmin is an indicator of the units that satisfied the first filter 294 | by `panelvar' (`timevar'): egen byte `sbmin'=min(`sb') if `timevar'>=`zmint2' & `timevar'<=`zmaxt2' & `touse' 295 | } 296 | 297 | * For obs that did not satisfy the first filter, policy var must be constant 298 | 299 | tempvar tag ndistinct 300 | egen `tag' = tag(`panelvar' `z') if `touse' 301 | egen `ndistinct' = total(`tag'), by(`panelvar') 302 | cap assert `ndistinct' == 1 if `sbmin' == 0 & `touse' 303 | if !_rc loc bounds 1 304 | 305 | } 306 | 307 | 308 | if `bounds'==0 & ("`impute'"=="stag" | "`impute'"=="instag") { 309 | di "For some units, the changes in policyvar are not consistent with no-unobserved-change. Reverting to default (no imputation)." 310 | loc impute ="" 311 | } 312 | } 313 | 314 | loc binnorev = 0 315 | if `bin'==1 & `norever'==1 { 316 | loc binnorev = 1 317 | } 318 | 319 | return scalar binnorev = `binnorev' 320 | 321 | if `bounds'==0 & "`w_type'"=="string" { 322 | di as err _n "Cannot use {bf:window(`lwindow')} if policyvar doesn't follow staggered adoption." 323 | exit 199 324 | } 325 | 326 | ***************** apply no unobserved change *************************** 327 | 328 | if "`impute'"!="" { 329 | qui replace `zn2'=`zminv2' if `timevar'<`zmint2' 330 | qui replace `zn2'=`zmaxv2' if `timevar'>`zmaxt2' 331 | } 332 | 333 | ************** impute inner missing values *********************** 334 | if "`impute'"=="instag" { 335 | 336 | tempvar zdown zup seq2 337 | qui gen double `zdown'=`z' 338 | sort `panelvar' `timevar' 339 | qui replace `zdown'=`zdown'[_n-1] if missing(`zdown') 340 | qui gen double `zup'=`z' 341 | sort `panelvar' `timevar' 342 | qui by `panelvar': gen int `seq2' = -_n 343 | sort `panelvar' `seq2' 344 | qui replace `zup'=`zup'[_n-1] if missing(`zup') 345 | sort `panelvar' `timevar' 346 | 347 | qui replace `zn2'=`zdown' if `timevar'>=`zmint2' & `timevar'<=`zmaxt2' & missing(`z') & `zdown'==`zup' 348 | 349 | } 350 | 351 | **************** find event-time limits based on observed data range ******** 352 | 353 | if "`w_type'"=="string" { 354 | 355 | * save window selection criteria 356 | loc ws_type `lwindow' 357 | 358 | qui xtset `panelvar' `timevar', noquery 359 | qui sort `panelvar' `timevar', stable 360 | 361 | *create relative-to-event-time variable 362 | tempvar d1z ttreat ttreat2 rtime minrtime maxrtime 363 | qui gen long `d1z'=d1.`zn2' if `touse' & `mkvarlist' 364 | qui gen long `ttreat' = `timevar' if `d1z'!=0 & !missing(`d1z') & `touse' & `mkvarlist' 365 | qui by `panelvar' (`timevar'): egen long `ttreat2' = min(`ttreat') if `touse' & `mkvarlist' 366 | qui by `panelvar' (`timevar'): gen long `rtime' = `timevar' - `ttreat2' if `touse' & `mkvarlist' 367 | 368 | if "`lwindow'"=="max" { 369 | qui sum `rtime' if `touse' & `mkvarlist' 370 | loc lwindow = r(min) 371 | loc rwindow = r(max) 372 | } 373 | if "`lwindow'"=="balanced" { 374 | qui by `panelvar' (`timevar'): egen long `minrtime' = min(`rtime') if `touse' & `mkvarlist' 375 | qui by `panelvar' (`timevar'): egen long `maxrtime' = max(`rtime') if !missing(`rtime') & `touse' & `mkvarlist' 376 | qui sum `minrtime' if `touse' & `mkvarlist' 377 | loc lwindow = r(max) 378 | qui sum `maxrtime' 379 | loc rwindow = r(min) 380 | } 381 | 382 | *adjust for the endpoints 383 | loc lwindow = `lwindow' +1 384 | loc rwindow = `rwindow' -1 385 | 386 | *message about calculated limits 387 | di "The calculated window by {bf:window(`ws_type')} is (`lwindow',`rwindow'), plus the endpoints `=`lwindow'-1' and `=`rwindow'+1'." 388 | 389 | **** Error messages if calculated window limits are not valid 390 | 391 | *These checks were made earlier for numeric window. We check them again here once we know the calculated window 392 | 393 | *make sure left window is negative and right window is positive 394 | if (-`lwindow'<0 | `rwindow'<0) { 395 | di as err _n "Left window can not be positive and right window can not be negative." 396 | di as err _n "Check for first-treated units and last-treated units. Both types of units might have few common periods around treatment time which causes a narrow calculated window." 397 | exit 198 398 | } 399 | 400 | *Normalized coefficient for trend adjustment is outside estimation window 401 | if "`trend'"!="" { 402 | if `trcoef'<`lwindow'-1 | `trcoef'>`rwindow'+1 { 403 | di as err "{bf:trend} is outside estimation window." 404 | exit 301 405 | } 406 | } 407 | 408 | * Check that normalization is in window 409 | if "`norm'"!="" { 410 | if (`norm' < `=`lwindow'-1' | `norm' > `rwindow') { 411 | di as err _n "The coefficient to be normalized to 0 is outside of the estimation window." 412 | exit 498 413 | } 414 | } 415 | 416 | } 417 | 418 | ****************************** event-time dummies *********************** 419 | *If impute is specified in the IV setting, note that the following code section is not executed in the first call to _eventgenvars because in the call the option static is added 420 | 421 | if "`static'"==""{ 422 | qui xtset `panelvar' `timevar', noquery 423 | 424 | qui sort `panelvar' `timevar', stable 425 | 426 | * Generate event time dummies 427 | 428 | *create z delta 429 | tempvar zd 430 | qui gen double `zd'=`zn2'- L1.`zn2' 431 | 432 | *observed data range 433 | tempvar minz maxz minz2 maxz2 434 | qui by `panelvar' (`timevar'): egen long `minz'=min(`timevar') if !missing(`zn2') 435 | qui by `panelvar' (`timevar'): egen long `minz2'=min(`minz') 436 | 437 | qui by `panelvar' (`timevar'): egen long `maxz'=max(`timevar') if !missing(`zn2') 438 | qui by `panelvar' (`timevar'): egen long `maxz2'=max(`maxz') 439 | 440 | qui forv klevel=`lwindow'(1)`rwindow' { 441 | loc absk = abs(`klevel') 442 | 443 | if `klevel'<0 { 444 | loc plus = "m" 445 | qui { 446 | by `panelvar' (`timevar'): gen double _k_eq_`plus'`absk'=F`absk'.`zd' if ((`timevar'>=`minz2') & (`timevar'<=`maxz2')) & `touse' 447 | la var _k_eq_`plus'`absk' "Event-time = - `absk'" 448 | *this to impute zeros and complete the observed range 449 | tempvar minp minp2 maxp maxp2 450 | if "`impute'"!=""{ 451 | by `panelvar' (`timevar'): egen long `minp'=min(`timevar') if !missing(_k_eq_`plus'`absk') 452 | by `panelvar' (`timevar'): egen long `minp2'=min(`minp') 453 | by `panelvar' (`timevar'): egen long `maxp'=max(`timevar') if !missing(_k_eq_`plus'`absk') 454 | by `panelvar' (`timevar'): egen long `maxp2'=max(`maxp') 455 | by `panelvar' (`timevar'): replace _k_eq_`plus'`absk'=0 if missing(_k_eq_`plus'`absk') & ((`timevar' < `minp2') & (`timevar' >= `minz2')) | ((`timevar' > `maxp2') & (`timevar' <= `maxz2')) & `touse' 456 | } 457 | } 458 | } 459 | else { 460 | loc plus "p" 461 | qui { 462 | by `panelvar' (`timevar'): gen double _k_eq_`plus'`absk'=L`absk'.`zd' if ((`timevar'>=`minz2') & (`timevar'<=`maxz2')) & `touse' 463 | la var _k_eq_`plus'`absk' "Event-time = + `absk'" 464 | *this to impute zeros and complete the observed range 465 | cap drop `minp' `minp2' `maxp' `maxp2' 466 | tempvar minp minp2 maxp maxp2 467 | if "`impute'"!=""{ 468 | by `panelvar' (`timevar'): egen long `minp'=min(`timevar') if !missing(_k_eq_`plus'`absk') 469 | by `panelvar' (`timevar'): egen long `minp2'=min(`minp') 470 | by `panelvar' (`timevar'): egen long `maxp'=max(`timevar') if !missing(_k_eq_`plus'`absk') 471 | by `panelvar' (`timevar'): egen long `maxp2'=max(`maxp') 472 | by `panelvar' (`timevar'): replace _k_eq_`plus'`absk'=0 if missing(_k_eq_`plus'`absk') & ((`timevar' < `minp2') & (`timevar' >= `minz2')) | ((`timevar' > `maxp2') & (`timevar' <= `maxz2')) & `touse' 473 | } 474 | } 475 | } 476 | 477 | } 478 | 479 | 480 | * Generate event time 481 | * To fix: If multiple events, should generate all. 482 | qui { 483 | if (`bin'==1 & `norever'==1) { 484 | tempvar __kmax p0mink 485 | gen long __k=. 486 | by `panelvar' (`timevar'): egen long `p0mink'=min(`timevar') if _k_eq_p0!=0 & !missing(_k_eq_p0) 487 | by `panelvar' (`timevar'): egen long `__kmax'=max(`p0mink') 488 | replace __k = `timevar' - `__kmax' 489 | order _k* __k, after(`zd') 490 | /* we only create the event time variable when we only have one event per unit. */ 491 | } 492 | else { 493 | qui gen byte __k=. 494 | } 495 | } 496 | 497 | 498 | * Error check for window outside event time range 499 | qui sum __k if `touse' 500 | if `=-`lwindow'+1' > abs(r(min)) | `=`rwindow'+1' > abs(r(max)) { 501 | di as err _n "Window outside event-time range" 502 | qui drop _k* 503 | qui drop __k 504 | exit 301 505 | } 506 | 507 | *Ordered list of event-time dummies 508 | unab evs : _k_eq_* 509 | loc f_evs : word 1 of `evs' 510 | 511 | * Generate endpoint dummies 512 | * Left 513 | * -6 : z lead 6 514 | * +5 : z lag 5 515 | 516 | * Absorbing version 517 | if "`impute'"!="" { 518 | qui { 519 | * Left 520 | gen double _k_eq_m`=-`lwindow'+1' = (1-f`=-`lwindow''.`zn2') if ((`timevar'>=`minz2') & (`timevar'<=`maxz2')) & `touse' 521 | *find maximum valid time for left endpoint 522 | tempvar maxl maxl2 523 | by `panelvar' (`timevar'): egen long `maxl'=max(`timevar') if !missing(_k_eq_m`=-`lwindow'+1') 524 | by `panelvar' (`timevar'): egen long `maxl2'=max(`maxl') 525 | *replace with zeros (the last observed for the endpoint) 526 | replace _k_eq_m`=-`lwindow'+1' = _k_eq_m`=-`lwindow'+1'[_n-1] if _k_eq_m`=-`lwindow'+1' == . & (`timevar'>`maxl2') & (`timevar'<=`maxz2') & `touse' 527 | order _k_eq_m`=-`lwindow'+1', before(`f_evs') 528 | 529 | * Right 530 | tempvar seq3 531 | gen double _k_eq_p`=`rwindow'+1'= l`=`rwindow'+1'.`zn2' if ((`timevar'>=`minz2') & (`timevar'<=`maxz2')) & `touse' 532 | *find minimun valid time for right endpoint 533 | tempvar minr minr2 534 | by `panelvar' (`timevar'): egen long `minr'=min(`timevar') if !missing(_k_eq_p`=`rwindow'+1') & `touse' 535 | by `panelvar' (`timevar'): egen long `minr2'=min(`minr') 536 | *replace missing values in the upper-right corner 537 | sort `panelvar' `timevar' 538 | by `panelvar': gen int `seq3' = -_n 539 | sort `panelvar' `seq3' 540 | by `panelvar': replace _k_eq_p`=`rwindow'+1'=_k_eq_p`=`rwindow'+1'[_n-1] if (`timevar'>=`minz2') & (`timevar'<`minr2') & `touse' 541 | sort `panelvar' `timevar' 542 | order __k, after(_k_eq_p`=`rwindow'+1') 543 | } 544 | } 545 | * Not absorbing version 546 | else { 547 | qui { 548 | * Left 549 | gen double _k_eq_m`=-`lwindow'+1' = (1-f`=-`lwindow''.`zn2') if ((`timevar'>=`minz2') & (`timevar'<=`maxz2')) & `touse' 550 | order _k_eq_m`=-`lwindow'+1', before(`f_evs') 551 | * Right 552 | gen double _k_eq_p`=`rwindow'+1'= l`=`rwindow'+1'.`zn2' if ((`timevar'>=`minz2') & (`timevar'<=`maxz2')) & `touse' 553 | order __k, after(_k_eq_p`=`rwindow'+1') 554 | } 555 | } 556 | 557 | 558 | la var _k_eq_m`=-`lwindow'+1' "Left endpoint" 559 | la var _k_eq_p`=`rwindow'+1' "Right endpoint" 560 | 561 | * Drop units where treatment can not be timed 562 | 563 | * If z is binary, check if event-time is missing 564 | if `bin' & `norever' { 565 | cap assert __k !=. if `touse' 566 | * If it is, check for which units 567 | if _rc { 568 | tempvar etmis etmismax minz 569 | qui { 570 | gen byte `etmis' = (__k==.) & `touse' 571 | by `panelvar' : egen byte `etmismax' = max(`etmis') if `touse' 572 | by `panelvar' : egen double `mz' = max(`z') if `touse' 573 | by `panelvar' : egen double `minz' = min(`z') if `touse' 574 | } 575 | * Exclude units with missing event-time 576 | foreach x of varlist _k_eq* { 577 | qui replace `x' = . if `etmismax'==1 & `mz'>0 & `minz'==0 & `touse' 578 | } 579 | qui replace __k = . if `etmismax'==1 & `touse' 580 | qui levelsof `panelvar' if `etmismax'==1 & `mz'>0 & `minz'==0 & `touse' , loc(mis) 581 | foreach j in `mis' { 582 | di as txt _n "Unit `j' not used because of ambiguous event-time due to missing values in policyvar." 583 | } 584 | qui replace `touse' = 0 if `etmismax'==1 & `mz'>0 & `minz'==0 & `touse' 585 | return local ambiguous = "`mis'" 586 | } 587 | } 588 | 589 | * Set omitted variable. 590 | unab included : _k* 591 | loc toexc "" 592 | foreach j in `norm' { 593 | loc abs = abs(`j') 594 | if `j'<0 loc toexc "`toexc' _k_eq_m`abs'" 595 | else if `j'>=0 loc toexc "`toexc' _k_eq_p`abs'" 596 | } 597 | loc included: list local included - toexc 598 | 599 | * Group k 600 | qui gen long `kg' = __k if `touse' 601 | * Group if outside window 602 | qui replace `kg' = `=`lwindow'-1' if __k < `=`lwindow'' & `touse' 603 | qui replace `kg' = `=`rwindow'+1' if __k >= `rwindow' & `touse' 604 | qui levelsof `kg', loc(kgs) 605 | 606 | * If extrapolating a linear trend, exclude some of the event time dummies 607 | loc komittrend "" 608 | * di "`included'" 609 | if "`methodt'" == "ols" { 610 | if `bin'!=1 | `norever'!=1 { 611 | di as err _n "Method ols cannot extrapolate linear trend using a policyvar that is not binary or has multiple events. Use GMM instead." 612 | exit 301 613 | } 614 | 615 | * Generate the trend 616 | qui gen int _ttrend = __k if `touse' 617 | qui replace _ttrend = 0 if !inrange(_ttrend,`lwindow',`=`rwindow'') & `touse' 618 | * qui replace _ttrend = 0 if _ttrend<`=`trend'' & `touse' 619 | qui replace _ttrend = 0 if mi(_ttrend) & `touse' 620 | la var _ttrend trend 621 | 622 | 623 | * Exclude the coefficients that are restricted: Those on the negative values of the trend range, plus the endpoints 624 | loc komittrend "" 625 | 626 | foreach klevel in `kgs' { 627 | if (inrange(`klevel',`trcoef',-1) & `klevel' != -1) { 628 | loc absk = abs(`klevel') 629 | if `klevel'<0 loc plus = "m" 630 | else loc plus "p" 631 | loc l`plus'`absk' "`plus'`absk'" 632 | loc toexc "_k_eq_`l`plus'`absk''" 633 | loc included: list included - toexc 634 | loc komittrend = "`komittrend' `klevel'" 635 | } 636 | *noi di "***" 637 | *noi di "`included'" 638 | } 639 | * loc toexc: word 1 of `included' 640 | *loc included: list local included - toexc 641 | *loc total: word count `included' 642 | *loc toexc: word `total' of `included' 643 | *loc included: list local included - toexc 644 | * di "`included'" 645 | } 646 | 647 | * Save the names of included reg vars to extract these coefficients from the overall matrix later 648 | 649 | loc j=1 650 | loc names "" 651 | foreach var in `included' { 652 | if `j'==1 loc names `""`var'""' 653 | else loc names `"`names'.."`var'""' 654 | loc ++ j 655 | } 656 | return local included = "`included'" 657 | return local names = `"`names'"' 658 | return local komittrend= "`komittrend'" 659 | 660 | } 661 | 662 | * if input window was max or balanced, return the calculated window limits 663 | if "`w_type'"=="string" { 664 | return local lwindow = `lwindow' 665 | return local rwindow = `rwindow' 666 | } 667 | 668 | ******* add the imputed policyvar to the database 669 | 670 | if "`impute'"!="" & "`saveimp'"!="" { 671 | qui gen float `policyvar'_imputed=`zn2' 672 | lab var `policyvar'_imputed "policyvar after imputation" 673 | order `policyvar'_imputed, after(`z') 674 | } 675 | *say if imputation succeeded 676 | return local impute= "`impute'" 677 | return local saveimp= "`saveimp'" 678 | *temporary variable equal to imputed policyvar (for _eventiv.ado) 679 | if "`rr'"!="" qui replace `rr'=`zn2' 680 | 681 | *close de process for the repeated cross-sectional dataset 682 | if "`repeatedcs'"!=""{ 683 | *check if trend and imputed policyvar should be merged to the individual-level dataset 684 | *In the IV setting, note that the following variables will not be created in the first call to _eventgenvars because the code that generates them was not executed 685 | loc _ttrend_include "" 686 | cap confirm var _ttrend 687 | if !_rc loc _ttrend_include "_ttrend" 688 | loc imputed_include "" 689 | cap confirm var `policyvar'_imputed 690 | if !_rc loc imputed_include "`policyvar'_imputed" 691 | loc kvarss "" 692 | cap confirm var __k 693 | if !_rc loc kvarss "_k* __k" 694 | 695 | keep `panelvar' `timevar' `policyvar' `kvarss' `_ttrend_include' `imputed_include' `rr' 696 | tempfile state_level 697 | qui save `state_level' 698 | *close the process with the state level dataset 699 | restore 700 | 701 | *merge back to the individual level dataset 702 | *merge also on the policyvar, so missing values in policyvar within a cell, will not get event-time dummy values 703 | qui merge m:1 `panelvar' `timevar' `policyvar' using `state_level', update nogen 704 | *sort `panelvar' `timevar' 705 | if "`kvarss'"!="" order `imputed_include' `kvarss' `_ttrend_include', after(`policyvar') 706 | xtset, clear //otherwise next time you run it: error, repeated time variable (in the repeated cross-sectional setting timevar cannot be setted) 707 | } 708 | end 709 | 710 | 711 | 712 | * Program to parse impute 713 | program define parseimpute, rclass 714 | 715 | syntax [anything] , [saveimp] 716 | 717 | return local impute "`anything'" 718 | return local saveimp "`saveimp'" 719 | end 720 | -------------------------------------------------------------------------------- /xtevent/_eventiv.ado: -------------------------------------------------------------------------------- 1 | version 13 2 | 3 | program define _eventiv, rclass 4 | #d; 5 | syntax varlist(fv ts numeric) [aw fw pw] [if] [in], /* Covariates go in varlist. Can add fv ts later */ 6 | Panelvar(varname) /* Panel variable */ 7 | Timevar(varname) /* Time variable */ 8 | POLicyvar(varname) /* Policy variable */ 9 | LWindow(string) /* Left window */ 10 | RWindow(string) /* Right window */ 11 | w_type(string) /* Window defined by the user (numeric) or define window based on the data time limits (string: max or balanced) */ 12 | proxy (varlist numeric) /* Proxy variable(s) */ 13 | [ 14 | proxyiv(string) /* Instruments. Either numlist with lags or varlist with names of instrumental variables */ 15 | nofe /* No fixed effects */ 16 | note /* No time effects */ 17 | SAVek(string) /* Generate the time-to-event dummies, trend and keep them in the dataset */ 18 | nogen /* Do not generate k variables */ 19 | kvars(string) /* Stub for event dummies to include, if they have been generated already */ 20 | norm(numlist integer max=1) /* Normalization */ 21 | reghdfe /* Use reghdfe for estimation */ 22 | IMPute(string) /*imputation on policyvar*/ 23 | *static /* in this ado used for calling the part of _eventgenvars that imputes*/ 24 | addabsorb(string) /* Absorb additional variables in reghdfe */ 25 | REPeatedcs /*indicate that the input data is a repeated cross-sectional dataset*/ 26 | DIFFavg /* Obtain regular DiD estimate implied by the model */ 27 | * 28 | ] 29 | ; 30 | #d cr 31 | 32 | marksample touse 33 | 34 | tempvar mkvarlist 35 | qui gen byte `mkvarlist' = `touse' 36 | 37 | tempname delta Vdelta bb VV bb2 VV2 delta2 Vdelta2 deltaov Vdeltaov deltax Vdeltax deltaxsc bby bbx VVy VVx tousegen 38 | * bb delta coefficients 39 | * VV variance of delta coefficients 40 | * bb2 delta coefficients for overlay plot 41 | * VV2 variance of delta coefficients for overlay plot 42 | * delta2 included cefficientes in overlaty plot 43 | * VVdelta2 variance of included delta coefficients in overlay plot 44 | 45 | * For eventgenvars, ignore missings in varlist 46 | mark `tousegen' `if' `in' 47 | 48 | * Set norm to -1 if missing 49 | if "`norm'"=="" loc norm -1 50 | 51 | loc i = "`panelvar'" 52 | loc t = "`timevar'" 53 | loc z = "`policyvar'" 54 | 55 | *parse savek 56 | if "`savek'"!="" parsesavek `savek' 57 | foreach l in savek noestimate kreplace { 58 | loc `l' = r(`l') 59 | if "``l''"=="." loc `l' "" 60 | return loc `l' = "``l''" 61 | } 62 | 63 | *If imputation is specified, _eventiv will call _eventgenvars twice. 64 | *The first call only imputes the policyvar, but the second call imputes both the policyvar and the event-time dummies 65 | *First call: bring the imputed policyvar calling only _eventgenvars' imputation code. This call is neccesary to choose the lead order using the imputed policyvar 66 | if ("`impute'"!="") { 67 | *rr is the tempvar to be imputed: create it in _eventiv, so after _eventgenvars we can still have access to it. 68 | tempvar rr 69 | qui gen double `rr'=. 70 | 71 | *call _eventgenvars 72 | _eventgenvars if `tousegen', panelvar(`panelvar') timevar(`timevar') policyvar(`policyvar') impute(`impute') static rr(`rr') lwindow(`lwindow') w_type(`w_type') mkvarlist(`mkvarlist') `repeatedcs' //with option static, we skip the code that generates the event-time dummies 73 | // Include options lwindow and w_type because when selecting lead order for proxyiv we need to evaluate all lead orders limited by the calculated left window in case the user specified window(max) or window(balanced) 74 | 75 | loc impute=r(impute) 76 | if "`impute'"=="." loc impute = "" 77 | *if imputation succeeded, use the values brought by rr 78 | if "`impute'"!="" { 79 | tempvar zimp 80 | qui gen double `zimp'=`rr' 81 | loc z="`zimp'" 82 | } 83 | *otherwise, keep using the original policyvar 84 | else loc z = "`policyvar'" 85 | 86 | * if window(max) or window(balanced), what value is left window? 87 | if "`w_type'"=="string" { 88 | loc lwindow_call1 = r(lwindow) 89 | } 90 | } 91 | 92 | *define local for left window. Can be the input by the user or the value calculated with the data 93 | if "`w_type'"=="numeric" loc lwindow_iter = `lwindow' 94 | else loc lwindow_iter = `lwindow_call1' 95 | 96 | * if dataset is repeated cross-sectional, create leads of policyvar at state level 97 | if "`repeatedcs'"!=""{ 98 | qui { 99 | preserve 100 | tempfile state_level_leads 101 | 102 | keep if `touse' 103 | keep `panelvar' `timevar' (`z') 104 | bysort `panelvar' `timevar' (`z'): keep if _n==1 105 | xtset `panelvar' `timevar' 106 | forv v=1(1)`=-`lwindow_iter''{ 107 | tempvar _fd`v'`z' 108 | qui gen double `_fd`v'`z'' = f`v'.d.`z' 109 | } 110 | save `state_level_leads' 111 | 112 | restore 113 | 114 | *merge on the policyvar as well, so missing values in policyvar within a cell will not get lead values 115 | merge m:1 `panelvar' `timevar' `z' using `state_level_leads', update nogen 116 | } 117 | } 118 | 119 | 120 | loc leads : word count `proxy' 121 | if "`proxyiv'"=="" & `leads'==1 loc proxyiv "select" 122 | 123 | * If proxy specified but no proxyiv, assume numlist for leads of policyvar 124 | if "`proxyiv'"=="" { 125 | di as text _n "No proxy instruments specified. Using leads of differenced policy variables as instruments." 126 | loc leads : word count `proxy' 127 | forv j=1(1)`leads' { 128 | loc proxyiv "`proxyiv' `j'" 129 | } 130 | } 131 | 132 | * IV selection if proxyiv = selection 133 | else if "`proxyiv'"=="select" { 134 | * Only for one proxy case 135 | if `leads'>1 { 136 | di as err "Proxy instrument selection only available for the one proxy - one instrument case" 137 | exit 301 138 | } 139 | else { 140 | di as text _n "proxyiv=select. Selecting lead order of differenced policy variable to use as instrument." 141 | loc Fstart = 0 142 | if `lwindow_iter'== 0 { 143 | di as err _n "Estimation window must contain at least 1 period before the policy change" 144 | di as err "for proxy instrument selection." 145 | exit 301 146 | } 147 | forv v=1(1)`=-`lwindow_iter'' { 148 | if "`repeatedcs'"=="" { 149 | tempvar _fd`v'`z' 150 | qui gen double `_fd`v'`z'' = f`v'.d.`z' if `touse' 151 | } 152 | qui reg `proxy' `_fd`v'`z'' if `touse' 153 | loc Floop = e(F) 154 | if `Floop' > `Fstart' { 155 | loc Fstart = `Floop' 156 | loc proxyiv "`v'" 157 | } 158 | } 159 | di as text _n "Lead `proxyiv' selected." 160 | } 161 | 162 | } 163 | 164 | 165 | * Parse proxyiv and generate leads if neccesary 166 | loc rc=0 167 | loc ivwords = 0 168 | foreach v in `proxyiv' { 169 | cap confirm integer number `v' 170 | if _rc loc ++rc 171 | loc ++ivwords 172 | } 173 | * Three possible types of lists: all numbers for leads, all vars for external instruments, or mixed 174 | * All numbers 175 | if `rc' == 0 { 176 | loc leadivs "" 177 | foreach v in `proxyiv' { 178 | if "`repeatedcs'"!=""{ 179 | qui gen double _fd`v'`z' = `_fd`v'`z'' if `touse' 180 | } 181 | else{ 182 | qui gen double _fd`v'`z' = f`v'.d.`z' if `touse' 183 | } 184 | loc leadivs "`leadivs' _fd`v'`z'" 185 | } 186 | loc instype = "numlist" 187 | loc varivs = "" 188 | } 189 | * All words 190 | else if `rc'==`ivwords' { 191 | foreach v in `proxyiv' { 192 | confirm numeric variable `v' 193 | } 194 | loc instype = "varlist" 195 | loc leadivs = "" 196 | loc varivs = "`proxyiv'" 197 | } 198 | * Mixed 199 | else { 200 | loc leadivs "" 201 | loc varivs "" 202 | foreach v in `proxyiv' { 203 | cap confirm integer number `v' 204 | if _rc loc varivs "`varivs' `v'" 205 | else { 206 | if "`repeatedcs'"!=""{ 207 | qui gen double _fd`v'`z' = `_fd`v'`z'' if `touse' 208 | } 209 | else{ 210 | qui gen double _fd`v'`z' = f`v'.d.`z' if `touse' 211 | } 212 | loc leadivs "`leadivs' _fd`v'`z'" 213 | } 214 | } 215 | 216 | loc instype "mixed" 217 | } 218 | 219 | * Count normalizations and set omitted coefs for plot accordingly 220 | * Need one more normalization per IV 221 | loc komit "" 222 | loc norm0 "`norm'" 223 | 224 | *split proxyiv into two lists: only numbers or only varnames 225 | loc proxyiv_numbers "" 226 | loc proxyiv_vrnames "" 227 | foreach v in `proxyiv' { 228 | cap confirm number `v' 229 | if !_rc loc proxyiv_numbers "`proxyiv_numbers' `v'" 230 | else loc proxyiv_vrnames "`proxyiv_vrnames' `v'" 231 | } 232 | foreach v in `proxyiv_numbers' { 233 | cap confirm integer number `v' 234 | if _rc { 235 | di as err "Lead of policy variable to be used as instrument must be an integer." 236 | exit 301 237 | } 238 | } 239 | 240 | * Check that leads in proxyiv are in the estimation window 241 | foreach v in `proxyiv_numbers' { 242 | if (`v' > `=-`lwindow_iter'+1') { 243 | di as err "Lead `v' of policy variable to be used as instrument is outside estimation window." 244 | exit 301 245 | } 246 | } 247 | 248 | * Set normalizations in case these are numbers, so we are using leads of delta z 249 | loc ivnorm "" 250 | if "`instype'"=="numlist" | "`instype'"=="mixed" { 251 | foreach v in `proxyiv_numbers' { 252 | if `=-`v''==`norm' { 253 | loc ivnorm "`ivnorm' `=-`v'-1'" 254 | di as txt _n "The corresponding coefficient of lead `v' and the normalized coefficient were the same. Lead `=`v'' has been changed to `=`v'+1'." 255 | if (-`ivnorm' > `=-`lwindow_iter'+1') { 256 | di as err "Lead `= - `ivnorm'' of policy variable to be used as instrument is outside estimation window." 257 | exit 301 258 | } 259 | loc repeatlead=strmatch("`proxyiv_numbers'","*`=`v'+1'*") 260 | if "`repeatlead'"=="0"{ 261 | di as txt _n "The coefficient at `norm' is normalized to zero." 262 | di as txt _n "For estimation with proxy variables, an additional coefficient needs to be normalized to zero." 263 | di as txt _n "The coefficient at `=-`v'-1' was selected to be normalized to zero." 264 | } 265 | } 266 | else { 267 | loc ivnorm "`ivnorm' -`v'" 268 | di as txt _n "The coefficient at `norm' is normalized to zero." 269 | di as txt _n "For estimation with proxy variables, an additional coefficient needs to be normalized to zero." 270 | di as txt _n "The coefficient at `=-`v'' was selected to be normalized to zero." 271 | } 272 | } 273 | } 274 | 275 | 276 | *set normalizations for external instruments 277 | *get the pool of available coefficients for normalization 278 | loc available "" 279 | forvalues l=1/`=-`lwindow_iter'+1'{ 280 | loc l = -`l' 281 | loc available "`available' `l'" 282 | } 283 | loc available: list available - norm 284 | foreach var of loc proxyiv_vrnames{ 285 | loc available: list available - ivnorm 286 | loc lenav: word count(`available') 287 | if `lenav'==1 { 288 | di as err "Number of instruments specified in {bf:proxyiv} reached the maximum imposed by the number of pre-event periods." 289 | exit 301 290 | } 291 | *normalize one extra coefficient per external instrument 292 | loc avcomma : subinstr loc available " " ",", all 293 | loc avmax = max(`avcomma') //choose the coefficient closest to zero 294 | loc ivnorm "`ivnorm' `avmax'" // add it to ivnorm 295 | di as text _n "The coefficient at `avmax' was selected to be normalized to zero" 296 | } 297 | 298 | * Normalize one more lag if normalization = number of proxys 299 | if "`instype'"=="numlist" | "`instype'"=="mixed" { 300 | loc np: word count `proxy' 301 | * loc npiv: word count `norm' `ivnorm' 302 | loc npiv : list norm | ivnorm 303 | loc npiv : list uniq npiv 304 | loc npiv : word count `npiv' 305 | if `np'==`npiv' { 306 | loc ivnormcomma : subinstr local ivnorm " " ",", all 307 | loc ivmin = min(`ivnormcomma') 308 | loc ivnorm "`ivnorm' `=`ivmin'-1'" 309 | } 310 | } 311 | 312 | * No need to normalize for external instruments. If the user generates a lead of z and uses it as a variable, the instrument is collinear. 313 | 314 | foreach j in `norm' `ivnorm' { 315 | loc norm "`norm' `j' " 316 | loc komit "`komit' `j'" 317 | } 318 | loc komit: list uniq komit 319 | 320 | if "`gen'" != "nogen" { 321 | *If impute was specified, this is the second call to _eventgenvars: this time, both the policyvar and the event-time dummies will be imputed. Additional computations will happen as well (e.g., macros, etc.). 322 | _eventgenvars if `tousegen', panelvar(`panelvar') timevar(`timevar') policyvar(`policyvar') lwindow(`lwindow') rwindow(`rwindow') w_type(`w_type') `trend' norm(`norm') impute(`impute') mkvarlist(`mkvarlist') `repeatedcs' 323 | loc included=r(included) 324 | loc names=r(names) 325 | loc komittrend=r(komittrend) 326 | loc ambiguous = r(ambiguous) 327 | if "`komittrend'"=="." loc komittrend = "" 328 | *if window was max or balanced, use calculated left and right window limits 329 | if "`w_type'"=="string" { 330 | loc lwindow = r(lwindow) 331 | loc rwindow = r(rwindow) 332 | } 333 | } 334 | else { 335 | loc kvstub "`kvars'" 336 | loc j=1 337 | loc names "" 338 | loc included "" 339 | foreach var of varlist `kvstub'* { 340 | if `norm' < 0 loc kvomit = "m`=abs(`norm')'" 341 | else loc kvomit "p`=abs(`norm')'" 342 | if "`var'"=="`kvstub'_evtime" | "`var'" == "`kvstub'_eq_`kvomit'" continue 343 | if "`kvstub'"!="_k" { 344 | loc sub : subinstr local var "`kvstub'" "_k", all 345 | clonevar `sub' = `var' 346 | } 347 | else { 348 | loc sub = "`var'" 349 | } 350 | if `j'==1 loc names `""`sub'""' 351 | else loc names `"`names'.."`sub'""' 352 | * " 353 | loc included "`included' `sub'" 354 | loc ++ j 355 | } 356 | loc komittrend=r(komittrend) 357 | if "`komittrend'"=="." loc komittrend = "" 358 | } 359 | *" 360 | loc komit "`norm' `komittrend'" 361 | loc komit = strtrim(stritrim("`komit'")) 362 | loc komit: list uniq komit 363 | 364 | * Check that the iv normalization works 365 | foreach v in `leadivs' `varivs' { 366 | cap _rmdcoll `v' `included' if `touse' 367 | if _rc { 368 | di as err "Instrument {bf:`v'} is collinear with the included event-time dummies. You may have generated leads of the policy variable and included them in the proxyiv option instead of specifying the lead numbers." 369 | exit 301 370 | } 371 | } 372 | 373 | if "`te'" == "note" loc tte "" 374 | else loc tte "i.`t'" 375 | 376 | **** Main regression 377 | 378 | ** In the repeated cross section case with fixed effects, cannot use xtivreg, so default to reghdfe 379 | 380 | if "`repeatedcs'"!="" & "`fe'"!="nofe" { 381 | loc reghdfe = "reghdfe" 382 | di as text _n "Using {cmd:reghdfe} for fixed effects estimation with repeated cross-sectional data." 383 | } 384 | 385 | if "`noestimate'"==""{ 386 | if "`reghdfe'"=="" { 387 | 388 | if "`fe'" == "nofe" { 389 | loc cmd "ivregress 2sls" 390 | loc ffe "" 391 | loc small "small" 392 | } 393 | else { 394 | loc cmd "xtivreg" 395 | loc ffe "fe" 396 | } 397 | *translate standard error specification: 398 | *analyze inclusion of cluster or robust in options 399 | parse_es ,`options' 400 | foreach orig in cl_orig rob_orig vce_orig other_opts{ 401 | loc `orig' = r(`orig') 402 | if "``orig''"=="." loc `orig' "" 403 | } 404 | *if xtivreg, warn the user about robust estandar errors equivalent to vce(cluster panelvar) 405 | if "`cmd'"=="xtivreg" & (("`vce_orig'"=="robust" | "`vce_orig'"=="r") | "`rob_orig'"!=""){ 406 | di as text _n "You asked for robust standard errors and the underlying estimation command is {cmd:xtivreg}. Standard errors will be clustered by panelvar. See {help xtivreg##options_fe:xtivreg}." 407 | } 408 | *if it doesn't contain cluster and robust: 409 | if "`cl_orig'"=="" & "`rob_orig'"=="" { 410 | `cmd' `varlist' (`proxy' = `leadivs' `varivs') `included' `tte' [`weight'`exp'] if `touse' , `ffe' `small' `options' 411 | } 412 | *if it contains either cluster or robust: 413 | else{ 414 | *if the user already specified vce, then we cannot specify a second vce 415 | if "`vce_orig'"!="" { 416 | *execute as defined by the user and expect some error 417 | `cmd' `varlist' (`proxy' = `leadivs' `varivs') `included' `tte' [`weight'`exp'] if `touse' , `ffe' `small' `options' 418 | } 419 | else{ 420 | loc vce_opt "" 421 | *parse cluster 422 | if "`cl_orig'"!=""{ 423 | loc vce_opt = "vce(cluster `cl_orig')" //if robust is also specified, no need to add it 424 | } 425 | else { 426 | loc vce_opt = "vce(robust)" 427 | } 428 | `cmd' `varlist' (`proxy' = `leadivs' `varivs') `included' `tte' [`weight'`exp'] if `touse' , `ffe' `small' `vce_opt' `other_opts' 429 | } 430 | } 431 | } 432 | else { 433 | loc noabsorb "" 434 | *absorb nothing 435 | if "`fe'" == "nofe" & "`tte'"=="" & "`addabsorb'"=="" { 436 | *loc noabsorb "noabsorb" 437 | /*the only option ivreghdfe inherits from reghdfe is absorb, therefore it doesn't support noabsorb. In contrast with reghdfe, ivreghdfe doesn't require noabsorb when absorb is not specified*/ 438 | loc abs "" 439 | } 440 | *absorb only one 441 | else if "`fe'" == "nofe" & "`tte'"=="" & "`addabsorb'"!="" { 442 | loc abs "absorb(`addabsorb')" 443 | } 444 | else if "`fe'" == "nofe" & "`tte'"!="" & "`addabsorb'"=="" { 445 | loc abs "absorb(`t')" 446 | } 447 | else if "`fe'" != "nofe" & "`tte'"=="" & "`addabsorb'"=="" { 448 | loc abs "absorb(`i')" 449 | } 450 | *absorb two 451 | else if "`fe'" == "nofe" & "`tte'"!="" & "`addabsorb'"!="" { 452 | loc abs "absorb(`t' `addabsorb')" 453 | } 454 | else if "`fe'" != "nofe" & "`tte'"=="" & "`addabsorb'"!="" { 455 | loc abs "absorb(`i' `addabsorb')" 456 | } 457 | else if "`fe'" != "nofe" & "`tte'"!="" & "`addabsorb'"=="" { 458 | loc abs "absorb(`i' `t')" 459 | } 460 | *absorb three 461 | else if "`fe'" != "nofe" & "`tte'"!="" & "`addabsorb'"!="" { 462 | loc abs "absorb(`i' `t' `addabsorb')" 463 | } 464 | * 465 | else { 466 | loc abs "absorb(`i' `t' `addabsorb')" 467 | } 468 | 469 | *analyze inclusion of vce in options 470 | loc vce_y= strmatch("`options'","*vce(*)*") 471 | 472 | *if user did not specify vce option 473 | if "`vce_y'"=="0" { 474 | ivreghdfe `varlist' (`proxy' = `leadivs' `varivs') `included' [`weight'`exp'] if `touse', `abs' `noabsorb' `options' 475 | } 476 | *if user did specify vce option 477 | else { 478 | *find start and end of vce text 479 | loc vces=strpos("`options'","vce(") 480 | loc vcef=0 481 | loc ocopy="`options'" 482 | while `vcef'<`vces' { 483 | loc vcef=strpos("`ocopy'", ")") 484 | loc ocopy=subinstr("`ocopy'",")", " ",1) 485 | } 486 | *substrac vce words 487 | loc svce_or=substr("`options'",`vces',`vcef') 488 | loc vce_len=strlen("`svce_or'") 489 | loc svce=substr("`svce_or'",5,`vce_len'-5) 490 | loc svce=strltrim("`svce'") 491 | loc svce=strrtrim("`svce'") 492 | *inspect whether vce contains bootstrap or jackknife 493 | loc vce_bt= strmatch("`svce'","*boot*") 494 | loc vce_jk= strmatch("`svce'","*jack*") 495 | if `vce_bt'==1 | `vce_jk'==1 { 496 | di as err "Options {bf:bootstrap} and {bf:jackknife} are not allowed" 497 | exit 301 498 | } 499 | 500 | *if vce contains valid options, parse those options 501 | *erase vce from original options 502 | loc options_wcve=subinstr("`options'","`svce_or'"," ",1) 503 | *** parse vce(*) **** 504 | loc vce_wc=wordcount("`svce'") 505 | tokenize `svce' 506 | *extract vce arguments 507 | *robust 508 | loc vce_r= strmatch("`svce'","*robust*") 509 | loc vce_r2=0 510 | forv i=1/`vce_wc'{ 511 | loc zz= strmatch("``i''","r") 512 | loc vce_r2=`vce_r2'+`zz' 513 | } 514 | if `vce_r'==1 | `vce_r2'==1 { 515 | loc vceop_r="robust" 516 | } 517 | *cluster 518 | loc vce_c= strmatch("`svce'","*cluster*") 519 | loc vce_c2= strmatch("`svce'","*cl*") 520 | if `vce_c'==1 | `vce_c2'==1 { 521 | forv i=1/`vce_wc'{ 522 | loc vce_r2= strmatch("``i''","*cl*") 523 | if `vce_r2'==1 { 524 | loc j=`i'+1 525 | } 526 | } 527 | loc vceop_c="cluster(``j'')" 528 | } 529 | 530 | ivreghdfe `varlist' (`proxy' = `leadivs' `varivs') `included' [`weight'`exp'] if `touse', `abs' `noabsorb' `options_wcve' `vceop_r' `vceop_c' 531 | } 532 | } 533 | 534 | *clear xtset if repeatedcs and xtivreg, otherwise error message because timevar not setted 535 | if ("`repeatedcs'"!="" & "`cmd'"=="xtivreg") qui xtset, clear 536 | 537 | * Return coefficients and variance matrix of the delta k estimates separately 538 | mat `bb'=e(b) 539 | mat `VV'=e(V) 540 | 541 | mat `delta' = `bb'[1,`names'] 542 | mat `Vdelta' = `VV'[`names',`names'] 543 | 544 | if "`reghdfe'"=="" { 545 | if "`fe'" == "nofe" { 546 | loc df=e(df_r) 547 | } 548 | else { 549 | loc df=e(df_rz) 550 | } 551 | } 552 | else { 553 | loc df=e(df_r) 554 | if `df'==. loc df=e(Fdf2) 555 | } 556 | 557 | loc kmax=`=`rwindow'+1' 558 | loc kmin=`=`lwindow'-1' 559 | 560 | tempvar esample 561 | gen byte `esample' = e(sample) 562 | 563 | if "`diffavg'"!=""{ 564 | *list of omitted coefficients 565 | loc komit_comma : subinstr local komit " " ",", all 566 | * fill in lists of pre and post coefficients 567 | loc pre_plus "" 568 | loc post_plus "" 569 | forvalues v = `=`lwindow'-1'/`=`rwindow'+1' { 570 | if inlist(`v', `komit_comma') continue 571 | if `v'<0 { 572 | loc pre_plus "`pre_plus' _k_eq_m`=-`v''" 573 | } 574 | else { 575 | loc post_plus "`post_plus' _k_eq_p`=`v''" 576 | } 577 | } 578 | loc pre_plus = strtrim("`pre_plus'") 579 | if "`pre_plus'"=="" { 580 | di as err "No pre-event coefficients to calulate the difference in averages" 581 | exit 301 582 | } 583 | loc post_plus = strtrim("`post_plus'") 584 | if "`post_plus'"=="" { 585 | di as err "No post-event coefficients to calulate the difference in averages" 586 | exit 301 587 | } 588 | loc pre_plus : subinstr local pre_plus " " " + ", all 589 | loc post_plus : subinstr local post_plus " " " + ", all 590 | di as text _n "Difference in pre and post-period averages from lincom:" 591 | lincom ((`post_plus') / (`rwindow' + 2)) - ((`pre_plus') / (`=-`lwindow'' + 1)), cformat(%9.4g) 592 | } 593 | 594 | 595 | * Plots 596 | 597 | * Calculate mean before change in policy for 2nd axis in plot 598 | * This needs to be relative to normalization 599 | tempvar temp_k 600 | if `norm0' < 0 loc kvomit = "m`=abs(`norm0')'" 601 | else loc kvomit "p`=abs(`norm0')'" 602 | qui gen `temp_k'=_k_eq_`kvomit' 603 | 604 | tokenize `varlist' 605 | qui su `1' if `temp_k'!=0 & `temp_k'!=. & `esample', meanonly 606 | loc y1 = r(mean) 607 | loc depvar "`1'" 608 | 609 | * Calculate mean proxy before change in policy for 2nd axis in plot 610 | if "`proxy'"!="" { 611 | loc nproxy: word count `proxy' 612 | if `nproxy' ==1 { 613 | qui su `proxy' if `temp_k'!=0 & `temp_k'!=. & `esample', meanonly 614 | loc x1 = r(mean) 615 | } 616 | else loc x1 = . 617 | } 618 | 619 | 620 | * Variables for overlay plots 621 | 622 | * Need the ols estimates for y and x 623 | * Do not exclude vars other than m1 624 | *loc toexc = "_k_eq_m1" 625 | *unab included2: _k_eq_* 626 | *loc included2 : list included2 - toexc 627 | 628 | _estimates hold main 629 | 630 | qui _eventols `varlist' [`weight'`exp'] if `touse' , panelvar(`panelvar') timevar(`timevar') policyvar(`policyvar') lwindow(`lwindow') rwindow(`rwindow') `fe' `te' nogen nodrop kvars(_k) norm(`norm0') impute(`impute') 631 | mat `deltaov' = r(delta) 632 | mat `Vdeltaov' = r(Vdelta) 633 | *mat `deltay' = `bby'[1,${names}] 634 | *mat `Vdeltay' = `VVy'[${names},${names}] 635 | qui _eventols `proxy' [`weight'`exp'] if `touse', panelvar(`panelvar') timevar(`timevar') policyvar(`policyvar') lwindow(`lwindow') rwindow(`rwindow') `fe' `te' nogen nodrop kvars(_k) norm(`norm0') impute(`impute') 636 | mat `deltax' = r(delta) 637 | mat `Vdeltax' = r(Vdelta) 638 | *mat `deltax' = `bb'[1,${names}] 639 | * mat `Vdeltax' = `VV'[${names},${names}] 640 | * Scaling factor 641 | loc ivnormcomma = strtrim("`ivnorm'") 642 | loc ivnorms : list sizeof ivnormcomma 643 | loc ivnormcomma : subinstr local ivnormcomma " " ",", all 644 | if `ivnorms'>1 loc scfactlead = -max(`ivnormcomma') 645 | else loc scfactlead = -`ivnormcomma' 646 | mat Mfn = `deltaov'[1,"_k_eq_m`scfactlead'"] 647 | mat Mfd = `deltax'[1,"_k_eq_m`scfactlead'"] 648 | loc fn = Mfn[1,1] 649 | loc fd = Mfd[1,1] 650 | loc factor = `fn'/`fd' 651 | * Scale x estimates by factor 652 | mat `deltaxsc' = `factor'*`deltax' 653 | } 654 | 655 | *recover omitted k vars 656 | loc kvars_omit "" 657 | if "`komit'"!=""{ 658 | foreach k in `komit' { 659 | if `k'<0 loc kvars_omit "`kvars_omit' _k_eq_m`=abs(`k')'" 660 | else loc kvars_omit "`kvars_omit' _k_eq_p`=abs(`k')'" 661 | } 662 | } 663 | *full list of event-time dummies (included + omitted) 664 | loc eventtd = "`included' `kvars_omit'" 665 | 666 | * Drop variables 667 | if "`savek'" == "" { 668 | cap confirm var `eventtd', exact 669 | if !_rc drop `eventtd' 670 | cap confirm var __k, exact 671 | if !_rc qui drop __k 672 | } 673 | else { 674 | *change prefix 675 | loc eventtd_savek : subinstr local eventtd "_k" "`savek'", all 676 | 677 | *If replace suboption, drop the existing variables before renaming the recently created ones 678 | if "`kreplace'"!="" { 679 | *event-time dummies 680 | foreach v in `eventtd_savek' { 681 | cap confirm variable `v', exact 682 | if !_rc drop `v' 683 | } 684 | *event-time variable 685 | cap confirm variable `savek'_evtime, exact 686 | if !_rc drop `savek'_evtime 687 | } 688 | 689 | *Check that variables don't exist 690 | cap confirm variable `eventtd_savek', exact 691 | if !_rc { 692 | di as err _n "You specified to save the event-time dummy variables using the prefix {bf:`savek'}, but you already have event-time dummy variables saved with that prefix." 693 | di as err _n "Use the {bf:replace} suboption to replace the existing variables." 694 | exit 110 695 | } 696 | cap confirm variable `savek'_evtime, exact 697 | if !_rc { 698 | di as err _n "You specified to save the event-time variable using the prefix {bf:`savek'}, but you already have an event-time variable saved with that prefix." 699 | di as err _n "Use the {bf:replace} suboption to replace the existing variable." 700 | exit 110 701 | } 702 | 703 | ren __k `savek'_evtime 704 | ren (`eventtd') (`eventtd_savek') 705 | } 706 | if "`instype'"=="numlist" | "`instype'"=="mixed" { 707 | foreach v in `leadivs' { 708 | drop `v' 709 | } 710 | } 711 | 712 | *skip the rest of the program if the user indicated not to estimate 713 | if "`noestimate'"!="" exit 714 | 715 | * Returns 716 | 717 | _estimates unhold main 718 | 719 | return matrix b = `bb' 720 | return matrix V = `VV' 721 | return matrix delta = `delta' 722 | return matrix Vdelta = `Vdelta' 723 | return matrix deltaov = `deltaov' 724 | return matrix Vdeltaov = `Vdeltaov' 725 | return matrix deltax = `deltax' 726 | return matrix Vdeltax = `Vdeltax' 727 | return matrix deltaxsc = `deltaxsc' 728 | loc names: subinstr local names ".." " ", all 729 | loc names: subinstr local names `"""' "", all 730 | return local names = `"`names'"' 731 | * " 732 | return local cmd = "`cmd'" 733 | return local df = `df' 734 | return local komit = "`komit'" 735 | return local kmiss = "`kmiss'" 736 | return local ambiguous = "`ambiguous'" 737 | return local y1 = `y1' 738 | return local depvar = "`depvar'" 739 | if `x1'!=. return local x1 = `x1' 740 | return local method = "iv" 741 | 742 | end 743 | 744 | *program to parse savek 745 | program define parsesavek, rclass 746 | 747 | syntax [anything] , [NOEstimate replace] 748 | 749 | return local savek "`anything'" 750 | return local noestimate "`noestimate'" 751 | return local kreplace "`replace'" 752 | end 753 | 754 | *program to parse standar error specification 755 | program define parse_es, rclass 756 | #d; 757 | syntax [anything], 758 | [ 759 | CLuster(varname) 760 | Robust 761 | vce(string) 762 | * 763 | ] 764 | ; 765 | #d cr 766 | return local cl_orig "`cluster'" 767 | return local rob_orig "`robust'" 768 | return local vce_orig "`vce'" 769 | return local other_opts "`options'" 770 | end 771 | 772 | -------------------------------------------------------------------------------- /xtevent/_eventivstatic.ado: -------------------------------------------------------------------------------- 1 | version 13 2 | 3 | program define _eventivstatic, rclass 4 | #d; 5 | syntax varlist(fv ts numeric) [aw fw pw] [if] [in], /* Covariates go in varlist. Can add fv ts later */ 6 | Panelvar(varname) /* Panel variable */ 7 | Timevar(varname) /* Time variable */ 8 | POLicyvar(varname) /* Policy variable */ 9 | proxy (varlist numeric) /* Proxy variable(s) */ 10 | [ 11 | proxyiv(string) /* Instruments. Either numlist with lags or varlist with names of instrumental variables */ 12 | nofe /* No fixed effects */ 13 | note /* No time effects */ 14 | reghdfe /* Use reghdfe for estimation */ 15 | addabsorb(string) /* Absorb additional variables in reghdfe */ 16 | IMPute(string) 17 | REPeatedcs /*data is repeated cross-sectional*/ 18 | STatic 19 | * 20 | ] 21 | ; 22 | #d cr 23 | 24 | marksample touse 25 | 26 | tempvar mkvarlist 27 | qui gen byte `mkvarlist' = `touse' 28 | 29 | tempvar kg tousegen 30 | * kg grouped event time, grouping outside window 31 | 32 | * For eventgenvars, ignore missings in varlist 33 | mark `tousegen' `if' `in' 34 | 35 | tempname delta Vdelta bb VV bb2 VV2 delta2 Vdelta2 deltay Vdeltay deltax Vdeltax deltaxsc bby bbx VVy VVx 36 | * bb delta coefficients 37 | * VV variance of delta coefficients 38 | * bb2 delta coefficients for overlay plot 39 | * VV2 variance of delta coefficients for overlay plot 40 | * delta2 included cefficientes in overlaty plot 41 | * VVdelta2 variance of included delta coefficients in overlay plot 42 | 43 | loc i = "`panelvar'" 44 | loc t = "`timevar'" 45 | loc z = "`policyvar'" 46 | 47 | *call _eventgenvars to impute z 48 | if "`impute'"!="" { 49 | *tempvar to be imputed 50 | tempvar rr 51 | qui gen double `rr'=. 52 | 53 | _eventgenvars if `tousegen', panelvar(`panelvar') timevar(`timevar') policyvar(`policyvar') impute(`impute') `repeatedcs' `static' rr(`rr') mkvarlist(`mkvarlist') 54 | 55 | loc impute=r(impute) 56 | if "`impute'"=="." loc impute = "" 57 | loc saveimp=r(saveimp) 58 | if "`saveimp'"=="." loc saveimp = "" 59 | *if imputation succeeded: 60 | if "`impute'"!="" { 61 | if "`saveimp'"=="" { 62 | cap confirm variable `policyvar'_imputed 63 | if !_rc { 64 | di as err _n "`policyvar'_imputed already exists. Please rename it or delete it before proceeding." 65 | exit 110 66 | } 67 | gen `policyvar'_imputed = `rr' 68 | } 69 | loc z = "`policyvar'_imputed" 70 | else 71 | } 72 | else loc z = "`policyvar'" 73 | } 74 | 75 | *In the static setting, we cannot define an interval through the -window- option to look for the strongest lead. Therefore, we will check the dataset and look for the greatest possible lead. The maximum number of forwards I can generate equals the number of observed periods -1. 76 | qui sum `timevar' 77 | loc drange_1=r(max) - r(min) // this equals number of obs periods -1 78 | 79 | * if dataset is repeated cross-sectional, create leads of policyvar at state level 80 | if "`repeatedcs'"!=""{ 81 | qui { 82 | preserve 83 | tempfile state_level_leads 84 | 85 | keep if `touse' 86 | keep `panelvar' `timevar' (`z') 87 | bysort `panelvar' `timevar' (`z'): keep if _n==1 88 | xtset `panelvar' `timevar' 89 | forv v=1(1)`drange_1'{ 90 | tempvar _fd`v'`z' 91 | qui gen double `_fd`v'`z'' = f`v'.d.`z' 92 | } 93 | save `state_level_leads' 94 | 95 | restore 96 | 97 | *merge on the policyvar as well, so missing values in policyvar within a cell will not get lead values 98 | merge m:1 `panelvar' `timevar' `z' using `state_level_leads', update nogen 99 | } 100 | } 101 | 102 | loc leads : word count `proxy' 103 | if "`proxyiv'"=="" & `leads'==1 loc proxyiv "select" 104 | 105 | * If proxy specified but no proxyiv, assume numlist for leads of policyvar 106 | if "`proxyiv'"=="" { 107 | di _n "No proxy instruments specified. Using leads of policy variables as instruments." 108 | loc leads : word count `proxy' 109 | forv j=1(1)`leads' { 110 | loc proxyiv "`proxyiv' `j'" 111 | } 112 | } 113 | 114 | * IV selection if proxyiv = selection 115 | else if "`proxyiv'"=="select" { 116 | * Only for one proxy case 117 | if `leads'>1 { 118 | di as err "Proxy instrument selection only available for the one proxy - one instrument case" 119 | exit 301 120 | } 121 | else { 122 | di as text _n "proxyiv=select. Selecting lead order of differenced policy variable to use as instrument." 123 | loc Fstart = 0 124 | * originally: Here I test up to 5 125 | forv v=1(1)`drange_1'{ 126 | if "`repeatedcs'"=="" { 127 | tempvar _fd`v'`z' 128 | qui gen double `_fd`v'`z'' = f`v'.d.`z' if `touse' 129 | } 130 | cap qui reg `proxy' `_fd`v'`z'' [`weight'`exp'] if `touse' 131 | if !_rc loc Floop = e(F) 132 | if `Floop' > `Fstart' { 133 | loc Fstart = `Floop' 134 | loc proxyiv "`v'" 135 | } 136 | } 137 | di as text _n "Lead `proxyiv' selected." 138 | } 139 | 140 | } 141 | 142 | * Parse proxyiv and generate leads if neccesary 143 | loc rc=0 144 | foreach v in `proxyiv' { 145 | cap confirm integer number `v' 146 | loc rc = `rc' + _rc 147 | } 148 | * If numlist take as leads of z 149 | if `rc' == 0 { 150 | loc insvars "" 151 | foreach v in `proxyiv' { 152 | if "`repeatedcs'"!=""{ 153 | qui gen double _fd`v'`z' = `_fd`v'`z'' if `touse' 154 | } 155 | else{ 156 | qui gen double _fd`v'`z' = f`v'.d.`z' if `touse' 157 | } 158 | *qui gen double _f`v'`z' = f`v'.`z' if `touse' 159 | *loc insvars "`insvars' _f`v'`z'" 160 | loc insvars "`insvars' _fd`v'`z'" 161 | } 162 | loc instype = "numlist" 163 | } 164 | else { 165 | foreach v in `proxyiv' { 166 | confirm numeric variable `v' 167 | } 168 | loc insvars = "`proxyiv'" 169 | } 170 | 171 | if "`te'" == "note" loc tte "" 172 | else loc tte "i.`t'" 173 | 174 | * Main regression 175 | 176 | if "`reghdfe'"=="" { 177 | if "`fe'" == "nofe" { 178 | loc cmd "ivregress 2sls" 179 | loc ffe "" 180 | } 181 | else { 182 | loc cmd "xtivreg" 183 | loc ffe "fe" 184 | if "`repeatedcs'"!="" qui xtset `panelvar' //xtivreg requires panelvar to be setted 185 | } 186 | `cmd' `varlist' (`proxy' = `insvars') `z' `tte' [`weight'`exp'] if `touse' , `ffe' `options' 187 | } 188 | else { 189 | loc noabsorb "" 190 | *absorb nothing 191 | if "`fe'" == "nofe" & "`tte'"=="" & "`addabsorb'"=="" { 192 | *loc noabsorb "noabsorb" 193 | /*the only option ivreghdfe inherits from reghdfe is absorb, therefore it doesn't support noabsorb. In contrast with reghdfe, ivreghdfe doesn't require noabsorb when absorb is not specified*/ 194 | loc abs "" 195 | } 196 | *absorb only one 197 | else if "`fe'" == "nofe" & "`tte'"=="" & "`addabsorb'"!="" { 198 | loc abs "absorb(`addabsorb')" 199 | } 200 | else if "`fe'" == "nofe" & "`tte'"!="" & "`addabsorb'"=="" { 201 | loc abs "absorb(`t')" 202 | } 203 | else if "`fe'" != "nofe" & "`tte'"=="" & "`addabsorb'"=="" { 204 | loc abs "absorb(`i')" 205 | } 206 | *absorb two 207 | else if "`fe'" == "nofe" & "`tte'"!="" & "`addabsorb'"!="" { 208 | loc abs "absorb(`t' `addabsorb')" 209 | } 210 | else if "`fe'" != "nofe" & "`tte'"=="" & "`addabsorb'"!="" { 211 | loc abs "absorb(`i' `addabsorb')" 212 | } 213 | else if "`fe'" != "nofe" & "`tte'"!="" & "`addabsorb'"=="" { 214 | loc abs "absorb(`i' `t')" 215 | } 216 | *absorb three 217 | else if "`fe'" != "nofe" & "`tte'"!="" & "`addabsorb'"!="" { 218 | loc abs "absorb(`i' `t' `addabsorb')" 219 | } 220 | * 221 | else { 222 | loc abs "absorb(`i' `t' `addabsorb')" 223 | } 224 | 225 | *analyze inclusion of vce in options 226 | loc vce_y= strmatch("`options'","*vce(*)*") 227 | 228 | *if user did not specify vce option 229 | if "`vce_y'"=="0" { 230 | ivreghdfe `varlist' (`proxy' = `insvars') `z' [`weight'`exp'] if `touse', `abs' `noabsorb' `options' 231 | } 232 | *if user did specify vce option 233 | else { 234 | *find start and end of vce text 235 | loc vces=strpos("`options'","vce(") 236 | loc vcef=0 237 | loc ocopy="`options'" 238 | while `vcef'<`vces' { 239 | loc vcef=strpos("`ocopy'", ")") 240 | loc ocopy=subinstr("`ocopy'",")", " ",1) 241 | } 242 | *substrac vce words 243 | loc svce_or=substr("`options'",`vces',`vcef') 244 | loc vce_len=strlen("`svce_or'") 245 | loc svce=substr("`svce_or'",5,`vce_len'-5) 246 | loc svce=strltrim("`svce'") 247 | loc svce=strrtrim("`svce'") 248 | *inspect whether vce contains bootstrap or jackknife 249 | loc vce_bt= strmatch("`svce'","*boot*") 250 | loc vce_jk= strmatch("`svce'","*jack*") 251 | if `vce_bt'==1 | `vce_jk'==1 { 252 | di as err "Options {bf:bootstrap} and {bf:jackknife} are not allowed" 253 | exit 301 254 | } 255 | 256 | *if vce contains valid options, parse those options 257 | *erase vce from original options 258 | loc options_wcve=subinstr("`options'","`svce_or'"," ",1) 259 | *** parse vce(*) **** 260 | loc vce_wc=wordcount("`svce'") 261 | tokenize `svce' 262 | *extract vce arguments 263 | *robust 264 | loc vce_r= strmatch("`svce'","*robust*") 265 | loc vce_r2=0 266 | forv i=1/`vce_wc'{ 267 | loc zz= strmatch("``i''","r") 268 | loc vce_r2=`vce_r2'+`zz' 269 | } 270 | if `vce_r'==1 | `vce_r2'==1 { 271 | loc vceop_r="robust" 272 | } 273 | *cluster 274 | loc vce_c= strmatch("`svce'","*cluster*") 275 | loc vce_c2= strmatch("`svce'","*cl*") 276 | if `vce_c'==1 | `vce_c2'==1 { 277 | forv i=1/`vce_wc'{ 278 | loc vce_r2= strmatch("``i''","*cl*") 279 | if `vce_r2'==1 { 280 | loc j=`i'+1 281 | } 282 | } 283 | loc vceop_c="cluster(``j'')" 284 | } 285 | ivreghdfe `varlist' (`proxy' = `insvars') `z' [`weight'`exp'] if `touse', `abs' `noabsorb' `options_wcve' `vceop_r' `vceop_c' 286 | } 287 | 288 | } 289 | 290 | *clear xtset if repeatedcs and xtivreg, otherwise error message because timevar not setted 291 | if ("`repeatedcs'"!="" & "`cmd'"=="xtivreg") qui xtset, clear 292 | 293 | mat `bb' = e(b) 294 | mat `VV' = e(V) 295 | mat `delta'=e(b) 296 | mat `Vdelta'=e(V) 297 | 298 | * Drop variables 299 | 300 | if "`instype'"=="numlist" { 301 | foreach v in `proxyiv' { 302 | *drop _f`v'`z' 303 | drop _fd`v'`z' 304 | } 305 | } 306 | if "`impute'"!="" & "`saveimp'"=="" drop `policyvar'_imputed 307 | 308 | tokenize `varlist' 309 | loc depvar "`1'" 310 | 311 | return matrix b = `bb' 312 | return matrix V = `VV' 313 | return matrix delta = `delta' 314 | return matrix Vdelta = `Vdelta' 315 | loc names: subinstr global names ".." " ", all 316 | loc names: subinstr local names `"""' "", all 317 | return local names = "`names'" 318 | return local cmd = "`cmd'" 319 | return local depvar = "`depvar'" 320 | 321 | 322 | 323 | end 324 | -------------------------------------------------------------------------------- /xtevent/_eventolsstatic.ado: -------------------------------------------------------------------------------- 1 | version 13 2 | 3 | program define _eventolsstatic, rclass 4 | 5 | #d; 6 | syntax varlist(fv ts numeric) [aw fw pw] [if] [in], /* Proxy for eta and covariates go in varlist. Can add fv ts later */ 7 | Panelvar(varname) /* Panel variable */ 8 | Timevar(varname) /* Time variable */ 9 | POLicyvar(varname) /* Policy variable */ 10 | [ 11 | nofe /* No fixed effects */ 12 | note /* No time effects */ 13 | reghdfe /* Use reghdfe for estimation */ 14 | addabsorb(string) /* Absorb additional variables in reghdfe */ 15 | IMPute(string) /*impute policyvar */ 16 | STatic /* Estimate static model */ 17 | REPeatedcs /*data is repeated cross-sectional*/ 18 | * 19 | ] 20 | ; 21 | #d cr 22 | 23 | marksample touse 24 | 25 | tempvar mkvarlist 26 | qui gen byte `mkvarlist' = `touse' 27 | 28 | tempname delta Vdelta bb VV bb2 VV2 delta2 Vdelta2 tousegen 29 | 30 | * For eventgenvars, ignore missings in varlist 31 | mark `tousegen' `if' `in' 32 | 33 | loc i = "`panelvar'" 34 | loc t = "`timevar'" 35 | loc z = "`policyvar'" 36 | 37 | *call _eventgenvars to impute z 38 | if "`impute'"!="" { 39 | *tempvar to be imputed 40 | tempvar rr 41 | qui gen double `rr'=. 42 | 43 | _eventgenvars if `tousegen', panelvar(`panelvar') timevar(`timevar') policyvar(`policyvar') impute(`impute') `repeatedcs' `static' rr(`rr') mkvarlist(`mkvarlist') 44 | 45 | loc impute=r(impute) 46 | if "`impute'"=="." loc impute = "" 47 | loc saveimp=r(saveimp) 48 | if "`saveimp'"=="." loc saveimp = "" 49 | *if imputation succeeded: 50 | if "`impute'"!="" { 51 | if "`saveimp'"=="" { 52 | cap confirm variable `policyvar'_imputed 53 | if !_rc { 54 | di as err _n "`policyvar'_imputed already exists. Please rename it or delete it before proceeding." 55 | exit 110 56 | } 57 | gen `policyvar'_imputed = `rr' 58 | } 59 | loc z = "`policyvar'_imputed" 60 | else 61 | } 62 | else loc z = "`policyvar'" 63 | } 64 | 65 | * Main regression 66 | 67 | if "`te'" == "note" loc te "" 68 | else loc te "i.`t'" 69 | 70 | if "`reghdfe'"=="" { 71 | if "`fe'" == "nofe" { 72 | loc absorb "" 73 | loc cmd "reg" 74 | } 75 | else { 76 | loc absorb "absorb(`i')" 77 | loc cmd "areg" 78 | } 79 | `cmd' `varlist' `z' `te' [`weight'`exp'] if `touse', `absorb' `options' 80 | } 81 | else { 82 | loc noabsorb "" 83 | *absorb nothing 84 | if "`fe'" == "nofe" & "`te'"=="" & "`addabsorb'"=="" { 85 | loc noabsorb "noabsorb" 86 | loc abs "" 87 | } 88 | *absorb only one 89 | else if "`fe'" == "nofe" & "`te'"=="" & "`addabsorb'"!="" { 90 | loc abs "absorb(`addabsorb')" 91 | } 92 | else if "`fe'" == "nofe" & "`te'"!="" & "`addabsorb'"=="" { 93 | loc abs "absorb(`t')" 94 | } 95 | else if "`fe'" != "nofe" & "`te'"=="" & "`addabsorb'"=="" { 96 | loc abs "absorb(`i')" 97 | } 98 | *absorb two 99 | else if "`fe'" == "nofe" & "`te'"!="" & "`addabsorb'"!="" { 100 | loc abs "absorb(`t' `addabsorb')" 101 | } 102 | else if "`fe'" != "nofe" & "`te'"=="" & "`addabsorb'"!="" { 103 | loc abs "absorb(`i' `addabsorb')" 104 | } 105 | else if "`fe'" != "nofe" & "`te'"!="" & "`addabsorb'"=="" { 106 | loc abs "absorb(`i' `t')" 107 | } 108 | *absorb three 109 | else if "`fe'" != "nofe" & "`te'"!="" & "`addabsorb'"!="" { 110 | loc abs "absorb(`i' `t' `addabsorb')" 111 | } 112 | * 113 | else { 114 | loc abs "absorb(`i' `t' `addabsorb')" 115 | } 116 | reghdfe `varlist' `z' [`weight'`exp'] if `touse', `abs' `noabsorb' `options' 117 | } 118 | 119 | mat `bb' = e(b) 120 | mat `VV' = e(V) 121 | mat `delta'=e(b) 122 | mat `Vdelta'=e(V) 123 | 124 | tokenize `varlist' 125 | loc depvar "`1'" 126 | 127 | return matrix b = `bb' 128 | return matrix V = `VV' 129 | return matrix delta=`delta' 130 | return matrix Vdelta = `Vdelta' 131 | return local cmd = "`cmd'" 132 | return local depvar = "`depvar'" 133 | 134 | if "`impute'"!="" & "`saveimp'"=="" drop `policyvar'_imputed 135 | 136 | end 137 | 138 | -------------------------------------------------------------------------------- /xtevent/get_unit_time_effects.ado: -------------------------------------------------------------------------------- 1 | *! get_unit_time_effects.ado 3.1.0 July 11, 2024 2 | 3 | version 13 4 | 5 | program define get_unit_time_effects, eclass 6 | 7 | #d; 8 | syntax varlist(fv ts numeric) [aw fw pw] [if] [in] , 9 | 10 | Panelvar(varname) /* Panel variable */ 11 | Timevar(varname) /* Time variable */ 12 | [ 13 | saving(string) /*specify file name and save the effects dataset*/ 14 | NOOutput /* supress output */ 15 | clear /*replace data in memory with results*/ 16 | * 17 | ] 18 | ; 19 | #d cr 20 | 21 | marksample touse 22 | 23 | tempvar predicted 24 | 25 | * Check for a variable named _unittimeeffects 26 | cap unab effvar : _unittimeeffects 27 | if !_rc { 28 | di as err _n "You have a variable named {bf:_unittimeeffects}. This name is reserved for the variable that will contain the unit-time effects." 29 | di as err _n "Please rename or drop this variable before proceeding." 30 | exit 110 31 | } 32 | 33 | *parse saving 34 | parsesaving `saving' 35 | loc filename= r(filename) 36 | if "`filename'"=="." loc filename "" 37 | loc replace= r(replace) 38 | if "`replace'"=="." loc replace "" 39 | 40 | *define file name 41 | if "`filename'"!=""{ 42 | *check if it contains dta extension 43 | loc cdta= strmatch("`filename'", "*.dta*") 44 | if `cdta'==0 loc filename "`filename'.dta" 45 | } 46 | else { 47 | loc filename "unit_time_effects.dta" 48 | } 49 | 50 | *in case replace is not specified, check if file already exists 51 | if "`replace'"==""{ 52 | cap confirm file "`filename'" 53 | if !_rc { 54 | di as err _n "File `filename' already exists." 55 | exit 602 56 | } 57 | } 58 | 59 | *split varlist 60 | unab varlist: `varlist' 61 | gettoken depenvar indepvars: varlist 62 | 63 | *generate unit-time effects 64 | * first step of the two-step estimation procedure for repeated cross-sectional datasets 65 | * regress dependent variable on controls and unit-time effects 66 | 67 | *omit regression table 68 | if "`nooutput'"!="" loc q quietly 69 | 70 | cap confirm variable unittimeinteraction 71 | if !_rc { 72 | di as err _n "Variable unittimeinteraction already exists. Please delete it or rename it before proceeding" 73 | exit 110 74 | } 75 | 76 | qui egen unittimeinteraction=group(`panelvar' `timevar') 77 | `q' areg `depenvar' `indepvars' [`weight'`exp'] if `touse', absorb(unittimeinteraction) `options' 78 | qui predict `predicted', d //calculates d_absorbvar, the individual coefficients for the absorbed variable. 79 | drop unittimeinteraction 80 | 81 | *create dta file necessary for step 2 82 | if "`clear'"=="" preserve 83 | qui gen _unittimeeffects = `predicted' 84 | qui bysort `panelvar' `timevar': keep if _n==1 //or collapse?: collapse (mean) _unittimeeffects [`weight'`exp'] if `touse', by(`panelvar' `timevar') 85 | qui keep `panelvar' `timevar' _unittimeeffects 86 | 87 | save "`filename'", `replace' 88 | *go back to the original dataset 89 | if "`clear'"=="" restore 90 | 91 | end 92 | 93 | * Program to parse saving 94 | program define parsesaving, rclass 95 | 96 | syntax [anything] , [replace] 97 | 98 | return local filename "`anything'" 99 | return local replace "`replace'" 100 | end 101 | -------------------------------------------------------------------------------- /xtevent/get_unit_time_effects.sthlp: -------------------------------------------------------------------------------- 1 | 2 | {smcl} 3 | {* *! version 3.1.0 July 11 2024}{...} 4 | {cmd:help get_unit_time_effects} 5 | {hline} 6 | 7 | {title:Title} 8 | 9 | {phang} 10 | {bf:get_unit_time_effects} {hline 2} Generate Group and Time Effects in a Repeated Cross-Sectional Dataset 11 | 12 | {marker syntax}{...} 13 | {title:Syntax} 14 | 15 | {pstd} 16 | 17 | {p 8 17 2} 18 | {cmd:get_unit_time_effects} 19 | {depvar} [{indepvars}] 20 | {ifin} {weight} 21 | {cmd:,} 22 | {opth p:anelvar(varname)} 23 | {opth t:imevar(varname)} 24 | [{it:options}] 25 | 26 | {synoptset 28 tabbed}{...} 27 | {synopthdr} 28 | {synoptline} 29 | {syntab:Main} 30 | {synopt: {opth p:anelvar(varname)}} variable that identifies the groups{p_end} 31 | {synopt: {opth t:imevar(varname)}} variable that identifies the time periods{p_end} 32 | {synopt: {opt saving(filename, [replace])}} save results to {it:filename}{p_end} 33 | {synopt:{opt noo:utput}} omit regression table{p_end} 34 | {synopt:{opt clear}} replace data in memory with the unit-time effects file{p_end} 35 | {synoptline} 36 | {p2colreset}{...} 37 | 38 | {p 4 6 2} {it: depvar} and {it:indepvars} may contain time-series operators; see {help tsvarlist}.{p_end} 39 | {p 4 6 2} {it: depvar} and {it:indepvars} may contain factor variables; see {help fvvarlist}.{p_end} 40 | 41 | {p 4 6 2}* {it:indepvars} should contain covariates that vary at the individual level. 42 | 43 | {marker description}{...} 44 | {title:Description} 45 | 46 | {pstd} 47 | {cmd: get_unit_time_effects} estimates group-time fixed effects in a repeated cross-sectional dataset. It produces a Stata data file with the 48 | variables {it:panelvar}, {it:timevar}, and {it:_unittimeeffects}. The variable {it:_unittimeeffects} contains the group-time effects. Hansen (2007) 49 | describes a two-step procedure to obtain the coefficient estimates of covariates that vary at the group level within a repeated 50 | cross-sectional framework. The two-step procedure can be used to obtain the coefficient estimates of an event-study when the data 51 | is repeated cross-sectional. {cmd:get_unit_time_effects} implements the first part of the two-step procedure. Then, {cmd: xtevent} can 52 | be used for the second part of the procedure to obtain the event-study coefficient estimates. See {help xtevent}.{p_end} 53 | 54 | {marker options}{...} 55 | {title:Options} 56 | {synoptline} 57 | 58 | {phang} 59 | {opth panelvar(varname)} specifies the group variable. The policy variable should vary at this group level. 60 | 61 | {phang} 62 | {opth timevar(varname)} specifies the time variable. 63 | 64 | {phang} 65 | {opt saving(filename, [replace])} specifies the name of the Stata data file to store the unit-time effects estimates. If {opt saving} is 66 | not specified, the file will be saved in the current directory with the name {it: unit_time_effects.dta}. The suboption {it:replace} overwrites 67 | the unit-time effects file. 68 | 69 | {phang} 70 | {opt nooutput} omits the regression table. 71 | 72 | {phang} 73 | {opt clear} replaces the dataset in memory with the unit-time effects file. 74 | 75 | {title:Examples} 76 | 77 | {hline} 78 | {pstd}Load example repeated cross-sectional dataset{p_end} 79 | {phang2}{cmd:. use "https://github.com/JMSLab/xtevent/blob/main/test/small_repeated_cross_sectional_example31.dta?raw=true", clear}{p_end} 80 | {phang2}{cmd:. {stata xtset, clear}}{p_end} 81 | 82 | {pstd}Get unit-time effects and save them as a dta file with the name and directory indicated through the {bf:saving} option. Add the 83 | {bf:replace} suboption to overwrite the file.{p_end} 84 | {phang2}{cmd:. {stata get_unit_time_effects y u eta, panelvar(state) timevar(t) saving("effect_file.dta", replace)}} 85 | {p_end} 86 | 87 | {pstd}Proceed with {bf:xtevent}{p_end} 88 | {phang2}{cmd:. {stata "bysort state t (z): keep if _n==1"}}{p_end} 89 | {phang2}{cmd:. {stata "keep state t z"}}{p_end} 90 | 91 | {pstd}Merge with the file that was created with {bf:get_unit_time_effects}{p_end} 92 | {phang2}{cmd:. {stata "merge m:1 state t using effect_file.dta"}}{p_end} 93 | {phang2}{cmd:. {stata drop _merge}}{p_end} 94 | {pstd}Use {bf:xtevent} to estimate an event-study{p_end} 95 | {phang2}{cmd:. {stata xtevent _unittimeeffects, panelvar(state) t(t) policyvar(z) window(5)}}{p_end} 96 | {phang2}{cmd:. {stata xteventplot}}{p_end} 97 | 98 | {title:Authors} 99 | 100 | {pstd}Simon Freyaldenhoven, Federal Reserve Bank of Philadelphia.{p_end} 101 | simon.freyaldenhoven@phil.frb.org 102 | {pstd}Christian Hansen, University of Chicago, Booth School of Business.{p_end} 103 | chansen1@chicagobooth.edu 104 | {pstd}Jorge Pérez Pérez, Banco de México.{p_end} 105 | jorgepp@banxico.org.mx 106 | {pstd}Jesse Shapiro, Harvard University and NBER.{p_end} 107 | jesse_shapiro@fas.harvard.edu 108 | 109 | {title:Support} 110 | 111 | {pstd}For support and to report bugs please email Jorge Pérez Pérez, Banco de México.{break} 112 | jorgepp@banxico.org.mx 113 | 114 | {pstd}{cmd:xtevent} can also be found on {browse "https://github.com/JMSLab/xtevent":GitHub}. 115 | 116 | {title:References} 117 | 118 | {pstd}Hansen, C. (2007). "Generalized Least Squares Inference in Panel and Multilevel Models 119 | with Serial Correlation and Fixed Effects" Journal of Econometrics, 140(2), 670-694.{p_end} 120 | 121 | 122 | -------------------------------------------------------------------------------- /xtevent/install.txt: -------------------------------------------------------------------------------- 1 | To install, please add the files from this archive to your personal directory: 2 | https://www.stata.com/support/faqs/programming/personal-ado-directory/ 3 | -------------------------------------------------------------------------------- /xtevent/release.txt: -------------------------------------------------------------------------------- 1 | xtevent 3.1.0 - July 11 2024 2 | 3 | 4 | Authors: 5 | 6 | Simon Freyaldenhoven, Federal Reserve Bank of Philadelphia. 7 | simon.freyaldenhoven@phil.frb.org 8 | Christian Hansen, University of Chicago, Booth School of Business. 9 | chansen1@chicagobooth.edu 10 | Jorge Pérez Pérez, Banco de México. 11 | jorgepp@banxico.org.mx 12 | Jesse Shapiro, Harvard University and NBER. 13 | jesse.m.shapiro@gmail.com 14 | 15 | 16 | Simon Freyaldenhoven, Christian Hansen, Jorge Pérez Pérez, and Jesse M. Shapiro. (2021) 17 | "Visualization, Identification, and Estimation in the Panel Event-Study Design." NBER Working Paper No. 29170. 18 | 19 | 20 | Changelog: 21 | - Options for choosing the largest available estimation window, and the largest available balanced estimation window 22 | - Simpler syntax for Sun and Abraham (2021) estimation with automatic cohort variables generation 23 | - New default graph style for Stata 18 24 | - Fixed bugs present in version 3.0.0 25 | 26 | For more information, see https://github.com/JMSLab/xtevent/releases/tag/v3.1.0. 27 | -------------------------------------------------------------------------------- /xtevent/xtevent.ado: -------------------------------------------------------------------------------- 1 | *! xtevent.ado 3.1.0 July 11, 2024 2 | 3 | version 13 4 | 5 | program define xtevent, eclass 6 | 7 | * Replay routine 8 | if replay() { 9 | if "`e(cmd2)'"!="xtevent" exit 301 10 | else { 11 | loc rep = e(cmd) 12 | `rep' 13 | } 14 | exit 15 | } 16 | 17 | #d; 18 | syntax varlist(fv ts numeric) [aw fw pw] [if] [in] , /* Proxy for eta and covariates go in varlist. Can add fv ts later */ 19 | POLicyvar(varname) /* Policy variable */ 20 | [ 21 | Window(string) /* Estimation window */ 22 | pre(numlist >=0 min=1 max=1 integer) /* Pre-event time periods where anticipation effects are allowed */ 23 | post(numlist >=0 min=1 max=1 integer) /* Post-event time periods where dynamic effects are allowed */ 24 | overidpre(numlist >=0 min=1 max=1 integer) /* Pre-event time periods for overidentification */ 25 | overidpost(numlist >=0 min=1 max=1 integer) /* Post-event time periods for overidentification */ 26 | Panelvar(varname) /* Panel variable */ 27 | Timevar(varname) /* Time variable */ 28 | proxyiv(string) /* Instruments. For FHS set ins equal to leads of the policy */ 29 | proxy (varlist numeric) /* Proxy variable */ 30 | TRend(string) /*trend(a -1) Include a linear trend from time a to -1. Method can be either GMM or OLS*/ 31 | SAVek(string) /* Generate the time-to-event dummies, trend, and cohort-relative time interactions and keep them in the dataset */ 32 | STatic /* Estimate static model */ 33 | reghdfe /* Estimate with reghdfe */ 34 | addabsorb(string) /* Absorb additional variables in reghdfe */ 35 | norm(numlist integer max=1) /* Normalization */ 36 | REPeatedcs /*indicate that the input data is a repeated cross-sectional dataset*/ 37 | cohort(string) /* create or variable varname, where varname is categorical variable indicating cohort */ 38 | control_cohort(string) /* dummy variable to indicate cohort to be used as control in SA estimation*/ 39 | SUNABraham /* Alias for cohort(create) */ 40 | plot /* Produce plot */ 41 | * 42 | /* 43 | These options passed to subcommands 44 | 45 | nofe /* No fixed effects */ 46 | note /* No time effects */ 47 | Kvars(string) /* Use previously generated dummies */ 48 | IMPute(string) /* impute policyvar */ 49 | */ 50 | ] 51 | ; 52 | #d cr 53 | 54 | 55 | 56 | * Capture errors 57 | 58 | if "`addabsorb'"!="" & "`reghdfe'"=="" { 59 | di as err "option {bf:addabsorb} only allowed with option {bf:reghdfe}" 60 | exit 198 61 | } 62 | 63 | if "`proxy'" == "" & "`proxyiv'" != "" { 64 | di as err _n "With instruments, you must specify a proxy variable" 65 | exit 198 66 | } 67 | 68 | * If xtset, don't need panelvar and timevar 69 | cap xtset 70 | if _rc==459 { 71 | if "`panelvar'"=="" & "`timevar'"!="" | "`panelvar'"!="" & "`timevar'"=="" | "`panelvar'"=="" & "`timevar'"=="" { 72 | di as err _n "If data have not been xtset, you must specify options {bf:panelvar} and {bf:timevar}" 73 | exit 198 74 | } 75 | } 76 | else if ("`panelvar'"!="" & "`panelvar'"!=r(panelvar)) | ("`timevar'"!="" & "`timevar'"!=r(timevar)) { 77 | di as err _n "Data have been xtset, and you specified options {bf:panelvar} or {bf:timevar} with variables different from those previously set. Run {cmd:xtset,clear} or {cmd:xtset} your data again" 78 | exit 198 79 | } 80 | else if ("`panelvar'"=="" & "`timevar'"=="") | ("`panelvar'"!="" & "`timevar'"=="") | ("`panelvar'"=="" & "`timevar'"!="") { 81 | di as txt _n "Using options {bf:panelvar} and {bf:timevar} from {cmd:xtset}" 82 | loc panelvar=r(panelvar) 83 | loc timevar=r(timevar) 84 | } 85 | 86 | if "`trend'"!="" & "`proxy'"!="" { 87 | di as err _n "options {bf:proxy} and {bf:trend} not allowed simultaneously" 88 | exit 198 89 | } 90 | 91 | if "`trend'"!="" & "`static'"!="" { 92 | di as err _n "options {bf:static} and {bf:trend} not allowed simultaneously" 93 | exit 198 94 | } 95 | 96 | * Always need window unless static is specified 97 | if "`window'"=="" & ("`static'"=="" & ("`pre'"=="" | "`post'"=="" | "`overidpre'"=="" | "`overidpost'"=="")) { 98 | di as err _n "option {bf:window} is required unless option {bf:static}, or options {bf:pre},{bf:post},{bf:overidpre}, and {bf:overidpost} are specified" 99 | exit 198 100 | } 101 | if "`window'"!="" & "`static'"!="" { 102 | di as err _n "option {bf:window} not allowed with option {bf:static}" 103 | exit 198 104 | } 105 | if "`window'"!="" & ("`static'"!="" | ("`pre'"!="" | "`post'"!="" | "`overidpre'"!="" | "`overidpost'"!="")) { 106 | di as err _n "option {bf:window} not allowed with options {bf:static},{bf:pre},{bf:post},{bf:overidpre}, or {bf:overidpost}" 107 | exit 198 108 | } 109 | if ("`static'"!="" & ("`pre'"!="" | "`post'"!="" | "`overidpre'"!="" | "`overidpost'"!="")) { 110 | di as err _n "option {bf:static} not allowed with options {bf:pre},{bf:post},{bf:overidpre}, or {bf:overidpost}" 111 | exit 198 112 | } 113 | 114 | if "`savek'"=="_k" { 115 | di as err _n "_k reserved for internal variables. Please choose a different stub" 116 | exit 198 117 | } 118 | 119 | if "`reghdfe'" != "" { 120 | foreach p in reghdfe ftools { 121 | cap which `p' 122 | if _rc { 123 | di as err _n "option {bf:reghdfe} requires {cmd: `p'} to be installed" 124 | exit 199 125 | } 126 | } 127 | if "`proxy'"!="" { 128 | foreach p in ivreghdfe ivreg2 ranktest avar { 129 | cap which `p' 130 | if _rc { 131 | di as err _n "option {bf:reghdfe} and IV estimation requires {cmd: `p'} to be installed" 132 | exit 199 133 | } 134 | } 135 | } 136 | } 137 | 138 | *inform panel variables in case of data is repeated cross-sectional 139 | if "`repeatedcs'"!=""{ 140 | di as txt _n "Option {bf:repeatedcs} was specified. Using {bf:`panelvar'} as the panel variable and {bf:`timevar'} as the time variable." 141 | } 142 | 143 | if "`cohort'"!="" { 144 | cap which avar 145 | if _rc { 146 | di as err _n "Sun-and-Abraham estimation requires {cmd: avar} to be installed" 147 | exit 199 148 | } 149 | } 150 | 151 | if "`control_cohort'"!="" & "`cohort'"=="" { 152 | di as err _n "{bf:control_cohort} requires {bf:cohort} to be specified" 153 | exit 198 154 | } 155 | 156 | if "`sunabraham'"!="" { 157 | if "`cohort'"=="" loc cohort "create" 158 | } 159 | 160 | * SA estimation not implemented with IV estimation yet 161 | if ("`cohort'"!="" | "`control_cohort'"!="" | "`sunabraham'"!="") & ("`proxy'"!="" | "`proxyiv'"!="") { 162 | di as err _n "Sun-and-Abraham estimation not allowed with proxy or instruments" 163 | exit 198 164 | } 165 | 166 | * Keep old vars that have reserved names to avoid dropping them if cleanup 167 | loc oldvars "" 168 | foreach x in _k_eq* _ttrend* __k* _f* _interact* _cohort _control_cohort { 169 | cap unab oldvarsadd: `x' 170 | loc oldvars "`oldvars' `oldvarsadd'" 171 | loc oldvarsadd "" 172 | } 173 | 174 | tempvar tousegen 175 | 176 | * Do not mark variables, only if in here 177 | 178 | mark `tousegen' `if' `in' 179 | 180 | loc flagerr=0 181 | 182 | 183 | * first parsing of window 184 | if "`window'"!="" { 185 | parsewindow `window' 186 | loc swindow = r(window) 187 | loc w_type = r(w_type) 188 | } 189 | 190 | * Set norm to -1 if not specified 191 | if "`norm'"=="" loc norm = -1 192 | 193 | if "`static'"=="" { 194 | if "`window'"!="" { 195 | * Parse window 196 | loc nw : word count `window' 197 | 198 | * if window is numeric 199 | if "`w_type'"=="numeric"{ 200 | if `nw'==1 { 201 | loc lwindow = -`window' 202 | loc rwindow = `window' 203 | } 204 | else if `nw'==2 { 205 | loc lwindow : word 1 of `window' 206 | loc rwindow : word 2 of `window' 207 | } 208 | } 209 | * if window is string (max or balanced) 210 | if "`w_type'"=="string"{ 211 | loc lwindow : word 1 of `window' 212 | loc rwindow : word 1 of `window' 213 | } 214 | 215 | if "`w_type'"=="numeric" { 216 | if (-`lwindow'<0 | `rwindow'<0) { 217 | di as err _n "Window can not be negative" 218 | exit 198 219 | } 220 | } 221 | } 222 | else if "`window'"=="" & ("`pre'"!="" & "`post'"!="" & "`overidpre'"!="" & "`overidpost'"!="") { 223 | loc lwindow = `pre' + `overidpre' 224 | loc lwindow = -`lwindow' 225 | loc rwindow = `post' + `overidpost' -1 226 | loc w_type = "numeric" 227 | } 228 | 229 | * If allowing for anticipation effects, change the normalization if norm is missing, or warn the user 230 | if ("`pre'"!="0" & "`pre'"!="") { 231 | if `norm'==-1 { 232 | loc norm = -`pre'-1 233 | di as text _n "You allowed for anticipation effects `pre' periods before the event, so the coefficient at `norm' was selected to be normalized to zero. Use options {bf:norm} and {bf:window} to override this." 234 | } 235 | } 236 | 237 | * Check that normalization is in window 238 | if "`w_type'"=="numeric" { 239 | if (`norm' < `=`lwindow'-1' | `norm' > `rwindow') { 240 | di as err _n "The coefficient to be normalized to 0 is outside of the estimation window" 241 | exit 498 242 | } 243 | } 244 | * Do not allow norm and trend 245 | if "`norm'" !="-1" & "`trend'" != "" { 246 | di as err _n "Option {bf:trend} not allowed with a value for option {bf:norm} different from -1." 247 | exit 198 248 | } 249 | *user 250 | *if "`trend'"!="" loc trend "trend(`trend')" 251 | *else loc trend "" 252 | 253 | * Estimate 254 | 255 | if "`proxy'" == "" & "`proxyiv'" == "" { 256 | di as txt _n "No proxy or instruments provided. Implementing OLS estimator" 257 | cap noi _eventols `varlist' [`weight'`exp'] if `tousegen', panelvar(`panelvar') timevar(`timevar') policyvar(`policyvar') lwindow(`lwindow') rwindow(`rwindow') w_type(`w_type') trend(`trend') savek(`savek') norm(`norm') `reghdfe' addabsorb(`addabsorb') `repeatedcs' cohort(`cohort') control_cohort(`control_cohort') `options' 258 | if _rc { 259 | errpostest `oldvars' 260 | } 261 | } 262 | else { 263 | di as txt _n "Proxy for the confound specified. Implementing FHS estimator" 264 | cap noi _eventiv `varlist' [`weight'`exp'] if `tousegen', panelvar(`panelvar') timevar(`timevar') policyvar(`policyvar') lwindow(`lwindow') rwindow(`rwindow') w_type(`w_type') proxyiv(`proxyiv') proxy (`proxy') savek(`savek') norm(`norm') `reghdfe' addabsorb(`addabsorb') `repeatedcs' `options' 265 | if _rc { 266 | errpostest `oldvars' 267 | } 268 | } 269 | * if window was max or balanced, return the found limits 270 | if "`w_type'"=="string" { 271 | loc lwindow = r(lwindow) 272 | loc rwindow = r(rwindow) 273 | } 274 | } 275 | else if "`static'"=="static" { 276 | loc lwindow=. 277 | loc rwindow=. 278 | di as txt _n "option {bf:static} specified. Estimating static model" 279 | di as txt _n "Plotting options ignored" 280 | if "`proxy'" == "" & "`proxyiv'" == "" { 281 | di as txt _n "No proxy or instruments provided. Implementing OLS estimator" 282 | cap noi _eventolsstatic `varlist' [`weight'`exp'] if `tousegen', panelvar(`panelvar') timevar(`timevar') policyvar(`policyvar') `reghdfe' addabsorb(`addabsorb') `repeatedcs' `options' `static' 283 | if _rc { 284 | errpostest `oldvars' 285 | } 286 | } 287 | 288 | else { 289 | di as txt _n "Proxy for the confound specified. Implementing FHS estimator" 290 | 291 | cap noi _eventivstatic `varlist' [`weight'`exp'] if `tousegen', panelvar(`panelvar') timevar(`timevar') policyvar(`policyvar') proxyiv(`proxyiv') proxy (`proxy') `reghdfe' addabsorb(`addabsorb') `repeatedcs' `options' `static' 292 | if _rc { 293 | errpostest `oldvars' 294 | } 295 | } 296 | } 297 | 298 | *don't try returning matrices and macros if noestimate is specified 299 | loc noestimate = r(noestimate) 300 | if "`noestimate'"=="." loc noestimate "" 301 | if "`noestimate'"!="" exit 302 | 303 | if `=r(flagerr)'!=1 { 304 | 305 | loc noestimate=r(noestimate) 306 | if "`noestimate'"=="." loc noestimate "" 307 | if "`noestimate'"!="" { 308 | loc savek = r(savek) 309 | *clear previous estimates, so it will not mix them with the new ones 310 | ereturn clear 311 | } 312 | 313 | ereturn scalar lwindow= `lwindow' 314 | ereturn scalar rwindow=`rwindow' 315 | if "`pre'"!="" { 316 | ereturn scalar pre = `pre' 317 | ereturn scalar post = `post' 318 | ereturn scalar overidpre = `overidpre' 319 | ereturn scalar overidpost = `overidpost' 320 | } 321 | ereturn local cmdline `"xtevent `0'"' /*"*/ 322 | ereturn local cmd2 "xtevent" 323 | ereturn local stub = "`savek'" 324 | ereturn local noestimate = "`noestimate'" 325 | ereturn local ambiguous = r(ambiguous) 326 | *don't return the remaining if the user indicated not to estimate 327 | if "`noestimate'"!="" exit 328 | 329 | mat delta=r(delta) 330 | mat Vdelta=r(Vdelta) 331 | mat b = r(b) 332 | mat V = r(V) 333 | ereturn repost b=b V=V, esample(`tousegen') 334 | ereturn matrix delta = delta 335 | ereturn matrix Vdelta = Vdelta 336 | if "`=r(method)'"=="iv" { 337 | mat deltaxsc = r(deltaxsc) 338 | mat deltaov = r(deltaov) 339 | mat Vdeltaov = r(Vdeltaov) 340 | mat deltax = r(deltax) 341 | mat Vdeltax = r(Vdeltax) 342 | ereturn matrix deltaxsc = deltaxsc 343 | ereturn matrix deltaov = deltaov 344 | ereturn matrix Vdeltaov = Vdeltaov 345 | ereturn matrix deltax = deltax 346 | ereturn matrix Vdeltax = Vdeltax 347 | if `=r(x1)'!=. ereturn local x1 = r(x1) 348 | 349 | } 350 | loc sun_abraham = r(sun_abraham) 351 | if "`sun_abraham'"=="." loc sun_abraham "" 352 | if "`sun_abraham'"!="" { 353 | mat b_ir = r(b_ir) 354 | mat V_ir = r(V_ir) 355 | mat b_interact = r(b_interact) 356 | mat V_interact = r(V_interact) 357 | mat ff_w = r(ff_w) 358 | mat Sigma_ff = r(Sigma_ff) 359 | ereturn matrix b_ir = b_ir 360 | ereturn matrix V_ir = V_ir 361 | ereturn matrix b_interact = b_interact 362 | ereturn matrix V_interact = V_interact 363 | ereturn matrix ff_w = ff_w 364 | ereturn matrix Sigma_ff = Sigma_ff 365 | } 366 | 367 | loc saveov = r(saveov) 368 | if "`saveov'"=="." loc saveov "" 369 | if "`saveov'"!="" { 370 | 371 | mat mattrendy = r(mattrendy) 372 | mat mattrendx = r(mattrendx) 373 | mat deltaov = r(deltaov) 374 | mat Vdeltaov = r(Vdeltaov) 375 | ereturn matrix mattrendy = mattrendy 376 | ereturn matrix mattrendx = mattrendx 377 | ereturn matrix deltaov = deltaov 378 | ereturn matrix Vdeltaov = Vdeltaov 379 | ereturn local trendsaveov = r(trendsaveov) 380 | } 381 | if "`trend'"!="" { 382 | ereturn local trendmethod = r(trendmethod) 383 | ereturn local trend = r(trend) 384 | } 385 | 386 | ereturn local names=r(names) 387 | loc cmd = r(cmd) 388 | ereturn local cmd = r(cmd) 389 | ereturn local df = r(df) 390 | ereturn local komit = r(komit) 391 | ereturn local kmiss = r(kmiss) 392 | ereturn local y1 = r(y1) 393 | ereturn local method = r(method) 394 | ereturn local depvar = r(depvar) 395 | } 396 | else { 397 | exit 198 398 | } 399 | 400 | if "`plot'"!="" xteventplot 401 | 402 | end 403 | 404 | * Program to parse window 405 | program define parsewindow, rclass 406 | 407 | syntax [anything] 408 | 409 | tokenize "`anything'" 410 | loc nwwindow = wordcount("`anything'") 411 | if !inlist(`nwwindow',1,2) { 412 | di as err _n "{bf:window} can only have one or two elements." 413 | exit 198 414 | } 415 | 416 | *check that all words are numeric or string 417 | loc isnum = 0 418 | forvalues i=1/`nwwindow'{ 419 | cap confirm number ``i'' 420 | if !_rc loc ++ isnum 421 | } 422 | if `isnum'>0 & `isnum'<`nwwindow'{ 423 | di as err _n "Invalid {bf:window} option." 424 | exit 198 425 | } 426 | 427 | * tell if all words are numeric or strings 428 | if `isnum' == 0 loc w_type ="string" 429 | if `isnum' == `nwwindow' loc w_type ="numeric" 430 | 431 | * if all words are numbers, check that they are integers 432 | if "`w_type'"=="numeric" { 433 | loc isnotint = 0 434 | forvalues i=1/`nwwindow'{ 435 | cap confirm integer number ``i'' 436 | if _rc!=0 loc ++ isnotint 437 | } 438 | if `isnotint'>0 { 439 | di as err _n "Number in {bf:window} must be integer." 440 | exit 126 441 | } 442 | } 443 | 444 | * if all words are string, check that it is only one word and it is a valid option name 445 | if "`w_type'"=="string" { 446 | 447 | if `nwwindow'>1 { 448 | di as err _n "If string, {bf:window} must have only one element." 449 | exit 198 450 | } 451 | 452 | if `nwwindow'==1 { 453 | if !inlist("`anything'","max","balanced"){ 454 | di as err _n "{bf:window} must be {bf:max} or {bf:balanced}." 455 | exit 198 456 | } 457 | } 458 | 459 | } 460 | 461 | return local swindow "`anything'" 462 | return local w_type "`w_type'" 463 | end 464 | 465 | program define cleanup 466 | 467 | syntax [anything] 468 | 469 | loc oldvars = "`anything'" 470 | 471 | foreach x in _k_eq* _ttrend* __k* _f* _interact* _cohort _control_cohort { 472 | cap unab todrop: `x' 473 | loc todrop: list local todrop - oldvars 474 | cap drop `todrop' 475 | loc todrop "" 476 | 477 | } 478 | cap _estimates clear 479 | end 480 | 481 | program define errpostest, rclass 482 | 483 | syntax [anything] 484 | 485 | cleanup `anything' _rc 486 | return local flagerr=1 487 | end 488 | 489 | 490 | 491 | -------------------------------------------------------------------------------- /xtevent/xtevent.sthlp: -------------------------------------------------------------------------------- 1 | 2 | {smcl} 3 | {* *! version 3.1.0 July 11 2024}{...} 4 | {cmd:help xtevent} 5 | {hline} 6 | 7 | {title:Title} 8 | 9 | {phang} 10 | {bf:xtevent} {hline 2} Panel Event Study Estimation 11 | 12 | 13 | {marker syntax}{...} 14 | {title:Syntax} 15 | 16 | {pstd} 17 | 18 | {p 8 17 2} 19 | {cmd:xtevent} 20 | {depvar} [{indepvars}] 21 | {ifin} {weight} 22 | {cmd:,} 23 | {opth pol:icyvar(varname)} 24 | {opth p:anelvar(varname)} 25 | {opth t:imevar(varname)} 26 | [{it:options}] 27 | 28 | {synoptset 50 tabbed}{...} 29 | {synopthdr} 30 | {synoptline} 31 | {syntab:Main} 32 | {p2coldent:* {opth pol:icyvar(varname)}} policy variable{p_end} 33 | {synopt: {opth p:anelvar(varname)}} variable that identifies the panels{p_end} 34 | {synopt: {opth t:imevar(varname)}} variable that identifies the time periods{p_end} 35 | {synopt: {opth w:indow(xtevent##windowspec:windowspec)}} estimation window{p_end} 36 | {synopt: {opth pre(integer)}} # of periods with anticipation effects{p_end} 37 | {synopt: {opth post(integer)}} # of periods with policy effects{p_end} 38 | {synopt: {opth overidpre(integer)}} # of periods to test pre-trends{p_end} 39 | {synopt: {opth overidpost(integer)}} # of periods to test effects leveling off{p_end} 40 | {synopt:{opt st:atic}} estimate static model {p_end} 41 | {synopt: {opth imp:ute(xtevent##imputetype:type [, saveimp])}} impute leads, lags, and missing values of policyvar{p_end} 42 | {synopt:{opth norm(integer)}} event-time coefficient to normalize to 0{p_end} 43 | {synopt:{opt diff:avg}} estimate the difference in averages between the post and pre-periods {p_end} 44 | {synopt:{opt sav:ek(stub [, subopt])}} save time-to-event, event-time, trend, and interaction variables{p_end} 45 | {synopt: {opt kvars(stub)}} use previously generated event-time variables{p_end} 46 | {synopt:{opt reghdfe}} use {help reghdfe} for estimation{p_end} 47 | {synopt:{opth addabsorb(varlist)}} absorb additional variables in {help reghdfe}{p_end} 48 | {synopt:{opt plot}} display plot. See {help xteventplot}{p_end} 49 | {synopt:{opt nofe}} omit panel fixed effects {p_end} 50 | {synopt:{opt note}} omit time fixed effects {p_end} 51 | {synopt:{it: additional_options}} additional options to be passed to the estimation command{p_end} 52 | 53 | {syntab:Instrumental variable estimation with proxy variables (Freyaldenhoven et al 2019)} 54 | {synopt:{opth proxy(varname)}} proxy for the confound{p_end} 55 | {synopt:{opth proxyiv:(xtevent##proxyiv_spec:proxyiv_spec)}} instruments for the proxy variable{p_end} 56 | 57 | {syntab:Controlling for event-time trends} 58 | {synopt:{opt tr:end(#1 [, subopt])}} extrapolate linear trend from time period #1 before treatment{p_end} 59 | 60 | {syntab: Heterogeneous treatment effects (Sun and Abraham 2021)} 61 | {synopt:{opth cohort:(xtevent##cohortspec:cohortspec [, subopt])}} cohorts for Sun and Abraham (2021) estimation{p_end} 62 | {synopt:{opth control_cohort:(xtevent##controlcohortspec:control_cohort_spec [, subopt])}} control cohort for Sun and Abraham (2021) estimation{p_end} 63 | {synopt:{opt sunab:raham}} Sun and Abraham (2021) estimation automatically creating cohort variables{p_end} 64 | 65 | {syntab: Estimation with repeated cross sectional data} 66 | {synopt:{opt rep:eatedcs}} indicate that the dataset in memory is repeated cross-sectional{p_end} 67 | {synoptline} 68 | {p2colreset}{...} 69 | 70 | {p 4 6 2} {it: depvar} and {it:indepvars} may contain time-series operators; see {help tsvarlist}.{p_end} 71 | {p 4 6 2} {it: depvar} and {it:indepvars} may contain factor variables; see {help fvvarlist}.{p_end} 72 | 73 | {p 4 6 2}* {opth policyvar(varname)} is required. {opth window(integer)} is required unless {opt static}, or {opt pre}, {opt post}, 74 | {opt overidpre}, and {opt overidpost} are specified. {opth panelvar(varname)} and {opth timevar(varname)} are required if the data 75 | have not been {cmd:xtset}, otherwise they are optional. See {help xtset}. {p_end} 76 | {p 4 6 2} 77 | See {help xteventtest} for hypothesis testing after estimation and {help xteventplot} for plotting after estimation.{p_end} 78 | 79 | 80 | {marker description}{...} 81 | {title:Description} 82 | 83 | {pstd} 84 | {cmd: xtevent} estimates the effect of a policy variable of interest on a dependent variable using a panel event-study 85 | design. Additional control variables can be included in {it:varlist}. The command allows for estimation when a pre-trend 86 | is present using the instrumental variables estimator of Freyaldenhoven et al. (2019). It also allows estimation in 87 | settings with heterogeneous effects by cohort using the Interaction Weighted Estimator of Sun and Abraham (2021).{p_end} 88 | 89 | 90 | {marker options}{...} 91 | {title:Options} 92 | 93 | {dlgtab:Main} 94 | 95 | {phang} 96 | {opth policyvar(varname)} specifies the policy variable of interest. {opt policyvar()} is required. 97 | 98 | {phang} 99 | {opth panelvar(varname)} specifies the cross-sectional identifier variable that identifies the panels. {cmd:panelvar()} is required if the data 100 | have not been previously {cmd:xtset}. See {help xtset}. 101 | 102 | {phang} 103 | {opth timevar(varname)} specifies the time variable. {cmd:timevar()} is required if the data have not been previously {cmd:xtset}. See 104 | {help xtset}. 105 | 106 | {marker windowspec}{...} 107 | {phang} 108 | {opt window(windowspec)} specifies the window around the policy change event to estimate dynamic effects. 109 | 110 | {phang2} 111 | {opt window(k)} with a single positive integer {it:k}>0 uses a symmetric window of {it:k} periods around the event. For example, if {it:k} = 2, there will be five 112 | coefficients in the window (-2,-1,0,1,2) and two endpoints: -3 and +3. 113 | 114 | {phang2} 115 | {opt window(k1 k2)} with two distinct integers {it:k1}<=0 and {it:k2}>=0 uses an asymmetric window with {it:k1} periods before the event and {it:k2} periods after the event. For example, with {it:k1} = -1 116 | and {it:k2} = 2, there will be four coefficients in the window (-1,0,1,2) and two endpoints: -2 and +3. 117 | 118 | {phang2} 119 | {cmd: window(max)} uses the largest possible window with the minimum and maximum event times in the estimation sample, accounting for the endpoints. 120 | {cmd: window(max)} is only allowed if the policy follows staggered adoption and requires {cmd: impute(stag)} or {cmd: impute(instag)} to be specified (see below). 121 | 122 | {phang2} 123 | {cmd: window(balanced)} uses the largest possible window with the minimum and maximum event times in the estimation sample for which all cross sectional units have data. 124 | {cmd: window(balanced)} is only allowed if the policy follows staggered adoption and requires {cmd: impute(stag)} or {cmd: impute(instag)} to be specified (see below). 125 | 126 | {phang} 127 | {opt window()} is required unless {opt static} is specified, or if the estimation window is specified using options {opt pre()}, {opt post()}, {opt overidpre()}, 128 | and {opt overidpost()} (See below). 129 | 130 | {phang} 131 | {opt pre}, 132 | {opt post}, 133 | {opt overidpre} and 134 | {opt overidpost} offer an alternative way to specify the estimation window: 135 | 136 | {phang2} {opt pre} is the number of pre-event periods where anticipation effects are allowed. With {opt window}, {opt pre} is 0. 137 | 138 | {phang2} {opt post} is the number of post-event periods where policy effects are allowed. With {opt window}, {opt post} is the number 139 | of periods after the event (not including the period for the event, e.g. event time = 0), 140 | except the latest two periods (assigned to {opt overidpost} for the leveling-off test). 141 | 142 | {phang2} {opt overidpre} is the number of pre-event periods for an overidentification test of pre-trends. With {opt window}, {opt overidpre} 143 | is the number of periods before the event. 144 | 145 | {phang2} {opt overidpost} is the number of post-event periods for an overidentification test of effects leveling off. With {opt window}, 146 | {opt overidpost} is 2. 147 | 148 | {phang} Only one of {opt window} or 149 | {opt pre}, 150 | {opt post}, 151 | {opt overidpre} and 152 | {opt overidpost} can be declared. 153 | 154 | {phang} 155 | {opt static} estimates a static panel data model and does not generate or plot event-time dummies. {opt static} is not allowed with 156 | {opt window}, {opt pre}, {opt post}, {opt overidpre}, {opt overidpost}, or {opt diffavg}. 157 | 158 | {marker imputetype}{...} 159 | {phang} 160 | {opt impute(type, [ saveimp])} imputes leads, lags, and missing values of {it:policyvar} and uses this new variable as the actual {it:policyvar}. 161 | {cmd:type} determines the imputation rule. The suboption {cmd:saveimp} adds the new variable to the dataset as 162 | {it:policyvar_imputed}. The following imputation types are available: 163 | 164 | {phang2} 165 | {cmd:impute(nuchange)} imputes missing values in {it:policyvar} according to {it:no unobserved change}: it assumes that 166 | for each unit: i) in periods before the first observed value, the policy value is the same as the first observed value and; 167 | ii) in periods after the last observed value, the policy value is the same as the last observed value. 168 | 169 | {phang2} 170 | {cmd:impute(stag)} applies {it:no unobserved change} if {it:policyvar} satisfies staggered-adoption assumptions for all units: 171 | i) {it:policyvar} must be binary, and ii) once {it:policyvar} reaches the adopted-policy state, it never reverts to the 172 | unadopted-policy state. See Freyaldenhoven et al. (2021) for detailed explanation of the staggered adoption case. 173 | 174 | {phang2} 175 | {cmd:impute(instag)} applies {opt impute(stag)} and additionally imputes missing values inside the observed data range: a missing 176 | value or a group of them will be imputed only if they are both preceded and followed by the unadopted-policy state or by the 177 | adopted-policy state. 178 | 179 | {phang2} 180 | See {browse "https://rawcdn.githack.com/JMSLab/xtevent/cf16d12f90ddf363df62c397cf0e9dc05bbd9875/impute_option_description.html":this} for a detailed example of the {opt impute} option. 181 | 182 | {phang} {opth norm(integer)} specifies the event-time coefficient to be normalized to 0. 183 | The default is to normalize the coefficient on -1. 184 | 185 | {phang} 186 | {opt diffavg} calculates the difference in averages between the post-event estimated coefficients and the pre-event estimated 187 | coefficients. It also calculates its standard error with {help lincom}. {opt diffavg} is not allowed with {opt static}. 188 | 189 | {phang} 190 | {opt savek(stub [, subopt])} saves variables for time-to-event, event-time, trend, and interaction variables. Event-time dummies are stored as 191 | {it: stub}_eq_m# for the dummy variable # periods before the policy change, and {it:stub}_eq_p# for the dummy variable # periods after the 192 | policy change. The dummy variable for the policy change time is {it:stub}_eq_p0. Event time is stored as {it:stub}_evtime. The trend is stored 193 | as {it:stub}_trend. For estimation with the Sun and Abrahm (2021) method, such that {opt cohort} and {opt control_cohort} or {opt sunabraham} are active, the 194 | interaction variables are stored as {it:stub}_m#_c# or {it:stub}_p#_c#, where c# indicates the cohort. The following suboptions can be 195 | specified: 196 | 197 | {phang2} 198 | {opt noe:stimate} saves variables for event-time dummies, event-time, and trends without estimating the model. This option is helpful if the 199 | users want to customize their regressions and plots. 200 | 201 | {phang2} 202 | {opt saveint:eract} saves interaction variables if {opt cohort} and {opt control_cohort}, or {opt sunabraham} are specified. {opt noe:stimate} and 203 | {opt saveint:eract} cannot be specified simultaneously. 204 | 205 | {phang2} 206 | {opt replace} replaces variables for time-to-event, event-time, trend, and interaction variables starting with {it:stub}. 207 | 208 | {phang} 209 | {opt kvars(stub)} uses previously used event-time dummies saved with prefix {it:stub}. This can be used to speed up estimation. 210 | 211 | {phang} 212 | {opt reghdfe} uses {help reghdfe} for estimation, instead of {help areg}, {help ivregress}, and {help xtivreg}. {opt reghdfe} is useful for large 213 | datasets. By default, it absorbs the panel fixed effects and the time fixed effects. For OLS estimation, the {opt reghdfe} 214 | option requires {help reghdfe} and {help ftools} to be installed. For IV estimation, it also requires {help ivreghdfe} and {help ivreg2} 215 | to be installed. Note that standard errors may be different and singleton clusters may be dropped using {help reghdfe}. 216 | See Correia (2016). 217 | 218 | {phang} 219 | {opth addabsorb(varlist)} specifies additional fixed effects to be absorbed when using {help reghdfe}. By default, {cmd:xtevent} includes time 220 | and unit fixed effects. {opt addabsorb} requires {opt reghdfe}. 221 | 222 | {phang} 223 | {opt plot} displays a default event-study plot with standard confidence intervals and sup-t confidence bands (Montiel Olea and Plagborg-Møller 2019). 224 | Additional options are available with the postestimation command {help xteventplot}. 225 | 226 | {phang} 227 | {opt nofe} excludes panel fixed effects. 228 | 229 | {phang} 230 | {opt note} excludes time fixed effects. 231 | 232 | {phang} 233 | {it: additional_options}: Additional options to be passed to the estimation command. When {opt proxy} is specified, these options are passed 234 | to {help ivregress}. When {opt reghdfe} is specified, these options are passed to {help reghdfe}. Otherwise, they are passed to {help areg} or 235 | to {help regress} if {opt nofe} is specified. This option is useful for calculating clustered standard errors or changing regression reporting. 236 | 237 | {dlgtab: Instrumental variable estimation with proxy variables (Freyaldenhoven et al 2019)} 238 | 239 | {phang} 240 | {opth proxy(varlist)} specifies proxy variables for the confound to be included. {opt proxy} is not allowed with {opt cohort}, 241 | {opt control_cohort} or {opt sunabraham}. 242 | 243 | {marker proxyiv_spec}{...} 244 | {phang} 245 | {opt proxyiv(proxyiv_spec)} specifies instruments for the proxy variable for the policy. {cmd:proxyiv()} admits three syntaxes to use 246 | either leads of the policy variable or additional variables as instruments. {opt proxy} is not allowed with {opt cohort}, 247 | {opt control_cohort} or {opt sunabraham}. 248 | 249 | {phang2} 250 | {cmd:proxyiv(select)} selects the lead with the strongest first stage among all possible leads of the 251 | differenced policy variable to be used as an instrument. 252 | {cmd:proxyiv(select)} is the default for the one proxy, one instrument case, and it is only available in this case. 253 | 254 | {phang2} 255 | {cmd:proxyiv(# ...)} specifies a {it: numlist} with the leads of the differenced policy variable as instruments. For example, 256 | {cmd:proxyiv(1 2)} specifies that the two first leads of the difference of the policy variable will be used as instruments. 257 | 258 | {phang2} 259 | {opth proxyiv(varlist)} specifies a {it:varlist} with the additional variables to be used as instruments. 260 | 261 | {dlgtab:Controlling for event-time trends} 262 | 263 | {phang} 264 | {opt tr:end(#1 [, subopt])} extrapolates a linear trend using the time periods from period #1 before the policy change to one 265 | period before the policy change, as in Dobkin et al. (2018). For example, {cmd: trend(-3)} uses the coefficients on event-times 266 | -3, -2, and -1 to estimate the trend. The estimated effect of the policy is the deviation from the extrapolated linear trend. 267 | #1 must be less than -1. {opt trend} is only available when the normalized coefficient is -1 and {opt pre} = 0. 268 | The following can be passed as suboptions: 269 | 270 | {phang2} 271 | {opt method(string)} sets the method to estimate the linear trend. It can be Ordinary Least Squares ({opt method(ols)}) or 272 | Generalized Method of Moments ({opt method(gmm)}). {opt method(ols)} omits the event-time dummies from {opt trend(#1)} to 273 | -1 and adds a linear trend (_ttrend) to the regression. {opt method(gmm)} uses the GMM to compute the trend for the event-time 274 | dummy coefficients. The default is {opt method(gmm)}. 275 | 276 | {phang2} 277 | Note that the coefficients for negative-event time will differ between {opt method(ols)} and {opt method(gmm)}. {opt method(ols)} 278 | omits the event-time coefficients used to calculate the trend, while {opt method(gmm)} expresses them as differences from the 279 | estimated linear trend. 280 | 281 | {phang2} 282 | {opt saveov:erlay} saves estimations for the overlay plot produced by {opt xteventplot, overlay(trend)}. 283 | 284 | {dlgtab:Heterogeneous treatment effects (Sun and Abraham 2021)} 285 | 286 | {marker cohortspec}{...} 287 | {phang} 288 | {opt cohort(cohort_spec)} specifies how to identify the treatment cohorts used for estimation of heterogenous effects by cohort using the 289 | estimator from Sun and Abraham(2021). {opt cohort} requires the Stata module {cmd:avar}; click {stata ssc install avar :here} to install or 290 | type "ssc install avar" from inside Stata. {opt cohort} is not allowed with {opt proxy} or {opt proxyiv}. 291 | 292 | {phang2} 293 | {cmd:cohort(variable {help varname}},{cmd: [,force])} specifies that the categorical variable {help varname} identifies each treatment cohort. 294 | By default, {cmd:xtevent} checks for consistency of the cohort variable and the policy variable. {bf:force} forces {cmd:xtevent} to 295 | skip this check. This can be useful when estimating heterogenous treatment effects across groups not defined by treatment cohorts. 296 | 297 | {phang2} 298 | {cmd:cohort(create},{cmd: [,save replace]}) asks {cmd:xtevent} to create the categorical treatment cohort variable based on values of the policy variable. 299 | {opt save} adds the new cohort variable to the dataset as {it: policyvar_cohort}. {opt replace} replaces the cohort variable if it already exists. 300 | The automatic creation of the cohort variable is only available in the staggered adoption case. 301 | 302 | {marker controlcohortspec}{...} 303 | {phang} 304 | {opt control_cohort(control_cohort_spec)} specifies how to identify the control cohort used for estimation of heterogenous effects by cohort 305 | using the estimator from Sun and Abraham(2021). {opt control_cohort} requires {opt cohort} to be specified. {opt control_cohort} is not 306 | allowed with {opt proxy} or {opt proxyiv}. 307 | 308 | {phang2} 309 | {cmd:control_cohort(variable {help varname}},{cmd: [,force])} specifies that the binary variable {help varname} identifies the control cohort. 310 | By default, {cmd:xtevent} checks for consistency of the control cohort variable and the policy variable. {bf:force} forces {cmd:xtevent} to 311 | skip this check. This can be useful when estimating heterogenous treatment effects across groups not defined by treatment cohorts. 312 | 313 | {phang2} 314 | {cmd:control_cohort(create},{cmd: [,save replace]}) asks {cmd:xtevent} to create the binary control cohort variable based on the missing values of 315 | the cohort variable. {opt save} adds the new control cohort variable to the dataset as {it: policyvar_control_cohort}. {opt replace} replaces 316 | the control cohort variable if it already exists. {opt control_cohort(create)} is the default if {opt cohort(create)} is specified but {opt control_cohort} 317 | is not specified. 318 | 319 | {phang} 320 | {opt sunab:raham} is a shorthand to specify estimation with heterogenous treatment effects by cohort using the estimator from Sun and Abraham (2021). 321 | {opt sunabraham} is equivalent to {opt cohort(create)} and {opt control_cohort(create)}. 322 | 323 | {dlgtab:Estimation with repeated cross sectional data} 324 | 325 | {phang} 326 | {opt repeatedcs} indicates that the dataset in memory is repeated cross-sectional. In this case, {opt panelvar} should indicate the groups 327 | at which {opt policyvar} changes. For instance, {opt panelvar} could indicate states at which {opt policyvar} changes, while the observations 328 | in the dataset are individuals in each state. An alternative method to 329 | estimate the event study in a repeated cross-sectional dataset involves using {cmd:get_unit_time_effects} first, and then {cmd:xtevent}. 330 | See {help get_unit_time_effects}. For fixed-effects estimation, {opt repeatedcs} enables {opt reghdfe}. 331 | 332 | {title:Examples} 333 | 334 | {hline} 335 | {pstd}Setup{p_end} 336 | {phang2}{cmd:. {stata webuse nlswork}}{p_end} 337 | {pstd}year variable has many missing observations.{p_end} 338 | {pstd}Create a time variable that ignores these gaps.{p_end} 339 | {phang2}{cmd:. {stata "by idcode (year): gen time=_n"}}{p_end} 340 | {phang2}{cmd:. {stata xtset idcode time}}{p_end} 341 | 342 | {pstd}Generate a policy variable that follows staggered adoption.{p_end} 343 | {phang2}{cmd:. {stata "by idcode (time): gen union2 = sum(union)"}}{p_end} 344 | {phang2}{cmd:. {stata replace union2 = 1 if union2 > 1}}{p_end} 345 | 346 | {hline} 347 | {pstd}Estimate a basic event study with clustered standard errors.{p_end} 348 | {pstd}Impute the policy variable assuming no unobserved changes{p_end} 349 | {phang2}{cmd:. {stata xtevent ln_w age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure , pol(union2) w(3) cluster(idcode) impute(nuchange)}} 350 | {p_end} 351 | 352 | {pstd}Omit unit and time fixed effects{p_end} 353 | {pstd}Impute the policy variable verifying staggered adoption{p_end} 354 | {phang2}{cmd:. {stata xtevent ln_w age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure , pol(union2) w(3) cluster(idcode) nofe note impute(stag)}} 355 | {p_end} 356 | 357 | {pstd}Save event-time dummies without estimating the model{p_end} 358 | {phang2}{cmd:. {stata xtevent ln_w age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure , pol(union2) w(3) cluster(idcode) impute(stag) savek(a, noe)}} 359 | {p_end} 360 | 361 | {pstd}Change the normalized coefficient and use an asymmetric window{p_end} 362 | {phang2}{cmd:. {stata xtevent ln_w age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure , pol(union2) cluster(idcode) w(-3 1) norm(-2) impute(stag)}} 363 | {p_end} 364 | 365 | {pstd}Estimate using all possible periods before and after the event{p_end} 366 | {phang2}{cmd:. {stata xtevent ln_w age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure , pol(union2) cluster(idcode) w(max) impute(stag)}} 367 | {p_end} 368 | 369 | {pstd}Adjust the pre-trend by estimating a linear trend by GMM {p_end} 370 | {phang2}{cmd:. {stata xtevent ln_w age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure , pol(union2) w(2) cluster(idcode) trend(-2, method(gmm)) impute(stag)}} 371 | {p_end} 372 | 373 | {hline} 374 | 375 | {pstd}Freyaldenhoven, Hansen and Shapiro (2019) estimator with proxy variables{p_end} 376 | {phang2}{cmd:. {stata xtevent ln_w age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure , pol(union2) w(3) vce(cluster idcode) proxy(wks_work) impute(stag)}} 377 | {p_end} 378 | 379 | {pstd}Include additional proxy variables, and additional policy variable leads as instruments{p_end} 380 | {phang2}{cmd:. {stata xtevent ln_w age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure , pol(union2) w(3) vce(cluster idcode) proxy(wks_work hours) proxyiv(1 2) impute(stag)}} 381 | {p_end} 382 | 383 | {pstd}{help reghdfe} and two-way clustering {p_end} 384 | {phang2}{cmd:. {stata xtevent ln_w age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure , pol(union2) w(3) cluster(idcode year) reghdfe proxy(wks_work) impute(stag)}} 385 | {p_end} 386 | 387 | {hline} 388 | 389 | {pstd}Interaction Weighted Estimator proposed by Sun and Abraham (2021){p_end} 390 | {phang2}{cmd:. {stata xtevent ln_w age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure, policyvar(union2) window(3) impute(stag) vce(cluster idcode) sunabraham}} 391 | {p_end} 392 | 393 | {hline} 394 | 395 | {pstd}Interaction Weighted Estimator proposed by Sun and Abraham (2021) with user-created cohort variables{p_end} 396 | {pstd}First, create the control and control cohort variables{p_end} 397 | {pstd}Generate the variable that indicates cohort{p_end} 398 | {phang2}{cmd:. {stata gen timet=year if union2==1}} 399 | {p_end} 400 | {phang2}{cmd:. {stata "by idcode: egen time_of_treat=min(timet)"}} 401 | {p_end} 402 | 403 | {pstd}Generate the variable that indicates the control cohort. We use the never-treated units as the control cohort{p_end} 404 | {phang2}{cmd:. {stata gen never_treat=time_of_treat==.}} 405 | {p_end} 406 | 407 | {pstd}Estimate{p_end} 408 | {phang2}{cmd:. {stata xtevent ln_w age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure, policyvar(union2) window(3) impute(stag) vce(cluster idcode) cohort(variable time_of_treat) control_cohort(variable never_treat)}} 409 | {p_end} 410 | 411 | 412 | {marker saved}{...} 413 | {title:Saved Results} 414 | 415 | {pstd} 416 | {cmd:xtevent} saves the following in {cmd:e()}: 417 | 418 | {synoptset 20 tabbed}{...} 419 | {p2col 5 20 24 2: Scalars}{p_end} 420 | {synopt:{cmd:e(lwindow)}}left endpoint for estimation window{p_end} 421 | {synopt:{cmd:e(rwindow)}}right endpoint for estimation window{p_end} 422 | 423 | {synoptset 20 tabbed}{...} 424 | {p2col 5 20 24 2: Macros}{p_end} 425 | {synopt:{cmd:e(names)}}names of the variables for the event-time dummies{p_end} 426 | {synopt:{cmd:e(y1)}}mean of dependent variable at event-time = -1{p_end} 427 | {synopt:{cmd:e(x1)}}mean of proxy variable at event-time = -1, when only one proxy is specified{p_end} 428 | {synopt:{cmd:e(trend)}}"trend" if estimation included extrapolation of a linear trend{p_end} 429 | {synopt:{cmd:e(trendmethod)}}method used to estimate the linear trend: can be "ols" or "gmm"{p_end} 430 | {synopt:{cmd:e(cmd)}}estimation command: can be {help regress}, {help areg}, {help ivregress}, {help xtivreg}, or {help reghdfe} 431 | {p_end} 432 | {synopt:{cmd:e(df)}}degrees of freedom{p_end} 433 | {synopt:{cmd:e(komit)}}list of lags/leads omitted from regression{p_end} 434 | {synopt:{cmd:e(kmiss)}}list of lags/leads to be omitted from plot{p_end} 435 | {synopt:{cmd:e(ambiguous)}}list of cross sectional units omitted because of ambiguous event times{p_end} 436 | {synopt:{cmd:e(method)}}"ols" or "iv"{p_end} 437 | {synopt:{cmd:e(cmd2)}}"xtevent"{p_end} 438 | {synopt:{cmd:e(depvar)}}dependent variable{p_end} 439 | {synopt:{cmd:e(pre)}}number of periods with anticipation effects{p_end} 440 | {synopt:{cmd:e(post)}}number of periods with policy effects{p_end} 441 | {synopt:{cmd:e(overidpre)}}number of periods to test for pre-trends{p_end} 442 | {synopt:{cmd:e(overidpost)}}number of periods to test for effects leveling off{p_end} 443 | {synopt:{cmd:e(stub)}}prefix for saved event-time dummy variables{p_end} 444 | 445 | {synoptset 20 tabbed}{...} 446 | {p2col 5 20 24 2: Matrices}{p_end} 447 | {synopt:{cmd:e(b)}}coefficient vector{p_end} 448 | {synopt:{cmd:e(V)}}variance-covariance matrix{p_end} 449 | {synopt:{cmd:e(delta)}}coefficient vector of event-time dummies{p_end} 450 | {synopt:{cmd:e(Vdelta)}}variance-covariance matrix of the event-time dummies coefficients{p_end} 451 | {synopt:{cmd:e(deltax)}} coefficients for proxy event-study to be used in overlay plot{p_end} 452 | {synopt:{cmd:e(deltaxsc)}}scaled coefficients for proxy event-study to be used in overlay plot{p_end} 453 | {synopt:{cmd:e(deltaov)}}coefficients for event-study to be used in overlay plot{p_end} 454 | {synopt:{cmd:e(Vdeltax)}} variance-covariance matrix of proxy-event study coefficients for overlay plot{p_end} 455 | {synopt:{cmd:e(Vdeltaov)}} variance-covariance matrix of event-study coefficients for overlay plot{p_end} 456 | {synopt:{cmd:e(mattrendy)}} matrix with y-axis values of trend for overlay plot, only when {opt trend(#1)} is specified{p_end} 457 | {synopt:{cmd:e(mattrendx)}} matrix with x-axis values of trend for overlay plot, only when {opt trend(#1)} is specified{p_end} 458 | {synopt:{cmd:e(b_ir)}} each column vector contains estimates of each cohort-relative-time interaction and controls included in the interaction regression. The interaction variables are named {it:_interact_m#_c#} or {it:_interact_p#_c#}, where {it:m#} indicates {it:#} periods before the policy change, {it:p#} indicates {it:#} periods after the policy change, and {it:c#} indicates the cohort. Available only when {opt cohort} and {opt control_cohort}, or {opt sunabraham} are specified{p_end} 459 | {synopt:{cmd:e(V_ir)}} covariance matrix of the cohort-relative-time interactions and controls included in the interaction regression. The interaction variables are named {it:_interact_m#_c#} or {it:_interact_p#_c#}, where {it:m#} indicates {it:#} periods before the policy change, {it:p#} indicates {it:#} periods after the policy change, and {it:c#} indicates the cohort. Available only when {opt cohort} and {opt control_cohort}, or {opt sunabraham} are specified{p_end} 460 | {synopt:{cmd:e(b_interact)}} each column vector contains estimates of cohort-specific effect for the given relative time, only when {opt cohort} and {opt control_cohort}, or {opt sunabraham} are specified{p_end} 461 | {synopt:{cmd:e(V_interact)}} each column vector contains variance estimate of the cohort-specific effect estimator for the given relative time, only when {opt cohort} and {opt control_cohort}, or {opt sunabraham} are specified{p_end} 462 | {synopt:{cmd:e(ff_w)}} each column vector contains estimates of cohort shares underlying the given relative time, only when {opt cohort} and {opt control_cohort}, or {opt sunabraham} are specified{p_end} 463 | {synopt:{cmd:e(Sigma_ff)}} variance estimate of the cohort share estimators, only when {opt cohort} and {opt control_cohort}, or {opt sunabraham} are specified{p_end} 464 | 465 | {synoptset 20 tabbed}{...} 466 | {p2col 5 20 24 2: Functions}{p_end} 467 | {synopt:{cmd:e(sample)}}marks estimation sample{p_end} 468 | 469 | {title:Authors} 470 | 471 | {pstd}Simon Freyaldenhoven, Federal Reserve Bank of Philadelphia.{p_end} 472 | simon.freyaldenhoven@phil.frb.org 473 | {pstd}Christian Hansen, University of Chicago, Booth School of Business.{p_end} 474 | chansen1@chicagobooth.edu 475 | {pstd}Jorge Pérez Pérez, Banco de México.{p_end} 476 | jorgepp@banxico.org.mx 477 | {pstd}Jesse Shapiro, Harvard University and NBER.{p_end} 478 | jesse_shapiro@fas.harvard.edu 479 | 480 | {title:Support} 481 | 482 | {pstd}For support and to report bugs please email Jorge Pérez Pérez, Banco de México.{break} 483 | jorgepp@banxico.org.mx 484 | 485 | {pstd}{cmd:xtevent} can also be found on {browse "https://github.com/JMSLab/xtevent":GitHub}. 486 | 487 | {title:References} 488 | 489 | {pstd}Correia, S. (2016) . "Linear Models with High-Dimensional Fixed Effects: An Efficient and Feasible Estimator" Working Paper. {browse "http://scorreia.com/research/hdfe.pdf"} 490 | 491 | {pstd}Dobkin, C., Finkelstein A., Kluender. R., and Notowidigdo, M. J. (2018) "The Economic Consequences of Hospital Admissions." 492 | {it:American Economic Review}, 108 (2): 308-52. 493 | 494 | {pstd}Freyaldenhoven, S., Hansen, C. and Shapiro, J. (2019) "Pre-event Trends in the Panel Event-study Design" {it:American Economic Review}, 109 (9): 495 | 3307-38. 496 | 497 | {pstd}Freyaldenhoven, S., Hansen, C., Pérez Pérez, J. and Shapiro, J. (2021) "Visualization, Identification, 498 | and Estimation in the Linear Panel Event-study Design". Working paper. 499 | 500 | {pstd}Montiel Olea, J.L. and Plagborg-Møller, M. (2019) "Simultaneous confidence bands: Theory, implementation, and an application to SVARs". 501 | {it:Journal of Applied Econometrics}, 34: 1– 17. 502 | 503 | {pstd}Sun, L. and Abraham, S. (2021) "Estimating Dynamic Treatment Effects in Event Studies with Heterogeneous Treatment Effects". 504 | {it:Journal of Econometrics}, 225 (2): 175-199. 505 | 506 | {title:Acknowledgements} 507 | {pstd}We are grateful to Veli Andirin, Mauricio Cáceres, Richard Calvo, Constantino Carreto, Kathryn Dawson-Townsend, Theresa Doppstadt, 508 | Ángel Espinoza, Miguel Fajardo-Steinhauser, Samuele Giambra, Santiago Hermo, Ray Huang, Stephen Jenkins, Chandra Kant Dhakal, 509 | Daniel Klein, Ryan Kobler, Panagiotis Konstantinou, Per Lidbom, Isabel Z. Martínez, Diego Mayorga, Eric Melse, Stefano Molina, 510 | Asjad Naqvi, René Nieto, Anna Pasnau, Nathan Schor, Emily Wang, Matthias Weigand, Wenli Xu and an anonymous reviewer for 511 | contributions to development and for testing earlier versions of this command. 512 | -------------------------------------------------------------------------------- /xtevent/xteventplot.sthlp: -------------------------------------------------------------------------------- 1 | {smcl} 2 | {* *! version 3.1.0 July 11 2024}{...} 3 | {cmd:help xteventplot} 4 | {hline} 5 | 6 | {title:Title} 7 | 8 | {phang} 9 | {bf:xteventplot} {hline 2} Plots After Panel Event Study Estimation 10 | 11 | 12 | {marker syntax}{...} 13 | {title:Syntax} 14 | 15 | {pstd} 16 | 17 | {p 8 17 2} 18 | {cmd:xteventplot} 19 | {cmd:,} 20 | [{it:options}] 21 | 22 | {synoptset 25 tabbed}{...} 23 | {synopthdr} 24 | {synoptline} 25 | {syntab:Main} 26 | {synopt:{opth suptreps(integer)}} number of repetitions for sup-t confidence bands{p_end} 27 | {synopt:{opt overlay(string)}} generate overlay plots{p_end} 28 | {synopt:{opt y}} generate event study plot for dependent variable in IV setting{p_end} 29 | {synopt:{opt proxy}} generate event study plot for proxy variable in IV setting{p_end} 30 | {synopt:{opt lev:els(numlist)}} customize confidence levels for plot{p_end} 31 | {synopt:{opt sm:path([type, subopt])}} smoothest path through confidence region{p_end} 32 | {synopt:{opt overidpre(integer)}} change pre-event coefficients to be tested{p_end} 33 | {synopt:{opt overidpost(integer)}} change post-event coefficients to be tested{p_end} 34 | 35 | {syntab:Appearance} 36 | {synopt: {opt noci}} omit all confidence intervals and bands{p_end} 37 | {synopt:{opt nosupt}} omit sup-t confidence bands{p_end} 38 | {synopt:{opt nozero:line}} omit reference line at 0{p_end} 39 | {synopt:{opt nonorml:abel}} omit label for value of dependent variable at event-time = -1 {p_end} 40 | {synopt:{opt noprepval}} omit p-value for pre-trends test{p_end} 41 | {synopt:{opt nopostpval}} omit p-value for leveling-off test{p_end} 42 | {synopt:{opt scatterplot:opts(string)}} graphics options for coefficient scatter plot{p_end} 43 | {synopt:{opt ciplot:opts(string)}} graphics options for confidence interval plot{p_end} 44 | {synopt:{opt suptciplot:opts(string)}} graphics options for sup-t confidence band plot{p_end} 45 | {synopt:{opt smplot:opts(string)}} graphics options for smoothest path plot{p_end} 46 | {synopt:{opt trendplot:opts(string)}} graphics options for extrapolated trend plot{p_end} 47 | {synopt:{opt staticovplot:opts(string)}} graphics options for the static effect overlay plot {p_end} 48 | {synopt:{opt textboxoption(string)}} textbox options for displaying the p-values of the pre-trend and leveling-off tests{p_end} 49 | {synopt:{opt addplots(string)}} plot to be overlaid on event-study plot{p_end} 50 | {synopt:{it: additional_options}} additional options to be passed to {cmd:twoway}{p_end} 51 | 52 | {synoptline} 53 | {p2colreset}{...} 54 | 55 | {marker description}{...} 56 | {title:Description} 57 | 58 | {pstd} 59 | {cmd: xteventplot} produces event-study plots after {cmd:xtevent}. {p_end} 60 | 61 | {marker options}{...} 62 | {title:Options} 63 | 64 | {dlgtab:Main} 65 | 66 | {phang} 67 | {opth suptreps(integer)} specifies the number of repetitions to calculate Montiel Olea and Plagborg-Møller (2019) sup-t confidence bands for 68 | the dynamic effects. The default is 10000. See {help xtevent}. 69 | 70 | {phang} 71 | {opt overlay(string)} creates overlay plots for trend extrapolation, instrumental variables estimation in presence of pre-trends, and constant 72 | policy effects over time. 73 | 74 | {phang2} {bf: overlay(trend)} overlays the event-time coefficients for the trajectory of the dependent variable and the extrapolated linear trend. 75 | {bf: overlay(trend)} is only available after {cmd: xtevent, trend(, saveoverlay)}. 76 | 77 | {phang2} {bf: overlay(iv)} overlays the event-time coefficients trajectory of the dependent variable and the proxy variable used to infer the 78 | trend of the confounder. {bf: overlay(iv)} is only available after {cmd: xtevent, proxy() proxyiv()}. 79 | 80 | {phang2} {bf: overlay(static)} overlays the event-time coefficients from the estimated model and the coefficients implied by a constant policy 81 | effect over time. These coefficients are calculated by (i) estimating a model where the policy affects the outcome contemporaneously and 82 | its effect is constant, (ii) obtaining predicted values of the outcome variable from this constant effects model, and (iii) regressing 83 | the predicted values on event-time dummy variables. 84 | 85 | {phang} 86 | {opt y} creates an event-study plot of the dependent variable in instrumental variables estimation. {opt y} is only available after 87 | {cmd: xtevent, proxy() proxyiv()}. 88 | 89 | {phang} 90 | {opt proxy} creates an event-study plot of the proxy variable in instrumental variables estimation. {opt proxy} is only available after 91 | {cmd: xtevent, proxy() proxyiv()}. 92 | 93 | {phang} 94 | {opt levels(numlist)} customizes the confidence level for the confidence intervals in the event-study plot. By default, xteventplot draws a standard confidence interval and a sup-t confidence band. 95 | {opt levels} allows different confidence levels for standard confidence intervals. For example, {opt levels(90 95)} draws both 90% and 95% level 96 | confidence intervals, along with a sup-t confidence band for Stata's default confidence level. 97 | 98 | {phang} 99 | {opt smpath([type , subopt])}} displays the "least wiggly" path through the Wald confidence region of the event-time coefficients. 100 | {opt type} determines the line type, which may be {opt scatter} or {opt line}. {opt smpath} is not allowed with {opt noci}. 101 | 102 | {phang} The following suboptions for {opt smpath} control the optimization process. Because of the nature of the 103 | optimization problem, optimization error messages 4 and 5 (missing derivatives) or 8 (flat regions) may be 104 | frequent. Nevertheless, the approximate results from the optimization should be close to the results that would be obtained with convergence of the optimization process. Modifying these optimization suboptions may improve optimization behavior. 105 | 106 | {phang2} 107 | {opt postwindow(scalar > 0)} sets the number of post event coefficient estimates to use for calculating the 108 | smoothest line. The default is to use all the estimates in the post event window. 109 | 110 | {phang2} 111 | {opt maxiter(integer)} sets the maximum number of inner iterations for optimization. The default is 100. 112 | 113 | {phang2} 114 | {opt maxorder(integer)} sets the maximum order for the polynomial smoothest line. Maxorder must be between 1 and 10. The default is 10. 115 | 116 | {phang2} 117 | {opt technique(string)} sets the optimization technique for the inner iterations of the smoothest-path optimization. 118 | "nr", "bfgs", "dfp", and combinations are allowed. See {help maximize}. The default is "nr 5 bfgs". 119 | 120 | {phang} 121 | {opt overidpre(integer)} changes the coefficients to be tested for the pre-trends overidentification test. 122 | The default is to test all pre-event coefficients. {opt overidpre(#1)} tests if the coefficients 123 | for the earliest #1 periods before the event are equal to 0, including the endpoints. 124 | For example, with a window of 3, {opt overidpre(2)} tests that the coefficients for event-times -4+ 125 | (the endpoint) and -3 are jointly equal to 0. #1 must be greater than 0. 126 | See {help xteventtest}. 127 | 128 | {phang} 129 | {opt overidpost(integer)} changes the coefficients to be tested for the leveling-off overidentification 130 | test. The default is to test that the rightmost coefficient and the previous coefficient are 131 | equal. {opt overidpost(#1)} tests if the coefficients for the latest 132 | #1 periods after the event are equal to each other, including the endpoints. For example, with a window of 3, 133 | {opt overidpost(3)} tests that the coefficients for event-times 4+ (the endpoint), 3, 134 | and 2 are equal to each other. #1 must be greater than 1. See {help xteventtest}. 135 | 136 | {dlgtab:Appearance} 137 | 138 | {phang} 139 | {opt noci} omits the display and calculation of both Wald and sup-t confidence bands. {opt noci} overrides {opt suptreps} if it is specified. 140 | {opt noci} is not allowed with {opt smpath}. 141 | 142 | {phang} 143 | {opt nosupt} omits the display and calculation of sup-t confidence bands. {opt nosupt} overrides {opt suptreps} if it is specified. 144 | 145 | {phang} 146 | {opt nozeroline} omits the display of the reference line at 0. Note that reference lines with different styles can be obtained by removing the 147 | default line with {opt nozeroline} and adding other lines with {opt yline}. See {help added_line_options}. 148 | 149 | {phang} 150 | {opt nonormlabel} suppresses the vertical-axis label for the mean of the dependent variable at 151 | event-time corresponding to the normalized coefficient. 152 | 153 | {phang} 154 | {opt noprepval} omits the display of the p-value for a test for pre-trends. By default, this is a 155 | Wald test for all the pre-event coefficients being equal to 0, unless {opt overidpre} is specified. 156 | 157 | {phang} 158 | {opt nopostpval} omits the display of the p-value for a test for effects leveling off. By default, 159 | this is a Wald test for the last post-event coefficients being equal, unless {opt overidpost} is specified. 160 | 161 | {phang} 162 | {opt scatterplotopts} specifies options to be passed to {cmd:scatter} for the coefficients' plot. 163 | 164 | {phang} 165 | {opt ciplotopts} specifies options to be passed to {cmd:rcap} for the confidence interval's 166 | plot. These options are disabled if {opt noci} is specified. 167 | 168 | {phang} 169 | {opt suptciplotopts} specifies options to be passed to {cmd:rcap} for the sup-t confidence 170 | band's plot. These options are disabled if {opt nosupt} is specified. 171 | 172 | {phang} 173 | {opt smplotopts} specifies options to be passed to {cmd:line} for the smoothest path through 174 | the confidence region plot. These options are only active if {opt smpath} is specified. 175 | 176 | {phang} 177 | {opt trendplotopts} specifies options to be passed to {cmd:line} for the extrapolated trend 178 | overlay plot. These options are only active if {opt overlay(trend)} is specified. 179 | 180 | {phang} 181 | {opt staticovplotopts} specifies options to be passed to {cmd:line} for the static effect overlay 182 | plot. These options are only active if {opt overlay(static)} is specified. 183 | 184 | {phang} 185 | {opt addplots} specifies additional plots to be overlaid to the event-study plot. 186 | 187 | {phang} 188 | {opt textboxoption} specifies options to be passed to the textbox of the pre-trend and leveling-off 189 | tests. These options are disabled if {opt noprepval} and {opt nopostval} are specified. See {help textbox_options}. 190 | 191 | {phang} 192 | {it: additional_options}: Additional options to be passed to {cmd:twoway}. See {help twoway}. 193 | 194 | {title:Examples} 195 | 196 | {hline} 197 | {pstd}Setup{p_end} 198 | {phang2}{cmd:. {stata webuse nlswork}}{p_end} 199 | {pstd}year variable has many missing observations{p_end} 200 | {pstd}Create a time variable that ignores these gaps{p_end} 201 | {phang2}{cmd:. {stata "by idcode (year): gen time=_n"}}{p_end} 202 | {phang2}{cmd:. {stata xtset idcode time}}{p_end} 203 | 204 | {hline} 205 | 206 | {pstd}Basic event study with clustered standard errors{p_end} 207 | {pstd}Impute policy variable assuming no unobserved changes{p_end} 208 | {phang2}{cmd:. {stata xtevent ln_wage age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure , pol(union) w(3) cluster(idcode) impute(nuchange)}} 209 | {p_end} 210 | 211 | {pstd}Simple plot{p_end} 212 | {phang2}{cmd:. {stata xteventplot}}{p_end} 213 | 214 | {pstd}Supress confidence intervals or sup-t confidence bands{p_end} 215 | {phang2}{cmd:. {stata xteventplot, noci}}{p_end} 216 | {phang2}{cmd:. {stata xteventplot, nosupt}}{p_end} 217 | 218 | {pstd}Plot smoothest path in confidence region{p_end} 219 | {phang2}{cmd:. {stata xteventplot, smpath(line)}}{p_end} 220 | {phang2}{cmd:. {stata xteventplot, smpath(line, technique(dfp))}}{p_end} 221 | 222 | {pstd}Adjust textbox options for the p-values of the pre-trend and leveling-off tests{p_end} 223 | {phang2}{cmd:. {stata xteventplot, textboxoption(color(blue) size(large))}}{p_end} 224 | 225 | {hline} 226 | 227 | {pstd}Freyaldenhoven, Hansen and Shapiro (2019) estimator with proxy variables{p_end} 228 | {phang2}{cmd:. {stata xtevent ln_wage age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure , pol(union) w(3) vce(cluster idcode) impute(nuchange) proxy(wks_work)}}{p_end} 229 | 230 | {pstd}Dependent variable, proxy variable, and overlay plots{p_end} 231 | {phang2}{cmd:. {stata xteventplot, y}}{p_end} 232 | {phang2}{cmd:. {stata xteventplot, proxy}}{p_end} 233 | {phang2}{cmd:. {stata xteventplot, overlay(iv)}}{p_end} 234 | {phang2}{cmd:. {stata xteventplot}}{p_end} 235 | 236 | {title:Authors} 237 | 238 | {pstd}Simon Freyaldenhoven, Federal Reserve Bank of Philadelphia.{p_end} 239 | simon.freyaldenhoven@phil.frb.org 240 | {pstd}Christian Hansen, University of Chicago, Booth School of Business.{p_end} 241 | chansen1@chicagobooth.edu 242 | {pstd}Jorge Pérez Pérez, Banco de México.{p_end} 243 | jorgepp@banxico.org.mx 244 | {pstd}Jesse Shapiro, Brown University.{p_end} 245 | jesse_shapiro_1@brown.edu 246 | 247 | {title:Support} 248 | 249 | {pstd}For support and to report bugs please email Jorge Pérez Pérez, Banco de México.{break} 250 | jorgepp@banxico.org.mx 251 | 252 | {pstd}{cmd:xtevent} can also be found on {browse "https://github.com/JMSLab/xtevent":Github}. 253 | 254 | -------------------------------------------------------------------------------- /xtevent/xteventtest.ado: -------------------------------------------------------------------------------- 1 | *! xteventtest.ado 3.1.0 July 11, 2024 2 | 3 | version 13 4 | 5 | program define xteventtest, rclass 6 | #d; 7 | syntax, 8 | [ 9 | coefs(numlist) /*Coefficients to test */ 10 | cumul /* Test sum of coefficients */ 11 | LINpretrend /* Test for linear pre-trend */ 12 | CONStanteff /* Test for constant effects */ 13 | TRend(numlist <0 integer min=1 max=1) /* Test significance of a linear trend from time a*/ 14 | overidpre(numlist >0 integer min=1 max=1) /* Test the leftmost coefficients as overid restriction */ 15 | overidpost(numlist >1 integer min=1 max=1) /* Test the rightmost coefficients as overid restriction */ 16 | overid /* Test overid restrictions from xtevent command */ 17 | allpre /* Test all pre-event coefficients */ 18 | allpost /* Test all post-event coefficients */ 19 | testopts(string) /* Options to be passed to -test- */ 20 | ] 21 | ; 22 | #d cr 23 | 24 | tempname dim est 25 | 26 | * Error checking 27 | 28 | if "`coefs'"!="" & ("`linpretrend'"!="" | "`constanteff'"!="" | "`trend'"!="" | "`overidpre'"!="" | "`overidpost'"!="" | "`overid'"!="" | "`allpre'"!="" | "`allpost'"!="" ) { 29 | di as err _n "option {bf:coefs} not allowed with options {bf:linpretrend}, {bf:constanteff}, {bf:trend}, {bf:overidpre}, {bf:overidpost}, {bf:overid}, {bf:allpre}, or {bf:allpost}" 30 | exit 301 31 | } 32 | 33 | if "`cumul'"!="" & ("`linpretrend'"!="" | "`constanteff'"!="" | "`trend'"!="" | "`overidpost'"!="" | "`overid'"!="") { 34 | di as err _n "option {bf:cumul} not allowed with options {bf:linpretrend}, {bf:constanteff}, {bf:trend}, {bf:overidpost}, or {bf:overid}" 35 | exit 301 36 | } 37 | 38 | if "`linpretrend'"!="" & ("`constanteff'"!="" | "`trend'"!="" | "`overidpre'"!="" | "`overidpost'"!="" | "`overid'"!="" | "`allpre'"!="" | "`allpost'"!="") { 39 | di as err _n "option {bf:linpretrend} not allowed with options {bf:constanteff}, {bf:trend}, {bf:overidpre}, {bf:overidpost}, {bf:overid}, {bf:allpre}, or {bf:allpost}" 40 | exit 301 41 | } 42 | 43 | if "`constanteff'"!="" & ("`trend'"!="" | "`overidpre'"!="" | "`overidpost'"!="" | "`overid'"!="" | "`allpre'"!="" | "`allpost'"!="" ) { 44 | di as err "option {bf:constanteff} not allowed with options {bf:trend}, {bf:overidpre}, {bf:overidpost}, {bf:overid}, {bf:allpre}, or {bf:allpost}" 45 | exit 301 46 | } 47 | 48 | if "`trend'"!="" & ("`overidpre'"!="" | "`overidpost'"!="" | "`overid'"!="" | "`allpre'"!="" | "`allpost'"!="") { 49 | di as err _n "option {bf:trend} not allowed with options {bf:overidpre}, {bf:overidpost}, {bf:overid}, {bf:allpre}, or {bf:allpost}" 50 | exit 301 51 | } 52 | 53 | if "`overidpre'"!="" & ("`overid'"!="" | "`allpre'"!="" | "`allpost'"!="" ) { 54 | di as err _n "option {bf:overidpre} not allowed with options {bf:overid}, {bf:allpre}, or {bf:allpost}" 55 | exit 301 56 | } 57 | 58 | if "`overidpost'"!="" & ("`overid'"!="" | "`allpre'"!="" | "`allpost'"!="" ) { 59 | di as err _n "option {bf:overidpost} not allowed with options {bf:overid}, {bf:allpre}, or {bf:allpost}" 60 | exit 301 61 | } 62 | 63 | loc names = e(names) 64 | 65 | *if previous trend adjustment, remove the endpoints from the stored list of event-time dummy variables 66 | if "`=e(trend)'"=="trend"{ 67 | loc lendp "_k_eq_m`=-`=`e(lwindow)'-1''" 68 | loc rendp "_k_eq_p`=`e(rwindow)'+1'" 69 | loc names: subinstr local names "`lendp'" "", all 70 | loc names: subinstr local names "`rendp'" "", all 71 | } 72 | 73 | * Turn overid into overidpre and overidpost 74 | if "`overid'"!="" { 75 | if e(pre)==. { 76 | * Default: All pre, two last 77 | loc i=0 78 | foreach w in `names' { 79 | loc wordp: subinstr local w "_k_eq_p" "", all 80 | if "`wordp'"!="`w'" continue 81 | loc ++i 82 | } 83 | loc overidpre = `i' 84 | loc wordt: word count `names' 85 | loc overidpost = 2 86 | } 87 | else { 88 | loc overidpre=e(overidpre) 89 | loc overidpost=e(overidpost)+1 90 | } 91 | } 92 | 93 | *Change the coefficients to test pretrends if there was trend adjustment with ols method and requested number of coefficients is greater than the number of available ones. 94 | if "`=e(trendmethod)'"=="ols" & ("`overid'"!="" | "`overidpre'"!="") { 95 | *number of available pre-event coefficients 96 | loc j=0 97 | foreach w in `names' { 98 | if regexm("`w'","_k_eq_m") loc ++j 99 | } 100 | 101 | if `j' == 0 { 102 | di as err _n "Cannot test pretrends if there are 0 available pre-event coefficients after trend adjustment with OLS method." 103 | exit 103 104 | } 105 | loc checkadj = 0 106 | if "`overid'"!="" & `overidpre'>`j' { 107 | di as text _n "{bf:overidpre} value from {cmd: xtevent} is `e(overidpre)', but available pre-event coefficients are {bf:`j'} after trend adjustment with OLS method." 108 | loc checkadj 1 109 | } 110 | if "`overidpre'"!="" & "`overid'"=="" & `overidpre'>`j'{ 111 | di as text _n "You requested pretrend test with the first {bf:`overidpre'} coefficients, but there are only {bf:`j'} available pre-event coefficients after trend adjustment with OLS method." 112 | loc checkadj 1 113 | } 114 | if `checkadj'==1 { 115 | di as text _n "The pretrend test will account only the {bf:`j'} available pre-event coefficients." 116 | loc overidpre=`j' 117 | } 118 | } 119 | 120 | * If overidpre, take the earlier coefs 121 | if "`overidpre'"!="" { 122 | di as txt _n "Overidentification test for pretrends: `overidpre' pre-event coefficients are 0" 123 | loc c "" 124 | forv i=1(1)`overidpre' { 125 | loc cplus: word `i' of `names' 126 | loc c "`c' `cplus'" 127 | if "`overid'"!="" loc cpre "`c'" 128 | } 129 | loc cbreak: subinstr local c "_p" "", all 130 | if "`c'"!="`cbreak'" { 131 | di as err _n "Cannot test more pre-event coefficients than were estimated" 132 | exit 199 133 | } 134 | if "`c'"=="" { 135 | di as err _n "No pre-event coefficients to test" 136 | exit 199 137 | } 138 | } 139 | 140 | * If overidpost, take the latter coefs and test equality 141 | if "`overidpost'"!="" { 142 | di as txt _n "Overidentification test for effects leveling off: `overidpost' last post-event coefficients are equal" 143 | * allow overidpre here 144 | if "`overidpre'"=="" loc c "" 145 | else loc c "(`c')" 146 | loc cplus "" 147 | loc wordt: word count `names' 148 | forv i=1(1)`overidpost' { 149 | loc cadd: word `=`wordt'-`i'+1' of `names' 150 | loc cplus "`cplus' `cadd'" 151 | } 152 | loc cplus = strltrim("`cplus'") 153 | loc cplus : subinstr local cplus " " "=", all 154 | if "`overid'"!="" loc cpost "`cplus'" 155 | if "`overidpre'"!="" loc cplus "(`cplus')" 156 | loc c "`c' `cplus'" 157 | loc cbreak: subinstr local cplus "_m" "", all 158 | if "`cplus'"!="`cbreak'" { 159 | di as err _n "Cannot test more post-event coefficients than were estimated" 160 | exit 199 161 | } 162 | if "`cplus'"=="" { 163 | di as err _n "No post-event coefficients to test" 164 | exit 199 165 | } 166 | } 167 | * Check that coefs were estimated and gather 168 | if "`coefs'"!="" { 169 | loc c "" 170 | foreach j in `coefs' { 171 | if `j'<0 { 172 | loc jj = abs(`j') 173 | loc cplus "_k_eq_m`jj'" 174 | } 175 | else if `j'>=0 loc cplus "_k_eq_p`j'" 176 | loc match = 0 177 | foreach name in `names' { 178 | if "`cplus'"=="`name'" loc ++ match 179 | } 180 | if `match'!=0 { 181 | loc c "`c' `cplus'" 182 | } 183 | else { 184 | di as err _n "Coefficient for event-time `j' not found" 185 | exit 301 186 | } 187 | } 188 | } 189 | 190 | * Gather pre estimates if all pre 191 | if "`allpre'"=="allpre" { 192 | di as txt _n "Test for all pre-event coefficients = 0" 193 | loc c "" 194 | foreach j in `names' { 195 | loc sub : subinstr local j "_k_eq_m" "", all 196 | if "`sub'"!="`j'" loc c "`c' `j'" 197 | } 198 | } 199 | * Gather post estimates if all post 200 | if "`allpost'"=="allpost" { 201 | di as txt _n "Test for all post-event coefficients = 0" 202 | * Allow allpre combination 203 | if "`allpre'"!="allpre" loc c "" 204 | foreach j in `names' { 205 | loc sub : subinstr local j "_k_eq_p" "", all 206 | if "`sub'"!="`j'" loc c "`c' `j'" 207 | } 208 | } 209 | 210 | * Gather post estimates with equal signs if constanteff 211 | if "`constanteff'"=="constanteff" { 212 | loc c "" 213 | di as txt _n "Test for constant post-event coefficients" 214 | foreach j in `names' { 215 | loc sub : subinstr local j "_k_eq_p" "", all 216 | if "`sub'"!="`j'" loc c "`c' `j'" 217 | } 218 | loc c = strltrim("`c'") 219 | loc c : subinstr local c " " "=", all 220 | } 221 | 222 | * Accumulate if cumul 223 | if "`cumul'"=="cumul" { 224 | di as txt _n "Test sums of coefficients" 225 | loc c: subinstr local c " " "+",all 226 | loc c=substr("`c'",2,.) 227 | loc c = "`c' = 0" 228 | } 229 | 230 | 231 | 232 | * Linear pre-trend specification test 233 | 234 | if "`linpretrend'"!="" { 235 | loc tt "" 236 | foreach j in `names' { 237 | loc sub : subinstr local j "_k_eq_m" "", all 238 | if "`sub'"!="`j'" loc tt "`tt' `j'" 239 | } 240 | loc d : word count `tt' 241 | scalar `dim' = `d' 242 | if `dim'<2 { 243 | di as err _n "Cannot test linear pre-trend with less than 2 pre-event coefficients" 244 | exit 199 245 | } 246 | mata: trendtest("`dim'") 247 | * scalar li Q 248 | 249 | loc df = `dim' - 2 250 | loc p = chi2tail(`df',Q) 251 | 252 | loc df: di %3.0f `df' 253 | loc q : di %8.2f `=Q' 254 | loc p: di %8.4f `p' 255 | 256 | di as txt _n "Specification test for linear pre-trend" 257 | di as txt _col(12) "chi2(`df') =" as res `q' 258 | di as txt _col(10) "Prob > chi2 =" as res `p' 259 | } 260 | 261 | * Trend test 262 | if "`trend'"!="" { 263 | loc trend "trend(`trend', method(ols))" 264 | loc cmd = e(cmdline) 265 | loc cmd = regexr("`cmd'","trend\(.*\)","") 266 | _estimates hold `est' 267 | *di as txt _n "Estimating trend using {cmd:xtevent, trend(, method(ols))}" 268 | di as txt _n "Estimating trend by OLS" 269 | qui `cmd' `trend' 270 | di as txt _n "Significance test for linear trend" 271 | test _ttrend 272 | _estimates unhold `est' 273 | } 274 | 275 | 276 | * Test 277 | if "`overid'"!="" { 278 | di as txt _n "Overidentification test for pretrends: `overidpre' pre-event coefficients are 0" 279 | test `cpre', `testopts' 280 | returntest pre 281 | return add 282 | di as txt _n "Overidentification test for effects leveling off: `overidpost' last post-event coefficients are equal" 283 | test `cpost', `testopts' 284 | returntest post 285 | return add 286 | di as txt _n "Joint overidentification test" 287 | test (`cpre') (`cpost'), `testopts' 288 | returntest prepost 289 | return add 290 | } 291 | else { 292 | if "`c'"!="" test `c', `testopts' 293 | return add 294 | } 295 | 296 | end 297 | 298 | program define returntest, rclass 299 | args stub 300 | foreach x in p F df_r chi2 ss rss drop { 301 | if r(`x')!=. return scalar `stub'_`x' = r(`x') 302 | } 303 | end 304 | 305 | 306 | mata: 307 | 308 | mata clear 309 | 310 | void trendtest(dim) 311 | { real matrix b,V,Vinv,X,ab,eta 312 | real scalar Q 313 | 314 | dim=st_numscalar(dim) 315 | b=st_matrix("e(b)")'[1..dim,1] 316 | V=st_matrix("e(V)")[1..dim,1..dim] 317 | Vinv=cholinv(V) 318 | X = (J(dim,1,1),range(1,dim,1)) 319 | ab = cholinv(X'*Vinv*X)*(X'*Vinv*b) 320 | eta = b - (X*ab) 321 | Q = eta'*Vinv*eta 322 | st_numscalar("Q",Q) 323 | } 324 | 325 | end 326 | 327 | 328 | 329 | 330 | -------------------------------------------------------------------------------- /xtevent/xteventtest.sthlp: -------------------------------------------------------------------------------- 1 | {smcl} 2 | {* *! version 3.1.0 July 11 2024}{...} 3 | {cmd:help xteventtest} 4 | {hline} 5 | 6 | {title:Title} 7 | 8 | {phang} 9 | {bf:xteventtest} {hline 2} Hypothesis Testing after Panel Event Study Estimation 10 | 11 | 12 | {marker syntax}{...} 13 | {title:Syntax} 14 | 15 | {pstd} 16 | 17 | {p 8 17 2} 18 | {cmd:xteventtest} 19 | {cmd:,} 20 | [{it:options}] 21 | 22 | {synoptset 25 tabbed}{...} 23 | {synopthdr} 24 | {synoptline} 25 | {syntab:Main} 26 | {p2coldent: {opth coefs(numlist)}} coefficients to be tested{p_end} 27 | {p2coldent: {opt cumul}} test sum of coefficients{p_end} 28 | {p2coldent: {opt allpre}} test all pre-event coefficients{p_end} 29 | {p2coldent: {opt allpost}} test all post-event coefficients{p_end} 30 | {p2coldent: {opt lin:pretrend}} test for a linear pretrend{p_end} 31 | {p2coldent: {opt tr:end(#1)}} tests for a linear trend from time period #1 before treatment{p_end} 32 | {p2coldent: {opt cons:tanteff}} test for constant post-event coefficients{p_end} 33 | {p2coldent: {opt overid}} test overidentifyng restrictions for pretrends and effects leveling off{p_end} 34 | {p2coldent: {opth overidpre(integer)}} test overidentifyng restriction for pretrends{p_end} 35 | {p2coldent: {opth overidpost(integer)}} test overidentifyng restriction for effects leveling off{p_end} 36 | {p2coldent: {opt testopts(string)}} options to be passed to {cmd:test}{p_end} 37 | {synoptline} 38 | {p2colreset}{...} 39 | 40 | {marker description}{...} 41 | {title:Description} 42 | 43 | {pstd} 44 | {cmd: xteventtest} tests common hypotheses after {cmd:xtevent}. {p_end} 45 | 46 | 47 | {marker options}{...} 48 | {title:Options} 49 | 50 | {dlgtab:Main} 51 | 52 | {phang} 53 | {opth coefs(numlist)} specifies a numeric list of event-times to be tested. These are tested to be equal to 0 jointly, unless otherwise requested in {cmd:testopts()}. 54 | 55 | {phang} 56 | {opt cumul} requests a test of equality to 0 for the sum of every coefficient for each event-time in {cmd:coefs()}. 57 | 58 | {phang} 59 | {opt allpre} tests that all pre-event coefficients are equal to 0. With {cmd:cumul}, it tests that the sum of all pre-event coefficients is equal to 0. 60 | 61 | {phang} 62 | {opt allpost} tests that all post-event coefficients are equal to 0. With {cmd:cumul}, it tests that the sum of all post-event coefficients is equal to 0. 63 | 64 | {phang} 65 | {opt linpretrend} requests a specification test to see if the coefficients follow a linear trend before the event. 66 | 67 | {phang} 68 | {opt trend(#1)} tests for a linear trend from time period #1 before the policy change. It uses {opt xtevent, trend(#1, method(ols))} to estimate the trend. #1 must be less than -1. 69 | 70 | {phang} 71 | {opt constanteff} tests that all post-event coefficients are equal. 72 | 73 | {phang} 74 | {opt overid} tests overidentifying restrictions: a test for pre-trends and a test for events leveling-off. The periods to be tested are those 75 | used in the {cmd: xtevent} call. See {help xtevent}. 76 | 77 | {phang} 78 | {opth overidpre(#1)} tests the pre-trends overidentifying restriction. It tests that the coefficients for the earliest #1 periods before the event 79 | are equal to 0, including the endpoints. For example, with a window of 3, {opt overidpre(2)} tests that the coefficients for event-times -4+ 80 | (the endpoint) and -3 are jointly equal to 0. #1 must be greater than 0. 81 | 82 | {phang} 83 | {opth overidpost(#1)} tests the effects leveling off overidentifying restriction. It tests that the coefficients for the latest #1 periods after 84 | the event are equal, including the endpoints. For example, with a window of 3, {opt overidpost(3)} tests that the coefficients for event-times 85 | 4+ (the endpoint), 3, and 2 are equal to each other. #1 must be greater than 1. 86 | 87 | {phang} 88 | {opt testopts(string)} specifies options to be passed to {cmd:test}. See {help test}. 89 | 90 | {title:Examples} 91 | 92 | {hline} 93 | {pstd}Setup{p_end} 94 | {phang2}{cmd:. {stata webuse nlswork}}{p_end} 95 | {phang2}{cmd:. {stata xtset idcode year}}{p_end} 96 | 97 | {hline} 98 | {pstd}Basic event study with clustered standard errors 99 | Impute policy variable assuming no unobserved changes{p_end} 100 | {phang2}{cmd:. {stata xtevent ln_w age c.age#c.age ttl_exp c.ttl_exp#c.ttl_exp tenure , pol(union) w(3) cluster(idcode) impute(nuchange)}} 101 | {p_end} 102 | 103 | {pstd}Test some coefficients to be equal to 0 jointly {p_end} 104 | {phang2}{cmd:. {stata xteventtest, coefs(1 2)}}{p_end} 105 | 106 | {pstd}Test that all pre-event coefficients are equal to 0 {p_end} 107 | {phang2}{cmd:. {stata xteventtest, allpre}}{p_end} 108 | 109 | {pstd}Test that the sum of all pre-event coefficients is equal to 0 {p_end} 110 | {phang2}{cmd:. {stata xteventtest, allpre cumul}}{p_end} 111 | 112 | {pstd}Test whether the coefficients before the event follow a linear trend {p_end} 113 | {phang2}{cmd:. {stata xteventtest, linpretrend}}{p_end} 114 | 115 | {pstd}Tests that the coefficients for the earliest 2 periods before the event are equal to 0{p_end} 116 | {phang2}{cmd:. {stata xteventtest, overidpre(2)}}{p_end} 117 | 118 | 119 | {marker saved}{...} 120 | {title:Saved Results} 121 | 122 | {pstd} 123 | {cmd:xteventtest} stores the following in {cmd:r()}: 124 | 125 | {synoptset 15 tabbed}{...} 126 | {p2col 5 15 19 2: Scalars}{p_end} 127 | {synopt:{cmd:r(p)}}two-sided p-value{p_end} 128 | {synopt:{cmd:r(F)}}F statistic{p_end} 129 | {synopt:{cmd:r(df)}}test constraints degrees of freedom{p_end} 130 | {synopt:{cmd:r(df_r)}}residual degrees of freedom{p_end} 131 | {synopt:{cmd:r(dropped_i)}}index of {it:i}th constraint dropped{p_end} 132 | {synopt:{cmd:r(chi2)}}chi-squared{p_end} 133 | {synopt:{cmd:r(drop)}}{cmd:1} if constraints were dropped, {cmd:0} otherwise{p_end} 134 | 135 | 136 | {synoptset 15 tabbed}{...} 137 | {p2col 5 15 19 2: Macros}{p_end} 138 | {synopt:{cmd:r(mtmethod)}}method of adjustment for multiple testing. This macro is inherited from {cmd:test}{p_end} 139 | 140 | {synoptset 15 tabbed}{...} 141 | {p2col 5 15 19 2: Matrices}{p_end} 142 | {synopt:{cmd:r(mtest)}}multiple test results. This matrix is inherited from {cmd:test}{p_end} 143 | 144 | 145 | {title:Authors} 146 | 147 | {pstd}Simon Freyaldenhoven, Federal Reserve Bank of Philadelphia.{p_end} 148 | simon.freyaldenhoven@phil.frb.org 149 | {pstd}Christian Hansen, University of Chicago, Booth School of Business.{p_end} 150 | chansen1@chicagobooth.edu 151 | {pstd}Jorge Pérez Pérez, Banco de México.{p_end} 152 | jorgepp@banxico.org.mx 153 | {pstd}Jesse Shapiro, Brown University.{p_end} 154 | jesse_shapiro_1@brown.edu 155 | 156 | {title:Support} 157 | 158 | {pstd}For support and to report bugs please email Jorge Pérez Pérez, Banco de México.{break} 159 | jorgepp@banxico.org.mx 160 | 161 | {pstd}{cmd:xtevent} can also be found on {browse "https://github.com/JMSLab/xtevent":GitHub}. 162 | 163 | 164 | --------------------------------------------------------------------------------