Skip to content
LutzTalk
Go back

I Tried to Beat Prediction Markets

Updated:
39 min read

It was a Tuesday afternoon in the middle of the MLB season, which mattered only because it meant my data pipeline was finally full. I had been backtesting Kalshi’s binary contracts, the ones where you buy YES or NO on something like the Yankees beating the Royals tonight. And there it was. Slight favorites, the teams Kalshi priced between 55 and 60 percent to win, were beating their implied probability by seventeen percentage points. The line said they should win 55.3 percent of the time. They were winning 71.5 percent of the time.

That is an enormous gap. T-statistic of 5.4. P-value of 1.4 × 10⁻⁷. The kind of number you check three times because it cannot possibly be right.

It came back the same every time.

I let myself sit in the obvious feeling for a minute. Holy shit, I found it. A clean, well-defined inefficiency in a real market with real liquidity, sitting in the open, exploitable with two lines of logic. Buy YES on every Kalshi MLB market that opens between 0.50 and 0.60. Hold to settlement. Collect the seventeen points minus fees.

The math worked too. Kalshi’s fees run about four cents per round trip, and the bid-ask spread adds another one or two, so call it six cents of friction. Seventeen percentage points of edge is roughly seventeen cents per contract in expected value. Net about eleven cents. The MLB book could support maybe a thousand dollars of expression at that size, so eleven cents on fifty contracts a day is $5.50 a day, every day, on a venue where I did not have to predict anything I was not already getting from public information for free.

I started drafting the deploy plan. Position sizing, walk-forward validation, and the kill switch.

Then I did the one thing that ended up mattering. Before I funded the live account, I ran the candidate through the validation harness I had spent three weeks building. The harness has seven gates. It killed the strategy in about eight minutes.

The seventeen-point edge was not real. It was not close to real. Once I controlled for candle-coverage survivorship bias, a technical artifact in how Kalshi’s API serves historical price data, the gap fell to negative 2.6 percentage points on a much larger sample, statistically indistinguishable from zero. The signal I had been certain about was a measurement error in my own pipeline. If I had skipped validation and funded the account, it would have taken roughly the whole thing inside twelve weeks.

I want to walk through exactly what that artifact was, how the harness caught it, what else died the same way, and what is left of the retail prediction-market trading dream after seven kills in a row.

I spent six weeks trying to beat Kalshi and lost $1.57. The $1.57 is almost the worst part. I would rather have lost a thousand dollars and had a story. What I got instead was a precise understanding of why retail prediction-market trading does not work at this scale on this venue.

Why I started

Prediction markets like Kalshi and Polymarket are the financial product that ought to work. Clear structure, binary outcomes, well-defined settlement. There is a deep academic literature (Wolfers and Zitzewitz, 2004) showing that their aggregate prices are well-calibrated forecasters of real-world events. Minimum bet sizes are in the low tens of dollars. The APIs are open to retail. The US venues are regulated. And by 2026 there was a real body of informal claims that retail traders were making consistent money on them.

I am the sort of person who reads finance Twitter and notices when the same edge claim keeps surfacing. By spring 2026, “I’ve been making $200/day market-making on Kalshi” had been showing up in my feed about once a week for six months. The claim was specific enough to be interesting. The venue was small enough that institutional money hopefully had not optimized it away yet. And the mechanic, buy at the bid, sell at the ask, collect a few cents of spread on every round trip, was simple enough that I could just go try it.

I am also the sort of person who builds infrastructure before I build anything else. The first three weeks of this project went entirely into groundwork:

That last bullet is the foundation everything else rests on. It is also why the MLB signal did not survive. A t-statistic of 5.4 sounds impressive right up until you remember you had nineteen other hypotheses queued behind it.

So that was the setup. The data, the costs modeled correctly, and the harness. Then I started testing edges.

Predictive models

The most obvious thing to try, and the thing every retail quant tries first, is to build a better model than the market. If Kalshi prices the Knicks at 58 percent to beat the Cavaliers tonight and your hand-rolled model says 64, you buy YES, hold to settlement, and collect six cents of edge per contract.

I built six predictive models over two weeks.

Weather. Kalshi runs contracts on tomorrow’s high temperature in various US cities. I took this one seriously. Pulled NWS hourly forecasts, ran a deterministic numerical-weather-prediction stack, scored its calibration against settled outcomes. The oracle backtest, where I gave the model perfect forward knowledge of tomorrow’s temperature, won 73 percent of trades. With the real production NWS forecast, it lost 0.07 cents per contract net of fees.

The reason is that the Kalshi market is already pricing an ensemble of forecasts, ECMWF and GFS and NWS together, and that ensemble dominates any single source. My single-source model was strictly worse than the consensus the market had already integrated. Even in the cases where my model was right, the market was a tighter combination of better information.

There was a second problem underneath the first. Most weather contracts on Kalshi have close to zero trading volume, so there was nowhere to express the trade at any meaningful size. Even a real edge would have had no money on the other side of it.

NBA team strength. I built an Elo model on NBA team performance, calibrated it on three years of regular-season data, and scored it against Kalshi’s pre-game win-probability contracts. Brier score of 0.218 against the market’s 0.224, roughly a 3 percent calibration improvement. Promising on its face.

Then I looked at the disagreements, the cases where my model and Kalshi materially differed, which is where the money would actually come from. The model was correct 35 percent of the time. Worse than a coin flip. Kalshi was tracking sharp sportsbook lines closely enough that my Elo model was effectively noise relative to the true probability, and the disagreement segment was an artifact of my model being underspecified.

The multi-horizon test made it plain. At six hours before tipoff my Elo model scored 0.235 against Kalshi’s 0.224. At one hour before tip, 0.222 to 0.220, roughly even. At fifteen minutes before tip, 0.218 to 0.232, and Kalshi beat me outright. The market was continuously absorbing sharp information right up to tip-off and my static model was not. The edge I thought I had found was the early-game window where my model happened to be about as bad as the market, and it inverted the moment the market started actually working.

MLB pitchers. This was the most serious modeling effort of the six. Starting-pitcher-aware models, same-day lineup data, sportsbook line ingestion, the whole nine yards. Brier score on a powered sample of 1,287 games: 0.250 for the model against 0.225 for the market. My model was worse. Even on the narrow subset of extreme favorites above 85 percent where the model and market disagreed, it lost four cents per contract across thirty-nine trades, which is below the significance threshold and below anything worth chasing.

ATP and WTA tennis. Two attempts. The first found a 7 percent edge buying favorites in low-liquidity ATP tournaments. The second found a similar gap in WTA underdogs. Validated against the full population with proper survivorship controls, both evaporated. ATP favorites inverted to a 24.3 percentage-point loss on the both-legs-covered subset. WTA failed differently, volume cherry-picking plus closing-line drift, but arrived at the same place, with no capacity and no edge.

Crypto tails. Kalshi runs daily and monthly above/below contracts on Bitcoin, Ethereum, Solana and others. I built a Black-Scholes-style tail-probability model off stale spot prices and implied volatility surfaces from Deribit. The model produced reasonable tail probabilities, and the market priced them at a sixteen percentage-point premium on average, concentrated in the deep out-of-the-money strikes. That premium is real. It is the volatility risk premium, and it has been documented for two decades. But Kalshi’s bid-ask spread on the affected strikes was wider than the premium itself, so there was no way to execute against it.

Gas prices. This one was painful to kill because the model was good. R² of 0.54 on daily Brent crude moves against publicly available AAA daily-average data. The catch is that the market knows about AAA’s daily average too, and prices it within minutes of release. Every threshold I tested came out at negative seven cents per contract after fees, on twenty-two effective independent events, which is nowhere near enough events to have statistical power in the first place.

Six models, none survived. Two weeks of evenings. The honest takeaway from the first wave was that Kalshi’s prices are sharp. The venue is not priced by amateurs anymore, if it ever was. It is priced by professional arbitrageurs running against the sharpest available sportsbook and information feeds, and a retail account with a hand-rolled model is on average worse than the market it is trying to beat.

That was hard to internalize. Going in, I believed “build a better model” was the canonical retail approach. It turned out to be the easiest one to disprove.

Structural arbitrage

If you cannot out-predict the market, the next thing to try is finding places where its prices contradict each other. Scan the venue for situations where the numbers do not add up, and execute risk-free.

I tested four structural angles.

Within-event arbitrage. Kalshi often runs several binary contracts on the same underlying event. A snowfall market might have separate contracts for 0 to 3 inches, 3 to 5, 5 to 10, and 10 or more, and those four buckets have to sum to 100 percent. If they do not, you buy the underpriced subset, sell the overpriced subset, and lock in the difference.

I scanned roughly 45,000 settled markets for logical violations. The largest one I found netted negative four cents per contract after fees and spread. The violations are there. They just live entirely inside the bid-ask spread. To capture one you have to cross both legs at once, paying the spread on each, and the spread swallows the gap. Across the four-year dataset I did not find a single executable lock.

Cross-market monotonicity. Same idea, slightly more sophisticated. If Kalshi prices “S&P 500 closes above 5,200 today” at 60 percent and “S&P 500 closes above 5,180 today” at 55 percent, that is a logical violation, because the first outcome implies the second and the second probability cannot be lower. I built a scanner and it found violations constantly. None were executable, for the same reason as before. The bid-ask spread always exceeded the size of the violation.

Cross-venue arbitrage with Polymarket. This was the one I had the most hope for. Kalshi is the US-regulated venue and Polymarket is the global crypto-collateralized one, and the two often run near-identical contracts. Same FOMC decision, same election, same Trump statement. If their prices diverge by more than transaction costs, you buy the cheap venue and sell the expensive one.

I built a cross-venue diff scanner. Over six weeks it found exactly one cleanly matched pair: the FOMC rate decisions. The gap on those averaged 2.5 percentage points in absolute terms, sitting inside the combined transaction-cost floor of both venues.

The rest of the apparent overlaps turned out to be settlement-rule mirages, and the Bitcoin contracts were the most tempting example. Kalshi settles Bitcoin against CF Benchmarks BRTI at 5pm Eastern. Polymarket settles against a Binance volume-weighted price at noon UTC. Those are two different bets that happen to share a subject. The arbitrage is not there because the venues are not substitutes for each other.

Parlay correlation. If Kalshi runs both single-game markets and parlay markets, and the parlay is priced above the product of the individual probabilities, you sell the parlay and buy the components. I found 59,675 plausible combinations. None had executable liquidity on the combo side. The combo books are buy-only, priced above the independence product because of vig, with no contra-side sellers. The math says sell the overpriced combo, and there is nobody to sell it to.

Four structural angles, none exploitable. The pattern was identical each time. The inefficiencies exist, in the sense that prices do not always perfectly cohere, but the bid-ask spread is calibrated tightly enough to the size of the typical inefficiency that you cannot trade through it. Kalshi’s market makers run tight, low-latency books, and they have priced the spread to absorb exactly this kind of structural noise.

The favorite-longshot bias

There is one persistent empirical regularity in prediction-market pricing that has survived three decades of academic scrutiny. It is called the favorite-longshot bias. Bets on outcomes with very low implied probabilities, longshots, typically below 15 percent, lose money on average against their realized win rates. Bets on heavy favorites above 85 percent make money on average. Griffith first documented it in horse racing in 1949, Snowberg and Wolfers replicated it on Tradesports in 2010, and it shows up in nearly every prediction-market dataset anyone has ever analyzed.

So: fade longshots, buy heavy favorites, collect the bias. I tested it three ways.

Weather longshot fade. Buy NO on Kalshi weather markets where YES is priced at 15 percent or lower. Sample of 252 trades drawn from 671 settled weather markets, VWAP-priced. Reported result: positive 4.4 cents per contract, t-statistic of 9, nearly a 100 percent win rate.

I almost deployed this one. The numbers were good and the win-rate-versus-implied-probability calibration looked dead on. The only thing that gave me pause was that another agent on the project had run a similar test against the same data and come back with a much more modest 0.6 cents per contract at a t-statistic of 0.44, which is no edge at all. Two adjacent backtests, same hypothesis, same data, wildly different answers.

So I dug in. The 4.4 cent result was pseudo-replicated. It was treating every weather contract as an independent trade when dozens of contracts on the same city-day are essentially one bet. After event-clustering, effective N dropped from 252 to about twenty-eight. The 0.6 cent result had its own problem, VWAP-versus-real-ask substitution, because VWAP includes mid prices you cannot actually execute against. Priced at the real ask, the bias was 0.24 cents at a t-statistic of 0.27. After Bonferroni, zero. And 87 percent of the trades had landed inside a single warm-anomaly regime between January and March 2026, where the model had a structural advantage that would not generalize past it.

Four separate artifacts stacked on one candidate: pseudo-replication, non-executable price substitution, single-regime selection, and settlement leakage. Any one of them alone would have been enough to hide a real edge if one existed, and stacked together they manufactured one that was never there.

MLB longshot fade. Same play, run on MLB markets where I had better volume. Sample of 513 trades, both legs covered, event-clustered, Bonferroni-corrected. Result: positive 2.3 cents per contract, t = 0.37, with the bottom of the 95 percent confidence interval at negative ten cents per contract.

The bias itself is real here. NO contracts at YES ≤ 25 percent won about 91 percent of the time against an implied 80, a sixteen-point bias, which is qualitatively the largest in the literature. But the spread on those markets is calibrated to it almost perfectly. The market makers know about the favorite-longshot bias too, and they post spreads sized to consume the predictable edge. Net expected value: zero. Capacity: about $1,500 of daily volume, which would be a fine constraint if the EV were not zero.

This is the same finding the academic work reports in horse racing and sports betting. The bias is empirically real, its execution cost is empirically calibrated to it, and documenting it is a different thing from being able to trade it.

Fade slight favorites. This is the candidate I opened with, the seventeen-point MLB signal I almost funded. It died the way I described up top. Candle-coverage survivorship plus pre-resolution leakage plus single-regime selection stacked into a fake +17 that collapsed to −2.6 on both-legs-covered data. The harness caught it eight minutes before it could do any damage.

That is three flavors of the same bias and none of them survived. The favorite-longshot bias passes academic scrutiny because the right comparison there is implied probability against realized probability. Change the comparison to the one that matters operationally, which is whether you make money trading it, and the answer is no.

Candle-coverage survivorship bias

I want to slow down here, because this is the single most important thing I learned on this project.

Kalshi’s API has a candlestick endpoint that returns one-minute and one-hour price candles for each market. A candle gets created when at least one trade occurs in the window. If no trades happen, if the book is quiet, no candle is written.

Now think about what that means for a binary contract that resolves YES.

In the hours and minutes before resolution, traders keep quoting and trading the winning leg, because its value is converging on a dollar. There is liquidity, there is interest, there are people buying and selling on small fluctuations near the settlement value. Candles keep getting written.

The losing leg does the opposite. Once the outcome is statistically obvious, nobody trades it anymore. The order book thins, then empties, and stays empty through the final minutes before resolution. No trades, no candles.

What that produces in the raw data is this: the winning leg of a settled binary contract is meaningfully more likely to have candle coverage in the final hour before resolution than the losing leg. Across sports, tennis, and macro categories in my powered dataset, the gap ran seven to eight percentage points.

Now think about what that does to a backtest.

If you filter your trades on “markets with at least one settled candle in the last hour before close,” which is a completely reasonable data-quality filter and the sort of thing every careful backtest does, you have accidentally filtered on the outcome. The trades that survive your filter are seven to eight points more likely to be on the winning side.

For a strategy like buy slight favorites on MLB, that looks exactly like favorites winning 71.5 percent of the time when they should win 55.3. It was the same sixteen-point illusion I saw on that Tuesday afternoon. A filter that reads as “show me markets with active price history” is functioning as a survivorship filter that selects winning legs into your sample.

The control is easy to state and annoying to implement on a typical retail pipeline. You require both legs of every binary contract to satisfy your candle-coverage criterion. If the YES leg has a candle and the NO leg does not, the event is excluded entirely. This is the both-legs-covered control and it is the most important methodological correction in the whole project.

Once I implemented it:

By my estimate this one bias accounts for somewhere between 60 and 100 percent of the apparent edge in seven of the nine candidates I evaluated. It is the dominant artifact in retail prediction-market backtests, and correcting for it does not shave a few points off your result, it removes most of what you thought you had.

As far as I can tell, and I did go looking, this specific mechanism is not documented in the prediction-markets literature. Survivorship bias in general is very well covered. Brown, Goetzmann, Ibbotson, and Ross wrote up the mutual-fund version in 1992 and it has been replicated many times since. But the venue-specific mechanic, where winning-leg trading activity continues past the point of decision while losing-leg activity stops, asymmetrically populating a trade-driven candle endpoint, is something I worked out the way I usually learn things, which is the hard way and at my own expense. I wrote it up formally in a separate paper called “An Adversarial Validation Harness for Retail Trading-Edge Claims in Binary Prediction Markets.” Email me at austin [at] lutztalk [dot] com and I will send it over.

The implication is uncomfortable. A non-trivial share of published retail-edge claims on Kalshi and Polymarket are very probably this artifact and nothing else. If you have read a thread that says “I backtested this strategy on Kalshi and it makes 10 percent a month,” and the author never described a both-legs-covered control, the odds are good that what they found was candle-coverage survivorship. Of everything here, this is the part I would defend as an actual contribution.

The pivot to market making

By week three I had accepted that predictive alpha was dead. Six models, no edges. Whatever advantage existed on the venue was not available to me as a model-builder.

But there was a second category of retail claims I had not touched. Be the market maker instead of the model-builder. Stop trying to predict where the price is going, post liquidity at the bid and the ask, collect the spread on every round trip, and manage adverse selection.

Kalshi’s fee structure makes this look attractive on paper. The maker fee is 25 percent of the taker fee, a 75 percent discount. The venue runs a Volume Incentive cashback program and a Liquidity Incentive Program that pays daily reward pools to accounts that rest size on the book. The $200 and $500 per day claims I kept seeing on Twitter almost always referenced these programs.

So I built a full market-making harness:

  1. Subscribe to the live WebSocket order book.
  2. Identify viable markets, a narrow shortlist of deep, low-adverse-selection books. Crypto strikes far from the money, deep sports championship books.
  3. Post symmetric quotes at the touch on both the YES buy and YES sell sides.
  4. Re-quote when the touch moves.
  5. Manage inventory with a flatten rule, closing any unwanted position as the spread comes to you.
  6. Pull or widen on adverse signals: large recent moves at two cents of momentum or more, wide spreads at three cents or more, and proximity to event resolution.
  7. Apply a regime filter, only quoting crypto when the mid sits between 8 and 92 percent YES and more than thirty minutes from expiry.

The thesis was that on a narrow viable shortlist, symmetric quoting clears breakeven after measured adverse selection. The economics my agents reported back:

This was the most promising thing I had found, and it is worth pausing on how it was produced. By this point the project was not me typing into a backtest framework. I had quants, a Bayesian statistician, a hedge-fund-style risk manager, and microstructure specialists all working different angles. The $100 a day on $10,000 figure was the consensus of three independent specialists coming at it separately, and it cleared the early validation gates.

So I started building the live market maker. Real WebSocket, real Kalshi authentication, real order placement code path. The order path sat behind a PaperOnlyGuard and a DisabledRestOrderPlacer, a chain of safety interlocks that made it impossible to transmit an order without walking through a series of deliberately annoying confirmation windows. The bot would simulate. It would not execute.

The market-making reality check

With the paper-only market maker running, I backtested the actual strategy code against eighteen months of real Kalshi data. Not a model of the strategy. The literal code that was about to be deployed, replayed against tick data.

Across 1,555 market-days, 861 markets, and 6,983 simulated fills:

The headline thesis of $100 a day at $10,000 of capital was off by one to two orders of magnitude. The most charitable reading, assuming a full VIP rebate I would never actually receive at retail volume, was about $3.40 a day on the sports book. Without VIP it was negative.

What had the original agents missed? Two things.

First, they had focused on spread capture, the +0.2 cents, and not on inventory carry to settlement, the −$1,321. The spread you collect on a fill is only the gross. If you cannot exit unwanted inventory before resolution, the position is held to outcome, and outcomes are adversely correlated with the prices at which your quote got hit in the first place. This is the thing every market maker has known forever. Post a YES sell at 52 cents on a market that resolves YES and you have just paid the venue 48 cents per contract for the privilege. Your 0.2 cent spread is a rounding error against that.

Second, they had assumed retail-tier access to the full VIP rebate. Kalshi’s VIP program is a shared pool where your share of the daily payout is proportional to your share of daily venue volume. At retail volume, meaning tens of contracts a day, that share is a fraction of a percent. The $0.005 per contract figure is the cap, not what a small account realizes. My realized rebate was closer to $0.0001 per contract, three orders of magnitude below the cap.

I tried every lever I could think of to rescue it.

Cloud co-location for latency. Maybe I was getting picked off by faster participants. I ran the sweep at 4ms, 25ms, 80ms, and 173ms. Net P&L was flat to the cent across all four. Going from 173ms to 4ms recovered exactly zero dollars. The decomposition explained why. Of the −$1,321 of inventory drag, +$411 was latency-sensitive, meaning good fills lost to slow reactions. The remaining −$1,732 was structural carry, slow one-sided accumulation in trending markets, and no amount of speed touches that. At a perfect zero milliseconds the structural carry stays put, and the +$411 of good latency-sensitive fills disappears too, because those fills only existed in scenarios where the slow reaction was what created them.

Inventory management. Hard caps, cross-to-flatten, regime avoidance. I rebuilt the strategy with aggressive inventory controls and it came out $78 worse. The cost of honestly crossing to flatten roughly equaled the inventory carry it saved, and the hard caps turned out to be redundant with the regime filter I already had. At one point this variant showed a tempting +$1,704, which was another artifact. When you replay candle data, intra-candle paths get interpolated, and the interpolation lets your flatten quotes fill at prices a real trending market would never have offered you.

Adverse-selection control. This was the make-or-break. The hypothesis was that variance, not direction, is the predictable part, and that a symmetric pull rule could cut the adverse cost. It tested positive on deep sports. A filter on absolute momentum at two cents or more, or spread at three cents or more, or proximity to the event, cut adverse cost by 87 percent in deep sports books, flipping −0.31 cents per decision to +0.13. So at the segment level, deep sports only, with real adverse-selection discipline, the strategy could clear breakeven. Near-expiry crypto stayed negative and the regime filter simply excluded it.

With every lever pulled, the honest aggregate was empirically breakeven, neither profitable nor losing money.

Market making on Kalshi at retail scale was dead. The $100 a day model was wrong by an order of magnitude. The most you could honestly say is that the strategy might clear breakeven on deep sports books if you are extremely disciplined about regime filtering and adverse selection.

This was the low point. Three weeks of infrastructure for a strategy that, once measured honestly, did not work. The team converged on the verdict in a single afternoon. No durable retail trading edge exists on Kalshi, predictive or structural or market-making, and the only reliable positive on the venue is the 3.25 to 3.75 percent APY Kalshi pays on deposited capital. Most HYSAs pay that with no risk at all. I sat with that for a day before I started looking seriously at the APY.

Structural mechanisms

If active trading is dead, what about passive structural mechanisms? Kalshi has two.

Deposit APY. Kalshi pays roughly 3.25 to 3.75 percent annualized on your portfolio net value, covering both idle cash and the collateral backing open positions. For US members with a $250 minimum, this is essentially risk-free. On a $50,000 deposit that is $1,750 a year, uncorrelated with anything you do as a trader.

The Liquidity Incentive Program. A daily reward pool Kalshi pays to accounts that rest size on designated incentivized markets. The mechanism is structural rather than predictive. Kalshi pays for liquidity provision by program design, and the trader does not need to be right about anything. They need to be present, with size, at the touch.

What I did not know yet was that the LIP rebate at retail capital is too small to exceed the adverse-selection cost of the books you have to sit on to collect it. But the concept was appealing. A source of yield that did not depend on outsmarting anyone.

That question, can I mechanically extract a rebate, is where the project turned. The next two weeks went into building the LIP harvester.

The LIP harvester

I built the harvester in a progression I ended up calling Paths A through E. Each path solved a real problem and surfaced two more. In retrospect it is the most back-assward part of the story.

Path A, the one-shot CLI. The first version was a lip-post command you ran by hand to post a target quote pair on one of two whitelisted markets. Every guard was in place. Hard-coded ticker whitelist with only two markets, both political contracts with known LIP activity. Maximum contract count of 100. Maximum exposure of $500. Dual-arming, requiring both the --live CLI flag and the KALSHI_LIVE_ORDERS_ARMED=true environment variable. A kill-switch file. Full audit log of every order attempt.

The first real order I placed through it was 100 contracts of KXTRUMPPHOTO-26JUN07 YES at seven cents, seven dollars of working capital, followed by the YES sell side at eight cents. My first real money on the venue. Six dollars of inventory exposure, all of it directional, in a market I had no intention of being directional in. A one-shot CLI is not a market-making strategy so much as a way to accumulate adverse positions by hand.

Path B, the continuous harvester. The next iteration was a daemon that ran continuously, subscribed to the WebSocket book, posted at-touch quotes, cancelled and re-quoted when the touch moved, and flattened inventory on fill. Good architecture. Then I tried to run it as a 24/7 operation, and here is everything that went wrong:

  1. The auto-sizer divided cash by per-pair cost across the whole whitelist, including markets whose order books were empty. With one ticker quiet, the safe-size computation returned None and the daemon halted itself. Fix: handle partial-touch sizing, size off the markets that have data, skip the rest.
  2. The placer’s MAX_ORDERS_PER_INVOCATION = 4 was scoped per daemon lifetime rather than per quote cycle. After the initial four-order cycle, every subsequent re-quote was refused. The daemon ran for thirty minutes generating 2,638 PLACE_REFUSED audit rows before I caught it. Fix: instantiate a fresh placer per call so the cap protects each cycle.
  3. On startup the daemon posted at --quote-size 100 before the auto-sizer had scaled down to my actual cash. Kalshi rejected it with insufficient_balance and the daemon hard-halted. I watched it cycle through three launches and three crashes over about thirty minutes before I worked out what was happening. Fix: wait for the first successful auto-size computation before posting anything.
  4. The whitelist was static, so markets that dropped below the LIP eligibility threshold stayed whitelisted anyway. Fix: re-check per-market eligibility every five minutes, pause quoting on markets that fall below target size, resume when they recover.
  5. The eligibility re-check tried to filter Kalshi’s /incentive_programs endpoint by ticker. The endpoint does not support that filter, and Kalshi has 1,690 active programs spread across nine paginated pages, so the daemon was only ever seeing page one. Every whitelisted ticker whose program lived past page one came back no_active_program and got paused. Fix: paginate the full list, filter locally.
  6. The Kalshi WebSocket feed dropped fill notifications, and the daemon’s fill detection was built entirely on WebSocket events. That meant the daemon was blind to fills. Real fills were happening, and Kalshi’s /portfolio/fills REST endpoint showed seven of them overnight, while the daemon’s audit log showed zero FILL events. With no fill detection the daemon never flattened the resulting inventory, and adverse-selection losses piled up silently. Fix: poll /portfolio/fills over REST every thirty seconds as a defensive fallback, regardless of what the WebSocket claims.

Every one of these was an honest bug, and every one of them surfaced only in production with real money on the line. The unit tests covered the bug-free path. The bugs lived in the integration with Kalshi’s live API, which is the part unit tests cannot reach. Systems that look correct in development will still fail in production, and the cost here was about fifty cents of realized losses across the night they happened, plus the embarrassment of waking up to positions I had never heard of.

Path C, strict eligibility curation. The original two-ticker whitelist closed in early June. I built a scan script that paginated Kalshi’s full LIP program list, applied a quality filter (spread ≤ 10c, daily pool ≥ $1, depth ≥ 50 contracts at the touch, 72 hours or more to close, two-sided book, active program, not already paid out), and selected the top fifteen by risk-adjusted expected income. The model predicted about $14 a day at $5,000 of working capital across that whitelist.

Path D, stricter eligibility. Then I audited which of those fifteen markets were currently paying LIP, meaning their total resting volume at the touch was actually meeting the program’s target_size minimum. Only three of the fifteen were above target. The other twelve were below it, which means the pool was paying nothing at all for snapshots taken on them. I tightened the filter to thinner_side_depth >= target_size and re-curated. The honest top-N from the stricter filter was four markets, not fifteen, with a cumulative risk-adjusted expected income of $2.83 a day at the full $1,500 of deployable capital.

The model fell by a factor of five between Path C and Path D, entirely because Path C had not been honest about which markets were actively paying.

Path E, runtime continuous eligibility. Markets drop below target during the day, come back, and drop again. A whitelist is static between re-curations, so a market that failed the gate at noon would keep receiving quotes until the weekly re-curation caught it. The fix was per-market eligibility re-checking every five minutes with automatic pause-on-fail and resume-on-recover, leaving the whitelist as the structural safety net and eligibility as the runtime decision maker.

By the end of Path E the architecture was complete. The bot ran 24/7, posted only on currently eligible markets, auto-sized to available cash, auto-flattened on fill, halted gracefully on insufficient balance, and respected the kill switch. It also did not make money.

The truth

One Thursday morning I woke up to find Kalshi’s UI saying I was down overnight. My own portfolio-value calculation, pulled from the API, said I was up $9.45. I was not up $9.45.

The portfolio value Kalshi’s API reports includes locked collateral at face value, so a YES sell I had posted at fourteen cents was being treated as if it were guaranteed to settle at fourteen cents. Kalshi’s daily P&L is realized plus mark-to-market, which correctly accounted for the fact that my YES sells had been adversely filled overnight and were now underwater against where the touch had moved.

So I went through the overnight logs.

The audit log showed zero FILL events from the night. The daemon had not detected anything. Kalshi’s /portfolio/fills endpoint showed seven fills, including three on KXTRUMPPHOTO-26JUN07 (a YES buy, a YES sell, another YES buy), one on KXEOWEEK, one on KXTRUMPENDORSEMENTS, and two on KXBTCMAXMON, which was not even on the current whitelist. Those last ones were orphan fills on a market I had dropped during the Path D re-curation. The resting orders had been left in place, and overnight somebody else’s order came along and hit them.

Realized P&L on the affected events was −$0.49 on KXTRUMPPHOTO and −$0.07 on KXVOTEHUBTRUMPUPDOWN, so −$0.56 net for the night, with open positions additionally underwater on mark-to-market.

I had built the bot to flatten inventory on fill. The bot did not detect the fills, so it did not flatten, so I spent the night directionally short a basket of Trump-related contracts while the market moved against me.

I patched fill detection that evening. The lesson underneath it is about scale. At $40 of working capital, an adverse-selection event costing $0.56 is roughly three days of expected LIP rebate incinerated in a single night. The economics are too thin to absorb even small operational errors, and the errors are not optional.

Here are the all-time numbers from the whole experiment:

The LIP rebate that was supposed to offset all of this never showed up. Zero verified LIP credits in my cash balance after weeks of operation. It is possible the rebate landed and Kalshi’s API simply did not expose it on any endpoint I could query, but either way it was not arriving at a rate that exceeded the adverse-selection cost of running the harvest.

What I actually learned

These are the facts as I measured them.

Kalshi is efficiently priced for retail. Across nine plausible candidate edges spanning predictive, structural, behavioral, market-making, and latency approaches, none survived validation. That was roughly thirty specialist agents working full-time against a powered dataset of 71,316 settled markets with proper statistical controls, and none of the nine cleared the gate. The venue is, by my measurement, an efficient market for a retail participant. This says nothing about inefficiencies available to institutional participants with different cost structures, low-latency access, or non-public information. For a retail account, it is efficient.

Candle-coverage survivorship bias is the dominant retail-backtest artifact. Asymmetric coverage of the winning versus losing legs of a binary contract in a trade-driven candle endpoint accounts for the majority of the artifact magnitude in retail prediction-market backtests. Anyone publishing a strategy on Kalshi or Polymarket without applying a both-legs-covered control is probably looking at this rather than at a real edge. Please prove me wrong.

The fee wall is structural and binding. Kalshi’s fee schedule is ⌈0.07 × p × (1-p) × N⌉ cents per order. At p = 0.5 that is 1.75 cents per contract per leg and 3.5 cents per round trip. Combined with a typical spread of one to three cents, round-trip friction on any active enter-and-exit strategy is five to eight cents per contract. No retail-detectable predictive edge I found was larger than five cents. The fee wall alone is not an argument against retail trading, but stacked on top of already-sharp pricing it is the second necessary kill.

Market making is breakeven, not modestly profitable. The “$100 to $500 a day market making Kalshi” claims are off by an order of magnitude in the modest case and qualitatively wrong in the aggressive case. With every lever pulled, adverse-selection control and regime filtering and inventory management and simulated zero latency, the honest result disappeared into the daily noise. Deep sports championship books with disciplined regime filtering may clear a few dollars a day at low tens of thousands of capital, but the ratio of expected value to variance is bad enough that it is not a good use of the capital.

The LIP rebate is real but capped at a small absolute number. Kalshi’s Liquidity Incentive Program does mechanically pay accounts that rest size on designated markets. The per-market pools typically run between $0.50 and $21 a day. After Path D’s strict curation, my model said about $2.83 a day at the structural ceiling of $1,500 of deployable capital. At my actual $40 of working capital, the realization was negative once adverse-selection costs were counted. The mechanics work, but the number on a retail account is too small to matter.

The 3.25 to 3.75 percent APY is the only reliable positive. On a $50,000 deposit that is about $1,750 a year, fully passive and uncorrelated with trading activity.

Discretionary edges may exist. Systematic ones do not. The harness I built can only evaluate systematic strategies, the kind that reduce to a backtest. It has nothing to say about a human being faster or sharper than the market at interpreting breaking news in the thirty seconds before automated quoters react. That class of edge was not what this project was about and I make no claim about it either way.

Where that leaves me

I went into this hoping to find a retail trading edge on prediction markets and came out with a system that says there isn’t one. The systematic-edge search is complete and negative.

I wanted to end this post with “and here’s how I make $300 a day on Kalshi.” What I have instead is $1.57 of realized losses, $5.93 of fees paid, and zero verified LIP credits.

For anyone considering Kalshi as a retail trader, my honest read is that you are very probably going to lose money trying to trade it actively. The pricing is sharper than a hand-rolled model can beat, the fee schedule is rigid enough to eat any predictable edge, and the most widely discussed retail strategies do not survive validation. Funding the account and earning the APY is real, modest, and has nothing to do with skill.

The candle-coverage mechanism is the piece I would push in front of a quantitative researcher. Any published retail-edge claim built on a trade-driven candle endpoint deserves re-examination with a both-legs-covered control applied, and I would not be surprised if a good chunk of the existing gray literature on these venues turns out to be the same artifact.

I spent six weeks believing I just had not found the strategy yet. Every dead candidate looked like the one before it died. The MLB favorite signal at t = 5.4. The fade-slight-favorite at +8.7 cents. The weather longshot at +4.4 cents with a t-statistic of 9. Every one of them, in the moment I first saw it, looked like the point where the project turned into a money machine, and every one of them was a measurement artifact. What ended the project was taking the harness seriously when it returned verdicts I did not want, which is also why the harness is the best thing I built here. I did not beat prediction markets, but I know why nobody at my scale does, and finding that out cost me $7.50 and six weeks.


Share this post on:

Next Post
Why A Podcast?

You might also like