$147K Prediction Market Bot vs Your Trading Bot
A live engineering log: how we reverse-engineered a bot that made $147K in 20 days, and rebuilt the strategy from first principles using real quant math — Kelly Criterion, Markov chains, and directional momentum signals.
The Bot That Made $147K in 20 Days
While your bot was doing absolutely nothing, someone else's bot was running 564 trades per day across BTC, ETH, and SOL prediction markets, compounding to $147,000 in profit across 20 days. Win rate: 39.6%. Not 75%. Not even a coin flip. Losing more than half the time, and printing money anyway.

The math that makes this work is Expected Value — not win rate. Here is the calculation the $147K bot was running on every trade:
EV = (0.396 × 3x) − (0.604 × 1x)
EV = 1.188 − 0.604
EV = +0.584 edge per trade
× 11,282 total trades = $147,000+ realized profit
The asymmetry is the strategy. Losses are capped at 1x — the bot exits losers early. Winners are held to full resolution — 5-minute markets self-resolve and pay out at 100 cents. The bot never exits a winner early. It just waits.
The edge source is not prediction. It is reaction speed. Binance processes a BTC price move. The Polymarket CLOB for "Will BTC be UP in 5 minutes?" has not repriced yet. There is a window — sometimes 2 seconds, sometimes 10 — where you can buy the correct side at a price that already doesn't reflect reality. That gap is the edge.
The $147K Bot
- Edge source — reacts to BTC spot price moves before Polymarket reprices
- Action model — active taker on directional signal
- Trigger — BTC moves +0.3% in last 30s → buy YES on "BTC up in 5 min"
- Exit — hold losers to 1x cap, winners to full resolution
- Win rate — 39.6%, doesn't matter
Normal Bot (Before)
- Edge source — passive discount entry, wait for mispriced ask
- Action model — passive maker on spread capture
- Trigger — ask drops to 0.44 (structurally impossible)
- Exit — undefined, market resolves
- Win rate — 0%, because 0 trades
These are not the same strategy tuned differently. They are architecturally different animals. One waits for the market to offer a bad price. The other exploits the lag between when information exists and when the market prices it. Only one of these conditions ever occurs in practice.
The Quant Math: What Actually Has to Run
To build a prediction model that generates real edge on 5-minute markets, three mathematical frameworks need to work in sequence. Here is each one, with the exact equations we are implementing.

1. Momentum Signal — Binance bookTicker → Directional Probability
The raw input is a 30-second rolling return from the Binance bookTicker WebSocket. This fires on every quote update — not just trades — so latency is sub-second. The return is computed as:
Where P is the bid/ask mid from feed.latest('BTCUSDT').price
Threshold: |r_30| ≥ 0.0010 ← 10 basis points
Signal: UP if r_30 > +0.0010, DOWN if r_30 < -0.0010
At normal BTC volatility, 10 bps fires approximately 6 times per hour. That is enough signal density to find live windows. The momentum return then maps to a directional probability estimate:
At 10 bps: p_est = 0.50 + 0.001 × 100 = 0.60
At 20 bps: p_est = 0.50 + 0.002 × 100 = 0.70
At 30 bps: p_est = 0.50 + 0.003 × 100 = 0.80
The multiplier of 100 was calibrated so that minimum-threshold signals
still produce positive edge against the max_ask filter of 0.65.
2. Kelly Criterion — How Much to Bet
Once we have a probability estimate and an entry price, Kelly tells us the mathematically optimal fraction of the portfolio to risk. The formula:
Where:
b = net odds (for a binary market paying 1:1 at fair value, b ≈ 1/entry_price − 1)
p = estimated probability of winning (our p_est)
q = 1 − p
Example: entry_ask = 0.58, p_est = 0.65
b = (1.00 / 0.58) − 1 = 0.724
f* = (0.724 × 0.65 − 0.35) / 0.724 = 0.168 → 16.8% of portfolio
Applied at 25% fractional Kelly: 16.8% × 0.25 = 4.2% actual position size
Hard cap at 8% per position regardless of Kelly output
We run fractional Kelly at 25% — a quarter of the theoretically optimal bet. Full Kelly maximizes long-run growth but produces drawdowns that are psychologically unmanageable. At 25% Kelly, five consecutive losses on a $500 portfolio drops you to roughly $450. At full Kelly, the same five losses drops you to $295. The fraction is the price of sleeping at night.
| Kelly Fraction | 5 Consecutive Losses | Long-Run Growth | Verdict |
|---|---|---|---|
| Full Kelly (100%) | $295 remaining | Maximum theoretical | Dangerous in practice |
| Half Kelly (50%) | $380 remaining | Strong | Aggressive but viable |
| Quarter Kelly (25%) | $450 remaining | Moderate | Quant Saber's setting |
| Tenth Kelly (10%) | $480 remaining | Conservative | Paper mode starting point |
3. Becker Longshot Calibration — Correcting for Market Bias
Jonathan Becker's 2026 analysis of 72.1 million prediction market trades across $18.26 billion in volume found a systematic bias: cheap contracts are overpriced. People bet too much on unlikely outcomes. The calibration table:
| Market Price | Naive Probability | Becker Calibrated | Overpricing |
|---|---|---|---|
| 1¢ | 1.00% | 0.43% | −57% |
| 5¢ | 5.00% | 4.18% | −16% |
| 10¢ | 10.00% | 9.20% | −8% |
| 50¢ | 50.00% | 50.00% | — |
| 90¢ | 90.00% | 90.80% | +0.9% |
We apply Becker calibration as a downward correction to our p_est before passing it to the Kelly formula. If the momentum signal produces p_est = 0.60 but we are entering at 0.58, the corrected estimate prevents us from over-sizing based on a probability the market has already baked in via the longshot premium.
The New Strategy: Momentum5Min
The rebuilt strategy is a fleet manager — one scan loop watching all simultaneously open 5-minute windows, rather than spawning per-market instances. Every 5 seconds it runs the following logic:
if market_id in already_entered: continue
if secs_left < 30 or secs_left > 240: continue ← entry window
r_30 = btc_return_last_30s()
if abs(r_30) < 0.0010: continue ← no signal
side = "YES" if r_30 > 0 else "NO" ← UP/DOWN mapped to YES/NO
ask = clob.get_ask(market.token_id[side])
if ask > 0.65: continue ← max_ask filter, skip thin CLOBs
p_est = 0.50 + abs(r_30) × 100
edge = p_est - ask
if edge <= 0: continue ← risk manager gate
orders.open_position(token_id, market_id, side, ask, size_usd, p_est)
One entry per market window, ever. The market self-resolves. Auto-claim collects winners every 60 seconds. No exit logic needed — the 5-minute clock is the exit.
The Fix That Unlocked Everything
Three changes between the broken strategy and the working one:
The Deploy: What the Logs Actually Show
After the Railway deploy landed (commit 3c207d3, 332 insertions), the startup logs confirmed all 9 tasks running:
Starting Container
INFO | src.strategies.wolf_hour | Wolf Hour: starting continuous research + scan loop
INFO | src.strategies.momentum_5min | Momentum5Min: starting | lookback=30s threshold=0.10% max_ask=0.65
INFO | bot | All systems running | 9 tasks | PAPER
INFO | src.market.binance_feed | Binance feed connecting: wss://stream.binance.com:9443/ws
INFO | src.strategies.wolf_hour | SKIP future_window resolves in 13h 39m: [Bitcoin Up or Down - May 22, 6:10AM ET]
The SKIP future_window lines are correct behavior. At 10:20 UTC, every visible 5-minute window was tomorrow morning's pre-scheduled market — not a currently-open window. The momentum scanner will emit SKIP ask_too_high when CLOBs are thin, and ✅ ENTRY when a live window has real liquidity. Both are expected.
The first entries will appear during US market hours (1pm–9pm ET) when 5-minute market volume picks up and CLOB depth materializes. The heartbeat will show it:
📊 Quant Saber Heartbeat
Balance: $500.00 | PnL today: $+0.00 (0.0%)
Trades: 3 | Win rate: 66.7%
Wolf trades today: 0 | Paper PnL: $+0.00
Momentum trades today: 3 | Momentum PnL: $+0.21
Open exposure: $15.00
That is what we are waiting for. The balance will not move much at first — $500 paper capital with 2% sizing means $10 per trade. The important number is the win rate convergence over 200+ trades. Does the directional signal actually predict direction better than 50%? The paper phase answers that question before any real capital is at risk.
The Road to the $147K Profile: What You Need To Build
The current Momentum5Min strategy is the foundation. Getting to the full $147K-style performance profile requires three more pieces that are not yet in the code.

1. EV-Asymmetric Exit Logic
The $147K bot's real edge is not the entry signal — it's the exit discipline. Losses capped at 1x, winners held to full resolution. Right now Quant Saber holds everything to resolution because 5-minute markets self-close. That is correct for winners. For losers that are clearly going the wrong direction mid-window, there is an opportunity to exit at 40 cents on a position that will likely resolve at zero — recovering 40% of the loss instead of 0%.
The math of early exit on losers:
Market moves against us: YES now trading at 0.35 with 90s left
Hold to resolution: EV = 0.65 × 1.00 + 0.35 × 0.00 − 0.58 = +0.07
Exit now at 0.35: guaranteed recovery = 0.35 − 0.58 = −0.23 loss
vs hold: expected outcome = 0.65 − 0.58 = +0.07 expected
Conclusion: if p_est > exit_price, hold. If p_est < exit_price, exit early.
The signal is whether your directional view has been invalidated by new price action.
2. Multi-Asset Signal Correlation
Currently the BTC signal trades BTC markets and the XRP signal trades XRP markets independently. But BTC and XRP are correlated — a strong BTC move often leads an XRP move by 15–30 seconds. A cross-asset signal that uses BTC momentum to pre-position on XRP markets before XRP reprices would add a second edge source without requiring any new data infrastructure. The Binance feed already has both symbols.
xrp_return_15s = (xrp_now - xrp_15s_ago) / xrp_15s_ago
if xrp_return_15s < 0.0005: ← XRP hasn't moved yet
signal = BTC_LED_XRP_UP ← enter XRP UP market
The edge: BTC already moved, XRP CLOB hasn't repriced yet.
You are buying the lag, not predicting the move.
3. Taker Mode in the Final 10 Seconds
The current max_ask = 0.65 filter correctly skips thin CLOBs where the ask is pinned at 0.99. But in the final 10 seconds of a 5-minute window, something changes: market makers start pulling their resting quotes because there is no time left to hedge. The ask briefly collapses from 0.99 to something real — 0.55, 0.60 — as the market converges to its resolution price.
A secondary scan that checks CLOB depth every 1 second in the final 15 seconds of a window, and fires a taker order the moment ask ≤ 0.65 appears, would capture entries that the 5-second scan cycle misses. This requires a faster scan loop only for markets inside the last-15-seconds window — a lightweight secondary task, not a rewrite of the fleet manager.
The Circuit Breakers: Why They Are Not Optional
Every math framework above produces edge in expectation. None of them work every trade. The circuit breakers are what prevent a bad day from becoming a blown account:
- Daily −20% halt — if portfolio drops 20% from day-start balance, all trading stops until manual restart. On $500, that is a $100 loss before the bot goes dark. Something is structurally wrong if that happens.
- 60% ATH drawdown halt — if portfolio falls below 60% of all-time high, the bot stops permanently until reviewed. A 40% drawdown means the strategy's edge has disappeared or market conditions have changed fundamentally.
- 5-loss streak pause — after five consecutive losses, 30-minute pause. This catches the scenario where volatility has spiked and the 30-second momentum signal is generating noise, not signal.
The Paper Phase: What 200 Trades Actually Tells You
Stay in paper mode for 200 trades first. Trade with $500 balance of fake money. The goal of the paper phase is not to accumulate fake profit — it is to answer four specific statistical questions before any real capital is at risk:
| Question | Minimum Threshold | Why This Number |
|---|---|---|
| Does the directional signal work? | Win rate > 50% | Below 50% means the signal is noise. At 39.6% with 3:1 ratio you still need >33% — we target higher. |
| Is the EV positive? | Paper PnL > 0 at 200 trades | 200 trades is enough for the law of large numbers to suppress variance. If EV is positive it shows up by then. |
| Is the infrastructure stable? | 7 consecutive days, no crashes | Railway restarts, Binance reconnects, Polymarket timeouts — all need to be handled before real money is on the line. |
| Does the win rate hold at scale? | Consistent across weeks | A good first week can be variance. A consistent second week is signal. |
This is the step most people want to skip. The bot is running, the math is there, the signal looks real — why wait? Because paper performance and live performance can diverge significantly when real capital introduces fill slippage, position limits, and psychological interference. The 200-trade threshold is not superstition. It is statistics.

Read the full Groove playbook →
→ Claim Free Access to Groove
What Do You Think?
Running your own version? Hit a different bug? Got a take on the p_est multiplier calibration? Drop it below — this is where the real conversation happens.