tests/test.sh exit code (0 → resolved); the classification below is post-hoc and cannot change it.cd /app && ls -la && echo "---DATA---" && ls -la data/ && echo "---R VERSION---" && Rscript --version 2>&1 | head -5
total 29 drwxrws--- 4 h2tagent h2tagent 4096 Jun 20 17:04 . drwxr-xr-x 26 root root 4096 Jun 20 17:55 .. -rw-rw---- 1 h2tagent h2tagent 12723 Jun 20 17:03 analysis.R drwxrws--- 2 h2tagent h2tagent 4096 Jun 20 17:04 data drwxrws--- 2 h2tagent h2tagent 4096 Jun 20 17:04 outputs ---DATA--- total 366 drwxrws--- 2 h2tagent h2tagent 4096 Jun 20 17:04 . drwxrws--- 4 h2tagent h2tagent 4096 Jun 20 17:04 .. -rw-rw---- 1 h2tagent h2tagent 903 Jun 20 17:03 firms.csv -rw-rw---- 1 h2tagent h2tagent 1716 Jun 20 17:03 recalls.csv -rw-rw---- 1 h2tagent h2tagent 363097 Jun 20 17:03 stock_returns.csv ---R VERSION--- Rscript (R) version 4.2.2 Patched (2022-11-10 r83330)
cd /app && echo "===STOCK_RETURNS HEAD===" && head -5 data/stock_returns.csv && echo "===RECALLS HEAD===" && head -5 data/recalls.csv && echo "===FIRMS HEAD===" && head -5 data/firms.csv && echo "===COUNTS===" && wc -l data/*.csv
===STOCK_RETURNS HEAD===
firm_id,date,return,market_return
FIRM_004,2006-11-22,0.005192,-0.004466
FIRM_008,2006-12-11,0.033288,0.00112
FIRM_017,2007-05-16,-0.002451,0.026981
FIRM_018,2007-02-14,0.022314,0.033039
===RECALLS HEAD===
event_id,firm_id,date,hazard_type,units_recalled,media_articles,severity_score
1,FIRM_007,2007-03-21,burn,9186546,5,7.17
2,FIRM_002,2007-03-28,laceration,75169,3,1.21
3,FIRM_017,2007-04-16,choking,637374,4,4.6
4,FIRM_013,2007-04-20,lead_paint,681155,5,8.88
===FIRMS HEAD===
firm_id,market_cap_mm,sic_code,beta,alpha,idio_vol,n_recalls
FIRM_001,2606.23,3944,1.043,-0.000135,0.0152,4
FIRM_002,519.06,3944,0.6799,-0.000219,0.0187,2
FIRM_003,4449.48,3944,0.6878,-3.8e-05,0.0252,0
FIRM_004,5589.68,3942,1.4253,0.000847,0.0183,0
===COUNTS===
19 data/firms.csv
36 data/recalls.csv
9365 data/stock_returns.csv
9420 totalcd /app && cat analysis.R
suppressPackageStartupMessages({
library(jsonlite)
library(ggplot2)
})
EST_LEN <- 200
EST_GAP <- 30
# =====================================================================
# 1. Load and clean
# =====================================================================
returns <- read.csv("/app/data/stock_returns.csv")
recalls <- read.csv("/app/data/recalls.csv")
firms <- read.csv("/app/data/firms.csv")
returns <- returns[!is.na(returns$return) & !is.na(returns$market_return), ]
returns <- returns[!duplicated(returns), ]
returns$date <- as.Date(returns$date)
returns <- returns[order(returns$firm_id, returns$date), ]
rownames(returns) <- NULL
recalls <- recalls[!duplicated(recalls), ]
recalls$date <- as.Date(recalls$date)
firms <- firms[!duplicated(firms), ]
n_events <- nrow(recalls)
n_firms <- nrow(firms)
n_firms_with_recalls <- length(unique(recalls$firm_id))
all_dates <- sort(unique(returns$date))
date_to_idx <- setNames(seq_along(all_dates) - 1L, as.character(all_dates))
# =====================================================================
# 2. Market model , basic OLS, returns raw AR (no Patell standardization)
# =====================================================================
event_market_model <- function(fid, eidx) {
est_end <- eidx - EST_GAP - 1
est_start <- est_end - EST_LEN + 1
if (est_start < 0) return(NULL)
est_dates <- all_dates[(est_start + 1):(est_end + 1)]
sub <- returns[returns$firm_id == fid & returns$date %in% est_dates, ]
if (nrow(sub) < 100) return(NULL)
m <- lm(return ~ market_return, data = sub)
list(alpha = unname(coef(m)[1]), beta = unname(coef(m)[2]),
sigma_eps = sd(resid(m)), n_est = nrow(sub),
mean_rm = mean(sub$market_return),
sum_sq_dev_rm = sum((sub$market_return - mean(sub$market_return))^2))
}
windows <- list(w3 = c(-1, 1), w2 = c(0, 1), w11 = c(-5, 5))
event_rows <- list()
daily_long <- list()
for (i in seq_len(n_events)) {
fid <- recalls$firm_id[i]
edate <- recalls$date[i]
estr <- as.character(edate)
if (!(estr %in% names(date_to_idx))) next
eidx <- as.integer(date_to_idx[estr])
m <- event_market_model(fid, eidx)
if (is.null(m)) next
firm <- returns[returns$firm_id == fid, ]
rownames(firm) <- as.character(firm$date)
cars <- list(); ar_day0 <- NA_real_; valid_w3 <- TRUE
for (wname in names(windows)) {
w <- windows[[wname]]; ars <- numeric(0); ok <- TRUE
for (off in seq.int(w[1], w[2])) {
tidx <- eidx + off
if (tidx < 0 || tidx >= length(all_dates)) { ok <- FALSE; break }
target <- all_dates[tidx + 1]
if (!(as.character(target) %in% rownames(firm))) { ok <- FALSE; break }
rm_t <- firm[as.character(target), "market_return"]
ret_t <- firm[as.character(target), "return"]
ar <- ret_t - (m$alpha + m$beta * rm_t)
ars <- c(ars, ar)
if (wname == "w3" && off == 0) ar_day0 <- ar
}
if (!ok) {
if (wname == "w3") valid_w3 <- FALSE
cars[[wname]] <- NA_real_
} else {
# NOTE: simple summation of raw ARs , spec wants standardized SCAR
cars[[wname]] <- sum(ars)
}
}
if (!valid_w3 || is.na(cars$w3)) next
event_rows[[length(event_rows) + 1L]] <- data.frame(
event_id = as.integer(recalls$event_id[i]),
firm_id = fid,
date = estr,
scar_3day = cars$w3,
scar_2day = cars$w2,
scar_11day = cars$w11,
ar_day0 = ar_day0,
alpha_hat = m$alpha,
beta_hat = m$beta,
sigma_eps = m$sigma_eps,
n_est = m$n_est,
stringsAsFactors = FALSE
)
for (off in seq.int(-30L, 30L)) {
tidx <- eidx + off
if (tidx < 0 || tidx >= length(all_dates)) next
target <- all_dates[tidx + 1]
if (!(as.character(target) %in% rownames(firm))) next
daily_long[[length(daily_long) + 1L]] <- data.frame(
date = target, firm_id = fid,
ret = firm[as.character(target), "return"],
market_return = firm[as.character(target), "market_return"],
event_id =
…[truncated]cd /app && Rscript -e '
returns <- read.csv("data/stock_returns.csv")
cat("Total rows:", nrow(returns), "\n")
cat("NA return:", sum(is.na(returns$return)), "\n")
cat("NA market_return:", sum(is.na(returns$market_return)), "\n")
cat("Sentinel (return < -10):", sum(returns$return < -10, na.rm=TRUE), "\n")
cat("Duplicated rows:", sum(duplicated(returns)), "\n")
cat("Unique firms:", length(unique(returns$firm_id)), "\n")
cat("Date range:", min(returns$date), "to", max(returns$date), "\n")
print(summary(returns$return))
cat("--- firms per firm counts ---\n")
print(table(returns$firm_id))
'Total rows: 9364
NA return: 0
NA market_return: 3
Sentinel (return < -10): 5
Duplicated rows: 4
Unique firms: 18
Date range: 2006-01-03 to 2007-12-31
Min. 1st Qu. Median Mean 3rd Qu. Max.
-99.00000 -0.02424 -0.00124 -0.05390 0.02130 0.14049
--- firms per firm counts ---
FIRM_001 FIRM_002 FIRM_003 FIRM_004 FIRM_005 FIRM_006 FIRM_007 FIRM_008
520 520 520 520 520 520 520 520
FIRM_009 FIRM_010 FIRM_011 FIRM_012 FIRM_013 FIRM_014 FIRM_015 FIRM_016
520 521 520 520 520 520 521 521
FIRM_017 FIRM_018
520 521cd /app && Rscript -e '
returns <- read.csv("data/stock_returns.csv")
# After cleaning
returns <- returns[!is.na(returns$return) & !is.na(returns$market_return), ]
returns <- returns[returns$return >= -10, ]
returns <- returns[!duplicated(returns), ]
cat("After cleaning rows:", nrow(returns), "\n")
print(table(returns$firm_id))
recalls <- read.csv("data/recalls.csv")
cat("--- recalls ---\n")
cat("n recalls:", nrow(recalls), "\n")
cat("dup recalls:", sum(duplicated(recalls)), "\n")
print(summary(recalls))
cat("hazard types:\n"); print(table(recalls$hazard_type))
firms <- read.csv("data/firms.csv")
cat("--- firms ---\n"); print(firms)
'After cleaning rows: 9352
FIRM_001 FIRM_002 FIRM_003 FIRM_004 FIRM_005 FIRM_006 FIRM_007 FIRM_008
520 520 520 519 520 519 520 520
FIRM_009 FIRM_010 FIRM_011 FIRM_012 FIRM_013 FIRM_014 FIRM_015 FIRM_016
520 518 520 520 519 520 518 520
FIRM_017 FIRM_018
519 520
--- recalls ---
n recalls: 35
dup recalls: 0
event_id firm_id date hazard_type
Min. : 1.0 Length:35 Length:35 Length:35
1st Qu.: 9.5 Class :character Class :character Class :character
Median :18.0 Mode :character Mode :character Mode :character
Mean :18.0
3rd Qu.:26.5
Max. :35.0
units_recalled media_articles severity_score
Min. : 67821 Min. : 3.000 Min. :1.070
1st Qu.: 492422 1st Qu.: 5.500 1st Qu.:3.955
Median : 1525113 Median : 7.000 Median :5.900
Mean : 3207624 Mean : 7.857 Mean :5.911
3rd Qu.: 4228210 3rd Qu.:10.000 3rd Qu.:8.110
Max. :19715949 Max. :13.000 Max. :9.800
hazard types:
burn chemical choking laceration lead_paint
3 4 10 6 12
--- firms ---
firm_id market_cap_mm sic_code beta alpha idio_vol n_recalls
1 FIRM_001 2606.23 3944 1.0430 -0.000135 0.0152 4
2 FIRM_002 519.06 3944 0.6799 -0.000219 0.0187 2
3 FIRM_003 4449.48 3944 0.6878 -0.000038 0.0252 0
4 FIRM_004 5589.68 3942 1.4253 0.000847 0.0183 0
5 FIRM_005 173.95 3944 1.4716 -0.000333 0.0235 3
6 FIRM_006 378.94 3944 1.3716 0.000584 0.0180 0
7 FIRM_007 2107.82 3944 0.7672 -0.000741 0.0203 2
8 FIRM_008 1237.08 3942 1.2161 -0.000067 0.0162 3
9 FIRM_009 1771.95 3944 1.1583 0.000181 0.0155 0
10 FIRM_010 649.59 3942 1.2093 0.000393 0.0156 2
11 FIRM_011 5194.09 3942 1.5357 0.000456 0.0155 1
12 FIRM_012 4597.88 3944 1.2118 0.000497 0.0152 2
13 FIRM_013 1957.13 3944 1.4395 -0.000074 0.0219 4
14 FIRM_014 6993.17 3944 1.1338 -0.000131 0.0168 5
15 FIRM_015 3168.49 3942 1.2446 0.000529 0.0165 1
16 FIRM_016 644.74 3942 1.4156 0.000004 0.0187 2
17 FIRM_017 2814.39 3944 0.3714 -0.000538 0.0157 3
18 FIRM_018 572.12 3942 0.9402 -0.000467 0.0227 1cd /app && Rscript -e '
returns <- read.csv("data/stock_returns.csv")
returns <- returns[!is.na(returns$return) & !is.na(returns$market_return), ]
returns <- returns[returns$return >= -10, ]
returns <- returns[!duplicated(returns), ]
returns$date <- as.Date(returns$date)
recalls <- read.csv("data/recalls.csv")
recalls$date <- as.Date(recalls$date)
all_dates <- sort(unique(returns$date))
cat("n global dates:", length(all_dates), "\n")
date_to_idx <- setNames(seq_along(all_dates)-1L, as.character(all_dates))
# event indices
for (i in 1:nrow(recalls)) {
estr <- as.character(recalls$date[i])
eidx <- if (estr %in% names(date_to_idx)) date_to_idx[estr] else NA
cat(sprintf("event %2d firm %s date %s idx %s\n", recalls$event_id[i], recalls$firm_id[i], estr, eidx))
}
' | head -40n global dates: 520 event 1 firm FIRM_007 date 2007-03-21 idx 316 event 2 firm FIRM_002 date 2007-03-28 idx 321 event 3 firm FIRM_017 date 2007-04-16 idx 334 event 4 firm FIRM_013 date 2007-04-20 idx 338 event 5 firm FIRM_014 date 2007-04-24 idx 340 event 6 firm FIRM_013 date 2007-05-04 idx 348 event 7 firm FIRM_012 date 2007-05-08 idx 350 event 8 firm FIRM_014 date 2007-05-16 idx 356 event 9 firm FIRM_001 date 2007-05-24 idx 362 event 10 firm FIRM_016 date 2007-05-30 idx 366 event 11 firm FIRM_014 date 2007-05-31 idx 367 event 12 firm FIRM_014 date 2007-06-13 idx 376 event 13 firm FIRM_008 date 2007-06-14 idx 377 event 14 firm FIRM_015 date 2007-06-15 idx 378 event 15 firm FIRM_007 date 2007-06-20 idx 381 event 16 firm FIRM_001 date 2007-06-25 idx 384 event 17 firm FIRM_017 date 2007-07-09 idx 394 event 18 firm FIRM_005 date 2007-07-13 idx 398 event 19 firm FIRM_002 date 2007-07-23 idx 404 event 20 firm FIRM_012 date 2007-08-09 idx 417 event 21 firm FIRM_005 date 2007-08-10 idx 418 event 22 firm FIRM_016 date 2007-08-13 idx 419 event 23 firm FIRM_008 date 2007-08-14 idx 420 event 24 firm FIRM_001 date 2007-08-21 idx 425 event 25 firm FIRM_018 date 2007-08-27 idx 429 event 26 firm FIRM_010 date 2007-09-04 idx 435 event 27 firm FIRM_005 date 2007-09-07 idx 438 event 28 firm FIRM_013 date 2007-09-18 idx 445 event 29 firm FIRM_010 date 2007-09-20 idx 447 event 30 firm FIRM_001 date 2007-09-25 idx 450 event 31 firm FIRM_014 date 2007-09-27 idx 452 event 32 firm FIRM_008 date 2007-11-07 idx 481 event 33 firm FIRM_013 date 2007-11-08 idx 482 event 34 firm FIRM_017 date 2007-11-09 idx 483 event 35 firm FIRM_011 date 2007-11-13 idx 485
cd /app && Rscript -e '
returns <- read.csv("data/stock_returns.csv")
returns <- returns[!is.na(returns$return) & !is.na(returns$market_return), ]
returns <- returns[returns$return >= -10, ]
returns <- returns[!duplicated(returns), ]
returns$date <- as.Date(returns$date)
recalls <- read.csv("data/recalls.csv"); recalls$date <- as.Date(recalls$date)
all_dates <- sort(unique(returns$date))
date_to_idx <- setNames(seq_along(all_dates)-1L, as.character(all_dates))
miss_any <- 0; est_counts <- c()
for (i in 1:nrow(recalls)) {
fid <- recalls$firm_id[i]; estr <- as.character(recalls$date[i])
eidx <- date_to_idx[estr]
fdates <- as.character(returns$date[returns$firm_id==fid])
# check windows -5..5 and -30..30 and est window
for (off in -5:5) { t <- eidx+off; if(t>=0 && t<length(all_dates)){ if(!(as.character(all_dates[t+1]) %in% fdates)) miss_any <- miss_any+1 } }
est_idx <- (eidx-230):(eidx-31)
present <- sum(as.character(all_dates[est_idx+1]) %in% fdates)
est_counts <- c(est_counts, present)
}
cat("missing event-window firm-dates (offsets -5..5 across all events):", miss_any, "\n")
cat("estimation window present counts: min", min(est_counts), "max", max(est_counts), "\n")
# per-firm missing dates vs global
for (f in sort(unique(returns$firm_id))) {
fd <- as.character(returns$date[returns$firm_id==f])
cat(f, "missing global dates:", length(setdiff(as.character(all_dates), fd)), "\n")
}
'missing event-window firm-dates (offsets -5..5 across all events): 0 estimation window present counts: min 199 max 200 FIRM_001 missing global dates: 0 FIRM_002 missing global dates: 0 FIRM_003 missing global dates: 0 FIRM_004 missing global dates: 1 FIRM_005 missing global dates: 0 FIRM_006 missing global dates: 1 FIRM_007 missing global dates: 0 FIRM_008 missing global dates: 0 FIRM_009 missing global dates: 0 FIRM_010 missing global dates: 2 FIRM_011 missing global dates: 0 FIRM_012 missing global dates: 0 FIRM_013 missing global dates: 1 FIRM_014 missing global dates: 0 FIRM_015 missing global dates: 2 FIRM_016 missing global dates: 0 FIRM_017 missing global dates: 1 FIRM_018 missing global dates: 0
cd /app && Rscript -e '
returns <- read.csv("data/stock_returns.csv")
returns <- returns[!is.na(returns$return) & !is.na(returns$market_return), ]
returns <- returns[returns$return >= -10, ]
returns <- returns[!duplicated(returns), ]
returns$date <- as.Date(returns$date)
# market_return uniqueness per date?
agg <- aggregate(market_return ~ date, data=returns, FUN=function(x) length(unique(round(x,8))))
cat("dates with >1 distinct market_return:", sum(agg$market_return>1), "\n")
# rho_bar across all 18 firms
firms_list <- sort(unique(returns$firm_id))
res_by_firm <- list()
for (f in firms_list){ sub <- returns[returns$firm_id==f,]; if(nrow(sub)<30) next; m<-lm(return~market_return,data=sub); res_by_firm[[f]] <- setNames(resid(m), as.character(sub$date)) }
cors <- c()
fn <- names(res_by_firm)
for (a in 1:(length(fn)-1)) for (b in (a+1):length(fn)){
r1<-res_by_firm[[fn[a]]]; r2<-res_by_firm[[fn[b]]]
cd<-intersect(names(r1),names(r2)); if(length(cd)<20) next
cors<-c(cors, cor(r1[cd],r2[cd]))
}
cat("n pairs:", length(cors), " rho_bar:", mean(cors), "\n")
# Andrews lag for N=35
N<-35; cat("hac_lag:", floor(4*(N/100)^(2/9)), "\n")
cat("sandwich installed:", requireNamespace("sandwich", quietly=TRUE), "\n")
cat("ggplot2 installed:", requireNamespace("ggplot2", quietly=TRUE), "\n")
'dates with >1 distinct market_return: 0 n pairs: 153 rho_bar: 0.005682146 hac_lag: 3 sandwich installed: FALSE ggplot2 installed: TRUE
command: create
path: /app/analysis.R
file_text: suppressPackageStartupMessages({
library(jsonlite)
library(ggplot2)
})
# =====================================================================
# Product-recall stock-price event study
# Deterministic base-R implementation (no random seeds, no bootstrap).
# =====================================================================
EST_LEN <- 200 # estimation window length (trading days)
EST_GAP <- 30 # gap (trading days) between estimation window end and event
# =====================================================================
# 1. Load and clean
# - drop NA returns / market returns
# - drop sentinel returns (return < -10)
# - drop exact duplicate rows
# - sort by (firm_id, date)
# =====================================================================
returns <- read.csv("/app/data/stock_returns.csv", stringsAsFactors = FALSE)
recalls <- read.csv("/app/data/recalls.csv", stringsAsFactors = FALSE)
firms <- read.csv("/app/data/firms.csv", stringsAsFactors = FALSE)
returns <- returns[!is.na(returns$return) & !is.na(returns$market_return), ]
returns <- returns[returns$return >= -10, ] # remove sentinel returns
returns <- returns[!duplicated(returns), ] # remove duplicate rows
returns$date <- as.Date(returns$date)
returns <- returns[order(returns$firm_id, returns$date), ]
rownames(returns) <- NULL
recalls <- recalls[!duplicated(recalls), ]
recalls$date <- as.Date(recalls$date)
recalls <- recalls[order(recalls$event_id), ]
firms <- firms[!duplicated(firms), ]
n_events <- nrow(recalls)
n_firms <- nrow(firms)
n_firms_with_recalls <- length(unique(recalls$firm_id))
# Global trading-day calendar (0-based index) used for all windowing.
all_dates <- sort(unique(returns$date))
n_dates <- length(all_dates)
date_to_idx <- setNames(seq_along(all_dates) - 1L, as.character(all_dates))
# Fast per-firm lookup of return / market_return keyed by date string.
returns_by_firm <- split(returns, returns$firm_id)
firm_ret <- lapply(returns_by_firm, function(d) setNames(d$return, as.character(d$date)))
firm_mkt <- lapply(returns_by_firm, function(d) setNames(d$market_return, as.character(d$date)))
# =====================================================================
# 2. Market model + prediction-error-corrected SCARs
# =====================================================================
# Estimation window: 200 trading days ending 30 days before the event,
# i.e. global indices [eidx-230, eidx-31]; require >= 100 valid obs.
event_market_model <- function(fid, eidx) {
est_end <- eidx - EST_GAP - 1L
est_start <- est_end - EST_LEN + 1L
if (est_start < 0L) return(NULL)
est_dates <- as.character(all_dates[(est_start + 1L):(est_end + 1L)])
rv <- firm_ret[[fid]]; mv <- firm_mkt[[fid]]
have <- est_dates[est_dates %in% names(rv)]
if (length(have) < 100L) return(NULL)
y <- rv[have]; x <- mv[have]
m <- lm(y ~ x)
b <- unname(coef(m))
res <- unname(resid(m))
L <- length(have)
sigma2 <- sum(res^2) / (L - 2) # OLS residual variance (ddof = 2)
xbar <- mean(x)
list(alpha = b[1], beta = b[2], sigma2 = sigma2, sigma = sqrt(sigma2),
n_est = L, xbar = xbar, Sxx = sum((x - xbar)^2),
resid = res)
}
windows <- list(w3 = c(-1L, 1L), w2 = c(0L, 1L), w11 = c(-5L, 5L))
win_names <- c(w3 = "window_3day", w2 = "window_2day", w11 = "window_11day")
event_models <- vector("list", n_events) # cache for reuse in plots
event_rows <- list()
for (i in seq_len(n_events)) {
fid <- recalls$firm_id[i]
estr <- as.character(recalls$date[i])
if (!(estr %in% names(date_to_idx))) next
if (is.null(firm_ret[[fid]])) next
eidx <- as.integer(date_to_idx[estr])
m <- event_market_model(fid, eidx)
if (is.null(m)) next
event_models[[i]] <- list(m = m, eidx = eidx, fid = fid)
rv <- firm_ret[[fid]]; mv <- firm_mkt[[fid]]
scar <- list(); ar_day0 <- NA_real_; ar_w3 <- numeric(0)
for (wn in names(windows)) {
w
…[truncated]Existing file was successfully overwritten at /app/analysis.R
cd /app && rm -f outputs/* && Rscript analysis.R 2>&1
Pipeline complete. events=35 valid=35 firms=18 (with recalls=14) rho_bar=0.005682 hac_lag=3 calendar n_days=230
cd /app && cat outputs/event_study_results.json
{
"n_events": 35,
"n_firms": 18,
"n_firms_with_recalls": 14,
"n_valid_events": 35,
"rho_bar": 0.005682,
"aggregate_scars": {
"window_3day": {
"mean_scar": -1.007522,
"patell_z": -5.960582,
"bmp_t": -4.999559,
"kp_t": -4.576952,
"pct_negative": 0.828571,
"n": 35
},
"window_2day": {
"mean_scar": -0.860918,
"patell_z": -5.093259,
"bmp_t": -4.514834,
"kp_t": -4.1332,
"pct_negative": 0.8,
"n": 35
},
"window_11day": {
"mean_scar": -0.439603,
"patell_z": -2.600725,
"bmp_t": -2.175121,
"kp_t": -1.991261,
"pct_negative": 0.628571,
"n": 35
}
},
"cross_section_hac": {
"intercept": 6.821578,
"log_units": -0.274975,
"media": -0.139001,
"severity": -0.048798,
"log_mcap": -0.348291,
"se_intercept": 1.974528,
"se_log_units": 0.142872,
"se_media": 0.045105,
"se_severity": 0.079198,
"se_log_mcap": 0.108597,
"r_squared": 0.365058,
"n_obs": 35,
"hac_lag": 3
},
"cross_section_wls": {
"intercept": 6.136437,
"log_units": -0.243218,
"media": -0.141911,
"severity": -0.068259,
"log_mcap": -0.300782,
"se_intercept": 2.362232,
"se_log_units": 0.119351,
"se_media": 0.062706,
"se_severity": 0.073877,
"se_log_mcap": 0.191097,
"r_squared": 0.307122,
"n_obs": 35
},
"calendar_time": {
"alpha": -0.000858,
"beta": 1.033203,
"t_alpha": -1.34566,
"p_value": 0.179749,
"n_days": 230
},
"corrado_rank": {
"z_stat": -2.426518,
"p_value": 0.015244
},
"cowan_sign": {
"z_stat": 0.253547,
"p_value": 0.799846,
"p_bar": 0.492859,
"frac_positive": 0.514286
},
"schipper_thompson": {
"wald_stat": 25.70181,
"wald_p": 1.1e-05,
"df": 3
}
}cd /app && Rscript -e '
suppressPackageStartupMessages(library(jsonlite))
r <- fromJSON("outputs/event_study_results.json")
ev <- read.csv("outputs/event_cars.csv")
# 1) BMP t should equal one-sample t-test on SCAR
cat("BMP check (3day): t.test=", unname(t.test(ev$scar_3day)$statistic),
" reported=", r$aggregate_scars$window_3day$bmp_t, "\n")
# 2) Patell = sum/sqrt(N)
cat("Patell check: ", sum(ev$scar_3day)/sqrt(nrow(ev)), " reported=", r$aggregate_scars$window_3day$patell_z, "\n")
# 3) pct_negative
cat("pct_neg check:", mean(ev$scar_3day<0), "\n")
# 4) Schipper-Thompson manual
M <- as.matrix(ev[,c("scar_3day","scar_2day","scar_11day")]); N<-nrow(M)
mb <- colMeans(M); S<-cov(M); W <- as.numeric(N*t(mb)%*%solve(S)%*%mb)
cat("ST wald:", W, " p=", 1-pchisq(W,3), " reported=", r$schipper_thompson$wald_stat, "\n")
cat("n rows event_cars:", nrow(ev), "\n")
print(head(ev,3))
'BMP check (3day): t.test= -4.999559 reported= -4.999559 Patell check: -5.960582 reported= -5.960582 pct_neg check: 0.8285714 ST wald: 25.70181 p= 1.101162e-05 reported= 25.70181 n rows event_cars: 35 event_id firm_id date scar_3day scar_2day scar_11day ar_day0 1 1 FIRM_007 2007-03-21 -2.761691 -2.026308 -2.690196 -0.018804840 2 2 FIRM_002 2007-03-28 1.641089 2.188681 1.395762 0.051341079 3 3 FIRM_017 2007-04-16 0.941157 1.028888 1.466067 0.005609555
cd /app && Rscript -e '
returns <- read.csv("data/stock_returns.csv", stringsAsFactors=FALSE)
returns <- returns[!is.na(returns$return)&!is.na(returns$market_return),]
returns <- returns[returns$return>=-10,]; returns<-returns[!duplicated(returns),]
returns$date<-as.Date(returns$date); returns<-returns[order(returns$firm_id,returns$date),]
recalls<-read.csv("data/recalls.csv",stringsAsFactors=FALSE); recalls$date<-as.Date(recalls$date)
all_dates<-sort(unique(returns$date)); d2i<-setNames(seq_along(all_dates)-1L,as.character(all_dates))
# Event 1: FIRM_007, 2007-03-21, idx 316
fid<-"FIRM_007"; eidx<-316
est_end<-eidx-31; est_start<-est_end-199
ed<-as.character(all_dates[(est_start+1):(est_end+1)])
sub<-returns[returns$firm_id==fid & as.character(returns$date)%in%ed,]
m<-lm(return~market_return,data=sub); a<-coef(m)[1]; b<-coef(m)[2]
L<-nrow(sub); s2<-sum(resid(m)^2)/(L-2); xbar<-mean(sub$market_return); Sxx<-sum((sub$market_return-xbar)^2)
cat("L=",L," alpha=",a," beta=",b," sigma2=",s2,"\n")
# 3-day window offsets -1,0,1
car<-0; Bm<-0
for(off in -1:1){ td<-as.character(all_dates[eidx+off+1]); row<-returns[returns$firm_id==fid&as.character(returns$date)==td,]
ar<-row$return-(a+b*row$market_return); car<-car+ar; Bm<-Bm+(row$market_return-xbar) }
K<-3; scar<-car/sqrt(s2*(K+K^2/L+Bm^2/Sxx))
cat("SCAR_3day event1 =", scar, " (expected -2.761691)\n")
# HAC independent check via explicit sandwich-style formula with lag 3
ev<-read.csv("outputs/event_cars.csv"); firms<-read.csv("data/firms.csv")
cs<-merge(ev[,c("event_id","firm_id","scar_3day")],recalls[,c("event_id","units_recalled","media_articles","severity_score")],by="event_id")
cs<-merge(cs,firms[,c("firm_id","market_cap_mm","idio_vol")],by="firm_id")
cs$log_units<-log(cs$units_recalled); cs$log_mcap<-log(cs$market_cap_mm); cs<-cs[order(cs$event_id),]
X<-model.matrix(~log_units+media_articles+severity_score+log_mcap,data=cs); y<-cs$scar_3day
n<-nrow(X); k<-ncol(X); bi<-solve(t(X)%*%X); bh<-bi%*%t(X)%*%y; u<-as.vector(y-X%*%bh)
Lg<-3; meat<-matrix(0,k,k)
for(t in 1:n) meat<-meat+u[t]^2*(X[t,]%*%t(X[t,]))
for(l in 1:Lg){wl<-1-l/(Lg+1); G<-matrix(0,k,k); for(t in (l+1):n) G<-G+u[t]*u[t-l]*(X[t,]%*%t(X[t-l,])); meat<-meat+wl*(G+t(G))}
V<-bi%*%meat%*%bi; cat("HAC se:", sqrt(diag(V)),"\n")
'L= 200 alpha= -0.002097156 beta= 0.7896948 sigma2= 0.0003974366 SCAR_3day event1 = -2.761691 (expected -2.761691) HAC se: 1.974528 0.1428724 0.04510496 0.07919782 0.1085973
cd /app && ls -la outputs/ && file outputs/*.png
total 157 drwxrws--- 2 h2tagent h2tagent 4096 Jun 20 18:00 . drwxrws--- 4 h2tagent h2tagent 4096 Jun 20 18:00 .. -rw-r--r-- 1 h2tagent h2tagent 49070 Jun 20 18:00 car_timeline.png -rw-r--r-- 1 h2tagent h2tagent 3619 Jun 20 18:00 event_cars.csv -rw-r--r-- 1 h2tagent h2tagent 1837 Jun 20 18:00 event_study_results.json -rw-r--r-- 1 h2tagent h2tagent 97162 Jun 20 18:00 portfolio_comparison.png [stderr] bash: file: command not found
command: view path: /app/outputs/car_timeline.png
<output_image> …[truncated]
cd /app && Rscript -e 'packageVersion("ggplot2")'[1] ‘3.4.1’
command: str_replace
path: /app/analysis.R
old_str: x = "Trading days relative to recall announcement (day 0)",
y = "Average cumulative abnormal return") +
theme_minimal(base_size = 12)
ggsave("/app/outputs/car_timeline.png", p1, width = 8, height = 5, dpi = 120)
new_str: x = "Trading days relative to recall announcement (day 0)",
y = "Average cumulative abnormal return") +
theme_minimal(base_size = 12) +
theme(plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "white", color = NA))
ggsave("/app/outputs/car_timeline.png", p1, width = 8, height = 5, dpi = 120, bg = "white")The file /app/analysis.R has been edited successfully.
command: str_replace
path: /app/analysis.R
old_str: x = "Date", y = "Cumulative simple return", color = "Series") +
theme_minimal(base_size = 12) +
theme(legend.position = "bottom")
ggsave("/app/outputs/portfolio_comparison.png", p2, width = 10, height = 5, dpi = 120)
new_str: x = "Date", y = "Cumulative simple return", color = "Series") +
theme_minimal(base_size = 12) +
theme(legend.position = "bottom",
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "white", color = NA))
ggsave("/app/outputs/portfolio_comparison.png", p2, width = 10, height = 5, dpi = 120, bg = "white")The file /app/analysis.R has been edited successfully.
cd /app && rm -f outputs/*.png && Rscript analysis.R 2>&1 | tail -2
events=35 valid=35 firms=18 (with recalls=14) rho_bar=0.005682 hac_lag=3 calendar n_days=230
command: view path: /app/outputs/car_timeline.png
<output_image> …[truncated]
command: view path: /app/outputs/portfolio_comparison.png
<output_image> …[truncated]
cd /app && Rscript -e '
returns <- read.csv("data/stock_returns.csv", stringsAsFactors=FALSE)
returns <- returns[!is.na(returns$return)&!is.na(returns$market_return),]
returns <- returns[returns$return>=-10,]; returns<-returns[!duplicated(returns),]
returns$date<-as.Date(returns$date); returns<-returns[order(returns$firm_id,returns$date),]
recalls<-read.csv("data/recalls.csv",stringsAsFactors=FALSE); recalls$date<-as.Date(recalls$date)
recalls<-recalls[order(recalls$event_id),]
all_dates<-sort(unique(returns$date)); d2i<-setNames(seq_along(all_dates)-1L,as.character(all_dates)); nd<-length(all_dates)
fr<-lapply(split(returns,returns$firm_id),function(d) setNames(d$return,as.character(d$date)))
# Corrado independent
Ks<-c()
for(i in 1:nrow(recalls)){ fid<-recalls$firm_id[i]; eidx<-d2i[as.character(recalls$date[i])]; rv<-fr[[fid]]
vals<-c(); offs<-c()
for(off in -30:30){ t<-eidx+off; if(t<0||t>=nd) next; td<-as.character(all_dates[t+1]); if(!(td%in%names(rv))) next; vals<-c(vals,rv[[td]]); offs<-c(offs,off)}
n<-length(vals); rk<-rank(vals); mr<-(n+1)/2; sr<-sd(rk)
evk<-rk[offs%in%c(-1,0,1)]; Ks<-c(Ks,mean((evk-mr)/sr)) }
z<-mean(Ks)/(sd(Ks)/sqrt(length(Ks)))
cat("Corrado z=",z," p=",2*(1-pnorm(abs(z)))," N=",length(Ks),"\n")
cat("K event1=",Ks[1],"\n")
# WLS independent check via matrix algebra
ev<-read.csv("outputs/event_cars.csv"); firms<-read.csv("data/firms.csv")
cs<-merge(ev[,c("event_id","firm_id","scar_3day")],recalls[,c("event_id","units_recalled","media_articles","severity_score")],by="event_id")
cs<-merge(cs,firms[,c("firm_id","market_cap_mm","idio_vol")],by="firm_id"); cs$log_units<-log(cs$units_recalled); cs$log_mcap<-log(cs$market_cap_mm); cs<-cs[order(cs$event_id),]
X<-model.matrix(~log_units+media_articles+severity_score+log_mcap,cs); y<-cs$scar_3day; w<-1/cs$idio_vol^2; W<-diag(w)
XtWX_inv<-solve(t(X)%*%W%*%X); b<-XtWX_inv%*%t(X)%*%W%*%y; r<-as.vector(y-X%*%b)
s2<-sum(w*r^2)/(nrow(X)-ncol(X)); V<-s2*XtWX_inv
cat("WLS coef:",as.vector(b),"\n"); cat("WLS se:",sqrt(diag(V)),"\n")
'Corrado z= -2.426518 p= 0.01524449 N= 35 K event1= -0.863707 WLS coef: 6.136437 -0.2432183 -0.1419114 -0.0682589 -0.3007825 WLS se: 2.362232 0.1193509 0.06270618 0.07387705 0.1910968
cd /app && Rscript -e '
returns <- read.csv("data/stock_returns.csv", stringsAsFactors=FALSE)
returns <- returns[!is.na(returns$return)&!is.na(returns$market_return),]
returns <- returns[returns$return>=-10,]; returns<-returns[!duplicated(returns),]
returns$date<-as.Date(returns$date); returns<-returns[order(returns$firm_id,returns$date),]
recalls<-read.csv("data/recalls.csv",stringsAsFactors=FALSE); recalls$date<-as.Date(recalls$date)
all_dates<-sort(unique(returns$date)); d2i<-setNames(seq_along(all_dates)-1L,as.character(all_dates)); nd<-length(all_dates)
fr<-lapply(split(returns,returns$firm_id),function(d) setNames(d$return,as.character(d$date)))
# Cowan: refit each event model, compute p_hat (est window resid>0) & I (any AR>0 in [-1,1])
fei<-list(); for(i in 1:nrow(recalls)){f<-recalls$firm_id[i]; fei[[f]]<-c(fei[[f]],as.integer(d2i[as.character(recalls$date[i])]))}
phat<-c(); I<-c()
for(i in 1:nrow(recalls)){ fid<-recalls$firm_id[i]; eidx<-as.integer(d2i[as.character(recalls$date[i])]); rv<-fr[[fid]]
mv<-setNames(returns$market_return[returns$firm_id==fid],as.character(returns$date[returns$firm_id==fid]))
ee<-eidx-31; es<-ee-199; ed<-as.character(all_dates[(es+1):(ee+1)]); ed<-ed[ed%in%names(rv)]
if(length(ed)<50) next; m<-lm(rv[ed]~mv[ed]); a<-coef(m)[1]; b<-coef(m)[2]
phat<-c(phat, mean(resid(m)>0))
ar3<-c(); for(off in -1:1){td<-as.character(all_dates[eidx+off+1]); if(td%in%names(rv)) ar3<-c(ar3, rv[[td]]-(a+b*mv[[td]]))}
I<-c(I, as.integer(any(ar3>0))) }
pbar<-mean(phat); fp<-mean(I); N<-length(I); z<-(fp-pbar)/sqrt(pbar*(1-pbar)/N)
cat("Cowan: p_bar=",pbar," frac_pos=",fp," z=",z," p=",2*(1-pnorm(abs(z))),"\n")
# Calendar-time independent
fidx<-list(); for(i in 1:nrow(recalls)){f<-recalls$firm_id[i]; fidx[[f]]<-c(fidx[[f]],as.integer(d2i[as.character(recalls$date[i])]))}
mkt<-tapply(returns$market_return,as.character(returns$date),function(z)z[1])
pr<-c(); pm<-c()
for(d in 0:(nd-1)){ incl<-c(); for(f in names(fidx)) if(any(abs(fidx[[f]]-d)<=30)) incl<-c(incl,f)
if(!length(incl)) next; dd<-as.character(all_dates[d+1]); rr<-c(); for(f in incl){rv<-fr[[f]]; if(dd%in%names(rv)) rr<-c(rr,rv[[dd]])}
if(!length(rr)) next; pr<-c(pr,mean(rr)); pm<-c(pm,as.numeric(mkt[[dd]])) }
cm<-lm(pr~pm); s<-summary(cm)
cat("Calendar: alpha=",coef(cm)[1]," beta=",coef(cm)[2]," t=",s$coefficients[1,3]," p=",s$coefficients[1,4]," n=",length(pr),"\n")
'Cowan: p_bar= 0.4928593 frac_pos= 0.5142857 z= 0.2535467 p= 0.7998458 Calendar: alpha= -0.0008577709 beta= 1.033203 t= -1.34566 p= 0.1797488 n= 230
cd /app && Rscript analysis.R >/dev/null 2>&1 && cp outputs/event_study_results.json /tmp/run1.json && cp outputs/event_cars.csv /tmp/run1.csv && Rscript analysis.R >/dev/null 2>&1 && diff /tmp/run1.json outputs/event_study_results.json && diff /tmp/run1.csv outputs/event_cars.csv && echo "DETERMINISTIC: outputs identical across runs"
DETERMINISTIC: outputs identical across runs
cd /app && Rscript -e '
suppressPackageStartupMessages(library(jsonlite)); r<-fromJSON("outputs/event_study_results.json")
h<-r$cross_section_hac
cat("HAC t-stats:\n")
for(v in c("log_units","media","severity","log_mcap","intercept")){
cat(sprintf(" %-10s coef=% .4f se=%.4f t=% .2f\n",v,h[[v]],h[[paste0("se_",v)]],h[[v]]/h[[paste0("se_",v)]]))
}
# average raw 3-day CAR (not standardized) for economic magnitude
returns <- read.csv("data/stock_returns.csv",stringsAsFactors=FALSE)
returns <- returns[!is.na(returns$return)&!is.na(returns$market_return),]; returns<-returns[returns$return>=-10,]; returns<-returns[!duplicated(returns),]
returns$date<-as.Date(returns$date)
recalls<-read.csv("data/recalls.csv",stringsAsFactors=FALSE); recalls$date<-as.Date(recalls$date)
all_dates<-sort(unique(returns$date)); d2i<-setNames(seq_along(all_dates)-1L,as.character(all_dates)); nd<-length(all_dates)
fr<-lapply(split(returns,returns$firm_id),function(d)setNames(d$return,as.character(d$date)))
fm<-lapply(split(returns,returns$firm_id),function(d)setNames(d$market_return,as.character(d$date)))
raw3<-c()
for(i in 1:nrow(recalls)){fid<-recalls$firm_id[i]; eidx<-as.integer(d2i[as.character(recalls$date[i])]); rv<-fr[[fid]]; mv<-fm[[fid]]
ee<-eidx-31; es<-ee-199; ed<-as.character(all_dates[(es+1):(ee+1)]); ed<-ed[ed%in%names(rv)]; m<-lm(rv[ed]~mv[ed]); a<-coef(m)[1]; b<-coef(m)[2]
car<-0; for(off in -1:1){td<-as.character(all_dates[eidx+off+1]); car<-car+(rv[[td]]-(a+b*mv[[td]]))}; raw3<-c(raw3,car)}
cat(sprintf("\nMean raw 3-day CAR = %.4f (%.2f%%); median = %.4f\n",mean(raw3),100*mean(raw3),median(raw3)))
cat(sprintf("Calendar alpha/day = %.5f => annualized ~ %.1f%%\n",r$calendar_time$alpha,100*((1+r$calendar_time$alpha)^252-1)))
'HAC t-stats: log_units coef=-0.2750 se=0.1429 t=-1.92 media coef=-0.1390 se=0.0451 t=-3.08 severity coef=-0.0488 se=0.0792 t=-0.62 log_mcap coef=-0.3483 se=0.1086 t=-3.21 intercept coef= 6.8216 se=1.9745 t= 3.45 Mean raw 3-day CAR = -0.0292 (-2.92%); median = -0.0277 Calendar alpha/day = -0.00086 => annualized ~ -19.5%
cd /app && Rscript -e '
ev<-read.csv("outputs/event_cars.csv"); rc<-read.csv("data/recalls.csv"); fm<-read.csv("data/firms.csv")
cs<-merge(ev,rc,by="event_id"); cs<-merge(cs,fm,by="firm_id")
cs$log_units<-log(cs$units_recalled); cs$log_mcap<-log(cs$market_cap_mm)
M<-cs[,c("log_units","media_articles","severity_score","log_mcap")]
cat("Correlation matrix of regressors:\n"); print(round(cor(M),2))
cat(sprintf("\ncor(idio_vol, log_mcap) = %.2f\n", cor(cs$idio_vol, cs$log_mcap)))
'[stderr] Error in fix.by(by.x, x) : 'by' must specify a uniquely valid column Calls: merge -> merge.data.frame -> fix.by Execution halted
cd /app && Rscript -e '
ev<-read.csv("outputs/event_cars.csv"); rc<-read.csv("data/recalls.csv"); fm<-read.csv("data/firms.csv")
cs<-merge(ev[,c("event_id","firm_id")],rc[,c("event_id","units_recalled","media_articles","severity_score")],by="event_id")
cs<-merge(cs,fm[,c("firm_id","market_cap_mm","idio_vol")],by="firm_id")
cs$log_units<-log(cs$units_recalled); cs$log_mcap<-log(cs$market_cap_mm)
M<-cs[,c("log_units","media_articles","severity_score","log_mcap")]
cat("Correlation matrix of regressors:\n"); print(round(cor(M),2))
cat(sprintf("cor(idio_vol, log_mcap) = %.2f\n", cor(cs$idio_vol, cs$log_mcap)))
'Correlation matrix of regressors:
log_units media_articles severity_score log_mcap
log_units 1.00 -0.09 -0.13 -0.07
media_articles -0.09 1.00 0.05 0.23
severity_score -0.13 0.05 1.00 0.07
log_mcap -0.07 0.23 0.07 1.00
cor(idio_vol, log_mcap) = -0.59command: create path: /app/outputs/research_memo.md file_text: # Research Memo: The Stock-Market Impact of Product Recalls in the Toy Industry **Prepared for:** Regulatory policy and institutional-investor audiences **Subject:** Event-study evidence on how equity markets price product-recall announcements **Sample:** 35 recall announcements by 14 of 18 listed toy manufacturers, 2006–2007 --- ## 1. Executive summary When a toy manufacturer announces a product recall, its share price falls sharply and almost immediately. Across the 35 announcements we study, the average firm lost roughly **2.9% of its market value over the three trading days surrounding the announcement** (event days −1 to +1), after stripping out normal market-wide movements with a market-model benchmark. This reaction is large, highly statistically significant, and concentrated in the first day or two. It is not subsequently reversed, but neither does it continue to drift in a way that a trader could reliably exploit. In short, the market treats a recall as genuine, value-relevant bad news and prices it quickly. ## 2. Magnitude and statistical significance of abnormal returns We measure each event with standardized cumulative abnormal returns (SCARs), which express the price reaction in units of the firm's own normal return volatility and correct for estimation error in the benchmark model. The evidence of a negative reaction is overwhelming across every test and window: | Window | Mean SCAR | Patell Z | BMP t | Kolari–Pynnönen t | % negative | |--------|-----------|----------|-------|-------------------|------------| | [−1,+1] (3-day) | −1.01 | −5.96 | −5.00 | −4.58 | 82.9% | | [0,+1] (2-day) | −0.86 | −5.09 | −4.51 | −4.13 | 80.0% | | [−5,+5] (11-day)| −0.44 | −2.60 | −2.18 | −1.99 | 62.9% | All three statistics reject the hypothesis of zero abnormal return at conventional levels. The Patell Z (which assumes independent, unit-variance SCARs) is the most extreme; the Boehmer–Musumeci–Poulsen (BMP) t-statistic is more conservative because it uses the realized cross-sectional dispersion of SCARs to absorb event-induced variance; and the Kolari–Pynnönen (KP) t adjusts further for cross-event correlation. Because the average pairwise residual correlation across firms is tiny (`rho_bar = 0.0057`), the KP adjustment barely differs from BMP , cross-sectional dependence is not a material concern in this sample. The joint Schipper–Thompson Wald test confirms the picture: the three window means are jointly non-zero (Wald = 25.7, p ≈ 1.1×10⁻⁵). Critically, the effect is strongest in the tightest windows and decays in the 11-day window, which tells us the information is absorbed essentially on the announcement day rather than leaking in early or bleeding out slowly. ## 3. What drives the cross-sectional heterogeneity Not all recalls are equally damaging. A cross-sectional regression of the 3-day SCAR on recall and firm characteristics (with heteroskedasticity- and autocorrelation-consistent Newey–West standard errors, automatic lag = 3) explains a meaningful share of the variation (R² ≈ 0.37). The weighted-least- squares specification, which down-weights high-idiosyncratic-volatility firms, tells the same story. - **Media coverage (t ≈ −3.1):** the single most robust amplifier. Each additional news article is associated with a more negative reaction. The intuition is that press attention increases the salience of the hazard to consumers, raises the odds of litigation and regulatory follow-through, and damages brand equity well beyond the direct cost of the recalled units. - **Firm size (log market cap, t ≈ −3.2):** larger firms show *larger* standardized drops. Part of this is mechanical and worth flagging to non-specialists: SCARs are scaled by each firm's return volatility, and larger toy makers here have materially lower idiosyncratic volatility (corr ≈ −0.59), so a given percentage drop converts into more "standard deviations." Part may …[truncated]
New file created successfully at /app/outputs/research_memo.md
cd /app && echo "Memo word count: $(wc -w < outputs/research_memo.md)" && echo "--- simulating grader: clean outputs, re-run ---" && rm -rf outputs && Rscript analysis.R 2>&1 && echo "--- output files ---" && ls -la outputs/
Memo word count: 1212 --- simulating grader: clean outputs, re-run --- Pipeline complete. events=35 valid=35 firms=18 (with recalls=14) rho_bar=0.005682 hac_lag=3 calendar n_days=230 --- output files --- total 159 drwxr-sr-x 2 h2tagent h2tagent 4096 Jun 20 18:04 . drwxrws--- 4 h2tagent h2tagent 4096 Jun 20 18:04 .. -rw-r--r-- 1 h2tagent h2tagent 51762 Jun 20 18:04 car_timeline.png -rw-r--r-- 1 h2tagent h2tagent 3619 Jun 20 18:04 event_cars.csv -rw-r--r-- 1 h2tagent h2tagent 1837 Jun 20 18:04 event_study_results.json -rw-r--r-- 1 h2tagent h2tagent 95243 Jun 20 18:04 portfolio_comparison.png
cd /app && echo "=== event_cars.csv (header + 3 rows + count) ===" && head -4 outputs/event_cars.csv && echo "total lines: $(wc -l < outputs/event_cars.csv)" && echo "" && Rscript -e '
suppressPackageStartupMessages(library(jsonlite))
r<-fromJSON("outputs/event_study_results.json", simplifyVector=FALSE)
# Validate schema keys
req_top<-c("n_events","n_firms","n_firms_with_recalls","n_valid_events","rho_bar","aggregate_scars","cross_section_hac","cross_section_wls","calendar_time","corrado_rank","cowan_sign","schipper_thompson")
cat("Top-level keys OK:", all(req_top %in% names(r)) && all(names(r)%in%req_top), "\n")
win<-c("mean_scar","patell_z","bmp_t","kp_t","pct_negative","n")
for(w in c("window_3day","window_2day","window_11day")) cat(" ",w,"keys OK:", all(win%in%names(r$aggregate_scars[[w]])),"\n")
hac<-c("intercept","log_units","media","severity","log_mcap","se_intercept","se_log_units","se_media","se_severity","se_log_mcap","r_squared","n_obs","hac_lag")
cat("hac keys OK:", all(hac%in%names(r$cross_section_hac)),"\n")
cat("wls keys OK:", all(setdiff(hac,"hac_lag")%in%names(r$cross_section_wls)),"\n")
cat("calendar keys OK:", all(c("alpha","beta","t_alpha","p_value","n_days")%in%names(r$calendar_time)),"\n")
cat("corrado keys OK:", all(c("z_stat","p_value")%in%names(r$corrado_rank)),"\n")
cat("cowan keys OK:", all(c("z_stat","p_value","p_bar","frac_positive")%in%names(r$cowan_sign)),"\n")
cat("schipper keys OK:", all(c("wald_stat","wald_p","df")%in%names(r$schipper_thompson)),"\n")
cat("df value:", r$schipper_thompson$df, "| n_obs:", r$cross_section_hac$n_obs, "| hac_lag:", r$cross_section_hac$hac_lag,"\n")
'=== event_cars.csv (header + 3 rows + count) === "event_id","firm_id","date","scar_3day","scar_2day","scar_11day","ar_day0" 1,"FIRM_007","2007-03-21",-2.76169061943763,-2.0263078461654,-2.69019603772194,-0.0188048397516721 2,"FIRM_002","2007-03-28",1.64108851598372,2.18868108156405,1.39576215701227,0.0513410791394269 3,"FIRM_017","2007-04-16",0.941157016907192,1.02888824906045,1.46606715865823,0.00560955526504031 total lines: 36 Top-level keys OK: TRUE window_3day keys OK: TRUE window_2day keys OK: TRUE window_11day keys OK: TRUE hac keys OK: TRUE wls keys OK: TRUE calendar keys OK: TRUE corrado keys OK: TRUE cowan keys OK: TRUE schipper keys OK: TRUE df value: 3 | n_obs: 35 | hac_lag: 3
cd /app && Rscript -e '
suppressPackageStartupMessages(library(jsonlite))
x <- list(tiny=signif(3.21e-8,10), p=signif(1.101162e-05,10), z=signif(-5.96058187,10), big=signif(25.70181,10), r=signif(0.0056821460,10))
cat("digits=6:\n"); cat(toJSON(x,auto_unbox=TRUE,digits=6),"\n\n")
cat("digits=10:\n"); cat(toJSON(x,auto_unbox=TRUE,digits=10),"\n\n")
cat("digits=15:\n"); cat(toJSON(x,auto_unbox=TRUE,digits=15),"\n")
'digits=6:
{"tiny":3.21e-08,"p":0.000011,"z":-5.960582,"big":25.70181,"r":0.005682}
digits=10:
{"tiny":3.21e-08,"p":1.101162e-05,"z":-5.96058187,"big":25.70181,"r":0.005682146}
digits=15:
{"tiny":3.21e-08,"p":1.101162e-05,"z":-5.96058187,"big":25.70181,"r":0.005682146}command: str_replace path: /app/analysis.R old_str: rnd <- function(x) if (is.numeric(x) && !is.integer(x)) round(x, 6) else x results <- list( n_events = as.integer(n_events), n_firms = as.integer(n_firms), n_firms_with_recalls = as.integer(n_firms_with_recalls), n_valid_events = as.integer(n_valid_events), rho_bar = round(rho_bar, 6), new_str: # Round to significant figures (not decimal places) so very small p-values # are preserved on the held-out data; integer fields pass through unchanged. rnd <- function(x) if (is.numeric(x) && !is.integer(x)) signif(x, 10) else x results <- list( n_events = as.integer(n_events), n_firms = as.integer(n_firms), n_firms_with_recalls = as.integer(n_firms_with_recalls), n_valid_events = as.integer(n_valid_events), rho_bar = signif(rho_bar, 10),
The file /app/analysis.R has been edited successfully.
command: str_replace
path: /app/analysis.R
old_str: event_out <- event_df[, c("event_id", "firm_id", "date",
"scar_3day", "scar_2day", "scar_11day", "ar_day0")]
write.csv(event_out, "/app/outputs/event_cars.csv", row.names = FALSE)
new_str: event_out <- event_df[, c("event_id", "firm_id", "date",
"scar_3day", "scar_2day", "scar_11day", "ar_day0")]
write.csv(event_out, "/app/outputs/event_cars.csv", row.names = FALSE, quote = FALSE)The file /app/analysis.R has been edited successfully.
cd /app && rm -rf outputs && Rscript analysis.R 2>&1 | tail -3 && echo "=== CSV head ===" && head -3 outputs/event_cars.csv && echo "=== JSON (spot check) ===" && Rscript -e 'suppressPackageStartupMessages(library(jsonlite)); r<-fromJSON("outputs/event_study_results.json"); cat("wald_p=",r$schipper_thompson$wald_p," patell_3d=",r$aggregate_scars$window_3day$patell_z," rho_bar=",r$rho_bar,"\n")'Pipeline complete. events=35 valid=35 firms=18 (with recalls=14) rho_bar=0.005682 hac_lag=3 calendar n_days=230 === CSV head === event_id,firm_id,date,scar_3day,scar_2day,scar_11day,ar_day0 1,FIRM_007,2007-03-21,-2.76169061943763,-2.0263078461654,-2.69019603772194,-0.0188048397516721 2,FIRM_002,2007-03-28,1.64108851598372,2.18868108156405,1.39576215701227,0.0513410791394269 === JSON (spot check) === wald_p= 1.101162e-05 patell_3d= -5.960582 rho_bar= 0.005682146
command: str_replace
path: /app/analysis.R
old_str: if (length(cd) < 20L) next # need >= 20 overlapping obs
pair_cors <- c(pair_cors, cor(r1[cd], r2[cd]))
}
}
rho_bar <- if (length(pair_cors) > 0L) mean(pair_cors) else 0.0
new_str: if (length(cd) < 20L) next # need >= 20 overlapping obs
if (sd(r1[cd]) == 0 || sd(r2[cd]) == 0) next
pair_cors <- c(pair_cors, cor(r1[cd], r2[cd]))
}
}
pair_cors <- pair_cors[is.finite(pair_cors)]
rho_bar <- if (length(pair_cors) > 0L) mean(pair_cors) else 0.0The file /app/analysis.R has been edited successfully.
cd /app && rm -rf outputs && Rscript analysis.R 2>&1 | tail -3 && echo "=== FILES ===" && ls outputs/ && echo "=== FULL JSON ===" && cat outputs/event_study_results.json
Pipeline complete.
events=35 valid=35 firms=18 (with recalls=14)
rho_bar=0.005682 hac_lag=3 calendar n_days=230
=== FILES ===
car_timeline.png
event_cars.csv
event_study_results.json
portfolio_comparison.png
=== FULL JSON ===
{
"n_events": 35,
"n_firms": 18,
"n_firms_with_recalls": 14,
"n_valid_events": 35,
"rho_bar": 0.005682145933,
"aggregate_scars": {
"window_3day": {
"mean_scar": -1.00752224,
"patell_z": -5.960581953,
"bmp_t": -4.999558757,
"kp_t": -4.576951762,
"pct_negative": 0.8285714286,
"n": 35
},
"window_2day": {
"mean_scar": -0.8609178343,
"patell_z": -5.093258594,
"bmp_t": -4.514834029,
"kp_t": -4.133200263,
"pct_negative": 0.8,
"n": 35
},
"window_11day": {
"mean_scar": -0.4396027964,
"patell_z": -2.600725216,
"bmp_t": -2.175121424,
"kp_t": -1.991260893,
"pct_negative": 0.6285714286,
"n": 35
}
},
"cross_section_hac": {
"intercept": 6.821577837,
"log_units": -0.2749748466,
"media": -0.1390013277,
"severity": -0.0487980734,
"log_mcap": -0.3482910788,
"se_intercept": 1.974528155,
"se_log_units": 0.1428724423,
"se_media": 0.0451049573,
"se_severity": 0.07919782147,
"se_log_mcap": 0.1085972811,
"r_squared": 0.3650583821,
"n_obs": 35,
"hac_lag": 3
},
"cross_section_wls": {
"intercept": 6.136437183,
"log_units": -0.2432183247,
"media": -0.1419113819,
"severity": -0.06825889797,
"log_mcap": -0.3007824624,
"se_intercept": 2.362232052,
"se_log_units": 0.1193509016,
"se_media": 0.06270618154,
"se_severity": 0.07387705437,
"se_log_mcap": 0.1910968419,
"r_squared": 0.3071220492,
"n_obs": 35
},
"calendar_time": {
"alpha": -0.0008577709211,
"beta": 1.033202648,
"t_alpha": -1.345659946,
"p_value": 0.1797488479,
"n_days": 230
},
"corrado_rank": {
"z_stat": -2.426518067,
"p_value": 0.01524449129
},
"cowan_sign": {
"z_stat": 0.2535466522,
"p_value": 0.7998458153,
"p_bar": 0.4928592965,
"frac_positive": 0.5142857143
},
"schipper_thompson": {
"wald_stat": 25.70180951,
"wald_p": 1.101161759e-05,
"df": 3
}
}cd /app && cat -n analysis.R | sed -n '1,130p'
1 suppressPackageStartupMessages({
2 library(jsonlite)
3 library(ggplot2)
4 })
5
6 # =====================================================================
7 # Product-recall stock-price event study
8 # Deterministic base-R implementation (no random seeds, no bootstrap).
9 # =====================================================================
10
11 EST_LEN <- 200 # estimation window length (trading days)
12 EST_GAP <- 30 # gap (trading days) between estimation window end and event
13
14 # =====================================================================
15 # 1. Load and clean
16 # - drop NA returns / market returns
17 # - drop sentinel returns (return < -10)
18 # - drop exact duplicate rows
19 # - sort by (firm_id, date)
20 # =====================================================================
21 returns <- read.csv("/app/data/stock_returns.csv", stringsAsFactors = FALSE)
22 recalls <- read.csv("/app/data/recalls.csv", stringsAsFactors = FALSE)
23 firms <- read.csv("/app/data/firms.csv", stringsAsFactors = FALSE)
24
25 returns <- returns[!is.na(returns$return) & !is.na(returns$market_return), ]
26 returns <- returns[returns$return >= -10, ] # remove sentinel returns
27 returns <- returns[!duplicated(returns), ] # remove duplicate rows
28 returns$date <- as.Date(returns$date)
29 returns <- returns[order(returns$firm_id, returns$date), ]
30 rownames(returns) <- NULL
31
32 recalls <- recalls[!duplicated(recalls), ]
33 recalls$date <- as.Date(recalls$date)
34 recalls <- recalls[order(recalls$event_id), ]
35
36 firms <- firms[!duplicated(firms), ]
37
38 n_events <- nrow(recalls)
39 n_firms <- nrow(firms)
40 n_firms_with_recalls <- length(unique(recalls$firm_id))
41
42 # Global trading-day calendar (0-based index) used for all windowing.
43 all_dates <- sort(unique(returns$date))
44 n_dates <- length(all_dates)
45 date_to_idx <- setNames(seq_along(all_dates) - 1L, as.character(all_dates))
46
47 # Fast per-firm lookup of return / market_return keyed by date string.
48 returns_by_firm <- split(returns, returns$firm_id)
49 firm_ret <- lapply(returns_by_firm, function(d) setNames(d$return, as.character(d$date)))
50 firm_mkt <- lapply(returns_by_firm, function(d) setNames(d$market_return, as.character(d$date)))
51
52 # =====================================================================
53 # 2. Market model + prediction-error-corrected SCARs
54 # =====================================================================
55 # Estimation window: 200 trading days ending 30 days before the event,
56 # i.e. global indices [eidx-230, eidx-31]; require >= 100 valid obs.
57 event_market_model <- function(fid, eidx) {
58 est_end <- eidx - EST_GAP - 1L
59 est_start <- est_end - EST_LEN + 1L
60 if (est_start < 0L) return(NULL)
61 est_dates <- as.character(all_dates[(est_start + 1L):(est_end + 1L)])
62 rv <- firm_ret[[fid]]; mv <- firm_mkt[[fid]]
63 have <- est_dates[est_dates %in% names(rv)]
64 if (length(have) < 100L) return(NULL)
65 y <- rv[have]; x <- mv[have]
66 m <- lm(y ~ x)
67 b <- unname(coef(m))
68 res <- unname(resid(m))
69 L <- length(have)
70 sigma2 <- sum(res^2) / (L - 2) # OLS residual variance (ddof = 2)
71 xbar <- mean(x)
72 list(alpha = b[1], beta = b[2], sigma2 = sigma2, sigma = sqrt(sigma2),
73 n_est = L, xbar = xbar, Sxx = sum((x - xbar)^2),
74 resid = res)
75 }
76
77 windows <- list(w3 = c(-1L, 1L), w2 = c(0L, 1L), w11 = c(-5L, 5L))
78 win_names <- c(w3 = "window_3day", w2 = "window_2day", w11 = "window_11day")
79
80 event_models <- vector("list", n_events) # cache for reuse in plots
…[truncated]cd /app && sed -n '130,200p' analysis.R
stringsAsFactors = FALSE
)
}
event_df <- do.call(rbind, event_rows)
event_df <- event_df[order(event_df$event_id), ]
rownames(event_df) <- NULL
n_valid_events <- nrow(event_df)
# =====================================================================
# 3. rho_bar: mean pairwise correlation of per-firm full-panel residuals
# =====================================================================
res_by_firm <- list()
for (fid in names(returns_by_firm)) {
d <- returns_by_firm[[fid]]
if (nrow(d) < 30L) next # drop short panels
mm <- lm(return ~ market_return, data = d)
res_by_firm[[fid]] <- setNames(unname(resid(mm)), as.character(d$date))
}
fn <- names(res_by_firm)
pair_cors <- c()
if (length(fn) >= 2L) {
for (a in 1:(length(fn) - 1L)) for (b in (a + 1L):length(fn)) {
r1 <- res_by_firm[[fn[a]]]; r2 <- res_by_firm[[fn[b]]]
cd <- intersect(names(r1), names(r2))
if (length(cd) < 20L) next # need >= 20 overlapping obs
if (sd(r1[cd]) == 0 || sd(r2[cd]) == 0) next
pair_cors <- c(pair_cors, cor(r1[cd], r2[cd]))
}
}
pair_cors <- pair_cors[is.finite(pair_cors)]
rho_bar <- if (length(pair_cors) > 0L) mean(pair_cors) else 0.0
# =====================================================================
# 3b. Aggregate cross-sectional statistics per window
# =====================================================================
aggregate_scars <- function(vals, rho) {
vals <- vals[!is.na(vals)]
n <- length(vals)
if (n < 2L) return(list(mean_scar = NA_real_, patell_z = NA_real_,
bmp_t = NA_real_, kp_t = NA_real_,
pct_negative = NA_real_, n = as.integer(n)))
mean_v <- mean(vals)
var_v <- var(vals) # sample variance, ddof = 1
sd_v <- sqrt(var_v)
patell <- sum(vals) / sqrt(n) # Patell (1976)
bmp <- mean_v / (sd_v / sqrt(n)) # BMP (1991)
kp_den <- sqrt(var_v * (1 + (n - 1) * rho) / n) # Kolari-Pynnonen (2010)
kp <- mean_v / kp_den
list(mean_scar = mean_v, patell_z = patell, bmp_t = bmp, kp_t = kp,
pct_negative = mean(vals < 0), n = as.integer(n))
}
agg <- list(
window_3day = aggregate_scars(event_df$scar_3day, rho_bar),
window_2day = aggregate_scars(event_df$scar_2day, rho_bar),
window_11day = aggregate_scars(event_df$scar_11day, rho_bar)
)
# =====================================================================
# 4/5. Cross-sectional regressions (HAC and WLS)
# =====================================================================
cs <- merge(event_df[, c("event_id", "firm_id", "scar_3day")],
recalls[, c("event_id", "units_recalled", "media_articles", "severity_score")],
by = "event_id")
cs <- merge(cs, firms[, c("firm_id", "market_cap_mm", "idio_vol")], by = "firm_id")
cs$log_units <- log(cs$units_recalled)
cs$log_mcap <- log(cs$market_cap_mm)
cs <- cs[order(cs$event_id), ]
rownames(cs) <- NULL
X <- as.matrix(cbind(1, cs$log_units, cs$media_articles, cs$severity_score, cs$log_mcap))command: str_replace
path: /app/analysis.R
old_str: windows <- list(w3 = c(-1L, 1L), w2 = c(0L, 1L), w11 = c(-5L, 5L))
win_names <- c(w3 = "window_3day", w2 = "window_2day", w11 = "window_11day")
event_models <- vector("list", n_events) # cache for reuse in plots
new_str: windows <- list(w3 = c(-1L, 1L), w2 = c(0L, 1L), w11 = c(-5L, 5L))
event_models <- vector("list", n_events) # cache for reuse in plotsThe file /app/analysis.R has been edited successfully.
cd /app && rm -rf outputs && Rscript analysis.R 2>&1 && echo "---" && ls -la outputs/ && echo "CSV rows:" && wc -l < outputs/event_cars.csv && echo "Memo words:" && wc -w < outputs/research_memo.md
Pipeline complete. events=35 valid=35 firms=18 (with recalls=14) rho_bar=0.005682 hac_lag=3 calendar n_days=230 --- total 158 drwxr-sr-x 2 h2tagent h2tagent 4096 Jun 20 18:07 . drwxrws--- 4 h2tagent h2tagent 4096 Jun 20 18:06 .. -rw-r--r-- 1 h2tagent h2tagent 51762 Jun 20 18:07 car_timeline.png -rw-r--r-- 1 h2tagent h2tagent 3465 Jun 20 18:06 event_cars.csv -rw-r--r-- 1 h2tagent h2tagent 2031 Jun 20 18:06 event_study_results.json -rw-r--r-- 1 h2tagent h2tagent 95243 Jun 20 18:07 portfolio_comparison.png CSV rows: 36 Memo words: [stderr] bash: outputs/research_memo.md: No such file or directory
command: str_replace
path: /app/analysis.R
old_str: scar <- list(); ar_day0 <- NA_real_; ar_w3 <- numeric(0)
for (wn in names(windows)) {
w <- windows[[wn]]
ars <- numeric(0); Bm <- 0; K <- 0L
new_str: scar <- list(); ar_day0 <- NA_real_; ar_w3 <- numeric(0); car3_raw <- NA_real_
for (wn in names(windows)) {
w <- windows[[wn]]
ars <- numeric(0); Bm <- 0; K <- 0LThe file /app/analysis.R has been edited successfully.
command: str_replace
path: /app/analysis.R
old_str: if (K == 0L) { scar[[wn]] <- NA_real_; next }
CAR <- sum(ars)
s_car <- sqrt(m$sigma2 * (K + (K^2) / m$n_est + (Bm^2) / m$Sxx))
scar[[wn]] <- CAR / s_car
}
new_str: if (K == 0L) { scar[[wn]] <- NA_real_; next }
CAR <- sum(ars)
if (wn == "w3") car3_raw <- CAR
s_car <- sqrt(m$sigma2 * (K + (K^2) / m$n_est + (Bm^2) / m$Sxx))
scar[[wn]] <- CAR / s_car
}The file /app/analysis.R has been edited successfully.
command: str_replace
path: /app/analysis.R
old_str: ar_day0 = ar_day0,
I_cowan = as.integer(any(ar_w3 > 0)), # Cowan 3-day positive indicator
p_hat = mean(m$resid > 0), # Cowan est-window positive frac
n_est = m$n_est,
new_str: ar_day0 = ar_day0,
car_3day = car3_raw, # raw (non-standardized) 3-day CAR
I_cowan = as.integer(any(ar_w3 > 0)), # Cowan 3-day positive indicator
p_hat = mean(m$resid > 0), # Cowan est-window positive frac
n_est = m$n_est,The file /app/analysis.R has been edited successfully.
command: str_replace
path: /app/analysis.R
old_str: ggsave("/app/outputs/portfolio_comparison.png", p2, width = 10, height = 5, dpi = 120, bg = "white")
cat("Pipeline complete.\n")
new_str: # =====================================================================
# research_memo.md (generated dynamically from the computed results)
# =====================================================================
pct <- function(x) sprintf("%.2f%%", 100 * x)
f2 <- function(x) sprintf("%.2f", x)
f3 <- function(x) sprintf("%.3f", x)
sig_word <- function(p) {
if (!is.finite(p)) return("not estimable")
if (p < 0.01) "highly statistically significant (p < 0.01)"
else if (p < 0.05) "statistically significant (p < 0.05)"
else if (p < 0.10) "marginally significant (p < 0.10)"
else "statistically insignificant"
}
dir_word <- function(x) if (x < 0) "negative" else if (x > 0) "positive" else "nil"
p_from_t <- function(t) 2 * (1 - pnorm(abs(t)))
coef_sentence <- function(name, coef, se) {
tval <- coef / se
sprintf("%s (coefficient %s, HAC t = %s, %s)",
name, f3(coef), f2(tval), sig_word(p_from_t(tval)))
}
a3 <- agg$window_3day; a2 <- agg$window_2day; a11 <- agg$window_11day
mean_raw_car3 <- mean(event_df$car_3day, na.rm = TRUE)
med_raw_car3 <- median(event_df$car_3day, na.rm = TRUE)
ann_alpha <- (1 + calendar$alpha)^252 - 1
h <- cs_hac
memo <- c(
"# Research Memo: The Stock-Market Impact of Product Recalls in the Toy Industry",
"",
"**Prepared for:** Financial regulators and institutional investors ",
"**Subject:** Event-study evidence on how equity markets price product-recall announcements ",
sprintf("**Sample:** %d valid recall announcements by %d of %d listed toy manufacturers.",
n_valid_events, n_firms_with_recalls, n_firms),
"",
"---",
"",
"## 1. Executive summary",
"",
sprintf(paste(
"When a toy manufacturer announces a product recall, the market reaction is %s and immediate.",
"Across the %d announcements analysed, the average firm's share price moved by roughly %s over the",
"three trading days bracketing the announcement (event days -1 to +1), measured as a raw market-model",
"cumulative abnormal return (the median event moved %s). After standardising each reaction by the firm's",
"own normal-period volatility, the average standardized cumulative abnormal return (SCAR) is %s, and %s",
"of events were negative. In plain terms, investors treat a recall as genuine, value-relevant news and",
"reprice the stock within a day or two."),
dir_word(a3$mean_scar), n_valid_events, pct(mean_raw_car3), pct(med_raw_car3),
f2(a3$mean_scar), pct(a3$pct_negative)),
"",
"## 2. Magnitude and statistical significance of abnormal returns",
"",
paste(
"We summarise each event with prediction-error-corrected SCARs, which express the price move in units of",
"the firm's normal return volatility and correct for estimation error in the benchmark market model. Three",
"complementary test statistics are reported for each window: the Patell Z (assuming independent unit-variance",
"SCARs), the Boehmer-Musumeci-Poulsen (BMP) t (which uses the realized cross-sectional dispersion to absorb",
"event-induced variance), and the Kolari-Pynnonen (KP) t (which further adjusts for cross-event correlation)."),
"",
"| Window | Mean SCAR | Patell Z | BMP t | KP t | % negative |",
"|--------|-----------|----------|-------|------|------------|",
sprintf("| [-1,+1] (3-day) | %s | %s | %s | %s | %s |",
f2(a3$mean_scar), f2(a3$patell_z), f2(a3$bmp_t), f2(a3$kp_t), pct(a3$pct_negative)),
sprintf("| [0,+1] (2-day) | %s | %s | %s | %s | %s |",
f2(a2$mean_scar), f2(a2$patell_z), f2(a2$bmp_t), f2(a2$kp_t), pct(a2$pct_negative)),
sprintf("| [-5,+5] (11-day)| %s | %s | %s | %s | %s |",
f2(a11$mean_scar), f2(a11$patell_z), f2(a11$bmp_t), f2(a11$kp_t), pct(a11$pct_negative)),
"",
sprintf(paste(
"For the headline 3-day window the effect is %s (BMP p = %s). The effect is strongest in the tightest",
"windows and weaker over the
…[truncated]The file /app/analysis.R has been edited successfully.
cd /app && rm -rf outputs && Rscript analysis.R 2>&1 | tail -3 && echo "=== FILES ===" && ls outputs/ && echo "=== MEMO WORDS ===" && wc -w < outputs/research_memo.md && echo "=== CSV COLS ===" && head -1 outputs/event_cars.csv
Pipeline complete. events=35 valid=35 firms=18 (with recalls=14) rho_bar=0.005682 hac_lag=3 calendar n_days=230 === FILES === car_timeline.png event_cars.csv event_study_results.json research_memo.md === MEMO WORDS === 1135 === CSV COLS === event_id,firm_id,date,scar_3day,scar_2day,scar_11day,ar_day0
cd /app && cat outputs/research_memo.md
# Research Memo: The Stock-Market Impact of Product Recalls in the Toy Industry **Prepared for:** Financial regulators and institutional investors **Subject:** Event-study evidence on how equity markets price product-recall announcements **Sample:** 35 valid recall announcements by 14 of 18 listed toy manufacturers. --- ## 1. Executive summary When a toy manufacturer announces a product recall, the market reaction is negative and immediate. Across the 35 announcements analysed, the average firm's share price moved by roughly -2.92% over the three trading days bracketing the announcement (event days -1 to +1), measured as a raw market-model cumulative abnormal return (the median event moved -2.77%). After standardising each reaction by the firm's own normal-period volatility, the average standardized cumulative abnormal return (SCAR) is -1.01, and 82.86% of events were negative. In plain terms, investors treat a recall as genuine, value-relevant news and reprice the stock within a day or two. ## 2. Magnitude and statistical significance of abnormal returns We summarise each event with prediction-error-corrected SCARs, which express the price move in units of the firm's normal return volatility and correct for estimation error in the benchmark market model. Three complementary test statistics are reported for each window: the Patell Z (assuming independent unit-variance SCARs), the Boehmer-Musumeci-Poulsen (BMP) t (which uses the realized cross-sectional dispersion to absorb event-induced variance), and the Kolari-Pynnonen (KP) t (which further adjusts for cross-event correlation). | Window | Mean SCAR | Patell Z | BMP t | KP t | % negative | |--------|-----------|----------|-------|------|------------| | [-1,+1] (3-day) | -1.01 | -5.96 | -5.00 | -4.58 | 82.86% | | [0,+1] (2-day) | -0.86 | -5.09 | -4.51 | -4.13 | 80.00% | | [-5,+5] (11-day)| -0.44 | -2.60 | -2.18 | -1.99 | 62.86% | For the headline 3-day window the effect is highly statistically significant (p < 0.01) (BMP p = 0.000). The effect is strongest in the tightest windows and weaker over the 11-day window (BMP t = -2.18), which indicates that the information is absorbed essentially on the announcement day rather than leaking in early or drifting out slowly. The average pairwise residual correlation across firms is 0.006 (rho_bar), so cross-sectional dependence is economically small here -- this is why the KP statistic barely differs from the BMP statistic. The joint Schipper-Thompson Wald test (three windows simultaneously) gives a statistic of 25.70 on 3 degrees of freedom, which is highly statistically significant (p < 0.01), confirming that the average reactions are jointly different from zero. ## 3. What drives the cross-sectional heterogeneity Not all recalls are equally costly. A cross-sectional regression of the 3-day SCAR on recall and firm characteristics, with heteroskedasticity- and autocorrelation-consistent Newey-West standard errors (automatic Bartlett lag = 3), explains an R-squared of about 0.37. The weighted-least-squares version, which down-weights high-idiosyncratic-volatility firms, tells a consistent story (R-squared 0.31). The estimated associations are: - **Media coverage** -- more news articles (coefficient -0.139, HAC t = -3.08, highly statistically significant (p < 0.01)). Press attention raises the salience of the hazard to consumers, increases litigation and regulatory-follow-through risk, and erodes brand equity well beyond the direct cost of the recalled units. - **Recall scale (log units recalled)** -- more units recalled (coefficient -0.275, HAC t = -1.92, marginally significant (p < 0.10)). A larger recall is a bigger, costlier operational and reputational problem. - **Hazard severity score** -- higher severity (coefficient -0.049, HAC t = -0.62, statistically insignificant). Once scale and publicity are controlled, the analyst severity rating adds little, suggesting markets respond more to the breadth and visibility of …[truncated]
command: str_replace
path: /app/analysis.R
old_str: dir_word <- function(x) if (x < 0) "negative" else if (x > 0) "positive" else "nil"
p_from_t <- function(t) 2 * (1 - pnorm(abs(t)))
new_str: dir_word <- function(x) if (x < 0) "negative" else if (x > 0) "positive" else "nil"
p_from_t <- function(t) 2 * (1 - pnorm(abs(t)))
pfmt <- function(p) if (!is.finite(p)) "n/a" else if (p < 0.001) "< 0.001" else sprintf("%.3f", p)The file /app/analysis.R has been edited successfully.
command: str_replace path: /app/analysis.R old_str: "For the headline 3-day window the effect is %s (BMP p = %s). The effect is strongest in the tightest", new_str: "For the headline 3-day window the effect is %s (BMP p = %s). The effect is strongest in the tightest", # nolint
The file /app/analysis.R has been edited successfully.
command: str_replace path: /app/analysis.R old_str: "For the headline 3-day window the effect is %s (BMP p = %s). The effect is strongest in the tightest", # nolint new_str: "For the headline 3-day window the effect is %s (BMP p = %s). The effect is strongest in the tightest",
The file /app/analysis.R has been edited successfully.
command: str_replace path: /app/analysis.R old_str: sig_word(p_from_t(a3$bmp_t)), f3(p_from_t(a3$bmp_t)), f2(a11$bmp_t), new_str: sig_word(p_from_t(a3$bmp_t)), pfmt(p_from_t(a3$bmp_t)), f2(a11$bmp_t),
The file /app/analysis.R has been edited successfully.
cd /app && rm -rf outputs && Rscript analysis.R 2>&1 | tail -2 && echo "---" && grep -n "headline 3-day" outputs/research_memo.md && echo "words: $(wc -w < outputs/research_memo.md)" && ls outputs/
events=35 valid=35 firms=18 (with recalls=14) rho_bar=0.005682 hac_lag=3 calendar n_days=230 --- 23:For the headline 3-day window the effect is highly statistically significant (p < 0.01) (BMP p = < 0.001). The effect is strongest in the tightest windows and weaker over the 11-day window (BMP t = -2.18), which indicates that the information is absorbed essentially on the announcement day rather than leaking in early or drifting out slowly. The average pairwise residual correlation across firms is 0.006 (rho_bar), so cross-sectional dependence is economically small here -- this is why the KP statistic barely differs from the BMP statistic. The joint Schipper-Thompson Wald test (three windows simultaneously) gives a statistic of 25.70 on 3 degrees of freedom, which is highly statistically significant (p < 0.01), confirming that the average reactions are jointly different from zero. words: 1136 car_timeline.png event_cars.csv event_study_results.json research_memo.md
command: str_replace
path: /app/analysis.R
old_str: pfmt <- function(p) if (!is.finite(p)) "n/a" else if (p < 0.001) "< 0.001" else sprintf("%.3f", p)
new_str: pfmt <- function(p) if (!is.finite(p)) "= n/a" else if (p < 0.001) "< 0.001" else sprintf("= %.3f", p)The file /app/analysis.R has been edited successfully.
command: str_replace path: /app/analysis.R old_str: "For the headline 3-day window the effect is %s (BMP p = %s). The effect is strongest in the tightest", new_str: "For the headline 3-day window the effect is %s (BMP p %s). The effect is strongest in the tightest",
The file /app/analysis.R has been edited successfully.
cd /app && rm -rf outputs && Rscript analysis.R 2>&1 | tail -1 && grep -o "BMP p [^)]*" outputs/research_memo.md | head -1
rho_bar=0.005682 hac_lag=3 calendar n_days=230 BMP p < 0.001
cd /app && rm -rf outputs && Rscript analysis.R >/dev/null 2>&1 && cp -r outputs /tmp/final1 && rm -rf outputs && Rscript analysis.R >/dev/null 2>&1 && echo "JSON identical: $(diff -q /tmp/final1/event_study_results.json outputs/event_study_results.json && echo YES)" && echo "CSV identical: $(diff -q /tmp/final1/event_cars.csv outputs/event_cars.csv && echo YES)" && echo "Memo identical: $(diff -q /tmp/final1/research_memo.md outputs/research_memo.md && echo YES)" && echo "--- key values ---" && Rscript -e 'suppressPackageStartupMessages(library(jsonlite)); r<-fromJSON("outputs/event_study_results.json"); cat("3d: patell",r$aggregate_scars$window_3day$patell_z,"bmp",r$aggregate_scars$window_3day$bmp_t,"kp",r$aggregate_scars$window_3day$kp_t,"\nhac media",r$cross_section_hac$media,"se",r$cross_section_hac$se_media,"\ncal alpha",r$calendar_time$alpha,"t",r$calendar_time$t_alpha,"\ncorrado",r$corrado_rank$z_stat,"cowan",r$cowan_sign$z_stat,"ST",r$schipper_thompson$wald_stat,"\n")'JSON identical: YES CSV identical: YES Memo identical: YES --- key values --- 3d: patell -5.960582 bmp -4.999559 kp -4.576952 hac media -0.1390013 se 0.04510496 cal alpha -0.0008577709 t -1.34566 corrado -2.426518 cowan 0.2535467 ST 25.70181
command: view path: /app/outputs/portfolio_comparison.png
[stderr] The path /app/outputs/portfolio_comparison.png does not exist. Please provide a valid path.
cd /app && ls -la outputs/
total 73 drwxr-sr-x 2 h2tagent h2tagent 4096 Jun 20 18:09 . drwxrws--- 4 h2tagent h2tagent 4096 Jun 20 18:09 .. -rw-r--r-- 1 h2tagent h2tagent 51762 Jun 20 18:09 car_timeline.png -rw-r--r-- 1 h2tagent h2tagent 3465 Jun 20 18:09 event_cars.csv -rw-r--r-- 1 h2tagent h2tagent 2031 Jun 20 18:09 event_study_results.json -rw-r--r-- 1 h2tagent h2tagent 7705 Jun 20 18:09 research_memo.md
cd /app && rm -rf outputs && Rscript analysis.R 2>&1; echo "EXIT: $?"; echo "---"; ls -la outputs/
Pipeline complete. events=35 valid=35 firms=18 (with recalls=14) rho_bar=0.005682 hac_lag=3 calendar n_days=230 EXIT: 0 --- total 73 drwxr-sr-x 2 h2tagent h2tagent 4096 Jun 20 18:10 . drwxrws--- 4 h2tagent h2tagent 4096 Jun 20 18:10 .. -rw-r--r-- 1 h2tagent h2tagent 51762 Jun 20 18:10 car_timeline.png -rw-r--r-- 1 h2tagent h2tagent 3465 Jun 20 18:10 event_cars.csv -rw-r--r-- 1 h2tagent h2tagent 2031 Jun 20 18:10 event_study_results.json -rw-r--r-- 1 h2tagent h2tagent 7705 Jun 20 18:10 research_memo.md
cd /app && grep -n "ggsave\|writeLines\|p2 <-\|port_plot\|cal_df\$cum" analysis.R
424:ggsave("/app/outputs/car_timeline.png", p1, width = 8, height = 5, dpi = 120, bg = "white")
429:cal_df$cum_port <- exp(cumsum(cal_df$port_ret)) - 1
430:cal_df$cum_mkt <- exp(cumsum(cal_df$mkt)) - 1
431:port_plot <- data.frame(
433: cumret = c(cal_df$cum_port, cal_df$cum_mkt),
437:p2 <- ggplot(port_plot, aes(x = date, y = cumret, color = series)) +
605:writeLines(memo, "/app/outputs/research_memo.md")cd /app && sed -n '437,460p' analysis.R
p2 <- ggplot(port_plot, aes(x = date, y = cumret, color = series)) +
geom_line(linewidth = 0.9) +
geom_hline(yintercept = 0, linetype = "dashed", color = "gray50") +
scale_color_manual(values = c("Recall-exposed calendar portfolio" = "#d95f02",
"Market index" = "#1b9e77")) +
labs(title = "Calendar-Time Portfolio vs. Market Cumulative Return",
subtitle = "Equal-weighted portfolio of firms within +/-30 trading days of a recall",
x = "Date", y = "Cumulative simple return", color = "Series") +
theme_minimal(base_size = 12) +
theme(legend.position = "bottom",
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "white", color = NA))
# =====================================================================
# research_memo.md (generated dynamically from the computed results)
# =====================================================================
pct <- function(x) sprintf("%.2f%%", 100 * x)
f2 <- function(x) sprintf("%.2f", x)
f3 <- function(x) sprintf("%.3f", x)
sig_word <- function(p) {
if (!is.finite(p)) return("not estimable")
if (p < 0.01) "highly statistically significant (p < 0.01)"
else if (p < 0.05) "statistically significant (p < 0.05)"
else if (p < 0.10) "marginally significant (p < 0.10)"
else "statistically insignificant"command: str_replace
path: /app/analysis.R
old_str: theme(legend.position = "bottom",
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "white", color = NA))
# =====================================================================
# research_memo.md (generated dynamically from the computed results)
new_str: theme(legend.position = "bottom",
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "white", color = NA))
ggsave("/app/outputs/portfolio_comparison.png", p2, width = 10, height = 5, dpi = 120, bg = "white")
# =====================================================================
# research_memo.md (generated dynamically from the computed results)The file /app/analysis.R has been edited successfully.
cd /app && rm -rf outputs && Rscript analysis.R 2>&1 | tail -1 && echo "---" && ls outputs/ && echo "ggsave count: $(grep -c ggsave analysis.R)"
rho_bar=0.005682 hac_lag=3 calendar n_days=230 --- car_timeline.png event_cars.csv event_study_results.json portfolio_comparison.png research_memo.md ggsave count: 2
command: view path: /app/outputs/portfolio_comparison.png
<output_image> …[truncated]
cd /app && rm -rf outputs && Rscript analysis.R >/dev/null 2>&1 && cp -r outputs /tmp/check1 && rm -rf outputs && Rscript analysis.R >/dev/null 2>&1 && echo "Files present: $(ls outputs | wc -l) of 5" && ls outputs && echo "--- stable across runs ---" && for f in event_study_results.json event_cars.csv research_memo.md; do diff -q /tmp/check1/$f outputs/$f >/dev/null && echo "$f: identical"; done && echo "--- PNGs present & non-empty ---" && for p in car_timeline.png portfolio_comparison.png; do [ -s outputs/$p ] && echo "$p: $(stat -c%s outputs/$p) bytes"; done
Files present: 5 of 5 car_timeline.png event_cars.csv event_study_results.json portfolio_comparison.png research_memo.md --- stable across runs --- event_study_results.json: identical event_cars.csv: identical research_memo.md: identical --- PNGs present & non-empty --- car_timeline.png: 51762 bytes portfolio_comparison.png: 95243 bytes
cd /app && sed -n '255,360p' analysis.R
# 6. Calendar-time portfolio (Jaffe-Mandelker / Fama 1998)
# =====================================================================
# Firm -> vector of its event indices.
firm_event_idx <- list()
for (i in seq_len(n_events)) {
fid <- recalls$firm_id[i]; estr <- as.character(recalls$date[i])
if (!(estr %in% names(date_to_idx))) next
firm_event_idx[[fid]] <- c(firm_event_idx[[fid]], as.integer(date_to_idx[estr]))
}
mkt_by_date <- tapply(returns$market_return, as.character(returns$date), function(z) z[1])
port_dates <- as.Date(character(0)); port_ret <- numeric(0); port_mkt <- numeric(0)
for (d in 0:(n_dates - 1L)) {
incl <- character(0)
for (fid in names(firm_event_idx)) {
if (any(abs(firm_event_idx[[fid]] - d) <= 30L)) incl <- c(incl, fid)
}
if (length(incl) == 0L) next
dd <- as.character(all_dates[d + 1L])
rr <- numeric(0)
for (fid in incl) {
rv <- firm_ret[[fid]]
if (!is.null(rv) && (dd %in% names(rv))) rr <- c(rr, rv[[dd]])
}
if (length(rr) == 0L) next
port_dates <- c(port_dates, all_dates[d + 1L])
port_ret <- c(port_ret, mean(rr))
port_mkt <- c(port_mkt, as.numeric(mkt_by_date[[dd]]))
}
cal_df <- data.frame(date = port_dates, port_ret = port_ret, mkt = port_mkt)
cal_df <- cal_df[order(cal_df$date), ]
cal_lm <- lm(port_ret ~ mkt, data = cal_df)
cal_s <- summary(cal_lm)
calendar <- list(
alpha = unname(coef(cal_lm)[1]),
beta = unname(coef(cal_lm)[2]),
t_alpha = unname(cal_s$coefficients["(Intercept)", "t value"]),
p_value = unname(cal_s$coefficients["(Intercept)", "Pr(>|t|)"]),
n_days = as.integer(nrow(cal_df))
)
# =====================================================================
# 7. Corrado (1989) non-parametric rank test
# =====================================================================
K_corrado <- c()
for (i in seq_len(n_events)) {
fid <- recalls$firm_id[i]; estr <- as.character(recalls$date[i])
if (!(estr %in% names(date_to_idx))) next
if (is.null(firm_ret[[fid]])) next
eidx <- as.integer(date_to_idx[estr])
rv <- firm_ret[[fid]]
offs <- -30:30
vals <- c(); off_present <- c()
for (off in offs) {
tidx <- eidx + off
if (tidx < 0L || tidx >= n_dates) next
td <- as.character(all_dates[tidx + 1L])
if (!(td %in% names(rv))) next
vals <- c(vals, rv[[td]]); off_present <- c(off_present, off)
}
n <- length(vals)
if (n < 2L) next
rk <- rank(vals) # average ties (R default)
mean_rank <- (n + 1) / 2
sd_rank <- sd(rk) # ddof = 1
if (!is.finite(sd_rank) || sd_rank == 0) next
ev_rk <- rk[off_present %in% c(-1L, 0L, 1L)]
if (length(ev_rk) == 0L) next
K_corrado <- c(K_corrado, mean((ev_rk - mean_rank) / sd_rank))
}
Nc <- length(K_corrado)
corr_z <- mean(K_corrado) / (sd(K_corrado) / sqrt(Nc))
corrado <- list(z_stat = corr_z, p_value = 2 * (1 - pnorm(abs(corr_z))))
# =====================================================================
# 8. Cowan (1992) generalized sign test
# =====================================================================
cw <- event_df[event_df$n_est >= 50L, ]
p_bar <- mean(cw$p_hat)
frac_positive <- mean(cw$I_cowan)
Ncw <- nrow(cw)
cowan_z <- (frac_positive - p_bar) / sqrt(p_bar * (1 - p_bar) / Ncw)
cowan <- list(z_stat = cowan_z, p_value = 2 * (1 - pnorm(abs(cowan_z))),
p_bar = p_bar, frac_positive = frac_positive)
# =====================================================================
# 9. Schipper-Thompson (1983) joint Wald test
# =====================================================================
M <- as.matrix(event_df[, c("scar_3day", "scar_2day", "scar_11day")])
M <- M[stats::complete.cases(M), , drop = FALSE]
Nst <- nrow(M)
mbar <- colMeans(M)
Sig <- cov(M) # ddof = 1
wald <- as.numeric(Nst * t(mbar) %*% solve(Sig) %*% mbar)
schipper <- list(wald_stat = wald, wald_p = 1 - pchisq(wald, df = 3), df = 3L)
# ====================================
…[truncated]cd /app && rm -rf outputs && Rscript analysis.R 2>&1 && echo "=== FINAL OUTPUTS ===" && ls -la outputs/ && echo "=== no forbidden packages ===" && grep -iE "library\(|require\(" analysis.RPipeline complete. events=35 valid=35 firms=18 (with recalls=14) rho_bar=0.005682 hac_lag=3 calendar n_days=230 === FINAL OUTPUTS === total 166 drwxr-sr-x 2 h2tagent h2tagent 4096 Jun 20 18:11 . drwxrws--- 4 h2tagent h2tagent 4096 Jun 20 18:11 .. -rw-r--r-- 1 h2tagent h2tagent 51762 Jun 20 18:11 car_timeline.png -rw-r--r-- 1 h2tagent h2tagent 3465 Jun 20 18:11 event_cars.csv -rw-r--r-- 1 h2tagent h2tagent 2031 Jun 20 18:11 event_study_results.json -rw-r--r-- 1 h2tagent h2tagent 95243 Jun 20 18:11 portfolio_comparison.png -rw-r--r-- 1 h2tagent h2tagent 7705 Jun 20 18:11 research_memo.md === no forbidden packages === library(jsonlite) library(ggplot2)
No file changes, the agent left the workspace untouched.
# SyncVals normalized verifier summary # task: product-recall-stock-price-event # attempt: 7 status: completed raw_score: 0.0 # reward rule: binary: score 0.0 != 1.0 # => reward = 0.0 (FAIL) # # Note: these tasks score via an external hidden grader; no pytest/verifier # block is embedded in the agent transcript. The block below is the last # sizable tool-output captured in the run (supporting evidence). ---------------------------------------------------------------------- Pipeline complete. events=35 valid=35 firms=18 (with recalls=14) rho_bar=0.005682 hac_lag=3 calendar n_days=230 === FINAL OUTPUTS === total 166 drwxr-sr-x 2 h2tagent h2tagent 4096 Jun 20 18:11 . drwxrws--- 4 h2tagent h2tagent 4096 Jun 20 18:11 .. -rw-r--r-- 1 h2tagent h2tagent 51762 Jun 20 18:11 car_timeline.png -rw-r--r-- 1 h2tagent h2tagent 3465 Jun 20 18:11 event_cars.csv -rw-r--r-- 1 h2tagent h2tagent 2031 Jun 20 18:11 event_study_results.json -rw-r--r-- 1 h2tagent h2tagent 95243 Jun 20 18:11 portfolio_comparison.png -rw-r--r-- 1 h2tagent h2tagent 7705 Jun 20 18:11 research_memo.md === no forbidden packages === library(jsonlite) library(ggplot2)
Reproduce this trial: git checkout 2f94510 && PYTHONPATH=src python3 scripts/build_site.py , then open trial/trial_dea29c9e2e67499f. Re-running the agent live requires EVAL_PLATFORM_ENABLE_OAUTH_SMOKE=1 and is non-deterministic.
Trial trial_dea29c9e2e67499f · verifier authoritative; classifier explanatory.