SPB Git

spb/wp27_uqo

Public
5.6 KB · 92 lines python
1"""Mechanism checks (descriptive, pre-registered as non-identified mediation):2(1) coordination rises with the LLM share (H2): llm_pairwise_corr, llm_synchrony and the classical analogue flow_corr_llm vs share;3(2) within each share, runs with more correlated LLM order flow have worse price discovery / more instability (seed-FE OLS, descriptive);4(3) diversity arms: coordination and outcomes for single vs diverse prompts vs two models at 25 % (paired);5(4) theory comparative statics: sign of the dose effect on informativeness proxies vs Table tab:predictions.6Writes results/tables/tab_mechanisms.tex and mechanisms.csv."""7from __future__ import annotations89import sys10from pathlib import Path1112import numpy as np13import pandas as pd14import statsmodels.formula.api as smf1516ROOT = Path(__file__).resolve().parents[1]17sys.path.insert(0, str(ROOT / "src"))18from market_agents.analysis.estimators import paired_effects  # noqa: E4021920p = ROOT / "results/runs/confirmatory_openai/outcomes.parquet"21if not p.exists():22    raise SystemExit("confirmatory design not run yet")23df = pd.read_parquet(p)24df["cell"] = df.name.str.split("/").str[-1]25OUT = ROOT / "results/tables"26rows = []2728# (1) coordination vs share29d = df[(df.tr_arm == "dose") & (df.llm_share > 0)].copy()30for oc in ("llm_pairwise_corr", "llm_synchrony", "flow_corr_llm", "llm_hold_rate", "llm_valid_rate"):31    if oc in d:32        g = d.groupby("llm_share")[oc].agg(["mean", "std", "size"])33        for s, r in g.iterrows():34            rows.append({"block": "coordination_by_share", "share": s, "stat": oc, "mean": r["mean"], "se": r["std"] / np.sqrt(r["size"]), "n": r["size"]})35        # trend test: OLS of stat on share with seed FE36        m = smf.ols(f"{oc} ~ llm_share + C(seed)", data=d.dropna(subset=[oc])).fit(cov_type="cluster", cov_kwds={"groups": d.dropna(subset=[oc])["seed"]})37        rows.append({"block": "trend", "stat": oc, "beta_share": m.params["llm_share"], "se": m.bse["llm_share"], "p": m.pvalues["llm_share"], "n": int(m.nobs)})3839# classical analogue: flow correlation of classical groups in the same runs40for grp in ("fundamentalist", "trend", "noise", "market_maker"):41    c = f"flow_corr_{grp}"42    if c in df:43        rows.append({"block": "classical_flow_corr", "stat": c, "mean": df[df.cell == "dose_000"][c].mean(), "n": int(df[df.cell == "dose_000"][c].notna().sum())})4445# (2) within-share association between coordination and outcomes (descriptive)46for oc in ("mad_pct", "msd_log", "time_to_absorb", "n_bubbles", "max_dd", "spread_bp", "lambda_bp_100"):47    for med in ("llm_pairwise_corr", "llm_synchrony"):48        dd = d.dropna(subset=[oc, med])49        if len(dd) < 8 or dd[med].std() == 0:50            continue51        z = dd.copy()52        z["med_z"] = (z[med] - z[med].mean()) / z[med].std()53        m = smf.ols(f"{oc} ~ med_z + C(llm_share) + C(seed)", data=z).fit(cov_type="cluster", cov_kwds={"groups": z["seed"]})54        rows.append({"block": "within_share_association", "stat": oc, "mediator": med, "beta_per_sd": m.params["med_z"], "se": m.bse["med_z"], "p": m.pvalues["med_z"], "n": int(m.nobs)})5556# (3) diversity arms at 25 %57for cell in ("div_prompts_025", "div_models_025", "memory_025", "channel_025", "adaptive_025", "fair_latency_025"):58    for oc in ("llm_pairwise_corr", "llm_synchrony", "mad_pct", "time_to_absorb", "n_bubbles", "max_dd", "spread_bp"):59        if cell in set(df.cell) and oc in df:60            e = paired_effects(df, oc, "cell", cell, "dose_025")61            rows.append({"block": "arm_vs_dose_025", "cell": cell, "stat": oc, "control_mean": e.get("control_mean"), "diff": e.get("diff"), "ci_lo": e.get("ci_lo"), "ci_hi": e.get("ci_hi"), "p": e.get("p"), "cohen_d": e.get("cohen_d"), "n": e.get("n")})6263t = pd.DataFrame(rows)64t.to_csv(OUT / "mechanisms.csv", index=False)6566# LaTeX: coordination by share + trend67lines = ["\\begin{tabular}{lrrrrrr}", "\\toprule", "Statistic & 5\\% & 10\\% & 25\\% & 50\\% & 100\\% & Trend per unit share (SE) \\\\", "\\midrule"]68for oc, lab in (("llm_pairwise_corr", "Pairwise order-flow correlation (LLM)"), ("llm_synchrony", "Synchrony (LLM)"), ("flow_corr_llm", "Flow correlation from trades (LLM)"), ("llm_hold_rate", "Hold rate"), ("llm_valid_rate", "Valid-decision rate")):69    c = t[(t.block == "coordination_by_share") & (t.stat == oc)].set_index("share")70    tr = t[(t.block == "trend") & (t.stat == oc)]71    if len(c) == 0:72        continue73    vals = " & ".join(f"{c.loc[s, 'mean']:.3f}" if s in c.index else "--" for s in (0.05, 0.10, 0.25, 0.50, 1.00))74    trs = f"{tr.iloc[0].beta_share:+.3f} ({tr.iloc[0].se:.3f})" + ("$^{***}$" if tr.iloc[0].p < 0.01 else "$^{**}$" if tr.iloc[0].p < 0.05 else "$^{*}$" if tr.iloc[0].p < 0.1 else "") if len(tr) else "--"75    lines.append(f"{lab} & {vals} & {trs} \\\\")76cl = t[t.block == "classical_flow_corr"]77if len(cl):78    lines.append("\\midrule")79    for r in cl.itertuples():80        lines.append(f"Flow correlation, {r.stat.replace('flow_corr_', '').replace('_', ' ')} (0\\% cell) & \\multicolumn{{5}}{{r}}{{{r.mean:.3f}}} & \\\\")81lines += ["\\bottomrule", "\\end{tabular}"]82(OUT / "tab_coordination.tex").write_text("\n".join(lines))8384w = t[t.block == "within_share_association"]85if len(w):86    lines = ["\\begin{tabular}{llrrr}", "\\toprule", "Outcome & Mediator (per 1 SD) & Coefficient & SE & $p$ \\\\", "\\midrule"]87    for r in w.itertuples():88        lines.append(f"{r.stat.replace('_', ' ')} & {r.mediator.replace('llm_', '').replace('_', ' ')} & {r.beta_per_sd:+.3f} & {r.se:.3f} & {r.p:.3f} \\\\")89    lines += ["\\bottomrule", "\\end{tabular}"]90    (OUT / "tab_mechanism_association.tex").write_text("\n".join(lines))91print(t.to_string(max_rows=80))92
Files · b6c1d09b402a43e723c06ff0c9a6073a51ea3fa7
Menu

Theme