I ran an arbitrage bot on Polymarket. Here are the real numbers.

17 min read Original article ↗

All "I built a Polymarket trading bot" posts I've read on X are absolute LARPs that are completely out of touch with reality. Whenever you come across articles claiming $X,XXX/mo, do yourself a favour and don't spend your time reading nonsense.

The story I'm going to share with you is not like that. It's nowhere near as impressive as those made-up stories, but it's real. I've got the numbers to back it up, and I believe you can genuinely learn from my experience — what I did right, and the mistakes I made along the way. Between January and late March 2026, my bot placed 3,858 bets on Polymarket and finished up $4,973. It never once tried to predict who would win. The only bets it actually made money on were the ones where it locked in an arbitrage — a bet on both teams at once.

My public Polymarket profile, @b00k13 — all-time profit/loss and stats
$4,960.77 all-time profit, biggest win $267.75. The 2,785 'predictions' are unique markets — the bot placed 3,858 bets across them, since a hedged arb is two bets in one market.

You don't have to trust me on any of it — the wallet is public: polymarket.com/@b00k13 — you can have a look at the all-time graph and number of predictions and confirm for yourself.

Polybot · cumulative P&L (Jan–Apr 2026)completed

Net P&L

+$4,973

Total bets

3,858

Volume

$95,830

Win rate

47.5%

The graph above gives you a more in-depth look into the bot's metrics — $4,973 of net profit from 3,858 bets, on just under $96k of volume. A slow, boring grind upward, which is exactly what you want it to be.

But that headline number hides the real story. One part of what the bot did made far more than $4,973, and another part quietly handed a big chunk of it back. Pulling those two apart is the whole point of this post — I'll get to it once I've explained how the thing actually worked.

The strategy of this bot is very simple — fetch odds from sports bookmakers, convert those odds into implied probabilities, and then place limit orders with a minimum 7% edge with respect to the implied prob. If we get filled for both teams of a game, we have locked in an arbitrage. If we get filled only for one team, we have a directional bet.

Either way, we should always be profitable, as we only place bets with a positive edge — but this wasn't the case. Obviously, there were some errors in my execution. Nothing fatal, since the bot was still profitable. But by the end I'd built an entire analytics stack just to understand why.

How I ended up betting on esports

Long story short — I didn't have much to do during the Christmas holidays and I was playing around with Polymarket. I was doing a thorough analysis (AI-assisted, of course) of whether the Trump administration would release the UFO files that week, or whether Israel was about to bomb some neighbouring country. To spare you a mouthful, I was gambling.

At some point I took an interest in esports games, such as CS2, Dota 2, and LoL. I know these games well from my childhood, but it's not nostalgia that grabbed my attention. I made a key observation — the spreads on these markets were unusually wide (sometimes 20–30¢) and, more importantly, these markets had volume. To put that in simpler terms, people were placing bets on Polymarket for esports games at significantly worse odds than the bookmakers, simply because there weren't enough market makers competing in the order book.

It seemed too good to be true, so I decided to give it a quick try. I placed a 100-share limit order on the favourite team in a Dota 2 game that was about to start in an hour or two. The spread was >20¢. It wasn't long before the order got filled. I'd checked the odds beforehand and already calculated my edge at roughly 12%. Nevertheless, I decided to try to close an arbitrage on the position — so I placed a bet on the other team too, another 100 shares. My order got filled before the game even started. I'd bought 200 shares for a combined $81, locking in $19 of riskless profit.

I wasn't predicting anything

Let's run through the math very quickly. If you are familiar with odds, vig, and Polymarket orderbooks, you can skip this part. For the strategy of the bot to work, all you need is odds from sportsbooks for the games you want to bet on and a bot that correctly places and updates bets based on these odds. That's it. Sounds simple, but it really isn't, at least from an execution POV.

Say you are betting on a CS2 game, FaZe Clan vs. NAVI. The odds are 1.57 and 2.28 respectively. What you need to do here is compute the underlying probabilities and remove the vig (the extra that bookmakers add to guarantee profit). You do this the following way:

First, turn each decimal odd into an implied probability — that's just one over the odds:

pFaZe=11.57=0.637,pNAVI=12.28=0.439p_{\text{FaZe}} = \frac{1}{1.57} = 0.637, \qquad p_{\text{NAVI}} = \frac{1}{2.28} = 0.439

pFaZe=11.57=0.637pNAVI=12.28=0.439\begin{aligned} p_{\text{FaZe}} &= \frac{1}{1.57} = 0.637 \\[1.0em] p_{\text{NAVI}} &= \frac{1}{2.28} = 0.439 \end{aligned}

Add them up and you get more than 100%:

0.637+0.439=1.0760.637 + 0.439 = 1.076

That extra 7.6% is the vig — the margin the bookmaker bakes in. To get the fair probabilities, normalise so they sum to one:

p^i=pipFaZe+pNAVI\hat{p}_i = \frac{p_i}{p_{\text{FaZe}} + p_{\text{NAVI}}}

p^FaZe=0.6371.076=0.592,p^NAVI=0.4391.076=0.408\hat{p}_{\text{FaZe}} = \frac{0.637}{1.076} = 0.592, \qquad \hat{p}_{\text{NAVI}} = \frac{0.439}{1.076} = 0.408

p^FaZe=0.6371.076=0.592p^NAVI=0.4391.076=0.408\begin{aligned} \hat{p}_{\text{FaZe}} &= \frac{0.637}{1.076} = 0.592 \\[1.0em] \hat{p}_{\text{NAVI}} &= \frac{0.439}{1.076} = 0.408 \end{aligned}

So the books make FaZe Clan about a 59% favourite and NAVI 41%. That's the fair value I line up against Polymarket.

The bot never guessed a winner. It arbitraged the gap between two venues:

  • Sportsbooks (Spinbetter, GGBet…) — easily scraped. I treat their odds as "fair value."
  • Polymarket — a binary prediction market where shares settle at $1 or $0, with a thinner, occasionally mispriced order book.

If the books imply a team has a 59% chance to win, a fair price on Polymarket is about 59¢. In such scenarios, we'd place a +1¢ bet with respect to the current best bid, up to 52¢ per share. The instant our order gets filled, we'd chase a hedge for the opposite outcome. In our example, if we got filled at 52¢, we'd bid up to 41¢ for the other team, locking in a minimum 7¢ per share. That way, whoever wins is completely meaningless for us, as we managed to buy $1 shares for 93¢ or less.

The hedge, one game — buy both sides for less than $1

Books implyFaZe 59%·NAVI 41%

Buy FaZe

≤ 52¢

+

Hedge NAVI

≤ 41¢

=

Total cost

93¢

One side settles at $1 — locked ≥ 7¢/share, whoever wins

Why esports games

The reason why I focused on esports games is that these opportunities that I described above are mostly found there. At the time of writing this post, I was still able to find a market for a Mobile Legends: Bang Bang (a mobile version of League of Legends) which satisfies both conditions that are necessary for this specific bot — high spread and a decent volume.

A Mobile Legends market on Polymarket — Bigetron By Vitality vs EVOS, MPL Indonesia
Bigetron vs EVOS (MPL Indonesia), 5 Jun 2026 — best bid 46¢, best ask 73¢: a 27¢ spread on $237.54 of volume.

The above image is a perfect example for the kind of games you should be placing limit orders on. The market already has a decent volume of $237.54 despite the high spread of 27¢. I took this screenshot on 5 Jun in the morning, meaning that there are still genuine opportunities on Polymarket to compete as a market maker.

The hard part — execution

Disclaimer: I coded my Polymarket bot all with the help of AI. I barely ever wrote a line of code. Nevertheless, don't even consider this will be trivial for any AI model to code on its own. The hard part is not the scraping of odds from bookies and writing those to a relational database. You can get your AI buddy to do this in under 30 minutes. What's challenging is to build a Polymarket bot that keeps track of hundreds of markets simultaneously and acts according to your strategy.

I'm not going to go into great detail here on what the bot actually does; that's a whole writeup of its own. Outbidding and downbidding, updating orders when the odds move, handling the fact that Polymarket and the sportsbooks spell team names differently. Plenty of moving parts. If you want to play this game you will have to compete head to head with other hustlers who are also chasing that bread. There's no easy money anywhere in the world, and when there is, it never lasts long — so you have to make it count.

The headline numbers

If you've got to this part, congrats, this is where things start to get interesting. Here's the whole run, start to finish:

MetricJanFebMarAprTotal
PeriodJan 6–31Feb 1–28Mar 1–31Apr 1–28Full
BetsTotal bets1,4201,2421,153433,858
PositionsClosed positions1,3551,1351,090423,622
TradedVolume traded$22,890$43,095$29,532$313$95,830
P&LNet realised P&L+$1,897.61+$2,505.56+$390.40+$179.50+$4,973.06
Win rate50.2%48.3%43.4%42.9%47.5%
Big winBiggest single win$165.22$207.31$267.75$65.99$267.75

Two things jump out month over month. First, the run was front-loaded — February alone made +$2,506, then March collapsed to +$390 and the win rate slid from 50% into the low 40s. The edge was decaying in real time, which is most of the reason I eventually pulled the plug. Second, April is just the tail — 43 trades settling out as I wound the bot down.

A few things went wrong in March. For one, I had a bug for a while where the implied probabilities for the two teams were swapped, so the bot was confidently betting on the wrong team. I was also using another approach for removing vig called Shin's method, which resulted in the bot being overconfident on the favourite teams and also lost me money.

That's just a couple of examples off the top of my head. I ended up building a complete analytics suite and a dashboard so I could investigate not-so-obvious issues like that. In the next article I will go into detail about all the mistakes I made and all the things I'd have done better if I were to start this over again.

A few of the metrics in the table may surprise people. A 47.5% win rate sounds like losing — but for this strategy it's fine. Half my bets are the hedge side of an arbitrage, deliberately placed on the less likely outcome, and the profit is locked across the pair, not won on either leg. So a win rate hovering around 50% is exactly what you'd expect.

You can even sit below 50% and still finish up — as long as your directional bets, the ones on the underdog, come good often enough to pay for themselves. On its own, the win rate tells you almost nothing here. The P&L is what matters.

Also, the biggest win being only $267.75 is the whole thesis: this wasn't a few lucky moonshots. It was 3,858 small, mostly-hedged bets, worth roughly ~$25 on average, grinding upward.

The arbs won. The forced legs lost — and they shouldn't have.

Split the P&L by what kind of bet it was and the run tells a different story:

BucketWhat it isP&L
Arbitrage (1,075 arbs)Both sides matched → guaranteed+$8,293.46
Directional (unhedged)The leg when a hedge didn't fill−$3,184.78
Cancelled matches (236)50/50 refunds — see below−$134.43
Fees / gas / rounding−$1.19
Net realised+$4,973.06

The arbitrage profit is genuinely impressive for a ~3 month run. But here's what people miss about this strategy: you can't capture those arbs without taking the directional bets. In a wide-spread market you can't just buy both sides at a good price — you post a limit order and wait. When one leg fills, you're already holding that side; you post the hedge and wait for it to fill too. If it does, the arb is locked. If it doesn't — the price moves, the game starts — you're left with an unhedged, directional position you never chose.

So the directional book isn't a clever side bet I decided to make — it's the forced residual of capturing the arbs, the leftover legs that never got hedged. But here's the part that matters: every one of those legs went on at a ≥7% edge, the same edge the arbs are built on. A book of bets like that should make money on its own. Mine lost −$3,184 instead. That gap, between bets that should have been profitable and a book that bled, is the real mystery of this post. The question isn't "why did I make bad bets." It's why a book of +EV bets lost money — and the answer is all execution: faster bots picking off my stale quotes, a spell where a sign-flip had the bot backing the wrong team, a devig method that ran hot on favourites.

I found out a lot to improve. I was eager to expand the bot to cover as many markets as possible. I was overconfident with some changes when I shouldn't have been. I've overlooked mistakes that the AI made and deployed them, only to find out later that I'd lost money because of that.

Where the money was made and lost: P&L by game

As soon as I confirmed that the strategy works for Dota 2 and CS2, I instantly started expanding into other esports markets. Eventually, I also gave some sports markets a shot that satisfied the condition of a decent volume and a sufficiently large spread.

Here's that same arb-vs-spec split broken down by game, and you can see exactly where the directional book bled (rounded; excludes cancelled matches, so it sums a little above net):

GameVolumeP&LROIW–LArb P&LSpec P&L
CS2$30,123+$2,380+7.9%571–609+$3,322−$942
Valorant$11,503+$875+7.6%230–233+$973−$98
Dota 2$8,485+$743+8.8%166–184+$825−$82
HoK$1,513+$584+38.6%68–64+$228+$355
LoL$17,376+$502+2.9%308–324+$1,983−$1,480
CoD$2,236+$356+15.9%41–48+$496−$140
MLBB$70+$13+18.1%7–7+$12+$1
Rugby$1,120−$54−4.8%27–26+$95−$150
Hockey$156−$56−35.8%1–5$0−$56
Other$12,178−$235−1.9%300–403+$359−$594

The shape of it is brutal: the arbitrage column is green almost everywhere, and the directional column is red almost everywhere. LoL is the cautionary tale — the arbs there made nearly $2k, but the directional bets gave back −$1,480, so a market that should have been a goldmine netted just +$502. The only game where the directional book actually made money was Honor of Kings (+$355), and I still don't have a clean explanation for why — it also had the best ROI of the lot at 38.6%, on a tiny $1,513 of volume. It was probably just luck.

The big, "efficient" markets paid worse: CS2 turned $30k of volume into 7.9%. That made sense anyway, as there is more competition for these markets with higher volume. And high competition in trading means that you are paying for your mistakes and inefficiencies in cash. In trading there are opportunities around every corner, and if you are not capturing those opportunities, you probably are the opportunity.

The weird one: getting paid (and losing) on cancelled matches

I will give you an example of something I was not even aware was losing me money one month into the project. I had no idea that for esports events it's common for the games to be cancelled, and how Polymarket resolves these cancelled events.

So, when a match is cancelled, Polymarket refunds 50¢ per share regardless of which side you held. That sounds neutral, but it isn't: if I'd bought a favourite at 70¢ and the match got cancelled, I got 50¢ back — a 20¢ loss per share on an outcome that never happened.

And as I mentioned before, my earlier strategy was actually biased towards the favourites, so at the point I found out about this I had lost ~$500. I balanced the strategy eventually and things evened out, so I wasn't hurt from this — but it's just one of the many things you have to be aware of.

Across the whole run, 236 matches were cancelled. A cheap longshot I'd bought at 13¢ turns a tidy profit on the 50¢ refund; a favourite I'd bought at 70¢ takes a loss. Netted out across all 236, they cost me −$134.43 — a whole P&L line from games that were never even played:

Cancelled matches

236

Total cost

$11070.56

Refund (50¢/share)

$10936.13

P&L from cancellations

−$134.43

MatchPositionSharesEntryCostRefundP&LDate

Atrix Esports vs Isurus (BO3) - ESL Challenge

Atrix Esports

393.214¢$54.91$196.60+$141.692026-03-21

MIBR vs FOLHA AMARELA (BO3) - PGL Bucharest:

FOLHA AMARELA

299.911¢$32.99$149.97+$116.982026-02-27

4 SWINES & A BUM vs Lundqvist Lightside (BO1)

4 SWINES & A BUM

300.013¢$40.00$150.00+$110.002026-02-26

SkinRave vs Outfit 49 (BO3) - ESL Challenger

Outfit 49

200.015¢$30.00$100.00+$70.002026-02-22

The Otter Side vs Vantex Esports (BO5) - LPLO

Vantex Esports

200.027¢$54.00$100.00+$46.002026-03-03

Rooster vs Skele (BO3) - CCT Oceania Series #

Skele

108.613¢$14.12$54.30+$40.182026-02-24

ODDIK vs Procyon Gaming (BO3) - ESL Challenge

Procyon Gaming

100.012¢$12.00$50.00+$38.002026-02-18

largadosypelados vs Curralzinho (BO3) - CCT S

Curralzinho

80.3$4.81$40.13+$35.312026-02-04
220 more cancelled matches …

Partizan Sangal vs Crvena zvezda Esports (BO3

Partizan Sangal

118.578¢$92.77$59.23−$33.542026-02-14

ex-KRÜ Esports vs RED Canids (BO3) - ESL Chal

RED Canids

100.087¢$87.00$50.00−$37.002026-02-18

Rooster vs Skele (BO3) - CCT Oceania Series #

Rooster

132.180¢$105.30$66.04−$39.262026-02-24

The Otter Side vs Vantex Esports (BO5) - LPLO

The Otter Side

304.364¢$195.86$152.17−$43.692026-03-03

largadosypelados vs Curralzinho (BO3) - CCT S

largadosypelados

180.378¢$140.35$90.13−$50.232026-02-04

CYBERSHOKE Esports vs DEPO (BO1) - META Cup G

CYBERSHOKE Esports

500.062¢$308.00$250.00−$58.002026-02-21

Atrix Esports vs Isurus (BO3) - ESL Challenge

Isurus

393.273¢$288.63$196.60−$92.032026-03-21

MIBR vs FOLHA AMARELA (BO3) - PGL Bucharest:

MIBR

399.974¢$296.95$199.97−$96.982026-02-27
Showing the 8 biggest gains and 8 biggest losses of 236. Cancelled matches are refunded at 50¢ per share regardless of entry price.

What's next

Why the edge faded

If you're ever fortunate and hard-working enough to find a trading edge in any market, unless your bot is fairly sophisticated or there is some other factor stopping others from replicating you, chances are that your edge is going to vanish fast.

This is, in rough terms, what happened with the bot. Eventually there was more competition for the market making and fees got introduced, so the edge got thinner. Nevertheless, I believe there is still room to compete in Polymarket, maybe not as much as before, but from what I've recently checked, the opportunities are there.

I switched the bot off at some point in April — not because it stopped working, but because I lost focus and I didn't spend the necessary time to improve and support it. In general, once it's working it's mostly plug and play, but it's always necessary to devote some of your time for things to run smoothly.

Rewriting the core in Rust

I have some plans for June. I'm rewriting the core in Rust — for correctness first, not speed. The reason is simple: I've had a couple of moments where the bot had >$3k riding on a single market, and a silent bug at that size is terrifying. I was fortunate enough to catch those early and cash out at roughly the same price, but I don't want to rely on luck. That means a lot more unit tests, to be sure every path is correct.

Speed matters too. The other market-making bots are quicker to outbid me, so their orders sit on top of the book most of the time — I need the rewrite to close that gap.

Writing this article helped me collect my thoughts, refresh my memory on how I built this, and get me hyped up about working on this bot again. I hope reading this was as fun and useful as it was for me. In the next article I will do a deeper teardown of the architecture and the analytics dashboard you've been looking at numbers from, and then — the fun part — turning it back on, in public, with the live numbers on this site.

If you want the early, unfiltered version of all of this, subscribe — and until then, the entire track record is sitting on the public wallet if you'd like to pick it apart.