CCXT
Java Examples

PolymarketEndToEnd

PolymarketEndToEnd — CCXT Java code example.

package examples.prediction;

import io.github.ccxt.exchanges.prediction.Polymarket;
import io.github.ccxt.types.PredictionEvent;
import io.github.ccxt.types.PredictionMarket;
import io.github.ccxt.types.PredictionOrder;
import io.github.ccxt.types.PredictionOrderBook;
import io.github.ccxt.types.PredictionOutcome;
import io.github.ccxt.types.PredictionTicker;
import io.github.ccxt.types.PredictionTrade;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Polymarket end-to-end example (read market data + place/fetch/cancel one order).
 *
 * Flow:
 *   1. pick a high-volume event, a market inside it, and an outcome with a live two-sided book
 *   2. fetch the order book, ticker and recent trades for that outcome
 *   3. place a resting limit BUY well below the book, fetch it back, then cancel it
 *
 * Usage:
 *   POLYMARKET_PRIVATEKEY=... POLYMARKET_WALLETADDRESS=0x... \
 *   ./gradlew :examples:run -PmainClass=examples.prediction.PolymarketEndToEnd
 *
 * walletAddress is the polymarket account wallet (the proxy / deposit wallet shown in
 * your polymarket profile), privateKey is the key of the EOA that owns it.
 */
public class PolymarketEndToEnd {

    static final double MAX_NOTIONAL_USD = 25;  // hard cap per trade
    static final double ORDER_SIZE_SHARES = 5;  // polymarket minimum order size

    public static void main(String[] args) {
        String privateKey = System.getenv("POLYMARKET_PRIVATEKEY");
        String walletAddress = System.getenv("POLYMARKET_WALLETADDRESS");
        if (privateKey == null || walletAddress == null) {
            System.out.println("Set POLYMARKET_PRIVATEKEY and POLYMARKET_WALLETADDRESS env vars first.");
            return;
        }
        Map<String, Object> config = new HashMap<>();
        config.put("privateKey", privateKey);
        config.put("walletAddress", walletAddress);
        Polymarket exchange = new Polymarket(config);

        // 1) pick a high-volume event and an outcome with a live two-sided book ------------
        Map<String, Object> eventsParams = new HashMap<>();
        // fetchEvents requires a scope (query/queries/tags/eventId/slug); sort/limit apply within it
        eventsParams.put("query", "fed");
        eventsParams.put("sort", "volume");
        eventsParams.put("limit", 15);
        List<PredictionEvent> events = exchange.fetchEvents(eventsParams);
        String symbol = null;
        PredictionOrderBook book = null;
        double tick = 0.01;
        int probes = 0;
        outer:
        for (PredictionEvent event : events) {
            if (event.markets == null) continue;
            for (PredictionMarket market : event.markets) {
                if (market.outcomes == null) continue;
                for (PredictionOutcome outcome : market.outcomes) {
                    if (probes >= 20 || outcome.outcome == null) {
                        break outer;
                    }
                    probes += 1;
                    PredictionOrderBook candidate = exchange.fetchOrderBook(outcome.outcome);
                    if (candidate.bids != null && !candidate.bids.isEmpty() && candidate.asks != null && !candidate.asks.isEmpty()) {
                        symbol = outcome.outcome;
                        book = candidate;
                        if (outcome.precision != null && outcome.precision.price != null) {
                            tick = outcome.precision.price;
                        }
                        System.out.println("event:    " + event.title);
                        System.out.println("outcome:  " + symbol + " (" + outcome.label + ")");
                        break outer;
                    }
                }
            }
        }
        if (symbol == null) {
            System.out.println("Could not find an outcome with a live two-sided order book right now.");
            return;
        }

        // 2) market data for the chosen outcome --------------------------------------------
        List<Double> bestBid = book.bids.get(0);
        List<Double> bestAsk = book.asks.get(0);
        System.out.println("\n--- market data ---");
        System.out.println("orderbook bid/ask: " + bestBid + " / " + bestAsk);
        try {
            PredictionTicker ticker = exchange.fetchTicker(symbol);
            System.out.println("ticker bid/ask/last: " + ticker.bid + " / " + ticker.ask + " / " + ticker.last);
        } catch (Exception e) {
            System.out.println("ticker:        n/a (" + e.getClass().getSimpleName() + ")");
        }
        try {
            List<PredictionTrade> trades = exchange.fetchTrades(symbol, null, 3L);
            System.out.println("recent trades: " + trades.size() + (trades.isEmpty() ? "" : (" last @ " + trades.get(0).price)));
        } catch (Exception e) {
            System.out.println("trades:        n/a (" + e.getClass().getSimpleName() + ")");
        }

        // 3) place a resting limit BUY well below the book, fetch it, then cancel -----------
        double bidPrice = bestBid.get(0);
        // half the best bid, floored to the tick — far below the ask, so it cannot fill
        double price = Math.floor((bidPrice * 0.5) / tick) * tick;
        price = Math.max(tick, Math.round(price * 10000.0) / 10000.0);
        double notional = ORDER_SIZE_SHARES * price;
        System.out.println("\n--- order ---");
        System.out.println("placing limit BUY " + ORDER_SIZE_SHARES + " shares @ " + price + " (notional " + (Math.round(notional * 100.0) / 100.0) + " USD)");
        if (notional >= MAX_NOTIONAL_USD) {
            System.out.println("ABORT: notional >= " + MAX_NOTIONAL_USD + " USD safety cap.");
            return;
        }

        PredictionOrder order = null;
        int exitCode = 0;
        try {
            order = exchange.createOrder(symbol, "limit", "buy", ORDER_SIZE_SHARES, price);
            System.out.println("placed:  id " + order.id + " | status " + order.status);
            PredictionOrder fetched = exchange.fetchOrder(order.id, symbol);
            System.out.println("fetched: id " + fetched.id + " | status " + fetched.status + " | remaining " + fetched.remaining);
        } catch (Exception e) {
            System.out.println("order flow failed: " + e);
            exitCode = 1;
        } finally {
            if (order != null && order.id != null) {
                PredictionOrder canceled = exchange.cancelOrder(order.id, symbol);
                System.out.println("canceled: id " + canceled.id + " | status " + canceled.status);
            }
        }
        System.exit(exitCode); // the exchange keeps a background thread pool alive
    }
}