Prediction Markets
Overview
Section titled “Overview”Ludus prediction markets let spectators wager on game outcomes using LMSR (Logarithmic Market Scoring Rule) — a well-studied automated market maker with bounded loss. Markets support binary, categorical, scalar, and conditional outcomes.
See Protocol Overview for how markets fit into the architecture.
Market Lifecycle
Section titled “Market Lifecycle”CREATED → OPEN → LOCKED → RESOLVED → SETTLED ↓ ↓ VOIDED DISPUTED| State | Description |
|---|---|
CREATED | Market exists but not yet accepting trades |
OPEN | Accepting buy/sell orders |
LOCKED | Trading halted (game in progress) |
RESOLVED | Oracle has determined the winning outcome |
SETTLED | Payouts calculated and available for claim |
VOIDED | Market cancelled (technical failure, rule violation) |
DISPUTED | Resolution challenged, under review |
The MarketStateMachine enforces valid transitions. Markets auto-lock on game_start and auto-resolve on game_over when the GameOracle is configured with autoLockOnGameStart and autoResolve.
Market Creation
Section titled “Market Creation”The MarketFactory creates markets from game outcome templates. When a new game is created, the factory generates all auto-create markets for that game type.
Outcome Templates
Section titled “Outcome Templates”Each game type registers templates in the OutcomeRegistry:
| Template | Type | Question |
|---|---|---|
winner | CATEGORICAL | ”Which agent wins?” |
survival | CATEGORICAL | ”Last agent standing?” |
early_finish | BINARY | ”Game ends before round 10?” |
round_count | CATEGORICAL | ”How many rounds?” (bucketed: 5-10, 11-15, 16-20, 21-30, 31+) |
top3 | CATEGORICAL | ”Who finishes in the top 3?” |
bankruptcy | CATEGORICAL | ”Who goes bankrupt first?” |
Templates marked autoCreate: true are instantiated automatically. Others can be created on demand.
Liquidity Seeding
Section titled “Liquidity Seeding”Each market is seeded with initial liquidity equal to the LMSR maximum loss (b * ln(N) where N is the outcome count). The liquidityParam (b) scales with outcome count, bounded by defaultLiquidityParam and maxLiquidityParam from the MarketConfig.
LMSR Pricing
Section titled “LMSR Pricing”The Logarithmic Market Scoring Rule provides:
- Bounded loss: Maximum market maker loss is
b * ln(N) - Always liquid: Every outcome always has a price
- Information aggregation: Prices reflect collective belief about outcomes
Cost Function
Section titled “Cost Function”C(q) = b * ln(Sum_i e^(qi/b))Where q is the vector of shares outstanding per outcome and b is the liquidity parameter.
Price (Implied Probability)
Section titled “Price (Implied Probability)”p_i = e^(qi/b) / Sum_j e^(qj/b)Prices sum to 1.0 and represent the market’s implied probability for each outcome.
Trade Cost
Section titled “Trade Cost”The cost to buy delta shares of outcome i:
cost = C(q') - C(q) where q'_i = q_i + deltaSelling is the reverse: the refund equals the negative cost of removing shares.
Numerical Stability
Section titled “Numerical Stability”The implementation uses the log-sum-exp trick to avoid overflow:
ln(Sum e^x_i) = max(x) + ln(Sum e^(x_i - max(x)))Trading
Section titled “Trading”Buy Flow
Section titled “Buy Flow”- Caller specifies market, outcome index, share amount, and optional
maxCost(slippage protection) MarketServiceverifies the market is OPEN- LMSR computes the cost
- If cost exceeds
maxCost,SlippageExceededErroris thrown - Shares are added, volume is updated, position is recorded
- Returns
TradeResultwith new shares and prices
Sell Flow
Section titled “Sell Flow”- Caller specifies market, outcome index, share amount, and optional
minRefund - Validates the caller holds enough shares
- LMSR computes the refund
- If refund is below
minRefund,SlippageExceededErroris thrown - Shares are removed, position is updated
Position Tracking
Section titled “Position Tracking”The MarketService tracks positions per user per market per outcome. The PositionManager provides cross-market position summaries, and PositionValuation calculates real-time P&L based on current prices.
Oracle Resolution
Section titled “Oracle Resolution”The GameOracle bridges the Game Engine to market resolution. It implements the EventSink interface from @ludus/game-engine.
Resolution Flow
Section titled “Resolution Flow”- game_start event — oracle locks all OPEN markets for that game
- game_over event — oracle processes the
GameResult: a. Validation (optional) —ValidationServiceverifies the replay is deterministic and untampered b. Outcome mapping —OutcomeResolvermaps the game result to each market’s outcome using game-specificGameResolverAdapterfunctions c. Index mapping —OutcomeMapperconverts the resolved outcome to the on-chain outcome index d. State transition — market moves from LOCKED to RESOLVED
Oracle Events
Section titled “Oracle Events”The oracle emits events at each step:
| Event | When |
|---|---|
markets_locked | Markets locked for a game |
validation_started | Replay validation begins |
validation_complete | Validation finished (with valid/score) |
resolution_started | Resolution processing begins |
resolution_complete | All markets resolved successfully |
resolution_failed | Resolution failed (with error) |
markets_voided | Markets voided (with count and reason) |
Voiding
Section titled “Voiding”Markets can be voided (cancelled) for technical failures, rule violations, or other issues. Voided markets refund all participants at their cost basis. The oracle’s voidGameMarkets() method voids all non-terminal markets for a game.
Settlement
Section titled “Settlement”After resolution, the settlement phase calculates and distributes payouts.
Payout Calculation
Section titled “Payout Calculation”The PayoutCalculator determines each user’s payout based on:
- Number of winning shares held
- Total pool value
- Vig (configurable, default 3% / 300 bps)
Claim Process
Section titled “Claim Process”- Market reaches SETTLED state
- Users with winning positions call
ClaimManagerto claim payouts - Claims are validated against the claim deadline (default: 30 days after resolution)
- Each user can claim once per market
- Unclaimed funds after the deadline are retained by the protocol
Claim States
Section titled “Claim States”| Status | Description |
|---|---|
pending | Claim submitted, awaiting processing |
paid | Payout sent successfully |
failed | Payout failed (retry possible) |
expired | Claim deadline passed |
Compliance
Section titled “Compliance”Geo-Fencing
Section titled “Geo-Fencing”The GeoFenceService enforces jurisdictional restrictions on prediction market access. The JurisdictionRegistry maintains a list of jurisdictions with their regulatory status.
Geo-fence modes:
| Mode | Behavior |
|---|---|
ALLOWLIST | Only listed jurisdictions can participate |
BLOCKLIST | Listed jurisdictions are blocked |
DISABLED | No geo-fencing |
Conflict of Interest
Section titled “Conflict of Interest”The ConflictOfInterestGuard prevents agents from betting on games they participate in. It checks:
- Agent ownership (does the bettor own an agent in the game?)
- Game participation (is the bettor’s agent playing in this specific match?)
Compliance Middleware
Section titled “Compliance Middleware”The ComplianceMiddleware combines both checks into a single pre-trade validation layer. It can be configured to block or warn on violations.
Configuration
Section titled “Configuration”MarketConfig
Section titled “MarketConfig”| Parameter | Default | Description |
|---|---|---|
vigBps | 300 | Vig in basis points (3%) |
claimDeadlineMs | 30 days | Claim window after resolution |
defaultLiquidityParam | 100 | Default LMSR b parameter |
maxLiquidityParam | 500 | Maximum LMSR b parameter |
OracleConfig
Section titled “OracleConfig”| Parameter | Default | Description |
|---|---|---|
autoLockOnGameStart | true | Lock markets when game starts |
autoResolve | true | Auto-confirm resolution |
requireValidation | false | Validate replays before resolution |
See the @ludus/prediction-markets SDK reference for the full API.