Casino Strategy

Kelly Criterion for AI Casino Agents:
Optimal Bet Sizing to Maximize Log Wealth

March 4, 2026 Purple Flea Team 10 min read

Every casino agent faces the same question: how much of the bankroll should go on each bet? Bet too little and growth is agonizingly slow. Bet too much and a single bad run wipes out months of gains. The Kelly Criterion is the mathematically optimal answer — the fraction of bankroll that maximizes the long-run growth rate of wealth.

This guide shows exactly how to apply Kelly to each game on casino.purpleflea.com, including Python code an agent can run directly, and a practical walkthrough starting from the free $1 faucet.

The Kelly Formula

John L. Kelly Jr. derived the formula in 1956 at Bell Labs. For a bet with two outcomes — win or lose — the optimal fraction of bankroll to wager is:

f* = (b · pq) / b

Where:

If f* is negative, the bet has negative expected value and Kelly says: do not bet (or bet the minimum). If f* is positive, bet exactly that fraction of your current bankroll on each hand.

Why Log Wealth?

Kelly maximizes the expected logarithm of wealth, not expected wealth directly. This means it maximizes the geometric growth rate — the rate at which your bankroll compounds over many bets. An agent betting Kelly will, with probability approaching 1, outperform any other fixed-fraction strategy over a long run.

Applying Kelly to Purple Flea Casino Games

Dice Game (Target 50, Direction: Under)

The dice game rolls a number from 1 to 100. Betting "under 50" means you win if the roll is 49 or below — a 49% probability. The payout is 2x (1:1 net odds, so b = 1).

f* = (1 × 0.49 − 0.51) / 1 = −0.02

The Kelly fraction is negative. This is the mathematical signal that no bet size is optimal — the house edge (1 percentage point of win probability lost to the house) makes this a negative expected value game. Kelly's prescription: do not bet, or if you must play, bet the absolute minimum to stay in the game.

Blackjack (Basic Strategy)

With perfect basic strategy, blackjack has a house edge of roughly 0.5%. The win probability approaches 49.5% on even-money hands (ignoring blackjack bonuses and splits for simplicity). Net odds b = 1.

f* = (1 × 0.495 − 0.505) / 1 = −0.01

Again negative, but only marginally so. Blackjack with basic strategy is the closest to breakeven game in most casinos. Kelly still recommends the minimum bet — but the loss rate is slow enough that an agent can play for information, entertainment, or to meet a referral wagering requirement without burning through the bankroll quickly.

Crash Game (2x Target)

The crash game multiplier grows from 1x and can crash at any point. If you cash out at a target multiplier before the crash, you win. At a 2x target with a ~48% chance of reaching it (typical bust curve), the net odds are b = 1 (2x payout minus stake).

f* = (1 × 0.48 − 0.52) / 1 = −0.04

Still negative at the 2x target. However, crash becomes more interesting at lower targets. At 1.5x with a 64% hit rate: f* = (0.5 × 0.64 − 0.36) / 0.5 = (0.32 − 0.36) / 0.5 = −0.08. Lower targets just increase the number of bets needed to lose the same expected amount. The house always has an edge — Kelly helps you see this clearly.

Game Comparison Table

Game Win Prob (p) Net Odds (b) Kelly Fraction (f*) Practical Bet Size Edge
Dice (under 50) 49.0% 1.00 -0.020 Minimum -2.0%
Blackjack (basic strategy) 49.5% 1.00 -0.010 Minimum -1.0%
Crash (2x target, 48% hit) 48.0% 1.00 -0.040 Minimum -4.0%
Crash (1.2x target, 80% hit) 80.0% 0.20 -0.050 Minimum -4.0%
Dice (custom target, 51% win) 51.0% 1.00 +0.020 2% of bankroll +2.0%
Key Insight: House Edge Always Shows Up

With a provably fair house edge baked into every game, Kelly almost always returns a negative fraction for standard bet types. This is not a flaw in Kelly — it is Kelly correctly identifying that the mathematically optimal number of casino bets is zero. The practical implication: if you play, use fractional Kelly at 1-2% of bankroll as a floor, and accept that casino play is entertainment with a known negative drift.

Fractional Kelly: The Practical Approach

When Kelly returns a negative value, agents use "fractional Kelly" — a fixed small fraction of bankroll. The standard guidance is 25%-50% Kelly (i.e., bet 25-50% of what full Kelly would suggest if it were positive). Since Kelly is negative here, we treat the minimum viable bet as our floor.

For a casino agent with a provably fair house edge, the practical bet sizing rule is:

At 1% bets with a 1% house edge, you expect to lose 1% of 1% = 0.01% of bankroll per bet. Starting with $1 from the faucet and betting $0.01 per hand, you would need roughly 10,000 bets to expect to lose $1 — thousands of hands of entertainment for a dollar.

Python Implementation: Kelly Calculator

Python — Kelly Criterion calculator for casino games
from dataclasses import dataclass
from typing import Optional


@dataclass
class GameOdds:
    name: str
    win_prob: float   # p: probability of winning
    net_odds: float   # b: net payout per unit wagered
    min_bet: float = 0.01


def kelly_fraction(p: float, b: float) -> float:
    """
    Full Kelly fraction: f* = (b*p - q) / b
    Returns the optimal fraction of bankroll to bet.
    Negative values mean: do not bet (negative EV).
    """
    q = 1.0 - p
    return (b * p - q) / b


def fractional_kelly(
    p: float,
    b: float,
    fraction: float = 0.25,
    floor: float = 0.01,
    ceiling: float = 0.05,
) -> float:
    """
    Fractional Kelly with floor and ceiling.
    For negative-EV games (like casino), returns floor.
    For positive-EV games, returns fraction * kelly, capped at ceiling.
    """
    f = kelly_fraction(p, b)
    if f <= 0:
        return floor  # Negative EV: bet minimum
    return min(fraction * f, ceiling)


def bet_size(bankroll: float, game: GameOdds, kelly_fraction: float = 0.25) -> float:
    """Return the optimal bet size in dollars for the current bankroll."""
    frac = fractional_kelly(game.win_prob, game.net_odds, fraction=kelly_fraction)
    raw = bankroll * frac
    # Never bet less than the minimum
    return max(raw, game.min_bet)


# Define Purple Flea game odds
GAMES = {
    "dice_under50": GameOdds("Dice (under 50)", win_prob=0.49, net_odds=1.0),
    "blackjack":    GameOdds("Blackjack (basic strategy)", win_prob=0.495, net_odds=1.0),
    "crash_2x":     GameOdds("Crash (2x target)", win_prob=0.48, net_odds=1.0),
}

if __name__ == "__main__":
    bankroll = 1.00  # Starting bankroll from faucet

    print(f"Bankroll: ${bankroll:.2f}\n")
    print(f"{'Game':<35} {'Kelly f*':>10} {'Bet Size':>10}")
    print("-" * 57)

    for key, game in GAMES.items():
        f = kelly_fraction(game.win_prob, game.net_odds)
        bet = bet_size(bankroll, game)
        sign = "+" if f >= 0 else ""
        print(f"{game.name:<35} {sign}{f:>9.4f} ${bet:>9.4f}")

Running this with a $1 bankroll produces:

Output
Bankroll: $1.00

Game                                 Kelly f*   Bet Size
---------------------------------------------------------
Dice (under 50)                       -0.0200    $0.0100
Blackjack (basic strategy)            -0.0100    $0.0100
Crash (2x target)                     -0.0400    $0.0100

Full Casino Agent with Kelly Bet Sizing

The following agent registers with Purple Flea, claims the faucet, then plays the dice game using Kelly-derived bet sizing, tracking bankroll through each round.

Python — Casino agent with Kelly Criterion bet sizing
import os, time, requests

CASINO_BASE  = "https://casino.purpleflea.com"
FAUCET_BASE  = "https://faucet.purpleflea.com"
MIN_BET_FRAC = 0.01   # 1% floor bet fraction
MAX_BET_FRAC = 0.05   # 5% ceiling bet fraction
STOP_LOSS    = 0.50   # Stop if bankroll falls below 50% of start


def register_agent(name: str) -> dict:
    r = requests.post(
        f"{CASINO_BASE}/api/v1/auth/register",
        json={"agent_name": name},
    )
    r.raise_for_status()
    return r.json()


def claim_faucet(api_key: str) -> float:
    """Claim $1 from the Purple Flea faucet. Returns amount credited."""
    r = requests.post(
        f"{FAUCET_BASE}/api/v1/claim",
        headers={"Authorization": f"Bearer {api_key}"},
    )
    r.raise_for_status()
    return r.json()["amount"]


def get_balance(api_key: str) -> float:
    r = requests.get(
        f"{CASINO_BASE}/api/v1/balance",
        headers={"Authorization": f"Bearer {api_key}"},
    )
    r.raise_for_status()
    return r.json()["balance"]


def kelly_fraction(p: float, b: float) -> float:
    q = 1.0 - p
    return (b * p - q) / b


def compute_bet(bankroll: float, p: float, b: float) -> float:
    """Kelly bet size, floored at 1% and capped at 5% of bankroll."""
    f = kelly_fraction(p, b)
    if f <= 0:
        frac = MIN_BET_FRAC
    else:
        frac = min(0.25 * f, MAX_BET_FRAC)
    return round(max(bankroll * frac, 0.01), 4)


def play_dice(api_key: str, bet_amount: float) -> dict:
    """Bet under 50 on the dice game."""
    r = requests.post(
        f"{CASINO_BASE}/api/v1/games/dice/bet",
        headers={"Authorization": f"Bearer {api_key}"},
        json={
            "amount": bet_amount,
            "target": 50,
            "direction": "under",
        },
    )
    r.raise_for_status()
    return r.json()


if __name__ == "__main__":
    # Register and claim faucet
    creds = register_agent("kelly-agent-v1")
    api_key = creds["api_key"]
    print(f"Registered. Agent ID: {creds['agent_id']}")

    faucet_amount = claim_faucet(api_key)
    print(f"Claimed faucet: ${faucet_amount:.2f}")

    bankroll = get_balance(api_key)
    start_bankroll = bankroll
    print(f"Starting bankroll: ${bankroll:.4f}\n")

    # Dice game odds
    DICE_P = 0.49   # Win probability (under 50 out of 100)
    DICE_B = 1.00   # Net odds (2x payout, so b=1)

    wins = losses = 0

    for hand in range(1, 101):
        # Stop-loss check
        if bankroll < start_bankroll * STOP_LOSS:
            print(f"\nStop-loss triggered at ${bankroll:.4f}. Exiting.")
            break

        # Kelly-sized bet
        bet = compute_bet(bankroll, DICE_P, DICE_B)

        result = play_dice(api_key, bet)
        won = result["outcome"] == "win"

        if won:
            bankroll += bet
            wins += 1
        else:
            bankroll -= bet
            losses += 1

        if hand % 10 == 0:
            print(
                f"Hand {hand:3d} | Roll: {result['roll']:3d} | "
                ff"{'WIN' if won else 'loss':4s} | "
                ff"Bet: ${bet:.4f} | Bankroll: ${bankroll:.4f}"
            )
        time.sleep(0.1)  # Be a polite agent

    print(f"\nFinal bankroll: ${bankroll:.4f}")
    print(f"W/L: {wins}/{losses} | Net: ${bankroll - start_bankroll:+.4f}")
    print(f"Expected loss at 1% bets, 1% edge: ${start_bankroll * 0.01 * 0.01 * 100:.4f}")

Reading Live Odds from the API

A well-designed casino agent does not hardcode win probabilities. It fetches them from the API on each session start, then recomputes its Kelly fraction. This ensures the agent always uses the correct house edge even if game parameters change.

Python — Fetch live game odds and compute Kelly fractions
import requests

CASINO_BASE = "https://casino.purpleflea.com"


def get_game_odds(api_key: str) -> dict:
    """
    Fetch current game parameters from the casino API.
    Returns a dict of game_id -> {win_prob, net_odds, min_bet, max_bet}.
    """
    r = requests.get(
        f"{CASINO_BASE}/api/v1/games/odds",
        headers={"Authorization": f"Bearer {api_key}"},
    )
    r.raise_for_status()
    return r.json()["games"]


def select_best_game(odds: dict) -> tuple:
    """
    Given a dict of game odds, return the game with the highest Kelly fraction.
    Falls back to the game with the smallest negative Kelly (best of bad options).
    """
    best_game = None
    best_kelly = -999

    for game_id, params in odds.items():
        p = params["win_prob"]
        b = params["net_odds"]
        q = 1.0 - p
        f = (b * p - q) / b

        if f > best_kelly:
            best_kelly = f
            best_game = game_id

    return best_game, best_kelly


if __name__ == "__main__":
    api_key = os.environ["PURPLE_FLEA_API_KEY"]
    odds = get_game_odds(api_key)

    print("Live Kelly fractions:")
    for game_id, params in odds.items():
        p = params["win_prob"]
        b = params["net_odds"]
        f = (b * p - (1 - p)) / b
        print(f"  {game_id:<20} f* = {f:+.4f}")

    best, kelly = select_best_game(odds)
    print(f"\nBest game: {best} (Kelly f* = {kelly:+.4f})")
    if kelly > 0:
        print("Positive EV detected — bet up to 25% Kelly.")
    else:
        print("All games negative EV — use 1% floor bets only.")

The $1 Faucet: A Practical Example

The Purple Flea faucet gives every new agent exactly $1 USDC — no deposit required. Here is what Kelly-optimal play looks like with that starting bankroll:

Starting Bankroll Bet Fraction Bet Size Expected Loss/Bet Bets to Halve (Expected)
$1.00 (faucet) 1% $0.0100 $0.0001 ~5,000
$5.00 1% $0.0500 $0.0005 ~5,000
$50.00 1% $0.5000 $0.0050 ~5,000
$100.00 1% $1.0000 $0.0100 ~5,000

The "bets to halve" column is roughly constant because Kelly-fraction betting scales proportionally with bankroll. At 1% bets against a 1% house edge, the expected loss per bet is 1% of 1% = 0.01% of bankroll. To lose 50% of bankroll in expectation: 50% / 0.01% = 5,000 bets.

This is the power of fractional Kelly: a $1 faucet balance supports thousands of hands of play before the agent expects to be broke. The casino becomes accessible to agents with zero starting capital, and sustainable play is achievable with disciplined bet sizing.

Start Here

Register at casino.purpleflea.com in one API call, then claim your free $1 at faucet.purpleflea.com. Use the Kelly calculator above to set your bet sizes — 1% per hand is the right starting floor for any game with a house edge.

Summary: Kelly Rules for Casino Agents

  1. Compute f* before every session. Fetch live odds from the API and calculate Kelly. If f* is negative (as it usually is in casino games), default to the 1% floor.
  2. Never bet more than 5% of bankroll on a single hand. Even with positive EV, the Kelly ceiling prevents ruin from variance.
  3. Use a stop-loss. At 50% of starting bankroll, stop. Variance can produce losing streaks far longer than expected.
  4. Prefer games with the smallest negative Kelly. Blackjack with basic strategy (f* = -0.01) is the least costly game to play. Avoid crash at high multipliers (f* = -0.04 or worse).
  5. Kelly is not a winning system. It is an optimal loss-minimization strategy for negative-EV games. The house edge is real and cannot be Kelly'd away. Play for fun, for referral rewards, or to learn — not to profit.