*! evstudydd: estimate event studies in D-i-D settings using the reghdfe or ppmlhdfe packages
*! It requires reghdfe and ppmlhdfe to be installed.
*! Version: February, 2025
*! Authors: Adrien Matray, Pablo E. Rodriguez

qui{

cap program drop evstudydd
program define evstudydd
version 13.0
    syntax varlist(numeric min=2 max=2) [if] [in], Treated(name) SHOCKTime(string) Period(string) CLuster(string) FE(string) Name(string) [Weight(name)] [CONTrols(string)] ///
    [Estimator(string)] [INTeraction(varname)]
    
    * Add top-level preserve motivated by the creation of new variables and to avoid erasing already existing variables 
    preserve

    * Temporarily capture variables from varlist
    tokenize `varlist'
    local Y `1'
    local t `2'
    
    * Validate options
    if "`treated'" == "" {
        di as err "You must specify a treated variable"
        exit 198
    }
    if "`shocktime'" == "" {
        di as err "You must specify the variable or numeric value for the time of the shock"
        exit 198
    }
    if "`period'" == "" {
        di as err "You must specify the period option in the format lower_bound upper_bound"
        exit 198
    }
    if "`name'" == "" {
        di as err "You must specify the name option to store .dta dataset"
        exit 198
    }
    
    * Parse the period option
    tokenize `period'
    local before `1'
    local after `2'
    
    * Check if `shockperiod` is a variable or a number
    capture confirm variable `shocktime'
    if _rc == 0 {
        local is_var = 1
    }
    else {
        local is_var = 0
    }
    
    * Make sure no DiD_evstudydd variables are in the sample
    cap drop DiD_evstudydd* 
    
    * Create coefficients
    if `is_var' {
        * If shocktime is a variable
        gen DiD_evstudydd0 = (`t' < `shocktime' - `before') * (`treated' == 1)
        if "`interaction'" != "" {
            gen DiD_evstudydd0_int = DiD_evstudydd0 * `interaction'
        }
        
        local T = `before' + `after' + 1
        forvalues time_evstudy = 1(1)`T' {
            if `time_evstudy' != `before' + 1 {
                qui gen DiD_evstudydd`time_evstudy' = (`t' == `shocktime' + `time_evstudy' - `before' - 1) * (`treated')
                if "`interaction'" != "" {
                    qui gen DiD_evstudydd`time_evstudy'_int = DiD_evstudydd`time_evstudy' * `interaction'
                }
            }
        }
        
        gen DiD_evstudyddpost = (`t' > `shocktime' + `after') * (`treated')
        if "`interaction'" != "" {
            gen DiD_evstudyddpost_int = DiD_evstudyddpost * `interaction'
        }
    }
    else {
        * If shocktime is a number
        local shockperiod_val = real("`shocktime'")
        gen DiD_evstudydd0 = (`t' < `shockperiod_val' - `before') * (`treated')
        if "`interaction'" != "" {
            gen DiD_evstudydd0_int = DiD_evstudydd0 * `interaction'
        }
        
        local T = `before' + `after' + 1
        forvalues time_evstudy = 1(1)`T' {
            if `time_evstudy' != `before' + 1 {
                qui gen DiD_evstudydd`time_evstudy' = (`t' == `shockperiod_val' + `time_evstudy' - `before' - 1) * (`treated')
                if "`interaction'" != "" {
                    qui gen DiD_evstudydd`time_evstudy'_int = DiD_evstudydd`time_evstudy' * `interaction'
                }
            }
        }
        
        gen DiD_evstudyddpost = (`t' > `shockperiod_val' + `after') * (`treated')
        if "`interaction'" != "" {
            gen DiD_evstudyddpost_int = DiD_evstudyddpost * `interaction'
        }
    }
    
    * Generate temporary variables for storing results
    tempvar B_temp SE_temp TIME_temp B_int_temp SE_int_temp
    
    * Estimate event study 
    if "`estimator'" == "" | "`estimator'" == "reghdfe" | "`estimator'" == "ols" {
        * if weight is specified
        if "`weight'" != "" {
            if "`controls'" != "" {
                reghdfe `Y' DiD_evstudydd* `controls' [aw = `weight'], a(`fe') vce(cluster `cluster') noconst
            }
            else {
                reghdfe `Y' DiD_evstudydd* [aw = `weight'], a(`fe') vce(cluster `cluster') noconst
            }
        }
        * if weight is not specified
        if "`weight'" == "" {
            if "`controls'" != "" {
                reghdfe `Y' DiD_evstudydd* `controls', a(`fe') vce(cluster `cluster') noconst
            }
            else {
                reghdfe `Y' DiD_evstudydd*, a(`fe') vce(cluster `cluster') noconst
            }
        }
    }
    
    if "`estimator'" == "ppmlhdfe" | "`estimator'" == "poisson" {
        * Poisson estimators do not allow analytical weights
        if "`weight'" != "" {    
            display in red "Poisson estimators do not allow analytical weights"
            exit 198
        }
        * if weight is not specified
        if "`weight'" == "" {    
            if "`controls'" != "" {
                ppmlhdfe `Y' DiD_evstudydd* `controls', a(`fe') vce(cluster `cluster')
            }
            else {
                ppmlhdfe `Y' DiD_evstudydd*, a(`fe') vce(cluster `cluster')
            }
        }
    }
    
    * Create dataset of results
    qui gen double `TIME_temp' = -`before' - 1 if _n == 1
    
    if "`interaction'" != "" {
        * For cases with interaction, store both base and interaction coefficients
        qui gen double `B_temp' = _b[DiD_evstudydd0] if _n == 1
        qui gen double `SE_temp' = _se[DiD_evstudydd0] if _n == 1
        qui gen double `B_int_temp' = _b[DiD_evstudydd0_int] if _n == 1
        qui gen double `SE_int_temp' = _se[DiD_evstudydd0_int] if _n == 1
    
        * Values from -k to k 
        forvalues time_evstudy = 1(1)`T' {
            if `time_evstudy' != `before' + 1 {
                qui replace `TIME_temp' = `time_evstudy' - `before' - 1 if _n == `time_evstudy' + 1
                qui replace `B_temp' = _b[DiD_evstudydd`time_evstudy'] if _n == `time_evstudy' + 1
                qui replace `SE_temp' = _se[DiD_evstudydd`time_evstudy'] if _n == `time_evstudy' + 1
                qui replace `B_int_temp' = _b[DiD_evstudydd`time_evstudy'_int] if _n == `time_evstudy' + 1
                qui replace `SE_int_temp' = _se[DiD_evstudydd`time_evstudy'_int] if _n == `time_evstudy' + 1
            }
        }
    
        * Ref year not estimated 
        qui replace `TIME_temp' = 0 if _n == `before' + 2
        qui replace `B_temp' = 0 if _n == `before' + 2
        qui replace `SE_temp' = 0 if _n == `before' + 2
        qui replace `B_int_temp' = 0 if _n == `before' + 2
        qui replace `SE_int_temp' = 0 if _n == `before' + 2
    
        * Adjust k+1 onward 
        qui replace `TIME_temp' = `after' + 1 if _n == `T' + 2
        qui replace `B_temp' = _b[DiD_evstudyddpost] if _n == `T' + 2
        qui replace `SE_temp' = _se[DiD_evstudyddpost] if _n == `T' + 2
        qui replace `B_int_temp' = _b[DiD_evstudyddpost_int] if _n == `T' + 2
        qui replace `SE_int_temp' = _se[DiD_evstudyddpost_int] if _n == `T' + 2
    }
    else {
        * For cases without interaction
        qui gen double `B_temp' = _b[DiD_evstudydd0] if _n == 1
        qui gen double `SE_temp' = _se[DiD_evstudydd0] if _n == 1
    
        * Values from -k to k 
        forvalues time_evstudy = 1(1)`T' {
            if `time_evstudy' != `before' + 1 {
                qui replace `TIME_temp' = `time_evstudy' - `before' - 1 if _n == `time_evstudy' + 1
                qui replace `B_temp' = _b[DiD_evstudydd`time_evstudy'] if _n == `time_evstudy' + 1
                qui replace `SE_temp' = _se[DiD_evstudydd`time_evstudy'] if _n == `time_evstudy' + 1
            }
        }
    
        * Ref year not estimated 
        qui replace `TIME_temp' = 0 if _n == `before' + 2
        qui replace `B_temp' = 0 if _n == `before' + 2
        qui replace `SE_temp' = 0 if _n == `before' + 2
    
        * Adjust k+1 onward 
        qui replace `TIME_temp' = `after' + 1 if _n == `T' + 2
        qui replace `B_temp' = _b[DiD_evstudyddpost] if _n == `T' + 2
        qui replace `SE_temp' = _se[DiD_evstudyddpost] if _n == `T' + 2
    }
    
    * Keep only necessary variables and save
    if "`interaction'" != "" {
        keep `TIME_temp' `B_temp' `SE_temp' `B_int_temp' `SE_int_temp'
        keep if `TIME_temp' < .
        rename `TIME_temp' time
        rename `B_temp' B
        rename `SE_temp' SE
        rename `B_int_temp' B_int
        rename `SE_int_temp' SE_int
    }
    else {
        keep `TIME_temp' `B_temp' `SE_temp'
        keep if `TIME_temp' < .
        rename `TIME_temp' time
        rename `B_temp' B
        rename `SE_temp' SE
    }
    
    save "`name'", replace
    
    * Restore user dataset     
    restore

end

}