Skip to contents

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 (Newey-West) for heteroskedasticity- and autocorrelation-consistent coefficient estimation
  • Kolari-Pynnonen Test that adjusts the BMP test for cross-sectional correlation
  • Wild Bootstrap inference for AAR and CAAR test statistics
  • Multiple Testing Corrections (Bonferroni, BH, Holm, etc.) for controlling the family-wise error rate or false discovery rate

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:

  • "rademacher" (default): w_i \in \{-1, +1\} with equal probability
  • "mammen": Mammen two-point distribution, better for skewed data

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

  • Kolari, J. W. and Pynnonen, S. (2010). Event study testing with cross-sectional correlation of abnormal returns. Review of Financial Studies, 23(11), 3996-4025.
  • Newey, W. K. and West, K. D. (1987). A simple, positive semi-definite, heteroskedasticity and autocorrelation consistent covariance matrix. Econometrica, 55(3), 703-708.
  • Benjamini, Y. and Hochberg, Y. (1995). Controlling the false discovery rate: A practical and powerful approach to multiple testing. Journal of the Royal Statistical Society, 57(1), 289-300.