The 50-Cent Kalshi Fee Trap: Why Your Edge Is Smaller Than You Think
TL;DR / Key Takeaways
- Kalshi fees are nonlinear and hit hardest around 50-cent contracts.
- A percentage edge is meaningless unless it survives fees, spread, and expected-profit calculations.
- The bot rejects trades when fees consume too much of the gross edge.
- Fee efficiency changes which contracts are worth trading, even when the model probability looks favorable.
Here is a mistake I made early on and have seen replicated in every bot I have reviewed since: calculating edge as (model probability minus market price) and entering whenever that number is positive.
That math ignores fees. On Kalshi, fees are not flat. They are a curve. And that curve is specifically designed to be most expensive right where most retail volume concentrates — at 50-cent contracts.
Here is the full breakdown.
The Kalshi fee formula
| Contract price | Fee pressure from P x (1-P) | Practical effect | Bot response | | --- | --- | --- | --- | | Near $0.05 or $0.95 | Low formula pressure | Often poor liquidity and wide spreads | Require minimum price and depth checks | | Near $0.50 | Highest formula pressure | Fees consume the most edge | Demand larger model-market gap | | Near $0.70 or $0.30 | Moderate formula pressure | Can be attractive if spread is tight | Evaluate fee-adjusted EV |
Kalshi charges fees on winning trades only. The formula is:
fee = 0.07 × C × P × (1 - P)
Where:
0.07is the fee rate (7%)Cis the number of contractsPis the contract price (in dollars, so $0.50 = 0.50)
This is a quadratic. It peaks at P = 0.50, where P × (1 - P) = 0.25. At P = 0.10 or P = 0.90, that product is only 0.09 — roughly one-third the peak value.
Let me show this concretely. For 100 contracts:
| Contract Price | P × (1-P) | Fee per 100 contracts | |---|---|---| | $0.10 | 0.09 | $0.063 | | $0.25 | 0.1875 | $0.131 | | $0.50 | 0.25 | $0.175 | | $0.75 | 0.1875 | $0.131 | | $0.90 | 0.09 | $0.063 |
A 50-cent contract costs almost three times more in fees than a 10-cent or 90-cent contract on the same number of contracts. Most retail Kalshi traders have no idea this curve exists. They see 50/50 odds, they see what looks like a price gap, and they trade. The fee is quietly eating their margin.
Why this kills most bot strategies
The standard algo trader approach: if your model says 65% probability and Kalshi prices it at 55 cents, you have a 10-point edge. Enter the trade.
Here is what actually happens at $0.55 with 100 contracts:
# Source: predictandprofit.io
price = 0.55
contracts = 100
fee_rate = 0.07
fee = fee_rate * contracts * price * (1 - price)
# fee = 0.07 * 100 * 0.55 * 0.45 = $1.7325
expected_gross_profit = (0.65 - 0.55) * contracts
# expected_gross_profit = $10.00
net_expected_profit = expected_gross_profit - fee
# net_expected_profit = $10.00 - $1.73 = $8.27
That looks fine. But now consider that your model is not perfectly calibrated. If you are running at 60% actual probability instead of 65%, the math changes:
# Source: predictandprofit.io
actual_prob = 0.60
expected_gross_profit = (actual_prob - price) * contracts
# expected_gross_profit = $5.00
net_expected_profit = expected_gross_profit - fee
# net_expected_profit = $5.00 - $1.73 = $3.27
Still positive, but a small miscalibration in the model is amplified by the fee. Now imagine your model is running at 57% when it says 65%. Happens. Models are not perfect. At 57%:
# Source: predictandprofit.io
actual_prob = 0.57
expected_gross_profit = (0.57 - 0.55) * 100 # $2.00
net_expected_profit = 2.00 - 1.73 # $0.27
You are essentially breaking even after fees on a trade you thought had 10 points of edge. This is why bots that look profitable in backtests lose money live. The backtest uses model probabilities as if they were true probabilities. Live trading uses real probabilities that are messier.
The fee efficiency filter
The fix is to calculate fee-adjusted edge before every trade and reject anything that does not clear the threshold.
# Source: predictandprofit.io
def calculate_fee_adjusted_edge(
model_prob: float,
market_price: float,
contracts: int,
max_fee_ratio: float = 0.40
) -> tuple[float, bool]:
"""
Returns (net_expected_profit, should_trade).
Rejects trade if fee > max_fee_ratio of gross expected profit.
"""
gross_edge = (model_prob - market_price) * contracts
if gross_edge <= 0:
return 0.0, False
fee = 0.07 * contracts * market_price * (1 - market_price)
fee_ratio = fee / gross_edge
net_profit = gross_edge - fee
should_trade = fee_ratio <= max_fee_ratio and net_profit > 0
return net_profit, should_trade
Usage:
# Source: predictandprofit.io
net_profit, should_trade = calculate_fee_adjusted_edge(
model_prob=0.65,
market_price=0.55,
contracts=100,
max_fee_ratio=0.40
)
if should_trade:
place_order(...)
else:
print(f"Trade skipped: fee consumes too much edge")
In the Predict & Profit system, I reject any trade where fees consume more than 40% of the gross expected profit. That number is configurable. Tighten it to 30% for more conservative filtering. Loosen it to 50% if you want to take more trades in the 40-60 cent range. The default of 40% reflects my actual tolerance for fee drag in the current live configuration.
What this means for which contracts to trade
The fee structure has a direct implication for contract selection. All else being equal, contracts priced near $0.10 or $0.90 are cheaper to trade than contracts near $0.50. The fee efficiency filter naturally steers the bot toward high-confidence predictions — which tend to price away from 50 cents — and away from coin-flip markets, which is exactly the behavior you want.
This is not an accident. It is one of the reasons I targeted weather markets specifically. When the GFS ensemble shows 58 out of 62 members agreeing on a temperature outcome, the probability estimate is often in the 0.80-0.90 range. Those are cheap contracts to trade from a fee perspective. The signal quality and the fee efficiency reinforce each other.
The bid-ask spread compounds the problem
Fees are only part of the execution cost. Every time you cross the bid-ask spread with a market order, you give up additional edge immediately.
On a liquid contract with a 1-cent spread, this is almost noise. On a thin contract with a 5-cent spread, you are starting every trade 5 cents in the hole before fees are even applied. At 55 cents with a 5-cent spread, you need a model probability of at least 65% just to break even after entering. Add fees on top and the real breakeven moves to around 67%.
The Predict & Profit system checks spread width as a component of the composite edge score. A wide spread is penalized, not ignored.
The full fee efficiency filter and edge scoring system are included in the Python source code. If you have been trading Kalshi without accounting for this, the numbers will probably surprise you.
Frequently Asked Questions
Q: Why are 50-cent Kalshi contracts fee-sensitive?
A: The Kalshi fee formula contains P x (1 - P), which peaks at 0.50. That means mid-priced contracts carry the largest fee burden relative to stake.
Q: What is fee-adjusted expected value?
A: It is expected gross profit minus expected fees and execution costs. The bot only cares about the net value that remains after those costs are applied.
Q: How does the fee efficiency filter change trading behavior?
A: It rejects trades where fees consume too much of the edge, even when the model-market gap is positive. This prevents small apparent edges from becoming negative after costs.