SPB Git forge

spb/wp15_uqo

Public MIT
3commits 1branches 1releases
7.6 MBsize
maindefault branch
53 min agolast push
TeX 53.6% Python 46.2%
8.0 KB · 172 lines python
Raw Blame History
1"""Robustness battery (Table VIII) from stored event files.23For each subsample/design variant, re-estimate (i) the market-wide baseline4ΔΔ breadth at h ∈ {5, 30} and (ii) the headline network differential at h=5,5with day-block bootstrap SEs:67  full                 full sample (reference)8  post-decimal         events dated 2002+ (kills 1/8–1/16 tick regime)9  mid-day              event minute in [10:00, 15:30) ET10  ex-announcement      exclude 10:00±10min and 14:00–14:30 ET event minutes11  calm-years           exclude 2008, 2009, 202012  crisis-years         2008, 2009, 2020 only13  |z|>=8, |z|>=12      nested source-magnitude thresholds14  ex-noncommon         exclude sources flagged non-common (ticker heuristic)15  placebo-network      network differential computed on placebo events only16                       (falsification: should be ≈ 0)17  month-cluster        full sample, bootstrap clustered by month instead of day1819Outputs: outputs/estimates/stage_f_robustness.json,20         outputs/tables/table8_robustness.{tex,csv}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"34UNIV = ROOT / "data" / "metadata" / "universe_monthly.parquet"35TAB = ROOT / "outputs" / "tables"36B = 50037SEED = 20260713383940def dd_boot(df, col, rng, cluster="date"):41    g = (df.filter(pl.col(col).is_not_null())42         .group_by(cluster, "kind")43         .agg(pl.col(col).sum().alias("s"), pl.len().alias("n"))44         .pivot(on="kind", index=cluster, values=["s", "n"]))45    need = ("s_real", "n_real", "s_placebo", "n_placebo")46    if any(c not in g.columns for c in need) or g.height < 10:47        return np.nan, np.nan, 048    g = g.fill_null(0)49    sr, nr = g["s_real"].to_numpy(), g["n_real"].to_numpy()50    sp, npb = g["s_placebo"].to_numpy(), g["n_placebo"].to_numpy()51    est = sr.sum() / max(nr.sum(), 1) - sp.sum() / max(npb.sum(), 1)52    idx = rng.integers(0, len(sr), size=(B, len(sr)))53    bs = (sr[idx].sum(1) / np.maximum(nr[idx].sum(1), 1)54          - sp[idx].sum(1) / np.maximum(npb[idx].sum(1), 1))55    return float(est), float(bs.std()), int(nr.sum())565758def one_boot(df, col, rng, kind="placebo"):59    """Single-arm mean with day bootstrap (for the placebo-network check)."""60    g = (df.filter((pl.col("kind") == kind) & pl.col(col).is_not_null())61         .group_by("date").agg(pl.col(col).sum().alias("s"), pl.len().alias("n")))62    s, n = g["s"].to_numpy(), g["n"].to_numpy()63    est = s.sum() / max(n.sum(), 1)64    idx = rng.integers(0, len(s), size=(B, len(s)))65    bs = s[idx].sum(1) / np.maximum(n[idx].sum(1), 1)66    return float(est), float(bs.std()), int(n.sum())676869def main() -> None:70    rng = np.random.default_rng(SEED)71    e1 = pl.concat([pl.read_parquet(p) for p in sorted(EV.glob("events_*.parquet"))72                    if p.stat().st_size > 500], how="diagonal").with_columns(73        pl.col("date").str.slice(0, 4).cast(pl.Int32).alias("year"),74        pl.col("z").abs().alias("absz"),75        (pl.col("breadth_5") - pl.col("pre_breadth")).alias("db_5"),76        (pl.col("breadth_30") - pl.col("pre_breadth")).alias("db_30"),77        pl.col("date").str.slice(0, 7).alias("ym"))78    e2 = pl.concat([pl.read_parquet(p) for p in sorted(EV2.glob("events2_*.parquet"))79                    if p.stat().st_size > 500], how="diagonal").with_columns(80        pl.col("date").str.slice(0, 4).cast(pl.Int32).alias("year"),81        pl.col("z").abs().alias("absz"),82        (pl.col("nbh_5") - pl.col("non_5")).alias("nd_5"),83        pl.col("date").str.slice(0, 7).alias("ym"))84    noncommon = (pl.read_parquet(UNIV)85                 .filter(pl.col("flag_noncommon"))86                 .select("month", "ticker").unique()87                 .with_columns(pl.lit(True).alias("nc")))88    e1 = e1.join(noncommon, on=["month", "ticker"], how="left")89    e2 = e2.join(noncommon, on=["month", "ticker"], how="left")9091    CRISIS = [2008, 2009, 2020]92    subs = {93        "Full sample": (pl.lit(True), pl.lit(True)),94        "Post-decimalization (2002+)": (pl.col("year") >= 2002,) * 2,95        "Mid-day events (10:00–15:30)": (pl.col("mod").is_between(600, 929),) * 2,96        "Ex announcement windows": (97            ~(pl.col("mod").is_between(595, 615)98              | pl.col("mod").is_between(840, 870)),) * 2,99        "Calm years (ex 2008–09, 2020)": (~pl.col("year").is_in(CRISIS),) * 2,100        "Crisis years only": (pl.col("year").is_in(CRISIS),) * 2,101        "Source $|z|\\ge 8$": (pl.col("absz") >= 8,) * 2,102        "Source $|z|\\ge 12$": (pl.col("absz") >= 12,) * 2,103        "Ex non-common tickers": (pl.col("nc").is_null(),) * 2,104    }105    rows = []106    for label, (c1, c2) in subs.items():107        b5, s5, n5 = dd_boot(e1.filter(c1), "db_5", rng)108        b30, s30, _ = dd_boot(e1.filter(c1), "db_30", rng)109        nd, snd, nn = dd_boot(e2.filter(c2), "nd_5", rng)110        rows.append({"sample": label, "db5": b5, "db5_se": s5,111                     "db30": b30, "db30_se": s30,112                     "nd5": nd, "nd5_se": snd, "n": n5})113        print(f"{label:<32} ΔΔb5={b5*1e4:+.2f}({s5*1e4:.2f}) "114              f"ΔΔb30={b30*1e4:+.2f}({s30*1e4:.2f}) "115              f"net5={nd*1e4:+.1f}({snd*1e4:.1f}) n={n5:,}", flush=True)116117    # month-cluster bootstrap variant (full sample)118    b5, s5, n5 = dd_boot(e1, "db_5", rng, cluster="ym")119    nd, snd, _ = dd_boot(e2, "nd_5", rng, cluster="ym")120    rows.append({"sample": "Month-cluster bootstrap", "db5": b5, "db5_se": s5,121                 "db30": None, "db30_se": None, "nd5": nd, "nd5_se": snd, "n": n5})122    print(f"month-cluster: ΔΔb5={b5*1e4:+.2f}({s5*1e4:.2f}) net5={nd*1e4:+.1f}({snd*1e4:.1f})")123124    # placebo-network falsification: nbh−non among placebos alone125    pe, ps, pn = one_boot(e2, "nd_5", rng, kind="placebo")126    rows.append({"sample": "Placebo events, neighbor diff", "db5": None,127                 "db5_se": None, "db30": None, "db30_se": None,128                 "nd5": pe, "nd5_se": ps, "n": pn})129    print(f"placebo network diff: {pe*1e4:+.1f}({ps*1e4:.1f})")130131    out = pl.DataFrame(rows)132    out.write_csv(TAB / "table8_robustness.csv")133    (ROOT / "outputs" / "estimates" / "stage_f_robustness.json").write_text(134        json.dumps(rows, indent=1))135136    def cell(v, s):137        if v is None:138            return "---"139        return f"{v*1e4:,.2f} ({s*1e4:,.2f})"140    lines = [141        "% Auto-generated by scripts/16_robustness.py — do not edit numbers.",142        "\\begin{table}[t]\\centering\\begin{adjustbox}{max width=\\textwidth}\\begin{threeparttable}",143        "\\caption{Robustness of the market-wide null and the network channel}",144        "\\label{tab:robustness}",145        "\\small\\begin{tabular}{lrrr}",146        "\\toprule",147        "Sample / design & $\\Delta\\Delta$Breadth$_{5}$ & "148        "$\\Delta\\Delta$Breadth$_{30}$ & Network diff$_{5}$ \\\\",149        "\\midrule"]150    for r in rows:151        lines.append(f"{r['sample']} & {cell(r['db5'], r['db5_se'])} & "152                     f"{cell(r['db30'], r['db30_se'])} & "153                     f"{cell(r['nd5'], r['nd5_se'])} \\\\")154    lines += [155        "\\bottomrule\\end{tabular}",156        "\\begin{tablenotes}[flushleft]\\footnotesize",157        "\\item Notes: All entries $\\times 10^{-4}$, day-block bootstrap SEs in",158        "parentheses (500 reps; final row of the market-wide block uses",159        "month-cluster resampling). $\\Delta\\Delta$Breadth = abnormal market-wide",160        "breadth (real $-$ placebo, post $-$ pre). Network diff = neighbor $-$",161        "non-neighbor abnormal breadth at $h{=}5$. `Placebo events' row reports",162        "the neighbor differential among placebo events alone (falsification;",163        "expected $\\approx 0$).",164        "\\end{tablenotes}\\end{threeparttable}\\end{adjustbox}\\end{table}"]165    (TAB / "table8_robustness.tex").write_text("\n".join(lines))166    (ROOT / "paper" / "tables" / "table8_robustness.tex").write_text("\n".join(lines))167    print("robustness table written")168169170if __name__ == "__main__":171    main()172