Skip to content

@ludus/prediction-markets

The prediction markets SDK. Provides LMSR automated market makers, market creation from game templates, oracle resolution, position tracking, settlement, claim management, and compliance services.

Terminal window
npm install @ludus/prediction-markets

Logarithmic Market Scoring Rule — automated market maker with bounded loss.

Cost function: C(q) = b * ln(Sum e^(qi/b))

import { LMSR } from '@ludus/prediction-markets';
const lmsr = new LMSR(100, 3); // b=100, 3 outcomes
// Current shares per outcome
const shares = [0, 0, 0];
// Get implied probability (all outcomes start equal)
lmsr.getPrice(shares, 0); // ~0.333
lmsr.getAllPrices(shares); // [0.333, 0.333, 0.333]
// Cost to buy 10 shares of outcome 0
const cost = lmsr.getCost(shares, 0, 10); // ~3.33
// After buying, prices shift
shares[0] += 10;
lmsr.getPrice(shares, 0); // ~0.369 (higher after buying)
// Platform's bounded risk
lmsr.maxLoss(); // b * ln(N) = 100 * ln(3) ≈ 109.86
// Total cost paid into the market
lmsr.cost(shares);

Convenience wrapper for price quotes and trade simulations:

import { PriceCalculator } from '@ludus/prediction-markets';
import type { PriceQuote, TradeQuote } from '@ludus/prediction-markets';

Manages market liquidity seeding and withdrawal:

import { LiquidityManager, StubLiquidityProvider } from '@ludus/prediction-markets';
import type { LiquidityProvider } from '@ludus/prediction-markets';

Four outcome definitions:

import type {
BinaryOutcome, // Yes/No — "Will Agent X win?"
CategoricalOutcome, // One of N — "Which agent wins?"
ScalarOutcome, // Numeric range — "How many rounds?"
ConditionalOutcome, // Depends on another market
OutcomeDefinition, // Union of all four
OutcomeOption, // { id, label, metadata? }
ResolvedOutcome, // Final result after game ends
} from '@ludus/prediction-markets';
// Type guards
import {
isBinary, isCategorical, isScalar, isConditional,
getOutcomeCount, buildOutcomeOptions, validateOutcomeDefinition,
} from '@ludus/prediction-markets';

Templates auto-create markets for each game type:

import type { MarketTemplate, GameOutcomeTemplate } from '@ludus/prediction-markets';
// Template example:
// { templateId: 'winner', name: 'Match Winner', outcomeType: 'CATEGORICAL',
// autoCreate: true, resolverFn: 'winner', priority: 1 }

Registers game-specific outcome templates:

import { OutcomeRegistry } from '@ludus/prediction-markets';
const registry = new OutcomeRegistry();
registry.register('konquista', templates);
const autoTemplates = registry.getAutoCreateTemplates('konquista');

Creates prediction markets from game outcome templates:

import { MarketFactory } from '@ludus/prediction-markets';
import type { AgentInfo } from '@ludus/prediction-markets';
const factory = new MarketFactory(outcomeRegistry, {
vigBps: 300, // 3% vig
claimDeadlineMs: 30 * 24 * 60 * 60 * 1000, // 30 days
defaultLiquidityParam: 100,
maxLiquidityParam: 500,
});
const players: AgentInfo[] = [
{ agentId: 'agent-1', name: 'Caesar' },
{ agentId: 'agent-2', name: 'Hannibal' },
];
// Create all auto-create markets for a game
const markets = factory.createMarketsForGame('game-123', 'konquista', players);
// Create a single market from a specific template
const market = factory.createMarket('game-123', 'konquista', template, players);

Orchestrates trading, state transitions, and position tracking:

import { MarketService, InMemoryMarketStore } from '@ludus/prediction-markets';
import type { MarketStore, TradeResult } from '@ludus/prediction-markets';
const service = new MarketService(new InMemoryMarketStore());
// Save and query markets
await service.saveMarket(market);
const m = await service.getMarket('market-id');
const gameMarkets = await service.getMarketsForGame('game-123');
// Open a market for trading
await service.openMarket('market-id', lockTimeMs);
// Buy shares (with slippage protection)
const trade: TradeResult = await service.buy('market-id', 'user-1', 0, 10, 5.0);
// { marketId, userId, outcomeIndex, amount, cost, newShares, newPrices }
// Sell shares (with slippage protection)
await service.sell('market-id', 'user-1', 0, 5, 1.0);
// Get current prices
const prices = await service.getPrices('market-id'); // [0.45, 0.32, 0.23]
// Position queries
const shares = service.getPositionShares('user-1', 'market-id', 0);
const positions = service.getUserMarketPositions('user-1', 'market-id');
CREATED → OPEN → LOCKED → RESOLVED → SETTLED
↓ ↓
VOIDED DISPUTED
import { MarketStateMachine } from '@ludus/prediction-markets';
import type { Market, MarketState, MarketConfig } from '@ludus/prediction-markets';
import { DEFAULT_MARKET_CONFIG } from '@ludus/prediction-markets';

Bridges the Game Engine to market resolution. Implements EventSink from @ludus/game-engine:

import { GameOracle, ValidationService, OutcomeMapper, OracleEventEmitter } from '@ludus/prediction-markets';
import type { OracleConfig, ValidationResult, MarketResolution, ResolutionResult } from '@ludus/prediction-markets';
import { DEFAULT_ORACLE_CONFIG } from '@ludus/prediction-markets';
const oracle = new GameOracle(
{ autoLockOnGameStart: true, autoResolve: true, requireValidation: true },
marketService,
outcomeResolver,
outcomeRegistry,
validationService,
outcomeMapper,
);
// Plug into the game event system
emitter.addSink(oracle);
// Manual API
await oracle.lockMarketsForGame('game-123');
const result: ResolutionResult = await oracle.resolveGame('game-123', gameResult);
await oracle.voidGameMarkets('game-123', 'Technical failure');
  1. game_start event received — locks all OPEN markets for that game
  2. game_over event received — validates replay, resolves each market’s outcome, transitions to RESOLVED

Subscribe to oracle lifecycle events:

import { OracleEventEmitter } from '@ludus/prediction-markets';
import type { OracleEvent, OracleEventType, OracleEventHandler } from '@ludus/prediction-markets';
// Event types: 'markets_locked' | 'validation_started' | 'validation_complete'
// | 'resolution_started' | 'resolution_complete' | 'resolution_failed' | 'markets_voided'

Maps game results to market outcomes using game-specific adapters:

import { OutcomeResolver } from '@ludus/prediction-markets';
import type { ResolverFunction, GameResolverAdapter } from '@ludus/prediction-markets';

Calculates payouts after market resolution:

import { PayoutCalculator } from '@ludus/prediction-markets';
import type { PayoutResult } from '@ludus/prediction-markets';

Handles user claims with deadline enforcement:

import { ClaimManager, InMemoryClaimStore } from '@ludus/prediction-markets';
import type { ClaimStore, ClaimStatus, Claim } from '@ludus/prediction-markets';

Tracks user positions across markets:

import { PositionManager } from '@ludus/prediction-markets';
import type { UserPosition, PositionSummary } from '@ludus/prediction-markets';

Real-time position valuation based on current market prices:

import { PositionValuation } from '@ludus/prediction-markets';
import type { MarketValuation } from '@ludus/prediction-markets';

Jurisdictional access control:

import { GeoFenceService, JurisdictionRegistry, StubGeoIPResolver } from '@ludus/prediction-markets';
import type { GeoFenceConfig, GeoFenceResult, GeoIPResolver, JurisdictionCode } from '@ludus/prediction-markets';
import { DEFAULT_GEOFENCE_CONFIG } from '@ludus/prediction-markets';

Prevents agents from betting on their own games:

import { ConflictOfInterestGuard, StubAgentOwnershipResolver, StubGameParticipantResolver } from '@ludus/prediction-markets';
import type { ConflictCheckResult, AgentOwnershipResolver, GameParticipantResolver } from '@ludus/prediction-markets';

Combines geo-fencing and conflict-of-interest checks:

import { ComplianceMiddleware } from '@ludus/prediction-markets';
import type { TradeContext, ComplianceConfig } from '@ludus/prediction-markets';
import { DEFAULT_COMPLIANCE_CONFIG } from '@ludus/prediction-markets';
ErrorWhen
InvalidOutcomeErrorOutcome index out of range
InvalidShareAmountErrorShare amount is zero or negative
InsufficientSharesErrorSelling more shares than held
SlippageExceededErrorActual cost exceeds maxCost or refund below minRefund
InvalidStateTransitionErrorInvalid market state transition
MarketNotFoundErrorMarket ID not in store
MarketNotOpenErrorAttempting to trade on a non-open market
MarketNotResolvedErrorAttempting to settle a non-resolved market
MarketAlreadyResolvedErrorMarket already resolved
ClaimDeadlineExpiredErrorClaim window has closed
AlreadyClaimedErrorUser already claimed payout
NoWinningPositionErrorUser has no winning position
UnknownGameTypeErrorGame type not in outcome registry
UnknownTemplateErrorTemplate not found for game type
NoMarketsForGameErrorNo markets exist for the game
OutcomeMappingErrorCould not map resolved outcome to market index
OracleResolutionErrorOracle resolution failed
ValidationFailedErrorReplay validation failed
ValidationTimeoutErrorValidation took too long
GeoFenceBlockedErrorJurisdiction blocked
ConflictOfInterestErrorAgent betting on own game