Inference and Robustness: HAC, Bootstrap, and Multiple Testing
Simon Mueller
2026-09-06
Source:vignettes/inference-robustness.Rmd
inference-robustness.RmdIntroduction
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.