BANGER
Product status: Banger’s public beta is paper-only. References to live execution are future-looking. Venue rules and legal claims can change; verify current primary sources for your jurisdiction.
2026-06-17 · 4 min read · reviewed 2026-10-14

Position Sizing for Prediction-Market Bots: Beyond Full Kelly

Position sizing starts with the instrument and the loss being modeled. This article considers a fully paid long in an ordinary contract paying $1 for the specified winning outcome and $0 otherwise. A scalar payout, financed position or multi-leg portfolio needs a different calculation. The ordinary binary example is not a universal description of prediction markets.

Kelly sizing optimizes a particular log-growth objective under a stated model. It does not establish the probability used by that model, guarantee a tolerable drawdown or prove that a strategy has an edge. An estimated probability is an input to investigate, not a known physical constant.

Derive the frictionless reference

Let p be the independently estimated winning probability and a the dollars paid per contract, excluding all costs. Net odds on the paid stake are b = (1 - a) / a. The unconstrained single-bet fraction is (b × p - (1 - p)) / b, equivalently (p - a) / (1 - a).

The fraction describes bankroll spent on this hypothetical long, not the number of contracts. If it is negative, a long-only illustration clips it to zero. That does not establish an attractive opportunity on the opposite side: its executable price, costs and constraints must be assessed independently.

from decimal import Decimal


def binary_kelly_reference(model_probability, entry_price, fraction):
    # Frictionless $1/$0 payout, paid long only. No orders or bankroll access.
    p, a, scale = map(lambda x: Decimal(str(x)),
                      [model_probability, entry_price, fraction])
    if not all(x.is_finite() for x in [p, a, scale]):
        raise ValueError("inputs must be finite")
    if not (0 <= p <= 1 and 0 < a < 1 and 0 <= scale <= 1):
        raise ValueError("invalid probability, price or fraction")
    full = (p - a) / (1 - a)
    return scale * max(Decimal("0"), full)


print(binary_kelly_reference("0.60", "0.50", "0.25"))  # 0.05
# Hypothetical fraction of bankroll spent; not a trade recommendation.

The example was checked offline for positive, zero and negative estimated edge, input boundaries and non-finite values. Its chosen quarter fraction is illustrative. It is not a recommended allocation, a forecast or a Banger order helper.

State what the reference leaves out

Kelly's original paper develops growth under specified probabilities and odds. Applying that idea here requires explicit assumptions about repeated opportunities, payouts, costs, capital availability and estimation uncertainty. A one-off event and an overlapping portfolio do not automatically fit a repeated independent-bet model.

Do not quote a universal chance of a particular drawdown or claim that twice a calculated fraction always guarantees ruin. Such conclusions depend on additional models. Likewise, a smaller fraction is not universally equivalent to a particular reduction in variance or growth for every event-contract strategy.

Treat estimated probability as uncertain

A lower fraction reduces the stake under the same assumed model; it does not repair a biased probability estimate. Preserve how p was produced and which information was available then. Evaluate later outcomes on a defined sample and show uncertainty, not just a selected winning example.

Fees and execution change the payoff. An executable ask differs from a last trade or midpoint. A model that ignores costs can produce a positive reference where the tested net outcome is unattractive. Do not silently fold an undocumented fee convention into the price.

Manage aggregate exposure separately

Dividing a single-bet fraction by a count of positions is not a general solution to correlation. Several identifiers can share the same underlying event; a single position count can also hide a large concentration.

Use the actual Banger envelope

Banger's public 0.1.7 RiskEnvelope exposes max_position_usd, max_daily_loss_usd, max_open_positions, max_concentration_pct and max_orders_per_minute. The position-dollar limit concerns cumulative cost basis, rather than an invented per_trade_cap. The daily-loss control blocks new exposure using the current UTC-day mark-to-market baseline; it does not guarantee liquidation or a maximum eventual loss.

The real callback is on_market_tick(market, tick, ctx). Context.place takes USD notional, while closing held contracts uses a different quantity convention. The earlier on_book/self.buy/fraction interface is not a shipped API. Verify units and mode before adapting a standalone calculation.

Banger's public beta is paper-only. Its packaged forward-paper path includes an assumed fill for a non-crossing requested limit. A sizing calculation and a simulated fill therefore establish different, limited kinds of evidence. Neither justifies raising a live allocation or assuming that a real exit will be available.

Sources

Keep reading

Run your first strategy free

Paper-trade on Polymarket and Kalshi market data without venue keys.

Start free