Difference-in-differences with strategic agents

causal-inference
difference-in-differences
strategic-interaction
parallel-trends
Applying the difference-in-differences estimator to settings where treatment alters a strategic equilibrium, demonstrating how agents’ endogenous responses can violate the parallel trends assumption and bias causal estimates.
Author

Raban Heller

Published

May 8, 2026

Modified

May 8, 2026

Keywords

difference-in-differences, DiD, causal inference, parallel trends, strategic interaction, equilibrium shift, antitrust, policy evaluation

Introduction & motivation

Difference-in-differences (DiD) is one of the most widely used methods for estimating causal effects of policy interventions using observational data. The basic logic is elegant: compare the change in outcomes over time for a treated group with the change for an untreated control group, and attribute the difference to the treatment. The identifying assumption — parallel trends — requires that, in the absence of treatment, both groups would have followed the same trajectory. Under this assumption, the DiD estimator isolates the causal effect of the policy.

However, a fundamental challenge arises when the agents being studied are strategic. In many economically important settings, a policy change does not simply shift outcomes mechanically; it alters the game that agents play, causing them to adjust their strategies in response. Consider antitrust enforcement: when a regulatory agency increases merger scrutiny in one industry (the treated group), firms in that industry do not passively absorb the shock. They strategically adapt — restructuring deals to avoid scrutiny, shifting to alternative growth strategies, or engaging in anticipatory behaviour before the policy takes effect. Crucially, firms in untreated industries may also respond strategically if the policy signals a broader regulatory shift, contaminating the control group.

This strategic adaptation can violate the parallel trends assumption in subtle ways. If treatment changes the equilibrium of a game, the treated group’s post-treatment trajectory reflects not just the direct effect of the policy but also the indirect effect of equilibrium adjustment. If the control group also partially adjusts (through spillovers, anticipation, or signalling), the bias is compounded. Standard DiD is designed for settings where treatment effects are additive and stable; it is not designed for settings where treatment fundamentally reshapes the strategic landscape.

In this tutorial, we formalise a simple Cournot duopoly game where a policy intervention (e.g., a tax or regulation) changes the cost structure for treated firms, compute the pre- and post-treatment equilibria, simulate panel data under both the standard (no strategic adaptation) and strategic scenarios, and show how DiD estimates diverge from the true causal effect when agents strategically re-optimise.

Mathematical formulation

Consider two markets, each containing a duopoly with firms playing Cournot competition. Market \(T\) (treated) receives a policy intervention at time \(t_0\); market \(C\) (control) does not.

In the Cournot model, firm \(j\) in market \(m\) chooses quantity \(q_{mj}\) to maximise profit:

\[ \pi_{mj} = (a - Q_m) q_{mj} - c_{mj} q_{mj} \]

where \(a\) is the demand intercept, \(Q_m = q_{m1} + q_{m2}\) is total output in market \(m\), and \(c_{mj}\) is marginal cost. The Nash equilibrium quantities are:

\[ q_{mj}^* = \frac{a - 2c_{mj} + c_{m,-j}}{3} \]

and equilibrium total output is:

\[ Q_m^* = \frac{2a - c_{m1} - c_{m2}}{3} \]

Pre-treatment (\(t < t_0\)): Both markets have identical cost structures \(c_{mj} = c_0\) for all firms. Equilibrium output in each market is \(Q^{\text{pre}} = \frac{2(a - c_0)}{3}\).

Post-treatment (\(t \geq t_0\)): The policy raises costs for both firms in market \(T\) by \(\delta\) (e.g., a regulatory compliance cost), so \(c_{Tj} = c_0 + \delta\). Market \(C\) is unaffected.

The true causal effect on total output is:

\[ \tau = Q_T^{\text{post}} - Q_T^{\text{pre}} = \frac{2(a - c_0 - \delta)}{3} - \frac{2(a - c_0)}{3} = -\frac{2\delta}{3} \]

Standard DiD computes:

\[ \hat{\tau}_{\text{DiD}} = (\bar{Y}_{T,\text{post}} - \bar{Y}_{T,\text{pre}}) - (\bar{Y}_{C,\text{post}} - \bar{Y}_{C,\text{pre}}) \]

Under parallel trends (control output is constant), DiD correctly estimates \(\tau\). But if the control group also adjusts — for example, if firms in market \(C\) anticipate that the policy will expand to their market and pre-emptively restructure — then \(\bar{Y}_{C,\text{post}} - \bar{Y}_{C,\text{pre}} \neq 0\), and the DiD estimate is biased:

\[ \text{Bias} = \hat{\tau}_{\text{DiD}} - \tau = -(\bar{Y}_{C,\text{post}} - \bar{Y}_{C,\text{pre}}) \]

We can also model asymmetric strategic response within the treated market: if the policy only affects one firm (say firm 1), the rival (firm 2) increases output in response (strategic substitutes in Cournot). The total effect is:

\[ \Delta q_1^* = -\frac{2\delta}{3}, \qquad \Delta q_2^* = +\frac{\delta}{3} \]

The net market-level effect is \(-\delta/3\), but the firm-level DiD would estimate \(-2\delta/3\) for the treated firm — the correct direct effect but missing the strategic spillover on the rival.

R implementation

We simulate panel data for treated and control markets across 20 time periods, with treatment at period 10. We compare DiD estimates under three scenarios: no strategic response, symmetric strategic adaptation, and spillover to the control group.

set.seed(2026)

# Parameters
a <- 100        # demand intercept
c0 <- 30        # baseline marginal cost
delta <- 15     # treatment effect (cost increase)
T_periods <- 20
t0 <- 10        # treatment begins
n_firms <- 50   # firms per group for simulation
sigma <- 3      # noise sd

# Generate panel data
generate_panel <- function(scenario) {
  periods <- 1:T_periods
  treated <- tibble(
    period = periods,
    group = "Treated",
    # Pre-treatment: equilibrium output + trend + noise
    base_output = 2 * (a - c0) / 3 + 0.5 * periods  # slight upward trend
  )
  control <- tibble(
    period = periods,
    group = "Control",
    base_output = 2 * (a - c0) / 3 + 0.5 * periods
  )

  # Apply treatment effects
  if (scenario == "no_strategic") {
    # Mechanical effect only: treated drops, control unchanged
    treated <- treated %>%
      mutate(output = base_output + ifelse(period >= t0, -2 * delta / 3, 0))
    control <- control %>%
      mutate(output = base_output)

  } else if (scenario == "strategic") {
    # Treated firms re-optimise; control also partially adjusts (anticipation)
    treated <- treated %>%
      mutate(output = base_output + ifelse(period >= t0, -2 * delta / 3, 0))
    # Control firms anticipate regulation spreading: reduce output slightly
    control <- control %>%
      mutate(output = base_output + ifelse(period >= t0, -delta / 6, 0) +
               ifelse(period == t0 - 1, -delta / 12, 0))  # anticipation

  } else if (scenario == "spillover") {
    # Strong spillover: control firms react substantially
    treated <- treated %>%
      mutate(output = base_output + ifelse(period >= t0, -2 * delta / 3, 0))
    control <- control %>%
      mutate(output = base_output + ifelse(period >= t0, -delta / 3, 0))
  }

  # Add noise (simulate multiple firms)
  panel <- bind_rows(treated, control) %>%
    crossing(firm_id = 1:n_firms) %>%
    mutate(output = output + rnorm(n(), 0, sigma)) %>%
    mutate(scenario = scenario)

  panel
}

scenarios <- c("no_strategic", "strategic", "spillover")
all_data <- bind_rows(lapply(scenarios, generate_panel))

# Compute DiD estimates
compute_did <- function(data) {
  means <- data %>%
    mutate(post = period >= t0) %>%
    group_by(group, post) %>%
    summarise(mean_output = mean(output), .groups = "drop") %>%
    pivot_wider(names_from = c(group, post), values_from = mean_output)

  did_est <- (means$Treated_TRUE - means$Treated_FALSE) -
             (means$Control_TRUE - means$Control_FALSE)
  did_est
}

true_effect <- -2 * delta / 3

cat("=== Difference-in-Differences Estimation ===\n\n")
=== Difference-in-Differences Estimation ===
cat(sprintf("  True causal effect (tau): %.2f\n\n", true_effect))
  True causal effect (tau): -10.00
for (s in scenarios) {
  est <- compute_did(all_data %>% filter(scenario == s))
  bias <- est - true_effect
  cat(sprintf("  %-18s | DiD estimate: %6.2f | Bias: %+6.2f\n",
              s, est, bias))
}
  no_strategic       | DiD estimate:  -9.66 | Bias:  +0.34
  strategic          | DiD estimate:  -7.70 | Bias:  +2.30
  spillover          | DiD estimate:  -5.58 | Bias:  +4.42
# Aggregate data for plotting (market-level means)
plot_data <- all_data %>%
  group_by(scenario, group, period) %>%
  summarise(mean_output = mean(output), .groups = "drop") %>%
  mutate(scenario_label = case_when(
    scenario == "no_strategic" ~ "A. No strategic response",
    scenario == "strategic"    ~ "B. Strategic adaptation",
    scenario == "spillover"    ~ "C. Control spillover"
  ))

Static publication-ready figure

The three-panel figure compares market-level output trajectories for treated and control groups across scenarios. The vertical dashed line marks the treatment date. Divergence in the control group’s trajectory after treatment signals a parallel trends violation.

# Add counterfactual line for treated group
counterfactual <- plot_data %>%
  filter(group == "Control") %>%
  mutate(group = "Treated (counterfactual)")

p_did <- ggplot(plot_data,
                aes(x = period, y = mean_output, colour = group,
                    text = paste0("Group: ", group,
                                  "\nPeriod: ", period,
                                  "\nOutput: ", round(mean_output, 1)))) +
  geom_vline(xintercept = t0 - 0.5, linetype = "dashed", colour = "grey50", linewidth = 0.5) +
  geom_line(linewidth = 1) +
  geom_point(size = 1.3, alpha = 0.7) +
  scale_colour_manual(values = okabe_ito[c(5, 1)]) +
  facet_wrap(~ scenario_label, ncol = 3) +
  labs(
    title = "Parallel trends and strategic adaptation",
    subtitle = "Market-level output (N = 50 firms per group, 20 periods)",
    x = "Period",
    y = "Mean market output",
    colour = "Group"
  ) +
  theme_publication() +
  theme(strip.text = element_text(face = "bold", size = 10))

p_did
Figure 1: Figure 1. Difference-in-differences under strategic behaviour. Panel A: standard case with no strategic adaptation (DiD unbiased). Panel B: control group partially anticipates treatment, causing mild bias. Panel C: strong spillover to the control group causes substantial DiD bias. Dashed vertical line marks treatment onset.

Interactive figure

Hover over the data points to compare exact output levels across treated and control groups in each scenario. The divergence between groups after the treatment period reveals the magnitude of the DiD estimate.

p_interact <- ggplot(plot_data,
                     aes(x = period, y = mean_output, colour = group,
                         text = paste0("Scenario: ", scenario_label,
                                       "\nGroup: ", group,
                                       "\nPeriod: ", period,
                                       "\nOutput: ", round(mean_output, 2)))) +
  geom_vline(xintercept = t0 - 0.5, linetype = "dashed", colour = "grey50") +
  geom_line(linewidth = 0.9) +
  geom_point(size = 1.5) +
  scale_colour_manual(values = okabe_ito[c(5, 1)]) +
  facet_wrap(~ scenario_label, ncol = 3) +
  labs(title = "DiD with strategic agents: interactive exploration",
       x = "Period", y = "Mean output", colour = "Group") +
  theme_publication()

ggplotly(p_interact, tooltip = "text") %>%
  config(displaylogo = FALSE) %>%
  layout(legend = list(orientation = "h", y = -0.15))
Figure 2

Interpretation

The simulation results demonstrate a fundamental tension between causal inference methodology and strategic behaviour. In the baseline scenario (Panel A), where treatment affects only the treated group and the control group follows a parallel trajectory, the standard DiD estimator recovers the true causal effect almost exactly (bias near zero, driven only by sampling noise). This is the textbook case: parallel trends hold, and DiD is unbiased.

In the strategic adaptation scenario (Panel B), the control group’s output dips slightly before and after treatment due to anticipatory adjustment. Firms in the control market, observing the regulatory change in the treated market, rationally infer that similar regulation may be coming and begin pre-emptive restructuring. This violates parallel trends in a way that is easy to miss in practice: the pre-treatment trends appear nearly parallel if one does not look closely at the periods just before treatment. The resulting DiD estimate is modestly biased — understating the true effect because the control group’s decline is subtracted from the treated group’s decline.

In the spillover scenario (Panel C), the bias is severe. When control-group firms substantially reduce output in response to the treatment (perhaps because of competitive interdependencies between markets, or because the policy has general-equilibrium effects), the parallel trends assumption fails catastrophically. The DiD estimator captures only the differential decline between treated and control, missing the substantial common decline that affects both groups.

These findings have important practical implications for applied researchers. First, the standard tests for parallel pre-trends — checking whether treated and control groups moved together before treatment — are necessary but insufficient in strategic settings, because strategic anticipation may be concentrated in the few periods just before the policy change. Second, researchers should think carefully about whether the treatment could generate spillovers to the control group through strategic channels (competition, signalling, anticipation). Third, structural estimation — explicitly modelling the game that agents play — may be necessary to recover the true causal effect in environments where treatment changes the equilibrium rather than simply shifting an outcome.

One promising approach is to combine DiD with a structural model of the game. The researcher specifies the agents’ objective functions and the equilibrium concept, uses pre-treatment data to estimate the structural parameters, and then simulates counterfactual outcomes under the treatment. This “structural DiD” approach is more demanding in terms of modelling assumptions but can correctly account for equilibrium effects that reduced-form DiD misses.

References

Back to top

Reuse

Citation

BibTeX citation:
@online{heller2026,
  author = {Heller, Raban},
  title = {Difference-in-Differences with Strategic Agents},
  date = {2026-05-08},
  url = {https://r-heller.github.io/equilibria/tutorials/causal-inference/difference-in-differences-strategic/},
  langid = {en}
}
For attribution, please cite this work as:
Heller, Raban. 2026. “Difference-in-Differences with Strategic Agents.” May 8. https://r-heller.github.io/equilibria/tutorials/causal-inference/difference-in-differences-strategic/.