CCXT
JavaScript Examples

Prediction Polymarket Spain World Cup

Prediction Polymarket Spain World Cup — CCXT JavaScript code example.

// @NO_AUTO_TRANSPILE
// Polymarket: will Spain win the World Cup? (fetch odds + place/cancel one order)
//
// Flow:
//   1. search Polymarket events for the World Cup winner event and pick the Spain market
//   2. fetch the YES outcome's ticker and order book — the YES price is the implied probability
//   3. place a resting limit BUY well below the book (so it cannot fill), fetch it back, then cancel it
//
// Usage:
//   POLYMARKET_PRIVATEKEY=0x... POLYMARKET_WALLETADDRESS=0x... \
//   npx tsx examples/ts/prediction-polymarket-spain-world-cup.ts
//
// Accounts funded through a Polymarket deposit/proxy wallet also need:
//   POLYMARKET_FUNDER=0x...         (the deposit wallet address)
//   POLYMARKET_SIGNATURETYPE=3      (0=EOA, 1=POLY_PROXY, 2=GNOSIS_SAFE, 3=POLY_1271)
import ccxt from '../../js/ccxt.js';
const MAX_NOTIONAL_USD = 25; // hard cap per trade
const ORDER_SIZE_SHARES = 5; // polymarket minimum order size
async function main() {
    const privateKey = process.env['POLYMARKET_PRIVATEKEY'];
    const walletAddress = process.env['POLYMARKET_WALLETADDRESS'];
    if (!privateKey || !walletAddress) {
        console.log('Set POLYMARKET_PRIVATEKEY and POLYMARKET_WALLETADDRESS env vars first.');
        return;
    }
    const options = {};
    if (process.env['POLYMARKET_FUNDER']) {
        options['funder'] = process.env['POLYMARKET_FUNDER'];
    }
    if (process.env['POLYMARKET_SIGNATURETYPE']) {
        options['signatureType'] = Number(process.env['POLYMARKET_SIGNATURETYPE']);
    }
    const exchange = new ccxt.prediction.polymarket({
        'privateKey': privateKey,
        'walletAddress': walletAddress,
        'options': options,
    });
    try {
        // 1) find the Spain market inside the World Cup winner event --------------------------
        const events = await exchange.fetchEvents({ 'query': 'World Cup Winner', 'limit': 10 });
        let spainMarket = undefined;
        let worldCupEvent = undefined;
        for (const ev of events) {
            for (const market of (ev.markets || [])) {
                const title = (market.title || market.market || '').toLowerCase();
                if (title.indexOf('spain') !== -1) {
                    worldCupEvent = ev;
                    spainMarket = market;
                    break;
                }
            }
            if (spainMarket)
                break;
        }
        if (spainMarket === undefined) {
            console.log('Could not find a Spain market in the World Cup winner event.');
            return;
        }
        // the YES outcome's price is the market-implied probability that Spain wins
        let yesOutcome = undefined;
        for (const oc of (spainMarket.outcomes || [])) {
            if (oc.label === 'Yes') {
                yesOutcome = oc;
                break;
            }
        }
        if (yesOutcome === undefined) {
            console.log('The Spain market has no YES outcome.');
            return;
        }
        // the tradeable handle is the outcome's `outcome` field ("MARKET:LABEL")
        const symbol = yesOutcome.outcome;
        console.log('event:   ', worldCupEvent.title);
        console.log('market:  ', spainMarket.symbol);
        console.log('outcome: ', symbol);
        // 2) is Spain going to win? the YES price is the implied probability ------------------
        const orderbook = await exchange.fetchOrderBook(symbol);
        const bestBid = orderbook.bids.length ? orderbook.bids[0] : undefined;
        const bestAsk = orderbook.asks.length ? orderbook.asks[0] : undefined;
        console.log('\n--- odds ---');
        console.log('orderbook bid/ask:', bestBid, '/', bestAsk);
        const ticker = await exchange.fetchTicker(symbol);
        console.log('ticker bid/ask/last:', ticker.bid, '/', ticker.ask, '/', ticker.last);
        const yesPrice = (ticker.last !== undefined) ? ticker.last : ticker.bid;
        if (yesPrice !== undefined) {
            console.log('market says Spain wins the World Cup with ~' + (yesPrice * 100).toFixed(1) + '% probability');
        }
        if (bestBid === undefined || bestAsk === undefined) {
            console.log('No two-sided order book right now — skipping the order.');
            return;
        }
        // 3) place a resting limit BUY well below the book, fetch it, then cancel -------------
        const tick = (yesOutcome.precision && yesOutcome.precision.price) ? yesOutcome.precision.price : 0.01;
        // half the best bid, floored to the tick — far below the ask, so it cannot fill
        let price = Math.floor((bestBid[0] * 0.5) / tick) * tick;
        price = Math.max(tick, Number(price.toFixed(4)));
        const notional = ORDER_SIZE_SHARES * price;
        console.log('\n--- order ---');
        console.log('placing limit BUY', ORDER_SIZE_SHARES, 'shares @', price, '(notional', notional.toFixed(2), 'USD)');
        if (notional >= MAX_NOTIONAL_USD) {
            console.log('ABORT: notional >= ' + MAX_NOTIONAL_USD + ' USD safety cap.');
            return;
        }
        let order = undefined;
        try {
            order = await exchange.createOrder(symbol, 'limit', 'buy', ORDER_SIZE_SHARES, price);
            console.log('placed:  id', order.id, '| status', order.status);
            const fetched = await exchange.fetchOrder(order.id, symbol);
            console.log('fetched: id', fetched.id, '| status', fetched.status, '| remaining', fetched.remaining);
        }
        finally {
            if (order && order.id) {
                const canceled = await exchange.cancelOrder(order.id, symbol);
                console.log('canceled: id', canceled.id, '| status', canceled.status);
            }
        }
    }
    finally {
        await exchange.close();
    }
}
main();