Identification and pre-analysis plan — WP 27
Version 1.0, 2026-10-27, written before any live API call. Everything in §3–§6 is frozen after the pilot (§6.2); changes afterwards are logged as deviations in the paper.
1. Causal structure
1.1 Variables
- Z — treatment (cell labels):
llm_shareφ ∈ {0, …, 1}; provider/model mix (provider_mix,models);prompt_set∈ {single, diverse};memory∈ {0, 5};channel∈ {off, on}; leverage (share of leveraged slots,leverage_cap); safeguards (halt_threshold,position_limit); fairness regime (decision_cap,usd_cap). A cell is a vector of these. - U — exogenous randomness, by named seed stream (
utils/rng.py):fundamentals(dividend path, jumps),news,slots(wealth multipliers, latencies, informed draws),assign(classical policy of each slot),llm_assign(which eligible slots become LLM, provider allocation, decision offsets),activation,tiebreak, and per-agent generatorsagent_rng(seed, id)(classical policy randomness, private-signal noise). - ε — provider noise: the LLM's sampling randomness, not seeded and not controllable (gpt-5 family rejects
temperature; no seed parameter). It is part of the treatment. - M — within-run mediators (
outcomes.py): order-flow correlation ρ̄ =llm_pairwise_corr, synchrony =llm_synchrony, aggressiveness β̂ =llm_market_share(share of market orders) and mean order size, validity =llm_valid_rate, realized dose =llm_share_orders,llm_share_volume. - Y — run-level outcomes (§3).
1.2 DAG
U_fund, U_news ───────────────────────────────────────────┐
U_slots, U_assign, U_agents ───────────────┐ │
│ │
cell label ─────► Z ───┐ ▼ ▼
├──► LLM slot set & policies ──► M (ρ̄, synchrony, β̂, validity, realized dose) ──► Y
U_llm_assign ──────────┘ ▲ ▲ ▲
│ │ │
ε_provider ─────────────────────────┴─────────────────────┘ │
U_activation, U_tiebreak ───────────────────────────────────────────────────────────────────────────────┘
Z ──────────────────────────────────────────────────────────────────────────────────────────────────────┘ (direct: fewer heuristic agents, halts, limits)Z → Y is identified because Z is set by the design file and the only random input that depends on Z (llm_assign) is independent of every other stream by construction (SeedSequence.spawn). Fundamentals, news, endowments, latencies, information draws and policy assignment are identical across cells sharing a seed; the treatment replaces the policy in a stratified subset of slots and nothing else. Slot replacement is stratified by classical policy type (proportional quotas, market makers never replaced).
Two leaks of common random numbers exist in the current code and must be closed before the pilot (gate 4 in research_plan.md): (i) the per-tick shuffle of active classical agents consumes the activation stream, whose length depends on the number of classical agents, so activation draws differ across arms after tick 0; (ii) an LLM slot draws its informed status from llm.informed_share (30 %) instead of inheriting it from the replaced slot, so the treatment arm holds more private information than the control (in a mock run, 44 % vs 6 % informed). Neither leak biases the ATE (Z is still randomized), but (ii) changes the estimand to "LLM policy + extra information" and (i) reduces the precision gain from pairing.
1.3 Confounders ruled out by design
| Potential confounder | Why it cannot confound Z → Y |
|---|---|
| Fundamental path, shocks, news | Same fundamentals/news streams within a seed; shocks at fixed ticks in every cell |
| Endowments, wealth dispersion, latencies | Same slots stream; LLM slots keep the replaced slot's cash, shares and latency |
| Composition of classical types | assign stream fixed; replacement stratified by type |
| Private information | After the gate-4 fix, inherited from the replaced slot |
| Market-maker behaviour | Market makers never replaced (llm_keep_market_makers) |
| Code/version drift | config_hash, git_revision, prompt_version, price table recorded in every manifest |
1.4 What is not identified
- Mediation. M is generated inside the run jointly with Y by the same path of U and ε. Sequential ignorability (M ⫫ Y(z, m) | Z, covariates) does not hold: e.g. a volatile fundamental path raises both ρ̄ and
msd_log. We therefore report mediation only as a descriptive decomposition (§4.6) and treat each M as an outcome of Z (that is H2 and is identified). - Effects of the model's reasoning per se. Z bundles the model, the prompt, the decision frequency and the invalid-decision rule. The estimand is intention-to-treat: "deploy this agent as configured".
2. Estimands
Unit = run (seed s × cell z). Y_s(z) is the potential outcome of seed s under cell z.
| Label | Estimand | Contrast |
|---|---|---|
| E1 | ATE of cell z vs control: τ_z = E_s[Y_s(z) − Y_s(0)] | Each treated cell vs share_0 of the same seed |
| E2 | Dose–response: τ(φ) for φ ∈ {0.01, 0.05, 0.10, 0.25, 0.50, 0.75, 1.00} with gpt-5.4-nano, single prompt; linear slope ∂τ/∂φ and ordered-trend test | Levels vs 0 and across levels |
| E3 | Diversity at equal share φ: (a) τ_div = E[Y(diverse, φ) − Y(single, φ)]; (b) τ_mix = E[Y(mix, φ) − ½(Y(nano, φ) + Y(mini, φ))] | (a) prompt diversity holding the model fixed; (b) model diversity net of composition (requires the mini-only cell) |
| E4 | Mechanism arms at φ = 0.25: memory 5 vs 0; channel on vs off; leverage high vs base; breaker vs none; position limit vs none | Each vs share_25 |
| E5 | φ × leverage interaction: [Y(φ, lev_hi) − Y(0, lev_hi)] − [Y(φ, lev_base) − Y(0, lev_base)] | Difference of paired differences |
| E6 | Local projection of the mid–value gap around the t = 300 fundamental jump: β_h = E[g_s(z, h) − g_s(0, h)], g = log P − log V, h ∈ [−20, 100]; placebo at t = 450 | Treated vs control path |
| E7 | Realized-dose-scaled effect: τ_z / E[llm_share_orders] (descriptive, not causal per unit of order flow) |
Reported alongside E1 |
Primary confirmatory contrast: E1 at φ = 0.25 (gpt-5.4-nano, single prompt, confirmatory fundamentals); secondary confirmatory: E1 at φ = 0.50 and E3(a). Everything else is exploratory unless the tiered budget allows the pre-specified seed count (protocol.md §5).
3. Outcomes
All variables are produced by metrics/outcomes.py on ticks t ≥ warmup = 100 (baseline.yaml), one row per run in outcomes.parquet. Multi-asset variants carry the suffix _a1 for asset 1.
3.1 Primary families and directional hypotheses
| Family | Variable | Definition (short) | Direction under H | Hypothesis |
|---|---|---|---|---|
| Efficiency | msd_log (lead H1, H4) |
mean (log P − log V)² | ↑ in φ; ↓ with diversity | H1, H4 |
mad_pct |
mean 100· | log P − log V | ||
time_to_absorb |
first h after t = 300 with | P/V − 1 | < 1 % for 5 ticks (censored at 100) | |
response_ratio |
(P_{t+20} − P_{t−1}) / (V_{t+20} − V_{t−1}) | RR − 1 | ||
vr5 |
VR(5) of log mid | VR − 1 | ||
| Liquidity | spread_bp (lead H1b) |
mean quoted spread / mid, bp | ↑ in φ | H1b |
eff_spread_bp |
2· | trade − pre-trade mid | / mid, bp | |
depth5 |
mean lots within 5 levels, both sides | ↓ in φ | H1b | |
lambda_bp_100 |
OLS of Δlog mid (bp) on signed volume / 100 | two-sided (theory: λ = Cov/Var falls as flow gets noisier) | H1b | |
resiliency |
half-life (ticks) of log-spread deviations | ↑ in φ | H1b | |
| Instability | n_crashes (lead H3) |
drops ≥ 10 % from the trailing 20-tick max | ↑ in φ × leverage | H3 |
max_dd (lead H6) |
max drawdown of mid | ↑ in φ; ↓ with safeguards | H3, H6 | |
n_bubbles, bubble_amp |
episodes with | P/V − 1 | > 5 % for ≥ 10 ticks; max amplitude | |
acf_absr_1_10 |
Σ ACF( | r | ) lags 1–10 | |
kurtosis |
excess kurtosis of tick returns | ↑ in φ | H3 | |
| Coordination | llm_pairwise_corr (lead H2) |
mean pairwise corr. of agents' net order sign per 20-tick window | ↑ in φ; ↓ with diversity | H2, H4 |
llm_synchrony (lead H5) |
mean | mean sign | per decision round | |
| Contagion (K = 2) | ret_corr, excess_comovement (lead), spillover_dy |
return correlation; minus fundamental correlation; Diebold–Yilmaz total spillover (VAR(2), h = 10) | ↑ in φ | H2/H3 extension |
Thresholds are fixed: bubble 5 % for ≥ 10 ticks; crash 10 % within 20 ticks. Sensitivity grid (reported for every instability result, no re-selection): bubble ∈ {3, 5, 8} %, crash ∈ {7, 10, 15} %. In the classical control (10 seeds) n_crashes = 0 at 10 %: a floor effect is expected and the 7 % variant is the pre-specified fallback for H3 (§research_plan.md §4).
Transformations fixed in advance: msd_log is analysed on log10 scale for t-based inference (strongly right-skewed: control mean 0.0020, SD 0.0017); time_to_absorb is censored at 100 and analysed with rank-based inference; counts (n_bubbles, n_crashes, n_halts) by mean difference with randomization inference.
3.2 Secondary outcomes
vol_bp, bias_pct, vr10, acf_r_1, bubble_dur, volume, turnover, cancel_ratio, fill_ratio, reject_ratio, n_halts, share_ticks_halted, pnl_<policy> (incl. pnl_llm), mm_pnl, mm_inventory_abs, gini_final, n_bankrupt, leadlag_01, fund_corr, and the LLM diagnostics llm_valid_rate, llm_hold_rate, llm_market_share, llm_buy_share, llm_usd, llm_tokens, llm_latency_s, llm_cached_hits, plus the manifest fields llm_share_orders, llm_share_volume, n_llm_decisions.
3.3 Known measurement caveats
llm_synchronygroups decisions by tick; with staggered offsets only agents sharing an offset are in the same round (≈ n_llm/20 per tick), so at φ ≤ 0.05 it rests on very few rounds.llm_pairwise_corr(20-tick windows) is the preferred coordination measure at low φ.- Both coordination measures exist only for LLM agents, so they are undefined in the control; H2 is tested as a dose–response among φ > 0 plus a comparison with a classical analogue (§3.4).
- In multi-asset runs the prompt covers asset 0 only (
user_message(obs, 0)): LLM agents never trade asset 1, so contagion is measured from the LLM-traded asset to a classical-only asset.
3.4 Analysis-stage additions (to be coded in analysis/, not in outcomes.py)
Classical analogue of ρ̄: the same pairwise_sign_corr applied to classical agents' aggressor-signed trades from trades.parquet, by policy type. Local-projection paths (E6) from ticks.parquet.
4. Estimators
4.1 Paired differences (primary)
For each cell z and seed s: Δ_s = Y_s(z) − Y_s(0). Report mean Δ̄, SD, paired-t 95 % CI (df = n − 1) and the effect size d_z = Δ̄ / SD(Δ). Also Glass's Δ = Δ̄ / SD(Y(0)) using 30 free control seeds, for comparability across cells.
4.2 Randomization inference (primary p-value)
Under the sharp null Y_s(z) = Y_s(0), the sign of each Δ_s is exchangeable within its pair. P-value = share of the 2ⁿ sign-flip assignments with |mean Δ| ≥ |observed| (exact enumeration for n ≤ 14; 10⁵ Monte Carlo draws otherwise). This is distribution-free and valid for counts, censored and skewed outcomes at small n. The paired t (§4.1) and Wilcoxon signed-rank are reported as secondary.
4.3 OLS with seed fixed effects (dose–response and multi-cell arms)
Y_{s,z} = α_s + Σ_k τ_k 1[z = k] + ε_{s,z}. With ≤ 12 clusters, cluster-robust SE by seed are unreliable; inference uses the wild cluster bootstrap (Rademacher weights, 9,999 draws, cluster = seed) and, for ordered φ, Page's L trend test for randomized blocks (block = seed). The linear slope ∂τ/∂φ is reported with a bootstrap CI; the dose–response figure shows cell means with paired CIs.
4.4 Diversity contrasts
E3(a): paired difference diverse − single within seed. E3(b): τ_mix,s = Y_s(mix) − ½[Y_s(nano) + Y_s(mini)], then §4.1–4.2.
4.5 Multiplicity, CIs, effect sizes
Holm–Bonferroni within each hypothesis family of §3.1 at family-wise α = 0.05 (the lead outcome is reported adjusted and unadjusted). Two-sided tests; the sign of the estimate is compared with the predicted direction. 95 % CIs everywhere; no result is reported as a bare p-value. Secondary outcomes are unadjusted and labelled exploratory.
4.6 Mediation (descriptive only)
Within treated cells, regress Y on M with seed FE and cell dummies; report the coefficient and the share of τ_z "associated" with M (product-of-coefficients with the Z → M regression). Stated assumption: this is a decomposition under no unmeasured mediator–outcome confounding, which §1.4 argues is false; the numbers describe co-movement, not causal paths.
4.7 Local projections (E6)
For h ∈ [−20, 100]: g_s(z, h) = log P_{300+h} − log V_{300+h}. β_h = mean over seeds of g_s(z, h) − g_s(0, h), with pointwise paired-t bands and a sup-t band from the bootstrap. The same at the placebo tick 450 must show β_h ≈ 0 (falsification). Summary scalars: time_to_absorb, response_ratio.
5. Power and seed counts
Paired design, standardized paired effect d = Δ̄ / SD(Δ), two-sided α = 0.05, power 0.80:
n ≈ (z_{0.975} + z_{0.80})² / d² = (1.960 + 0.842)² / d² = 7.85 / d²
| d | n (pairs) |
|---|---|
| 0.5 | 32 |
| 0.8 | 13 |
| 1.0 | 8 |
| MDE at n = 10 | 0.89 |
| MDE at n = 8 | 0.99 |
σ_d (the SD of paired differences) is TBD from the pilot (3 pairs per share; a poor estimate — the 30-seed classical control SD is used as a floor: e.g. SD(msd_log) = 0.0017, SD(spread_bp) = 6.4, SD(max_dd) = 0.042 in the default calibration cell, n = 10). Rule for the confirmatory seed count of the primary contrast: n_conf = max(8, ⌈7.85 σ̂_d² / Δ_min²⌉) with Δ_min = 0.5 σ̂_d (i.e. 32), capped by the tier budget (protocol.md §5: 10 seeds at 25 % and 50 % ≈ 10 USD). The achieved MDE is reported in the paper; detecting d = 0.5 on the primary contrast would require ≈ 32 USD for that contrast alone and is only done if extra budget is approved before unblinding. Classical-only cells cost nothing and use 30 seeds.
6. Stopping rules, freeze, negative results
6.1 Stopping rules
- Pilot stops at 5 USD (hard
usd_cap), or if provider failure rate > 10 %, or validity < 80 %; the prompt is then revised once (v2), re-dry-run with the mock, re-piloted on one seed. - Confirmatory: tiers run in the fixed order of
protocol.md§5; no interim efficacy looks; spend reconciled after each tier against the plan; a tier is complete only when every cell has the same seeds (unbalanced seeds are dropped from all cells of the tier). The 40 USD cap is enforced by per-runusd_capand by the running total.
6.2 Frozen after the pilot
Prompt v1 text (prompting.py SHA) and schema; configs/baseline.yaml plus the confirmatory overrides (fundamentals with latent uncertainty, shock size) and their config_hash; cell list and seed lists (pilot seeds 1–3, confirmatory seeds from 101; pilot runs are not pooled into confirmatory estimates); outcome definitions (outcomes.py SHA), thresholds and sensitivity grid; estimators of §4; tier budgets; model snapshots and price table; the git revision. A freeze record is written to results/manifests/freeze.json.
6.3 What counts as a negative result
For a lead outcome with predicted sign: (i) refuting: CI of d excludes d ≥ 0.5 in the predicted direction; (ii) no large effect: CI contains 0 and excludes |d| ≥ MDE; (iii) inconclusive: CI contains both 0 and the MDE — reported as such, never as "no effect". Secondary outcomes cannot rescue a refuted lead.
7. Threats to validity
7.1 Internal (inside the simulator)
| Threat | Consequence | Handling |
|---|---|---|
Provider non-determinism (no seed; temperature unsupported on gpt-5) |
Same seed ≠ same LLM decisions across repeated online runs | Every response cached with request id; replay from cache is exact; ε treated as part of the treatment variance; online-vs-replay flag llm_cached_hits reported |
| Cache replay across cells | Identical prompts (e.g. first decision at t = 50) hit the cache across cells — desirable CRN, but mixes online and replayed decisions | Report cache hit share per cell; cache key includes provider, model, prompt version and full prompt text |
| Invalid-decision handling (→ hold) | Validity differs by model; "hold" is a one-sided failure | ITT estimand; llm_valid_rate reported as outcome; cells with validity < 80 % flagged; no per-protocol rerun |
| Decision interval (20 ticks) vs classical activation (0.2–0.5 per tick) | Realized order share ≪ slot share (mock: 3.5 % of orders, 8 % of volume at φ = 0.25) | Report assigned and realized dose; E7; decision_interval = 10 sensitivity with the mock |
| Exact-revelation fundamentals in the baseline | V^pub ≡ V; private signals redundant; H1 unidentified | Confirmatory fundamentals: noisy dividends (jump_prob = 1, jump_sigma TBD ≈ 4 c → SD(V^pub/V − 1) ≈ 0.5 %) |
| Shock size (+8.4 % at 40 c) exceeds bubble threshold | Lagged adjustment counted as a bubble | Shock ≈ 10 c (+2.1 %) |
| Information-parity leak and activation-stream leak (§1.2) | Estimand drift; precision loss | Gate-4 fixes before the pilot |
Budget halt mid-run (halted_for_budget) |
Remaining decisions become hold | Per-run usd_cap set ≥ 2× expected spend; halted runs excluded and rerun |
| Model drift between pilot and confirmatory | Different behaviour over weeks | Confirmatory calls within a short window; model id and date in manifest |
| Frozen library (nearest-neighbour replay, k = 5) | Not an adaptive agent; distribution shift at N = 1,000 | Labelled provider library; used only for scale and cached-vs-online arms; hit distance diagnostics reported |
7.2 External
Simulated heuristic traders and prompted LLM agents are not institutions: no risk management, no regulation, no learning across runs (memory ≤ 5 own decisions), one asset at a time in the prompt, model time without calendar mapping, fees and latencies stylized. Effects are statements about this market mechanism and these model snapshots; magnitudes do not transfer, signs and dose–response shapes may.