Edge Autopsy邊際解剖

8 lines · 0 passed · $3,300 capital · 1 stale · 2026-08-27which one?

Polymarket · market making killed English

20 days of Polymarket reward market making: $78.86 on paper, $6.36 in the wallet.

updated 2,850 words · about 13 min

20 days live on $1,295.95. The statement said +$78.86; the wallet moved +$6.36. The missing $72.50 sat in inventory, and the mid marks were measured 21.6% high.

Reward-driven two-sided quoting on a prediction market, 2026-07-31 to 08-19, 20 days. Capital $1,295.95 in, $1,302.31 out. Net $6.36, +0.49%. I withdrew on 2026-08-19 and left the infrastructure running; the market tracker is still scanning.

The problem with this line was not that it lost money. It is that every daily P&L was positive and the wallet barely moved.

This is the English record of a line the site marks as killed. The verdict and the criteria behind it are on the methodology page, and the equity curve is on the performance page.

1. +$78.86 on paper, +$6.36 in the wallet

Here is the accounting. The three items come from three separate sources, not three columns of one file.

Item20-day total
Rewards accrued (platform settlement)+93.34
Of which forfeited below the payout threshold−3.00
60-second markout (99 fills)−11.48
Paper entitlement+78.86
Actual wallet change+6.36
Unexplained difference−72.50

The difference is not a bookkeeping error. The difference is the finding. All of it was in inventory: positions bought inside the band that never reverted, the spread between entry and exit prices, and holdings left when the bid side went to zero.

And the inventory itself was overstated, which I did measure. Liquidating everything on 2026-08-19 gave one settlement-accurate comparison: the last mid-marked inventory was $388.93, and selling it actually returned $304.88 — the mid mark was high by $84.05, or 21.6%. That error is larger than the −72.50 gap above. The two have different accounting bases and cannot be netted against each other, but it demonstrates the mechanism: inventory you are carrying is worth roughly eighty percent of what the daily statement claims, and no column on that statement shows it.

Mid is the wrong way to mark a book like this, because it is the average of two sides and one of those sides is frequently empty. Across the exit-depth distribution of 19 markets, three had a lower-quartile depth of exactly $0 (no bid at all), six more sat below $50, and the best was $2,404 — that distribution is not merely wide, it is extreme. On liquidation day three positions carried a non-zero mid mark all the way to the end and returned $0 on sale: there was no bid.

The shape of the mistake is precise. Position size was set from exit depth in the first place; what was wrong was using a depth-aware rule to enter and a depth-blind price to mark the result. Entry asked “how much can I get out of” while accounting asked “what is the midpoint”, two different questions, and from 2026-07-31 onward the inconsistency accumulated quietly. Same positions, same day, priced by walking the book instead: the error was 1.7%. Mid was 21.6%. A factor of thirteen.

I documented the limitation, and documenting is not quantifying

The dataset’s README already carried a sentence saying that inventory is marked at mid and will therefore look optimistic against true realisable value by “roughly one bid-ask spread”. That reads like a disclosure obligation discharged — the limitation was stated, and the direction was right.

It was wrong by an order of magnitude, and it was wrong for three weeks.

It survived precisely because it had been written down. An honest caveat makes the matter feel handled, so nobody goes back to measure it, including the person who wrote the caveat. What got it measured was somebody else saying “I think you are understating this, and I have data”, which is when I went and reconciled.

Stating a limitation is not the same as quantifying it. To a reader, an unquantified limitation looks exactly like a quantified one.

This is a new variant of 檢查因為錯誤的理由而通過 (in Chinese): the disclosure was made, and the disclosure was wrong. That is harder to catch than no disclosure at all, because it consumes the attention that would otherwise have gone into checking.

The markout distribution across those 99 fills: mean −$0.136, median +$0.009, standard deviation $0.763, 67.7% of fills positive, t = −1.78 (n=99). The median is positive and the mean is negative. That shape has a name: collect a few cents most of the time, get taken out by one large fill occasionally. Picking up pennies in front of the news looks like this. At t=−1.78 the result is not significant at the 5% level, so strictly I cannot even claim the markout was negative.

Markout is defined against a time window by construction. Databento’s microstructure guide states it plainly: a markout is “the change in price over some arbitrary time interval before or after an event”, and the standard practice is to compute it at several intervals and plot the curve (Databento microstructure guide, retrieved 2026-08-27).

I computed one point, and I picked the point least meaningful for this market. The real time scale is days, because that is how long the positions were actually held. A 60-second markout is not a P&L statement. It is a 60-second markout.

2. The ex-ante model overstated by 12.8×

Every close I kept a pair: what the model predicted today would earn, and what actually settled. Over the 20 days from 2026-07-31 to 08-19 the median overstatement was 12.8×, ranging from 1.0× to 177.6×. There was one cause: competition was being estimated from the visible book.

Reward scoring takes the minimum of the two sides and then weights it quadratically by distance from the mid. That shape lets a book that looks thin score far above its appearance. So I believed nobody was there; somebody was there, and they were standing in a better spot than I was.

The only estimator that survived was the inverse: take yesterday’s actual share of the pool and solve for the competing score that would have left me with exactly that fraction. Recalibrated against measurement, predictions landed within 2×. Every allocation decision after 2026-08-01 used the measured value, not the model value.

The disease is the same one the US equities line was audited for: in My backtest said 13.25×. After auditing it myself, 2.26×. all three bugs biased in the direction of “the strategy looks better”. Here too. Not one day was an underestimate — all 20 days in August 2026 were overstatements. A one-sided bias says the thing that was biased was not the code; it was my expectation of how crowded the trade was.

3. Every edge found died within 4 to 8 days

The calibration loop did find things. Three markets were judged genuinely mispriced, and real money went in. All three died.

MarketMeasured share at entrySurvivedHow it ended
A (football league futures)26%8 daysShare fell to about 1.5%; a large maker moved in
B (basketball rosters)10%4 daysReward pool cut from $33 a day to $7
C (entertainment)11%6 daysPlatform terminated the market’s reward pool outright

Three ways to die, and none of them was something I did wrong. Revenue = discovery rate × edge lifetime. When the lifetime is a handful of days, the daily recalibration loop is not a supporting feature; it is the strategy. A static allocation left alone goes to zero inside two weeks.

This is a zero-sum share of a pool: everyone who finds the same gap dilutes everyone already in it. I can find them, so can other people, and their capital is an order of magnitude larger than mine.

4. “Can be scaled” and “has a big pool” are structurally exclusive

I had assumed the binding constraint was the competing score. It was not. The constraint is the depth you can get out through. Once a minute I measured how many dollars of bids sat within a narrow band around the mid, and recorded the distribution per market. That number decides whether a market can absorb size, and it runs opposite to pool size:

A market with real depth attracts professional makers; a market with no professional makers has no depth for a reason. The intersection is a brief mispricing, which is exactly why the previous section happens. This is the same conclusion as 出場規則換一下,同一批交易從 −86% 變 +3% (in Chinese) in a second form: getting in is not getting out, and both a backtest and an order-book snapshot will only ever tell you about getting in.

5. Structural obstacles at the platform level

Each of these cost real time or real money.

The payout threshold is per wallet, not per market. One day I accrued $3.00 across 62 markets and received none of it. Another day I accrued $12.40 and it landed 23 seconds after UTC midnight, in full. My strategy at the time was breadth-first: many markets, a few cents in each. That strategy was silently forfeiting its own revenue, and the statement looked entirely normal.

A matched YES and NO pair is $1 by identity, and I could not merge them. The Gnosis conditional tokens framework documents the operation clearly: merging positions “does exactly the opposite of splitting, burning the conditional tokens of deeper positions in order to mint shallower ones or return collateral to the caller” (Conditional Tokens developer guide, retrieved 2026-08-27).

The obstacle is that these markets route through a second adapter contract, which “allows users to convert a set of NO tokens in a mutually exclusive binary market into the corresponding YES positions and collateral” (Polymarket neg-risk-ctf-adapter docs, retrieved 2026-08-27).

The gasless relayer I used only whitelists the conditional tokens contract itself and blocks that adapter layer. Sending from my own address is rejected too: the factory contract keeps an on-chain caller allowlist. I ran the same benign call through eth_call once from my address and once from the relayer’s, and only the second one passed. So the $1 sitting there on paper was not collectable, and I exited by selling the two legs separately on the book at a cost of 1.1%.

Three platform outages in 15 days (2026-08-05, 08-13, 08-19): one post-only mode, one halt, one order timeout. The engine flattened as designed all three times, and none of them cost money.

The exchange’s history endpoint returns empty for anything older than about 7 days. The consequence of that is vicious: a cumulative P&L sum shrinks by itself. It does not raise an error; it just gives you slightly less every day. Historical earnings now read only from local daily snapshots.

6. My own code broke six times

All six defects happened during live trading between 2026-07-30 and 08-19. Every one of them passed every test and was then found in production.

  1. The watchdog launched a compound command in the background and captured the subshell’s process id rather than the process itself. The liveness check failed permanently, so it restarted the engine over and over.
  2. The service unit’s default kill mode takes child processes with it, so every restart of the outer bot killed the trading engine too. It was only noticed because the engine’s shutdown handler cancels orders, and the cancellation record gave it away.
  3. A module rewrapped standard output on import. A consolidation routine imported it, and the engine then crashed and restarted on its own logging for 33 consecutive hours.
  4. A merge that could never succeed retried every 10 minutes, exhausted the relayer’s quota, and blocked all wallet operations for about 10 hours.
  5. Jump detection used a size-weighted microprice with a window counted in ticks. On a thin book, size churning back and forth reads as price movement, and stale jumps kept re-triggering. Two markets sat paused for an entire day earning nothing. Switching to the best bid-ask mid with a time-based window fixed it.
  6. The target list was truncated before the “filter to active markets” step, so newly added markets were dropped silently. A deployed allocation was quoting nothing at all, with no error in the log.

7. The tracker is still on

The money came out; the scanning did not stop. Every 6 hours since 2026-08-02, 98 scans by 2026-08-27.

Off-season baselineObserved rangeMedian
Total reward pools (per day)$769 – $2,048$1,066
Markets passing the basic filters192 – 314—
Markets also passing the depth gate1 – 53

The re-entry conditions are fixed in the tracker and the values are not published. The most recent scan (2026-08-27 00:05 UTC, total pools $1,168, four markets through the depth gate) did not meet them.

One change is worth recording: American football became the largest single category on 2026-08-22 at $652 a day. The count of markets passing the depth gate did not rise with it. The pool is growing and the good markets are not. More markets is not better markets.

What this line concluded

The system was right. Every risk control fired correctly in production, all three August 2026 platform outages cost nothing, and the calibration loop genuinely found three real mispricings. The strategy was wrong: 20 days, $1,296 of capital, $6.36.

What bound this line was never execution quality or infrastructure. It was that the exploitable set is about two markets, and each of them stops being exploitable within a week. That is why this line is marked killed rather than unproven. Unproven means the sample is too small and still accumulating; this sample was sufficient, and what it told me is a ceiling on size, not a probability.

FAQ

+0.49% is positive. Why withdraw? Because that 0.49% was held up by recalibrating every day, and edge lifetime is 4 to 8 days. This is not a position that keeps earning while you leave it alone; it is a job that has to be redone daily, annualising to roughly 9%. At this capital size the workload and the return are not proportionate. The reason for withdrawing was scale, not loss.

Could the −$72.50 difference just be a bookkeeping error? The wallet change is an on-chain settlement fact, one number at each end, with no room for interpretation. The three paper items come from the platform’s earnings endpoint, the fill record, and 60-second mid snapshots. The direction of the difference also matches the shape of the markout — positive median, negative mean, meaning a few large adverse fills. Inventory eating the rewards is what that looks like.

Why is the 60-second markout so wrong? Because it measures the mid 60 seconds after a fill, and on a book trading single digits an hour, usually nothing happens in those 60 seconds. The events that actually cost the positions money arrived days later: the counterparty disappeared, the bid went to zero, the platform terminated the market. Not one of those falls inside a 60-second window around a July or August 2026 fill.

Will this line come back? That is what the tracker is for. The restart conditions are written down, and the 2026-08-27 scan did not meet them. Worth noting: an in-season peak makes the reward pools larger, but the structural problem in section 4 does not go away because the pool grew. It only turns the two markets in the intersection into three or four.