The market was efficient after fees: an honest negative result
Agent swarms tested about 1,500 hypotheses under pre-registered rules. Several edges turned out to be bugs. The market was efficient after fees.

I built a real-time trading stack for Polymarket’s 5-minute crypto markets. It streamed order books, signed orders and redeemed on-chain on Polygon. None of that answers the question that decides whether the work was worth doing: after fees, is there an edge to trade? The tool for that question is a backtest, and a backtest is a program. Programs have bugs, and in my research several of them looked exactly like profit. This note is about how I separated the bugs from the market, and why the answer I trust is a negative one: the market is efficient after fees.
The question and the bar
Each market is a 5-minute window with an Up side and a Down side. The engineering is in the trading stack case file: a Bun platform, a Rust engine on the hot path, and a research lab on a 1.7 GB lookahead-free cache with a Rust backtester.
Fees set the bar. In the fee structure I recorded, takers pay 0.07 × p × (1 − p) per share at price p, makers pay nothing, and the maker rebate is 20% of the taker fee. A taker trades against a resting order. A maker is the one who posted it.
# A sketch, not code from my scripts.
# Taker fee per share at price p, from the recorded fee structure.
def taker_fee(p):
return 0.07 * p * (1 - p)The overnight research spec set the bar. A strategy counted only on out-of-sample days, which it never saw while being designed. The 5th percentile of a bootstrap of its per-window net dollars had to be above zero, so 95% of resamples had to come out net positive. Discovery used only the first 60% of days. Agent swarms tested about 1,500 hypotheses under pre-registered rules.
From a search for yes to a search for no
An earlier research loop was built the other way round. It piped one research prompt into headless claude –print for up to 50 iterations, each a fresh session that re-read the research log. Each iteration ran a web researcher and a data analyst in parallel, then a backtest engineer on one hypothesis. The loop’s only early exit was a STRATEGY FOUND tag. Nothing in it rewarded a clean no.
Lookahead bias means a backtest uses information that did not exist when the decision was made. On 4 July 2026 my dashboard recorded that an n-D model beat the market out of sample on log-loss, 0.427 against 0.484 (lower is better). Five days later, a commit fixed two lookahead leaks in the ETL. One exchange’s 1-minute candles were keyed at the start of the bar, so rows could see up to 60 seconds of future. Another exchange’s 1-second candles leaked one second. The commit says the leaks “had fabricated the m10 edge, the ND model’s OOS advantage, and the close-zone filtered rules”.
Other edges came from fills that could never happen. A book is crossed when the best bid is at or above the best ask. In the raw data, 9.06% of seconds had crossed books made by quotes of 1 to 8 shares, and they fabricated false edges. Simulated touch fills, which assume a resting order filled because the price reached it, fabricated about 6 cents per dollar of false edge.
The lab now guards against each of these. The cache has no look-ahead by construction: each row is one second of one window, with the book at that second’s boundary and the flow of the second that just ended. The ETL applies a $10-per-level dust filter, and only rows flagged book_valid are tradable. Through-fills, where the price has to trade past the order, are mandatory for maker backtests. The lab’s methodology has ten mandatory checks, including a temporal split, a multiple-testing correction and latency at 1, 2 and 5 seconds. In the 24–25 July session, those checks killed every positive candidate.
I also encoded failures like these in a Claude Code skill marked “MANDATORY before reporting any positive backtest result”. The constitution of my later research workspace states the goal plainly: find profitable, real strategies “and, far more often, to prove quickly that a candidate is not one”.
What the market said
Nothing left in the book
The direct tests all pointed the same way:
- A passive buy at 0.50 fills in about 91% of windows but wins only 45.2% on Up and 45.8% on Down.
- A model using every book feature scored 0.6556 accuracy. The mid price alone scored 0.6549.
- Buying Up and Down together for under $1 is algebraically impossible on a mirrored book. The data agreed: zero rows out of 10.1M.
- Active market making tested at −5.10% per dollar, with 0 of 145 days positive over 3.9M fills.
The winners did not replicate
Wallet forensics over 225 windows between 18 and 24 July covered 1.74M fills and 20k wallets. Takers as a class were at −$177k, which the log calls the fee pool. Makers were at +$28k before rewards. Replicating the top wallets’ mechanisms on 145 days found them dead. The log puts their July results down to selection bias or a private signal that cannot be replicated.
The one exception
A July synthesis of six alpha hunts found the market efficient in every zone studied except the last ~15 seconds after a big move. It also found that execution was not the limiting factor (“falsified on 46M moments”). Speed was not what I was missing.
The only positive strategy on record was also near the close: a close-zone book-lag rule. It showed an expected value of +14.3% per dollar over 9,965 windows, with a $5 fixed stake and real fees, an 88.7% win rate and 78% positive days. Its cross-validation (CPCV), permutation test (MCPT) and live paper run were still listed as not done. Then the guard recorded that 43% of its “good” signals had an ask that had not moved for more than 120 seconds, and that about 40% of its edge was fiction. A frozen quote has not moved in a long time, so it may not be a price anyone would still trade at.
The handoff’s verdict, after about 15 of my own tests, 9 agents and the literature: the BTC 5-minute market is efficient at the level at which it can be traded, and nothing tested is recommended for real money.
Other markets, same answer
Longer markets, from 15 minutes to 1 day, were all efficient net of cost across about 208 configurations tested with SPA and StepM. SPA asks whether the best of many candidates beats a benchmark by more than the search itself would explain. StepM names which candidates, if any, do.
In September I started a governed research workspace. Hypotheses are pre-registered there, and every trial is counted. Its first phase included weather and sports. The weather model was worse than the market price at both decision times in every pilot city and month, so it stopped at the gate. In sports, SPA over 162 pre-declared configurations gave p = 0.51, and StepM kept none. The skeptic agent’s summary: “Paper trade: nothing qualifies.” The trial log counts zero survivors from the fee era, which the constitution defines as trades on or after 30 March 2026.
Why I trust a no
A negative result can be a bug too. The SPA implementation I used takes losses, not profits, and my skill warns that the wrong sign “silently rejects real edge and accepts noise”. So the judges were checked against known answers. The second judge was rewritten from scratch and validated on an oracle plus noise. The guard, run directly, reproduces four known failures on synthetic data and then runs a clean control with real edge, which must pass. The skeptic agent used its own code and matched 867,435 keys row by row, with 0 settle mismatches.
Fees got the same care. The workspace takes fee facts only from official docs, never from skills or model memory. Its constitution even flags my own skill for hardcoding a 0.07 rate that is “true only for crypto”. Each simulated trade pays its own market’s fee on its own date.
Real money had its own rule. As of 4 July, the real-money versions of my paper experiments were paused, under a rule that real money stays paused until paper confirms for several days. The honest result: the market is efficient after fees. Bots paused. The rigor stayed.
What I’d keep
- Write the bar down before the search: out-of-sample days, net dollars, a pessimistic bootstrap above zero.
- Build the data so no row can see the future, and fix the fill model before trusting any edge.
- Test the judges on known answers. A broken test can reject a real edge as easily as it accepts noise.
- Count every configuration, including the ones written by hand.
- Keep real money paused until paper confirms, and treat a clean no as a result.
