The fundamental insight behind market making is elegant: you don't need to know which direction the price will move. You only need to earn more from the bid-ask spread on completed trades than you lose from adverse price moves on your inventory. A casino doesn't need to predict which players will win — it just needs the house edge to be positive over enough rounds. A market maker doesn't need directional conviction — it just needs the spread income to exceed the inventory risk over enough trades.

This analogy is not coincidental. The Purple Flea Casino API operates on exactly the same mathematical principle: each game is designed with a positive expected value for the house. The house edge — the percentage of each bet the casino keeps in expectation — is the market maker's spread equivalent. Both systems profit from volume, not from correct prediction of outcomes. This guide explores how AI agents can apply casino-style house-edge thinking to on-chain market making, and how to build a production AMM agent using the Purple Flea Trading API.

0.1%
Typical mid-market spread
$2.4T
Daily crypto volume (2026)
~40%
Market share of top 5 MM agents

The Casino Model Applied to Market Making

Consider a simple casino game: a player bets $100 on a coin flip with 49% win probability (the house keeps a 2% edge). Over one bet, the outcome is random. Over 10,000 bets, the casino's profit converges tightly to $2 per $100 wagered. The law of large numbers is the casino's protection against variance — enough volume makes the edge nearly deterministic.

Market making works identically. If you post a bid at $100 and an ask at $100.20 for an ETH pair, you're offering a $0.20 spread (0.2%). If a trader buys from you at $100.20 and another trader immediately sells to you at $100, you've earned $0.20 without any directional exposure. Do this 10,000 times per day, and your gross income is $2,000/day from spread alone. The risk — the equivalent of the casino's "big jackpot" risk — is that you accumulate a large one-sided position during a directional price move, and that inventory loses value before you can liquidate or rebalance it. This is the inventory risk problem.

The goal of a sophisticated market making agent is to maximize spread income while minimizing inventory risk — widening spreads (reducing trade frequency but increasing per-trade profit) during volatile periods when adverse selection is high, and tightening spreads (increasing trade volume) during calm periods when the informativeness of each trade is low.

Bid-Ask Spread Optimization

The canonical academic reference for spread optimization is the Avellaneda-Stoikov model (2008), which derives the optimal quotes for a market maker who wants to maximize expected utility of terminal wealth while managing inventory and volatility risk. The key insight: the optimal spread widens with volatility and with how far the current inventory deviates from neutral.

The Avellaneda-Stoikov reservation price formula:

Mid price (S)$2,845.00
Inventory (q, negative = short)-0.8 ETH
Risk aversion param (gamma)0.1
Volatility (sigma, per unit time)0.015
Time to horizon (T-t)0.5
Reservation price r = S - q*gamma*sigma^2*(T-t)$2,846.71

Because the agent is net short 0.8 ETH, it adjusts its reservation price upward slightly to bias toward buying. This naturally skews the agent's quotes to accumulate the asset it's short, correcting the inventory imbalance without any explicit rebalancing trade. The spread around this reservation price is:

Optimal spread = gamma*sigma^2*(T-t) + (2/gamma)*ln(1 + gamma/k)$2.84
Bid = r - spread/2$2,845.29
Ask = r + spread/2$2,848.13

In practice, the model parameters (gamma, k) are calibrated against historical fill rates and inventory behavior. Lower gamma = more aggressive quoting with higher inventory risk tolerance. Higher k = thinner order book depth = wider optimal spread.

Order Book Visualization: Your Agent's View

A healthy market maker's order book looks symmetric around mid-price. Here is an example of an ETH/USDC order book with agent quotes posted:

PriceSize (ETH)Total
2851.400.501,425.70
2850.201.203,420.24
2849.102.005,698.20
2848.003.509,968.00 ← agent ask
Mid: $2,846.65 Spread: $2.71 (0.095%)
2845.293.509,958.52 ← agent bid
2844.001.805,119.20
2842.500.902,558.25
2841.000.401,136.40

The agent posts 3.5 ETH of quotes on each side at $2,845.29 and $2,848.00. If market volume is 500 ETH/day and the agent captures 5% of trades, expected daily spread income is approximately 500 * 0.05 * $2.71 / 2 = $33.88. At scale (50,000 ETH daily volume), this grows to $3,388/day from spread alone — before accounting for inventory risk losses.

JavaScript: Production AMM Agent Core

The following complete JavaScript module implements the Avellaneda-Stoikov quoting engine, posts orders via the Purple Flea Trading API, monitors fills, and manages inventory by skewing quotes dynamically.

amm-agent.js
/**
 * Avellaneda-Stoikov Market Making Agent
 * Posts bid/ask quotes on ETH/USDC, manages inventory risk,
 * and executes via Purple Flea Trading API.
 */

const PF_TRADING = 'https://purpleflea.com/trading-api'
const API_KEY    = 'your_api_key'

// Model parameters
const CONFIG = {
  gamma:          0.10,   // risk aversion (0 = neutral, 1 = very risk averse)
  kappa:          1.50,   // order book density param
  quoteSize:      0.50,   // ETH per quote level
  quoteLevels:    3,      // number of bid/ask levels to post
  levelSpacing:   0.0003, // 0.03% between levels
  maxInventory:   5.0,    // max ±ETH position before quoting halts
  targetInventory:0.0,    // desired neutral inventory
  horizonSec:     300,    // rebalance horizon (5 minutes)
  refreshSec:     5,      // how often to refresh quotes
}

// Shared state
let state = {
  midPrice:    0,
  sigma:       0.015,      // realized volatility (updated dynamically)
  inventory:   0,          // current ETH inventory
  activeOrders: [],
  totalSpreadPnl: 0,
  totalInventoryPnl: 0,
}

/**
 * Avellaneda-Stoikov reservation price and optimal spread.
 */
function computeQuotes(midPrice, inventory, sigma, tRemain) {
  const { gamma, kappa } = CONFIG
  // Reservation price: biased away from current inventory
  const reservationPrice = midPrice - inventory * gamma * sigma * sigma * tRemain

  // Optimal half-spread
  const spread = gamma * sigma * sigma * tRemain
              + (2 / gamma) * Math.log(1 + gamma / kappa)

  return {
    bid: reservationPrice - spread / 2,
    ask: reservationPrice + spread / 2,
    spread,
    reservationPrice,
  }
}

/**
 * Compute realized volatility from recent price ticks.
 * @param {number[]} priceTicks - array of recent mid prices
 */
function computeRealizedVol(priceTicks) {
  if (priceTicks.length < 2) return 0.015
  const returns = []
  for (let i = 1; i < priceTicks.length; i++) {
    returns.push(Math.log(priceTicks[i] / priceTicks[i - 1]))
  }
  const mean = returns.reduce((a, b) => a + b, 0) / returns.length
  const variance = returns.reduce((s, r) => s + (r - mean) ** 2, 0) / returns.length
  return Math.sqrt(variance)  // per-tick vol; scale by sqrt(ticks_per_unit_time)
}

/**
 * Cancel all active orders and post fresh quotes.
 */
async function refreshQuotes(wallet) {
  const tRemain = CONFIG.horizonSec / 86400  // fraction of trading day
  const { bid, ask, spread } = computeQuotes(
    state.midPrice,
    state.inventory - CONFIG.targetInventory,
    state.sigma,
    tRemain
  )

  // Halt quoting if inventory limit breached
  if (Math.abs(state.inventory) >= CONFIG.maxInventory) {
    console.warn(`Inventory limit reached (${state.inventory} ETH) — pausing quotes`)
    await cancelAllOrders(wallet)
    return
  }

  // Cancel existing orders
  await cancelAllOrders(wallet)

  // Post layered quotes at multiple levels
  const newOrders = []
  for (let i = 0; i < CONFIG.quoteLevels; i++) {
    const offset = i * CONFIG.levelSpacing * state.midPrice
    newOrders.push(
      postOrder(wallet, 'buy',  (bid  - offset).toFixed(2), CONFIG.quoteSize),
      postOrder(wallet, 'sell', (ask  + offset).toFixed(2), CONFIG.quoteSize)
    )
  }
  const results = await Promise.all(newOrders)
  state.activeOrders = results.map(r => r.order_id).filter(Boolean)

  console.log(`Quotes refreshed | Bid: $${bid.toFixed(2)} | Ask: $${ask.toFixed(2)} | Spread: $${spread.toFixed(2)} | Inv: ${state.inventory.toFixed(3)} ETH`)
}

async function postOrder(wallet, side, price, size) {
  const res = await fetch(`${PF_TRADING}/v1/orders/limit`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'X-API-Key': API_KEY },
    body: JSON.stringify({
      wallet, side, price, size,
      pair: 'ETH/USDC',
      time_in_force: 'GTC',
      post_only: true  // ensure maker fee tier, never take liquidity
    })
  })
  return res.json()
}

async function cancelAllOrders(wallet) {
  if (!state.activeOrders.length) return
  await fetch(`${PF_TRADING}/v1/orders/cancel-batch`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'X-API-Key': API_KEY },
    body: JSON.stringify({ wallet, order_ids: state.activeOrders })
  })
  state.activeOrders = []
}

/**
 * Process a fill notification from the exchange websocket.
 * Updates inventory and PnL tracking.
 */
function processFill(fill) {
  const { side, size, price } = fill
  if (side === 'buy')  state.inventory += parseFloat(size)
  if (side === 'sell') state.inventory -= parseFloat(size)

  // Spread PnL approximation: half the spread per fill
  const spreadPerFill = Math.abs(parseFloat(price) - state.midPrice)
  state.totalSpreadPnl += spreadPerFill * parseFloat(size)

  console.log(`Fill: ${side} ${size} ETH @ $${price} | Inv: ${state.inventory.toFixed(3)} ETH | Spread PnL: $${state.totalSpreadPnl.toFixed(2)}`)
}

/**
 * Main agent loop.
 */
async function runAMMAgent(wallet) {
  const priceTicks = []

  setInterval(async () => {
    // 1. Fetch current mid price
    const tickRes = await fetch(`${PF_TRADING}/v1/ticker/ETH-USDC`, {
      headers: { 'X-API-Key': API_KEY }
    })
    const ticker = await tickRes.json()
    state.midPrice = (ticker.best_bid + ticker.best_ask) / 2
    priceTicks.push(state.midPrice)
    if (priceTicks.length > 100) priceTicks.shift()

    // 2. Update realized volatility
    state.sigma = computeRealizedVol(priceTicks)

    // 3. Check for fills and update inventory
    const fillsRes = await fetch(`${PF_TRADING}/v1/fills/recent`, {
      headers: { 'X-API-Key': API_KEY },
    })
    const { fills } = await fillsRes.json()
    fills.forEach(processFill)

    // 4. Refresh quotes with updated parameters
    await refreshQuotes(wallet)

  }, CONFIG.refreshSec * 1000)

  console.log(`AMM Agent started for wallet ${wallet}`)
}

runAMMAgent('0xYourAgentWallet')

Inventory Risk and Impermanent Loss Hedging

The largest risk for a market making agent is accumulating a large inventory position in a directionally moving market. If ETH is falling and your agent keeps buying ETH from panicking sellers (filling your bid orders), you accumulate long ETH exposure that's losing value faster than your spread income compensates.

Dynamic spread widening

As inventory diverges from neutral, widen spreads to slow fill rate on the side that's building inventory. If long 3 ETH in a falling market, widen bids (making them less competitive) and tighten asks (making them more attractive). This naturally attracts sellers to restore your inventory toward neutral without any explicit sell order.

Perpetual hedge

For significant inventory positions (above 2 ETH, for example), open a countervailing position in a perpetual futures market. If long 3 ETH from market making, short 3 ETH perp on Hyperliquid. The spread income continues to accrue on the spot position, while the perp short eliminates directional exposure. The cost of this hedge is the funding rate — if funding is positive (longs pay shorts), you earn funding income on top of spread income.

Impermanent loss (Uniswap V3 LP context)

When operating as a Uniswap V3 LP (a passive AMM rather than an active market maker), the equivalent risk is impermanent loss. For a concentrated liquidity position in an active range, IL can be hedged with delta-neutral options positions on the base asset. The cost of the hedge (options premium) must be weighed against the expected fee income from the LP position.

Hedge Strategy Cost Coverage Best For
Dynamic spread widening Reduced fill rate Slows accumulation Low-vol markets, small inventory
Perp hedge (delta neutral) Funding rate (±0.01-0.1%/day) Full directional hedge High-conviction inventory risk
Options collar Net premium (0.5-2% of position) Bounded downside Large positions, low-liquidity markets
Cross-venue netting Gas / bridge fees Venue-level balance Multi-venue MM operations

Python: Impermanent Loss Calculator and Hedge Optimizer

il_hedge.py
import math
import httpx
import asyncio
from dataclasses import dataclass

PF_TRADING = "https://purpleflea.com/trading-api"
API_KEY    = "your_api_key"

@dataclass
class LPPosition:
    entry_price: float      # ETH price at position entry
    tick_lower:  float      # lower price bound of concentrated range
    tick_upper:  float      # upper price bound of concentrated range
    liquidity_usd: float    # total USD value at entry

def compute_il(pos: LPPosition, current_price: float) -> float:
    """
    Compute impermanent loss for a Uniswap V3 concentrated LP position.
    Returns IL as a fraction (negative = loss relative to HODL).
    Uses V3 formula accounting for concentrated range.
    """
    p0 = pos.entry_price
    p1 = current_price
    pa = pos.tick_lower
    pb = pos.tick_upper

    # Handle out-of-range: full exposure to one asset
    if p1 <= pa:
        # Fully in token0 (ETH): all assets are ETH
        eth_ratio = 1.0
        # Value of LP if HODL'd: 50% ETH + 50% USDC at entry
        hodl_value = 0.5 * pos.liquidity_usd * (p1 / p0) + 0.5 * pos.liquidity_usd
        lp_value   = pos.liquidity_usd * (p1 / p0)  # all became ETH, now worth p1/p0
        return (lp_value - hodl_value) / hodl_value

    elif p1 >= pb:
        # Fully in token1 (USDC): no price exposure
        hodl_value = 0.5 * pos.liquidity_usd * (p1 / p0) + 0.5 * pos.liquidity_usd
        lp_value   = pos.liquidity_usd  # all USDC, no change
        return (lp_value - hodl_value) / hodl_value

    # In-range: standard V3 IL formula
    sq_p0, sq_p1 = math.sqrt(p0), math.sqrt(p1)
    sq_pa, sq_pb = math.sqrt(pa), math.sqrt(pb)

    # LP value ratio (normalized)
    lp_ratio = (
        (2 * math.sqrt(sq_p0 * sq_p1) - sq_p0 - sq_p1) /
        (2 * math.sqrt(sq_p0 * sq_pb) - sq_p0 - sq_pb)
    )
    hold_ratio = (p1 / p0 + 1) / 2

    return (lp_ratio / hold_ratio) - 1

def optimal_hedge_size(
    pos: LPPosition,
    current_price: float,
    perp_funding_rate: float  # daily funding rate
) -> dict:
    """
    Calculate optimal delta hedge size for an LP position.
    Only hedge if IL risk exceeds hedge cost.
    """
    il_pct = abs(compute_il(pos, current_price)) * 100

    # Estimate delta exposure: how much ETH does LP "hold" at current price?
    # For in-range V3 position, delta ≈ 0.5 at midpoint of range
    range_center = math.sqrt(pos.tick_lower * pos.tick_upper)
    relative_pos = (current_price - pos.tick_lower) / (pos.tick_upper - pos.tick_lower)
    delta_fraction = max(0, min(1, 1 - relative_pos))  # ETH fraction

    eth_exposure = (pos.liquidity_usd * delta_fraction) / current_price
    hedge_cost_pct = abs(perp_funding_rate) * 30  # 30-day cost

    should_hedge = il_pct > hedge_cost_pct * 2  # hedge only if IL > 2x cost

    return {
        "il_pct": round(il_pct, 4),
        "eth_delta": round(eth_exposure, 4),
        "hedge_short_eth": round(eth_exposure, 4) if should_hedge else 0,
        "hedge_cost_pct_30d": round(hedge_cost_pct, 4),
        "should_hedge": should_hedge,
    }

async def open_perp_hedge(wallet: str, short_eth: float):
    """Open a short perp position to hedge LP delta."""
    async with httpx.AsyncClient() as client:
        res = await client.post(
            f"{PF_TRADING}/v1/perp/open",
            json={
                "wallet": wallet,
                "asset": "ETH",
                "side": "short",
                "size": short_eth,
                "leverage": 2,
                "order_type": "market",
            },
            headers={"X-API-Key": API_KEY}
        )
        result = res.json()
    print(f"Hedge opened: short {short_eth:.4f} ETH perp | txid: {result.get('tx_hash','')[:16]}")

// Example usage
pos = LPPosition(
    entry_price=2800,
    tick_lower=2500,
    tick_upper=3200,
    liquidity_usd=100_000
)

current_price = 2600
funding = 0.0001  # 0.01% daily funding
hedge = optimal_hedge_size(pos, current_price, funding)
print(f"IL: {hedge['il_pct']:.2f}% | Delta: {hedge['eth_delta']:.3f} ETH | Hedge needed: {hedge['should_hedge']}")

The Casino House Edge Model: Steady-State Revenue

The reason the Purple Flea Casino API is architecturally similar to a market making system is that both rely on the same statistical guarantee: over sufficient volume, a positive edge converts to reliable revenue. The Casino API's house edge on provably fair games ranges from 1-5% depending on the game. The market maker's spread edge might be 0.05-0.2% per trade. The magnitudes differ, but the business model is identical: never bet on outcomes, just charge for participation.

An AI agent can be a participant on both sides of this model. It can run games through the Casino API (as a player exploiting variance, or as a house agent with its own edge), and simultaneously market-make on the trading side. The diversification across both revenue streams — casino variance and trading spread — smooths the overall PnL profile and increases Sharpe ratio for the combined operation.

Typical combined agent P&L breakdown (monthly, $200k deployed):
Spread income (market making): +$4,200
Yield farming income: +$1,800
Casino net P&L (provably fair, house side): +$600
Inventory losses (market making): -$800
Gas + fees: -$350
Net: +$5,450/month (32.7% annualized)

Running the Agent: Infrastructure Requirements

A production market making agent has strict infrastructure requirements that differ from most DeFi bots:

Critical risk reminder: Market making agents are not "set and forget." During extreme market events (flash crashes, exchange outages, oracle attacks), a market maker can accumulate devastating inventory losses in seconds. Always implement hard position limits, circuit breakers, and maintain a human emergency contact channel. Start with small position sizes (under $10,000) and expand only after validating behavior through multiple volatile market sessions.

Start Market Making with Purple Flea

Use the Trading API for order placement and fills, Casino API for understanding house-edge revenue models, and the Faucet to get free USDC for initial capital deployment.