SPB Git

spb/wp27_uqo

Public

This looks like a research repository (LaTeX, bibliography, figures). Preview Research Mode· enable it with research: true on the API.

History
NameLast commitUpdated
configs WP27 v1.0: confirmatory results, mechanisms, robustness,...
data WP27: exchange engine, agents, LLM adapters, designs, theory, literature,...
docs paper: model/implementation/limitations/appendices; docs consistency fixes
paper WP27 v1.0: confirmatory results, mechanisms, robustness,...
results WP27 v1.0: confirmatory results, mechanisms, robustness,...
scripts WP27 v1.0: confirmatory results, mechanisms, robustness,...
src WP27 v1.0: confirmatory results, mechanisms, robustness,...
tests fix: leaf dict overrides replace instead of merge (provider_mix was half mock in...
.env.example WP27: exchange engine, agents, LLM adapters, designs, theory, literature,...
.gitignore WP27: exchange engine, agents, LLM adapters, designs, theory, literature,...
Makefile WP27: exchange engine, agents, LLM adapters, designs, theory, literature,...
pyproject.toml WP27: exchange engine, agents, LLM adapters, designs, theory, literature,...
README.md README: SSRN 7679801
uv.lock WP27: exchange engine, agents, LLM adapters, designs, theory, literature,...
README.md source

# 🤖 The Invisible Hand of Machines

UQO Working Paper No. 27 — Price Discovery in Markets Populated by LLM Agents

Paper Python Data Reproducible Institution Author

TL;DR — An event-sourced limit-order-book exchange with exact accounting and a latent dividend process (fundamental value ≠ public value ≠ price), seven calibrated classical trader types, and an adapter that lets a language model trade from a canonical snapshot through strictly validated JSON. Randomized matched-seed experiments assign 0–100 % of trader slots to gpt-5.4-nano (7 seeds per live cell, 30 per classical cell, pre-registered plan). A stylized correlated-error model predicts that a shared model degrades informativeness and amplifies spirals; the simulator confirms the coordination and diversity predictions and rejects the price predictions: spreads fall from 19 to 6 bp, depth ×7, impact −60 %, pricing error unchanged, no bubble or crash in 112 live runs, and LLM agents dampen leverage-driven fire sales — while latent information is absorbed more slowly and returns become predictable (VR(5) 1.1 → 2.1, ACF₁ 0.02 → 0.32). Mechanism: patient, public-value-anchored liquidity provision by agents that act infrequently.


# 📌 Status

Item Value
Version 1.0 (October 2026)
SSRN 7679801 (submitted 2026-10-28, preliminary upload)
Journal — (candidates: JEDC, Quantitative Finance, Journal of Financial Markets)
Code & data fully reproducible without an API key (make replicate); live cells need OPENAI_API_KEY (≈ 60 USD for the full design); Anthropic cells implemented but not run (key rejected, HTTP 401)
Pre-registration docs/identification.md, docs/protocol.md; every post-plan change in docs/research_log.md

# 🎯 What this paper does

  1. Builds a sandbox exchange (src/market_agents/exchange): continuous double auction, price–time priority, limit/market/cancel, partial fills, fees, per-agent latency, order TTL, price band, circuit breaker, position limit, periodic call auction, leverage/margin/forced liquidation, integer-cent accounting checked after every fill, append-only event log with deterministic replay (24 unit/property/integration tests).
  2. Specifies the economy (assets, signals): dividends every 20 ticks with a latent AR(1) deviation of the next dividend; latent value V = μ/r + x/(1+r−ρ); public value from a Kalman filter on announced dividends and (possibly misleading) news; private signals of known precision; macro, funding, liquidity and placebo shocks.
  3. Populates it (agents): fundamentalists, trend followers, mean-reverters, noise traders, bounded inventory-skew market makers, cross-asset arbitrageurs, leveraged traders — calibrated to stylized facts (ACF₁ ≈ 0, fat tails, volatility clustering, 10–30 bp spreads); 10,000 traders run in 24 s.
  4. Adds LLM agents (llm): OpenAI (Responses API, json_schema), Anthropic (Messages API, json_schema), mock; strict validation (invalid → hold, logged), bounded memory, cohort communication channel, adaptive feedback, prompt personas, USD/token caps, SQLite decision cache, frozen k-NN decision library for zero-cost scaling.
  5. Derives the mechanisms (paper/sections/theory.tex, proofs in the appendix, Monte Carlo checks in scripts/90_theory_checks.py): six propositions on informativeness vs shared-model share, order-flow correlation and synchrony, effective number of models, feedback amplification, leverage spirals, safeguards.
  6. Runs randomized designs (configs/*.yaml, experiments/design.py): common random numbers across matched seeds; LLM slots replace policy slots stratified by type and inherit their information set.

# 🏆 Headline results (confirmatory design, 7 matched seeds per cell)

Outcome 0 % LLM 25 % 95 % Paired effect at 95 %
Quoted spread (bp) 18.6 11.0 6.1 −12.5 [−17.8, −7.3], d = −2.2
Depth, 5 levels (lots) 141 201 956 +816
Price impact (bp / 100 lots) 9.3 5.2 3.6 −5.7, Holm p = 0.01
Mean |P/V − 1| (%) 0.73 0.50 0.54 −0.18 [−0.67, +0.30] (n.s.)
Variance ratio (5 ticks) 1.14 1.55 2.12 +0.97, d = 3.5
Return ACF₁ 0.02 0.15 0.32 +0.30, d = 3.5
Bubbles / crashes / halts 0 0 0 —
LLM decision synchrony / pairwise flow corr. — 0.95 / 0.40 0.90 / 0.34 corr. falls with share
Leverage 19 %: tracking error (0 % vs 25 % LLM) 2.16 % 0.96 % — −1.2, p = 0.03; bubbles 0.7 → 0
Diverse prompts / two models at 25 %: pairwise corr. 0.40 0.32 / 0.30 — absorb shock 4 ticks faster, spread +0.5 / +4.6 bp
Communication channel at 25 %: pairwise corr. 0.40 0.48 — tracking error +0.05 pp, d = 2.3

Classical design (16 cells × 30 seeds): leverage and trend-heavy populations create bubbles and crashes; misleading public news raises pricing error; a position limit harms price discovery; circuit breaker inert at a 2 % shock; two-asset contagion through common funding (asset-1 drawdown 3.6 → 11.6 % with untouched fundamentals).

# 🗂 Repository layout

text
├── CLAUDE.md                 # research protocol (the brief this repo executes)
├── Makefile · pyproject.toml · uv.lock · .env.example
├── configs/                  # baseline.yaml, classical_confirmatory, exploratory (mock), pilot_openai, confirmatory_openai
├── src/market_agents/        # exchange/ assets/ signals/ agents/ llm/ experiments/ metrics/ analysis/ utils/ cli.py
├── tests/                    # unit, property (hypothesis), integration
├── scripts/                  # 10 calibration · 11 scale · 40 figures · 45 recompute · 46 thresholds · 47 mechanisms · 50 tables · 60 library · 90 theory checks · smoke_test
├── docs/                     # research_plan, identification (pre-analysis plan), protocol, assumptions, model_cards, literature_ledger (143 verified refs), novelty_positioning, research_log
├── results/                  # runs/<design>/<cell>/seed*/ (config, manifest, ticks/trades/agents/llm parquet), tables/, figures/, calibration/, library/
└── paper/                    # main.tex, sections/, appendices/, tables/, figures/, references.bib → main.pdf

# 🚀 Quick start

bash
uv sync --extra dev                      # Python 3.12 environment from uv.lock
make test                                # 24 tests
make smoke                               # 100 agents, fixed seed, zero paid calls, accounting verified
make replicate                           # classical designs + theory checks + figures + tables (no API, ~5 min)
cp .env.example .env                     # add OPENAI_API_KEY (and ANTHROPIC_API_KEY) for live cells
make pilot                               # ≈ 4 USD ;  make confirmatory   # ≈ 60 USD, ~3 h with 5 workers
make library                             # frozen-library replay at N = 1,000 (no API)
make paper                               # latexmk → paper/main.pdf

Runtime on an Apple-silicon laptop: one 100-trader run 0.6 s, 1,000 traders 2.6 s, 10,000 traders 24 s; a 25 % LLM run ≈ 7 min (API-latency bound).

# 🗃 Data

All data are synthetic and generated by the simulator; nothing is downloaded. Every run directory holds config.yaml, manifest.json (config hash, git revision, seeds, providers/models, price assumptions, prompt version, realized LLM shares, usage), ticks/trades/agents/llm_decisions.parquet and outcomes.json. Live decisions (80,031 in the confirmatory design, 35,652 in the dose arm) are cached in results/cache/llm_cache.sqlite (not committed; regenerate with the designs). Total API spend for the reported runs: 56 USD (plus ≈ 14 USD on runs discarded after bug fixes, see docs/research_log.md).

# 🔬 Methodology

  1. Common random numbers: one seed → named streams (fundamentals, news, slots, assignment, LLM slots, activation, tie-breaks, shuffle); matched pairs differ only in who decides (utils/rng.py, experiments/population.py).
  2. Outcomes at the run level (metrics/outcomes.py): pricing error, time to absorb a latent shock, response ratio, variance ratios, spreads, depth, Kyle λ, resiliency, bubbles/crashes at pre-registered thresholds, volatility clustering, contagion metrics, LLM validity/synchrony/pairwise correlation, cost.
  3. Inference (analysis/estimators.py): paired t with 95 % CI, Cohen's d_z, Wilcoxon, seed-FE OLS with cluster-robust SE, Holm within families, local projections, threshold sensitivity grid.
  4. Theory ↔ simulator mapping and H1–H6 in paper/sections/theory.tex; pre-analysis plan in docs/identification.md.

# 🖼 Figures & tables inventory

Figure Content
fig_baseline_paths mid vs latent/public value, three classical economies
fig_classical_* classical mechanism cells (tracking error, absorption, drawdown, spread, impact, bubbles)
fig_contagion_*, fig_wealth_depth two-asset comovement/spillover; P&L distribution and depth
fig_dose_conf, fig_paths_conf, fig_realized_share_conf, fig_sync_conf, fig_lp_conf dose–response, price paths, realized dose, coordination, local projections
fig_arms_conf_*, fig_cost_quality_conf 25 % arms; cost–quality frontier

Tables (paper/tables/*.tex, generated by scripts/50_tables.py): stylized facts, scale, classical cells/effects, contagion, dose (pilot and confirmatory), arms cells/effects, LLM reliability, coordination, mechanism association, library, threshold sensitivity; theory checks in results/tables/theory_checks.csv.

# ⚠️ Limitations

  • Seven matched seeds per live cell: effects below d ≈ 1.2 are not detectable; nulls on pricing error, absorption and drawdown are inconclusive for small effects.
  • One provider family (OpenAI nano/mini); Anthropic cells implemented but not run (key rejected on 2026-10-27).
  • LLM agents decide every 20 ticks from a fixed prompt with no tools; realized order share is 5–10× below the assigned slot share.
  • Partial-equilibrium sandbox; stylized model assumes market-order traders, the data point to limit-order liquidity providers.
  • No claim about live markets.

# 📝 Citation

bibtex
@techreport{boucher2026invisiblehand,
  author      = {Boucher, Simon-Pierre},
  title       = {The Invisible Hand of Machines: Price Discovery in Markets Populated by LLM Agents},
  institution = {Universit\'e du Qu\'ebec en Outaouais, D\'epartement des sciences administratives},
  type        = {Working Paper},
  number      = {27},
  year        = {2026},
  month       = {October}
}

# 👤 Author & contact

Simon-Pierre Boucher — Département des sciences administratives, Université du Québec en Outaouais (UQO), Gatineau, QC. 📧 simon-pierre.boucher@uqo.ca

# 🗒 Notes

  • Built on 2026-10-27 from the protocol in CLAUDE.md; numbers 25–26 of the series are unassigned.
  • Deviations from Recherche-UQO/HARMONISATION.md: pyproject.toml + uv.lock instead of requirements.txt; results and cache directories are not committed (regenerable).

Clone wp27_uqo

Anonymous clone and fetch. Pushing requires the owner's personal access token.

ZIP tar.gz snapshot of main
Files · main
Menu

Theme