// Companion code for Brodeur, Cook and Heyes (2020) Speccheck
// module for specification checking
// this code comes with no warranties, but we welcome your questions at ncook@uottawa.ca

*** Speccheck

capture program drop speccheck

program speccheck // define the command's call

quietly {

version 11.0

syntax varlist(min=2 max=20 numeric fv ts) [if] [, method(string) absorb(string) xt(string) vce(string) nocon(string) always(string)] // require what gets fed into it be numeric, allow factor variables, time series

args yvar xvar xvar1 xvar2  // first argument is yvar

local pos = strpos("`varlist'", " ") + 1 // position to cut user input into yvar xvars

local xvars = substr("`varlist'", `pos', .) // cut varlist into yvar xvars

local pos = strpos("`xvars'", " ") + 1 // position to cut xvars into xvar xvars

local xvars = substr("`xvars'", `pos', .) // cut xvars into xvar xvars

if "`always'" != "1" & "`always'" != "2"  {
local xvar1 = ""
local xvar2 = ""
}
if "`always'" == "1" {
local xvar2 = ""
}
if "`always'" == "2" {
}

if "`always'" == "1" {

	local pos = strpos("`xvars'", " ") + 1 // position to cut xvars into xvar xvars

	local xvars = substr("`xvars'", `pos', .) // cut xvars into xvar xvars

}

if "`always'" == "2" {

	local pos = strpos("`xvars'", " ") + 1 // position to cut xvars into xvar xvars

	local xvars = substr("`xvars'", `pos', .) // cut xvars into xvar xvars

	local pos = strpos("`xvars'", " ") + 1 // position to cut xvars into xvar xvars

	local xvars = substr("`xvars'", `pos', .) // cut xvars into xvar xvars

}

*reg `varlist'

di "`yvar'"

di "`xvars'"

di "`if'"

di "`vce'"

*ssc install tuples

tuples `xvars', di
global tuple0 " "

di "`tuple1'"

local ticker 0

capture drop betas
gen betas = .
capture drop tstats
gen tstats = .
capture drop stds
gen stds = .
capture drop numcoeffs
gen numcoeffs = .
capture drop numobs
gen numobs = .

local wordcount = wordcount("`xvars'")
di `wordcount'

local n = (2^`wordcount')-1
local numruns = (2^`wordcount')-1


forval i = 0(1)`n' {
di `i'
di "`tuple`i''"


if "`method'" == "" | "`method'" == "reg" {
	local method = "reg"
	reg `yvar' `xvar' `xvar1' `xvar2' `tuple`i'' `if' , vce(`vce')
	}
else if "`method'" == "areg" {
	areg `yvar' `xvar' `xvar1' `xvar2'   `tuple`i'' `if', absorb(`absorb') `vce'
	}
else if "`method'" == "xtreg" {
	xtreg `yvar' `xvar' `xvar1' `xvar2'   `tuple`i'' `if', `xt' `vce'
	}

	local ticker = `ticker' + 1
	di `ticker'

	matrix B =  e(b)
	local b B[1,1]
	replace betas = `b' in `ticker'

	matrix V =  e(V)
	local t B[1,1]/(V[1,1]^0.5)
	replace tstats = `t' in `ticker'

	local std (V[1,1]^0.5)
	replace stds = `std' in `ticker'

	local wordtuple = wordcount("`tuple`i''")
	replace numcoeffs = `wordtuple' in `ticker'
	replace numobs = `e(N)'
}

replace tstats = abs(tstats)
*replace betas = abs(betas)

if "`nocon'" == "Yes" {

local numruns = (2^`wordcount')-1

hist tstats if tstats!=0 & _n>2 , width() start(0) xline(1.96) title("t-Curve") xtitle("t-Statistic") saving(1.gph, replace) graphregion(color(white)) scheme(s1mono) percent color(none) lwidth(medium) lcolor(black) nodraw

hist betas if tstats!=0 & _n>2 , width() start() xline(0) title("Effect Curve") xtitle("Effect Size") saving(2.gph, replace) graphregion(color(white)) scheme(s1mono) percent color(none) lwidth(medium) lcolor(black) nodraw

graph box tstats if tstats!=0 & _n>2 , over(numcoeffs) yline() title("t-Statistic by # of controls") ytitle("t-Statistic") saving(3.gph, replace) graphregion(color(white)) ylab(, nogrid) scheme(s1mono) marker(1, msize(small) mlcolor(none) mfcolor(black)) box(1, fcolor(white) lcolor(black)) medline(lcolor(black) lwidth(medthin)) graphregion(color(white)) nodraw

graph box betas if tstats!=0 & _n>2 , over(numcoeffs) yline(0) title("Effect Size by # of controls") ytitle("Effect Size") saving(4.gph, replace) graphregion(color(white)) ylab(, nogrid) scheme(s1mono) marker(1, msize(small) mlcolor(none) mfcolor(black)) box(1, fcolor(white) lcolor(black)) medline(lcolor(black) lwidth(medthin)) graphregion(color(white)) nodraw

}
else {

local numruns = (2^`wordcount')

hist tstats if tstats!=0  , width() start(0) xline(1.96) title("t-Curve") xtitle("t-Statistic") saving(1.gph, replace) graphregion(color(white)) scheme(s1mono) percent color(none) lwidth(medium) lcolor(black) nodraw

hist betas if tstats!=0  , width() start() xline(0) title("Effect Curve") xtitle("Effect Size") saving(2.gph, replace) graphregion(color(white)) scheme(s1mono) percent color(none) lwidth(medium) lcolor(black) nodraw

graph box tstats if tstats!=0  , over(numcoeffs) yline() title("t-Statistic by # of controls") ytitle("t-Statistic") saving(3.gph, replace) graphregion(color(white)) ylab(, nogrid) scheme(s1mono) marker(1, msize(small) mlcolor(none) mfcolor(black)) box(1, fcolor(white) lcolor(black)) medline(lcolor(black) lwidth(medthin)) graphregion(color(white)) nodraw

graph box betas if tstats!=0  , over(numcoeffs) yline(0) title("Effect Size by # of controls") ytitle("Effect Size") saving(4.gph, replace) graphregion(color(white)) ylab(, nogrid) scheme(s1mono) marker(1, msize(small) mlcolor(none) mfcolor(black)) box(1, fcolor(white) lcolor(black)) medline(lcolor(black) lwidth(medthin)) graphregion(color(white)) nodraw

}

sum numobs if numobs != .
local my1 = r(N)
local my2 = r(mean)
local my3 = r(sd)

graph combine 1.gph 2.gph 3.gph 4.gph ,   graphregion(color(white))  ///
caption( ///
"`method' `varlist' `if', always(`always')" ///
"Displaying estimates for: `xvar' " ///
"Always included: `xvar1' `xvar2'" ///
"Total Permutations: `numruns'" ///
"Sample Size Mean: `my2' S.D.: `my3'" , size(*0.6))
*graph export speccheck.png, replace  width(2000)


di "`yvar'"

di "`xvars'"

di "`if'"

di "`vce'"

}

end

**** Examples

* ssc install tuples

* bcuse wagepan, clear

* speccheck lwage educ union married black exper expersq , nocon(Yes)

*speccheck lwage educ union married black exper expersq if south==1

*speccheck lwage educ union married black exper expersq , vce(robust)

*speccheck lwage educ union married black exper expersq , vce(cluster nr)

*speccheck lwage educ union married black exper expersq , absorb(nr) method(areg)

*xtset nr year
*speccheck lwage educ union married black exper expersq , xt( ) method(xtreg)