---
title: "Intraday Event Studies with EventStudy"
author: "Simon Mueller"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Intraday Event Studies}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  eval = FALSE
)
```

# Introduction

Traditional event studies measure abnormal returns at a daily frequency, but many corporate events --- earnings announcements, regulatory decisions, central-bank communications --- have effects that materialize within minutes or hours. Intraday event studies capture these short-lived dynamics by operating on high-frequency (tick or bar) data.

The `EventStudy` package supports two intraday workflows:

1. **Task-based preparation** via `IntradayEventStudyTask` and `prepare_intraday_event_study()`, which compute intraday returns and assign estimation/event windows.
2. **Non-parametric significance testing** via `nonparametric_intraday_test()`, which implements the methodology of Rinaudo & Saha (2014) in pure R.

This vignette walks through both.

# Data Structure

The non-parametric test requires three inputs:

| Input | Description |
|-------|-------------|
| `estimation_window` | A data frame with columns `day`, `time`, and `abnormalReturn`. Contains intraday abnormal returns for multiple estimation days (e.g., 20 trading days before the event). Each day has the same time grid. |
| `event_window` | A data frame with columns `time` and `abnormalReturn` for the single event day. |
| `event_times` | A character vector of intraday times at which events occurred (e.g., `c("10:00", "14:30")`). |

The `time` column should be a character representation of the intraday time (e.g., `"10:15"`, `"14:30"`), consistent across all days. The `abnormalReturn` column contains the pre-computed abnormal return for each bar.

```{r data-structure}
library(tibble)

# Estimation window: 20 days x 50 bars per day
estimation_window <- tibble(
  day  = rep(1:20, each = 50),
  time = rep(sprintf("%02d:%02d", 10L + 1:50 %/% 60L, 1:50 %% 60L), 20),
  abnormalReturn = rnorm(1000, 0, 0.001)
)

# Event window: single day, same 50 bars
event_window <- tibble(
  time = sprintf("%02d:%02d", 10L + 1:50 %/% 60L, 1:50 %% 60L),
  abnormalReturn = rnorm(50, 0, 0.001)
)

# Event times to test
event_times <- c("10:10", "10:30")
```

# The IntradayEventStudyTask

For studies where you want to use the full pipeline (return computation, window assignment), start with `IntradayEventStudyTask`:

```{r task-workflow}
library(EventStudy)

# Firm intraday data
firm_data <- tibble(
  symbol    = "FIRM_A",
  timestamp = seq(as.POSIXct("2020-06-15 09:30:00", tz = "UTC"),
                  by = "1 min", length.out = 500),
  price     = 100 * cumprod(1 + rnorm(500, 0, 0.001))
)

# Index intraday data
index_data <- tibble(
  symbol    = "INDEX_1",
  timestamp = seq(as.POSIXct("2020-06-15 09:30:00", tz = "UTC"),
                  by = "1 min", length.out = 500),
  price     = 1000 * cumprod(1 + rnorm(500, 0, 0.0008))
)

# Request table
request <- tibble(
  event_id                 = 1L,
  firm_symbol              = "FIRM_A",
  index_symbol             = "INDEX_1",
  event_timestamp          = as.POSIXct("2020-06-15 14:30:00", tz = "UTC"),
  group                    = "Earnings",
  event_window_start       = -30L,
  event_window_end         = 30L,
  shift_estimation_window  = -31L,
  estimation_window_length = 120L
)

# Create task and prepare
task <- IntradayEventStudyTask$new(firm_data, index_data, request)
ps   <- ParameterSet$new()
task <- prepare_intraday_event_study(task, ps)
```

The prepared task contains returns (`firm_returns`, `index_returns`) and window flags (`event_window`, `estimation_window`) in the nested data column.

# Non-Parametric Test

The `nonparametric_intraday_test()` function implements the algorithm of Rinaudo & Saha (2014). The key idea:

1. For each event time, accumulate **cumulative abnormal returns (CARs)** observation-by-observation on the event day.
2. In parallel, accumulate CARs for the same time range on each estimation day.
3. After an initial window (default 5 bars), compare the event-day CAR to the **empirical percentile** of the estimation-day CARs.
4. If the event-day CAR falls outside the confidence band, the event is **significant** at that point. The algorithm continues extending the window as long as significance holds.
5. When significance is lost, the window stops growing.

This approach is **non-parametric**: no distributional assumptions are needed. The confidence band comes directly from the empirical distribution of estimation-day CARs.

```{r run-test}
results <- nonparametric_intraday_test(
  estimation_window = estimation_window,
  event_window      = event_window,
  event_times       = event_times,
  p                 = 0.05,
  init_window       = 5L,
  upper             = FALSE
)
```

Parameters:

- **`p`**: Significance level (default 0.05). Smaller values require more extreme event-day CARs.
- **`init_window`**: Number of bars to accumulate before the first significance check (default 5).
- **`upper`**: If `TRUE`, test the upper tail (positive abnormal returns); if `FALSE` (default), test the lower tail (negative abnormal returns).

# Interpreting Results

The function returns a **named list** of tibbles, one per event time:

```{r inspect-results}
results[["10:10"]]
#> # A tibble: 12 x 4
#>       id      CAR       CI    fitCI
#>    <int>    <dbl>    <dbl>    <dbl>
#>  1     1 -0.00823 -0.00654 -0.00671
#>  2     2 -0.00912 -0.00701 -0.00698
#>  ...
```

| Column | Description |
|--------|-------------|
| `id` | Observation index within the significant window (1, 2, ...) |
| `CAR` | Cumulative abnormal return on the event day |
| `CI` | Raw empirical confidence interval boundary from estimation days |
| `fitCI` | Polynomial-smoothed CI (degree-4 polynomial) for smoother visualization |

When an event time is **not significant**, the result is a single-row tibble with all values equal to zero:

```{r non-significant}
results[["10:30"]]
#> # A tibble: 1 x 4
#>      id   CAR    CI fitCI
#>   <int> <dbl> <dbl> <dbl>
#> 1     1     0     0     0
```

The `fitCI` column applies a degree-4 polynomial smoothing to the raw CI values. This removes noise from the empirical percentile boundary and produces cleaner confidence bands for visualization.

# Plotting Results

The CAR path with confidence bands can be visualized with `ggplot2`:
```{r plot-results, fig.width=7, fig.height=4}
library(ggplot2)

plot_np_result <- function(result_tbl, event_time) {
  if (nrow(result_tbl) <= 1 && result_tbl$CAR[1] == 0) {
    message("Event at ", event_time, " is not significant.")
    return(invisible(NULL))
  }

  ggplot(result_tbl, aes(x = id)) +
    geom_line(aes(y = CAR), colour = "steelblue", linewidth = 1) +
    geom_line(aes(y = fitCI), colour = "firebrick", linetype = "dashed",
              linewidth = 0.8) +
    geom_ribbon(aes(ymin = fitCI, ymax = 0), alpha = 0.1, fill = "firebrick") +
    labs(
      title = paste("Non-Parametric Intraday CAR — Event at", event_time),
      x     = "Bars after event",
      y     = "Cumulative Abnormal Return"
    ) +
    theme_minimal()
}

# Plot the first significant event
plot_np_result(results[["10:10"]], "10:10")
```

# References

- Rinaudo, J.B. & Saha, A. (2014). Non-parametric intraday event studies.
- MacKinlay, A.C. (1997). Event Studies in Economics and Finance. *Journal of Economic Literature*, 35(1), 13--39.
