Reverse-engineering Polymarket's most profitable traders, in real-time.
What if you could mirror them at a fraction of their bankroll?
Copying trades 1:1 sounds simple, but every second of delay between their fill and yours costs slippage. By the time the public data API surfaces a trade — about 22 seconds later — the orderbook has already moved. The price you see is the price they got. The price you'd actually fill at is somewhere worse.
The whole project sits on a single bet: if you can shave detection latency below the slippage window, an asymmetric copy of a profitable trader is mathematically tractable. If you can't, you are paying the trader for the privilege of being late to their own ideas.
Before writing a line of code, the question was who, not how. Polymarket leaderboards surface dozens of traders that look superficially profitable, but most of them are unmirror-able for structural reasons — they hedge aggressively, they trade in sizes that exceed a small bankroll, or their edge is concentrated in a narrow window.
I built a profile of what an "ideal" target looks like:
The selected target — a quiet LoL specialist I'll call Target A — was the least flashy of the three. Lower headline returns. Smaller fills. But on the four dimensions that actually mattered for a mirror — hedge rate, sizing fit, market liquidity, and consistency — nobody else came close.
Polymarket settles on Polygon. Every fill emits an OrderFilled event from the exchange contracts — both NegRisk and CTF. The public data API just reads those events and re-publishes them with caching delay baked in.
If you're willing to subscribe to the chain directly, you get the same information without the cache. That's the whole architecture.
# both Polymarket exchange contracts emit OrderFilled NEG_RISK_EXCHANGE = "0xC5d563A36AE78145C45a50134d48A1215220f80a" CTF_EXCHANGE = "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E" ORDER_FILLED = "0xd543adfd945773f1a62f74f0ee55a5e3b9b1a28262980ba90b1a89f2ea84d8ee" async def subscribe(ws, target_addr): await ws.send_json({ "jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": [ "logs", { "address": [NEG_RISK_EXCHANGE, CTF_EXCHANGE], "topics": [ORDER_FILLED, None, target_addr], }, ], }) async for raw in ws: log = json.loads(raw)["params"]["result"] fill = decode_order_filled(log) await trade_queue.put(fill) # < 1s from block to queue
The chain listener worked on day three. Most of the four weeks went into the much harder problem: figuring out who to copy, after the first two attempts proved that profitable-on-paper doesn't mean mirror-able-in-practice.
First target: a high-frequency tennis specialist with 90 days of clean returns. We caught his initial position on the favourite, missed the in-play hedge on the underdog 40 minutes later, and lost on every match that didn't go straight to plan.
The lesson wasn't subtle. A 77% hedge rate means he wasn't betting on outcomes, he was running a structured book. Mirroring one leg of a hedged book isn't a copy — it's an unhedged version of someone else's hedged trade.
Pivoted to a multi-sport spreader who looked statistically incredible: 26 of 26 winning days going in. Within 72 hours of switching the listener over, he went 0 of 3 losing days — and his streak ended.
This is the trap with leaderboard targeting. Of 1,000 traders, a handful will look impossibly clean over 30 days by chance alone. By the time you notice them, you're watching the peak of a curve that almost certainly mean-reverts.
The two failures forced an explicit rewrite of the target spec: single-sport (no edge dilution), low hedge rate (mirrorable book), modest sizing (5k bankroll viable), edge sustained over 60+ days (not 30). That spec excluded almost everyone on the public leaderboard.
The trader who survived the filter was a quiet LoL specialist with smaller absolute returns but a much harder-to-fake consistency. Less impressive on paper. More copy-able in reality.
Final round was parameter work, not architecture. Slippage cap moved from 7% to 10% (a 7% cap was rejecting Target A's most profitable fills, where he was the one moving the price). Minimum trade threshold tuned to filter dust without missing real positions. Hedge follow-up logic disabled entirely — for this target, the hedge events were noise, not signal.
Each parameter was ratified through a 7-day clean test: deploy the change, then make zero engineering changes for the full window so the result wasn't polluted by mid-flight tweaks.
The original silent 5%-of-bankroll cap was clipping every position the trader sized big — precisely the trades that signalled conviction. Removing the cap turned a 70%-fidelity copy into a true 1:1 mirror, and the captured edge per dollar moved with it.
Following hedges sounds prudent. In practice it captures the trader's losses while the original entry already absorbed the risk premium. We eat the asymmetry on purpose: copy the conviction trade, ignore the protective leg.
On every deploy the bot only follows positions opened after its start timestamp. A naive backfill would dump the bankroll into the trader's existing book — including markets already resolved but not yet redeemed. Fresh-start sidesteps the entire class of errors.
When deprecating a target, the bot stops opening new positions but lets existing ones resolve naturally. A hard kill-switch would force-close at whatever the orderbook offered, paying maximum slippage to exit. Graceful deprecation costs nothing and protects realised P&L.
14:02:18 FILL target opened YES @ 0.11 size $1,420 14:02:24 MIRROR placed YES @ 0.11 size $142 14:18:51 FILL target DCA'd YES @ 0.08 size $2,100 14:18:57 MIRROR placed YES @ 0.08 size $210 14:41:09 FILL target DCA'd YES @ 0.04 size $3,800 14:41:14 MIRROR placed YES @ 0.04 size $380 15:24:02 RESOLVE market settled NO 15:24:02 PNL realised −$174 (target −$7,320) # a real loss, surfaced cleanly. the bot did exactly what it # was supposed to: mirror conviction, including when conviction # is wrong. selection of who to mirror is a separate problem.
What worked. The infrastructure. The chain listener has run for weeks without a missed event. Detection latency held at 3–6 seconds across thousands of fills. The bot, mechanically, does what it was built to do.
What didn't. Target selection — repeatedly. Both rejected targets looked correct on the metrics that public leaderboards expose, and both reverted within days of being mirrored. The pattern was so consistent that it stopped being bad luck and became the actual problem.
The lesson. The hardest problem in copy trading isn't the engineering. It's the selection bias. Anyone who looks profitable for 30 days might be having a hot streak indistinguishable from real edge. Real edge needs to be observed for months — ideally across regime changes, not just inside one.
The thing I want to build next is a target-vetting tool that tracks 100+ candidate traders over time, and only surfaces the small subset whose edge persists across multi-month windows. The listener was the prerequisite. The vetting layer is the actual product.