forked from borusyak/shift-share
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ssaggregate.ado
174 lines (154 loc) · 5.08 KB
/
ssaggregate.ado
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
*! version 1.2.2, jan2020
*! Kirill Borusyak ([email protected]), Peter Hull ([email protected]), Xavier Jaravel ([email protected])
program def ssaggregate
version 11.0
syntax varlist [if] [in] [aw iw fw pw/], n(string) s(string) [t(varlist) Controls(string asis) Absorb(string asis) ADDMissing ///
STRring /// wide syntax only
l(varlist) SFILEname(string)] // long syntax only
qui {
local wideformat = (`"`sfilename'"'=="")
* Check syntax
if (`wideformat' & ("`l'"!="")) {
di as error "Option l may not be used with shares in wide format"
exit 198
}
if ((!`wideformat') & ("`string'"!="")) {
di as error "Option string may not be used with shares in long format"
exit 198
}
if (strtrim(`"`controls'"')!="" & !regexm(`"`controls'"',`"""')) {
di as error "Each set of controls must be specified in quotes"
exit 198
}
if (strtrim(`"`absorb'"')!="" & !regexm(`"`absorb'"',`"""')) {
di as error "Each set of fixed effects must be specified in quotes"
exit 198
}
if ("`weight'"=="") local ww = ""
else local ww = `"[`weight' = `exp']"'
* Prepare main file
marksample touse
keep if `touse'
if (`wideformat') {
tempvar l
gen `l' = _n
}
tempfile main
save `"`main'"'
* Reshape to get long shares file
if (`wideformat') {
keep `l' `t' `s'*
reshape long `s', i(`l' `t') j(`n') `string'
tempfile sfilename
save `"`sfilename'"'
}
* Deal with missing industry shares
use `"`sfilename'"', clear
collapse (sum) `s', by(`l' `t')
replace `s' = 1-`s'
* If sum of shares varies, will verify S_l is controlled for
sum `s'
if (r(sd)>10^-5) {
tempvar s0
rename `s' `s0'
local checkcontrols = 1
tempfile missingind
save `"`missingind'"'
rename `s0' `s'
}
else local checkcontrols = 0
* Add missing industry
if ("`addmissing'"!="") {
replace `s'=0 if inrange(`s',-10^-5,0) // if the sum of shares is one, it can look like 1.00001 => creates negative weights
append using `"`sfilename'"'
tempfile newshares
save `"`newshares'"', replace
local sfilename = `"`newshares'"'
}
use `"`main'"', clear
if (`checkcontrols') merge 1:1 `l' `t' using `"`missingind'"', assert(2 3) keep(3) nogen
* Residualize outcomes on each set of controls
tempvar resid
if (`"`controls'"'!="") {
tokenize `"`controls'"'
if (`"`51'"'!="") {
noi di as error "More than 50 control sets are not allowed"
exit 198
}
local ccount = 50
while (`"``ccount''"'=="" & `ccount'>0) {
local ccount = `ccount' - 1
}
if (`ccount'>0) {
forvalues index = 1/`ccount' {
local cset`index' = `"``index''"'
}
}
}
else local ccount = 0
if (`"`absorb'"'!="") {
tokenize `"`absorb'"'
if (`"`51'"'!="") {
noi di as error "More than 50 fixed effect sets are not allowed"
exit 198
}
local acount = 50
while (`"``acount''"'=="" & `acount'>0) {
local acount = `acount' - 1
}
if (`acount'>0) {
forvalues index = 1/`acount' {
local aset`index' = `"``index''"'
}
}
}
else local acount = 0 // # of absorb sets
local cacount = max(`acount',`ccount',1) // if no controls or FE, still have one iteration with simple demeaning
local vars = ""
forvalues index = 1/`cacount' {
if (`checkcontrols') { // Make sure S_l is perfectly predicted by each set of controls, then no problem
if (`"`aset`index''"'=="") reg `s0' `cset`index''
else reghdfe `s0' `cset`index'', absorb(`aset`index'')
if (e(r2)<0.9999) {
if ("`addmissing'"=="") {
noi di as error "WARNING: You are in the incomplete share case (the sum of exposure shares varies)"
noi di as error "and you have not controlled for the sum of shares (in control set `index')."
noi di as error "You should either include the missing industry or (better in most cases) add the sum-of-share"
noi di as error "control. Otherwise the shock-level IV coefficient does not equal the shift-share IV."
}
else {
noi di "Warning: You are in the incomplete share case (the sum of exposure shares varies)"
noi di "and you have not controlled for the sum of shares (in control set `index')."
noi di "Keep in mind that this imposes the assumption that shocks are mean-zero."
}
drop `s0'
local checkcontrols = 0
}
}
foreach v of varlist `varlist' {
if (`"`aset`index''"'=="") {
reg `v' `cset`index'' `ww'
predict `resid', resid
}
else reghdfe `v' `cset`index'' `ww', absorb(`aset`index'') resid(`resid') keepsing
if (`cacount'>1) {
gen `v'`index' = `resid'
local vars `vars' `v'`index'
}
else { // if <=1 set of controls & FE, don't append 1 to variable names
replace `v' = `resid'
local vars `vars' `v'
}
drop `resid'
}
}
* Merge with the shares
if (`wideformat') drop `s'*
merge 1:m `l' `t' using `"`sfilename'"', assert(2 3) keep(3) nogen
* Collapse to industry level
if ("`weight'"!="") replace `s' = `s' * (`exp')
collapse (rawsum) s_n=`s' (mean) `vars' [aw=`s'], by(`n' `t') fast
sum s_n
replace s_n = s_n/r(sum)
}
end