End-to-end production guide: programmatically create markets, seed liquidity, trade with edge detection, resolve, redeem, and claim fees in a continuous loop.
This guide covers how to build an autonomous market maker that creates prediction markets, trades them, resolves them on expiry, and collects profits — all running unattended. Patterns are extracted from the PMX reference agent.
The /v2/markets/create-full endpoint handles market creation and mint initialization in a single call. It returns multiple transactions to sign sequentially.
Crypto price markets need a parseable title like "BTC above $95,000 by Mar 7 6:30 PM UTC". The threshold should be near the current price with slight randomization:
After a market’s resolutionTime passes, the creator must resolve it. For crypto price markets, compare the historical price at expiry against the threshold:
async function resolveMarket(marketId, winningSide) { const res = await api("POST", `/v2/markets/${marketId}/resolve`, { wallet: WALLET, winningSide, }); if (!res.success) throw new Error(res.error); // Idempotent — API returns success if already resolved if (res.alreadyResolved || !res.data?.transaction) return null; return signAndSend(res.data.transaction);}function determineWinner(price, threshold, direction) { if (direction === "above" || direction === "over") { return price >= threshold ? "YES" : "NO"; } return price < threshold ? "YES" : "NO";}
async function resolutionLoop() { while (true) { const markets = await api("GET", `/v2/markets?status=active&creator=${WALLET}`); const now = Math.floor(Date.now() / 1000); for (const market of markets.data || []) { if (market.resolutionTime > now) continue; // not yet expired const parsed = parseTitle(market.title); if (!parsed) continue; // Get price at the exact resolution timestamp const price = await getHistoricalPrice(parsed.symbol, market.resolutionTime); if (!price) continue; const winner = determineWinner(price, parsed.threshold, parsed.direction); try { await resolveMarket(market.id, winner); } catch {} } await new Promise((r) => setTimeout(r, 5 * 60_000)); }}
Resolution requires Clock.unix_timestamp >= market.resolutionTimeand both YES and NO mints must have non-zero supply. If nobody traded one side, resolution will fail.
After resolution, redeem winning tokens and collect creator fees:
async function drainResolved() { const positions = await api("GET", `/v2/positions/${WALLET}`); for (const pos of positions.data || []) { if (!pos.isResolved) continue; // Redeem winning tokens if (pos.needsRedemption && pos.winningSide) { const res = await api("POST", `/v2/markets/${pos.marketId}/redeem`, { wallet: WALLET, side: pos.winningSide, }); if (res.data?.transaction) await signAndSend(res.data.transaction); } // Claim creator fees const feeRes = await api("POST", `/v2/markets/${pos.marketId}/claim-fees`, { wallet: WALLET, }); if (feeRes.data?.transaction) await signAndSend(feeRes.data.transaction); }}
Both redeem and claim-fees are idempotent — calling them on already-redeemed or zero-fee markets returns success with no transaction. Safe to call unconditionally.