get_market_open_interest
Retrieve top Polymarket prediction markets ranked by open interest (USDC locked in positions). Uses a unique subgraph for accurate open interest data.
Instructions
Get the top Polymarket markets ranked by open interest (USDC locked in outstanding positions). This data is unique to the Open Interest subgraph — no other Polymarket subgraph tracks OI.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| first | No | Number of markets to return (1-100) | |
| orderBy | No | Field to rank markets by | amount |
| orderDirection | No | Sort direction | desc |
Implementation Reference
- src/index.ts:590-653 (registration)Registration of the 'get_market_open_interest' tool via server.registerTool(). Defines the tool name, description, input schema (zod), and the handler callback.
// --------------------------------------------------------------------------- // Tool 13: get_market_open_interest // --------------------------------------------------------------------------- server.registerTool( "get_market_open_interest", { description: "Get the top Polymarket markets ranked by open interest (USDC locked in outstanding positions). This data is unique to the Open Interest subgraph — no other Polymarket subgraph tracks OI.", inputSchema: { first: z.number().min(1).max(100).default(10).describe("Number of markets to return (1-100)"), orderBy: z .enum(["amount", "splitCount", "mergeCount", "lastUpdatedTimestamp"]) .default("amount") .describe("Field to rank markets by"), orderDirection: z.enum(["asc", "desc"]).default("desc").describe("Sort direction"), }, }, async ({ first, orderBy, orderDirection }) => { try { const oiQuery = `{ marketOpenInterests(first: ${first}, orderBy: ${orderBy}, orderDirection: ${orderDirection}) { id conditionId amount amountRaw splitCount mergeCount redemptionCount createdAtTimestamp lastUpdatedTimestamp } }`; const oiData = await querySubgraph(SUBGRAPHS.open_interest.ipfsHash, oiQuery); const od = oiData as { marketOpenInterests?: Array<{ conditionId: string; amount: string }> }; const conditionIds = (od.marketOpenInterests ?? []).map((m) => m.conditionId).filter(Boolean); // Cross-ref Main subgraph: payoutDenominator > 0 means market is resolved const resolutionMap = new Map<string, boolean>(); if (conditionIds.length > 0) { const ids = conditionIds.map((id) => `"${id}"`).join(", "); const mainQuery = `{ conditions(where: { id_in: [${ids}] }) { id payoutDenominator } }`; const mainData = await querySubgraph(SUBGRAPHS.main.ipfsHash, mainQuery).catch(() => null); if (mainData) { const md = mainData as { conditions?: Array<{ id: string; payoutDenominator: string }> }; for (const c of md.conditions ?? []) { resolutionMap.set(c.id, parseInt(c.payoutDenominator || "0") > 0); } } } const annotated = (od.marketOpenInterests ?? []).map((m) => { if (resolutionMap.get(m.conditionId)) { return { ...m, warning: `⚠ dead money — market resolved. $${parseFloat(m.amount).toFixed(2)} OI represents worthless losing-side tokens that will never be redeemed on-chain.`, }; } return m; }); const deadMoneyTotal = (od.marketOpenInterests ?? []) .filter((m) => resolutionMap.get(m.conditionId)) .reduce((sum, m) => sum + parseFloat(m.amount || "0"), 0); return textResult({ markets: annotated, ...(deadMoneyTotal > 0 ? { deadMoneyOI: `$${deadMoneyTotal.toFixed(2)} of displayed OI is from resolved markets (losing tokens — not redeemable on-chain)` } : {}), }); } catch (error) { return errorResult(error); } } ); - src/index.ts:598-605 (schema)Input schema for get_market_open_interest: defines 'first' (number 1-100, default 10), 'orderBy' (enum of amount/splitCount/mergeCount/lastUpdatedTimestamp, default amount), and 'orderDirection' (asc/desc, default desc).
inputSchema: { first: z.number().min(1).max(100).default(10).describe("Number of markets to return (1-100)"), orderBy: z .enum(["amount", "splitCount", "mergeCount", "lastUpdatedTimestamp"]) .default("amount") .describe("Field to rank markets by"), orderDirection: z.enum(["asc", "desc"]).default("desc").describe("Sort direction"), }, - src/index.ts:607-653 (handler)Handler function for get_market_open_interest. Queries the Open Interest subgraph for marketOpenInterests, cross-references the Main subgraph to detect resolved markets (dead money), annotates results with warnings, and returns annotated data.
async ({ first, orderBy, orderDirection }) => { try { const oiQuery = `{ marketOpenInterests(first: ${first}, orderBy: ${orderBy}, orderDirection: ${orderDirection}) { id conditionId amount amountRaw splitCount mergeCount redemptionCount createdAtTimestamp lastUpdatedTimestamp } }`; const oiData = await querySubgraph(SUBGRAPHS.open_interest.ipfsHash, oiQuery); const od = oiData as { marketOpenInterests?: Array<{ conditionId: string; amount: string }> }; const conditionIds = (od.marketOpenInterests ?? []).map((m) => m.conditionId).filter(Boolean); // Cross-ref Main subgraph: payoutDenominator > 0 means market is resolved const resolutionMap = new Map<string, boolean>(); if (conditionIds.length > 0) { const ids = conditionIds.map((id) => `"${id}"`).join(", "); const mainQuery = `{ conditions(where: { id_in: [${ids}] }) { id payoutDenominator } }`; const mainData = await querySubgraph(SUBGRAPHS.main.ipfsHash, mainQuery).catch(() => null); if (mainData) { const md = mainData as { conditions?: Array<{ id: string; payoutDenominator: string }> }; for (const c of md.conditions ?? []) { resolutionMap.set(c.id, parseInt(c.payoutDenominator || "0") > 0); } } } const annotated = (od.marketOpenInterests ?? []).map((m) => { if (resolutionMap.get(m.conditionId)) { return { ...m, warning: `⚠ dead money — market resolved. $${parseFloat(m.amount).toFixed(2)} OI represents worthless losing-side tokens that will never be redeemed on-chain.`, }; } return m; }); const deadMoneyTotal = (od.marketOpenInterests ?? []) .filter((m) => resolutionMap.get(m.conditionId)) .reduce((sum, m) => sum + parseFloat(m.amount || "0"), 0); return textResult({ markets: annotated, ...(deadMoneyTotal > 0 ? { deadMoneyOI: `$${deadMoneyTotal.toFixed(2)} of displayed OI is from resolved markets (losing tokens — not redeemable on-chain)` } : {}), }); } catch (error) { return errorResult(error); } } ); - src/subgraphs.ts:71-81 (helper)Configuration for the 'open_interest' subgraph used by get_market_open_interest. Contains the ipfsHash (QmbT2MmS2VGbGihiTUmWk6GMc2QYqoT9ZhiupUicYMWt6H) and description explaining OI tracking.
open_interest: { name: "Open Interest", ipfsHash: "QmbT2MmS2VGbGihiTUmWk6GMc2QYqoT9ZhiupUicYMWt6H", description: "The only Polymarket subgraph dedicated to open interest. Tracks USDC currently locked in outstanding YES/NO positions per market, with hourly snapshots for time-series analysis. OI is computed from PositionSplit (increases) and PositionsMerge (decreases) events on the ConditionalTokens contract. IMPORTANT: Polymarket does NOT use on-chain PayoutRedemption — winners sell shares on the orderbook or merge positions instead. This means resolved markets will still show residual OI from losing-side tokens that will never be redeemed. High OI on a resolved market = dead money (worthless losing tokens), not unclaimed winnings. Best for: identifying markets with the most capital at risk, charting OI trends over time, and detecting capital flow shifts across markets.", keyEntities: [ "MarketOpenInterest (amount in USDC, splitCount, mergeCount — cross-reference with main subgraph for resolution status)", "OISnapshot (hourly bucketed OI per market — amount, timestamp, blockNumber)", "GlobalOpenInterest (total OI across all markets, marketCount)", ], },