26 min read

> "The best way to predict the future is to create a market for it."

Chapter 5: The Modern Platform Landscape

"The best way to predict the future is to create a market for it." — Adapted from Peter Drucker

In previous chapters, we explored what prediction markets are, why they work, and the mathematical foundations that make them powerful aggregators of information. Now it is time to get concrete. Where do you actually go to trade on beliefs about the future?

The modern prediction market landscape is surprisingly diverse. A crypto-native exchange processing hundreds of millions in volume sits alongside a federally regulated derivatives exchange, which coexists with play-money platforms where anyone can spin up a market in seconds. Some platforms pay you in dollars, others in cryptocurrency, and still others in points that have no monetary value at all — yet somehow still produce remarkably accurate forecasts.

This chapter is your field guide. We will examine every major platform in detail: how they work technically, what makes each one unique, where they excel, and where they fall short. By the end, you will know exactly which platform to use for any given purpose — whether you want to trade for profit, conduct research, practice forecasting, or build applications on top of prediction market data.


5.1 The Platform Ecosystem Overview

The prediction market ecosystem can be organized along two primary axes: monetary type (real money vs. play money) and regulatory framework (regulated vs. unregulated/decentralized). This creates a useful four-quadrant map.

The Four Quadrants

Quadrant 1: Regulated Real-Money Platforms These platforms operate under explicit regulatory approval from bodies like the U.S. Commodity Futures Trading Commission (CFTC). They require KYC (Know Your Customer) verification, comply with financial reporting requirements, and offer the strongest consumer protections.

  • Kalshi — CFTC-regulated Designated Contract Market (DCM)
  • PredictIt — Operated under a CFTC no-action letter (historically)

Quadrant 2: Crypto-Native Real-Money Platforms These platforms use blockchain technology and cryptocurrency for settlement. They operate outside traditional financial regulation, often through offshore entities or decentralized protocols.

  • Polymarket — Largest by volume, built on Polygon
  • Augur — Pioneering decentralized protocol on Ethereum
  • Gnosis/Omen — Conditional token framework

Quadrant 3: Play-Money Platforms These platforms use virtual currencies with no real-world monetary value. Despite this, they often produce forecasts competitive with real-money markets, thanks to reputation incentives and intrinsic motivation.

  • Manifold Markets — Community-driven, anyone can create markets
  • Metaculus — Forecasting platform with continuous distributions

Quadrant 4: Adjacent Platforms These are not pure prediction markets but share significant overlap in function.

  • Sports betting exchanges (Betfair, FanDuel)
  • Corporate internal prediction markets (Google, Microsoft internal tools)
  • Forecasting tournaments (Good Judgment Project, IARPA ACE)

Market Map: Platform Positioning

                    Real Money
                        |
         Kalshi         |        Polymarket
         PredictIt      |        Augur
                        |        Gnosis/Omen
    Regulated ——————————+———————————— Unregulated
                        |
         Corporate      |        Manifold
         Internal       |        Metaculus
                        |
                    Play Money / Points

This map is a simplification — platforms like Metaculus defy easy categorization since they are neither a traditional market nor purely play-money — but it provides a useful mental framework.

Volume and Activity (Approximate, as of late 2025)

Platform Monthly Volume Active Markets User Base
Polymarket $500M–$1B+ 300–500 ~200K+
Kalshi $100M–$300M 200–400 ~100K+
Metaculus N/A (points) 5,000+ ~50K+
Manifold N/A (play money) 10,000+ ~30K+
PredictIt Winding down ~50 ~80K (peak)

Note: These figures are approximate and fluctuate significantly. Polymarket volumes spike dramatically around major political events.


5.2 Polymarket — The Crypto-Native Leader

History and Background

Polymarket was founded in 2020 by Shayne Coplan when he was just 22 years old. It launched during the COVID-19 pandemic, initially attracting users who wanted to trade on pandemic-related outcomes. The platform gained massive attention during the 2024 U.S. presidential election cycle, when trading volumes surged past $1 billion per month, making it by far the largest prediction market in history by volume.

In January 2022, Polymarket settled with the CFTC for $1.4 million over offering unregistered binary options to U.S. persons. Following this, the platform officially blocked U.S. users through geofencing, though enforcement of this restriction has been a matter of ongoing discussion.

How It Works: The Conditional Token Framework

Polymarket is built on the Conditional Token Framework (CTF), originally developed by Gnosis. Here is how it works at a technical level:

  1. Token Creation: For a binary market (Yes/No), the system creates two complementary tokens. If you deposit $1 of USDC (a stablecoin pegged to the U.S. dollar), you receive one "Yes" token and one "No" token.

  2. The Invariant: One Yes token + one No token can always be redeemed for exactly $1 (minus fees). This ensures the market is zero-sum and prices stay anchored to probabilities.

  3. Trading: Users trade these tokens on an order book. If you think the probability of an event is higher than the current Yes price, you buy Yes tokens. If lower, you buy No tokens (or sell Yes tokens).

  4. Resolution: When the event resolves, the winning token is redeemable for $1, and the losing token becomes worthless.

Deposit $1 USDC
    |
    v
+--------+--------+
| 1 Yes  | 1 No   |
| Token  | Token  |
+--------+--------+
    |         |
    v         v
Trade on    Trade on
Order Book  Order Book
    |         |
    v         v
Event Resolves
    |
    v
Winning token = $1
Losing token  = $0

The CLOB via BLOB

Polymarket uses a Central Limit Order Book (CLOB) for matching trades, but with a twist: orders are signed off-chain and matched by an operator, with settlement happening on-chain. This hybrid approach is sometimes called BLOB (bounded/blended limit order book).

This design gives Polymarket the speed and efficiency of a centralized exchange while maintaining the transparency and non-custodial properties of blockchain settlement. Users sign orders with their wallet, and the matching engine pairs compatible orders.

Market Creation

Polymarket markets are created by the Polymarket team and a set of trusted market makers. Unlike some platforms, individual users cannot freely create markets. Each market includes:

  • A clearly defined resolution question
  • Resolution criteria and source
  • An expiration date
  • The underlying Conditional Token pair

Interacting with the Polymarket API in Python

Polymarket offers several public endpoints. The two primary APIs are:

  1. CLOB API — For order book data, trade execution
  2. Gamma API — For market metadata, historical data

Here is a basic example of fetching market data:

"""
Example: Fetching Polymarket market data using the Gamma API.

The Gamma API provides market metadata, current prices, and
historical information without requiring authentication.
"""

import httpx
import json
from datetime import datetime


# Polymarket Gamma API base URL
GAMMA_API_BASE = "https://gamma-api.polymarket.com"


def fetch_active_markets(limit: int = 10, offset: int = 0) -> list[dict]:
    """
    Fetch active markets from Polymarket.

    Args:
        limit: Number of markets to return (max 100).
        offset: Pagination offset.

    Returns:
        List of market dictionaries.
    """
    url = f"{GAMMA_API_BASE}/markets"
    params = {
        "limit": limit,
        "offset": offset,
        "active": True,
        "closed": False,
    }

    response = httpx.get(url, params=params, timeout=30.0)
    response.raise_for_status()
    return response.json()


def fetch_market_by_slug(slug: str) -> dict:
    """
    Fetch a specific market by its URL slug.

    Args:
        slug: The market's URL slug (e.g., 'will-bitcoin-hit-100k-in-2025').

    Returns:
        Market data dictionary.
    """
    url = f"{GAMMA_API_BASE}/markets"
    params = {"slug": slug}

    response = httpx.get(url, params=params, timeout=30.0)
    response.raise_for_status()
    markets = response.json()

    if markets:
        return markets[0]
    return {}


def display_market_summary(market: dict) -> None:
    """Print a formatted summary of a market."""
    question = market.get("question", "N/A")

    # Tokens contain outcome/price information
    tokens = market.get("tokens", [])

    print(f"Question: {question}")
    print(f"  Slug: {market.get('slug', 'N/A')}")
    print(f"  End Date: {market.get('endDate', 'N/A')}")
    print(f"  Active: {market.get('active', 'N/A')}")
    print(f"  Volume: ${float(market.get('volume', 0)):,.2f}")
    print(f"  Liquidity: ${float(market.get('liquidity', 0)):,.2f}")

    for token in tokens:
        outcome = token.get("outcome", "?")
        price = float(token.get("price", 0))
        print(f"    {outcome}: {price:.1%}")

    print()


def main():
    print("=== Polymarket Active Markets ===\n")

    markets = fetch_active_markets(limit=5)

    for market in markets:
        display_market_summary(market)

    # Demonstrate fetching market time series
    print("\n=== Detailed Market Lookup ===\n")

    # Fetch a specific market (slug may change — this is illustrative)
    specific = fetch_market_by_slug("will-bitcoin-hit-100k-in-2025")
    if specific:
        display_market_summary(specific)
    else:
        print("Market not found (slug may have changed).")


if __name__ == "__main__":
    main()

Strengths and Weaknesses

Strengths: - Highest liquidity of any prediction market (especially for political and crypto events) - Non-custodial: users control their own wallets - Fast order execution with the hybrid CLOB model - Strong mobile experience - Rich public API for data access - No position limits on most markets

Weaknesses: - Not available to U.S. residents (officially) - Requires cryptocurrency (USDC on Polygon) to participate - Market creation is centralized — users cannot create their own markets - Resolution disputes can be contentious (relies on UMA oracle system) - Regulatory uncertainty remains


5.3 Kalshi — The Regulated Exchange

History and Regulatory Journey

Kalshi was founded in 2018 by Tarek Mansour and Luana Lopes Lara, both MIT graduates. In 2020, Kalshi received approval from the CFTC as a Designated Contract Market (DCM) — the same regulatory designation held by the Chicago Mercantile Exchange (CME). This made Kalshi the first federally regulated exchange dedicated to event contracts in the United States.

The regulatory journey was anything but smooth. Each new contract type Kalshi wanted to offer had to go through a CFTC approval process:

  • 2020: Initial DCM approval
  • 2021: Launch with weather and economic event contracts
  • 2022: Expanded into entertainment, tech events, and more
  • 2023–2024: Landmark legal battle over election contracts. Kalshi sued the CFTC after the commission blocked its congressional control contracts. A federal court ruled in Kalshi's favor, allowing election-related event contracts.
  • 2024–2025: Rapid expansion into political markets, becoming a major player during election season

How It Works: Event Contracts

Kalshi uses a straightforward binary event contract model:

  1. Contracts: Each contract pays $1 if the event occurs and $0 if it does not.
  2. Pricing: Contracts trade between $0.01 and $0.99 (representing 1% to 99% probability).
  3. Order Book: Kalshi runs a traditional CLOB where buyers and sellers are matched.
  4. Settlement: Cash settlement in U.S. dollars, directly to your bank account.

Unlike Polymarket's crypto-based system, Kalshi operates entirely in U.S. dollars with standard banking infrastructure.

Market Categories

Kalshi organizes markets into several categories:

  • Economics: Fed rate decisions, GDP, inflation, unemployment
  • Politics: Elections, legislation, government actions
  • Climate/Weather: Temperature records, hurricane activity
  • Tech: Product launches, company milestones
  • Finance: Stock price thresholds, crypto prices
  • Culture/Entertainment: Award shows, TV ratings

Fee Structure

Kalshi's fee structure has evolved over time:

  • Trading fee: Typically $0.01 per contract per side (varies by contract)
  • No withdrawal fees for standard bank transfers
  • No deposit fees for bank transfers
  • Exchange fees may apply to certain contract types

Interacting with the Kalshi API in Python

Kalshi provides a well-documented REST API. Accessing market data requires authentication.

"""
Example: Interacting with the Kalshi API.

Kalshi provides a REST API for market data and trading.
Authentication is required for most endpoints.
Public market data can also be accessed via their demo/public endpoints.
"""

import httpx
import json
from datetime import datetime, timezone


# Kalshi API base URLs
KALSHI_API_BASE = "https://api.elections.kalshi.com/trade-api/v2"
KALSHI_DEMO_BASE = "https://demo-api.kalshi.co/trade-api/v2"


class KalshiClient:
    """Simple Kalshi API client for fetching market data."""

    def __init__(self, email: str = "", password: str = "", demo: bool = True):
        """
        Initialize the Kalshi client.

        Args:
            email: Kalshi account email.
            password: Kalshi account password.
            demo: If True, use demo API (no real money).
        """
        self.base_url = KALSHI_DEMO_BASE if demo else KALSHI_API_BASE
        self.token = None
        self.client = httpx.Client(timeout=30.0)

        if email and password:
            self._login(email, password)

    def _login(self, email: str, password: str) -> None:
        """Authenticate and store the access token."""
        url = f"{self.base_url}/login"
        payload = {"email": email, "password": password}

        response = self.client.post(url, json=payload)
        response.raise_for_status()

        data = response.json()
        self.token = data.get("token")
        self.client.headers["Authorization"] = f"Bearer {self.token}"

    def get_events(self, limit: int = 10, status: str = "open") -> list[dict]:
        """
        Fetch events from Kalshi.

        Args:
            limit: Number of events to return.
            status: Filter by status ('open', 'closed', 'settled').

        Returns:
            List of event dictionaries.
        """
        url = f"{self.base_url}/events"
        params = {"limit": limit, "status": status}

        response = self.client.get(url, params=params)
        response.raise_for_status()

        data = response.json()
        return data.get("events", [])

    def get_markets(
        self,
        event_ticker: str = None,
        limit: int = 20,
        status: str = "open",
    ) -> list[dict]:
        """
        Fetch markets, optionally filtered by event.

        Args:
            event_ticker: Filter by parent event ticker.
            limit: Number of markets to return.
            status: Filter by status.

        Returns:
            List of market dictionaries.
        """
        url = f"{self.base_url}/markets"
        params = {"limit": limit, "status": status}
        if event_ticker:
            params["event_ticker"] = event_ticker

        response = self.client.get(url, params=params)
        response.raise_for_status()

        data = response.json()
        return data.get("markets", [])

    def get_market(self, ticker: str) -> dict:
        """
        Fetch a single market by ticker.

        Args:
            ticker: The market ticker (e.g., 'INXD-25FEB14-B5450').

        Returns:
            Market data dictionary.
        """
        url = f"{self.base_url}/markets/{ticker}"

        response = self.client.get(url)
        response.raise_for_status()

        return response.json().get("market", {})

    def get_market_orderbook(self, ticker: str, depth: int = 10) -> dict:
        """
        Fetch the order book for a market.

        Args:
            ticker: The market ticker.
            depth: Number of price levels to return.

        Returns:
            Order book dictionary with bids and asks.
        """
        url = f"{self.base_url}/markets/{ticker}/orderbook"
        params = {"depth": depth}

        response = self.client.get(url, params=params)
        response.raise_for_status()

        return response.json().get("orderbook", {})


def display_market(market: dict) -> None:
    """Print a formatted market summary."""
    print(f"Ticker: {market.get('ticker', 'N/A')}")
    print(f"  Title: {market.get('title', 'N/A')}")
    print(f"  Subtitle: {market.get('subtitle', 'N/A')}")
    print(f"  Status: {market.get('status', 'N/A')}")

    yes_bid = market.get("yes_bid", 0)
    yes_ask = market.get("yes_ask", 0)

    if yes_bid and yes_ask:
        midpoint = (yes_bid + yes_ask) / 2
        print(f"  Yes Bid: ${yes_bid/100:.2f}")
        print(f"  Yes Ask: ${yes_ask/100:.2f}")
        print(f"  Midpoint: {midpoint:.1f}%")

    volume = market.get("volume", 0)
    print(f"  Volume: {volume:,} contracts")
    print(f"  Open Interest: {market.get('open_interest', 0):,}")
    print()


def main():
    # Using demo mode — no real credentials needed for illustration
    # In practice, you would pass real credentials for the live API
    print("=== Kalshi Market Data (Demo) ===\n")
    print("Note: This example shows API structure.")
    print("You need valid credentials to run against the live API.\n")

    # Demonstrate the structure without requiring authentication
    # The following shows what the data looks like
    sample_market = {
        "ticker": "FED-25MAR12-T4.50",
        "title": "Fed funds rate",
        "subtitle": "Will the Fed cut rates at the March 2025 meeting?",
        "status": "open",
        "yes_bid": 35,
        "yes_ask": 38,
        "volume": 152340,
        "open_interest": 45200,
    }

    print("Sample market data structure:\n")
    display_market(sample_market)

    # To use with real credentials:
    # client = KalshiClient(email="you@example.com", password="pass", demo=True)
    # events = client.get_events(limit=5)
    # for event in events:
    #     print(event["title"])
    #     markets = client.get_markets(event_ticker=event["event_ticker"])
    #     for m in markets:
    #         display_market(m)


if __name__ == "__main__":
    main()

Strengths and Weaknesses

Strengths: - Fully CFTC-regulated — highest level of consumer protection - USD deposits and withdrawals via standard banking - No cryptocurrency required - Clean, professional interface - Growing selection of market categories - Legal for U.S. residents - Robust, well-documented API

Weaknesses: - U.S. only (currently) - Market creation is centralized (Kalshi team only) - Lower liquidity than Polymarket on many markets - Individual position limits on certain contracts - New contract types require regulatory approval, limiting agility - Fee structure can reduce profitability on small trades


5.4 Metaculus — The Forecasting Platform

A Different Paradigm

Metaculus is not a prediction market in the traditional sense — it is a forecasting platform. Instead of buying and selling contracts, users submit probability estimates (forecasts) for questions. There is no order book, no trading, and no real money at stake. Yet Metaculus has become one of the most respected sources of crowdsourced forecasts in the world.

Founded in 2015 by Anthony Aguirre and Greg Laughlin, both physics professors at UC Santa Cruz, Metaculus was designed to advance the science of human judgment and forecasting.

How It Works

Question Types: Metaculus supports several question types:

  1. Binary Questions: Will event X happen? Users submit a probability between 1% and 99%.
  2. Continuous Questions: What will the value of X be? Users submit a full probability distribution (using a mixture of logistic distributions).
  3. Date Questions: When will event X happen? Users submit a probability distribution over dates.
  4. Conditional Questions: If X happens, what is the probability of Y?
  5. Group Questions: Collections of related questions.

Aggregation: The "Community Prediction" is not a simple average — Metaculus uses a proprietary aggregation algorithm that weights forecasters by their track records, recency of prediction, and other factors. The exact algorithm is not fully public, but it generally produces predictions that outperform the simple median.

The platform also produces a "Metaculus Prediction," which is an algorithmically generated forecast that tends to outperform the community average.

Scoring System

Metaculus uses several scoring metrics:

Log Score: The primary scoring rule. For a binary question where the outcome is Yes, the log score is:

$$\text{Log Score} = \log_2(p)$$

where $p$ is the probability you assigned to the correct outcome. If you said 90% and the answer was Yes, your log score is $\log_2(0.9) \approx -0.152$. If you said 10% and the answer was Yes, your log score is $\log_2(0.1) \approx -3.322$.

Baseline Score: Metaculus compares your score against a baseline (typically the community median at the time of your prediction). Positive relative scores mean you outperformed the crowd.

Peer Score: A score comparing your prediction to what other forecasters predicted, designed to be more robust and harder to game.

Coverage: The fraction of a question's lifetime during which you had an active prediction. Higher coverage earns more weight.

Track Records and Calibration

One of Metaculus's most valuable features is transparent track records. Every user's calibration curve is visible:

Perfect Calibration (diagonal line):

100% |                              /
     |                           /
 80% |                        /
     |                     /
 60% |                  /
     |               /
 40% |            /
     |         /
 20% |      /
     |   /
  0% |/______________________________
     0%  20%  40%  60%  80%  100%
           Predicted Probability

A well-calibrated forecaster should have their actual resolution rate match their predicted probability: events they predicted at 70% should resolve positively about 70% of the time.

Metaculus vs. Traditional Prediction Markets

Feature Prediction Markets Metaculus
Incentive Financial profit Reputation, points
Mechanism Order book trading Direct probability submission
Output Price (= probability) Aggregated distribution
Updating Market dynamics Individual re-forecasts
Sybil resistance Capital requirement Account verification, track record
Question types Mostly binary Binary, continuous, date, conditional
Distribution info Price only Full probability distributions

Python API Interaction

"""
Example: Interacting with the Metaculus API.

Metaculus provides a public API for accessing questions,
predictions, and user data.
"""

import httpx
import json
from datetime import datetime


METACULUS_API_BASE = "https://www.metaculus.com/api2"


def fetch_questions(
    search: str = "",
    limit: int = 10,
    offset: int = 0,
    status: str = "open",
    order_by: str = "-activity",
) -> dict:
    """
    Fetch questions from Metaculus.

    Args:
        search: Search string to filter questions.
        limit: Number of questions to return.
        offset: Pagination offset.
        status: 'open', 'closed', 'resolved', or 'all'.
        order_by: Sort order ('-activity', '-publish_time', '-votes', etc.).

    Returns:
        API response dictionary with 'results' list.
    """
    url = f"{METACULUS_API_BASE}/questions/"
    params = {
        "search": search,
        "limit": limit,
        "offset": offset,
        "status": status,
        "order_by": order_by,
    }

    response = httpx.get(url, params=params, timeout=30.0)
    response.raise_for_status()
    return response.json()


def fetch_question_detail(question_id: int) -> dict:
    """
    Fetch detailed data for a specific question.

    Args:
        question_id: The numeric question ID.

    Returns:
        Question data dictionary.
    """
    url = f"{METACULUS_API_BASE}/questions/{question_id}/"

    response = httpx.get(url, timeout=30.0)
    response.raise_for_status()
    return response.json()


def display_question(q: dict) -> None:
    """Print a formatted question summary."""
    title = q.get("title", "N/A")
    q_id = q.get("id", "N/A")
    status = q.get("status", "N/A")

    # Community prediction
    community = q.get("community_prediction", {})
    if isinstance(community, dict):
        full = community.get("full", {})
        if isinstance(full, dict):
            q2 = full.get("q2")  # Median
        else:
            q2 = None
    else:
        q2 = None

    forecasters = q.get("number_of_forecasters", 0)

    print(f"[{q_id}] {title}")
    print(f"  Status: {status}")
    if q2 is not None:
        print(f"  Community Median: {q2:.1%}")
    print(f"  Forecasters: {forecasters}")
    print(f"  URL: https://www.metaculus.com/questions/{q_id}/")
    print()


def main():
    print("=== Metaculus Open Questions ===\n")

    # Fetch popular open questions
    result = fetch_questions(limit=5, order_by="-votes")
    questions = result.get("results", [])

    for q in questions:
        display_question(q)

    # Search for specific topics
    print("=== AI-Related Questions ===\n")
    ai_result = fetch_questions(search="artificial intelligence", limit=5)
    for q in ai_result.get("results", []):
        display_question(q)


if __name__ == "__main__":
    main()

Strengths and Weaknesses

Strengths: - Rich question types including continuous distributions and conditionals - Transparent track records and calibration data - Strong community of skilled forecasters - No money required — low barrier to entry - Excellent for long-term, complex questions - Valuable for research and decision-support - Open API for data access

Weaknesses: - No financial incentives (may reduce participation for some) - Slower to react than real-money markets (no continuous trading) - Community can be insular — new users face a learning curve - Question resolution can be slow or contentious - Cannot express intensity of belief through position sizing (everyone's vote weights similarly, modulo track record) - API rate limits can be restrictive for heavy data users


5.5 Manifold Markets — Play Money Innovation

The Vision: Markets for Everything

Manifold Markets launched in 2022 with a radical premise: What if anyone could create a prediction market about anything? By using play money (called Mana, denoted M$), Manifold removed the regulatory barriers that constrain real-money platforms and created a vibrant, experimental ecosystem.

Founded by Stephen Grugett and James Grugett (brothers) along with Austin Chen, Manifold operates as a social prediction market platform — think of it as the Twitter of forecasting.

How Mana Works

  • New users receive a starting balance of Mana (the amount has varied over time, typically M$500–M$1000).
  • Mana can be purchased with real money but historically could not be redeemed for cash (this has evolved — Manifold experimented with charity donation redemption and prize points).
  • Despite having no direct monetary value, Mana creates meaningful incentives: leaderboards, reputation, and the satisfaction of being right.
  • In 2024, Manifold introduced "sweepstakes" markets using a separate "sweepcash" currency that could be redeemed for prizes, partially bridging the gap to real-money prediction.

Market Types

Manifold supports a remarkable variety of market types:

  1. Binary (Yes/No): Standard binary outcome markets.
  2. Multiple Choice: Markets with more than two outcomes.
  3. Free Response: Open-ended markets where users can add new answers.
  4. Numeric: Markets that resolve to a number within a range.
  5. Stock-like: Pseudo-stock markets for individuals or concepts.
  6. Bounty: Not a market — a bounty question where the creator pays Mana for useful answers.
  7. Polls: Simple polling questions.

Market Mechanism

Manifold uses an Automated Market Maker (AMM) based on the Constant Product (Maniswap) formula, though this has evolved over time. The key properties:

  • Markets always have liquidity — you can always buy or sell.
  • The price moves along a bonding curve.
  • Creators provide initial liquidity when creating a market.
  • Limit orders are also supported, creating a hybrid AMM+order-book system.

Unique Features

Anyone Can Create Markets: The defining feature. A user can create a market in under 30 seconds on any topic — "Will my cat knock over the Christmas tree this year?" or "Will GPT-5 be released before July 2025?"

Loans: Manifold provides daily "loans" on outstanding bets, freeing up capital. If you have M$100 bet on a market, Manifold gives you back a portion daily to bet elsewhere. This encourages active participation across many markets.

Comments and Discussion: Each market has a comment thread, creating community discussion around every question.

Groups and Topics: Markets are organized by topic (Politics, AI, Science, Personal, etc.), and users can create custom groups.

Calibration and Track Records: Like Metaculus, Manifold tracks user calibration.

Python API Interaction

"""
Example: Interacting with the Manifold Markets API.

Manifold provides a comprehensive public API.
Most read endpoints require no authentication.
"""

import httpx
import json
from datetime import datetime


MANIFOLD_API_BASE = "https://api.manifold.markets/v0"


def fetch_markets(
    limit: int = 10,
    sort: str = "liquidity",
    order: str = "desc",
) -> list[dict]:
    """
    Fetch markets from Manifold.

    Args:
        limit: Number of markets to return (max 1000).
        sort: Sort field ('liquidity', 'created-time', 'updated-time', etc.).
        order: Sort order ('asc' or 'desc').

    Returns:
        List of market dictionaries.
    """
    url = f"{MANIFOLD_API_BASE}/search-markets"
    params = {
        "limit": limit,
        "sort": sort,
        "order": order,
    }

    response = httpx.get(url, params=params, timeout=30.0)
    response.raise_for_status()
    return response.json()


def fetch_market_by_slug(slug: str) -> dict:
    """
    Fetch a specific market by its URL slug.

    Args:
        slug: The market slug (from the URL).

    Returns:
        Market data dictionary.
    """
    url = f"{MANIFOLD_API_BASE}/slug/{slug}"

    response = httpx.get(url, timeout=30.0)
    response.raise_for_status()
    return response.json()


def fetch_user(username: str) -> dict:
    """
    Fetch a user profile.

    Args:
        username: The Manifold username.

    Returns:
        User data dictionary.
    """
    url = f"{MANIFOLD_API_BASE}/user/{username}"

    response = httpx.get(url, timeout=30.0)
    response.raise_for_status()
    return response.json()


def search_markets(query: str, limit: int = 10) -> list[dict]:
    """
    Search for markets by text query.

    Args:
        query: Search string.
        limit: Maximum results.

    Returns:
        List of matching market dictionaries.
    """
    url = f"{MANIFOLD_API_BASE}/search-markets"
    params = {"term": query, "limit": limit}

    response = httpx.get(url, params=params, timeout=30.0)
    response.raise_for_status()
    return response.json()


def display_market(market: dict) -> None:
    """Print a formatted market summary."""
    question = market.get("question", "N/A")

    probability = market.get("probability")
    total_liquidity = market.get("totalLiquidity", 0)
    volume = market.get("volume", 0)
    unique_bettors = market.get("uniqueBettorCount", 0)
    creator = market.get("creatorUsername", "N/A")
    mechanism = market.get("mechanism", "N/A")

    print(f"Q: {question}")
    if probability is not None:
        print(f"  Probability: {probability:.1%}")
    print(f"  Creator: @{creator}")
    print(f"  Mechanism: {mechanism}")
    print(f"  Liquidity: M${total_liquidity:,.0f}")
    print(f"  Volume: M${volume:,.0f}")
    print(f"  Unique Bettors: {unique_bettors}")
    print(f"  URL: https://manifold.markets/{market.get('creatorUsername')}/{market.get('slug', '')}")
    print()


def main():
    print("=== Manifold Markets — Top by Liquidity ===\n")

    markets = fetch_markets(limit=5, sort="liquidity", order="desc")
    for m in markets:
        display_market(m)

    print("=== Search: 'AI' ===\n")
    ai_markets = search_markets("AI", limit=5)
    for m in ai_markets:
        display_market(m)


if __name__ == "__main__":
    main()

Strengths and Weaknesses

Strengths: - Anyone can create markets — massive breadth of topics - Very low barrier to entry (free to play) - Excellent API, open source codebase - Creative market types (multiple choice, numeric, free response) - Active, engaged community - Fast iteration on features - Great for learning and practice

Weaknesses: - Play money reduces financial incentives (though sweepstakes addresses this partially) - Market quality varies enormously (many poorly specified markets) - Mana inflation and economic design challenges - Lower accuracy than real-money markets on high-stakes questions - Resolution disputes are common on community-created markets - Some markets are jokes or personal (not useful for serious forecasting)


5.6 PredictIt — The Academic Pioneer

The No-Action Letter

PredictIt holds a unique place in prediction market history. Launched in 2014 by Victoria University of Wellington (New Zealand), it operated in the United States under a CFTC no-action letter — essentially a promise from the regulator not to take enforcement action, contingent on several conditions:

  1. The platform must be operated by a university for academic research purposes.
  2. No market may have more than 850 traders.
  3. No trader may invest more than $850 in any single market.
  4. The platform must share data with academic researchers.

This was the first time a prediction market could legally operate in the U.S. with real money (outside of the Iowa Electronic Markets, which had a similar academic exemption).

Fee Structure

PredictIt's fees were notably high:

  • 10% profit fee: PredictIt took 10% of any profits on a market.
  • 5% withdrawal fee: An additional 5% fee on all withdrawals.
  • No deposit fees.

These fees meant a trader needed a significant edge to be profitable. A contract priced at 50 cents with a true probability of 60% — normally a very attractive bet — would barely break even after PredictIt's fees.

The 850-Trader Limit Problem

The 850-trader limit created severe distortions:

  • Popular markets would fill up almost instantly.
  • Locked-out traders created artificial demand with no way to enter.
  • Traders who got in early could hold positions hostage.
  • It limited the information aggregation properties that make prediction markets valuable.

Regulatory Challenges and 2023 Status

In August 2022, the CFTC announced it would withdraw PredictIt's no-action letter, effective February 2023. After legal challenges, PredictIt was given limited extensions to wind down existing markets, but was prohibited from creating new ones.

As of late 2023 and into 2024, PredictIt was in a protracted wind-down phase. Several legal challenges were mounted, and the platform's ultimate fate remained uncertain. Victoria University and PredictIt's operators argued the CFTC's withdrawal was arbitrary and capricious.

Regardless of its operational status, PredictIt's legacy is significant: it proved that real-money prediction markets could operate in the U.S. regulatory environment and generated a wealth of academic research.

Historical Importance

PredictIt was instrumental in:

  • Demonstrating demand: Millions of dollars traded on political outcomes proved there was a real market.
  • Academic research: Dozens of published papers used PredictIt data.
  • Regulatory precedent: The no-action letter model (and its limitations) informed subsequent regulatory approaches.
  • Popularizing prediction markets: PredictIt was the first real-money prediction market many Americans encountered.
  • Paving the way for Kalshi: Kalshi's founders have acknowledged that PredictIt demonstrated the market existed.

5.7 Other Notable Platforms

Augur

Augur was the first major decentralized prediction market protocol, launched on Ethereum in 2018 after a 2015 ICO that raised $5.3 million. Augur's key innovation was fully decentralized market creation and resolution using the REP token for dispute resolution.

However, Augur suffered from poor UX, high Ethereum gas fees, and low liquidity. Augur v2 launched in 2020 with improvements but never achieved mainstream adoption. Augur's ideas heavily influenced subsequent projects, including Polymarket.

Gnosis and Omen

Gnosis developed the Conditional Token Framework — the technical standard that Polymarket and others build upon. The Omen prediction market, powered by Gnosis, provided a decentralized interface for trading conditional tokens.

Gnosis has since pivoted to focus on its Gnosis Chain (formerly xDai) and the Gnosis Safe (now Safe) multi-signature wallet, but its conditional token framework remains one of the most important technical contributions to the prediction market ecosystem.

Insight Prediction

Insight Prediction is a smaller crypto-native platform that has carved out a niche with competitive fees and markets focused on geopolitics, conflict, and international events — topics that larger platforms sometimes shy away from.

Sports Betting Platforms as Prediction Markets

Platforms like Betfair Exchange function as prediction markets in all but name. Betfair's exchange model — where users bet against each other rather than against the house — is mechanically identical to a prediction market order book.

Key differences from dedicated prediction markets: - Focus exclusively on sports and racing events - Highly liquid (Betfair processes billions annually) - Regulated as gambling, not financial instruments - Sophisticated API and data infrastructure - Odds formats differ (decimal, fractional) but are convertible to probabilities

Corporate Internal Prediction Markets

Several major technology companies have operated internal prediction markets:

  • Google: Ran internal prediction markets for product launch dates, quarterly metrics, and strategic decisions. Research published by Bo Cowgill showed these markets were well-calibrated.
  • Microsoft: Used internal markets for project timeline predictions.
  • Ford Motor Company: Experimented with internal prediction markets for quality control.
  • Intel: Used prediction markets for forecasting chip yields.

These internal markets typically use play money and are designed to surface information that might not flow through normal management channels.


5.8 Head-to-Head Comparison

The following table provides a detailed comparison across the major platforms:

Feature Polymarket Kalshi Metaculus Manifold PredictIt
Type Real money (crypto) Real money (USD) Points/reputation Play money (Mana) Real money (USD)
Founded 2020 2018 (launched 2021) 2015 2022 2014
Regulation Offshore/unregulated CFTC-regulated DCM N/A N/A CFTC no-action letter
Geography Global (excl. U.S. officially) U.S. only Global Global U.S. only
Market Mechanism CLOB (hybrid on/off-chain) CLOB Aggregated forecasts AMM + limit orders CLOB
Market Creation Centralized (Polymarket team) Centralized (Kalshi team) Centralized (community nominations) Open (anyone) Centralized
Contract Type Conditional tokens (USDC) Binary event contracts Probability estimates Mana-denominated shares Binary shares
Deposit Method Crypto (USDC on Polygon) Bank transfer, debit card N/A Credit card (for Mana purchase) Bank transfer, credit card
Trading Fees ~1-2% (taker) ~$0.01/contract/side N/A Built into AMM spread N/A
Withdrawal Fees Gas fees Free N/A N/A 5%
Profit Fees None None N/A N/A 10%
Position Limits Generally none Varies by contract N/A None $850/market
Trader Limits None None None None 850/market
API Quality Good (Gamma + CLOB) Good (REST) Good (REST) Excellent (REST) Basic (REST)
Open Source Partial No Partial Yes (fully) No
Typical Liquidity Very high (political) Medium-high N/A Low-medium Low
Mobile App Yes Yes Yes Yes (web) Yes
Resolution UMA oracle Kalshi team Metaculus team Market creator PredictIt team

Fee Impact Analysis

Consider a simple trade: buying a Yes contract at $0.60 that resolves to $1.00 (a $0.40 profit per contract).

Platform Gross Profit Fees Net Profit Effective Fee Rate
Polymarket $0.40 | ~$0.01 (taker fee) ~$0.39 ~2.5%
Kalshi $0.40 | ~$0.02 (buy + sell) ~$0.38 ~5%
PredictIt $0.40 | $0.04 (10% profit) + $0.018 (5% withdrawal) | ~$0.34 ~15%

PredictIt's fee structure was notably punishing, especially the combination of profit and withdrawal fees.


5.9 Choosing the Right Platform

Your ideal platform depends on your goals. Here is a decision framework:

Goal: Trading for Profit

Best choice: Polymarket (if non-U.S.) or Kalshi (if U.S.)

Rationale: - Real money creates real incentives and real rewards. - Polymarket offers the highest liquidity, making it easier to enter and exit positions. - Kalshi provides the safest regulatory environment for U.S. traders. - Both have APIs suitable for algorithmic trading.

Goal: Research and Data Analysis

Best choice: Metaculus or Manifold

Rationale: - Both offer excellent public APIs with rich data. - Metaculus provides continuous distributions, not just point estimates. - Manifold is fully open source — you can analyze the entire codebase. - Historical data is freely available from both. - Polymarket also has good data APIs for real-money market analysis.

Goal: Forecasting Practice

Best choice: Metaculus (for serious forecasting) or Manifold (for casual practice)

Rationale: - Metaculus has the best scoring system and calibration tools. - The Metaculus community is focused on accuracy and improvement. - Manifold offers more variety and social engagement. - Both are free to use.

Goal: Building Applications

Best choice: Manifold (fully open source) or Polymarket (best data APIs)

Rationale: - Manifold's entire codebase is open source on GitHub. - Manifold's API has the fewest restrictions. - Polymarket's APIs are well-suited for data applications. - Kalshi's API is excellent but more restricted.

Goal: Understanding Market Microstructure

Best choice: Start with Manifold (risk-free), then move to Polymarket or Kalshi

Rationale: - Manifold lets you experiment with trading strategies using play money. - You can observe how order books and AMMs work without financial risk. - Once comfortable, real-money platforms provide authentic market dynamics.


5.10 API Landscape and Data Access

Each platform offers different levels of API access. Here is a comprehensive overview:

Polymarket APIs

Gamma API (Market Data): - Base URL: https://gamma-api.polymarket.com - Authentication: None required for public data - Rate limits: Moderate (undocumented, but approximately 60 requests/minute) - Key endpoints: - GET /markets — List markets with filtering - GET /markets/{id} — Single market detail - GET /events — Events (groups of related markets)

CLOB API (Trading): - Base URL: https://clob.polymarket.com - Authentication: Wallet signature (EIP-712) - Key endpoints: - GET /book — Order book for a token - GET /price — Current price for a token - POST /order — Place an order (authenticated)

Kalshi API

  • Base URL: https://api.elections.kalshi.com/trade-api/v2
  • Authentication: Email/password login, returns JWT token
  • Rate limits: 10 requests/second
  • Key endpoints:
  • GET /events — List events
  • GET /markets — List markets
  • GET /markets/{ticker} — Market details
  • GET /markets/{ticker}/orderbook — Order book
  • GET /markets/{ticker}/history — Price history
  • POST /portfolio/orders — Place order (authenticated)
  • Documentation: Comprehensive Swagger/OpenAPI docs available

Metaculus API

  • Base URL: https://www.metaculus.com/api2
  • Authentication: Optional (token-based for write operations)
  • Rate limits: Relatively restrictive (varies)
  • Key endpoints:
  • GET /questions/ — List questions with search/filter
  • GET /questions/{id}/ — Question detail
  • GET /questions/{id}/predictions/ — Prediction history
  • POST /questions/{id}/predict/ — Submit prediction (authenticated)

Manifold API

  • Base URL: https://api.manifold.markets/v0
  • Authentication: API key (optional for read, required for write)
  • Rate limits: 100 requests/minute (read), 10/minute (write)
  • Key endpoints:
  • GET /markets — List all markets
  • GET /search-markets — Search markets
  • GET /slug/{slug} — Market by slug
  • GET /market/{id} — Market by ID
  • GET /market/{id}/positions — Positions on a market
  • GET /user/{username} — User profile
  • POST /bet — Place a bet (authenticated)
  • POST /market — Create a market (authenticated)
  • Documentation: Well-maintained, with TypeScript type definitions

Comparison of API Capabilities

Capability Polymarket Kalshi Metaculus Manifold
Public market data Yes Yes Yes Yes
Historical prices Yes Yes Partial Yes
Order book data Yes Yes N/A N/A (AMM)
Place trades Yes (wallet) Yes (JWT) Yes (predict) Yes (API key)
Create markets No No No Yes
User profiles Limited Limited Yes Yes
WebSocket/streaming Yes Yes No No
Bulk data export Limited Limited Yes (datasets) Yes (via Supabase)

5.11 Building a Multi-Platform Dashboard in Python

Let us build a practical tool that fetches data from multiple platforms and presents it in a unified view. This demonstrates how to work across APIs and normalize data from different sources.

"""
Multi-Platform Prediction Market Dashboard

Fetches data from Polymarket, Kalshi, Metaculus, and Manifold,
then presents a unified view for comparison.

Usage:
    python multi_platform_dashboard.py

Requirements:
    pip install httpx tabulate
"""

import httpx
from dataclasses import dataclass, field
from datetime import datetime
from typing import Optional


@dataclass
class NormalizedMarket:
    """A market normalized across platforms for comparison."""
    platform: str
    question: str
    probability: Optional[float]
    volume: Optional[float]
    num_traders: Optional[int]
    url: str
    close_date: Optional[str] = None
    category: str = ""
    raw_data: dict = field(default_factory=dict, repr=False)


class PolymarketFetcher:
    """Fetch and normalize Polymarket data."""

    BASE_URL = "https://gamma-api.polymarket.com"

    def __init__(self):
        self.client = httpx.Client(timeout=30.0)

    def fetch_markets(self, limit: int = 20) -> list[NormalizedMarket]:
        url = f"{self.BASE_URL}/markets"
        params = {"limit": limit, "active": True, "closed": False}

        try:
            response = self.client.get(url, params=params)
            response.raise_for_status()
            raw_markets = response.json()
        except (httpx.HTTPError, Exception) as e:
            print(f"  [Polymarket] Error: {e}")
            return []

        markets = []
        for m in raw_markets:
            tokens = m.get("tokens", [])
            prob = None
            for t in tokens:
                if t.get("outcome", "").lower() == "yes":
                    try:
                        prob = float(t.get("price", 0))
                    except (ValueError, TypeError):
                        pass

            nm = NormalizedMarket(
                platform="Polymarket",
                question=m.get("question", "N/A"),
                probability=prob,
                volume=float(m.get("volume", 0)) if m.get("volume") else None,
                num_traders=None,
                url=f"https://polymarket.com/event/{m.get('slug', '')}",
                close_date=m.get("endDate"),
                raw_data=m,
            )
            markets.append(nm)

        return markets

    def search(self, query: str, limit: int = 10) -> list[NormalizedMarket]:
        url = f"{self.BASE_URL}/markets"
        params = {"limit": limit, "active": True, "closed": False}

        try:
            response = self.client.get(url, params=params)
            response.raise_for_status()
            raw_markets = response.json()
        except (httpx.HTTPError, Exception) as e:
            print(f"  [Polymarket] Search error: {e}")
            return []

        # Client-side filter (Gamma API has limited search)
        query_lower = query.lower()
        filtered = [
            m for m in raw_markets
            if query_lower in m.get("question", "").lower()
        ]

        return self._normalize_list(filtered)

    def _normalize_list(self, raw_markets: list) -> list[NormalizedMarket]:
        markets = []
        for m in raw_markets:
            tokens = m.get("tokens", [])
            prob = None
            for t in tokens:
                if t.get("outcome", "").lower() == "yes":
                    try:
                        prob = float(t.get("price", 0))
                    except (ValueError, TypeError):
                        pass

            nm = NormalizedMarket(
                platform="Polymarket",
                question=m.get("question", "N/A"),
                probability=prob,
                volume=float(m.get("volume", 0)) if m.get("volume") else None,
                num_traders=None,
                url=f"https://polymarket.com/event/{m.get('slug', '')}",
                close_date=m.get("endDate"),
                raw_data=m,
            )
            markets.append(nm)
        return markets


class MetaculusFetcher:
    """Fetch and normalize Metaculus data."""

    BASE_URL = "https://www.metaculus.com/api2"

    def __init__(self):
        self.client = httpx.Client(timeout=30.0)

    def fetch_questions(self, limit: int = 20) -> list[NormalizedMarket]:
        url = f"{self.BASE_URL}/questions/"
        params = {
            "limit": limit,
            "status": "open",
            "order_by": "-activity",
            "type": "forecast",
        }

        try:
            response = self.client.get(url, params=params)
            response.raise_for_status()
            data = response.json()
        except (httpx.HTTPError, Exception) as e:
            print(f"  [Metaculus] Error: {e}")
            return []

        markets = []
        for q in data.get("results", []):
            prob = self._extract_probability(q)
            q_id = q.get("id", "")

            nm = NormalizedMarket(
                platform="Metaculus",
                question=q.get("title", "N/A"),
                probability=prob,
                volume=None,
                num_traders=q.get("number_of_forecasters"),
                url=f"https://www.metaculus.com/questions/{q_id}/",
                close_date=q.get("close_time"),
                raw_data=q,
            )
            markets.append(nm)

        return markets

    def search(self, query: str, limit: int = 10) -> list[NormalizedMarket]:
        url = f"{self.BASE_URL}/questions/"
        params = {
            "search": query,
            "limit": limit,
            "status": "open",
        }

        try:
            response = self.client.get(url, params=params)
            response.raise_for_status()
            data = response.json()
        except (httpx.HTTPError, Exception) as e:
            print(f"  [Metaculus] Search error: {e}")
            return []

        markets = []
        for q in data.get("results", []):
            prob = self._extract_probability(q)
            q_id = q.get("id", "")

            nm = NormalizedMarket(
                platform="Metaculus",
                question=q.get("title", "N/A"),
                probability=prob,
                volume=None,
                num_traders=q.get("number_of_forecasters"),
                url=f"https://www.metaculus.com/questions/{q_id}/",
                close_date=q.get("close_time"),
                raw_data=q,
            )
            markets.append(nm)

        return markets

    @staticmethod
    def _extract_probability(q: dict) -> Optional[float]:
        community = q.get("community_prediction", {})
        if isinstance(community, dict):
            full = community.get("full", {})
            if isinstance(full, dict):
                return full.get("q2")
        return None


class ManifoldFetcher:
    """Fetch and normalize Manifold Markets data."""

    BASE_URL = "https://api.manifold.markets/v0"

    def __init__(self):
        self.client = httpx.Client(timeout=30.0)

    def fetch_markets(self, limit: int = 20) -> list[NormalizedMarket]:
        url = f"{self.BASE_URL}/search-markets"
        params = {"limit": limit, "sort": "liquidity", "order": "desc"}

        try:
            response = self.client.get(url, params=params)
            response.raise_for_status()
            raw_markets = response.json()
        except (httpx.HTTPError, Exception) as e:
            print(f"  [Manifold] Error: {e}")
            return []

        return self._normalize_list(raw_markets)

    def search(self, query: str, limit: int = 10) -> list[NormalizedMarket]:
        url = f"{self.BASE_URL}/search-markets"
        params = {"term": query, "limit": limit}

        try:
            response = self.client.get(url, params=params)
            response.raise_for_status()
            raw_markets = response.json()
        except (httpx.HTTPError, Exception) as e:
            print(f"  [Manifold] Search error: {e}")
            return []

        return self._normalize_list(raw_markets)

    def _normalize_list(self, raw_markets: list) -> list[NormalizedMarket]:
        markets = []
        for m in raw_markets:
            creator = m.get("creatorUsername", "")
            slug = m.get("slug", "")

            nm = NormalizedMarket(
                platform="Manifold",
                question=m.get("question", "N/A"),
                probability=m.get("probability"),
                volume=m.get("volume"),
                num_traders=m.get("uniqueBettorCount"),
                url=f"https://manifold.markets/{creator}/{slug}",
                close_date=None,
                raw_data=m,
            )
            markets.append(nm)

        return markets


class MultiPlatformDashboard:
    """Aggregates data from multiple prediction market platforms."""

    def __init__(self):
        self.polymarket = PolymarketFetcher()
        self.metaculus = MetaculusFetcher()
        self.manifold = ManifoldFetcher()

    def fetch_all_top_markets(self, limit_per_platform: int = 10):
        """Fetch top markets from all platforms."""
        print("Fetching from all platforms...\n")

        all_markets = []

        print("  Fetching Polymarket...")
        all_markets.extend(self.polymarket.fetch_markets(limit_per_platform))

        print("  Fetching Metaculus...")
        all_markets.extend(self.metaculus.fetch_questions(limit_per_platform))

        print("  Fetching Manifold...")
        all_markets.extend(self.manifold.fetch_markets(limit_per_platform))

        return all_markets

    def search_across_platforms(self, query: str, limit: int = 5):
        """Search for a topic across all platforms."""
        print(f"Searching for '{query}' across platforms...\n")

        results = {}

        print("  Searching Polymarket...")
        results["Polymarket"] = self.polymarket.search(query, limit)

        print("  Searching Metaculus...")
        results["Metaculus"] = self.metaculus.search(query, limit)

        print("  Searching Manifold...")
        results["Manifold"] = self.manifold.search(query, limit)

        return results

    def display_search_results(self, results: dict):
        """Display cross-platform search results."""
        for platform, markets in results.items():
            print(f"\n{'='*60}")
            print(f"  {platform} ({len(markets)} results)")
            print(f"{'='*60}")

            if not markets:
                print("  No results found.")
                continue

            for m in markets:
                prob_str = f"{m.probability:.1%}" if m.probability else "N/A"
                print(f"\n  Q: {m.question}")
                print(f"     Prob: {prob_str}")
                if m.volume:
                    print(f"     Volume: ${m.volume:,.0f}")
                if m.num_traders:
                    print(f"     Traders/Forecasters: {m.num_traders}")

    def display_dashboard(self, markets: list[NormalizedMarket]):
        """Display a unified dashboard of markets."""
        try:
            from tabulate import tabulate
            use_tabulate = True
        except ImportError:
            use_tabulate = False

        print(f"\n{'='*80}")
        print("  MULTI-PLATFORM PREDICTION MARKET DASHBOARD")
        print(f"  Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        print(f"  Total markets: {len(markets)}")
        print(f"{'='*80}\n")

        if use_tabulate:
            rows = []
            for m in markets:
                prob_str = f"{m.probability:.1%}" if m.probability else "N/A"
                vol_str = f"${m.volume:,.0f}" if m.volume else "N/A"
                question = m.question[:60] + "..." if len(m.question) > 60 else m.question

                rows.append([
                    m.platform,
                    question,
                    prob_str,
                    vol_str,
                ])

            headers = ["Platform", "Question", "Prob", "Volume"]
            print(tabulate(rows, headers=headers, tablefmt="simple"))
        else:
            for m in markets:
                prob_str = f"{m.probability:.1%}" if m.probability else "N/A"
                vol_str = f"${m.volume:,.0f}" if m.volume else "N/A"

                print(f"[{m.platform:12s}] {prob_str:>6s} | {m.question[:65]}")


def main():
    dashboard = MultiPlatformDashboard()

    # 1. Fetch top markets from all platforms
    all_markets = dashboard.fetch_all_top_markets(limit_per_platform=5)
    dashboard.display_dashboard(all_markets)

    # 2. Cross-platform search
    print("\n\n")
    results = dashboard.search_across_platforms("AI", limit=3)
    dashboard.display_search_results(results)


if __name__ == "__main__":
    main()

This dashboard is a starting point. In a production setting, you would add caching, error handling, persistent storage, and a web frontend.


5.12 Practical Considerations

Before you choose a platform and start trading or forecasting, consider these practical factors:

Regulatory Risk

The regulatory landscape for prediction markets is evolving rapidly. Consider:

  • Polymarket: Settled with the CFTC in 2022. Officially unavailable to U.S. users, but enforcement has been inconsistent. Regulatory risk for U.S.-based users remains significant.
  • Kalshi: As a CFTC-regulated DCM, Kalshi has the most solid regulatory footing. However, new contract types still face regulatory challenges.
  • Manifold/Metaculus: As play-money platforms, they face minimal regulatory risk.
  • General trend: Regulatory clarity is improving, with courts and regulators increasingly recognizing prediction markets as legitimate.

Counterparty Risk

Who holds your money, and what happens if they fail?

  • Polymarket: Non-custodial — your funds are in your own wallet. Smart contract risk exists but counterparty risk is minimal.
  • Kalshi: Custodial — Kalshi holds your funds. As a regulated entity, customer funds should be segregated, but the risk is non-zero.
  • PredictIt: Custodial, and the wind-down process illustrated the risks: some users had difficulty withdrawing funds.
  • Manifold/Metaculus: No real money at risk.

Liquidity Fragmentation

The same event might be traded on multiple platforms with different prices. This creates challenges:

  • Price discrepancies: A market might show 62% on Polymarket and 58% on Kalshi. Which is right?
  • Arbitrage difficulty: Moving money between platforms is slow and costly, making cross-platform arbitrage impractical for most users.
  • Information siloing: Each platform's market reflects only its user base's information.

Platform Lock-In

Consider how easy it is to leave a platform:

  • Polymarket: Non-custodial, so exit is straightforward (just sell your positions and withdraw USDC).
  • Kalshi: Standard withdrawal process to bank account.
  • Data portability: Can you export your forecasting history? Metaculus and Manifold offer good data portability. Others vary.

Tax Implications

Prediction market profits may be taxable. In the U.S.:

  • Kalshi: Issues 1099 forms for profits. Treated as regulated futures or event contracts.
  • Polymarket: Crypto taxation rules apply. Users are responsible for self-reporting.
  • PredictIt: Historically issued 1099-MISC for net profits.
  • Play money: No tax implications (no real money changes hands).

Always consult a tax professional. This is not tax advice.


5.13 Chapter Summary

The prediction market landscape in 2025 is more diverse and accessible than at any point in history. Let us recap the major players:

Polymarket dominates by volume, offering the deepest liquidity in a crypto-native, non-custodial format. It is the platform of choice for serious traders outside the U.S. Its hybrid CLOB model and Conditional Token Framework make it technically sophisticated.

Kalshi provides the strongest regulatory protections as a CFTC-regulated exchange. It is the go-to platform for U.S.-based traders who want real-money exposure to event outcomes. Its regulatory journey has been groundbreaking.

Metaculus stands apart as a forecasting platform rather than a market. Its support for continuous distributions, rigorous scoring systems, and transparent track records make it the best platform for serious forecasting practice and research.

Manifold Markets democratizes prediction markets by letting anyone create a market on any topic. Its play-money model and open-source codebase make it ideal for learning, experimentation, and building applications.

PredictIt pioneered the modern era of U.S. prediction markets. While it is winding down, its legacy — in proving demand, generating research, and shaping regulation — is immense.

Each platform occupies a distinct niche, and the "best" platform depends entirely on your goals. Traders seek Polymarket and Kalshi. Researchers gravitate toward Metaculus. Builders love Manifold. And increasingly, sophisticated users maintain accounts on multiple platforms to take advantage of each one's strengths.

The APIs provided by these platforms open up exciting possibilities for data analysis, algorithmic trading, and building new tools on top of prediction market data. In the chapters ahead, we will use these APIs extensively as we explore trading strategies, calibration techniques, and advanced applications.


What's Next

In Chapter 6, we will explore Market Mechanics and Order Types — how orders are actually matched, what different order types mean, and how market microstructure affects your trading. We will build on the platform knowledge from this chapter with hands-on examples of placing orders, managing positions, and understanding the order book.


Chapter 5 of "Learning Prediction Markets — From Concepts to Strategies"