*! version 1.0.2 03jul2026

Copyright

Copyright 2026 Xuanyu Cai and Wenli Xu

Title

diddesign -- Double Difference-in-Differences Estimation

Syntax

diddesign
depvar
[covariates]
[if] [in]
,
treatment(varname)
time(varname)
[options]

where covariates specifies optional control variables.
Alternatively, use the covariates() option.

Required
treatment(varname) treatment indicator variable

time(varname) time variable

Model
covariates(string) optional control variables; supports factor variables

Design
design(did|sa) estimation design; default is did

panel panel data format (default)

rcs repeated cross-section data format; optional when post() already implies RCS

Panel Options
id(varname) unit identifier variable (required for panel)

RCS Options
post(varname) post-treatment indicator (required for RCS; also triggers RCS auto-detection)

Inference
cluster(varname) cluster variable for standard errors; required for RCS data

nboot(#) number of bootstrap replications (# >= 2); default is 30

seboot use bootstrap percentile confidence intervals

seed(#) random number seed for reproducibility

Dynamic Effects
lead(numlist) lead periods for dynamic effects; default is 0

Staggered Adoption
thres(#) minimum treated units threshold for design(sa); default is 2

Generalized K-DID
kmax(#) maximum number of moment conditions (K-DID components); default is 2

jtest(on|off) enable/disable J-test moment selection; default is off

Reporting
level(#) confidence level; default is 95

quiet suppress bootstrap progress display

parallel enable parallel bootstrap (requires SSC parallel package)

covariates supports factor-variable notation; see fvvarlist.

Description

diddesign implements the Double Difference-in-Differences (Double DID)
method from Egami and Yamauchi (2023). The method optimally combines the
standard DID estimator with the sequential DID (s-DID) estimator using GMM
weights to improve efficiency while maintaining robustness.

For standard DID designs (design(did)), the command estimates both
DID and s-DID, then computes the GMM-optimal weighted combination (Double DID).
When kmax() is set to a value greater than 2 and sufficient pre-treatment
periods are available, the command uses the generalized K-DID estimator from
Appendix E of Egami and Yamauchi (2023), combining up to K moment conditions
via GMM. The k-th component uses the k-th order parallel trends assumption,
which can account for (k-1)-th degree polynomial time-varying confounding.

For staggered adoption (SA) designs (design(sa)), the command handles
multiple treatment timing groups and computes time-weighted averages across
cohorts.

The Double DID estimator is:

tau_dDID = w1 * tau_DID + w2 * tau_sDID

where w1 and w2 are GMM-optimal weights that minimize the asymptotic variance,
and tau_sDID denotes the sequential DID (s-DID) estimator.

Options

Required

treatment(varname) specifies the binary treatment indicator variable.
For panel data, the treatment variable indicates whether unit i is treated at
time t (1 = treated, 0 = control). For RCS data, the treatment variable
indicates treatment group membership.

time(varname) specifies the time variable (e.g., year). This variable
may be numeric or string. String time labels are encoded automatically to
numeric period indices only when that encoding preserves the observed order
of distinct labels in the estimation sample. If labels such as t1,
t2, and t10 would be silently reordered lexicographically or by
their numeric suffixes during encoding, diddesign stops with an error
and asks users to recode time() to a numeric or lexically ordered
variable first.

Model

covariates specified directly after the dependent variable in the varlist
are optional control variables (e.g., diddesign y x1 x2 x3, ...).
Alternatively, the covariates() option below can be used to specify
control variables.

covariates(fvvarlist) specifies additional control variables to include
in the estimation. This option has no limit on the number of variables and
can be combined with inline covariates. These variables are included as
covariates in the difference-in-differences regression to improve precision
and control for confounding factors.

Factor-variable notation is supported; see fvvarlist. For example,
covariates(i.region) automatically expands a categorical variable
region into dummy variables. If region is stored as a string,
it is encoded automatically before factor expansion. Factor variables are
expanded using fvrevar before estimation, and the number of expanded
variables is displayed.

Examples of factor-variable syntax:

i.region - indicator (dummy) variables for categorical region

ib3.region - same as above, but with region=3 as base

ibn.region - no base category (include all levels)

c.x1#c.x2 - interaction of continuous variables

i.region#c.gdp - interaction of factor and continuous

Design

design(did|sa) specifies the estimation design. did (default)
uses the standard DID design with a single treatment time. sa uses
the staggered adoption design for multiple treatment times across units.
The current design(sa) implementation requires complete and unique
id() x time() cells, that is, a balanced panel with exactly one
observation per unit-period. It also requires at least three distinct time
periods so the SA DID/sDID estimators can use the window.

panel indicates that the data is in panel format where the same units
are observed over time. This is the default when id() is specified.

rcs indicates that the data is in repeated cross-section format where
different units are observed at each time point. When using RCS data,
the post() option is required. If post() is already specified,
explicit rcs is optional and only makes the data type declaration
more explicit.

Panel Options

id(varname) specifies the variable identifying units (individuals,
firms, states, etc.). This option is required for panel data. String variables
are automatically encoded to numeric using egen group().

RCS Options

post(varname) specifies the post-treatment period indicator variable
for repeated cross-section data. This variable should equal 1 for post-treatment
periods and 0 for pre-treatment periods. Values within the 1e-6 tolerance band
around 0 or 1 are canonicalized to the exact binary values before validation
and estimation. This option is required when using RCS data and, when supplied
without panel, it is sufficient to trigger RCS auto-detection even if
explicit rcs is omitted.

Inference

cluster(varname) specifies the variable for cluster-robust bootstrap
standard errors. If not specified for panel data, clustering is done at the
unit level (same as id()). For RCS data, cluster() is required and
should identify the treatment-assignment bootstrap block; the command now
rejects RCS runs that omit cluster(). String variables are
automatically encoded to numeric.

nboot(#) specifies the number of bootstrap replications for variance
estimation. # must be an integer greater than or equal to 2. The default
is 30. For publication-quality results, consider using around 2000
replications to match the empirical applications in Egami and Yamauchi (2023).
If fewer than 10 replications are requested
and all iterations succeed, diddesign reports this as a low-nboot()
warning rather than as a bootstrap-failure warning.

seboot requests that bootstrap percentile confidence intervals be used
instead of asymptotic normal confidence intervals.
This option only affects the standard DID design.

seed(#) sets the random number seed for reproducibility of bootstrap
results.

Dynamic Effects

lead(numlist) specifies the lead periods for dynamic effects.
The default is 0, which estimates the instantaneous treatment effect. The
window shorthand below describes the default lead(0) case. When
lead()>0 is requested in the standard DID design, the command instead
uses the lead-specific dynamic extension from Appendix E, anchored at the
last pre-treatment period and targeting the requested post-treatment lead.
Positive values therefore estimate dynamic effects `k' periods after
treatment using the {-1, lead} contrast implied by that
Appendix E construction. In the
staggered adoption design, positive values estimate time-average SA-ATT
effects `k' periods after adoption.

Staggered Adoption

thres(#) specifies the minimum number of treated units required for a
time period to be included in the SA analysis. The default is 2. Periods with
fewer treated units are excluded from the estimation.

Reporting

level(#) specifies the confidence level, as a percentage, for confidence
intervals. The default is 95.

quiet suppresses the bootstrap progress display. This is useful when
running the command in batch mode or when progress messages are not needed.

parallel enables parallel bootstrap computation using the SSC
parallel package. When specified, bootstrap replications are distributed
across multiple Stata instances for faster computation. Requires
ssc install parallel and Stata/MP or SE. If the parallel
package is not available, computation falls back to sequential bootstrap
with a warning.

Stored results

diddesign stores the following in e():

Scalars

e(N) number of observations

e(n_units) number of units (panel data only; missing for RCS)

e(n_periods) number of time periods

e(n_boot) number of bootstrap replications

e(n_clusters) number of clusters in the posted estimation sample

e(n_lead) number of retained lead periods

e(n_lead_requested) number of leads requested in lead()

e(n_lead_filtered) number of requested leads filtered out before estimation

e(n_lead_identified) number of retained leads with at least one posted coefficient in e(b)

e(level) confidence level

e(is_panel) 1 if panel data, 0 otherwise

e(n_boot_success) number of bootstrap iterations with at least one finite DID/sDID component estimate

e(seboot) 1 if bootstrap CI used, 0 otherwise

e(kmax) maximum K-DID components requested

e(jtest_on) 1 if J-test moment selection enabled, 0 otherwise

e(parallel) 1 if parallel bootstrap was used, 0 otherwise

Scalars (SA design only)

e(n_periods_valid) number of adoption periods that actually enter SA estimation

e(thres) minimum treated units threshold used

Macros

e(cmd) diddesign

e(cmdline) command as typed

e(design) estimation design (did or sa)

e(depvar) name of dependent variable

e(treatment) name of treatment variable

e(covariates) names of covariates

e(covars) alias of e(covariates) for cross-command compatibility

e(id) name of unit identifier variable

e(time) name of time variable

e(post) name of post-treatment indicator variable (RCS only)

e(datatype) data type (panel or rcs)

e(clustvar) name of cluster variable

e(sample_ifin) stored if/in sample restriction; used to verify cross-command sample compatibility such as diddesign_plot, use_check()

e(ci_method) confidence interval method (bootstrap or asymptotic)

e(lead) lead values that still have at least one coefficient posted in e(b) and e(V)

e(requested_lead) lead values originally requested in lead()

e(filtered_lead) requested lead values filtered out before estimation

e(identified_lead) retained lead values with at least one coefficient posted in e(b) and e(V)

e(unidentified_lead) requested lead values with no coefficient posted in e(b) and e(V)

e(time_weight_labels) pipe-delimited adoption period labels for e(time_weights) (SA design only)

e(properties) b V

Matrices

e(b) coefficient vector with columns named dDID:lead_k,
DID:lead_k, sDID:lead_k for standard DID design;
SA_dDID:lead_k, SA_DID:lead_k,
SA_sDID:lead_k for SA design

e(V) variance-covariance matrix of the posted estimator vector; in mixed-support multi-lead SA runs, it uses jointly observed posted bootstrap draws for postestimation

e(estimates) estimation results matrix with columns: lead, estimate,
std_error, ci_lo, ci_hi, weight; in mixed-support multi-lead SA runs, component DID/sDID std_error and ci_* use lead-specific marginal bootstrap draws and may differ from diag(e(V))

e(lead_values) posted lead values vector aligned with e(b) and e(V)

e(bootstrap_support) per-lead bootstrap support counts with columns n_valid_did, n_valid_sdid, and n_joint_valid; rows align with retained lead() values and e(weights)

e(weights) GMM weights matrix with columns: w_did, w_sdid

e(W) GMM optimal weight matrix; single lead returns 2 x 2, multiple leads return one row per lead with the flattened 2 x 2 matrix

e(vcov_gmm) variance-covariance matrix of moment conditions; single lead returns 2 x 2, multiple leads return one row per lead with the flattened 2 x 2 matrix

Matrices (K-DID with kmax()>2 only)

e(k_summary) K summary matrix with columns K_init, K_sel, K_final; one row per lead

e(moment_selected) moment selection mask (0/1) per lead and component

e(moment_dropped_jtest) moments dropped by J-test (0/1) per lead and component

e(moment_dropped_numerical) moments dropped for numerical reasons (0/1) per lead and component

e(jtest_stats) J-test statistics with columns J_stat, J_df, J_pval; one row per lead

Matrices (SA design only)

e(time_weights) union-level SA time weights over adoption periods that enter the posted common-support SA targets under the requested lead(); when multiple leads are requested, this is a summary over the union of those common-support periods rather than a per-lead surface

e(time_weight_periods) adoption periods corresponding to each row of e(time_weights)

e(time_weights_by_lead) lead-specific SA time weights; rows follow e(time_weight_periods), columns follow requested lead() values, and each lead column re-normalizes only over periods where both SA-DID and SA-sDID are jointly identified for that lead

Functions

e(sample) marks the posted estimation sample, that is, the union of retained lead-specific support rows that enter at least one DID/sDID component after listwise deletion

Coefficient labels such as DID:lead_0 are stored as Stata equation names.
For postestimation commands, use equation syntax rather than the raw colon label:
lincom [DID]lead_0, test [DID]lead_0 = 0, or
lincom _b[DID:lead_0]. The same pattern applies to
dDID, sDID, SA_dDID, SA_DID, and SA_sDID.
In general, a stored label of DID:lead_k is accessed as
[DID]lead_k; for example, [DID]lead_k means
"the DID equation at lead k".

Methods and formulas

The Double DID method optimally combines the standard DID estimator with the
sequential DID (s-DID) estimator using the generalized method of moments (GMM).
When kmax() > 2, the generalized K-DID extension from Appendix E is used,
combining K component estimators. Each k-th component uses a k-th order
difference operator that accounts for (k-1)-th degree polynomial confounding.

Identification assumptions:

The standard DID estimator (tau_DID) requires the parallel trends assumption:
the outcome trend of the control group would have been the same as the trend
of the treatment group in the absence of treatment.

The sequential DID estimator (tau_sDID) requires only the weaker
parallel trends-in-trends assumption: the change in outcome trends is the
same across treatment and control groups.

Estimation:

tau_DID uses the last pre-treatment period and the requested post-treatment
lead, that is, the 2x2 contrast on periods {-1, lead}.

tau_sDID applies the same 2x2 regression to the first-differenced outcome,
which is equivalent to subtracting the earlier pre-treatment DID from the
standard DID in the default lead(0) case when two pre-treatment periods are
available.

When lead()>0 is requested, the command instead uses the lead-specific
dynamic extension from Appendix E, anchored at the last pre-treatment period
rather than the adjacent post-treatment difference shown above.

The generalized K-DID with kmax()>2 uses the Appendix E difference
operator. If fewer pre-treatment periods are available than requested by
kmax(), the effective K is automatically truncated to the data support.
Numerical fallback drops the highest-order moments if the bootstrap VCOV
is near-singular.

The Double DID estimator combines both estimators via GMM:

tau_dDID = w1 * tau_DID + w2 * tau_sDID

where w1 + w2 = 1 and the GMM optimal weight matrix W is the inverse of the
variance-covariance matrix of the two estimators. This yields the smallest
asymptotic variance among all weighted combinations. See Egami and Yamauchi
(2023, equations 12-14) for details.

When more than two pre-treatment periods are available and kmax()>2,
the K-DID extends the moment set beyond DID and sDID using higher-order
difference operators. The GMM-optimal weight matrix is estimated from
bootstrap covariance of the K component estimators. Optional J-test moment
selection (jtest(on)) uses Hansen's overidentification test to adaptively
drop violated moment conditions from lowest order first.

In the paper's theoretical moment-selection result, when only parallel trends-in-trends is plausible, the Double DID converges to the sequential DID. The current diddesign command still reports the GMM-weighted Double-DID row together with DID and sDID; interpret those reported estimates alongside diddesign_check diagnostics rather than expecting the displayed Double-DID row to automatically collapse to sDID.

For staggered adoption designs, the SA-Double-DID estimator computes
time-weighted averages across treatment cohorts, with weights proportional to
the number of newly treated units in each period.

Example Data Files

The package includes two example datasets distributed as ancillary files:

malesky2014.dta - Vietnam communes data from Malesky et al. (2014)

paglayan2019.dta - US states panel data from Paglayan (2019)

To download the datasets to your current working directory:

. net get diddesign

Then load with use:

. use malesky2014, clear

. use paglayan2019, clear

Then run the complete example analyses:

. do example_malesky.do

. do example_paglayan.do

References

Egami, N. and S. Yamauchi. 2023.
Using Multiple Pretreatment Periods to Improve Difference-in-Differences
and Staggered Adoption Designs.
Political Analysis 31(2): 195-212.
https://doi.org/10.1017/pan.2022.8

Author

Xuanyu Cai

City University of Macau

xuanyuCAI@outlook.com

Wenli Xu

City University of Macau

wlxu@cityu.edu.mo

Also see

Online: diddesign, diddesign_check, diddesign_plot, diddesign_intro