design runner: resume only on identical config hash; prompt decision interval; docstrings
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
37 changed files +1,505 −301
modified
docs/literature_ledger.md
+3 −0
@@ -210,3 +210,6 @@ Claim-to-citation ledger. Every row was verified on 2026-10-27 against Crossref | ||
| 210 | 210 | | Thurner, Farmer & Geanakoplos — guessed DOI 10.1080/14697688.2011.586336 | Crossref | not found; corrected to 10.1080/14697688.2012.674301 (now `thurner2012`) | |
| 211 | 211 | | Pasquariello — guessed DOI 10.1093/rfs/hhl046 | Crossref | not found; corrected to 10.1093/rfs/hhl010 (now `pasquariello2007`) | |
| 212 | 212 | | Chiarella–Iori–Perelló, Back–Cao–Willard, Foster–Viswanathan, Ladley, Hasbrouck 1991, Kondor 2012 — first-guess DOIs | Crossref | each first guess resolved to a *different* paper; all corrected by Crossref bibliographic query and re-verified (DOIs in table above) | |
| 213 | + | |
| 214 | + | |
| 215 | +| 144 | feedback-delong | Positive-feedback traders can destabilize prices and rational speculators may amplify rather than offset them | delong1990 | 10.1111/j.1540-6261.1990.tb03695.x | 1990 | Crossref (title/authors/venue matched) | 2026-10-27 | | |
modified
docs/research_log.md
+1 −0
@@ -17,6 +17,7 @@ between the pre-analysis plan and the confirmatory runs, so that nothing is revi | ||
| 17 | 17 | | 10 | 21:10 | 1→2 | LLM slots inherit the replaced slot's information set (`llm.informed_share: null`); within-tick shuffle moved to its own RNG stream | methodologist audit: treatment bundled information; activations diverged across arms | matched-pair CRN now exact for every non-replaced agent | |
| 18 | 18 | | 11 | 21:12 | — | First git commit; pilot and confirmatory designs launched on the frozen revision | manifests now carry `git_revision` | **freeze** | |
| 19 | 19 | | 12 | — | 5 | Pilot (3 seeds × {5, 25, 50 %}, gpt-5.4-nano) runs concurrently with the confirmatory design on the same revision; pilot seeds (1–3) are never pooled with confirmatory seeds (201–207) | time budget | the pilot's role is reduced to cost/validity/variance reporting; the seed count (7) was fixed by the USD cap, not by the pilot variance | |
| 20 | +| 13 | 22:05 | 6 | **Override bug**: `deep_update` merged leaf dictionaries, so `provider_mix: {openai: 1.0}` on the baseline `{mock: 1.0}` became a 50/50 mock/OpenAI mixture in every live cell (caught by a co-author reading run manifests). Leaf dicts now replace; regression test added; all live and classical designs discarded and relaunched on commit c1fdf6c | half of the "LLM" slots were the mock rule | **new freeze** (c1fdf6c); nothing from the discarded runs is used | | |
| 20 | 21 | |
| 21 | 22 | Known deviations from `docs/identification.md` / `docs/protocol.md` (written before items 8–12): |
| 22 | 23 | - Noisy dividends (`jump_prob = 1`) were proposed to make V^pub ≠ V; item 8 achieves this through the timing of information instead (no jumps in the baseline). |
modified
paper/appendices/hyperparameters.tex
+243 −1
@@ -1 +1,243 @@ | ||
| 1 | −% hyperparameters — to be written | |
| 1 | +\section{Full hyperparameters} | |
| 2 | +\label{app:hyperparameters} | |
| 3 | + | |
| 4 | +Table~\ref{tab:hp_baseline} lists every parameter of \code{configs/baseline.yaml} together with the defaults of the configuration schema (\code{experiments/config.py}) that the file does not set explicitly. Tables~\ref{tab:hp_classical}--\ref{tab:hp_pilot} list the overrides of every cell of the four design files; a cell inherits everything else from the baseline. Table~\ref{tab:hp_models} summarises the model cards (\code{docs/model\_cards.md}). | |
| 5 | + | |
| 6 | +\begin{small} | |
| 7 | +\begin{longtable}{@{}p{5.0cm} p{3.4cm} p{6.6cm}@{}} | |
| 8 | +\caption{Baseline configuration (\code{configs/baseline.yaml}; schema defaults in italics)} | |
| 9 | +\label{tab:hp_baseline}\\ | |
| 10 | +\toprule | |
| 11 | +Parameter & Value & Meaning \\ | |
| 12 | +\midrule | |
| 13 | +\endfirsthead | |
| 14 | +\toprule | |
| 15 | +Parameter & Value & Meaning \\ | |
| 16 | +\midrule | |
| 17 | +\endhead | |
| 18 | +\bottomrule | |
| 19 | +\endfoot | |
| 20 | +\multicolumn{3}{@{}l}{\emph{Run}}\\ | |
| 21 | +\code{name} & baseline & run label (design/cell in designs) \\ | |
| 22 | +\code{seed} & 1 & master seed of all streams \\ | |
| 23 | +\code{n\_ticks} & 600 & ticks per run \\ | |
| 24 | +\code{warmup} & 100 & ticks excluded from outcomes; LLM decisions start at \code{warmup}/2 \\ | |
| 25 | +\multicolumn{3}{@{}l}{\emph{assets}}\\ | |
| 26 | +\code{n\_assets} & 1 & number of risky assets \\ | |
| 27 | +\code{tick\_cents} & 1 & tick size in cents \\ | |
| 28 | +\code{initial\_price\_cents} & 20000 & reference price $=\mu/r$ (200.00) \\ | |
| 29 | +\code{price\_band\_pct} & 0.5 & limit prices farther than $\pm50\%$ from the midpoint are rejected \\ | |
| 30 | +\code{max\_order\_qty} & 10000 & maximum lots per order at the exchange \\ | |
| 31 | +\multicolumn{3}{@{}l}{\emph{fundamentals}}\\ | |
| 32 | +\code{div\_interval} & 20 & ticks between dividends ($\Delta$) \\ | |
| 33 | +\code{r\_period} & 0.0025 & discount rate per dividend period ($r$) \\ | |
| 34 | +\code{mu} & [50.0] & mean dividend in cents, regime 0, per asset ($\mu$) \\ | |
| 35 | +\code{mu\_shift} & 0.0 & regime-1 mean $=\mu(1+\text{mu\_shift})$; 0 disables regimes \\ | |
| 36 | +\code{p\_switch} & 0.0 & per-period regime switch probability \\ | |
| 37 | +\code{rho} & 0.98 & persistence of the deviation ($\rho$) \\ | |
| 38 | +\code{sigma} & 2.0 & innovation standard deviation in cents ($\sigma$) \\ | |
| 39 | +\code{jump\_prob} & 0.0 & per-period jump probability (dividend noise) \\ | |
| 40 | +\code{jump\_sigma} & 0.0 & jump standard deviation in cents \\ | |
| 41 | +\code{factor\_loading} & [0.0] & loading $b_k$ on the common factor \\ | |
| 42 | +\code{x0} & 0.0 & initial revealed deviation \\ | |
| 43 | +\multicolumn{3}{@{}l}{\emph{news}}\\ | |
| 44 | +\code{rate} & 0.02 & per-tick probability of a public item per asset \\ | |
| 45 | +\code{noise\_sd} & 1.0 & stated noise of credible items (cents) \\ | |
| 46 | +\code{misleading\_share} & 0.0 & share of biased items \\ | |
| 47 | +\code{misleading\_bias} & 10.0 & bias of a misleading item (cents, random sign) \\ | |
| 48 | +\code{window} & 20 & ticks of news visible in the observation \\ | |
| 49 | +\multicolumn{3}{@{}l}{\emph{exchange}}\\ | |
| 50 | +\code{maker\_fee\_bps} & 0.0 & maker fee (negative = rebate) \\ | |
| 51 | +\code{taker\_fee\_bps} & 2.0 & taker fee in basis points of notional \\ | |
| 52 | +\code{min\_latency} & 0.05 & floor on per-agent latency (ticks) \\ | |
| 53 | +\code{halt\_threshold} & null & circuit breaker off (treatment: 0.05) \\ | |
| 54 | +\code{halt\_window} & 10 & ticks over which the move is measured \\ | |
| 55 | +\code{halt\_duration} & 20 & halt length in ticks \\ | |
| 56 | +\code{halt\_cancel\_resting} & true & cancel resting orders at a halt \\ | |
| 57 | +\code{allow\_short} & false & short selling switch (inert: every account has \code{short\_allowed=false}) \\ | |
| 58 | +\code{max\_short\_lots} & 0 & short limit when shorting is allowed \\ | |
| 59 | +\code{margin\_check} & true & end-of-tick margin and bankruptcy check \\ | |
| 60 | +\code{liquidation\_fraction} & 0.5 & share of each position sold on a margin call \\ | |
| 61 | +\code{order\_ttl} & 50 & resting orders expire after this many ticks \\ | |
| 62 | +\code{position\_limit} & \emph{null} & max $|$position$|$ in lots (treatment: 300) \\ | |
| 63 | +\code{call\_auction\_interval} & \emph{null} & batch clearing every $k$ ticks (treatment: 5); collar 0.02 fixed in \code{ExchangeConfig} \\ | |
| 64 | +\multicolumn{3}{@{}l}{\emph{population}}\\ | |
| 65 | +\code{n\_traders} & 100 & trader slots ($N$) \\ | |
| 66 | +\code{mix} & fund.\ 0.25, trend 0.06, meanrev 0.10, noise 0.50, MM 0.05, statarb 0.0, leveraged 0.04 & shares of slots by policy (calibrated 2026-10-27) \\ | |
| 67 | +\code{activity} & fund.\ 0.20, trend 0.20, meanrev 0.20, noise 0.30, MM 0.5, statarb 0.2, leveraged 0.2 & per-tick activation probability \\ | |
| 68 | +\code{policy\_kwargs} & fundamentalist \{aggressiveness: 20.0\}; noise \{p\_market: 0.6\}; market\_maker \{quote\_qty: 20\} & calibrated policy parameters (others at code defaults, Table~\ref{tab:policies}) \\ | |
| 69 | +\code{llm\_share} & 0.0 & fraction of slots controlled by the LLM policy ($\varphi$) \\ | |
| 70 | +\code{llm\_replace} & proportional & stratified replacement across types (or one policy name) \\ | |
| 71 | +\code{llm\_keep\_market\_makers} & true & market makers are never replaced \\ | |
| 72 | +\code{informed\_share} & 0.3 & share of fundamentalists receiving private signals \\ | |
| 73 | +\code{precision} & 2.0 & signal precision $\pi$ of informed fundamentalists \\ | |
| 74 | +\code{leverage\_cap} & 3.0 & gross exposure / equity cap of leveraged slots ($L$) \\ | |
| 75 | +\code{maintenance\_margin} & 0.25 & margin-call threshold on equity / gross ($m$) \\ | |
| 76 | +\code{endowment.cash\_cents} & 2000000 & base cash (20,000.00) \\ | |
| 77 | +\code{endowment.shares} & 100 & base lots per asset \\ | |
| 78 | +\code{endowment.wealth\_dispersion} & 0.3 & lognormal sd of the wealth multiplier \\ | |
| 79 | +\code{latency.min}, \code{latency.max} & 0.05, 1.0 & uniform latency range (ticks) \\ | |
| 80 | +\code{latency.llm\_latency} & null & matched-latency override for LLM slots \\ | |
| 81 | +\multicolumn{3}{@{}l}{\emph{population.llm}}\\ | |
| 82 | +\code{provider\_mix} & \{mock: 1.0\} & provider $\to$ share of LLM slots \\ | |
| 83 | +\code{models} & mock: mock-v1; openai: gpt-5.4-nano; anthropic: claude-haiku-5-5 & model id per provider alias \\ | |
| 84 | +\code{prompt\_set} & single & single (generalist) or diverse (8 personas) \\ | |
| 85 | +\code{prompt\_version} & v1 & frozen prompt version \\ | |
| 86 | +\code{memory} & 0 & past own decisions shown (treatment: 5) \\ | |
| 87 | +\code{channel} & false & cohort communication channel \\ | |
| 88 | +\code{cohort\_size} & 5 & cohort size and channel length \\ | |
| 89 | +\code{decision\_interval} & 20 & ticks between LLM decisions \\ | |
| 90 | +\code{stagger}, \code{stagger\_slots} & true, 4 & offsets $\{0,5,10,15\}$ \\ | |
| 91 | +\code{informed\_share} & null & inherit the replaced slot's information (a number overrides) \\ | |
| 92 | +\code{precision} & 2.0 & precision used when \code{informed\_share} is a number \\ | |
| 93 | +\code{max\_order\_qty} & 50 & cap on LLM order size (lots) \\ | |
| 94 | +\code{reasoning\_effort} & low & OpenAI \code{reasoning.effort} / Anthropic \code{effort} \\ | |
| 95 | +\code{max\_output\_tokens} & 1200 & output cap per call (includes reasoning tokens) \\ | |
| 96 | +\code{concurrency} & 8 & parallel requests per decision batch \\ | |
| 97 | +\code{usd\_cap} & 5.0 & hard USD cap per run \\ | |
| 98 | +\code{cache} & true & reuse identical (provider, model, prompt) decisions \\ | |
| 99 | +\code{temperature} & \emph{null} & never sent to gpt-5 / o-series models \\ | |
| 100 | +\code{timeout\_s}, \code{max\_retries} & \emph{60.0, 3} & client timeout and retries \\ | |
| 101 | +\code{token\_cap}, \code{decision\_cap} & \emph{null, null} & optional total-token and per-agent decision caps \\ | |
| 102 | +\code{frozen\_library} & \emph{null} & path of the recorded decision library (provider \code{library}) \\ | |
| 103 | +\code{adaptive\_prompt} & \emph{false} & P\&L feedback line in the prompt \\ | |
| 104 | +\code{price\_assumptions} & \emph{Table~\ref{tab:hp_models}} & USD per 1M tokens by model id \\ | |
| 105 | +\multicolumn{3}{@{}l}{\emph{shocks}}\\ | |
| 106 | +shock 1 & \{time: 300, kind: fundamental\_jump, size: 10.0\} & $+10$ cents on the latent deviation ($+2.2\%$ of $V$) \\ | |
| 107 | +shock 2 & \{time: 450, kind: placebo\} & logged, no effect \\ | |
| 108 | +\multicolumn{3}{@{}l}{\emph{output}}\\ | |
| 109 | +\code{events} & false & write the full event log \\ | |
| 110 | +\code{trades}, \code{llm\_log} & true, true & write trade and LLM-decision frames \\ | |
| 111 | +\code{lookback} & 60 & ticks of midpoint history in the observation \\ | |
| 112 | +\end{longtable} | |
| 113 | +\end{small} | |
| 114 | + | |
| 115 | +\begin{small} | |
| 116 | +\begin{longtable}{@{}p{4.3cm} p{10.7cm}@{}} | |
| 117 | +\caption{Cells of \code{configs/classical\_confirmatory.yaml} (seeds 101--130; design overrides: \code{provider\_mix} \{mock: 1.0\}, \code{usd\_cap} 0.0)} | |
| 118 | +\label{tab:hp_classical}\\ | |
| 119 | +\toprule | |
| 120 | +Cell & Overrides (dotted paths relative to the baseline) \\ | |
| 121 | +\midrule | |
| 122 | +\endfirsthead | |
| 123 | +\toprule | |
| 124 | +Cell & Overrides \\ | |
| 125 | +\midrule | |
| 126 | +\endhead | |
| 127 | +\bottomrule | |
| 128 | +\endfoot | |
| 129 | +control & none \\ | |
| 130 | +placebo\_shock & shocks: placebo at 300 and at 450 (no jump) \\ | |
| 131 | +trend\_heavy & population.mix: trend 0.16, noise 0.40 (others as baseline) \\ | |
| 132 | +leverage\_heavy & population.mix: leveraged 0.19, noise 0.35 \\ | |
| 133 | +leverage\_heavy\_funding\_shock & population.mix as leverage\_heavy; shocks: fundamental\_jump $-10.0$ at 300, funding 0.5 at 320 \\ | |
| 134 | +liquidity\_shock & shocks: fundamental\_jump 10.0 at 300, liquidity at 300, liquidity\_end at 360 \\ | |
| 135 | +info\_low & population.precision 0.5 \\ | |
| 136 | +info\_high & population.precision 8.0 \\ | |
| 137 | +misleading\_news & news.misleading\_share 0.5 \\ | |
| 138 | +breaker\_5 & exchange.halt\_threshold 0.05 \\ | |
| 139 | +poslimit\_300 & exchange.position\_limit 300 \\ | |
| 140 | +auction\_5 & exchange.call\_auction\_interval 5 \\ | |
| 141 | +mock\_llm\_25\_indep & population.llm\_share 0.25 (mock provider) \\ | |
| 142 | +mock\_llm\_50\_indep & population.llm\_share 0.50 (mock provider) \\ | |
| 143 | +k2\_control & assets.n\_assets 2; fundamentals.mu [50, 50]; factor\_loading [0.7, 0.7]; population.mix: noise 0.45, statarb 0.05 \\ | |
| 144 | +k2\_leverage & as k2\_control with leveraged 0.19, noise 0.30; shocks: fundamental\_jump $-10.0$ on asset 0 at 300, funding 0.5 at 320 \\ | |
| 145 | +\end{longtable} | |
| 146 | +\end{small} | |
| 147 | + | |
| 148 | +\begin{small} | |
| 149 | +\begin{longtable}{@{}p{4.3cm} p{10.7cm}@{}} | |
| 150 | +\caption{Cells of \code{configs/confirmatory\_openai.yaml} (seeds 201--207; design overrides: \code{provider\_mix} \{openai: 1.0\}, \code{models} \{openai: gpt-5.4-nano, openai\_mini: gpt-5.4-mini, mock: mock-v1, anthropic: claude-haiku-5-5\}, \code{usd\_cap} 2.0, \code{concurrency} 8)} | |
| 151 | +\label{tab:hp_confirmatory}\\ | |
| 152 | +\toprule | |
| 153 | +Cell & Overrides \\ | |
| 154 | +\midrule | |
| 155 | +\endfirsthead | |
| 156 | +\toprule | |
| 157 | +Cell & Overrides \\ | |
| 158 | +\midrule | |
| 159 | +\endhead | |
| 160 | +\bottomrule | |
| 161 | +\endfoot | |
| 162 | +dose\_000 / 005 / 010 / 025 / 050 / 100 & population.llm\_share 0.00 / 0.05 / 0.10 / 0.25 / 0.50 / 1.00 (single generalist prompt, gpt-5.4-nano) \\ | |
| 163 | +div\_prompts\_025 & llm\_share 0.25; llm.prompt\_set diverse \\ | |
| 164 | +div\_models\_025 & llm\_share 0.25; llm.prompt\_set diverse; llm.provider\_mix \{openai: 0.5, openai\_mini: 0.5\} \\ | |
| 165 | +memory\_025 & llm\_share 0.25; llm.memory 5 \\ | |
| 166 | +channel\_025 & llm\_share 0.25; llm.channel true \\ | |
| 167 | +adaptive\_025 & llm\_share 0.25; llm.adaptive\_prompt true \\ | |
| 168 | +leverage\_025 & llm\_share 0.25; population.mix: leveraged 0.19, noise 0.35 \\ | |
| 169 | +breaker\_025 & llm\_share 0.25; exchange.halt\_threshold 0.05 \\ | |
| 170 | +poslimit\_025 & llm\_share 0.25; exchange.position\_limit 300 \\ | |
| 171 | +fair\_mini\_005 & llm\_share 0.05; llm.provider\_mix \{openai\_mini: 1.0\} (gpt-5.4-mini; equal-USD comparison with dose\_025) \\ | |
| 172 | +fair\_latency\_025 & llm\_share 0.25; population.latency.llm\_latency 0.05 \\ | |
| 173 | +\end{longtable} | |
| 174 | +\end{small} | |
| 175 | + | |
| 176 | +\begin{small} | |
| 177 | +\begin{longtable}{@{}p{4.3cm} p{10.7cm}@{}} | |
| 178 | +\caption{Cells of \code{configs/exploratory.yaml} (mock dry run, seeds 1--3; design overrides: \code{provider\_mix} \{mock: 1.0\}, \code{usd\_cap} 0.0)} | |
| 179 | +\label{tab:hp_exploratory}\\ | |
| 180 | +\toprule | |
| 181 | +Cell & Overrides \\ | |
| 182 | +\midrule | |
| 183 | +\endfirsthead | |
| 184 | +\toprule | |
| 185 | +Cell & Overrides \\ | |
| 186 | +\midrule | |
| 187 | +\endhead | |
| 188 | +\bottomrule | |
| 189 | +\endfoot | |
| 190 | +share\_0 / 5 / 25 / 50 & population.llm\_share 0.0 / 0.05 / 0.25 / 0.50 \\ | |
| 191 | +diverse\_25 & llm\_share 0.25; llm.prompt\_set diverse \\ | |
| 192 | +memory\_25 & llm\_share 0.25; llm.memory 5 \\ | |
| 193 | +channel\_25 & llm\_share 0.25; llm.channel true \\ | |
| 194 | +leverage\_25 & llm\_share 0.25; population.mix: leveraged 0.19, noise 0.35 \\ | |
| 195 | +breaker\_25 & llm\_share 0.25; exchange.halt\_threshold 0.05 \\ | |
| 196 | +poslimit\_25 & llm\_share 0.25; exchange.position\_limit 300 \\ | |
| 197 | +auction\_25 & llm\_share 0.25; exchange.call\_auction\_interval 5 \\ | |
| 198 | +k2\_25 & llm\_share 0.25; assets.n\_assets 2; fundamentals.mu [50, 50]; factor\_loading [0.7, 0.7]; population.mix: noise 0.45, statarb 0.05 \\ | |
| 199 | +\end{longtable} | |
| 200 | +\end{small} | |
| 201 | + | |
| 202 | +\begin{small} | |
| 203 | +\begin{longtable}{@{}p{4.3cm} p{10.7cm}@{}} | |
| 204 | +\caption{Cells of \code{configs/pilot\_openai.yaml} (seeds 1--3; design overrides: \code{provider\_mix} \{openai: 1.0\}, \code{models} \{openai: gpt-5.4-nano, mock: mock-v1, anthropic: claude-haiku-5-5\}, \code{usd\_cap} 1.5, \code{concurrency} 8)} | |
| 205 | +\label{tab:hp_pilot}\\ | |
| 206 | +\toprule | |
| 207 | +Cell & Overrides \\ | |
| 208 | +\midrule | |
| 209 | +\endfirsthead | |
| 210 | +\toprule | |
| 211 | +Cell & Overrides \\ | |
| 212 | +\midrule | |
| 213 | +\endhead | |
| 214 | +\bottomrule | |
| 215 | +\endfoot | |
| 216 | +share\_5 / 25 / 50 & population.llm\_share 0.05 / 0.25 / 0.50 (gpt-5.4-nano, single prompt) \\ | |
| 217 | +\end{longtable} | |
| 218 | +\end{small} | |
| 219 | + | |
| 220 | +\begin{table}[H] | |
| 221 | +\centering | |
| 222 | +\caption{Model cards summary (prices in USD per 1M tokens: input / cached input / output; source: provider pricing pages fetched 2026-10-27 for OpenAI, Anthropic's published table; identical to \code{LLMCfg.price\_assumptions})} | |
| 223 | +\label{tab:hp_models} | |
| 224 | +\footnotesize | |
| 225 | +\begin{tabular}{@{}p{2.7cm} p{2.6cm} p{2.3cm} p{1.6cm} p{5.6cm}@{}} | |
| 226 | +\toprule | |
| 227 | +Model id & Price & Reasoning effort & Output cap & Status / role \\ | |
| 228 | +\midrule | |
| 229 | +gpt-5.4-nano & 0.20 / 0.02 / 1.25 & low & 1,200 & live; primary model (all dose, mechanism, safeguard and fairness cells; library source) \\ | |
| 230 | +gpt-5.4-mini & 0.75 / 0.075 / 4.50 & low & 1,200 & live-capable; two-model mix and equal-USD cells \\ | |
| 231 | +gpt-5-nano & 0.05 / 0.005 / 0.40 & low & 1,200 & priced; optional replication, not planned \\ | |
| 232 | +gpt-5-mini & 0.25 / 0.025 / 2.00 & low & 1,200 & priced only \\ | |
| 233 | +gpt-4.1-nano & 0.10 / 0.025 / 0.40 & n/a & 1,200 & priced only \\ | |
| 234 | +gpt-4.1-mini & 0.40 / 0.10 / 1.60 & n/a & 1,200 & priced only \\ | |
| 235 | +claude-haiku-5-5 & 0.10 / 0.01 / 0.50 & low (\code{effort}) & 1,200 & blocked (Anthropic key rejected, HTTP 401); mock dry run only \\ | |
| 236 | +claude-sonnet-5-5 & 2.00 / 0.20 / 10.00 & low & 1,200 & blocked; mock dry run only \\ | |
| 237 | +mock-v1 & 0 / 0 / 0 & n/a & n/a & mock (seeded value rule, 5\% invalid); not evidence about LLMs \\ | |
| 238 | +library (knn-replay) & 0 & n/a & n/a & frozen nearest-neighbour replay of recorded decisions; non-adaptive; reported separately \\ | |
| 239 | +\bottomrule | |
| 240 | +\end{tabular} | |
| 241 | +\end{table} | |
| 242 | + | |
| 243 | +\noindent The Anthropic adapter sends \code{max\_tokens} $=\max(\texttt{max\_output\_tokens},256)$ and \code{output\_config.effort}. Reasoning-token accounting exists only in the OpenAI adapter; the Anthropic adapter reports input, cached-input and output tokens. An unknown model id is priced at the most expensive known output price. Every manifest stores the price table in force when the run was made. | |
modified
paper/appendices/prompts.tex
+115 −1
@@ -1 +1,115 @@ | ||
| 1 | −% prompts — to be written | |
| 1 | +\section{Prompts} | |
| 2 | +\label{app:prompts} | |
| 3 | + | |
| 4 | +This appendix reproduces verbatim the frozen prompt version \code{v1} of \code{llm/prompting.py}, one rendered example, the JSON schema sent to the providers, and the validation rules of \code{llm/policy.py}. The system prompt is the persona sentence followed by the rules block; the user message is a deterministic rendering of the agent's \code{Observation}. Nothing in either text depends on the latent state. | |
| 5 | + | |
| 6 | +\lstset{literate={≈}{{$\approx$ }}2} | |
| 7 | + | |
| 8 | +\subsection*{Personas (\code{ROLE\_PERSONAS})} | |
| 9 | +The single-prompt design uses \code{generalist} for every LLM slot; the diverse-prompt design assigns the eight personas cyclically in slot order. | |
| 10 | + | |
| 11 | +\begin{lstlisting} | |
| 12 | +generalist : You are a professional trader managing your own account in a single-stock electronic market. | |
| 13 | +value : You are a value investor: you compare the price to the present value of expected dividends and trade mispricings patiently. | |
| 14 | +momentum : You are a momentum trader: you follow recent price trends and cut positions when the trend reverses. | |
| 15 | +contrarian : You are a contrarian trader: you fade sharp moves that are not justified by dividend news. | |
| 16 | +liquidity : You are a liquidity provider: you prefer resting limit orders near the best quotes and keep your inventory small. | |
| 17 | +risk_averse: You are a risk-averse trader: you keep positions small, avoid market orders and prefer to hold when uncertain. | |
| 18 | +macro : You are a news-driven trader: you weigh public announcements and dividend surprises and act when the market has not yet reacted. | |
| 19 | +quant : You are a quantitative trader: you reason about expected value per share, spread costs and inventory before each order. | |
| 20 | +\end{lstlisting} | |
| 21 | + | |
| 22 | +\subsection*{Rules template (\code{RULES})} | |
| 23 | +Placeholders in braces are filled from the configuration and the observation (\code{div\_interval}, \code{mu}, \code{r\_pct}, \code{pv\_mean}, \code{rho}, \code{tick}, \code{taker\_bps}, \code{ttl}, \code{max\_qty}, \code{interval}); doubled braces are Python format escapes and render as single braces. | |
| 24 | + | |
| 25 | +\begin{lstlisting} | |
| 26 | +Market mechanics (read carefully): | |
| 27 | +- One risky stock paying a cash dividend every {div_interval} ticks; the long-run mean dividend is {mu:.2f} per share per period and the per-period discount rate is {r_pct:.2f}%, so a share paying the mean dividend forever is worth about {pv_mean:.2f}. | |
| 28 | +- Dividends deviate persistently from the mean (autocorrelation {rho:.2f} per period). The deviation of the NEXT dividend is not yet announced; public news and, when you have one, your private signal are noisy estimates of it (in cents). The present value of a share rises by about 1/(1+r-rho) ≈ 44 cents per cent of next-dividend deviation. | |
| 29 | +- Prices are in currency units with a tick of {tick:.2f}. Limit orders rest in the book; market orders execute immediately against the best quotes (taker fee {taker_bps:.1f} bps). Orders may be partially filled. Unfilled limit orders expire after {ttl} ticks. | |
| 30 | +- You cannot short sell and cannot spend more than your free cash. Maximum order size: {max_qty} shares. | |
| 31 | +- You decide every {interval} ticks. Between decisions the market keeps trading. | |
| 32 | + | |
| 33 | +Respond with ONLY a JSON object with exactly these keys: | |
| 34 | +{{"action": "hold"|"place"|"cancel", "side": "buy"|"sell"|null, "order_type": "limit"|"market"|null, "quantity": integer|null, "limit_price": number|null, "cancel_order_ids": [integers], "confidence": number in [0,1]|null, "rationale": "one sentence, max 200 characters"}} | |
| 35 | +"place" requires side, order_type and quantity (and limit_price for a limit order). "cancel" requires cancel_order_ids. Anything else is treated as hold. | |
| 36 | +\end{lstlisting} | |
| 37 | + | |
| 38 | +\noindent The placeholder \code{interval} is filled with the dividend interval (\code{obs.div\_interval}), not with \code{decision\_interval}; the two coincide (20) in every reported design. | |
| 39 | + | |
| 40 | +\subsection*{Rendered example} | |
| 41 | +The following system prompt and user message were produced by running the prompt builder on the observation of an informed LLM slot (agent~9, persona \code{generalist}, tick~110) in a mock run of the baseline configuration with $\varphi=0.25$, seed~1.\footnote{Generated with \code{system\_prompt()} and \code{user\_message()} of \code{llm/prompting.py}; the observation was captured from \code{Simulation.\_observe}. The mid-price history is sub-sampled to every sixth tick of the 61-tick window.} | |
| 42 | + | |
| 43 | +\begin{lstlisting} | |
| 44 | +You are a professional trader managing your own account in a single-stock electronic market. | |
| 45 | + | |
| 46 | +Market mechanics (read carefully): | |
| 47 | +- One risky stock paying a cash dividend every 20 ticks; the long-run mean dividend is 0.50 per share per period and the per-period discount rate is 0.25%, so a share paying the mean dividend forever is worth about 200.00. | |
| 48 | +- Dividends deviate persistently from the mean (autocorrelation 0.98 per period). The deviation of the NEXT dividend is not yet announced; public news and, when you have one, your private signal are noisy estimates of it (in cents). The present value of a share rises by about 1/(1+r-rho) ≈ 44 cents per cent of next-dividend deviation. | |
| 49 | +- Prices are in currency units with a tick of 0.01. Limit orders rest in the book; market orders execute immediately against the best quotes (taker fee 2.0 bps). Orders may be partially filled. Unfilled limit orders expire after 50 ticks. | |
| 50 | +- You cannot short sell and cannot spend more than your free cash. Maximum order size: 50 shares. | |
| 51 | +- You decide every 20 ticks. Between decisions the market keeps trading. | |
| 52 | + | |
| 53 | +Respond with ONLY a JSON object with exactly these keys: | |
| 54 | +{"action": "hold"|"place"|"cancel", "side": "buy"|"sell"|null, "order_type": "limit"|"market"|null, "quantity": integer|null, "limit_price": number|null, "cancel_order_ids": [integers], "confidence": number in [0,1]|null, "rationale": "one sentence, max 200 characters"} | |
| 55 | +"place" requires side, order_type and quantity (and limit_price for a limit order). "cancel" requires cancel_order_ids. Anything else is treated as hold. | |
| 56 | +\end{lstlisting} | |
| 57 | + | |
| 58 | +\begin{lstlisting} | |
| 59 | +Time: tick 110. Next dividend in 10 ticks. | |
| 60 | +Best bid 200.80 / best ask 201.12 / mid 200.96 / last trade 201.12. | |
| 61 | +Bid depth (price x shares): 200.80x14, 200.70x28, 200.69x13, 200.68x14, 200.65x12. Ask depth: 201.12x14, 201.15x4, 201.19x8, 201.20x14, 201.23x4. | |
| 62 | +Mid-price history (oldest to newest, 61 ticks sampled): 200.20, 200.29, 200.48, 200.09, 200.54, 200.66, 200.64, 200.64, 200.60, 200.73, 200.96. | |
| 63 | +Change over the window: +0.38%. Recent volatility (std of tick returns): 0.042%. | |
| 64 | +Last 30 trades: volume 168 shares, 57% buyer-initiated. | |
| 65 | +Last dividend paid: 0.55 per share. Public estimate of the next dividend's deviation from its mean: public estimate +5.0 cents. Present value of expected dividends using only public information: 202.23. | |
| 66 | +Your private signal: next dividend deviation estimated at +3.6 cents (signal precision 2.0; higher is more reliable). | |
| 67 | +Your account: cash 18634.09 (free 18634.09), holdings 92 shares, equity 37122.41. | |
| 68 | +You have no open orders. | |
| 69 | +Decide now. Reply with the JSON object only. | |
| 70 | +\end{lstlisting} | |
| 71 | + | |
| 72 | +\noindent\sloppy Optional lines, in the order in which they are inserted when the corresponding treatment is active: \code{TRADING IS HALTED on this stock (orders will be rejected).} after the first line; up to three \code{Public news (tick $t$): \dots} lines after the public-value line; \code{Your open orders: id \dots: buy/sell remaining/qty @ price (age)} (at most six) instead of \code{You have no open orders.}; \code{Feedback: your equity changed by $\pm x$\% since your last decision (cumulative $\pm y$\%).} (adaptive feedback); \code{Your recent decisions and outcomes:} followed by up to five bullet lines (memory); \code{Messages from other traders in your group (unverified opinions):} followed by up to five bullet lines (communication channel). | |
| 73 | + | |
| 74 | +\subsection*{JSON schema (\code{DECISION\_JSON\_SCHEMA})} | |
| 75 | +Sent as \code{text.format} (OpenAI, \code{strict: true}) and \code{output\_config.format} (Anthropic). Every key is required; \code{null} is allowed where indicated; no additional properties. | |
| 76 | + | |
| 77 | +\begin{lstlisting} | |
| 78 | +{ | |
| 79 | + "type": "object", | |
| 80 | + "properties": { | |
| 81 | + "action": {"type": "string", "enum": ["hold", "place", "cancel"]}, | |
| 82 | + "side": {"type": ["string", "null"], "enum": ["buy", "sell", null]}, | |
| 83 | + "order_type": {"type": ["string", "null"], "enum": ["limit", "market", null]}, | |
| 84 | + "quantity": {"type": ["integer", "null"]}, | |
| 85 | + "limit_price": {"type": ["number", "null"]}, | |
| 86 | + "cancel_order_ids": {"type": "array", "items": {"type": "integer"}}, | |
| 87 | + "confidence": {"type": ["number", "null"]}, | |
| 88 | + "rationale": {"type": "string"} | |
| 89 | + }, | |
| 90 | + "required": ["action", "side", "order_type", "quantity", "limit_price", | |
| 91 | + "cancel_order_ids", "confidence", "rationale"], | |
| 92 | + "additionalProperties": false | |
| 93 | +} | |
| 94 | +\end{lstlisting} | |
| 95 | + | |
| 96 | +\subsection*{Parsing and validation rules} | |
| 97 | +The reply is first parsed, then validated against the pydantic model \code{Decision}, then checked for feasibility by \code{LLMPolicy.to\_action}. Any failure yields a hold action with \code{valid = False} and the reason string below; a provider error or a budget halt yields a hold with the error string as reason. | |
| 98 | + | |
| 99 | +\begin{enumerate}\sloppy | |
| 100 | +\item \textbf{Parsing} (\code{parse\_decision}): whitespace and a surrounding \code{```}/\code{```json} fence are stripped; if the text is not valid JSON, the first \code{\{\dots\}} block is tried; failure $\to$ reason \code{json: <message>}. | |
| 101 | +\item \textbf{Schema} (\code{Decision}): \code{action} $\in$ \{hold, place, cancel\}; \code{side} $\in$ \{buy, sell, null\}; \code{order\_type} $\in$ \{limit, market, null\}; \code{quantity} integer $\ge 0$ or null; \code{limit\_price} number $>0$ or null (currency units); \code{cancel\_order\_ids} list of integers; \code{confidence} in $[0,1]$ or null; \code{rationale} trimmed to 400 characters; failure $\to$ reason \code{schema: <field>: <message>} (first three errors). | |
| 102 | +\item \textbf{hold}: always valid; no intent. | |
| 103 | +\item \textbf{cancel}: the ids are intersected with the agent's open orders (resting and in flight); empty intersection $\to$ \code{cancel: no valid order ids}; otherwise one cancel intent per surviving id. | |
| 104 | +\item \textbf{place}, in this order: | |
| 105 | + \begin{enumerate} | |
| 106 | + \item side, order type and quantity must all be present (\code{place: missing side/order\_type/quantity}); | |
| 107 | + \item quantity $>0$ (\code{place: quantity <= 0}) and $\le$ \code{max\_order\_qty} $=50$ (\code{place: quantity q > max 50}); | |
| 108 | + \item the asset must not be halted (\code{place: market halted}); | |
| 109 | + \item limit orders need a price (\code{place: limit without price}); the price is converted to ticks by $\operatorname{round}(100\,\text{price}/\text{tick\_cents})$ and must be positive and within $\pm20\%$ of the midpoint (\code{place: limit price outside ±20\% of mid}); | |
| 110 | + \item market orders need a non-empty opposite side (\code{place: market order with empty opposite side}); | |
| 111 | + \item buys: $\lfloor \text{ref}\times\text{tick\_cents}\times q\times1.001\rfloor \le$ free cash, where ref is the limit price or else the best ask or midpoint (\code{place: cost c > free cash f}); | |
| 112 | + \item sells: $q$ is clipped to holdings minus the remaining quantity of the agent's resting sells; if nothing is free the decision is invalid (\code{place: no free shares to sell}); a clipped sell is recorded as valid. | |
| 113 | + \end{enumerate} | |
| 114 | +\item The surviving intent is tagged \code{llm} and submitted to the exchange, which applies its own checks (price band, cash reservation, position limit, leverage); an exchange rejection is logged in the event log, not in the decision log. | |
| 115 | +\end{enumerate} | |
modified
paper/appendices/proofs.tex
+1 −1
@@ -203,7 +203,7 @@ i.e.\ $\Delta P_0<-b/X$, which fails. For $a>1$ and $b>0$ the same algebra shows | ||
| 203 | 203 | \emph{does} lie in $R$ whenever $\Delta P_0>-b/X$ (dividing by $1-a<0$ reverses the inequality): a |
| 204 | 204 | second, self-fulfilling fixed point exists in which the cohort is forced to sell because the price has |
| 205 | 205 | fallen enough to exhaust the buffer, and the price has fallen because the cohort sells. This is the |
| 206 | −multiplicity familiar from \citet{brunnermeierpedersen2009}. We select the cascade limit, which is | |
| 206 | +multiplicity familiar from \citet{brunnermeier2009}. We select the cascade limit, which is | |
| 207 | 207 | $\Delta P_0$; the simulator's forced-liquidation rule is likewise sequential and cannot jump to the |
| 208 | 208 | self-fulfilling point. |
| 209 | 209 | |
modified
paper/appendices/pseudocode.tex
+152 −1
@@ -1 +1,152 @@ | ||
| 1 | −% pseudocode — to be written | |
| 1 | +\section{Pseudocode} | |
| 2 | +\label{app:pseudocode} | |
| 3 | + | |
| 4 | +The five algorithms below follow the control flow of the code line by line; names in typewriter font are the corresponding functions. $\Delta$ is the dividend interval, $T$ the number of ticks. | |
| 5 | + | |
| 6 | +\begin{algorithm}[H] | |
| 7 | +\caption{Tick loop (\code{Simulation.run}, \code{experiments/runner.py})} | |
| 8 | +\label{alg:tick} | |
| 9 | +\footnotesize\begin{algorithmic}[1] | |
| 10 | +\State build streams from the master seed; draw fundamentals, population, accounts, latencies; register accounts | |
| 11 | +\State $\text{pending\_liq} \gets \emptyset$; record $P_0$ | |
| 12 | +\For{$t = 0,\dots,T-1$} | |
| 13 | + \State $\text{exchange.time} \gets t$ | |
| 14 | + \If{$t>0$ and $t \bmod \Delta = 0$} | |
| 15 | + \State $D \gets \code{fund.step()}$ \Comment{pay $D_n$, reveal $x_n$, update public filter, draw latent $x_{n+1}$} | |
| 16 | + \State pay $\operatorname{round}(D_k)$ cents per lot of every asset $k$; check conservation | |
| 17 | + \EndIf | |
| 18 | + \State \code{apply\_shocks}$(t)$ \Comment{after the dividend: a jump stays latent until news or the next dividend} | |
| 19 | + \ForAll{news items drawn this tick} \State update the public filter with the item's value and stated noise \EndFor | |
| 20 | + \ForAll{$(i,\text{act}) \in \text{pending\_liq}$} \State \code{exchange.submit}$(i,\text{act},t)$ \EndFor; $\text{pending\_liq}\gets\emptyset$ | |
| 21 | + \State $u \gets$ \code{activation} stream, one uniform per slot | |
| 22 | + \State $A_c \gets \{i \text{ classical}: u_i < \text{activity}_i\}$; shuffle $A_c$ with the \code{shuffle} stream | |
| 23 | + \State $A_\ell \gets \{i \text{ LLM}: (t-\text{offset}_i) \bmod \text{interval} = 0,\ t \ge \texttt{warmup}/2\}$, minus agents at their decision cap | |
| 24 | + \State $\text{tw} \gets$ last 30 trades per asset | |
| 25 | + \ForAll{$i \in A_c$} | |
| 26 | + \State $o \gets$ \code{observe}$(i,t,\text{tw})$ \Comment{book depth 5, 60-tick mids, trades, dividend, $V^{\mathrm{pub}}$, $\hat x$, news, fresh private signal, account, open orders} | |
| 27 | + \State $a \gets \text{policy}_i.\code{decide}(o,\text{rng}_i)$; \textbf{if} $a$ is not hold \textbf{then} \code{exchange.submit}$(i,a,t)$ | |
| 28 | + \EndFor | |
| 29 | + \If{$A_\ell \neq \emptyset$} | |
| 30 | + \State build observations for all $i\in A_\ell$; update each agent's equity-change history | |
| 31 | + \State $(a_i, \text{rec}_i)_{i\in A_\ell} \gets$ \code{llm\_policy.decide\_batch} \Comment{Algorithm~\ref{alg:llm}, thread pool of width 8} | |
| 32 | + \ForAll{$i \in A_\ell$} \State submit $a_i$ if not hold; update memory and cohort channel from $\text{rec}_i$ \EndFor | |
| 33 | + \EndIf | |
| 34 | + \State \code{exchange.process\_until}$(t+1)$ \Comment{arrivals with time $<t+1$ in (arrival, sequence) order: Algorithm~\ref{alg:order}} | |
| 35 | + \State $\text{pending\_liq} \gets$ \code{exchange.margin\_check()} \Comment{Algorithm~\ref{alg:margin}} | |
| 36 | + \State \code{exchange.end\_of\_tick}$(t)$ \Comment{call auction if enabled and $(t+1)\bmod k=0$; append midpoint; expire orders with $t-\text{submit}\ge$ TTL; log tick} | |
| 37 | + \State record $P_{t+1}$ and the tick row (mid, quotes, depth, volume, LLM volume, $V_t$, $V^{\mathrm{pub}}_t$, $x$, halts, effective spread, total equity) | |
| 38 | +\EndFor | |
| 39 | +\State final conservation check; write manifest, frames and outcomes | |
| 40 | +\end{algorithmic} | |
| 41 | +\end{algorithm} | |
| 42 | + | |
| 43 | +\begin{algorithm}[H] | |
| 44 | +\caption{Order arrival (\code{Exchange.\_on\_order}, \code{exchange/engine.py})} | |
| 45 | +\label{alg:order} | |
| 46 | +\footnotesize\begin{algorithmic}[1] | |
| 47 | +\Require order $o$ (agent $i$, asset $k$, side, type, $q$, price $p$ or none, tag), account $c_i$, book $B_k$ | |
| 48 | +\State remove $o$ from the agent's in-flight set | |
| 49 | +\State \textbf{reject} \code{frozen} if $c_i$ is frozen and tag $\ne$ liquidation; \code{halted} if $k$ is halted; \code{bad\_qty} unless $1\le q\le$ max order | |
| 50 | +\If{limit} \State \textbf{reject} \code{bad\_price} if $p\le0$; \code{price\_band} if $|p-\text{mid}_k| > 0.5\,\text{mid}_k$ \EndIf | |
| 51 | +\If{buy and limit} | |
| 52 | + \State $\text{need} \gets p\,q + \max(\text{fee}_{\rm taker},\text{fee}_{\rm maker}) + q$; \textbf{reject} \code{insufficient\_cash} if need $>$ buying power | |
| 53 | + \State reserve need cents on $c_i$ and on $o$ \Comment{buying power = free cash, or free cash $+(L-1)\,$equity if leveraged} | |
| 54 | +\ElsIf{buy and market} | |
| 55 | + \State \textbf{reject} \code{no\_liquidity} if no ask; $q \gets$ largest $q'\le q$ whose notional plus taker fee on the total fits in buying power, walking the asks; \textbf{reject} \code{insufficient\_cash} if $q=0$ | |
| 56 | +\Else \Comment{sell} | |
| 57 | + \State $h \gets$ free (unreserved) holdings; \textbf{if} $q>h$ \textbf{then} \textbf{reject} \code{insufficient\_shares} if $h\le0$ else $q\gets h$ \Comment{no short selling} | |
| 58 | + \State \textbf{reject} \code{no\_liquidity} if market and no bid; reserve $q$ lots | |
| 59 | +\EndIf | |
| 60 | +\If{position limit set and tag $\ne$ liquidation} | |
| 61 | + \State \textbf{release and reject} \code{position\_limit} if $|\text{pos}+\text{side}\,(\text{same-side resting}+q)| >$ limit | |
| 62 | +\EndIf | |
| 63 | +\If{margin check on, $L_i>1$ and tag $\ne$ liquidation} | |
| 64 | + \State gross$' \gets$ gross exposure with asset $k$ revalued at the post-trade position and price ($p$ or mid) | |
| 65 | + \State \textbf{release and reject} \code{leverage\_cap} if equity $\le0$ or gross$' > L_i\,$equity | |
| 66 | +\EndIf | |
| 67 | +\State log \code{accept} | |
| 68 | +\If{call-auction mode} | |
| 69 | + \State \textbf{if} market \textbf{then} convert to a limit at the band edge, tag \code{ioc}, reserve cash for buys; rest $o$ in $B_k$; \textbf{return} | |
| 70 | +\EndIf | |
| 71 | +\State fills $\gets B_k.\code{match}(o)$ \Comment{walk the opposite side while crossing; price--time priority; resting price} | |
| 72 | +\ForAll{fill $(r, p', q')$} | |
| 73 | + \State fees: taker $=\operatorname{round}(p'q'\cdot 2/10^4)$ on $o$, maker $=0$ on $r$; move $p'q'$ cents and $q'$ lots; release the reservations consumed (all of it on the last fill); log \code{trade}; update last price and tick volume; check conservation | |
| 74 | +\EndFor | |
| 75 | +\If{$o$ has remaining quantity} | |
| 76 | + \State \textbf{if} limit \textbf{then} rest $o$ in $B_k$ \textbf{else} release the reservation, cancel the remainder (\code{ioc}); count a rejection if nothing filled | |
| 77 | +\EndIf | |
| 78 | +\State \code{maybe\_halt}$(k)$: if a breaker is set and $|\text{last}/\text{mid}_{t-10}-1|>$ threshold, halt $k$ for 20 ticks and cancel all resting orders (releasing reservations) | |
| 79 | +\end{algorithmic} | |
| 80 | +\end{algorithm} | |
| 81 | + | |
| 82 | +\begin{algorithm}[H] | |
| 83 | +\caption{Margin check and liquidation (\code{Exchange.margin\_check}, \code{\_liquidate}; submitted by the runner at $t+1$)} | |
| 84 | +\label{alg:margin} | |
| 85 | +\footnotesize\begin{algorithmic}[1] | |
| 86 | +\State marks $\gets$ midpoint of every asset (fallback: last trade, reference) | |
| 87 | +\ForAll{accounts $c$ not frozen} | |
| 88 | + \State $E \gets$ cash $+\sum_k Q_k\,\text{mark}_k$; $G \gets \sum_k |Q_k|\,\text{mark}_k$; \textbf{continue} if $G\le0$ | |
| 89 | + \If{$E\le0$} | |
| 90 | + \State freeze $c$; log \code{bankrupt}; $f\gets1$ \Comment{full liquidation; no further orders accepted} | |
| 91 | + \ElsIf{$L_c>1$ and $E/G < m$ ($m=0.25$)} | |
| 92 | + \State log \code{margin\_call}; $f\gets$ liquidation fraction ($0.5$) | |
| 93 | + \Else \State \textbf{continue} | |
| 94 | + \EndIf | |
| 95 | + \State act $\gets$ cancel every resting order of $c$; for each asset with $Q_k\ne0$ add a market order for $\max(1,\lfloor f|Q_k|\rfloor)$ lots on the side that reduces $|Q_k|$, tagged \code{liquidation} | |
| 96 | + \State log \code{liquidate}; append $(c,\text{act})$ to the list returned to the runner | |
| 97 | +\EndFor | |
| 98 | +\State \Return list \Comment{the runner submits these at the start of tick $t+1$; liquidation orders bypass the frozen, position-limit and leverage checks} | |
| 99 | +\end{algorithmic} | |
| 100 | +\end{algorithm} | |
| 101 | + | |
| 102 | +\begin{algorithm}[H] | |
| 103 | +\caption{LLM decision pipeline (\code{LLMPolicy.decide\_one}, \code{\_call}, \code{to\_action}, \code{llm/policy.py})} | |
| 104 | +\label{alg:llm} | |
| 105 | +\footnotesize\begin{algorithmic}[1] | |
| 106 | +\Require observation $o$, provider $\pi$, model $M$, prompt id $\psi$ | |
| 107 | +\State $S \gets$ \code{system\_prompt}$(\psi, o)$; $U \gets$ \code{user\_message}$(o, \text{adaptive})$ \Comment{Appendix~\ref{app:prompts}} | |
| 108 | +\If{$\pi=$ library} | |
| 109 | + \State text $\gets$ recorded decision of a random one of the 5 nearest neighbours of \code{features}$(o)$, limit price rescaled to the midpoint; cached $\gets$ true | |
| 110 | +\Else | |
| 111 | + \State key $\gets$ SHA-256$(\pi\,|\,M\,|\,\text{version}\,|\,S\,|\,U)$ | |
| 112 | + \If{cache enabled and key present} \State text $\gets$ cached response; cached $\gets$ true | |
| 113 | + \ElsIf{frozen-only or halted for budget} \State error $\gets$ \code{frozen\_miss} / \code{budget\_halt} | |
| 114 | + \ElsIf{budget cap reached} \State halt for budget; error $\gets$ \code{USD cap reached} | |
| 115 | + \Else | |
| 116 | + \State result $\gets$ provider$(\pi).\code{complete}(S,U,M,\text{effort}=\text{low},\text{max\_output}=1200,\text{cache\_key}=\code{ma-v1-}\psi)$ | |
| 117 | + \State \textbf{if} text non-empty and no error \textbf{then} store (key, $S$, $U$, text, usage) in the cache | |
| 118 | + \EndIf | |
| 119 | +\EndIf | |
| 120 | +\State cost $\gets$ \code{budget.record}(tokens, latency, cached) \Comment{0 for cached hits; priced from the model table otherwise} | |
| 121 | +\State \textbf{if} error \textbf{then} dec $\gets$ none \textbf{else} (dec, err) $\gets$ \code{parse\_decision}(text) \Comment{lenient JSON, strict pydantic schema} | |
| 122 | +\State (action, valid, reason) $\gets$ \code{to\_action}(dec, $o$) if dec else (hold, false, err) \Comment{rules of Appendix~\ref{app:prompts}} | |
| 123 | +\State count invalid and failures | |
| 124 | +\State record $(t, i, \pi, M, \psi, \text{valid}, \text{reason}, \text{decision fields}, \text{tokens}, \text{latency}, \text{cost}, \text{cached}, \text{error})$ | |
| 125 | +\Statex \hspace{\algorithmicindent} together with the prompt SHA, the request id, \code{features}$(o)$ and the relative limit price | |
| 126 | +\State \Return (action with meta \{valid, reason, rationale, confidence, llm\_action, provider, model\}, record) | |
| 127 | +\end{algorithmic} | |
| 128 | +\end{algorithm} | |
| 129 | + | |
| 130 | +\begin{algorithm}[H] | |
| 131 | +\caption{Matched-seed design runner (\code{run\_design}, \code{cell\_config}, \code{\_run\_one}, \code{experiments/design.py})} | |
| 132 | +\label{alg:design} | |
| 133 | +\footnotesize\begin{algorithmic}[1] | |
| 134 | +\Require design file with \code{base}, \code{seeds}, design-level \code{overrides}, \code{cells} (name, overrides, treatment) and optional \code{factors} | |
| 135 | +\State cells $\gets$ listed cells $\cup$ full factorial product of the factor levels (overrides merged, labels joined); keep only \code{--cells} if given | |
| 136 | +\State seeds $\gets$ explicit list or $\{\text{start},\dots,\text{start}+n-1\}$; truncate to \code{--max-seeds} if given | |
| 137 | +\ForAll{cell $c$, seed $s$} | |
| 138 | + \State cfg $\gets$ base configuration; apply design overrides then cell overrides (dotted keys; sections merged, leaf dictionaries replaced); set name $\gets$ design/cell, seed $\gets s$, treatment $\gets$ design labels $\cup$ cell labels | |
| 139 | + \State job $\gets$ (cfg as JSON, \code{results/runs/<design>/<cell>/seed<s>}, cache path) | |
| 140 | +\EndFor | |
| 141 | +\ForAll{jobs, sequentially if workers $\le1$ else in a process pool of the given width} | |
| 142 | + \If{\code{outcomes.json} exists in the run directory} \State reuse it \Comment{an interrupted design resumes without new API calls} | |
| 143 | + \Else | |
| 144 | + \State build the LLM policy (shared SQLite cache) if \code{llm\_share}$>0$; run Algorithm~\ref{alg:tick}; save config, manifest and frames | |
| 145 | + \State outcomes $\gets$ \code{run\_outcomes}(result); write \code{outcomes.json} | |
| 146 | + \EndIf | |
| 147 | +\EndFor | |
| 148 | +\State write \code{outcomes.parquet} (one row per run) and \code{design.json} (expanded cells, seeds, run count, wall-clock) | |
| 149 | +\end{algorithmic} | |
| 150 | +\end{algorithm} | |
| 151 | + | |
| 152 | +\noindent Because every cell of a design is run on the same seed list and the only streams touched by $\varphi$ are the LLM assignment and the LLM agents' own generators, the control and each treatment of a given seed share the dividend path, the news, the endowments, latencies and activation draws of every non-replaced slot (common random numbers); the paired estimators of the analysis exploit this matching seed by seed. | |
modified
paper/appendices/reproducibility.tex
+29 −1
@@ -1 +1,29 @@ | ||
| 1 | −% reproducibility — to be written | |
| 1 | +\section{Reproducibility and replication package} | |
| 2 | +\label{app:repro} | |
| 3 | + | |
| 4 | +\paragraph{Environment.} | |
| 5 | +The package requires Python $\ge 3.12$ and is installed with \code{uv sync --extra dev} from the committed lock file \code{uv.lock}; the runs reported here used Python 3.12.13 and uv 0.11.7 on macOS (arm64). Locked versions of the libraries that affect numerical results or provider calls are numpy 2.5.4, pandas 3.1.0, scipy 1.18.1, statsmodels 0.15.0, pyarrow 26.0.0, duckdb 2.0.0, sortedcontainers 2.4.0, pydantic 2.14.1, pyyaml 6.0.3, openai 3.40.0, anthropic 2.3.0, with pytest 9.2.0 and hypothesis 6.170.12 for the test suite. API keys are read from a local \code{.env} file (\code{OPENAI\_API\_KEY}, \code{ANTHROPIC\_API\_KEY}) by the \code{ma} command line and never appear in configurations, manifests or logs. | |
| 6 | + | |
| 7 | +\paragraph{One-command replication without an API key.} | |
| 8 | +\code{make replicate} runs, in order, the test suite (\code{make test}, 24 tests), the smoke test (two 300-tick runs with 100 agents at $\varphi=0$ and $\varphi=0.25$ with the mock provider, accounting verified), the classical confirmatory design (16 cells $\times$ 30 seeds) and the mock exploratory design (12 cells $\times$ 3 seeds), the numerical theory checks, the figures and the tables, writing \code{results/runs/<design>/outcomes.parquet}, \code{results/figures/} and \code{results/tables/}; \code{make paper} copies tables and figures into \code{paper/} and compiles \code{main.tex} with \code{latexmk}. The classical designs take about a minute on six worker processes (\code{W=6}); every number in the classical tables is reproduced bit for bit because those runs are deterministic functions of the seed. | |
| 9 | + | |
| 10 | +\paragraph{Paid designs and cost estimates.}\sloppy | |
| 11 | +\code{make pilot} and \code{make confirmatory} first print the \code{ma budget} estimate and then run the design with the shared decision cache \code{results/cache/llm\_cache.sqlite}. With the planning token profile of 760 input and 230 output tokens per call, \code{ma budget} reports 6,480 calls and 2.85~USD for \code{pilot\_openai.yaml} (3 cells $\times$ 3 seeds, per-run cap 1.5~USD) and 79,380 calls and 38.74~USD for \code{confirmatory\_openai.yaml} (16 cells $\times$ 7 seeds, per-run cap 2.0~USD; 41.65~USD at the default 800/250 profile); the mock designs cost nothing. The realised profile of gpt-5.4-nano at prompt version v1 in the live runs on disk is about 850--870 input tokens (no provider-side cache hits were reported), 320--460 output tokens of which 240--380 are reasoning tokens, 2.5--3.2~s of latency and 0.00057--0.00075~USD per decision, i.e.\ 0.08--0.10~USD per run at $\varphi=0.05$ and about 0.22~USD at $\varphi=0.25$.\footnote{Means over the online gpt-5.4-nano decisions in the \code{llm\_decisions.parquet} frames of the pilot \code{share\_5} runs (seeds 1--3) and of the confirmatory \code{dose\_005}, \code{dose\_010} and \code{dose\_025} runs completed at the time of writing; the planning figure of 0.00044~USD per decision in \code{docs/model\_cards.md} was measured on shorter smoke-test prompts.} The project-wide ceiling for live calls is 40~USD; per-run caps are set at least twice the expected run cost so that no confirmatory run halts for budget. | |
| 12 | + | |
| 13 | +\paragraph{Where things live.} | |
| 14 | +Each run writes \code{results/runs/<design>/<cell>/seed<NNNN>/} containing \code{config.yaml} (the fully resolved configuration), \code{manifest.json}, \code{ticks.parquet} (one row per tick and asset), \code{trades.parquet}, \code{agents.parquet} (one row per slot with policy, replaced type, provider, model, prompt id, cohort, stratum, latency, precision, leverage cap, initial and final wealth, fees, dividends, order counts and the frozen flag), \code{llm\_decisions.parquet} (one row per LLM decision, Section~\ref{sec:implementation}) and \code{outcomes.json}; optionally \code{events.parquet}. The design level adds \code{outcomes.parquet} (one row per run with all outcome metrics and treatment labels) and \code{design.json}. The decision cache is a single SQLite table keyed by the SHA-256 of (provider, model, prompt version, system prompt, user prompt) storing both prompts, the raw response text, the token usage and a timestamp; the frozen library built from the recorded decisions is \code{results/library/gpt-5.4-nano\_single.npz} (feature matrix plus JSON decisions), produced and replayed by \code{scripts/60\_frozen\_library.py}. Calibration artefacts are in \code{results/calibration/} (\code{sweep.parquet}, \code{sweep\_summary.csv}, \code{scale\_benchmark.json}). | |
| 15 | + | |
| 16 | +\paragraph{Provider non-determinism.} | |
| 17 | +OpenAI reasoning models are sampled at the provider and expose no seed; identical prompts can yield different decisions across calls, and the Anthropic API is likewise unseeded. Three mechanisms bound the consequences. First, every online decision is written to the cache before it is used, so a re-run of the same design with the same cache replays every recorded decision exactly, performs zero calls and costs nothing; cache hits are counted separately (\code{cached\_hits} in the manifest, \code{cached\_hit} per decision, \code{llm\_cached\_hits} in the outcomes) so that online and replayed runs are never confused. Second, the request id and the dated model snapshot are stored per decision and the price table per run, so that a provider-side model change is visible. Third, the mock and library providers are fully seeded and serve as the deterministic reference for the pipeline; the frozen library is a non-adaptive replay and its runs are labelled provider \code{library}. The git revision in each manifest carries a \code{-dirty} suffix when the working tree had uncommitted changes at the time of the run; the record of every change made between the pre-analysis plan and the confirmatory runs is kept in \code{docs/research\_log.md}. | |
| 18 | + | |
| 19 | +\paragraph{Replication checklist.} | |
| 20 | +\begin{enumerate}\sloppy | |
| 21 | +\item Check out the revision named in the manifests of the design to be replicated (\code{git\_revision}, without the \code{-dirty} suffix) and install with \code{uv sync --extra dev}; run \code{make test} (24 tests pass). | |
| 22 | +\item Run \code{make smoke}; the printed conservation flag is \code{True} and the artefacts appear in \code{results/runs/smoke/}. | |
| 23 | +\item Run \code{make replicate}; compare \code{results/runs/classical\_confirmatory/outcomes.parquet} and \code{results/runs/exploratory\_mock/outcomes.parquet} with the published copies (identical up to floating-point formatting). | |
| 24 | +\item For the live designs, place the published \code{results/cache/llm\_cache.sqlite} in \code{results/cache/} and run \code{make pilot} and \code{make confirmatory}: every manifest then reports \code{cached\_hits} equal to the number of calls and zero USD. Without the cache, the same commands reproduce the design with fresh provider samples at the cost printed by \code{ma budget}. | |
| 25 | +\item Verify that every run directory carries a \code{manifest.json} with \code{config\_hash}, \code{git\_revision}, \code{prompt\_version}, \code{price\_assumptions}, \code{llm\_usage}, \code{shocks} and \code{final\_conservation\_ok}, and that the \code{providers} list of every live cell contains only the providers named in the design file. | |
| 26 | +\item Regenerate figures and tables with \code{make figures tables}; the analysis scripts read only the parquet frames and never re-simulate. The threshold sensitivity grid (bubble 3/5/8\%, crash 7/10/15\%) is obtained by re-running \code{run\_outcomes} on the saved tick frames. | |
| 27 | +\item Compile the paper with \code{make paper}; the sections that quote numbers from result files cite those files in footnotes so that each figure can be traced to a frame and a script. | |
| 28 | +\item Record any deviation from \code{docs/protocol.md} in \code{docs/research\_log.md} with its date and reason before analysing new runs. | |
| 29 | +\end{enumerate} | |
modified
paper/references.bib
+13 −2
@@ -836,7 +836,7 @@ | ||
| 836 | 836 | @article{lillo2004, |
| 837 | 837 | author = {Lillo, Fabrizio and Farmer, J. Doyne}, |
| 838 | 838 | title = {The Long Memory of the Efficient Market}, |
| 839 | − journal = {Studies in Nonlinear Dynamics & Econometrics}, | |
| 839 | + journal = {Studies in Nonlinear Dynamics \& Econometrics}, | |
| 840 | 840 | year = {2004}, |
| 841 | 841 | volume = {8}, |
| 842 | 842 | number = {3}, |
@@ -1136,7 +1136,7 @@ | ||
| 1136 | 1136 | @article{danielsson2022, |
| 1137 | 1137 | author = {Daníelsson, Jón and Macrae, Robert and Uthemann, Andreas}, |
| 1138 | 1138 | title = {Artificial intelligence and systemic risk}, |
| 1139 | − journal = {Journal of Banking & Finance}, | |
| 1139 | + journal = {Journal of Banking \& Finance}, | |
| 1140 | 1140 | year = {2022}, |
| 1141 | 1141 | volume = {140}, |
| 1142 | 1142 | pages = {106290}, |
@@ -1524,3 +1524,14 @@ | ||
| 1524 | 1524 | month = {November}, |
| 1525 | 1525 | url = {https://www.fsb.org/uploads/P14112024.pdf} |
| 1526 | 1526 | } |
| 1527 | + | |
| 1528 | +@article{delong1990, | |
| 1529 | + author = {De Long, J. Bradford and Shleifer, Andrei and Summers, Lawrence H. and Waldmann, Robert J.}, | |
| 1530 | + title = {Positive Feedback Investment Strategies and Destabilizing Rational Speculation}, | |
| 1531 | + journal = {The Journal of Finance}, | |
| 1532 | + year = {1990}, | |
| 1533 | + volume = {45}, | |
| 1534 | + number = {2}, | |
| 1535 | + pages = {379--395}, | |
| 1536 | + doi = {10.1111/j.1540-6261.1990.tb03695.x} | |
| 1537 | +} | |
modified
paper/sections/identification.tex
+291 −1
@@ -1 +1,291 @@ | ||
| 1 | −% identification — to be written | |
| 1 | +% paper/sections/identification.tex — UQO WP 27, "The Invisible Hand of Machines" | |
| 2 | +% Section: Experimental identification. Mirrors docs/identification.md, docs/protocol.md and the | |
| 3 | +% deviations recorded in docs/research_log.md. Hypotheses H1–H6 are those of Table~\ref{tab:predictions}. | |
| 4 | +\section{Experimental identification} | |
| 5 | +\label{sec:identification} | |
| 6 | + | |
| 7 | +The simulator is an experimental apparatus and we describe it as one. Causal language refers to effects | |
| 8 | +\emph{inside the simulator}: the object identified is the effect of replacing a share of rule-based | |
| 9 | +trader slots by LLM-controlled slots in this market mechanism, with these model snapshots and these | |
| 10 | +prompts. | |
| 11 | + | |
| 12 | +\subsection{Unit of observation and randomization} | |
| 13 | +\label{sec:identification:unit} | |
| 14 | + | |
| 15 | +The unit of observation is one run: $N=100$ trader slots simulated for 600 ticks, with outcomes | |
| 16 | +computed on $t\ge100$. A run is indexed by a seed $s$ and a cell $z$, a vector of design settings---the | |
| 17 | +LLM share $\phi$ (\code{llm\_share}), the provider and model mix, the prompt set, memory, the | |
| 18 | +communication channel, the leverage composition, the safeguards and the fairness regime---and $Y_s(z)$ | |
| 19 | +is the potential outcome of seed $s$ under cell $z$. All exogenous randomness derives from a master | |
| 20 | +seed through independent named streams: \code{fundamentals} (dividend path), \code{news}, \code{slots} | |
| 21 | +(wealth multipliers, latencies, informed draws), \code{assign} (the classical policy of each slot), | |
| 22 | +\code{llm\_assign} (which eligible slots become LLM-controlled, their provider and decision offsets), | |
| 23 | +\code{activation}, \code{tiebreak}, \code{shuffle} (within-tick processing order), and one generator | |
| 24 | +per agent for policy randomness and private-signal noise. Slot assignment is stratified: slots are | |
| 25 | +ranked by wealth multiplier and the policy types are dealt down the ranking, so each wealth tercile | |
| 26 | +receives the same policy composition. The treatment replaces a fraction $\phi$ of eligible slots with | |
| 27 | +proportional quotas within each policy type; market makers are never replaced, so liquidity supply is | |
| 28 | +held fixed and $\phi=1$ means 95 of 100 slots. A replaced slot keeps its cash, shares, latency | |
| 29 | +and---after the change described in Section~\ref{sec:identification:changes}---its information set: an | |
| 30 | +LLM agent is informed, at the same precision, if and only if the slot it replaces was. The treatment | |
| 31 | +changes the decision policy of a stratified subset of slots and nothing else. | |
| 32 | + | |
| 33 | +In a matched pair $\{(s,0),(s,z)\}$ the dividend path and latent value, the news, the shock at $t=300$ | |
| 34 | +and the placebo at $t=450$, every endowment, latency and informed status, the policy of every | |
| 35 | +non-replaced slot, the activation and tie-break draws, and the random numbers of every non-replaced | |
| 36 | +agent are identical by construction. What is \emph{not} held fixed is what the treatment is meant to | |
| 37 | +change: the decisions of the LLM slots, including the provider's sampling noise, which is not seedable | |
| 38 | +and is treated as part of the treatment, and the market's reaction to them---the book, the fills of | |
| 39 | +every other agent, and hence the subsequent decisions of the rule-based agents, which condition on the | |
| 40 | +book. Common random numbers remove the variance of the environment from the paired difference, not | |
| 41 | +the equilibrium response of the market. | |
| 42 | + | |
| 43 | +\subsection{Causal structure} | |
| 44 | +\label{sec:identification:dag} | |
| 45 | + | |
| 46 | +The cell label sets the treatment $Z$; with the \code{llm\_assign} stream and the provider noise, $Z$ | |
| 47 | +determines the LLM slot set and its policies. Those policies generate, inside the run, a set of | |
| 48 | +mediators $M$---pairwise order-flow correlation among LLM agents, synchrony, aggressiveness, validity | |
| 49 | +rate and realized dose---and outcomes $Y$ are produced jointly by $M$, by the exogenous streams, and by | |
| 50 | +a direct path from $Z$ that bypasses LLM behaviour (fewer heuristic agents, halts, position limits). The | |
| 51 | +effect of $Z$ on $Y$ is identified because $Z$ is set by the design file and the only random input | |
| 52 | +that depends on it, \code{llm\_assign}, is independent of every other stream by construction; the | |
| 53 | +effect of $Z$ on each mediator is identified for the same reason, which is how H2 and H5 are tested. | |
| 54 | +Mediation is \emph{not} identified: $M$ and $Y$ are generated by the same path of draws and provider | |
| 55 | +noise, so sequential ignorability---independence of $M$ and the potential outcomes given $Z$---fails; | |
| 56 | +a volatile fundamental path raises both the correlation of LLM orders and the pricing error. We report | |
| 57 | +mediation only as a descriptive decomposition and treat each mediator as an outcome. $Z$ also bundles | |
| 58 | +model, prompt, decision frequency and the rule that invalid decisions become holds; the estimand is | |
| 59 | +intention-to-treat, ``deploy this agent as configured''. | |
| 60 | + | |
| 61 | +\subsection{Estimands} | |
| 62 | +\label{sec:identification:estimands} | |
| 63 | + | |
| 64 | +Let $\tau_z=\E_s[Y_s(z)-Y_s(0)]$ be the paired average treatment effect of cell $z$ against the control | |
| 65 | +of the same seed. In order of priority: (E1) $\tau_z$ at each share against $\phi=0$, the primary | |
| 66 | +confirmatory contrast being $\phi=0.25$ with \code{gpt-5.4-nano} and the single prompt, $\phi=0.50$ the | |
| 67 | +secondary; (E2) the dose--response $\tau(\phi)$ over $\phi\in\{0.05,0.10,0.25,0.50,1.00\}$, summarized | |
| 68 | +by a linear slope and an ordered trend test; (E3) diversity at equal share, | |
| 69 | +$\E[Y(\text{diverse},0.25)-Y(\text{single},0.25)]$ for prompt diversity at a fixed model and | |
| 70 | +$\E[Y(\text{mix},0.25)-Y(\text{diverse},0.25)]$ for a two-model mix against the single-model | |
| 71 | +diverse-prompt cell; (E4) cognition contrasts at $\phi=0.25$---memory of five decisions, a rationale | |
| 72 | +channel in cohorts of five, adaptive profit-and-loss feedback---each against the single-prompt cell at | |
| 73 | +the same share; (E5) the interaction with leverage, | |
| 74 | +$[Y(0.25,\text{lev}_{\text{hi}})-Y(0,\text{lev}_{\text{hi}})]-[Y(0.25,\text{lev}_{\text{base}})-Y(0,\text{lev}_{\text{base}})]$, | |
| 75 | +and the safeguard contrasts (circuit breaker, position limit) at $\phi=0.25$; (E6) the fairness | |
| 76 | +contrasts, a more capable model at the share that equalizes the dollar budget with the primary cell, | |
| 77 | +and a matched-latency cell in which LLM orders arrive with the minimum latency. The leveraged control | |
| 78 | +at $\phi=0$ is a classical run, free of API cost and deterministic given the seed, so it is produced on | |
| 79 | +the matched seeds and the second difference of E5 is also within-seed. Two further objects are | |
| 80 | +descriptive: a local projection of the log price--value gap around the latent shock, and $\tau_z$ | |
| 81 | +divided by the mean realized order share, a rescaling and not a causal effect per order. | |
| 82 | + | |
| 83 | +\subsection{Pre-registered outcomes, hypotheses and thresholds} | |
| 84 | +\label{sec:identification:outcomes} | |
| 85 | + | |
| 86 | +Table~\ref{tab:outcomes} lists the outcome families, their lead variables and the sign predicted by the | |
| 87 | +hypotheses of Table~\ref{tab:predictions}. The coordination statistics exist only when $\phi>0$, so H2 | |
| 88 | +is tested as a dose--response among treated cells plus a comparison with the same statistic computed on | |
| 89 | +the aggressor-signed trades of classical agents of the replaced types. Thresholds were fixed before any | |
| 90 | +live call: a bubble is $|P/V-1|>5\,\%$ for at least ten ticks, a crash a drop of at least 10\,\% from | |
| 91 | +the trailing 20-tick maximum within 20 ticks. A sensitivity grid---bubbles at 3, 5 and 8\,\%, crashes | |
| 92 | +at 7, 10 and 15\,\%---is reported for every instability result by re-scoring saved price paths; no | |
| 93 | +threshold is re-selected after the fact. The classical control produces no crash at 10\,\%, so a floor | |
| 94 | +effect is anticipated and the 7\,\% variant is the pre-specified fallback for H3. \code{msd\_log} is | |
| 95 | +analysed on the $\log_{10}$ scale, \code{time\_to\_absorb} with rank-based inference, and counts by | |
| 96 | +mean differences with randomization inference. | |
| 97 | + | |
| 98 | +\begin{table}[htbp] | |
| 99 | +\centering | |
| 100 | +\caption{Pre-registered outcome families, lead variables and predicted signs.} | |
| 101 | +\label{tab:outcomes} | |
| 102 | +\footnotesize | |
| 103 | +\begin{tabular}{@{}p{1.7cm}p{3.2cm}p{5.0cm}p{3.4cm}p{1.3cm}@{}} | |
| 104 | +\toprule | |
| 105 | +Family & Variable & Definition & Predicted sign & H \\ | |
| 106 | +\midrule | |
| 107 | +Efficiency & \code{msd\_log} \newline (lead) & mean $(\log P-\log V)^2$ & $\uparrow$ in $\phi$; $\downarrow$ with diversity & H1, H4 \\ | |
| 108 | + & \code{mad\_pct} & mean $100\,|\log P-\log V|$ & $\uparrow$ in $\phi$ & H1 \\ | |
| 109 | + & \code{time\_to\_absorb} & first $h$ after $t=300$ with $|P/V-1|<1\,\%$ for 5 ticks (censored at 100) & $\uparrow$ in $\phi$; $\uparrow$ with safeguards & H1, H6 \\ | |
| 110 | + & \code{response\_ratio} & $(P_{t+20}-P_{t-1})/(V_{t+20}-V_{t-1})$ at the shock & $|RR-1|$ $\uparrow$ (two-sided) & H1 \\ | |
| 111 | + & \code{vr5} & variance ratio of log mid at lag 5 & $|VR-1|$ $\uparrow$ (two-sided) & H1 \\ | |
| 112 | +Liquidity & \code{spread\_bp} \newline (lead) & mean quoted spread / mid, bp & $\uparrow$ in $\phi$ & H1b \\ | |
| 113 | + & \code{eff\_spread\_bp} & $2\,|\text{trade}-\text{mid}|/\text{mid}$, bp & $\uparrow$ in $\phi$ & H1b \\ | |
| 114 | + & \code{depth5} & mean lots within 5 levels, both sides & $\downarrow$ in $\phi$ & H1b \\ | |
| 115 | + & \code{lambda\_bp\_100} & slope of $\Delta\log$ mid (bp) on signed volume / 100 & two-sided ($\lambda$ falls as flow gets noisier) & H1b \\ | |
| 116 | + & \code{resiliency} & half-life of log-spread deviations, ticks & $\uparrow$ in $\phi$ & H1b \\ | |
| 117 | +Instability & \code{n\_crashes} \newline (lead H3) & drops $\ge10\,\%$ from the trailing 20-tick max & $\uparrow$ in $\phi\times$leverage & H3 \\ | |
| 118 | + & \code{max\_dd} \newline (lead H6) & maximum drawdown of mid & $\uparrow$ in $\phi$; $\downarrow$ with safeguards & H3, H6 \\ | |
| 119 | + & \code{n\_bubbles}, \newline \code{bubble\_amp} & episodes $|P/V-1|>5\,\%$ for $\ge10$ ticks; max amplitude & $\uparrow$ in $\phi\times$leverage & H3 \\ | |
| 120 | + & \code{acf\_absr\_1\_10} & $\sum_{k=1}^{10}\mathrm{ACF}_k(|r|)$ & $\uparrow$ in $\phi$ & H3 \\ | |
| 121 | + & \code{kurtosis} & excess kurtosis of tick returns & $\uparrow$ in $\phi$ & H3 \\ | |
| 122 | +Coordination & \code{llm\_pairwise\_corr} \newline (lead H2) & mean pairwise corr.\ of LLM net order signs, 20-tick windows & $\uparrow$ in $\phi$; $\downarrow$ with diversity & H2, H4 \\ | |
| 123 | + & \code{llm\_synchrony} \newline (lead H5) & mean $|\text{mean sign}|$ per decision round & $\uparrow$ with channel & H2, H5 \\ | |
| 124 | +\bottomrule | |
| 125 | +\end{tabular} | |
| 126 | +\end{table} | |
| 127 | + | |
| 128 | +\subsection{Estimators} | |
| 129 | +\label{sec:identification:estimators} | |
| 130 | + | |
| 131 | +For each cell and seed we form $\Delta_s=Y_s(z)-Y_s(0)$ and report its mean, the paired-$t$ 95\,\% | |
| 132 | +confidence interval and Cohen's $d_z=\bar\Delta/\mathrm{SD}(\Delta)$, with Glass's $\Delta$ against the | |
| 133 | +control standard deviation from the 30 free classical seeds for comparability across cells. The primary | |
| 134 | +$p$-value is from randomization inference under the sharp null: within a pair the sign of $\Delta_s$ is | |
| 135 | +exchangeable, and the $p$-value is the share of the $2^n$ sign assignments whose absolute mean is at | |
| 136 | +least the observed one; the Wilcoxon signed-rank test is a secondary distribution-free check. | |
| 137 | +Multi-cell arms and the dose--response use a seed fixed-effects regression, | |
| 138 | +$Y_{s,z}=\alpha_s+\sum_k\tau_k\mathbf 1[z=k]+\varepsilon_{s,z}$, with standard errors clustered by | |
| 139 | +seed; because there are at most seven live clusters, inference rests on the wild cluster bootstrap with | |
| 140 | +Rademacher weights and, for the ordered share, on Page's $L$ trend test for randomized blocks. Within | |
| 141 | +each family the Holm step-down procedure controls the family-wise error rate at 5\,\%, the lead outcome | |
| 142 | +is reported adjusted and unadjusted, all tests are two-sided and each sign is compared with its | |
| 143 | +prediction. For the shock we estimate local projections of the gap | |
| 144 | +$g_s(z,h)=\log P_{300+h}-\log V_{300+h}$: the coefficient at horizon $h\in[-20,100]$ is the seed mean | |
| 145 | +of $g_s(z,h)-g_s(0,h)$, with pointwise paired-$t$ bands and a bootstrap sup-$t$ band; the same | |
| 146 | +projection around the placebo tick must be flat. Descriptive mediation regresses $Y$ on each mediator | |
| 147 | +with seed and cell fixed effects and reports the product-of-coefficients share of $\tau_z$ associated | |
| 148 | +with it, as a decomposition and not a causal path. | |
| 149 | + | |
| 150 | +\subsection{Power and the number of seeds} | |
| 151 | +\label{sec:identification:power} | |
| 152 | + | |
| 153 | +For a paired design with standardized effect $d=\bar\Delta/\mathrm{SD}(\Delta)$, two-sided | |
| 154 | +$\alpha=0.05$ and power $0.80$, the normal approximation gives | |
| 155 | +\begin{equation} | |
| 156 | + n\;\approx\;\frac{(z_{0.975}+z_{0.80})^2}{d^2}\;=\;\frac{(1.96+0.84)^2}{d^2}\;=\;\frac{7.85}{d^2}, | |
| 157 | + \label{eq:power} | |
| 158 | +\end{equation} | |
| 159 | +so $d=0.5$ requires 32 pairs, $d=0.8$ requires 13 and $d=1$ requires 8. The live design has seven | |
| 160 | +matched seeds. Inverting \eqref{eq:power} gives a minimum detectable paired effect of $d\approx1.06$, | |
| 161 | +and the exact noncentral-$t$ calculation with six degrees of freedom gives $d\approx1.27$; we state | |
| 162 | +plainly that only paired effects of roughly $1.2$ standard deviations or more are detectable at 80\,\% | |
| 163 | +power in the live cells. The seed count was fixed by the dollar cap on live calls, not by the pilot | |
| 164 | +variance, and the pilot's three seeds are never pooled with the confirmatory seeds. Effects below the | |
| 165 | +minimum detectable effect are not thereby absent: a lead outcome is reported as \emph{refuted} if the | |
| 166 | +confidence interval of $d$ excludes $d\ge0.5$ in the predicted direction, as showing \emph{no large | |
| 167 | +effect} if the interval contains zero and excludes the minimum detectable effect, and as | |
| 168 | +\emph{inconclusive} if it contains both. The classical design, which costs nothing, uses 30 matched | |
| 169 | +seeds and detects $d\approx0.5$; it carries the stylized-fact validation, the mock shared-model knob, | |
| 170 | +the leverage, safeguard and information comparative statics and the placebo controls. | |
| 171 | + | |
| 172 | +\subsection{Design matrix} | |
| 173 | +\label{sec:identification:design} | |
| 174 | + | |
| 175 | +Table~\ref{tab:design} lists the cells of the two confirmatory designs. The live design runs every cell | |
| 176 | +on seeds 201--207 with \code{gpt-5.4-nano} and a single generalist prompt unless stated, a per-run | |
| 177 | +dollar cap well above the expected spend so that no run halts for budget, and cached responses so that | |
| 178 | +every run replays exactly. The classical design runs on seeds 101--130 with the mock provider, a seeded | |
| 179 | +rule that is not evidence about LLM behaviour. Both share the baseline economy: 600 ticks, one asset, a | |
| 180 | +latent $+10$-cent shock at $t=300$ and a placebo at $t=450$. | |
| 181 | + | |
| 182 | +\begin{table}[htbp] | |
| 183 | +\centering | |
| 184 | +\caption{Design matrix of the confirmatory runs.} | |
| 185 | +\label{tab:design} | |
| 186 | +\footnotesize | |
| 187 | +\begin{threeparttable} | |
| 188 | +\begin{tabular}{@{}lp{4.4cm}p{4.6cm}lr@{}} | |
| 189 | +\toprule | |
| 190 | +Arm & Cell & Treatment & Seeds & Runs \\ | |
| 191 | +\midrule | |
| 192 | +\multicolumn{5}{@{}l}{\emph{Live design}: \code{confirmatory\_openai.yaml} (gpt-5.4-nano, single prompt unless stated)} \\ | |
| 193 | +Dose & \code{dose\_000} & $\phi=0$ (control; no API cost) & 201--207 & 7 \\ | |
| 194 | +Dose & \code{dose\_005} & $\phi=0.05$ & 201--207 & 7 \\ | |
| 195 | +Dose & \code{dose\_010} & $\phi=0.10$ & 201--207 & 7 \\ | |
| 196 | +Dose & \code{dose\_025} & $\phi=0.25$ (primary contrast) & 201--207 & 7 \\ | |
| 197 | +Dose & \code{dose\_050} & $\phi=0.50$ (secondary contrast) & 201--207 & 7 \\ | |
| 198 | +Dose & \code{dose\_100} & $\phi=1.00$ (95 eligible slots) & 201--207 & 7 \\ | |
| 199 | +Diversity & \code{div\_prompts\_025} & $\phi=0.25$, eight diverse persona prompts & 201--207 & 7 \\ | |
| 200 | +Diversity & \code{div\_models\_025} & $\phi=0.25$, diverse prompts, nano/mini 50/50 & 201--207 & 7 \\ | |
| 201 | +Cognition & \code{memory\_025} & $\phi=0.25$, memory of five past decisions & 201--207 & 7 \\ | |
| 202 | +Cognition & \code{channel\_025} & $\phi=0.25$, rationale channel in cohorts of five & 201--207 & 7 \\ | |
| 203 | +Cognition & \code{adaptive\_025} & $\phi=0.25$, adaptive P\&L feedback in the prompt & 201--207 & 7 \\ | |
| 204 | +Leverage & \code{leverage\_025} & $\phi=0.25$, leveraged slots 19\,\% (trend 6\,\%) & 201--207 & 7 \\ | |
| 205 | +Safeguard & \code{breaker\_025} & $\phi=0.25$, halt when the 10-tick move exceeds 5\,\% & 201--207 & 7 \\ | |
| 206 | +Safeguard & \code{poslimit\_025} & $\phi=0.25$, position limit 300 lots & 201--207 & 7 \\ | |
| 207 | +Fairness & \code{fair\_mini\_005} & $\phi=0.05$, gpt-5.4-mini (equal USD to \code{dose\_025}) & 201--207 & 7 \\ | |
| 208 | +Fairness & \code{fair\_latency\_025} & $\phi=0.25$, LLM latency fixed at the minimum & 201--207 & 7 \\ | |
| 209 | +\midrule | |
| 210 | +\multicolumn{5}{@{}l}{\emph{Classical design}: \code{classical\_confirmatory.yaml} (mock provider, no API cost)} \\ | |
| 211 | +Control & \code{control} & baseline economy & 101--130 & 30 \\ | |
| 212 | +Placebo & \code{placebo\_shock} & placebo events at $t=300$ and $t=450$ & 101--130 & 30 \\ | |
| 213 | +Feedback & \code{trend\_heavy} & trend followers 16\,\% & 101--130 & 30 \\ | |
| 214 | +Leverage & \code{leverage\_heavy} & leveraged slots 19\,\% & 101--130 & 30 \\ | |
| 215 | +Leverage & \code{leverage\_heavy\_} \newline \code{funding\_shock} & leveraged 19\,\%; $-10$-cent shock, then funding shock & 101--130 & 30 \\ | |
| 216 | +Liquidity & \code{liquidity\_shock} & market makers withdrawn for 60 ticks after the shock & 101--130 & 30 \\ | |
| 217 | +Information & \code{info\_low}, \code{info\_high} & private-signal precision 0.5, 8 & 101--130 & 60 \\ | |
| 218 | +Information & \code{misleading\_news} & half of the news items misleading & 101--130 & 30 \\ | |
| 219 | +Safeguard & \code{breaker\_5}, \code{poslimit\_300} & halt at 5\,\%; position limit 300 lots & 101--130 & 60 \\ | |
| 220 | +Mechanism & \code{auction\_5} & call auction every five ticks & 101--130 & 30 \\ | |
| 221 | +Mock herd & \code{mock\_llm\_25\_indep}, \newline \code{mock\_llm\_50\_indep} & mock LLM slots 25\,\%, 50\,\%, independent errors & 101--130 & 60 \\ | |
| 222 | +Contagion & \code{k2\_control}, \code{k2\_leverage} & two assets, factor loading 0.7; with leverage and funding shock & 101--130 & 60 \\ | |
| 223 | +\midrule | |
| 224 | +\multicolumn{4}{@{}l}{Total} & 592 \\ | |
| 225 | +\bottomrule | |
| 226 | +\end{tabular} | |
| 227 | +\begin{tablenotes} | |
| 228 | +\item Every live cell is paired with \code{dose\_000} and every classical cell with \code{control} on | |
| 229 | +the same seed. The leveraged $\phi=0$ control for the E5 interaction is the \code{leverage\_heavy} | |
| 230 | +configuration re-run on seeds 201--207 (deterministic, no API cost). A pilot on seeds 1--3 at | |
| 231 | +$\phi\in\{0.05,0.25,0.50\}$ served cost, validity and variance reporting only. | |
| 232 | +\end{tablenotes} | |
| 233 | +\end{threeparttable} | |
| 234 | +\end{table} | |
| 235 | + | |
| 236 | +\subsection{What was frozen and what changed} | |
| 237 | +\label{sec:identification:changes} | |
| 238 | + | |
| 239 | +The plan fixed the prompt text and schema, the baseline configuration and its hash, the cell and seed | |
| 240 | +lists, the outcome definitions, thresholds and sensitivity grid, the estimators, the budget tiers, the | |
| 241 | +model identifiers and price table, and the code revision recorded in every manifest. Four changes were | |
| 242 | +made after the plan and before any confirmatory run was analysed; each is logged with its date and | |
| 243 | +reason. First, the timing of fundamental information was restructured: in the | |
| 244 | +plan's economy the announced dividend revealed the cash-flow state exactly, so the public value | |
| 245 | +coincided with the latent value and private signals and news were redundant, leaving H1 unidentified; | |
| 246 | +in the frozen economy the deviation of the upcoming dividend is latent during the period, the public | |
| 247 | +value is a filtered estimate updated by news, and private signals concern the latent deviation. Because | |
| 248 | +no confirmatory run had been analysed, this is the pre-registered economy rather than a deviation, and | |
| 249 | +the noisy dividends originally proposed for the same purpose were not used. Second, shocks are applied | |
| 250 | +after the dividend step and the latent jump was reduced from 40 to 10 cents, about $+2.2\,\%$ of value, | |
| 251 | +so that a lagged adjustment is not counted as a 5\,\% bubble; thresholds were unchanged. Third, two | |
| 252 | +leaks of common random numbers were closed: LLM slots now inherit the information set of the slot they | |
| 253 | +replace instead of drawing their own informed status, which had given the treated arm more private | |
| 254 | +information than the control, and the within-tick processing order was moved to its own stream so that | |
| 255 | +activation draws no longer diverge across arms. Fourth, the first commit marks the freeze; the pilot | |
| 256 | +ran concurrently with the confirmatory design on the same revision, its role confined to cost, validity | |
| 257 | +and variance reporting, and the seed count was set by the budget rather than by the pilot. Two | |
| 258 | +limitations follow from budget and environment rather than choice: the mini-only cell that would let the | |
| 259 | +two-model mix be decomposed net of composition was not run, so the mix is compared with the | |
| 260 | +single-model diverse-prompt cell only; and the Anthropic cells could not be run because the available | |
| 261 | +key was rejected, so the adapter is dry-run tested with the mock provider and no Claude result is | |
| 262 | +reported. | |
| 263 | + | |
| 264 | +\subsection{Assigned versus realized dose} | |
| 265 | +\label{sec:identification:dose} | |
| 266 | + | |
| 267 | +The treatment is defined on slots, but LLM agents decide once every 20 ticks at staggered offsets, | |
| 268 | +whereas rule-based agents are activated with probability $0.2$ to $0.5$ per tick. The realized share of | |
| 269 | +order flow originated by LLM agents is therefore much smaller than the slot share: in the pilot, a | |
| 270 | +25\,\% slot share produced about 4\,\% of orders and 12 to 16\,\% of traded volume. Every manifest | |
| 271 | +records both, and estimates are reported against the assigned dose with the realized dose alongside: | |
| 272 | +the assigned dose is what a deployer chooses, while the realized dose is itself an outcome, since an | |
| 273 | +agent that holds or returns an invalid decision originates no order. This asymmetry is why the fairness | |
| 274 | +cells exist: equal decision opportunities is the default, equal dollars is implemented by a per-run | |
| 275 | +budget cap, and matched latency removes the one speed disadvantage the LLM agents would otherwise | |
| 276 | +carry. | |
| 277 | + | |
| 278 | +\subsection{Internal and external validity} | |
| 279 | +\label{sec:identification:validity} | |
| 280 | + | |
| 281 | +Internal validity concerns inference inside the simulator. Its threats are provider non-determinism, | |
| 282 | +treated as part of the treatment variance, with exact replay only from the cache; cache replay across | |
| 283 | +cells, which turns identical early prompts into shared decisions across arms and whose share we report; | |
| 284 | +the one-sided invalid-decision rule, whose rate is itself an outcome; and model drift, bounded by | |
| 285 | +calling within a short window and recording the model snapshot. External validity is a different | |
| 286 | +matter and we do not claim it. The rule-based traders are documented heuristics and the LLM agents are | |
| 287 | +prompted policies without risk management, regulation or learning across runs; there is one asset in | |
| 288 | +the prompt, model time has no calendar mapping, and fees and latencies are stylized. The findings are | |
| 289 | +statements about this market mechanism and these model snapshots: signs and dose--response shapes may | |
| 290 | +transfer to markets in which many participants obtain their signals from a common model; magnitudes | |
| 291 | +will not. | |
modified
paper/sections/implementation.tex
+48 −1
@@ -1 +1,48 @@ | ||
| 1 | −% implementation — to be written | |
| 1 | +\section{Implementation and validation} | |
| 2 | +\label{sec:implementation} | |
| 3 | + | |
| 4 | +\paragraph{Architecture.} | |
| 5 | +The simulator is a pure-Python package (\code{market\_agents}, about 3,900 lines, Python~3.12) organised in six layers: \code{assets/} (dividend process and the two value benchmarks), \code{signals/} (news feed and shock schedule), \code{exchange/} (typed order records, the limit order book, the integer ledger, the engine with latency queue, fees, risk checks and halts, and the call auction), \code{agents/} (the \code{Observation} interface and the classical policies), \code{llm/} (decision schema, prompt builder, policy with validation, budget tracker, SQLite cache, frozen library, and the provider adapters), and \code{experiments/} (pydantic configuration, population builder, single-run \code{Simulation}, and the design runner), with \code{metrics/outcomes.py} computing every run-level outcome from the saved frames and \code{analysis/} holding the estimators and plots. The exchange is \emph{event-sourced}: every state change (\code{submit}, \code{accept}, \code{reject}, \code{trade}, \code{cancel}, \code{expire}, \code{halt}, \code{resume}, \code{dividend}, \code{margin\_call}, \code{liquidate}, \code{bankrupt}, \code{auction}, \code{shock}, \code{tick}) is appended to a sequence-numbered log with a flat scalar payload, and the engine exposes a hashable \code{snapshot} of all balances, positions, resting orders and counters.\footnote{\code{exchange/orders.py}, \code{Event}; \code{exchange/engine.py}, \code{Exchange.snapshot}. The full event log is written to \code{events.parquet} when \code{output.events} is on; by default a run saves the per-tick, trade, agent and LLM-decision frames.} All randomness flows from one master seed through named \code{SeedSequence} children (fundamentals, news, slots, assignment, LLM assignment, activation, tie-break, mock LLM, within-tick shuffle) plus one generator per agent, so a run is a deterministic function of (configuration, seed) up to the provider's sampling; two executions of the same classical configuration produce identical event logs, tick frames and trade frames. Every run writes a \code{manifest.json} with the configuration hash (first 16 hex digits of the SHA-256 of the canonical JSON configuration), the package version, the git revision (suffixed \code{-dirty} when the working tree had uncommitted changes), the seed and stream names, the trader and realised LLM shares of traders, orders and volume, the number and validity rate of LLM decisions, token and USD usage by model, the provider and model identifiers, the prompt version, the full price table used by the budget tracker, the treatment labels, the shock log, the number of trades, halts and bankruptcies, and the wall-clock time.\footnote{\code{experiments/runner.py}, \code{Simulation.\_finish}; \code{utils/hashing.py}.} | |
| 6 | + | |
| 7 | +\paragraph{Test suite.} | |
| 8 | +The package ships 24 tests in three groups.\footnote{\code{tests/unit}, \code{tests/property}, \code{tests/integration}; run with \code{make test}.} \emph{Unit tests} cover the book (price--time priority, limit orders never cross beyond their price, market orders walk the book and stop when it is empty, cancel and depth), the engine (a fill moves exactly the notional and fees, latency orders arrivals, each rejection reason, cancel releases the reservation, a market buy is capped by cash, dividends preserve conservation, a halt rejects new orders, a margin breach generates a liquidation), the LLM layer (lenient parsing versus strict schema, each validation rule, the budget cap halting calls) and the configuration override semantics. \emph{Property tests} use Hypothesis to generate random streams of up to 120 intents from six agents with partial activations, cancels, a maker rebate and a short time-to-live, and assert after every processed tick that the book is uncrossed and indexed consistently, that the ledger identities \eqref{eq:conservation} hold, that each account's reserved cash and lots equal the sums over its resting orders, and that no position is negative; a second property asserts that two executions of the same stream yield identical snapshots and identical event logs (determinism and replay). \emph{Integration tests} run the full simulation: an end-to-end classical run with 100 agents writing all artefacts, bit-for-bit determinism across runs, common random numbers across \code{llm\_share} (identical latent value paths and identical endowments and latencies of every non-replaced slot), a mock-LLM run whose realised shares and validity rate are within the expected bands, the \emph{zero-trade edge case} (a population of inactive market makers produces 30 ticks with a well-defined midpoint and no trades), and the logging and application of each shock kind. | |
| 9 | + | |
| 10 | +\paragraph{Computational performance.} | |
| 11 | +Table~\ref{tab:scale} reports the wall-clock time of a 600-tick baseline run as the population grows from 100 to 10,000 traders with the per-type activity scaled by $100/N$ so that order flow per tick is constant.\footnote{\code{scripts/11\_scale\_benchmark.py}; numbers from \code{results/calibration/scale\_benchmark.json}, single process on an Apple-silicon (arm64) laptop. The benchmark was produced at the calibration stage, before the final revision of the dividend timing; the engine code it exercises is unchanged.} A run takes 0.6~s at $N=100$ (three seeds: 0.57, 0.58, 0.58~s), 2.7~s at $N=1{,}000$ and 23.7~s at $N=10{,}000$, i.e.\ roughly linear in $N$ at fixed order flow, with 13,300--15,400 orders and 10,300--12,200 trades per run. The 480-run classical confirmatory design (16 cells $\times$ 30 seeds) completes in about one minute on six worker processes. Live LLM cells are bound by provider latency: with 25 LLM agents in four staggered batches a run makes about 690 calls at 2.5--3.2~s each, so a $\varphi=0.25$ run takes 6--8 minutes with eight concurrent requests (Appendix~\ref{app:repro}). | |
| 12 | + | |
| 13 | +\begin{table}[t] | |
| 14 | +\centering | |
| 15 | +\caption{Computational performance of the simulator at constant order flow} | |
| 16 | +\label{tab:scale} | |
| 17 | +\small | |
| 18 | +\input{tables/tab_scale} | |
| 19 | +\begin{minipage}{0.95\linewidth}\footnotesize\vspace{2pt} | |
| 20 | +Baseline configuration, 600 ticks, activities scaled by $100/N$; generated by \code{scripts/50\_tables.py} from \code{results/calibration/scale\_benchmark.json}. Mean over seeds. | |
| 21 | +\end{minipage} | |
| 22 | +\end{table} | |
| 23 | + | |
| 24 | +\paragraph{Calibration.} | |
| 25 | +The classical baseline was calibrated once, before any LLM run, by a sweep over four behavioural parameters: fundamentalist aggressiveness $\{10,20,40\}$ lots per percent of mispricing, the combined trend-plus-leveraged share $\{0.10,0.15,0.25\}$ (split 60/40 between the two types and rebalanced against noise traders), the noise traders' market-order probability $\{0.3,0.6\}$ and the market makers' quote size $\{10,20\}$ lots---36 cells $\times$ 10 seeds $=360$ runs of 600 ticks.\footnote{\code{scripts/10\_calibration\_sweep.py}; cell means in \code{results/calibration/sweep\_summary.csv}.} The pre-stated criteria were those of the stylized-facts literature \citep{cont2001}: first-order return autocorrelation close to zero, excess kurtosis above~3, positive volatility clustering (positive sum of the first ten autocorrelations of absolute returns), a quoted spread of 10--30 basis points, and a tracking error $\overline{|P/V-1|}$ near 1\%. The retained cell (aggressiveness 20, trend-plus-leveraged share 0.10, market-order probability 0.6, quote size 20) has ACF$_1=0.015$, variance ratio VR(5) $=1.30$, excess kurtosis 26.6, $\sum_{1}^{10}\mathrm{ACF}|r|=2.71$, tick volatility 30~bp, quoted spread 24.1~bp, depth 141 lots, price impact 15.6~bp per 100 lots, 0.8 bubble episodes and a maximum drawdown of 5.2\% on average over ten seeds. The alternative with market-order probability 0.3 produced spreads of 6--12~bp but tracking errors of 2--3\% and ACF$_1$ of 0.2--0.3; the aggressiveness-40 cells reduced the tracking error below 1\% at the cost of positive return autocorrelation and larger drawdowns. The retained mix (25/6/10/50/5/4\%) and the three parameters are frozen in \code{configs/baseline.yaml} and were not revisited after the LLM pilot. | |
| 26 | + | |
| 27 | +\paragraph{Stylized facts at the frozen revision.} | |
| 28 | +Table~\ref{tab:stylized_facts} summarises the 30 control runs of the classical confirmatory design (seeds 101--130) at the frozen code revision. Returns of the midpoint are uncorrelated at lag one (mean ACF$_1=-0.007$) with a variance ratio near one (VR(5) $=1.18$), heavy-tailed (excess kurtosis 20.4) and volatility-clustered ($\sum_1^{10}\mathrm{ACF}|r|=2.02$); the quoted spread averages 20.7~bp and the effective spread 30.4~bp, depth within five levels is 139 lots, price impact is 10.8~bp per 100 lots, the tracking error is 0.63\% and no run contains a bubble episode under the pre-specified 5\%/10-tick rule. Market makers earn 4.9\% over the run and the wealth Gini coefficient stays at 0.285. These are the properties against which every treatment is compared. | |
| 29 | + | |
| 30 | +\begin{table}[t] | |
| 31 | +\centering | |
| 32 | +\caption{Stylized facts of the classical baseline (control cell, 30 seeds)} | |
| 33 | +\label{tab:stylized_facts} | |
| 34 | +\small | |
| 35 | +\input{tables/tab_stylized_facts} | |
| 36 | +\begin{minipage}{0.95\linewidth}\footnotesize\vspace{2pt} | |
| 37 | +Outcomes on ticks $100$--$599$ of the \code{control} cell of \code{configs/classical\_confirmatory.yaml}; generated by \code{scripts/50\_tables.py} from \code{results/runs/classical\_confirmatory/outcomes.parquet}. | |
| 38 | +\end{minipage} | |
| 39 | +\end{table} | |
| 40 | + | |
| 41 | +\paragraph{LLM adapters.} | |
| 42 | +Three provider adapters share one interface, \code{complete(system, user, model, \dots)}~$\to$~\code{ProviderResult} (text, input, cached-input, output and reasoning tokens, latency, model, request id, error).\footnote{\code{llm/providers/}.} The \emph{OpenAI} adapter calls the Responses API with the frozen system prompt as \code{instructions}, the snapshot as the single user input, \code{max\_output\_tokens} $=1{,}200$ (raised from 400 because reasoning tokens count against the cap), \code{text.format} $=$ \{\code{json\_schema}, name \code{trading\_decision}, \code{strict: true}\} with the schema of Appendix~\ref{app:prompts}, \code{store=False} so that no response is retained in the account's response store, \code{reasoning.effort} $=$ \code{low} for the gpt-5 and o-series models (temperature is never sent to them), and \code{prompt\_cache\_key} $=$ \code{ma-v1-<prompt\_id>} so that the provider can reuse the cached prefix across agents sharing a persona; the client is configured with a 60-second timeout and three retries, and rate-limit, status and connection errors are returned as error strings rather than raised. The \emph{Anthropic} adapter calls the Messages API with the system prompt marked \code{cache\_control: ephemeral}, \code{output\_config.format} $=$ \{\code{json\_schema}, schema\} and \code{output\_config.effort} $=$ \code{low}, no \code{thinking} parameter, \code{max\_tokens} $=\max(1{,}200, 256)$, and maps \code{stop\_reason = refusal} to an invalid decision; it is implemented and dry-run tested but, because the key available on 2026-10-27 is rejected (HTTP~401), no Claude decision enters the reported results. The \emph{mock} adapter parses the midpoint, public value, public estimate, private signal, holdings and free cash out of the user message and applies a seeded value rule (buy or sell when $|\hat V/P-1|>0.5\%$, 60\% limit and 40\% market orders) with a 5\% share of deliberately malformed answers (half non-JSON, half schema-invalid) and an optional common tilt (\code{herd\_bias}); its tokens are word counts priced at zero. It is used for dry runs, tests and the mock cells, and is not evidence about LLM behaviour. | |
| 43 | + | |
| 44 | +\paragraph{Budget and cost accounting.} | |
| 45 | +A \code{BudgetTracker} per run prices every call from the table of USD per million tokens stored in the configuration (input, cached input, output) and reproduced in the manifest; an unknown model id is priced at the most expensive known output price so that the cap is conservative. The cap is checked before each call; once reached, the policy is halted for the rest of the run and every subsequent decision becomes a logged hold with error \code{budget\_halt}. Cached hits are counted (\code{cached\_hits}) but contribute no tokens and no cost; failures and invalid decisions are counted separately. Optional caps on total tokens and on decision opportunities per agent (\code{decision\_cap}) implement the fairness designs. Before any live design, \code{ma budget} enumerates the calls of every cell $\times$ seed (number of LLM slots $\times\lfloor(T-\texttt{warmup}/2)/20\rfloor$) and prices them at a stated token profile.\footnote{\code{llm/budget.py}; \code{cli.py}, \code{budget}.} | |
| 46 | + | |
| 47 | +\paragraph{Decision log.} | |
| 48 | +Each decision appends one row to \code{llm\_decisions.parquet}: tick, agent id, provider, model, prompt id, validity flag and reason, the parsed action, side, order type, quantity, limit price, confidence and rationale, input, cached-input, output and reasoning tokens, latency in seconds, USD cost, whether it was a cache hit, the provider error string, the first 16 hex digits of the SHA-256 of the concatenated system and user prompts, the provider request id, the seven-dimensional feature vector of the observation used by the frozen library, and the limit price relative to the midpoint.\footnote{\code{llm/policy.py}, \code{LLMDecisionRecord}.} Raw prompts are not stored in the decision log (they are reconstructible from the saved run and are stored verbatim in the SQLite cache together with the raw response text and token usage). | |
modified
paper/sections/limitations.tex
+63 −1
@@ -1 +1,63 @@ | ||
| 1 | −% limitations — to be written | |
| 1 | +\section{Limitations} | |
| 2 | +\label{sec:limitations} | |
| 3 | + | |
| 4 | +The results of this paper are causal statements about a simulator. Every limitation below bounds | |
| 5 | +what they can say about actual markets, and several bound what they can say about the simulator itself. | |
| 6 | + | |
| 7 | +\paragraph{Internal validity.} | |
| 8 | +The confirmatory design uses seven matched seeds per live cell. With paired inference this gives | |
| 9 | +80\,\% power only for standardized effects of roughly $d \approx 1.2$ (Section~\ref{sec:identification}); | |
| 10 | +smaller effects are reported with their confidence intervals and must be read as inconclusive rather | |
| 11 | +than absent. The seed count was fixed by the monetary cap on API calls (about 40~USD for the whole | |
| 12 | +project), not by a pilot variance estimate, and the pilot ran concurrently with the confirmatory | |
| 13 | +design on the same frozen code revision; the pilot therefore informs cost, validity and realized-dose | |
| 14 | +reporting but did not feed back into the design. The thresholds that define bubbles and crashes were | |
| 15 | +pre-registered, and the control economy exhibits a floor effect at the crash threshold (no crashes in | |
| 16 | +30 control seeds), so crash frequencies can only increase under treatment; the sensitivity grid in | |
| 17 | +Section~\ref{sec:robustness} reports how the conclusions move with the thresholds. | |
| 18 | + | |
| 19 | +\paragraph{Provider coverage.} | |
| 20 | +All live cells use one provider family (OpenAI, \code{gpt-5.4-nano} as the primary model and | |
| 21 | +\code{gpt-5.4-mini} in the two-model and equal-budget arms). The Anthropic adapter is implemented and | |
| 22 | +exercised with the mock provider, but the key available when the experiments were run was rejected by | |
| 23 | +the API, so no Claude-populated market is reported. The cross-provider comparison that the research | |
| 24 | +questions call for is therefore reduced to a within-provider comparison of two model sizes, and the | |
| 25 | +``shared-model'' mechanism is tested through prompt diversity and the model mix rather than through | |
| 26 | +genuinely different foundation-model families. The two-model arm is compared with the single-model | |
| 27 | +diverse-prompt arm only; the mini-only cell needed to decompose a model-mix effect into a composition | |
| 28 | +effect and a diversity effect was not run. | |
| 29 | + | |
| 30 | +\paragraph{What the LLM agents are.} | |
| 31 | +An LLM agent here is a small reasoning model answering a short, fixed prompt every twenty ticks with a | |
| 32 | +strictly validated JSON action. It has no tools, no retrieval, no fine-tuning and, outside the memory | |
| 33 | +and channel arms, no state. The rationale field is logged but is never treated as evidence about the | |
| 34 | +model's internal reasoning. Decision frequency is deliberately separated from event frequency: an LLM | |
| 35 | +agent makes 27~decisions in a 600-tick run while classical agents act on roughly one tick in five, so | |
| 36 | +the realized share of orders and volume is far below the assigned share of trader slots; both are | |
| 37 | +reported and the dose--response is read on the assigned share only. Provider outputs are not | |
| 38 | +deterministic across repeated calls; recorded decisions are cached and replayed exactly, and cache hits | |
| 39 | +are counted separately from online calls. The frozen nearest-neighbour library used to scale the | |
| 40 | +population to 1,000 traders is a non-adaptive approximation of recorded behaviour and is reported as a | |
| 41 | +separate arm, never pooled with online runs. | |
| 42 | + | |
| 43 | +\paragraph{The economy.} | |
| 44 | +The exchange is a partial-equilibrium sandbox: dividends, discount rate, endowments and noise trading | |
| 45 | +are exogenous; there is no entry, no learning across runs, no short selling by default, and the | |
| 46 | +market makers follow a bounded inventory rule rather than an optimizing strategy. The stylized model of | |
| 47 | +Section~\ref{sec:theory} shares these restrictions: linear order submission, a competitive risk-neutral | |
| 48 | +market maker, Gaussian primitives and a fixed pricing schedule in the feedback extension. Its | |
| 49 | +propositions are statements about that model; the simulator tests their comparative statics, not their | |
| 50 | +closed forms. The classical population was calibrated to reproduce a short list of stylized facts | |
| 51 | +(near-zero return autocorrelation, fat tails, volatility clustering, spreads of 10--30 basis points) at | |
| 52 | +$N = 100$, and the same activity rates produce a somewhat different microstructure at $N = 1{,}000$ and | |
| 53 | +$N = 10{,}000$ unless the activity rate is scaled; all penetration experiments are run at $N = 100$. | |
| 54 | + | |
| 55 | +\paragraph{External validity.} | |
| 56 | +Simulated LLM traders are not institutions, and the market here has no regulation, no intermediaries | |
| 57 | +beyond five quoting agents, no overnight, and no cross-market linkages beyond the two-asset arm. Nothing | |
| 58 | +in the paper should be read as evidence about how deployed trading systems behave. The claim is narrower: | |
| 59 | +within a transparent, replayable economy, raising the share of traders driven by one language model | |
| 60 | +changes price discovery and coordination in the direction, and through the channel, that a | |
| 61 | +correlated-error model predicts, and those changes can be undone or amplified by interventions that | |
| 62 | +real markets can also implement. Whether the same mechanism operates at scale in live markets is an | |
| 63 | +empirical question this design cannot answer. | |
modified
paper/sections/literature.tex
+206 −256
@@ -4,271 +4,222 @@ | ||
| 4 | 4 | \section{Related literature} |
| 5 | 5 | \label{sec:literature} |
| 6 | 6 | |
| 7 | −The paper sits at the intersection of six literatures: agent-based and heterogeneous-agent models of | |
| 8 | −financial markets; the microstructure of double auctions and the measurement of price discovery; | |
| 9 | −information aggregation with correlated signals, herding and feedback trading; liquidity spirals, | |
| 10 | −fire sales and the measurement of contagion; algorithmic trading, flash crashes, algorithmic | |
| 11 | −monoculture and algorithmic collusion; and, closest to our question, the rapidly growing work that | |
| 12 | −places large language model (LLM) agents in economic experiments and market simulations. We review | |
| 13 | −each in turn and close with a statement of what the present paper adds and where it overlaps with | |
| 14 | −existing work. | |
| 7 | +The paper draws on six literatures: agent-based models of financial markets; double-auction | |
| 8 | +microstructure and the measurement of price discovery; information aggregation with correlated | |
| 9 | +signals, herding and feedback; liquidity spirals, fire sales and contagion; algorithmic trading, | |
| 10 | +monoculture and algorithmic collusion; and, closest to our question, LLM agents in economic experiments | |
| 11 | +and market simulations. We close with what this paper adds and where it overlaps with existing work. | |
| 15 | 12 | |
| 16 | −\subsection{Agent-based and heterogeneous-agent models of financial markets} | |
| 13 | +\subsection{Agent-based and heterogeneous-agent models} | |
| 17 | 14 | \label{sec:literature:abm} |
| 18 | 15 | |
| 19 | −Our baseline population of rule-based traders descends from agent-based computational finance, which | |
| 20 | −showed that interacting heterogeneous rules reproduce the fat tails, volatility clustering and bubbles | |
| 21 | −of real markets \citep{lebaron2006}. In the Santa Fe artificial stock market the speed of belief | |
| 22 | −updating governs whether prices track fundamentals or display excess volatility and technical-trading | |
| 23 | −regimes \citep{lebaron1999}; in adaptive belief systems, discrete-choice switching between predictors | |
| 24 | −produces increasingly complex price dynamics as the intensity of choice rises \citep{brock1997}, and | |
| 25 | −the share of trend followers determines whether the fundamental price is stable \citep{brock1998}. | |
| 26 | −Interacting noise traders who switch between optimistic, pessimistic and fundamentalist states generate | |
| 27 | −scaling laws and clustered volatility endogenously \citep{lux1999,lux2000}; herd behaviour among | |
| 28 | −speculators yields bubbles and crashes in a mean-field model of contagious optimism \citep{lux1995}; and | |
| 29 | −recruitment-type opinion dynamics generate persistent herding states without any informational reason | |
| 30 | −\citep{kirman1993}. These mechanisms matter for us because they describe what a \emph{classical} | |
| 31 | −population does on its own, before any LLM is introduced, and because trend following and herding are | |
| 32 | −precisely the channels through which a correlated LLM order flow can be amplified. | |
| 33 | − | |
| 34 | −Closer to our exchange design, \citet{chiarella2002} simulate a double auction populated by | |
| 35 | −fundamentalist, chartist and noise traders and reproduce order-book regularities, and | |
| 36 | −\citet{chiarella2009} show that heterogeneous rules in a limit-order book generate fat tails, | |
| 37 | −volatility clustering and order-flow autocorrelation, with the chartist share driving excess | |
| 38 | −volatility. Leverage in an agent-based model produces fat tails and clustered volatility through | |
| 39 | −synchronized margin-driven selling \citep{thurner2012}, which is the closest antecedent of our leverage | |
| 40 | −treatment. Multi-agent reinforcement-learning markets reproduce the same stylized facts | |
| 41 | −\citep{lussange2021} and are a precursor of LLM-populated markets. The surveys of \citet{hommes2006} | |
| 42 | −and \citet{hommes2021} summarize the laboratory and agent-based evidence that expectation heterogeneity | |
| 43 | −drives aggregate instability; we follow their lead in treating an LLM policy as one more expectation | |
| 44 | −type. \citet{farmer2009} argue that agent-based models are the appropriate tool for out-of-equilibrium | |
| 45 | −dynamics and for policy counterfactuals that cannot be solved analytically; \citet{axtell2025} | |
| 46 | −identify validation, calibration and scale as the open frontiers, which our matched-seed design and | |
| 47 | −power analysis address directly. For calibration and validation we use the moment set of structural | |
| 48 | −stochastic volatility models \citep{franke2012} and the catalogue of stylized facts: heavy tails | |
| 49 | −\citep{mandelbrot1963,cont2001}, conditional heteroskedasticity \citep{engle1982,bollerslev1986}, the | |
| 50 | −long memory of absolute returns \citep{ding1993}, and the long memory of order-flow signs coexisting | |
| 51 | −with nearly efficient prices \citep{lillo2004}. | |
| 52 | − | |
| 53 | −\subsection{Double auctions, zero intelligence and the measurement of price discovery} | |
| 16 | +Our rule-based population descends from agent-based computational finance, in which interacting | |
| 17 | +heterogeneous rules reproduce fat tails, volatility clustering and bubbles \citep{lebaron2006}. In the | |
| 18 | +Santa Fe artificial stock market the speed of belief updating governs whether prices track fundamentals | |
| 19 | +or display excess volatility and technical-trading regimes \citep{lebaron1999}; in adaptive belief | |
| 20 | +systems, switching between predictors produces increasingly complex dynamics as the intensity of choice | |
| 21 | +rises \citep{brock1997}, and the share of trend followers determines whether the fundamental price is | |
| 22 | +stable \citep{brock1998}. Noise traders switching between optimistic, pessimistic and fundamentalist | |
| 23 | +states generate scaling laws and clustered volatility endogenously \citep{lux1999,lux2000}, contagious | |
| 24 | +optimism yields bubbles and crashes \citep{lux1995}, and recruitment dynamics generate persistent | |
| 25 | +herding without any informational reason \citep{kirman1993}. These are the channels through which a | |
| 26 | +correlated LLM order flow can be amplified by a classical population. Closer to our exchange, | |
| 27 | +\citet{chiarella2002} reproduce order-book regularities with fundamentalist, chartist and noise traders | |
| 28 | +in a double auction, and \citet{chiarella2009} show that heterogeneous rules in a limit-order book | |
| 29 | +generate fat tails, volatility clustering and order-flow autocorrelation, with the chartist share | |
| 30 | +driving excess volatility. Leverage in an agent-based model produces fat tails and clustered volatility | |
| 31 | +through synchronized margin-driven selling \citep{thurner2012}, the closest antecedent of our leverage | |
| 32 | +treatment, and multi-agent reinforcement-learning markets reproduce the same facts | |
| 33 | +\citep{lussange2021}. Following the surveys of \citet{hommes2006} and \citet{hommes2021}, we treat an | |
| 34 | +LLM policy as one more expectation type; following \citet{farmer2009} and \citet{axtell2025}, we use | |
| 35 | +the agent-based method for counterfactuals that cannot be solved analytically and address its open | |
| 36 | +frontiers---validation, calibration, scale---with matched seeds and a power analysis. Validation | |
| 37 | +targets are the moment set of \citet{franke2012} and the stylized facts of heavy tails | |
| 38 | +\citep{mandelbrot1963,cont2001}, conditional heteroskedasticity \citep{engle1982,bollerslev1986} and the | |
| 39 | +long memory of absolute returns \citep{ding1993}. | |
| 40 | + | |
| 41 | +\subsection{Double auctions, zero intelligence and price-discovery measurement} | |
| 54 | 42 | \label{sec:literature:lob} |
| 55 | 43 | |
| 56 | −The continuous double auction is the institution we study, and its experimental pedigree is long: | |
| 57 | 44 | \citet{smith1962} showed that double auctions with induced values converge to competitive equilibrium |
| 58 | −with few traders, and \citet{smith1988} showed that experimental asset markets with known dividend | |
| 59 | −streams nevertheless produce bubbles and crashes; our dividend-paying asset and bubble metrics follow | |
| 60 | −that design. The zero-intelligence tradition established that market structure, not trader | |
| 61 | −rationality, drives allocative efficiency \citep{gode1993}, a point reviewed by \citet{ladley2012}. A | |
| 62 | −zero-intelligence model of the continuous double auction predicts spread and price impact from | |
| 63 | −order-flow rates alone \citep{farmer2005}, and the statistical theory of \citet{smith2003} ties | |
| 64 | −spread, depth and price diffusion to the same parameters. These results fix the benchmark against | |
| 65 | −which any effect of ``intelligence'' must be measured: a change in the composition of a population can | |
| 66 | −move liquidity merely by changing order-flow rates, which is why we report realized order and volume | |
| 67 | −shares alongside the assigned treatment. On the empirical side we rely on the order-book facts of | |
| 68 | −\citet{bouchaud2002}, the Markovian book model of \citet{cont2010}, the linear relation between | |
| 69 | −order-flow imbalance and short-horizon price changes \citep{cont2014}, the survey of | |
| 70 | −\citet{gould2013}, and the treatment of impact and the slow digestion of order flow in | |
| 71 | −\citet{bouchaud2018}. Our event-driven exchange, with per-agent latency and a price--time matching | |
| 72 | −engine, follows the design of ABIDES \citep{byrd2020}, and our realism checklist is that of | |
| 73 | −\citet{vyetrenko2020}. | |
| 74 | − | |
| 75 | −The outcome variables are standard measures of price discovery and market quality. The price-impact | |
| 76 | −coefficient $\lambda=\Cov(v,Y)/\Var(Y)$ of \citet{kyle1985} and the adverse-selection spread of | |
| 77 | −\citet{glosten1985} tie liquidity to the information content of order flow. \citet{hasbrouck1991} | |
| 78 | −measures that content by the permanent price impact in a VAR of quote revisions and trades; | |
| 79 | −\citet{hasbrouck1993} decomposes the transaction price into an efficient random-walk component and a | |
| 80 | −pricing error whose standard deviation is a market-quality metric; \citet{hasbrouck1995} attributes | |
| 81 | −price discovery across venues through information shares, which we adapt to LLM versus rule-based | |
| 82 | −order flow. Variance ratios reject the random walk under autocorrelation \citep{lo1988}, the effective | |
| 83 | −spread can be inferred from serial covariance \citep{roll1984}, and illiquidity can be measured as | |
| 84 | −absolute return per unit of volume \citep{amihud2002}. \citet{bai2016} define informativeness by how | |
| 85 | −well prices forecast future fundamentals, and \citet{bond2012} argue that informativeness is the | |
| 86 | −welfare-relevant object because real decisions feed on prices. \citet{hendershott2014} separate | |
| 87 | −transitory price pressure from permanent information effects, which justifies our separation of the | |
| 88 | −pricing error from the efficient-price variance. Because the simulator observes the latent value, we | |
| 89 | −can compute these statistics against the truth rather than against a proxy, which is the main | |
| 90 | −advantage of a simulated economy for this question \citep{fama1970}. | |
| 91 | − | |
| 92 | −\subsection{Information aggregation with correlated signals, herding and feedback} | |
| 45 | +with few traders, and \citet{smith1988} that experimental asset markets with known dividend streams | |
| 46 | +nevertheless produce bubbles and crashes; our dividend-paying asset and bubble metrics follow that | |
| 47 | +design. Zero-intelligence traders constrained by budgets reach near-full allocative efficiency, so | |
| 48 | +market structure rather than rationality drives efficiency \citep{gode1993,ladley2012}, and a | |
| 49 | +zero-intelligence model of the double auction predicts spread and price impact from order-flow rates | |
| 50 | +alone \citep{farmer2005,smith2003}. This fixes the benchmark against which any effect of | |
| 51 | +``intelligence'' must be read: changing the composition of a population can move liquidity merely by | |
| 52 | +changing order-flow rates, which is why we report realized order and volume shares alongside the | |
| 53 | +assigned treatment. The book is parametrized from the empirical regularities in \citet{bouchaud2002}, \citet{cont2010}, | |
| 54 | +\citet{cont2014}, \citet{gould2013} and \citet{bouchaud2018}; the event-driven exchange with per-agent | |
| 55 | +latency follows ABIDES \citep{byrd2020} and the realism checklist is that of \citet{vyetrenko2020}. | |
| 56 | + | |
| 57 | +The outcomes are standard measures of price discovery. The price-impact coefficient | |
| 58 | +$\lambda=\Cov(v,Y)/\Var(Y)$ of \citet{kyle1985} and the adverse-selection spread of \citet{glosten1985} | |
| 59 | +tie liquidity to the information in order flow; \citet{hasbrouck1991} measures that information by the | |
| 60 | +permanent price impact in a VAR of quotes and trades, \citet{hasbrouck1993} decomposes the transaction | |
| 61 | +price into an efficient random-walk component and a pricing error, and \citet{hasbrouck1995} | |
| 62 | +attributes price discovery through information shares, which we adapt to LLM versus rule-based flow. | |
| 63 | +Variance ratios test the random walk \citep{lo1988}, the effective spread follows from serial | |
| 64 | +covariance \citep{roll1984}, and illiquidity is absolute return per unit of volume \citep{amihud2002}. | |
| 65 | +\citet{bai2016} define informativeness by how well prices forecast future fundamentals, | |
| 66 | +\citet{bond2012} argue that it is the welfare-relevant object because real decisions feed on prices, | |
| 67 | +and \citet{hendershott2014} separate transitory price pressure from permanent information effects, | |
| 68 | +which justifies separating the pricing error from the efficient-price variance. Because the simulator | |
| 69 | +observes the latent value, these statistics are computed against the truth rather than a proxy, which | |
| 70 | +is how we operationalize the efficiency notions of \citet{fama1970}. | |
| 71 | + | |
| 72 | +\subsection{Correlated signals, herding and feedback} | |
| 93 | 73 | \label{sec:literature:info} |
| 94 | 74 | |
| 95 | −The theoretical core of the paper is the aggregation of \emph{correlated} private signals. | |
| 96 | −\citet{hellwig1980} shows that competitive prices aggregate dispersed private signals imperfectly | |
| 97 | −because of noise supply, and \citet{grossman1980} that prices cannot be fully informationally | |
| 98 | −efficient when information is costly. With strategic informed traders, \citet{foster1996} show that | |
| 99 | −the correlation structure of signals, not only their precision, governs trading intensity and price | |
| 100 | −informativeness, and \citet{back2000} that more correlated information leads to faster revelation. | |
| 101 | −\citet{goldstein2015} show that information diversity across traders is complementary, so that a loss | |
| 102 | −of diversity reduces informativeness about some components of value; \citet{dugast2018} show that | |
| 103 | −abundant cheap data can lower informativeness by crowding out costly deep information, a close parallel | |
| 104 | −to shared-model crowding; and \citet{kondor2012} warn that public information can raise disagreement | |
| 105 | −through higher-order beliefs, so that shared inputs do not trivially homogenize behaviour. | |
| 106 | −\citet{pasquariello2007} ties information heterogeneity among strategic speculators to contagion and | |
| 107 | −shows that homogeneity reduces it. Our stylized model (Section~\ref{sec:theory}) borrows the | |
| 108 | −correlated-error structure of these papers and reinterprets the common component as the signature of a | |
| 109 | −shared foundation model or prompt family. | |
| 110 | − | |
| 111 | −Herding supplies the behavioural counterpart. Sequential decision-makers who follow their predecessors | |
| 112 | −destroy information aggregation \citep{banerjee1992}, informational cascades become more likely as | |
| 113 | −signals become more correlated \citep{bikhchandani1992}, reputational concerns make managers mimic one | |
| 114 | −another \citep{scharfstein1990}, and short-horizon speculators herd on the same information source | |
| 115 | −even when it is unrelated to fundamentals \citep{froot1992}. The buy-side imbalance statistic of | |
| 116 | −\citet{lakonishok1992} is the standard empirical measure of institutional herding, and we compute it | |
| 117 | −for LLM cohorts. Feedback trading closes the loop: the share of trend followers determines the | |
| 118 | −stability of the fundamental price in heterogeneous-belief models \citep{brock1998}, and | |
| 119 | −contagious optimism among speculators produces bubbles and crashes \citep{lux1995}. Our feedback | |
| 120 | −extension (Proposition~\ref{prop:feedback}) is a mechanical version of this amplification. | |
| 121 | − | |
| 122 | −\subsection{Liquidity spirals, fire sales, leverage and contagion measurement} | |
| 75 | +The theoretical core of the paper is the aggregation of \emph{correlated} private signals. Competitive | |
| 76 | +prices aggregate dispersed signals imperfectly because of noise supply \citep{hellwig1980} and cannot | |
| 77 | +be fully efficient when information is costly \citep{grossman1980}. With strategic informed traders, | |
| 78 | +the correlation structure of signals, not only their precision, governs trading intensity and | |
| 79 | +informativeness \citep{foster1996}, and more correlated information leads to faster revelation | |
| 80 | +\citep{back2000}. Information diversity across traders is complementary, so a loss of diversity | |
| 81 | +reduces informativeness about some components of value \citep{goldstein2015}; abundant cheap data can | |
| 82 | +lower informativeness by crowding out costly deep information \citep{dugast2018}, a close parallel to | |
| 83 | +shared-model crowding; public information can raise disagreement through higher-order beliefs, so | |
| 84 | +shared inputs do not trivially homogenize behaviour \citep{kondor2012}; and information heterogeneity | |
| 85 | +among speculators generates contagion while homogeneity reduces it \citep{pasquariello2007}. Our | |
| 86 | +stylized model (Section~\ref{sec:theory}) borrows this correlated-error structure and reinterprets the | |
| 87 | +common component as the signature of a shared foundation model. Herding supplies the behavioural | |
| 88 | +counterpart: sequential decision-makers who follow predecessors destroy information aggregation | |
| 89 | +\citep{banerjee1992}, cascades become more likely as signals become more correlated | |
| 90 | +\citep{bikhchandani1992}, reputational concerns make managers mimic one another | |
| 91 | +\citep{scharfstein1990}, and short-horizon speculators herd on the same information source even when it | |
| 92 | +is unrelated to fundamentals \citep{froot1992}; the buy-side imbalance statistic of | |
| 93 | +\citet{lakonishok1992} is the standard measure and we compute it for LLM cohorts. Feedback trading | |
| 94 | +closes the loop, since the share of trend followers governs the stability of the fundamental price | |
| 95 | +\citep{brock1998}; Proposition~\ref{prop:feedback} is a mechanical version of this amplification. | |
| 96 | + | |
| 97 | +\subsection{Liquidity spirals, fire sales and contagion} | |
| 123 | 98 | \label{sec:literature:spirals} |
| 124 | 99 | |
| 125 | −The leverage treatment and the H3 and H6 hypotheses draw on the literature on funding constraints. | |
| 126 | −\citet{brunnermeier2009} show that market liquidity and funding liquidity reinforce each other through | |
| 127 | −margins, producing liquidity spirals; our margin-call mechanism follows this logic. Liquidation values | |
| 128 | −fall when potential buyers are themselves constrained \citep{shleifer1992}, constrained arbitrageurs can | |
| 129 | −amplify rather than correct mispricing \citep{shleifer1997}, forced sales push prices below | |
| 130 | −fundamentals and propagate losses \citep{shleifer2011}, and financially constrained arbitrageurs may | |
| 131 | −under-provide liquidity \citep{gromb2002}. Endogenous margins amplify booms and crashes in the leverage | |
| 132 | −cycle, with leverage caps as a policy lever \citep{geanakoplos2010}, and procyclical intermediary | |
| 133 | −leverage amplifies price moves \citep{adrian2010}. In the agent-based setting, \citet{thurner2012} | |
| 134 | −obtain fat tails and clustered volatility from synchronized margin-driven selling. Across assets, | |
| 135 | −losses of convergence traders propagate through reduced risk-bearing capacity \citep{kyle2001}, | |
| 136 | −distressed selling induces endogenous correlation \citep{cont2013}, fire-sale spillovers scale with | |
| 137 | −common exposures and leverage \citep{greenwood2015}, cross-market rebalancing transmits shocks between | |
| 138 | −assets with unrelated fundamentals \citep{kodres2002}, and dense networks are robust to small shocks | |
| 139 | −but fragile to large ones \citep{acemoglu2015}. For measurement we use the spillover index of | |
| 140 | −\citet{diebold2009}, its generalized directional form \citep{diebold2012} and its network | |
| 141 | −interpretation \citep{diebold2014}, and we heed the warning of \citet{forbes2002} that correlation-based | |
| 142 | −contagion tests must adjust for heteroskedasticity. | |
| 143 | − | |
| 144 | −\subsection{Algorithmic trading, flash crashes, monoculture and algorithmic collusion} | |
| 100 | +Hypotheses H3 and H6 draw on the literature on funding constraints. Market liquidity and funding | |
| 101 | +liquidity reinforce each other through margins, producing liquidity spirals \citep{brunnermeier2009}; | |
| 102 | +our margin-call mechanism follows this logic. Liquidation values fall when potential buyers are | |
| 103 | +themselves constrained \citep{shleifer1992}, constrained arbitrageurs can amplify rather than correct | |
| 104 | +mispricing \citep{shleifer1997} and may under-provide liquidity \citep{gromb2002}, forced sales push | |
| 105 | +prices below fundamentals and propagate losses \citep{shleifer2011}, endogenous margins amplify the | |
| 106 | +leverage cycle with leverage caps as a policy lever \citep{geanakoplos2010}, and procyclical | |
| 107 | +intermediary leverage amplifies price moves \citep{adrian2010}. Across assets, losses propagate through reduced risk-bearing capacity \citep{kyle2001}, distressed | |
| 108 | +selling induces endogenous correlation \citep{cont2013}, fire-sale spillovers scale with common | |
| 109 | +exposures and leverage \citep{greenwood2015}, rebalancing transmits shocks between unrelated assets | |
| 110 | +\citep{kodres2002}, and dense networks are fragile to large shocks \citep{acemoglu2015}. For measurement we use the spillover index of \citet{diebold2009}, its | |
| 111 | +generalized directional form \citep{diebold2012} and network interpretation \citep{diebold2014}, and we | |
| 112 | +heed the warning of \citet{forbes2002} that correlation-based contagion tests must adjust for | |
| 113 | +heteroskedasticity. | |
| 114 | + | |
| 115 | +\subsection{Algorithmic trading, monoculture and algorithmic collusion} | |
| 145 | 116 | \label{sec:literature:algo} |
| 146 | 117 | |
| 147 | −The evidence on algorithmic and high-frequency trading is, on average, favourable to liquidity and | |
| 148 | −price discovery: algorithmic trading improves liquidity \citep{hendershott2011}, high-frequency traders | |
| 149 | −trade in the direction of permanent price changes and against transitory pricing errors | |
| 150 | −\citep{brogaard2014}, and algorithmic strategies in foreign exchange improve price efficiency even | |
| 151 | −though they are more correlated with each other than human trades \citep{chaboud2014}. That last | |
| 152 | −finding is the one we take most seriously: correlation among algorithms is a documented fact, and its | |
| 153 | −consequences depend on what the algorithms are correlated \emph{on}. High-frequency market makers | |
| 154 | −behave as new market makers with tight inventory control \citep{menkveld2013}, a feature we build into | |
| 155 | −the market-maker policy; \citet{menkveld2016} reviews the speed, adverse-selection and liquidity | |
| 156 | −consequences, and \citet{foucault2016} show how fast traders reacting to common news signals affect | |
| 157 | −informativeness and liquidity. The darker evidence concerns stress. During the Flash Crash, | |
| 158 | −high-frequency traders did not trigger the crash but their synchronized inventory management amplified | |
| 159 | −it \citep{kirilenko2017}; order-flow toxicity rose before the crash and liquidity provision withdrew | |
| 160 | −when flow became toxic \citep{easley2011}; the August 2007 quant unwind revealed that common strategies | |
| 161 | −produce simultaneous deleveraging \citep{khandani2011}; and ultrafast extreme events proliferate in a | |
| 162 | −machine ecology operating beyond human response time \citep{johnson2013}. \citet{budish2015} show that | |
| 163 | −continuous-time trading creates a latency arms race with correlated sniping and propose frequent batch | |
| 164 | −auctions, which motivates our latency-matching treatment and call-auction robustness arm. | |
| 165 | − | |
| 166 | −The algorithmic-monoculture literature gives the homogenization mechanism a name. \citet{kleinberg2021} | |
| 167 | −show that when many decision-makers rely on the same algorithm, outcomes can be worse even if that | |
| 168 | −algorithm is individually more accurate; \citet{bommasani2021} identify homogenization as a | |
| 169 | −structural risk of foundation models, and \citet{bommasani2022} document outcome homogenization | |
| 170 | −across deployers of shared models. On the output side, generative AI raises individual creativity but | |
| 171 | −reduces collective diversity \citep{doshi2024}, standard LLM outputs show diminished diversity of | |
| 172 | −thought relative to human samples \citep{park2024brm}, and LLM assistance homogenizes human ideation | |
| 173 | −\citep{anderson2024}; conversely, ensembles of twelve LLMs match human crowd forecasting accuracy | |
| 174 | −\citep{schoenegger2024}, which illustrates the diversity benefit our H4 tests in a market. The | |
| 175 | −algorithmic-collusion literature shows that learning algorithms can coordinate without being told to: | |
| 176 | −Q-learning pricing algorithms learn tacit collusion \citep{calvano2020,klein2021}, algorithmic pricing | |
| 177 | −raised margins in German retail gasoline \citep{assad2024}, asymmetric adoption softens competition | |
| 178 | −\citep{brown2023}, LLM pricing agents reach supracompetitive prices with a sensitivity to prompt | |
| 179 | −wording \citep{fish2024}, and LLM sellers in continuous double auctions collude more when they can | |
| 180 | −communicate \citep{agrawal2025}. In securities markets, \citet{colliard2026} show that Q-learning market | |
| 181 | −makers learn adverse-selection pricing but quote markups that shrink with the number of algorithms, | |
| 182 | −and \citet{dou2025} show that reinforcement-learning informed speculators in a Kyle-type market sustain | |
| 183 | −supracompetitive profits through price-trigger strategies or homogenized learning biases, lowering | |
| 184 | −price informativeness and liquidity. \citet{dou2025} is the nearest theoretical neighbour of our | |
| 185 | −mechanism; the difference is that their channel is strategic, whereas ours is a shared-error structure | |
| 186 | −in signals, and that they have no language models. | |
| 187 | − | |
| 188 | −The policy literature has already drawn the systemic conclusion. \citet{gensler2020} argue that deep | |
| 189 | −learning increases uniformity and interconnectedness because models built on the same data generate | |
| 190 | −correlated predictions; \citet{danielsson2022} show that AI in risk management can create endogenous | |
| 191 | −systemic risk through common models and procyclical responses; the \citet{fsb2024} warns that common AI | |
| 192 | −models and data sources could raise correlations in trading, lending and pricing; | |
| 193 | −\citet{danielsson2025} model AI as amplifying existing vulnerabilities and making crises faster; | |
| 194 | −\citet{aldasoro2025} list monoculture and correlation among the financial-stability concerns of an | |
| 195 | −intelligent financial system; and \citet{meng2026} propose a unified model in which the AI adoption | |
| 118 | +The average evidence on algorithmic trading is favourable: it improves liquidity | |
| 119 | +\citep{hendershott2011}, high-frequency traders trade with permanent price changes and against | |
| 120 | +transitory errors \citep{brogaard2014}, and algorithmic strategies in foreign exchange improve price | |
| 121 | +efficiency even though they are more correlated with each other than human trades | |
| 122 | +\citep{chaboud2014}---the finding we take most seriously, since correlation among algorithms is a | |
| 123 | +documented fact whose consequences depend on what they are correlated \emph{on}. High-frequency | |
| 124 | +market makers keep tight inventory control \citep{menkveld2013}, a feature of our market-maker policy; | |
| 125 | +\citet{menkveld2016} reviews the speed and adverse-selection consequences and \citet{foucault2016} show | |
| 126 | +how fast traders reacting to common news affect informativeness and liquidity. Under stress the picture darkens: during the Flash Crash, high-frequency traders did not | |
| 127 | +trigger the crash but their synchronized inventory management amplified it \citep{kirilenko2017}; | |
| 128 | +order-flow toxicity rose and liquidity withdrew when flow became toxic \citep{easley2011}; the August | |
| 129 | +2007 quant unwind showed common strategies deleveraging simultaneously \citep{khandani2011}; and | |
| 130 | +ultrafast extreme events proliferate in a machine ecology beyond human response time | |
| 131 | +\citep{johnson2013}. \citet{budish2015} show that continuous trading creates a latency arms race with | |
| 132 | +correlated sniping and propose frequent batch auctions, which motivates our matched-latency treatment | |
| 133 | +and call-auction robustness arm. | |
| 134 | + | |
| 135 | +Algorithmic monoculture names the homogenization mechanism: when many decision-makers rely on the same | |
| 136 | +algorithm, outcomes can be worse even if it is individually more accurate \citep{kleinberg2021}; | |
| 137 | +foundation models carry a homogenization risk because downstream systems inherit the same failure modes | |
| 138 | +\citep{bommasani2021}, and shared models mis-serve the same individuals across deployers | |
| 139 | +\citep{bommasani2022}. Generative AI raises individual creativity but reduces collective diversity | |
| 140 | +\citep{doshi2024}, standard LLM outputs show diminished diversity of thought \citep{park2024brm}, and | |
| 141 | +LLM assistance homogenizes human ideation \citep{anderson2024}; conversely, ensembles of twelve LLMs | |
| 142 | +match human crowd forecasting accuracy \citep{schoenegger2024}, the diversity benefit that H4 tests in a | |
| 143 | +market. Learning algorithms also coordinate without being told to: Q-learning pricing algorithms learn | |
| 144 | +tacit collusion \citep{calvano2020,klein2021}, algorithmic pricing raised retail gasoline margins | |
| 145 | +\citep{assad2024}, asymmetric adoption softens competition \citep{brown2023}, LLM pricing agents reach | |
| 146 | +supracompetitive prices with a sensitivity to prompt wording \citep{fish2024}, and LLM sellers in double | |
| 147 | +auctions collude more when they can communicate \citep{agrawal2025}. In securities markets, Q-learning | |
| 148 | +market makers quote markups that shrink with the number of algorithms \citep{colliard2026}, and | |
| 149 | +reinforcement-learning speculators in a Kyle-type market sustain supracompetitive profits through | |
| 150 | +price-trigger strategies or homogenized learning biases, lowering informativeness and liquidity | |
| 151 | +\citep{dou2025}. \citet{dou2025} is the nearest theoretical neighbour of our mechanism: their channel is | |
| 152 | +strategic, ours is a shared-error structure in signals, and they have no language models. The policy | |
| 153 | +literature has drawn the systemic conclusion: deep learning increases uniformity because models built | |
| 154 | +on the same data generate correlated predictions \citep{gensler2020}; common AI models and data sources | |
| 155 | +could raise correlations in trading, lending and pricing \citep{fsb2024}; AI amplifies existing | |
| 156 | +vulnerabilities and makes crises faster \citep{danielsson2025}; monoculture and correlation are among | |
| 157 | +the stability concerns of an intelligent financial system \citep{aldasoro2025}; and the AI adoption | |
| 196 | 158 | share and signal correlation raise systemic coupling convexly, with 13F evidence of tail-loss |
| 197 | −amplification. None of these provides a controlled experiment; the present paper is an attempt to | |
| 198 | −supply one. | |
| 159 | +amplification \citep{meng2026}. None of these provides a controlled experiment. | |
| 199 | 160 | |
| 200 | 161 | \subsection{LLM agents in economics and markets} |
| 201 | 162 | \label{sec:literature:llm} |
| 202 | 163 | |
| 203 | −\paragraph{LLMs as simulated subjects.} | |
| 204 | −\citet{horton2023} proposed using LLMs as simulated economic agents to replicate classic behavioural | |
| 205 | −experiments at low cost; \citet{aher2023} showed that LLMs can simulate multiple humans and replicate | |
| 206 | −human-subject studies with fidelity that varies by model and task; and \citet{argyle2023} showed that | |
| 207 | −conditioning on demographic backstories produces ``silicon samples'' matching sub-population response | |
| 208 | −distributions. GPT choices in budgetary tasks score higher on rationality than humans and are largely | |
| 209 | −stable across demographic prompts \citep{chen2023pnas}; chatbot choices in behavioural games fall within | |
| 210 | −the human distribution and are more cooperative than the median human \citep{mei2024}; in repeated | |
| 211 | −games LLMs cooperate well but coordinate poorly \citep{akata2025}; and expectations elicited from an | |
| 212 | −LLM reading newspaper articles track professional forecasters and are extrapolative | |
| 213 | −\citep{bybee2023}. \citet{manning2024} automate hypothesis generation and testing with LLM subjects, and | |
| 214 | −\citet{immorlica2024} propose a framework in which generative agents are economic agents with new | |
| 215 | −primitives such as prompts and context. Generative agents with memory, reflection and planning produce | |
| 216 | −believable emergent social behaviour \citep{park2023}, social-network simulations reproduce | |
| 217 | −information propagation \citep{gao2023s3}, and EconAgent produces realistic macroeconomic dynamics | |
| 218 | −\citep{li2024econagent}. \citet{anthis2025} conclude that LLM social simulations are a promising | |
| 219 | −method provided that validity concerns---homogeneity, sycophancy, drift---are addressed; homogeneity is | |
| 220 | −the concern this paper turns into a treatment. | |
| 164 | +\paragraph{Simulated subjects.} | |
| 165 | +\citet{horton2023} proposed LLMs as simulated economic agents for replicating behavioural experiments | |
| 166 | +at low cost; \citet{aher2023} showed that they can simulate multiple humans with fidelity varying by | |
| 167 | +model and task, and \citet{argyle2023} that demographic backstories yield silicon samples matching | |
| 168 | +sub-population responses. GPT choices score higher on rationality than humans and are stable across | |
| 169 | +demographic prompts \citep{chen2023pnas}, chatbot choices in behavioural games fall within the human | |
| 170 | +distribution \citep{mei2024}, LLMs cooperate well but coordinate poorly in repeated games | |
| 171 | +\citep{akata2025}, and LLM-elicited expectations are extrapolative \citep{bybee2023}. Generative agents | |
| 172 | +with memory and reflection produce believable emergent behaviour \citep{park2023,gao2023s3}, EconAgent | |
| 173 | +produces realistic macroeconomic dynamics \citep{li2024econagent}, and \citet{immorlica2024} treat | |
| 174 | +generative agents as economic agents with prompts as primitives \citep[see also][]{manning2024}. | |
| 175 | +\citet{anthis2025} conclude that LLM social simulations are promising provided that homogeneity, | |
| 176 | +sycophancy and drift are addressed; homogeneity is the concern this paper turns into a treatment. | |
| 221 | 177 | |
| 222 | 178 | \paragraph{Single-agent trading and backtests.} |
| 223 | −ChatGPT-derived sentiment on headlines predicts next-day returns \citep{lopezlira2023chatgpt}. | |
| 224 | −FinMem \citep{yu2023finmem}, TradingGPT \citep{li2023tradinggpt} and FinAgent \citep{zhang2024finagent} | |
| 225 | −evaluate one LLM agent, or a small team with layered memory and distinct characters, by backtesting on | |
| 226 | −historical prices. In these designs the market does not react to the agent, so nothing can be learned | |
| 227 | −about equilibrium prices, and \citet{lopezlira2025memorization} show that such backtests are | |
| 228 | −contaminated by memorization of the historical period. Both points motivate a synthetic economy with | |
| 229 | −an endogenous price and no history to memorize. | |
| 179 | +ChatGPT-derived headline sentiment predicts next-day returns \citep{lopezlira2023chatgpt}. FinMem | |
| 180 | +\citep{yu2023finmem}, TradingGPT \citep{li2023tradinggpt} and FinAgent \citep{zhang2024finagent} | |
| 181 | +evaluate one agent, or a small team with layered memory, by backtesting on historical prices. The | |
| 182 | +market does not react to the agent, so nothing is learned about equilibrium prices, and such backtests | |
| 183 | +are contaminated by memorization of the historical period \citep{lopezlira2025memorization}---two | |
| 184 | +reasons for a synthetic economy with an endogenous price and no history to memorize. | |
| 230 | 185 | |
| 231 | 186 | \paragraph{LLM-populated market simulators.} |
| 232 | −A second generation places many LLM agents on a matching engine. ASFM couples LLM traders to a real | |
| 233 | −order-matching system and finds reactions to rate and inflation shocks consistent with economic priors | |
| 234 | −\citep{gao2024asfm}; StockAgent adds bulletin-board communication and reports that GPT- and | |
| 235 | −Gemini-driven agents differ systematically in trading behaviour \citep{zhang2024stockagent}; | |
| 236 | −TwinMarket scales to a social-media-connected population, reproduces bubbles and crashes, and reports | |
| 237 | −that agents of one model family are more homogeneous \citep{yang2025twinmarket}; FCLAgent combines | |
| 238 | −LLM-decided direction with rule-based sizing and reproduces path-dependent patterns absent from | |
| 239 | −conventional agent-based models \citep{hashimoto2025}; StockSim provides an open order-level simulator | |
| 240 | −with latency, slippage and price--time matching \citep{papadakis2025}; and \citet{li2026switch} find | |
| 241 | −that LLM agents' trading-style switching in a year-long simulation is only partially consistent with | |
| 242 | −behavioural-finance theory. \citet{lopezlira2025trade} is the closest in spirit: LLM agents in a | |
| 243 | −persistent order-book simulation adhere to assigned strategies, generate price discovery and bubbles, | |
| 244 | −and are analysed with partial-dependence-style perturbations. These papers establish that LLM markets | |
| 245 | −\emph{can} produce the patterns we study. They are descriptive in the sense that the population | |
| 246 | −composition is fixed, the information set of LLM agents is not held equal to that of the agents they | |
| 247 | −replace, and the fundamental value, the efficient price and the transaction price are not separated. | |
| 187 | +A second generation places many LLM agents on a matching engine. ASFM finds reactions to rate and | |
| 188 | +inflation shocks consistent with economic priors \citep{gao2024asfm}; StockAgent adds bulletin-board | |
| 189 | +communication and reports that GPT- and Gemini-driven agents differ systematically | |
| 190 | +\citep{zhang2024stockagent}; TwinMarket scales to a social-media-connected population, reproduces | |
| 191 | +bubbles and crashes, and reports that agents of one model family are more homogeneous | |
| 192 | +\citep{yang2025twinmarket}; FCLAgent reproduces path-dependent patterns absent from conventional | |
| 193 | +agent-based models \citep{hashimoto2025}; StockSim is an open order-level simulator with latency and | |
| 194 | +price--time matching \citep{papadakis2025}; and trading-style switching in a year-long simulation is only | |
| 195 | +partially consistent with behavioural-finance theory \citep{li2026switch}. \citet{lopezlira2025trade} is | |
| 196 | +closest in spirit: LLM agents in a persistent order book adhere to assigned strategies, generate price | |
| 197 | +discovery and bubbles, and are analysed with partial-dependence-style perturbations. These papers show | |
| 198 | +that LLM markets \emph{can} produce the patterns we study; they hold the population composition fixed, | |
| 199 | +do not equalize the information set of LLM agents with that of the agents they replace, and do not | |
| 200 | +separate the fundamental value, the efficient price and the transaction price. | |
| 248 | 201 | |
| 249 | 202 | \paragraph{Experimental-finance replications.} |
| 250 | −A third strand replicates laboratory markets with LLM subjects. In Smith--Suchanek--Williams-type | |
| 251 | −markets, LLM agents price near fundamentals and form only muted bubbles, with lower variance in their | |
| 252 | −strategy text than humans \citep{henning2025}. In a simulated call auction, LLM agents exhibit | |
| 253 | −disposition effects and extrapolative beliefs, and prompt interventions causally change bubble size | |
| 254 | −\citep{ouyang2026}. Across fifteen models, \citet{saxena2026} find behaviour ranging from fundamental | |
| 255 | −pricing to human-like bubbles, with mixed populations unstable and advanced models adapting to exploit | |
| 256 | −weaker ones. Replications of \citet{smith1962} find slower or absent convergence with model-family | |
| 257 | −differences \citep{struski2026}, and the trading institution alone moves the efficiency of identical | |
| 258 | −LLM agents from 56\,\% to 89\,\% \citep{chupilkin2026}. These studies answer the question of | |
| 259 | −behavioural fidelity---whether LLMs behave like humans---and their answers conflict. Our results do not | |
| 260 | −require fidelity: they require only that errors be correlated within a model. | |
| 203 | +In Smith--Suchanek--Williams-type markets, LLM agents price near fundamentals and form only muted | |
| 204 | +bubbles, with less variable strategy text than humans \citep{henning2025}; in a call auction they | |
| 205 | +exhibit disposition effects and extrapolative beliefs, and prompt interventions causally change bubble | |
| 206 | +size \citep{ouyang2026}; across fifteen models, behaviour ranges from fundamental pricing to human-like | |
| 207 | +bubbles, mixed populations are unstable, and advanced models adapt to exploit weaker ones | |
| 208 | +\citep{saxena2026}; replications of \citet{smith1962} converge more slowly or not at all, with | |
| 209 | +model-family differences \citep{struski2026}; and the institution alone moves the efficiency of | |
| 210 | +identical agents from 56\,\% to 89\,\% \citep{chupilkin2026}. These studies ask whether LLMs behave | |
| 211 | +like humans and their answers conflict. Our results do not require fidelity, only that errors be | |
| 212 | +correlated within a model. | |
| 261 | 213 | |
| 262 | 214 | \paragraph{Correlated behaviour from shared inputs.} |
| 263 | −The single paper that measures correlated trading from shared inputs is \citet{song2026}: overlap in | |
| 215 | +The one paper that measures correlated trading from shared inputs is \citet{song2026}: overlap in | |
| 264 | 216 | retrieved documents among retrieval-augmented trading agents raises mean pairwise action correlation |
| 265 | −from 0.04 to 0.41 and increases position volatility without raising profits. That design is a backtest | |
| 266 | −without market feedback, so it cannot speak to informativeness or liquidity. \citet{cho2025} document | |
| 267 | −peer conformity in LLM multi-agent systems, with the confidence gap and format of peer information | |
| 268 | −controlling its strength. In LLM re-creations of herding experiments, \citet{hansen2025} find that AI | |
| 269 | −agents rely more on private signals than humans but can be prompted to herd when maximizing profit. | |
| 270 | −Finally, LLM service behaviour drifts over months \citep{chen2024hdsr}, which is why we cache every | |
| 271 | −response and replay from a frozen decision library. | |
| 217 | +from 0.04 to 0.41 and increases position volatility without raising profits. That is a backtest without | |
| 218 | +market feedback, so it cannot speak to informativeness or liquidity. \citet{cho2025} document peer | |
| 219 | +conformity in LLM multi-agent systems, and \citet{hansen2025} find in re-created herding experiments | |
| 220 | +that AI agents rely more on private signals than humans but can be prompted to herd when maximizing | |
| 221 | +profit. LLM service behaviour drifts over months \citep{chen2024hdsr}, which is why every response is | |
| 222 | +cached and replayed from a frozen library. | |
| 272 | 223 | |
| 273 | 224 | \subsection{What this paper adds} |
| 274 | 225 | \label{sec:literature:adds} |
@@ -277,22 +228,21 @@ We should be candid about overlap. The hypothesis that shared models correlate t | ||
| 277 | 228 | is stated by \citet{gensler2020}, the \citet{fsb2024} and \citet{danielsson2025}, modelled as |
| 278 | 229 | homogenized learning biases by \citet{dou2025}, and measured in a backtest by \citet{song2026}. |
| 279 | 230 | Model-family homogeneity inside LLM markets has been observed by \citet{yang2025twinmarket}, |
| 280 | −\citet{zhang2024stockagent} and \citet{saxena2026}. LLM-populated order-book simulators exist and are | |
| 281 | −open source \citep{lopezlira2025trade,papadakis2025,gao2024asfm}. Our simulator is therefore not the | |
| 282 | −contribution; the design is. First, we treat the LLM penetration share as a randomized treatment | |
| 283 | −assigned to trader slots by an independent seed stream, so that matched pairs across the share share | |
| 284 | −every other stream---fundamentals, news, endowments, latencies, policy assignment, activation, | |
| 285 | −tie-breaks---and differences in outcomes are causal effects on the same market history | |
| 286 | −(Section~\ref{sec:identification}). Second, we separate three prices: the latent value known to the | |
| 287 | −simulator only, the public value implied by announced dividends and news, and the transaction price, so | |
| 288 | −that an information-efficiency loss can be distinguished from transitory pressure | |
| 289 | −\citep{hasbrouck1993,hendershott2014}. Third, we derive the shared-error mechanism in a Kyle--Hellwig | |
| 290 | −setting with a model-specific common error and an effective number of models, and then test it by | |
| 291 | −intervention---prompt diversity at equal share, a two-model mix, inherited information sets, memory, | |
| 292 | −communication cohorts---so that the effective number of models is manipulated rather than observed. | |
| 293 | −Fourth, we report the realized dose (the share of orders and volume actually originated by LLM agents) | |
| 294 | −alongside the assigned share, and we run fairness designs with equal decision opportunities, equal | |
| 295 | −dollar budgets and matched latency, because naive comparisons confound ``being an LLM'' with being slow | |
| 296 | −and infrequent. Fifth, the leverage interaction and the safeguard counterfactuals connect the LLM | |
| 231 | +\citet{zhang2024stockagent} and \citet{saxena2026}, and open LLM order-book simulators exist | |
| 232 | +\citep{lopezlira2025trade,papadakis2025,gao2024asfm}. Our simulator is not the contribution; the design | |
| 233 | +is. First, the LLM penetration share is a randomized treatment assigned to trader slots by an | |
| 234 | +independent seed stream, so that matched pairs share every other stream---fundamentals, news, | |
| 235 | +endowments, latencies, policy assignment, activation, tie-breaks---and paired differences are causal | |
| 236 | +effects on the same market history (Section~\ref{sec:identification}). Second, we separate three | |
| 237 | +prices---the latent value known to the simulator only, the public value implied by announced dividends | |
| 238 | +and news, and the transaction price---so that an efficiency loss can be distinguished from transitory | |
| 239 | +pressure \citep{hasbrouck1993,hendershott2014}. Third, we derive the shared-error mechanism in a | |
| 240 | +Kyle--Hellwig setting with a model-specific common error and an effective number of models, then test | |
| 241 | +it by intervention---prompt diversity at equal share, a two-model mix, inherited information sets, | |
| 242 | +memory, communication cohorts---so that the effective number of models is manipulated rather than | |
| 243 | +observed. Fourth, we report the realized dose, the share of orders and volume actually originated by | |
| 244 | +LLM agents, alongside the assigned share, and run fairness designs with equal decision opportunities, | |
| 245 | +equal dollar budgets and matched latency, because naive comparisons confound being an LLM with being | |
| 246 | +slow and infrequent. Fifth, the leverage interaction and safeguard counterfactuals connect the LLM | |
| 297 | 247 | literature to \citet{brunnermeier2009}, \citet{thurner2012} and \citet{kirilenko2017}, with thresholds, |
| 298 | 248 | estimators and seed counts fixed before the first confirmatory call. |
modified
paper/sections/model.tex
+140 −1
@@ -1 +1,140 @@ | ||
| 1 | −% model — to be written | |
| 1 | +\section{The experimental economy} | |
| 2 | +\label{sec:model} | |
| 3 | + | |
| 4 | +This section describes the simulated economy exactly as it is implemented in the \code{market\_agents} package.\footnote{Source files are cited in footnotes as \code{module.py}; all paths are relative to \code{src/market\_agents/}. Default parameter values are those of \code{configs/baseline.yaml}; every value is listed again in Appendix~\ref{app:hyperparameters}.} The economy has one risky asset (optionally $K\in\{2,\dots,5\}$ assets with a common factor), riskless cash, a continuous double auction with explicit latency, fees and risk limits, a population of trader slots filled by seven documented classical policies, and a treatment that replaces a fraction of those slots by a large-language-model (LLM) policy. Nothing in the simulator imposes an equilibrium: prices are whatever the order book produces. | |
| 5 | + | |
| 6 | +\paragraph{Time, units and accounting.} | |
| 7 | +Model time is a discrete tick $t=0,1,\dots,T-1$ with $T=600$ in the baseline; outcome statistics are computed on $t\ge 100$. There is no calendar mapping. Prices are integers in ticks (one tick $=1$~cent), quantities are integers in lots, and cash is an integer in cents. Every trade moves exactly $p\,q$ cents from buyer to seller and $q$ lots from seller to buyer; fees are integers computed as $\operatorname{round}(p\,q\cdot \text{bps}/10^4)$ and are paid to the exchange. After every fill and every dividend the ledger asserts the two conservation identities | |
| 8 | +\begin{equation} | |
| 9 | +\label{eq:conservation} | |
| 10 | +\sum_{i} C_{i,t} + F_t \;=\; \sum_i C_{i,0} + \sum_{s\le t} D^{\text{paid}}_s, | |
| 11 | +\qquad | |
| 12 | +\sum_i Q^{k}_{i,t} \;=\; \sum_i Q^{k}_{i,0}\quad (k=1,\dots,K), | |
| 13 | +\end{equation} | |
| 14 | +where $C_{i,t}$ is agent $i$'s cash, $F_t$ the cumulative fees collected by the exchange, $D^{\text{paid}}_s$ the cash injected by dividends and $Q^k_{i,t}$ the lots of asset $k$ held.\footnote{\code{exchange/accounts.py}, \code{Ledger.check\_conservation}; the check also verifies that reserved cash and reserved lots never exceed holdings for unleveraged agents.} The exchange never creates shares and there is no outside money other than dividends, so wealth is redistributed, not created, by trading. | |
| 15 | + | |
| 16 | +\paragraph{Dividends and the latent-timing convention.} | |
| 17 | +Asset $k$ pays a cash dividend every $\Delta=20$ ticks. Index dividend dates by $n=1,2,\dots$ (paid at $t=n\Delta$) and write | |
| 18 | +\begin{equation} | |
| 19 | +\label{eq:dividend} | |
| 20 | +D_n \;=\; \mu_{s_n} + x_n + J_n, | |
| 21 | +\qquad | |
| 22 | +x_{n+1} \;=\; \rho\, x_n + \sigma\, \varepsilon_{n+1},\quad \varepsilon_{n+1}\sim N(0,1), | |
| 23 | +\end{equation} | |
| 24 | +with mean dividend $\mu=50$~cents, persistence $\rho=0.98$ and innovation scale $\sigma=2$~cents per period. The regime $s_n\in\{0,1\}$ (a two-state Markov chain shifting $\mu$ by a factor $1+\texttt{mu\_shift}$) and the compound-Poisson jump $J_n$ are available but switched off in every design reported here ($\texttt{mu\_shift}=\texttt{p\_switch}=\texttt{jump\_prob}=0$). With $K>1$ assets the innovations load on a common factor, $\varepsilon_k = b_k f + \sqrt{1-b_k^2}\,u_k$, with $b_k=0.7$ in the two-asset cells. The convention that matters for information is the \emph{timing}: when $D_n$ is paid at $t=n\Delta$ the deviation $x_n$ becomes public, and the deviation of the \emph{next} dividend, $x_{n+1}$, is drawn immediately but remains latent until $t=(n+1)\Delta$.\footnote{\code{assets/fundamentals.py}, \code{Fundamentals.step} and \code{\_draw\_next}: the dividend is paid, \code{x\_revealed} is set, the public filter is updated, then $x_{n+1}$ is drawn. The initial deviation $x_1$ is latent from $t=0$.} During the period $n\Delta\le t<(n+1)\Delta$ the state of the economy is therefore $x_{n+1}$, and all information (news, private signals) is about this already-drawn but unannounced number. No agent ever observes a future innovation. | |
| 25 | + | |
| 26 | +\paragraph{Two value benchmarks.} | |
| 27 | +Let $r=0.25\%$ be the discount rate per dividend period. Discounting the expected dividend stream given the latent state gives the \emph{latent fundamental value} | |
| 28 | +\begin{equation} | |
| 29 | +\label{eq:V} | |
| 30 | +V_t \;=\; \frac{\mu}{r} + \frac{x_{n(t)+1}}{1+r-\rho}, | |
| 31 | +\qquad n(t)=\lfloor t/\Delta\rfloor, | |
| 32 | +\end{equation} | |
| 33 | +because $\mathbb{E}[x_{n+k}\mid x_{n+1}]=\rho^{k-1}x_{n+1}$ and $\sum_{k\ge1}\rho^{k-1}(1+r)^{-k}=1/(1+r-\rho)$. At the baseline $\mu/r=20{,}000$ cents, so $V\approx 200.00$ currency units and the price--dividend ratio is about~400; one cent of next-dividend deviation is worth $1/(1+r-\rho)\approx 44.4$ cents of value. The \emph{public value} applies the same closed form to the public posterior mean $\hat{x}_t$ of the latent deviation, | |
| 34 | +\begin{equation} | |
| 35 | +\label{eq:Vpub} | |
| 36 | +V^{\mathrm{pub}}_t \;=\; \frac{\mu}{r} + \frac{\hat{x}_t}{1+r-\rho}, | |
| 37 | +\end{equation} | |
| 38 | +where $\hat x_t$ is maintained by a Kalman filter: after the dividend at $n\Delta$ the prior is $\hat x=\rho x_n$ with variance $\Sigma=\sigma^2$ (the dividend reveals $x_n$ exactly when jumps are off), and each public news item $y$ with stated noise $\eta$ updates $\hat x\leftarrow \hat x + \kappa\,(y-\hat x)$, $\Sigma\leftarrow(1-\kappa)\Sigma$, $\kappa=\Sigma/(\Sigma+\max(\eta,0.1)^2)$.\footnote{\code{assets/fundamentals.py}, \code{public\_observe}; the regime and jump branches of \code{\_update\_public} are a two-state mixture filter and a Kalman step on a noisy dividend, unused in the reported designs.} Hence $V^{\mathrm{pub}}_t\neq V_t$ in general: the two coincide only at dividend dates, and they diverge most right after a latent shock. | |
| 39 | + | |
| 40 | +Four price concepts are kept apart throughout the paper. (i) The \emph{fundamental value} $V_t$ of \eqref{eq:V} is latent and is never shown to any agent; it is the benchmark for pricing error, bubbles and absorption. (ii) The \emph{efficient (public) price} $V^{\mathrm{pub}}_t$ of \eqref{eq:Vpub} is the value a risk-neutral agent would assign using public information only; it is shown to every agent. (iii) The \emph{transaction price} is the price of the last trade, in ticks. (iv) The \emph{quote midpoint} $P_t=(a_t+b_t)/2$ is the price used in every outcome statistic; when one side of the book is empty the engine falls back to the last trade price and then to the reference price.\footnote{\code{exchange/engine.py}, \code{Exchange.mid\_ticks}; in call-auction mode the midpoint is replaced by the last clearing price because the book may be crossed between auctions.} Deviations are measured as $\log P_t-\log V_t$ (and, as a robustness check, relative to $V^{\mathrm{pub}}_t$). | |
| 41 | + | |
| 42 | +\paragraph{Information.} | |
| 43 | +Three channels carry information about the latent deviation. \emph{Public news} arrives each tick with probability $0.02$ per asset. An item reports $y=x_{n+1}+\nu+\beta$, with credible noise $\nu\sim N(0,\eta^2)$, $\eta=1$~cent, and a bias $\beta=\pm10$~cents (random sign) with probability \code{misleading\_share} ($0$ in the baseline, $0.5$ in the misleading-news cell). Every item is rendered as one sentence (``Analyst note: next dividend expected $|y|$ cents above/below its long-run mean'') and updates the public filter with the \emph{stated} noise $\eta$ whether or not it is biased, so misleading items fool $V^{\mathrm{pub}}$ by construction.\footnote{\code{signals/news.py}, \code{NewsFeed.step}.} Agents see the items of the last 20 ticks. \emph{Private signals} are drawn at every activation of an informed agent, $s_i=x_{n+1}+\xi_i$, $\xi_i\sim N(0,\sigma^2/\pi_i)$, with precision $\pi_i=2$ (signal standard deviation $\sigma/\sqrt{2}\approx1.4$~cents) for the 30\% of fundamentalist slots that are informed and $\pi_i=0$ otherwise; the draw is about the current latent state, never about future innovations.\footnote{\code{assets/fundamentals.py}, \code{private\_signal}; a new draw is made at each activation from the agent's own random stream.} \emph{Announced dividends} reveal $x_n$ at each dividend date. All three channels, plus the order book, trade tape, own account and open orders, are packaged into a single \code{Observation} object that is the only interface between the market and any policy, classical or LLM.\footnote{\code{agents/base.py}; the LLM prompt is a deterministic rendering of the same object (Appendix~\ref{app:prompts}).} | |
| 44 | + | |
| 45 | +\paragraph{The exchange.} | |
| 46 | +Each asset trades on a continuous double auction with price--time priority.\footnote{\code{exchange/book.py} (matching), \code{exchange/engine.py} (latency, fees, risk, halts, event log).} Agents submit \emph{limit} orders (price in ticks), \emph{market} orders, and \emph{cancels} of their own resting orders. An order submitted at decision time $t$ by agent $i$ arrives at $t+\ell_i$, where the latency $\ell_i\sim U[0.05,1]$ tick is drawn once per slot and fixed for the run; the exchange processes arrivals in $(\text{arrival time},\ \text{sequence})$ order, so agents deciding at the same tick are ordered by latency and then by submission sequence. On arrival the engine checks, in order: the account is not frozen, the asset is not halted, $1\le q\le 10{,}000$ lots, and for limit orders a positive price within $\pm50\%$ of the current midpoint (price band). Limit bids reserve the notional plus the worst-case fee plus one cent per lot; market bids are capped to the largest quantity whose notional plus taker fee fits in free cash while walking the ask side; sells are clipped to free (unreserved) holdings and reserve them. A market order executes against the best quotes until filled or the opposite side is empty; the unfilled remainder is cancelled (immediate-or-cancel). Limit orders execute against crossing quotes and rest for the remainder. Partial fills are the norm. The taker pays $2$~basis points of notional, the maker pays nothing. Resting orders expire 50 ticks after submission. Short selling is disabled: a sell larger than free holdings is clipped, and a sell with no holdings is rejected.\footnote{\code{ExchangeConfig.allow\_short} and \code{Account.short\_allowed} both exist, but the population builder sets \code{short\_allowed=False} for every slot, so the \code{allow\_short} switch has no effect in the current code; see Appendix~\ref{app:hyperparameters}.} | |
| 47 | + | |
| 48 | +Three safeguards are implemented as treatments and are off in the baseline. A \emph{circuit breaker} halts an asset for 20 ticks when the last trade price has moved by more than 5\% relative to the midpoint 10 ticks earlier; resting orders are cancelled at the halt and arriving orders are rejected while halted. A \emph{position limit} rejects any order that would push $|{\rm position}+{\rm same\text{-}side\ resting}+q|$ above 300 lots. A \emph{periodic call auction} replaces continuous matching: orders rest without matching and every 5 ticks the book clears at the uniform price that maximises executable volume among limit prices within $\pm2\%$ of the last clearing price (ties go to the price nearest the reference); market orders are converted to band-edge limits for one auction and cancelled afterwards.\footnote{\code{exchange/call\_auction.py}; the later-submitted of two crossing resting orders is treated as the taker for fee purposes.} This last variant is a robustness check run with the mock provider only. | |
| 49 | + | |
| 50 | +\paragraph{Leverage, margin and bankruptcy.} | |
| 51 | +Unleveraged slots can only spend free cash. ``Leveraged'' slots have a cap $L=3$ on gross exposure over equity: their buying power is free cash plus $(L-1)\times$equity, and an order is rejected if the post-trade gross exposure (valued at the limit price or the midpoint) would exceed $L\times$equity. At the end of every tick the exchange marks every account to the midpoint. A leveraged account whose equity-to-gross-exposure ratio falls below the maintenance margin $m=0.25$ receives a margin call: all its resting orders are cancelled and a market order for $50\%$ of each position is submitted at the start of the next tick, tagged \code{liquidation} so that it bypasses the position and leverage checks. Any account with non-positive equity is declared bankrupt, frozen (no further orders accepted), and liquidated in full.\footnote{\code{exchange/engine.py}, \code{Exchange.margin\_check}, \code{\_liquidate}; liquidation orders are submitted by the runner at $t+1$.} Dividends on short positions would be charged, but shorts never arise. | |
| 52 | + | |
| 53 | +\paragraph{Classical policies.} | |
| 54 | +Trader slots are filled by seven heuristics, summarised in Table~\ref{tab:policies}; each sees only the \code{Observation} and returns at most a handful of intents.\footnote{\code{agents/classical.py}. Order sizes are capped by the policy's \code{max\_qty} and by what free cash or holdings allow (\code{\_affordable} divides free cash by $1.001\times$ the reference price).} Each classical agent is activated independently each tick with its type's probability (``activity''). Fundamentalists form a posterior value $\hat V_i=V^{\mathrm{pub}}+w_i\,(s_i-\hat x)/(1+r-\rho)$ with $w_i=\pi_i/(1+\pi_i)$ (so uninformed fundamentalists use $V^{\mathrm{pub}}$ exactly), trade when $|\hat V_i/P-1|$ exceeds 0.5\%, and size the order at 20 lots per percent of mispricing; a mispricing beyond twice the threshold makes the limit order marketable. Trend followers compare the mean midpoint over the last 5 ticks with the mean over the last 30 and trade in the direction of the difference once it exceeds 0.2\%; mean reverters trade against the $z$-score of the midpoint relative to its 40-tick mean when $|z|>1.5$. Noise traders are zero-intelligence-plus liquidity demanders in the spirit of \citet{gode1993}: random side, size $1+\mathrm{Geometric}(1/8)$, a market order with probability 0.6 or otherwise a limit order placed on the passive side at a distance $|N(0,(0.001\,P)^2)|$ from the best quote. Market makers are inventory-aware two-sided quoters: with inventory deviation $I$ from the endowment they quote around the reservation price $P\,(1-0.02\,I/200)$ with half-spread $\operatorname{clip}(8+\hat\sigma,1,60)$ ticks, where $\hat\sigma$ is the standard deviation of tick-to-tick midpoint changes over 30 ticks, 20 lots per side, re-quoting (cancelling all) at every activation; they stop quoting on one side when $|I|$ reaches 200 lots and withdraw entirely during a liquidity shock. They are endowed with five times the standard endowment and are never replaced by LLM agents. Leveraged traders run the trend rule with size 30 and market-order probability 0.7 on an account with $L=3$. Cross-asset arbitrageurs trade the residual of a 60-tick log-price regression between the two first assets when its $z$-score exceeds 1.5, and are inactive in single-asset runs. The baseline mix and the behavioural parameters were frozen by the calibration sweep of Section~\ref{sec:implementation}. | |
| 55 | + | |
| 56 | +\begin{table}[t] | |
| 57 | +\centering | |
| 58 | +\caption{Classical policies, baseline shares and parameters} | |
| 59 | +\label{tab:policies} | |
| 60 | +\small | |
| 61 | +\begin{threeparttable} | |
| 62 | +\begin{tabular}{@{}l r r p{4.7cm} p{4.1cm}@{}} | |
| 63 | +\toprule | |
| 64 | +Type & Share & Activity & Rule & Key parameters \\ | |
| 65 | +\midrule | |
| 66 | +Fundamentalist & 25\% & 0.20 & Trade toward $\hat V_i=V^{\mathrm{pub}}+w_i(s_i-\hat x)/(1+r-\rho)$ when $|\hat V_i/P-1|>$ threshold; marketable limit beyond $2\times$ threshold & threshold 0.5\%; 20 lots per 1\% mispricing; $p_{\rm mkt}=0.3$; max 50; improve 1 tick; 30\% informed, $\pi=2$ \\ | |
| 67 | +Trend follower & 6\% & 0.20 & Sign of $\bar P_{5}/\bar P_{30}-1$ when $|\cdot|>$ threshold; size scales with $\min(|s|/\text{thr},3)$ & threshold 0.2\%; size 20; $p_{\rm mkt}=0.6$; max 50 \\ | |
| 68 | +Mean reverter & 10\% & 0.20 & Fade $z=(P-\bar P_{40})/\mathrm{sd}_{40}$ when $|z|>$ threshold; limit at best quote $\pm1$ tick & threshold 1.5; size 15; $p_{\rm mkt}=0.2$; max 50 \\ | |
| 69 | +Noise trader & 50\% & 0.30 & Random side; size $1+\mathrm{Geom}(1/8)$; market order or passive limit at $|N(0,(10^{-3}P)^2)|$ from the best quote; cancels when $>3$ open orders & $p_{\rm mkt}=0.6$ (calibrated); max 40 \\ | |
| 70 | +Market maker & 5\% & 0.50 & Two-sided quotes around $P(1-0.02\,I/200)$, half-spread $\operatorname{clip}(8+\hat\sigma_{30},1,60)$ ticks; re-quotes every activation & 20 lots per side; max inventory 200; endowment $\times5$; never replaced \\ | |
| 71 | +Leveraged & 4\% & 0.20 & Trend rule on a leveraged account & size 30; $p_{\rm mkt}=0.7$; $L=3$; $m=0.25$; liquidation 50\% \\ | |
| 72 | +Cross-asset arb. & 0\% (5\% if $K=2$) & 0.20 & Pairs trade on the residual of a 60-tick log-price regression when $|z|>1.5$ & size 10; max 40; inactive if $K=1$ \\ | |
| 73 | +\bottomrule | |
| 74 | +\end{tabular} | |
| 75 | +\begin{tablenotes}\footnotesize | |
| 76 | +\item Shares and activities from \code{configs/baseline.yaml} (\code{population.mix}, \code{population.activity}, \code{policy\_kwargs}); rules and defaults from \code{agents/classical.py}. $P$ is the midpoint, $\bar P_w$ its mean over the last $w$ ticks, $p_{\rm mkt}$ the probability that an order is a market order. With $N=100$ the integer counts are 25/6/10/50/5/4. | |
| 77 | +\end{tablenotes} | |
| 78 | +\end{threeparttable} | |
| 79 | +\end{table} | |
| 80 | + | |
| 81 | +\paragraph{The LLM policy.} | |
| 82 | +An LLM-controlled slot decides every 20 ticks from $t=50$ onward, at one of four staggered offsets $\{0,5,10,15\}$ drawn per slot, so that 27--28 decisions are made per agent in a 600-tick run and roughly a quarter of the LLM population decides in each batch.\footnote{\code{experiments/runner.py}, \code{Simulation.run}: an LLM slot is active when $(t-\text{offset})\bmod 20=0$ and $t\ge \texttt{warmup}/2=50$; batches are sent to the provider through a thread pool of width 8.} Each decision is built from a canonical snapshot: the five best levels of each side of the book, best bid/ask, midpoint and last trade; the midpoint history over the last 60 ticks (sub-sampled to ten points in the prompt, with the window return and the standard deviation of tick returns); the last 30 trades (volume and buyer-initiated share); the last dividend, the public posterior $\hat x_t$ and $V^{\mathrm{pub}}_t$; the news items of the last 20 ticks (the last three are shown); the agent's own private signal and precision if it is informed; its cash, free cash, holdings and marked equity; and its open orders. The system prompt states the market mechanics (dividend interval, $\mu$, $r$, $\mu/r$, $\rho$, the $1/(1+r-\rho)$ conversion, tick, taker fee, time-to-live, no-short rule, the 50-lot cap and the decision interval) and asks for a JSON object with exactly eight keys: \code{action}~$\in\{$hold, place, cancel$\}$, \code{side}, \code{order\_type}, \code{quantity}, \code{limit\_price}, \code{cancel\_order\_ids}, \code{confidence} and a one-sentence \code{rationale}. The reply is parsed leniently (code fences and surrounding text are stripped) and validated strictly: a \code{place} must carry side, type and a quantity in $[1,50]$; a limit price must lie within $\pm20\%$ of the midpoint; a market order needs a non-empty opposite side; a buy must cost at most free cash at the reference price $\times1.001$; a sell is clipped to holdings net of resting sells and is invalid when nothing is free; a \code{cancel} must name at least one order the agent owns; trading must not be halted. Any parse, schema or feasibility failure, provider error, or budget halt becomes a \emph{hold} that is logged with its reason, so the estimand is intention-to-treat on the slot rather than on successful calls.\footnote{\code{llm/schema.py}, \code{llm/policy.py} (\code{to\_action}); the validation rules are reproduced in Appendix~\ref{app:prompts}.} Realised shares of LLM agents, orders and volume are reported for every run. | |
| 83 | + | |
| 84 | +Four cognition treatments change only the prompt. \emph{Personas}: the single ``generalist'' system prompt, or eight diverse personas (value, momentum, contrarian, liquidity, risk-averse, macro, quant, generalist) assigned cyclically to LLM slots. \emph{Memory}: the last 5 own decisions (action, side, quantity, type, price, rejection reason, the midpoint and equity at the time) are appended. \emph{Communication channel}: LLM slots are grouped in cohorts of 5 in slot order, and the last 5 rationales posted in the cohort are shown as ``unverified opinions''. \emph{Adaptive feedback}: the percentage change in equity since the last decision and its cumulative sum are appended. Provider adapters cover the OpenAI Responses API, the Anthropic Messages API, a seeded mock that parses the user message and applies a value rule with a 5\% share of deliberately invalid answers, and a \emph{frozen library} provider that returns the recorded decision of one of the five nearest neighbours of the current observation in a seven-dimensional standardised feature space (mispricing versus $V^{\mathrm{pub}}$, window momentum, signal, inventory and cash ratios, spread, open-order flag), rescaling the limit price to the current midpoint. The library is a non-adaptive, non-reasoning replay of recorded decisions, used only to scale the population at zero cost; runs that use it are labelled provider \code{library} and are never pooled with online runs. Identical (provider, model, prompt version, system, user) requests are served from a SQLite cache and counted separately as cached hits; a per-run USD cap (and optional token and decision caps) halts further calls, after which every decision is a logged hold.\footnote{\code{llm/cache.py}, \code{llm/budget.py}, \code{llm/library.py}, \code{llm/providers/}.} | |
| 85 | + | |
| 86 | +\paragraph{Endowments.} | |
| 87 | +Slot $i$ receives cash $C_i=\lfloor 2{,}000{,}000\,w_i\rfloor$ cents and $\operatorname{round}(100\,w_i)$ lots of each asset, where $w_i=\exp(0.3\,z_i)/\overline{\exp(0.3 z)}$, $z_i\sim N(0,1)$, is a lognormal wealth multiplier normalised to mean one across the population; at the initial price $\mu/r=200.00$ the endowment is split evenly between cash and stock. Market-maker slots receive five times these amounts. The initial reference price equals $\mu/r$, so the economy starts at the latent value when $x_1=0$. | |
| 88 | + | |
| 89 | +\paragraph{Population builder and matched replacement.} | |
| 90 | +Slot characteristics (wealth multiplier, latency, information draw) are generated from the \code{slots} stream; the classical policy deck---the integer counts implied by the mix---is shuffled by the \code{assign} stream and dealt down the wealth ranking, which stratifies policy types across wealth terciles.\footnote{\code{experiments/population.py}, \code{build\_population}; streams are spawned from the master seed by \code{numpy.random.SeedSequence} (\code{utils/rng.py}): \code{fundamentals}, \code{news}, \code{slots}, \code{assign}, \code{llm\_assign}, \code{activation}, \code{tiebreak}, \code{mock\_llm}, \code{shuffle}, plus one generator per agent.} The LLM treatment then replaces $\operatorname{round}(\varphi N)$ of the non-market-maker slots, chosen by the dedicated \code{llm\_assign} stream with quotas proportional to each policy type's count among eligible slots (remainder at random), so the composition of the replaced population is held constant along the dose and $\varphi=1$ means 95 of 100 slots. A replaced slot keeps its endowment, latency, leverage cap and---crucially---its information set: an LLM agent that replaces an informed fundamentalist receives the same private signal precision, and one that replaces any other type receives none.\footnote{\code{LLMCfg.informed\_share: null}; a number instead overrides the inherited status (information-equalisation variant). The matched-latency variant sets \code{latency.llm\_latency}.} Providers are allocated to LLM slots in proportion to \code{provider\_mix} and shuffled; prompt personas, stagger offsets and cohorts are assigned from the same stream. Because activation draws, within-tick ordering and all other streams are untouched by $\varphi$, a treatment and its control share the dividend path, the news, every classical agent's endowment, latency, activations and signals; the only difference is the policy of the replaced slots. | |
| 91 | + | |
| 92 | +\paragraph{Shocks.} | |
| 93 | +Scheduled events are applied after the dividend step of the tick at which they occur and are written to the event log so that matched pairs share their timing.\footnote{\code{experiments/runner.py}, \code{\_apply\_shocks}; \code{signals/news.py}, \code{Shock}.} A \emph{fundamental jump} adds \code{size} cents to the latent deviation $x_{n+1}$; the baseline applies $+10$~cents at $t=300$, which raises $V_t$ by $10/(1+r-\rho)\approx 4.44$ currency units ($+2.2\%$), while $V^{\mathrm{pub}}_t$ moves only when news reports the new state or the dividend at $t=320$ reveals it. A \emph{rate} shock adds \code{size} to $r$; a \emph{funding} shock multiplies every leverage cap by $1-\text{size}$ (0.5 in the leverage cells, i.e.\ $3\times\to1.5\times$); a \emph{liquidity} shock makes all market makers withdraw their quotes until the matching \code{liquidity\_end} event (60 ticks in the liquidity cell); a \emph{news burst} injects a credible item with noise 0.5 reporting \code{size} and updates the public filter; a \emph{placebo} is logged and changes nothing. The placebo at $t=450$ is present in every baseline-derived cell and the placebo-only cell replaces the jump with a second placebo. | |
| 94 | + | |
| 95 | +\paragraph{Implementation assumptions and their checks.} | |
| 96 | +Table~\ref{tab:assumptions} condenses the assumption register maintained with the code (\code{docs/assumptions.md}). Each row states an assumption as implemented and the pre-specified check that probes it; the register's identifiers are reproduced with the prefix ``I'' to avoid confusion with the theoretical Assumptions A1--A7 of Section~\ref{sec:theory}. | |
| 97 | + | |
| 98 | +\begin{small} | |
| 99 | +\begin{longtable}{@{}p{0.7cm} p{8.6cm} p{5.6cm}@{}} | |
| 100 | +\caption{Implementation assumptions (register \code{docs/assumptions.md}) and robustness checks} | |
| 101 | +\label{tab:assumptions}\\ | |
| 102 | +\toprule | |
| 103 | +ID & Assumption as implemented & Robustness check \\ | |
| 104 | +\midrule | |
| 105 | +\endfirsthead | |
| 106 | +\toprule | |
| 107 | +ID & Assumption as implemented & Robustness check \\ | |
| 108 | +\midrule | |
| 109 | +\endhead | |
| 110 | +\bottomrule | |
| 111 | +\endfoot | |
| 112 | +I1 & 600 ticks, outcomes on $t\ge100$; no calendar mapping & $T=1{,}000$ on the control \\ | |
| 113 | +I2 & Dividend every 20 ticks, eq.~\eqref{eq:dividend} with $\mu=50$c, $\rho=0.98$, $\sigma=2$c, $r=0.25\%$; one asset & $\rho\in\{0.95,0.98\}$, $\sigma\in\{1,2,4\}$ \\ | |
| 114 | +I3 & Benchmark = latent value $V_t$ of eq.~\eqref{eq:V}, risk neutral, constant $r$ & Deviations from $V^{\mathrm{pub}}$ and from the end-of-run mean price also reported \\ | |
| 115 | +I4 & Public value = Kalman posterior from dividends and news (timing convention above); $V^{\mathrm{pub}}\ne V$ within a period & Noisy-dividend variant (\code{jump\_prob} 1, \code{jump\_sigma} 4c) \\ | |
| 116 | +I5 & No look-ahead: signals and news concern the latent current state & Unit test of \code{private\_signal}; prompt built from \code{Observation} only \\ | |
| 117 | +I6 & Latent jump $+10$c at $t=300$ ($+2.2\%$ of $V$), placebo at $t=450$ & Sizes 10/20/40c; \code{news\_burst} variant \\ | |
| 118 | +I7 & Continuous double auction, price--time priority, limit/market/cancel, partial fills, IOC remainder, integer ticks and lots & Call auction every 5 ticks, $\pm2\%$ collar (mock only) \\ | |
| 119 | +I8 & Latency $U[0.05,1]$ per agent, fixed; arrival-order processing; LLM slots draw like others & Matched latency \code{llm\_latency} 0.05 \\ | |
| 120 | +I9 & Taker 2 bp, maker 0, fees leave the economy; conservation asserted after every fill & Taker 0 and 5 bp \\ | |
| 121 | +I10 & TTL 50 ticks; price band $\pm50\%$; max order 10,000 lots (policies cap at 40--50) & TTL 20/100 \\ | |
| 122 | +I11 & No short selling; buys limited by free (unreserved) cash & \code{allow\_short} variant (currently inert, see footnote in the exchange paragraph) \\ | |
| 123 | +I12 & Leverage only for leveraged slots: cap $3\times$, maintenance 25\%, forced sale of 50\% by market order next tick, bankrupt agents frozen & Cap $2\times/5\times$; maintenance 15\%/35\% \\ | |
| 124 | +I13 & Safeguards off by default; breaker 5\% over 10 ticks, 20-tick halt, resting cancelled; position limit 300 & Halt 3\%/8\%; limit 150/600 \\ | |
| 125 | +I14 & Five bounded inventory-skewed market makers (half-spread 8 ticks $+$ volatility, skew 2\% at 200 lots), endowed $\times5$, never replaced & \code{quote\_qty} 10/20; liquidity shock \\ | |
| 126 | +I15 & No rationality or equilibrium assumption: classical agents are documented heuristics, LLM agents output whatever the model returns & Calibration sweep (360 runs) \\ | |
| 127 | +I16 & Mix 25/6/10/50/5/4\% (fundamentalist/trend/mean-rev/noise/MM/leveraged), stratified by wealth; endowment $20{,}000+100$ shares $\times$ lognormal(0.3) & Trend share 10--25\% in the sweep; dispersion 0/0.6 \\ | |
| 128 | +I17 & Classical activation Bernoulli(0.2--0.5) per tick; LLM decisions every 20 ticks from $t=50$, staggered & Realised order/volume shares reported; \code{decision\_interval} 10 (mock) \\ | |
| 129 | +I18 & 30\% of fundamentalists informed ($\pi=2$); an LLM slot inherits the information of the slot it replaces & Stratum composition reported; \code{llm.informed\_share} 0 variant \\ | |
| 130 | +I19 & Stratified replacement, market makers excluded (95 eligible slots) & \code{llm\_replace: noise} at $\varphi=0.25$ (mock) \\ | |
| 131 | +I20 & LLM observation = canonical snapshot (5 levels, 60-tick history, 30 trades, last dividend, $V^{\mathrm{pub}}$, $\le3$ news, own signal, account, open orders); prompt v1 frozen & Prompt audit \\ | |
| 132 | +I21 & Invalid or infeasible decisions become hold (quantity, $\pm20\%$ price window, cash/holdings, owned cancel ids); sells clipped & Validity reported per cell; cells $<80\%$ flagged \\ | |
| 133 | +I22 & Provider sampling is not seeded; cached responses replay exactly; the library is a nearest-neighbour map, not an agent & Cache-hit share and online/replay flag per run \\ | |
| 134 | +I23 & Equal decision opportunities by default (27--28 per agent); equal USD via per-run \code{usd\_cap} & Fairness cell \\ | |
| 135 | +I24 & Midpoint is the price in every metric (fallback to last trade); effective spread uses the start-of-tick midpoint & \code{msd\_log} on last trade prices \\ | |
| 136 | +I25 & Bubble: $|P/V-1|>5\%$ for $\ge10$ ticks; crash: drop $\ge10\%$ from the trailing 20-tick max; absorption: $|P/V-1|<1\%$ for 5 ticks, censored at 100 & Grid 3/5/8\% and 7/10/15\% on saved ticks \\ | |
| 137 | +I26 & Coordination measured among LLM agents (sign correlation in 20-tick windows; synchrony per decision round) & Classical analogue from signed trades (\code{flow\_corr\_by\_policy}) \\ | |
| 138 | +I27 & Two-asset variant: factor loading 0.7; LLM prompt covers asset 0 only & Spillover reported from the LLM-traded to the classical-only asset \\ | |
| 139 | +\end{longtable} | |
| 140 | +\end{small} | |
modified
paper/sections/theory.tex
+4 −4
Large diff (26 lines) is not loaded by default to keep the page fast.Load diff
modified
results/figures/fig_baseline_paths.pdf
+0 −0
Binary file not shown.
modified
results/figures/fig_classical_lambda_bp_100.pdf
+0 −0
Binary file not shown.
modified
results/figures/fig_classical_mad_pct.pdf
+0 −0
Binary file not shown.
modified
results/figures/fig_classical_max_dd.pdf
+0 −0
Binary file not shown.
modified
results/figures/fig_classical_n_bubbles.pdf
+0 −0
Binary file not shown.
modified
results/figures/fig_classical_spread_bp.pdf
+0 −0
Binary file not shown.
modified
results/figures/fig_classical_time_to_absorb.pdf
+0 −0
Binary file not shown.
modified
results/figures/fig_contagion_excess.pdf
+0 −0
Binary file not shown.
modified
results/figures/fig_contagion_spillover.pdf
+0 −0
Binary file not shown.
modified
results/figures/fig_wealth_depth.pdf
+0 −0
Binary file not shown.
modified
results/tables/tab_classical_effects.csv
+20 −20
Large diff (86 lines) is not loaded by default to keep the page fast.Load diff
added
results/tables/tab_dose_pilot.tex
+23 −0
Large diff (24 lines) is not loaded by default to keep the page fast.Load diff
added
results/tables/tab_llm_reliability_pilot.csv
+4 −0
Large diff (4 lines) is not loaded by default to keep the page fast.Load diff
added
results/tables/tab_llm_reliability_pilot.tex
+9 −0
Large diff (10 lines) is not loaded by default to keep the page fast.Load diff
modified
results/tables/tab_threshold_sensitivity.tex
+2 −0
Large diff (8 lines) is not loaded by default to keep the page fast.Load diff
modified
results/tables/threshold_sensitivity.csv
+27 −0
Large diff (30 lines) is not loaded by default to keep the page fast.Load diff
added
scripts/47_mechanisms.py
+91 −0
Large diff (91 lines) is not loaded by default to keep the page fast.Load diff
modified
scripts/50_tables.py
+5 −1
Large diff (12 lines) is not loaded by default to keep the page fast.Load diff
modified
src/market_agents/agents/classical.py
+2 −2
Large diff (16 lines) is not loaded by default to keep the page fast.Load diff
modified
src/market_agents/experiments/design.py
+8 −3
Large diff (17 lines) is not loaded by default to keep the page fast.Load diff
modified
src/market_agents/experiments/runner.py
+1 −1
Large diff (8 lines) is not loaded by default to keep the page fast.Load diff
modified
src/market_agents/llm/policy.py
+2 −1
Large diff (15 lines) is not loaded by default to keep the page fast.Load diff
modified
src/market_agents/llm/prompting.py
+2 −2
Large diff (17 lines) is not loaded by default to keep the page fast.Load diff