diff --git a/core/src/exchanges/gemini-titan/normalizer.ts b/core/src/exchanges/gemini-titan/normalizer.ts index e91390a2..80cb576a 100644 --- a/core/src/exchanges/gemini-titan/normalizer.ts +++ b/core/src/exchanges/gemini-titan/normalizer.ts @@ -213,12 +213,19 @@ export class GeminiNormalizer implements IExchangeNormalizer ({ price: parseFloat(level.px), size: parseFloat(level.sz), + orderCount: level.n, })); const asks = rawAsks.map(level => ({ price: parseFloat(level.px), size: parseFloat(level.sz), + orderCount: level.n, })); return { diff --git a/core/src/exchanges/kalshi/fetcher.ts b/core/src/exchanges/kalshi/fetcher.ts index 17ab5000..c3781c66 100644 --- a/core/src/exchanges/kalshi/fetcher.ts +++ b/core/src/exchanges/kalshi/fetcher.ts @@ -23,6 +23,8 @@ export interface KalshiRawMarket { last_price_dollars?: string; yes_ask_dollars?: string; yes_bid_dollars?: string; + yes_ask_size_fp?: string; + yes_bid_size_fp?: string; rules_primary?: string; rules_secondary?: string; expiration_time: string; diff --git a/core/src/exchanges/kalshi/normalizer.ts b/core/src/exchanges/kalshi/normalizer.ts index e694c208..ade69ea1 100644 --- a/core/src/exchanges/kalshi/normalizer.ts +++ b/core/src/exchanges/kalshi/normalizer.ts @@ -118,7 +118,12 @@ export class KalshiNormalizer implements IExchangeNormalizer, KALSHI_PROMOTED_MARKET_KEYS, - { series_ticker: event.series_ticker, series_title: event.series_title }, + { + yes_ask_size_fp: market.yes_ask_size_fp, + yes_bid_size_fp: market.yes_bid_size_fp, + series_ticker: event.series_ticker, + series_title: event.series_title, + }, ), } as UnifiedMarket; diff --git a/core/src/exchanges/limitless/fetcher.ts b/core/src/exchanges/limitless/fetcher.ts index aca7f6bf..6b3c306e 100644 --- a/core/src/exchanges/limitless/fetcher.ts +++ b/core/src/exchanges/limitless/fetcher.ts @@ -16,6 +16,10 @@ export interface LimitlessRawMarket { description?: string; tokens?: Record; prices?: number[]; + tradePrices?: { + buy?: { market?: number[]; limit?: number[] }; + sell?: { market?: number[]; limit?: number[] }; + }; expirationTimestamp?: string; volumeFormatted?: number; volume?: number; diff --git a/core/src/exchanges/limitless/utils.ts b/core/src/exchanges/limitless/utils.ts index 29a54076..099d37cb 100644 --- a/core/src/exchanges/limitless/utils.ts +++ b/core/src/exchanges/limitless/utils.ts @@ -73,9 +73,9 @@ export function mapMarketToUnified(market: any, context: LimitlessMarketContext throw new Error(`[limitless] Market "${market.slug}" is missing token addresses`); } - const prices = Array.isArray(market.prices) ? market.prices : []; - const yesPrice = prices[0] || 0; - const noPrice = prices[1] || 0; + const legacyPrices = Array.isArray(market.prices) ? market.prices : []; + const yesPrice = market.tradePrices?.buy?.market?.[0] ?? legacyPrices[0] ?? 0.5; + const noPrice = market.tradePrices?.sell?.market?.[0] ?? legacyPrices[1] ?? Math.max(0, Math.min(1, 1 - yesPrice)); const yesLabel = hasParentContext ? rawTitle : 'Yes'; const noLabel = hasParentContext ? `Not ${rawTitle}` : 'No'; diff --git a/core/src/server/openapi.yaml b/core/src/server/openapi.yaml index 6c1789f8..c91cfce5 100644 --- a/core/src/server/openapi.yaml +++ b/core/src/server/openapi.yaml @@ -72,26 +72,7 @@ paths: summary: Fetch Markets operationId: fetchMarkets parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - baozi - - myriad - - opinion - - metaculus - - smarkets - - polymarket_us - - suibets - - router - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: limit required: false @@ -277,26 +258,7 @@ paths: summary: Fetch Events operationId: fetchEvents parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - baozi - - myriad - - opinion - - metaculus - - smarkets - - polymarket_us - - suibets - - router - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: query required: false @@ -421,19 +383,7 @@ paths: summary: Fetch Series operationId: fetchSeries parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - opinion - - polymarket_us - - router - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' responses: '200': description: Fetch Series response @@ -687,21 +637,7 @@ paths: summary: Fetch OHLCV operationId: fetchOHLCV parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - baozi - - myriad - - opinion - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: outcomeId required: true @@ -753,25 +689,7 @@ paths: summary: Fetch Order Book operationId: fetchOrderBook parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - baozi - - myriad - - opinion - - smarkets - - polymarket_us - - suibets - - router - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: outcomeId required: true @@ -839,16 +757,7 @@ paths: summary: Fetch Order Books operationId: fetchOrderBooks parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' requestBody: content: application/json: @@ -890,21 +799,7 @@ paths: summary: Fetch Trades operationId: fetchTrades parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - baozi - - myriad - - smarkets - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: outcomeId required: true @@ -950,24 +845,7 @@ paths: summary: Create Order operationId: createOrder parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - baozi - - myriad - - opinion - - metaculus - - smarkets - - polymarket_us - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' requestBody: content: application/json: @@ -1003,19 +881,7 @@ paths: summary: Build Order operationId: buildOrder parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - opinion - - smarkets - - polymarket_us - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' requestBody: content: application/json: @@ -1053,19 +919,7 @@ paths: summary: Submit Order operationId: submitOrder parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - opinion - - smarkets - - polymarket_us - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' requestBody: content: application/json: @@ -1101,22 +955,7 @@ paths: summary: Cancel Order operationId: cancelOrder parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - opinion - - metaculus - - smarkets - - polymarket_us - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' requestBody: content: application/json: @@ -1152,21 +991,7 @@ paths: summary: Fetch Order operationId: fetchOrder parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - probable - - baozi - - opinion - - smarkets - - polymarket_us - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: orderId required: true @@ -1190,23 +1015,7 @@ paths: summary: Fetch Open Orders operationId: fetchOpenOrders parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - baozi - - myriad - - opinion - - smarkets - - polymarket_us - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: marketId required: false @@ -1232,22 +1041,7 @@ paths: summary: Fetch My Trades operationId: fetchMyTrades parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - myriad - - opinion - - smarkets - - polymarket_us - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: outcomeId required: false @@ -1305,18 +1099,7 @@ paths: summary: Fetch Closed Orders operationId: fetchClosedOrders parameters: - - in: path - name: exchange - schema: - type: string - enum: - - kalshi - - kalshi-demo - - limitless - - opinion - - smarkets - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: marketId required: false @@ -1368,18 +1151,7 @@ paths: summary: Fetch All Orders operationId: fetchAllOrders parameters: - - in: path - name: exchange - schema: - type: string - enum: - - kalshi - - kalshi-demo - - limitless - - opinion - - smarkets - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: marketId required: false @@ -1431,24 +1203,7 @@ paths: summary: Fetch Positions operationId: fetchPositions parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - baozi - - myriad - - opinion - - smarkets - - polymarket_us - - suibets - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: address required: false @@ -1474,22 +1229,7 @@ paths: summary: Fetch Balance operationId: fetchBalance parameters: - - in: path - name: exchange - schema: - type: string - enum: - - polymarket - - kalshi - - kalshi-demo - - limitless - - probable - - baozi - - myriad - - smarkets - - polymarket_us - required: true - description: The prediction market exchange to target. + - $ref: '#/components/parameters/ExchangeParam' - in: query name: address required: false diff --git a/core/src/types.ts b/core/src/types.ts index af3b74ec..e9bdda9a 100644 --- a/core/src/types.ts +++ b/core/src/types.ts @@ -171,6 +171,7 @@ export interface PriceCandle { export interface OrderLevel { price: number; // 0.0 to 1.0 (probability) size: number; // contracts/shares + orderCount?: number; } export interface OrderBook { diff --git a/core/test/normalizers/exchange-normalizers-3.test.ts b/core/test/normalizers/exchange-normalizers-3.test.ts index db37edc1..312dca3c 100644 --- a/core/test/normalizers/exchange-normalizers-3.test.ts +++ b/core/test/normalizers/exchange-normalizers-3.test.ts @@ -878,19 +878,21 @@ describe('HyperliquidNormalizer', () => { expect(book.asks[0].price).toBeLessThanOrEqual(book.asks[1].price); }); - test('bid prices and sizes are numbers', () => { + test('bid prices, sizes, and order counts are numbers', () => { const book = normalizer.normalizeOrderBook(rawBook, 'hl-outcome-42'); for (const b of book.bids) { expect(typeof b.price).toBe('number'); expect(typeof b.size).toBe('number'); + expect(typeof b.orderCount).toBe('number'); } }); - test('ask prices and sizes are numbers', () => { + test('ask prices, sizes, and order counts are numbers', () => { const book = normalizer.normalizeOrderBook(rawBook, 'hl-outcome-42'); for (const a of book.asks) { expect(typeof a.price).toBe('number'); expect(typeof a.size).toBe('number'); + expect(typeof a.orderCount).toBe('number'); } }); diff --git a/core/test/normalizers/exchange-normalizers.test.ts b/core/test/normalizers/exchange-normalizers.test.ts index 35f4a2b3..056551ad 100644 --- a/core/test/normalizers/exchange-normalizers.test.ts +++ b/core/test/normalizers/exchange-normalizers.test.ts @@ -381,6 +381,8 @@ describe('KalshiNormalizer', () => { last_price_dollars: '0.4540', yes_ask_dollars: '0.4600', yes_bid_dollars: '0.4500', + yes_ask_size_fp: '125.00', + yes_bid_size_fp: '90.00', previous_price_dollars: '0.4200', rules_primary: 'This market resolves YES if the Fed Funds Rate is above 4.75% on Jan 29, 2025.', rules_secondary: 'Data source: Federal Reserve', @@ -509,6 +511,11 @@ describe('KalshiNormalizer', () => { expect(market.liquidity).toBe(5000); }); + it('surfaces top-of-book depth sizes in sourceMetadata', () => { + expect((market.sourceMetadata as any).yes_ask_size_fp).toBe('125.00'); + expect((market.sourceMetadata as any).yes_bid_size_fp).toBe('90.00'); + }); + it('maps url to kalshi events URL', () => { expect(market.url).toBe('https://kalshi.com/events/FED-25JAN29'); }); @@ -800,6 +807,10 @@ describe('LimitlessNormalizer', () => { no: 'token-eth-flip-no', }, prices: [0.08, 0.92], + tradePrices: { + buy: { market: [0.11] }, + sell: { market: [0.89] }, + }, expirationTimestamp: '2025-12-31T23:59:59Z', volumeFormatted: 75000, volume: 1500000, @@ -863,14 +874,14 @@ describe('LimitlessNormalizer', () => { expect(market.outcomes[1].label).toBe('Not Will ETH flip BTC in 2025?'); }); - it('maps yes price from prices[0] as a number', () => { + it('maps yes price from directional tradePrices when available', () => { expect(typeof market.outcomes[0].price).toBe('number'); - expect(market.outcomes[0].price).toBeCloseTo(0.08); + expect(market.outcomes[0].price).toBeCloseTo(0.11); }); - it('maps no price from prices[1] as a number', () => { + it('maps no price from directional tradePrices when available', () => { expect(typeof market.outcomes[1].price).toBe('number'); - expect(market.outcomes[1].price).toBeCloseTo(0.92); + expect(market.outcomes[1].price).toBeCloseTo(0.89); }); it('prices sum to 1.0', () => { diff --git a/core/test/normalizers/gemini-titan-normalizer.test.ts b/core/test/normalizers/gemini-titan-normalizer.test.ts new file mode 100644 index 00000000..663bbd9d --- /dev/null +++ b/core/test/normalizers/gemini-titan-normalizer.test.ts @@ -0,0 +1,66 @@ +import { GeminiNormalizer } from '../../src/exchanges/gemini-titan/normalizer'; + +describe('GeminiTitanNormalizer', () => { + const normalizer = new GeminiNormalizer(); + + const rawEvent: any = { + id: 'evt-1', + title: 'Gemini prediction market event', + slug: 'gemini-prediction-market-event', + description: 'Event-level description fallback', + ticker: 'GEM-1', + status: 'active', + type: 'prediction-market', + liquidity: undefined, + volume24h: '1234.5', + volume: '9999.9', + contracts: [ + { + id: 'contract-1', + label: 'BTC above $100k', + description: { + nodeType: 'document', + content: [ + { + nodeType: 'paragraph', + content: [ + { nodeType: 'text', value: 'Contract rich-text description' }, + ], + }, + ], + }, + ticker: 'GEM-1-A', + instrumentSymbol: 'GEM-1-A', + status: 'active', + marketState: 'open', + prices: { + buy: { yes: '0.71', no: '0.29' }, + sell: { yes: '0.69', no: '0.31' }, + bestBid: '0.60', + bestAsk: '0.40', + lastTradePrice: '0.55', + }, + expiryDate: '2026-06-30T23:59:00Z', + }, + ], + }; + + test('extracts rich-text contract descriptions', () => { + const market = normalizer.normalizeMarket(rawEvent); + expect(market).not.toBeNull(); + expect(market!.description).toBe('Contract rich-text description'); + }); + + test('prefers directional buy/sell prices when present', () => { + const market = normalizer.normalizeMarket(rawEvent); + expect(market).not.toBeNull(); + expect(market!.outcomes[0].price).toBeCloseTo(0.71); + expect(market!.outcomes[1].price).toBeCloseTo(0.29); + }); + + test('falls back to volume fields when liquidity is absent', () => { + const market = normalizer.normalizeMarket(rawEvent); + expect(market).not.toBeNull(); + expect(market!.liquidity).toBeCloseTo(1234.5); + }); +}); diff --git a/docs/api-reference/fetch-order-book.mdx b/docs/api-reference/fetch-order-book.mdx index 1aea6e43..55b0cb4d 100644 --- a/docs/api-reference/fetch-order-book.mdx +++ b/docs/api-reference/fetch-order-book.mdx @@ -7,7 +7,11 @@ openapi: GET /api/{exchange}/fetchOrderBook ### Live order book -Fetch the current L2 order book for an outcome. If you already have an outcome token ID, pass it directly: +Fetch the current L2 order book for an outcome from the exchange's live order book endpoint. For Polymarket this is a live CLOB call: pass the outcome token ID directly and omit historical params. + + +`poly.fetch_order_book(token_id)` without `params.since` or `params.until` is live-only. It does not read PMXT Archive data and may fail for closed, resolved, or otherwise inactive Polymarket markets with an error such as `No orderbook exists`. + ```python Python @@ -28,15 +32,15 @@ console.log(`${book.bids.length} bids, ${book.asks.length} asks`); ```bash curl curl "https://api.pmxt.dev/api/polymarket/fetchOrderBook?outcomeId=104932610032177696635191871147557737718087870958469629338467406422339967452218" \ - -H "Authorization: Bearer $PMXT_API_KEY" + -H "Authorization: Bearer ***" ``` ### Historical snapshot -Get the order book at a specific point in time. Pass `since` as a Unix timestamp in milliseconds — returns the nearest snapshot at or before that time. +Get the order book at a specific point in time by passing `since` as a Unix timestamp in milliseconds. Historical Polymarket queries are served from the PMXT Archive and return the nearest reconstructed snapshot at or before that time. -For binary markets, you can pass the market ID and choose the side with `params.outcome`. Use `"yes"` or `"no"` instead of copying the long outcome token ID: +For binary markets, you can pass the Polymarket condition ID as the first argument and choose the side with `params.outcome`. Use `"yes"` or `"no"` instead of copying the long outcome token ID. ```python Python @@ -44,12 +48,9 @@ import pmxt poly = pmxt.Polymarket(pmxt_api_key="pmxt_...") -market = poly.fetch_market( - slug="will-spacex-starship-flight-test-12-launch-by-may-22-354-721" -) - +# Historical lookup against PMXT Archive, not the live CLOB. book = poly.fetch_order_book( - market.market_id, + "0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", params={"since": 1779487200000, "outcome": "yes"}, ) @@ -63,12 +64,9 @@ import { Polymarket } from "pmxtjs"; const poly = new Polymarket({ pmxtApiKey: "pmxt_..." }); -const market = await poly.fetchMarket({ - slug: "will-spacex-starship-flight-test-12-launch-by-may-22-354-721", -}); - +// Historical lookup against PMXT Archive, not the live CLOB. const book = await poly.fetchOrderBook( - market.marketId, + "0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", undefined, { since: 1779487200000, outcome: "yes" } ); @@ -79,9 +77,51 @@ console.log(` best ask: ${Math.min(...book.asks.map((a) => a.price)).toFixed(3) ```bash curl curl -X POST "https://api.pmxt.dev/api/polymarket/fetchOrderBook" \ - -H "Authorization: Bearer $PMXT_API_KEY" \ + -H "Authorization: Bearer ***" \ + -H "Content-Type: application/json" \ + -d '{"args":["0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", null, {"since": 1779487200000, "outcome": "yes"}]}' +``` + + +### Historical crypto 5m events + +Polymarket crypto 5-minute slugs such as `btc-updown-5m-1779481500` are event slugs. Use `fetch_event(slug=...)`, then select the nested market you want. Do not use `fetch_market(slug=...)` for these event-level slugs. + + +```python Python +import pmxt + +poly = pmxt.Polymarket(pmxt_api_key="pmxt_...") +event = poly.fetch_event(slug="btc-updown-5m-1779481500") +market = event.markets[0] + +book = poly.fetch_order_book( + market.market_id, + params={"since": 1779487200000, "outcome": "yes"}, +) +print(f"{market.title}: {book.dt}") +``` + +```javascript JavaScript +import { Polymarket } from "pmxtjs"; + +const poly = new Polymarket({ pmxtApiKey: "pmxt_..." }); +const event = await poly.fetchEvent({ slug: "btc-updown-5m-1779481500" }); +const market = event.markets[0]; + +const book = await poly.fetchOrderBook( + market.marketId, + undefined, + { since: 1779487200000, outcome: "yes" } +); +console.log(`${market.title}: ${book.datetime}`); +``` + +```bash curl +curl -X POST "https://api.pmxt.dev/api/polymarket/fetchEvent" \ + -H "Authorization: Bearer ***" \ -H "Content-Type: application/json" \ - -d '{"args":["61b0ed20-7f42-41fd-af15-7b86153f6bb7", null, {"since": 1779487200000, "outcome": "yes"}]}' + -d '{"kwargs":{"slug":"btc-updown-5m-1779481500"}}' ``` @@ -89,17 +129,15 @@ curl -X POST "https://api.pmxt.dev/api/polymarket/fetchOrderBook" \ Pass both `since` and `until` to get an array of fully reconstructed L2 order book snapshots. Each snapshot is a complete book at that moment in time — not deltas. -Default 100 snapshots per request, max 1000. +The API returns up to `limit` snapshots from the PMXT Archive. Defaults to 100 snapshots per request, max 1000. For raw bulk downloads, use the Parquet files in the [PMXT Archive](https://archive.pmxt.dev) instead of trying to stream bulk data through the live order book endpoint. ```python Python import pmxt poly = pmxt.Polymarket(pmxt_api_key="pmxt_...") - -market = poly.fetch_market( - slug="will-spacex-starship-flight-test-12-launch-by-may-22-354-721" -) +event = poly.fetch_event(slug="btc-updown-5m-1779481500") +market = event.markets[0] books = poly.fetch_order_book( market.market_id, @@ -107,6 +145,7 @@ books = poly.fetch_order_book( "since": 1779480000000, "until": 1779487200000, "outcome": "yes", + "limit": 100, } ) print(f"{len(books)} snapshots") @@ -118,15 +157,13 @@ for ob in books[:3]: import { Polymarket } from "pmxtjs"; const poly = new Polymarket({ pmxtApiKey: "pmxt_..." }); - -const market = await poly.fetchMarket({ - slug: "will-spacex-starship-flight-test-12-launch-by-may-22-354-721", -}); +const event = await poly.fetchEvent({ slug: "btc-updown-5m-1779481500" }); +const market = event.markets[0]; const books = await poly.fetchOrderBook( market.marketId, undefined, - { since: 1779480000000, until: 1779487200000, outcome: "yes" } + { since: 1779480000000, until: 1779487200000, outcome: "yes", limit: 100 } ); console.log(`${books.length} snapshots`); books.slice(0, 3).forEach((ob) => @@ -136,12 +173,12 @@ books.slice(0, 3).forEach((ob) => ```bash curl curl -X POST "https://api.pmxt.dev/api/polymarket/fetchOrderBook" \ - -H "Authorization: Bearer $PMXT_API_KEY" \ + -H "Authorization: Bearer ***" \ -H "Content-Type: application/json" \ - -d '{"args":["61b0ed20-7f42-41fd-af15-7b86153f6bb7", null, {"since": 1779480000000, "until": 1779487200000, "outcome": "yes"}]}' + -d '{"args":["0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", null, {"since": 1779480000000, "until": 1779487200000, "outcome": "yes", "limit": 100}]}' ``` -Historical order book data is backed by the [PMXT Archive](https://archive.pmxt.dev) — the same tick-level data available as Parquet files, but queryable directly from the API without downloading or parsing files. Supports Polymarket, Kalshi, Limitless, and Opinion. +Historical order book data is backed by the [PMXT Archive](https://archive.pmxt.dev) — the same tick-level data available as Parquet files, but queryable directly from the API without downloading or parsing files. Historical `fetchOrderBook` supports Polymarket, Kalshi, Limitless, and Opinion. diff --git a/docs/llms-full.txt b/docs/llms-full.txt index 55c492dc..bbf82c1f 100644 --- a/docs/llms-full.txt +++ b/docs/llms-full.txt @@ -3274,7 +3274,12 @@ Source: https://pmxt.dev/docs/api-reference/fetch-order-book ##### Live order book -Fetch the current L2 order book for an outcome. If you already have an outcome token ID, pass it directly: +Fetch the current L2 order book for an outcome from the exchange's live order book endpoint. For Polymarket this is a live CLOB call: pass the outcome token ID directly and omit historical params. + + +> **Warning:** +`poly.fetch_order_book(token_id)` without `params.since` or `params.until` is live-only. It does not read PMXT Archive data and may fail for closed, resolved, or otherwise inactive Polymarket markets with an error such as `No orderbook exists`. + ```python Python @@ -3295,15 +3300,15 @@ console.log(`${book.bids.length} bids, ${book.asks.length} asks`); ```bash curl curl "https://api.pmxt.dev/api/polymarket/fetchOrderBook?outcomeId=104932610032177696635191871147557737718087870958469629338467406422339967452218" \ - -H "Authorization: Bearer $PMXT_API_KEY" + -H "Authorization: Bearer ***" ``` ##### Historical snapshot -Get the order book at a specific point in time. Pass `since` as a Unix timestamp in milliseconds — returns the nearest snapshot at or before that time. +Get the order book at a specific point in time by passing `since` as a Unix timestamp in milliseconds. Historical Polymarket queries are served from the PMXT Archive and return the nearest reconstructed snapshot at or before that time. -For binary markets, you can pass the market ID and choose the side with `params.outcome`. Use `"yes"` or `"no"` instead of copying the long outcome token ID: +For binary markets, you can pass the Polymarket condition ID as the first argument and choose the side with `params.outcome`. Use `"yes"` or `"no"` instead of copying the long outcome token ID. ```python Python @@ -3311,12 +3316,9 @@ import pmxt poly = pmxt.Polymarket(pmxt_api_key="pmxt_...") -market = poly.fetch_market( -slug="will-spacex-starship-flight-test-12-launch-by-may-22-354-721" -) - +# Historical lookup against PMXT Archive, not the live CLOB. book = poly.fetch_order_book( -market.market_id, +"0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", params={"since": 1779487200000, "outcome": "yes"}, ) @@ -3330,12 +3332,9 @@ import { Polymarket } from "pmxtjs"; const poly = new Polymarket({ pmxtApiKey: "pmxt_..." }); -const market = await poly.fetchMarket({ - slug: "will-spacex-starship-flight-test-12-launch-by-may-22-354-721", -}); - +// Historical lookup against PMXT Archive, not the live CLOB. const book = await poly.fetchOrderBook( - market.marketId, + "0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", undefined, { since: 1779487200000, outcome: "yes" } ); @@ -3346,9 +3345,51 @@ console.log(` best ask: ${Math.min(...book.asks.map((a) => a.price)).toFixed(3) ```bash curl curl -X POST "https://api.pmxt.dev/api/polymarket/fetchOrderBook" \ - -H "Authorization: Bearer $PMXT_API_KEY" \ + -H "Authorization: Bearer ***" \ + -H "Content-Type: application/json" \ + -d '{"args":["0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", null, {"since": 1779487200000, "outcome": "yes"}]}' +``` + + +##### Historical crypto 5m events + +Polymarket crypto 5-minute slugs such as `btc-updown-5m-1779481500` are event slugs. Use `fetch_event(slug=...)`, then select the nested market you want. Do not use `fetch_market(slug=...)` for these event-level slugs. + + +```python Python +import pmxt + +poly = pmxt.Polymarket(pmxt_api_key="pmxt_...") +event = poly.fetch_event(slug="btc-updown-5m-1779481500") +market = event.markets[0] + +book = poly.fetch_order_book( +market.market_id, +params={"since": 1779487200000, "outcome": "yes"}, +) +print(f"{market.title}: {book.dt}") +``` + +```javascript JavaScript +import { Polymarket } from "pmxtjs"; + +const poly = new Polymarket({ pmxtApiKey: "pmxt_..." }); +const event = await poly.fetchEvent({ slug: "btc-updown-5m-1779481500" }); +const market = event.markets[0]; + +const book = await poly.fetchOrderBook( + market.marketId, + undefined, + { since: 1779487200000, outcome: "yes" } +); +console.log(`${market.title}: ${book.datetime}`); +``` + +```bash curl +curl -X POST "https://api.pmxt.dev/api/polymarket/fetchEvent" \ + -H "Authorization: Bearer ***" \ -H "Content-Type: application/json" \ - -d '{"args":["61b0ed20-7f42-41fd-af15-7b86153f6bb7", null, {"since": 1779487200000, "outcome": "yes"}]}' + -d '{"kwargs":{"slug":"btc-updown-5m-1779481500"}}' ``` @@ -3356,17 +3397,15 @@ curl -X POST "https://api.pmxt.dev/api/polymarket/fetchOrderBook" \ Pass both `since` and `until` to get an array of fully reconstructed L2 order book snapshots. Each snapshot is a complete book at that moment in time — not deltas. -Default 100 snapshots per request, max 1000. +The API returns up to `limit` snapshots from the PMXT Archive. Defaults to 100 snapshots per request, max 1000. For raw bulk downloads, use the Parquet files in the [PMXT Archive](https://archive.pmxt.dev) instead of trying to stream bulk data through the live order book endpoint. ```python Python import pmxt poly = pmxt.Polymarket(pmxt_api_key="pmxt_...") - -market = poly.fetch_market( -slug="will-spacex-starship-flight-test-12-launch-by-may-22-354-721" -) +event = poly.fetch_event(slug="btc-updown-5m-1779481500") +market = event.markets[0] books = poly.fetch_order_book( market.market_id, @@ -3374,6 +3413,7 @@ params={ "since": 1779480000000, "until": 1779487200000, "outcome": "yes", + "limit": 100, } ) print(f"{len(books)} snapshots") @@ -3385,15 +3425,13 @@ print(f" {ob.dt} bid={ob.bids[0].price} ask={ob.asks[0].price}") import { Polymarket } from "pmxtjs"; const poly = new Polymarket({ pmxtApiKey: "pmxt_..." }); - -const market = await poly.fetchMarket({ - slug: "will-spacex-starship-flight-test-12-launch-by-may-22-354-721", -}); +const event = await poly.fetchEvent({ slug: "btc-updown-5m-1779481500" }); +const market = event.markets[0]; const books = await poly.fetchOrderBook( market.marketId, undefined, - { since: 1779480000000, until: 1779487200000, outcome: "yes" } + { since: 1779480000000, until: 1779487200000, outcome: "yes", limit: 100 } ); console.log(`${books.length} snapshots`); books.slice(0, 3).forEach((ob) => @@ -3403,15 +3441,15 @@ books.slice(0, 3).forEach((ob) => ```bash curl curl -X POST "https://api.pmxt.dev/api/polymarket/fetchOrderBook" \ - -H "Authorization: Bearer $PMXT_API_KEY" \ + -H "Authorization: Bearer ***" \ -H "Content-Type: application/json" \ - -d '{"args":["61b0ed20-7f42-41fd-af15-7b86153f6bb7", null, {"since": 1779480000000, "until": 1779487200000, "outcome": "yes"}]}' + -d '{"args":["0xc704f74e2f9dfae70f770cb253ffadde10768eeab41233098bf5ac67995a94b5", null, {"since": 1779480000000, "until": 1779487200000, "outcome": "yes", "limit": 100}]}' ``` > **Note:** -Historical order book data is backed by the [PMXT Archive](https://archive.pmxt.dev) — the same tick-level data available as Parquet files, but queryable directly from the API without downloading or parsing files. Supports Polymarket, Kalshi, Limitless, and Opinion. +Historical order book data is backed by the [PMXT Archive](https://archive.pmxt.dev) — the same tick-level data available as Parquet files, but queryable directly from the API without downloading or parsing files. Historical `fetchOrderBook` supports Polymarket, Kalshi, Limitless, and Opinion. ### Fetch Order Books