13 Difference in differences

In this chapter, we illustrate the concept of difference in differences (DD) estimators by evaluating the effects of climate change regulation on the pricing of bonds across firms. DD estimators are typically used to recover the treatment effects of natural or quasi-natural experiments that trigger sharp changes in the environment of a specific group. Instead of looking at differences in just one group (e.g., the effect in the treated group), DD investigates the treatment effects by looking at the difference between differences in two groups. Such experiments are usually exploited to address endogeneity concerns (e.g., Roberts and Whited 2013). The identifying assumption is that the outcome variable would change equally in both groups without the treatment. This assumption is also often referred to as the assumption of parallel trends. Moreover, we would ideally also want a random assignment to the treatment and control groups. Due to lobbying or other activities, this randomness is often violated in (financial) economics.

In the context of our setting, we investigate the impact of the Paris Agreement (PA), signed on December 12, 2015, on the bond yields of polluting firms. We first estimate the treatment effect of the agreement using panel regression techniques that we discuss in the Chapter 12. We then present two methods to illustrate the treatment effect over time graphically. Although we demonstrate that the treatment effect of the agreement is anticipated by bond market participants well in advance, the techniques we present below can also be applied to many other settings.

The approach we use here replicates the results of Seltzer, Starks, and Zhu (2022) partly. Specifically, we borrow their industry definitions for grouping firms into green and brown types. Overall, the literature on ESG effects in corporate bond markets is already large but continues to grow (for recent examples, see, e.g., Halling, Yu, and Zechner (2021), Handler, Jankowitsch, and Pasler (2022), Huynh and Xia (2021), among many others).

The current chapter relies on this set of packages.

13.1 Data preparation

We use TRACE and Mergent FISD as data sources from our SQLite-database introduced in Chapters 2-4.

tidy_finance <- dbConnect(
  extended_types = TRUE

mergent <- tbl(tidy_finance, "mergent") |>

trace_enhanced <- tbl(tidy_finance, "trace_enhanced") |>

We start our analysis by preparing the sample of bonds. We only consider bonds with a time to maturity of more than one year to the signing of the PA, so that we have sufficient data to analyze the yield behavior after the treatment date. This restriction also excludes all bonds issued after the agreement. We also consider only the first two digits of the SIC industry code to identify the polluting industries (in line with Seltzer, Starks, and Zhu 2022).

treatment_date <- ymd("2015-12-12")

bonds <- mergent |>
    time_to_maturity = as.numeric(maturity - treatment_date),
    time_to_maturity = time_to_maturity / 365,
    sic_code = as.integer(substr(sic_code, 1, 2)),
    log_offering_amt = log(offering_amt)
  ) |>
  filter(time_to_maturity >= 1) |>
    cusip_id = complete_cusip,
    time_to_maturity, log_offering_amt, sic_code

polluting_industries <- c(
  49, 13, 45, 29, 28, 33, 40, 20,
  26, 42, 10, 53, 32, 99, 37

bonds <- bonds |>
  mutate(polluter = sic_code %in% polluting_industries)

Next, we aggregate the individual transactions as reported in TRACE to a monthly panel of bond yields. We consider bond yields for a bond’s last trading day in a month. Therefore, we first aggregate bond data to daily frequency and apply common restrictions from the literature (see, e.g., Bessembinder et al. 2008). We weigh each transaction by volume to reflect a trade’s relative importance and avoid emphasizing small trades. Moreover, we only consider transactions with reported prices rptd_pr larger than 25 (to exclude bonds that are close to default) and only bond-day observations with more than five trades on a corresponding day (to exclude prices based on too few, potentially non-representative transactions).

trace_aggregated <- trace_enhanced |>
  filter(rptd_pr > 25) |>
  group_by(cusip_id, trd_exctn_dt) |>
    avg_yield = weighted.mean(yld_pt, entrd_vol_qt * rptd_pr),
    trades = n(),
    .groups = "drop"
  ) |>
  drop_na(avg_yield) |>
  filter(trades >= 5) |>
  mutate(month = floor_date(trd_exctn_dt, "months")) |>
  group_by(cusip_id, month) |>
  slice_max(trd_exctn_dt) |>
  ungroup() |>
  select(cusip_id, month, avg_yield)

By combining the bond-specific information from Mergent FISD for our bond sample with the aggregated TRACE data, we arrive at the main sample for our analysis.

bonds_panel <- bonds |>
  inner_join(trace_aggregated, by = "cusip_id") |>

Before we can run the first regression, we need to define the treated indicator, which is the product of the post_period (i.e., all months after the signing of the PA) and the polluter indicator defined above.

bonds_panel <- bonds_panel |>
  mutate(post_period = month >= floor_date(treatment_date, "months"))

bonds_panel <- bonds_panel |>
  mutate(treated = polluter & post_period)

As usual, we tabulate summary statistics of the variables that enter the regression to check the validity of our variable definitions.

bonds_panel |>
    cols = c(avg_yield, time_to_maturity, log_offering_amt),
    names_to = "measure"
  ) |>
  group_by(measure) |>
    mean = mean(value),
    sd = sd(value),
    min = min(value),
    q05 = quantile(value, 0.05),
    q50 = quantile(value, 0.50),
    q95 = quantile(value, 0.95),
    max = max(value),
    n = n(),
    .groups = "drop"
# A tibble: 3 × 9
  measure           mean    sd    min   q05   q50   q95   max      n
  <chr>            <dbl> <dbl>  <dbl> <dbl> <dbl> <dbl> <dbl>  <int>
1 avg_yield         4.08 4.21  0.0595  1.27  3.37  8.07 128.  127428
2 log_offering_amt 13.3  0.823 4.64   12.2  13.2  14.5   16.5 127428
3 time_to_maturity  8.54 8.41  1.01    1.50  5.81 27.4  101.  127428

13.2 Panel regressions

The PA is a legally binding international treaty on climate change. It was adopted by 196 Parties at COP 21 in Paris on 12 December 2015 and entered into force on 4 November 2016. The PA obliges developed countries to support efforts to build clean, climate-resilient futures. One may thus hypothesize that adopting climate-related policies may affect financial markets. To measure the magnitude of this effect, we first run an OLS regression without fixed effects where we include the treated, post_period, and polluter dummies, as well as the bond-specific characteristics log_offering_amt and time_to_maturity. This simple model assumes that there are essentially two periods (before and after the PA) and two groups (polluters and non-polluters). Nonetheless, it should indicate whether polluters have higher yields following the PA compared to non-polluters.

The second model follows the typical DD regression approach by including individual (cusip_id) and time (month) fixed effects. In this model, we do not include any other variables from the simple model because the fixed effects subsume them, and we observe the coefficient of our main variable of interest: treated.

model_without_fe <- feols(
  fml = avg_yield ~ treated + post_period + polluter +
    log_offering_amt + time_to_maturity,
  vcov = "iid",
  data = bonds_panel

model_with_fe <- feols(
  fml = avg_yield ~ treated | cusip_id + month,
  vcov = "iid",
  data = bonds_panel

etable(model_without_fe, model_with_fe, coefstat = "tstat")
                    model_without_fe     model_with_fe
Dependent Var.:            avg_yield         avg_yield
(Intercept)         10.66*** (56.60)                  
treatedTRUE        0.4610*** (9.284) 0.9807*** (29.43)
post_periodTRUE  -0.1747*** (-5.940)                  
polluterTRUE       0.4745*** (15.05)                  
log_offering_amt -0.5451*** (-38.55)                  
time_to_maturity   0.0573*** (41.29)                  
Fixed-Effects:   ------------------- -----------------
cusip_id                          No               Yes
month                             No               Yes
________________ ___________________ _________________
VCOV type                        IID               IID
Observations                 127,428           127,428
R2                           0.03151           0.64689
Within R2                         --           0.00713
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Both models indicate that polluters have significantly higher yields after the PA than non-polluting firms. Note that the magnitude of the treated coefficient varies considerably across models.

13.4 Exercises

  1. The 46th president of the US rejoined the Paris Agreement in February 2021. Repeat the difference in differences analysis for the day of his election victory. Note that you will also have to download new TRACE data. How did polluters’ yields react to this action?
  2. Based on the exercise on ratings in Chapter 4, include ratings as a control variable in the analysis above. Do the results change?