TeX 53.6%
Python 46.2%
1"""Revision checks (editorial-board review, 2026-10-17) from stored event files.23Everything here is recomputed from outputs/estimates/events*/ (stage F/F24outputs); no raw minute data are needed. Day-block bootstrap, B=500, same seed.56 A. Pre-event levels: real vs placebo breadth/intensity at t-5 (the "no7 pre-event response" falsification quantified; by decade as well).8 B. Base rates: placebo breadth, eligible receivers, neighbor / non-neighbor9 levels by arm and decade (components of the network differential).10 C. Co-arrivals: share of real events that share their minute with other11 events; network differential and market-wide ΔΔ by co-arrival class.12 D. Pre-level-stratified post comparison (mean-reversion check): real minus13 placebo breadth at t+h within deciles of the pre-event breadth level.14 E. Alternative clustering: ticker-cluster bootstrap SEs.15 F. Signed response (real minus placebo), network differential by shock sign.1617Outputs: outputs/estimates/revision_checks.json,18 outputs/tables/table9_design_checks.{tex,csv},19 outputs/tables/table10_network_components.{tex,csv}20 (+ copies in paper/tables/).21"""2223from __future__ import annotations2425import json26from pathlib import Path2728import numpy as np29import polars as pl3031ROOT = Path(__file__).resolve().parents[1]32EV = ROOT / "outputs" / "estimates" / "events"33EV2 = ROOT / "outputs" / "estimates" / "events2"34TAB = ROOT / "outputs" / "tables"35PTAB = ROOT / "paper" / "tables"36B = 50037SEED = 2026071338DEC = {"1992–1999": (1992, 1999), "2000–2009": (2000, 2009),39 "2010–2019": (2010, 2019), "2020–2026": (2020, 2026)}404142def load(folder: Path, prefix: str) -> pl.DataFrame:43 """Concatenate monthly event files, keeping the real→placebo row order and44 labelling each placebo with the minute of the real event it matches."""45 frames = []46 for p in sorted(folder.glob(f"{prefix}_*.parquet")):47 if p.stat().st_size <= 500:48 continue49 d = pl.read_parquet(p).with_row_index("i")50 prev = d.select((pl.col("i") + 1).alias("i"), pl.col("kind").alias("pk"),51 pl.col("ticker").alias("pt"), pl.col("z").alias("pz"),52 pl.col("date").alias("ev_date"), pl.col("mod").alias("ev_mod"))53 d = d.join(prev, on="i", how="left")54 ok = (pl.col("kind") == "real") | (55 (pl.col("pk") == "real") & (pl.col("pt") == pl.col("ticker"))56 & (pl.col("pz") == pl.col("z")))57 d = d.filter(ok).with_columns(58 pl.when(pl.col("kind") == "real").then(pl.col("date")).otherwise(pl.col("ev_date")).alias("ev_date"),59 pl.when(pl.col("kind") == "real").then(pl.col("mod")).otherwise(pl.col("ev_mod")).alias("ev_mod"),60 ).drop("i", "pk", "pt", "pz")61 frames.append(d)62 df = pl.concat(frames, how="diagonal")63 return df.with_columns(pl.col("date").str.slice(0, 4).cast(pl.Int32).alias("year"))646566def dd_boot(df: pl.DataFrame, col: str, rng, cluster: str = "date"):67 """Real-minus-placebo difference of means with cluster bootstrap SE."""68 g = (df.filter(pl.col(col).is_not_null())69 .group_by(cluster, "kind")70 .agg(pl.col(col).sum().alias("s"), pl.len().alias("n"))71 .pivot(on="kind", index=cluster, values=["s", "n"]))72 need = ("s_real", "n_real", "s_placebo", "n_placebo")73 if any(c not in g.columns for c in need) or g.height < 10:74 return np.nan, np.nan, 075 g = g.fill_null(0).sort(cluster) # deterministic cluster order → reproducible draws76 sr, nr = g["s_real"].to_numpy(), g["n_real"].to_numpy()77 sp, npb = g["s_placebo"].to_numpy(), g["n_placebo"].to_numpy()78 est = sr.sum() / max(nr.sum(), 1) - sp.sum() / max(npb.sum(), 1)79 idx = rng.integers(0, len(sr), size=(B, len(sr)))80 bs = (sr[idx].sum(1) / np.maximum(nr[idx].sum(1), 1)81 - sp[idx].sum(1) / np.maximum(npb[idx].sum(1), 1))82 return float(est), float(bs.std()), int(nr.sum())838485def level_boot(df: pl.DataFrame, col: str, kind: str, rng):86 g = (df.filter((pl.col("kind") == kind) & pl.col(col).is_not_null())87 .group_by("date").agg(pl.col(col).sum().alias("s"), pl.len().alias("n")).sort("date"))88 s, n = g["s"].to_numpy(), g["n"].to_numpy()89 est = s.sum() / max(n.sum(), 1)90 idx = rng.integers(0, len(s), size=(B, len(s)))91 bs = s[idx].sum(1) / np.maximum(n[idx].sum(1), 1)92 return float(est), float(bs.std()), int(n.sum())939495def stratified_boot(df: pl.DataFrame, post: str, pre: str, nbins: int, rng):96 """Real minus placebo mean of `post` within quantile bins of `pre`,97 weighted by the real-arm bin counts; day bootstrap."""98 d = df.filter(pl.col(post).is_not_null() & pl.col(pre).is_not_null())99 qs = [d[pre].quantile(q / nbins) for q in range(1, nbins)]100 d = d.with_columns(pl.col(pre).cut(qs, labels=[str(k) for k in range(nbins)]).alias("bin"))101 g = (d.group_by("date", "bin", "kind")102 .agg(pl.col(post).sum().alias("s"), pl.len().alias("n")))103 dates = sorted(g["date"].unique().to_list())104 di = {x: k for k, x in enumerate(dates)}105 S = {k: np.zeros((len(dates), nbins)) for k in ("real", "placebo")}106 N = {k: np.zeros((len(dates), nbins)) for k in ("real", "placebo")}107 for r in g.iter_rows(named=True):108 S[r["kind"]][di[r["date"]], int(r["bin"])] += r["s"]109 N[r["kind"]][di[r["date"]], int(r["bin"])] += r["n"]110111 def stat(ix):112 sr, nr = S["real"][ix].sum(0), N["real"][ix].sum(0)113 sp, npb = S["placebo"][ix].sum(0), N["placebo"][ix].sum(0)114 w = nr / nr.sum()115 return float(np.sum(w * (sr / np.maximum(nr, 1) - sp / np.maximum(npb, 1))))116117 est = stat(np.arange(len(dates)))118 idx = rng.integers(0, len(dates), size=(B, len(dates)))119 bs = np.array([stat(idx[b]) for b in range(B)])120 return est, float(bs.std()), int(N["real"].sum())121122123def main() -> None:124 rng = np.random.default_rng(SEED)125 e1 = load(EV, "events").with_columns(126 *[(pl.col(f"breadth_{h}") - pl.col("pre_breadth")).alias(f"db_{h}") for h in (5, 15, 30)],127 (pl.col("intensity_5") - pl.col("pre_intensity")).alias("di_5"))128 e2 = load(EV2, "events2").with_columns(129 *[(pl.col(f"nbh_{h}") - pl.col(f"non_{h}")).alias(f"nd_{h}") for h in (5, 15, 30)])130 real1 = e1.filter(pl.col("kind") == "real")131 print(f"events: {real1.height:,} real, {e1.height - real1.height:,} paired placebos")132133 out: dict = {"params": {"bootstrap": B, "seed": SEED}}134135 # ---------------- A. pre-event levels136 pre = {}137 for col, lab in [("pre_breadth", "breadth"), ("pre_intensity", "intensity")]:138 r = level_boot(e1, col, "real", rng)139 p = level_boot(e1, col, "placebo", rng)140 dlt = dd_boot(e1, col, rng)141 pre[lab] = {"real": r[0], "placebo": p[0], "diff": dlt[0], "se": dlt[1], "n": dlt[2]}142 print(f"pre {lab}: real {r[0]:.5f} placebo {p[0]:.5f} diff {dlt[0]*1e4:+.2f} ({dlt[1]*1e4:.2f}) x1e-4")143 pre["breadth_by_decade"] = []144 for dec, (a, b) in DEC.items():145 sub = e1.filter(pl.col("year").is_between(a, b))146 dlt = dd_boot(sub, "pre_breadth", rng)147 pre["breadth_by_decade"].append({"dec": dec, "diff": dlt[0], "se": dlt[1], "n": dlt[2]})148 out["pre_event"] = pre149150 # ---------------- B. base rates and network components151 base = {152 "placebo_breadth_5": float(e1.filter(pl.col("kind") == "placebo")["breadth_5"].mean()),153 "placebo_breadth_30": float(e1.filter(pl.col("kind") == "placebo")["breadth_30"].mean()),154 "nelig_5_mean": float(real1["nelig_5"].mean()),155 "nelig_5_by_decade": {d: float(real1.filter(pl.col("year").is_between(a, b))["nelig_5"].mean())156 for d, (a, b) in DEC.items()},157 "placebo_nbh_5": float(e2.filter(pl.col("kind") == "placebo")["nbh_5"].mean()),158 "placebo_non_5": float(e2.filter(pl.col("kind") == "placebo")["non_5"].mean()),159 "event_days": int(real1["date"].n_unique()),160 "match_rate": float((e1.height - real1.height) / real1.height),161 "match_rate_by_decade": {d: float(e1.filter(pl.col("year").is_between(a, b) & (pl.col("kind") == "placebo")).height162 / max(real1.filter(pl.col("year").is_between(a, b)).height, 1))163 for d, (a, b) in DEC.items()},164 "placebo_cum_15": float(e2.filter(pl.col("kind") == "placebo")["cum_15"].mean()),165 "events_per_stock_week": float(real1.height / (500 * real1["date"].n_unique() / 5)),166 }167 comp = []168 for scope, (a, b) in [("all", (1992, 2026))] + list(DEC.items()):169 sub = e2.filter(pl.col("year").is_between(a, b))170 row = {"scope": scope}171 for kind in ("real", "placebo"):172 for col in ("nbh_5", "non_5", "nd_5"):173 est, se, n = level_boot(sub, col, kind, rng)174 row[f"{kind}_{col}"] = est175 row[f"{kind}_{col}_se"] = se176 dd, se, n = dd_boot(sub, "nd_5", rng)177 row.update({"dd": dd, "dd_se": se, "n": n})178 comp.append(row)179 print(f"net components {scope:10} real nd {row['real_nd_5']*1e4:+.1f} placebo nd {row['placebo_nd_5']*1e4:+.1f} dd {dd*1e4:+.1f} ({se*1e4:.1f})")180 out["base_rates"] = base181 out["network_components"] = comp182183 # ---------------- C. co-arrivals184 cnt = real1.group_by("date", "mod").len().rename({"len": "n_co"})185 e1c = e1.join(cnt, left_on=["ev_date", "ev_mod"], right_on=["date", "mod"], how="left")186 e2c = e2.join(cnt, left_on=["ev_date", "ev_mod"], right_on=["date", "mod"], how="left")187 dist = (real1.join(cnt, on=["date", "mod"], how="left")188 .with_columns(pl.when(pl.col("n_co") == 1).then(pl.lit("1"))189 .when(pl.col("n_co") == 2).then(pl.lit("2"))190 .when(pl.col("n_co") <= 5).then(pl.lit("3–5"))191 .otherwise(pl.lit("6+")).alias("cls"))192 .group_by("cls").len().sort("cls"))193 tot = dist["len"].sum()194 coarr = {"share_by_class": {r["cls"]: r["len"] / tot for r in dist.iter_rows(named=True)},195 "mean_events_per_event_minute": float(real1.height / cnt.height),196 "by_class": []}197 classes = {"1": (1, 1), "2": (2, 2), "3–5": (3, 5), "6+": (6, 10**6)}198 for cls, (lo, hi) in classes.items():199 s1 = e1c.filter(pl.col("n_co").is_between(lo, hi))200 s2 = e2c.filter(pl.col("n_co").is_between(lo, hi))201 row = {"cls": cls, "share": coarr["share_by_class"].get(cls, 0.0)}202 for h in (5, 30):203 est, se, n = dd_boot(s1, f"db_{h}", rng)204 row[f"db{h}"], row[f"db{h}_se"], row["n"] = est, se, n205 for h in (5, 15, 30):206 est, se, n2 = dd_boot(s2, f"nd_{h}", rng)207 row[f"nd{h}"], row[f"nd{h}_se"] = est, se208 coarr["by_class"].append(row)209 print(f"co-arrival {cls:4} share {row['share']:.3f} ΔΔb5 {row['db5']*1e4:+.2f} ({row['db5_se']*1e4:.2f}) nd5 {row['nd5']*1e4:+.1f} ({row['nd5_se']*1e4:.1f})")210 out["coarrival"] = coarr211212 # ---------------- D. pre-level-stratified post comparison213 strat = []214 for h in (5, 15, 30):215 raw = dd_boot(e1, f"breadth_{h}", rng) # post level, real − placebo216 dd = dd_boot(e1, f"db_{h}", rng) # ΔΔ (baseline estimand)217 st = stratified_boot(e1, f"breadth_{h}", "pre_breadth", 10, rng)218 strat.append({"h": h, "post_diff": raw[0], "post_se": raw[1], "dd": dd[0], "dd_se": dd[1],219 "strat": st[0], "strat_se": st[1], "n": st[2]})220 print(f"h={h}: post diff {raw[0]*1e4:+.2f} ({raw[1]*1e4:.2f}); ΔΔ {dd[0]*1e4:+.2f} ({dd[1]*1e4:.2f}); pre-stratified {st[0]*1e4:+.2f} ({st[1]*1e4:.2f})")221 out["stratified"] = strat222223 # ---------------- E. ticker-cluster bootstrap224 clus = {}225 for lab, df, col in [("db5", e1, "db_5"), ("db30", e1, "db_30"), ("nd5", e2, "nd_5")]:226 d_est, d_se, _ = dd_boot(df, col, rng, cluster="date")227 t_est, t_se, _ = dd_boot(df, col, rng, cluster="ticker")228 clus[lab] = {"est": d_est, "se_day": d_se, "se_ticker": t_se}229 print(f"{lab}: est {d_est*1e4:+.2f} day-SE {d_se*1e4:.2f} ticker-SE {t_se*1e4:.2f}")230 out["cluster"] = clus231232 # ---------------- F. signed response and network differential by sign233 signed = []234 for h in (1, 5, 15, 30):235 est, se, n = dd_boot(e1, f"signed_{h}", rng)236 signed.append({"h": h, "diff": est, "se": se, "n": n})237 print(f"signed h={h}: {est*1e4:+.2f} ({se*1e4:.2f}) x1e-4")238 out["signed"] = signed239 bysign = []240 for sgn, lab in [(-1, "negative"), (1, "positive")]:241 est, se, n = dd_boot(e2.filter(pl.col("sign") == sgn), "nd_5", rng)242 bysign.append({"sign": lab, "dd": est, "se": se, "n": n})243 print(f"network diff, {lab} shocks: {est*1e4:+.1f} ({se*1e4:.1f})")244 out["network_by_sign"] = bysign245246 (ROOT / "outputs" / "estimates" / "revision_checks.json").write_text(json.dumps(out, indent=1))247 (ROOT / "outputs" / "diagnostics" / "revision_checks.json").write_text(json.dumps(out, indent=1))248249 # ---------------- Table 9: design diagnostics250 f4 = lambda v, s: f"{v*1e4:,.2f} ({s*1e4:,.2f})"251 rows9 = []252 L = ["% Auto-generated by scripts/17_revision_checks.py — do not edit numbers.",253 "\\begin{table}[htbp]\\centering\\begin{adjustbox}{max width=\\textwidth}\\begin{threeparttable}",254 "\\caption{Design diagnostics: pre-event levels, co-arrivals, mean reversion, and clustering}",255 "\\label{tab:design_checks}",256 "\\small\\begin{tabular}{lrrr}", "\\toprule"]257 L += ["\\multicolumn{4}{l}{\\emph{Panel A: Pre-event levels at $t-5$ ($\\times10^{-4}$)}} \\\\",258 " & Real & Placebo & Real $-$ placebo \\\\ \\midrule"]259 for lab, key in [("Breadth", "breadth"), ("Intensity", "intensity")]:260 r = pre[key]261 L.append(f"{lab} & {r['real']*1e4:,.0f} & {r['placebo']*1e4:,.0f} & {f4(r['diff'], r['se'])} \\\\")262 rows9.append({"panel": "A", "row": lab, "real": r["real"], "placebo": r["placebo"], "diff": r["diff"], "se": r["se"]})263 L += ["\\addlinespace", "\\multicolumn{4}{l}{\\emph{Panel B: By number of source events in the same minute}} \\\\",264 "Events in minute (share of events) & $\\Delta\\Delta$Breadth$_{5}$ & $\\Delta\\Delta$Breadth$_{30}$ & Network diff$_{5}$ \\\\ \\midrule"]265 for r in coarr["by_class"]:266 L.append(f"{r['cls']} ({r['share']*100:.1f}\\%) & {f4(r['db5'], r['db5_se'])} & {f4(r['db30'], r['db30_se'])} & {f4(r['nd5'], r['nd5_se'])} \\\\")267 rows9.append({"panel": "B", "row": r["cls"], "share": r["share"], "db5": r["db5"], "db5_se": r["db5_se"],268 "db30": r["db30"], "db30_se": r["db30_se"], "nd5": r["nd5"], "nd5_se": r["nd5_se"]})269 L += ["\\addlinespace", "\\multicolumn{4}{l}{\\emph{Panel C: Market-wide breadth, alternative estimands ($\\times10^{-4}$)}} \\\\",270 "Horizon & Post level, real $-$ placebo & $\\Delta\\Delta$ (baseline) & Pre-level stratified \\\\ \\midrule"]271 for r in strat:272 L.append(f"$h={r['h']}$ & {f4(r['post_diff'], r['post_se'])} & {f4(r['dd'], r['dd_se'])} & {f4(r['strat'], r['strat_se'])} \\\\")273 rows9.append({"panel": "C", "row": r["h"], "post": r["post_diff"], "post_se": r["post_se"], "dd": r["dd"],274 "dd_se": r["dd_se"], "strat": r["strat"], "strat_se": r["strat_se"]})275 L += ["\\addlinespace", "\\multicolumn{4}{l}{\\emph{Panel D: Bootstrap standard errors by clustering unit ($\\times10^{-4}$)}} \\\\",276 "Statistic & Estimate & Day clusters & Ticker clusters \\\\ \\midrule"]277 for lab, key in [("$\\Delta\\Delta$Breadth$_{5}$", "db5"), ("$\\Delta\\Delta$Breadth$_{30}$", "db30"), ("Network diff$_{5}$", "nd5")]:278 r = clus[key]279 L.append(f"{lab} & {r['est']*1e4:,.2f} & ({r['se_day']*1e4:,.2f}) & ({r['se_ticker']*1e4:,.2f}) \\\\")280 rows9.append({"panel": "D", "row": key, "est": r["est"], "se_day": r["se_day"], "se_ticker": r["se_ticker"]})281 L += ["\\addlinespace", "\\multicolumn{4}{l}{\\emph{Panel E: Signed response, real $-$ placebo ($\\times10^{-4}$)}} \\\\",282 "Horizon & $h=1$ & $h=5$ & $h=30$ \\\\ \\midrule"]283 sg = {r["h"]: r for r in signed}284 L.append("sign$(e_{i,t})\\times$ mean $z_{j,t+h}$ & " + " & ".join(f4(sg[h]["diff"], sg[h]["se"]) for h in (1, 5, 30)) + " \\\\")285 L += ["\\bottomrule\\end{tabular}",286 "\\begin{tablenotes}[flushleft]\\footnotesize",287 "\\item Notes: All statistics from the baseline event files (isolated $|z|\\ge6$ source",288 "shocks, lagged top-500 universe, 1992--2026; matched within-stock placebos).",289 "Day-block bootstrap standard errors (500 replications) in parentheses unless stated.",290 "Panel A: levels of the outcomes five minutes before the event minute. Panel B: events",291 "classified by the number of source events detected in the same minute (the isolation",292 "filters cap the share of simultaneous $|z|>4$ names at 2\\%, not the number of $|z|\\ge6$",293 "events); each placebo inherits the class of its real event. Panel C: `post level' is",294 "the real-minus-placebo difference in breadth at $t+h$ without pre-differencing; `pre-level",295 "stratified' reweights the same difference within deciles of the pre-event breadth level.",296 "Panel D: standard errors from resampling trading days versus resampling source tickers.",297 "Panel E: cross-sectional mean standardized return of receivers signed by the source shock.",298 "\\end{tablenotes}\\end{threeparttable}\\end{adjustbox}\\end{table}"]299 (TAB / "table9_design_checks.tex").write_text("\n".join(L))300 (PTAB / "table9_design_checks.tex").write_text("\n".join(L))301 pl.DataFrame(rows9, strict=False).write_csv(TAB / "table9_design_checks.csv")302303 # ---------------- Table 10: network components by decade304 L = ["% Auto-generated by scripts/17_revision_checks.py — do not edit numbers.",305 "\\begin{table}[htbp]\\centering\\begin{adjustbox}{max width=\\textwidth}\\begin{threeparttable}",306 "\\caption{Components of the network differential at $h=5$, by decade}",307 "\\label{tab:network_components}",308 "\\small\\begin{tabular}{lrrrrrrr}", "\\toprule",309 " & \\multicolumn{3}{c}{Real events} & \\multicolumn{3}{c}{Placebo events} & \\\\",310 "\\cmidrule(lr){2-4}\\cmidrule(lr){5-7}",311 "Sample & Neighbors & Others & Diff. & Neighbors & Others & Diff. & Real $-$ placebo \\\\ \\midrule"]312 rows10 = []313 for r in comp:314 lab = "Full sample" if r["scope"] == "all" else r["scope"]315 L.append(f"{lab} & {r['real_nbh_5']*1e4:,.0f} & {r['real_non_5']*1e4:,.0f} & {r['real_nd_5']*1e4:,.1f} ({r['real_nd_5_se']*1e4:,.1f})"316 f" & {r['placebo_nbh_5']*1e4:,.0f} & {r['placebo_non_5']*1e4:,.0f} & {r['placebo_nd_5']*1e4:,.1f} ({r['placebo_nd_5_se']*1e4:,.1f})"317 f" & {r['dd']*1e4:,.1f} ({r['dd_se']*1e4:,.1f}) \\\\")318 rows10.append(r)319 L += ["\\bottomrule\\end{tabular}",320 "\\begin{tablenotes}[flushleft]\\footnotesize",321 "\\item Notes: Share of receivers with $|z|>2$ five minutes after the event minute, among",322 "the source's twenty lagged-correlation neighbors and among all other receivers, for real",323 "events and for their matched placebos ($\\times10^{-4}$). `Diff.' is neighbors minus",324 "others within an arm; the last column is the network differential of",325 "Table~\\ref{tab:network_mechanism} (real minus placebo). Day-block bootstrap SEs (500",326 "replications) in parentheses.",327 "\\end{tablenotes}\\end{threeparttable}\\end{adjustbox}\\end{table}"]328 (TAB / "table10_network_components.tex").write_text("\n".join(L))329 (PTAB / "table10_network_components.tex").write_text("\n".join(L))330 pl.DataFrame(rows10).write_csv(TAB / "table10_network_components.csv")331 print("revision checks complete")332333334if __name__ == "__main__":335 main()336