Skip to content

Repository files navigation

mm-lab

This repository asks one question: do closed-form market-making quotes (Avellaneda-Stoikov and its Guéant-Lehalle-Fernandez-Tapia refinement, derived from scratch in THEORY.md) survive contact with real exchange data once the fill model is realistic? Every hypothesis, window, and threshold was frozen in PREREGISTRATION.md before any confirmatory data was downloaded, and the negative results are reported with the positive ones. The short answer: the formulas deliver the inventory-risk reduction they promise, no configuration in the frozen design is profitable after fees, and the backtest's adverse-selection structure reappeared in a live 2026 paper session, 2.7 years out of the calibration era.

Preregistered test Verdict In one line
H1: GLFT vs fill-matched symmetric baseline ETHUSDT ACCEPTED, ATOMUSDT REJECTED 17% less inventory risk and smaller losses on the deep pole; on the thin pole a 60% risk cut costs more PnL than the frozen bound allows
H2: strategy ranking invariant to the fill model ACCEPTED, expectation falsified We expected rankings to flip across fill models; they did not, and on ETHUSDT the optimistic model shows 11% more loss
H3: exponential fill intensity λ(δ)=A·e^(−κδ) REJECTED (67.9% < 70%) ETHUSDT 14/14 for the exponential, ATOMUSDT 5/14: one λ(δ) family does not span both liquidity regimes
Paper gate: one ≥6 h live session, ≥50 simulated fills PASSED (7.0 h, 41,427 fills) Offline replay of the recorded stream reproduces the live fill log exactly; no order was ever sent

ETHUSDT per-fill markouts: backtest 2023 vs live paper 2026

Design

  • Data. Binance USDT-M perpetual futures, event-level L1 (bookTicker) plus tick trades. Two liquidity poles: ETHUSDT (tick ≈ 0.05 bp) and ATOMUSDT (tick ≈ 1.1 bp in era). Sources, gap statistics, and verified matching rules (FIFO, fees, tick/lot) are in DATA.md.
  • Strategies. S0, a constant symmetric spread (baseline); S1, Avellaneda-Stoikov with a finite rolling horizon; S2, the GLFT closed form. All calibrated once on the calibration week, then frozen.
  • Fill models. F0 touch fill (optimistic bound), F1 FIFO queue estimated from L1 with cancel-ahead/cancel-behind variants (reported as a band), F2 trade-through only (pessimistic bound). Headline results are reported under all of them.
  • Execution realism. 50 ms action latency (0 and 250 ms sensitivity), post-only quotes, inventory cap, 0.02% maker fee, exact integer arithmetic with the accounting identity cash + inventory × mark = equity asserted on every run.
  • Discipline. Calibration (2023-11-06 to 12) and confirmatory windows (Block A 2023-11-13 to 26; Block B 2024-03-04 to 10) were declared before download. The download tooling refuses confirmatory dates unless the preregistration is already in git history.

Results

Full experiment log with per-cell numbers: RESULTS.md.

  • The formulas work as risk tools, not as money machines. GLFT cuts daily inventory std by 17% on ETHUSDT at the preregistered fill match, and a review-forced exploratory run shows the cut survives (19%) against a distance-matched opponent, where the PnL margin does not (RESULTS §2). Every cell in the frozen grid loses money after fees on both symbols and both blocks (§7).
  • The fee wall is structural. On ETHUSDT, break-even needs about 38 ticks of captured edge per round trip; the frozen grid ends at 8 ticks and GLFT quotes near 15. No configuration of this design could have been fee-positive there, independent of adverse selection (§7). The design measures quoting mechanics, not a deployable strategy.
  • Queue-free backtests did not inflate PnL here; they deflated it. The folk claim is that optimistic fill models flatter market-making backtests. On ETHUSDT the opposite held: F0 shows 11% more loss than F1-pes, because the extra fills an optimistic model grants are the toxic ones (§3).

Fill intensity fits Fill-model sensitivity

The live paper session

The same engine and the same frozen tables ran for 7.001 uninterrupted hours against the live Binance websocket on 2026-07-11 (bookTicker + trade streams, simulated fills, no orders). Three engineering properties held: the accounting identity was asserted every 60 s with zero violations; feed integrity stayed inside the preregistered gap rule; and replaying the recorded raw stream through a fresh engine reproduced all 41,427 live fills bit for bit. Two research observations came out (§9): on ETHUSDT, fill-count ordering, markout levels, and GLFT's inventory advantage all replicated live (figure above); on ATOMUSDT, tables calibrated in tick space produced 24 fills in 7 hours at one fifth of the calibration-era price level. An intensity surface calibrated in ticks does not survive a large price-level regime change.

H1 risk and PnL

v2: the tick cross-section

v1 measured where the formulas die; v2 asks whether coarser ticks buy better after-fee economics. A second preregistration (PREREGISTRATION_V2.md, frozen before any confirmatory download) added six symbols, extending the cross-section to eight symbols spanning a ~230× range of empirical tick size in basis points, plus two mechanism candidates (S3, a microprice-referenced GLFT; S2g, a trade-flow toxicity gate) and four hypotheses. Full log: RESULTS §11–§18.

Preregistered test Verdict In one line
H4: after-fee markout improves with tick coarseness REJECTED (ρ 0.21–0.36 < 0.5) In basis points the cross-section is flat, −1.8 to −4.1 bp everywhere; only the optimistic fill model shows a steep slope (ρ +0.96) and a fee-positive coarse pole
H5: microprice reference beats mid reference REJECTED 0/7 More trades, and the preregistered markout improvement appears in 0 of 7 symbols under both queue variants
H6: toxicity gating cuts the markout tail REJECTED 0/5, 3 untestable The gate worsens the tail it targets, and its frozen threshold saturates at 1.0 on thin books, where it provably cannot arm
H7: a zero-fee frontier exists somewhere ACCEPTED, ALGOUSDT only Both ALGOUSDT primary cells are net-positive at zero fee; only S2 (f* = +0.042%) clears the posted rate, its bootstrap CI includes zero, and the frontier is absent in the March block
v2 paper gate: ≥6 h live, ≥50 simulated fills PASSED (7.0 h, 77,713 fills) Replay reproduced the live fill log bit for bit across 27 real reconnects; fills tracked each symbol's live tick, not the frozen 2023 ladder

H4 cross-section: flat in bp under queue-aware fills

Three findings summarize v2. First, tick coarseness does not buy after-fee edge: the cross-section is flat in basis points under queue-aware fills, and the slope a touch-fill backtest shows is an artifact of the fill model (the figure's blue line turns the coarse pole fee-positive; the queue-aware lines do not). Second, both mechanism candidates failed their preregistered tests, and the gate's threshold saturation on thin books is itself a design finding: a percentile of trade-flow imbalance stops being informative when the trailing tape is routinely one-sided. Third, the single acceptance (H7) is reported at its actual strength: one symbol, fragile under resampling, regime-local.

The second live session (2026-07-12, six symbols, 72 cells, simulated fills) passed the same engineering gates as the first: accounting identity asserted throughout, and offline replay reproduced all 77,713 live fills exactly, this time through 27 websocket reconnects from a real network outage. The theta-saturation identity predicted in the preregistration's deviation log held bit for bit live on both saturated symbols, and one symbol failed the preregistered feed-gap rule and is reported as such (RESULTS §18). The v2 additions bring the test suite to 98 tests.

Rust port

The replay inner loop is ported to Rust (rust/mmlab-replay, no dependencies) and held to bit-identical fill logs against the Python engines on full real days. Benchmark (Apple M4, median of 5 after warmup, like-for-like engine timing; RESULTS §10):

workload pure Python (est.) numba kernel Rust
ETHUSDT day, 9.5M events, 56 cells ~416 s 13.99 s 3.27 s
ATOMUSDT day, 2.0M events, 56 cells ~90 s 3.00 s 0.72 s

Repository map

Path Content
notebooks/mmlab_tour.ipynb The whole story in one runnable notebook; needs only the committed result files
PREREGISTRATION.md Frozen hypotheses, thresholds, windows, deviations log
PREREGISTRATION_V2.md The v2 freeze: cross-section symbols, S3/S2g, H4–H7
THEORY.md S1/S2 derivations; each formula maps to a named function
DATA.md Verified sources, matching rules, fees, QA reports
RESULTS.md §-numbered experiment log, including the failures
src/mmlab/ Data layer, reference engine, numba kernel, metrics, paper session
rust/mmlab-replay/ Rust port of the replay loop
scripts/ Download, backtest, calibration, paper runner, report, benchmark, figures
results/ Calibration, verdicts, per-cell summaries, benchmark, paper report
REFERENCES.md, FUTURE_WORK.md Sources; what was cut and what comes next

Reproducing

python3.12 -m venv .venv && .venv/bin/pip install -r requirements.txt
.venv/bin/python -m pytest                    # 98 tests; data/Rust-marked ones skip without raw data or the Rust binary
.venv/bin/python scripts/download_data.py --window calibration
.venv/bin/python scripts/run_backtest.py --window calibration
(cd rust/mmlab-replay && cargo build --release)  # optional: enables equivalence tests
.venv/bin/python scripts/make_figures.py

Raw data is not committed; data/MANIFEST.json pins SHA256 checksums for every downloaded file, and one seed (20231106) reproduces any run. For a guided pass over the committed results without downloading anything, run notebooks/mmlab_tour.ipynb.

Limitations

Stated throughout, summarized once: the v1 results cover one venue, two symbols, and three short windows, and v2 widens the cross-section to eight symbols on the same venue and windows; the F1 queue model is an L1-estimated band, not ground truth (its assumptions are measured against a 25-level book in §6); latency above 5-6 ms of exchange publish delay is assumed, not measured end to end; and every PnL statement is about a design that loses money after fees (the one zero-fee exception is scoped in §15). Nothing here is investment advice.

AI usage

The use of AI tools in this repository was limited to the following purposes: coding support; derivation checking; literature and documentation search; analysis interpretation; drafting under a fixed style guide. At all stages, the outputs of AI tools were critically reviewed, cross-checked with the underlying data and sources, and revised by me. The responsibility for the final content, analysis, and conclusions rests entirely with me.

About

Preregistered market-making research on real order-book data: Avellaneda-Stoikov/GLFT quotes under three fill models, live paper sessions, a Rust replay port, and negative results reported with the positive ones.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages