The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

Inference and Robustness: HAC, Bootstrap, and Multiple Testing

Simon Mueller

2026-10-09

Introduction

Classical event study inference relies on i.i.d. assumptions that are often violated in practice. The EventStudy package provides several tools to strengthen the robustness of your inference:

HAC Standard Errors

When estimation-window residuals exhibit heteroskedasticity or autocorrelation, OLS standard errors are biased. The use_hac = TRUE option applies Newey-West HAC standard errors via the sandwich package.

library(EventStudy)

task <- EventStudyTask$new(firm_data, index_data, request)

# Enable HAC SEs on the MarketModel
ps <- ParameterSet$new(
  return_model = MarketModel$new(use_hac = TRUE)
)
task <- run_event_study(task, ps)

You can also specify a custom lag for the Newey-West estimator:

ps <- ParameterSet$new(
  return_model = MarketModel$new(use_hac = TRUE, hac_lag = 5)
)

HAC standard errors are also available for factor models:

ps <- ParameterSet$new(
  return_model = FamaFrench3FactorModel$new(use_hac = TRUE)
)

The OLS sigma (used for abnormal return standardization in test statistics) is deliberately unchanged – HAC only affects coefficient standard errors and p-values.

Kolari-Pynnonen Test

The Boehmer, Musumeci & Poulsen (BMP) test can be oversized when event-window residuals are cross-sectionally correlated. The Kolari-Pynnonen adjustment corrects for this:

\[t_{KP} = t_{BMP} \sqrt{\frac{1 - \bar{r}}{1 + (N-1)\bar{r}}}\]

where \(\bar{r}\) is the average pairwise correlation of standardized abnormal returns in the estimation window.

ps <- ParameterSet$new(
  multi_event_statistics = MultiEventStatisticsSet$new(
    tests = list(
      BMPTest$new(),
      KolariPynnonenTest$new()
    )
  )
)
task <- run_event_study(task, ps)

Wild Bootstrap Inference

The wild bootstrap provides distribution-free inference by resampling with random per-firm sign flips. This preserves the cross-sectional dependence structure while testing the null hypothesis of zero abnormal returns.

task <- create_fitted_mock_task()

# Run bootstrap with 999 replications
boot_result <- bootstrap_test(
  task,
  n_boot = 999,
  weight_type = "rademacher",
  seed = 42
)

boot_result
#> # A tibble: 11 x 5
#>    relative_index observed_aar observed_caar boot_p_aar boot_p_caar
#>             <int>        <dbl>         <dbl>      <dbl>       <dbl>
#>  1             -5      0.00123       0.00123      0.842       0.842
#>  ...

Two weight distributions are available:

You can also bootstrap only the AAR or CAAR statistic:

boot_aar <- bootstrap_test(task, n_boot = 499, statistic = "aar")
boot_caar <- bootstrap_test(task, n_boot = 499, statistic = "caar")

Multiple Testing Corrections

When testing abnormal returns at many event-window days, the probability of a false positive increases. The adjust_p_values() function applies standard correction methods:

task <- create_fitted_mock_task()

# Benjamini-Hochberg (controls FDR)
result_bh <- adjust_p_values(task, method = "BH", stat_name = "CSectT")

# Bonferroni (controls FWER)
result_bonf <- adjust_p_values(task, method = "bonferroni", stat_name = "CSectT")

# Holm (less conservative than Bonferroni)
result_holm <- adjust_p_values(task, method = "holm", stat_name = "CSectT")

The result includes both raw and adjusted p-values:

result_bh
#> # A tibble: 11 x 8+
#>    relative_index   aar  caar p_raw_aar p_adj_aar p_raw_caar p_adj_caar ...

The function automatically detects the test statistic type (t, z, BMP, etc.) and computes p-values from the appropriate distribution.

You can filter by group:

result <- adjust_p_values(task, method = "BH", stat_name = "CSectT",
                           group = "MyGroup")

Combining Approaches

For maximum robustness, combine multiple approaches:

# 1. Use HAC SEs in the model
ps <- ParameterSet$new(
  return_model = MarketModel$new(use_hac = TRUE),
  multi_event_statistics = MultiEventStatisticsSet$new(
    tests = list(
      CSectTTest$new(),
      KolariPynnonenTest$new()
    )
  )
)
task <- run_event_study(task, ps)

# 2. Apply multiple testing correction
adjusted <- adjust_p_values(task, method = "BH", stat_name = "CSectT")

# 3. Validate with bootstrap
boot <- bootstrap_test(task, n_boot = 999, seed = 42)

References

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.