diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4a511d1 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,24 @@ +name: CI + +on: + push: + pull_request: + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: oven-sh/setup-bun@v2 + + - run: bun install + + - run: bun test + + - run: bun run typecheck + + - run: bun run build diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1b52f26..fb3f367 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -17,6 +17,10 @@ jobs: - run: bun install + - run: bun test + + - run: bun run typecheck + - run: bun run build - uses: actions/setup-node@v4 diff --git a/.gitignore b/.gitignore index 6fd243c..aac2149 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ dist/ *.tsbuildinfo .DS_Store .env -logs/ \ No newline at end of file +logs/ +*.tgz +openapi/.spec-source.json \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index bd4941c..c57847b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -11,15 +11,16 @@ TypeScript SDK (`@structbuild/sdk`) for prediction market APIs via `api.struct.t - **Build:** `bun run build` (outputs ESM + CJS to `dist/`, generates declaration files) - **Typecheck:** `bun run typecheck` - **Install deps:** `bun install` -- **Fetch OpenAPI spec:** `bun run fetch-spec:polymarket` -- **Generate types:** `bun run generate:polymarket` -- **Full pipeline:** `bun run prep` (fetch specs, generate types, check routes, build) -- **Check routes:** `bun run check-routes` (validates namespace routes match OpenAPI spec) -- **Fetch webhook spec:** `bun run fetch-spec:webhooks` -- **Generate webhook types:** `bun run generate:webhooks` +- **Fetch all specs:** `bun run fetch-specs` (fetches from production by default; use `STRUCT_ENV=staging` for staging) +- **Generate types:** `bun run generate:polymarket`, `bun run generate:webhooks`, `bun run generate:ws` +- **Full pipeline (prod):** `bun run prep` (fetch specs from prod, generate types, check routes, build) +- **Full pipeline (staging):** `bun run prep:staging` (same as prep but fetches from staging-api.struct.to) +- **Check routes:** `bun run check-routes` (validates namespace routes match OpenAPI spec; warns if specs are from staging) - **Fix spec:** `bun run fix-spec` (fixes broken `$ref`s in the OpenAPI spec) - **Test:** `bun test` (integration tests against live API, requires `STRUCT_API_KEY`) - **Test watch:** `bun test --watch` +- **Link for local dev:** `bun link` (then `bun link @structbuild/sdk` in consumer repo) +- **Pack for testing:** `bun run pack` (builds and creates `.tgz`) ## Architecture @@ -29,11 +30,12 @@ TypeScript SDK (`@structbuild/sdk`) for prediction market APIs via `api.struct.t - `src/http.ts` — Generic `HttpClient` built on `fetch` with timeout via `AbortController`, query param building, exponential backoff retry (429/5xx), request/response hooks, and typed `HttpResponse` responses. - `src/errors.ts` — Error hierarchy: `StructError` → `HttpError` | `NetworkError` | `TimeoutError` | `WebSocketError` | `WebSocketClosedError`. - `src/paginate.ts` — `paginate()` async generator for offset-based pagination across any namespace method. -- `src/ws.ts` — `StructWebSocket` for real-time trade streaming. Event system (`on`/`off`/`once`), subscriptions (markets, positions, wallets, conditions, whale/smart-money/insider rooms), auto-replays subscriptions on reconnect. -- `src/ws-transport.ts` — Low-level WebSocket connection management with reconnection (exponential backoff + jitter), pending/replay message queues. -- `src/types/` — All type definitions. `common.ts` (pagination/sort/Venue), `helpers.ts` (OpenAPI type utilities: `OperationQuery`, `OperationPath`, `OperationResponse`), `webhook-helpers.ts` (webhook OpenAPI type utilities), `http.ts` (client config/request/response), `ws.ts` (WebSocket types). `index.ts` barrel exports all types. +- `src/ws.ts` — `StructWebSocket` for real-time streaming. Room-based protocol (`join_room`/`room_message`). Generic typed `subscribe(room, filters?)` / `unsubscribe(room)` with Promise-based acks. Event system (`on`/`off`/`once`/`removeAllListeners`), `on()` returns disposer function. Auto-replays subscriptions on reconnect, keepalive ping. +- `src/ws-transport.ts` — Low-level WebSocket connection management with reconnection (exponential backoff + jitter), pending/replay message queues. `connect()` returns `Promise`. +- `src/types/` — All type definitions. `common.ts` (pagination/sort/Venue), `helpers.ts` (OpenAPI type utilities: `OperationQuery`, `OperationPath`, `OperationResponse`), `webhook-helpers.ts` (webhook OpenAPI type utilities), `ws-helpers.ts` (WS type utilities: `WsSchemas`), `http.ts` (client config/request/response), `ws.ts` (WebSocket types: room IDs, subscribe filters, event types, event map, subscription/response maps). `index.ts` barrel exports all types. - `src/generated/polymarket.ts` — Auto-generated types from the Polymarket OpenAPI spec via `openapi-typescript`. Do not edit manually. - `src/generated/webhooks.ts` — Auto-generated types from the Webhooks OpenAPI spec. Do not edit manually. +- `src/generated/ws.ts` — Auto-generated types from the WS AsyncAPI spec (`scripts/generate-ws-types.ts`). Do not edit manually. - `src/index.ts` — Public barrel export. - `tests/integration.test.ts` — Auto-discovers namespace methods and runs them against the live API. Test config in `tests/integration.meta.ts` defines params and operationId mappings per method. diff --git a/README.md b/README.md index da93f99..34747b4 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,6 @@ TypeScript SDK for prediction market data via [api.struct.to](https://api.struct ```bash npm install @structbuild/sdk # or -pnpm add @structbuild/sdk -# or bun add @structbuild/sdk ``` @@ -73,11 +71,11 @@ const trades = await client.trader.getTraderTrades({ address: "0x..." }); const profile = await client.trader.getTraderProfile({ address: "0x..." }); const profiles = await client.trader.getTraderProfilesBatch({ addresses: "0x...,0x..." }); const pnl = await client.trader.getTraderPnl({ address: "0x..." }); -const pnlByMarket = await client.trader.getTraderMarketPnl({ address: "0x..." }); -const pnlByEvent = await client.trader.getTraderEventPnl({ address: "0x..." }); +const marketPnl = await client.trader.getTraderMarketPnl({ address: "0x..." }); +const eventPnl = await client.trader.getTraderEventPnl({ address: "0x..." }); +const outcomePnl = await client.trader.getTraderOutcomePnl({ address: "0x..." }); const pnlCandles = await client.trader.getTraderPnlCandles({ address: "0x..." }); -const calendar = await client.trader.getTraderPnlCalendar({ address: "0x..." }); -const positionPnl = await client.trader.getTraderOutcomePnl({ address: "0x..." }); +const pnlCalendar = await client.trader.getTraderPnlCalendar({ address: "0x..." }); const volumeChart = await client.trader.getTraderVolumeChart({ address: "0x..." }); const leaderboard = await client.trader.getGlobalPnl(); ``` @@ -91,16 +89,20 @@ const history = await client.holders.getMarketHoldersHistory({ condition_id: "0x const posHistory = await client.holders.getPositionHoldersHistory({ positionId: "123" }); ``` -### Series +### Order Book ```typescript -const series = await client.series.getSeriesList(); -const outcomes = await client.series.getSeriesOutcomes({ series_slug: "my-series" }); +const orderBook = await client.orderBook.getOrderBook({ asset_id: "0x..." }); +const history = await client.orderBook.getOrderBookHistory(); +const marketBook = await client.orderBook.getMarketOrderBook(); +const spreads = await client.orderBook.getSpreadHistory(); ``` -### Assets, Search, Tags, Bonds +### Series, Search, Tags, Assets, Bonds ```typescript +const series = await client.series.getSeriesList(); +const outcomes = await client.series.getSeriesOutcomes({ series_slug: "my-series" }); const assetHistory = await client.assets.getAssetHistory({ symbol: "BTC", variant: "1d" }); const results = await client.search.search({ query: "election" }); const tags = await client.tags.getTags(); @@ -147,7 +149,7 @@ Individual schemas are also exported: `OrderFilledTrade`, `RedemptionTrade`, `Me ### Webhooks -Manage webhook subscriptions for real-time event notifications. Webhook endpoints are platform-level (not venue-scoped). +Manage webhook subscriptions for real-time event notifications: ```typescript const webhooks = await client.webhooks.list(); @@ -159,32 +161,129 @@ const webhook = await client.webhooks.create({ min_usd_value: 100, }, }); -const detail = await client.webhooks.getWebhook({ webhookId: webhook.data.id }); -await client.webhooks.update({ webhookId: webhook.data.id, events: ["first_trade"] }); await client.webhooks.test({ webhookId: webhook.data.id }); await client.webhooks.rotateSecret({ webhookId: webhook.data.id }); await client.webhooks.deleteWebhook({ webhookId: webhook.data.id }); const events = await client.webhooks.listEvents(); ``` -#### Webhook Payload Types +## WebSocket API -The SDK exports typed payload schemas for building webhook receivers: +Real-time streaming via room-based subscriptions with fully typed filters, responses, and events. ```typescript -import type { - FirstTradePayload, - ProbabilitySpikePayload, - GlobalPnlPayload, - VolumeMilestonePayload, -} from "@structbuild/sdk"; - -function handleWebhook(payload: FirstTradePayload) { - console.log(payload.trader, payload.price, payload.side); -} +import { StructWebSocket } from "@structbuild/sdk"; + +const ws = new StructWebSocket({ apiKey: "your-api-key" }); +await ws.connect(); +``` + +### Subscribing to rooms + +Each room has typed filters and a typed subscribe response: + +```typescript +const res = await ws.subscribe("polymarket_trades", { + condition_ids: ["0xabc123"], +}); + +await ws.subscribe("polymarket_order_book", { + asset_ids: ["0xabc123"], +}); + +// Some rooms have optional filters +await ws.subscribe("polymarket_asset_prices"); +await ws.subscribe("polymarket_clob_rewards", { subscribe_all: true }); +``` + +### Listening for events + +```typescript +ws.on("trade_stream_update", (event) => { + event.condition_id; + event.price; + event.size; + event.side; +}); + +ws.on("order_book_update", (event) => { + event.asset_id; + event.bids; + event.asks; +}); + +ws.on("clob_rewards_update", (event) => { + event.event_type; // "added" | "removed" | "updated" + event.condition_id; + event.reward; +}); ``` -Available payload types: `FirstTradePayload`, `GlobalPnlPayload`, `MarketPnlPayload`, `EventPnlPayload`, `ConditionMetricsPayload`, `EventMetricsPayload`, `PositionMetricsPayload`, `VolumeMilestonePayload`, `EventVolumeMilestonePayload`, `PositionVolumeMilestonePayload`, `ProbabilitySpikePayload`. +### Alerts + +Alerts use a separate client with per-event typed filters and payloads: + +```typescript +import { StructAlertsWebSocket } from "@structbuild/sdk"; + +const alerts = new StructAlertsWebSocket({ apiKey: "your-api-key" }); +await alerts.connect(); + +await alerts.subscribe("trader_whale_trade", { + wallet_addresses: ["0xd91..."], + min_usd_value: 10000, +}); + +await alerts.subscribe("probability_spike", { + spike_direction: "up", + min_probability_change_pct: 5, +}); + +alerts.on("trader_whale_trade", (payload) => { + payload.data.trader; + payload.data.amount_usd; +}); + +alerts.on("probability_spike", (payload) => { + payload.data.spike_direction; + payload.data.spike_pct; +}); +``` + +### Available rooms + +| Room | Filters | Event | +|------|---------|-------| +| `polymarket_trades` | `condition_ids` | `trade_stream_update` | +| `polymarket_asset_prices` | `condition_ids?` | `asset_price_tick`, `asset_price_window_update` | +| `polymarket_asset_window_updates` | `condition_ids` | `asset_window_update` | +| `polymarket_market_metrics` | `condition_ids` | `market_metrics_update` | +| `polymarket_event_metrics` | `event_slugs` | `event_metrics_update` | +| `polymarket_position_metrics` | `position_ids` | `position_metrics_update` | +| `polymarket_trader_pnl` | `addresses` | `trader_global_pnl_update`, `trader_market_pnl_update`, `trader_event_pnl_update` | +| `polymarket_trader_positions` | `addresses` | `trader_position_update` | +| `polymarket_accounts` | `wallets` | `accounts_update`, `usdce_update`, `matic_update` | +| `polymarket_order_book` | `asset_ids` | `order_book_update` | +| `polymarket_clob_rewards` | `condition_ids?`, `subscribe_all?` | `clob_rewards_update` | + +### Lifecycle events + +```typescript +ws.on("connected", () => {}); +ws.on("disconnected", ({ code, reason }) => {}); +ws.on("reconnecting", ({ attempt }) => {}); +ws.on("reconnect_failed", (err) => {}); +ws.on("auth_failed", (err) => {}); +ws.on("warning", (warning) => {}); +ws.on("error", (err) => {}); +``` + +### Cleanup + +```typescript +ws.unsubscribe("polymarket_trades"); +ws.disconnect(); +``` ## JWT Auth @@ -208,6 +307,15 @@ const ws = new StructWebSocket({ The `pk_jwt_*` key is safe to hardcode in frontend bundles — it is useless without a valid JWT from your configured auth provider. +If your JWT can rotate while a socket stays alive, prefer `getJwt` so reconnects always rebuild the URL with a fresh token: + +```typescript +const ws = new StructWebSocket({ + apiKey: "pk_jwt_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", + getJwt: () => userAccessToken, +}); +``` + ## Pagination Use the `paginate` helper to iterate through all results: diff --git a/openapi/polymarket.json b/openapi/polymarket.json index a75dd22..4117f78 100644 --- a/openapi/polymarket.json +++ b/openapi/polymarket.json @@ -1 +1 @@ -{"openapi":"3.1.0","info":{"title":"Polymarket API","description":"RESTful API for querying Polymarket prediction markets data including events, markets, traders, holders, and real-time metrics","license":{"name":""},"version":"1.0.0"},"servers":[{"url":"https://api.struct.to/v1","description":"Production"}],"paths":{"/polymarket/asset-history":{"get":{"tags":["Assets"],"summary":"Get Asset Price History","description":"Returns historical price data for supported crypto assets from Polymarket API","operationId":"get_asset_history","parameters":[{"name":"asset_symbol","in":"query","description":"Asset ticker: BTC, ETH, XRP, SOL, DOGE, BNB, HYPE","required":true,"schema":{"type":"string"},"example":"BTC"},{"name":"variant","in":"query","description":"Time window: 5m, 15m, 1h, 4h, 1d","required":true,"schema":{"type":"string"},"example":"1h"},{"name":"from","in":"query","description":"Start timestamp in seconds (Unix epoch, inclusive)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"End timestamp in seconds (Unix epoch, inclusive)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"limit","in":"query","description":"Number of results (default: 10, max: 100)","required":false,"schema":{"type":"integer","format":"int64"},"example":10},{"name":"pagination_key","in":"query","description":"Cursor from previous response for keyset pagination. Pass the pagination_key from the previous page to fetch the next page.","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AssetPriceHistoryRow"}}}}},"400":{"description":"Bad request - missing or invalid parameters"},"500":{"description":"Internal server error"}}}},"/polymarket/events":{"get":{"tags":["Events"],"summary":"Get events","description":"Retrieve a paginated list of events with filtering, sorting, and optional nested tags/markets","operationId":"get_events","parameters":[{"name":"id","in":"query","description":"Filter by event ID(s) - comma-separated (max 50). Cannot be used with 'event_slugs'. Example: id=99600,99601,99583","required":false,"schema":{"type":"string"}},{"name":"event_slugs","in":"query","description":"Filter by event slug(s) - comma-separated (max 50). Cannot be used with 'id'. Example: event_slugs=will-trump-win,bitcoin-100k","required":false,"schema":{"type":"string"}},{"name":"search","in":"query","description":"Search in title and description (3-100 characters). Example: search=trump","required":false,"schema":{"type":"string"}},{"name":"sort_by","in":"query","description":"Sort: volume, txns, unique_traders, title, creation_date, start_date, end_date, relevance (relevance only works in search mode) (default: volume)","required":false,"schema":{"$ref":"#/components/schemas/EventSortBy"}},{"name":"sort_dir","in":"query","description":"Sort direction: asc, desc (default: desc)","required":false,"schema":{"$ref":"#/components/schemas/SortDirection"}},{"name":"timeframe","in":"query","description":"Metrics timeframe: 1m, 5m, 30m, 1h, 6h, 24h, 7d, 30d (default: 24h)","required":false,"schema":{"$ref":"#/components/schemas/MetricsTimeframe"}},{"name":"status","in":"query","description":"Filter by status: open, closed, or all (default: all)","required":false,"schema":{"$ref":"#/components/schemas/MarketStatus"}},{"name":"categories","in":"query","description":"Comma-separated category filters","required":false,"schema":{"type":"string"}},{"name":"exclude_categories","in":"query","description":"Comma-separated categories to exclude","required":false,"schema":{"type":"string"}},{"name":"tags","in":"query","description":"Filter by tag slug(s) - comma-separated (max 50). Example: tags=sports,football,crypto","required":false,"schema":{"type":"string"}},{"name":"exclude_tags","in":"query","description":"Comma-separated tag slugs to exclude","required":false,"schema":{"type":"string"}},{"name":"min_volume","in":"query","description":"Minimum volume in selected timeframe","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_volume","in":"query","description":"Maximum volume in selected timeframe","required":false,"schema":{"type":"number","format":"double"}},{"name":"min_txns","in":"query","description":"Minimum transactions in selected timeframe","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"max_txns","in":"query","description":"Maximum transactions in selected timeframe","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"min_unique_traders","in":"query","description":"Minimum unique traders in selected timeframe","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"max_unique_traders","in":"query","description":"Maximum unique traders in selected timeframe","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"include_tags","in":"query","description":"Include tags array (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_markets","in":"query","description":"Include markets array with outcomes (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_metrics","in":"query","description":"Include metrics object with all timeframes (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"limit","in":"query","description":"Results limit (default: 10, max: 100)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key","required":false,"schema":{"type":"string"}},{"name":"ai","in":"query","description":"Return truncated response optimized for AI consumers (default: false)","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"List of Polymarket events with nested tags, markets, and series. Response includes `pagination: { has_more, pagination_key }` for cursor-based pagination.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PolymarketEvent"}}}}},"400":{"description":"Bad request - validation error (search length, array limits, conflicting params)"}}}},"/polymarket/events/chart":{"get":{"tags":["Events"],"summary":"Get event chart","description":"Retrieve price data over time for up to 4 markets with highest YES outcome (outcome_index 0) prices in an event. Perfect for rendering multi-line charts showing price movement across top markets. TradingView-style: resolution parameter determines both candle size and implicit lookback period.","operationId":"get_event_chart","parameters":[{"name":"event_slug","in":"query","description":"Event slug (required)","required":true,"schema":{"type":"string"}},{"name":"condition_ids","in":"query","description":"Comma-separated condition IDs to include (max 4, optional)","required":false,"schema":{"type":"string"}},{"name":"market_slugs","in":"query","description":"Comma-separated market slugs to include (max 4, optional). Use either condition_ids or market_slugs, not both","required":false,"schema":{"type":"string"}},{"name":"resolution","in":"query","description":"Time interval with implicit lookback: 1H (1 hour), 6H (6 hours), 1D (1 day), 1W (1 week), 1M (30 days), ALL (all data) (required)","required":true,"schema":{"type":"string","enum":["1H","6H","1D","1W","1M","ALL"]}}],"responses":{"200":{"description":"Price data over time for up to 4 markets with highest YES odds","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/EventMarketChartOutcome"}}}}}}}},"/polymarket/events/metrics":{"get":{"tags":["Events"],"summary":"Get event metrics","description":"Retrieve volume, transaction, and trader metrics for an event. Supports single timeframe (e.g., '1m'), multiple timeframes (e.g., '1m,5m,1h'), or 'all' to get all available timeframes.","operationId":"get_event_metrics","parameters":[{"name":"event_slug","in":"query","description":"Event slug","required":true,"schema":{"type":"string"}},{"name":"timeframe","in":"query","description":"Timeframe: single (1m, 5m, 30m, 1h, 6h, 24h, 7d, 30d), comma-separated (1m,5m,1h), or 'all'","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Event metrics for the specified timeframe(s). Returns single object for one timeframe, array for multiple.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventMetricsResponse"}}}},"400":{"description":"Invalid timeframe"}}}},"/polymarket/events/outcomes":{"get":{"tags":["Events"],"summary":"Get event market outcomes","description":"Returns the winning outcome name for each resolved market in an event, keyed by market slug. Useful for quickly checking which outcomes won across a series.","operationId":"get_event_outcomes","parameters":[{"name":"event_slug","in":"query","description":"Event slug (required)","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","description":"Number of resolved markets per page (default: 10, max: 250)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key for fetching next page","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Map of market_slug → winning outcome name with pagination metadata","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}}}}},"400":{"description":"Missing event_slug parameter"}}}},"/polymarket/events/slug/{event_slug}":{"get":{"tags":["Events"],"summary":"Get event by slug","description":"Retrieve a single event by its slug with optional nested tags, markets, and metrics","operationId":"get_event_by_slug","parameters":[{"name":"event_slug","in":"path","description":"Event slug","required":true,"schema":{"type":"string"}},{"name":"include_tags","in":"query","description":"Include tags array (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_markets","in":"query","description":"Include markets array with outcomes (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_metrics","in":"query","description":"Include metrics object with all timeframes (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"ai","in":"query","description":"Return truncated response optimized for AI consumers (default: false)","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Event with nested tags, markets, and series","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolymarketEvent"}}}},"404":{"description":"Event not found"}}}},"/polymarket/events/{identifier}":{"get":{"tags":["Events"],"summary":"Get event by ID or slug","description":"Retrieve a single event by its numeric ID or slug with optional nested tags, markets, and metrics","operationId":"get_event","parameters":[{"name":"identifier","in":"path","description":"Event ID or slug","required":true,"schema":{"type":"string"}},{"name":"include_tags","in":"query","description":"Include tags array (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_markets","in":"query","description":"Include markets array with outcomes (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_metrics","in":"query","description":"Include metrics object with all timeframes (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"ai","in":"query","description":"Return truncated response optimized for AI consumers (default: false)","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Event with nested tags, markets, and series","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolymarketEvent"}}}},"404":{"description":"Event not found"}}}},"/polymarket/holders/markets":{"get":{"tags":["Holders"],"summary":"Get market holders","description":"Retrieve holders of a market grouped by outcome, sorted by shares held. Identify the market with either `condition_id` or `market_slug` — exactly one must be provided. Set `include_pnl=true` to include a nested holder `pnl` object.","operationId":"get_market_holders","parameters":[{"name":"condition_id","in":"query","description":"Market condition ID (0x-prefixed hex)","required":false,"schema":{"type":"string"}},{"name":"market_slug","in":"query","description":"Market slug (e.g. `will-trump-win`)","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","description":"Results limit (default: 10, max: 100)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"min_shares","in":"query","description":"Minimum shares held (decimal string)","required":false,"schema":{"type":"string"}},{"name":"max_shares","in":"query","description":"Maximum shares held (decimal string)","required":false,"schema":{"type":"string"}},{"name":"include_pnl","in":"query","description":"Include nested holder PnL data (default: false, +1 credit)","required":false,"schema":{"type":"boolean"}},{"name":"ai","in":"query","description":"Return truncated response optimized for AI consumers (default: false)","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Market holders grouped by outcome (sorted by shares DESC). Holder `pnl` is included only when `include_pnl=true`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarketHoldersResponse"}}}},"404":{"description":"Market not found"}}}},"/polymarket/holders/markets/history":{"get":{"tags":["Holders"],"summary":"Get market holders history","description":"Retrieve historical holder count snapshots for a market over a time range. Identify the market with either `condition_id` or `market_slug` — exactly one must be provided.","operationId":"get_market_holders_history","parameters":[{"name":"condition_id","in":"query","description":"Market condition ID (0x-prefixed hex)","required":false,"schema":{"type":"string"}},{"name":"market_slug","in":"query","description":"Market slug (e.g. `will-trump-win`)","required":false,"schema":{"type":"string"}},{"name":"hours","in":"query","description":"Time range in hours (default: 24, max: 336 = 14 days)","required":false,"schema":{"type":"number","format":"double"}}],"responses":{"200":{"description":"Market holder count history with automatic granularity (1m/5m/15m/1h/6h buckets)","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/HolderHistoryCandle"}}}}},"404":{"description":"Market not found"}}}},"/polymarket/holders/positions/{position_id}":{"get":{"tags":["Holders"],"summary":"Get position holders","description":"Retrieve holders of a specific position (ERC1155 token), sorted by shares held. Set `include_pnl=true` to include nested holder PnL. Uses cursor-based pagination for efficient traversal.","operationId":"get_position_holders","parameters":[{"name":"position_id","in":"path","description":"Position ID (ERC1155 token ID)","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","description":"Results limit (default: 10, max: 100)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key","required":false,"schema":{"type":"string"}},{"name":"min_shares","in":"query","description":"Minimum shares held (decimal string)","required":false,"schema":{"type":"string"}},{"name":"max_shares","in":"query","description":"Maximum shares held (decimal string)","required":false,"schema":{"type":"string"}},{"name":"include_pnl","in":"query","description":"Include nested holder PnL data (default: false, +1 credit)","required":false,"schema":{"type":"boolean"}},{"name":"ai","in":"query","description":"Return truncated response optimized for AI consumers (default: false)","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Position holders (sorted by shares DESC). Holder `pnl` is included only when `include_pnl=true`. Response includes `pagination: { has_more, pagination_key }` for cursor-based pagination.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PositionHoldersResponse"}}}},"404":{"description":"Position not found"}}}},"/polymarket/holders/positions/{position_id}/history":{"get":{"tags":["Holders"],"summary":"Get position holders history","description":"Retrieve historical holder count snapshots for a position over a time range","operationId":"get_position_holders_history","parameters":[{"name":"position_id","in":"path","description":"Position ID (ERC1155 token ID)","required":true,"schema":{"type":"string"}},{"name":"hours","in":"query","description":"Time range in hours (default: 24, max: 336 = 14 days)","required":false,"schema":{"type":"number","format":"double"}}],"responses":{"200":{"description":"Position holder count history with automatic granularity (1m/5m/15m/1h/6h buckets)","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/HolderHistoryCandle"}}}}},"404":{"description":"Position not found"}}}},"/polymarket/market":{"get":{"tags":["Market"],"summary":"Get markets","description":"Retrieve a paginated list of markets with filtering, sorting, and optional nested tags/events/metrics","operationId":"list_markets","parameters":[{"name":"condition_ids","in":"query","description":"Filter by condition ID(s) - comma-separated (max 50)","required":false,"schema":{"type":"string"}},{"name":"question_ids","in":"query","description":"Filter by question ID(s) - comma-separated (max 50)","required":false,"schema":{"type":"string"}},{"name":"market_ids","in":"query","description":"Filter by market ID(s) - comma-separated (max 50)","required":false,"schema":{"type":"string"}},{"name":"market_slugs","in":"query","description":"Filter by market slug(s) - comma-separated (max 50)","required":false,"schema":{"type":"string"}},{"name":"event_slugs","in":"query","description":"Filter by event slug(s) - comma-separated (max 50)","required":false,"schema":{"type":"string"}},{"name":"position_ids","in":"query","description":"Filter by position ID(s) - comma-separated (max 50)","required":false,"schema":{"type":"string"}},{"name":"search","in":"query","description":"Search in title (3-100 characters)","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","description":"Filter by status: open, closed, or all (default: all)","required":false,"schema":{"$ref":"#/components/schemas/MarketStatus"}},{"name":"sort_by","in":"query","description":"Sort: volume, txns, unique_traders, liquidity, holders, total_daily_rate, end_time, start_time, created_time, relevance","required":false,"schema":{"$ref":"#/components/schemas/MarketSortBy"}},{"name":"sort_dir","in":"query","description":"Sort direction: asc, desc (default: desc)","required":false,"schema":{"$ref":"#/components/schemas/SortDirection"}},{"name":"timeframe","in":"query","description":"Metrics timeframe: 1m, 5m, 30m, 1h, 6h, 24h, 7d, 30d (default: 24h)","required":false,"schema":{"$ref":"#/components/schemas/MetricsTimeframe"}},{"name":"min_volume","in":"query","description":"Minimum total volume USD","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_volume","in":"query","description":"Maximum total volume USD","required":false,"schema":{"type":"number","format":"double"}},{"name":"min_liquidity","in":"query","description":"Minimum liquidity USD","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_liquidity","in":"query","description":"Maximum liquidity USD","required":false,"schema":{"type":"number","format":"double"}},{"name":"min_txns","in":"query","description":"Minimum transactions in selected timeframe","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"max_txns","in":"query","description":"Maximum transactions in selected timeframe","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"min_unique_traders","in":"query","description":"Minimum unique traders in selected timeframe","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"max_unique_traders","in":"query","description":"Maximum unique traders in selected timeframe","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"min_holders","in":"query","description":"Minimum total holders","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"max_holders","in":"query","description":"Maximum total holders","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"categories","in":"query","description":"Comma-separated category filters (max 50)","required":false,"schema":{"type":"string"}},{"name":"exclude_categories","in":"query","description":"Comma-separated categories to exclude","required":false,"schema":{"type":"string"}},{"name":"tags","in":"query","description":"Filter by tag(s) - comma-separated (max 50)","required":false,"schema":{"type":"string"}},{"name":"exclude_tags","in":"query","description":"Comma-separated tags to exclude","required":false,"schema":{"type":"string"}},{"name":"start_time","in":"query","description":"Filter markets with end_time >= start_time (Unix timestamp)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"end_time","in":"query","description":"Filter markets with end_time <= end_time (Unix timestamp)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"include_tags","in":"query","description":"Include tags array (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_event","in":"query","description":"Include event object (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_metrics","in":"query","description":"Include all timeframe metrics (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"limit","in":"query","description":"Results limit (default: 10, max: 100)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key","required":false,"schema":{"type":"string"}},{"name":"has_rewards","in":"query","description":"Only return markets that have CLOB rewards (default: false)","required":false,"schema":{"type":"boolean"}},{"name":"ai","in":"query","description":"Return truncated response optimized for AI consumers (default: false)","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"List of markets with metadata, outcomes, tags, event, and metrics. Response includes `pagination: { has_more, pagination_key }` for cursor-based pagination.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MarketResponse"}}}}},"400":{"description":"Bad request - validation error (search length, array limits, conflicting params)"}}}},"/polymarket/market/bonds":{"get":{"tags":["Bonds"],"summary":"Get bonds","description":"Retrieve a list of bond markets sorted by yield, filtered by probability and time to expiry","operationId":"get_bonds","parameters":[{"name":"min_probability","in":"query","description":"Minimum probability threshold (default: 0.85)","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_hours","in":"query","description":"Maximum hours until market end","required":false,"schema":{"type":"number","format":"double"}},{"name":"limit","in":"query","description":"Number of results (default: 10, max: 250)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Cursor for pagination: end_date (unix epoch) of the last item from the previous page","required":false,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"List of bond markets sorted by yield","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BondMarket"}}}}}}}},"/polymarket/market/candlestick":{"get":{"tags":["Market"],"summary":"Get market candlesticks by condition_id","description":"Retrieve OHLCV candlestick data for a market by its condition_id","operationId":"get_market_candlestick","parameters":[{"name":"condition_id","in":"query","description":"Market condition ID (required)","required":true,"schema":{"type":"string"}},{"name":"resolution","in":"query","description":"Candle interval: 1, 5, 15, 30, 60, 240, D, 1D","required":true,"schema":{"$ref":"#/components/schemas/CandlestickResolution"}},{"name":"count_back","in":"query","description":"Number of candles (max: 2500)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"from","in":"query","description":"Start timestamp (Unix seconds)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"End timestamp (Unix seconds)","required":false,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OHLCV candlestick data","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PredictionCandlestickBar"}}}}}}}},"/polymarket/market/chart":{"get":{"tags":["Market"],"summary":"Get market chart","description":"Retrieve price data over time for up to 4 position outcomes in a market condition. TradingView-style: resolution parameter determines both candle size and implicit lookback period. Auto-selects the 4 most active outcomes if position_ids not specified.","operationId":"get_chart","parameters":[{"name":"condition_id","in":"query","description":"Market condition ID or market_slug (one required)","required":false,"schema":{"type":"string"}},{"name":"market_slug","in":"query","description":"Market slug (alternative to condition_id)","required":false,"schema":{"type":"string"}},{"name":"position_ids","in":"query","description":"Comma-separated list of position IDs (max 4, optional). Auto-selected if not provided","required":false,"schema":{"type":"string"}},{"name":"resolution","in":"query","description":"Time interval with implicit lookback: 1H (1 hour), 6H (6 hours), 1D (1 day), 1W (1 week), 1M (30 days), ALL (all data) (required)","required":true,"schema":{"type":"string","enum":["1H","6H","1D","1W","1M","ALL"]}}],"responses":{"200":{"description":"Price data over time for up to 4 market outcomes","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PositionChartOutcome"}}}}}}}},"/polymarket/market/metrics":{"get":{"tags":["Market"],"summary":"Get market metrics","description":"Retrieve volume, transaction, and trader metrics for a market. Supports single timeframe (e.g., '1m'), multiple timeframes (e.g., '1m,5m,1h'), or 'all' to get all available timeframes.","operationId":"get_market_metrics","parameters":[{"name":"condition_id","in":"query","description":"Market condition ID","required":true,"schema":{"type":"string"}},{"name":"timeframe","in":"query","description":"Timeframe: single (1m, 5m, 30m, 1h, 6h, 24h, 7d, 30d), comma-separated (1m,5m,1h), or 'all'","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Market metrics for the specified timeframe(s). Returns single object for one timeframe, array for multiple.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConditionMetricsResponse"}}}},"400":{"description":"Invalid timeframe"}}}},"/polymarket/market/position/candlestick":{"get":{"tags":["Market"],"summary":"Get position candlesticks by position_id","description":"Retrieve OHLCV candlestick data for a specific position by its position_id","operationId":"get_position_candlestick","parameters":[{"name":"position_id","in":"query","description":"Position/token ID (required)","required":true,"schema":{"type":"string"}},{"name":"resolution","in":"query","description":"Candle interval: 1, 5, 15, 30, 60, 240, D, 1D","required":true,"schema":{"$ref":"#/components/schemas/CandlestickResolution"}},{"name":"count_back","in":"query","description":"Number of candles (max: 2500)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"from","in":"query","description":"Start timestamp (Unix seconds)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"End timestamp (Unix seconds)","required":false,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OHLCV candlestick data","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PredictionCandlestickBar"}}}}}}}},"/polymarket/market/position/metrics":{"get":{"tags":["Market"],"summary":"Get position metrics","description":"Retrieve volume, transaction, and trader metrics for a specific position. Supports single timeframe (e.g., '1m'), multiple timeframes (e.g., '1m,5m,1h'), or 'all' to get all available timeframes.","operationId":"get_position_metrics","parameters":[{"name":"position_id","in":"query","description":"Position/token ID","required":true,"schema":{"type":"string"}},{"name":"timeframe","in":"query","description":"Timeframe: single (1m, 5m, 30m, 1h, 6h, 24h, 7d, 30d), comma-separated (1m,5m,1h), or 'all'","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Position metrics for the specified timeframe(s). Returns single object for one timeframe, array for multiple.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PositionMetricsResponse"}}}},"400":{"description":"Invalid timeframe"}}}},"/polymarket/market/position/volume-chart":{"get":{"tags":["Market"],"summary":"Get position volume chart","description":"Retrieve volume over time for a specific position with buy/sell breakdown","operationId":"get_position_volume_chart","parameters":[{"name":"position_id","in":"query","description":"Position/token ID (required)","required":true,"schema":{"type":"string"}},{"name":"resolution","in":"query","description":"Time interval: 1, 5, 15, 30, 60, 240, D, 1D (default: 60)","required":false,"schema":{"$ref":"#/components/schemas/CandlestickResolution"}},{"name":"count_back","in":"query","description":"Number of data points (max: 2500)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"from","in":"query","description":"Start timestamp (Unix seconds)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"End timestamp (Unix seconds)","required":false,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"Volume with buy/sell breakdown over time","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PositionVolumeDataPoint"}}}}}}}},"/polymarket/market/price-jumps":{"get":{"tags":["Market"],"summary":"Detect price jumps","description":"Scan candles for significant price movements. Returns jumps with from/to timestamps in milliseconds, directly usable as trades API time range parameters to identify traders who traded before or during the movement.","operationId":"get_price_jumps","parameters":[{"name":"condition_id","in":"query","description":"Market condition ID (one of condition_id or market_slug required)","required":false,"schema":{"type":"string"}},{"name":"market_slug","in":"query","description":"Market slug (resolved to condition_id)","required":false,"schema":{"type":"string"}},{"name":"resolution","in":"query","description":"Candle resolution in minutes: 1, 5, 15, 30, 60, 240 (default: 15)","required":false,"schema":{"type":"string"}},{"name":"min_change_pct","in":"query","description":"Minimum relative percent change to qualify as a jump (default: 10.0)","required":false,"schema":{"type":"number","format":"double"}},{"name":"lookback","in":"query","description":"Number of candles to scan back from now (default: 1440, max: 2500)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"offset","in":"query","description":"Offset in candles from now — window is [now - (lookback + offset) * resolution, now - offset * resolution] (default: 0)","required":false,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"Price jumps sorted by timestamp descending","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PriceJump"}}}}}}}},"/polymarket/market/slug/{market_slug}":{"get":{"tags":["Market"],"summary":"Get market by slug","description":"Retrieve a single market by its slug with optional nested tags, event, and metrics","operationId":"get_market_by_slug","parameters":[{"name":"market_slug","in":"path","description":"Market slug (e.g. `will-trump-win`)","required":true,"schema":{"type":"string"}},{"name":"include_tags","in":"query","description":"Include tags array (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_event","in":"query","description":"Include event object (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_metrics","in":"query","description":"Include all timeframe metrics (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"ai","in":"query","description":"Return truncated response optimized for AI consumers (default: false)","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Market with metadata, outcomes, tags, event, and metrics","content":{"application/json":{"schema":{"type":"object","description":"Formatted market response with structured metrics, tags, outcomes, and event","required":["condition_id","status"],"properties":{"condition_id":{"type":"string"},"id":{"type":["string","null"]},"market_slug":{"type":["string","null"]},"question":{"type":["string","null"]},"title":{"type":["string","null"]},"description":{"type":["string","null"]},"image_url":{"type":["string","null"]},"oracle":{"type":["string","null"]},"status":{"type":"string"},"created_time":{"type":["integer","null"],"format":"int64"},"start_time":{"type":["integer","null"],"format":"int64"},"game_start_time":{"type":["integer","null"],"format":"int64"},"closed_time":{"type":["integer","null"],"format":"int64"},"end_time":{"type":["integer","null"],"format":"int64"},"accepting_orders":{"type":["boolean","null"]},"uma_resolution_status":{"type":["string","null"]},"is_neg_risk":{"type":["boolean","null"]},"market_maker_address":{"type":["string","null"]},"creator":{"type":["string","null"]},"category":{"type":["string","null"]},"volume_usd":{"type":["number","null"],"format":"double"},"liquidity_usd":{"type":["number","null"],"format":"double"},"highest_probability":{"type":["number","null"],"format":"double"},"total_holders":{"type":["integer","null"],"format":"int64"},"total_daily_rate":{"type":["number","null"],"format":"double"},"winning_outcome":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/MarketOutcome"}]},"outcomes":{"type":"array","items":{"$ref":"#/components/schemas/MarketOutcome"}},"clob_rewards":{"type":"array","items":{"$ref":"#/components/schemas/ClobReward"}},"tags":{"type":"array","items":{"type":"string"}},"event_slug":{"type":["string","null"]},"resolution_source":{"type":["string","null"]},"metrics":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/SimpleTimeframeMetrics"},"propertyNames":{"type":"string"}},"relevance_score":{"type":["number","null"],"format":"double"}}}}}},"404":{"description":"Market not found"}}}},"/polymarket/market/trades":{"get":{"tags":["Market"],"summary":"Get market trades","description":"Retrieve trades for one or more markets, with filtering by trader, side, price, amount, and time range","operationId":"get_market_trades","parameters":[{"name":"condition_ids","in":"query","description":"Comma-separated condition IDs (max 20)","required":false,"schema":{"type":"string"}},{"name":"slugs","in":"query","description":"Comma-separated market slugs","required":false,"schema":{"type":"string"}},{"name":"position_ids","in":"query","description":"Comma-separated position IDs","required":false,"schema":{"type":"string"}},{"name":"traders","in":"query","description":"Comma-separated trader addresses (max 25)","required":false,"schema":{"type":"string"}},{"name":"side","in":"query","description":"Trade side: 0 (Buy), 1 (Sell)","required":false,"schema":{"$ref":"#/components/schemas/TradeSide"}},{"name":"outcome","in":"query","description":"Outcome name filter (e.g. Yes, No)","required":false,"schema":{"type":"string"}},{"name":"outcome_index","in":"query","description":"Outcome index: 0 (Yes), 1 (No)","required":false,"schema":{"$ref":"#/components/schemas/OutcomeIndex"}},{"name":"trade_types","in":"query","description":"Comma-separated trade types: OrderFilled, Redemption, Merge, Split, Cancelled, PositionsConverted, OrdersMatched","required":false,"schema":{"type":"string"}},{"name":"min_usd_amount","in":"query","description":"Min USD amount","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_usd_amount","in":"query","description":"Max USD amount","required":false,"schema":{"type":"number","format":"double"}},{"name":"min_shares_amount","in":"query","description":"Min shares amount","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_shares_amount","in":"query","description":"Max shares amount","required":false,"schema":{"type":"number","format":"double"}},{"name":"min_price","in":"query","description":"Min price (0.0-1.0)","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_price","in":"query","description":"Max price (0.0-1.0)","required":false,"schema":{"type":"number","format":"double"}},{"name":"from","in":"query","description":"Start timestamp (ms)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"End timestamp (ms)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"all","in":"query","description":"Return all-time trades, not just last 30 days (default: false)","required":false,"schema":{"type":"boolean"}},{"name":"limit","in":"query","description":"Results per page (default: 10, max: 250)","required":false,"schema":{"type":"integer","format":"int32","minimum":0}},{"name":"offset","in":"query","description":"Pagination offset (number of results to skip). Takes precedence over pagination_key.","required":false,"schema":{"type":"integer","format":"int32","minimum":0}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key obtained from previous response's pagination.pagination_key","required":false,"schema":{"type":"string"}},{"name":"sort_desc","in":"query","description":"Sort newest first (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"ai","in":"query","description":"Return truncated response optimized for AI consumers (default: false)","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Prediction trades matching filters","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TradeEvent"}}}}}}}},"/polymarket/market/volume-chart":{"get":{"tags":["Market"],"summary":"Get market volume chart","description":"Retrieve volume breakdown by YES/NO outcome over time for a prediction market","operationId":"get_market_volume_chart","parameters":[{"name":"condition_id","in":"query","description":"Market condition ID (or use market_slug)","required":false,"schema":{"type":"string"}},{"name":"market_slug","in":"query","description":"Market slug (alternative to condition_id)","required":false,"schema":{"type":"string"}},{"name":"resolution","in":"query","description":"Time interval: 1, 5, 15, 30, 60, 240, D, 1D (default: 60)","required":false,"schema":{"$ref":"#/components/schemas/CandlestickResolution"}},{"name":"count_back","in":"query","description":"Number of data points (max: 2500)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"from","in":"query","description":"Start timestamp (Unix seconds)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"End timestamp (Unix seconds)","required":false,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"Volume breakdown by YES/NO over time","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MarketVolumeDataPoint"}}}}}}}},"/polymarket/market/{condition_id}":{"get":{"tags":["Market"],"summary":"Get market by condition ID","description":"Retrieve a single market by its condition ID with optional nested tags, event, and metrics","operationId":"get_market","parameters":[{"name":"condition_id","in":"path","description":"Market condition ID (0x-prefixed hex)","required":true,"schema":{"type":"string"}},{"name":"include_tags","in":"query","description":"Include tags array (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_event","in":"query","description":"Include event object (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_metrics","in":"query","description":"Include all timeframe metrics (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"ai","in":"query","description":"Return truncated response optimized for AI consumers (default: false)","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Market with metadata, outcomes, tags, event, and metrics","content":{"application/json":{"schema":{"type":"object","description":"Formatted market response with structured metrics, tags, outcomes, and event","required":["condition_id","status"],"properties":{"condition_id":{"type":"string"},"id":{"type":["string","null"]},"market_slug":{"type":["string","null"]},"question":{"type":["string","null"]},"title":{"type":["string","null"]},"description":{"type":["string","null"]},"image_url":{"type":["string","null"]},"oracle":{"type":["string","null"]},"status":{"type":"string"},"created_time":{"type":["integer","null"],"format":"int64"},"start_time":{"type":["integer","null"],"format":"int64"},"game_start_time":{"type":["integer","null"],"format":"int64"},"closed_time":{"type":["integer","null"],"format":"int64"},"end_time":{"type":["integer","null"],"format":"int64"},"accepting_orders":{"type":["boolean","null"]},"uma_resolution_status":{"type":["string","null"]},"is_neg_risk":{"type":["boolean","null"]},"market_maker_address":{"type":["string","null"]},"creator":{"type":["string","null"]},"category":{"type":["string","null"]},"volume_usd":{"type":["number","null"],"format":"double"},"liquidity_usd":{"type":["number","null"],"format":"double"},"highest_probability":{"type":["number","null"],"format":"double"},"total_holders":{"type":["integer","null"],"format":"int64"},"total_daily_rate":{"type":["number","null"],"format":"double"},"winning_outcome":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/MarketOutcome"}]},"outcomes":{"type":"array","items":{"$ref":"#/components/schemas/MarketOutcome"}},"clob_rewards":{"type":"array","items":{"$ref":"#/components/schemas/ClobReward"}},"tags":{"type":"array","items":{"type":"string"}},"event_slug":{"type":["string","null"]},"resolution_source":{"type":["string","null"]},"metrics":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/SimpleTimeframeMetrics"},"propertyNames":{"type":"string"}},"relevance_score":{"type":["number","null"],"format":"double"}}}}}},"404":{"description":"Market not found"}}}},"/polymarket/order-book":{"get":{"tags":["Order Book"],"summary":"Get order book","description":"Returns the latest CLOB orderbook snapshot for a position, including derived metrics (best bid/ask, mid price, spread, liquidity depth). Data is sourced from the real-time Polymarket WebSocket feed. `bids` and `asks` are arrays of `{\"p\": price, \"s\": size}` objects, sorted best-first.","operationId":"get_order_book","parameters":[{"name":"position_id","in":"query","description":"Token ID (position ID) to query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Latest orderbook snapshot. ts is Unix milliseconds.","content":{"application/json":{"schema":{"type":"object","required":["ts","position_id","condition_id","bids","asks","hash"],"properties":{"ts":{"type":"integer","format":"int64"},"position_id":{"type":"string"},"condition_id":{"type":"string"},"bids":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"asks":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"hash":{"type":"string"},"best_bid":{"type":["number","null"],"format":"double"},"best_ask":{"type":["number","null"],"format":"double"},"mid_price":{"type":["number","null"],"format":"double"},"spread":{"type":["number","null"],"format":"double"},"bid_liquidity_usd":{"type":["number","null"],"format":"double"},"ask_liquidity_usd":{"type":["number","null"],"format":"double"},"bid_levels":{"type":["integer","null"],"format":"int32"},"ask_levels":{"type":["integer","null"],"format":"int32"}}}}}},"400":{"description":"Missing or invalid parameters"}}}},"/polymarket/order-book/history":{"get":{"tags":["Order Book"],"summary":"Get order book history","description":"Paginated history of raw CLOB orderbook snapshots including full bids/asks levels and derived metrics. Default limit 20, max 200. `bids` and `asks` are arrays of `{\"p\": price, \"s\": size}` objects, sorted best-first.","operationId":"get_order_book_history","parameters":[{"name":"position_id","in":"query","description":"Token ID (required if condition_id / market_slug not set)","required":false,"schema":{"type":"string"}},{"name":"condition_id","in":"query","description":"Condition ID — returns history for all positions in this market","required":false,"schema":{"type":"string"}},{"name":"market_slug","in":"query","description":"Market slug (alternative to condition_id)","required":false,"schema":{"type":"string"}},{"name":"from","in":"query","description":"Start timestamp (Unix milliseconds, inclusive)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"End timestamp (Unix milliseconds, inclusive)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"min_spread","in":"query","description":"Only return snapshots with spread >= this value","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_spread","in":"query","description":"Only return snapshots with spread <= this value","required":false,"schema":{"type":"number","format":"double"}},{"name":"min_liquidity","in":"query","description":"Only return snapshots where total liquidity (bid + ask) >= this value","required":false,"schema":{"type":"number","format":"double"}},{"name":"limit","in":"query","description":"Number of results (default: 20, max: 200)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"pagination_key","in":"query","description":"Cursor from previous response's pagination.pagination_key","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Orderbook snapshot rows, newest first. ts is Unix milliseconds.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","required":["ts","position_id","condition_id","bids","asks","hash"],"properties":{"ts":{"type":"integer","format":"int64"},"position_id":{"type":"string"},"condition_id":{"type":"string"},"bids":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"asks":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"hash":{"type":"string"},"best_bid":{"type":["number","null"],"format":"double"},"best_ask":{"type":["number","null"],"format":"double"},"mid_price":{"type":["number","null"],"format":"double"},"spread":{"type":["number","null"],"format":"double"},"bid_liquidity_usd":{"type":["number","null"],"format":"double"},"ask_liquidity_usd":{"type":["number","null"],"format":"double"},"bid_levels":{"type":["integer","null"],"format":"int32"},"ask_levels":{"type":["integer","null"],"format":"int32"}}}}}}},"400":{"description":"Missing or invalid parameters"}}}},"/polymarket/order-book/market":{"get":{"tags":["Order Book"],"summary":"Get order books for a market","description":"Returns the latest orderbook snapshot for every position (outcome) in a market. Accepts condition_id or market_slug. `bids` and `asks` are arrays of `{\"p\": price, \"s\": size}` objects, sorted best-first.","operationId":"get_market_order_book","parameters":[{"name":"condition_id","in":"query","description":"Condition ID of the market","required":false,"schema":{"type":"string"}},{"name":"market_slug","in":"query","description":"Market slug (alternative to condition_id)","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Latest orderbook snapshot per position. ts is Unix milliseconds.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","required":["ts","position_id","condition_id","bids","asks","hash"],"properties":{"ts":{"type":"integer","format":"int64"},"position_id":{"type":"string"},"condition_id":{"type":"string"},"bids":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"asks":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"hash":{"type":"string"},"best_bid":{"type":["number","null"],"format":"double"},"best_ask":{"type":["number","null"],"format":"double"},"mid_price":{"type":["number","null"],"format":"double"},"spread":{"type":["number","null"],"format":"double"},"bid_liquidity_usd":{"type":["number","null"],"format":"double"},"ask_liquidity_usd":{"type":["number","null"],"format":"double"},"bid_levels":{"type":["integer","null"],"format":"int32"},"ask_levels":{"type":["integer","null"],"format":"int32"}}}}}}},"400":{"description":"Missing or invalid parameters"}}}},"/polymarket/order-book/spread":{"get":{"tags":["Order Book"],"summary":"Get spread history","description":"Lightweight time series of derived orderbook metrics (best bid/ask, mid price, spread, liquidity depth) without raw bids/asks — ideal for charting. Default limit 20, max 200.","operationId":"get_spread_history","parameters":[{"name":"position_id","in":"query","description":"Token ID (required if condition_id / market_slug not set)","required":false,"schema":{"type":"string"}},{"name":"condition_id","in":"query","description":"Condition ID — returns spread history for all positions in this market","required":false,"schema":{"type":"string"}},{"name":"market_slug","in":"query","description":"Market slug (alternative to condition_id)","required":false,"schema":{"type":"string"}},{"name":"from","in":"query","description":"Start timestamp (Unix milliseconds, inclusive)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"End timestamp (Unix milliseconds, inclusive)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"min_spread","in":"query","description":"Only return rows with spread >= this value","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_spread","in":"query","description":"Only return rows with spread <= this value","required":false,"schema":{"type":"number","format":"double"}},{"name":"min_liquidity","in":"query","description":"Only return rows where total liquidity (bid + ask) >= this value","required":false,"schema":{"type":"number","format":"double"}},{"name":"limit","in":"query","description":"Number of results (default: 100, max: 1000)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"pagination_key","in":"query","description":"Cursor from previous response's pagination.pagination_key","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Spread time series rows, newest first. ts is Unix milliseconds.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","description":"Lightweight row — derived metrics only, no bids/asks JSONB.","required":["ts","position_id","condition_id"],"properties":{"ts":{"type":"integer","format":"int64"},"position_id":{"type":"string"},"condition_id":{"type":"string"},"best_bid":{"type":["number","null"],"format":"double"},"best_ask":{"type":["number","null"],"format":"double"},"mid_price":{"type":["number","null"],"format":"double"},"spread":{"type":["number","null"],"format":"double"},"bid_liquidity_usd":{"type":["number","null"],"format":"double"},"ask_liquidity_usd":{"type":["number","null"],"format":"double"},"bid_levels":{"type":["integer","null"],"format":"int32"},"ask_levels":{"type":["integer","null"],"format":"int32"}}}}}}},"400":{"description":"Missing or invalid parameters"}}}},"/polymarket/search":{"get":{"tags":["Search"],"summary":"Search events, markets, and traders","description":"Search across markets, events, and traders. Use `type` to limit which categories are searched. Trader search supports wallet address lookup or name search. Results for each category are independently paginated. Only requested categories are included in the response.","operationId":"search","parameters":[{"name":"q","in":"query","description":"Search query (min 2 characters). Prefix with 0x for exact wallet address lookup.","required":true,"schema":{"type":"string"}},{"name":"type","in":"query","description":"Comma-separated categories to search: events, markets, traders (default: all three, 1 credit per type). Example: type=markets,traders","required":false,"schema":{"type":"string"}},{"name":"include_pnl","in":"query","description":"Include lifetime PnL summary for each trader (default: false, +1 credit)","required":false,"schema":{"type":"boolean"}},{"name":"sort_by","in":"query","description":"Sort field applied to both events and markets (default: volume). Fields marked events-only or markets-only fall back to volume on the other category.","required":false,"schema":{"$ref":"#/components/schemas/SearchSortBy"}},{"name":"sort_dir","in":"query","description":"Sort direction (default: desc)","required":false,"schema":{"$ref":"#/components/schemas/SortDirection"}},{"name":"timeframe","in":"query","description":"Metrics timeframe used for volume/txns/unique_traders sort (default: 24h)","required":false,"schema":{"$ref":"#/components/schemas/MetricsTimeframe"}},{"name":"limit","in":"query","description":"Results limit per category (default: 10, max: 250)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"events_pagination_key","in":"query","description":"Cursor for the next page of events, obtained from previous response's events_pagination.pagination_key","required":false,"schema":{"type":"string"}},{"name":"markets_pagination_key","in":"query","description":"Cursor for the next page of markets, obtained from previous response's markets_pagination.pagination_key","required":false,"schema":{"type":"string"}},{"name":"traders_pagination_key","in":"query","description":"Cursor for the next page of traders, obtained from previous response's traders_pagination.pagination_key","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Search results. Only requested categories (via `type`) are included in the response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"400":{"description":"Bad request — q is missing or shorter than 2 characters"}}}},"/polymarket/series":{"get":{"tags":["Series"],"summary":"List or get series","description":"Retrieve series. Use `id` or `series_slug` for single lookup, `series_ids` or `series_slugs` (comma-separated, max 250, mutually exclusive) for multi-lookup, or paginate with `active_only`.","operationId":"get_series_list","parameters":[{"name":"id","in":"query","description":"Single series ID for direct lookup","required":false,"schema":{"type":"string"}},{"name":"series_ids","in":"query","description":"Comma-separated series IDs (max 250). Mutually exclusive with series_slugs.","required":false,"schema":{"type":"string"}},{"name":"series_slugs","in":"query","description":"Comma-separated series slugs (max 250). Mutually exclusive with series_ids.","required":false,"schema":{"type":"string"}},{"name":"active_only","in":"query","description":"Only active series (default: true). Ignored when series_ids or series_slugs are provided.","required":false,"schema":{"type":"boolean"}},{"name":"limit","in":"query","description":"Results limit (default: 10, max: 250)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Cursor for pagination: id of the last series from the previous page","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Series or list of series","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PolymarketSeries"}}}}},"400":{"description":"series_ids and series_slugs cannot both be provided"}}}},"/polymarket/series/outcomes":{"get":{"tags":["Series"],"summary":"Get series market outcomes","description":"Returns the winning outcome name for each resolved market across all events in a series, keyed by market slug. Useful for checking historical results across recurring series (e.g., btc-updown-5m).","operationId":"get_series_outcomes","parameters":[{"name":"series_slug","in":"query","description":"Series slug (required)","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","description":"Number of resolved markets per page (default: 10, max: 250)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key for fetching next page","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Map of market_slug → winning outcome name with pagination metadata","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}}}}},"400":{"description":"Missing series_slug parameter"}}}},"/polymarket/tags":{"get":{"tags":["Tags"],"summary":"Get tags","description":"Retrieve all available event tags/categories. Uses cursor-based pagination for efficient traversal.","operationId":"get_tags","parameters":[{"name":"limit","in":"query","description":"Results limit (default: 10, max: 250)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"List of all tags","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PolymarketTag"}}}}}}}},"/polymarket/tags/{identifier}":{"get":{"tags":["Tags"],"summary":"Get tag by slug","description":"Retrieve a single tag by its ID or slug","operationId":"get_tag_by_id","parameters":[{"name":"identifier","in":"path","description":"Tag ID or slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Tag details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolymarketTag"}}}},"404":{"description":"Tag not found"}}}},"/polymarket/trader/global_pnl":{"get":{"tags":["Trader"],"summary":"Get global leaderboard","description":"Retrieve the global PnL leaderboard ranked by profit and loss. Uses cursor-based pagination for efficient traversal of large datasets.","operationId":"get_global_pnl","parameters":[{"name":"timeframe","in":"query","description":"Timeframe: 1d, 7d, 30d, lifetime (default: lifetime)","required":false,"schema":{"$ref":"#/components/schemas/PnlTimeframe"}},{"name":"sort_by","in":"query","description":"Sort: realized_pnl_usd, buys, sells, redemptions, merges, avg_hold_time, markets_traded, events_traded, markets_won, volume_usd, fees, best_trade (default: realized_pnl_usd)","required":false,"schema":{"$ref":"#/components/schemas/GlobalPnlSortBy"}},{"name":"sort_direction","in":"query","description":"Sort direction: asc, desc (default: desc)","required":false,"schema":{"$ref":"#/components/schemas/SortDirection"}},{"name":"limit","in":"query","description":"Results limit (default: 10, max: 200)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Global PnL leaderboard with trader stats. Response includes `pagination: { has_more, pagination_key }` for cursor-based pagination.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GlobalPnlTrader"}}}}}}}},"/polymarket/trader/pnl/{address}":{"get":{"tags":["Trader"],"summary":"Get trader PnL summary","description":"Retrieve a trader's overall profit and loss summary","operationId":"get_trader_pnl","parameters":[{"name":"address","in":"path","description":"Trader wallet address","required":true,"schema":{"type":"string"}},{"name":"timeframe","in":"query","description":"Timeframe: 1d, 7d, 30d, lifetime (default: lifetime)","required":false,"schema":{"$ref":"#/components/schemas/PnlTimeframe"}}],"responses":{"200":{"description":"Trader's global PnL summary","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TraderPnlSummary"}}}}}}},"/polymarket/trader/pnl/{address}/calendar":{"get":{"tags":["Trader"],"summary":"Get trader PnL calendar","description":"Retrieve raw per-day PnL. Each entry is the realized PnL for that calendar day. Paginate backwards using the pagination key.","operationId":"get_trader_pnl_calendar","parameters":[{"name":"address","in":"path","description":"Trader wallet address","required":true,"schema":{"type":"string"}},{"name":"days","in":"query","description":"Window size in days (default: 30, max: 30)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Pagination key obtained from a previous response to fetch an earlier window","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Daily PnL entries (one per active trading day)","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PnlCandleEntry"}}}}}}}},"/polymarket/trader/pnl/{address}/candles":{"get":{"tags":["Trader"],"summary":"Get trader PnL candles","description":"Retrieve PnL candles for charting a trader's equity curve over time.","operationId":"get_trader_pnl_candles","parameters":[{"name":"address","in":"path","description":"Trader wallet address","required":true,"schema":{"type":"string"}},{"name":"resolution","in":"query","description":"Candle resolution (default: 1h)","required":false,"schema":{"$ref":"#/components/schemas/PnlCandleResolution"}},{"name":"timeframe","in":"query","description":"Time range (default: lifetime)","required":false,"schema":{"$ref":"#/components/schemas/PnlCandleTimeframe"}}],"responses":{"200":{"description":"Cumulative PnL candles","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PnlCandleEntry"}}}}}}}},"/polymarket/trader/pnl/{address}/events":{"get":{"tags":["Trader"],"summary":"Get trader event PnL","description":"Retrieve event-level PnL breakdown for a trader","operationId":"get_trader_event_pnl","parameters":[{"name":"address","in":"path","description":"Trader wallet address","required":true,"schema":{"type":"string"}},{"name":"timeframe","in":"query","description":"Timeframe: 1d, 7d, 30d, lifetime (default: lifetime)","required":false,"schema":{"$ref":"#/components/schemas/PnlTimeframe"}},{"name":"sort_by","in":"query","description":"Sort: realized_pnl_usd, total_volume_usd, markets_traded, total_fees (default: realized_pnl_usd)","required":false,"schema":{"$ref":"#/components/schemas/EventPnlSortBy"}},{"name":"sort_direction","in":"query","description":"Sort direction: asc, desc (default: desc)","required":false,"schema":{"$ref":"#/components/schemas/SortDirection"}},{"name":"limit","in":"query","description":"Results limit (default: 10, max: 200)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"offset","in":"query","description":"Pagination offset (number of results to skip). Takes precedence over pagination_key.","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key obtained from previous response's pagination.pagination_key","required":false,"schema":{"type":"string"}},{"name":"event_slug","in":"query","description":"Filter by event slug","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Event-level PnL entries for this trader","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TraderEventPnlEntry"}}}}}}}},"/polymarket/trader/pnl/{address}/markets":{"get":{"tags":["Trader"],"summary":"Get trader market PnL","description":"Retrieve market-level PnL breakdown for a trader","operationId":"get_trader_market_pnl","parameters":[{"name":"address","in":"path","description":"Trader wallet address","required":true,"schema":{"type":"string"}},{"name":"timeframe","in":"query","description":"Timeframe: 1d, 7d, 30d, lifetime (default: lifetime)","required":false,"schema":{"$ref":"#/components/schemas/PnlTimeframe"}},{"name":"sort_by","in":"query","description":"Sort: realized_pnl_usd, buy_usd, total_buys, total_fees, outcomes_traded (default: realized_pnl_usd)","required":false,"schema":{"$ref":"#/components/schemas/MarketPnlSortBy"}},{"name":"sort_direction","in":"query","description":"Sort direction: asc, desc (default: desc)","required":false,"schema":{"$ref":"#/components/schemas/SortDirection"}},{"name":"limit","in":"query","description":"Results limit (default: 10, max: 200)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"offset","in":"query","description":"Pagination offset (number of results to skip). Takes precedence over pagination_key.","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key obtained from previous response's pagination.pagination_key","required":false,"schema":{"type":"string"}},{"name":"condition_id","in":"query","description":"Filter by condition ID (or use market_slug)","required":false,"schema":{"type":"string"}},{"name":"market_slug","in":"query","description":"Filter by market slug (alternative to condition_id)","required":false,"schema":{"type":"string"}},{"name":"event_slug","in":"query","description":"Filter by event slug","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Market-level PnL entries for this trader","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TraderMarketPnlEntry"}}}}}}}},"/polymarket/trader/pnl/{address}/positions":{"get":{"tags":["Trader"],"summary":"Get trader position PnL","description":"Retrieve per-outcome-token lifetime PnL for a trader from polymarket_accounts. Includes open/closed state, win/loss outcome, redemption payouts, and share quantities. Filter by status and won/lost to segment portfolio views.","operationId":"get_trader_position_pnl","parameters":[{"name":"address","in":"path","description":"Trader wallet address","required":true,"schema":{"type":"string"}},{"name":"status","in":"query","description":"Required. Filter by position status","required":true,"schema":{"type":"string","enum":["open","closed"]}},{"name":"won","in":"query","description":"Filter by outcome: true (won), false (lost), only for status=closed","required":false,"schema":{"type":"boolean"}},{"name":"search","in":"query","description":"Search by market title","required":false,"schema":{"type":"string"}},{"name":"sort_by","in":"query","description":"Sort field (default: realized_pnl_usd)","required":false,"schema":{"$ref":"#/components/schemas/PositionPnlSortBy"}},{"name":"sort_direction","in":"query","description":"Sort direction: asc, desc (default: desc)","required":false,"schema":{"$ref":"#/components/schemas/SortDirection"}},{"name":"limit","in":"query","description":"Results limit (default: 10, max: 200)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"offset","in":"query","description":"Pagination offset","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"condition_id","in":"query","description":"Filter by market condition ID (or use market_slug)","required":false,"schema":{"type":"string"}},{"name":"market_slug","in":"query","description":"Filter by market slug (alternative to condition_id)","required":false,"schema":{"type":"string"}},{"name":"position_id","in":"query","description":"Filter by specific outcome token (position ID)","required":false,"schema":{"type":"string"}},{"name":"min_shares","in":"query","description":"Minimum shares balance to include. Status filtering already treats balances below 0.01 shares as dust.","required":false,"schema":{"type":"number","format":"double"}}],"responses":{"200":{"description":"Position-level PnL entries for this trader. Each entry is a specific outcome token with open/closed state, avg_entry_price, avg_exit_price, and redemption info.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TraderOutcomePnlEntry"}}}}}}}},"/polymarket/trader/profile/{address}":{"get":{"tags":["Trader"],"summary":"Get trader overview","description":"Retrieve a trader's profile including stats and trading history summary","operationId":"get_trader_profile","parameters":[{"name":"address","in":"path","description":"Trader wallet address","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Trader profile information with username, bio, and badges","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolymarketUserProfile"}}}}}}},"/polymarket/trader/profiles/batch":{"get":{"tags":["Trader"],"summary":"Get multiple trader profiles","description":"Retrieve profiles for multiple traders in a single request. Returns an array of profiles.","operationId":"get_trader_profiles_batch","parameters":[{"name":"addresses","in":"query","description":"Comma-separated list of trader addresses (max: 20)","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Array of trader profiles","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PolymarketUserProfile"}}}}},"400":{"description":"Invalid request (empty addresses or more than 20)"}}}},"/polymarket/trader/trades/{address}":{"get":{"tags":["Trader"],"summary":"Get trader trades","description":"Retrieve trade history for a specific trader across all markets","operationId":"get_trader_trades","parameters":[{"name":"address","in":"path","description":"Trader wallet address","required":true,"schema":{"type":"string"}},{"name":"condition_ids","in":"query","description":"Comma-separated condition IDs (max 20)","required":false,"schema":{"type":"string"}},{"name":"slugs","in":"query","description":"Comma-separated market slugs","required":false,"schema":{"type":"string"}},{"name":"position_ids","in":"query","description":"Comma-separated position IDs","required":false,"schema":{"type":"string"}},{"name":"side","in":"query","description":"Trade side: 0 (Buy), 1 (Sell)","required":false,"schema":{"$ref":"#/components/schemas/TradeSide"}},{"name":"outcome","in":"query","description":"Outcome name filter (e.g. Yes, No)","required":false,"schema":{"type":"string"}},{"name":"outcome_index","in":"query","description":"Outcome index: 0 (Yes), 1 (No)","required":false,"schema":{"$ref":"#/components/schemas/OutcomeIndex"}},{"name":"trade_types","in":"query","description":"Comma-separated trade types: OrderFilled, Redemption, Merge, Split, Cancelled, PositionsConverted, OrdersMatched","required":false,"schema":{"type":"string"}},{"name":"min_usd_amount","in":"query","description":"Min USD amount","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_usd_amount","in":"query","description":"Max USD amount","required":false,"schema":{"type":"number","format":"double"}},{"name":"min_shares_amount","in":"query","description":"Min shares amount","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_shares_amount","in":"query","description":"Max shares amount","required":false,"schema":{"type":"number","format":"double"}},{"name":"min_price","in":"query","description":"Min price (0.0-1.0)","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_price","in":"query","description":"Max price (0.0-1.0)","required":false,"schema":{"type":"number","format":"double"}},{"name":"from","in":"query","description":"Start timestamp (ms)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"End timestamp (ms)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"all","in":"query","description":"Return all-time trades, not just last 30 days (default: false)","required":false,"schema":{"type":"boolean"}},{"name":"limit","in":"query","description":"Results per page (default: 10, max: 250)","required":false,"schema":{"type":"integer","format":"int32","minimum":0}},{"name":"offset","in":"query","description":"Pagination offset (number of results to skip). Takes precedence over pagination_key.","required":false,"schema":{"type":"integer","format":"int32","minimum":0}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key obtained from previous response's pagination.pagination_key","required":false,"schema":{"type":"string"}},{"name":"sort_desc","in":"query","description":"Sort newest first (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"ai","in":"query","description":"Return truncated response optimized for AI consumers (default: false)","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Trader's trade history with advanced filtering (same as /market/trades but filtered by trader)","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TradeEvent"}}}}}}}},"/polymarket/trader/volume-chart/{address}":{"get":{"tags":["Trader"],"summary":"Get trader volume chart","description":"Retrieve volume breakdown by buy/sell over time for a specific trader","operationId":"get_trader_volume_chart","parameters":[{"name":"address","in":"path","description":"Trader wallet address","required":true,"schema":{"type":"string"}},{"name":"resolution","in":"query","description":"Time interval: 1, 5, 15, 30, 60, 240, D, 1D (default: 60)","required":false,"schema":{"$ref":"#/components/schemas/CandlestickResolution"}},{"name":"count_back","in":"query","description":"Number of data points (max: 2500)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"from","in":"query","description":"Start timestamp (Unix seconds)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"End timestamp (Unix seconds)","required":false,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"Trader volume with buy/sell breakdown over time","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TraderVolumeDataPoint"}}}}}}}}},"components":{"schemas":{"ApprovalTrade":{"type":"object","description":"Output payload for ERC1155 setApprovalForAll events.","required":["id","hash","block","confirmed_at","log_index","block_index","trader","operator","approved","exchange"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":"integer","format":"int64","minimum":0},"confirmed_at":{"type":"integer","format":"int64","minimum":0},"log_index":{"type":"integer","format":"int64","minimum":0},"block_index":{"type":"integer","format":"int64","minimum":0},"trader":{"$ref":"#/components/schemas/TraderInfo"},"operator":{"type":"string"},"approved":{"type":"boolean"},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]},"exchange":{"$ref":"#/components/schemas/PolymarketExchange"}}},"AssertionDisputedEvent":{"type":"object","description":"V3 UMA OOv3: an assertion was disputed.","required":["id","hash","block","confirmed_at","log_index","block_index","oracle_contract","assertion_id","caller","disputer"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":"integer","format":"int64","minimum":0},"confirmed_at":{"type":"integer","format":"int64","minimum":0},"log_index":{"type":"integer","format":"int64","minimum":0},"block_index":{"type":"integer","format":"int64","minimum":0},"oracle_contract":{"type":"string"},"assertion_id":{"type":"string"},"caller":{"type":"string"},"disputer":{"type":"string"},"condition_id":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"AssertionMadeEvent":{"type":"object","description":"V3 UMA OOv3: a new assertion (resolution proposal) was made.","required":["id","hash","block","confirmed_at","log_index","block_index","oracle_contract","assertion_id","domain_id","claim","asserter","callback_recipient","escalation_manager","caller","expiration_time","currency","bond","identifier"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":"integer","format":"int64","minimum":0},"confirmed_at":{"type":"integer","format":"int64","minimum":0},"log_index":{"type":"integer","format":"int64","minimum":0},"block_index":{"type":"integer","format":"int64","minimum":0},"oracle_contract":{"type":"string"},"assertion_id":{"type":"string"},"domain_id":{"type":"string"},"claim":{"type":"string"},"asserter":{"type":"string"},"callback_recipient":{"type":"string"},"escalation_manager":{"type":"string"},"caller":{"type":"string"},"expiration_time":{"type":"integer","format":"int64","minimum":0},"currency":{"type":"string"},"bond":{"type":"string"},"identifier":{"type":"string"},"condition_id":{"type":["string","null"]},"proposed_outcome":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"AssertionSettledEvent":{"type":"object","description":"V3 UMA OOv3: an assertion liveness period expired and was settled.","required":["id","hash","block","confirmed_at","log_index","block_index","oracle_contract","assertion_id","bond_recipient","disputed","settlement_resolution","settle_caller"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":"integer","format":"int64","minimum":0},"confirmed_at":{"type":"integer","format":"int64","minimum":0},"log_index":{"type":"integer","format":"int64","minimum":0},"block_index":{"type":"integer","format":"int64","minimum":0},"oracle_contract":{"type":"string"},"assertion_id":{"type":"string"},"bond_recipient":{"type":"string"},"disputed":{"type":"boolean"},"settlement_resolution":{"type":"boolean"},"settle_caller":{"type":"string"},"condition_id":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"AssetPriceHistoryRow":{"type":"object","description":"A single asset price history record from the `asset_price_history` table.","required":["asset_symbol","asset_open_price","asset_close_price","variant","start_time","end_time"],"properties":{"asset_symbol":{"type":"string"},"asset_open_price":{"type":"number","format":"double","description":"Opening price at start_time (cast to f64 from NUMERIC in query)"},"asset_close_price":{"type":"number","format":"double","description":"Closing price at end_time (cast to f64 from NUMERIC in query)"},"price_change_percentage":{"type":["number","null"],"format":"double"},"outcome":{"type":["string","null"],"description":"Generated column: \"up\" if close > open, \"down\" if close ≤ open, null if no close yet"},"variant":{"type":"string","description":"Time window: \"5m\", \"15m\", \"1h\", \"1d\""},"start_time":{"type":"integer","format":"int64","description":"Window start timestamp (seconds since epoch)"},"end_time":{"type":"integer","format":"int64","description":"Window end timestamp (seconds since epoch)"}}},"AssetSymbol":{"type":"string","enum":["BTC","ETH","XRP","SOL","DOGE","BNB","HYPE"]},"AssetVariant":{"type":"string","enum":["5m","15m","1h","4h","1d"]},"BondMarket":{"type":"object","required":["condition_id","question","market_slug","end_time","best_outcome_index","return_pct","apy","outcomes"],"properties":{"condition_id":{"type":"string"},"title":{"type":["string","null"]},"question":{"type":"string"},"market_slug":{"type":"string"},"event_slug":{"type":["string","null"]},"image_url":{"type":["string","null"]},"end_time":{"type":"integer","format":"int64"},"best_outcome_index":{"type":"integer","format":"int32","minimum":0},"return_pct":{"type":"number","format":"double"},"apy":{"type":"number","format":"double"},"volume_24h":{"type":["number","null"],"format":"double"},"outcomes":{"type":"array","items":{"$ref":"#/components/schemas/BondOutcome"}}}},"BondOutcome":{"type":"object","required":["name","index","position_id","price"],"properties":{"name":{"type":"string"},"index":{"type":"integer","format":"int32"},"position_id":{"type":"string"},"price":{"type":"number","format":"double"}}},"CancelledTrade":{"type":"object","description":"Output payload for Cancelled orders.","required":["id","hash","block","confirmed_at","log_index","block_index","order_hash","exchange"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":"integer","format":"int64","minimum":0},"confirmed_at":{"type":"integer","format":"int64","minimum":0},"log_index":{"type":"integer","format":"int64","minimum":0},"block_index":{"type":"integer","format":"int64","minimum":0},"order_hash":{"type":"string"},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]},"exchange":{"$ref":"#/components/schemas/PolymarketExchange"}}},"CandlestickResolution":{"type":"string","enum":["1","5","15","30","60","240","D","1D"]},"ChartResolution":{"type":"string","enum":["1H","6H","1D","1W","1M","ALL"]},"ClobReward":{"type":"object","description":"CLOB reward (public API format)","required":["id","condition_id"],"properties":{"id":{"type":"string"},"condition_id":{"type":"string"},"asset_address":{"type":["string","null"]},"rewards_amount":{"type":["number","null"],"format":"double"},"rewards_daily_rate":{"type":["number","null"],"format":"double"},"start_date":{"type":["string","null"]},"end_date":{"type":["string","null"]},"rewards_max_spread":{"type":["number","null"],"format":"double"},"rewards_min_size":{"type":["number","null"],"format":"double"},"native_daily_rate":{"type":["number","null"],"format":"double"},"sponsored_daily_rate":{"type":["number","null"],"format":"double"},"total_daily_rate":{"type":["number","null"],"format":"double"},"sponsors_count":{"type":["integer","null"],"format":"int32"}}},"ConditionMetricsResponse":{"type":"object","description":"Response type for condition metrics query","required":["condition_id","timeframe","volume_usd","fees","txns","unique_traders"],"properties":{"condition_id":{"type":"string"},"timeframe":{"type":"string"},"volume_usd":{"type":"number","format":"double"},"fees":{"type":"number","format":"double"},"txns":{"type":"integer","format":"int32"},"unique_traders":{"type":"integer","format":"int32"}}},"ConditionOrderbookRow":{"type":"object","required":["ts","position_id","condition_id","bids","asks","hash"],"properties":{"ts":{"type":"integer","format":"int64"},"position_id":{"type":"string"},"condition_id":{"type":"string"},"bids":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"asks":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"hash":{"type":"string"},"best_bid":{"type":["number","null"],"format":"double"},"best_ask":{"type":["number","null"],"format":"double"},"mid_price":{"type":["number","null"],"format":"double"},"spread":{"type":["number","null"],"format":"double"},"bid_liquidity_usd":{"type":["number","null"],"format":"double"},"ask_liquidity_usd":{"type":["number","null"],"format":"double"},"bid_levels":{"type":["integer","null"],"format":"int32"},"ask_levels":{"type":["integer","null"],"format":"int32"}}},"ConditionResolutionEvent":{"type":"object","description":"CTF ConditionResolution: positions become redeemable on the Conditional Tokens contract.","required":["id","hash","block","confirmed_at","log_index","block_index","oracle_contract","condition_id","oracle"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":"integer","format":"int64","minimum":0},"confirmed_at":{"type":"integer","format":"int64","minimum":0},"log_index":{"type":"integer","format":"int64","minimum":0},"block_index":{"type":"integer","format":"int64","minimum":0},"oracle_contract":{"type":"string"},"condition_id":{"type":"string"},"oracle":{"type":"string"},"proposed_outcome":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"EventMarket":{"type":"object","description":"Enriched market data for event API responses","properties":{"condition_id":{"type":"string","default":""},"id":{"type":["string","null"],"default":null},"title":{"type":["string","null"],"default":null},"question":{"type":"string","default":""},"market_slug":{"type":"string","default":""},"status":{"type":"string","default":""},"created_time":{"type":["integer","null"],"format":"int64","default":null},"end_time":{"type":["integer","null"],"format":"int64","default":null},"volume":{"type":["number","null"],"format":"double","default":null},"liquidity_usd":{"type":["number","null"],"format":"double","default":null},"volume_24hr":{"type":["number","null"],"format":"double","default":null},"image_url":{"type":["string","null"],"default":null},"market_maker_address":{"type":["string","null"],"default":null},"creator":{"type":["string","null"],"default":null},"category":{"type":["string","null"],"default":null},"accepting_orders":{"type":["boolean","null"],"default":null},"uma_resolution_status":{"type":["string","null"],"default":null},"clob_rewards":{"type":"array","items":{"$ref":"#/components/schemas/ClobReward"},"default":[]},"outcomes":{"type":"array","items":{"$ref":"#/components/schemas/EventMarketOutcome"},"default":[]},"winning_outcome":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/EventMarketOutcome"}],"default":null}}},"EventMarketChartDataPoint":{"type":"object","required":["v","t"],"properties":{"v":{"type":"number","format":"double"},"t":{"type":"integer","format":"int64","minimum":0}}},"EventMarketChartOutcome":{"type":"object","required":["condition_id","market_slug","question","title","data"],"properties":{"condition_id":{"type":"string"},"market_slug":{"type":"string"},"question":{"type":"string"},"title":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/EventMarketChartDataPoint"}}}},"EventMarketOutcome":{"type":"object","description":"Market outcome for event API responses","required":["name"],"properties":{"name":{"type":"string"},"price":{"type":["number","null"],"format":"double"},"position_id":{"type":["string","null"]},"outcome_index":{"type":["integer","null"],"format":"int32"}}},"EventMetricsResponse":{"type":"object","description":"Response type for event metrics query","required":["event_slug","timeframe","volume_usd","fees","txns","unique_traders"],"properties":{"event_slug":{"type":"string"},"timeframe":{"type":"string"},"volume_usd":{"type":"number","format":"double"},"fees":{"type":"number","format":"double"},"txns":{"type":"integer","format":"int32"},"unique_traders":{"type":"integer","format":"int32"}}},"EventPnlSortBy":{"type":"string","enum":["realized_pnl_usd","total_volume_usd","markets_traded","total_fees","realized_pnl_pct"]},"EventSortBy":{"type":"string","enum":["volume","txns","unique_traders","title","creation_date","start_date","end_date","relevance"]},"GlobalPnlSortBy":{"type":"string","enum":["realized_pnl_usd","buys","sells","redemptions","merges","avg_hold_time","markets_traded","events_traded","markets_won","volume_usd","fees","best_trade"]},"GlobalPnlTrader":{"type":"object","description":"Individual trader entry in the global PnL leaderboard","required":["trader"],"properties":{"trader":{"$ref":"#/components/schemas/TraderInfo"},"realized_pnl_usd":{"type":["number","null"],"format":"double"},"events_traded":{"type":["integer","null"],"format":"int64"},"markets_traded":{"type":["integer","null"],"format":"int64"},"markets_won":{"type":["integer","null"],"format":"int64"},"markets_lost":{"type":["integer","null"],"format":"int64"},"market_win_rate_pct":{"type":["number","null"],"format":"double"},"total_volume_usd":{"type":["number","null"],"format":"double"},"buy_volume_usd":{"type":["number","null"],"format":"double"},"sell_volume_usd":{"type":["number","null"],"format":"double"},"redemption_volume_usd":{"type":["number","null"],"format":"double"},"merge_volume_usd":{"type":["number","null"],"format":"double"},"total_buys":{"type":["integer","null"],"format":"int64"},"total_sells":{"type":["integer","null"],"format":"int64"},"total_redemptions":{"type":["integer","null"],"format":"int64"},"total_merges":{"type":["integer","null"],"format":"int64"},"total_trades":{"type":["integer","null"],"format":"int64"},"total_fees":{"type":["number","null"],"format":"double"},"avg_pnl_per_market":{"type":["number","null"],"format":"double"},"avg_pnl_per_trade":{"type":["number","null"],"format":"double"},"avg_hold_time_seconds":{"type":["number","null"],"format":"double"},"best_trade_pnl_usd":{"type":["number","null"],"format":"double"},"best_trade_condition_id":{"type":["string","null"]},"first_trade_at":{"type":["integer","null"],"format":"int64"},"last_trade_at":{"type":["integer","null"],"format":"int64"}}},"Holder":{"type":"object","description":"Individual holder data","required":["trader","shares"],"properties":{"trader":{"$ref":"#/components/schemas/Trader","description":"Trader profile information"},"shares":{"type":"string","description":"Position shares held (raw balance as string for precision)"},"shares_usd":{"type":["string","null"],"description":"USD value of shares (shares * latest price)"},"usd_balance":{"type":["string","null"],"description":"USD balance of wallet (USDe on Polygon)"},"pnl":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/HolderPnl","description":"Holder-level PnL, included only when `include_pnl=true`"}]}}},"HolderHistoryCandle":{"type":"object","description":"Holder statistics data point (single time bucket)","required":["t"],"properties":{"t":{"type":"integer","format":"int64"},"h":{"type":["integer","null"],"format":"int64"}}},"HolderPnl":{"type":"object","description":"Holder-level PnL data, included when `include_pnl=true`","properties":{"avg_entry_price":{"type":["number","null"],"format":"double"},"total_cost_usd":{"type":["string","null"]},"unrealized_pnl_usd":{"type":["string","null"]},"realized_sell_pnl_usd":{"type":["string","null"]},"avg_exit_price":{"type":["number","null"],"format":"double"},"buy_usd":{"type":["number","null"],"format":"double"},"sell_usd":{"type":["number","null"],"format":"double"},"total_buys":{"type":["integer","null"],"format":"int64"},"total_sells":{"type":["integer","null"],"format":"int64"},"total_fees":{"type":["number","null"],"format":"double"},"first_trade_at":{"type":["integer","null"],"format":"int64"},"last_trade_at":{"type":["integer","null"],"format":"int64"}}},"MarketHoldersResponse":{"type":"object","description":"Response for market (condition_id) holders endpoint","required":["condition_id","total_holders","outcomes"],"properties":{"condition_id":{"type":"string","description":"Market condition ID"},"question":{"type":["string","null"],"description":"Market question/title"},"slug":{"type":["string","null"],"description":"Market slug"},"total_holders":{"type":"integer","format":"int64","description":"Total unique holders across all outcomes (from holder_stats)"},"outcomes":{"type":"array","items":{"$ref":"#/components/schemas/OutcomeHolders"},"description":"Holders grouped by outcome"}}},"MarketMetadataOutcome":{"type":"object","description":"Market outcome with timeframe metrics (websocket API format)","required":["token_id","outcome"],"properties":{"token_id":{"type":"string"},"outcome":{"type":"string"},"last_price":{"type":["number","null"],"format":"double"},"last_probability":{"type":["number","null"],"format":"double"},"metrics":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/OutcomeTimeframeMetrics"},"propertyNames":{"type":"string"}}}},"MarketOutcome":{"type":"object","description":"Outcome for market API responses","properties":{"name":{"type":"string","default":""},"price":{"type":["number","null"],"format":"double","default":null},"position_id":{"type":["string","null"],"default":null},"outcome_index":{"type":["integer","null"],"format":"int32","default":null}}},"MarketPnlSortBy":{"type":"string","enum":["realized_pnl_usd","buy_usd","total_buys","total_fees","outcomes_traded","realized_pnl_pct"]},"MarketResponse":{"type":"object","description":"Formatted market response with structured metrics, tags, outcomes, and event","required":["condition_id","status"],"properties":{"condition_id":{"type":"string"},"id":{"type":["string","null"]},"market_slug":{"type":["string","null"]},"question":{"type":["string","null"]},"title":{"type":["string","null"]},"description":{"type":["string","null"]},"image_url":{"type":["string","null"]},"oracle":{"type":["string","null"]},"status":{"type":"string"},"created_time":{"type":["integer","null"],"format":"int64"},"start_time":{"type":["integer","null"],"format":"int64"},"game_start_time":{"type":["integer","null"],"format":"int64"},"closed_time":{"type":["integer","null"],"format":"int64"},"end_time":{"type":["integer","null"],"format":"int64"},"accepting_orders":{"type":["boolean","null"]},"uma_resolution_status":{"type":["string","null"]},"is_neg_risk":{"type":["boolean","null"]},"market_maker_address":{"type":["string","null"]},"creator":{"type":["string","null"]},"category":{"type":["string","null"]},"volume_usd":{"type":["number","null"],"format":"double"},"liquidity_usd":{"type":["number","null"],"format":"double"},"highest_probability":{"type":["number","null"],"format":"double"},"total_holders":{"type":["integer","null"],"format":"int64"},"total_daily_rate":{"type":["number","null"],"format":"double"},"winning_outcome":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/MarketOutcome"}]},"outcomes":{"type":"array","items":{"$ref":"#/components/schemas/MarketOutcome"}},"clob_rewards":{"type":"array","items":{"$ref":"#/components/schemas/ClobReward"}},"tags":{"type":"array","items":{"type":"string"}},"event_slug":{"type":["string","null"]},"resolution_source":{"type":["string","null"]},"metrics":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/SimpleTimeframeMetrics"},"propertyNames":{"type":"string"}},"relevance_score":{"type":["number","null"],"format":"double"}}},"MarketSortBy":{"type":"string","enum":["volume","txns","unique_traders","liquidity","holders","total_daily_rate","end_time","start_time","created_time","relevance"]},"MarketStatus":{"type":"string","enum":["open","closed","all"]},"MarketVolumeChartResponse":{"type":"object","required":["volumes","has_more"],"properties":{"volumes":{"type":"array","items":{"$ref":"#/components/schemas/MarketVolumeDataPoint"}},"has_more":{"type":"boolean"}}},"MarketVolumeDataPoint":{"type":"object","required":["t","v","yv","nv","tc","ytc","ntc"],"properties":{"t":{"type":"integer","format":"int64"},"v":{"type":"number","format":"double"},"yv":{"type":"number","format":"double"},"nv":{"type":"number","format":"double"},"tc":{"type":"integer","format":"int32"},"ytc":{"type":"integer","format":"int32"},"ntc":{"type":"integer","format":"int32"}}},"MergeTrade":{"type":"object","description":"Output payload for Merge trades (burn outcome tokens → receive collateral).","required":["id","hash","block","confirmed_at","log_index","block_index","trader","usd_amount","exchange"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":"integer","format":"int64","minimum":0},"confirmed_at":{"type":"integer","format":"int64","minimum":0},"log_index":{"type":"integer","format":"int64","minimum":0},"block_index":{"type":"integer","format":"int64","minimum":0},"trader":{"$ref":"#/components/schemas/TraderInfo"},"condition_id":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]},"usd_amount":{"type":"number","format":"double"},"position_details":{"type":"array","items":{"$ref":"#/components/schemas/PositionDetail"},"description":"Per-position burn amounts"},"exchange":{"$ref":"#/components/schemas/PolymarketExchange"}}},"MetricsTimeframe":{"type":"string","enum":["1m","5m","30m","1h","6h","24h","7d","30d"]},"NegRiskOutcomeReportedEvent":{"type":"object","description":"NegRisk Adapter: outcome reported for a neg-risk market question.","required":["id","hash","block","confirmed_at","log_index","block_index","oracle_contract","condition_id"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":"integer","format":"int64","minimum":0},"confirmed_at":{"type":"integer","format":"int64","minimum":0},"log_index":{"type":"integer","format":"int64","minimum":0},"block_index":{"type":"integer","format":"int64","minimum":0},"oracle_contract":{"type":"string"},"condition_id":{"type":"string"},"proposed_outcome":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"OrderFilledTrade":{"type":"object","description":"Output payload for OrderFilled and OrdersMatched trades (actual buy/sell).","required":["id","hash","block","confirmed_at","log_index","block_index","order_hash","trader","taker","side","position_id","usd_amount","shares_amount","price","fee","fee_shares","fee_pct","exchange"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":"integer","format":"int64","minimum":0},"confirmed_at":{"type":"integer","format":"int64","minimum":0},"log_index":{"type":"integer","format":"int64","minimum":0},"block_index":{"type":"integer","format":"int64","minimum":0},"order_hash":{"type":"string"},"trader":{"$ref":"#/components/schemas/TraderInfo"},"taker":{"type":"string"},"side":{"type":"string"},"condition_id":{"type":["string","null"]},"position_id":{"type":"string"},"outcome":{"type":["string","null"]},"outcome_index":{"type":["integer","null"],"format":"int32","minimum":0},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]},"usd_amount":{"type":"number","format":"double"},"shares_amount":{"type":"number","format":"double"},"price":{"type":"number","format":"double"},"probability":{"type":["number","null"],"format":"double"},"fee":{"type":"number","format":"double"},"fee_shares":{"type":"number","format":"double"},"fee_pct":{"type":"number","format":"double"},"exchange":{"$ref":"#/components/schemas/PolymarketExchange"}}},"OrderbookHistoryRow":{"type":"object","required":["ts","position_id","condition_id","bids","asks","hash"],"properties":{"ts":{"type":"integer","format":"int64"},"position_id":{"type":"string"},"condition_id":{"type":"string"},"bids":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"asks":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"hash":{"type":"string"},"best_bid":{"type":["number","null"],"format":"double"},"best_ask":{"type":["number","null"],"format":"double"},"mid_price":{"type":["number","null"],"format":"double"},"spread":{"type":["number","null"],"format":"double"},"bid_liquidity_usd":{"type":["number","null"],"format":"double"},"ask_liquidity_usd":{"type":["number","null"],"format":"double"},"bid_levels":{"type":["integer","null"],"format":"int32"},"ask_levels":{"type":["integer","null"],"format":"int32"}}},"OrderbookLevel":{"type":"object","description":"A single price level in a bids/asks array.","required":["p","s"],"properties":{"p":{"type":"string","description":"Price as a decimal string"},"s":{"type":"string","description":"Size as a decimal string"}}},"OrderbookSnapshotRow":{"type":"object","required":["ts","position_id","condition_id","bids","asks","hash"],"properties":{"ts":{"type":"integer","format":"int64"},"position_id":{"type":"string"},"condition_id":{"type":"string"},"bids":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"asks":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"hash":{"type":"string"},"best_bid":{"type":["number","null"],"format":"double"},"best_ask":{"type":["number","null"],"format":"double"},"mid_price":{"type":["number","null"],"format":"double"},"spread":{"type":["number","null"],"format":"double"},"bid_liquidity_usd":{"type":["number","null"],"format":"double"},"ask_liquidity_usd":{"type":["number","null"],"format":"double"},"bid_levels":{"type":["integer","null"],"format":"int32"},"ask_levels":{"type":["integer","null"],"format":"int32"}}},"OutcomeHolders":{"type":"object","description":"Holder data grouped by outcome for market-level queries","required":["outcome_index","outcome_name","position_id","total_holders","holders"],"properties":{"outcome_index":{"type":"integer","format":"int32","description":"Outcome index (0, 1, etc.)"},"outcome_name":{"type":"string","description":"Outcome name (Yes, No, etc.)"},"position_id":{"type":"string","description":"Position ID for this outcome"},"price":{"type":["number","null"],"format":"double","description":"Current price/probability"},"total_holders":{"type":"integer","format":"int64","description":"Total holders count from holder_stats"},"holders":{"type":"array","items":{"$ref":"#/components/schemas/Holder"},"description":"Top holders for this outcome"}}},"OutcomeIndex":{"type":"string","enum":["0","1"]},"OutcomeTimeframeMetrics":{"type":"object","properties":{"volume":{"type":"number","format":"double","default":0},"buy_volume":{"type":"number","format":"double","default":0},"sell_volume":{"type":"number","format":"double","default":0},"txns":{"type":"integer","format":"int32","default":0},"buys":{"type":"integer","format":"int32","default":0},"sells":{"type":"integer","format":"int32","default":0},"price_open":{"type":["number","null"],"format":"double","default":null},"price_close":{"type":["number","null"],"format":"double","default":null},"price_high":{"type":["number","null"],"format":"double","default":null},"price_low":{"type":["number","null"],"format":"double","default":null},"probability_open":{"type":["number","null"],"format":"double","default":null},"probability_close":{"type":["number","null"],"format":"double","default":null},"probability_high":{"type":["number","null"],"format":"double","default":null},"probability_low":{"type":["number","null"],"format":"double","default":null},"price_change_percent":{"type":["number","null"],"format":"double","default":null}}},"PaginationMeta":{"type":"object","description":"Pagination metadata to include in API responses","required":["has_more"],"properties":{"has_more":{"type":"boolean","description":"Whether there are more results available"},"pagination_key":{"type":["string","null"],"description":"Pagination key for the next page (if has_more is true)"}}},"PnlCandleEntry":{"type":"object","description":"A single PnL candle entry","required":["t","pnl"],"properties":{"t":{"type":"integer","format":"int64","description":"Timestamp in epoch seconds (start of bucket window)"},"pnl":{"type":"number","format":"double","description":"Realized PnL in this bucket (USD)"}}},"PnlCandleResolution":{"type":"string","enum":["1m","1h","1d"]},"PnlCandleTimeframe":{"type":"string","enum":["1d","7d","30d","lifetime"]},"PnlTimeframe":{"type":"string","enum":["1d","7d","30d","lifetime"]},"PolymarketEvent":{"type":"object","description":"A Polymarket event from the Gamma API","properties":{"id":{"type":"string","default":""},"event_slug":{"type":["string","null"],"default":null},"title":{"type":["string","null"],"default":null},"ticker":{"type":["string","null"],"default":null},"description":{"type":["string","null"],"default":null},"resolution_source":{"type":["string","null"],"default":null},"category":{"type":["string","null"],"default":null},"image_url":{"type":["string","null"],"default":null},"market_count":{"type":"integer","format":"int32","default":0},"created_time":{"type":["integer","null"],"format":"int64","default":null},"closed_time":{"type":["integer","null"],"format":"int64","default":null},"start_time":{"type":["integer","null"],"format":"int64","default":null},"end_time":{"type":["integer","null"],"format":"int64","default":null},"neg_risk":{"type":"boolean","default":false},"neg_risk_market_id":{"type":["string","null"],"default":null},"game_status":{"type":["string","null"],"default":null},"show_market_images":{"type":"boolean","default":false},"status":{"type":["string","null"],"description":"Event status: \"open\" or \"closed\"","default":null},"metrics":{"type":"object","default":{},"additionalProperties":{"$ref":"#/components/schemas/SimpleTimeframeMetrics"},"propertyNames":{"type":"string"}},"tags":{"type":"array","items":{"$ref":"#/components/schemas/PolymarketTag"},"default":[]},"markets":{"type":"array","items":{"$ref":"#/components/schemas/EventMarket"},"default":[]},"series":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PolymarketSeries"}],"default":null}}},"PolymarketExchange":{"type":"string","description":"Polymarket exchange contract types","enum":["CTFExchange","NegRiskExchange","ConditionalTokens","NegRiskAdapter","Unknown"]},"PolymarketSeries":{"type":"object","description":"A Polymarket series from the Gamma API\nSeries are parent groupings above events (e.g., \"NBA Season 2024-25\")","properties":{"id":{"type":"string","default":""},"slug":{"type":["string","null"],"default":null},"ticker":{"type":["string","null"],"default":null},"title":{"type":["string","null"],"default":null},"description":{"type":["string","null"],"default":null},"series_type":{"type":["string","null"],"default":null},"recurrence":{"type":["string","null"],"default":null},"layout":{"type":["string","null"],"default":null},"image_url":{"type":["string","null"],"default":null},"icon_url":{"type":["string","null"],"default":null},"active":{"type":"boolean","default":false},"closed":{"type":"boolean","default":false},"archived":{"type":"boolean","default":false},"featured":{"type":"boolean","default":false},"restricted":{"type":"boolean","default":false},"pyth_token_id":{"type":["string","null"],"default":null},"cg_asset_name":{"type":["string","null"],"default":null},"start_date":{"type":["integer","null"],"format":"int64","default":null},"event_count":{"type":"integer","format":"int32","default":0}}},"PolymarketTag":{"type":"object","description":"A Polymarket tag from the Gamma API","properties":{"id":{"type":"string","default":""},"label":{"type":"string","default":""},"slug":{"type":["string","null"],"default":null}}},"PolymarketUserProfile":{"type":"object","description":"Polymarket user profile (public API format)","required":["proxy_wallet","display_username_public","verified_badge","is_creator","is_mod"],"properties":{"proxy_wallet":{"type":"string"},"name":{"type":["string","null"]},"pseudonym":{"type":["string","null"]},"bio":{"type":["string","null"]},"profile_image":{"type":["string","null"]},"x_username":{"type":["string","null"]},"display_username_public":{"type":"boolean"},"verified_badge":{"type":"boolean"},"user_id":{"type":["string","null"]},"is_creator":{"type":"boolean"},"is_mod":{"type":"boolean"},"created_at":{"type":["string","null"]}}},"PositionChartDataPoint":{"type":"object","required":["v","t"],"properties":{"v":{"type":"number","format":"double"},"t":{"type":"integer","format":"int64","minimum":0}}},"PositionChartOutcome":{"type":"object","required":["position_id","name","outcome_index","data"],"properties":{"position_id":{"type":"string"},"name":{"type":"string"},"outcome_index":{"type":"integer","format":"int32"},"data":{"type":"array","items":{"$ref":"#/components/schemas/PositionChartDataPoint"}}}},"PositionDetail":{"type":"object","description":"Per-position detail for Split/Merge/Redemption trades.","required":["position_id","outcome_index","amount"],"properties":{"position_id":{"type":"string","description":"ERC1155 position ID"},"outcome_index":{"type":"integer","format":"int32","description":"Outcome index (0 = Yes, 1 = No for binary)","minimum":0},"amount":{"type":"string","description":"Amount of shares created/burned/redeemed for this position"}}},"PositionHoldersResponse":{"type":"object","description":"Response for position (position_id) holders endpoint","required":["position_id","total_holders","holders"],"properties":{"position_id":{"type":"string","description":"Position ID (ERC1155 token ID)"},"condition_id":{"type":["string","null"],"description":"Condition ID this position belongs to"},"outcome_name":{"type":["string","null"],"description":"Outcome name"},"outcome_index":{"type":["integer","null"],"format":"int32","description":"Outcome index"},"price":{"type":["number","null"],"format":"double","description":"Current price"},"total_holders":{"type":"integer","format":"int64","description":"Total holders count from holder_stats"},"holders":{"type":"array","items":{"$ref":"#/components/schemas/Holder"},"description":"Top holders"}}},"PositionMetricsResponse":{"type":"object","description":"Response type for position metrics query","required":["position_id","condition_id","timeframe","volume_usd","buy_volume_usd","sell_volume_usd","fees","txns","buys","sells","unique_traders","price_open","price_high","price_low","price_close"],"properties":{"position_id":{"type":"string"},"condition_id":{"type":"string"},"timeframe":{"type":"string"},"volume_usd":{"type":"number","format":"double"},"buy_volume_usd":{"type":"number","format":"double"},"sell_volume_usd":{"type":"number","format":"double"},"fees":{"type":"number","format":"double"},"txns":{"type":"integer","format":"int32"},"buys":{"type":"integer","format":"int32"},"sells":{"type":"integer","format":"int32"},"unique_traders":{"type":"integer","format":"int32"},"price_open":{"type":"number","format":"double"},"price_high":{"type":"number","format":"double"},"price_low":{"type":"number","format":"double"},"price_close":{"type":"number","format":"double"}}},"PositionPnlSortBy":{"type":"string","enum":["realized_pnl_usd","buy_usd","sell_usd","redemption_usd","total_buys","total_sells","total_shares_bought","total_shares_sold","avg_entry_price","avg_exit_price","total_fees","first_trade_at","last_trade_at","current_value","realized_pnl_pct","title"]},"PositionStatus":{"type":"string","description":"Position status filter for open/closed positions.","enum":["open","closed"]},"PositionVolumeChartResponse":{"type":"object","required":["volumes","has_more"],"properties":{"volumes":{"type":"array","items":{"$ref":"#/components/schemas/PositionVolumeDataPoint"}},"has_more":{"type":"boolean"}}},"PositionVolumeDataPoint":{"type":"object","required":["t","v","bv","sv","tc","btc","stc"],"properties":{"t":{"type":"integer","format":"int64"},"v":{"type":"number","format":"double"},"bv":{"type":"number","format":"double"},"sv":{"type":"number","format":"double"},"tc":{"type":"integer","format":"int32"},"btc":{"type":"integer","format":"int32"},"stc":{"type":"integer","format":"int32"}}},"PositionsConvertedTrade":{"type":"object","description":"Output payload for NegRisk PositionsConverted trades\n(convert NO-position tokens → YES tokens + collateral).","required":["id","hash","block","confirmed_at","log_index","block_index","trader","market_id","index_set","shares_amount","exchange"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":"integer","format":"int64","minimum":0},"confirmed_at":{"type":"integer","format":"int64","minimum":0},"log_index":{"type":"integer","format":"int64","minimum":0},"block_index":{"type":"integer","format":"int64","minimum":0},"trader":{"$ref":"#/components/schemas/TraderInfo"},"market_id":{"type":"string","description":"NegRisk umbrella market ID"},"index_set":{"type":"string","description":"Bitmask of question indices whose NO tokens are being converted"},"shares_amount":{"type":"number","format":"double"},"exchange":{"$ref":"#/components/schemas/PolymarketExchange"}}},"PredictionCandlestickBar":{"type":"object","required":["t"],"properties":{"l":{"type":["number","null"],"format":"double"},"h":{"type":["number","null"],"format":"double"},"o":{"type":["number","null"],"format":"double"},"c":{"type":["number","null"],"format":"double"},"v":{"type":["number","null"],"format":"double"},"t":{"type":"integer","format":"int64","minimum":0},"tc":{"type":["integer","null"],"format":"int32"},"m":{"type":["number","null"],"format":"double"}}},"PriceJump":{"type":"object","required":["from","to","price_before","price_after","change_pct","direction","volume","trades_count","condition_id"],"properties":{"from":{"type":"integer","format":"int64","minimum":0},"to":{"type":"integer","format":"int64","minimum":0},"price_before":{"type":"number","format":"double"},"price_after":{"type":"number","format":"double"},"change_pct":{"type":"number","format":"double"},"direction":{"type":"string"},"volume":{"type":"number","format":"double"},"trades_count":{"type":"integer","format":"int32"},"condition_id":{"type":"string"}}},"QuestionEmergencyResolvedEvent":{"type":"object","description":"UMA CTF Adapter: admin emergency resolution.","required":["id","hash","block","confirmed_at","log_index","block_index","oracle_contract","condition_id"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":"integer","format":"int64","minimum":0},"confirmed_at":{"type":"integer","format":"int64","minimum":0},"log_index":{"type":"integer","format":"int64","minimum":0},"block_index":{"type":"integer","format":"int64","minimum":0},"oracle_contract":{"type":"string"},"condition_id":{"type":"string"},"proposed_outcome":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"QuestionFlaggedEvent":{"type":"object","description":"UMA CTF Adapter: market flagged for emergency resolution.","required":["id","hash","block","confirmed_at","log_index","block_index","oracle_contract","condition_id"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":"integer","format":"int64","minimum":0},"confirmed_at":{"type":"integer","format":"int64","minimum":0},"log_index":{"type":"integer","format":"int64","minimum":0},"block_index":{"type":"integer","format":"int64","minimum":0},"oracle_contract":{"type":"string"},"condition_id":{"type":"string"},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"QuestionInitializedEvent":{"type":"object","description":"UMA CTF Adapter: questionID first initialized on-chain.","required":["id","hash","block","confirmed_at","log_index","block_index","oracle_contract","condition_id","creator","reward_token","reward","proposal_bond"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":"integer","format":"int64","minimum":0},"confirmed_at":{"type":"integer","format":"int64","minimum":0},"log_index":{"type":"integer","format":"int64","minimum":0},"block_index":{"type":"integer","format":"int64","minimum":0},"oracle_contract":{"type":"string"},"condition_id":{"type":"string"},"creator":{"type":"string"},"reward_token":{"type":"string"},"reward":{"type":"string"},"proposal_bond":{"type":"string"},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"QuestionPausedEvent":{"type":"object","description":"UMA CTF Adapter: market paused by admin.","required":["id","hash","block","confirmed_at","log_index","block_index","oracle_contract","condition_id"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":"integer","format":"int64","minimum":0},"confirmed_at":{"type":"integer","format":"int64","minimum":0},"log_index":{"type":"integer","format":"int64","minimum":0},"block_index":{"type":"integer","format":"int64","minimum":0},"oracle_contract":{"type":"string"},"condition_id":{"type":"string"},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"QuestionResetEvent":{"type":"object","description":"UMA CTF Adapter: dispute succeeded, market returns to active.","required":["id","hash","block","confirmed_at","log_index","block_index","oracle_contract","condition_id"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":"integer","format":"int64","minimum":0},"confirmed_at":{"type":"integer","format":"int64","minimum":0},"log_index":{"type":"integer","format":"int64","minimum":0},"block_index":{"type":"integer","format":"int64","minimum":0},"oracle_contract":{"type":"string"},"condition_id":{"type":"string"},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"QuestionResolvedEvent":{"type":"object","description":"UMA CTF Adapter: market resolved with definitive outcome.","required":["id","hash","block","confirmed_at","log_index","block_index","oracle_contract","condition_id","settled_price"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":"integer","format":"int64","minimum":0},"confirmed_at":{"type":"integer","format":"int64","minimum":0},"log_index":{"type":"integer","format":"int64","minimum":0},"block_index":{"type":"integer","format":"int64","minimum":0},"oracle_contract":{"type":"string"},"condition_id":{"type":"string"},"settled_price":{"type":"integer","format":"int64"},"proposed_outcome":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"QuestionUnflaggedEvent":{"type":"object","description":"UMA CTF Adapter: flag removed.","required":["id","hash","block","confirmed_at","log_index","block_index","oracle_contract","condition_id"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":"integer","format":"int64","minimum":0},"confirmed_at":{"type":"integer","format":"int64","minimum":0},"log_index":{"type":"integer","format":"int64","minimum":0},"block_index":{"type":"integer","format":"int64","minimum":0},"oracle_contract":{"type":"string"},"condition_id":{"type":"string"},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"QuestionUnpausedEvent":{"type":"object","description":"UMA CTF Adapter: market unpaused.","required":["id","hash","block","confirmed_at","log_index","block_index","oracle_contract","condition_id"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":"integer","format":"int64","minimum":0},"confirmed_at":{"type":"integer","format":"int64","minimum":0},"log_index":{"type":"integer","format":"int64","minimum":0},"block_index":{"type":"integer","format":"int64","minimum":0},"oracle_contract":{"type":"string"},"condition_id":{"type":"string"},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"RedemptionTrade":{"type":"object","description":"Output payload for Redemption trades (payout after market resolution).","required":["id","hash","block","confirmed_at","log_index","block_index","trader","usd_amount","exchange"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":"integer","format":"int64","minimum":0},"confirmed_at":{"type":"integer","format":"int64","minimum":0},"log_index":{"type":"integer","format":"int64","minimum":0},"block_index":{"type":"integer","format":"int64","minimum":0},"trader":{"$ref":"#/components/schemas/TraderInfo"},"condition_id":{"type":["string","null"]},"outcome":{"type":["string","null"]},"outcome_index":{"type":["integer","null"],"format":"int32","minimum":0},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]},"usd_amount":{"type":"number","format":"double"},"winning_outcome_index":{"type":["integer","null"],"format":"int32","minimum":0},"position_details":{"type":"array","items":{"$ref":"#/components/schemas/PositionDetail"},"description":"Per-position burn amounts"},"exchange":{"$ref":"#/components/schemas/PolymarketExchange"}}},"RegisterTokenTrade":{"type":"object","description":"Output payload for RegisterToken events (YES/NO token pair registered for a condition).","required":["id","hash","block","confirmed_at","log_index","block_index","condition_id","token0","token1","exchange"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":"integer","format":"int64","minimum":0},"confirmed_at":{"type":"integer","format":"int64","minimum":0},"log_index":{"type":"integer","format":"int64","minimum":0},"block_index":{"type":"integer","format":"int64","minimum":0},"condition_id":{"type":"string"},"token0":{"type":"string"},"token1":{"type":"string"},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]},"exchange":{"$ref":"#/components/schemas/PolymarketExchange"}}},"SearchResponse":{"type":"object","properties":{"events":{"type":["array","null"],"items":{"$ref":"#/components/schemas/PolymarketEvent"}},"events_pagination":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PaginationMeta"}]},"markets":{"type":["array","null"],"items":{"$ref":"#/components/schemas/MarketResponse"}},"markets_pagination":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PaginationMeta"}]},"traders":{"type":["array","null"],"items":{"$ref":"#/components/schemas/TraderWithPnl"}},"traders_pagination":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PaginationMeta"}]}}},"SearchSortBy":{"type":"string","description":"Combined sort options valid for both events and markets in search","enum":["volume","txns","unique_traders","relevance","title","creation_date","start_date","end_date","liquidity","holders","end_time","start_time","created_time"]},"SimpleTimeframeMetrics":{"type":"object","properties":{"volume":{"type":"number","format":"double","default":0},"fees":{"type":"number","format":"double","default":0},"txns":{"type":"integer","format":"int32","default":0},"unique_traders":{"type":"integer","format":"int32","default":0}}},"SortDirection":{"type":"string","enum":["asc","desc"]},"SpikeDirection":{"type":"string","description":"Direction filter for spike webhooks.","enum":["up","down","both"]},"SplitTrade":{"type":"object","description":"Output payload for Split trades (deposit collateral → receive outcome tokens).","required":["id","hash","block","confirmed_at","log_index","block_index","trader","usd_amount","exchange"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":"integer","format":"int64","minimum":0},"confirmed_at":{"type":"integer","format":"int64","minimum":0},"log_index":{"type":"integer","format":"int64","minimum":0},"block_index":{"type":"integer","format":"int64","minimum":0},"trader":{"$ref":"#/components/schemas/TraderInfo"},"condition_id":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]},"usd_amount":{"type":"number","format":"double"},"position_details":{"type":"array","items":{"$ref":"#/components/schemas/PositionDetail"},"description":"Per-position mint amounts"},"exchange":{"$ref":"#/components/schemas/PolymarketExchange"}}},"SpreadRow":{"type":"object","description":"Lightweight row — derived metrics only, no bids/asks JSONB.","required":["ts","position_id","condition_id"],"properties":{"ts":{"type":"integer","format":"int64"},"position_id":{"type":"string"},"condition_id":{"type":"string"},"best_bid":{"type":["number","null"],"format":"double"},"best_ask":{"type":["number","null"],"format":"double"},"mid_price":{"type":["number","null"],"format":"double"},"spread":{"type":["number","null"],"format":"double"},"bid_liquidity_usd":{"type":["number","null"],"format":"double"},"ask_liquidity_usd":{"type":["number","null"],"format":"double"},"bid_levels":{"type":["integer","null"],"format":"int32"},"ask_levels":{"type":["integer","null"],"format":"int32"}}},"TokenOutcome":{"type":"object","description":"Token outcome (position)","required":["token_id","outcome"],"properties":{"token_id":{"type":"string"},"outcome":{"type":"string"}}},"TradeEvent":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/OrderFilledTrade"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["OrderFilled"]}}}]},{"allOf":[{"$ref":"#/components/schemas/OrderFilledTrade"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["OrdersMatched"]}}}]},{"allOf":[{"$ref":"#/components/schemas/RedemptionTrade"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Redemption"]}}}]},{"allOf":[{"$ref":"#/components/schemas/MergeTrade"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Merge"]}}}]},{"allOf":[{"$ref":"#/components/schemas/SplitTrade"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Split"]}}}]},{"allOf":[{"$ref":"#/components/schemas/PositionsConvertedTrade"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["PositionsConverted"]}}}]},{"allOf":[{"$ref":"#/components/schemas/CancelledTrade"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Cancelled"]}}}]},{"allOf":[{"$ref":"#/components/schemas/QuestionInitializedEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Initialization"]}}}]},{"allOf":[{"$ref":"#/components/schemas/AssertionMadeEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Proposal"]}}}]},{"allOf":[{"$ref":"#/components/schemas/AssertionDisputedEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Dispute"]}}}]},{"allOf":[{"$ref":"#/components/schemas/AssertionSettledEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Settled"]}}}]},{"allOf":[{"$ref":"#/components/schemas/QuestionResolvedEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Resolution"]}}}]},{"allOf":[{"$ref":"#/components/schemas/ConditionResolutionEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["ConditionResolution"]}}}]},{"allOf":[{"$ref":"#/components/schemas/QuestionResetEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Reset"]}}}]},{"allOf":[{"$ref":"#/components/schemas/QuestionFlaggedEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Flag"]}}}]},{"allOf":[{"$ref":"#/components/schemas/QuestionUnflaggedEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Unflag"]}}}]},{"allOf":[{"$ref":"#/components/schemas/QuestionPausedEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Pause"]}}}]},{"allOf":[{"$ref":"#/components/schemas/QuestionUnpausedEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Unpause"]}}}]},{"allOf":[{"$ref":"#/components/schemas/QuestionEmergencyResolvedEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["ManualResolution"]}}}]},{"allOf":[{"$ref":"#/components/schemas/NegRiskOutcomeReportedEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["NegRiskOutcomeReported"]}}}]},{"allOf":[{"$ref":"#/components/schemas/RegisterTokenTrade"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["RegisterToken"]}}}]},{"allOf":[{"$ref":"#/components/schemas/ApprovalTrade"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Approval"]}}}]}],"description":"Tagged enum for all trade types — serializes with `\"trade_type\": \"...\"` discriminator\nand only includes fields relevant to each type."},"TradeSide":{"type":"string","enum":["0","1"]},"TradeType":{"type":"string","enum":["0","1","2","3","4","5","6"]},"Trader":{"type":"object","description":"Trader profile info embedded in API responses\n\nCorresponds to SQL function: `pm_build_trader(address, name, pseudonym, profile_image, x_username, verified_badge)`\n\nUsed in:\n- holders endpoints (market/event holders)\n- trades endpoints\n- leaderboard endpoints","required":["address"],"properties":{"address":{"type":"string"},"name":{"type":["string","null"]},"pseudonym":{"type":["string","null"]},"profile_image":{"type":["string","null"]},"x_username":{"type":["string","null"]},"verified_badge":{"type":"boolean"}}},"TraderEventPnlEntry":{"type":"object","description":"Event-level PnL entry","properties":{"event_slug":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"markets_traded":{"type":["integer","null"],"format":"int64"},"outcomes_traded":{"type":["integer","null"],"format":"int64"},"total_buys":{"type":["integer","null"],"format":"int64"},"total_sells":{"type":["integer","null"],"format":"int64"},"total_redemptions":{"type":["integer","null"],"format":"int64"},"total_merges":{"type":["integer","null"],"format":"int64"},"total_volume_usd":{"type":["number","null"],"format":"double"},"buy_usd":{"type":["number","null"],"format":"double"},"sell_usd":{"type":["number","null"],"format":"double"},"redemption_usd":{"type":["number","null"],"format":"double"},"merge_usd":{"type":["number","null"],"format":"double"},"realized_pnl_usd":{"type":["number","null"],"format":"double"},"winning_markets":{"type":["integer","null"],"format":"int64"},"losing_markets":{"type":["integer","null"],"format":"int64"},"total_fees":{"type":["number","null"],"format":"double"},"first_trade_at":{"type":["integer","null"],"format":"int64"},"last_trade_at":{"type":["integer","null"],"format":"int64"}}},"TraderInfo":{"type":"object","description":"Trader profile info - backwards compatibility","required":["address"],"properties":{"address":{"type":"string"},"name":{"type":["string","null"]},"pseudonym":{"type":["string","null"]},"profile_image":{"type":["string","null"]},"x_username":{"type":["string","null"]},"verified_badge":{"type":"boolean"}}},"TraderMarketPnlEntry":{"type":"object","description":"Market-level PnL entry","properties":{"condition_id":{"type":["string","null"]},"event_slug":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"outcomes_traded":{"type":["integer","null"],"format":"int64"},"total_buys":{"type":["integer","null"],"format":"int64"},"total_sells":{"type":["integer","null"],"format":"int64"},"total_redemptions":{"type":["integer","null"],"format":"int64"},"total_merges":{"type":["integer","null"],"format":"int64"},"buy_usd":{"type":["number","null"],"format":"double"},"sell_usd":{"type":["number","null"],"format":"double"},"redemption_usd":{"type":["number","null"],"format":"double"},"merge_usd":{"type":["number","null"],"format":"double"},"realized_pnl_usd":{"type":["number","null"],"format":"double"},"winning_outcomes":{"type":["integer","null"],"format":"int64"},"total_fees":{"type":["number","null"],"format":"double"},"first_trade_at":{"type":["integer","null"],"format":"int64"},"last_trade_at":{"type":["integer","null"],"format":"int64"}}},"TraderOutcomePnlEntry":{"type":"object","description":"Outcome-level PnL entry (per outcome token / position_id)","properties":{"position_id":{"type":["string","null"]},"condition_id":{"type":["string","null"]},"market_slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]},"title":{"type":["string","null"]},"image_url":{"type":["string","null"]},"outcome":{"type":["string","null"]},"outcome_index":{"type":["integer","null"],"format":"int32"},"won":{"type":["boolean","null"],"description":"TRUE = won, FALSE = lost, NULL = open or sold before resolution"},"total_buys":{"type":["integer","null"],"format":"int64"},"total_sells":{"type":["integer","null"],"format":"int64"},"total_shares_bought":{"type":["number","null"],"format":"double"},"total_shares_sold":{"type":["number","null"],"format":"double"},"total_buy_usd":{"type":["number","null"],"format":"double"},"total_sell_usd":{"type":["number","null"],"format":"double"},"redemption_usd":{"type":["number","null"],"format":"double","description":"Payout on redemption (non-zero only if won)"},"avg_entry_price":{"type":["number","null"],"format":"double","description":"VWAP price paid per share across all buys (0–1)"},"avg_exit_price":{"type":["number","null"],"format":"double","description":"VWAP price received per share across all sells (0–1)"},"realized_pnl_usd":{"type":["number","null"],"format":"double"},"total_fees":{"type":["number","null"],"format":"double"},"first_trade_at":{"type":["integer","null"],"format":"int64"},"last_trade_at":{"type":["integer","null"],"format":"int64"},"current_price":{"type":["number","null"],"format":"double","description":"Last traded price for this outcome (0–1). NULL if market_outcomes has no price yet."},"current_shares_balance":{"type":["number","null"],"format":"double","description":"Current shares held: balance / 1e6."},"current_value":{"type":["number","null"],"format":"double","description":"Estimated current USD value of held shares: (balance / 1e6) * current_price.\nOnly meaningful for open positions (balance above dust threshold)."},"realized_pnl_pct":{"type":["number","null"],"format":"double","description":"Realized PnL as a percentage of total spend: (realized_pnl_usd / total_buy_usd) * 100.\nNULL when total_buy_usd = 0."}}},"TraderPnlSummary":{"type":"object","description":"Trader's global PnL summary (single trader)","properties":{"trader":{"type":["string","null"]},"realized_pnl_usd":{"type":["number","null"],"format":"double"},"events_traded":{"type":["integer","null"],"format":"int64"},"markets_traded":{"type":["integer","null"],"format":"int64"},"total_buys":{"type":["integer","null"],"format":"int64"},"total_sells":{"type":["integer","null"],"format":"int64"},"total_redemptions":{"type":["integer","null"],"format":"int64"},"total_merges":{"type":["integer","null"],"format":"int64"},"total_volume_usd":{"type":["number","null"],"format":"double"},"buy_volume_usd":{"type":["number","null"],"format":"double"},"sell_volume_usd":{"type":["number","null"],"format":"double"},"redemption_volume_usd":{"type":["number","null"],"format":"double"},"merge_volume_usd":{"type":["number","null"],"format":"double"},"markets_won":{"type":["integer","null"],"format":"int64"},"markets_lost":{"type":["integer","null"],"format":"int64"},"market_win_rate_pct":{"type":["number","null"],"format":"double"},"avg_pnl_per_market":{"type":["number","null"],"format":"double"},"avg_pnl_per_trade":{"type":["number","null"],"format":"double"},"avg_hold_time_seconds":{"type":["number","null"],"format":"double"},"total_fees":{"type":["number","null"],"format":"double"},"best_trade_pnl_usd":{"type":["number","null"],"format":"double"},"best_trade_condition_id":{"type":["string","null"]},"first_trade_at":{"type":["integer","null"],"format":"int64"},"last_trade_at":{"type":["integer","null"],"format":"int64"}}},"TraderVolumeChartResponse":{"type":"object","required":["volumes","has_more"],"properties":{"volumes":{"type":"array","items":{"$ref":"#/components/schemas/TraderVolumeDataPoint"}},"has_more":{"type":"boolean"}}},"TraderVolumeDataPoint":{"type":"object","required":["t","v","bv","sv","tc","btc","stc"],"properties":{"t":{"type":"integer","format":"int64"},"v":{"type":"number","format":"double"},"bv":{"type":"number","format":"double"},"sv":{"type":"number","format":"double"},"tc":{"type":"integer","format":"int32"},"btc":{"type":"integer","format":"int32"},"stc":{"type":"integer","format":"int32"}}},"TraderWithPnl":{"allOf":[{"$ref":"#/components/schemas/Trader"},{"type":"object","properties":{"pnl":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TraderPnlSummary"}]}}}]},"WebhookAssetSymbol":{"type":"string","description":"Crypto asset symbols accepted by `asset_price_tick` and `asset_price_window_update` filters.","enum":["BTC","ETH","SOL","XRP","DOGE","BNB","HYPE"]},"WebhookTimeframe":{"type":"string","description":"Timeframe values accepted by webhook metric, milestone, spike, and asset-price filters.","enum":["1m","5m","15m","30m","1h","4h","6h","1d","24h","7d","30d"]}}}} \ No newline at end of file +{"openapi":"3.1.0","info":{"title":"Polymarket API","description":"RESTful API for querying Polymarket prediction markets data including events, markets, traders, holders, and real-time metrics","license":{"name":""},"version":"1.0.0"},"servers":[{"url":"https://api.struct.to/v1","description":"Production"}],"paths":{"/polymarket/asset-history":{"get":{"tags":["Assets"],"summary":"Get Asset Price History","description":"Returns historical price data for supported crypto assets from Polymarket API","operationId":"get_asset_history","parameters":[{"name":"asset_symbol","in":"query","description":"Asset ticker: BTC, ETH, XRP, SOL, DOGE, BNB, HYPE","required":true,"schema":{"type":"string"},"example":"BTC"},{"name":"variant","in":"query","description":"Time window: 5m, 15m, 1h, 4h, 1d","required":true,"schema":{"type":"string"},"example":"1h"},{"name":"from","in":"query","description":"Start timestamp in seconds (Unix epoch, inclusive)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"End timestamp in seconds (Unix epoch, inclusive)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"limit","in":"query","description":"Number of results (default: 10, max: 100)","required":false,"schema":{"type":"integer","format":"int64"},"example":10},{"name":"pagination_key","in":"query","description":"Cursor from previous response for keyset pagination. Pass the pagination_key from the previous page to fetch the next page.","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AssetPriceHistoryRow"}}}}},"400":{"description":"Bad request - missing or invalid parameters"},"500":{"description":"Internal server error"}}}},"/polymarket/events":{"get":{"tags":["Events"],"summary":"Get events","description":"Retrieve a paginated list of events with filtering, sorting, and optional nested tags/markets","operationId":"get_events","parameters":[{"name":"id","in":"query","description":"Filter by event ID(s) - comma-separated (max 50). Cannot be used with 'event_slugs'. Example: id=99600,99601,99583","required":false,"schema":{"type":"string"}},{"name":"event_slugs","in":"query","description":"Filter by event slug(s) - comma-separated (max 50). Cannot be used with 'id'. Example: event_slugs=will-trump-win,bitcoin-100k","required":false,"schema":{"type":"string"}},{"name":"search","in":"query","description":"Search in title and description (3-100 characters). Example: search=trump","required":false,"schema":{"type":"string"}},{"name":"sort_by","in":"query","description":"Sort: volume, txns, unique_traders, title, creation_date, start_date, end_date, relevance (relevance only works in search mode) (default: volume)","required":false,"schema":{"$ref":"#/components/schemas/EventSortBy"}},{"name":"sort_dir","in":"query","description":"Sort direction: asc, desc (default: desc)","required":false,"schema":{"$ref":"#/components/schemas/SortDirection"}},{"name":"timeframe","in":"query","description":"Metrics timeframe: 1m, 5m, 30m, 1h, 6h, 24h, 7d, 30d (default: 24h)","required":false,"schema":{"$ref":"#/components/schemas/MetricsTimeframe"}},{"name":"status","in":"query","description":"Filter by status: open, closed, or all (default: all)","required":false,"schema":{"$ref":"#/components/schemas/MarketStatus"}},{"name":"categories","in":"query","description":"Comma-separated category filters","required":false,"schema":{"type":"string"}},{"name":"exclude_categories","in":"query","description":"Comma-separated categories to exclude","required":false,"schema":{"type":"string"}},{"name":"tags","in":"query","description":"Filter by tag slug(s) - comma-separated (max 50). Example: tags=sports,football,crypto","required":false,"schema":{"type":"string"}},{"name":"exclude_tags","in":"query","description":"Comma-separated tag slugs to exclude","required":false,"schema":{"type":"string"}},{"name":"min_volume","in":"query","description":"Minimum volume in selected timeframe","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_volume","in":"query","description":"Maximum volume in selected timeframe","required":false,"schema":{"type":"number","format":"double"}},{"name":"min_txns","in":"query","description":"Minimum transactions in selected timeframe","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"max_txns","in":"query","description":"Maximum transactions in selected timeframe","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"min_unique_traders","in":"query","description":"Minimum unique traders in selected timeframe","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"max_unique_traders","in":"query","description":"Maximum unique traders in selected timeframe","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"include_tags","in":"query","description":"Include tags array (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_markets","in":"query","description":"Include markets array with outcomes (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_metrics","in":"query","description":"Include metrics object with all timeframes (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"limit","in":"query","description":"Results limit (default: 10, max: 100)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key","required":false,"schema":{"type":"string"}},{"name":"ai","in":"query","description":"Return truncated response optimized for AI consumers (default: false)","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"List of Polymarket events with nested tags, markets, and series. Response includes `pagination: { has_more, pagination_key }` for cursor-based pagination.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PolymarketEvent"}}}}},"400":{"description":"Bad request - validation error (search length, array limits, conflicting params)"}}}},"/polymarket/events/chart":{"get":{"tags":["Events"],"summary":"Get event chart","description":"Retrieve price data over time for up to 4 markets with highest YES outcome (outcome_index 0) prices in an event. Perfect for rendering multi-line charts showing price movement across top markets. TradingView-style: resolution parameter determines both candle size and implicit lookback period.","operationId":"get_event_chart","parameters":[{"name":"event_slug","in":"query","description":"Event slug (required)","required":true,"schema":{"type":"string"}},{"name":"condition_ids","in":"query","description":"Comma-separated condition IDs to include (max 4, optional)","required":false,"schema":{"type":"string"}},{"name":"market_slugs","in":"query","description":"Comma-separated market slugs to include (max 4, optional). Use either condition_ids or market_slugs, not both","required":false,"schema":{"type":"string"}},{"name":"resolution","in":"query","description":"Time interval with implicit lookback: 1H (1 hour), 6H (6 hours), 1D (1 day), 1W (1 week), 1M (30 days), ALL (all data) (required)","required":true,"schema":{"$ref":"#/components/schemas/ChartResolution"}}],"responses":{"200":{"description":"Price data over time for up to 4 markets with highest YES odds","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/EventMarketChartOutcome"}}}}}}}},"/polymarket/events/metrics":{"get":{"tags":["Events"],"summary":"Get event metrics","description":"Retrieve volume, transaction, and trader metrics for an event. Supports single timeframe (e.g., '1m'), multiple timeframes (e.g., '1m,5m,1h'), or 'all' to get all available timeframes.","operationId":"get_event_metrics","parameters":[{"name":"event_slug","in":"query","description":"Event slug","required":true,"schema":{"type":"string"}},{"name":"timeframe","in":"query","description":"Timeframe: single (1m, 5m, 30m, 1h, 6h, 24h, 7d, 30d), comma-separated (1m,5m,1h), or 'all'","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Event metrics for the specified timeframe(s). Returns single object for one timeframe, array for multiple.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventMetricsResponse"}}}},"400":{"description":"Invalid timeframe"}}}},"/polymarket/events/outcomes":{"get":{"tags":["Events"],"summary":"Get event market outcomes","description":"Returns the winning outcome name for each resolved market in an event, keyed by market slug. Useful for quickly checking which outcomes won across a series.","operationId":"get_event_outcomes","parameters":[{"name":"event_slug","in":"query","description":"Event slug (required)","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","description":"Number of resolved markets per page (default: 10, max: 250)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key for fetching next page","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Map of market_slug → winning outcome name with pagination metadata","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}}}}},"400":{"description":"Missing event_slug parameter"}}}},"/polymarket/events/slug/{event_slug}":{"get":{"tags":["Events"],"summary":"Get event by slug","description":"Retrieve a single event by its slug with optional nested tags, markets, and metrics","operationId":"get_event_by_slug","parameters":[{"name":"event_slug","in":"path","description":"Event slug","required":true,"schema":{"type":"string"}},{"name":"include_tags","in":"query","description":"Include tags array (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_markets","in":"query","description":"Include markets array with outcomes (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_metrics","in":"query","description":"Include metrics object with all timeframes (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"ai","in":"query","description":"Return truncated response optimized for AI consumers (default: false)","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Event with nested tags, markets, and series","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolymarketEvent"}}}},"404":{"description":"Event not found"}}}},"/polymarket/events/{identifier}":{"get":{"tags":["Events"],"summary":"Get event by ID or slug","description":"Retrieve a single event by its numeric ID or slug with optional nested tags, markets, and metrics","operationId":"get_event","parameters":[{"name":"identifier","in":"path","description":"Event ID or slug","required":true,"schema":{"type":"string"}},{"name":"include_tags","in":"query","description":"Include tags array (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_markets","in":"query","description":"Include markets array with outcomes (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_metrics","in":"query","description":"Include metrics object with all timeframes (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"ai","in":"query","description":"Return truncated response optimized for AI consumers (default: false)","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Event with nested tags, markets, and series","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolymarketEvent"}}}},"404":{"description":"Event not found"}}}},"/polymarket/holders/markets":{"get":{"tags":["Holders"],"summary":"Get market holders","description":"Retrieve holders of a market grouped by outcome, sorted by shares held. Identify the market with either `condition_id` or `market_slug` — exactly one must be provided. Set `include_pnl=true` to include a nested holder `pnl` object.","operationId":"get_market_holders","parameters":[{"name":"condition_id","in":"query","description":"Market condition ID (0x-prefixed hex)","required":false,"schema":{"type":"string"}},{"name":"market_slug","in":"query","description":"Market slug (e.g. `will-trump-win`)","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","description":"Results limit (default: 10, max: 100)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"min_shares","in":"query","description":"Minimum shares held (decimal string)","required":false,"schema":{"type":"string"}},{"name":"max_shares","in":"query","description":"Maximum shares held (decimal string)","required":false,"schema":{"type":"string"}},{"name":"include_pnl","in":"query","description":"Include nested holder PnL data (default: false, +1 credit)","required":false,"schema":{"type":"boolean"}},{"name":"ai","in":"query","description":"Return truncated response optimized for AI consumers (default: false)","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Market holders grouped by outcome (sorted by shares DESC). Holder `pnl` is included only when `include_pnl=true`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarketHoldersResponse"}}}},"404":{"description":"Market not found"}}}},"/polymarket/holders/markets/history":{"get":{"tags":["Holders"],"summary":"Get market holders history","description":"Retrieve historical holder count snapshots for a market over a time range. Identify the market with either `condition_id` or `market_slug` — exactly one must be provided.","operationId":"get_market_holders_history","parameters":[{"name":"condition_id","in":"query","description":"Market condition ID (0x-prefixed hex)","required":false,"schema":{"type":"string"}},{"name":"market_slug","in":"query","description":"Market slug (e.g. `will-trump-win`)","required":false,"schema":{"type":"string"}},{"name":"hours","in":"query","description":"Time range in hours (default: 24, max: 336 = 14 days)","required":false,"schema":{"type":"number","format":"double"}}],"responses":{"200":{"description":"Market holder count history with automatic granularity (1m/5m/15m/1h/6h buckets)","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/HolderHistoryCandle"}}}}},"404":{"description":"Market not found"}}}},"/polymarket/holders/positions/{position_id}":{"get":{"tags":["Holders"],"summary":"Get position holders","description":"Retrieve holders of a specific position (ERC1155 token), sorted by shares held. Set `include_pnl=true` to include nested holder PnL. Uses cursor-based pagination for efficient traversal.","operationId":"get_position_holders","parameters":[{"name":"position_id","in":"path","description":"Position ID (ERC1155 token ID)","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","description":"Results limit (default: 10, max: 100)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key","required":false,"schema":{"type":"string"}},{"name":"min_shares","in":"query","description":"Minimum shares held (decimal string)","required":false,"schema":{"type":"string"}},{"name":"max_shares","in":"query","description":"Maximum shares held (decimal string)","required":false,"schema":{"type":"string"}},{"name":"include_pnl","in":"query","description":"Include nested holder PnL data (default: false, +1 credit)","required":false,"schema":{"type":"boolean"}},{"name":"ai","in":"query","description":"Return truncated response optimized for AI consumers (default: false)","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Position holders (sorted by shares DESC). Holder `pnl` is included only when `include_pnl=true`. Response includes `pagination: { has_more, pagination_key }` for cursor-based pagination.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PositionHoldersResponse"}}}},"404":{"description":"Position not found"}}}},"/polymarket/holders/positions/{position_id}/history":{"get":{"tags":["Holders"],"summary":"Get position holders history","description":"Retrieve historical holder count snapshots for a position over a time range","operationId":"get_position_holders_history","parameters":[{"name":"position_id","in":"path","description":"Position ID (ERC1155 token ID)","required":true,"schema":{"type":"string"}},{"name":"hours","in":"query","description":"Time range in hours (default: 24, max: 336 = 14 days)","required":false,"schema":{"type":"number","format":"double"}}],"responses":{"200":{"description":"Position holder count history with automatic granularity (1m/5m/15m/1h/6h buckets)","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/HolderHistoryCandle"}}}}},"404":{"description":"Position not found"}}}},"/polymarket/market":{"get":{"tags":["Market"],"summary":"Get markets","description":"Retrieve a paginated list of markets with filtering, sorting, and optional nested tags/events/metrics","operationId":"list_markets","parameters":[{"name":"condition_ids","in":"query","description":"Filter by condition ID(s) - comma-separated (max 50)","required":false,"schema":{"type":"string"}},{"name":"question_ids","in":"query","description":"Filter by question ID(s) - comma-separated (max 50)","required":false,"schema":{"type":"string"}},{"name":"market_ids","in":"query","description":"Filter by market ID(s) - comma-separated (max 50)","required":false,"schema":{"type":"string"}},{"name":"market_slugs","in":"query","description":"Filter by market slug(s) - comma-separated (max 50)","required":false,"schema":{"type":"string"}},{"name":"event_slugs","in":"query","description":"Filter by event slug(s) - comma-separated (max 50)","required":false,"schema":{"type":"string"}},{"name":"position_ids","in":"query","description":"Filter by position ID(s) - comma-separated (max 50)","required":false,"schema":{"type":"string"}},{"name":"search","in":"query","description":"Search in title (3-100 characters)","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","description":"Filter by status: open, closed, or all (default: all)","required":false,"schema":{"$ref":"#/components/schemas/MarketStatus"}},{"name":"sort_by","in":"query","description":"Sort: volume, txns, unique_traders, liquidity, holders, total_daily_rate, end_time, start_time, created_time, relevance","required":false,"schema":{"$ref":"#/components/schemas/MarketSortBy"}},{"name":"sort_dir","in":"query","description":"Sort direction: asc, desc (default: desc)","required":false,"schema":{"$ref":"#/components/schemas/SortDirection"}},{"name":"timeframe","in":"query","description":"Metrics timeframe: 1m, 5m, 30m, 1h, 6h, 24h, 7d, 30d (default: 24h)","required":false,"schema":{"$ref":"#/components/schemas/MetricsTimeframe"}},{"name":"min_volume","in":"query","description":"Minimum total volume USD","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_volume","in":"query","description":"Maximum total volume USD","required":false,"schema":{"type":"number","format":"double"}},{"name":"min_liquidity","in":"query","description":"Minimum liquidity USD","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_liquidity","in":"query","description":"Maximum liquidity USD","required":false,"schema":{"type":"number","format":"double"}},{"name":"min_txns","in":"query","description":"Minimum transactions in selected timeframe","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"max_txns","in":"query","description":"Maximum transactions in selected timeframe","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"min_unique_traders","in":"query","description":"Minimum unique traders in selected timeframe","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"max_unique_traders","in":"query","description":"Maximum unique traders in selected timeframe","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"min_holders","in":"query","description":"Minimum total holders","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"max_holders","in":"query","description":"Maximum total holders","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"categories","in":"query","description":"Comma-separated category filters (max 50)","required":false,"schema":{"type":"string"}},{"name":"exclude_categories","in":"query","description":"Comma-separated categories to exclude","required":false,"schema":{"type":"string"}},{"name":"tags","in":"query","description":"Filter by tag(s) - comma-separated (max 50)","required":false,"schema":{"type":"string"}},{"name":"exclude_tags","in":"query","description":"Comma-separated tags to exclude","required":false,"schema":{"type":"string"}},{"name":"start_time","in":"query","description":"Filter markets with end_time >= start_time (Unix timestamp)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"end_time","in":"query","description":"Filter markets with end_time <= end_time (Unix timestamp)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"include_tags","in":"query","description":"Include tags array (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_event","in":"query","description":"Include event object (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_metrics","in":"query","description":"Include all timeframe metrics (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"limit","in":"query","description":"Results limit (default: 10, max: 100)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key","required":false,"schema":{"type":"string"}},{"name":"has_rewards","in":"query","description":"Only return markets that have CLOB rewards (default: false)","required":false,"schema":{"type":"boolean"}},{"name":"ai","in":"query","description":"Return truncated response optimized for AI consumers (default: false)","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"List of markets with metadata, outcomes, tags, event, and metrics. Response includes `pagination: { has_more, pagination_key }` for cursor-based pagination.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MarketResponse"}}}}},"400":{"description":"Bad request - validation error (search length, array limits, conflicting params)"}}}},"/polymarket/market/bonds":{"get":{"tags":["Bonds"],"summary":"Get bonds","description":"Retrieve a list of bond markets sorted by yield, filtered by probability and time to expiry","operationId":"get_bonds","parameters":[{"name":"min_probability","in":"query","description":"Minimum probability threshold (default: 0.85)","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_hours","in":"query","description":"Maximum hours until market end","required":false,"schema":{"type":"number","format":"double"}},{"name":"limit","in":"query","description":"Number of results (default: 10, max: 250)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Cursor for pagination: end_date (unix epoch) of the last item from the previous page","required":false,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"List of bond markets sorted by yield","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BondMarket"}}}}}}}},"/polymarket/market/candlestick":{"get":{"tags":["Market"],"summary":"Get market candlesticks by condition_id","description":"Retrieve OHLCV candlestick data for a market by its condition_id","operationId":"get_market_candlestick","parameters":[{"name":"condition_id","in":"query","description":"Market condition ID (required)","required":true,"schema":{"type":"string"}},{"name":"resolution","in":"query","description":"Candle interval: 1, 5, 15, 30, 60, 240, D, 1D","required":true,"schema":{"$ref":"#/components/schemas/CandlestickResolution"}},{"name":"count_back","in":"query","description":"Number of candles (max: 2500)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"from","in":"query","description":"Start timestamp (Unix seconds)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"End timestamp (Unix seconds)","required":false,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OHLCV candlestick data","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PredictionCandlestickBar"}}}}}}}},"/polymarket/market/chart":{"get":{"tags":["Market"],"summary":"Get market chart","description":"Retrieve price data over time for up to 4 position outcomes in a market condition. TradingView-style: resolution parameter determines both candle size and implicit lookback period. Auto-selects the 4 most active outcomes if position_ids not specified.","operationId":"get_chart","parameters":[{"name":"condition_id","in":"query","description":"Market condition ID or market_slug (one required)","required":false,"schema":{"type":"string"}},{"name":"market_slug","in":"query","description":"Market slug (alternative to condition_id)","required":false,"schema":{"type":"string"}},{"name":"position_ids","in":"query","description":"Comma-separated list of position IDs (max 4, optional). Auto-selected if not provided","required":false,"schema":{"type":"string"}},{"name":"resolution","in":"query","description":"Time interval with implicit lookback: 1H (1 hour), 6H (6 hours), 1D (1 day), 1W (1 week), 1M (30 days), ALL (all data) (required)","required":true,"schema":{"$ref":"#/components/schemas/ChartResolution"}}],"responses":{"200":{"description":"Price data over time for up to 4 market outcomes","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PositionChartOutcome"}}}}}}}},"/polymarket/market/metrics":{"get":{"tags":["Market"],"summary":"Get market metrics","description":"Retrieve volume, transaction, and trader metrics for a market. Supports single timeframe (e.g., '1m'), multiple timeframes (e.g., '1m,5m,1h'), or 'all' to get all available timeframes.","operationId":"get_market_metrics","parameters":[{"name":"condition_id","in":"query","description":"Market condition ID","required":true,"schema":{"type":"string"}},{"name":"timeframe","in":"query","description":"Timeframe: single (1m, 5m, 30m, 1h, 6h, 24h, 7d, 30d), comma-separated (1m,5m,1h), or 'all'","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Market metrics for the specified timeframe(s). Returns single object for one timeframe, array for multiple.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConditionMetricsResponse"}}}},"400":{"description":"Invalid timeframe"}}}},"/polymarket/market/position/candlestick":{"get":{"tags":["Market"],"summary":"Get position candlesticks by position_id","description":"Retrieve OHLCV candlestick data for a specific position by its position_id","operationId":"get_position_candlestick","parameters":[{"name":"position_id","in":"query","description":"Position/token ID (required)","required":true,"schema":{"type":"string"}},{"name":"resolution","in":"query","description":"Candle interval: 1, 5, 15, 30, 60, 240, D, 1D","required":true,"schema":{"$ref":"#/components/schemas/CandlestickResolution"}},{"name":"count_back","in":"query","description":"Number of candles (max: 2500)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"from","in":"query","description":"Start timestamp (Unix seconds)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"End timestamp (Unix seconds)","required":false,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OHLCV candlestick data","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PredictionCandlestickBar"}}}}}}}},"/polymarket/market/position/metrics":{"get":{"tags":["Market"],"summary":"Get position metrics","description":"Retrieve volume, transaction, and trader metrics for a specific position. Supports single timeframe (e.g., '1m'), multiple timeframes (e.g., '1m,5m,1h'), or 'all' to get all available timeframes.","operationId":"get_position_metrics","parameters":[{"name":"position_id","in":"query","description":"Position/token ID","required":true,"schema":{"type":"string"}},{"name":"timeframe","in":"query","description":"Timeframe: single (1m, 5m, 30m, 1h, 6h, 24h, 7d, 30d), comma-separated (1m,5m,1h), or 'all'","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Position metrics for the specified timeframe(s). Returns single object for one timeframe, array for multiple.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PositionMetricsResponse"}}}},"400":{"description":"Invalid timeframe"}}}},"/polymarket/market/position/volume-chart":{"get":{"tags":["Market"],"summary":"Get position volume chart","description":"Retrieve volume over time for a specific position with buy/sell breakdown","operationId":"get_position_volume_chart","parameters":[{"name":"position_id","in":"query","description":"Position/token ID (required)","required":true,"schema":{"type":"string"}},{"name":"resolution","in":"query","description":"Time interval: 1, 5, 15, 30, 60, 240, D, 1D (default: 60)","required":false,"schema":{"$ref":"#/components/schemas/CandlestickResolution"}},{"name":"count_back","in":"query","description":"Number of data points (max: 2500)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"from","in":"query","description":"Start timestamp (Unix seconds)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"End timestamp (Unix seconds)","required":false,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"Volume with buy/sell breakdown over time","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PositionVolumeDataPoint"}}}}}}}},"/polymarket/market/price-jumps":{"get":{"tags":["Market"],"summary":"Detect price jumps","description":"Scan candles for significant price movements. Returns jumps with from/to timestamps in milliseconds, directly usable as trades API time range parameters to identify traders who traded before or during the movement.","operationId":"get_price_jumps","parameters":[{"name":"condition_id","in":"query","description":"Market condition ID (one of condition_id or market_slug required)","required":false,"schema":{"type":"string"}},{"name":"market_slug","in":"query","description":"Market slug (resolved to condition_id)","required":false,"schema":{"type":"string"}},{"name":"resolution","in":"query","description":"Candle resolution in minutes: 1, 5, 15, 30, 60, 240 (default: 15)","required":false,"schema":{"type":"string"}},{"name":"min_change_pct","in":"query","description":"Minimum relative percent change to qualify as a jump (default: 10.0)","required":false,"schema":{"type":"number","format":"double"}},{"name":"lookback","in":"query","description":"Number of candles to scan back from now (default: 1440, max: 2500)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"offset","in":"query","description":"Offset in candles from now — window is [now - (lookback + offset) * resolution, now - offset * resolution] (default: 0)","required":false,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"Price jumps sorted by timestamp descending","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PriceJump"}}}}}}}},"/polymarket/market/slug/{market_slug}":{"get":{"tags":["Market"],"summary":"Get market by slug","description":"Retrieve a single market by its slug with optional nested tags, event, and metrics","operationId":"get_market_by_slug","parameters":[{"name":"market_slug","in":"path","description":"Market slug (e.g. `will-trump-win`)","required":true,"schema":{"type":"string"}},{"name":"include_tags","in":"query","description":"Include tags array (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_event","in":"query","description":"Include event object (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_metrics","in":"query","description":"Include all timeframe metrics (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"ai","in":"query","description":"Return truncated response optimized for AI consumers (default: false)","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Market with metadata, outcomes, tags, event, and metrics","content":{"application/json":{"schema":{"type":"object","description":"Formatted market response with structured metrics, tags, outcomes, and event","required":["condition_id","status"],"properties":{"condition_id":{"type":"string"},"id":{"type":["string","null"]},"market_slug":{"type":["string","null"]},"question":{"type":["string","null"]},"title":{"type":["string","null"]},"description":{"type":["string","null"]},"image_url":{"type":["string","null"]},"oracle":{"type":["string","null"]},"status":{"type":"string"},"created_time":{"type":["integer","null"],"format":"int64"},"start_time":{"type":["integer","null"],"format":"int64"},"game_start_time":{"type":["integer","null"],"format":"int64"},"closed_time":{"type":["integer","null"],"format":"int64"},"end_time":{"type":["integer","null"],"format":"int64"},"accepting_orders":{"type":["boolean","null"]},"uma_resolution_status":{"type":["string","null"]},"is_neg_risk":{"type":["boolean","null"]},"market_maker_address":{"type":["string","null"]},"creator":{"type":["string","null"]},"category":{"type":["string","null"]},"volume_usd":{"type":["number","null"],"format":"double"},"liquidity_usd":{"type":["number","null"],"format":"double"},"highest_probability":{"type":["number","null"],"format":"double"},"total_holders":{"type":["integer","null"],"format":"int64"},"total_daily_rate":{"type":["number","null"],"format":"double"},"winning_outcome":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/MarketOutcome"}]},"outcomes":{"type":"array","items":{"$ref":"#/components/schemas/MarketOutcome"}},"clob_rewards":{"type":"array","items":{"$ref":"#/components/schemas/ClobReward"}},"tags":{"type":"array","items":{"type":"string"}},"event_slug":{"type":["string","null"]},"resolution_source":{"type":["string","null"]},"metrics":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/SimpleTimeframeMetrics"},"propertyNames":{"type":"string"}},"relevance_score":{"type":["number","null"],"format":"double"}}}}}},"404":{"description":"Market not found"}}}},"/polymarket/market/trades":{"get":{"tags":["Market"],"summary":"Get market trades","description":"Retrieve trades for one or more markets, with filtering by trader, side, price, amount, and time range","operationId":"get_market_trades","parameters":[{"name":"condition_ids","in":"query","description":"Comma-separated condition IDs (max 20)","required":false,"schema":{"type":"string"}},{"name":"slugs","in":"query","description":"Comma-separated market slugs","required":false,"schema":{"type":"string"}},{"name":"position_ids","in":"query","description":"Comma-separated position IDs","required":false,"schema":{"type":"string"}},{"name":"traders","in":"query","description":"Comma-separated trader addresses (max 25)","required":false,"schema":{"type":"string"}},{"name":"side","in":"query","description":"Trade side: 0 (Buy), 1 (Sell)","required":false,"schema":{"$ref":"#/components/schemas/TradeSide"}},{"name":"outcome","in":"query","description":"Outcome name filter (e.g. Yes, No)","required":false,"schema":{"type":"string"}},{"name":"outcome_index","in":"query","description":"Outcome index: 0 (Yes), 1 (No)","required":false,"schema":{"$ref":"#/components/schemas/OutcomeIndex"}},{"name":"trade_types","in":"query","description":"Comma-separated trade types: OrderFilled, Redemption, Merge, Split, Cancelled, PositionsConverted, OrdersMatched","required":false,"schema":{"type":"string"}},{"name":"min_usd_amount","in":"query","description":"Min USD amount","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_usd_amount","in":"query","description":"Max USD amount","required":false,"schema":{"type":"number","format":"double"}},{"name":"min_shares_amount","in":"query","description":"Min shares amount","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_shares_amount","in":"query","description":"Max shares amount","required":false,"schema":{"type":"number","format":"double"}},{"name":"min_price","in":"query","description":"Min price (0.0-1.0)","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_price","in":"query","description":"Max price (0.0-1.0)","required":false,"schema":{"type":"number","format":"double"}},{"name":"from","in":"query","description":"Start timestamp (ms)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"End timestamp (ms)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"all","in":"query","description":"Return all-time trades, not just last 30 days (default: false)","required":false,"schema":{"type":"boolean"}},{"name":"limit","in":"query","description":"Results per page (default: 10, max: 250)","required":false,"schema":{"type":"integer","format":"int32","minimum":0}},{"name":"offset","in":"query","description":"Pagination offset (number of results to skip). Takes precedence over pagination_key.","required":false,"schema":{"type":"integer","format":"int32","minimum":0}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key obtained from previous response's pagination.pagination_key","required":false,"schema":{"type":"string"}},{"name":"sort_desc","in":"query","description":"Sort newest first (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"ai","in":"query","description":"Return truncated response optimized for AI consumers (default: false)","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Prediction trades matching filters","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TradeEvent"}}}}}}}},"/polymarket/market/volume-chart":{"get":{"tags":["Market"],"summary":"Get market volume chart","description":"Retrieve volume breakdown by YES/NO outcome over time for a prediction market","operationId":"get_market_volume_chart","parameters":[{"name":"condition_id","in":"query","description":"Market condition ID (or use market_slug)","required":false,"schema":{"type":"string"}},{"name":"market_slug","in":"query","description":"Market slug (alternative to condition_id)","required":false,"schema":{"type":"string"}},{"name":"resolution","in":"query","description":"Time interval: 1, 5, 15, 30, 60, 240, D, 1D (default: 60)","required":false,"schema":{"$ref":"#/components/schemas/CandlestickResolution"}},{"name":"count_back","in":"query","description":"Number of data points (max: 2500)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"from","in":"query","description":"Start timestamp (Unix seconds)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"End timestamp (Unix seconds)","required":false,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"Volume breakdown by YES/NO over time","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MarketVolumeDataPoint"}}}}}}}},"/polymarket/market/{condition_id}":{"get":{"tags":["Market"],"summary":"Get market by condition ID","description":"Retrieve a single market by its condition ID with optional nested tags, event, and metrics","operationId":"get_market","parameters":[{"name":"condition_id","in":"path","description":"Market condition ID (0x-prefixed hex)","required":true,"schema":{"type":"string"}},{"name":"include_tags","in":"query","description":"Include tags array (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_event","in":"query","description":"Include event object (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"include_metrics","in":"query","description":"Include all timeframe metrics (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"ai","in":"query","description":"Return truncated response optimized for AI consumers (default: false)","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Market with metadata, outcomes, tags, event, and metrics","content":{"application/json":{"schema":{"type":"object","description":"Formatted market response with structured metrics, tags, outcomes, and event","required":["condition_id","status"],"properties":{"condition_id":{"type":"string"},"id":{"type":["string","null"]},"market_slug":{"type":["string","null"]},"question":{"type":["string","null"]},"title":{"type":["string","null"]},"description":{"type":["string","null"]},"image_url":{"type":["string","null"]},"oracle":{"type":["string","null"]},"status":{"type":"string"},"created_time":{"type":["integer","null"],"format":"int64"},"start_time":{"type":["integer","null"],"format":"int64"},"game_start_time":{"type":["integer","null"],"format":"int64"},"closed_time":{"type":["integer","null"],"format":"int64"},"end_time":{"type":["integer","null"],"format":"int64"},"accepting_orders":{"type":["boolean","null"]},"uma_resolution_status":{"type":["string","null"]},"is_neg_risk":{"type":["boolean","null"]},"market_maker_address":{"type":["string","null"]},"creator":{"type":["string","null"]},"category":{"type":["string","null"]},"volume_usd":{"type":["number","null"],"format":"double"},"liquidity_usd":{"type":["number","null"],"format":"double"},"highest_probability":{"type":["number","null"],"format":"double"},"total_holders":{"type":["integer","null"],"format":"int64"},"total_daily_rate":{"type":["number","null"],"format":"double"},"winning_outcome":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/MarketOutcome"}]},"outcomes":{"type":"array","items":{"$ref":"#/components/schemas/MarketOutcome"}},"clob_rewards":{"type":"array","items":{"$ref":"#/components/schemas/ClobReward"}},"tags":{"type":"array","items":{"type":"string"}},"event_slug":{"type":["string","null"]},"resolution_source":{"type":["string","null"]},"metrics":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/SimpleTimeframeMetrics"},"propertyNames":{"type":"string"}},"relevance_score":{"type":["number","null"],"format":"double"}}}}}},"404":{"description":"Market not found"}}}},"/polymarket/order-book":{"get":{"tags":["Order Book"],"summary":"Get order book","description":"Returns the latest CLOB orderbook snapshot for a position, including derived metrics (best bid/ask, mid price, spread, liquidity depth). Data is sourced from the real-time Polymarket WebSocket feed. `bids` and `asks` are arrays of `{\"p\": price, \"s\": size}` objects, sorted best-first.","operationId":"get_order_book","parameters":[{"name":"position_id","in":"query","description":"Token ID (position ID) to query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Latest orderbook snapshot. ts is Unix milliseconds.","content":{"application/json":{"schema":{"type":"object","required":["ts","position_id","condition_id","bids","asks","hash"],"properties":{"ts":{"type":"integer","format":"int64"},"position_id":{"type":"string"},"condition_id":{"type":"string"},"bids":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"asks":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"hash":{"type":"string"},"best_bid":{"type":["number","null"],"format":"double"},"best_ask":{"type":["number","null"],"format":"double"},"mid_price":{"type":["number","null"],"format":"double"},"spread":{"type":["number","null"],"format":"double"},"bid_liquidity_usd":{"type":["number","null"],"format":"double"},"ask_liquidity_usd":{"type":["number","null"],"format":"double"},"bid_levels":{"type":["integer","null"],"format":"int32"},"ask_levels":{"type":["integer","null"],"format":"int32"}}}}}},"400":{"description":"Missing or invalid parameters"}}}},"/polymarket/order-book/history":{"get":{"tags":["Order Book"],"summary":"Get order book history","description":"Paginated history of raw CLOB orderbook snapshots including full bids/asks levels and derived metrics. Default limit 20, max 200. `bids` and `asks` are arrays of `{\"p\": price, \"s\": size}` objects, sorted best-first.","operationId":"get_order_book_history","parameters":[{"name":"position_id","in":"query","description":"Token ID (required if condition_id / market_slug not set)","required":false,"schema":{"type":"string"}},{"name":"condition_id","in":"query","description":"Condition ID — returns history for all positions in this market","required":false,"schema":{"type":"string"}},{"name":"market_slug","in":"query","description":"Market slug (alternative to condition_id)","required":false,"schema":{"type":"string"}},{"name":"from","in":"query","description":"Start timestamp (Unix milliseconds, inclusive)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"End timestamp (Unix milliseconds, inclusive)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"min_spread","in":"query","description":"Only return snapshots with spread >= this value","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_spread","in":"query","description":"Only return snapshots with spread <= this value","required":false,"schema":{"type":"number","format":"double"}},{"name":"min_liquidity","in":"query","description":"Only return snapshots where total liquidity (bid + ask) >= this value","required":false,"schema":{"type":"number","format":"double"}},{"name":"limit","in":"query","description":"Number of results (default: 20, max: 200)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"pagination_key","in":"query","description":"Cursor from previous response's pagination.pagination_key","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Orderbook snapshot rows, newest first. ts is Unix milliseconds.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","required":["ts","position_id","condition_id","bids","asks","hash"],"properties":{"ts":{"type":"integer","format":"int64"},"position_id":{"type":"string"},"condition_id":{"type":"string"},"bids":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"asks":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"hash":{"type":"string"},"best_bid":{"type":["number","null"],"format":"double"},"best_ask":{"type":["number","null"],"format":"double"},"mid_price":{"type":["number","null"],"format":"double"},"spread":{"type":["number","null"],"format":"double"},"bid_liquidity_usd":{"type":["number","null"],"format":"double"},"ask_liquidity_usd":{"type":["number","null"],"format":"double"},"bid_levels":{"type":["integer","null"],"format":"int32"},"ask_levels":{"type":["integer","null"],"format":"int32"}}}}}}},"400":{"description":"Missing or invalid parameters"}}}},"/polymarket/order-book/market":{"get":{"tags":["Order Book"],"summary":"Get order books for a market","description":"Returns the latest orderbook snapshot for every position (outcome) in a market. Accepts condition_id or market_slug. `bids` and `asks` are arrays of `{\"p\": price, \"s\": size}` objects, sorted best-first.","operationId":"get_market_order_book","parameters":[{"name":"condition_id","in":"query","description":"Condition ID of the market","required":false,"schema":{"type":"string"}},{"name":"market_slug","in":"query","description":"Market slug (alternative to condition_id)","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Latest orderbook snapshot per position. ts is Unix milliseconds.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","required":["ts","position_id","condition_id","bids","asks","hash"],"properties":{"ts":{"type":"integer","format":"int64"},"position_id":{"type":"string"},"condition_id":{"type":"string"},"bids":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"asks":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"hash":{"type":"string"},"best_bid":{"type":["number","null"],"format":"double"},"best_ask":{"type":["number","null"],"format":"double"},"mid_price":{"type":["number","null"],"format":"double"},"spread":{"type":["number","null"],"format":"double"},"bid_liquidity_usd":{"type":["number","null"],"format":"double"},"ask_liquidity_usd":{"type":["number","null"],"format":"double"},"bid_levels":{"type":["integer","null"],"format":"int32"},"ask_levels":{"type":["integer","null"],"format":"int32"}}}}}}},"400":{"description":"Missing or invalid parameters"}}}},"/polymarket/order-book/spread":{"get":{"tags":["Order Book"],"summary":"Get spread history","description":"Lightweight time series of derived orderbook metrics (best bid/ask, mid price, spread, liquidity depth) without raw bids/asks — ideal for charting. Default limit 20, max 200.","operationId":"get_spread_history","parameters":[{"name":"position_id","in":"query","description":"Token ID (required if condition_id / market_slug not set)","required":false,"schema":{"type":"string"}},{"name":"condition_id","in":"query","description":"Condition ID — returns spread history for all positions in this market","required":false,"schema":{"type":"string"}},{"name":"market_slug","in":"query","description":"Market slug (alternative to condition_id)","required":false,"schema":{"type":"string"}},{"name":"from","in":"query","description":"Start timestamp (Unix milliseconds, inclusive)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"End timestamp (Unix milliseconds, inclusive)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"min_spread","in":"query","description":"Only return rows with spread >= this value","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_spread","in":"query","description":"Only return rows with spread <= this value","required":false,"schema":{"type":"number","format":"double"}},{"name":"min_liquidity","in":"query","description":"Only return rows where total liquidity (bid + ask) >= this value","required":false,"schema":{"type":"number","format":"double"}},{"name":"limit","in":"query","description":"Number of results (default: 100, max: 1000)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"pagination_key","in":"query","description":"Cursor from previous response's pagination.pagination_key","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Spread time series rows, newest first. ts is Unix milliseconds.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","description":"Lightweight row — derived metrics only, no bids/asks JSONB.","required":["ts","position_id","condition_id"],"properties":{"ts":{"type":"integer","format":"int64"},"position_id":{"type":"string"},"condition_id":{"type":"string"},"best_bid":{"type":["number","null"],"format":"double"},"best_ask":{"type":["number","null"],"format":"double"},"mid_price":{"type":["number","null"],"format":"double"},"spread":{"type":["number","null"],"format":"double"},"bid_liquidity_usd":{"type":["number","null"],"format":"double"},"ask_liquidity_usd":{"type":["number","null"],"format":"double"},"bid_levels":{"type":["integer","null"],"format":"int32"},"ask_levels":{"type":["integer","null"],"format":"int32"}}}}}}},"400":{"description":"Missing or invalid parameters"}}}},"/polymarket/search":{"get":{"tags":["Search"],"summary":"Search events, markets, and traders","description":"Search across markets, events, and traders. Use `type` to limit which categories are searched. Trader search supports wallet address lookup or name search. Results for each category are independently paginated. Only requested categories are included in the response.","operationId":"search","parameters":[{"name":"q","in":"query","description":"Search query (min 2 characters). Prefix with 0x for exact wallet address lookup.","required":true,"schema":{"type":"string"}},{"name":"type","in":"query","description":"Comma-separated categories to search: events, markets, traders (default: all three, 1 credit per type). Example: type=markets,traders","required":false,"schema":{"type":"string"}},{"name":"include_pnl","in":"query","description":"Include lifetime PnL summary for each trader (default: false, +1 credit)","required":false,"schema":{"type":"boolean"}},{"name":"sort_by","in":"query","description":"Sort field applied to both events and markets (default: volume). Fields marked events-only or markets-only fall back to volume on the other category.","required":false,"schema":{"$ref":"#/components/schemas/SearchSortBy"}},{"name":"sort_dir","in":"query","description":"Sort direction (default: desc)","required":false,"schema":{"$ref":"#/components/schemas/SortDirection"}},{"name":"timeframe","in":"query","description":"Metrics timeframe used for volume/txns/unique_traders sort (default: 24h)","required":false,"schema":{"$ref":"#/components/schemas/MetricsTimeframe"}},{"name":"limit","in":"query","description":"Results limit per category (default: 10, max: 250)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"events_pagination_key","in":"query","description":"Cursor for the next page of events, obtained from previous response's events_pagination.pagination_key","required":false,"schema":{"type":"string"}},{"name":"markets_pagination_key","in":"query","description":"Cursor for the next page of markets, obtained from previous response's markets_pagination.pagination_key","required":false,"schema":{"type":"string"}},{"name":"traders_pagination_key","in":"query","description":"Cursor for the next page of traders, obtained from previous response's traders_pagination.pagination_key","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Search results. Only requested categories (via `type`) are included in the response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"400":{"description":"Bad request — q is missing or shorter than 2 characters"}}}},"/polymarket/series":{"get":{"tags":["Series"],"summary":"List or get series","description":"Retrieve series. Use `id` or `series_slug` for single lookup, `series_ids` or `series_slugs` (comma-separated, max 250, mutually exclusive) for multi-lookup, or paginate with `active_only`.","operationId":"get_series_list","parameters":[{"name":"id","in":"query","description":"Single series ID for direct lookup","required":false,"schema":{"type":"string"}},{"name":"series_ids","in":"query","description":"Comma-separated series IDs (max 250). Mutually exclusive with series_slugs.","required":false,"schema":{"type":"string"}},{"name":"series_slugs","in":"query","description":"Comma-separated series slugs (max 250). Mutually exclusive with series_ids.","required":false,"schema":{"type":"string"}},{"name":"active_only","in":"query","description":"Only active series (default: true). Ignored when series_ids or series_slugs are provided.","required":false,"schema":{"type":"boolean"}},{"name":"limit","in":"query","description":"Results limit (default: 10, max: 250)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Cursor for pagination: id of the last series from the previous page","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Series or list of series","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PolymarketSeries"}}}}},"400":{"description":"series_ids and series_slugs cannot both be provided"}}}},"/polymarket/series/outcomes":{"get":{"tags":["Series"],"summary":"Get series market outcomes","description":"Returns the winning outcome name for each resolved market across all events in a series, keyed by market slug. Useful for checking historical results across recurring series (e.g., btc-updown-5m).","operationId":"get_series_outcomes","parameters":[{"name":"series_slug","in":"query","description":"Series slug (required)","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","description":"Number of resolved markets per page (default: 10, max: 250)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key for fetching next page","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Map of market_slug → winning outcome name with pagination metadata","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}}}}},"400":{"description":"Missing series_slug parameter"}}}},"/polymarket/tags":{"get":{"tags":["Tags"],"summary":"Get tags","description":"Retrieve all available event tags/categories. Supports both cursor-based and offset-based pagination.","operationId":"get_tags","parameters":[{"name":"limit","in":"query","description":"Results limit (default: 10, max: 250)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"offset","in":"query","description":"Offset-based pagination (number of results to skip). Takes precedence over pagination_key.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"List of all tags","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PolymarketTag"}}}}}}}},"/polymarket/tags/{identifier}":{"get":{"tags":["Tags"],"summary":"Get tag by slug","description":"Retrieve a single tag by its ID or slug","operationId":"get_tag_by_id","parameters":[{"name":"identifier","in":"path","description":"Tag ID or slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Tag details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolymarketTag"}}}},"404":{"description":"Tag not found"}}}},"/polymarket/trader/global_pnl":{"get":{"tags":["Trader"],"summary":"Get global leaderboard","description":"Retrieve the global PnL leaderboard ranked by profit and loss. Uses cursor-based pagination for efficient traversal of large datasets.","operationId":"get_global_pnl","parameters":[{"name":"timeframe","in":"query","description":"Timeframe: 1d, 7d, 30d, lifetime (default: lifetime)","required":false,"schema":{"$ref":"#/components/schemas/PnlTimeframe"}},{"name":"sort_by","in":"query","description":"Sort: realized_pnl_usd, buys, sells, redemptions, merges, avg_hold_time, markets_traded, events_traded, markets_won, volume_usd, fees, best_trade (default: realized_pnl_usd)","required":false,"schema":{"$ref":"#/components/schemas/GlobalPnlSortBy"}},{"name":"sort_direction","in":"query","description":"Sort direction: asc, desc (default: desc)","required":false,"schema":{"$ref":"#/components/schemas/SortDirection"}},{"name":"limit","in":"query","description":"Results limit (default: 10, max: 200)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Global PnL leaderboard with trader stats. Response includes `pagination: { has_more, pagination_key }` for cursor-based pagination.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GlobalPnlTrader"}}}}}}}},"/polymarket/trader/pnl/{address}":{"get":{"tags":["Trader"],"summary":"Get trader PnL summary","description":"Retrieve a trader's overall profit and loss summary","operationId":"get_trader_pnl","parameters":[{"name":"address","in":"path","description":"Trader wallet address","required":true,"schema":{"type":"string"}},{"name":"timeframe","in":"query","description":"Timeframe: 1d, 7d, 30d, lifetime (default: lifetime)","required":false,"schema":{"$ref":"#/components/schemas/PnlTimeframe"}}],"responses":{"200":{"description":"Trader's global PnL summary","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TraderPnlSummary"}}}}}}},"/polymarket/trader/pnl/{address}/calendar":{"get":{"tags":["Trader"],"summary":"Get trader PnL calendar","description":"Retrieve raw per-day PnL. Each entry is the realized PnL for that calendar day. Paginate backwards using the pagination key.","operationId":"get_trader_pnl_calendar","parameters":[{"name":"address","in":"path","description":"Trader wallet address","required":true,"schema":{"type":"string"}},{"name":"days","in":"query","description":"Window size in days (default: 30, max: 30)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Pagination key obtained from a previous response to fetch an earlier window","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Daily PnL entries (one per active trading day)","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PnlCandleEntry"}}}}}}}},"/polymarket/trader/pnl/{address}/candles":{"get":{"tags":["Trader"],"summary":"Get trader PnL candles","description":"Retrieve PnL candles for charting a trader's equity curve over time.","operationId":"get_trader_pnl_candles","parameters":[{"name":"address","in":"path","description":"Trader wallet address","required":true,"schema":{"type":"string"}},{"name":"resolution","in":"query","description":"Candle resolution (default: 1h)","required":false,"schema":{"$ref":"#/components/schemas/PnlCandleResolution"}},{"name":"timeframe","in":"query","description":"Time range (default: lifetime)","required":false,"schema":{"$ref":"#/components/schemas/PnlCandleTimeframe"}}],"responses":{"200":{"description":"Cumulative PnL candles","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PnlCandleEntry"}}}}}}}},"/polymarket/trader/pnl/{address}/events":{"get":{"tags":["Trader"],"summary":"Get trader event PnL","description":"Retrieve event-level PnL breakdown for a trader","operationId":"get_trader_event_pnl","parameters":[{"name":"address","in":"path","description":"Trader wallet address","required":true,"schema":{"type":"string"}},{"name":"timeframe","in":"query","description":"Timeframe: 1d, 7d, 30d, lifetime (default: lifetime)","required":false,"schema":{"$ref":"#/components/schemas/PnlTimeframe"}},{"name":"sort_by","in":"query","description":"Sort: realized_pnl_usd, total_volume_usd, markets_traded, total_fees (default: realized_pnl_usd)","required":false,"schema":{"$ref":"#/components/schemas/EventPnlSortBy"}},{"name":"sort_direction","in":"query","description":"Sort direction: asc, desc (default: desc)","required":false,"schema":{"$ref":"#/components/schemas/SortDirection"}},{"name":"limit","in":"query","description":"Results limit (default: 10, max: 200)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"offset","in":"query","description":"Pagination offset (number of results to skip). Takes precedence over pagination_key.","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key obtained from previous response's pagination.pagination_key","required":false,"schema":{"type":"string"}},{"name":"event_slug","in":"query","description":"Filter by event slug","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Event-level PnL entries for this trader","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TraderEventPnlEntry"}}}}}}}},"/polymarket/trader/pnl/{address}/markets":{"get":{"tags":["Trader"],"summary":"Get trader market PnL","description":"Retrieve market-level PnL breakdown for a trader","operationId":"get_trader_market_pnl","parameters":[{"name":"address","in":"path","description":"Trader wallet address","required":true,"schema":{"type":"string"}},{"name":"timeframe","in":"query","description":"Timeframe: 1d, 7d, 30d, lifetime (default: lifetime)","required":false,"schema":{"$ref":"#/components/schemas/PnlTimeframe"}},{"name":"sort_by","in":"query","description":"Sort: realized_pnl_usd, buy_usd, total_buys, total_fees, outcomes_traded (default: realized_pnl_usd)","required":false,"schema":{"$ref":"#/components/schemas/MarketPnlSortBy"}},{"name":"sort_direction","in":"query","description":"Sort direction: asc, desc (default: desc)","required":false,"schema":{"$ref":"#/components/schemas/SortDirection"}},{"name":"limit","in":"query","description":"Results limit (default: 10, max: 200)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"offset","in":"query","description":"Pagination offset (number of results to skip). Takes precedence over pagination_key.","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key obtained from previous response's pagination.pagination_key","required":false,"schema":{"type":"string"}},{"name":"condition_id","in":"query","description":"Filter by condition ID (or use market_slug)","required":false,"schema":{"type":"string"}},{"name":"market_slug","in":"query","description":"Filter by market slug (alternative to condition_id)","required":false,"schema":{"type":"string"}},{"name":"event_slug","in":"query","description":"Filter by event slug","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Market-level PnL entries for this trader","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TraderMarketPnlEntry"}}}}}}}},"/polymarket/trader/pnl/{address}/positions":{"get":{"tags":["Trader"],"summary":"Get trader position PnL","description":"Retrieve per-outcome-token lifetime PnL for a trader from polymarket_accounts. Includes open/closed state, win/loss outcome, redemption payouts, and share quantities. Filter by status and won/lost to segment portfolio views.","operationId":"get_trader_position_pnl","parameters":[{"name":"address","in":"path","description":"Trader wallet address","required":true,"schema":{"type":"string"}},{"name":"status","in":"query","description":"Required. Filter by position status","required":true,"schema":{"$ref":"#/components/schemas/PositionStatus"}},{"name":"won","in":"query","description":"Filter by outcome: true (won), false (lost), only for status=closed","required":false,"schema":{"type":"boolean"}},{"name":"search","in":"query","description":"Search by market title","required":false,"schema":{"type":"string"}},{"name":"sort_by","in":"query","description":"Sort field (default: realized_pnl_usd)","required":false,"schema":{"$ref":"#/components/schemas/PositionPnlSortBy"}},{"name":"sort_direction","in":"query","description":"Sort direction: asc, desc (default: desc)","required":false,"schema":{"$ref":"#/components/schemas/SortDirection"}},{"name":"limit","in":"query","description":"Results limit (default: 10, max: 200)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"offset","in":"query","description":"Pagination offset","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"condition_id","in":"query","description":"Filter by market condition ID (or use market_slug)","required":false,"schema":{"type":"string"}},{"name":"market_slug","in":"query","description":"Filter by market slug (alternative to condition_id)","required":false,"schema":{"type":"string"}},{"name":"position_id","in":"query","description":"Filter by specific outcome token (position ID)","required":false,"schema":{"type":"string"}},{"name":"min_shares","in":"query","description":"Minimum shares balance to include. Status filtering already treats balances below 0.01 shares as dust.","required":false,"schema":{"type":"number","format":"double"}}],"responses":{"200":{"description":"Position-level PnL entries for this trader. Each entry is a specific outcome token with open/closed state, avg_entry_price, avg_exit_price, and redemption info.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TraderOutcomePnlEntry"}}}}}}}},"/polymarket/trader/profile/{address}":{"get":{"tags":["Trader"],"summary":"Get trader overview","description":"Retrieve a trader's profile including stats and trading history summary","operationId":"get_trader_profile","parameters":[{"name":"address","in":"path","description":"Trader wallet address","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Trader profile information with username, bio, and badges","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolymarketUserProfile"}}}}}}},"/polymarket/trader/profiles/batch":{"get":{"tags":["Trader"],"summary":"Get multiple trader profiles","description":"Retrieve profiles for multiple traders in a single request. Returns an array of profiles.","operationId":"get_trader_profiles_batch","parameters":[{"name":"addresses","in":"query","description":"Comma-separated list of trader addresses (max: 20)","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Array of trader profiles","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PolymarketUserProfile"}}}}},"400":{"description":"Invalid request (empty addresses or more than 20)"}}}},"/polymarket/trader/trades/{address}":{"get":{"tags":["Trader"],"summary":"Get trader trades","description":"Retrieve trade history for a specific trader across all markets","operationId":"get_trader_trades","parameters":[{"name":"address","in":"path","description":"Trader wallet address","required":true,"schema":{"type":"string"}},{"name":"condition_ids","in":"query","description":"Comma-separated condition IDs (max 20)","required":false,"schema":{"type":"string"}},{"name":"slugs","in":"query","description":"Comma-separated market slugs","required":false,"schema":{"type":"string"}},{"name":"position_ids","in":"query","description":"Comma-separated position IDs","required":false,"schema":{"type":"string"}},{"name":"side","in":"query","description":"Trade side: 0 (Buy), 1 (Sell)","required":false,"schema":{"$ref":"#/components/schemas/TradeSide"}},{"name":"outcome","in":"query","description":"Outcome name filter (e.g. Yes, No)","required":false,"schema":{"type":"string"}},{"name":"outcome_index","in":"query","description":"Outcome index: 0 (Yes), 1 (No)","required":false,"schema":{"$ref":"#/components/schemas/OutcomeIndex"}},{"name":"trade_types","in":"query","description":"Comma-separated trade types: OrderFilled, Redemption, Merge, Split, Cancelled, PositionsConverted, OrdersMatched","required":false,"schema":{"type":"string"}},{"name":"min_usd_amount","in":"query","description":"Min USD amount","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_usd_amount","in":"query","description":"Max USD amount","required":false,"schema":{"type":"number","format":"double"}},{"name":"min_shares_amount","in":"query","description":"Min shares amount","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_shares_amount","in":"query","description":"Max shares amount","required":false,"schema":{"type":"number","format":"double"}},{"name":"min_price","in":"query","description":"Min price (0.0-1.0)","required":false,"schema":{"type":"number","format":"double"}},{"name":"max_price","in":"query","description":"Max price (0.0-1.0)","required":false,"schema":{"type":"number","format":"double"}},{"name":"from","in":"query","description":"Start timestamp (ms)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"End timestamp (ms)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"all","in":"query","description":"Return all-time trades, not just last 30 days (default: false)","required":false,"schema":{"type":"boolean"}},{"name":"limit","in":"query","description":"Results per page (default: 10, max: 250)","required":false,"schema":{"type":"integer","format":"int32","minimum":0}},{"name":"offset","in":"query","description":"Pagination offset (number of results to skip). Takes precedence over pagination_key.","required":false,"schema":{"type":"integer","format":"int32","minimum":0}},{"name":"pagination_key","in":"query","description":"Cursor-based pagination key obtained from previous response's pagination.pagination_key","required":false,"schema":{"type":"string"}},{"name":"sort_desc","in":"query","description":"Sort newest first (default: true)","required":false,"schema":{"type":"boolean"}},{"name":"ai","in":"query","description":"Return truncated response optimized for AI consumers (default: false)","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Trader's trade history with advanced filtering (same as /market/trades but filtered by trader)","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TradeEvent"}}}}}}}},"/polymarket/trader/volume-chart/{address}":{"get":{"tags":["Trader"],"summary":"Get trader volume chart","description":"Retrieve volume breakdown by buy/sell over time for a specific trader","operationId":"get_trader_volume_chart","parameters":[{"name":"address","in":"path","description":"Trader wallet address","required":true,"schema":{"type":"string"}},{"name":"resolution","in":"query","description":"Time interval: 1, 5, 15, 30, 60, 240, D, 1D (default: 60)","required":false,"schema":{"$ref":"#/components/schemas/CandlestickResolution"}},{"name":"count_back","in":"query","description":"Number of data points (max: 2500)","required":false,"schema":{"type":"integer","format":"int32"}},{"name":"from","in":"query","description":"Start timestamp (Unix seconds)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"End timestamp (Unix seconds)","required":false,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"Trader volume with buy/sell breakdown over time","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TraderVolumeDataPoint"}}}}}}}}},"components":{"schemas":{"ApprovalTrade":{"type":"object","description":"Output payload for ERC1155 setApprovalForAll events.","required":["id","hash","trader","operator","approved","exchange"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":["integer","null"],"format":"int64","minimum":0},"confirmed_at":{"type":["integer","null"],"format":"int64","minimum":0},"received_at":{"type":["integer","null"],"format":"int64","minimum":0},"log_index":{"type":["integer","null"],"format":"int64","minimum":0},"block_index":{"type":["integer","null"],"format":"int64","minimum":0},"trader":{"$ref":"#/components/schemas/TraderInfo"},"operator":{"type":"string"},"approved":{"type":"boolean"},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]},"exchange":{"$ref":"#/components/schemas/PolymarketExchange"}}},"AssertionDisputedEvent":{"type":"object","description":"V3 UMA OOv3: an assertion was disputed.","required":["id","hash","oracle_contract","assertion_id","caller","disputer"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":["integer","null"],"format":"int64","minimum":0},"confirmed_at":{"type":["integer","null"],"format":"int64","minimum":0},"received_at":{"type":["integer","null"],"format":"int64","minimum":0},"log_index":{"type":["integer","null"],"format":"int64","minimum":0},"block_index":{"type":["integer","null"],"format":"int64","minimum":0},"oracle_contract":{"type":"string"},"assertion_id":{"type":"string"},"caller":{"type":"string"},"disputer":{"type":"string"},"condition_id":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"AssertionMadeEvent":{"type":"object","description":"V3 UMA OOv3: a new assertion (resolution proposal) was made.","required":["id","hash","oracle_contract","assertion_id","domain_id","claim","asserter","callback_recipient","escalation_manager","caller","expiration_time","currency","bond","identifier"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":["integer","null"],"format":"int64","minimum":0},"confirmed_at":{"type":["integer","null"],"format":"int64","minimum":0},"received_at":{"type":["integer","null"],"format":"int64","minimum":0},"log_index":{"type":["integer","null"],"format":"int64","minimum":0},"block_index":{"type":["integer","null"],"format":"int64","minimum":0},"oracle_contract":{"type":"string"},"assertion_id":{"type":"string"},"domain_id":{"type":"string"},"claim":{"type":"string"},"asserter":{"type":"string"},"callback_recipient":{"type":"string"},"escalation_manager":{"type":"string"},"caller":{"type":"string"},"expiration_time":{"type":"integer","format":"int64","minimum":0},"currency":{"type":"string"},"bond":{"type":"string"},"identifier":{"type":"string"},"condition_id":{"type":["string","null"]},"proposed_outcome":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"AssertionSettledEvent":{"type":"object","description":"V3 UMA OOv3: an assertion liveness period expired and was settled.","required":["id","hash","oracle_contract","assertion_id","bond_recipient","disputed","settlement_resolution","settle_caller"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":["integer","null"],"format":"int64","minimum":0},"confirmed_at":{"type":["integer","null"],"format":"int64","minimum":0},"received_at":{"type":["integer","null"],"format":"int64","minimum":0},"log_index":{"type":["integer","null"],"format":"int64","minimum":0},"block_index":{"type":["integer","null"],"format":"int64","minimum":0},"oracle_contract":{"type":"string"},"assertion_id":{"type":"string"},"bond_recipient":{"type":"string"},"disputed":{"type":"boolean"},"settlement_resolution":{"type":"boolean"},"settle_caller":{"type":"string"},"condition_id":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"AssetPriceHistoryRow":{"type":"object","description":"A single asset price history record from the `asset_price_history` table.","required":["asset_symbol","asset_open_price","asset_close_price","variant","start_time","end_time"],"properties":{"asset_symbol":{"type":"string"},"asset_open_price":{"type":"number","format":"double","description":"Opening price at start_time (cast to f64 from NUMERIC in query)"},"asset_close_price":{"type":"number","format":"double","description":"Closing price at end_time (cast to f64 from NUMERIC in query)"},"price_change_percentage":{"type":["number","null"],"format":"double"},"outcome":{"type":["string","null"],"description":"Generated column: \"up\" if close > open, \"down\" if close ≤ open, null if no close yet"},"variant":{"type":"string","description":"Time window: \"5m\", \"15m\", \"1h\", \"1d\""},"start_time":{"type":"integer","format":"int64","description":"Window start timestamp (seconds since epoch)"},"end_time":{"type":"integer","format":"int64","description":"Window end timestamp (seconds since epoch)"}}},"AssetSymbol":{"type":"string","enum":["BTC","ETH","XRP","SOL","DOGE","BNB","HYPE"]},"AssetVariant":{"type":"string","enum":["5m","15m","1h","4h","1d"]},"BondMarket":{"type":"object","required":["condition_id","question","market_slug","end_time","best_outcome_index","return_pct","apy","outcomes"],"properties":{"condition_id":{"type":"string"},"title":{"type":["string","null"]},"question":{"type":"string"},"market_slug":{"type":"string"},"event_slug":{"type":["string","null"]},"image_url":{"type":["string","null"]},"end_time":{"type":"integer","format":"int64"},"best_outcome_index":{"type":"integer","format":"int32","minimum":0},"return_pct":{"type":"number","format":"double"},"apy":{"type":"number","format":"double"},"volume_24h":{"type":["number","null"],"format":"double"},"outcomes":{"type":"array","items":{"$ref":"#/components/schemas/BondOutcome"}}}},"BondOutcome":{"type":"object","required":["name","index","position_id","price"],"properties":{"name":{"type":"string"},"index":{"type":"integer","format":"int32"},"position_id":{"type":"string"},"price":{"type":"number","format":"double"}}},"CancelledTrade":{"type":"object","description":"Output payload for Cancelled orders.","required":["id","hash","exchange"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":["integer","null"],"format":"int64","minimum":0},"confirmed_at":{"type":["integer","null"],"format":"int64","minimum":0},"received_at":{"type":["integer","null"],"format":"int64","minimum":0},"log_index":{"type":["integer","null"],"format":"int64","minimum":0},"block_index":{"type":["integer","null"],"format":"int64","minimum":0},"order_hash":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]},"exchange":{"$ref":"#/components/schemas/PolymarketExchange"}}},"CandlestickResolution":{"type":"string","enum":["1","5","15","30","60","240","D","1D"]},"ChartResolution":{"type":"string","enum":["1H","6H","1D","1W","1M","ALL"]},"ClobReward":{"type":"object","description":"CLOB reward (public API format)","required":["id","condition_id"],"properties":{"id":{"type":"string"},"condition_id":{"type":"string"},"asset_address":{"type":["string","null"]},"rewards_amount":{"type":["number","null"],"format":"double"},"rewards_daily_rate":{"type":["number","null"],"format":"double"},"start_date":{"type":["string","null"]},"end_date":{"type":["string","null"]},"rewards_max_spread":{"type":["number","null"],"format":"double"},"rewards_min_size":{"type":["number","null"],"format":"double"},"native_daily_rate":{"type":["number","null"],"format":"double"},"sponsored_daily_rate":{"type":["number","null"],"format":"double"},"total_daily_rate":{"type":["number","null"],"format":"double"},"sponsors_count":{"type":["integer","null"],"format":"int32"}}},"ConditionMetricsResponse":{"type":"object","description":"Response type for condition metrics query","required":["condition_id","timeframe","volume_usd","fees","txns","unique_traders"],"properties":{"condition_id":{"type":"string"},"timeframe":{"type":"string"},"volume_usd":{"type":"number","format":"double"},"fees":{"type":"number","format":"double"},"txns":{"type":"integer","format":"int32"},"unique_traders":{"type":"integer","format":"int32"}}},"ConditionOrderbookRow":{"type":"object","required":["ts","position_id","condition_id","bids","asks","hash"],"properties":{"ts":{"type":"integer","format":"int64"},"position_id":{"type":"string"},"condition_id":{"type":"string"},"bids":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"asks":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"hash":{"type":"string"},"best_bid":{"type":["number","null"],"format":"double"},"best_ask":{"type":["number","null"],"format":"double"},"mid_price":{"type":["number","null"],"format":"double"},"spread":{"type":["number","null"],"format":"double"},"bid_liquidity_usd":{"type":["number","null"],"format":"double"},"ask_liquidity_usd":{"type":["number","null"],"format":"double"},"bid_levels":{"type":["integer","null"],"format":"int32"},"ask_levels":{"type":["integer","null"],"format":"int32"}}},"ConditionResolutionEvent":{"type":"object","description":"CTF ConditionResolution: positions become redeemable on the Conditional Tokens contract.","required":["id","hash","oracle_contract","condition_id","oracle"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":["integer","null"],"format":"int64","minimum":0},"confirmed_at":{"type":["integer","null"],"format":"int64","minimum":0},"received_at":{"type":["integer","null"],"format":"int64","minimum":0},"log_index":{"type":["integer","null"],"format":"int64","minimum":0},"block_index":{"type":["integer","null"],"format":"int64","minimum":0},"oracle_contract":{"type":"string"},"condition_id":{"type":"string"},"oracle":{"type":"string"},"proposed_outcome":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"EventMarket":{"type":"object","description":"Enriched market data for event API responses","properties":{"condition_id":{"type":"string","default":""},"id":{"type":["string","null"],"default":null},"title":{"type":["string","null"],"default":null},"question":{"type":"string","default":""},"market_slug":{"type":"string","default":""},"status":{"type":"string","default":""},"created_time":{"type":["integer","null"],"format":"int64","default":null},"end_time":{"type":["integer","null"],"format":"int64","default":null},"volume":{"type":["number","null"],"format":"double","default":null},"liquidity_usd":{"type":["number","null"],"format":"double","default":null},"volume_24hr":{"type":["number","null"],"format":"double","default":null},"image_url":{"type":["string","null"],"default":null},"market_maker_address":{"type":["string","null"],"default":null},"creator":{"type":["string","null"],"default":null},"category":{"type":["string","null"],"default":null},"accepting_orders":{"type":["boolean","null"],"default":null},"uma_resolution_status":{"type":["string","null"],"default":null},"clob_rewards":{"type":"array","items":{"$ref":"#/components/schemas/ClobReward"},"default":[]},"outcomes":{"type":"array","items":{"$ref":"#/components/schemas/EventMarketOutcome"},"default":[]},"winning_outcome":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/EventMarketOutcome"}],"default":null}}},"EventMarketChartDataPoint":{"type":"object","required":["v","t"],"properties":{"v":{"type":"number","format":"double"},"t":{"type":"integer","format":"int64","minimum":0}}},"EventMarketChartOutcome":{"type":"object","required":["condition_id","market_slug","question","title","data"],"properties":{"condition_id":{"type":"string"},"market_slug":{"type":"string"},"question":{"type":"string"},"title":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/EventMarketChartDataPoint"}}}},"EventMarketOutcome":{"type":"object","description":"Market outcome for event API responses","required":["name"],"properties":{"name":{"type":"string"},"price":{"type":["number","null"],"format":"double"},"position_id":{"type":["string","null"]},"outcome_index":{"type":["integer","null"],"format":"int32"}}},"EventMetricsResponse":{"type":"object","description":"Response type for event metrics query","required":["event_slug","timeframe","volume_usd","fees","txns","unique_traders"],"properties":{"event_slug":{"type":"string"},"timeframe":{"type":"string"},"volume_usd":{"type":"number","format":"double"},"fees":{"type":"number","format":"double"},"txns":{"type":"integer","format":"int32"},"unique_traders":{"type":"integer","format":"int32"}}},"EventPnlSortBy":{"type":"string","enum":["realized_pnl_usd","total_volume_usd","markets_traded","total_fees","realized_pnl_pct"]},"EventSortBy":{"type":"string","enum":["volume","txns","unique_traders","title","creation_date","start_date","end_date","relevance"]},"GlobalPnlSortBy":{"type":"string","enum":["realized_pnl_usd","buys","sells","redemptions","merges","avg_hold_time","markets_traded","events_traded","markets_won","volume_usd","fees","best_trade"]},"GlobalPnlTrader":{"type":"object","description":"Individual trader entry in the global PnL leaderboard","required":["trader"],"properties":{"trader":{"$ref":"#/components/schemas/TraderInfo"},"realized_pnl_usd":{"type":["number","null"],"format":"double"},"events_traded":{"type":["integer","null"],"format":"int64"},"markets_traded":{"type":["integer","null"],"format":"int64"},"markets_won":{"type":["integer","null"],"format":"int64"},"markets_lost":{"type":["integer","null"],"format":"int64"},"market_win_rate_pct":{"type":["number","null"],"format":"double"},"total_volume_usd":{"type":["number","null"],"format":"double"},"buy_volume_usd":{"type":["number","null"],"format":"double"},"sell_volume_usd":{"type":["number","null"],"format":"double"},"redemption_volume_usd":{"type":["number","null"],"format":"double"},"merge_volume_usd":{"type":["number","null"],"format":"double"},"total_buys":{"type":["integer","null"],"format":"int64"},"total_sells":{"type":["integer","null"],"format":"int64"},"total_redemptions":{"type":["integer","null"],"format":"int64"},"total_merges":{"type":["integer","null"],"format":"int64"},"total_trades":{"type":["integer","null"],"format":"int64"},"total_fees":{"type":["number","null"],"format":"double"},"avg_pnl_per_market":{"type":["number","null"],"format":"double"},"avg_pnl_per_trade":{"type":["number","null"],"format":"double"},"avg_hold_time_seconds":{"type":["number","null"],"format":"double"},"best_trade_pnl_usd":{"type":["number","null"],"format":"double"},"best_trade_condition_id":{"type":["string","null"]},"first_trade_at":{"type":["integer","null"],"format":"int64"},"last_trade_at":{"type":["integer","null"],"format":"int64"}}},"Holder":{"type":"object","description":"Individual holder data","required":["trader","shares"],"properties":{"trader":{"$ref":"#/components/schemas/Trader","description":"Trader profile information"},"shares":{"type":"string","description":"Position shares held (raw balance as string for precision)"},"shares_usd":{"type":["string","null"],"description":"USD value of shares (shares * latest price)"},"usd_balance":{"type":["string","null"],"description":"USD balance of wallet (USDe on Polygon)"},"pnl":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/HolderPnl","description":"Holder-level PnL, included only when `include_pnl=true`"}]}}},"HolderHistoryCandle":{"type":"object","description":"Holder statistics data point (single time bucket)","required":["t"],"properties":{"t":{"type":"integer","format":"int64"},"h":{"type":["integer","null"],"format":"int64"}}},"HolderPnl":{"type":"object","description":"Holder-level PnL data, included when `include_pnl=true`","properties":{"avg_entry_price":{"type":["number","null"],"format":"double"},"total_cost_usd":{"type":["string","null"]},"unrealized_pnl_usd":{"type":["string","null"]},"realized_sell_pnl_usd":{"type":["string","null"]},"avg_exit_price":{"type":["number","null"],"format":"double"},"buy_usd":{"type":["number","null"],"format":"double"},"sell_usd":{"type":["number","null"],"format":"double"},"total_buys":{"type":["integer","null"],"format":"int64"},"total_sells":{"type":["integer","null"],"format":"int64"},"total_fees":{"type":["number","null"],"format":"double"},"first_trade_at":{"type":["integer","null"],"format":"int64"},"last_trade_at":{"type":["integer","null"],"format":"int64"}}},"MarketHoldersResponse":{"type":"object","description":"Response for market (condition_id) holders endpoint","required":["condition_id","total_holders","outcomes"],"properties":{"condition_id":{"type":"string","description":"Market condition ID"},"question":{"type":["string","null"],"description":"Market question/title"},"slug":{"type":["string","null"],"description":"Market slug"},"total_holders":{"type":"integer","format":"int64","description":"Total unique holders across all outcomes (from holder_stats)"},"outcomes":{"type":"array","items":{"$ref":"#/components/schemas/OutcomeHolders"},"description":"Holders grouped by outcome"}}},"MarketMetadataOutcome":{"type":"object","description":"Market outcome with timeframe metrics (websocket API format)","required":["token_id","outcome"],"properties":{"token_id":{"type":"string"},"outcome":{"type":"string"},"last_price":{"type":["number","null"],"format":"double"},"last_probability":{"type":["number","null"],"format":"double"},"metrics":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/OutcomeTimeframeMetrics"},"propertyNames":{"type":"string"}}}},"MarketOutcome":{"type":"object","description":"Outcome for market API responses","properties":{"name":{"type":"string","default":""},"price":{"type":["number","null"],"format":"double","default":null},"position_id":{"type":["string","null"],"default":null},"outcome_index":{"type":["integer","null"],"format":"int32","default":null}}},"MarketPnlSortBy":{"type":"string","enum":["realized_pnl_usd","buy_usd","total_buys","total_fees","outcomes_traded","realized_pnl_pct"]},"MarketResponse":{"type":"object","description":"Formatted market response with structured metrics, tags, outcomes, and event","required":["condition_id","status"],"properties":{"condition_id":{"type":"string"},"id":{"type":["string","null"]},"market_slug":{"type":["string","null"]},"question":{"type":["string","null"]},"title":{"type":["string","null"]},"description":{"type":["string","null"]},"image_url":{"type":["string","null"]},"oracle":{"type":["string","null"]},"status":{"type":"string"},"created_time":{"type":["integer","null"],"format":"int64"},"start_time":{"type":["integer","null"],"format":"int64"},"game_start_time":{"type":["integer","null"],"format":"int64"},"closed_time":{"type":["integer","null"],"format":"int64"},"end_time":{"type":["integer","null"],"format":"int64"},"accepting_orders":{"type":["boolean","null"]},"uma_resolution_status":{"type":["string","null"]},"is_neg_risk":{"type":["boolean","null"]},"market_maker_address":{"type":["string","null"]},"creator":{"type":["string","null"]},"category":{"type":["string","null"]},"volume_usd":{"type":["number","null"],"format":"double"},"liquidity_usd":{"type":["number","null"],"format":"double"},"highest_probability":{"type":["number","null"],"format":"double"},"total_holders":{"type":["integer","null"],"format":"int64"},"total_daily_rate":{"type":["number","null"],"format":"double"},"winning_outcome":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/MarketOutcome"}]},"outcomes":{"type":"array","items":{"$ref":"#/components/schemas/MarketOutcome"}},"clob_rewards":{"type":"array","items":{"$ref":"#/components/schemas/ClobReward"}},"tags":{"type":"array","items":{"type":"string"}},"event_slug":{"type":["string","null"]},"resolution_source":{"type":["string","null"]},"metrics":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/SimpleTimeframeMetrics"},"propertyNames":{"type":"string"}},"relevance_score":{"type":["number","null"],"format":"double"}}},"MarketSortBy":{"type":"string","enum":["volume","txns","unique_traders","liquidity","holders","total_daily_rate","end_time","start_time","created_time","relevance"]},"MarketStatus":{"type":"string","enum":["open","closed","all"]},"MarketVolumeChartResponse":{"type":"object","required":["volumes","has_more"],"properties":{"volumes":{"type":"array","items":{"$ref":"#/components/schemas/MarketVolumeDataPoint"}},"has_more":{"type":"boolean"}}},"MarketVolumeDataPoint":{"type":"object","required":["t","v","yv","nv","tc","ytc","ntc"],"properties":{"t":{"type":"integer","format":"int64"},"v":{"type":"number","format":"double"},"yv":{"type":"number","format":"double"},"nv":{"type":"number","format":"double"},"tc":{"type":"integer","format":"int32"},"ytc":{"type":"integer","format":"int32"},"ntc":{"type":"integer","format":"int32"}}},"MergeTrade":{"type":"object","description":"Output payload for Merge trades (burn outcome tokens → receive collateral).","required":["id","hash","trader","usd_amount","exchange"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":["integer","null"],"format":"int64","minimum":0},"confirmed_at":{"type":["integer","null"],"format":"int64","minimum":0},"received_at":{"type":["integer","null"],"format":"int64","minimum":0},"log_index":{"type":["integer","null"],"format":"int64","minimum":0},"block_index":{"type":["integer","null"],"format":"int64","minimum":0},"trader":{"$ref":"#/components/schemas/TraderInfo"},"condition_id":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]},"usd_amount":{"type":"number","format":"double"},"position_details":{"type":"array","items":{"$ref":"#/components/schemas/PositionDetail"},"description":"Per-position burn amounts"},"exchange":{"$ref":"#/components/schemas/PolymarketExchange"}}},"MetricsTimeframe":{"type":"string","enum":["1m","5m","30m","1h","6h","24h","7d","30d"]},"NegRiskOutcomeReportedEvent":{"type":"object","description":"NegRisk Adapter: outcome reported for a neg-risk market question.","required":["id","hash","oracle_contract","condition_id"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":["integer","null"],"format":"int64","minimum":0},"confirmed_at":{"type":["integer","null"],"format":"int64","minimum":0},"received_at":{"type":["integer","null"],"format":"int64","minimum":0},"log_index":{"type":["integer","null"],"format":"int64","minimum":0},"block_index":{"type":["integer","null"],"format":"int64","minimum":0},"oracle_contract":{"type":"string"},"condition_id":{"type":"string"},"proposed_outcome":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"OrderFilledTrade":{"type":"object","description":"Output payload for OrderFilled and OrdersMatched trades (actual buy/sell).","required":["id","hash","trader","side","position_id","usd_amount","shares_amount","price","exchange"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":["integer","null"],"format":"int64","minimum":0},"confirmed_at":{"type":["integer","null"],"format":"int64","minimum":0},"received_at":{"type":["integer","null"],"format":"int64","minimum":0},"log_index":{"type":["integer","null"],"format":"int64","minimum":0},"block_index":{"type":["integer","null"],"format":"int64","minimum":0},"order_hash":{"type":["string","null"]},"trader":{"$ref":"#/components/schemas/TraderInfo"},"taker":{"type":["string","null"]},"side":{"type":"string"},"condition_id":{"type":["string","null"]},"position_id":{"type":"string"},"outcome":{"type":["string","null"]},"outcome_index":{"type":["integer","null"],"format":"int32","minimum":0},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]},"usd_amount":{"type":"number","format":"double"},"shares_amount":{"type":"number","format":"double"},"price":{"type":"number","format":"double"},"probability":{"type":["number","null"],"format":"double"},"fee":{"type":["number","null"],"format":"double"},"fee_shares":{"type":["number","null"],"format":"double"},"fee_pct":{"type":["number","null"],"format":"double"},"exchange":{"$ref":"#/components/schemas/PolymarketExchange"}}},"OrderbookHistoryRow":{"type":"object","required":["ts","position_id","condition_id","bids","asks","hash"],"properties":{"ts":{"type":"integer","format":"int64"},"position_id":{"type":"string"},"condition_id":{"type":"string"},"bids":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"asks":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"hash":{"type":"string"},"best_bid":{"type":["number","null"],"format":"double"},"best_ask":{"type":["number","null"],"format":"double"},"mid_price":{"type":["number","null"],"format":"double"},"spread":{"type":["number","null"],"format":"double"},"bid_liquidity_usd":{"type":["number","null"],"format":"double"},"ask_liquidity_usd":{"type":["number","null"],"format":"double"},"bid_levels":{"type":["integer","null"],"format":"int32"},"ask_levels":{"type":["integer","null"],"format":"int32"}}},"OrderbookLevel":{"type":"object","description":"A single price level in a bids/asks array.","required":["p","s"],"properties":{"p":{"type":"string","description":"Price as a decimal string"},"s":{"type":"string","description":"Size as a decimal string"}}},"OrderbookSnapshotRow":{"type":"object","required":["ts","position_id","condition_id","bids","asks","hash"],"properties":{"ts":{"type":"integer","format":"int64"},"position_id":{"type":"string"},"condition_id":{"type":"string"},"bids":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"asks":{"type":"array","items":{"$ref":"#/components/schemas/OrderbookLevel"}},"hash":{"type":"string"},"best_bid":{"type":["number","null"],"format":"double"},"best_ask":{"type":["number","null"],"format":"double"},"mid_price":{"type":["number","null"],"format":"double"},"spread":{"type":["number","null"],"format":"double"},"bid_liquidity_usd":{"type":["number","null"],"format":"double"},"ask_liquidity_usd":{"type":["number","null"],"format":"double"},"bid_levels":{"type":["integer","null"],"format":"int32"},"ask_levels":{"type":["integer","null"],"format":"int32"}}},"OutcomeHolders":{"type":"object","description":"Holder data grouped by outcome for market-level queries","required":["outcome_index","outcome_name","position_id","total_holders","holders"],"properties":{"outcome_index":{"type":"integer","format":"int32","description":"Outcome index (0, 1, etc.)"},"outcome_name":{"type":"string","description":"Outcome name (Yes, No, etc.)"},"position_id":{"type":"string","description":"Position ID for this outcome"},"price":{"type":["number","null"],"format":"double","description":"Current price/probability"},"total_holders":{"type":"integer","format":"int64","description":"Total holders count from holder_stats"},"holders":{"type":"array","items":{"$ref":"#/components/schemas/Holder"},"description":"Top holders for this outcome"}}},"OutcomeIndex":{"type":"string","enum":["0","1"]},"OutcomeTimeframeMetrics":{"type":"object","properties":{"volume":{"type":"number","format":"double","default":0.0},"buy_volume":{"type":"number","format":"double","default":0.0},"sell_volume":{"type":"number","format":"double","default":0.0},"txns":{"type":"integer","format":"int32","default":0},"buys":{"type":"integer","format":"int32","default":0},"sells":{"type":"integer","format":"int32","default":0},"price_open":{"type":["number","null"],"format":"double","default":null},"price_close":{"type":["number","null"],"format":"double","default":null},"price_high":{"type":["number","null"],"format":"double","default":null},"price_low":{"type":["number","null"],"format":"double","default":null},"probability_open":{"type":["number","null"],"format":"double","default":null},"probability_close":{"type":["number","null"],"format":"double","default":null},"probability_high":{"type":["number","null"],"format":"double","default":null},"probability_low":{"type":["number","null"],"format":"double","default":null},"price_change_percent":{"type":["number","null"],"format":"double","default":null}}},"PaginationMeta":{"type":"object","description":"Pagination metadata to include in API responses","required":["has_more"],"properties":{"has_more":{"type":"boolean","description":"Whether there are more results available"},"pagination_key":{"type":["string","null"],"description":"Pagination key for the next page (if has_more is true)"}}},"PnlCandleEntry":{"type":"object","description":"A single PnL candle entry","required":["t","pnl"],"properties":{"t":{"type":"integer","format":"int64","description":"Timestamp in epoch seconds (start of bucket window)"},"pnl":{"type":"number","format":"double","description":"Realized PnL in this bucket (USD)"}}},"PnlCandleResolution":{"type":"string","enum":["1m","1h","1d"]},"PnlCandleTimeframe":{"type":"string","enum":["1d","7d","30d","lifetime"]},"PnlTimeframe":{"type":"string","enum":["1d","7d","30d","lifetime"]},"PolymarketEvent":{"type":"object","description":"A Polymarket event from the Gamma API","properties":{"id":{"type":"string","default":""},"event_slug":{"type":["string","null"],"default":null},"title":{"type":["string","null"],"default":null},"ticker":{"type":["string","null"],"default":null},"description":{"type":["string","null"],"default":null},"resolution_source":{"type":["string","null"],"default":null},"category":{"type":["string","null"],"default":null},"image_url":{"type":["string","null"],"default":null},"market_count":{"type":"integer","format":"int32","default":0},"created_time":{"type":["integer","null"],"format":"int64","default":null},"closed_time":{"type":["integer","null"],"format":"int64","default":null},"start_time":{"type":["integer","null"],"format":"int64","default":null},"end_time":{"type":["integer","null"],"format":"int64","default":null},"neg_risk":{"type":"boolean","default":false},"neg_risk_market_id":{"type":["string","null"],"default":null},"game_status":{"type":["string","null"],"default":null},"show_market_images":{"type":"boolean","default":false},"status":{"type":["string","null"],"description":"Event status: \"open\" or \"closed\"","default":null},"metrics":{"type":"object","default":{},"additionalProperties":{"$ref":"#/components/schemas/SimpleTimeframeMetrics"},"propertyNames":{"type":"string"}},"tags":{"type":"array","items":{"$ref":"#/components/schemas/PolymarketTag"},"default":[]},"markets":{"type":"array","items":{"$ref":"#/components/schemas/EventMarket"},"default":[]},"series":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PolymarketSeries"}],"default":null}}},"PolymarketExchange":{"type":"string","description":"Polymarket exchange contract types","enum":["CTFExchange","NegRiskExchange","ConditionalTokens","NegRiskAdapter","Unknown"]},"PolymarketSeries":{"type":"object","description":"A Polymarket series from the Gamma API\nSeries are parent groupings above events (e.g., \"NBA Season 2024-25\")","properties":{"id":{"type":"string","default":""},"slug":{"type":["string","null"],"default":null},"ticker":{"type":["string","null"],"default":null},"title":{"type":["string","null"],"default":null},"description":{"type":["string","null"],"default":null},"series_type":{"type":["string","null"],"default":null},"recurrence":{"type":["string","null"],"default":null},"layout":{"type":["string","null"],"default":null},"image_url":{"type":["string","null"],"default":null},"icon_url":{"type":["string","null"],"default":null},"active":{"type":"boolean","default":false},"closed":{"type":"boolean","default":false},"archived":{"type":"boolean","default":false},"featured":{"type":"boolean","default":false},"restricted":{"type":"boolean","default":false},"pyth_token_id":{"type":["string","null"],"default":null},"cg_asset_name":{"type":["string","null"],"default":null},"start_date":{"type":["integer","null"],"format":"int64","default":null},"event_count":{"type":"integer","format":"int32","default":0}}},"PolymarketTag":{"type":"object","description":"A Polymarket tag from the Gamma API","properties":{"id":{"type":"string","default":""},"label":{"type":"string","default":""},"slug":{"type":["string","null"],"default":null}}},"PolymarketUserProfile":{"type":"object","description":"Polymarket user profile (public API format)","required":["proxy_wallet","display_username_public","verified_badge","is_creator","is_mod"],"properties":{"proxy_wallet":{"type":"string"},"name":{"type":["string","null"]},"pseudonym":{"type":["string","null"]},"bio":{"type":["string","null"]},"profile_image":{"type":["string","null"]},"x_username":{"type":["string","null"]},"display_username_public":{"type":"boolean"},"verified_badge":{"type":"boolean"},"user_id":{"type":["string","null"]},"is_creator":{"type":"boolean"},"is_mod":{"type":"boolean"},"created_at":{"type":["string","null"]}}},"PositionChartDataPoint":{"type":"object","required":["v","t"],"properties":{"v":{"type":"number","format":"double"},"t":{"type":"integer","format":"int64","minimum":0}}},"PositionChartOutcome":{"type":"object","required":["position_id","name","outcome_index","data"],"properties":{"position_id":{"type":"string"},"name":{"type":"string"},"outcome_index":{"type":"integer","format":"int32"},"data":{"type":"array","items":{"$ref":"#/components/schemas/PositionChartDataPoint"}}}},"PositionDetail":{"type":"object","description":"Per-position detail for Split/Merge/Redemption trades.","required":["position_id","outcome_index","amount"],"properties":{"position_id":{"type":"string","description":"ERC1155 position ID"},"outcome_index":{"type":"integer","format":"int32","description":"Outcome index (0 = Yes, 1 = No for binary)","minimum":0},"outcome":{"type":["string","null"],"description":"Outcome name (e.g. \"Yes\", \"No\") — enriched from market metadata"},"amount":{"type":"string","description":"Amount of shares created/burned/redeemed for this position"}}},"PositionHoldersResponse":{"type":"object","description":"Response for position (position_id) holders endpoint","required":["position_id","total_holders","holders"],"properties":{"position_id":{"type":"string","description":"Position ID (ERC1155 token ID)"},"condition_id":{"type":["string","null"],"description":"Condition ID this position belongs to"},"outcome_name":{"type":["string","null"],"description":"Outcome name"},"outcome_index":{"type":["integer","null"],"format":"int32","description":"Outcome index"},"price":{"type":["number","null"],"format":"double","description":"Current price"},"total_holders":{"type":"integer","format":"int64","description":"Total holders count from holder_stats"},"holders":{"type":"array","items":{"$ref":"#/components/schemas/Holder"},"description":"Top holders"}}},"PositionMetricsResponse":{"type":"object","description":"Response type for position metrics query","required":["position_id","condition_id","timeframe","volume_usd","buy_volume_usd","sell_volume_usd","fees","txns","buys","sells","unique_traders","price_open","price_high","price_low","price_close"],"properties":{"position_id":{"type":"string"},"condition_id":{"type":"string"},"timeframe":{"type":"string"},"volume_usd":{"type":"number","format":"double"},"buy_volume_usd":{"type":"number","format":"double"},"sell_volume_usd":{"type":"number","format":"double"},"fees":{"type":"number","format":"double"},"txns":{"type":"integer","format":"int32"},"buys":{"type":"integer","format":"int32"},"sells":{"type":"integer","format":"int32"},"unique_traders":{"type":"integer","format":"int32"},"price_open":{"type":"number","format":"double"},"price_high":{"type":"number","format":"double"},"price_low":{"type":"number","format":"double"},"price_close":{"type":"number","format":"double"}}},"PositionPnlSortBy":{"type":"string","enum":["realized_pnl_usd","buy_usd","sell_usd","redemption_usd","total_buys","total_sells","total_shares_bought","total_shares_sold","avg_entry_price","avg_exit_price","total_fees","first_trade_at","last_trade_at","current_value","realized_pnl_pct","title"]},"PositionStatus":{"type":"string","description":"Position status filter for open/closed positions.","enum":["open","closed"]},"PositionVolumeChartResponse":{"type":"object","required":["volumes","has_more"],"properties":{"volumes":{"type":"array","items":{"$ref":"#/components/schemas/PositionVolumeDataPoint"}},"has_more":{"type":"boolean"}}},"PositionVolumeDataPoint":{"type":"object","required":["t","v","bv","sv","tc","btc","stc"],"properties":{"t":{"type":"integer","format":"int64"},"v":{"type":"number","format":"double"},"bv":{"type":"number","format":"double"},"sv":{"type":"number","format":"double"},"tc":{"type":"integer","format":"int32"},"btc":{"type":"integer","format":"int32"},"stc":{"type":"integer","format":"int32"}}},"PositionsConvertedTrade":{"type":"object","description":"Output payload for NegRisk PositionsConverted trades\n(convert NO-position tokens → YES tokens + collateral).","required":["id","hash","trader","market_id","index_set","shares_amount","exchange"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":["integer","null"],"format":"int64","minimum":0},"confirmed_at":{"type":["integer","null"],"format":"int64","minimum":0},"received_at":{"type":["integer","null"],"format":"int64","minimum":0},"log_index":{"type":["integer","null"],"format":"int64","minimum":0},"block_index":{"type":["integer","null"],"format":"int64","minimum":0},"trader":{"$ref":"#/components/schemas/TraderInfo"},"market_id":{"type":"string","description":"NegRisk umbrella market ID"},"index_set":{"type":"string","description":"Bitmask of question indices whose NO tokens are being converted"},"shares_amount":{"type":"number","format":"double"},"exchange":{"$ref":"#/components/schemas/PolymarketExchange"}}},"PredictionCandlestickBar":{"type":"object","required":["t"],"properties":{"l":{"type":["number","null"],"format":"double"},"h":{"type":["number","null"],"format":"double"},"o":{"type":["number","null"],"format":"double"},"c":{"type":["number","null"],"format":"double"},"v":{"type":["number","null"],"format":"double"},"t":{"type":"integer","format":"int64","minimum":0},"tc":{"type":["integer","null"],"format":"int32"},"m":{"type":["number","null"],"format":"double"}}},"PriceJump":{"type":"object","required":["from","to","price_before","price_after","change_pct","direction","volume","trades_count","condition_id"],"properties":{"from":{"type":"integer","format":"int64","minimum":0},"to":{"type":"integer","format":"int64","minimum":0},"price_before":{"type":"number","format":"double"},"price_after":{"type":"number","format":"double"},"change_pct":{"type":"number","format":"double"},"direction":{"type":"string"},"volume":{"type":"number","format":"double"},"trades_count":{"type":"integer","format":"int32"},"condition_id":{"type":"string"}}},"QuestionEmergencyResolvedEvent":{"type":"object","description":"UMA CTF Adapter: admin emergency resolution.","required":["id","hash","oracle_contract","condition_id"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":["integer","null"],"format":"int64","minimum":0},"confirmed_at":{"type":["integer","null"],"format":"int64","minimum":0},"received_at":{"type":["integer","null"],"format":"int64","minimum":0},"log_index":{"type":["integer","null"],"format":"int64","minimum":0},"block_index":{"type":["integer","null"],"format":"int64","minimum":0},"oracle_contract":{"type":"string"},"condition_id":{"type":"string"},"proposed_outcome":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"QuestionFlaggedEvent":{"type":"object","description":"UMA CTF Adapter: market flagged for emergency resolution.","required":["id","hash","oracle_contract","condition_id"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":["integer","null"],"format":"int64","minimum":0},"confirmed_at":{"type":["integer","null"],"format":"int64","minimum":0},"received_at":{"type":["integer","null"],"format":"int64","minimum":0},"log_index":{"type":["integer","null"],"format":"int64","minimum":0},"block_index":{"type":["integer","null"],"format":"int64","minimum":0},"oracle_contract":{"type":"string"},"condition_id":{"type":"string"},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"QuestionInitializedEvent":{"type":"object","description":"UMA CTF Adapter: questionID first initialized on-chain.","required":["id","hash","oracle_contract","condition_id","creator","reward_token","reward","proposal_bond"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":["integer","null"],"format":"int64","minimum":0},"confirmed_at":{"type":["integer","null"],"format":"int64","minimum":0},"received_at":{"type":["integer","null"],"format":"int64","minimum":0},"log_index":{"type":["integer","null"],"format":"int64","minimum":0},"block_index":{"type":["integer","null"],"format":"int64","minimum":0},"oracle_contract":{"type":"string"},"condition_id":{"type":"string"},"creator":{"type":"string"},"reward_token":{"type":"string"},"reward":{"type":"string"},"proposal_bond":{"type":"string"},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"QuestionPausedEvent":{"type":"object","description":"UMA CTF Adapter: market paused by admin.","required":["id","hash","oracle_contract","condition_id"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":["integer","null"],"format":"int64","minimum":0},"confirmed_at":{"type":["integer","null"],"format":"int64","minimum":0},"received_at":{"type":["integer","null"],"format":"int64","minimum":0},"log_index":{"type":["integer","null"],"format":"int64","minimum":0},"block_index":{"type":["integer","null"],"format":"int64","minimum":0},"oracle_contract":{"type":"string"},"condition_id":{"type":"string"},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"QuestionResetEvent":{"type":"object","description":"UMA CTF Adapter: dispute succeeded, market returns to active.","required":["id","hash","oracle_contract","condition_id"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":["integer","null"],"format":"int64","minimum":0},"confirmed_at":{"type":["integer","null"],"format":"int64","minimum":0},"received_at":{"type":["integer","null"],"format":"int64","minimum":0},"log_index":{"type":["integer","null"],"format":"int64","minimum":0},"block_index":{"type":["integer","null"],"format":"int64","minimum":0},"oracle_contract":{"type":"string"},"condition_id":{"type":"string"},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"QuestionResolvedEvent":{"type":"object","description":"UMA CTF Adapter: market resolved with definitive outcome.","required":["id","hash","oracle_contract","condition_id","settled_price"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":["integer","null"],"format":"int64","minimum":0},"confirmed_at":{"type":["integer","null"],"format":"int64","minimum":0},"received_at":{"type":["integer","null"],"format":"int64","minimum":0},"log_index":{"type":["integer","null"],"format":"int64","minimum":0},"block_index":{"type":["integer","null"],"format":"int64","minimum":0},"oracle_contract":{"type":"string"},"condition_id":{"type":"string"},"settled_price":{"type":"integer","format":"int64"},"proposed_outcome":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"QuestionUnflaggedEvent":{"type":"object","description":"UMA CTF Adapter: flag removed.","required":["id","hash","oracle_contract","condition_id"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":["integer","null"],"format":"int64","minimum":0},"confirmed_at":{"type":["integer","null"],"format":"int64","minimum":0},"received_at":{"type":["integer","null"],"format":"int64","minimum":0},"log_index":{"type":["integer","null"],"format":"int64","minimum":0},"block_index":{"type":["integer","null"],"format":"int64","minimum":0},"oracle_contract":{"type":"string"},"condition_id":{"type":"string"},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"QuestionUnpausedEvent":{"type":"object","description":"UMA CTF Adapter: market unpaused.","required":["id","hash","oracle_contract","condition_id"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":["integer","null"],"format":"int64","minimum":0},"confirmed_at":{"type":["integer","null"],"format":"int64","minimum":0},"received_at":{"type":["integer","null"],"format":"int64","minimum":0},"log_index":{"type":["integer","null"],"format":"int64","minimum":0},"block_index":{"type":["integer","null"],"format":"int64","minimum":0},"oracle_contract":{"type":"string"},"condition_id":{"type":"string"},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]}}},"RedemptionTrade":{"type":"object","description":"Output payload for Redemption trades (payout after market resolution).","required":["id","hash","trader","usd_amount","exchange"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":["integer","null"],"format":"int64","minimum":0},"confirmed_at":{"type":["integer","null"],"format":"int64","minimum":0},"received_at":{"type":["integer","null"],"format":"int64","minimum":0},"log_index":{"type":["integer","null"],"format":"int64","minimum":0},"block_index":{"type":["integer","null"],"format":"int64","minimum":0},"trader":{"$ref":"#/components/schemas/TraderInfo"},"condition_id":{"type":["string","null"]},"outcome":{"type":["string","null"]},"outcome_index":{"type":["integer","null"],"format":"int32","minimum":0},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]},"usd_amount":{"type":"number","format":"double"},"winning_outcome_index":{"type":["integer","null"],"format":"int32","minimum":0},"position_details":{"type":"array","items":{"$ref":"#/components/schemas/PositionDetail"},"description":"Per-position burn amounts"},"exchange":{"$ref":"#/components/schemas/PolymarketExchange"}}},"RegisterTokenTrade":{"type":"object","description":"Output payload for RegisterToken events (YES/NO token pair registered for a condition).","required":["id","hash","condition_id","token0","token1","exchange"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":["integer","null"],"format":"int64","minimum":0},"confirmed_at":{"type":["integer","null"],"format":"int64","minimum":0},"received_at":{"type":["integer","null"],"format":"int64","minimum":0},"log_index":{"type":["integer","null"],"format":"int64","minimum":0},"block_index":{"type":["integer","null"],"format":"int64","minimum":0},"condition_id":{"type":"string"},"token0":{"type":"string"},"token1":{"type":"string"},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]},"exchange":{"$ref":"#/components/schemas/PolymarketExchange"}}},"SearchResponse":{"type":"object","properties":{"events":{"type":["array","null"],"items":{"$ref":"#/components/schemas/PolymarketEvent"}},"events_pagination":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PaginationMeta"}]},"markets":{"type":["array","null"],"items":{"$ref":"#/components/schemas/MarketResponse"}},"markets_pagination":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PaginationMeta"}]},"traders":{"type":["array","null"],"items":{"$ref":"#/components/schemas/TraderWithPnl"}},"traders_pagination":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PaginationMeta"}]}}},"SearchSortBy":{"type":"string","description":"Combined sort options valid for both events and markets in search","enum":["volume","txns","unique_traders","relevance","title","creation_date","start_date","end_date","liquidity","holders","end_time","start_time","created_time"]},"SimpleTimeframeMetrics":{"type":"object","properties":{"volume":{"type":"number","format":"double","default":0.0},"fees":{"type":"number","format":"double","default":0.0},"txns":{"type":"integer","format":"int32","default":0},"unique_traders":{"type":"integer","format":"int32","default":0}}},"SortDirection":{"type":"string","enum":["asc","desc"]},"SpikeDirection":{"type":"string","description":"Direction filter for spike webhooks.","enum":["up","down","both"]},"SplitTrade":{"type":"object","description":"Output payload for Split trades (deposit collateral → receive outcome tokens).","required":["id","hash","trader","usd_amount","exchange"],"properties":{"id":{"type":"string"},"hash":{"type":"string"},"block":{"type":["integer","null"],"format":"int64","minimum":0},"confirmed_at":{"type":["integer","null"],"format":"int64","minimum":0},"received_at":{"type":["integer","null"],"format":"int64","minimum":0},"log_index":{"type":["integer","null"],"format":"int64","minimum":0},"block_index":{"type":["integer","null"],"format":"int64","minimum":0},"trader":{"$ref":"#/components/schemas/TraderInfo"},"condition_id":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]},"usd_amount":{"type":"number","format":"double"},"position_details":{"type":"array","items":{"$ref":"#/components/schemas/PositionDetail"},"description":"Per-position mint amounts"},"exchange":{"$ref":"#/components/schemas/PolymarketExchange"}}},"SpreadRow":{"type":"object","description":"Lightweight row — derived metrics only, no bids/asks JSONB.","required":["ts","position_id","condition_id"],"properties":{"ts":{"type":"integer","format":"int64"},"position_id":{"type":"string"},"condition_id":{"type":"string"},"best_bid":{"type":["number","null"],"format":"double"},"best_ask":{"type":["number","null"],"format":"double"},"mid_price":{"type":["number","null"],"format":"double"},"spread":{"type":["number","null"],"format":"double"},"bid_liquidity_usd":{"type":["number","null"],"format":"double"},"ask_liquidity_usd":{"type":["number","null"],"format":"double"},"bid_levels":{"type":["integer","null"],"format":"int32"},"ask_levels":{"type":["integer","null"],"format":"int32"}}},"TokenOutcome":{"type":"object","description":"Token outcome (position)","required":["token_id","outcome"],"properties":{"token_id":{"type":"string"},"outcome":{"type":"string"}}},"TradeEvent":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/OrderFilledTrade"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["OrderFilled"]}}}]},{"allOf":[{"$ref":"#/components/schemas/OrderFilledTrade"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["OrdersMatched"]}}}]},{"allOf":[{"$ref":"#/components/schemas/RedemptionTrade"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Redemption"]}}}]},{"allOf":[{"$ref":"#/components/schemas/MergeTrade"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Merge"]}}}]},{"allOf":[{"$ref":"#/components/schemas/SplitTrade"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Split"]}}}]},{"allOf":[{"$ref":"#/components/schemas/PositionsConvertedTrade"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["PositionsConverted"]}}}]},{"allOf":[{"$ref":"#/components/schemas/CancelledTrade"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Cancelled"]}}}]},{"allOf":[{"$ref":"#/components/schemas/QuestionInitializedEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Initialization"]}}}]},{"allOf":[{"$ref":"#/components/schemas/AssertionMadeEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Proposal"]}}}]},{"allOf":[{"$ref":"#/components/schemas/AssertionDisputedEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Dispute"]}}}]},{"allOf":[{"$ref":"#/components/schemas/AssertionSettledEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Settled"]}}}]},{"allOf":[{"$ref":"#/components/schemas/QuestionResolvedEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Resolution"]}}}]},{"allOf":[{"$ref":"#/components/schemas/ConditionResolutionEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["ConditionResolution"]}}}]},{"allOf":[{"$ref":"#/components/schemas/QuestionResetEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Reset"]}}}]},{"allOf":[{"$ref":"#/components/schemas/QuestionFlaggedEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Flag"]}}}]},{"allOf":[{"$ref":"#/components/schemas/QuestionUnflaggedEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Unflag"]}}}]},{"allOf":[{"$ref":"#/components/schemas/QuestionPausedEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Pause"]}}}]},{"allOf":[{"$ref":"#/components/schemas/QuestionUnpausedEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Unpause"]}}}]},{"allOf":[{"$ref":"#/components/schemas/QuestionEmergencyResolvedEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["ManualResolution"]}}}]},{"allOf":[{"$ref":"#/components/schemas/NegRiskOutcomeReportedEvent"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["NegRiskOutcomeReported"]}}}]},{"allOf":[{"$ref":"#/components/schemas/RegisterTokenTrade"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["RegisterToken"]}}}]},{"allOf":[{"$ref":"#/components/schemas/ApprovalTrade"},{"type":"object","required":["trade_type"],"properties":{"trade_type":{"type":"string","enum":["Approval"]}}}]}],"description":"Tagged enum for all trade types — serializes with `\"trade_type\": \"...\"` discriminator\nand only includes fields relevant to each type."},"TradeSide":{"type":"string","enum":["0","1"]},"TradeType":{"type":"string","enum":["0","1","2","3","4","5","6"]},"Trader":{"type":"object","description":"Trader profile info embedded in API responses\n\nCorresponds to SQL function: `pm_build_trader(address, name, pseudonym, profile_image, x_username, verified_badge)`\n\nUsed in:\n- holders endpoints (market/event holders)\n- trades endpoints\n- leaderboard endpoints","required":["address"],"properties":{"address":{"type":"string"},"name":{"type":["string","null"]},"pseudonym":{"type":["string","null"]},"profile_image":{"type":["string","null"]},"x_username":{"type":["string","null"]},"verified_badge":{"type":"boolean"}}},"TraderEventPnlEntry":{"type":"object","description":"Event-level PnL entry","properties":{"event_slug":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"markets_traded":{"type":["integer","null"],"format":"int64"},"outcomes_traded":{"type":["integer","null"],"format":"int64"},"total_buys":{"type":["integer","null"],"format":"int64"},"total_sells":{"type":["integer","null"],"format":"int64"},"total_redemptions":{"type":["integer","null"],"format":"int64"},"total_merges":{"type":["integer","null"],"format":"int64"},"total_volume_usd":{"type":["number","null"],"format":"double"},"buy_usd":{"type":["number","null"],"format":"double"},"sell_usd":{"type":["number","null"],"format":"double"},"redemption_usd":{"type":["number","null"],"format":"double"},"merge_usd":{"type":["number","null"],"format":"double"},"realized_pnl_usd":{"type":["number","null"],"format":"double"},"winning_markets":{"type":["integer","null"],"format":"int64"},"losing_markets":{"type":["integer","null"],"format":"int64"},"total_fees":{"type":["number","null"],"format":"double"},"first_trade_at":{"type":["integer","null"],"format":"int64"},"last_trade_at":{"type":["integer","null"],"format":"int64"}}},"TraderInfo":{"type":"object","description":"Trader profile info - backwards compatibility","required":["address"],"properties":{"address":{"type":"string"},"name":{"type":["string","null"]},"pseudonym":{"type":["string","null"]},"profile_image":{"type":["string","null"]},"x_username":{"type":["string","null"]},"verified_badge":{"type":"boolean"}}},"TraderMarketPnlEntry":{"type":"object","description":"Market-level PnL entry","properties":{"condition_id":{"type":["string","null"]},"event_slug":{"type":["string","null"]},"question":{"type":["string","null"]},"image_url":{"type":["string","null"]},"outcomes_traded":{"type":["integer","null"],"format":"int64"},"total_buys":{"type":["integer","null"],"format":"int64"},"total_sells":{"type":["integer","null"],"format":"int64"},"total_redemptions":{"type":["integer","null"],"format":"int64"},"total_merges":{"type":["integer","null"],"format":"int64"},"buy_usd":{"type":["number","null"],"format":"double"},"sell_usd":{"type":["number","null"],"format":"double"},"redemption_usd":{"type":["number","null"],"format":"double"},"merge_usd":{"type":["number","null"],"format":"double"},"realized_pnl_usd":{"type":["number","null"],"format":"double"},"winning_outcomes":{"type":["integer","null"],"format":"int64"},"total_fees":{"type":["number","null"],"format":"double"},"first_trade_at":{"type":["integer","null"],"format":"int64"},"last_trade_at":{"type":["integer","null"],"format":"int64"}}},"TraderOutcomePnlEntry":{"type":"object","description":"Outcome-level PnL entry (per outcome token / position_id)","properties":{"position_id":{"type":["string","null"]},"condition_id":{"type":["string","null"]},"market_slug":{"type":["string","null"]},"event_slug":{"type":["string","null"]},"title":{"type":["string","null"]},"image_url":{"type":["string","null"]},"outcome":{"type":["string","null"]},"outcome_index":{"type":["integer","null"],"format":"int32"},"won":{"type":["boolean","null"],"description":"TRUE = won, FALSE = lost, NULL = open or sold before resolution"},"total_buys":{"type":["integer","null"],"format":"int64"},"total_sells":{"type":["integer","null"],"format":"int64"},"total_shares_bought":{"type":["number","null"],"format":"double"},"total_shares_sold":{"type":["number","null"],"format":"double"},"total_buy_usd":{"type":["number","null"],"format":"double"},"total_sell_usd":{"type":["number","null"],"format":"double"},"redemption_usd":{"type":["number","null"],"format":"double","description":"Payout on redemption (non-zero only if won)"},"avg_entry_price":{"type":["number","null"],"format":"double","description":"VWAP price paid per share across all buys (0–1)"},"avg_exit_price":{"type":["number","null"],"format":"double","description":"VWAP price received per share across all sells (0–1)"},"realized_pnl_usd":{"type":["number","null"],"format":"double"},"total_fees":{"type":["number","null"],"format":"double"},"first_trade_at":{"type":["integer","null"],"format":"int64"},"last_trade_at":{"type":["integer","null"],"format":"int64"},"current_price":{"type":["number","null"],"format":"double","description":"Last traded price for this outcome (0–1). NULL if market_outcomes has no price yet."},"current_shares_balance":{"type":["number","null"],"format":"double","description":"Current shares held: balance / 1e6."},"current_value":{"type":["number","null"],"format":"double","description":"Estimated current USD value of held shares: (balance / 1e6) * current_price.\nOnly meaningful for open positions (balance above dust threshold)."},"realized_pnl_pct":{"type":["number","null"],"format":"double","description":"Realized PnL as a percentage of total spend: (realized_pnl_usd / total_buy_usd) * 100.\nNULL when total_buy_usd = 0."}}},"TraderPnlSummary":{"type":"object","description":"Trader's global PnL summary (single trader)","properties":{"trader":{"type":["string","null"]},"realized_pnl_usd":{"type":["number","null"],"format":"double"},"events_traded":{"type":["integer","null"],"format":"int64"},"markets_traded":{"type":["integer","null"],"format":"int64"},"total_buys":{"type":["integer","null"],"format":"int64"},"total_sells":{"type":["integer","null"],"format":"int64"},"total_redemptions":{"type":["integer","null"],"format":"int64"},"total_merges":{"type":["integer","null"],"format":"int64"},"total_volume_usd":{"type":["number","null"],"format":"double"},"buy_volume_usd":{"type":["number","null"],"format":"double"},"sell_volume_usd":{"type":["number","null"],"format":"double"},"redemption_volume_usd":{"type":["number","null"],"format":"double"},"merge_volume_usd":{"type":["number","null"],"format":"double"},"markets_won":{"type":["integer","null"],"format":"int64"},"markets_lost":{"type":["integer","null"],"format":"int64"},"market_win_rate_pct":{"type":["number","null"],"format":"double"},"avg_pnl_per_market":{"type":["number","null"],"format":"double"},"avg_pnl_per_trade":{"type":["number","null"],"format":"double"},"avg_hold_time_seconds":{"type":["number","null"],"format":"double"},"total_fees":{"type":["number","null"],"format":"double"},"best_trade_pnl_usd":{"type":["number","null"],"format":"double"},"best_trade_condition_id":{"type":["string","null"]},"first_trade_at":{"type":["integer","null"],"format":"int64"},"last_trade_at":{"type":["integer","null"],"format":"int64"}}},"TraderVolumeChartResponse":{"type":"object","required":["volumes","has_more"],"properties":{"volumes":{"type":"array","items":{"$ref":"#/components/schemas/TraderVolumeDataPoint"}},"has_more":{"type":"boolean"}}},"TraderVolumeDataPoint":{"type":"object","required":["t","v","bv","sv","tc","btc","stc"],"properties":{"t":{"type":"integer","format":"int64"},"v":{"type":"number","format":"double"},"bv":{"type":"number","format":"double"},"sv":{"type":"number","format":"double"},"tc":{"type":"integer","format":"int32"},"btc":{"type":"integer","format":"int32"},"stc":{"type":"integer","format":"int32"}}},"TraderWithPnl":{"allOf":[{"$ref":"#/components/schemas/Trader"},{"type":"object","properties":{"pnl":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TraderPnlSummary"}]}}}]},"WebhookAssetSymbol":{"type":"string","description":"Crypto asset symbols accepted by `asset_price_tick` and `asset_price_window_update` filters.","enum":["BTC","ETH","SOL","XRP","DOGE","BNB","HYPE"]},"WebhookTimeframe":{"type":"string","description":"Timeframe values accepted by webhook metric, milestone, spike, and asset-price filters.","enum":["1m","5m","15m","30m","1h","4h","6h","1d","24h","7d","30d"]}}}} \ No newline at end of file diff --git a/openapi/ws-alerts.json b/openapi/ws-alerts.json new file mode 100644 index 0000000..ef6ffeb --- /dev/null +++ b/openapi/ws-alerts.json @@ -0,0 +1,9663 @@ +{ + "asyncapi": "3.0.0", + "info": { + "title": "Polymarket WebSocket Alerts API", + "version": "1.0.0", + "description": "Real-time alert subscriptions delivered over WebSocket. Same events and filters as HTTP webhooks — ephemeral delivery that ends when the connection closes. The alerts WS path shares the same event/filter pipeline as HTTP webhooks; only delivery transport differs.\n\n## Connection\n\n```\nwss://api.struct.to/ws/alerts\n```\n\nAll alert channels share the same physical endpoint — each channel in this spec represents one event type you can subscribe to.\n\n## Authentication\n\nTwo authentication methods are supported:\n\n**1. API key** (`apiKeyHeader` or `apiKeyQuery`):\n```\nX-Api-Key: \n```\nor\n```\nwss://api.struct.to/ws/alerts?api-key=\n```\n\n**2. Public-key JWT combo** (`publicKeyJwt`): for builders who embed a `pk_jwt_*` key in their frontend and issue JWTs to their end users. Credits bill to the builder's org; rate limits are per session.\n```\nAuthorization: Bearer \n```\nor\n```\nwss://api.struct.to/ws/alerts?token=\n```\n\n## Protocol\n\nAfter connecting, send JSON messages to subscribe or unsubscribe:\n\n```json\n{ \"op\": \"subscribe\", \"event\": \"trader_whale_trade\", \"min_usd_value\": 10000 }\n```\n\nServer responds with `{\"op\":\"subscribed\", ...}`. Pushed alert events use the format `{\"event\":\"...\",\"timestamp\":...,\"data\": ...}`.\n\n## Keepalive\n\nSend `{\"type\":\"ping\"}` periodically to keep your connection alive. The server responds with `{\"type\":\"pong\"}`. Connections that have not received **any** frame (ping, subscribe, or unsubscribe) within **60 seconds** are closed. Recommended: send a ping every 30 seconds.\n\n## Pricing\n\nEach delivered alert costs **1-2 credits**, matching the corresponding HTTP webhook cost exactly. See each channel's description for its specific credit cost." + }, + "servers": { + "alerts": { + "host": "api.struct.to", + "pathname": "/ws/alerts", + "protocol": "wss", + "description": "WebSocket alerts endpoint — ephemeral real-time alert subscriptions. Pass API key via ?api-key= query parameter. Subscribe/unsubscribe by sending JSON messages. Subscriptions are ephemeral and end on disconnect." + } + }, + "channels": { + "ws_alerts.trader_first_trade": { + "address": "/ws/alerts", + "title": "First Trade", + "summary": "Real-time `trader_first_trade` alert subscription", + "description": "Fired when a tracked trader executes their first trade on Polymarket\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `wallet_addresses`, `min_usd_value`, `min_probability`, `max_probability`, `condition_ids`, `event_slugs`, `exclude_shortterm_markets`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "trader" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertTraderFirstTradeSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertTraderFirstTradeUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertTraderFirstTradeEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + }, + "ws_alerts.trader_new_market": { + "address": "/ws/alerts", + "title": "New Market Entry", + "summary": "Real-time `trader_new_market` alert subscription", + "description": "Fired when a trader places their first trade in a specific market/condition (fires once per trader+market pair)\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `wallet_addresses`, `condition_ids`, `event_slugs`, `min_usd_value`, `min_probability`, `max_probability`, `exclude_shortterm_markets`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "trader" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertTraderNewMarketSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertTraderNewMarketUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertTraderNewMarketEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + }, + "ws_alerts.trader_whale_trade": { + "address": "/ws/alerts", + "title": "Whale Trade", + "summary": "Real-time `trader_whale_trade` alert subscription", + "description": "Fired when a trade meets the configured criteria. Use `min_usd_value` to filter by minimum trade size (optional, defaults to 0 — matches all trades), and `min_probability`/`max_probability` to restrict to a probability range.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `wallet_addresses`, `min_usd_value`, `min_probability`, `max_probability`, `condition_ids`, `event_slugs`, `exclude_shortterm_markets`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "trader" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertTraderWhaleTradeSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertTraderWhaleTradeUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertTraderWhaleTradeEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + }, + "ws_alerts.trader_new_trade": { + "address": "/ws/alerts", + "title": "New Trade", + "summary": "Real-time `trader_new_trade` alert subscription", + "description": "Fired on every order-filled trade. Use `wallet_addresses` to watch specific traders, `min_usd_value` to filter by size, and `min_probability`/`max_probability` to restrict to a probability range.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `wallet_addresses`, `min_usd_value`, `min_probability`, `max_probability`, `condition_ids`, `event_slugs`, `exclude_shortterm_markets`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "trader" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertTraderNewTradeSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertTraderNewTradeUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertTraderNewTradeEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + }, + "ws_alerts.trader_global_pnl": { + "address": "/ws/alerts", + "title": "Global PnL", + "summary": "Real-time `trader_global_pnl` alert subscription", + "description": "Fired when a trader's global PnL crosses a configured threshold\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `traders`, `min_realized_pnl_usd`, `max_realized_pnl_usd`, `min_volume_usd`, `max_volume_usd`, `min_buy_usd`, `min_sell_volume_usd`, `min_win_rate`, `min_markets_traded`, `timeframes`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "trader" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertTraderGlobalPnlSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertTraderGlobalPnlUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertTraderGlobalPnlEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + }, + "ws_alerts.trader_market_pnl": { + "address": "/ws/alerts", + "title": "Market PnL", + "summary": "Real-time `trader_market_pnl` alert subscription", + "description": "Fired when a trader's market-level PnL crosses a configured threshold\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `traders`, `condition_ids`, `event_slugs`, `min_realized_pnl_usd`, `max_realized_pnl_usd`, `min_volume_usd`, `max_volume_usd`, `min_buy_usd`, `min_sell_volume_usd`, `timeframes`, `exclude_shortterm_markets`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "trader" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertTraderMarketPnlSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertTraderMarketPnlUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertTraderMarketPnlEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + }, + "ws_alerts.trader_event_pnl": { + "address": "/ws/alerts", + "title": "Event PnL", + "summary": "Real-time `trader_event_pnl` alert subscription", + "description": "Fired when a trader's event-level PnL crosses a configured threshold\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `traders`, `event_slugs`, `min_realized_pnl_usd`, `max_realized_pnl_usd`, `min_volume_usd`, `max_volume_usd`, `min_buy_usd`, `min_sell_volume_usd`, `min_markets_traded`, `timeframes`, `exclude_shortterm_markets`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "trader" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertTraderEventPnlSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertTraderEventPnlUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertTraderEventPnlEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + }, + "ws_alerts.condition_metrics": { + "address": "/ws/alerts", + "title": "Market Metrics", + "summary": "Real-time `condition_metrics` alert subscription", + "description": "Fired when a market's volume or transaction metrics cross a configured threshold. Use `timeframes` to restrict to specific windows (valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`).\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `condition_ids`, `min_volume_usd`, `max_volume_usd`, `min_fees`, `min_txns`, `min_unique_traders`, `timeframes`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "market" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertConditionMetricsSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertConditionMetricsUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertConditionMetricsEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + }, + "ws_alerts.event_metrics": { + "address": "/ws/alerts", + "title": "Event Metrics", + "summary": "Real-time `event_metrics` alert subscription", + "description": "Fired when an event's volume or transaction metrics cross a configured threshold. Use `timeframes` to restrict to specific windows (valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`).\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `event_slugs`, `min_volume_usd`, `max_volume_usd`, `min_fees`, `min_txns`, `min_unique_traders`, `timeframes`, `exclude_shortterm_markets`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "event" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertEventMetricsSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertEventMetricsUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertEventMetricsEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + }, + "ws_alerts.position_metrics": { + "address": "/ws/alerts", + "title": "Position Metrics", + "summary": "Real-time `position_metrics` alert subscription", + "description": "Fired when a position's volume or transaction metrics cross a configured threshold. Use `timeframes` to restrict to specific windows (valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`).\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `position_ids`, `condition_ids`, `outcomes`, `min_volume_usd`, `max_volume_usd`, `min_buy_usd`, `min_sell_volume_usd`, `min_fees`, `min_txns`, `min_price_change_pct`, `min_probability_change_pct`, `timeframes`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "position" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertPositionMetricsSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertPositionMetricsUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertPositionMetricsEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + }, + "ws_alerts.market_volume_milestone": { + "address": "/ws/alerts", + "title": "Market Volume Milestone", + "summary": "Real-time `market_volume_milestone` alert subscription", + "description": "Fired when a market's trading volume crosses a milestone threshold in the specified timeframe. **`timeframes` is required** (e.g. `[\"1h\", \"24h\"]`) — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`. Optional `milestone_amounts` restricts to specific USD thresholds (e.g. `[10000, 100000]`). Optional `condition_ids` restricts to specific markets.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `condition_ids`, `timeframes`, `milestone_amounts`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "market" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertMarketVolumeMilestoneSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertMarketVolumeMilestoneUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertMarketVolumeMilestoneEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + }, + "ws_alerts.event_volume_milestone": { + "address": "/ws/alerts", + "title": "Event Volume Milestone", + "summary": "Real-time `event_volume_milestone` alert subscription", + "description": "Fired when an event's trading volume crosses a milestone threshold in the specified timeframe. **`timeframes` is required** (e.g. `[\"1h\", \"24h\"]`) — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`. Optional `milestone_amounts` restricts to specific USD thresholds. Optional `event_slugs` restricts to specific events.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `event_slugs`, `timeframes`, `milestone_amounts`, `exclude_shortterm_markets`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "event" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertEventVolumeMilestoneSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertEventVolumeMilestoneUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertEventVolumeMilestoneEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + }, + "ws_alerts.position_volume_milestone": { + "address": "/ws/alerts", + "title": "Position Volume Milestone", + "summary": "Real-time `position_volume_milestone` alert subscription", + "description": "Fired when a position's trading volume crosses a milestone threshold in the specified timeframe. **`timeframes` is required** (e.g. `[\"1h\", \"24h\"]`) — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`. Optional `milestone_amounts` restricts to specific USD thresholds. Optional `position_ids` or `condition_ids` restrict to specific positions/markets.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `position_ids`, `condition_ids`, `outcomes`, `timeframes`, `milestone_amounts`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "position" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertPositionVolumeMilestoneSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertPositionVolumeMilestoneUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertPositionVolumeMilestoneEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + }, + "ws_alerts.probability_spike": { + "address": "/ws/alerts", + "title": "Probability Spike", + "summary": "Real-time `probability_spike` alert subscription", + "description": "Fired when a position's probability moves significantly. Use `min_probability_change_pct` to set the minimum move (e.g. `10` for 10%). Use `window_secs` to observe moves within a specific time window (e.g. `60` for 60 seconds). Use `spike_direction` (`\"up\"` | `\"down\"` | `\"both\"`) — defaults to `\"up\"` when not provided. Filter by `position_ids` or `outcomes` to narrow scope.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `position_ids`, `condition_ids`, `event_slugs`, `outcomes`, `min_probability_change_pct`, `spike_direction`, `window_secs`, `exclude_shortterm_markets`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "position" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertProbabilitySpikeSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertProbabilitySpikeUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertProbabilitySpikeEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + }, + "ws_alerts.price_spike": { + "address": "/ws/alerts", + "title": "Price Spike", + "summary": "Real-time `price_spike` alert subscription", + "description": "Fired when a position's price moves significantly. Use `min_price_change_pct` to set the minimum move (e.g. `10` for 10%). Use `window_secs` to observe moves within a specific time window. Use `spike_direction` (`\"up\"` | `\"down\"` | `\"both\"`) — defaults to `\"up\"` when not provided. Filter by `position_ids` or `outcomes` to narrow scope.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `position_ids`, `condition_ids`, `event_slugs`, `outcomes`, `min_price_change_pct`, `spike_direction`, `window_secs`, `exclude_shortterm_markets`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "position" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertPriceSpikeSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertPriceSpikeUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertPriceSpikeEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + }, + "ws_alerts.market_volume_spike": { + "address": "/ws/alerts", + "title": "Market Volume Spike", + "summary": "Real-time `market_volume_spike` alert subscription", + "description": "Fired when a market's trading volume grows by a multiple of `spike_ratio` within a timeframe. Requires `spike_ratio` (> 1.0, e.g. `2.0` fires when volume doubles). Optional `window_secs` sets the observation window in seconds (max 600). Optional `timeframes` — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `1d`, `24h`, `7d`, `30d`. Optional `condition_ids` restricts to specific markets.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `spike_ratio`, `window_secs`, `condition_ids`, `timeframes`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "market" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertMarketVolumeSpikeSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertMarketVolumeSpikeUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertMarketVolumeSpikeEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + }, + "ws_alerts.event_volume_spike": { + "address": "/ws/alerts", + "title": "Event Volume Spike", + "summary": "Real-time `event_volume_spike` alert subscription", + "description": "Fired when an event's aggregated trading volume grows by a multiple of `spike_ratio` within a timeframe. Requires `spike_ratio` (> 1.0, e.g. `2.0` fires when volume doubles). Optional `window_secs` sets the observation window in seconds (max 600). Optional `timeframes` and `event_slugs` to narrow scope.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `spike_ratio`, `window_secs`, `event_slugs`, `timeframes`, `exclude_shortterm_markets`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "event" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertEventVolumeSpikeSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertEventVolumeSpikeUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertEventVolumeSpikeEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + }, + "ws_alerts.position_volume_spike": { + "address": "/ws/alerts", + "title": "Position Volume Spike", + "summary": "Real-time `position_volume_spike` alert subscription", + "description": "Fired when a position's trading volume grows by a multiple of `spike_ratio` within a timeframe. Requires `spike_ratio` (> 1.0, e.g. `2.0` fires when volume doubles). Optional `window_secs` sets the observation window in seconds (max 600). Optional `position_ids`, `condition_ids`, `outcomes`, and `timeframes` to narrow scope.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `spike_ratio`, `window_secs`, `position_ids`, `condition_ids`, `outcomes`, `timeframes`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "position" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertPositionVolumeSpikeSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertPositionVolumeSpikeUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertPositionVolumeSpikeEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + }, + "ws_alerts.close_to_bond": { + "address": "/ws/alerts", + "title": "Close To Bond", + "summary": "Real-time `close_to_bond` alert subscription", + "description": "Fired when a trade occurs at a near-certain-outcome price. **At least one of `min_probability` or `max_probability` is required.** Use `min_probability` (e.g. `0.95`) to trigger when YES is near-certain; use `max_probability` (e.g. `0.05`) for NO near-certain. Optional filters: `position_outcome_indices` — restrict by outcome index (`0` = Yes/Up, `1` = No); `condition_ids` — restrict to specific markets; `position_ids` — restrict to specific outcome tokens; `outcomes` — restrict by outcome name (e.g. `\"Yes\"`, `\"No\"`); `event_slugs` — restrict to specific events.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `min_probability`, `max_probability`, `condition_ids`, `position_ids`, `outcomes`, `position_outcome_indices`, `event_slugs`, `exclude_shortterm_markets`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "market" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertCloseToBondSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertCloseToBondUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertCloseToBondEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + }, + "ws_alerts.market_created": { + "address": "/ws/alerts", + "title": "Market Created", + "summary": "Real-time `market_created` alert subscription", + "description": "Fired when a new prediction market is created on Polymarket. Filterable by `tags` and `event_slugs`.\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `event_slugs`, `tags`, `exclude_shortterm_markets`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "market" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertMarketCreatedSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertMarketCreatedUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertMarketCreatedEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + }, + "ws_alerts.asset_price_tick": { + "address": "/ws/alerts", + "title": "Asset Price Tick", + "summary": "Real-time `asset_price_tick` alert subscription", + "description": "Fired when the price of a tracked crypto asset updates (BTC, ETH, SOL, XRP, DOGE, BNB, HYPE). Use `asset_symbols` to restrict to specific assets (empty = all).\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `asset_symbols`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "assets" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertAssetPriceTickSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertAssetPriceTickUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertAssetPriceTickEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + }, + "ws_alerts.asset_price_window_update": { + "address": "/ws/alerts", + "title": "Asset Price Window Update", + "summary": "Real-time `asset_price_window_update` alert subscription", + "description": "Fired at the start and end of each price candle for tracked crypto assets (BTC, ETH, SOL, XRP, DOGE, BNB, HYPE). Payload includes `update_type` (`\"open\"` or `\"close\"`) indicating whether the candle is opening or closing. Use `asset_symbols` to restrict to specific assets. Use `timeframes` to restrict to specific candle sizes — valid values: `\"5m\"`, `\"15m\"`, `\"1h\"`, `\"4h\"`, `\"1d\"`, `\"24h\"`.\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `asset_symbols`, `timeframes`\n\nConnection: `wss://api.struct.to/ws/alerts?api-key=` — all alert channels share the same physical endpoint.", + "servers": [ + { + "$ref": "#/servers/alerts" + } + ], + "tags": [ + { + "name": "assets" + } + ], + "messages": { + "subscribe": { + "$ref": "#/components/messages/WsAlertAssetPriceWindowUpdateSubscribe" + }, + "unsubscribe": { + "$ref": "#/components/messages/WsAlertAssetPriceWindowUpdateUnsubscribe" + }, + "event": { + "$ref": "#/components/messages/WsAlertAssetPriceWindowUpdateEvent" + }, + "subscribed": { + "$ref": "#/components/messages/WsAlertSubscribed" + }, + "unsubscribed": { + "$ref": "#/components/messages/WsAlertUnsubscribed" + }, + "error": { + "$ref": "#/components/messages/WsAlertError" + } + } + } + }, + "operations": { + "subscribeTraderFirstTrade": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.trader_first_trade" + }, + "title": "Subscribe — First Trade", + "summary": "Subscribe to `trader_first_trade` alerts", + "description": "Fired when a tracked trader executes their first trade on Polymarket", + "tags": [ + { + "name": "trader" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.trader_first_trade/messages/subscribe" + } + ] + }, + "unsubscribeTraderFirstTrade": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.trader_first_trade" + }, + "title": "Unsubscribe — First Trade", + "summary": "Unsubscribe from `trader_first_trade` alerts", + "description": "Stop receiving `trader_first_trade` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "trader" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.trader_first_trade/messages/unsubscribe" + } + ] + }, + "receiveTraderFirstTrade": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.trader_first_trade" + }, + "title": "Event — First Trade", + "summary": "Receive `trader_first_trade` alerts", + "description": "Fired when a tracked trader executes their first trade on Polymarket\n\n**Credits cost:** 1 per delivered alert.", + "tags": [ + { + "name": "trader" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.trader_first_trade/messages/event" + } + ] + }, + "subscribeTraderNewMarket": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.trader_new_market" + }, + "title": "Subscribe — New Market Entry", + "summary": "Subscribe to `trader_new_market` alerts", + "description": "Fired when a trader places their first trade in a specific market/condition (fires once per trader+market pair)", + "tags": [ + { + "name": "trader" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.trader_new_market/messages/subscribe" + } + ] + }, + "unsubscribeTraderNewMarket": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.trader_new_market" + }, + "title": "Unsubscribe — New Market Entry", + "summary": "Unsubscribe from `trader_new_market` alerts", + "description": "Stop receiving `trader_new_market` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "trader" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.trader_new_market/messages/unsubscribe" + } + ] + }, + "receiveTraderNewMarket": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.trader_new_market" + }, + "title": "Event — New Market Entry", + "summary": "Receive `trader_new_market` alerts", + "description": "Fired when a trader places their first trade in a specific market/condition (fires once per trader+market pair)\n\n**Credits cost:** 1 per delivered alert.", + "tags": [ + { + "name": "trader" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.trader_new_market/messages/event" + } + ] + }, + "subscribeTraderWhaleTrade": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.trader_whale_trade" + }, + "title": "Subscribe — Whale Trade", + "summary": "Subscribe to `trader_whale_trade` alerts", + "description": "Fired when a trade meets the configured criteria. Use `min_usd_value` to filter by minimum trade size (optional, defaults to 0 — matches all trades), and `min_probability`/`max_probability` to restrict to a probability range.", + "tags": [ + { + "name": "trader" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.trader_whale_trade/messages/subscribe" + } + ] + }, + "unsubscribeTraderWhaleTrade": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.trader_whale_trade" + }, + "title": "Unsubscribe — Whale Trade", + "summary": "Unsubscribe from `trader_whale_trade` alerts", + "description": "Stop receiving `trader_whale_trade` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "trader" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.trader_whale_trade/messages/unsubscribe" + } + ] + }, + "receiveTraderWhaleTrade": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.trader_whale_trade" + }, + "title": "Event — Whale Trade", + "summary": "Receive `trader_whale_trade` alerts", + "description": "Fired when a trade meets the configured criteria. Use `min_usd_value` to filter by minimum trade size (optional, defaults to 0 — matches all trades), and `min_probability`/`max_probability` to restrict to a probability range.\n\n**Credits cost:** 2 per delivered alert.", + "tags": [ + { + "name": "trader" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.trader_whale_trade/messages/event" + } + ] + }, + "subscribeTraderNewTrade": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.trader_new_trade" + }, + "title": "Subscribe — New Trade", + "summary": "Subscribe to `trader_new_trade` alerts", + "description": "Fired on every order-filled trade. Use `wallet_addresses` to watch specific traders, `min_usd_value` to filter by size, and `min_probability`/`max_probability` to restrict to a probability range.", + "tags": [ + { + "name": "trader" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.trader_new_trade/messages/subscribe" + } + ] + }, + "unsubscribeTraderNewTrade": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.trader_new_trade" + }, + "title": "Unsubscribe — New Trade", + "summary": "Unsubscribe from `trader_new_trade` alerts", + "description": "Stop receiving `trader_new_trade` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "trader" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.trader_new_trade/messages/unsubscribe" + } + ] + }, + "receiveTraderNewTrade": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.trader_new_trade" + }, + "title": "Event — New Trade", + "summary": "Receive `trader_new_trade` alerts", + "description": "Fired on every order-filled trade. Use `wallet_addresses` to watch specific traders, `min_usd_value` to filter by size, and `min_probability`/`max_probability` to restrict to a probability range.\n\n**Credits cost:** 2 per delivered alert.", + "tags": [ + { + "name": "trader" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.trader_new_trade/messages/event" + } + ] + }, + "subscribeTraderGlobalPnl": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.trader_global_pnl" + }, + "title": "Subscribe — Global PnL", + "summary": "Subscribe to `trader_global_pnl` alerts", + "description": "Fired when a trader's global PnL crosses a configured threshold", + "tags": [ + { + "name": "trader" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.trader_global_pnl/messages/subscribe" + } + ] + }, + "unsubscribeTraderGlobalPnl": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.trader_global_pnl" + }, + "title": "Unsubscribe — Global PnL", + "summary": "Unsubscribe from `trader_global_pnl` alerts", + "description": "Stop receiving `trader_global_pnl` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "trader" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.trader_global_pnl/messages/unsubscribe" + } + ] + }, + "receiveTraderGlobalPnl": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.trader_global_pnl" + }, + "title": "Event — Global PnL", + "summary": "Receive `trader_global_pnl` alerts", + "description": "Fired when a trader's global PnL crosses a configured threshold\n\n**Credits cost:** 1 per delivered alert.", + "tags": [ + { + "name": "trader" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.trader_global_pnl/messages/event" + } + ] + }, + "subscribeTraderMarketPnl": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.trader_market_pnl" + }, + "title": "Subscribe — Market PnL", + "summary": "Subscribe to `trader_market_pnl` alerts", + "description": "Fired when a trader's market-level PnL crosses a configured threshold", + "tags": [ + { + "name": "trader" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.trader_market_pnl/messages/subscribe" + } + ] + }, + "unsubscribeTraderMarketPnl": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.trader_market_pnl" + }, + "title": "Unsubscribe — Market PnL", + "summary": "Unsubscribe from `trader_market_pnl` alerts", + "description": "Stop receiving `trader_market_pnl` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "trader" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.trader_market_pnl/messages/unsubscribe" + } + ] + }, + "receiveTraderMarketPnl": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.trader_market_pnl" + }, + "title": "Event — Market PnL", + "summary": "Receive `trader_market_pnl` alerts", + "description": "Fired when a trader's market-level PnL crosses a configured threshold\n\n**Credits cost:** 1 per delivered alert.", + "tags": [ + { + "name": "trader" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.trader_market_pnl/messages/event" + } + ] + }, + "subscribeTraderEventPnl": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.trader_event_pnl" + }, + "title": "Subscribe — Event PnL", + "summary": "Subscribe to `trader_event_pnl` alerts", + "description": "Fired when a trader's event-level PnL crosses a configured threshold", + "tags": [ + { + "name": "trader" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.trader_event_pnl/messages/subscribe" + } + ] + }, + "unsubscribeTraderEventPnl": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.trader_event_pnl" + }, + "title": "Unsubscribe — Event PnL", + "summary": "Unsubscribe from `trader_event_pnl` alerts", + "description": "Stop receiving `trader_event_pnl` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "trader" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.trader_event_pnl/messages/unsubscribe" + } + ] + }, + "receiveTraderEventPnl": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.trader_event_pnl" + }, + "title": "Event — Event PnL", + "summary": "Receive `trader_event_pnl` alerts", + "description": "Fired when a trader's event-level PnL crosses a configured threshold\n\n**Credits cost:** 1 per delivered alert.", + "tags": [ + { + "name": "trader" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.trader_event_pnl/messages/event" + } + ] + }, + "subscribeConditionMetrics": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.condition_metrics" + }, + "title": "Subscribe — Market Metrics", + "summary": "Subscribe to `condition_metrics` alerts", + "description": "Fired when a market's volume or transaction metrics cross a configured threshold. Use `timeframes` to restrict to specific windows (valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`).", + "tags": [ + { + "name": "market" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.condition_metrics/messages/subscribe" + } + ] + }, + "unsubscribeConditionMetrics": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.condition_metrics" + }, + "title": "Unsubscribe — Market Metrics", + "summary": "Unsubscribe from `condition_metrics` alerts", + "description": "Stop receiving `condition_metrics` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "market" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.condition_metrics/messages/unsubscribe" + } + ] + }, + "receiveConditionMetrics": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.condition_metrics" + }, + "title": "Event — Market Metrics", + "summary": "Receive `condition_metrics` alerts", + "description": "Fired when a market's volume or transaction metrics cross a configured threshold. Use `timeframes` to restrict to specific windows (valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`).\n\n**Credits cost:** 1 per delivered alert.", + "tags": [ + { + "name": "market" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.condition_metrics/messages/event" + } + ] + }, + "subscribeEventMetrics": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.event_metrics" + }, + "title": "Subscribe — Event Metrics", + "summary": "Subscribe to `event_metrics` alerts", + "description": "Fired when an event's volume or transaction metrics cross a configured threshold. Use `timeframes` to restrict to specific windows (valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`).", + "tags": [ + { + "name": "event" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.event_metrics/messages/subscribe" + } + ] + }, + "unsubscribeEventMetrics": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.event_metrics" + }, + "title": "Unsubscribe — Event Metrics", + "summary": "Unsubscribe from `event_metrics` alerts", + "description": "Stop receiving `event_metrics` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "event" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.event_metrics/messages/unsubscribe" + } + ] + }, + "receiveEventMetrics": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.event_metrics" + }, + "title": "Event — Event Metrics", + "summary": "Receive `event_metrics` alerts", + "description": "Fired when an event's volume or transaction metrics cross a configured threshold. Use `timeframes` to restrict to specific windows (valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`).\n\n**Credits cost:** 1 per delivered alert.", + "tags": [ + { + "name": "event" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.event_metrics/messages/event" + } + ] + }, + "subscribePositionMetrics": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.position_metrics" + }, + "title": "Subscribe — Position Metrics", + "summary": "Subscribe to `position_metrics` alerts", + "description": "Fired when a position's volume or transaction metrics cross a configured threshold. Use `timeframes` to restrict to specific windows (valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`).", + "tags": [ + { + "name": "position" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.position_metrics/messages/subscribe" + } + ] + }, + "unsubscribePositionMetrics": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.position_metrics" + }, + "title": "Unsubscribe — Position Metrics", + "summary": "Unsubscribe from `position_metrics` alerts", + "description": "Stop receiving `position_metrics` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "position" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.position_metrics/messages/unsubscribe" + } + ] + }, + "receivePositionMetrics": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.position_metrics" + }, + "title": "Event — Position Metrics", + "summary": "Receive `position_metrics` alerts", + "description": "Fired when a position's volume or transaction metrics cross a configured threshold. Use `timeframes` to restrict to specific windows (valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`).\n\n**Credits cost:** 1 per delivered alert.", + "tags": [ + { + "name": "position" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.position_metrics/messages/event" + } + ] + }, + "subscribeMarketVolumeMilestone": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.market_volume_milestone" + }, + "title": "Subscribe — Market Volume Milestone", + "summary": "Subscribe to `market_volume_milestone` alerts", + "description": "Fired when a market's trading volume crosses a milestone threshold in the specified timeframe. **`timeframes` is required** (e.g. `[\"1h\", \"24h\"]`) — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`. Optional `milestone_amounts` restricts to specific USD thresholds (e.g. `[10000, 100000]`). Optional `condition_ids` restricts to specific markets.", + "tags": [ + { + "name": "market" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.market_volume_milestone/messages/subscribe" + } + ] + }, + "unsubscribeMarketVolumeMilestone": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.market_volume_milestone" + }, + "title": "Unsubscribe — Market Volume Milestone", + "summary": "Unsubscribe from `market_volume_milestone` alerts", + "description": "Stop receiving `market_volume_milestone` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "market" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.market_volume_milestone/messages/unsubscribe" + } + ] + }, + "receiveMarketVolumeMilestone": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.market_volume_milestone" + }, + "title": "Event — Market Volume Milestone", + "summary": "Receive `market_volume_milestone` alerts", + "description": "Fired when a market's trading volume crosses a milestone threshold in the specified timeframe. **`timeframes` is required** (e.g. `[\"1h\", \"24h\"]`) — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`. Optional `milestone_amounts` restricts to specific USD thresholds (e.g. `[10000, 100000]`). Optional `condition_ids` restricts to specific markets.\n\n**Credits cost:** 2 per delivered alert.", + "tags": [ + { + "name": "market" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.market_volume_milestone/messages/event" + } + ] + }, + "subscribeEventVolumeMilestone": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.event_volume_milestone" + }, + "title": "Subscribe — Event Volume Milestone", + "summary": "Subscribe to `event_volume_milestone` alerts", + "description": "Fired when an event's trading volume crosses a milestone threshold in the specified timeframe. **`timeframes` is required** (e.g. `[\"1h\", \"24h\"]`) — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`. Optional `milestone_amounts` restricts to specific USD thresholds. Optional `event_slugs` restricts to specific events.", + "tags": [ + { + "name": "event" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.event_volume_milestone/messages/subscribe" + } + ] + }, + "unsubscribeEventVolumeMilestone": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.event_volume_milestone" + }, + "title": "Unsubscribe — Event Volume Milestone", + "summary": "Unsubscribe from `event_volume_milestone` alerts", + "description": "Stop receiving `event_volume_milestone` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "event" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.event_volume_milestone/messages/unsubscribe" + } + ] + }, + "receiveEventVolumeMilestone": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.event_volume_milestone" + }, + "title": "Event — Event Volume Milestone", + "summary": "Receive `event_volume_milestone` alerts", + "description": "Fired when an event's trading volume crosses a milestone threshold in the specified timeframe. **`timeframes` is required** (e.g. `[\"1h\", \"24h\"]`) — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`. Optional `milestone_amounts` restricts to specific USD thresholds. Optional `event_slugs` restricts to specific events.\n\n**Credits cost:** 2 per delivered alert.", + "tags": [ + { + "name": "event" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.event_volume_milestone/messages/event" + } + ] + }, + "subscribePositionVolumeMilestone": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.position_volume_milestone" + }, + "title": "Subscribe — Position Volume Milestone", + "summary": "Subscribe to `position_volume_milestone` alerts", + "description": "Fired when a position's trading volume crosses a milestone threshold in the specified timeframe. **`timeframes` is required** (e.g. `[\"1h\", \"24h\"]`) — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`. Optional `milestone_amounts` restricts to specific USD thresholds. Optional `position_ids` or `condition_ids` restrict to specific positions/markets.", + "tags": [ + { + "name": "position" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.position_volume_milestone/messages/subscribe" + } + ] + }, + "unsubscribePositionVolumeMilestone": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.position_volume_milestone" + }, + "title": "Unsubscribe — Position Volume Milestone", + "summary": "Unsubscribe from `position_volume_milestone` alerts", + "description": "Stop receiving `position_volume_milestone` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "position" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.position_volume_milestone/messages/unsubscribe" + } + ] + }, + "receivePositionVolumeMilestone": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.position_volume_milestone" + }, + "title": "Event — Position Volume Milestone", + "summary": "Receive `position_volume_milestone` alerts", + "description": "Fired when a position's trading volume crosses a milestone threshold in the specified timeframe. **`timeframes` is required** (e.g. `[\"1h\", \"24h\"]`) — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`. Optional `milestone_amounts` restricts to specific USD thresholds. Optional `position_ids` or `condition_ids` restrict to specific positions/markets.\n\n**Credits cost:** 2 per delivered alert.", + "tags": [ + { + "name": "position" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.position_volume_milestone/messages/event" + } + ] + }, + "subscribeProbabilitySpike": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.probability_spike" + }, + "title": "Subscribe — Probability Spike", + "summary": "Subscribe to `probability_spike` alerts", + "description": "Fired when a position's probability moves significantly. Use `min_probability_change_pct` to set the minimum move (e.g. `10` for 10%). Use `window_secs` to observe moves within a specific time window (e.g. `60` for 60 seconds). Use `spike_direction` (`\"up\"` | `\"down\"` | `\"both\"`) — defaults to `\"up\"` when not provided. Filter by `position_ids` or `outcomes` to narrow scope.", + "tags": [ + { + "name": "position" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.probability_spike/messages/subscribe" + } + ] + }, + "unsubscribeProbabilitySpike": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.probability_spike" + }, + "title": "Unsubscribe — Probability Spike", + "summary": "Unsubscribe from `probability_spike` alerts", + "description": "Stop receiving `probability_spike` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "position" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.probability_spike/messages/unsubscribe" + } + ] + }, + "receiveProbabilitySpike": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.probability_spike" + }, + "title": "Event — Probability Spike", + "summary": "Receive `probability_spike` alerts", + "description": "Fired when a position's probability moves significantly. Use `min_probability_change_pct` to set the minimum move (e.g. `10` for 10%). Use `window_secs` to observe moves within a specific time window (e.g. `60` for 60 seconds). Use `spike_direction` (`\"up\"` | `\"down\"` | `\"both\"`) — defaults to `\"up\"` when not provided. Filter by `position_ids` or `outcomes` to narrow scope.\n\n**Credits cost:** 2 per delivered alert.", + "tags": [ + { + "name": "position" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.probability_spike/messages/event" + } + ] + }, + "subscribePriceSpike": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.price_spike" + }, + "title": "Subscribe — Price Spike", + "summary": "Subscribe to `price_spike` alerts", + "description": "Fired when a position's price moves significantly. Use `min_price_change_pct` to set the minimum move (e.g. `10` for 10%). Use `window_secs` to observe moves within a specific time window. Use `spike_direction` (`\"up\"` | `\"down\"` | `\"both\"`) — defaults to `\"up\"` when not provided. Filter by `position_ids` or `outcomes` to narrow scope.", + "tags": [ + { + "name": "position" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.price_spike/messages/subscribe" + } + ] + }, + "unsubscribePriceSpike": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.price_spike" + }, + "title": "Unsubscribe — Price Spike", + "summary": "Unsubscribe from `price_spike` alerts", + "description": "Stop receiving `price_spike` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "position" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.price_spike/messages/unsubscribe" + } + ] + }, + "receivePriceSpike": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.price_spike" + }, + "title": "Event — Price Spike", + "summary": "Receive `price_spike` alerts", + "description": "Fired when a position's price moves significantly. Use `min_price_change_pct` to set the minimum move (e.g. `10` for 10%). Use `window_secs` to observe moves within a specific time window. Use `spike_direction` (`\"up\"` | `\"down\"` | `\"both\"`) — defaults to `\"up\"` when not provided. Filter by `position_ids` or `outcomes` to narrow scope.\n\n**Credits cost:** 2 per delivered alert.", + "tags": [ + { + "name": "position" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.price_spike/messages/event" + } + ] + }, + "subscribeMarketVolumeSpike": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.market_volume_spike" + }, + "title": "Subscribe — Market Volume Spike", + "summary": "Subscribe to `market_volume_spike` alerts", + "description": "Fired when a market's trading volume grows by a multiple of `spike_ratio` within a timeframe. Requires `spike_ratio` (> 1.0, e.g. `2.0` fires when volume doubles). Optional `window_secs` sets the observation window in seconds (max 600). Optional `timeframes` — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `1d`, `24h`, `7d`, `30d`. Optional `condition_ids` restricts to specific markets.", + "tags": [ + { + "name": "market" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.market_volume_spike/messages/subscribe" + } + ] + }, + "unsubscribeMarketVolumeSpike": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.market_volume_spike" + }, + "title": "Unsubscribe — Market Volume Spike", + "summary": "Unsubscribe from `market_volume_spike` alerts", + "description": "Stop receiving `market_volume_spike` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "market" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.market_volume_spike/messages/unsubscribe" + } + ] + }, + "receiveMarketVolumeSpike": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.market_volume_spike" + }, + "title": "Event — Market Volume Spike", + "summary": "Receive `market_volume_spike` alerts", + "description": "Fired when a market's trading volume grows by a multiple of `spike_ratio` within a timeframe. Requires `spike_ratio` (> 1.0, e.g. `2.0` fires when volume doubles). Optional `window_secs` sets the observation window in seconds (max 600). Optional `timeframes` — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `1d`, `24h`, `7d`, `30d`. Optional `condition_ids` restricts to specific markets.\n\n**Credits cost:** 2 per delivered alert.", + "tags": [ + { + "name": "market" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.market_volume_spike/messages/event" + } + ] + }, + "subscribeEventVolumeSpike": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.event_volume_spike" + }, + "title": "Subscribe — Event Volume Spike", + "summary": "Subscribe to `event_volume_spike` alerts", + "description": "Fired when an event's aggregated trading volume grows by a multiple of `spike_ratio` within a timeframe. Requires `spike_ratio` (> 1.0, e.g. `2.0` fires when volume doubles). Optional `window_secs` sets the observation window in seconds (max 600). Optional `timeframes` and `event_slugs` to narrow scope.", + "tags": [ + { + "name": "event" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.event_volume_spike/messages/subscribe" + } + ] + }, + "unsubscribeEventVolumeSpike": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.event_volume_spike" + }, + "title": "Unsubscribe — Event Volume Spike", + "summary": "Unsubscribe from `event_volume_spike` alerts", + "description": "Stop receiving `event_volume_spike` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "event" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.event_volume_spike/messages/unsubscribe" + } + ] + }, + "receiveEventVolumeSpike": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.event_volume_spike" + }, + "title": "Event — Event Volume Spike", + "summary": "Receive `event_volume_spike` alerts", + "description": "Fired when an event's aggregated trading volume grows by a multiple of `spike_ratio` within a timeframe. Requires `spike_ratio` (> 1.0, e.g. `2.0` fires when volume doubles). Optional `window_secs` sets the observation window in seconds (max 600). Optional `timeframes` and `event_slugs` to narrow scope.\n\n**Credits cost:** 2 per delivered alert.", + "tags": [ + { + "name": "event" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.event_volume_spike/messages/event" + } + ] + }, + "subscribePositionVolumeSpike": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.position_volume_spike" + }, + "title": "Subscribe — Position Volume Spike", + "summary": "Subscribe to `position_volume_spike` alerts", + "description": "Fired when a position's trading volume grows by a multiple of `spike_ratio` within a timeframe. Requires `spike_ratio` (> 1.0, e.g. `2.0` fires when volume doubles). Optional `window_secs` sets the observation window in seconds (max 600). Optional `position_ids`, `condition_ids`, `outcomes`, and `timeframes` to narrow scope.", + "tags": [ + { + "name": "position" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.position_volume_spike/messages/subscribe" + } + ] + }, + "unsubscribePositionVolumeSpike": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.position_volume_spike" + }, + "title": "Unsubscribe — Position Volume Spike", + "summary": "Unsubscribe from `position_volume_spike` alerts", + "description": "Stop receiving `position_volume_spike` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "position" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.position_volume_spike/messages/unsubscribe" + } + ] + }, + "receivePositionVolumeSpike": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.position_volume_spike" + }, + "title": "Event — Position Volume Spike", + "summary": "Receive `position_volume_spike` alerts", + "description": "Fired when a position's trading volume grows by a multiple of `spike_ratio` within a timeframe. Requires `spike_ratio` (> 1.0, e.g. `2.0` fires when volume doubles). Optional `window_secs` sets the observation window in seconds (max 600). Optional `position_ids`, `condition_ids`, `outcomes`, and `timeframes` to narrow scope.\n\n**Credits cost:** 2 per delivered alert.", + "tags": [ + { + "name": "position" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.position_volume_spike/messages/event" + } + ] + }, + "subscribeCloseToBond": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.close_to_bond" + }, + "title": "Subscribe — Close To Bond", + "summary": "Subscribe to `close_to_bond` alerts", + "description": "Fired when a trade occurs at a near-certain-outcome price. **At least one of `min_probability` or `max_probability` is required.** Use `min_probability` (e.g. `0.95`) to trigger when YES is near-certain; use `max_probability` (e.g. `0.05`) for NO near-certain. Optional filters: `position_outcome_indices` — restrict by outcome index (`0` = Yes/Up, `1` = No); `condition_ids` — restrict to specific markets; `position_ids` — restrict to specific outcome tokens; `outcomes` — restrict by outcome name (e.g. `\"Yes\"`, `\"No\"`); `event_slugs` — restrict to specific events.", + "tags": [ + { + "name": "market" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.close_to_bond/messages/subscribe" + } + ] + }, + "unsubscribeCloseToBond": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.close_to_bond" + }, + "title": "Unsubscribe — Close To Bond", + "summary": "Unsubscribe from `close_to_bond` alerts", + "description": "Stop receiving `close_to_bond` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "market" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.close_to_bond/messages/unsubscribe" + } + ] + }, + "receiveCloseToBond": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.close_to_bond" + }, + "title": "Event — Close To Bond", + "summary": "Receive `close_to_bond` alerts", + "description": "Fired when a trade occurs at a near-certain-outcome price. **At least one of `min_probability` or `max_probability` is required.** Use `min_probability` (e.g. `0.95`) to trigger when YES is near-certain; use `max_probability` (e.g. `0.05`) for NO near-certain. Optional filters: `position_outcome_indices` — restrict by outcome index (`0` = Yes/Up, `1` = No); `condition_ids` — restrict to specific markets; `position_ids` — restrict to specific outcome tokens; `outcomes` — restrict by outcome name (e.g. `\"Yes\"`, `\"No\"`); `event_slugs` — restrict to specific events.\n\n**Credits cost:** 2 per delivered alert.", + "tags": [ + { + "name": "market" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.close_to_bond/messages/event" + } + ] + }, + "subscribeMarketCreated": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.market_created" + }, + "title": "Subscribe — Market Created", + "summary": "Subscribe to `market_created` alerts", + "description": "Fired when a new prediction market is created on Polymarket. Filterable by `tags` and `event_slugs`.", + "tags": [ + { + "name": "market" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.market_created/messages/subscribe" + } + ] + }, + "unsubscribeMarketCreated": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.market_created" + }, + "title": "Unsubscribe — Market Created", + "summary": "Unsubscribe from `market_created` alerts", + "description": "Stop receiving `market_created` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "market" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.market_created/messages/unsubscribe" + } + ] + }, + "receiveMarketCreated": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.market_created" + }, + "title": "Event — Market Created", + "summary": "Receive `market_created` alerts", + "description": "Fired when a new prediction market is created on Polymarket. Filterable by `tags` and `event_slugs`.\n\n**Credits cost:** 1 per delivered alert.", + "tags": [ + { + "name": "market" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.market_created/messages/event" + } + ] + }, + "subscribeAssetPriceTick": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.asset_price_tick" + }, + "title": "Subscribe — Asset Price Tick", + "summary": "Subscribe to `asset_price_tick` alerts", + "description": "Fired when the price of a tracked crypto asset updates (BTC, ETH, SOL, XRP, DOGE, BNB, HYPE). Use `asset_symbols` to restrict to specific assets (empty = all).", + "tags": [ + { + "name": "assets" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.asset_price_tick/messages/subscribe" + } + ] + }, + "unsubscribeAssetPriceTick": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.asset_price_tick" + }, + "title": "Unsubscribe — Asset Price Tick", + "summary": "Unsubscribe from `asset_price_tick` alerts", + "description": "Stop receiving `asset_price_tick` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "assets" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.asset_price_tick/messages/unsubscribe" + } + ] + }, + "receiveAssetPriceTick": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.asset_price_tick" + }, + "title": "Event — Asset Price Tick", + "summary": "Receive `asset_price_tick` alerts", + "description": "Fired when the price of a tracked crypto asset updates (BTC, ETH, SOL, XRP, DOGE, BNB, HYPE). Use `asset_symbols` to restrict to specific assets (empty = all).\n\n**Credits cost:** 1 per delivered alert.", + "tags": [ + { + "name": "assets" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.asset_price_tick/messages/event" + } + ] + }, + "subscribeAssetPriceWindowUpdate": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.asset_price_window_update" + }, + "title": "Subscribe — Asset Price Window Update", + "summary": "Subscribe to `asset_price_window_update` alerts", + "description": "Fired at the start and end of each price candle for tracked crypto assets (BTC, ETH, SOL, XRP, DOGE, BNB, HYPE). Payload includes `update_type` (`\"open\"` or `\"close\"`) indicating whether the candle is opening or closing. Use `asset_symbols` to restrict to specific assets. Use `timeframes` to restrict to specific candle sizes — valid values: `\"5m\"`, `\"15m\"`, `\"1h\"`, `\"4h\"`, `\"1d\"`, `\"24h\"`.", + "tags": [ + { + "name": "assets" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.asset_price_window_update/messages/subscribe" + } + ] + }, + "unsubscribeAssetPriceWindowUpdate": { + "action": "send", + "channel": { + "$ref": "#/channels/ws_alerts.asset_price_window_update" + }, + "title": "Unsubscribe — Asset Price Window Update", + "summary": "Unsubscribe from `asset_price_window_update` alerts", + "description": "Stop receiving `asset_price_window_update` alerts. Filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "assets" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.asset_price_window_update/messages/unsubscribe" + } + ] + }, + "receiveAssetPriceWindowUpdate": { + "action": "receive", + "channel": { + "$ref": "#/channels/ws_alerts.asset_price_window_update" + }, + "title": "Event — Asset Price Window Update", + "summary": "Receive `asset_price_window_update` alerts", + "description": "Fired at the start and end of each price candle for tracked crypto assets (BTC, ETH, SOL, XRP, DOGE, BNB, HYPE). Payload includes `update_type` (`\"open\"` or `\"close\"`) indicating whether the candle is opening or closing. Use `asset_symbols` to restrict to specific assets. Use `timeframes` to restrict to specific candle sizes — valid values: `\"5m\"`, `\"15m\"`, `\"1h\"`, `\"4h\"`, `\"1d\"`, `\"24h\"`.\n\n**Credits cost:** 1 per delivered alert.", + "tags": [ + { + "name": "assets" + } + ], + "messages": [ + { + "$ref": "#/channels/ws_alerts.asset_price_window_update/messages/event" + } + ] + } + }, + "components": { + "messages": { + "WsAlertTraderFirstTradeSubscribe": { + "name": "WsAlertTraderFirstTradeSubscribe", + "title": "First Trade — Subscribe", + "summary": "Subscribe to `trader_first_trade` alerts", + "description": "Fired when a tracked trader executes their first trade on Polymarket\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `wallet_addresses`, `min_usd_value`, `min_probability`, `max_probability`, `condition_ids`, `event_slugs`, `exclude_shortterm_markets`", + "tags": [ + { + "name": "trader" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertTraderFirstTradeSubscribeMessage" + } + }, + "WsAlertTraderFirstTradeUnsubscribe": { + "name": "WsAlertTraderFirstTradeUnsubscribe", + "title": "First Trade — Unsubscribe", + "summary": "Unsubscribe from `trader_first_trade` alerts", + "description": "Unsubscribe from `trader_first_trade` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "trader" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertTraderFirstTradeUnsubscribeMessage" + } + }, + "WsAlertTraderFirstTradeEvent": { + "name": "WsAlertTraderFirstTradeEvent", + "title": "First Trade — Event", + "summary": "`trader_first_trade` alert pushed from server", + "description": "Fired when a tracked trader executes their first trade on Polymarket\n\n**Credits cost:** 1 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "trader" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertTraderFirstTradeEvent" + } + }, + "WsAlertTraderNewMarketSubscribe": { + "name": "WsAlertTraderNewMarketSubscribe", + "title": "New Market Entry — Subscribe", + "summary": "Subscribe to `trader_new_market` alerts", + "description": "Fired when a trader places their first trade in a specific market/condition (fires once per trader+market pair)\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `wallet_addresses`, `condition_ids`, `event_slugs`, `min_usd_value`, `min_probability`, `max_probability`, `exclude_shortterm_markets`", + "tags": [ + { + "name": "trader" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertTraderNewMarketSubscribeMessage" + } + }, + "WsAlertTraderNewMarketUnsubscribe": { + "name": "WsAlertTraderNewMarketUnsubscribe", + "title": "New Market Entry — Unsubscribe", + "summary": "Unsubscribe from `trader_new_market` alerts", + "description": "Unsubscribe from `trader_new_market` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "trader" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertTraderNewMarketUnsubscribeMessage" + } + }, + "WsAlertTraderNewMarketEvent": { + "name": "WsAlertTraderNewMarketEvent", + "title": "New Market Entry — Event", + "summary": "`trader_new_market` alert pushed from server", + "description": "Fired when a trader places their first trade in a specific market/condition (fires once per trader+market pair)\n\n**Credits cost:** 1 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "trader" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertTraderNewMarketEvent" + } + }, + "WsAlertTraderWhaleTradeSubscribe": { + "name": "WsAlertTraderWhaleTradeSubscribe", + "title": "Whale Trade — Subscribe", + "summary": "Subscribe to `trader_whale_trade` alerts", + "description": "Fired when a trade meets the configured criteria. Use `min_usd_value` to filter by minimum trade size (optional, defaults to 0 — matches all trades), and `min_probability`/`max_probability` to restrict to a probability range.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `wallet_addresses`, `min_usd_value`, `min_probability`, `max_probability`, `condition_ids`, `event_slugs`, `exclude_shortterm_markets`", + "tags": [ + { + "name": "trader" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertTraderWhaleTradeSubscribeMessage" + } + }, + "WsAlertTraderWhaleTradeUnsubscribe": { + "name": "WsAlertTraderWhaleTradeUnsubscribe", + "title": "Whale Trade — Unsubscribe", + "summary": "Unsubscribe from `trader_whale_trade` alerts", + "description": "Unsubscribe from `trader_whale_trade` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "trader" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertTraderWhaleTradeUnsubscribeMessage" + } + }, + "WsAlertTraderWhaleTradeEvent": { + "name": "WsAlertTraderWhaleTradeEvent", + "title": "Whale Trade — Event", + "summary": "`trader_whale_trade` alert pushed from server", + "description": "Fired when a trade meets the configured criteria. Use `min_usd_value` to filter by minimum trade size (optional, defaults to 0 — matches all trades), and `min_probability`/`max_probability` to restrict to a probability range.\n\n**Credits cost:** 2 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "trader" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertTraderWhaleTradeEvent" + } + }, + "WsAlertTraderNewTradeSubscribe": { + "name": "WsAlertTraderNewTradeSubscribe", + "title": "New Trade — Subscribe", + "summary": "Subscribe to `trader_new_trade` alerts", + "description": "Fired on every order-filled trade. Use `wallet_addresses` to watch specific traders, `min_usd_value` to filter by size, and `min_probability`/`max_probability` to restrict to a probability range.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `wallet_addresses`, `min_usd_value`, `min_probability`, `max_probability`, `condition_ids`, `event_slugs`, `exclude_shortterm_markets`", + "tags": [ + { + "name": "trader" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertTraderNewTradeSubscribeMessage" + } + }, + "WsAlertTraderNewTradeUnsubscribe": { + "name": "WsAlertTraderNewTradeUnsubscribe", + "title": "New Trade — Unsubscribe", + "summary": "Unsubscribe from `trader_new_trade` alerts", + "description": "Unsubscribe from `trader_new_trade` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "trader" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertTraderNewTradeUnsubscribeMessage" + } + }, + "WsAlertTraderNewTradeEvent": { + "name": "WsAlertTraderNewTradeEvent", + "title": "New Trade — Event", + "summary": "`trader_new_trade` alert pushed from server", + "description": "Fired on every order-filled trade. Use `wallet_addresses` to watch specific traders, `min_usd_value` to filter by size, and `min_probability`/`max_probability` to restrict to a probability range.\n\n**Credits cost:** 2 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "trader" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertTraderNewTradeEvent" + } + }, + "WsAlertTraderGlobalPnlSubscribe": { + "name": "WsAlertTraderGlobalPnlSubscribe", + "title": "Global PnL — Subscribe", + "summary": "Subscribe to `trader_global_pnl` alerts", + "description": "Fired when a trader's global PnL crosses a configured threshold\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `traders`, `min_realized_pnl_usd`, `max_realized_pnl_usd`, `min_volume_usd`, `max_volume_usd`, `min_buy_usd`, `min_sell_volume_usd`, `min_win_rate`, `min_markets_traded`, `timeframes`", + "tags": [ + { + "name": "trader" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertTraderGlobalPnlSubscribeMessage" + } + }, + "WsAlertTraderGlobalPnlUnsubscribe": { + "name": "WsAlertTraderGlobalPnlUnsubscribe", + "title": "Global PnL — Unsubscribe", + "summary": "Unsubscribe from `trader_global_pnl` alerts", + "description": "Unsubscribe from `trader_global_pnl` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "trader" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertTraderGlobalPnlUnsubscribeMessage" + } + }, + "WsAlertTraderGlobalPnlEvent": { + "name": "WsAlertTraderGlobalPnlEvent", + "title": "Global PnL — Event", + "summary": "`trader_global_pnl` alert pushed from server", + "description": "Fired when a trader's global PnL crosses a configured threshold\n\n**Credits cost:** 1 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "trader" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertTraderGlobalPnlEvent" + } + }, + "WsAlertTraderMarketPnlSubscribe": { + "name": "WsAlertTraderMarketPnlSubscribe", + "title": "Market PnL — Subscribe", + "summary": "Subscribe to `trader_market_pnl` alerts", + "description": "Fired when a trader's market-level PnL crosses a configured threshold\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `traders`, `condition_ids`, `event_slugs`, `min_realized_pnl_usd`, `max_realized_pnl_usd`, `min_volume_usd`, `max_volume_usd`, `min_buy_usd`, `min_sell_volume_usd`, `timeframes`, `exclude_shortterm_markets`", + "tags": [ + { + "name": "trader" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertTraderMarketPnlSubscribeMessage" + } + }, + "WsAlertTraderMarketPnlUnsubscribe": { + "name": "WsAlertTraderMarketPnlUnsubscribe", + "title": "Market PnL — Unsubscribe", + "summary": "Unsubscribe from `trader_market_pnl` alerts", + "description": "Unsubscribe from `trader_market_pnl` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "trader" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertTraderMarketPnlUnsubscribeMessage" + } + }, + "WsAlertTraderMarketPnlEvent": { + "name": "WsAlertTraderMarketPnlEvent", + "title": "Market PnL — Event", + "summary": "`trader_market_pnl` alert pushed from server", + "description": "Fired when a trader's market-level PnL crosses a configured threshold\n\n**Credits cost:** 1 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "trader" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertTraderMarketPnlEvent" + } + }, + "WsAlertTraderEventPnlSubscribe": { + "name": "WsAlertTraderEventPnlSubscribe", + "title": "Event PnL — Subscribe", + "summary": "Subscribe to `trader_event_pnl` alerts", + "description": "Fired when a trader's event-level PnL crosses a configured threshold\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `traders`, `event_slugs`, `min_realized_pnl_usd`, `max_realized_pnl_usd`, `min_volume_usd`, `max_volume_usd`, `min_buy_usd`, `min_sell_volume_usd`, `min_markets_traded`, `timeframes`, `exclude_shortterm_markets`", + "tags": [ + { + "name": "trader" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertTraderEventPnlSubscribeMessage" + } + }, + "WsAlertTraderEventPnlUnsubscribe": { + "name": "WsAlertTraderEventPnlUnsubscribe", + "title": "Event PnL — Unsubscribe", + "summary": "Unsubscribe from `trader_event_pnl` alerts", + "description": "Unsubscribe from `trader_event_pnl` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "trader" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertTraderEventPnlUnsubscribeMessage" + } + }, + "WsAlertTraderEventPnlEvent": { + "name": "WsAlertTraderEventPnlEvent", + "title": "Event PnL — Event", + "summary": "`trader_event_pnl` alert pushed from server", + "description": "Fired when a trader's event-level PnL crosses a configured threshold\n\n**Credits cost:** 1 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "trader" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertTraderEventPnlEvent" + } + }, + "WsAlertConditionMetricsSubscribe": { + "name": "WsAlertConditionMetricsSubscribe", + "title": "Market Metrics — Subscribe", + "summary": "Subscribe to `condition_metrics` alerts", + "description": "Fired when a market's volume or transaction metrics cross a configured threshold. Use `timeframes` to restrict to specific windows (valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`).\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `condition_ids`, `min_volume_usd`, `max_volume_usd`, `min_fees`, `min_txns`, `min_unique_traders`, `timeframes`", + "tags": [ + { + "name": "market" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertConditionMetricsSubscribeMessage" + } + }, + "WsAlertConditionMetricsUnsubscribe": { + "name": "WsAlertConditionMetricsUnsubscribe", + "title": "Market Metrics — Unsubscribe", + "summary": "Unsubscribe from `condition_metrics` alerts", + "description": "Unsubscribe from `condition_metrics` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "market" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertConditionMetricsUnsubscribeMessage" + } + }, + "WsAlertConditionMetricsEvent": { + "name": "WsAlertConditionMetricsEvent", + "title": "Market Metrics — Event", + "summary": "`condition_metrics` alert pushed from server", + "description": "Fired when a market's volume or transaction metrics cross a configured threshold. Use `timeframes` to restrict to specific windows (valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`).\n\n**Credits cost:** 1 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "market" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertConditionMetricsEvent" + } + }, + "WsAlertEventMetricsSubscribe": { + "name": "WsAlertEventMetricsSubscribe", + "title": "Event Metrics — Subscribe", + "summary": "Subscribe to `event_metrics` alerts", + "description": "Fired when an event's volume or transaction metrics cross a configured threshold. Use `timeframes` to restrict to specific windows (valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`).\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `event_slugs`, `min_volume_usd`, `max_volume_usd`, `min_fees`, `min_txns`, `min_unique_traders`, `timeframes`, `exclude_shortterm_markets`", + "tags": [ + { + "name": "event" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertEventMetricsSubscribeMessage" + } + }, + "WsAlertEventMetricsUnsubscribe": { + "name": "WsAlertEventMetricsUnsubscribe", + "title": "Event Metrics — Unsubscribe", + "summary": "Unsubscribe from `event_metrics` alerts", + "description": "Unsubscribe from `event_metrics` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "event" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertEventMetricsUnsubscribeMessage" + } + }, + "WsAlertEventMetricsEvent": { + "name": "WsAlertEventMetricsEvent", + "title": "Event Metrics — Event", + "summary": "`event_metrics` alert pushed from server", + "description": "Fired when an event's volume or transaction metrics cross a configured threshold. Use `timeframes` to restrict to specific windows (valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`).\n\n**Credits cost:** 1 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "event" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertEventMetricsEvent" + } + }, + "WsAlertPositionMetricsSubscribe": { + "name": "WsAlertPositionMetricsSubscribe", + "title": "Position Metrics — Subscribe", + "summary": "Subscribe to `position_metrics` alerts", + "description": "Fired when a position's volume or transaction metrics cross a configured threshold. Use `timeframes` to restrict to specific windows (valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`).\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `position_ids`, `condition_ids`, `outcomes`, `min_volume_usd`, `max_volume_usd`, `min_buy_usd`, `min_sell_volume_usd`, `min_fees`, `min_txns`, `min_price_change_pct`, `min_probability_change_pct`, `timeframes`", + "tags": [ + { + "name": "position" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertPositionMetricsSubscribeMessage" + } + }, + "WsAlertPositionMetricsUnsubscribe": { + "name": "WsAlertPositionMetricsUnsubscribe", + "title": "Position Metrics — Unsubscribe", + "summary": "Unsubscribe from `position_metrics` alerts", + "description": "Unsubscribe from `position_metrics` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "position" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertPositionMetricsUnsubscribeMessage" + } + }, + "WsAlertPositionMetricsEvent": { + "name": "WsAlertPositionMetricsEvent", + "title": "Position Metrics — Event", + "summary": "`position_metrics` alert pushed from server", + "description": "Fired when a position's volume or transaction metrics cross a configured threshold. Use `timeframes` to restrict to specific windows (valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`).\n\n**Credits cost:** 1 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "position" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertPositionMetricsEvent" + } + }, + "WsAlertMarketVolumeMilestoneSubscribe": { + "name": "WsAlertMarketVolumeMilestoneSubscribe", + "title": "Market Volume Milestone — Subscribe", + "summary": "Subscribe to `market_volume_milestone` alerts", + "description": "Fired when a market's trading volume crosses a milestone threshold in the specified timeframe. **`timeframes` is required** (e.g. `[\"1h\", \"24h\"]`) — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`. Optional `milestone_amounts` restricts to specific USD thresholds (e.g. `[10000, 100000]`). Optional `condition_ids` restricts to specific markets.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `condition_ids`, `timeframes`, `milestone_amounts`", + "tags": [ + { + "name": "market" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertMarketVolumeMilestoneSubscribeMessage" + } + }, + "WsAlertMarketVolumeMilestoneUnsubscribe": { + "name": "WsAlertMarketVolumeMilestoneUnsubscribe", + "title": "Market Volume Milestone — Unsubscribe", + "summary": "Unsubscribe from `market_volume_milestone` alerts", + "description": "Unsubscribe from `market_volume_milestone` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "market" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertMarketVolumeMilestoneUnsubscribeMessage" + } + }, + "WsAlertMarketVolumeMilestoneEvent": { + "name": "WsAlertMarketVolumeMilestoneEvent", + "title": "Market Volume Milestone — Event", + "summary": "`market_volume_milestone` alert pushed from server", + "description": "Fired when a market's trading volume crosses a milestone threshold in the specified timeframe. **`timeframes` is required** (e.g. `[\"1h\", \"24h\"]`) — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`. Optional `milestone_amounts` restricts to specific USD thresholds (e.g. `[10000, 100000]`). Optional `condition_ids` restricts to specific markets.\n\n**Credits cost:** 2 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "market" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertMarketVolumeMilestoneEvent" + } + }, + "WsAlertEventVolumeMilestoneSubscribe": { + "name": "WsAlertEventVolumeMilestoneSubscribe", + "title": "Event Volume Milestone — Subscribe", + "summary": "Subscribe to `event_volume_milestone` alerts", + "description": "Fired when an event's trading volume crosses a milestone threshold in the specified timeframe. **`timeframes` is required** (e.g. `[\"1h\", \"24h\"]`) — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`. Optional `milestone_amounts` restricts to specific USD thresholds. Optional `event_slugs` restricts to specific events.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `event_slugs`, `timeframes`, `milestone_amounts`, `exclude_shortterm_markets`", + "tags": [ + { + "name": "event" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertEventVolumeMilestoneSubscribeMessage" + } + }, + "WsAlertEventVolumeMilestoneUnsubscribe": { + "name": "WsAlertEventVolumeMilestoneUnsubscribe", + "title": "Event Volume Milestone — Unsubscribe", + "summary": "Unsubscribe from `event_volume_milestone` alerts", + "description": "Unsubscribe from `event_volume_milestone` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "event" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertEventVolumeMilestoneUnsubscribeMessage" + } + }, + "WsAlertEventVolumeMilestoneEvent": { + "name": "WsAlertEventVolumeMilestoneEvent", + "title": "Event Volume Milestone — Event", + "summary": "`event_volume_milestone` alert pushed from server", + "description": "Fired when an event's trading volume crosses a milestone threshold in the specified timeframe. **`timeframes` is required** (e.g. `[\"1h\", \"24h\"]`) — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`. Optional `milestone_amounts` restricts to specific USD thresholds. Optional `event_slugs` restricts to specific events.\n\n**Credits cost:** 2 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "event" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertEventVolumeMilestoneEvent" + } + }, + "WsAlertPositionVolumeMilestoneSubscribe": { + "name": "WsAlertPositionVolumeMilestoneSubscribe", + "title": "Position Volume Milestone — Subscribe", + "summary": "Subscribe to `position_volume_milestone` alerts", + "description": "Fired when a position's trading volume crosses a milestone threshold in the specified timeframe. **`timeframes` is required** (e.g. `[\"1h\", \"24h\"]`) — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`. Optional `milestone_amounts` restricts to specific USD thresholds. Optional `position_ids` or `condition_ids` restrict to specific positions/markets.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `position_ids`, `condition_ids`, `outcomes`, `timeframes`, `milestone_amounts`", + "tags": [ + { + "name": "position" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertPositionVolumeMilestoneSubscribeMessage" + } + }, + "WsAlertPositionVolumeMilestoneUnsubscribe": { + "name": "WsAlertPositionVolumeMilestoneUnsubscribe", + "title": "Position Volume Milestone — Unsubscribe", + "summary": "Unsubscribe from `position_volume_milestone` alerts", + "description": "Unsubscribe from `position_volume_milestone` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "position" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertPositionVolumeMilestoneUnsubscribeMessage" + } + }, + "WsAlertPositionVolumeMilestoneEvent": { + "name": "WsAlertPositionVolumeMilestoneEvent", + "title": "Position Volume Milestone — Event", + "summary": "`position_volume_milestone` alert pushed from server", + "description": "Fired when a position's trading volume crosses a milestone threshold in the specified timeframe. **`timeframes` is required** (e.g. `[\"1h\", \"24h\"]`) — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`. Optional `milestone_amounts` restricts to specific USD thresholds. Optional `position_ids` or `condition_ids` restrict to specific positions/markets.\n\n**Credits cost:** 2 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "position" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertPositionVolumeMilestoneEvent" + } + }, + "WsAlertProbabilitySpikeSubscribe": { + "name": "WsAlertProbabilitySpikeSubscribe", + "title": "Probability Spike — Subscribe", + "summary": "Subscribe to `probability_spike` alerts", + "description": "Fired when a position's probability moves significantly. Use `min_probability_change_pct` to set the minimum move (e.g. `10` for 10%). Use `window_secs` to observe moves within a specific time window (e.g. `60` for 60 seconds). Use `spike_direction` (`\"up\"` | `\"down\"` | `\"both\"`) — defaults to `\"up\"` when not provided. Filter by `position_ids` or `outcomes` to narrow scope.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `position_ids`, `condition_ids`, `event_slugs`, `outcomes`, `min_probability_change_pct`, `spike_direction`, `window_secs`, `exclude_shortterm_markets`", + "tags": [ + { + "name": "position" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertProbabilitySpikeSubscribeMessage" + } + }, + "WsAlertProbabilitySpikeUnsubscribe": { + "name": "WsAlertProbabilitySpikeUnsubscribe", + "title": "Probability Spike — Unsubscribe", + "summary": "Unsubscribe from `probability_spike` alerts", + "description": "Unsubscribe from `probability_spike` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "position" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertProbabilitySpikeUnsubscribeMessage" + } + }, + "WsAlertProbabilitySpikeEvent": { + "name": "WsAlertProbabilitySpikeEvent", + "title": "Probability Spike — Event", + "summary": "`probability_spike` alert pushed from server", + "description": "Fired when a position's probability moves significantly. Use `min_probability_change_pct` to set the minimum move (e.g. `10` for 10%). Use `window_secs` to observe moves within a specific time window (e.g. `60` for 60 seconds). Use `spike_direction` (`\"up\"` | `\"down\"` | `\"both\"`) — defaults to `\"up\"` when not provided. Filter by `position_ids` or `outcomes` to narrow scope.\n\n**Credits cost:** 2 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "position" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertProbabilitySpikeEvent" + } + }, + "WsAlertPriceSpikeSubscribe": { + "name": "WsAlertPriceSpikeSubscribe", + "title": "Price Spike — Subscribe", + "summary": "Subscribe to `price_spike` alerts", + "description": "Fired when a position's price moves significantly. Use `min_price_change_pct` to set the minimum move (e.g. `10` for 10%). Use `window_secs` to observe moves within a specific time window. Use `spike_direction` (`\"up\"` | `\"down\"` | `\"both\"`) — defaults to `\"up\"` when not provided. Filter by `position_ids` or `outcomes` to narrow scope.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `position_ids`, `condition_ids`, `event_slugs`, `outcomes`, `min_price_change_pct`, `spike_direction`, `window_secs`, `exclude_shortterm_markets`", + "tags": [ + { + "name": "position" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertPriceSpikeSubscribeMessage" + } + }, + "WsAlertPriceSpikeUnsubscribe": { + "name": "WsAlertPriceSpikeUnsubscribe", + "title": "Price Spike — Unsubscribe", + "summary": "Unsubscribe from `price_spike` alerts", + "description": "Unsubscribe from `price_spike` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "position" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertPriceSpikeUnsubscribeMessage" + } + }, + "WsAlertPriceSpikeEvent": { + "name": "WsAlertPriceSpikeEvent", + "title": "Price Spike — Event", + "summary": "`price_spike` alert pushed from server", + "description": "Fired when a position's price moves significantly. Use `min_price_change_pct` to set the minimum move (e.g. `10` for 10%). Use `window_secs` to observe moves within a specific time window. Use `spike_direction` (`\"up\"` | `\"down\"` | `\"both\"`) — defaults to `\"up\"` when not provided. Filter by `position_ids` or `outcomes` to narrow scope.\n\n**Credits cost:** 2 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "position" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertPriceSpikeEvent" + } + }, + "WsAlertMarketVolumeSpikeSubscribe": { + "name": "WsAlertMarketVolumeSpikeSubscribe", + "title": "Market Volume Spike — Subscribe", + "summary": "Subscribe to `market_volume_spike` alerts", + "description": "Fired when a market's trading volume grows by a multiple of `spike_ratio` within a timeframe. Requires `spike_ratio` (> 1.0, e.g. `2.0` fires when volume doubles). Optional `window_secs` sets the observation window in seconds (max 600). Optional `timeframes` — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `1d`, `24h`, `7d`, `30d`. Optional `condition_ids` restricts to specific markets.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `spike_ratio`, `window_secs`, `condition_ids`, `timeframes`", + "tags": [ + { + "name": "market" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertMarketVolumeSpikeSubscribeMessage" + } + }, + "WsAlertMarketVolumeSpikeUnsubscribe": { + "name": "WsAlertMarketVolumeSpikeUnsubscribe", + "title": "Market Volume Spike — Unsubscribe", + "summary": "Unsubscribe from `market_volume_spike` alerts", + "description": "Unsubscribe from `market_volume_spike` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "market" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertMarketVolumeSpikeUnsubscribeMessage" + } + }, + "WsAlertMarketVolumeSpikeEvent": { + "name": "WsAlertMarketVolumeSpikeEvent", + "title": "Market Volume Spike — Event", + "summary": "`market_volume_spike` alert pushed from server", + "description": "Fired when a market's trading volume grows by a multiple of `spike_ratio` within a timeframe. Requires `spike_ratio` (> 1.0, e.g. `2.0` fires when volume doubles). Optional `window_secs` sets the observation window in seconds (max 600). Optional `timeframes` — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `1d`, `24h`, `7d`, `30d`. Optional `condition_ids` restricts to specific markets.\n\n**Credits cost:** 2 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "market" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertMarketVolumeSpikeEvent" + } + }, + "WsAlertEventVolumeSpikeSubscribe": { + "name": "WsAlertEventVolumeSpikeSubscribe", + "title": "Event Volume Spike — Subscribe", + "summary": "Subscribe to `event_volume_spike` alerts", + "description": "Fired when an event's aggregated trading volume grows by a multiple of `spike_ratio` within a timeframe. Requires `spike_ratio` (> 1.0, e.g. `2.0` fires when volume doubles). Optional `window_secs` sets the observation window in seconds (max 600). Optional `timeframes` and `event_slugs` to narrow scope.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `spike_ratio`, `window_secs`, `event_slugs`, `timeframes`, `exclude_shortterm_markets`", + "tags": [ + { + "name": "event" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertEventVolumeSpikeSubscribeMessage" + } + }, + "WsAlertEventVolumeSpikeUnsubscribe": { + "name": "WsAlertEventVolumeSpikeUnsubscribe", + "title": "Event Volume Spike — Unsubscribe", + "summary": "Unsubscribe from `event_volume_spike` alerts", + "description": "Unsubscribe from `event_volume_spike` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "event" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertEventVolumeSpikeUnsubscribeMessage" + } + }, + "WsAlertEventVolumeSpikeEvent": { + "name": "WsAlertEventVolumeSpikeEvent", + "title": "Event Volume Spike — Event", + "summary": "`event_volume_spike` alert pushed from server", + "description": "Fired when an event's aggregated trading volume grows by a multiple of `spike_ratio` within a timeframe. Requires `spike_ratio` (> 1.0, e.g. `2.0` fires when volume doubles). Optional `window_secs` sets the observation window in seconds (max 600). Optional `timeframes` and `event_slugs` to narrow scope.\n\n**Credits cost:** 2 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "event" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertEventVolumeSpikeEvent" + } + }, + "WsAlertPositionVolumeSpikeSubscribe": { + "name": "WsAlertPositionVolumeSpikeSubscribe", + "title": "Position Volume Spike — Subscribe", + "summary": "Subscribe to `position_volume_spike` alerts", + "description": "Fired when a position's trading volume grows by a multiple of `spike_ratio` within a timeframe. Requires `spike_ratio` (> 1.0, e.g. `2.0` fires when volume doubles). Optional `window_secs` sets the observation window in seconds (max 600). Optional `position_ids`, `condition_ids`, `outcomes`, and `timeframes` to narrow scope.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `spike_ratio`, `window_secs`, `position_ids`, `condition_ids`, `outcomes`, `timeframes`", + "tags": [ + { + "name": "position" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertPositionVolumeSpikeSubscribeMessage" + } + }, + "WsAlertPositionVolumeSpikeUnsubscribe": { + "name": "WsAlertPositionVolumeSpikeUnsubscribe", + "title": "Position Volume Spike — Unsubscribe", + "summary": "Unsubscribe from `position_volume_spike` alerts", + "description": "Unsubscribe from `position_volume_spike` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "position" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertPositionVolumeSpikeUnsubscribeMessage" + } + }, + "WsAlertPositionVolumeSpikeEvent": { + "name": "WsAlertPositionVolumeSpikeEvent", + "title": "Position Volume Spike — Event", + "summary": "`position_volume_spike` alert pushed from server", + "description": "Fired when a position's trading volume grows by a multiple of `spike_ratio` within a timeframe. Requires `spike_ratio` (> 1.0, e.g. `2.0` fires when volume doubles). Optional `window_secs` sets the observation window in seconds (max 600). Optional `position_ids`, `condition_ids`, `outcomes`, and `timeframes` to narrow scope.\n\n**Credits cost:** 2 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "position" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertPositionVolumeSpikeEvent" + } + }, + "WsAlertCloseToBondSubscribe": { + "name": "WsAlertCloseToBondSubscribe", + "title": "Close To Bond — Subscribe", + "summary": "Subscribe to `close_to_bond` alerts", + "description": "Fired when a trade occurs at a near-certain-outcome price. **At least one of `min_probability` or `max_probability` is required.** Use `min_probability` (e.g. `0.95`) to trigger when YES is near-certain; use `max_probability` (e.g. `0.05`) for NO near-certain. Optional filters: `position_outcome_indices` — restrict by outcome index (`0` = Yes/Up, `1` = No); `condition_ids` — restrict to specific markets; `position_ids` — restrict to specific outcome tokens; `outcomes` — restrict by outcome name (e.g. `\"Yes\"`, `\"No\"`); `event_slugs` — restrict to specific events.\n\n**Credits cost:** 2 per delivered alert.\n\n**Applicable filters:** `min_probability`, `max_probability`, `condition_ids`, `position_ids`, `outcomes`, `position_outcome_indices`, `event_slugs`, `exclude_shortterm_markets`", + "tags": [ + { + "name": "market" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertCloseToBondSubscribeMessage" + } + }, + "WsAlertCloseToBondUnsubscribe": { + "name": "WsAlertCloseToBondUnsubscribe", + "title": "Close To Bond — Unsubscribe", + "summary": "Unsubscribe from `close_to_bond` alerts", + "description": "Unsubscribe from `close_to_bond` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "market" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertCloseToBondUnsubscribeMessage" + } + }, + "WsAlertCloseToBondEvent": { + "name": "WsAlertCloseToBondEvent", + "title": "Close To Bond — Event", + "summary": "`close_to_bond` alert pushed from server", + "description": "Fired when a trade occurs at a near-certain-outcome price. **At least one of `min_probability` or `max_probability` is required.** Use `min_probability` (e.g. `0.95`) to trigger when YES is near-certain; use `max_probability` (e.g. `0.05`) for NO near-certain. Optional filters: `position_outcome_indices` — restrict by outcome index (`0` = Yes/Up, `1` = No); `condition_ids` — restrict to specific markets; `position_ids` — restrict to specific outcome tokens; `outcomes` — restrict by outcome name (e.g. `\"Yes\"`, `\"No\"`); `event_slugs` — restrict to specific events.\n\n**Credits cost:** 2 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "market" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertCloseToBondEvent" + } + }, + "WsAlertMarketCreatedSubscribe": { + "name": "WsAlertMarketCreatedSubscribe", + "title": "Market Created — Subscribe", + "summary": "Subscribe to `market_created` alerts", + "description": "Fired when a new prediction market is created on Polymarket. Filterable by `tags` and `event_slugs`.\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `event_slugs`, `tags`, `exclude_shortterm_markets`", + "tags": [ + { + "name": "market" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertMarketCreatedSubscribeMessage" + } + }, + "WsAlertMarketCreatedUnsubscribe": { + "name": "WsAlertMarketCreatedUnsubscribe", + "title": "Market Created — Unsubscribe", + "summary": "Unsubscribe from `market_created` alerts", + "description": "Unsubscribe from `market_created` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "market" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertMarketCreatedUnsubscribeMessage" + } + }, + "WsAlertMarketCreatedEvent": { + "name": "WsAlertMarketCreatedEvent", + "title": "Market Created — Event", + "summary": "`market_created` alert pushed from server", + "description": "Fired when a new prediction market is created on Polymarket. Filterable by `tags` and `event_slugs`.\n\n**Credits cost:** 1 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "market" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertMarketCreatedEvent" + } + }, + "WsAlertAssetPriceTickSubscribe": { + "name": "WsAlertAssetPriceTickSubscribe", + "title": "Asset Price Tick — Subscribe", + "summary": "Subscribe to `asset_price_tick` alerts", + "description": "Fired when the price of a tracked crypto asset updates (BTC, ETH, SOL, XRP, DOGE, BNB, HYPE). Use `asset_symbols` to restrict to specific assets (empty = all).\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `asset_symbols`", + "tags": [ + { + "name": "assets" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertAssetPriceTickSubscribeMessage" + } + }, + "WsAlertAssetPriceTickUnsubscribe": { + "name": "WsAlertAssetPriceTickUnsubscribe", + "title": "Asset Price Tick — Unsubscribe", + "summary": "Unsubscribe from `asset_price_tick` alerts", + "description": "Unsubscribe from `asset_price_tick` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "assets" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertAssetPriceTickUnsubscribeMessage" + } + }, + "WsAlertAssetPriceTickEvent": { + "name": "WsAlertAssetPriceTickEvent", + "title": "Asset Price Tick — Event", + "summary": "`asset_price_tick` alert pushed from server", + "description": "Fired when the price of a tracked crypto asset updates (BTC, ETH, SOL, XRP, DOGE, BNB, HYPE). Use `asset_symbols` to restrict to specific assets (empty = all).\n\n**Credits cost:** 1 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "assets" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertAssetPriceTickEvent" + } + }, + "WsAlertAssetPriceWindowUpdateSubscribe": { + "name": "WsAlertAssetPriceWindowUpdateSubscribe", + "title": "Asset Price Window Update — Subscribe", + "summary": "Subscribe to `asset_price_window_update` alerts", + "description": "Fired at the start and end of each price candle for tracked crypto assets (BTC, ETH, SOL, XRP, DOGE, BNB, HYPE). Payload includes `update_type` (`\"open\"` or `\"close\"`) indicating whether the candle is opening or closing. Use `asset_symbols` to restrict to specific assets. Use `timeframes` to restrict to specific candle sizes — valid values: `\"5m\"`, `\"15m\"`, `\"1h\"`, `\"4h\"`, `\"1d\"`, `\"24h\"`.\n\n**Credits cost:** 1 per delivered alert.\n\n**Applicable filters:** `asset_symbols`, `timeframes`", + "tags": [ + { + "name": "assets" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertAssetPriceWindowUpdateSubscribeMessage" + } + }, + "WsAlertAssetPriceWindowUpdateUnsubscribe": { + "name": "WsAlertAssetPriceWindowUpdateUnsubscribe", + "title": "Asset Price Window Update — Unsubscribe", + "summary": "Unsubscribe from `asset_price_window_update` alerts", + "description": "Unsubscribe from `asset_price_window_update` alerts. The filter fields must match the original subscription exactly.", + "tags": [ + { + "name": "assets" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertAssetPriceWindowUpdateUnsubscribeMessage" + } + }, + "WsAlertAssetPriceWindowUpdateEvent": { + "name": "WsAlertAssetPriceWindowUpdateEvent", + "title": "Asset Price Window Update — Event", + "summary": "`asset_price_window_update` alert pushed from server", + "description": "Fired at the start and end of each price candle for tracked crypto assets (BTC, ETH, SOL, XRP, DOGE, BNB, HYPE). Payload includes `update_type` (`\"open\"` or `\"close\"`) indicating whether the candle is opening or closing. Use `asset_symbols` to restrict to specific assets. Use `timeframes` to restrict to specific candle sizes — valid values: `\"5m\"`, `\"15m\"`, `\"1h\"`, `\"4h\"`, `\"1d\"`, `\"24h\"`.\n\n**Credits cost:** 1 per delivered alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "tags": [ + { + "name": "assets" + } + ], + "payload": { + "$ref": "#/components/schemas/WsAlertAssetPriceWindowUpdateEvent" + } + }, + "WsAlertSubscribed": { + "summary": "Alert subscription acknowledgement", + "payload": { + "$ref": "#/components/schemas/WsAlertSubscribedResponse" + } + }, + "WsAlertUnsubscribed": { + "summary": "Alert unsubscription acknowledgement", + "payload": { + "$ref": "#/components/schemas/WsAlertUnsubscribedResponse" + } + }, + "WsAlertError": { + "summary": "Alerts WebSocket error response", + "payload": { + "$ref": "#/components/schemas/WsAlertErrorResponse" + } + } + }, + "schemas": { + "WebhookDeliveryEnvelope": { + "type": "object", + "description": "Outer envelope for every webhook HTTP POST delivery. The `data` field contains the event-specific payload. Delivery headers sent with every POST: `X-Webhook-ID` (subscription UUID), `X-Delivery-ID` (this attempt's UUID), `X-Event-Type` (event name string, e.g. `trader_first_trade`), `X-Attempt` (attempt number, 1-indexed). When the webhook has a secret configured, `X-Webhook-Signature: sha256=` is also included — compute HMAC-SHA256 over the raw request body using your secret to verify.", + "required": [ + "id", + "event", + "data", + "timestamp", + "webhook_id", + "attempt" + ], + "properties": { + "id": { + "type": "string", + "format": "uuid", + "description": "UUID of this specific delivery attempt (matches X-Delivery-ID header)" + }, + "event": { + "type": "string", + "description": "Event name (e.g. `trader_first_trade`). On test deliveries the suffix `_test` is appended." + }, + "data": { + "type": "object", + "description": "Event-specific payload — schema varies by event type; see the individual callback definitions" + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds when this delivery was created" + }, + "webhook_id": { + "type": "string", + "format": "uuid", + "description": "UUID of the webhook subscription that fired (matches X-Webhook-ID header)" + }, + "attempt": { + "type": "integer", + "minimum": 1, + "description": "Delivery attempt number. 1 = first attempt; increments on each retry." + } + } + }, + "FirstTradePayload": { + "type": "object", + "description": "Payload delivered when a tracked trader executes their first-ever trade on Polymarket", + "required": [ + "trader", + "taker", + "position_id", + "trade_id", + "hash", + "block", + "confirmed_at", + "amount_usd", + "shares_amount", + "fee", + "side", + "price", + "exchange", + "trade_type" + ], + "properties": { + "trader": { + "type": "string", + "description": "Limit-order maker wallet address (lowercase)" + }, + "taker": { + "type": "string", + "description": "Order filler wallet address (lowercase)" + }, + "position_id": { + "type": "string", + "description": "ERC-1155 outcome token ID" + }, + "condition_id": { + "type": [ + "string", + "null" + ], + "description": "Parent market condition ID (0x-prefixed hex)" + }, + "outcome": { + "type": [ + "string", + "null" + ], + "description": "Outcome name (e.g. \"Yes\", \"No\")" + }, + "outcome_index": { + "type": [ + "integer", + "null" + ], + "description": "Outcome index: 0 = Yes/Up, 1 = No" + }, + "question": { + "type": [ + "string", + "null" + ], + "description": "Market question text" + }, + "market_slug": { + "type": [ + "string", + "null" + ], + "description": "Market slug" + }, + "event_slug": { + "type": [ + "string", + "null" + ], + "description": "Parent event slug" + }, + "trade_id": { + "type": "string", + "description": "Unique trade identifier" + }, + "hash": { + "type": "string", + "description": "Transaction hash" + }, + "block": { + "type": "integer", + "format": "int64", + "description": "Block number" + }, + "confirmed_at": { + "type": "integer", + "format": "int64", + "description": "Block confirmation timestamp (Unix seconds)" + }, + "amount_usd": { + "type": "number", + "description": "USD size of the trade (6 decimal places)" + }, + "shares_amount": { + "type": "number", + "description": "Outcome shares traded (6 decimal places)" + }, + "fee": { + "type": "number", + "description": "Fee paid in USD (6 decimal places)" + }, + "side": { + "type": "string", + "enum": [ + "Buy", + "Sell" + ], + "description": "Trade direction" + }, + "price": { + "type": "number", + "minimum": 0.0, + "maximum": 1.0, + "description": "Outcome token price (0.0–1.0)" + }, + "exchange": { + "type": "string", + "description": "Exchange identifier" + }, + "trade_type": { + "type": "string", + "description": "Trade type identifier" + } + } + }, + "NewMarketPayload": { + "type": "object", + "description": "Payload delivered when a trader places their first trade in a specific market (fires once per trader+market pair)", + "required": [ + "trader", + "taker", + "position_id", + "trade_id", + "hash", + "block", + "confirmed_at", + "amount_usd", + "shares_amount", + "fee", + "side", + "price", + "exchange", + "trade_type" + ], + "properties": { + "trader": { + "type": "string", + "description": "Limit-order maker wallet address (lowercase)" + }, + "taker": { + "type": "string", + "description": "Order filler wallet address (lowercase)" + }, + "position_id": { + "type": "string", + "description": "ERC-1155 outcome token ID" + }, + "condition_id": { + "type": [ + "string", + "null" + ], + "description": "Parent market condition ID" + }, + "outcome": { + "type": [ + "string", + "null" + ], + "description": "Outcome name (e.g. \"Yes\", \"No\")" + }, + "outcome_index": { + "type": [ + "integer", + "null" + ], + "description": "Outcome index: 0 = Yes/Up, 1 = No" + }, + "question": { + "type": [ + "string", + "null" + ], + "description": "Market question text" + }, + "market_slug": { + "type": [ + "string", + "null" + ], + "description": "Market slug" + }, + "event_slug": { + "type": [ + "string", + "null" + ], + "description": "Parent event slug" + }, + "trade_id": { + "type": "string", + "description": "Unique trade identifier" + }, + "hash": { + "type": "string", + "description": "Transaction hash" + }, + "block": { + "type": "integer", + "format": "int64", + "description": "Block number" + }, + "confirmed_at": { + "type": "integer", + "format": "int64", + "description": "Block confirmation timestamp (Unix seconds)" + }, + "amount_usd": { + "type": "number", + "description": "USD size of the trade (6 decimal places)" + }, + "shares_amount": { + "type": "number", + "description": "Outcome shares traded (6 decimal places)" + }, + "fee": { + "type": "number", + "description": "Fee paid in USD (6 decimal places)" + }, + "side": { + "type": "string", + "enum": [ + "Buy", + "Sell" + ], + "description": "Trade direction" + }, + "price": { + "type": "number", + "minimum": 0.0, + "maximum": 1.0, + "description": "Outcome token price (0.0–1.0)" + }, + "probability": { + "type": [ + "number", + "null" + ], + "minimum": 0.0, + "maximum": 1.0, + "description": "Implied probability (0.0–1.0); null when outcome is unknown" + }, + "exchange": { + "type": "string", + "description": "Exchange identifier" + }, + "trade_type": { + "type": "string", + "description": "Trade type identifier" + } + } + }, + "WhaleTradePayload": { + "type": "object", + "description": "Payload delivered when a trade exceeds the configured size and probability thresholds", + "required": [ + "trader", + "taker", + "position_id", + "trade_id", + "hash", + "block", + "confirmed_at", + "amount_usd", + "shares_amount", + "fee", + "side", + "price", + "exchange", + "trade_type" + ], + "properties": { + "trader": { + "type": "string", + "description": "Limit-order maker wallet address (lowercase)" + }, + "taker": { + "type": "string", + "description": "Order filler wallet address (lowercase)" + }, + "position_id": { + "type": "string", + "description": "ERC-1155 outcome token ID" + }, + "condition_id": { + "type": [ + "string", + "null" + ], + "description": "Parent market condition ID" + }, + "outcome": { + "type": [ + "string", + "null" + ], + "description": "Outcome name (e.g. \"Yes\", \"No\")" + }, + "outcome_index": { + "type": [ + "integer", + "null" + ], + "description": "Outcome index: 0 = Yes/Up, 1 = No" + }, + "question": { + "type": [ + "string", + "null" + ], + "description": "Market question text" + }, + "market_slug": { + "type": [ + "string", + "null" + ], + "description": "Market slug" + }, + "event_slug": { + "type": [ + "string", + "null" + ], + "description": "Parent event slug" + }, + "trade_id": { + "type": "string", + "description": "Unique trade identifier" + }, + "hash": { + "type": "string", + "description": "Transaction hash" + }, + "block": { + "type": "integer", + "format": "int64", + "description": "Block number" + }, + "confirmed_at": { + "type": "integer", + "format": "int64", + "description": "Block confirmation timestamp (Unix seconds)" + }, + "amount_usd": { + "type": "number", + "description": "USD size of the trade (6 decimal places)" + }, + "shares_amount": { + "type": "number", + "description": "Outcome shares traded (6 decimal places)" + }, + "fee": { + "type": "number", + "description": "Fee paid in USD (6 decimal places)" + }, + "side": { + "type": "string", + "enum": [ + "Buy", + "Sell" + ], + "description": "Trade direction" + }, + "price": { + "type": "number", + "minimum": 0.0, + "maximum": 1.0, + "description": "Outcome token price (0.0–1.0)" + }, + "probability": { + "type": [ + "number", + "null" + ], + "minimum": 0.0, + "maximum": 1.0, + "description": "Implied probability (0.0–1.0); null when outcome is unknown" + }, + "exchange": { + "type": "string", + "description": "Exchange identifier" + }, + "trade_type": { + "type": "string", + "description": "Trade type identifier" + } + } + }, + "NewTradePayload": { + "type": "object", + "description": "Payload delivered on every order-filled trade", + "required": [ + "trader", + "taker", + "position_id", + "trade_id", + "hash", + "block", + "confirmed_at", + "amount_usd", + "shares_amount", + "fee", + "side", + "price", + "exchange", + "trade_type" + ], + "properties": { + "trader": { + "type": "string", + "description": "Limit-order maker wallet address (lowercase)" + }, + "taker": { + "type": "string", + "description": "Order filler wallet address (lowercase)" + }, + "position_id": { + "type": "string", + "description": "ERC-1155 outcome token ID" + }, + "condition_id": { + "type": [ + "string", + "null" + ], + "description": "Parent market condition ID" + }, + "outcome": { + "type": [ + "string", + "null" + ], + "description": "Outcome name (e.g. \"Yes\", \"No\")" + }, + "outcome_index": { + "type": [ + "integer", + "null" + ], + "description": "Outcome index: 0 = Yes/Up, 1 = No" + }, + "question": { + "type": [ + "string", + "null" + ], + "description": "Market question text" + }, + "market_slug": { + "type": [ + "string", + "null" + ], + "description": "Market slug" + }, + "event_slug": { + "type": [ + "string", + "null" + ], + "description": "Parent event slug" + }, + "trade_id": { + "type": "string", + "description": "Unique trade identifier" + }, + "hash": { + "type": "string", + "description": "Transaction hash" + }, + "block": { + "type": "integer", + "format": "int64", + "description": "Block number" + }, + "confirmed_at": { + "type": "integer", + "format": "int64", + "description": "Block confirmation timestamp (Unix seconds)" + }, + "amount_usd": { + "type": "number", + "description": "USD size of the trade (6 decimal places)" + }, + "shares_amount": { + "type": "number", + "description": "Outcome shares traded (6 decimal places)" + }, + "fee": { + "type": "number", + "description": "Fee paid in USD (6 decimal places)" + }, + "side": { + "type": "string", + "enum": [ + "Buy", + "Sell" + ], + "description": "Trade direction" + }, + "price": { + "type": "number", + "minimum": 0.0, + "maximum": 1.0, + "description": "Outcome token price (0.0–1.0)" + }, + "probability": { + "type": [ + "number", + "null" + ], + "minimum": 0.0, + "maximum": 1.0, + "description": "Implied probability (0.0–1.0); null when outcome is unknown" + }, + "exchange": { + "type": "string", + "description": "Exchange identifier" + }, + "trade_type": { + "type": "string", + "description": "Trade type identifier" + } + } + }, + "GlobalPnlPayload": { + "type": "object", + "description": "Payload delivered when a trader's global PnL (across all markets) crosses a configured threshold", + "required": [ + "timeframe" + ], + "properties": { + "trader": { + "type": [ + "string", + "null" + ], + "description": "Trader wallet address (lowercase)" + }, + "timeframe": { + "type": "string", + "enum": [ + "1d", + "7d", + "30d", + "lifetime" + ], + "description": "PnL aggregation window" + }, + "realized_pnl_usd": { + "type": [ + "number", + "null" + ], + "description": "Realized PnL in USD (positive = profit, negative = loss)" + }, + "events_traded": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Number of distinct events traded" + }, + "markets_traded": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Number of distinct markets traded" + }, + "total_buys": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Total buy transactions" + }, + "total_sells": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Total sell transactions" + }, + "total_redemptions": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Total redemption transactions" + }, + "total_merges": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Total merge transactions" + }, + "total_volume_usd": { + "type": [ + "number", + "null" + ], + "description": "Total USD volume (buys + sells + redemptions + merges)" + }, + "buy_volume_usd": { + "type": [ + "number", + "null" + ], + "description": "Total buy volume in USD" + }, + "sell_volume_usd": { + "type": [ + "number", + "null" + ], + "description": "Total sell volume in USD" + }, + "redemption_volume_usd": { + "type": [ + "number", + "null" + ], + "description": "Total redemption volume in USD" + }, + "merge_volume_usd": { + "type": [ + "number", + "null" + ], + "description": "Total merge volume in USD" + }, + "markets_won": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Number of markets where trader realised a profit" + }, + "markets_lost": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Number of markets where trader realised a loss" + }, + "market_win_rate_pct": { + "type": [ + "number", + "null" + ], + "description": "Market win rate as a percentage (0.0–100.0)" + }, + "avg_pnl_per_market": { + "type": [ + "number", + "null" + ], + "description": "Average PnL per market in USD" + }, + "avg_pnl_per_trade": { + "type": [ + "number", + "null" + ], + "description": "Average PnL per trade in USD" + }, + "avg_hold_time_seconds": { + "type": [ + "number", + "null" + ], + "description": "Average hold time across all positions (seconds)" + }, + "total_fees": { + "type": [ + "number", + "null" + ], + "description": "Total fees paid in USD" + }, + "best_trade_pnl_usd": { + "type": [ + "number", + "null" + ], + "description": "Best single-trade PnL in USD" + }, + "best_trade_condition_id": { + "type": [ + "string", + "null" + ], + "description": "Condition ID of the best trade" + }, + "first_trade_at": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Timestamp of the first trade (Unix seconds)" + }, + "last_trade_at": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Timestamp of the most recent trade (Unix seconds)" + } + } + }, + "MarketPnlPayload": { + "type": "object", + "description": "Payload delivered when a trader's per-market PnL crosses a configured threshold", + "required": [ + "timeframe" + ], + "properties": { + "trader": { + "type": [ + "string", + "null" + ], + "description": "Trader wallet address (lowercase)" + }, + "condition_id": { + "type": [ + "string", + "null" + ], + "description": "Market condition ID" + }, + "event_slug": { + "type": [ + "string", + "null" + ], + "description": "Parent event slug" + }, + "timeframe": { + "type": "string", + "enum": [ + "1d", + "7d", + "30d", + "lifetime" + ], + "description": "PnL aggregation window" + }, + "outcomes_traded": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Number of distinct outcomes traded in this market" + }, + "total_buys": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_sells": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_redemptions": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_merges": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "buy_usd": { + "type": [ + "number", + "null" + ], + "description": "Total buy volume in USD" + }, + "sell_usd": { + "type": [ + "number", + "null" + ], + "description": "Total sell volume in USD" + }, + "redemption_usd": { + "type": [ + "number", + "null" + ], + "description": "Total redemption volume in USD" + }, + "merge_usd": { + "type": [ + "number", + "null" + ], + "description": "Total merge volume in USD" + }, + "realized_pnl_usd": { + "type": [ + "number", + "null" + ], + "description": "Realized PnL in USD for this market" + }, + "winning_outcomes": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Number of outcomes with positive PnL" + }, + "total_fees": { + "type": [ + "number", + "null" + ], + "description": "Total fees paid in USD for this market" + }, + "first_trade_at": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Timestamp of first trade in market (Unix seconds)" + }, + "last_trade_at": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Timestamp of most recent trade in market (Unix seconds)" + } + } + }, + "EventPnlPayload": { + "type": "object", + "description": "Payload delivered when a trader's per-event PnL crosses a configured threshold", + "required": [ + "timeframe" + ], + "properties": { + "trader": { + "type": [ + "string", + "null" + ], + "description": "Trader wallet address (lowercase)" + }, + "event_slug": { + "type": [ + "string", + "null" + ], + "description": "Event slug" + }, + "timeframe": { + "type": "string", + "enum": [ + "1d", + "7d", + "30d", + "lifetime" + ], + "description": "PnL aggregation window" + }, + "markets_traded": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Number of distinct markets traded in this event" + }, + "outcomes_traded": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_buys": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_sells": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_redemptions": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_merges": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_volume_usd": { + "type": [ + "number", + "null" + ], + "description": "Total volume in USD" + }, + "buy_usd": { + "type": [ + "number", + "null" + ] + }, + "sell_usd": { + "type": [ + "number", + "null" + ] + }, + "redemption_usd": { + "type": [ + "number", + "null" + ] + }, + "merge_usd": { + "type": [ + "number", + "null" + ] + }, + "realized_pnl_usd": { + "type": [ + "number", + "null" + ], + "description": "Realized PnL in USD for this event" + }, + "winning_markets": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "losing_markets": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_fees": { + "type": [ + "number", + "null" + ] + }, + "first_trade_at": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Unix seconds" + }, + "last_trade_at": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Unix seconds" + } + } + }, + "ConditionMetricsPayload": { + "type": "object", + "description": "Payload delivered when a market's volume or transaction metrics cross a configured threshold", + "properties": { + "condition_id": { + "type": [ + "string", + "null" + ], + "description": "Market condition ID" + }, + "timeframe": { + "type": [ + "string", + "null" + ], + "description": "Aggregation window (e.g. \"1h\", \"24h\")" + }, + "volume_usd": { + "type": [ + "number", + "null" + ], + "description": "Total trading volume in USD for this timeframe" + }, + "fees": { + "type": [ + "number", + "null" + ], + "description": "Total fees collected in USD" + }, + "txns": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Total number of transactions" + }, + "unique_traders": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Number of unique traders" + } + } + }, + "EventMetricsPayload": { + "type": "object", + "description": "Payload delivered when an event's aggregated volume or transaction metrics cross a configured threshold", + "properties": { + "event_slug": { + "type": [ + "string", + "null" + ], + "description": "Event slug" + }, + "timeframe": { + "type": [ + "string", + "null" + ], + "description": "Aggregation window (e.g. \"1h\", \"24h\")" + }, + "volume_usd": { + "type": [ + "number", + "null" + ], + "description": "Total aggregated volume across all markets in the event (USD)" + }, + "fees": { + "type": [ + "number", + "null" + ], + "description": "Total fees collected in USD" + }, + "txns": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Total number of transactions" + }, + "unique_traders": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Number of unique traders" + } + } + }, + "PositionMetricsPayload": { + "type": "object", + "description": "Payload delivered when a position's volume or transaction metrics cross a configured threshold", + "properties": { + "position_id": { + "type": [ + "string", + "null" + ], + "description": "ERC-1155 outcome token ID" + }, + "outcome": { + "type": [ + "string", + "null" + ], + "description": "Outcome name (e.g. \"Yes\", \"No\")" + }, + "outcome_index": { + "type": [ + "integer", + "null" + ], + "format": "int16", + "description": "Outcome index" + }, + "timeframe": { + "type": [ + "string", + "null" + ], + "description": "Aggregation window (e.g. \"1h\", \"24h\")" + }, + "volume_usd": { + "type": [ + "number", + "null" + ], + "description": "Total trading volume in USD" + }, + "buy_volume_usd": { + "type": [ + "number", + "null" + ], + "description": "Buy volume in USD" + }, + "sell_volume_usd": { + "type": [ + "number", + "null" + ], + "description": "Sell volume in USD" + }, + "fees": { + "type": [ + "number", + "null" + ], + "description": "Total fees in USD" + }, + "txns": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "buys": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "sells": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "unique_traders": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "price_open": { + "type": [ + "number", + "null" + ], + "minimum": 0.0, + "maximum": 1.0 + }, + "price_close": { + "type": [ + "number", + "null" + ], + "minimum": 0.0, + "maximum": 1.0 + }, + "price_high": { + "type": [ + "number", + "null" + ], + "minimum": 0.0, + "maximum": 1.0 + }, + "price_low": { + "type": [ + "number", + "null" + ], + "minimum": 0.0, + "maximum": 1.0 + }, + "probability_open": { + "type": [ + "number", + "null" + ], + "minimum": 0.0, + "maximum": 1.0 + }, + "probability_close": { + "type": [ + "number", + "null" + ], + "minimum": 0.0, + "maximum": 1.0 + }, + "probability_high": { + "type": [ + "number", + "null" + ], + "minimum": 0.0, + "maximum": 1.0 + }, + "probability_low": { + "type": [ + "number", + "null" + ], + "minimum": 0.0, + "maximum": 1.0 + } + } + }, + "VolumeMilestonePayload": { + "type": "object", + "description": "Payload delivered when a market's trading volume crosses a USD milestone in the specified timeframe", + "required": [ + "condition_id", + "timeframe", + "milestone_usd", + "current_volume_usd", + "fees", + "txns" + ], + "properties": { + "condition_id": { + "type": "string", + "description": "Market condition ID" + }, + "timeframe": { + "type": "string", + "description": "Aggregation window that crossed the milestone (e.g. \"1h\", \"24h\")" + }, + "milestone_usd": { + "type": "number", + "description": "The USD milestone amount that was crossed" + }, + "current_volume_usd": { + "type": "number", + "description": "Current volume at time of trigger (USD)" + }, + "fees": { + "type": "number", + "description": "Total fees in USD for this timeframe" + }, + "txns": { + "type": "integer", + "format": "int64", + "description": "Total transactions in this timeframe" + } + } + }, + "EventVolumeMilestonePayload": { + "type": "object", + "description": "Payload delivered when an event's aggregated trading volume crosses a USD milestone", + "required": [ + "event_slug", + "timeframe", + "milestone_usd", + "current_volume_usd", + "fees", + "txns" + ], + "properties": { + "event_slug": { + "type": "string", + "description": "Event slug" + }, + "timeframe": { + "type": "string", + "description": "Aggregation window (e.g. \"1h\", \"24h\")" + }, + "milestone_usd": { + "type": "number", + "description": "The USD milestone amount that was crossed" + }, + "current_volume_usd": { + "type": "number", + "description": "Current aggregated event volume at time of trigger (USD)" + }, + "fees": { + "type": "number", + "description": "Total fees in USD for this timeframe" + }, + "txns": { + "type": "integer", + "format": "int64", + "description": "Total transactions in this timeframe" + } + } + }, + "PositionVolumeMilestonePayload": { + "type": "object", + "description": "Payload delivered when a position's trading volume crosses a USD milestone", + "required": [ + "position_id", + "timeframe", + "milestone_usd", + "current_volume_usd", + "buy_volume_usd", + "sell_volume_usd", + "fees", + "txns", + "buys", + "sells" + ], + "properties": { + "condition_id": { + "type": [ + "string", + "null" + ], + "description": "Parent market condition ID" + }, + "position_id": { + "type": "string", + "description": "ERC-1155 outcome token ID" + }, + "outcome": { + "type": [ + "string", + "null" + ], + "description": "Outcome name (e.g. \"Yes\", \"No\")" + }, + "outcome_index": { + "type": [ + "integer", + "null" + ], + "format": "int16", + "description": "Outcome index" + }, + "timeframe": { + "type": "string", + "description": "Aggregation window (e.g. \"1h\", \"24h\")" + }, + "milestone_usd": { + "type": "number", + "description": "The USD milestone amount that was crossed" + }, + "current_volume_usd": { + "type": "number", + "description": "Current position volume at time of trigger (USD)" + }, + "buy_volume_usd": { + "type": "number", + "description": "Buy volume in USD for this timeframe" + }, + "sell_volume_usd": { + "type": "number", + "description": "Sell volume in USD for this timeframe" + }, + "fees": { + "type": "number", + "description": "Total fees in USD" + }, + "txns": { + "type": "integer", + "format": "int64" + }, + "buys": { + "type": "integer", + "format": "int64" + }, + "sells": { + "type": "integer", + "format": "int64" + } + } + }, + "ProbabilitySpikePayload": { + "type": "object", + "required": [ + "position_id", + "previous_probability", + "current_probability", + "spike_direction", + "spike_pct" + ], + "properties": { + "position_id": { + "type": "string", + "description": "Outcome token ID" + }, + "condition_id": { + "type": [ + "string", + "null" + ], + "description": "Market condition ID" + }, + "event_slug": { + "type": [ + "string", + "null" + ], + "description": "Event slug" + }, + "outcome": { + "type": [ + "string", + "null" + ], + "description": "Outcome name (e.g. \"Yes\", \"No\")" + }, + "outcome_index": { + "type": [ + "integer", + "null" + ], + "format": "int16", + "description": "Outcome index" + }, + "previous_probability": { + "type": "number", + "description": "Probability at the start of the observation window (baseline snapshot, 0.0–1.0)" + }, + "current_probability": { + "type": "number", + "description": "Current probability that triggered the spike (0.0–1.0)" + }, + "spike_direction": { + "type": "string", + "enum": [ + "up", + "down" + ], + "description": "`\"up\"` = probability rising, `\"down\"` = probability falling" + }, + "spike_pct": { + "type": "number", + "description": "Percentage move that triggered this notification. Positive = up, negative = down." + } + } + }, + "PriceSpikePayload": { + "type": "object", + "required": [ + "position_id", + "previous_price", + "current_price", + "spike_direction", + "spike_pct" + ], + "properties": { + "position_id": { + "type": "string", + "description": "Outcome token ID" + }, + "condition_id": { + "type": [ + "string", + "null" + ], + "description": "Market condition ID" + }, + "event_slug": { + "type": [ + "string", + "null" + ], + "description": "Event slug" + }, + "outcome": { + "type": [ + "string", + "null" + ], + "description": "Outcome name (e.g. \"Yes\", \"No\")" + }, + "outcome_index": { + "type": [ + "integer", + "null" + ], + "format": "int16", + "description": "Outcome index" + }, + "previous_price": { + "type": "number", + "description": "Price at the start of the observation window (baseline snapshot, 0.0–1.0)" + }, + "current_price": { + "type": "number", + "description": "Current price that triggered the spike (0.0–1.0)" + }, + "spike_direction": { + "type": "string", + "enum": [ + "up", + "down" + ], + "description": "`\"up\"` = price rising, `\"down\"` = price falling" + }, + "spike_pct": { + "type": "number", + "description": "Percentage move that triggered this notification. Positive = up, negative = down." + } + } + }, + "MarketVolumeSpikePayload": { + "type": "object", + "description": "Payload delivered when a market's volume has spiked since the last snapshot", + "required": [ + "condition_id", + "timeframe", + "current_volume_usd", + "snapshot_volume_usd", + "delta_volume_usd", + "spike_pct", + "txns", + "fees" + ], + "properties": { + "condition_id": { + "type": "string", + "description": "Market condition ID" + }, + "timeframe": { + "type": "string", + "description": "Aggregation window (e.g. \"1h\", \"24h\")" + }, + "current_volume_usd": { + "type": "number", + "description": "Current volume at the time of the spike (USD)" + }, + "snapshot_volume_usd": { + "type": "number", + "description": "Volume at the snapshot baseline (USD)" + }, + "delta_volume_usd": { + "type": "number", + "description": "New volume since the snapshot that triggered this notification (USD)" + }, + "spike_pct": { + "type": "number", + "description": "Volume growth as a percentage of the snapshot (e.g. 200.0 means volume tripled)" + }, + "txns": { + "type": "integer", + "format": "int64", + "description": "Total transactions in this timeframe" + }, + "fees": { + "type": "number", + "description": "Total fees in USD for this timeframe" + } + } + }, + "EventVolumeSpikePayload": { + "type": "object", + "description": "Payload delivered when an event's aggregated volume has spiked since the last snapshot", + "required": [ + "event_slug", + "timeframe", + "current_volume_usd", + "snapshot_volume_usd", + "delta_volume_usd", + "spike_pct", + "txns", + "fees" + ], + "properties": { + "event_slug": { + "type": "string", + "description": "Event slug" + }, + "timeframe": { + "type": "string", + "description": "Aggregation window (e.g. \"1h\", \"24h\")" + }, + "current_volume_usd": { + "type": "number", + "description": "Current aggregated event volume at time of the spike (USD)" + }, + "snapshot_volume_usd": { + "type": "number", + "description": "Volume at the snapshot baseline (USD)" + }, + "delta_volume_usd": { + "type": "number", + "description": "New volume since the snapshot that triggered this notification (USD)" + }, + "spike_pct": { + "type": "number", + "description": "Volume growth as a percentage of the snapshot (e.g. 200.0 means volume tripled)" + }, + "txns": { + "type": "integer", + "format": "int64" + }, + "fees": { + "type": "number" + } + } + }, + "PositionVolumeSpikePayload": { + "type": "object", + "description": "Payload delivered when a position's volume has spiked since the last snapshot", + "required": [ + "position_id", + "condition_id", + "timeframe", + "current_volume_usd", + "snapshot_volume_usd", + "delta_volume_usd", + "spike_pct", + "txns", + "fees" + ], + "properties": { + "position_id": { + "type": "string", + "description": "ERC-1155 outcome token ID" + }, + "condition_id": { + "type": "string", + "description": "Parent market condition ID" + }, + "outcome": { + "type": [ + "string", + "null" + ], + "description": "Outcome name (e.g. \"Yes\", \"No\")" + }, + "outcome_index": { + "type": [ + "integer", + "null" + ], + "format": "int16" + }, + "timeframe": { + "type": "string", + "description": "Aggregation window (e.g. \"1h\", \"24h\")" + }, + "current_volume_usd": { + "type": "number", + "description": "Current position volume at the time of the spike (USD)" + }, + "snapshot_volume_usd": { + "type": "number", + "description": "Volume at the snapshot baseline (USD)" + }, + "delta_volume_usd": { + "type": "number", + "description": "New volume since the snapshot that triggered this notification (USD)" + }, + "spike_pct": { + "type": "number", + "description": "Volume growth as a percentage of the snapshot (e.g. 200.0 means volume tripled)" + }, + "txns": { + "type": "integer", + "format": "int64" + }, + "fees": { + "type": "number" + } + } + }, + "CloseToBondPayload": { + "type": "object", + "description": "Payload delivered when a trade occurs at a near-certain-outcome price", + "required": [ + "trader", + "taker", + "position_id", + "trade_id", + "hash", + "block", + "confirmed_at", + "amount_usd", + "shares_amount", + "fee", + "side", + "price", + "bond_side", + "threshold" + ], + "properties": { + "trader": { + "type": "string", + "description": "Limit-order maker wallet address (lowercase)" + }, + "taker": { + "type": "string", + "description": "Order filler wallet address (lowercase)" + }, + "position_id": { + "type": "string", + "description": "ERC-1155 outcome token ID" + }, + "condition_id": { + "type": [ + "string", + "null" + ], + "description": "Parent market condition ID" + }, + "outcome": { + "type": [ + "string", + "null" + ], + "description": "Outcome name (e.g. \"Yes\", \"No\")" + }, + "outcome_index": { + "type": [ + "integer", + "null" + ], + "description": "0 = Yes/Up, 1 = No" + }, + "question": { + "type": [ + "string", + "null" + ] + }, + "market_slug": { + "type": [ + "string", + "null" + ] + }, + "event_slug": { + "type": [ + "string", + "null" + ] + }, + "trade_id": { + "type": "string" + }, + "hash": { + "type": "string", + "description": "Transaction hash" + }, + "block": { + "type": "integer", + "format": "int64" + }, + "confirmed_at": { + "type": "integer", + "format": "int64", + "description": "Unix seconds" + }, + "amount_usd": { + "type": "number", + "description": "USD size of the trade" + }, + "shares_amount": { + "type": "number" + }, + "fee": { + "type": "number", + "description": "Fee paid in USD" + }, + "side": { + "type": "string", + "enum": [ + "Buy", + "Sell" + ] + }, + "price": { + "type": "number", + "minimum": 0.0, + "maximum": 1.0, + "description": "Price that triggered the notification (0.0–1.0)" + }, + "probability": { + "type": [ + "number", + "null" + ], + "minimum": 0.0, + "maximum": 1.0, + "description": "Implied probability (0.0–1.0)" + }, + "bond_side": { + "type": "string", + "enum": [ + "high", + "low" + ], + "description": "\"high\" when near YES (price ≥ threshold), \"low\" when near NO (price ≤ threshold)" + }, + "threshold": { + "type": "number", + "description": "The probability threshold from the subscription filter that was breached" + } + } + }, + "MarketCreatedOutcome": { + "type": "object", + "description": "An outcome entry within a newly created market", + "required": [ + "index", + "name", + "position_id" + ], + "properties": { + "index": { + "type": "integer", + "description": "Outcome index (0 = Yes, 1 = No)" + }, + "name": { + "type": "string", + "description": "Outcome name (e.g. \"Yes\", \"No\")" + }, + "position_id": { + "type": "string", + "description": "ERC-1155 position token ID for this outcome" + } + } + }, + "MarketCreatedPayload": { + "type": "object", + "description": "Payload delivered when a new prediction market is detected on-chain and enriched with Gamma API metadata", + "required": [ + "condition_id", + "market_slug", + "outcomes", + "question", + "description", + "tags", + "neg_risk" + ], + "properties": { + "condition_id": { + "type": "string", + "description": "Condition ID (0x-prefixed hex, lowercase)" + }, + "market_slug": { + "type": "string", + "description": "Market slug" + }, + "event_slug": { + "type": [ + "string", + "null" + ], + "description": "Parent event slug" + }, + "event_id": { + "type": [ + "string", + "null" + ], + "description": "Parent event ID" + }, + "event_title": { + "type": [ + "string", + "null" + ], + "description": "Parent event title" + }, + "series_slug": { + "type": [ + "string", + "null" + ], + "description": "Series slug (for recurring markets)" + }, + "outcomes": { + "type": "array", + "description": "List of market outcomes with their position IDs", + "items": { + "$ref": "#/components/schemas/MarketCreatedOutcome" + } + }, + "question": { + "type": "string", + "description": "Full market question text" + }, + "title": { + "type": [ + "string", + "null" + ], + "description": "Short display title" + }, + "description": { + "type": "string", + "description": "Market description" + }, + "category": { + "type": [ + "string", + "null" + ], + "description": "Market category (e.g. \"Sports\", \"Politics\")" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Market tags" + }, + "image_url": { + "type": [ + "string", + "null" + ], + "description": "Cover image URL" + }, + "neg_risk": { + "type": "boolean", + "description": "Whether this is a neg-risk market" + } + } + }, + "AssetPriceTickPayload": { + "type": "object", + "description": "Payload delivered on every raw Chainlink price tick for a tracked crypto asset", + "required": [ + "symbol", + "price", + "timestamp_ms" + ], + "properties": { + "symbol": { + "type": "string", + "enum": [ + "BTC", + "ETH", + "SOL", + "XRP", + "DOGE", + "BNB", + "HYPE" + ], + "description": "Asset symbol" + }, + "price": { + "type": "number", + "description": "Current asset price in USD from the Chainlink feed" + }, + "timestamp_ms": { + "type": "integer", + "format": "int64", + "description": "Tick timestamp (milliseconds since Unix epoch)" + } + } + }, + "AssetPriceWindowUpdatePayload": { + "type": "object", + "description": "Payload delivered twice per candle — once on open and once on close. `close_price` is 0.0 on the \"open\" update.", + "required": [ + "symbol", + "variant", + "start_time", + "end_time", + "open_price", + "close_price", + "update_type" + ], + "properties": { + "symbol": { + "type": "string", + "enum": [ + "BTC", + "ETH", + "SOL", + "XRP", + "DOGE", + "BNB", + "HYPE" + ], + "description": "Asset symbol" + }, + "variant": { + "type": "string", + "enum": [ + "5m", + "15m", + "1h", + "4h", + "1d", + "24h" + ], + "description": "Candle / window size" + }, + "start_time": { + "type": "integer", + "format": "int64", + "description": "Window start timestamp (milliseconds since Unix epoch)" + }, + "end_time": { + "type": "integer", + "format": "int64", + "description": "Window end timestamp (milliseconds since Unix epoch)" + }, + "open_price": { + "type": "number", + "description": "Opening price at start_time (USD)" + }, + "close_price": { + "type": "number", + "description": "Closing price at end_time (USD). 0.0 when update_type is \"open\" (not yet available)." + }, + "update_type": { + "type": "string", + "enum": [ + "open", + "close" + ], + "description": "\"open\" when the candle opens, \"close\" when it closes with a confirmed price" + } + } + }, + "TraderFirstTradeFilters": { + "type": "object", + "description": "Subscription filters for the `trader_first_trade` event. All fields are optional.", + "properties": { + "wallet_addresses": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Only fire for trades by these wallet addresses (lowercase). Empty = all traders." + }, + "condition_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to trades in these markets. Empty = all markets." + }, + "event_slugs": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to trades in markets belonging to these events." + }, + "min_usd_value": { + "type": "number", + "description": "Minimum trade size in USD. Omit to match all sizes." + }, + "min_probability": { + "type": "number", + "minimum": 0.0, + "maximum": 1.0, + "description": "Only fire when the outcome probability is ≥ this value." + }, + "max_probability": { + "type": "number", + "minimum": 0.0, + "maximum": 1.0, + "description": "Only fire when the outcome probability is ≤ this value." + }, + "exclude_shortterm_markets": { + "type": "boolean", + "description": "When `true`, suppress webhooks for short-term \"updown\" markets (event slugs containing `updown`). Default: `false`." + } + } + }, + "TraderNewMarketFilters": { + "type": "object", + "description": "Subscription filters for the `trader_new_market` event. All fields are optional.", + "properties": { + "wallet_addresses": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Only fire for these wallet addresses (lowercase). Empty = all traders." + }, + "condition_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to these markets." + }, + "event_slugs": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to markets belonging to these events." + }, + "exclude_shortterm_markets": { + "type": "boolean", + "description": "When `true`, suppress webhooks for short-term \"updown\" markets. Default: `false`." + } + } + }, + "TraderWhaleTradeFilters": { + "type": "object", + "description": "Subscription filters for the `trader_whale_trade` event. All fields are optional.", + "properties": { + "wallet_addresses": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Only fire for trades by these wallet addresses. Empty = all traders." + }, + "condition_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to these markets." + }, + "event_slugs": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to markets belonging to these events." + }, + "min_usd_value": { + "type": "number", + "default": 0, + "description": "Minimum trade size in USD. Defaults to 0 (matches all trades)." + }, + "min_probability": { + "type": "number", + "minimum": 0.0, + "maximum": 1.0, + "description": "Only fire when outcome probability is ≥ this value." + }, + "max_probability": { + "type": "number", + "minimum": 0.0, + "maximum": 1.0, + "description": "Only fire when outcome probability is ≤ this value." + }, + "exclude_shortterm_markets": { + "type": "boolean", + "description": "When `true`, suppress webhooks for short-term \"updown\" markets. Default: `false`." + } + } + }, + "TraderNewTradeFilters": { + "type": "object", + "description": "Subscription filters for the `trader_new_trade` event. All fields are optional.", + "properties": { + "wallet_addresses": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Only fire for trades by these wallet addresses. Empty = all traders." + }, + "condition_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to these markets." + }, + "event_slugs": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to markets belonging to these events." + }, + "min_usd_value": { + "type": "number", + "default": 0, + "description": "Minimum trade size in USD. Defaults to 0 (matches all trades)." + }, + "min_probability": { + "type": "number", + "minimum": 0.0, + "maximum": 1.0, + "description": "Only fire when outcome probability is ≥ this value." + }, + "max_probability": { + "type": "number", + "minimum": 0.0, + "maximum": 1.0, + "description": "Only fire when outcome probability is ≤ this value." + }, + "exclude_shortterm_markets": { + "type": "boolean", + "description": "When `true`, suppress webhooks for short-term \"updown\" markets. Default: `false`." + } + } + }, + "TraderGlobalPnlFilters": { + "type": "object", + "description": "Subscription filters for the `trader_global_pnl` event. All fields are optional.", + "properties": { + "traders": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Track only these trader wallet addresses. Empty = all traders." + }, + "min_realized_pnl_usd": { + "type": "number", + "description": "Only fire when realized PnL ≥ this value (USD). Use negative values for loss thresholds." + }, + "max_realized_pnl_usd": { + "type": "number", + "description": "Only fire when realized PnL ≤ this value (USD)." + }, + "min_volume_usd": { + "type": "number", + "description": "Only fire when total trading volume ≥ this value (USD)." + }, + "max_volume_usd": { + "type": "number", + "description": "Only fire when total trading volume ≤ this value (USD)." + }, + "min_buy_usd": { + "type": "number", + "description": "Only fire when buy volume ≥ this value (USD)." + }, + "min_sell_volume_usd": { + "type": "number", + "description": "Only fire when sell volume ≥ this value (USD)." + }, + "min_win_rate": { + "type": "number", + "minimum": 0.0, + "maximum": 100.0, + "description": "Only fire when market win rate ≥ this percentage (0.0–100.0)." + }, + "min_markets_traded": { + "type": "integer", + "format": "int64", + "description": "Only fire when the trader has traded in ≥ this many markets." + }, + "timeframes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "1d", + "7d", + "30d", + "lifetime" + ] + }, + "description": "Restrict to these PnL windows. Empty = all windows." + } + } + }, + "TraderMarketPnlFilters": { + "type": "object", + "description": "Subscription filters for the `trader_market_pnl` event. All fields are optional.", + "properties": { + "traders": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Track only these trader wallet addresses." + }, + "condition_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to these markets." + }, + "event_slugs": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to markets in these events." + }, + "min_realized_pnl_usd": { + "type": "number", + "description": "Only fire when per-market realized PnL ≥ this value (USD)." + }, + "max_realized_pnl_usd": { + "type": "number", + "description": "Only fire when per-market realized PnL ≤ this value (USD)." + }, + "min_volume_usd": { + "type": "number", + "description": "Only fire when total volume (buy + sell + redemption + merge) ≥ this value (USD)." + }, + "max_volume_usd": { + "type": "number", + "description": "Only fire when total volume ≤ this value (USD)." + }, + "min_buy_usd": { + "type": "number", + "description": "Only fire when buy volume in the market ≥ this value (USD)." + }, + "min_sell_volume_usd": { + "type": "number", + "description": "Only fire when sell volume in the market ≥ this value (USD)." + }, + "timeframes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "1d", + "7d", + "30d", + "lifetime" + ] + }, + "description": "Restrict to these PnL windows." + }, + "exclude_shortterm_markets": { + "type": "boolean", + "description": "When `true`, suppress webhooks for short-term \"updown\" markets. Default: `false`." + } + } + }, + "TraderEventPnlFilters": { + "type": "object", + "description": "Subscription filters for the `trader_event_pnl` event. All fields are optional.", + "properties": { + "traders": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Track only these trader wallet addresses." + }, + "event_slugs": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to these events." + }, + "min_realized_pnl_usd": { + "type": "number", + "description": "Only fire when per-event realized PnL ≥ this value (USD)." + }, + "max_realized_pnl_usd": { + "type": "number", + "description": "Only fire when per-event realized PnL ≤ this value (USD)." + }, + "min_volume_usd": { + "type": "number", + "description": "Only fire when total event volume ≥ this value (USD)." + }, + "max_volume_usd": { + "type": "number", + "description": "Only fire when total event volume ≤ this value (USD)." + }, + "min_buy_usd": { + "type": "number", + "description": "Only fire when buy volume within the event ≥ this value (USD)." + }, + "min_sell_volume_usd": { + "type": "number", + "description": "Only fire when sell volume within the event ≥ this value (USD)." + }, + "min_markets_traded": { + "type": "integer", + "format": "int64", + "description": "Only fire when the trader has traded in ≥ this many markets within the event." + }, + "timeframes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "1d", + "7d", + "30d", + "lifetime" + ] + }, + "description": "Restrict to these PnL windows." + }, + "exclude_shortterm_markets": { + "type": "boolean", + "description": "When `true`, suppress webhooks for short-term \"updown\" markets. Default: `false`." + } + } + }, + "MarketMetricsFilters": { + "type": "object", + "description": "Subscription filters for the `condition_metrics` event. All fields are optional.", + "properties": { + "condition_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to these markets. Empty = all markets." + }, + "event_slugs": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to markets in these events." + }, + "timeframes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "1m", + "5m", + "30m", + "1h", + "6h", + "24h", + "7d", + "30d" + ] + }, + "maxItems": 500, + "description": "Restrict to these aggregation windows. Empty = all windows." + }, + "min_volume_usd": { + "type": "number", + "description": "Only fire when volume ≥ this value (USD)." + }, + "max_volume_usd": { + "type": "number", + "description": "Only fire when volume ≤ this value (USD)." + }, + "min_txns": { + "type": "integer", + "format": "int64", + "description": "Only fire when transaction count ≥ this value." + }, + "min_unique_traders": { + "type": "integer", + "format": "int64", + "description": "Only fire when unique trader count ≥ this value." + }, + "min_fees": { + "type": "number", + "description": "Only fire when total fees ≥ this value (USD)." + } + } + }, + "EventMetricsFilters": { + "type": "object", + "description": "Subscription filters for the `event_metrics` event. All fields are optional.", + "properties": { + "event_slugs": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to these events. Empty = all events." + }, + "timeframes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "1m", + "5m", + "30m", + "1h", + "6h", + "24h", + "7d", + "30d" + ] + }, + "maxItems": 500, + "description": "Restrict to these aggregation windows." + }, + "min_volume_usd": { + "type": "number", + "description": "Only fire when aggregated event volume ≥ this value (USD)." + }, + "max_volume_usd": { + "type": "number" + }, + "min_txns": { + "type": "integer", + "format": "int64" + }, + "min_unique_traders": { + "type": "integer", + "format": "int64" + }, + "min_fees": { + "type": "number" + }, + "exclude_shortterm_markets": { + "type": "boolean", + "description": "When `true`, suppress webhooks for short-term \"updown\" markets. Default: `false`." + } + } + }, + "PositionMetricsFilters": { + "type": "object", + "description": "Subscription filters for the `position_metrics` event. All fields are optional.", + "properties": { + "position_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to these outcome token IDs." + }, + "condition_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to positions within these markets." + }, + "outcomes": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to positions with these outcome names (e.g. [\"Yes\", \"No\"])." + }, + "timeframes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "1m", + "5m", + "30m", + "1h", + "6h", + "24h", + "7d", + "30d" + ] + }, + "maxItems": 500, + "description": "Restrict to these aggregation windows." + }, + "min_volume_usd": { + "type": "number", + "description": "Only fire when position volume ≥ this value (USD)." + }, + "max_volume_usd": { + "type": "number" + }, + "min_buy_usd": { + "type": "number" + }, + "min_sell_volume_usd": { + "type": "number" + }, + "min_txns": { + "type": "integer", + "format": "int64" + }, + "min_unique_traders": { + "type": "integer", + "format": "int64" + }, + "min_price_change_pct": { + "type": "number", + "description": "Only fire when price change % ≥ this value." + }, + "min_probability_change_pct": { + "type": "number", + "description": "Only fire when probability change % ≥ this value." + }, + "min_fees": { + "type": "number" + } + } + }, + "MarketVolumeMilestoneFilters": { + "type": "object", + "description": "Subscription filters for the `market_volume_milestone` event.", + "required": [ + "timeframes" + ], + "properties": { + "timeframes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "1m", + "5m", + "30m", + "1h", + "6h", + "24h", + "7d", + "30d" + ] + }, + "minItems": 1, + "maxItems": 500, + "description": "**Required.** Aggregation windows to monitor (e.g. [\"1h\", \"24h\"])." + }, + "condition_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to these markets. Empty = all markets." + }, + "milestone_amounts": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + }, + "maxItems": 500, + "description": "Specific USD milestones to trigger on (e.g. [10000, 100000, 1000000]). Empty = all milestones." + } + } + }, + "EventVolumeMilestoneFilters": { + "type": "object", + "description": "Subscription filters for the `event_volume_milestone` event.", + "required": [ + "timeframes" + ], + "properties": { + "timeframes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "1m", + "5m", + "30m", + "1h", + "6h", + "24h", + "7d", + "30d" + ] + }, + "minItems": 1, + "maxItems": 500, + "description": "**Required.** Aggregation windows to monitor." + }, + "event_slugs": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to these events." + }, + "milestone_amounts": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + }, + "maxItems": 500, + "description": "Specific USD milestones to trigger on." + }, + "exclude_shortterm_markets": { + "type": "boolean", + "description": "When `true`, suppress webhooks for short-term \"updown\" markets. Default: `false`." + } + } + }, + "PositionVolumeMilestoneFilters": { + "type": "object", + "description": "Subscription filters for the `position_volume_milestone` event.", + "required": [ + "timeframes" + ], + "properties": { + "timeframes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "1m", + "5m", + "30m", + "1h", + "6h", + "24h", + "7d", + "30d" + ] + }, + "minItems": 1, + "maxItems": 500, + "description": "**Required.** Aggregation windows to monitor." + }, + "position_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to these outcome token IDs." + }, + "condition_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to positions within these markets." + }, + "milestone_amounts": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + }, + "maxItems": 500, + "description": "Specific USD milestones to trigger on." + } + } + }, + "ProbabilitySpikeFilters": { + "type": "object", + "description": "Subscription filters for the `probability_spike` event.", + "properties": { + "position_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to specific outcome token IDs. Empty = all positions." + }, + "condition_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to specific market condition IDs. Empty = all markets." + }, + "event_slugs": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to specific events. Empty = all events." + }, + "outcomes": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to these outcome names (e.g. [\"Yes\", \"No\"])." + }, + "min_probability_change_pct": { + "type": "number", + "description": "Minimum probability percentage move to trigger (e.g. `10` for a 10% move)." + }, + "spike_direction": { + "type": "string", + "enum": [ + "up", + "down", + "both" + ], + "description": "`\"up\"` = probability rising only (default when omitted), `\"down\"` = falling only, `\"both\"` = either direction." + }, + "window_secs": { + "type": "integer", + "minimum": 1, + "maximum": 600, + "description": "Observation window in seconds. The first trade in each window sets the reference price; subsequent trades are compared to it. E.g. `60` detects moves that occur within 60 seconds." + }, + "exclude_shortterm_markets": { + "type": "boolean", + "description": "When `true`, suppress webhooks for short-term \"updown\" markets. Default: `false`." + } + } + }, + "PriceSpikeFilters": { + "type": "object", + "description": "Subscription filters for the `price_spike` event.", + "properties": { + "position_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to specific outcome token IDs. Empty = all positions." + }, + "condition_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to specific market condition IDs. Empty = all markets." + }, + "event_slugs": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to specific events. Empty = all events." + }, + "outcomes": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to these outcome names (e.g. [\"Yes\", \"No\"])." + }, + "min_price_change_pct": { + "type": "number", + "description": "Minimum price percentage move to trigger (e.g. `10` for a 10% move)." + }, + "spike_direction": { + "type": "string", + "enum": [ + "up", + "down", + "both" + ], + "description": "`\"up\"` = price rising only (default when omitted), `\"down\"` = falling only, `\"both\"` = either direction." + }, + "window_secs": { + "type": "integer", + "minimum": 1, + "maximum": 600, + "description": "Observation window in seconds. The first trade in each window sets the reference price; subsequent trades are compared to it. E.g. `60` detects moves that occur within 60 seconds." + }, + "exclude_shortterm_markets": { + "type": "boolean", + "description": "When `true`, suppress webhooks for short-term \"updown\" markets. Default: `false`." + } + } + }, + "MarketVolumeSpikeFilters": { + "type": "object", + "description": "Subscription filters for the `market_volume_spike` event. `spike_ratio` is required.", + "required": [ + "spike_ratio" + ], + "properties": { + "spike_ratio": { + "type": "number", + "exclusiveMinimum": 1.0, + "description": "**Required.** Multiplier threshold (must be > 1.0). Fires when current volume >= snapshot × ratio. The snapshot is set automatically on first data and resets after each fire." + }, + "window_secs": { + "type": "integer", + "minimum": 1, + "maximum": 600, + "description": "Force snapshot reset after this many seconds (max 600 / 10 minutes)." + }, + "condition_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to these markets. Empty = all markets." + }, + "timeframes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "1m", + "5m", + "30m", + "1h", + "6h", + "1d", + "24h", + "7d", + "30d" + ] + }, + "maxItems": 500, + "description": "Restrict to these aggregation windows. Empty = all windows." + } + } + }, + "EventVolumeSpikeFilters": { + "type": "object", + "description": "Subscription filters for the `event_volume_spike` event. `spike_ratio` is required.", + "required": [ + "spike_ratio" + ], + "properties": { + "spike_ratio": { + "type": "number", + "exclusiveMinimum": 1.0, + "description": "**Required.** Multiplier threshold (must be > 1.0). Fires when current volume >= snapshot × ratio." + }, + "window_secs": { + "type": "integer", + "minimum": 1, + "maximum": 600, + "description": "Force snapshot reset after this many seconds (max 600 / 10 minutes)." + }, + "event_slugs": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to these events." + }, + "timeframes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "1m", + "5m", + "30m", + "1h", + "6h", + "1d", + "24h", + "7d", + "30d" + ] + }, + "maxItems": 500, + "description": "Restrict to these aggregation windows." + }, + "exclude_shortterm_markets": { + "type": "boolean", + "description": "When `true`, suppress webhooks for short-term \"updown\" markets. Default: `false`." + } + } + }, + "PositionVolumeSpikeFilters": { + "type": "object", + "description": "Subscription filters for the `position_volume_spike` event. `spike_ratio` is required.", + "required": [ + "spike_ratio" + ], + "properties": { + "spike_ratio": { + "type": "number", + "exclusiveMinimum": 1.0, + "description": "**Required.** Multiplier threshold (must be > 1.0). Fires when current volume >= snapshot × ratio." + }, + "window_secs": { + "type": "integer", + "minimum": 1, + "maximum": 600, + "description": "Force snapshot reset after this many seconds (max 600 / 10 minutes)." + }, + "position_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to these outcome token IDs." + }, + "condition_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to positions within these markets." + }, + "outcomes": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to these outcome names." + }, + "timeframes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "1m", + "5m", + "30m", + "1h", + "6h", + "1d", + "24h", + "7d", + "30d" + ] + }, + "maxItems": 500, + "description": "Restrict to these aggregation windows." + } + } + }, + "CloseToBondFilters": { + "type": "object", + "description": "Subscription filters for the `close_to_bond` event. At least one of `min_probability` or `max_probability` is required.", + "anyOf": [ + { + "required": [ + "min_probability" + ] + }, + { + "required": [ + "max_probability" + ] + } + ], + "properties": { + "min_probability": { + "type": "number", + "minimum": 0.0, + "maximum": 1.0, + "description": "Trigger when the YES outcome price is ≥ this value (e.g. 0.95 for 95% certainty). At least one of `min_probability` or `max_probability` must be set." + }, + "max_probability": { + "type": "number", + "minimum": 0.0, + "maximum": 1.0, + "description": "Trigger when the YES outcome price is ≤ this value (e.g. 0.05 for near-certain NO)." + }, + "condition_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to these markets." + }, + "position_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to these outcome token IDs." + }, + "event_slugs": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to markets in these events." + }, + "outcomes": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to these outcome names (e.g. [\"Yes\", \"No\"])." + }, + "position_outcome_indices": { + "type": "array", + "items": { + "type": "integer", + "minimum": 0, + "maximum": 255 + }, + "maxItems": 500, + "description": "Restrict by outcome index. 0 = Yes/Up, 1 = No. Position 0 usually represents the Up/Yes side in binary markets." + }, + "exclude_shortterm_markets": { + "type": "boolean", + "description": "When `true`, suppress webhooks for short-term \"updown\" markets. Default: `false`." + } + } + }, + "MarketCreatedFilters": { + "type": "object", + "description": "Subscription filters for the `market_created` event. All fields are optional.", + "properties": { + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to markets with these tags or category names (case-insensitive match)." + }, + "condition_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to these specific markets." + }, + "event_slugs": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "Restrict to markets belonging to these events." + }, + "exclude_shortterm_markets": { + "type": "boolean", + "description": "When `true`, suppress webhooks for short-term \"updown\" markets (event slugs containing `updown`). Default: `false`." + } + } + }, + "AssetPriceTickFilters": { + "type": "object", + "description": "Subscription filters for the `asset_price_tick` event. All fields are optional.", + "properties": { + "asset_symbols": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "BTC", + "ETH", + "SOL", + "XRP", + "DOGE", + "BNB", + "HYPE" + ] + }, + "description": "Restrict to these crypto assets. Empty = all assets." + } + } + }, + "AssetPriceWindowUpdateFilters": { + "type": "object", + "description": "Subscription filters for the `asset_price_window_update` event. All fields are optional.", + "properties": { + "asset_symbols": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "BTC", + "ETH", + "SOL", + "XRP", + "DOGE", + "BNB", + "HYPE" + ] + }, + "maxItems": 500, + "description": "Restrict to these crypto assets. Empty = all assets." + }, + "timeframes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "5m", + "15m", + "1h", + "4h", + "1d", + "24h" + ] + }, + "maxItems": 500, + "description": "Restrict to these candle sizes. Empty = all sizes." + } + } + }, + "WsAlertEventType": { + "type": "string", + "description": "All alert event types supported by both HTTP webhooks and the alerts WebSocket.", + "enum": [ + "trader_first_trade", + "trader_new_market", + "trader_whale_trade", + "trader_new_trade", + "trader_global_pnl", + "trader_market_pnl", + "trader_event_pnl", + "condition_metrics", + "event_metrics", + "position_metrics", + "market_volume_milestone", + "event_volume_milestone", + "position_volume_milestone", + "probability_spike", + "price_spike", + "market_volume_spike", + "event_volume_spike", + "position_volume_spike", + "close_to_bond", + "market_created", + "asset_price_tick", + "asset_price_window_update" + ] + }, + "WsAlertSubscribedResponse": { + "type": "object", + "required": [ + "op", + "event", + "subscription_id" + ], + "description": "Server acknowledgement for a successful alert subscription.", + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribed" + ] + }, + "event": { + "$ref": "#/components/schemas/WsAlertEventType" + }, + "subscription_id": { + "type": "string", + "format": "uuid" + } + } + }, + "WsAlertUnsubscribedResponse": { + "type": "object", + "required": [ + "op", + "event" + ], + "description": "Server acknowledgement for a successful alert unsubscription.", + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribed" + ] + }, + "event": { + "$ref": "#/components/schemas/WsAlertEventType" + } + } + }, + "WsAlertErrorResponse": { + "type": "object", + "required": [ + "error" + ], + "description": "Error returned by the alerts WebSocket when a message is invalid or a subscription request fails.", + "properties": { + "error": { + "type": "string" + } + } + }, + "WsAlertTraderFirstTradeSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `trader_first_trade` alerts. Fired when a tracked trader executes their first trade on Polymarket", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "trader_first_trade" + ] + } + }, + "example": { + "op": "subscribe", + "event": "trader_first_trade", + "wallet_addresses": [ + "0x0000000000000000000000000000000000000000" + ], + "min_usd_value": 10000.0, + "min_probability": 0.95, + "max_probability": 0.05, + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "event_slugs": [ + "example-event" + ], + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/TraderFirstTradeFilters" + } + ] + }, + "WsAlertTraderFirstTradeUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `trader_first_trade` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "trader_first_trade" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "trader_first_trade", + "wallet_addresses": [ + "0x0000000000000000000000000000000000000000" + ], + "min_usd_value": 10000.0, + "min_probability": 0.95, + "max_probability": 0.05, + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "event_slugs": [ + "example-event" + ], + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/TraderFirstTradeFilters" + } + ] + }, + "WsAlertTraderFirstTradeEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `trader_first_trade` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "trader_first_trade" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/FirstTradePayload" + } + }, + "example": { + "event": "trader_first_trade", + "timestamp": 1743500000000, + "data": { + "trader": "0x0000000000000000000000000000000000000000", + "taker": "0x0000000000000000000000000000000000000000", + "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656", + "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + "outcome": "Yes", + "outcome_index": 0, + "question": "Will this test webhook fire correctly?", + "market_slug": "test-market-0000000000", + "event_slug": "test-event-0000000000", + "trade_id": "00000000-0000-0000-0000-000000000000", + "hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "block": 0, + "confirmed_at": 1700000000, + "amount_usd": 125.0, + "shares_amount": 250.0, + "fee": 0.125, + "side": "Buy", + "price": 0.5, + "exchange": "polymarket", + "trade_type": "OrderFilled" + } + } + }, + "WsAlertTraderNewMarketSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `trader_new_market` alerts. Fired when a trader places their first trade in a specific market/condition (fires once per trader+market pair)", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "trader_new_market" + ] + } + }, + "example": { + "op": "subscribe", + "event": "trader_new_market", + "wallet_addresses": [ + "0x0000000000000000000000000000000000000000" + ], + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "event_slugs": [ + "example-event" + ], + "min_usd_value": 10000.0, + "min_probability": 0.95, + "max_probability": 0.05, + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/TraderNewMarketFilters" + } + ] + }, + "WsAlertTraderNewMarketUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `trader_new_market` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "trader_new_market" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "trader_new_market", + "wallet_addresses": [ + "0x0000000000000000000000000000000000000000" + ], + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "event_slugs": [ + "example-event" + ], + "min_usd_value": 10000.0, + "min_probability": 0.95, + "max_probability": 0.05, + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/TraderNewMarketFilters" + } + ] + }, + "WsAlertTraderNewMarketEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `trader_new_market` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "trader_new_market" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/NewMarketPayload" + } + }, + "example": { + "event": "trader_new_market", + "timestamp": 1743500000000, + "data": { + "trader": "0x0000000000000000000000000000000000000000", + "taker": "0x0000000000000000000000000000000000000000", + "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656", + "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + "outcome": "Yes", + "outcome_index": 0, + "question": "Will this test webhook fire correctly?", + "market_slug": "test-market-0000000000", + "event_slug": "test-event-0000000000", + "trade_id": "00000000-0000-0000-0000-000000000000", + "hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "block": 0, + "confirmed_at": 1700000000, + "amount_usd": 125.0, + "shares_amount": 250.0, + "fee": 0.125, + "side": "Buy", + "price": 0.5, + "probability": 0.5, + "exchange": "polymarket", + "trade_type": "OrderFilled" + } + } + }, + "WsAlertTraderWhaleTradeSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `trader_whale_trade` alerts. Fired when a trade meets the configured criteria. Use `min_usd_value` to filter by minimum trade size (optional, defaults to 0 — matches all trades), and `min_probability`/`max_probability` to restrict to a probability range.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "trader_whale_trade" + ] + } + }, + "example": { + "op": "subscribe", + "event": "trader_whale_trade", + "wallet_addresses": [ + "0x0000000000000000000000000000000000000000" + ], + "min_usd_value": 10000.0, + "min_probability": 0.95, + "max_probability": 0.05, + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "event_slugs": [ + "example-event" + ], + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/TraderWhaleTradeFilters" + } + ] + }, + "WsAlertTraderWhaleTradeUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `trader_whale_trade` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "trader_whale_trade" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "trader_whale_trade", + "wallet_addresses": [ + "0x0000000000000000000000000000000000000000" + ], + "min_usd_value": 10000.0, + "min_probability": 0.95, + "max_probability": 0.05, + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "event_slugs": [ + "example-event" + ], + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/TraderWhaleTradeFilters" + } + ] + }, + "WsAlertTraderWhaleTradeEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `trader_whale_trade` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "trader_whale_trade" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/WhaleTradePayload" + } + }, + "example": { + "event": "trader_whale_trade", + "timestamp": 1743500000000, + "data": { + "trader": "0x0000000000000000000000000000000000000000", + "taker": "0x0000000000000000000000000000000000000000", + "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656", + "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + "outcome": "Yes", + "outcome_index": 0, + "question": "Will this test webhook fire correctly?", + "market_slug": "test-market-0000000000", + "event_slug": "test-event-0000000000", + "trade_id": "00000000-0000-0000-0000-000000000000", + "hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "block": 0, + "confirmed_at": 1700000000, + "amount_usd": 125.0, + "shares_amount": 250.0, + "fee": 0.125, + "side": "Buy", + "price": 0.5, + "probability": 0.5, + "exchange": "polymarket", + "trade_type": "OrderFilled" + } + } + }, + "WsAlertTraderNewTradeSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `trader_new_trade` alerts. Fired on every order-filled trade. Use `wallet_addresses` to watch specific traders, `min_usd_value` to filter by size, and `min_probability`/`max_probability` to restrict to a probability range.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "trader_new_trade" + ] + } + }, + "example": { + "op": "subscribe", + "event": "trader_new_trade", + "wallet_addresses": [ + "0x0000000000000000000000000000000000000000" + ], + "min_usd_value": 10000.0, + "min_probability": 0.95, + "max_probability": 0.05, + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "event_slugs": [ + "example-event" + ], + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/TraderNewTradeFilters" + } + ] + }, + "WsAlertTraderNewTradeUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `trader_new_trade` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "trader_new_trade" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "trader_new_trade", + "wallet_addresses": [ + "0x0000000000000000000000000000000000000000" + ], + "min_usd_value": 10000.0, + "min_probability": 0.95, + "max_probability": 0.05, + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "event_slugs": [ + "example-event" + ], + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/TraderNewTradeFilters" + } + ] + }, + "WsAlertTraderNewTradeEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `trader_new_trade` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "trader_new_trade" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/NewTradePayload" + } + }, + "example": { + "event": "trader_new_trade", + "timestamp": 1743500000000, + "data": { + "trader": "0x0000000000000000000000000000000000000000", + "taker": "0x0000000000000000000000000000000000000000", + "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656", + "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + "outcome": "Yes", + "outcome_index": 0, + "question": "Will this test webhook fire correctly?", + "market_slug": "test-market-0000000000", + "event_slug": "test-event-0000000000", + "trade_id": "00000000-0000-0000-0000-000000000000", + "hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "block": 0, + "confirmed_at": 1700000000, + "amount_usd": 25.0, + "shares_amount": 50.0, + "fee": 0.025, + "side": "Buy", + "price": 0.5, + "probability": 0.5, + "exchange": "polymarket", + "trade_type": "OrderFilled" + } + } + }, + "WsAlertTraderGlobalPnlSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `trader_global_pnl` alerts. Fired when a trader's global PnL crosses a configured threshold", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "trader_global_pnl" + ] + } + }, + "example": { + "op": "subscribe", + "event": "trader_global_pnl", + "traders": [ + "0x0000000000000000000000000000000000000000" + ], + "min_realized_pnl_usd": 1000.0, + "max_realized_pnl_usd": 5000.0, + "min_volume_usd": 10000.0, + "max_volume_usd": 100000.0, + "min_buy_usd": 5000.0, + "min_sell_volume_usd": 2500.0, + "min_win_rate": 60.0, + "min_markets_traded": 5, + "timeframes": [ + "1h" + ] + } + }, + { + "$ref": "#/components/schemas/TraderGlobalPnlFilters" + } + ] + }, + "WsAlertTraderGlobalPnlUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `trader_global_pnl` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "trader_global_pnl" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "trader_global_pnl", + "traders": [ + "0x0000000000000000000000000000000000000000" + ], + "min_realized_pnl_usd": 1000.0, + "max_realized_pnl_usd": 5000.0, + "min_volume_usd": 10000.0, + "max_volume_usd": 100000.0, + "min_buy_usd": 5000.0, + "min_sell_volume_usd": 2500.0, + "min_win_rate": 60.0, + "min_markets_traded": 5, + "timeframes": [ + "1h" + ] + } + }, + { + "$ref": "#/components/schemas/TraderGlobalPnlFilters" + } + ] + }, + "WsAlertTraderGlobalPnlEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `trader_global_pnl` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "trader_global_pnl" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/GlobalPnlPayload" + } + }, + "example": { + "event": "trader_global_pnl", + "timestamp": 1743500000000, + "data": { + "trader": "0x0000000000000000000000000000000000000000", + "timeframe": "7d", + "realized_pnl_usd": 250.0, + "events_traded": 3, + "markets_traded": 5, + "total_buys": 12, + "total_sells": 8, + "total_redemptions": 1, + "total_merges": 0, + "total_volume_usd": 1500.0, + "buy_volume_usd": 900.0, + "sell_volume_usd": 600.0, + "redemption_volume_usd": 50.0, + "merge_volume_usd": 0.0, + "markets_won": 3, + "markets_lost": 2, + "market_win_rate_pct": 60.0, + "avg_pnl_per_market": 50.0, + "avg_pnl_per_trade": 12.5, + "avg_hold_time_seconds": 86400.0, + "total_fees": 7.5, + "best_trade_pnl_usd": 180.0, + "best_trade_condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + "first_trade_at": 1700000000, + "last_trade_at": 1700000000 + } + } + }, + "WsAlertTraderMarketPnlSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `trader_market_pnl` alerts. Fired when a trader's market-level PnL crosses a configured threshold", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "trader_market_pnl" + ] + } + }, + "example": { + "op": "subscribe", + "event": "trader_market_pnl", + "traders": [ + "0x0000000000000000000000000000000000000000" + ], + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "event_slugs": [ + "example-event" + ], + "min_realized_pnl_usd": 1000.0, + "max_realized_pnl_usd": 5000.0, + "min_volume_usd": 10000.0, + "max_volume_usd": 100000.0, + "min_buy_usd": 5000.0, + "min_sell_volume_usd": 2500.0, + "timeframes": [ + "1h" + ], + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/TraderMarketPnlFilters" + } + ] + }, + "WsAlertTraderMarketPnlUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `trader_market_pnl` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "trader_market_pnl" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "trader_market_pnl", + "traders": [ + "0x0000000000000000000000000000000000000000" + ], + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "event_slugs": [ + "example-event" + ], + "min_realized_pnl_usd": 1000.0, + "max_realized_pnl_usd": 5000.0, + "min_volume_usd": 10000.0, + "max_volume_usd": 100000.0, + "min_buy_usd": 5000.0, + "min_sell_volume_usd": 2500.0, + "timeframes": [ + "1h" + ], + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/TraderMarketPnlFilters" + } + ] + }, + "WsAlertTraderMarketPnlEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `trader_market_pnl` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "trader_market_pnl" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/MarketPnlPayload" + } + }, + "example": { + "event": "trader_market_pnl", + "timestamp": 1743500000000, + "data": { + "trader": "0x0000000000000000000000000000000000000000", + "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + "event_slug": "test-event-0000000000", + "timeframe": "7d", + "outcomes_traded": 2, + "total_buys": 4, + "total_sells": 3, + "total_redemptions": 1, + "total_merges": 0, + "buy_usd": 300.0, + "sell_usd": 200.0, + "redemption_usd": 50.0, + "merge_usd": 0.0, + "realized_pnl_usd": 100.0, + "winning_outcomes": 1, + "total_fees": 2.5, + "first_trade_at": 1700000000, + "last_trade_at": 1700000000 + } + } + }, + "WsAlertTraderEventPnlSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `trader_event_pnl` alerts. Fired when a trader's event-level PnL crosses a configured threshold", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "trader_event_pnl" + ] + } + }, + "example": { + "op": "subscribe", + "event": "trader_event_pnl", + "traders": [ + "0x0000000000000000000000000000000000000000" + ], + "event_slugs": [ + "example-event" + ], + "min_realized_pnl_usd": 1000.0, + "max_realized_pnl_usd": 5000.0, + "min_volume_usd": 10000.0, + "max_volume_usd": 100000.0, + "min_buy_usd": 5000.0, + "min_sell_volume_usd": 2500.0, + "min_markets_traded": 5, + "timeframes": [ + "1h" + ], + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/TraderEventPnlFilters" + } + ] + }, + "WsAlertTraderEventPnlUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `trader_event_pnl` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "trader_event_pnl" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "trader_event_pnl", + "traders": [ + "0x0000000000000000000000000000000000000000" + ], + "event_slugs": [ + "example-event" + ], + "min_realized_pnl_usd": 1000.0, + "max_realized_pnl_usd": 5000.0, + "min_volume_usd": 10000.0, + "max_volume_usd": 100000.0, + "min_buy_usd": 5000.0, + "min_sell_volume_usd": 2500.0, + "min_markets_traded": 5, + "timeframes": [ + "1h" + ], + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/TraderEventPnlFilters" + } + ] + }, + "WsAlertTraderEventPnlEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `trader_event_pnl` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "trader_event_pnl" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/EventPnlPayload" + } + }, + "example": { + "event": "trader_event_pnl", + "timestamp": 1743500000000, + "data": { + "trader": "0x0000000000000000000000000000000000000000", + "event_slug": "test-event-0000000000", + "timeframe": "7d", + "markets_traded": 2, + "outcomes_traded": 3, + "total_buys": 6, + "total_sells": 4, + "total_redemptions": 1, + "total_merges": 0, + "total_volume_usd": 800.0, + "buy_usd": 480.0, + "sell_usd": 320.0, + "redemption_usd": 50.0, + "merge_usd": 0.0, + "realized_pnl_usd": 150.0, + "winning_markets": 1, + "losing_markets": 1, + "total_fees": 4.0, + "first_trade_at": 1700000000, + "last_trade_at": 1700000000 + } + } + }, + "WsAlertConditionMetricsSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `condition_metrics` alerts. Fired when a market's volume or transaction metrics cross a configured threshold. Use `timeframes` to restrict to specific windows (valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`).", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "condition_metrics" + ] + } + }, + "example": { + "op": "subscribe", + "event": "condition_metrics", + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "min_volume_usd": 10000.0, + "max_volume_usd": 100000.0, + "min_fees": 100.0, + "min_txns": 10, + "min_unique_traders": 10, + "timeframes": [ + "1h" + ] + } + }, + { + "$ref": "#/components/schemas/MarketMetricsFilters" + } + ] + }, + "WsAlertConditionMetricsUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `condition_metrics` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "condition_metrics" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "condition_metrics", + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "min_volume_usd": 10000.0, + "max_volume_usd": 100000.0, + "min_fees": 100.0, + "min_txns": 10, + "min_unique_traders": 10, + "timeframes": [ + "1h" + ] + } + }, + { + "$ref": "#/components/schemas/MarketMetricsFilters" + } + ] + }, + "WsAlertConditionMetricsEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `condition_metrics` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "condition_metrics" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/ConditionMetricsPayload" + } + }, + "example": { + "event": "condition_metrics", + "timestamp": 1743500000000, + "data": { + "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + "timeframe": "1h", + "volume_usd": 50000.0, + "fees": 250.0, + "txns": 320, + "unique_traders": 85 + } + } + }, + "WsAlertEventMetricsSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `event_metrics` alerts. Fired when an event's volume or transaction metrics cross a configured threshold. Use `timeframes` to restrict to specific windows (valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`).", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "event_metrics" + ] + } + }, + "example": { + "op": "subscribe", + "event": "event_metrics", + "event_slugs": [ + "example-event" + ], + "min_volume_usd": 10000.0, + "max_volume_usd": 100000.0, + "min_fees": 100.0, + "min_txns": 10, + "min_unique_traders": 10, + "timeframes": [ + "1h" + ], + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/EventMetricsFilters" + } + ] + }, + "WsAlertEventMetricsUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `event_metrics` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "event_metrics" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "event_metrics", + "event_slugs": [ + "example-event" + ], + "min_volume_usd": 10000.0, + "max_volume_usd": 100000.0, + "min_fees": 100.0, + "min_txns": 10, + "min_unique_traders": 10, + "timeframes": [ + "1h" + ], + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/EventMetricsFilters" + } + ] + }, + "WsAlertEventMetricsEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `event_metrics` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "event_metrics" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/EventMetricsPayload" + } + }, + "example": { + "event": "event_metrics", + "timestamp": 1743500000000, + "data": { + "event_slug": "test-event-0000000000", + "timeframe": "1h", + "volume_usd": 120000.0, + "fees": 600.0, + "txns": 740, + "unique_traders": 210 + } + } + }, + "WsAlertPositionMetricsSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `position_metrics` alerts. Fired when a position's volume or transaction metrics cross a configured threshold. Use `timeframes` to restrict to specific windows (valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`).", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "position_metrics" + ] + } + }, + "example": { + "op": "subscribe", + "event": "position_metrics", + "position_ids": [ + "452312848583266388373324160190187140051835877600158453279131187530910662656" + ], + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "outcomes": [ + "Yes" + ], + "min_volume_usd": 10000.0, + "max_volume_usd": 100000.0, + "min_buy_usd": 5000.0, + "min_sell_volume_usd": 2500.0, + "min_fees": 100.0, + "min_txns": 10, + "min_price_change_pct": 10.0, + "min_probability_change_pct": 10.0, + "timeframes": [ + "1h" + ] + } + }, + { + "$ref": "#/components/schemas/PositionMetricsFilters" + } + ] + }, + "WsAlertPositionMetricsUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `position_metrics` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "position_metrics" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "position_metrics", + "position_ids": [ + "452312848583266388373324160190187140051835877600158453279131187530910662656" + ], + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "outcomes": [ + "Yes" + ], + "min_volume_usd": 10000.0, + "max_volume_usd": 100000.0, + "min_buy_usd": 5000.0, + "min_sell_volume_usd": 2500.0, + "min_fees": 100.0, + "min_txns": 10, + "min_price_change_pct": 10.0, + "min_probability_change_pct": 10.0, + "timeframes": [ + "1h" + ] + } + }, + { + "$ref": "#/components/schemas/PositionMetricsFilters" + } + ] + }, + "WsAlertPositionMetricsEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `position_metrics` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "position_metrics" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/PositionMetricsPayload" + } + }, + "example": { + "event": "position_metrics", + "timestamp": 1743500000000, + "data": { + "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656", + "outcome": "Yes", + "outcome_index": 0, + "timeframe": "1h", + "volume_usd": 25000.0, + "buy_volume_usd": 15000.0, + "sell_volume_usd": 10000.0, + "fees": 125.0, + "txns": 160, + "buys": 95, + "sells": 65, + "unique_traders": 48, + "price_open": 0.48, + "price_close": 0.52, + "price_high": 0.55, + "price_low": 0.46, + "probability_open": 0.48, + "probability_close": 0.52, + "probability_high": 0.55, + "probability_low": 0.46 + } + } + }, + "WsAlertMarketVolumeMilestoneSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `market_volume_milestone` alerts. Fired when a market's trading volume crosses a milestone threshold in the specified timeframe. **`timeframes` is required** (e.g. `[\"1h\", \"24h\"]`) — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`. Optional `milestone_amounts` restricts to specific USD thresholds (e.g. `[10000, 100000]`). Optional `condition_ids` restricts to specific markets.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "market_volume_milestone" + ] + } + }, + "example": { + "op": "subscribe", + "event": "market_volume_milestone", + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "timeframes": [ + "1h" + ], + "milestone_amounts": [ + 10000 + ] + } + }, + { + "$ref": "#/components/schemas/MarketVolumeMilestoneFilters" + } + ] + }, + "WsAlertMarketVolumeMilestoneUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `market_volume_milestone` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "market_volume_milestone" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "market_volume_milestone", + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "timeframes": [ + "1h" + ], + "milestone_amounts": [ + 10000 + ] + } + }, + { + "$ref": "#/components/schemas/MarketVolumeMilestoneFilters" + } + ] + }, + "WsAlertMarketVolumeMilestoneEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `market_volume_milestone` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "market_volume_milestone" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/VolumeMilestonePayload" + } + }, + "example": { + "event": "market_volume_milestone", + "timestamp": 1743500000000, + "data": { + "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + "timeframe": "24h", + "milestone_usd": 100000.0, + "current_volume_usd": 100125.0, + "fees": 500.0, + "txns": 650 + } + } + }, + "WsAlertEventVolumeMilestoneSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `event_volume_milestone` alerts. Fired when an event's trading volume crosses a milestone threshold in the specified timeframe. **`timeframes` is required** (e.g. `[\"1h\", \"24h\"]`) — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`. Optional `milestone_amounts` restricts to specific USD thresholds. Optional `event_slugs` restricts to specific events.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "event_volume_milestone" + ] + } + }, + "example": { + "op": "subscribe", + "event": "event_volume_milestone", + "event_slugs": [ + "example-event" + ], + "timeframes": [ + "1h" + ], + "milestone_amounts": [ + 10000 + ], + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/EventVolumeMilestoneFilters" + } + ] + }, + "WsAlertEventVolumeMilestoneUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `event_volume_milestone` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "event_volume_milestone" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "event_volume_milestone", + "event_slugs": [ + "example-event" + ], + "timeframes": [ + "1h" + ], + "milestone_amounts": [ + 10000 + ], + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/EventVolumeMilestoneFilters" + } + ] + }, + "WsAlertEventVolumeMilestoneEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `event_volume_milestone` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "event_volume_milestone" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/EventVolumeMilestonePayload" + } + }, + "example": { + "event": "event_volume_milestone", + "timestamp": 1743500000000, + "data": { + "event_slug": "test-event-0000000000", + "timeframe": "24h", + "milestone_usd": 500000.0, + "current_volume_usd": 500250.0, + "fees": 2500.0, + "txns": 3200 + } + } + }, + "WsAlertPositionVolumeMilestoneSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `position_volume_milestone` alerts. Fired when a position's trading volume crosses a milestone threshold in the specified timeframe. **`timeframes` is required** (e.g. `[\"1h\", \"24h\"]`) — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`. Optional `milestone_amounts` restricts to specific USD thresholds. Optional `position_ids` or `condition_ids` restrict to specific positions/markets.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "position_volume_milestone" + ] + } + }, + "example": { + "op": "subscribe", + "event": "position_volume_milestone", + "position_ids": [ + "452312848583266388373324160190187140051835877600158453279131187530910662656" + ], + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "outcomes": [ + "Yes" + ], + "timeframes": [ + "1h" + ], + "milestone_amounts": [ + 10000 + ] + } + }, + { + "$ref": "#/components/schemas/PositionVolumeMilestoneFilters" + } + ] + }, + "WsAlertPositionVolumeMilestoneUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `position_volume_milestone` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "position_volume_milestone" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "position_volume_milestone", + "position_ids": [ + "452312848583266388373324160190187140051835877600158453279131187530910662656" + ], + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "outcomes": [ + "Yes" + ], + "timeframes": [ + "1h" + ], + "milestone_amounts": [ + 10000 + ] + } + }, + { + "$ref": "#/components/schemas/PositionVolumeMilestoneFilters" + } + ] + }, + "WsAlertPositionVolumeMilestoneEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `position_volume_milestone` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "position_volume_milestone" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/PositionVolumeMilestonePayload" + } + }, + "example": { + "event": "position_volume_milestone", + "timestamp": 1743500000000, + "data": { + "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656", + "outcome": "Yes", + "outcome_index": 0, + "timeframe": "24h", + "milestone_usd": 50000.0, + "current_volume_usd": 50125.0, + "buy_volume_usd": 30000.0, + "sell_volume_usd": 20000.0, + "fees": 250.0, + "txns": 320, + "buys": 190, + "sells": 130 + } + } + }, + "WsAlertProbabilitySpikeSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `probability_spike` alerts. Fired when a position's probability moves significantly. Use `min_probability_change_pct` to set the minimum move (e.g. `10` for 10%). Use `window_secs` to observe moves within a specific time window (e.g. `60` for 60 seconds). Use `spike_direction` (`\"up\"` | `\"down\"` | `\"both\"`) — defaults to `\"up\"` when not provided. Filter by `position_ids` or `outcomes` to narrow scope.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "probability_spike" + ] + } + }, + "example": { + "op": "subscribe", + "event": "probability_spike", + "position_ids": [ + "452312848583266388373324160190187140051835877600158453279131187530910662656" + ], + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "event_slugs": [ + "example-event" + ], + "outcomes": [ + "Yes" + ], + "min_probability_change_pct": 10.0, + "spike_direction": "up", + "window_secs": 60, + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/ProbabilitySpikeFilters" + } + ] + }, + "WsAlertProbabilitySpikeUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `probability_spike` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "probability_spike" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "probability_spike", + "position_ids": [ + "452312848583266388373324160190187140051835877600158453279131187530910662656" + ], + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "event_slugs": [ + "example-event" + ], + "outcomes": [ + "Yes" + ], + "min_probability_change_pct": 10.0, + "spike_direction": "up", + "window_secs": 60, + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/ProbabilitySpikeFilters" + } + ] + }, + "WsAlertProbabilitySpikeEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `probability_spike` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "probability_spike" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/ProbabilitySpikePayload" + } + }, + "example": { + "event": "probability_spike", + "timestamp": 1743500000000, + "data": { + "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656", + "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + "event_slug": "test-event-0000000000", + "outcome": "Yes", + "outcome_index": 0, + "previous_probability": 0.4, + "current_probability": 0.5, + "spike_direction": "up", + "spike_pct": 25.0 + } + } + }, + "WsAlertPriceSpikeSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `price_spike` alerts. Fired when a position's price moves significantly. Use `min_price_change_pct` to set the minimum move (e.g. `10` for 10%). Use `window_secs` to observe moves within a specific time window. Use `spike_direction` (`\"up\"` | `\"down\"` | `\"both\"`) — defaults to `\"up\"` when not provided. Filter by `position_ids` or `outcomes` to narrow scope.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "price_spike" + ] + } + }, + "example": { + "op": "subscribe", + "event": "price_spike", + "position_ids": [ + "452312848583266388373324160190187140051835877600158453279131187530910662656" + ], + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "event_slugs": [ + "example-event" + ], + "outcomes": [ + "Yes" + ], + "min_price_change_pct": 10.0, + "spike_direction": "up", + "window_secs": 60, + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/PriceSpikeFilters" + } + ] + }, + "WsAlertPriceSpikeUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `price_spike` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "price_spike" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "price_spike", + "position_ids": [ + "452312848583266388373324160190187140051835877600158453279131187530910662656" + ], + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "event_slugs": [ + "example-event" + ], + "outcomes": [ + "Yes" + ], + "min_price_change_pct": 10.0, + "spike_direction": "up", + "window_secs": 60, + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/PriceSpikeFilters" + } + ] + }, + "WsAlertPriceSpikeEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `price_spike` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "price_spike" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/PriceSpikePayload" + } + }, + "example": { + "event": "price_spike", + "timestamp": 1743500000000, + "data": { + "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656", + "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + "event_slug": "test-event-0000000000", + "outcome": "Yes", + "outcome_index": 0, + "previous_price": 0.4, + "current_price": 0.5, + "spike_direction": "up", + "spike_pct": 25.0 + } + } + }, + "WsAlertMarketVolumeSpikeSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `market_volume_spike` alerts. Fired when a market's trading volume grows by a multiple of `spike_ratio` within a timeframe. Requires `spike_ratio` (> 1.0, e.g. `2.0` fires when volume doubles). Optional `window_secs` sets the observation window in seconds (max 600). Optional `timeframes` — valid values: `1m`, `5m`, `30m`, `1h`, `6h`, `1d`, `24h`, `7d`, `30d`. Optional `condition_ids` restricts to specific markets.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "market_volume_spike" + ] + } + }, + "example": { + "op": "subscribe", + "event": "market_volume_spike", + "spike_ratio": 2.0, + "window_secs": 60, + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "timeframes": [ + "1h" + ] + } + }, + { + "$ref": "#/components/schemas/MarketVolumeSpikeFilters" + } + ] + }, + "WsAlertMarketVolumeSpikeUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `market_volume_spike` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "market_volume_spike" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "market_volume_spike", + "spike_ratio": 2.0, + "window_secs": 60, + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "timeframes": [ + "1h" + ] + } + }, + { + "$ref": "#/components/schemas/MarketVolumeSpikeFilters" + } + ] + }, + "WsAlertMarketVolumeSpikeEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `market_volume_spike` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "market_volume_spike" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/MarketVolumeSpikePayload" + } + }, + "example": { + "event": "market_volume_spike", + "timestamp": 1743500000000, + "data": { + "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + "timeframe": "1h", + "current_volume_usd": 32000.0, + "snapshot_volume_usd": 10000.0, + "delta_volume_usd": 22000.0, + "spike_pct": 220.0, + "txns": 480, + "fees": 160.0 + } + } + }, + "WsAlertEventVolumeSpikeSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `event_volume_spike` alerts. Fired when an event's aggregated trading volume grows by a multiple of `spike_ratio` within a timeframe. Requires `spike_ratio` (> 1.0, e.g. `2.0` fires when volume doubles). Optional `window_secs` sets the observation window in seconds (max 600). Optional `timeframes` and `event_slugs` to narrow scope.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "event_volume_spike" + ] + } + }, + "example": { + "op": "subscribe", + "event": "event_volume_spike", + "spike_ratio": 2.0, + "window_secs": 60, + "event_slugs": [ + "example-event" + ], + "timeframes": [ + "1h" + ], + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/EventVolumeSpikeFilters" + } + ] + }, + "WsAlertEventVolumeSpikeUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `event_volume_spike` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "event_volume_spike" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "event_volume_spike", + "spike_ratio": 2.0, + "window_secs": 60, + "event_slugs": [ + "example-event" + ], + "timeframes": [ + "1h" + ], + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/EventVolumeSpikeFilters" + } + ] + }, + "WsAlertEventVolumeSpikeEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `event_volume_spike` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "event_volume_spike" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/EventVolumeSpikePayload" + } + }, + "example": { + "event": "event_volume_spike", + "timestamp": 1743500000000, + "data": { + "event_slug": "test-event-0000000000", + "timeframe": "1h", + "current_volume_usd": 140000.0, + "snapshot_volume_usd": 50000.0, + "delta_volume_usd": 90000.0, + "spike_pct": 180.0, + "txns": 1100, + "fees": 700.0 + } + } + }, + "WsAlertPositionVolumeSpikeSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `position_volume_spike` alerts. Fired when a position's trading volume grows by a multiple of `spike_ratio` within a timeframe. Requires `spike_ratio` (> 1.0, e.g. `2.0` fires when volume doubles). Optional `window_secs` sets the observation window in seconds (max 600). Optional `position_ids`, `condition_ids`, `outcomes`, and `timeframes` to narrow scope.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "position_volume_spike" + ] + } + }, + "example": { + "op": "subscribe", + "event": "position_volume_spike", + "spike_ratio": 2.0, + "window_secs": 60, + "position_ids": [ + "452312848583266388373324160190187140051835877600158453279131187530910662656" + ], + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "outcomes": [ + "Yes" + ], + "timeframes": [ + "1h" + ] + } + }, + { + "$ref": "#/components/schemas/PositionVolumeSpikeFilters" + } + ] + }, + "WsAlertPositionVolumeSpikeUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `position_volume_spike` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "position_volume_spike" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "position_volume_spike", + "spike_ratio": 2.0, + "window_secs": 60, + "position_ids": [ + "452312848583266388373324160190187140051835877600158453279131187530910662656" + ], + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "outcomes": [ + "Yes" + ], + "timeframes": [ + "1h" + ] + } + }, + { + "$ref": "#/components/schemas/PositionVolumeSpikeFilters" + } + ] + }, + "WsAlertPositionVolumeSpikeEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `position_volume_spike` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "position_volume_spike" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/PositionVolumeSpikePayload" + } + }, + "example": { + "event": "position_volume_spike", + "timestamp": 1743500000000, + "data": { + "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656", + "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + "outcome": "Yes", + "outcome_index": 0, + "timeframe": "1h", + "current_volume_usd": 20500.0, + "snapshot_volume_usd": 5000.0, + "delta_volume_usd": 15500.0, + "spike_pct": 310.0, + "txns": 240, + "fees": 102.5 + } + } + }, + "WsAlertCloseToBondSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `close_to_bond` alerts. Fired when a trade occurs at a near-certain-outcome price. **At least one of `min_probability` or `max_probability` is required.** Use `min_probability` (e.g. `0.95`) to trigger when YES is near-certain; use `max_probability` (e.g. `0.05`) for NO near-certain. Optional filters: `position_outcome_indices` — restrict by outcome index (`0` = Yes/Up, `1` = No); `condition_ids` — restrict to specific markets; `position_ids` — restrict to specific outcome tokens; `outcomes` — restrict by outcome name (e.g. `\"Yes\"`, `\"No\"`); `event_slugs` — restrict to specific events.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "close_to_bond" + ] + } + }, + "example": { + "op": "subscribe", + "event": "close_to_bond", + "min_probability": 0.95, + "max_probability": 0.05, + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "position_ids": [ + "452312848583266388373324160190187140051835877600158453279131187530910662656" + ], + "outcomes": [ + "Yes" + ], + "position_outcome_indices": [ + 0 + ], + "event_slugs": [ + "example-event" + ], + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/CloseToBondFilters" + } + ], + "anyOf": [ + { + "required": [ + "min_probability" + ] + }, + { + "required": [ + "max_probability" + ] + } + ] + }, + "WsAlertCloseToBondUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `close_to_bond` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "close_to_bond" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "close_to_bond", + "min_probability": 0.95, + "max_probability": 0.05, + "condition_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "position_ids": [ + "452312848583266388373324160190187140051835877600158453279131187530910662656" + ], + "outcomes": [ + "Yes" + ], + "position_outcome_indices": [ + 0 + ], + "event_slugs": [ + "example-event" + ], + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/CloseToBondFilters" + } + ], + "anyOf": [ + { + "required": [ + "min_probability" + ] + }, + { + "required": [ + "max_probability" + ] + } + ] + }, + "WsAlertCloseToBondEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `close_to_bond` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "close_to_bond" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/CloseToBondPayload" + } + }, + "example": { + "event": "close_to_bond", + "timestamp": 1743500000000, + "data": { + "trader": "0x0000000000000000000000000000000000000000", + "taker": "0x0000000000000000000000000000000000000000", + "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656", + "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + "outcome": "Yes", + "outcome_index": 0, + "question": "Will this test webhook fire correctly?", + "market_slug": "test-market-0000000000", + "event_slug": "test-event-0000000000", + "trade_id": "00000000-0000-0000-0000-000000000000", + "hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "block": 0, + "confirmed_at": 1700000000, + "amount_usd": 500.0, + "shares_amount": 515.46, + "fee": 2.5, + "side": "Buy", + "price": 0.97, + "probability": 0.97, + "bond_side": "high", + "threshold": 0.95 + } + } + }, + "WsAlertMarketCreatedSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `market_created` alerts. Fired when a new prediction market is created on Polymarket. Filterable by `tags` and `event_slugs`.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "market_created" + ] + } + }, + "example": { + "op": "subscribe", + "event": "market_created", + "event_slugs": [ + "example-event" + ], + "tags": [ + "Politics" + ], + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/MarketCreatedFilters" + } + ] + }, + "WsAlertMarketCreatedUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `market_created` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "market_created" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "market_created", + "event_slugs": [ + "example-event" + ], + "tags": [ + "Politics" + ], + "exclude_shortterm_markets": true + } + }, + { + "$ref": "#/components/schemas/MarketCreatedFilters" + } + ] + }, + "WsAlertMarketCreatedEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `market_created` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "market_created" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/MarketCreatedPayload" + } + }, + "example": { + "event": "market_created", + "timestamp": 1743500000000, + "data": { + "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + "market_slug": "test-market-0000000000", + "event_slug": "test-event-0000000000", + "event_id": null, + "event_title": "Test Event 0000", + "series_slug": null, + "outcomes": [ + { + "index": 0, + "name": "Yes", + "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656" + }, + { + "index": 1, + "name": "No", + "position_id": "0" + } + ], + "question": "Will this test webhook fire correctly?", + "title": "Test Market 0000", + "description": "A test market for webhook payload verification.", + "category": "Crypto", + "tags": [ + "test", + "crypto" + ], + "image_url": null, + "neg_risk": false + } + } + }, + "WsAlertAssetPriceTickSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `asset_price_tick` alerts. Fired when the price of a tracked crypto asset updates (BTC, ETH, SOL, XRP, DOGE, BNB, HYPE). Use `asset_symbols` to restrict to specific assets (empty = all).", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "asset_price_tick" + ] + } + }, + "example": { + "op": "subscribe", + "event": "asset_price_tick", + "asset_symbols": [ + "BTC" + ] + } + }, + { + "$ref": "#/components/schemas/AssetPriceTickFilters" + } + ] + }, + "WsAlertAssetPriceTickUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `asset_price_tick` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "asset_price_tick" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "asset_price_tick", + "asset_symbols": [ + "BTC" + ] + } + }, + { + "$ref": "#/components/schemas/AssetPriceTickFilters" + } + ] + }, + "WsAlertAssetPriceTickEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `asset_price_tick` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "asset_price_tick" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/AssetPriceTickPayload" + } + }, + "example": { + "event": "asset_price_tick", + "timestamp": 1743500000000, + "data": { + "symbol": "BTC", + "price": 65000.0, + "timestamp_ms": 1700000000000 + } + } + }, + "WsAlertAssetPriceWindowUpdateSubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Subscribe to `asset_price_window_update` alerts. Fired at the start and end of each price candle for tracked crypto assets (BTC, ETH, SOL, XRP, DOGE, BNB, HYPE). Payload includes `update_type` (`\"open\"` or `\"close\"`) indicating whether the candle is opening or closing. Use `asset_symbols` to restrict to specific assets. Use `timeframes` to restrict to specific candle sizes — valid values: `\"5m\"`, `\"15m\"`, `\"1h\"`, `\"4h\"`, `\"1d\"`, `\"24h\"`.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "subscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "asset_price_window_update" + ] + } + }, + "example": { + "op": "subscribe", + "event": "asset_price_window_update", + "asset_symbols": [ + "BTC" + ], + "timeframes": [ + "1h" + ] + } + }, + { + "$ref": "#/components/schemas/AssetPriceWindowUpdateFilters" + } + ] + }, + "WsAlertAssetPriceWindowUpdateUnsubscribeMessage": { + "allOf": [ + { + "type": "object", + "description": "Unsubscribe from `asset_price_window_update` alerts. The filter fields must match the original subscription exactly.", + "required": [ + "op", + "event" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "unsubscribe" + ] + }, + "event": { + "type": "string", + "enum": [ + "asset_price_window_update" + ] + } + }, + "example": { + "op": "unsubscribe", + "event": "asset_price_window_update", + "asset_symbols": [ + "BTC" + ], + "timeframes": [ + "1h" + ] + } + }, + { + "$ref": "#/components/schemas/AssetPriceWindowUpdateFilters" + } + ] + }, + "WsAlertAssetPriceWindowUpdateEvent": { + "type": "object", + "required": [ + "event", + "timestamp", + "data" + ], + "description": "Pushed `asset_price_window_update` alert. The `data` payload matches the corresponding HTTP webhook payload schema.", + "properties": { + "event": { + "type": "string", + "enum": [ + "asset_price_window_update" + ] + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds" + }, + "data": { + "$ref": "#/components/schemas/AssetPriceWindowUpdatePayload" + } + }, + "example": { + "event": "asset_price_window_update", + "timestamp": 1743500000000, + "data": { + "symbol": "BTC", + "variant": "1h", + "start_time": 1700000000000, + "end_time": 1700003600000, + "open_price": 64800.0, + "close_price": 65200.0, + "update_type": "close" + } + } + }, + "WsAlertSubscribeMessage": { + "description": "Typed subscribe request for the alerts WebSocket. The request shape depends on `event` and reuses the matching webhook filter schema.", + "oneOf": [ + { + "$ref": "#/components/schemas/WsAlertTraderFirstTradeSubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertTraderNewMarketSubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertTraderWhaleTradeSubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertTraderNewTradeSubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertTraderGlobalPnlSubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertTraderMarketPnlSubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertTraderEventPnlSubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertConditionMetricsSubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertEventMetricsSubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertPositionMetricsSubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertMarketVolumeMilestoneSubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertEventVolumeMilestoneSubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertPositionVolumeMilestoneSubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertProbabilitySpikeSubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertPriceSpikeSubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertMarketVolumeSpikeSubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertEventVolumeSpikeSubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertPositionVolumeSpikeSubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertCloseToBondSubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertMarketCreatedSubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertAssetPriceTickSubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertAssetPriceWindowUpdateSubscribeMessage" + } + ], + "discriminator": "event" + }, + "WsAlertUnsubscribeMessage": { + "description": "Typed unsubscribe request for the alerts WebSocket. The request shape depends on `event` and must match the original subscription filters.", + "oneOf": [ + { + "$ref": "#/components/schemas/WsAlertTraderFirstTradeUnsubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertTraderNewMarketUnsubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertTraderWhaleTradeUnsubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertTraderNewTradeUnsubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertTraderGlobalPnlUnsubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertTraderMarketPnlUnsubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertTraderEventPnlUnsubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertConditionMetricsUnsubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertEventMetricsUnsubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertPositionMetricsUnsubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertMarketVolumeMilestoneUnsubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertEventVolumeMilestoneUnsubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertPositionVolumeMilestoneUnsubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertProbabilitySpikeUnsubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertPriceSpikeUnsubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertMarketVolumeSpikeUnsubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertEventVolumeSpikeUnsubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertPositionVolumeSpikeUnsubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertCloseToBondUnsubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertMarketCreatedUnsubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertAssetPriceTickUnsubscribeMessage" + }, + { + "$ref": "#/components/schemas/WsAlertAssetPriceWindowUpdateUnsubscribeMessage" + } + ], + "discriminator": "event" + }, + "WsAlertEventPayload": { + "description": "Typed pushed-event envelope for the alerts WebSocket. The `data` payload depends on `event` and matches the corresponding HTTP webhook payload schema.", + "oneOf": [ + { + "$ref": "#/components/schemas/WsAlertTraderFirstTradeEvent" + }, + { + "$ref": "#/components/schemas/WsAlertTraderNewMarketEvent" + }, + { + "$ref": "#/components/schemas/WsAlertTraderWhaleTradeEvent" + }, + { + "$ref": "#/components/schemas/WsAlertTraderNewTradeEvent" + }, + { + "$ref": "#/components/schemas/WsAlertTraderGlobalPnlEvent" + }, + { + "$ref": "#/components/schemas/WsAlertTraderMarketPnlEvent" + }, + { + "$ref": "#/components/schemas/WsAlertTraderEventPnlEvent" + }, + { + "$ref": "#/components/schemas/WsAlertConditionMetricsEvent" + }, + { + "$ref": "#/components/schemas/WsAlertEventMetricsEvent" + }, + { + "$ref": "#/components/schemas/WsAlertPositionMetricsEvent" + }, + { + "$ref": "#/components/schemas/WsAlertMarketVolumeMilestoneEvent" + }, + { + "$ref": "#/components/schemas/WsAlertEventVolumeMilestoneEvent" + }, + { + "$ref": "#/components/schemas/WsAlertPositionVolumeMilestoneEvent" + }, + { + "$ref": "#/components/schemas/WsAlertProbabilitySpikeEvent" + }, + { + "$ref": "#/components/schemas/WsAlertPriceSpikeEvent" + }, + { + "$ref": "#/components/schemas/WsAlertMarketVolumeSpikeEvent" + }, + { + "$ref": "#/components/schemas/WsAlertEventVolumeSpikeEvent" + }, + { + "$ref": "#/components/schemas/WsAlertPositionVolumeSpikeEvent" + }, + { + "$ref": "#/components/schemas/WsAlertCloseToBondEvent" + }, + { + "$ref": "#/components/schemas/WsAlertMarketCreatedEvent" + }, + { + "$ref": "#/components/schemas/WsAlertAssetPriceTickEvent" + }, + { + "$ref": "#/components/schemas/WsAlertAssetPriceWindowUpdateEvent" + } + ], + "discriminator": "event" + } + }, + "securitySchemes": { + "apiKeyHeader": { + "type": "httpApiKey", + "in": "header", + "name": "X-Api-Key", + "description": "Pass the API key via the `X-Api-Key` header on the WebSocket upgrade request." + }, + "apiKeyQuery": { + "type": "httpApiKey", + "in": "query", + "name": "api-key", + "description": "Pass the API key via `?api-key=` on the WebSocket upgrade URL (browser/frontend friendly)." + }, + "publicKeyJwt": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT", + "description": "Public-key JWT combo: the builder embeds a `pk_jwt_*` key in their frontend, and end users supply a JWT signed by the builder. Send via `Authorization: Bearer ` header or `?token=` query parameter. Credits bill to the builder's org; rate limits are applied per session." + } + } + } +} \ No newline at end of file diff --git a/openapi/ws.json b/openapi/ws.json new file mode 100644 index 0000000..e79dce6 --- /dev/null +++ b/openapi/ws.json @@ -0,0 +1,3826 @@ +{ + "asyncapi": "3.0.0", + "info": { + "title": "Polymarket WebSocket Streams API", + "version": "1.0.0", + "description": "Real-time WebSocket streaming API for Polymarket prediction-market data.\n\n## Connection\nConnect to `wss://api.struct.to/ws` using a standard WebSocket client.\n\n## Authentication\n\nTwo authentication methods are supported:\n\n**1. API key** (`apiKeyHeader` or `apiKeyQuery`):\n```\nX-Api-Key: \n```\nor\n```\nwss://api.struct.to/ws?api_key=\n```\n\n**2. Public-key JWT combo** (`publicKeyJwt`): for builders who embed a `pk_jwt_*` key in their frontend and issue JWTs to their end users. Credits bill to the builder's org; rate limits are per session.\n```\nAuthorization: Bearer \n```\nor\n```\nwss://api.struct.to/ws?token=\n```\n\n## Joining a room\nAfter connecting, send:\n```json\n{\"type\":\"join_room\",\"payload\":{\"room_id\":\"polymarket_trades\"}}\n```\n\n## Subscribing\nAfter joining, send a `room_message` with room-specific subscribe payload:\n```json\n{\n\"type\": \"room_message\",\n\"payload\": {\n\"room_id\": \"polymarket_trades\",\n\"message\": {\"action\":\"subscribe\",\"condition_ids\":[\"0xabc…\"]}\n}\n}\n```\n\n## Keepalive\nSend `{\"type\":\"ping\"}` periodically to keep your connection alive. The server responds with `{\"type\":\"pong\"}`. Connections that have not sent a `ping` within **60 seconds** are closed. Recommended: send a ping every 30 seconds.\n\n## Pricing\nWebSocket billing is **per message**, with costs varying by room type. The credit system is integer-based, so fractional costs accumulate per connection and are only deducted when a whole credit is reached. Any fractional remainder is **rounded up to the next credit** when the connection closes.\n\n**Connection hold:** Every new authenticated connection is charged a **1-credit hold** immediately on connect, even if the client disconnects before receiving any messages.\n\n**Per-message cost by room:**\n\n| Room | Credits per message |\n|---|---|\n| `polymarket_trades` (Trades) | 0.025 |\n| `polymarket_order_book` (Order Book) | 0.01 |\n| `polymarket_asset_prices` (Asset Prices) | 0.05 |\n| `polymarket_asset_window_updates` (Asset Window Updates) | 0.25 |\n| `polymarket_market_metrics` (Market Metrics) | 0.25 |\n| `polymarket_event_metrics` (Event Metrics) | 0.25 |\n| `polymarket_position_metrics` (Position Metrics) | 0.25 |\n| `polymarket_trader_pnl` (Trader PnL) | 1 |\n| `polymarket_trader_positions` (Trader Positions) | 0.05 |\n| `polymarket_accounts` (Accounts) | 0.05 |\n| `polymarket_clob_rewards` (CLOB Rewards) | 0.1 |\n\nSystem messages (`pong`, `room_joined`, `room_left`, errors) are free." + }, + "servers": { + "production": { + "host": "api.struct.to", + "pathname": "/ws", + "protocol": "wss", + "description": "Production WebSocket endpoint (room-based subscriptions)" + } + }, + "channels": { + "polymarket_trades": { + "address": "polymarket_trades", + "title": "Trades", + "summary": "Trades stream", + "description": "Real-time stream of Polymarket prediction-market events (trades + oracle lifecycle). Supports both confirmed (on-chain) and pending (mempool) trades.\n\n**Filters (optional):**\n- `condition_ids` — 64-char hex condition IDs (with or without `0x` prefix)\n- `market_slugs` — market slugs\n- `event_slugs` — event slugs (delivers all markets under each event)\n- `position_ids` — ERC-1155 token IDs (decimal or hex)\n- `traders` — trader wallet addresses\n- `trade_types` — optional allowlist (e.g. `[\"OrderFilled\", \"Resolution\"]`). Omit or pass `[]` for all types.\n- `status` — `\"confirmed\"` (default), `\"pending\"`, or `\"all\"`\n\nIf no filters are provided, subscribes to ALL trades (open-ended).\n\n**Pending trades:** Fields unavailable from mempool (`block`, `confirmed_at`, `log_index`, `block_index`, `order_hash`, `taker`, `fee`, `fee_shares`, `fee_pct`) are omitted. `received_at` (ms) is included instead.\n\n**Unsubscribe:** Send `action: \"unsubscribe_all\"` to clear all subscriptions.", + "messages": { + "subscribe": { + "$ref": "#/components/messages/TradesStreamSubscribe" + }, + "subscribeResponse": { + "$ref": "#/components/messages/TradesStreamSubscribeResponse" + }, + "tradeStreamEvent": { + "$ref": "#/components/messages/TradeStreamEvent" + } + } + }, + "polymarket_asset_prices": { + "address": "polymarket_asset_prices", + "title": "Asset Prices", + "summary": "Asset prices stream", + "description": "Real-time crypto-asset price feed (BTC, ETH, SOL, XRP, …). Delivers both raw `asset_price_tick` events on every price update and `asset_price_window_update` events at candle open/close boundaries.\n\n**Filter (optional):** `asset_symbols` — uppercase symbols to filter by. Empty array subscribes to all symbols.\n\n**Unsubscribe:** Send `action: \"unsubscribe_all\"`.", + "messages": { + "subscribe": { + "$ref": "#/components/messages/AssetPricesSubscribe" + }, + "subscribeResponse": { + "$ref": "#/components/messages/AssetPricesSubscribeResponse" + }, + "assetPriceTickEvent": { + "$ref": "#/components/messages/AssetPriceTickEvent" + }, + "assetPriceWindowUpdateEvent": { + "$ref": "#/components/messages/AssetPriceWindowUpdateEvent" + } + } + }, + "polymarket_asset_window_updates": { + "address": "polymarket_asset_window_updates", + "title": "Asset Window Updates", + "summary": "Asset window updates stream", + "description": "Filtered stream of asset-price candle open/close events. Unlike `polymarket_asset_prices`, this room **requires** at least one filter so clients receive only the symbols and/or timeframes they care about.\n\n**Filters (at least one required):**\n- `asset_symbols` — uppercase symbols (e.g. `\"BTC\"`, `\"ETH\"`)\n- `timeframes` — candle sizes: `\"5m\"`, `\"15m\"`, `\"1h\"`, `\"4h\"`, `\"1d\"` / `\"24h\"`\n\n**Unsubscribe:** Send `action: \"unsubscribe_all\"`.", + "messages": { + "subscribe": { + "$ref": "#/components/messages/AssetWindowUpdatesSubscribe" + }, + "subscribeResponse": { + "$ref": "#/components/messages/AssetWindowUpdatesSubscribeResponse" + }, + "assetWindowUpdateEvent": { + "$ref": "#/components/messages/AssetWindowUpdateEvent" + } + } + }, + "polymarket_market_metrics": { + "address": "polymarket_market_metrics", + "title": "Market Metrics", + "summary": "Market (condition) metrics stream", + "description": "Real-time volume and transaction-count metrics per market condition, aggregated across multiple timeframes (`1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`). One event is pushed per timeframe window on each update.\n\n**Filter (required):** `condition_ids` — 64-char hex condition IDs. Invalid or malformed IDs are returned in `rejected`.\n\n**Unsubscribe:** Send `action: \"unsubscribe_all\"`.", + "messages": { + "subscribe": { + "$ref": "#/components/messages/MarketMetricsSubscribe" + }, + "subscribeResponse": { + "$ref": "#/components/messages/MarketMetricsSubscribeResponse" + }, + "marketMetricsEvent": { + "$ref": "#/components/messages/MarketMetricsEvent" + } + } + }, + "polymarket_event_metrics": { + "address": "polymarket_event_metrics", + "title": "Event Metrics", + "summary": "Event metrics stream", + "description": "Real-time volume and transaction-count metrics aggregated at the event (parent market group) level, across multiple timeframes. One event is pushed per timeframe window on each update.\n\n**Filter (required):** `event_slugs` — event slugs (lowercase).\n\n**Unsubscribe:** Send `action: \"unsubscribe_all\"`.", + "messages": { + "subscribe": { + "$ref": "#/components/messages/EventMetricsSubscribe" + }, + "subscribeResponse": { + "$ref": "#/components/messages/EventMetricsSubscribeResponse" + }, + "eventMetricsEvent": { + "$ref": "#/components/messages/EventMetricsEvent" + } + } + }, + "polymarket_position_metrics": { + "address": "polymarket_position_metrics", + "title": "Position Metrics", + "summary": "Position (outcome token) metrics stream", + "description": "Real-time volume, trade-count, and OHLC price/probability metrics per outcome-token position, across multiple timeframes. One event is pushed per timeframe window on each update.\n\n**Filter (required):** `position_ids` — ERC-1155 token IDs (decimal or hex).\n\n**Unsubscribe:** Send `action: \"unsubscribe_all\"`.", + "messages": { + "subscribe": { + "$ref": "#/components/messages/PositionMetricsSubscribe" + }, + "subscribeResponse": { + "$ref": "#/components/messages/PositionMetricsSubscribeResponse" + }, + "positionMetricsEvent": { + "$ref": "#/components/messages/PositionMetricsEvent" + } + } + }, + "polymarket_trader_pnl": { + "address": "polymarket_trader_pnl", + "title": "Trader PnL", + "summary": "Trader PnL stream", + "description": "Real-time profit-and-loss updates for tracked traders, delivered at global, market (condition), and event granularity.\n\n**Filter (required):** `traders` — EVM wallet addresses. Invalid addresses are returned in `rejected`.\n\n**Events pushed:** `trader_global_pnl_update`, `trader_market_pnl_update`, `trader_event_pnl_update`.\n\n**Unsubscribe:** Send `action: \"unsubscribe_all\"`.", + "messages": { + "subscribe": { + "$ref": "#/components/messages/TraderPnlSubscribe" + }, + "subscribeResponse": { + "$ref": "#/components/messages/TraderPnlSubscribeResponse" + }, + "traderGlobalPnlEvent": { + "$ref": "#/components/messages/TraderGlobalPnlEvent" + }, + "traderMarketPnlEvent": { + "$ref": "#/components/messages/TraderMarketPnlEvent" + }, + "traderEventPnlEvent": { + "$ref": "#/components/messages/TraderEventPnlEvent" + } + } + }, + "polymarket_trader_positions": { + "address": "polymarket_trader_positions", + "title": "Trader Positions", + "summary": "Trader position updates stream", + "description": "Real-time position updates for tracked traders. Streams full position snapshots whenever a position PnL changes.\n\n**Filter (required):** `traders` — EVM wallet addresses. Invalid addresses are returned in `rejected`.\n\n**Events pushed:** `trader_position_update` — includes PnL fields, shares balance, market slug, outcome, and all enriched data.\n\n**Unsubscribe:** Send `action: \"unsubscribe_all\"`.", + "messages": { + "subscribe": { + "$ref": "#/components/messages/TraderPositionsSubscribe" + }, + "subscribeResponse": { + "$ref": "#/components/messages/TraderPositionsSubscribeResponse" + }, + "traderPositionUpdateEvent": { + "$ref": "#/components/messages/TraderPositionUpdateEvent" + } + } + }, + "polymarket_accounts": { + "address": "polymarket_accounts", + "title": "Accounts", + "summary": "Account balances stream", + "description": "Real-time balance updates for Polymarket wallet accounts.\n\nAlways streams ERC-1155 outcome token (shares) balance changes for subscribed wallets as `accounts_update` events. Opt in to additional token streams via boolean flags:\n- `include_usdce: true` — also receive `usdce_update` events (USDCe collateral balance)\n- `include_matic: true` — also receive `matic_update` events (MATIC gas balance)\n\n**Filter (required):** `wallets` — EVM wallet addresses. Invalid addresses are returned in `rejected`.\n\n**Unsubscribe:** Send `action: \"unsubscribe_all\"`.", + "messages": { + "subscribe": { + "$ref": "#/components/messages/AccountsSubscribe" + }, + "subscribeResponse": { + "$ref": "#/components/messages/AccountsSubscribeResponse" + }, + "accountsUpdateEvent": { + "$ref": "#/components/messages/AccountsUpdateEvent" + }, + "usdceUpdateEvent": { + "$ref": "#/components/messages/UsdceUpdateEvent" + }, + "maticUpdateEvent": { + "$ref": "#/components/messages/MaticUpdateEvent" + } + } + }, + "polymarket_order_book": { + "address": "polymarket_order_book", + "title": "Order Book", + "summary": "Order book snapshots stream", + "description": "Real-time CLOB orderbook snapshots for Polymarket outcome tokens. Each event contains the full aggregated bid/ask book plus pre-computed derived metrics (best bid/ask, mid price, spread, USD liquidity depth, level counts).\n\n**Filters (at least one required, max 500 combined):**\n- `condition_ids` — 64-char hex condition IDs (market); delivers snapshots for all positions in that market\n- `position_ids` — hex token/asset IDs (individual outcome tokens)\n\n**Unsubscribe:** Send `action: \"unsubscribe_all\"`.", + "messages": { + "subscribe": { + "$ref": "#/components/messages/OrderBookSubscribe" + }, + "subscribeResponse": { + "$ref": "#/components/messages/OrderBookSubscribeResponse" + }, + "orderBookUpdateEvent": { + "$ref": "#/components/messages/OrderBookUpdateEvent" + } + } + }, + "polymarket_clob_rewards": { + "address": "polymarket_clob_rewards", + "title": "CLOB Rewards", + "summary": "CLOB reward configuration changes stream", + "description": "Real-time stream of Polymarket CLOB reward configuration changes. Emits events when rewards are added to a market, removed, or updated (e.g. rate changes, sponsor count changes, spread/size threshold changes).\n\n**Filters (optional):**\n- `condition_ids` — 64-char hex condition IDs to watch for reward changes\n- `subscribe_all` — set to `true` to receive ALL reward changes across all markets\n\nIf neither is provided, no events are delivered.\n\n**Event types:**\n- `added` — new reward configuration appeared on a market\n- `removed` — reward configuration removed from a market\n- `updated` — reward parameters changed (rate, spread, size, sponsors, etc.)\n\n**Unsubscribe:** Send `action: \"unsubscribe_all\"`.", + "messages": { + "subscribe": { + "$ref": "#/components/messages/ClobRewardsSubscribe" + }, + "subscribeResponse": { + "$ref": "#/components/messages/ClobRewardsSubscribeResponse" + }, + "clobRewardsUpdateEvent": { + "$ref": "#/components/messages/ClobRewardsUpdateEvent" + } + } + } + }, + "operations": { + "subscribePolymarketTrades": { + "action": "send", + "channel": { + "$ref": "#/channels/polymarket_trades" + }, + "summary": "Subscribe to trades stream", + "messages": [ + { + "$ref": "#/channels/polymarket_trades/messages/subscribe" + } + ] + }, + "receivePolymarketTrades": { + "action": "receive", + "channel": { + "$ref": "#/channels/polymarket_trades" + }, + "summary": "Receive trades stream events", + "messages": [ + { + "$ref": "#/channels/polymarket_trades/messages/subscribeResponse" + }, + { + "$ref": "#/channels/polymarket_trades/messages/tradeStreamEvent" + } + ] + }, + "subscribePolymarketAssetPrices": { + "action": "send", + "channel": { + "$ref": "#/channels/polymarket_asset_prices" + }, + "summary": "Subscribe to asset prices stream", + "messages": [ + { + "$ref": "#/channels/polymarket_asset_prices/messages/subscribe" + } + ] + }, + "receivePolymarketAssetPrices": { + "action": "receive", + "channel": { + "$ref": "#/channels/polymarket_asset_prices" + }, + "summary": "Receive asset prices stream events", + "messages": [ + { + "$ref": "#/channels/polymarket_asset_prices/messages/subscribeResponse" + }, + { + "$ref": "#/channels/polymarket_asset_prices/messages/assetPriceTickEvent" + }, + { + "$ref": "#/channels/polymarket_asset_prices/messages/assetPriceWindowUpdateEvent" + } + ] + }, + "subscribePolymarketAssetWindowUpdates": { + "action": "send", + "channel": { + "$ref": "#/channels/polymarket_asset_window_updates" + }, + "summary": "Subscribe to asset window updates stream", + "messages": [ + { + "$ref": "#/channels/polymarket_asset_window_updates/messages/subscribe" + } + ] + }, + "receivePolymarketAssetWindowUpdates": { + "action": "receive", + "channel": { + "$ref": "#/channels/polymarket_asset_window_updates" + }, + "summary": "Receive asset window updates stream events", + "messages": [ + { + "$ref": "#/channels/polymarket_asset_window_updates/messages/subscribeResponse" + }, + { + "$ref": "#/channels/polymarket_asset_window_updates/messages/assetWindowUpdateEvent" + } + ] + }, + "subscribePolymarketMarketMetrics": { + "action": "send", + "channel": { + "$ref": "#/channels/polymarket_market_metrics" + }, + "summary": "Subscribe to market (condition) metrics stream", + "messages": [ + { + "$ref": "#/channels/polymarket_market_metrics/messages/subscribe" + } + ] + }, + "receivePolymarketMarketMetrics": { + "action": "receive", + "channel": { + "$ref": "#/channels/polymarket_market_metrics" + }, + "summary": "Receive market (condition) metrics stream events", + "messages": [ + { + "$ref": "#/channels/polymarket_market_metrics/messages/subscribeResponse" + }, + { + "$ref": "#/channels/polymarket_market_metrics/messages/marketMetricsEvent" + } + ] + }, + "subscribePolymarketEventMetrics": { + "action": "send", + "channel": { + "$ref": "#/channels/polymarket_event_metrics" + }, + "summary": "Subscribe to event metrics stream", + "messages": [ + { + "$ref": "#/channels/polymarket_event_metrics/messages/subscribe" + } + ] + }, + "receivePolymarketEventMetrics": { + "action": "receive", + "channel": { + "$ref": "#/channels/polymarket_event_metrics" + }, + "summary": "Receive event metrics stream events", + "messages": [ + { + "$ref": "#/channels/polymarket_event_metrics/messages/subscribeResponse" + }, + { + "$ref": "#/channels/polymarket_event_metrics/messages/eventMetricsEvent" + } + ] + }, + "subscribePolymarketPositionMetrics": { + "action": "send", + "channel": { + "$ref": "#/channels/polymarket_position_metrics" + }, + "summary": "Subscribe to position (outcome token) metrics stream", + "messages": [ + { + "$ref": "#/channels/polymarket_position_metrics/messages/subscribe" + } + ] + }, + "receivePolymarketPositionMetrics": { + "action": "receive", + "channel": { + "$ref": "#/channels/polymarket_position_metrics" + }, + "summary": "Receive position (outcome token) metrics stream events", + "messages": [ + { + "$ref": "#/channels/polymarket_position_metrics/messages/subscribeResponse" + }, + { + "$ref": "#/channels/polymarket_position_metrics/messages/positionMetricsEvent" + } + ] + }, + "subscribePolymarketTraderPnl": { + "action": "send", + "channel": { + "$ref": "#/channels/polymarket_trader_pnl" + }, + "summary": "Subscribe to trader pnl stream", + "messages": [ + { + "$ref": "#/channels/polymarket_trader_pnl/messages/subscribe" + } + ] + }, + "receivePolymarketTraderPnl": { + "action": "receive", + "channel": { + "$ref": "#/channels/polymarket_trader_pnl" + }, + "summary": "Receive trader pnl stream events", + "messages": [ + { + "$ref": "#/channels/polymarket_trader_pnl/messages/subscribeResponse" + }, + { + "$ref": "#/channels/polymarket_trader_pnl/messages/traderGlobalPnlEvent" + }, + { + "$ref": "#/channels/polymarket_trader_pnl/messages/traderMarketPnlEvent" + }, + { + "$ref": "#/channels/polymarket_trader_pnl/messages/traderEventPnlEvent" + } + ] + }, + "subscribePolymarketTraderPositions": { + "action": "send", + "channel": { + "$ref": "#/channels/polymarket_trader_positions" + }, + "summary": "Subscribe to trader position updates stream", + "messages": [ + { + "$ref": "#/channels/polymarket_trader_positions/messages/subscribe" + } + ] + }, + "receivePolymarketTraderPositions": { + "action": "receive", + "channel": { + "$ref": "#/channels/polymarket_trader_positions" + }, + "summary": "Receive trader position updates stream events", + "messages": [ + { + "$ref": "#/channels/polymarket_trader_positions/messages/subscribeResponse" + }, + { + "$ref": "#/channels/polymarket_trader_positions/messages/traderPositionUpdateEvent" + } + ] + }, + "subscribePolymarketAccounts": { + "action": "send", + "channel": { + "$ref": "#/channels/polymarket_accounts" + }, + "summary": "Subscribe to account balances stream", + "messages": [ + { + "$ref": "#/channels/polymarket_accounts/messages/subscribe" + } + ] + }, + "receivePolymarketAccounts": { + "action": "receive", + "channel": { + "$ref": "#/channels/polymarket_accounts" + }, + "summary": "Receive account balances stream events", + "messages": [ + { + "$ref": "#/channels/polymarket_accounts/messages/subscribeResponse" + }, + { + "$ref": "#/channels/polymarket_accounts/messages/accountsUpdateEvent" + }, + { + "$ref": "#/channels/polymarket_accounts/messages/usdceUpdateEvent" + }, + { + "$ref": "#/channels/polymarket_accounts/messages/maticUpdateEvent" + } + ] + }, + "subscribePolymarketOrderBook": { + "action": "send", + "channel": { + "$ref": "#/channels/polymarket_order_book" + }, + "summary": "Subscribe to order book snapshots stream", + "messages": [ + { + "$ref": "#/channels/polymarket_order_book/messages/subscribe" + } + ] + }, + "receivePolymarketOrderBook": { + "action": "receive", + "channel": { + "$ref": "#/channels/polymarket_order_book" + }, + "summary": "Receive order book snapshots stream events", + "messages": [ + { + "$ref": "#/channels/polymarket_order_book/messages/subscribeResponse" + }, + { + "$ref": "#/channels/polymarket_order_book/messages/orderBookUpdateEvent" + } + ] + }, + "subscribePolymarketClobRewards": { + "action": "send", + "channel": { + "$ref": "#/channels/polymarket_clob_rewards" + }, + "summary": "Subscribe to clob reward configuration changes stream", + "messages": [ + { + "$ref": "#/channels/polymarket_clob_rewards/messages/subscribe" + } + ] + }, + "receivePolymarketClobRewards": { + "action": "receive", + "channel": { + "$ref": "#/channels/polymarket_clob_rewards" + }, + "summary": "Receive clob reward configuration changes stream events", + "messages": [ + { + "$ref": "#/channels/polymarket_clob_rewards/messages/subscribeResponse" + }, + { + "$ref": "#/channels/polymarket_clob_rewards/messages/clobRewardsUpdateEvent" + } + ] + } + }, + "components": { + "messages": { + "TradesStreamSubscribe": { + "summary": "Subscribe to trades stream", + "payload": { + "$ref": "#/components/schemas/TradesStreamSubscribeMessage" + } + }, + "TradeStreamEvent": { + "summary": "Server-pushed event. Discriminated by `trade_type` — each variant only includes relevant fields.\n\nEnvelope: `{\"type\": \"trade_stream_update\", \"room_id\": \"polymarket_trades\", \"status\": \"confirmed\"|\"pending\", \"data\": {...}}`\n\n**Pending trades:** `block`, `confirmed_at`, `log_index`, `block_index` are absent. `received_at` (milliseconds) is included instead. For OrderFilled/OrdersMatched, `order_hash`, `taker`, `fee`, `fee_shares`, `fee_pct` are also absent", + "payload": { + "$ref": "#/components/schemas/TradeStreamEvent" + } + }, + "TradesStreamSubscribeResponse": { + "summary": "Subscription acknowledgement for trades stream", + "payload": { + "$ref": "#/components/schemas/TradesStreamSubscribeResponse" + } + }, + "AssetPricesSubscribe": { + "summary": "Subscribe to asset prices stream", + "payload": { + "$ref": "#/components/schemas/AssetPricesSubscribeMessage" + } + }, + "AssetPriceTickEvent": { + "summary": "a crypto-asset price tick", + "payload": { + "$ref": "#/components/schemas/AssetPriceTickEvent" + } + }, + "AssetPriceWindowUpdateEvent": { + "summary": "candle open or close for a crypto asset", + "payload": { + "$ref": "#/components/schemas/AssetPriceWindowUpdateEvent" + } + }, + "AssetPricesSubscribeResponse": { + "summary": "Subscription acknowledgement for asset prices stream", + "payload": { + "$ref": "#/components/schemas/AssetPricesSubscribeResponse" + } + }, + "AssetWindowUpdatesSubscribe": { + "summary": "Subscribe to asset window updates stream", + "payload": { + "$ref": "#/components/schemas/AssetWindowUpdatesSubscribeMessage" + } + }, + "AssetWindowUpdateEvent": { + "summary": "Server-pushed event from the polymarket_asset_window_updates room. Same payload as AssetPriceWindowUpdateEvent", + "payload": { + "$ref": "#/components/schemas/AssetWindowUpdateEvent" + } + }, + "AssetWindowUpdatesSubscribeResponse": { + "summary": "Subscription acknowledgement for asset window updates stream", + "payload": { + "$ref": "#/components/schemas/AssetWindowUpdatesSubscribeResponse" + } + }, + "MarketMetricsSubscribe": { + "summary": "Subscribe to market (condition) metrics stream", + "payload": { + "$ref": "#/components/schemas/MarketMetricsSubscribeMessage" + } + }, + "MarketMetricsEvent": { + "summary": "metrics update for one timeframe of a condition", + "payload": { + "$ref": "#/components/schemas/MarketMetricsEvent" + } + }, + "MarketMetricsSubscribeResponse": { + "summary": "Subscription acknowledgement for market (condition) metrics stream", + "payload": { + "$ref": "#/components/schemas/MarketMetricsSubscribeResponse" + } + }, + "EventMetricsSubscribe": { + "summary": "Subscribe to event metrics stream", + "payload": { + "$ref": "#/components/schemas/EventMetricsSubscribeMessage" + } + }, + "EventMetricsEvent": { + "summary": "aggregated metrics update for one timeframe of an event", + "payload": { + "$ref": "#/components/schemas/EventMetricsEvent" + } + }, + "EventMetricsSubscribeResponse": { + "summary": "Subscription acknowledgement for event metrics stream", + "payload": { + "$ref": "#/components/schemas/EventMetricsSubscribeResponse" + } + }, + "PositionMetricsSubscribe": { + "summary": "Subscribe to position (outcome token) metrics stream", + "payload": { + "$ref": "#/components/schemas/PositionMetricsSubscribeMessage" + } + }, + "PositionMetricsEvent": { + "summary": "metrics update for one timeframe of an outcome token", + "payload": { + "$ref": "#/components/schemas/PositionMetricsEvent" + } + }, + "PositionMetricsSubscribeResponse": { + "summary": "Subscription acknowledgement for position (outcome token) metrics stream", + "payload": { + "$ref": "#/components/schemas/PositionMetricsSubscribeResponse" + } + }, + "TraderPnlSubscribe": { + "summary": "Subscribe to trader pnl stream", + "payload": { + "$ref": "#/components/schemas/TraderPnlSubscribeMessage" + } + }, + "TraderGlobalPnlEvent": { + "summary": "global (portfolio-level) PnL update for a trader", + "payload": { + "$ref": "#/components/schemas/TraderGlobalPnlEvent" + } + }, + "TraderMarketPnlEvent": { + "summary": "per-market PnL update for a trader", + "payload": { + "$ref": "#/components/schemas/TraderMarketPnlEvent" + } + }, + "TraderEventPnlEvent": { + "summary": "per-event PnL update for a trader", + "payload": { + "$ref": "#/components/schemas/TraderEventPnlEvent" + } + }, + "TraderPnlSubscribeResponse": { + "summary": "Subscription acknowledgement for trader pnl stream", + "payload": { + "$ref": "#/components/schemas/TraderPnlSubscribeResponse" + } + }, + "TraderPositionsSubscribe": { + "summary": "Subscribe to trader position updates stream", + "payload": { + "$ref": "#/components/schemas/TraderPositionsSubscribeMessage" + } + }, + "TraderPositionUpdateEvent": { + "summary": "full position snapshot for a tracked trader", + "payload": { + "$ref": "#/components/schemas/TraderPositionUpdateEvent" + } + }, + "TraderPositionsSubscribeResponse": { + "summary": "Subscription acknowledgement for trader position updates stream", + "payload": { + "$ref": "#/components/schemas/TraderPositionsSubscribeResponse" + } + }, + "AccountsSubscribe": { + "summary": "Subscribe to account balances stream", + "payload": { + "$ref": "#/components/schemas/AccountsSubscribeMessage" + } + }, + "AccountsUpdateEvent": { + "summary": "ERC-1155 outcome token balance change for a wallet", + "payload": { + "$ref": "#/components/schemas/AccountsUpdateEvent" + } + }, + "UsdceUpdateEvent": { + "summary": "USDCe collateral balance change for a wallet", + "payload": { + "$ref": "#/components/schemas/UsdceUpdateEvent" + } + }, + "MaticUpdateEvent": { + "summary": "MATIC native balance change for a wallet", + "payload": { + "$ref": "#/components/schemas/MaticUpdateEvent" + } + }, + "AccountsSubscribeResponse": { + "summary": "Subscription acknowledgement for account balances stream", + "payload": { + "$ref": "#/components/schemas/AccountsSubscribeResponse" + } + }, + "OrderBookSubscribe": { + "summary": "Subscribe to order book snapshots stream", + "payload": { + "$ref": "#/components/schemas/OrderBookSubscribeMessage" + } + }, + "OrderBookUpdateEvent": { + "summary": "full CLOB orderbook snapshot for an outcome token", + "payload": { + "$ref": "#/components/schemas/OrderBookUpdateEvent" + } + }, + "OrderBookSubscribeResponse": { + "summary": "Subscription acknowledgement for order book snapshots stream", + "payload": { + "$ref": "#/components/schemas/OrderBookSubscribeResponse" + } + }, + "ClobRewardsSubscribe": { + "summary": "Subscribe to clob reward configuration changes stream", + "payload": { + "$ref": "#/components/schemas/ClobRewardsSubscribeMessage" + } + }, + "ClobRewardsUpdateEvent": { + "summary": "Server-pushed CLOB reward change event", + "payload": { + "$ref": "#/components/schemas/ClobRewardsUpdateEvent" + } + }, + "ClobRewardsSubscribeResponse": { + "summary": "Subscription acknowledgement for clob reward configuration changes stream", + "payload": { + "$ref": "#/components/schemas/ClobRewardsSubscribeResponse" + } + } + }, + "schemas": { + "ClobRewardsUpdateEvent": { + "type": "object", + "description": "Server-pushed CLOB reward change event. Envelope type: \"clob_rewards_update\".", + "properties": { + "event_type": { + "type": "string", + "enum": [ + "added", + "removed", + "updated" + ], + "description": "Type of change" + }, + "condition_id": { + "type": "string", + "description": "Affected market condition ID" + }, + "reward": { + "type": [ + "object", + "null" + ], + "description": "Full reward state (null for 'removed' events)", + "properties": { + "condition_id": { + "type": "string" + }, + "rewards_config": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "asset_address": { + "type": "string", + "description": "Reward token address (e.g. USDC)" + }, + "start_date": { + "type": "string", + "format": "date" + }, + "end_date": { + "type": "string", + "format": "date" + }, + "rate_per_day": { + "type": "number", + "description": "Daily reward rate in USDC" + }, + "total_rewards": { + "type": "number", + "description": "Cumulative rewards distributed" + } + } + } + }, + "rewards_max_spread": { + "type": [ + "number", + "null" + ], + "description": "Max spread to qualify for rewards" + }, + "rewards_min_size": { + "type": [ + "number", + "null" + ], + "description": "Min order size to qualify for rewards" + }, + "native_daily_rate": { + "type": [ + "number", + "null" + ], + "description": "Native (non-sponsored) daily rate" + }, + "sponsored_daily_rate": { + "type": [ + "number", + "null" + ], + "description": "Sponsored daily rate" + }, + "total_daily_rate": { + "type": [ + "number", + "null" + ], + "description": "Combined daily rate (native + sponsored)" + }, + "sponsors_count": { + "type": [ + "integer", + "null" + ], + "description": "Number of sponsors" + } + } + }, + "timestamp_ms": { + "type": "integer", + "description": "Unix timestamp in milliseconds" + } + } + }, + "ClobRewardsSubscribeResponse": { + "type": "object", + "description": "Server acknowledgement for a CLOB rewards subscription. Envelope type: \"clob_rewards_stream_subscribe_response\".", + "properties": { + "condition_ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Accepted condition IDs" + }, + "subscribe_all": { + "type": "boolean", + "description": "Whether subscribed to all changes" + }, + "rejected": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Filter values that were rejected" + } + } + }, + "ClobRewardsSubscribeMessage": { + "type": "object", + "description": "Subscribe to CLOB reward changes. Either provide specific condition_ids or set subscribe_all to true.", + "required": [ + "action" + ], + "properties": { + "action": { + "type": "string", + "enum": [ + "subscribe", + "unsubscribe_all" + ] + }, + "condition_ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Condition IDs to watch for reward changes.", + "maxItems": 500 + }, + "subscribe_all": { + "type": "boolean", + "description": "If true, receive ALL reward changes across all markets. Overrides condition_ids.", + "default": false + } + } + }, + "OrderBookUpdateEvent": { + "type": "object", + "description": "Server-pushed event: full CLOB orderbook snapshot for an outcome token. Envelope type: \"order_book_update\". Delivered whenever the book changes for a subscribed condition or position.", + "required": [ + "asset_id", + "market", + "bids", + "asks", + "timestamp", + "hash" + ], + "properties": { + "asset_id": { + "type": "string", + "description": "Hex token ID (position / outcome token)" + }, + "market": { + "type": "string", + "description": "Condition ID (hex)" + }, + "bids": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OrderBookLevel" + }, + "description": "Bid levels sorted best-first (highest price first)" + }, + "asks": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OrderBookLevel" + }, + "description": "Ask levels sorted best-first (lowest price first)" + }, + "timestamp": { + "type": "integer", + "format": "int64", + "description": "Unix milliseconds from CLOB message" + }, + "hash": { + "type": "string", + "description": "Orderbook content hash — identical hash means no change" + }, + "best_bid": { + "type": [ + "number", + "null" + ], + "description": "Best bid price (0–1)" + }, + "best_ask": { + "type": [ + "number", + "null" + ], + "description": "Best ask price (0–1)" + }, + "mid_price": { + "type": [ + "number", + "null" + ], + "description": "(best_bid + best_ask) / 2" + }, + "spread": { + "type": [ + "number", + "null" + ], + "description": "best_ask − best_bid" + }, + "bid_liquidity_usd": { + "type": [ + "number", + "null" + ], + "description": "Total USD value of all bid levels" + }, + "ask_liquidity_usd": { + "type": [ + "number", + "null" + ], + "description": "Total USD value of all ask levels" + }, + "bid_levels": { + "type": [ + "integer", + "null" + ], + "description": "Number of bid price levels" + }, + "ask_levels": { + "type": [ + "integer", + "null" + ], + "description": "Number of ask price levels" + } + } + }, + "OrderBookLevel": { + "type": "array", + "description": "A single price level: [price_string, size_string]", + "items": { + "type": "string" + }, + "minItems": 2, + "maxItems": 2 + }, + "TradesStreamSubscribeMessage": { + "type": "object", + "description": "Subscribe to the trades stream. No filters = subscribe to all trades.", + "required": [ + "action" + ], + "properties": { + "action": { + "type": "string", + "enum": [ + "subscribe", + "unsubscribe_all" + ] + }, + "condition_ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "64-char hex condition IDs (with or without 0x prefix)" + }, + "market_slugs": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Market slugs" + }, + "event_slugs": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Event slugs — subscribes to all markets under each event" + }, + "position_ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "ERC-1155 outcome token IDs (decimal or hex strings)" + }, + "traders": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Trader wallet addresses (lowercase 0x-prefixed)" + }, + "trade_types": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "OrderFilled", + "OrdersMatched", + "Redemption", + "Merge", + "Split", + "Cancelled", + "PositionsConverted", + "Initialization", + "Proposal", + "Dispute", + "Settled", + "Resolution", + "ConditionResolution", + "Reset", + "Flag", + "Unflag", + "Pause", + "Unpause", + "ManualResolution", + "NegRiskOutcomeReported", + "RegisterToken", + "Approval" + ] + }, + "description": "Only receive events of these types. Empty array = all types." + }, + "status": { + "type": "string", + "enum": [ + "confirmed", + "pending", + "all" + ], + "default": "confirmed", + "description": "Trade status filter: \"confirmed\" (default) = on-chain only, \"pending\" = mempool only, \"all\" = both" + }, + "subscribe_all": { + "type": "boolean", + "default": false, + "description": "Explicitly subscribe to all trades. Also implicitly true when no filters are provided." + } + } + }, + "TradesStreamSubscribeResponse": { + "type": "object", + "description": "Server acknowledgement for a trades stream subscription", + "properties": { + "condition_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "market_slugs": { + "type": "array", + "items": { + "type": "string" + } + }, + "event_slugs": { + "type": "array", + "items": { + "type": "string" + } + }, + "position_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "traders": { + "type": "array", + "items": { + "type": "string" + } + }, + "trade_types": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "type": "string", + "enum": [ + "confirmed", + "pending", + "all" + ] + }, + "subscribe_all": { + "type": "boolean" + }, + "rejected": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Filter values that were rejected (invalid format or unknown type)" + } + } + }, + "TradeStreamEvent": { + "description": "Server-pushed event. Discriminated by `trade_type` — each variant only includes relevant fields.\n\nEnvelope: `{\"type\": \"trade_stream_update\", \"room_id\": \"polymarket_trades\", \"status\": \"confirmed\"|\"pending\", \"data\": {...}}`\n\n**Pending trades:** `block`, `confirmed_at`, `log_index`, `block_index` are absent. `received_at` (milliseconds) is included instead. For OrderFilled/OrdersMatched, `order_hash`, `taker`, `fee`, `fee_shares`, `fee_pct` are also absent.", + "oneOf": [ + { + "title": "OrderFilled / OrdersMatched", + "description": "A buy/sell trade was matched on the exchange.", + "type": "object", + "required": [ + "trade_type", + "id", + "hash", + "trader", + "exchange" + ], + "properties": { + "trade_type": { + "type": "string", + "enum": [ + "OrderFilled", + "OrdersMatched" + ] + }, + "id": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "block": { + "type": "integer", + "description": "Absent for pending trades" + }, + "confirmed_at": { + "type": "integer", + "description": "Unix seconds. Absent for pending trades" + }, + "received_at": { + "type": "integer", + "description": "Unix milliseconds. Present for pending trades only" + }, + "log_index": { + "type": "integer", + "description": "Absent for pending trades" + }, + "block_index": { + "type": "integer", + "description": "Absent for pending trades" + }, + "order_hash": { + "type": "string", + "description": "Absent for pending trades" + }, + "trader": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "pseudonym": { + "type": [ + "string", + "null" + ] + }, + "profile_image": { + "type": [ + "string", + "null" + ] + }, + "x_username": { + "type": [ + "string", + "null" + ] + }, + "verified_badge": { + "type": "boolean" + } + } + }, + "taker": { + "type": "string", + "description": "Absent for pending trades" + }, + "side": { + "type": "string", + "enum": [ + "Buy", + "Sell" + ] + }, + "condition_id": { + "type": [ + "string", + "null" + ] + }, + "position_id": { + "type": "string" + }, + "outcome": { + "type": [ + "string", + "null" + ] + }, + "outcome_index": { + "type": [ + "integer", + "null" + ] + }, + "question": { + "type": [ + "string", + "null" + ] + }, + "image_url": { + "type": [ + "string", + "null" + ] + }, + "slug": { + "type": [ + "string", + "null" + ] + }, + "event_slug": { + "type": [ + "string", + "null" + ] + }, + "usd_amount": { + "type": "number" + }, + "shares_amount": { + "type": "number" + }, + "price": { + "type": "number" + }, + "probability": { + "type": [ + "number", + "null" + ] + }, + "fee": { + "type": "number", + "description": "Absent for pending trades" + }, + "fee_shares": { + "type": "number", + "description": "Absent for pending trades" + }, + "fee_pct": { + "type": "number", + "description": "Absent for pending trades" + }, + "exchange": { + "type": "integer" + } + } + }, + { + "title": "Redemption", + "description": "Positions redeemed after market resolution.", + "type": "object", + "required": [ + "trade_type", + "id", + "hash", + "trader", + "exchange" + ], + "properties": { + "trade_type": { + "type": "string", + "enum": [ + "Redemption" + ] + }, + "id": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "block": { + "type": "integer" + }, + "confirmed_at": { + "type": "integer" + }, + "received_at": { + "type": "integer" + }, + "log_index": { + "type": "integer" + }, + "block_index": { + "type": "integer" + }, + "trader": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "pseudonym": { + "type": [ + "string", + "null" + ] + }, + "profile_image": { + "type": [ + "string", + "null" + ] + }, + "x_username": { + "type": [ + "string", + "null" + ] + }, + "verified_badge": { + "type": "boolean" + } + } + }, + "condition_id": { + "type": [ + "string", + "null" + ] + }, + "outcome": { + "type": [ + "string", + "null" + ] + }, + "outcome_index": { + "type": [ + "integer", + "null" + ] + }, + "question": { + "type": [ + "string", + "null" + ] + }, + "image_url": { + "type": [ + "string", + "null" + ] + }, + "slug": { + "type": [ + "string", + "null" + ] + }, + "event_slug": { + "type": [ + "string", + "null" + ] + }, + "usd_amount": { + "type": "number" + }, + "winning_outcome_index": { + "type": [ + "integer", + "null" + ] + }, + "position_details": { + "type": "array", + "items": { + "type": "object", + "properties": { + "position_id": { + "type": "string" + }, + "outcome_index": { + "type": "integer" + }, + "amount": { + "type": "string" + } + } + } + }, + "exchange": { + "type": "integer" + } + } + }, + { + "title": "Merge", + "description": "Outcome tokens burned to receive collateral.", + "type": "object", + "required": [ + "trade_type", + "id", + "hash", + "trader", + "exchange" + ], + "properties": { + "trade_type": { + "type": "string", + "enum": [ + "Merge" + ] + }, + "id": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "block": { + "type": "integer" + }, + "confirmed_at": { + "type": "integer" + }, + "received_at": { + "type": "integer" + }, + "log_index": { + "type": "integer" + }, + "block_index": { + "type": "integer" + }, + "trader": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "pseudonym": { + "type": [ + "string", + "null" + ] + }, + "profile_image": { + "type": [ + "string", + "null" + ] + }, + "x_username": { + "type": [ + "string", + "null" + ] + }, + "verified_badge": { + "type": "boolean" + } + } + }, + "condition_id": { + "type": [ + "string", + "null" + ] + }, + "question": { + "type": [ + "string", + "null" + ] + }, + "image_url": { + "type": [ + "string", + "null" + ] + }, + "slug": { + "type": [ + "string", + "null" + ] + }, + "event_slug": { + "type": [ + "string", + "null" + ] + }, + "usd_amount": { + "type": "number" + }, + "position_details": { + "type": "array", + "items": { + "type": "object", + "properties": { + "position_id": { + "type": "string" + }, + "outcome_index": { + "type": "integer" + }, + "amount": { + "type": "string" + } + } + } + }, + "exchange": { + "type": "integer" + } + } + }, + { + "title": "Split", + "description": "Collateral deposited to receive outcome tokens.", + "type": "object", + "required": [ + "trade_type", + "id", + "hash", + "trader", + "exchange" + ], + "properties": { + "trade_type": { + "type": "string", + "enum": [ + "Split" + ] + }, + "id": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "block": { + "type": "integer" + }, + "confirmed_at": { + "type": "integer" + }, + "received_at": { + "type": "integer" + }, + "log_index": { + "type": "integer" + }, + "block_index": { + "type": "integer" + }, + "trader": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "pseudonym": { + "type": [ + "string", + "null" + ] + }, + "profile_image": { + "type": [ + "string", + "null" + ] + }, + "x_username": { + "type": [ + "string", + "null" + ] + }, + "verified_badge": { + "type": "boolean" + } + } + }, + "condition_id": { + "type": [ + "string", + "null" + ] + }, + "question": { + "type": [ + "string", + "null" + ] + }, + "image_url": { + "type": [ + "string", + "null" + ] + }, + "slug": { + "type": [ + "string", + "null" + ] + }, + "event_slug": { + "type": [ + "string", + "null" + ] + }, + "usd_amount": { + "type": "number" + }, + "position_details": { + "type": "array", + "items": { + "type": "object", + "properties": { + "position_id": { + "type": "string" + }, + "outcome_index": { + "type": "integer" + }, + "amount": { + "type": "string" + } + } + } + }, + "exchange": { + "type": "integer" + } + } + }, + { + "title": "PositionsConverted", + "description": "NegRisk NO tokens converted to YES tokens + collateral.", + "type": "object", + "required": [ + "trade_type", + "id", + "hash", + "trader", + "exchange" + ], + "properties": { + "trade_type": { + "type": "string", + "enum": [ + "PositionsConverted" + ] + }, + "id": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "block": { + "type": "integer" + }, + "confirmed_at": { + "type": "integer" + }, + "received_at": { + "type": "integer" + }, + "log_index": { + "type": "integer" + }, + "block_index": { + "type": "integer" + }, + "trader": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "pseudonym": { + "type": [ + "string", + "null" + ] + }, + "profile_image": { + "type": [ + "string", + "null" + ] + }, + "x_username": { + "type": [ + "string", + "null" + ] + }, + "verified_badge": { + "type": "boolean" + } + } + }, + "market_id": { + "type": "string" + }, + "index_set": { + "type": "string" + }, + "shares_amount": { + "type": "number" + }, + "exchange": { + "type": "integer" + } + } + }, + { + "title": "Cancelled", + "description": "Order cancelled on-chain.", + "type": "object", + "required": [ + "trade_type", + "id", + "hash", + "exchange" + ], + "properties": { + "trade_type": { + "type": "string", + "enum": [ + "Cancelled" + ] + }, + "id": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "block": { + "type": "integer" + }, + "confirmed_at": { + "type": "integer" + }, + "received_at": { + "type": "integer" + }, + "log_index": { + "type": "integer" + }, + "block_index": { + "type": "integer" + }, + "order_hash": { + "type": "string" + }, + "question": { + "type": [ + "string", + "null" + ] + }, + "image_url": { + "type": [ + "string", + "null" + ] + }, + "slug": { + "type": [ + "string", + "null" + ] + }, + "event_slug": { + "type": [ + "string", + "null" + ] + }, + "exchange": { + "type": "integer" + } + } + }, + { + "title": "Oracle Lifecycle Event", + "description": "Market lifecycle events: Initialization, Proposal, Dispute, Settled, Resolution, ConditionResolution, Reset, Flag, Unflag, Pause, Unpause, ManualResolution, NegRiskOutcomeReported.", + "type": "object", + "required": [ + "trade_type", + "id", + "hash", + "oracle_contract", + "condition_id" + ], + "properties": { + "trade_type": { + "type": "string", + "enum": [ + "Initialization", + "Proposal", + "Dispute", + "Settled", + "Resolution", + "ConditionResolution", + "Reset", + "Flag", + "Unflag", + "Pause", + "Unpause", + "ManualResolution", + "NegRiskOutcomeReported" + ] + }, + "id": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "block": { + "type": "integer" + }, + "confirmed_at": { + "type": "integer" + }, + "received_at": { + "type": "integer" + }, + "log_index": { + "type": "integer" + }, + "block_index": { + "type": "integer" + }, + "oracle_contract": { + "type": "string" + }, + "condition_id": { + "type": "string" + }, + "question": { + "type": [ + "string", + "null" + ] + }, + "image_url": { + "type": [ + "string", + "null" + ] + }, + "slug": { + "type": [ + "string", + "null" + ] + }, + "event_slug": { + "type": [ + "string", + "null" + ] + }, + "assertion_id": { + "type": [ + "string", + "null" + ] + }, + "proposer": { + "type": [ + "string", + "null" + ] + }, + "disputer": { + "type": [ + "string", + "null" + ] + }, + "proposed_outcome": { + "type": [ + "string", + "null" + ] + }, + "settled_price": { + "type": [ + "integer", + "null" + ] + }, + "disputed": { + "type": [ + "boolean", + "null" + ] + }, + "settlement_resolution": { + "type": [ + "boolean", + "null" + ] + }, + "bond": { + "type": [ + "string", + "null" + ] + }, + "expiration_time": { + "type": [ + "integer", + "null" + ] + }, + "creator": { + "type": [ + "string", + "null" + ] + }, + "reward_token": { + "type": [ + "string", + "null" + ] + }, + "reward": { + "type": [ + "string", + "null" + ] + }, + "proposal_bond": { + "type": [ + "string", + "null" + ] + } + } + }, + { + "title": "RegisterToken", + "description": "YES/NO token pair registered for a condition.", + "type": "object", + "required": [ + "trade_type", + "id", + "hash", + "condition_id", + "exchange" + ], + "properties": { + "trade_type": { + "type": "string", + "enum": [ + "RegisterToken" + ] + }, + "id": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "block": { + "type": "integer" + }, + "confirmed_at": { + "type": "integer" + }, + "received_at": { + "type": "integer" + }, + "log_index": { + "type": "integer" + }, + "block_index": { + "type": "integer" + }, + "condition_id": { + "type": "string" + }, + "token0": { + "type": "string" + }, + "token1": { + "type": "string" + }, + "question": { + "type": [ + "string", + "null" + ] + }, + "image_url": { + "type": [ + "string", + "null" + ] + }, + "slug": { + "type": [ + "string", + "null" + ] + }, + "event_slug": { + "type": [ + "string", + "null" + ] + }, + "exchange": { + "type": "integer" + } + } + }, + { + "title": "Approval", + "description": "ERC-1155 setApprovalForAll — operator approved/revoked.", + "type": "object", + "required": [ + "trade_type", + "id", + "hash", + "trader", + "exchange" + ], + "properties": { + "trade_type": { + "type": "string", + "enum": [ + "Approval" + ] + }, + "id": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "block": { + "type": "integer" + }, + "confirmed_at": { + "type": "integer" + }, + "received_at": { + "type": "integer" + }, + "log_index": { + "type": "integer" + }, + "block_index": { + "type": "integer" + }, + "trader": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "pseudonym": { + "type": [ + "string", + "null" + ] + }, + "profile_image": { + "type": [ + "string", + "null" + ] + }, + "x_username": { + "type": [ + "string", + "null" + ] + }, + "verified_badge": { + "type": "boolean" + } + } + }, + "operator": { + "type": "string" + }, + "approved": { + "type": "boolean" + }, + "question": { + "type": [ + "string", + "null" + ] + }, + "image_url": { + "type": [ + "string", + "null" + ] + }, + "slug": { + "type": [ + "string", + "null" + ] + }, + "event_slug": { + "type": [ + "string", + "null" + ] + }, + "exchange": { + "type": "integer" + } + } + } + ], + "discriminator": "trade_type" + }, + "AssetPricesSubscribeMessage": { + "type": "object", + "description": "Subscribe to the asset prices stream. Empty asset_symbols = all assets.", + "required": [ + "action" + ], + "properties": { + "action": { + "type": "string", + "enum": [ + "subscribe", + "unsubscribe_all" + ] + }, + "asset_symbols": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Uppercase asset symbols (e.g. \"BTC\", \"ETH\"). Empty = subscribe to all." + } + } + }, + "AssetPricesSubscribeResponse": { + "type": "object", + "description": "Server acknowledgement for an asset prices subscription", + "properties": { + "asset_symbols": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Accepted symbols. Empty array means subscribed to all symbols." + } + } + }, + "AssetPriceTickEvent": { + "type": "object", + "description": "Server-pushed event: a crypto-asset price tick. Envelope type: \"asset_price_tick\".", + "required": [ + "event_type", + "symbol", + "price", + "timestamp_ms", + "published_at" + ], + "properties": { + "event_type": { + "type": "string", + "description": "Always \"asset_price_tick\"" + }, + "symbol": { + "type": "string", + "description": "Uppercase asset symbol (e.g. \"BTC\")" + }, + "price": { + "type": "number", + "description": "Current price in USD" + }, + "timestamp_ms": { + "type": "integer", + "format": "int64", + "description": "Event timestamp in Unix milliseconds" + }, + "published_at": { + "type": "integer", + "format": "int64", + "description": "Publish timestamp in Unix milliseconds" + } + } + }, + "AssetPriceWindowUpdateEvent": { + "type": "object", + "description": "Server-pushed event: candle open or close for a crypto asset. Envelope type: \"asset_price_window_update\". Delivered from both `polymarket_asset_prices` and `polymarket_asset_window_updates` rooms.", + "required": [ + "event_type", + "symbol", + "variant", + "start_time", + "end_time", + "open_price", + "close_price", + "update_type", + "published_at" + ], + "properties": { + "event_type": { + "type": "string", + "description": "Always \"asset_price_window_update\"" + }, + "symbol": { + "type": "string", + "description": "Uppercase asset symbol (e.g. \"BTC\")" + }, + "variant": { + "type": "string", + "description": "Candle size / timeframe (e.g. \"5m\", \"1h\", \"1d\")" + }, + "start_time": { + "type": "integer", + "format": "int64", + "description": "Candle start in Unix milliseconds" + }, + "end_time": { + "type": "integer", + "format": "int64", + "description": "Candle end in Unix milliseconds" + }, + "open_price": { + "type": "number", + "description": "Candle open price in USD" + }, + "close_price": { + "type": "number", + "description": "Candle close price in USD" + }, + "update_type": { + "type": "string", + "description": "\"open\" = candle starting, \"close\" = candle finalised" + }, + "published_at": { + "type": "integer", + "format": "int64", + "description": "Publish timestamp in Unix milliseconds" + } + } + }, + "AssetWindowUpdatesSubscribeMessage": { + "type": "object", + "description": "Subscribe to the asset window updates stream. At least one of asset_symbols or timeframes must be non-empty.", + "required": [ + "action" + ], + "properties": { + "action": { + "type": "string", + "enum": [ + "subscribe", + "unsubscribe_all" + ] + }, + "asset_symbols": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Uppercase asset symbols (e.g. \"BTC\", \"ETH\")" + }, + "timeframes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "5m", + "15m", + "1h", + "4h", + "1d", + "24h" + ] + }, + "description": "Candle sizes to filter by. \"1d\" and \"24h\" are treated as equivalent." + } + } + }, + "AssetWindowUpdatesSubscribeResponse": { + "type": "object", + "description": "Server acknowledgement for an asset window updates subscription", + "properties": { + "asset_symbols": { + "type": "array", + "items": { + "type": "string" + } + }, + "timeframes": { + "type": "array", + "items": { + "type": "string" + } + }, + "error": { + "type": [ + "string", + "null" + ], + "description": "Set if the subscription was rejected (e.g. no filters provided)" + } + } + }, + "AssetWindowUpdateEvent": { + "allOf": [ + { + "$ref": "#/components/schemas/AssetPriceWindowUpdateEvent" + } + ], + "description": "Server-pushed event from the polymarket_asset_window_updates room. Same payload as AssetPriceWindowUpdateEvent. Envelope type: \"asset_price_window_update\"." + }, + "MarketMetricsSubscribeMessage": { + "type": "object", + "description": "Subscribe to the market metrics stream. condition_ids is required and must be non-empty.", + "required": [ + "action", + "condition_ids" + ], + "properties": { + "action": { + "type": "string", + "enum": [ + "subscribe", + "unsubscribe_all" + ] + }, + "condition_ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "64-char hex condition IDs (with or without 0x prefix)", + "minItems": 1 + } + } + }, + "MarketMetricsSubscribeResponse": { + "type": "object", + "description": "Server acknowledgement for a market metrics subscription", + "properties": { + "condition_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "rejected": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Condition IDs that were rejected (invalid format)" + }, + "error": { + "type": [ + "string", + "null" + ], + "description": "Set if the entire subscription was rejected" + } + } + }, + "MarketMetricsEvent": { + "type": "object", + "description": "Server-pushed event: metrics update for one timeframe of a condition. Envelope type: \"market_metrics_update\". One event is emitted per timeframe window on each update.", + "required": [ + "condition_id", + "timeframe", + "usd_volume", + "fees", + "txns", + "unique_traders" + ], + "properties": { + "condition_id": { + "type": "string", + "description": "64-char hex condition ID" + }, + "timeframe": { + "type": "string", + "enum": [ + "1m", + "5m", + "30m", + "1h", + "6h", + "24h", + "7d", + "30d" + ] + }, + "timestamp": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Optional event timestamp (Unix seconds)" + }, + "usd_volume": { + "type": "string", + "description": "USD volume in this timeframe window (decimal string)" + }, + "fees": { + "type": "number", + "description": "Total fees in this window" + }, + "txns": { + "type": "integer", + "format": "int64", + "description": "Number of transactions" + }, + "unique_traders": { + "type": "integer", + "format": "int64" + } + } + }, + "EventMetricsSubscribeMessage": { + "type": "object", + "description": "Subscribe to the event metrics stream. event_slugs is required and must be non-empty.", + "required": [ + "action", + "event_slugs" + ], + "properties": { + "action": { + "type": "string", + "enum": [ + "subscribe", + "unsubscribe_all" + ] + }, + "event_slugs": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Event slugs (lowercase)", + "minItems": 1 + } + } + }, + "EventMetricsSubscribeResponse": { + "type": "object", + "description": "Server acknowledgement for an event metrics subscription", + "properties": { + "event_slugs": { + "type": "array", + "items": { + "type": "string" + } + }, + "rejected": { + "type": "array", + "items": { + "type": "string" + } + }, + "error": { + "type": [ + "string", + "null" + ] + } + } + }, + "EventMetricsEvent": { + "type": "object", + "description": "Server-pushed event: aggregated metrics update for one timeframe of an event. Envelope type: \"event_metrics_update\". One event is emitted per timeframe window on each update.", + "required": [ + "event_slug", + "timeframe", + "usd_volume", + "fees", + "txns", + "unique_traders" + ], + "properties": { + "event_slug": { + "type": "string" + }, + "timeframe": { + "type": "string", + "enum": [ + "1m", + "5m", + "30m", + "1h", + "6h", + "24h", + "7d", + "30d" + ] + }, + "timestamp": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Optional event timestamp (Unix seconds)" + }, + "usd_volume": { + "type": "string", + "description": "USD volume in this timeframe window (decimal string)" + }, + "fees": { + "type": "number" + }, + "txns": { + "type": "integer", + "format": "int64" + }, + "unique_traders": { + "type": "integer", + "format": "int64" + } + } + }, + "PositionMetricsSubscribeMessage": { + "type": "object", + "description": "Subscribe to the position metrics stream. position_ids is required and must be non-empty.", + "required": [ + "action", + "position_ids" + ], + "properties": { + "action": { + "type": "string", + "enum": [ + "subscribe", + "unsubscribe_all" + ] + }, + "position_ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "ERC-1155 outcome token IDs (decimal or hex strings)", + "minItems": 1 + } + } + }, + "PositionMetricsSubscribeResponse": { + "type": "object", + "description": "Server acknowledgement for a position metrics subscription", + "properties": { + "position_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "rejected": { + "type": "array", + "items": { + "type": "string" + } + }, + "error": { + "type": [ + "string", + "null" + ] + } + } + }, + "PositionMetricsEvent": { + "type": "object", + "description": "Server-pushed event: metrics update for one timeframe of an outcome token. Envelope type: \"position_metrics_update\". One event is emitted per timeframe window on each update.", + "required": [ + "condition_id", + "position_id", + "timeframe", + "usd_volume", + "usd_buy_volume", + "usd_sell_volume", + "fees", + "txns", + "buys", + "sells", + "unique_traders", + "price_open", + "price_close", + "price_high", + "price_low", + "probability_open", + "probability_close", + "probability_high", + "probability_low", + "historical_confirmed_at", + "latest_confirmed_at", + "latest_block" + ], + "properties": { + "condition_id": { + "type": "string", + "description": "64-char hex condition ID" + }, + "position_id": { + "type": "string", + "description": "ERC-1155 token ID (decimal string)" + }, + "outcome": { + "type": [ + "string", + "null" + ], + "description": "Outcome name (e.g. \"Yes\")" + }, + "outcome_index": { + "type": [ + "integer", + "null" + ] + }, + "timeframe": { + "type": "string", + "enum": [ + "1m", + "5m", + "30m", + "1h", + "6h", + "24h", + "7d", + "30d" + ] + }, + "timestamp": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Optional event timestamp (Unix seconds)" + }, + "usd_volume": { + "type": "string", + "description": "Total USD volume (decimal string)" + }, + "usd_buy_volume": { + "type": "string", + "description": "USD buy volume (decimal string)" + }, + "usd_sell_volume": { + "type": "string", + "description": "USD sell volume (decimal string)" + }, + "fees": { + "type": "number" + }, + "txns": { + "type": "integer", + "format": "int64" + }, + "buys": { + "type": "integer", + "format": "int64" + }, + "sells": { + "type": "integer", + "format": "int64" + }, + "unique_traders": { + "type": "integer", + "format": "int64" + }, + "price_open": { + "type": "number", + "description": "OHLC open price (0–1)" + }, + "price_close": { + "type": "number", + "description": "OHLC close price (0–1)" + }, + "price_high": { + "type": "number", + "description": "OHLC high price (0–1)" + }, + "price_low": { + "type": "number", + "description": "OHLC low price (0–1)" + }, + "probability_open": { + "type": "number", + "description": "Implied probability at open (0–1)" + }, + "probability_close": { + "type": "number", + "description": "Implied probability at close (0–1)" + }, + "probability_high": { + "type": "number", + "description": "Highest implied probability in window (0–1)" + }, + "probability_low": { + "type": "number", + "description": "Lowest implied probability in window (0–1)" + }, + "historical_confirmed_at": { + "type": "integer", + "format": "int64" + }, + "latest_confirmed_at": { + "type": "integer", + "format": "int64" + }, + "latest_block": { + "type": "integer", + "format": "int64" + } + } + }, + "TraderPnlSubscribeMessage": { + "type": "object", + "description": "Subscribe to the trader PnL stream. traders is required and must be non-empty.", + "required": [ + "action", + "traders" + ], + "properties": { + "action": { + "type": "string", + "enum": [ + "subscribe", + "unsubscribe_all" + ] + }, + "traders": { + "type": "array", + "items": { + "type": "string" + }, + "description": "EVM wallet addresses", + "minItems": 1 + } + } + }, + "TraderPnlSubscribeResponse": { + "type": "object", + "description": "Server acknowledgement for a trader PnL subscription", + "properties": { + "traders": { + "type": "array", + "items": { + "type": "string" + } + }, + "rejected": { + "type": "array", + "items": { + "type": "string" + } + }, + "error": { + "type": [ + "string", + "null" + ] + } + } + }, + "TraderGlobalPnlEvent": { + "type": "object", + "description": "Server-pushed event: global (portfolio-level) PnL update for a trader. Envelope type: \"trader_global_pnl_update\".", + "required": [ + "trader", + "realized_pnl_usd", + "total_volume_usd", + "buy_volume_usd", + "sell_volume_usd", + "redemption_volume_usd", + "merge_volume_usd", + "market_win_rate_pct", + "avg_pnl_per_market", + "avg_pnl_per_trade", + "avg_hold_time_seconds", + "total_fees", + "best_trade_pnl_usd", + "worst_trade_pnl_usd" + ], + "properties": { + "trader": { + "type": "string", + "description": "Trader EVM wallet address" + }, + "realized_pnl_usd": { + "type": "string", + "description": "Total realized PnL in USD (decimal string)" + }, + "events_traded": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "markets_traded": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_buys": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_sells": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_redemptions": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_merges": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_volume_usd": { + "type": "string", + "description": "Total USD volume (decimal string)" + }, + "buy_volume_usd": { + "type": "string" + }, + "sell_volume_usd": { + "type": "string" + }, + "redemption_volume_usd": { + "type": "string" + }, + "merge_volume_usd": { + "type": "string" + }, + "markets_won": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "markets_lost": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "market_win_rate_pct": { + "type": "string", + "description": "Win rate percentage (decimal string)" + }, + "avg_pnl_per_market": { + "type": "string" + }, + "avg_pnl_per_trade": { + "type": "string" + }, + "avg_hold_time_seconds": { + "type": "string" + }, + "total_fees": { + "type": "string" + }, + "best_trade_pnl_usd": { + "type": "string" + }, + "best_trade_condition_id": { + "type": [ + "string", + "null" + ] + }, + "worst_trade_pnl_usd": { + "type": "string" + }, + "worst_trade_condition_id": { + "type": [ + "string", + "null" + ] + }, + "first_trade_at": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Unix seconds" + }, + "last_trade_at": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Unix seconds" + }, + "timestamp": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "timeframe": { + "type": [ + "string", + "null" + ], + "description": "\"1d\", \"7d\", \"30d\", or \"lifetime\"" + } + } + }, + "TraderMarketPnlEvent": { + "type": "object", + "description": "Server-pushed event: per-market PnL update for a trader. Envelope type: \"trader_market_pnl_update\".", + "required": [ + "trader", + "condition_id", + "buy_usd", + "sell_usd", + "redemption_usd", + "merge_usd", + "realized_pnl_usd", + "total_fees" + ], + "properties": { + "trader": { + "type": "string" + }, + "condition_id": { + "type": "string", + "description": "64-char hex condition ID" + }, + "event_slug": { + "type": [ + "string", + "null" + ] + }, + "outcomes_traded": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_buys": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_sells": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_redemptions": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_merges": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "buy_usd": { + "type": "string", + "description": "Total buy volume in USD (decimal string)" + }, + "sell_usd": { + "type": "string" + }, + "redemption_usd": { + "type": "string" + }, + "merge_usd": { + "type": "string" + }, + "realized_pnl_usd": { + "type": "string", + "description": "Realized PnL in USD (decimal string)" + }, + "winning_outcomes": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_fees": { + "type": "string" + }, + "first_trade_at": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Unix seconds" + }, + "last_trade_at": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Unix seconds" + }, + "timestamp": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "timeframe": { + "type": [ + "string", + "null" + ], + "description": "\"1d\", \"7d\", \"30d\", or \"lifetime\"" + } + } + }, + "TraderEventPnlEvent": { + "type": "object", + "description": "Server-pushed event: per-event PnL update for a trader. Envelope type: \"trader_event_pnl_update\".", + "required": [ + "trader", + "event_slug", + "buy_usd", + "sell_usd", + "redemption_usd", + "merge_usd", + "total_volume_usd", + "realized_pnl_usd", + "total_fees" + ], + "properties": { + "trader": { + "type": "string" + }, + "event_slug": { + "type": "string" + }, + "markets_traded": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "outcomes_traded": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_buys": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_sells": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_redemptions": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_merges": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_volume_usd": { + "type": "string", + "description": "Total USD volume (decimal string)" + }, + "buy_usd": { + "type": "string" + }, + "sell_usd": { + "type": "string" + }, + "redemption_usd": { + "type": "string" + }, + "merge_usd": { + "type": "string" + }, + "realized_pnl_usd": { + "type": "string", + "description": "Realized PnL in USD (decimal string)" + }, + "winning_markets": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "losing_markets": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_fees": { + "type": "string" + }, + "first_trade_at": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Unix seconds" + }, + "last_trade_at": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Unix seconds" + }, + "timestamp": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "timeframe": { + "type": [ + "string", + "null" + ], + "description": "\"1d\", \"7d\", \"30d\", or \"lifetime\"" + } + } + }, + "TraderPositionsSubscribeMessage": { + "type": "object", + "description": "Subscribe to the trader positions stream. traders is required and must be non-empty.", + "required": [ + "action", + "traders" + ], + "properties": { + "action": { + "type": "string", + "enum": [ + "subscribe", + "unsubscribe_all" + ] + }, + "traders": { + "type": "array", + "items": { + "type": "string" + }, + "description": "EVM wallet addresses", + "minItems": 1 + } + } + }, + "TraderPositionsSubscribeResponse": { + "type": "object", + "description": "Server acknowledgement for a trader positions subscription", + "properties": { + "traders": { + "type": "array", + "items": { + "type": "string" + } + }, + "rejected": { + "type": "array", + "items": { + "type": "string" + } + }, + "error": { + "type": [ + "string", + "null" + ] + } + } + }, + "TraderPositionUpdateEvent": { + "type": "object", + "description": "Server-pushed event: full position snapshot for a tracked trader. Envelope type: \"trader_position_update\". Pushed whenever a position's PnL changes.", + "required": [ + "trader" + ], + "properties": { + "trader": { + "type": "string", + "description": "Trader EVM wallet address" + }, + "position_id": { + "type": [ + "string", + "null" + ], + "description": "ERC-1155 token ID (decimal string)" + }, + "condition_id": { + "type": [ + "string", + "null" + ] + }, + "market_slug": { + "type": [ + "string", + "null" + ] + }, + "event_slug": { + "type": [ + "string", + "null" + ] + }, + "title": { + "type": [ + "string", + "null" + ], + "description": "Market title / question" + }, + "image_url": { + "type": [ + "string", + "null" + ] + }, + "outcome": { + "type": [ + "string", + "null" + ], + "description": "Outcome name (e.g. \"Yes\")" + }, + "outcome_index": { + "type": [ + "integer", + "null" + ] + }, + "won": { + "type": [ + "boolean", + "null" + ], + "description": "True if this outcome resolved as winner" + }, + "total_buys": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_sells": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_shares_bought": { + "type": [ + "number", + "null" + ] + }, + "total_shares_sold": { + "type": [ + "number", + "null" + ] + }, + "total_buy_usd": { + "type": [ + "number", + "null" + ] + }, + "total_sell_usd": { + "type": [ + "number", + "null" + ] + }, + "redemption_usd": { + "type": [ + "number", + "null" + ] + }, + "avg_entry_price": { + "type": [ + "number", + "null" + ], + "description": "Average entry price (0–1)" + }, + "avg_exit_price": { + "type": [ + "number", + "null" + ], + "description": "Average exit price (0–1)" + }, + "realized_pnl_usd": { + "type": [ + "number", + "null" + ] + }, + "total_fees": { + "type": [ + "number", + "null" + ] + }, + "first_trade_at": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Unix seconds" + }, + "last_trade_at": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Unix seconds" + }, + "current_shares_balance": { + "type": [ + "number", + "null" + ], + "description": "Current ERC-1155 token balance" + }, + "realized_pnl_pct": { + "type": [ + "number", + "null" + ], + "description": "Realized PnL as a percentage of cost basis" + } + } + }, + "AccountsSubscribeMessage": { + "type": "object", + "description": "Subscribe to the accounts stream. `wallets` is required. Share balance updates (`accounts_update`) are always delivered. Set `include_usdce` or `include_matic` to also receive those balance streams.", + "required": [ + "action", + "wallets" + ], + "properties": { + "action": { + "type": "string", + "enum": [ + "subscribe", + "unsubscribe_all" + ] + }, + "wallets": { + "type": "array", + "items": { + "type": "string" + }, + "description": "EVM wallet addresses", + "minItems": 1 + }, + "include_usdce": { + "type": "boolean", + "default": false, + "description": "Also stream USDCe collateral balance updates for subscribed wallets" + }, + "include_matic": { + "type": "boolean", + "default": false, + "description": "Also stream MATIC gas balance updates for subscribed wallets" + } + } + }, + "AccountsSubscribeResponse": { + "type": "object", + "description": "Server acknowledgement for an accounts subscription", + "properties": { + "wallets": { + "type": "array", + "items": { + "type": "string" + } + }, + "rejected": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Addresses rejected (invalid format)" + }, + "include_usdce": { + "type": "boolean" + }, + "include_matic": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + } + } + }, + "AccountsUpdateEvent": { + "type": "object", + "description": "Server-pushed event: ERC-1155 outcome token balance change for a wallet. Envelope type: \"accounts_update\".", + "required": [ + "wallet", + "position_id", + "balance", + "block_number", + "updated_at" + ], + "properties": { + "wallet": { + "type": "string", + "description": "Wallet address" + }, + "position_id": { + "type": "string", + "description": "ERC-1155 outcome token ID (decimal string)" + }, + "balance": { + "type": "string", + "description": "Current token balance (decimal string)" + }, + "block_number": { + "type": "integer", + "format": "int64" + }, + "updated_at": { + "type": "integer", + "format": "int64", + "description": "Unix seconds" + }, + "condition_id": { + "type": "string", + "description": "Condition ID — omitted when not available" + }, + "event_slug": { + "type": "string", + "description": "Event slug — omitted when not available" + } + } + }, + "UsdceUpdateEvent": { + "type": "object", + "description": "Server-pushed event: USDCe collateral balance change for a wallet. Envelope type: \"usdce_update\". Only delivered when `include_usdce: true`.", + "required": [ + "address", + "block_number", + "updated_at" + ], + "properties": { + "address": { + "type": "string", + "description": "Wallet address (0x-prefixed hex)" + }, + "token_address": { + "type": "string", + "description": "USDCe contract address — omitted when not available" + }, + "balance": { + "type": "string", + "description": "Current USDCe balance (decimal string) — omitted when not available" + }, + "block_number": { + "type": "integer", + "format": "uint64" + }, + "updated_at": { + "type": "integer", + "format": "int64", + "description": "Unix seconds" + } + } + }, + "MaticUpdateEvent": { + "type": "object", + "description": "Server-pushed event: MATIC native balance change for a wallet. Envelope type: \"matic_update\". Only delivered when `include_matic: true`.", + "required": [ + "address", + "block_number", + "updated_at" + ], + "properties": { + "address": { + "type": "string", + "description": "Wallet address (0x-prefixed hex)" + }, + "token_address": { + "type": "string", + "description": "Native token address — omitted when not available" + }, + "balance": { + "type": "string", + "description": "Current MATIC balance (decimal string) — omitted when not available" + }, + "block_number": { + "type": "integer", + "format": "uint64" + }, + "updated_at": { + "type": "integer", + "format": "int64", + "description": "Unix seconds" + } + } + }, + "OrderBookSubscribeMessage": { + "type": "object", + "description": "Subscribe to the order book stream. At least one filter is required. Maximum 500 combined condition_ids + position_ids per client. No `type` field is needed — the server routes by room_id.", + "required": [ + "action" + ], + "properties": { + "action": { + "type": "string", + "enum": [ + "subscribe", + "unsubscribe_all" + ] + }, + "condition_ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Condition IDs (markets). All positions within each market are delivered.", + "maxItems": 500 + }, + "position_ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Token / asset IDs (individual outcome positions, hex strings).", + "maxItems": 500 + } + } + }, + "OrderBookSubscribeResponse": { + "type": "object", + "description": "Server acknowledgement for an order book subscription. Envelope type: \"order_book_stream_subscribe_response\".", + "properties": { + "condition_ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Accepted condition IDs" + }, + "position_ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Accepted position IDs" + }, + "rejected": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Filter values that were rejected (invalid format or limit exceeded)" + } + } + } + }, + "securitySchemes": { + "apiKeyHeader": { + "type": "httpApiKey", + "in": "header", + "name": "X-Api-Key", + "description": "Pass the API key via the `X-Api-Key` header on the WebSocket upgrade request." + }, + "apiKeyQuery": { + "type": "httpApiKey", + "in": "query", + "name": "api_key", + "description": "Pass the API key via `?api_key=` on the WebSocket upgrade URL (browser/frontend friendly)." + }, + "publicKeyJwt": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT", + "description": "Public-key JWT combo: the builder embeds a `pk_jwt_*` key in their frontend, and end users supply a JWT signed by the builder. Send via `Authorization: Bearer ` header or `?token=` query parameter. Credits bill to the builder's org; rate limits are applied per session." + } + } + } +} \ No newline at end of file diff --git a/package.json b/package.json index d59727f..9b2d2c9 100644 --- a/package.json +++ b/package.json @@ -24,14 +24,17 @@ "build": "rm -rf dist && bun build ./src/index.ts --target browser --format esm --sourcemap --outdir ./dist && bun build ./src/index.ts --target browser --format cjs --sourcemap --outdir ./dist-cjs && mv ./dist-cjs/index.js ./dist/index.cjs && mv ./dist-cjs/index.js.map ./dist/index.cjs.map && rm -rf dist-cjs && tsc --emitDeclarationOnly", "check-routes": "bun run scripts/check-routes.ts", "fix-spec": "bun run scripts/fix-spec.ts", - "prep": "bun run fetch-spec:polymarket && bun run fix-spec && bun run generate:polymarket && bun run fetch-spec:webhooks && bun run generate:webhooks && bun run check-routes && bun run build", + "fetch-specs": "bun run scripts/fetch-specs.ts", + "prep": "bun run fetch-specs && bun run fix-spec && bun run generate:polymarket && bun run generate:webhooks && bun run generate:ws && bun run check-routes && bun run build", + "prep:staging": "STRUCT_ENV=staging bun run prep", "test": "bun test", "test:watch": "bun test --watch", "typecheck": "bun run tsc -p tsconfig.check.json", - "fetch-spec:polymarket": "curl -s -o openapi/polymarket.json https://api.struct.to/api-docs/openapi.json", "generate:polymarket": "openapi-typescript openapi/polymarket.json -o src/generated/polymarket.ts", - "fetch-spec:webhooks": "curl -s -o openapi/webhooks.json https://api.struct.to/webhookopenapi.json", - "generate:webhooks": "openapi-typescript openapi/webhooks.json -o src/generated/webhooks.ts" + "generate:webhooks": "openapi-typescript openapi/webhooks.json -o src/generated/webhooks.ts", + "generate:ws": "bun run scripts/generate-ws-types.ts", + "link": "bun link", + "pack": "bun run build && bun pm pack" }, "devDependencies": { "@types/bun": "latest", diff --git a/scripts/check-routes.ts b/scripts/check-routes.ts index 6eebad4..36dffa9 100644 --- a/scripts/check-routes.ts +++ b/scripts/check-routes.ts @@ -3,6 +3,8 @@ import { join } from "node:path"; const NAMESPACES_DIR = join(import.meta.dirname, "../src/namespaces"); const TYPES_FILE = join(import.meta.dirname, "../src/types/index.ts"); +const WS_TYPES_FILE = join(import.meta.dirname, "../src/types/ws.ts"); +const WS_ALERTS_FILE = join(import.meta.dirname, "../src/ws-alerts.ts"); interface SpecConfig { specPath: string; @@ -112,17 +114,56 @@ async function getSpecSchemas(jsonSpecPath: string): Promise { async function getExportedSchemas(typesContent: string): Promise> { const exported = new Set(); - for (const m of typesContent.matchAll(/Schemas\["(\w+)"\]/g)) exported.add(m[1]); - for (const m of typesContent.matchAll(/WebhookSchemas\["(\w+)"\]/g)) exported.add(m[1]); + for (const m of typesContent.matchAll(/(?:\w+)?Schemas\["(\w+)"\]/g)) exported.add(m[1]); + for (const m of typesContent.matchAll(/components\["schemas"\]\["(\w+)"\]/g)) exported.add(m[1]); for (const m of typesContent.matchAll(/export type (\w+)\s*=/g)) exported.add(m[1]); for (const m of typesContent.matchAll(/export interface (\w+)/g)) exported.add(m[1]); return exported; } +async function getWsSpecRooms(jsonSpecPath: string): Promise { + const spec = JSON.parse(await readFile(jsonSpecPath, "utf-8")); + return Object.keys(spec.channels ?? {}); +} + +function extractInterfaceKeys(content: string, interfaceName: string): Set { + const keys = new Set(); + const re = new RegExp(`interface\\s+${interfaceName}\\s*\\{([\\s\\S]*?)\\n\\}`); + const match = content.match(re); + if (!match) return keys; + const body = match[1]; + const keyRegex = /^\s*(\w+)\s*:/gm; + let km: RegExpExecArray | null; + while ((km = keyRegex.exec(body)) !== null) { + keys.add(km[1]); + } + return keys; +} + +async function getSdkStreamingRooms(): Promise> { + const content = await readFile(WS_TYPES_FILE, "utf-8"); + return extractInterfaceKeys(content, "WsSubscriptionMap"); +} + +async function getSdkAlertEvents(): Promise> { + try { + const alertsContent = await readFile(WS_ALERTS_FILE, "utf-8"); + if (!/class\s+StructAlertsWebSocket\b/.test(alertsContent)) return new Set(); + } catch { + return new Set(); + } + const generated = await readFile(join(import.meta.dirname, "../src/generated/ws-alerts.ts"), "utf-8"); + return extractInterfaceKeys(generated, "WsAlertSubscribeMap"); +} + let hasErrors = false; const typesContent = await readFile(TYPES_FILE, "utf-8"); -const exportedSchemas = await getExportedSchemas(typesContent); +const wsTypesContent = await readFile(join(import.meta.dirname, "../src/types/ws.ts"), "utf-8"); +const wsGeneratedContent = await readFile(join(import.meta.dirname, "../src/generated/ws.ts"), "utf-8"); +const wsAlertsGeneratedContent = await readFile(join(import.meta.dirname, "../src/generated/ws-alerts.ts"), "utf-8"); +const combinedTypesContent = [typesContent, wsTypesContent, wsGeneratedContent, wsAlertsGeneratedContent].join("\n"); +const exportedSchemas = await getExportedSchemas(combinedTypesContent); for (const config of specs) { const specName = config.venuePrefix ?? "platform"; @@ -176,4 +217,71 @@ for (const config of specs) { } } +const wsJsonPath = join(import.meta.dirname, "../openapi/ws.json"); +const wsAlertsJsonPath = join(import.meta.dirname, "../openapi/ws-alerts.json"); + +interface WsCheckConfig { + label: string; + specRooms: string[]; + sdkRooms: Set; +} + +const streamingSpecRooms = await getWsSpecRooms(wsJsonPath); +const alertsSpecChannels = await getWsSpecRooms(wsAlertsJsonPath); +const alertsSpecEvents = alertsSpecChannels + .filter((c) => c.startsWith("ws_alerts.")) + .map((c) => c.slice("ws_alerts.".length)); + +const wsChecks: WsCheckConfig[] = [ + { label: "ws", specRooms: streamingSpecRooms, sdkRooms: await getSdkStreamingRooms() }, + { label: "ws-alerts", specRooms: alertsSpecEvents, sdkRooms: await getSdkAlertEvents() }, +]; + +for (const check of wsChecks) { + const phantom = [...check.sdkRooms].filter((r) => !check.specRooms.includes(r)); + const missing = check.specRooms.filter((r) => !check.sdkRooms.has(r)); + + if (phantom.length > 0) { + hasErrors = true; + console.error(`\x1b[31m✗ [${check.label}] Phantom rooms (SDK rooms not in WS OpenAPI spec):\x1b[0m\n`); + for (const r of phantom) console.error(` ${r}`); + console.error(); + } + + if (missing.length > 0) { + hasErrors = true; + console.error(`\x1b[31m✗ [${check.label}] Unimplemented rooms (WS OpenAPI spec rooms missing from SDK):\x1b[0m\n`); + for (const r of missing) console.error(` ${r}`); + console.error(); + } + + if (phantom.length === 0 && missing.length === 0) { + console.log(`\x1b[32m✓ [${check.label}] All SDK rooms match the WS OpenAPI spec.\x1b[0m`); + } +} + +const wsJsonPaths = [wsJsonPath, wsAlertsJsonPath]; +const wsSpecSchemasList = (await Promise.all(wsJsonPaths.map(getSpecSchemas))).flat(); +const wsSpecSchemas = [...new Set(wsSpecSchemasList)]; +const missingWsSchemas = wsSpecSchemas.filter((s) => !exportedSchemas.has(s)); + +if (missingWsSchemas.length > 0) { + hasErrors = true; + console.error(`\x1b[31m✗ [ws] Missing schema exports:\x1b[0m\n`); + for (const schema of missingWsSchemas) { + console.error(` WsSchemas["${schema}"]`); + } + console.error(); +} else { + console.log(`\x1b[32m✓ [ws] All WS schemas exported.\x1b[0m`); +} + +const specSourcePath = join(import.meta.dirname, "../openapi/.spec-source.json"); +try { + const specSource = JSON.parse(await readFile(specSourcePath, "utf-8")); + if (specSource.env === "staging") { + console.warn(`\n\x1b[33m⚠ Specs were fetched from staging-api.struct.to. Run 'bun run prep' before merging.\x1b[0m\n`); + } +} catch {} + process.exit(hasErrors ? 1 : 0); diff --git a/scripts/fetch-specs.ts b/scripts/fetch-specs.ts new file mode 100644 index 0000000..20c38b3 --- /dev/null +++ b/scripts/fetch-specs.ts @@ -0,0 +1,59 @@ +import { writeFile } from "node:fs/promises"; +import { join } from "node:path"; + +type Env = "production" | "staging"; + +const HOSTS: Record = { + production: "https://api.struct.to", + staging: "https://staging-api.struct.to", +}; + +const SPECS = [ + { name: "polymarket", path: "/api-docs/openapi.json", output: "openapi/polymarket.json" }, + { name: "webhooks", path: "/webhookopenapi.json", output: "openapi/webhooks.json" }, + { name: "ws", path: "/asyncapi.json", output: "openapi/ws.json" }, + { name: "ws-alerts", path: "/asyncapi-alerts.json", output: "openapi/ws-alerts.json" }, +] as const; + +const raw = (Bun.env.STRUCT_ENV ?? "production").toLowerCase(); +if (raw !== "production" && raw !== "staging") { + console.error(`Invalid STRUCT_ENV: "${raw}". Must be "production" or "staging".`); + process.exit(1); +} +const env = raw as Env; +const host = HOSTS[env]; + +const root = join(import.meta.dirname, ".."); + +let failed = false; +for (const spec of SPECS) { + const url = `${host}${spec.path}`; + try { + const res = await fetch(url, { + headers: { "Accept-Encoding": "gzip, deflate, br" }, + }); + if (!res.ok) { + console.error(`✗ ${spec.name}: HTTP ${res.status} from ${url}`); + failed = true; + continue; + } + const body = await res.text(); + JSON.parse(body); + await writeFile(join(root, spec.output), body); + console.log(`✓ ${spec.name}`); + } catch (err) { + console.error(`✗ ${spec.name}: ${err instanceof Error ? err.message : err}`); + failed = true; + } +} + +if (failed) { + process.exit(1); +} + +await writeFile( + join(root, "openapi/.spec-source.json"), + JSON.stringify({ env, fetchedAt: new Date().toISOString() }, null, 2) + "\n", +); + +console.log(`\nFetched all specs from ${env} (${host})`); diff --git a/scripts/generate-ws-types.ts b/scripts/generate-ws-types.ts new file mode 100644 index 0000000..af8712e --- /dev/null +++ b/scripts/generate-ws-types.ts @@ -0,0 +1,152 @@ +import openapiTS, { astToString } from "openapi-typescript"; +import { readFile, writeFile } from "node:fs/promises"; +import { join } from "node:path"; + +const root = join(import.meta.dirname, ".."); + +interface SpecConfig { + name: string; + inputPath: string; + outputPath: string; + withAlertDiscriminators?: boolean; +} + +const SPECS: SpecConfig[] = [ + { + name: "ws", + inputPath: join(root, "openapi/ws.json"), + outputPath: join(root, "src/generated/ws.ts"), + }, + { + name: "ws-alerts", + inputPath: join(root, "openapi/ws-alerts.json"), + outputPath: join(root, "src/generated/ws-alerts.ts"), + withAlertDiscriminators: true, + }, +]; + +const ALERT_DISCRIMINATED_SCHEMAS = [ + "WsAlertSubscribeMessage", + "WsAlertUnsubscribeMessage", + "WsAlertEventPayload", +] as const; + +function stripOptionalDefaults(schemas: Record>): void { + for (const schema of Object.values(schemas)) { + const required = new Set((schema.required as string[]) ?? []); + const properties = schema.properties as Record> | undefined; + if (!properties) continue; + for (const [propName, prop] of Object.entries(properties)) { + if ("default" in prop && !required.has(propName)) { + delete prop.default; + } + } + } +} + +function buildAlertDiscriminatorMappings(schemas: Record>): void { + for (const parentName of ALERT_DISCRIMINATED_SCHEMAS) { + const parent = schemas[parentName] as + | { oneOf?: Array<{ $ref?: string }>; discriminator?: unknown } + | undefined; + if (!parent?.oneOf || parent.discriminator === undefined) continue; + + const propertyName = + typeof parent.discriminator === "string" + ? parent.discriminator + : (parent.discriminator as { propertyName?: string }).propertyName ?? "event"; + + const mapping: Record = {}; + for (const variant of parent.oneOf) { + const ref = variant.$ref; + if (!ref) continue; + const schemaName = ref.split("/").pop()!; + const variantSchema = schemas[schemaName]; + + let eventValue: string | undefined; + const allOf = variantSchema?.allOf as Array> | undefined; + if (allOf) { + for (const part of allOf) { + const props = part.properties as Record | undefined; + eventValue ??= props?.[propertyName]?.enum?.[0]; + } + } + const props = variantSchema?.properties as Record | undefined; + eventValue ??= props?.[propertyName]?.enum?.[0]; + + if (eventValue) { + mapping[eventValue] = ref; + } + } + + (parent as Record).discriminator = { propertyName, mapping }; + } +} + +function buildAlertMapOutput(schemas: Record>): string { + const subscribeEntries: string[] = []; + const eventDataEntries: string[] = []; + + const subscribeMapping = (schemas.WsAlertSubscribeMessage as { discriminator?: { mapping?: Record } } | undefined) + ?.discriminator?.mapping; + const eventMapping = (schemas.WsAlertEventPayload as { discriminator?: { mapping?: Record } } | undefined) + ?.discriminator?.mapping; + + if (subscribeMapping) { + for (const [eventName, ref] of Object.entries(subscribeMapping)) { + const schemaName = ref.split("/").pop()!; + subscribeEntries.push(`\t${eventName}: components["schemas"]["${schemaName}"];`); + } + } + + if (eventMapping) { + for (const [eventName, ref] of Object.entries(eventMapping)) { + const schemaName = ref.split("/").pop()!; + const eventSchema = schemas[schemaName] as { properties?: { data?: { $ref?: string } } } | undefined; + const dataRef = eventSchema?.properties?.data?.$ref; + const dataSchemaName = dataRef?.split("/").pop(); + if (dataSchemaName) { + eventDataEntries.push(`\t${eventName}: components["schemas"]["${dataSchemaName}"];`); + } + } + } + + if (subscribeEntries.length === 0) return ""; + + return ( + `\nexport interface WsAlertSubscribeMap {\n${subscribeEntries.join("\n")}\n}\n` + + `\nexport interface WsAlertEventDataMap {\n${eventDataEntries.join("\n")}\n}\n` + + `\nexport type WsAlertEventName = keyof WsAlertSubscribeMap;\n` + ); +} + +async function generateSpec(spec: SpecConfig): Promise { + const asyncapi = JSON.parse(await readFile(spec.inputPath, "utf-8")); + const schemas = asyncapi.components.schemas as Record>; + + stripOptionalDefaults(schemas); + + if (spec.withAlertDiscriminators) { + buildAlertDiscriminatorMappings(schemas); + } + + const ast = await openapiTS({ + openapi: "3.1.0", + info: asyncapi.info, + paths: {}, + components: { schemas }, + }); + + let output = astToString(ast); + + if (spec.withAlertDiscriminators) { + output += buildAlertMapOutput(schemas); + } + + await writeFile(spec.outputPath, output); + console.log(`✓ Generated ${spec.name} types`); +} + +for (const spec of SPECS) { + await generateSpec(spec); +} diff --git a/src/generated/polymarket.ts b/src/generated/polymarket.ts index 6541746..95e2f61 100644 --- a/src/generated/polymarket.ts +++ b/src/generated/polymarket.ts @@ -633,7 +633,7 @@ export interface paths { }; /** * Get tags - * @description Retrieve all available event tags/categories. Uses cursor-based pagination for efficient traversal. + * @description Retrieve all available event tags/categories. Supports both cursor-based and offset-based pagination. */ get: operations["get_tags"]; put?: never; @@ -893,13 +893,15 @@ export interface components { id: string; hash: string; /** Format: int64 */ - block: number; + block?: number | null; /** Format: int64 */ - confirmed_at: number; + confirmed_at?: number | null; /** Format: int64 */ - log_index: number; + received_at?: number | null; /** Format: int64 */ - block_index: number; + log_index?: number | null; + /** Format: int64 */ + block_index?: number | null; trader: components["schemas"]["TraderInfo"]; operator: string; approved: boolean; @@ -914,13 +916,15 @@ export interface components { id: string; hash: string; /** Format: int64 */ - block: number; + block?: number | null; + /** Format: int64 */ + confirmed_at?: number | null; /** Format: int64 */ - confirmed_at: number; + received_at?: number | null; /** Format: int64 */ - log_index: number; + log_index?: number | null; /** Format: int64 */ - block_index: number; + block_index?: number | null; oracle_contract: string; assertion_id: string; caller: string; @@ -936,13 +940,15 @@ export interface components { id: string; hash: string; /** Format: int64 */ - block: number; + block?: number | null; + /** Format: int64 */ + confirmed_at?: number | null; /** Format: int64 */ - confirmed_at: number; + received_at?: number | null; /** Format: int64 */ - log_index: number; + log_index?: number | null; /** Format: int64 */ - block_index: number; + block_index?: number | null; oracle_contract: string; assertion_id: string; domain_id: string; @@ -968,13 +974,15 @@ export interface components { id: string; hash: string; /** Format: int64 */ - block: number; + block?: number | null; /** Format: int64 */ - confirmed_at: number; + confirmed_at?: number | null; /** Format: int64 */ - log_index: number; + received_at?: number | null; /** Format: int64 */ - block_index: number; + log_index?: number | null; + /** Format: int64 */ + block_index?: number | null; oracle_contract: string; assertion_id: string; bond_recipient: string; @@ -1053,14 +1061,16 @@ export interface components { id: string; hash: string; /** Format: int64 */ - block: number; + block?: number | null; + /** Format: int64 */ + confirmed_at?: number | null; /** Format: int64 */ - confirmed_at: number; + received_at?: number | null; /** Format: int64 */ - log_index: number; + log_index?: number | null; /** Format: int64 */ - block_index: number; - order_hash: string; + block_index?: number | null; + order_hash?: string | null; question?: string | null; image_url?: string | null; slug?: string | null; @@ -1138,13 +1148,15 @@ export interface components { id: string; hash: string; /** Format: int64 */ - block: number; + block?: number | null; + /** Format: int64 */ + confirmed_at?: number | null; /** Format: int64 */ - confirmed_at: number; + received_at?: number | null; /** Format: int64 */ - log_index: number; + log_index?: number | null; /** Format: int64 */ - block_index: number; + block_index?: number | null; oracle_contract: string; condition_id: string; oracle: string; @@ -1472,13 +1484,15 @@ export interface components { id: string; hash: string; /** Format: int64 */ - block: number; + block?: number | null; /** Format: int64 */ - confirmed_at: number; + confirmed_at?: number | null; /** Format: int64 */ - log_index: number; + received_at?: number | null; /** Format: int64 */ - block_index: number; + log_index?: number | null; + /** Format: int64 */ + block_index?: number | null; trader: components["schemas"]["TraderInfo"]; condition_id?: string | null; question?: string | null; @@ -1498,13 +1512,15 @@ export interface components { id: string; hash: string; /** Format: int64 */ - block: number; + block?: number | null; + /** Format: int64 */ + confirmed_at?: number | null; /** Format: int64 */ - confirmed_at: number; + received_at?: number | null; /** Format: int64 */ - log_index: number; + log_index?: number | null; /** Format: int64 */ - block_index: number; + block_index?: number | null; oracle_contract: string; condition_id: string; proposed_outcome?: string | null; @@ -1518,16 +1534,18 @@ export interface components { id: string; hash: string; /** Format: int64 */ - block: number; + block?: number | null; + /** Format: int64 */ + confirmed_at?: number | null; /** Format: int64 */ - confirmed_at: number; + received_at?: number | null; /** Format: int64 */ - log_index: number; + log_index?: number | null; /** Format: int64 */ - block_index: number; - order_hash: string; + block_index?: number | null; + order_hash?: string | null; trader: components["schemas"]["TraderInfo"]; - taker: string; + taker?: string | null; side: string; condition_id?: string | null; position_id: string; @@ -1547,11 +1565,11 @@ export interface components { /** Format: double */ probability?: number | null; /** Format: double */ - fee: number; + fee?: number | null; /** Format: double */ - fee_shares: number; + fee_shares?: number | null; /** Format: double */ - fee_pct: number; + fee_pct?: number | null; exchange: components["schemas"]["PolymarketExchange"]; }; OrderbookHistoryRow: { @@ -1908,6 +1926,8 @@ export interface components { * @description Outcome index (0 = Yes, 1 = No for binary) */ outcome_index: number; + /** @description Outcome name (e.g. "Yes", "No") — enriched from market metadata */ + outcome?: string | null; /** @description Amount of shares created/burned/redeemed for this position */ amount: string; }; @@ -2002,13 +2022,15 @@ export interface components { id: string; hash: string; /** Format: int64 */ - block: number; + block?: number | null; + /** Format: int64 */ + confirmed_at?: number | null; /** Format: int64 */ - confirmed_at: number; + received_at?: number | null; /** Format: int64 */ - log_index: number; + log_index?: number | null; /** Format: int64 */ - block_index: number; + block_index?: number | null; trader: components["schemas"]["TraderInfo"]; /** @description NegRisk umbrella market ID */ market_id: string; @@ -2059,13 +2081,15 @@ export interface components { id: string; hash: string; /** Format: int64 */ - block: number; + block?: number | null; + /** Format: int64 */ + confirmed_at?: number | null; /** Format: int64 */ - confirmed_at: number; + received_at?: number | null; /** Format: int64 */ - log_index: number; + log_index?: number | null; /** Format: int64 */ - block_index: number; + block_index?: number | null; oracle_contract: string; condition_id: string; proposed_outcome?: string | null; @@ -2079,13 +2103,15 @@ export interface components { id: string; hash: string; /** Format: int64 */ - block: number; + block?: number | null; /** Format: int64 */ - confirmed_at: number; + confirmed_at?: number | null; /** Format: int64 */ - log_index: number; + received_at?: number | null; /** Format: int64 */ - block_index: number; + log_index?: number | null; + /** Format: int64 */ + block_index?: number | null; oracle_contract: string; condition_id: string; question?: string | null; @@ -2098,13 +2124,15 @@ export interface components { id: string; hash: string; /** Format: int64 */ - block: number; + block?: number | null; + /** Format: int64 */ + confirmed_at?: number | null; /** Format: int64 */ - confirmed_at: number; + received_at?: number | null; /** Format: int64 */ - log_index: number; + log_index?: number | null; /** Format: int64 */ - block_index: number; + block_index?: number | null; oracle_contract: string; condition_id: string; creator: string; @@ -2121,13 +2149,15 @@ export interface components { id: string; hash: string; /** Format: int64 */ - block: number; + block?: number | null; + /** Format: int64 */ + confirmed_at?: number | null; /** Format: int64 */ - confirmed_at: number; + received_at?: number | null; /** Format: int64 */ - log_index: number; + log_index?: number | null; /** Format: int64 */ - block_index: number; + block_index?: number | null; oracle_contract: string; condition_id: string; question?: string | null; @@ -2140,13 +2170,15 @@ export interface components { id: string; hash: string; /** Format: int64 */ - block: number; + block?: number | null; /** Format: int64 */ - confirmed_at: number; + confirmed_at?: number | null; /** Format: int64 */ - log_index: number; + received_at?: number | null; /** Format: int64 */ - block_index: number; + log_index?: number | null; + /** Format: int64 */ + block_index?: number | null; oracle_contract: string; condition_id: string; question?: string | null; @@ -2159,13 +2191,15 @@ export interface components { id: string; hash: string; /** Format: int64 */ - block: number; + block?: number | null; + /** Format: int64 */ + confirmed_at?: number | null; /** Format: int64 */ - confirmed_at: number; + received_at?: number | null; /** Format: int64 */ - log_index: number; + log_index?: number | null; /** Format: int64 */ - block_index: number; + block_index?: number | null; oracle_contract: string; condition_id: string; /** Format: int64 */ @@ -2181,13 +2215,15 @@ export interface components { id: string; hash: string; /** Format: int64 */ - block: number; + block?: number | null; + /** Format: int64 */ + confirmed_at?: number | null; /** Format: int64 */ - confirmed_at: number; + received_at?: number | null; /** Format: int64 */ - log_index: number; + log_index?: number | null; /** Format: int64 */ - block_index: number; + block_index?: number | null; oracle_contract: string; condition_id: string; question?: string | null; @@ -2200,13 +2236,15 @@ export interface components { id: string; hash: string; /** Format: int64 */ - block: number; + block?: number | null; /** Format: int64 */ - confirmed_at: number; + confirmed_at?: number | null; /** Format: int64 */ - log_index: number; + received_at?: number | null; /** Format: int64 */ - block_index: number; + log_index?: number | null; + /** Format: int64 */ + block_index?: number | null; oracle_contract: string; condition_id: string; question?: string | null; @@ -2219,13 +2257,15 @@ export interface components { id: string; hash: string; /** Format: int64 */ - block: number; + block?: number | null; + /** Format: int64 */ + confirmed_at?: number | null; /** Format: int64 */ - confirmed_at: number; + received_at?: number | null; /** Format: int64 */ - log_index: number; + log_index?: number | null; /** Format: int64 */ - block_index: number; + block_index?: number | null; trader: components["schemas"]["TraderInfo"]; condition_id?: string | null; outcome?: string | null; @@ -2248,13 +2288,15 @@ export interface components { id: string; hash: string; /** Format: int64 */ - block: number; + block?: number | null; + /** Format: int64 */ + confirmed_at?: number | null; /** Format: int64 */ - confirmed_at: number; + received_at?: number | null; /** Format: int64 */ - log_index: number; + log_index?: number | null; /** Format: int64 */ - block_index: number; + block_index?: number | null; condition_id: string; token0: string; token1: string; @@ -2311,13 +2353,15 @@ export interface components { id: string; hash: string; /** Format: int64 */ - block: number; + block?: number | null; /** Format: int64 */ - confirmed_at: number; + confirmed_at?: number | null; /** Format: int64 */ - log_index: number; + received_at?: number | null; /** Format: int64 */ - block_index: number; + log_index?: number | null; + /** Format: int64 */ + block_index?: number | null; trader: components["schemas"]["TraderInfo"]; condition_id?: string | null; question?: string | null; @@ -4148,6 +4192,8 @@ export interface operations { query?: { /** @description Results limit (default: 10, max: 250) */ limit?: number; + /** @description Offset-based pagination (number of results to skip). Takes precedence over pagination_key. */ + offset?: number; /** @description Cursor-based pagination key */ pagination_key?: string; }; diff --git a/src/generated/ws-alerts.ts b/src/generated/ws-alerts.ts new file mode 100644 index 0000000..718f963 --- /dev/null +++ b/src/generated/ws-alerts.ts @@ -0,0 +1,2602 @@ +export type paths = Record; +export type webhooks = Record; +export interface components { + schemas: { + /** @description Outer envelope for every webhook HTTP POST delivery. The `data` field contains the event-specific payload. Delivery headers sent with every POST: `X-Webhook-ID` (subscription UUID), `X-Delivery-ID` (this attempt's UUID), `X-Event-Type` (event name string, e.g. `trader_first_trade`), `X-Attempt` (attempt number, 1-indexed). When the webhook has a secret configured, `X-Webhook-Signature: sha256=` is also included — compute HMAC-SHA256 over the raw request body using your secret to verify. */ + WebhookDeliveryEnvelope: { + /** + * Format: uuid + * @description UUID of this specific delivery attempt (matches X-Delivery-ID header) + */ + id: string; + /** @description Event name (e.g. `trader_first_trade`). On test deliveries the suffix `_test` is appended. */ + event: string; + /** @description Event-specific payload — schema varies by event type; see the individual callback definitions */ + data: Record; + /** + * Format: int64 + * @description Unix timestamp in milliseconds when this delivery was created + */ + timestamp: number; + /** + * Format: uuid + * @description UUID of the webhook subscription that fired (matches X-Webhook-ID header) + */ + webhook_id: string; + /** @description Delivery attempt number. 1 = first attempt; increments on each retry. */ + attempt: number; + }; + /** @description Payload delivered when a tracked trader executes their first-ever trade on Polymarket */ + FirstTradePayload: { + /** @description Limit-order maker wallet address (lowercase) */ + trader: string; + /** @description Order filler wallet address (lowercase) */ + taker: string; + /** @description ERC-1155 outcome token ID */ + position_id: string; + /** @description Parent market condition ID (0x-prefixed hex) */ + condition_id?: string | null; + /** @description Outcome name (e.g. "Yes", "No") */ + outcome?: string | null; + /** @description Outcome index: 0 = Yes/Up, 1 = No */ + outcome_index?: number | null; + /** @description Market question text */ + question?: string | null; + /** @description Market slug */ + market_slug?: string | null; + /** @description Parent event slug */ + event_slug?: string | null; + /** @description Unique trade identifier */ + trade_id: string; + /** @description Transaction hash */ + hash: string; + /** + * Format: int64 + * @description Block number + */ + block: number; + /** + * Format: int64 + * @description Block confirmation timestamp (Unix seconds) + */ + confirmed_at: number; + /** @description USD size of the trade (6 decimal places) */ + amount_usd: number; + /** @description Outcome shares traded (6 decimal places) */ + shares_amount: number; + /** @description Fee paid in USD (6 decimal places) */ + fee: number; + /** + * @description Trade direction + * @enum {string} + */ + side: "Buy" | "Sell"; + /** @description Outcome token price (0.0–1.0) */ + price: number; + /** @description Exchange identifier */ + exchange: string; + /** @description Trade type identifier */ + trade_type: string; + }; + /** @description Payload delivered when a trader places their first trade in a specific market (fires once per trader+market pair) */ + NewMarketPayload: { + /** @description Limit-order maker wallet address (lowercase) */ + trader: string; + /** @description Order filler wallet address (lowercase) */ + taker: string; + /** @description ERC-1155 outcome token ID */ + position_id: string; + /** @description Parent market condition ID */ + condition_id?: string | null; + /** @description Outcome name (e.g. "Yes", "No") */ + outcome?: string | null; + /** @description Outcome index: 0 = Yes/Up, 1 = No */ + outcome_index?: number | null; + /** @description Market question text */ + question?: string | null; + /** @description Market slug */ + market_slug?: string | null; + /** @description Parent event slug */ + event_slug?: string | null; + /** @description Unique trade identifier */ + trade_id: string; + /** @description Transaction hash */ + hash: string; + /** + * Format: int64 + * @description Block number + */ + block: number; + /** + * Format: int64 + * @description Block confirmation timestamp (Unix seconds) + */ + confirmed_at: number; + /** @description USD size of the trade (6 decimal places) */ + amount_usd: number; + /** @description Outcome shares traded (6 decimal places) */ + shares_amount: number; + /** @description Fee paid in USD (6 decimal places) */ + fee: number; + /** + * @description Trade direction + * @enum {string} + */ + side: "Buy" | "Sell"; + /** @description Outcome token price (0.0–1.0) */ + price: number; + /** @description Implied probability (0.0–1.0); null when outcome is unknown */ + probability?: number | null; + /** @description Exchange identifier */ + exchange: string; + /** @description Trade type identifier */ + trade_type: string; + }; + /** @description Payload delivered when a trade exceeds the configured size and probability thresholds */ + WhaleTradePayload: { + /** @description Limit-order maker wallet address (lowercase) */ + trader: string; + /** @description Order filler wallet address (lowercase) */ + taker: string; + /** @description ERC-1155 outcome token ID */ + position_id: string; + /** @description Parent market condition ID */ + condition_id?: string | null; + /** @description Outcome name (e.g. "Yes", "No") */ + outcome?: string | null; + /** @description Outcome index: 0 = Yes/Up, 1 = No */ + outcome_index?: number | null; + /** @description Market question text */ + question?: string | null; + /** @description Market slug */ + market_slug?: string | null; + /** @description Parent event slug */ + event_slug?: string | null; + /** @description Unique trade identifier */ + trade_id: string; + /** @description Transaction hash */ + hash: string; + /** + * Format: int64 + * @description Block number + */ + block: number; + /** + * Format: int64 + * @description Block confirmation timestamp (Unix seconds) + */ + confirmed_at: number; + /** @description USD size of the trade (6 decimal places) */ + amount_usd: number; + /** @description Outcome shares traded (6 decimal places) */ + shares_amount: number; + /** @description Fee paid in USD (6 decimal places) */ + fee: number; + /** + * @description Trade direction + * @enum {string} + */ + side: "Buy" | "Sell"; + /** @description Outcome token price (0.0–1.0) */ + price: number; + /** @description Implied probability (0.0–1.0); null when outcome is unknown */ + probability?: number | null; + /** @description Exchange identifier */ + exchange: string; + /** @description Trade type identifier */ + trade_type: string; + }; + /** @description Payload delivered on every order-filled trade */ + NewTradePayload: { + /** @description Limit-order maker wallet address (lowercase) */ + trader: string; + /** @description Order filler wallet address (lowercase) */ + taker: string; + /** @description ERC-1155 outcome token ID */ + position_id: string; + /** @description Parent market condition ID */ + condition_id?: string | null; + /** @description Outcome name (e.g. "Yes", "No") */ + outcome?: string | null; + /** @description Outcome index: 0 = Yes/Up, 1 = No */ + outcome_index?: number | null; + /** @description Market question text */ + question?: string | null; + /** @description Market slug */ + market_slug?: string | null; + /** @description Parent event slug */ + event_slug?: string | null; + /** @description Unique trade identifier */ + trade_id: string; + /** @description Transaction hash */ + hash: string; + /** + * Format: int64 + * @description Block number + */ + block: number; + /** + * Format: int64 + * @description Block confirmation timestamp (Unix seconds) + */ + confirmed_at: number; + /** @description USD size of the trade (6 decimal places) */ + amount_usd: number; + /** @description Outcome shares traded (6 decimal places) */ + shares_amount: number; + /** @description Fee paid in USD (6 decimal places) */ + fee: number; + /** + * @description Trade direction + * @enum {string} + */ + side: "Buy" | "Sell"; + /** @description Outcome token price (0.0–1.0) */ + price: number; + /** @description Implied probability (0.0–1.0); null when outcome is unknown */ + probability?: number | null; + /** @description Exchange identifier */ + exchange: string; + /** @description Trade type identifier */ + trade_type: string; + }; + /** @description Payload delivered when a trader's global PnL (across all markets) crosses a configured threshold */ + GlobalPnlPayload: { + /** @description Trader wallet address (lowercase) */ + trader?: string | null; + /** + * @description PnL aggregation window + * @enum {string} + */ + timeframe: "1d" | "7d" | "30d" | "lifetime"; + /** @description Realized PnL in USD (positive = profit, negative = loss) */ + realized_pnl_usd?: number | null; + /** + * Format: int64 + * @description Number of distinct events traded + */ + events_traded?: number | null; + /** + * Format: int64 + * @description Number of distinct markets traded + */ + markets_traded?: number | null; + /** + * Format: int64 + * @description Total buy transactions + */ + total_buys?: number | null; + /** + * Format: int64 + * @description Total sell transactions + */ + total_sells?: number | null; + /** + * Format: int64 + * @description Total redemption transactions + */ + total_redemptions?: number | null; + /** + * Format: int64 + * @description Total merge transactions + */ + total_merges?: number | null; + /** @description Total USD volume (buys + sells + redemptions + merges) */ + total_volume_usd?: number | null; + /** @description Total buy volume in USD */ + buy_volume_usd?: number | null; + /** @description Total sell volume in USD */ + sell_volume_usd?: number | null; + /** @description Total redemption volume in USD */ + redemption_volume_usd?: number | null; + /** @description Total merge volume in USD */ + merge_volume_usd?: number | null; + /** + * Format: int64 + * @description Number of markets where trader realised a profit + */ + markets_won?: number | null; + /** + * Format: int64 + * @description Number of markets where trader realised a loss + */ + markets_lost?: number | null; + /** @description Market win rate as a percentage (0.0–100.0) */ + market_win_rate_pct?: number | null; + /** @description Average PnL per market in USD */ + avg_pnl_per_market?: number | null; + /** @description Average PnL per trade in USD */ + avg_pnl_per_trade?: number | null; + /** @description Average hold time across all positions (seconds) */ + avg_hold_time_seconds?: number | null; + /** @description Total fees paid in USD */ + total_fees?: number | null; + /** @description Best single-trade PnL in USD */ + best_trade_pnl_usd?: number | null; + /** @description Condition ID of the best trade */ + best_trade_condition_id?: string | null; + /** + * Format: int64 + * @description Timestamp of the first trade (Unix seconds) + */ + first_trade_at?: number | null; + /** + * Format: int64 + * @description Timestamp of the most recent trade (Unix seconds) + */ + last_trade_at?: number | null; + }; + /** @description Payload delivered when a trader's per-market PnL crosses a configured threshold */ + MarketPnlPayload: { + /** @description Trader wallet address (lowercase) */ + trader?: string | null; + /** @description Market condition ID */ + condition_id?: string | null; + /** @description Parent event slug */ + event_slug?: string | null; + /** + * @description PnL aggregation window + * @enum {string} + */ + timeframe: "1d" | "7d" | "30d" | "lifetime"; + /** + * Format: int64 + * @description Number of distinct outcomes traded in this market + */ + outcomes_traded?: number | null; + /** Format: int64 */ + total_buys?: number | null; + /** Format: int64 */ + total_sells?: number | null; + /** Format: int64 */ + total_redemptions?: number | null; + /** Format: int64 */ + total_merges?: number | null; + /** @description Total buy volume in USD */ + buy_usd?: number | null; + /** @description Total sell volume in USD */ + sell_usd?: number | null; + /** @description Total redemption volume in USD */ + redemption_usd?: number | null; + /** @description Total merge volume in USD */ + merge_usd?: number | null; + /** @description Realized PnL in USD for this market */ + realized_pnl_usd?: number | null; + /** + * Format: int64 + * @description Number of outcomes with positive PnL + */ + winning_outcomes?: number | null; + /** @description Total fees paid in USD for this market */ + total_fees?: number | null; + /** + * Format: int64 + * @description Timestamp of first trade in market (Unix seconds) + */ + first_trade_at?: number | null; + /** + * Format: int64 + * @description Timestamp of most recent trade in market (Unix seconds) + */ + last_trade_at?: number | null; + }; + /** @description Payload delivered when a trader's per-event PnL crosses a configured threshold */ + EventPnlPayload: { + /** @description Trader wallet address (lowercase) */ + trader?: string | null; + /** @description Event slug */ + event_slug?: string | null; + /** + * @description PnL aggregation window + * @enum {string} + */ + timeframe: "1d" | "7d" | "30d" | "lifetime"; + /** + * Format: int64 + * @description Number of distinct markets traded in this event + */ + markets_traded?: number | null; + /** Format: int64 */ + outcomes_traded?: number | null; + /** Format: int64 */ + total_buys?: number | null; + /** Format: int64 */ + total_sells?: number | null; + /** Format: int64 */ + total_redemptions?: number | null; + /** Format: int64 */ + total_merges?: number | null; + /** @description Total volume in USD */ + total_volume_usd?: number | null; + buy_usd?: number | null; + sell_usd?: number | null; + redemption_usd?: number | null; + merge_usd?: number | null; + /** @description Realized PnL in USD for this event */ + realized_pnl_usd?: number | null; + /** Format: int64 */ + winning_markets?: number | null; + /** Format: int64 */ + losing_markets?: number | null; + total_fees?: number | null; + /** + * Format: int64 + * @description Unix seconds + */ + first_trade_at?: number | null; + /** + * Format: int64 + * @description Unix seconds + */ + last_trade_at?: number | null; + }; + /** @description Payload delivered when a market's volume or transaction metrics cross a configured threshold */ + ConditionMetricsPayload: { + /** @description Market condition ID */ + condition_id?: string | null; + /** @description Aggregation window (e.g. "1h", "24h") */ + timeframe?: string | null; + /** @description Total trading volume in USD for this timeframe */ + volume_usd?: number | null; + /** @description Total fees collected in USD */ + fees?: number | null; + /** + * Format: int64 + * @description Total number of transactions + */ + txns?: number | null; + /** + * Format: int64 + * @description Number of unique traders + */ + unique_traders?: number | null; + }; + /** @description Payload delivered when an event's aggregated volume or transaction metrics cross a configured threshold */ + EventMetricsPayload: { + /** @description Event slug */ + event_slug?: string | null; + /** @description Aggregation window (e.g. "1h", "24h") */ + timeframe?: string | null; + /** @description Total aggregated volume across all markets in the event (USD) */ + volume_usd?: number | null; + /** @description Total fees collected in USD */ + fees?: number | null; + /** + * Format: int64 + * @description Total number of transactions + */ + txns?: number | null; + /** + * Format: int64 + * @description Number of unique traders + */ + unique_traders?: number | null; + }; + /** @description Payload delivered when a position's volume or transaction metrics cross a configured threshold */ + PositionMetricsPayload: { + /** @description ERC-1155 outcome token ID */ + position_id?: string | null; + /** @description Outcome name (e.g. "Yes", "No") */ + outcome?: string | null; + /** + * Format: int16 + * @description Outcome index + */ + outcome_index?: number | null; + /** @description Aggregation window (e.g. "1h", "24h") */ + timeframe?: string | null; + /** @description Total trading volume in USD */ + volume_usd?: number | null; + /** @description Buy volume in USD */ + buy_volume_usd?: number | null; + /** @description Sell volume in USD */ + sell_volume_usd?: number | null; + /** @description Total fees in USD */ + fees?: number | null; + /** Format: int64 */ + txns?: number | null; + /** Format: int64 */ + buys?: number | null; + /** Format: int64 */ + sells?: number | null; + /** Format: int64 */ + unique_traders?: number | null; + price_open?: number | null; + price_close?: number | null; + price_high?: number | null; + price_low?: number | null; + probability_open?: number | null; + probability_close?: number | null; + probability_high?: number | null; + probability_low?: number | null; + }; + /** @description Payload delivered when a market's trading volume crosses a USD milestone in the specified timeframe */ + VolumeMilestonePayload: { + /** @description Market condition ID */ + condition_id: string; + /** @description Aggregation window that crossed the milestone (e.g. "1h", "24h") */ + timeframe: string; + /** @description The USD milestone amount that was crossed */ + milestone_usd: number; + /** @description Current volume at time of trigger (USD) */ + current_volume_usd: number; + /** @description Total fees in USD for this timeframe */ + fees: number; + /** + * Format: int64 + * @description Total transactions in this timeframe + */ + txns: number; + }; + /** @description Payload delivered when an event's aggregated trading volume crosses a USD milestone */ + EventVolumeMilestonePayload: { + /** @description Event slug */ + event_slug: string; + /** @description Aggregation window (e.g. "1h", "24h") */ + timeframe: string; + /** @description The USD milestone amount that was crossed */ + milestone_usd: number; + /** @description Current aggregated event volume at time of trigger (USD) */ + current_volume_usd: number; + /** @description Total fees in USD for this timeframe */ + fees: number; + /** + * Format: int64 + * @description Total transactions in this timeframe + */ + txns: number; + }; + /** @description Payload delivered when a position's trading volume crosses a USD milestone */ + PositionVolumeMilestonePayload: { + /** @description Parent market condition ID */ + condition_id?: string | null; + /** @description ERC-1155 outcome token ID */ + position_id: string; + /** @description Outcome name (e.g. "Yes", "No") */ + outcome?: string | null; + /** + * Format: int16 + * @description Outcome index + */ + outcome_index?: number | null; + /** @description Aggregation window (e.g. "1h", "24h") */ + timeframe: string; + /** @description The USD milestone amount that was crossed */ + milestone_usd: number; + /** @description Current position volume at time of trigger (USD) */ + current_volume_usd: number; + /** @description Buy volume in USD for this timeframe */ + buy_volume_usd: number; + /** @description Sell volume in USD for this timeframe */ + sell_volume_usd: number; + /** @description Total fees in USD */ + fees: number; + /** Format: int64 */ + txns: number; + /** Format: int64 */ + buys: number; + /** Format: int64 */ + sells: number; + }; + ProbabilitySpikePayload: { + /** @description Outcome token ID */ + position_id: string; + /** @description Market condition ID */ + condition_id?: string | null; + /** @description Event slug */ + event_slug?: string | null; + /** @description Outcome name (e.g. "Yes", "No") */ + outcome?: string | null; + /** + * Format: int16 + * @description Outcome index + */ + outcome_index?: number | null; + /** @description Probability at the start of the observation window (baseline snapshot, 0.0–1.0) */ + previous_probability: number; + /** @description Current probability that triggered the spike (0.0–1.0) */ + current_probability: number; + /** + * @description `"up"` = probability rising, `"down"` = probability falling + * @enum {string} + */ + spike_direction: "up" | "down"; + /** @description Percentage move that triggered this notification. Positive = up, negative = down. */ + spike_pct: number; + }; + PriceSpikePayload: { + /** @description Outcome token ID */ + position_id: string; + /** @description Market condition ID */ + condition_id?: string | null; + /** @description Event slug */ + event_slug?: string | null; + /** @description Outcome name (e.g. "Yes", "No") */ + outcome?: string | null; + /** + * Format: int16 + * @description Outcome index + */ + outcome_index?: number | null; + /** @description Price at the start of the observation window (baseline snapshot, 0.0–1.0) */ + previous_price: number; + /** @description Current price that triggered the spike (0.0–1.0) */ + current_price: number; + /** + * @description `"up"` = price rising, `"down"` = price falling + * @enum {string} + */ + spike_direction: "up" | "down"; + /** @description Percentage move that triggered this notification. Positive = up, negative = down. */ + spike_pct: number; + }; + /** @description Payload delivered when a market's volume has spiked since the last snapshot */ + MarketVolumeSpikePayload: { + /** @description Market condition ID */ + condition_id: string; + /** @description Aggregation window (e.g. "1h", "24h") */ + timeframe: string; + /** @description Current volume at the time of the spike (USD) */ + current_volume_usd: number; + /** @description Volume at the snapshot baseline (USD) */ + snapshot_volume_usd: number; + /** @description New volume since the snapshot that triggered this notification (USD) */ + delta_volume_usd: number; + /** @description Volume growth as a percentage of the snapshot (e.g. 200.0 means volume tripled) */ + spike_pct: number; + /** + * Format: int64 + * @description Total transactions in this timeframe + */ + txns: number; + /** @description Total fees in USD for this timeframe */ + fees: number; + }; + /** @description Payload delivered when an event's aggregated volume has spiked since the last snapshot */ + EventVolumeSpikePayload: { + /** @description Event slug */ + event_slug: string; + /** @description Aggregation window (e.g. "1h", "24h") */ + timeframe: string; + /** @description Current aggregated event volume at time of the spike (USD) */ + current_volume_usd: number; + /** @description Volume at the snapshot baseline (USD) */ + snapshot_volume_usd: number; + /** @description New volume since the snapshot that triggered this notification (USD) */ + delta_volume_usd: number; + /** @description Volume growth as a percentage of the snapshot (e.g. 200.0 means volume tripled) */ + spike_pct: number; + /** Format: int64 */ + txns: number; + fees: number; + }; + /** @description Payload delivered when a position's volume has spiked since the last snapshot */ + PositionVolumeSpikePayload: { + /** @description ERC-1155 outcome token ID */ + position_id: string; + /** @description Parent market condition ID */ + condition_id: string; + /** @description Outcome name (e.g. "Yes", "No") */ + outcome?: string | null; + /** Format: int16 */ + outcome_index?: number | null; + /** @description Aggregation window (e.g. "1h", "24h") */ + timeframe: string; + /** @description Current position volume at the time of the spike (USD) */ + current_volume_usd: number; + /** @description Volume at the snapshot baseline (USD) */ + snapshot_volume_usd: number; + /** @description New volume since the snapshot that triggered this notification (USD) */ + delta_volume_usd: number; + /** @description Volume growth as a percentage of the snapshot (e.g. 200.0 means volume tripled) */ + spike_pct: number; + /** Format: int64 */ + txns: number; + fees: number; + }; + /** @description Payload delivered when a trade occurs at a near-certain-outcome price */ + CloseToBondPayload: { + /** @description Limit-order maker wallet address (lowercase) */ + trader: string; + /** @description Order filler wallet address (lowercase) */ + taker: string; + /** @description ERC-1155 outcome token ID */ + position_id: string; + /** @description Parent market condition ID */ + condition_id?: string | null; + /** @description Outcome name (e.g. "Yes", "No") */ + outcome?: string | null; + /** @description 0 = Yes/Up, 1 = No */ + outcome_index?: number | null; + question?: string | null; + market_slug?: string | null; + event_slug?: string | null; + trade_id: string; + /** @description Transaction hash */ + hash: string; + /** Format: int64 */ + block: number; + /** + * Format: int64 + * @description Unix seconds + */ + confirmed_at: number; + /** @description USD size of the trade */ + amount_usd: number; + shares_amount: number; + /** @description Fee paid in USD */ + fee: number; + /** @enum {string} */ + side: "Buy" | "Sell"; + /** @description Price that triggered the notification (0.0–1.0) */ + price: number; + /** @description Implied probability (0.0–1.0) */ + probability?: number | null; + /** + * @description "high" when near YES (price ≥ threshold), "low" when near NO (price ≤ threshold) + * @enum {string} + */ + bond_side: "high" | "low"; + /** @description The probability threshold from the subscription filter that was breached */ + threshold: number; + }; + /** @description An outcome entry within a newly created market */ + MarketCreatedOutcome: { + /** @description Outcome index (0 = Yes, 1 = No) */ + index: number; + /** @description Outcome name (e.g. "Yes", "No") */ + name: string; + /** @description ERC-1155 position token ID for this outcome */ + position_id: string; + }; + /** @description Payload delivered when a new prediction market is detected on-chain and enriched with Gamma API metadata */ + MarketCreatedPayload: { + /** @description Condition ID (0x-prefixed hex, lowercase) */ + condition_id: string; + /** @description Market slug */ + market_slug: string; + /** @description Parent event slug */ + event_slug?: string | null; + /** @description Parent event ID */ + event_id?: string | null; + /** @description Parent event title */ + event_title?: string | null; + /** @description Series slug (for recurring markets) */ + series_slug?: string | null; + /** @description List of market outcomes with their position IDs */ + outcomes: components["schemas"]["MarketCreatedOutcome"][]; + /** @description Full market question text */ + question: string; + /** @description Short display title */ + title?: string | null; + /** @description Market description */ + description: string; + /** @description Market category (e.g. "Sports", "Politics") */ + category?: string | null; + /** @description Market tags */ + tags: string[]; + /** @description Cover image URL */ + image_url?: string | null; + /** @description Whether this is a neg-risk market */ + neg_risk: boolean; + }; + /** @description Payload delivered on every raw Chainlink price tick for a tracked crypto asset */ + AssetPriceTickPayload: { + /** + * @description Asset symbol + * @enum {string} + */ + symbol: "BTC" | "ETH" | "SOL" | "XRP" | "DOGE" | "BNB" | "HYPE"; + /** @description Current asset price in USD from the Chainlink feed */ + price: number; + /** + * Format: int64 + * @description Tick timestamp (milliseconds since Unix epoch) + */ + timestamp_ms: number; + }; + /** @description Payload delivered twice per candle — once on open and once on close. `close_price` is 0.0 on the "open" update. */ + AssetPriceWindowUpdatePayload: { + /** + * @description Asset symbol + * @enum {string} + */ + symbol: "BTC" | "ETH" | "SOL" | "XRP" | "DOGE" | "BNB" | "HYPE"; + /** + * @description Candle / window size + * @enum {string} + */ + variant: "5m" | "15m" | "1h" | "4h" | "1d" | "24h"; + /** + * Format: int64 + * @description Window start timestamp (milliseconds since Unix epoch) + */ + start_time: number; + /** + * Format: int64 + * @description Window end timestamp (milliseconds since Unix epoch) + */ + end_time: number; + /** @description Opening price at start_time (USD) */ + open_price: number; + /** @description Closing price at end_time (USD). 0.0 when update_type is "open" (not yet available). */ + close_price: number; + /** + * @description "open" when the candle opens, "close" when it closes with a confirmed price + * @enum {string} + */ + update_type: "open" | "close"; + }; + /** @description Subscription filters for the `trader_first_trade` event. All fields are optional. */ + TraderFirstTradeFilters: { + /** @description Only fire for trades by these wallet addresses (lowercase). Empty = all traders. */ + wallet_addresses?: string[]; + /** @description Restrict to trades in these markets. Empty = all markets. */ + condition_ids?: string[]; + /** @description Restrict to trades in markets belonging to these events. */ + event_slugs?: string[]; + /** @description Minimum trade size in USD. Omit to match all sizes. */ + min_usd_value?: number; + /** @description Only fire when the outcome probability is ≥ this value. */ + min_probability?: number; + /** @description Only fire when the outcome probability is ≤ this value. */ + max_probability?: number; + /** @description When `true`, suppress webhooks for short-term "updown" markets (event slugs containing `updown`). Default: `false`. */ + exclude_shortterm_markets?: boolean; + }; + /** @description Subscription filters for the `trader_new_market` event. All fields are optional. */ + TraderNewMarketFilters: { + /** @description Only fire for these wallet addresses (lowercase). Empty = all traders. */ + wallet_addresses?: string[]; + /** @description Restrict to these markets. */ + condition_ids?: string[]; + /** @description Restrict to markets belonging to these events. */ + event_slugs?: string[]; + /** @description When `true`, suppress webhooks for short-term "updown" markets. Default: `false`. */ + exclude_shortterm_markets?: boolean; + }; + /** @description Subscription filters for the `trader_whale_trade` event. All fields are optional. */ + TraderWhaleTradeFilters: { + /** @description Only fire for trades by these wallet addresses. Empty = all traders. */ + wallet_addresses?: string[]; + /** @description Restrict to these markets. */ + condition_ids?: string[]; + /** @description Restrict to markets belonging to these events. */ + event_slugs?: string[]; + /** @description Minimum trade size in USD. Defaults to 0 (matches all trades). */ + min_usd_value?: number; + /** @description Only fire when outcome probability is ≥ this value. */ + min_probability?: number; + /** @description Only fire when outcome probability is ≤ this value. */ + max_probability?: number; + /** @description When `true`, suppress webhooks for short-term "updown" markets. Default: `false`. */ + exclude_shortterm_markets?: boolean; + }; + /** @description Subscription filters for the `trader_new_trade` event. All fields are optional. */ + TraderNewTradeFilters: { + /** @description Only fire for trades by these wallet addresses. Empty = all traders. */ + wallet_addresses?: string[]; + /** @description Restrict to these markets. */ + condition_ids?: string[]; + /** @description Restrict to markets belonging to these events. */ + event_slugs?: string[]; + /** @description Minimum trade size in USD. Defaults to 0 (matches all trades). */ + min_usd_value?: number; + /** @description Only fire when outcome probability is ≥ this value. */ + min_probability?: number; + /** @description Only fire when outcome probability is ≤ this value. */ + max_probability?: number; + /** @description When `true`, suppress webhooks for short-term "updown" markets. Default: `false`. */ + exclude_shortterm_markets?: boolean; + }; + /** @description Subscription filters for the `trader_global_pnl` event. All fields are optional. */ + TraderGlobalPnlFilters: { + /** @description Track only these trader wallet addresses. Empty = all traders. */ + traders?: string[]; + /** @description Only fire when realized PnL ≥ this value (USD). Use negative values for loss thresholds. */ + min_realized_pnl_usd?: number; + /** @description Only fire when realized PnL ≤ this value (USD). */ + max_realized_pnl_usd?: number; + /** @description Only fire when total trading volume ≥ this value (USD). */ + min_volume_usd?: number; + /** @description Only fire when total trading volume ≤ this value (USD). */ + max_volume_usd?: number; + /** @description Only fire when buy volume ≥ this value (USD). */ + min_buy_usd?: number; + /** @description Only fire when sell volume ≥ this value (USD). */ + min_sell_volume_usd?: number; + /** @description Only fire when market win rate ≥ this percentage (0.0–100.0). */ + min_win_rate?: number; + /** + * Format: int64 + * @description Only fire when the trader has traded in ≥ this many markets. + */ + min_markets_traded?: number; + /** @description Restrict to these PnL windows. Empty = all windows. */ + timeframes?: ("1d" | "7d" | "30d" | "lifetime")[]; + }; + /** @description Subscription filters for the `trader_market_pnl` event. All fields are optional. */ + TraderMarketPnlFilters: { + /** @description Track only these trader wallet addresses. */ + traders?: string[]; + /** @description Restrict to these markets. */ + condition_ids?: string[]; + /** @description Restrict to markets in these events. */ + event_slugs?: string[]; + /** @description Only fire when per-market realized PnL ≥ this value (USD). */ + min_realized_pnl_usd?: number; + /** @description Only fire when per-market realized PnL ≤ this value (USD). */ + max_realized_pnl_usd?: number; + /** @description Only fire when total volume (buy + sell + redemption + merge) ≥ this value (USD). */ + min_volume_usd?: number; + /** @description Only fire when total volume ≤ this value (USD). */ + max_volume_usd?: number; + /** @description Only fire when buy volume in the market ≥ this value (USD). */ + min_buy_usd?: number; + /** @description Only fire when sell volume in the market ≥ this value (USD). */ + min_sell_volume_usd?: number; + /** @description Restrict to these PnL windows. */ + timeframes?: ("1d" | "7d" | "30d" | "lifetime")[]; + /** @description When `true`, suppress webhooks for short-term "updown" markets. Default: `false`. */ + exclude_shortterm_markets?: boolean; + }; + /** @description Subscription filters for the `trader_event_pnl` event. All fields are optional. */ + TraderEventPnlFilters: { + /** @description Track only these trader wallet addresses. */ + traders?: string[]; + /** @description Restrict to these events. */ + event_slugs?: string[]; + /** @description Only fire when per-event realized PnL ≥ this value (USD). */ + min_realized_pnl_usd?: number; + /** @description Only fire when per-event realized PnL ≤ this value (USD). */ + max_realized_pnl_usd?: number; + /** @description Only fire when total event volume ≥ this value (USD). */ + min_volume_usd?: number; + /** @description Only fire when total event volume ≤ this value (USD). */ + max_volume_usd?: number; + /** @description Only fire when buy volume within the event ≥ this value (USD). */ + min_buy_usd?: number; + /** @description Only fire when sell volume within the event ≥ this value (USD). */ + min_sell_volume_usd?: number; + /** + * Format: int64 + * @description Only fire when the trader has traded in ≥ this many markets within the event. + */ + min_markets_traded?: number; + /** @description Restrict to these PnL windows. */ + timeframes?: ("1d" | "7d" | "30d" | "lifetime")[]; + /** @description When `true`, suppress webhooks for short-term "updown" markets. Default: `false`. */ + exclude_shortterm_markets?: boolean; + }; + /** @description Subscription filters for the `condition_metrics` event. All fields are optional. */ + MarketMetricsFilters: { + /** @description Restrict to these markets. Empty = all markets. */ + condition_ids?: string[]; + /** @description Restrict to markets in these events. */ + event_slugs?: string[]; + /** @description Restrict to these aggregation windows. Empty = all windows. */ + timeframes?: ("1m" | "5m" | "30m" | "1h" | "6h" | "24h" | "7d" | "30d")[]; + /** @description Only fire when volume ≥ this value (USD). */ + min_volume_usd?: number; + /** @description Only fire when volume ≤ this value (USD). */ + max_volume_usd?: number; + /** + * Format: int64 + * @description Only fire when transaction count ≥ this value. + */ + min_txns?: number; + /** + * Format: int64 + * @description Only fire when unique trader count ≥ this value. + */ + min_unique_traders?: number; + /** @description Only fire when total fees ≥ this value (USD). */ + min_fees?: number; + }; + /** @description Subscription filters for the `event_metrics` event. All fields are optional. */ + EventMetricsFilters: { + /** @description Restrict to these events. Empty = all events. */ + event_slugs?: string[]; + /** @description Restrict to these aggregation windows. */ + timeframes?: ("1m" | "5m" | "30m" | "1h" | "6h" | "24h" | "7d" | "30d")[]; + /** @description Only fire when aggregated event volume ≥ this value (USD). */ + min_volume_usd?: number; + max_volume_usd?: number; + /** Format: int64 */ + min_txns?: number; + /** Format: int64 */ + min_unique_traders?: number; + min_fees?: number; + /** @description When `true`, suppress webhooks for short-term "updown" markets. Default: `false`. */ + exclude_shortterm_markets?: boolean; + }; + /** @description Subscription filters for the `position_metrics` event. All fields are optional. */ + PositionMetricsFilters: { + /** @description Restrict to these outcome token IDs. */ + position_ids?: string[]; + /** @description Restrict to positions within these markets. */ + condition_ids?: string[]; + /** @description Restrict to positions with these outcome names (e.g. ["Yes", "No"]). */ + outcomes?: string[]; + /** @description Restrict to these aggregation windows. */ + timeframes?: ("1m" | "5m" | "30m" | "1h" | "6h" | "24h" | "7d" | "30d")[]; + /** @description Only fire when position volume ≥ this value (USD). */ + min_volume_usd?: number; + max_volume_usd?: number; + min_buy_usd?: number; + min_sell_volume_usd?: number; + /** Format: int64 */ + min_txns?: number; + /** Format: int64 */ + min_unique_traders?: number; + /** @description Only fire when price change % ≥ this value. */ + min_price_change_pct?: number; + /** @description Only fire when probability change % ≥ this value. */ + min_probability_change_pct?: number; + min_fees?: number; + }; + /** @description Subscription filters for the `market_volume_milestone` event. */ + MarketVolumeMilestoneFilters: { + /** @description **Required.** Aggregation windows to monitor (e.g. ["1h", "24h"]). */ + timeframes: ("1m" | "5m" | "30m" | "1h" | "6h" | "24h" | "7d" | "30d")[]; + /** @description Restrict to these markets. Empty = all markets. */ + condition_ids?: string[]; + /** @description Specific USD milestones to trigger on (e.g. [10000, 100000, 1000000]). Empty = all milestones. */ + milestone_amounts?: number[]; + }; + /** @description Subscription filters for the `event_volume_milestone` event. */ + EventVolumeMilestoneFilters: { + /** @description **Required.** Aggregation windows to monitor. */ + timeframes: ("1m" | "5m" | "30m" | "1h" | "6h" | "24h" | "7d" | "30d")[]; + /** @description Restrict to these events. */ + event_slugs?: string[]; + /** @description Specific USD milestones to trigger on. */ + milestone_amounts?: number[]; + /** @description When `true`, suppress webhooks for short-term "updown" markets. Default: `false`. */ + exclude_shortterm_markets?: boolean; + }; + /** @description Subscription filters for the `position_volume_milestone` event. */ + PositionVolumeMilestoneFilters: { + /** @description **Required.** Aggregation windows to monitor. */ + timeframes: ("1m" | "5m" | "30m" | "1h" | "6h" | "24h" | "7d" | "30d")[]; + /** @description Restrict to these outcome token IDs. */ + position_ids?: string[]; + /** @description Restrict to positions within these markets. */ + condition_ids?: string[]; + /** @description Specific USD milestones to trigger on. */ + milestone_amounts?: number[]; + }; + /** @description Subscription filters for the `probability_spike` event. */ + ProbabilitySpikeFilters: { + /** @description Restrict to specific outcome token IDs. Empty = all positions. */ + position_ids?: string[]; + /** @description Restrict to specific market condition IDs. Empty = all markets. */ + condition_ids?: string[]; + /** @description Restrict to specific events. Empty = all events. */ + event_slugs?: string[]; + /** @description Restrict to these outcome names (e.g. ["Yes", "No"]). */ + outcomes?: string[]; + /** @description Minimum probability percentage move to trigger (e.g. `10` for a 10% move). */ + min_probability_change_pct?: number; + /** + * @description `"up"` = probability rising only (default when omitted), `"down"` = falling only, `"both"` = either direction. + * @enum {string} + */ + spike_direction?: "up" | "down" | "both"; + /** @description Observation window in seconds. The first trade in each window sets the reference price; subsequent trades are compared to it. E.g. `60` detects moves that occur within 60 seconds. */ + window_secs?: number; + /** @description When `true`, suppress webhooks for short-term "updown" markets. Default: `false`. */ + exclude_shortterm_markets?: boolean; + }; + /** @description Subscription filters for the `price_spike` event. */ + PriceSpikeFilters: { + /** @description Restrict to specific outcome token IDs. Empty = all positions. */ + position_ids?: string[]; + /** @description Restrict to specific market condition IDs. Empty = all markets. */ + condition_ids?: string[]; + /** @description Restrict to specific events. Empty = all events. */ + event_slugs?: string[]; + /** @description Restrict to these outcome names (e.g. ["Yes", "No"]). */ + outcomes?: string[]; + /** @description Minimum price percentage move to trigger (e.g. `10` for a 10% move). */ + min_price_change_pct?: number; + /** + * @description `"up"` = price rising only (default when omitted), `"down"` = falling only, `"both"` = either direction. + * @enum {string} + */ + spike_direction?: "up" | "down" | "both"; + /** @description Observation window in seconds. The first trade in each window sets the reference price; subsequent trades are compared to it. E.g. `60` detects moves that occur within 60 seconds. */ + window_secs?: number; + /** @description When `true`, suppress webhooks for short-term "updown" markets. Default: `false`. */ + exclude_shortterm_markets?: boolean; + }; + /** @description Subscription filters for the `market_volume_spike` event. `spike_ratio` is required. */ + MarketVolumeSpikeFilters: { + /** @description **Required.** Multiplier threshold (must be > 1.0). Fires when current volume >= snapshot × ratio. The snapshot is set automatically on first data and resets after each fire. */ + spike_ratio: number; + /** @description Force snapshot reset after this many seconds (max 600 / 10 minutes). */ + window_secs?: number; + /** @description Restrict to these markets. Empty = all markets. */ + condition_ids?: string[]; + /** @description Restrict to these aggregation windows. Empty = all windows. */ + timeframes?: ("1m" | "5m" | "30m" | "1h" | "6h" | "1d" | "24h" | "7d" | "30d")[]; + }; + /** @description Subscription filters for the `event_volume_spike` event. `spike_ratio` is required. */ + EventVolumeSpikeFilters: { + /** @description **Required.** Multiplier threshold (must be > 1.0). Fires when current volume >= snapshot × ratio. */ + spike_ratio: number; + /** @description Force snapshot reset after this many seconds (max 600 / 10 minutes). */ + window_secs?: number; + /** @description Restrict to these events. */ + event_slugs?: string[]; + /** @description Restrict to these aggregation windows. */ + timeframes?: ("1m" | "5m" | "30m" | "1h" | "6h" | "1d" | "24h" | "7d" | "30d")[]; + /** @description When `true`, suppress webhooks for short-term "updown" markets. Default: `false`. */ + exclude_shortterm_markets?: boolean; + }; + /** @description Subscription filters for the `position_volume_spike` event. `spike_ratio` is required. */ + PositionVolumeSpikeFilters: { + /** @description **Required.** Multiplier threshold (must be > 1.0). Fires when current volume >= snapshot × ratio. */ + spike_ratio: number; + /** @description Force snapshot reset after this many seconds (max 600 / 10 minutes). */ + window_secs?: number; + /** @description Restrict to these outcome token IDs. */ + position_ids?: string[]; + /** @description Restrict to positions within these markets. */ + condition_ids?: string[]; + /** @description Restrict to these outcome names. */ + outcomes?: string[]; + /** @description Restrict to these aggregation windows. */ + timeframes?: ("1m" | "5m" | "30m" | "1h" | "6h" | "1d" | "24h" | "7d" | "30d")[]; + }; + /** @description Subscription filters for the `close_to_bond` event. At least one of `min_probability` or `max_probability` is required. */ + CloseToBondFilters: { + /** @description Trigger when the YES outcome price is ≥ this value (e.g. 0.95 for 95% certainty). At least one of `min_probability` or `max_probability` must be set. */ + min_probability?: number; + /** @description Trigger when the YES outcome price is ≤ this value (e.g. 0.05 for near-certain NO). */ + max_probability?: number; + /** @description Restrict to these markets. */ + condition_ids?: string[]; + /** @description Restrict to these outcome token IDs. */ + position_ids?: string[]; + /** @description Restrict to markets in these events. */ + event_slugs?: string[]; + /** @description Restrict to these outcome names (e.g. ["Yes", "No"]). */ + outcomes?: string[]; + /** @description Restrict by outcome index. 0 = Yes/Up, 1 = No. Position 0 usually represents the Up/Yes side in binary markets. */ + position_outcome_indices?: number[]; + /** @description When `true`, suppress webhooks for short-term "updown" markets. Default: `false`. */ + exclude_shortterm_markets?: boolean; + } | unknown | unknown; + /** @description Subscription filters for the `market_created` event. All fields are optional. */ + MarketCreatedFilters: { + /** @description Restrict to markets with these tags or category names (case-insensitive match). */ + tags?: string[]; + /** @description Restrict to these specific markets. */ + condition_ids?: string[]; + /** @description Restrict to markets belonging to these events. */ + event_slugs?: string[]; + /** @description When `true`, suppress webhooks for short-term "updown" markets (event slugs containing `updown`). Default: `false`. */ + exclude_shortterm_markets?: boolean; + }; + /** @description Subscription filters for the `asset_price_tick` event. All fields are optional. */ + AssetPriceTickFilters: { + /** @description Restrict to these crypto assets. Empty = all assets. */ + asset_symbols?: ("BTC" | "ETH" | "SOL" | "XRP" | "DOGE" | "BNB" | "HYPE")[]; + }; + /** @description Subscription filters for the `asset_price_window_update` event. All fields are optional. */ + AssetPriceWindowUpdateFilters: { + /** @description Restrict to these crypto assets. Empty = all assets. */ + asset_symbols?: ("BTC" | "ETH" | "SOL" | "XRP" | "DOGE" | "BNB" | "HYPE")[]; + /** @description Restrict to these candle sizes. Empty = all sizes. */ + timeframes?: ("5m" | "15m" | "1h" | "4h" | "1d" | "24h")[]; + }; + /** + * @description All alert event types supported by both HTTP webhooks and the alerts WebSocket. + * @enum {string} + */ + WsAlertEventType: "trader_first_trade" | "trader_new_market" | "trader_whale_trade" | "trader_new_trade" | "trader_global_pnl" | "trader_market_pnl" | "trader_event_pnl" | "condition_metrics" | "event_metrics" | "position_metrics" | "market_volume_milestone" | "event_volume_milestone" | "position_volume_milestone" | "probability_spike" | "price_spike" | "market_volume_spike" | "event_volume_spike" | "position_volume_spike" | "close_to_bond" | "market_created" | "asset_price_tick" | "asset_price_window_update"; + /** @description Server acknowledgement for a successful alert subscription. */ + WsAlertSubscribedResponse: { + /** @enum {string} */ + op: "subscribed"; + event: components["schemas"]["WsAlertEventType"]; + /** Format: uuid */ + subscription_id: string; + }; + /** @description Server acknowledgement for a successful alert unsubscription. */ + WsAlertUnsubscribedResponse: { + /** @enum {string} */ + op: "unsubscribed"; + event: components["schemas"]["WsAlertEventType"]; + }; + /** @description Error returned by the alerts WebSocket when a message is invalid or a subscription request fails. */ + WsAlertErrorResponse: { + error: string; + }; + WsAlertTraderFirstTradeSubscribeMessage: { + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "trader_first_trade"; + } & components["schemas"]["TraderFirstTradeFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "trader_first_trade"; + }; + WsAlertTraderFirstTradeUnsubscribeMessage: { + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "trader_first_trade"; + } & components["schemas"]["TraderFirstTradeFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "trader_first_trade"; + }; + /** + * @description Pushed `trader_first_trade` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "trader_first_trade", + * "timestamp": 1743500000000, + * "data": { + * "trader": "0x0000000000000000000000000000000000000000", + * "taker": "0x0000000000000000000000000000000000000000", + * "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656", + * "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + * "outcome": "Yes", + * "outcome_index": 0, + * "question": "Will this test webhook fire correctly?", + * "market_slug": "test-market-0000000000", + * "event_slug": "test-event-0000000000", + * "trade_id": "00000000-0000-0000-0000-000000000000", + * "hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + * "block": 0, + * "confirmed_at": 1700000000, + * "amount_usd": 125, + * "shares_amount": 250, + * "fee": 0.125, + * "side": "Buy", + * "price": 0.5, + * "exchange": "polymarket", + * "trade_type": "OrderFilled" + * } + * } + */ + WsAlertTraderFirstTradeEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "trader_first_trade"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["FirstTradePayload"]; + }; + WsAlertTraderNewMarketSubscribeMessage: { + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "trader_new_market"; + } & components["schemas"]["TraderNewMarketFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "trader_new_market"; + }; + WsAlertTraderNewMarketUnsubscribeMessage: { + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "trader_new_market"; + } & components["schemas"]["TraderNewMarketFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "trader_new_market"; + }; + /** + * @description Pushed `trader_new_market` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "trader_new_market", + * "timestamp": 1743500000000, + * "data": { + * "trader": "0x0000000000000000000000000000000000000000", + * "taker": "0x0000000000000000000000000000000000000000", + * "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656", + * "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + * "outcome": "Yes", + * "outcome_index": 0, + * "question": "Will this test webhook fire correctly?", + * "market_slug": "test-market-0000000000", + * "event_slug": "test-event-0000000000", + * "trade_id": "00000000-0000-0000-0000-000000000000", + * "hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + * "block": 0, + * "confirmed_at": 1700000000, + * "amount_usd": 125, + * "shares_amount": 250, + * "fee": 0.125, + * "side": "Buy", + * "price": 0.5, + * "probability": 0.5, + * "exchange": "polymarket", + * "trade_type": "OrderFilled" + * } + * } + */ + WsAlertTraderNewMarketEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "trader_new_market"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["NewMarketPayload"]; + }; + WsAlertTraderWhaleTradeSubscribeMessage: { + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "trader_whale_trade"; + } & components["schemas"]["TraderWhaleTradeFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "trader_whale_trade"; + }; + WsAlertTraderWhaleTradeUnsubscribeMessage: { + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "trader_whale_trade"; + } & components["schemas"]["TraderWhaleTradeFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "trader_whale_trade"; + }; + /** + * @description Pushed `trader_whale_trade` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "trader_whale_trade", + * "timestamp": 1743500000000, + * "data": { + * "trader": "0x0000000000000000000000000000000000000000", + * "taker": "0x0000000000000000000000000000000000000000", + * "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656", + * "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + * "outcome": "Yes", + * "outcome_index": 0, + * "question": "Will this test webhook fire correctly?", + * "market_slug": "test-market-0000000000", + * "event_slug": "test-event-0000000000", + * "trade_id": "00000000-0000-0000-0000-000000000000", + * "hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + * "block": 0, + * "confirmed_at": 1700000000, + * "amount_usd": 125, + * "shares_amount": 250, + * "fee": 0.125, + * "side": "Buy", + * "price": 0.5, + * "probability": 0.5, + * "exchange": "polymarket", + * "trade_type": "OrderFilled" + * } + * } + */ + WsAlertTraderWhaleTradeEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "trader_whale_trade"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["WhaleTradePayload"]; + }; + WsAlertTraderNewTradeSubscribeMessage: { + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "trader_new_trade"; + } & components["schemas"]["TraderNewTradeFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "trader_new_trade"; + }; + WsAlertTraderNewTradeUnsubscribeMessage: { + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "trader_new_trade"; + } & components["schemas"]["TraderNewTradeFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "trader_new_trade"; + }; + /** + * @description Pushed `trader_new_trade` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "trader_new_trade", + * "timestamp": 1743500000000, + * "data": { + * "trader": "0x0000000000000000000000000000000000000000", + * "taker": "0x0000000000000000000000000000000000000000", + * "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656", + * "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + * "outcome": "Yes", + * "outcome_index": 0, + * "question": "Will this test webhook fire correctly?", + * "market_slug": "test-market-0000000000", + * "event_slug": "test-event-0000000000", + * "trade_id": "00000000-0000-0000-0000-000000000000", + * "hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + * "block": 0, + * "confirmed_at": 1700000000, + * "amount_usd": 25, + * "shares_amount": 50, + * "fee": 0.025, + * "side": "Buy", + * "price": 0.5, + * "probability": 0.5, + * "exchange": "polymarket", + * "trade_type": "OrderFilled" + * } + * } + */ + WsAlertTraderNewTradeEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "trader_new_trade"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["NewTradePayload"]; + }; + WsAlertTraderGlobalPnlSubscribeMessage: { + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "trader_global_pnl"; + } & components["schemas"]["TraderGlobalPnlFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "trader_global_pnl"; + }; + WsAlertTraderGlobalPnlUnsubscribeMessage: { + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "trader_global_pnl"; + } & components["schemas"]["TraderGlobalPnlFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "trader_global_pnl"; + }; + /** + * @description Pushed `trader_global_pnl` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "trader_global_pnl", + * "timestamp": 1743500000000, + * "data": { + * "trader": "0x0000000000000000000000000000000000000000", + * "timeframe": "7d", + * "realized_pnl_usd": 250, + * "events_traded": 3, + * "markets_traded": 5, + * "total_buys": 12, + * "total_sells": 8, + * "total_redemptions": 1, + * "total_merges": 0, + * "total_volume_usd": 1500, + * "buy_volume_usd": 900, + * "sell_volume_usd": 600, + * "redemption_volume_usd": 50, + * "merge_volume_usd": 0, + * "markets_won": 3, + * "markets_lost": 2, + * "market_win_rate_pct": 60, + * "avg_pnl_per_market": 50, + * "avg_pnl_per_trade": 12.5, + * "avg_hold_time_seconds": 86400, + * "total_fees": 7.5, + * "best_trade_pnl_usd": 180, + * "best_trade_condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + * "first_trade_at": 1700000000, + * "last_trade_at": 1700000000 + * } + * } + */ + WsAlertTraderGlobalPnlEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "trader_global_pnl"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["GlobalPnlPayload"]; + }; + WsAlertTraderMarketPnlSubscribeMessage: { + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "trader_market_pnl"; + } & components["schemas"]["TraderMarketPnlFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "trader_market_pnl"; + }; + WsAlertTraderMarketPnlUnsubscribeMessage: { + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "trader_market_pnl"; + } & components["schemas"]["TraderMarketPnlFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "trader_market_pnl"; + }; + /** + * @description Pushed `trader_market_pnl` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "trader_market_pnl", + * "timestamp": 1743500000000, + * "data": { + * "trader": "0x0000000000000000000000000000000000000000", + * "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + * "event_slug": "test-event-0000000000", + * "timeframe": "7d", + * "outcomes_traded": 2, + * "total_buys": 4, + * "total_sells": 3, + * "total_redemptions": 1, + * "total_merges": 0, + * "buy_usd": 300, + * "sell_usd": 200, + * "redemption_usd": 50, + * "merge_usd": 0, + * "realized_pnl_usd": 100, + * "winning_outcomes": 1, + * "total_fees": 2.5, + * "first_trade_at": 1700000000, + * "last_trade_at": 1700000000 + * } + * } + */ + WsAlertTraderMarketPnlEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "trader_market_pnl"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["MarketPnlPayload"]; + }; + WsAlertTraderEventPnlSubscribeMessage: { + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "trader_event_pnl"; + } & components["schemas"]["TraderEventPnlFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "trader_event_pnl"; + }; + WsAlertTraderEventPnlUnsubscribeMessage: { + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "trader_event_pnl"; + } & components["schemas"]["TraderEventPnlFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "trader_event_pnl"; + }; + /** + * @description Pushed `trader_event_pnl` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "trader_event_pnl", + * "timestamp": 1743500000000, + * "data": { + * "trader": "0x0000000000000000000000000000000000000000", + * "event_slug": "test-event-0000000000", + * "timeframe": "7d", + * "markets_traded": 2, + * "outcomes_traded": 3, + * "total_buys": 6, + * "total_sells": 4, + * "total_redemptions": 1, + * "total_merges": 0, + * "total_volume_usd": 800, + * "buy_usd": 480, + * "sell_usd": 320, + * "redemption_usd": 50, + * "merge_usd": 0, + * "realized_pnl_usd": 150, + * "winning_markets": 1, + * "losing_markets": 1, + * "total_fees": 4, + * "first_trade_at": 1700000000, + * "last_trade_at": 1700000000 + * } + * } + */ + WsAlertTraderEventPnlEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "trader_event_pnl"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["EventPnlPayload"]; + }; + WsAlertConditionMetricsSubscribeMessage: { + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "condition_metrics"; + } & components["schemas"]["MarketMetricsFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "condition_metrics"; + }; + WsAlertConditionMetricsUnsubscribeMessage: { + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "condition_metrics"; + } & components["schemas"]["MarketMetricsFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "condition_metrics"; + }; + /** + * @description Pushed `condition_metrics` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "condition_metrics", + * "timestamp": 1743500000000, + * "data": { + * "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + * "timeframe": "1h", + * "volume_usd": 50000, + * "fees": 250, + * "txns": 320, + * "unique_traders": 85 + * } + * } + */ + WsAlertConditionMetricsEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "condition_metrics"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["ConditionMetricsPayload"]; + }; + WsAlertEventMetricsSubscribeMessage: { + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "event_metrics"; + } & components["schemas"]["EventMetricsFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "event_metrics"; + }; + WsAlertEventMetricsUnsubscribeMessage: { + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "event_metrics"; + } & components["schemas"]["EventMetricsFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "event_metrics"; + }; + /** + * @description Pushed `event_metrics` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "event_metrics", + * "timestamp": 1743500000000, + * "data": { + * "event_slug": "test-event-0000000000", + * "timeframe": "1h", + * "volume_usd": 120000, + * "fees": 600, + * "txns": 740, + * "unique_traders": 210 + * } + * } + */ + WsAlertEventMetricsEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "event_metrics"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["EventMetricsPayload"]; + }; + WsAlertPositionMetricsSubscribeMessage: { + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "position_metrics"; + } & components["schemas"]["PositionMetricsFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "position_metrics"; + }; + WsAlertPositionMetricsUnsubscribeMessage: { + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "position_metrics"; + } & components["schemas"]["PositionMetricsFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "position_metrics"; + }; + /** + * @description Pushed `position_metrics` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "position_metrics", + * "timestamp": 1743500000000, + * "data": { + * "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656", + * "outcome": "Yes", + * "outcome_index": 0, + * "timeframe": "1h", + * "volume_usd": 25000, + * "buy_volume_usd": 15000, + * "sell_volume_usd": 10000, + * "fees": 125, + * "txns": 160, + * "buys": 95, + * "sells": 65, + * "unique_traders": 48, + * "price_open": 0.48, + * "price_close": 0.52, + * "price_high": 0.55, + * "price_low": 0.46, + * "probability_open": 0.48, + * "probability_close": 0.52, + * "probability_high": 0.55, + * "probability_low": 0.46 + * } + * } + */ + WsAlertPositionMetricsEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "position_metrics"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["PositionMetricsPayload"]; + }; + WsAlertMarketVolumeMilestoneSubscribeMessage: { + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "market_volume_milestone"; + } & components["schemas"]["MarketVolumeMilestoneFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "market_volume_milestone"; + }; + WsAlertMarketVolumeMilestoneUnsubscribeMessage: { + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "market_volume_milestone"; + } & components["schemas"]["MarketVolumeMilestoneFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "market_volume_milestone"; + }; + /** + * @description Pushed `market_volume_milestone` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "market_volume_milestone", + * "timestamp": 1743500000000, + * "data": { + * "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + * "timeframe": "24h", + * "milestone_usd": 100000, + * "current_volume_usd": 100125, + * "fees": 500, + * "txns": 650 + * } + * } + */ + WsAlertMarketVolumeMilestoneEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "market_volume_milestone"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["VolumeMilestonePayload"]; + }; + WsAlertEventVolumeMilestoneSubscribeMessage: { + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "event_volume_milestone"; + } & components["schemas"]["EventVolumeMilestoneFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "event_volume_milestone"; + }; + WsAlertEventVolumeMilestoneUnsubscribeMessage: { + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "event_volume_milestone"; + } & components["schemas"]["EventVolumeMilestoneFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "event_volume_milestone"; + }; + /** + * @description Pushed `event_volume_milestone` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "event_volume_milestone", + * "timestamp": 1743500000000, + * "data": { + * "event_slug": "test-event-0000000000", + * "timeframe": "24h", + * "milestone_usd": 500000, + * "current_volume_usd": 500250, + * "fees": 2500, + * "txns": 3200 + * } + * } + */ + WsAlertEventVolumeMilestoneEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "event_volume_milestone"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["EventVolumeMilestonePayload"]; + }; + WsAlertPositionVolumeMilestoneSubscribeMessage: { + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "position_volume_milestone"; + } & components["schemas"]["PositionVolumeMilestoneFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "position_volume_milestone"; + }; + WsAlertPositionVolumeMilestoneUnsubscribeMessage: { + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "position_volume_milestone"; + } & components["schemas"]["PositionVolumeMilestoneFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "position_volume_milestone"; + }; + /** + * @description Pushed `position_volume_milestone` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "position_volume_milestone", + * "timestamp": 1743500000000, + * "data": { + * "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + * "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656", + * "outcome": "Yes", + * "outcome_index": 0, + * "timeframe": "24h", + * "milestone_usd": 50000, + * "current_volume_usd": 50125, + * "buy_volume_usd": 30000, + * "sell_volume_usd": 20000, + * "fees": 250, + * "txns": 320, + * "buys": 190, + * "sells": 130 + * } + * } + */ + WsAlertPositionVolumeMilestoneEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "position_volume_milestone"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["PositionVolumeMilestonePayload"]; + }; + WsAlertProbabilitySpikeSubscribeMessage: { + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "probability_spike"; + } & components["schemas"]["ProbabilitySpikeFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "probability_spike"; + }; + WsAlertProbabilitySpikeUnsubscribeMessage: { + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "probability_spike"; + } & components["schemas"]["ProbabilitySpikeFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "probability_spike"; + }; + /** + * @description Pushed `probability_spike` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "probability_spike", + * "timestamp": 1743500000000, + * "data": { + * "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656", + * "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + * "event_slug": "test-event-0000000000", + * "outcome": "Yes", + * "outcome_index": 0, + * "previous_probability": 0.4, + * "current_probability": 0.5, + * "spike_direction": "up", + * "spike_pct": 25 + * } + * } + */ + WsAlertProbabilitySpikeEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "probability_spike"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["ProbabilitySpikePayload"]; + }; + WsAlertPriceSpikeSubscribeMessage: { + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "price_spike"; + } & components["schemas"]["PriceSpikeFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "price_spike"; + }; + WsAlertPriceSpikeUnsubscribeMessage: { + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "price_spike"; + } & components["schemas"]["PriceSpikeFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "price_spike"; + }; + /** + * @description Pushed `price_spike` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "price_spike", + * "timestamp": 1743500000000, + * "data": { + * "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656", + * "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + * "event_slug": "test-event-0000000000", + * "outcome": "Yes", + * "outcome_index": 0, + * "previous_price": 0.4, + * "current_price": 0.5, + * "spike_direction": "up", + * "spike_pct": 25 + * } + * } + */ + WsAlertPriceSpikeEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "price_spike"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["PriceSpikePayload"]; + }; + WsAlertMarketVolumeSpikeSubscribeMessage: { + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "market_volume_spike"; + } & components["schemas"]["MarketVolumeSpikeFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "market_volume_spike"; + }; + WsAlertMarketVolumeSpikeUnsubscribeMessage: { + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "market_volume_spike"; + } & components["schemas"]["MarketVolumeSpikeFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "market_volume_spike"; + }; + /** + * @description Pushed `market_volume_spike` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "market_volume_spike", + * "timestamp": 1743500000000, + * "data": { + * "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + * "timeframe": "1h", + * "current_volume_usd": 32000, + * "snapshot_volume_usd": 10000, + * "delta_volume_usd": 22000, + * "spike_pct": 220, + * "txns": 480, + * "fees": 160 + * } + * } + */ + WsAlertMarketVolumeSpikeEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "market_volume_spike"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["MarketVolumeSpikePayload"]; + }; + WsAlertEventVolumeSpikeSubscribeMessage: { + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "event_volume_spike"; + } & components["schemas"]["EventVolumeSpikeFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "event_volume_spike"; + }; + WsAlertEventVolumeSpikeUnsubscribeMessage: { + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "event_volume_spike"; + } & components["schemas"]["EventVolumeSpikeFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "event_volume_spike"; + }; + /** + * @description Pushed `event_volume_spike` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "event_volume_spike", + * "timestamp": 1743500000000, + * "data": { + * "event_slug": "test-event-0000000000", + * "timeframe": "1h", + * "current_volume_usd": 140000, + * "snapshot_volume_usd": 50000, + * "delta_volume_usd": 90000, + * "spike_pct": 180, + * "txns": 1100, + * "fees": 700 + * } + * } + */ + WsAlertEventVolumeSpikeEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "event_volume_spike"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["EventVolumeSpikePayload"]; + }; + WsAlertPositionVolumeSpikeSubscribeMessage: { + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "position_volume_spike"; + } & components["schemas"]["PositionVolumeSpikeFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "position_volume_spike"; + }; + WsAlertPositionVolumeSpikeUnsubscribeMessage: { + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "position_volume_spike"; + } & components["schemas"]["PositionVolumeSpikeFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "position_volume_spike"; + }; + /** + * @description Pushed `position_volume_spike` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "position_volume_spike", + * "timestamp": 1743500000000, + * "data": { + * "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656", + * "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + * "outcome": "Yes", + * "outcome_index": 0, + * "timeframe": "1h", + * "current_volume_usd": 20500, + * "snapshot_volume_usd": 5000, + * "delta_volume_usd": 15500, + * "spike_pct": 310, + * "txns": 240, + * "fees": 102.5 + * } + * } + */ + WsAlertPositionVolumeSpikeEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "position_volume_spike"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["PositionVolumeSpikePayload"]; + }; + WsAlertCloseToBondSubscribeMessage: ({ + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "close_to_bond"; + } & components["schemas"]["CloseToBondFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "close_to_bond"; + }) | unknown | unknown; + WsAlertCloseToBondUnsubscribeMessage: ({ + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "close_to_bond"; + } & components["schemas"]["CloseToBondFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "close_to_bond"; + }) | unknown | unknown; + /** + * @description Pushed `close_to_bond` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "close_to_bond", + * "timestamp": 1743500000000, + * "data": { + * "trader": "0x0000000000000000000000000000000000000000", + * "taker": "0x0000000000000000000000000000000000000000", + * "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656", + * "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + * "outcome": "Yes", + * "outcome_index": 0, + * "question": "Will this test webhook fire correctly?", + * "market_slug": "test-market-0000000000", + * "event_slug": "test-event-0000000000", + * "trade_id": "00000000-0000-0000-0000-000000000000", + * "hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + * "block": 0, + * "confirmed_at": 1700000000, + * "amount_usd": 500, + * "shares_amount": 515.46, + * "fee": 2.5, + * "side": "Buy", + * "price": 0.97, + * "probability": 0.97, + * "bond_side": "high", + * "threshold": 0.95 + * } + * } + */ + WsAlertCloseToBondEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "close_to_bond"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["CloseToBondPayload"]; + }; + WsAlertMarketCreatedSubscribeMessage: { + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "market_created"; + } & components["schemas"]["MarketCreatedFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "market_created"; + }; + WsAlertMarketCreatedUnsubscribeMessage: { + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "market_created"; + } & components["schemas"]["MarketCreatedFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "market_created"; + }; + /** + * @description Pushed `market_created` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "market_created", + * "timestamp": 1743500000000, + * "data": { + * "condition_id": "0x0000000000000000000000000000000000000000000000000000000000000000", + * "market_slug": "test-market-0000000000", + * "event_slug": "test-event-0000000000", + * "event_id": null, + * "event_title": "Test Event 0000", + * "series_slug": null, + * "outcomes": [ + * { + * "index": 0, + * "name": "Yes", + * "position_id": "452312848583266388373324160190187140051835877600158453279131187530910662656" + * }, + * { + * "index": 1, + * "name": "No", + * "position_id": "0" + * } + * ], + * "question": "Will this test webhook fire correctly?", + * "title": "Test Market 0000", + * "description": "A test market for webhook payload verification.", + * "category": "Crypto", + * "tags": [ + * "test", + * "crypto" + * ], + * "image_url": null, + * "neg_risk": false + * } + * } + */ + WsAlertMarketCreatedEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "market_created"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["MarketCreatedPayload"]; + }; + WsAlertAssetPriceTickSubscribeMessage: { + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "asset_price_tick"; + } & components["schemas"]["AssetPriceTickFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "asset_price_tick"; + }; + WsAlertAssetPriceTickUnsubscribeMessage: { + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "asset_price_tick"; + } & components["schemas"]["AssetPriceTickFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "asset_price_tick"; + }; + /** + * @description Pushed `asset_price_tick` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "asset_price_tick", + * "timestamp": 1743500000000, + * "data": { + * "symbol": "BTC", + * "price": 65000, + * "timestamp_ms": 1700000000000 + * } + * } + */ + WsAlertAssetPriceTickEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "asset_price_tick"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["AssetPriceTickPayload"]; + }; + WsAlertAssetPriceWindowUpdateSubscribeMessage: { + /** @enum {string} */ + op: "subscribe"; + /** @enum {string} */ + event: "asset_price_window_update"; + } & components["schemas"]["AssetPriceWindowUpdateFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "asset_price_window_update"; + }; + WsAlertAssetPriceWindowUpdateUnsubscribeMessage: { + /** @enum {string} */ + op: "unsubscribe"; + /** @enum {string} */ + event: "asset_price_window_update"; + } & components["schemas"]["AssetPriceWindowUpdateFilters"] & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "asset_price_window_update"; + }; + /** + * @description Pushed `asset_price_window_update` alert. The `data` payload matches the corresponding HTTP webhook payload schema. + * @example { + * "event": "asset_price_window_update", + * "timestamp": 1743500000000, + * "data": { + * "symbol": "BTC", + * "variant": "1h", + * "start_time": 1700000000000, + * "end_time": 1700003600000, + * "open_price": 64800, + * "close_price": 65200, + * "update_type": "close" + * } + * } + */ + WsAlertAssetPriceWindowUpdateEvent: { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + event: "asset_price_window_update"; + /** + * Format: int64 + * @description Unix timestamp in milliseconds + */ + timestamp: number; + data: components["schemas"]["AssetPriceWindowUpdatePayload"]; + }; + /** @description Typed subscribe request for the alerts WebSocket. The request shape depends on `event` and reuses the matching webhook filter schema. */ + WsAlertSubscribeMessage: components["schemas"]["WsAlertTraderFirstTradeSubscribeMessage"] | components["schemas"]["WsAlertTraderNewMarketSubscribeMessage"] | components["schemas"]["WsAlertTraderWhaleTradeSubscribeMessage"] | components["schemas"]["WsAlertTraderNewTradeSubscribeMessage"] | components["schemas"]["WsAlertTraderGlobalPnlSubscribeMessage"] | components["schemas"]["WsAlertTraderMarketPnlSubscribeMessage"] | components["schemas"]["WsAlertTraderEventPnlSubscribeMessage"] | components["schemas"]["WsAlertConditionMetricsSubscribeMessage"] | components["schemas"]["WsAlertEventMetricsSubscribeMessage"] | components["schemas"]["WsAlertPositionMetricsSubscribeMessage"] | components["schemas"]["WsAlertMarketVolumeMilestoneSubscribeMessage"] | components["schemas"]["WsAlertEventVolumeMilestoneSubscribeMessage"] | components["schemas"]["WsAlertPositionVolumeMilestoneSubscribeMessage"] | components["schemas"]["WsAlertProbabilitySpikeSubscribeMessage"] | components["schemas"]["WsAlertPriceSpikeSubscribeMessage"] | components["schemas"]["WsAlertMarketVolumeSpikeSubscribeMessage"] | components["schemas"]["WsAlertEventVolumeSpikeSubscribeMessage"] | components["schemas"]["WsAlertPositionVolumeSpikeSubscribeMessage"] | components["schemas"]["WsAlertCloseToBondSubscribeMessage"] | components["schemas"]["WsAlertMarketCreatedSubscribeMessage"] | components["schemas"]["WsAlertAssetPriceTickSubscribeMessage"] | components["schemas"]["WsAlertAssetPriceWindowUpdateSubscribeMessage"]; + /** @description Typed unsubscribe request for the alerts WebSocket. The request shape depends on `event` and must match the original subscription filters. */ + WsAlertUnsubscribeMessage: components["schemas"]["WsAlertTraderFirstTradeUnsubscribeMessage"] | components["schemas"]["WsAlertTraderNewMarketUnsubscribeMessage"] | components["schemas"]["WsAlertTraderWhaleTradeUnsubscribeMessage"] | components["schemas"]["WsAlertTraderNewTradeUnsubscribeMessage"] | components["schemas"]["WsAlertTraderGlobalPnlUnsubscribeMessage"] | components["schemas"]["WsAlertTraderMarketPnlUnsubscribeMessage"] | components["schemas"]["WsAlertTraderEventPnlUnsubscribeMessage"] | components["schemas"]["WsAlertConditionMetricsUnsubscribeMessage"] | components["schemas"]["WsAlertEventMetricsUnsubscribeMessage"] | components["schemas"]["WsAlertPositionMetricsUnsubscribeMessage"] | components["schemas"]["WsAlertMarketVolumeMilestoneUnsubscribeMessage"] | components["schemas"]["WsAlertEventVolumeMilestoneUnsubscribeMessage"] | components["schemas"]["WsAlertPositionVolumeMilestoneUnsubscribeMessage"] | components["schemas"]["WsAlertProbabilitySpikeUnsubscribeMessage"] | components["schemas"]["WsAlertPriceSpikeUnsubscribeMessage"] | components["schemas"]["WsAlertMarketVolumeSpikeUnsubscribeMessage"] | components["schemas"]["WsAlertEventVolumeSpikeUnsubscribeMessage"] | components["schemas"]["WsAlertPositionVolumeSpikeUnsubscribeMessage"] | components["schemas"]["WsAlertCloseToBondUnsubscribeMessage"] | components["schemas"]["WsAlertMarketCreatedUnsubscribeMessage"] | components["schemas"]["WsAlertAssetPriceTickUnsubscribeMessage"] | components["schemas"]["WsAlertAssetPriceWindowUpdateUnsubscribeMessage"]; + /** @description Typed pushed-event envelope for the alerts WebSocket. The `data` payload depends on `event` and matches the corresponding HTTP webhook payload schema. */ + WsAlertEventPayload: components["schemas"]["WsAlertTraderFirstTradeEvent"] | components["schemas"]["WsAlertTraderNewMarketEvent"] | components["schemas"]["WsAlertTraderWhaleTradeEvent"] | components["schemas"]["WsAlertTraderNewTradeEvent"] | components["schemas"]["WsAlertTraderGlobalPnlEvent"] | components["schemas"]["WsAlertTraderMarketPnlEvent"] | components["schemas"]["WsAlertTraderEventPnlEvent"] | components["schemas"]["WsAlertConditionMetricsEvent"] | components["schemas"]["WsAlertEventMetricsEvent"] | components["schemas"]["WsAlertPositionMetricsEvent"] | components["schemas"]["WsAlertMarketVolumeMilestoneEvent"] | components["schemas"]["WsAlertEventVolumeMilestoneEvent"] | components["schemas"]["WsAlertPositionVolumeMilestoneEvent"] | components["schemas"]["WsAlertProbabilitySpikeEvent"] | components["schemas"]["WsAlertPriceSpikeEvent"] | components["schemas"]["WsAlertMarketVolumeSpikeEvent"] | components["schemas"]["WsAlertEventVolumeSpikeEvent"] | components["schemas"]["WsAlertPositionVolumeSpikeEvent"] | components["schemas"]["WsAlertCloseToBondEvent"] | components["schemas"]["WsAlertMarketCreatedEvent"] | components["schemas"]["WsAlertAssetPriceTickEvent"] | components["schemas"]["WsAlertAssetPriceWindowUpdateEvent"]; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export type operations = Record; + +export interface WsAlertSubscribeMap { + trader_first_trade: components["schemas"]["WsAlertTraderFirstTradeSubscribeMessage"]; + trader_new_market: components["schemas"]["WsAlertTraderNewMarketSubscribeMessage"]; + trader_whale_trade: components["schemas"]["WsAlertTraderWhaleTradeSubscribeMessage"]; + trader_new_trade: components["schemas"]["WsAlertTraderNewTradeSubscribeMessage"]; + trader_global_pnl: components["schemas"]["WsAlertTraderGlobalPnlSubscribeMessage"]; + trader_market_pnl: components["schemas"]["WsAlertTraderMarketPnlSubscribeMessage"]; + trader_event_pnl: components["schemas"]["WsAlertTraderEventPnlSubscribeMessage"]; + condition_metrics: components["schemas"]["WsAlertConditionMetricsSubscribeMessage"]; + event_metrics: components["schemas"]["WsAlertEventMetricsSubscribeMessage"]; + position_metrics: components["schemas"]["WsAlertPositionMetricsSubscribeMessage"]; + market_volume_milestone: components["schemas"]["WsAlertMarketVolumeMilestoneSubscribeMessage"]; + event_volume_milestone: components["schemas"]["WsAlertEventVolumeMilestoneSubscribeMessage"]; + position_volume_milestone: components["schemas"]["WsAlertPositionVolumeMilestoneSubscribeMessage"]; + probability_spike: components["schemas"]["WsAlertProbabilitySpikeSubscribeMessage"]; + price_spike: components["schemas"]["WsAlertPriceSpikeSubscribeMessage"]; + market_volume_spike: components["schemas"]["WsAlertMarketVolumeSpikeSubscribeMessage"]; + event_volume_spike: components["schemas"]["WsAlertEventVolumeSpikeSubscribeMessage"]; + position_volume_spike: components["schemas"]["WsAlertPositionVolumeSpikeSubscribeMessage"]; + close_to_bond: components["schemas"]["WsAlertCloseToBondSubscribeMessage"]; + market_created: components["schemas"]["WsAlertMarketCreatedSubscribeMessage"]; + asset_price_tick: components["schemas"]["WsAlertAssetPriceTickSubscribeMessage"]; + asset_price_window_update: components["schemas"]["WsAlertAssetPriceWindowUpdateSubscribeMessage"]; +} + +export interface WsAlertEventDataMap { + trader_first_trade: components["schemas"]["FirstTradePayload"]; + trader_new_market: components["schemas"]["NewMarketPayload"]; + trader_whale_trade: components["schemas"]["WhaleTradePayload"]; + trader_new_trade: components["schemas"]["NewTradePayload"]; + trader_global_pnl: components["schemas"]["GlobalPnlPayload"]; + trader_market_pnl: components["schemas"]["MarketPnlPayload"]; + trader_event_pnl: components["schemas"]["EventPnlPayload"]; + condition_metrics: components["schemas"]["ConditionMetricsPayload"]; + event_metrics: components["schemas"]["EventMetricsPayload"]; + position_metrics: components["schemas"]["PositionMetricsPayload"]; + market_volume_milestone: components["schemas"]["VolumeMilestonePayload"]; + event_volume_milestone: components["schemas"]["EventVolumeMilestonePayload"]; + position_volume_milestone: components["schemas"]["PositionVolumeMilestonePayload"]; + probability_spike: components["schemas"]["ProbabilitySpikePayload"]; + price_spike: components["schemas"]["PriceSpikePayload"]; + market_volume_spike: components["schemas"]["MarketVolumeSpikePayload"]; + event_volume_spike: components["schemas"]["EventVolumeSpikePayload"]; + position_volume_spike: components["schemas"]["PositionVolumeSpikePayload"]; + close_to_bond: components["schemas"]["CloseToBondPayload"]; + market_created: components["schemas"]["MarketCreatedPayload"]; + asset_price_tick: components["schemas"]["AssetPriceTickPayload"]; + asset_price_window_update: components["schemas"]["AssetPriceWindowUpdatePayload"]; +} + +export type WsAlertEventName = keyof WsAlertSubscribeMap; diff --git a/src/generated/ws.ts b/src/generated/ws.ts new file mode 100644 index 0000000..34f4ef6 --- /dev/null +++ b/src/generated/ws.ts @@ -0,0 +1,921 @@ +export type paths = Record; +export type webhooks = Record; +export interface components { + schemas: { + /** @description Server-pushed CLOB reward change event. Envelope type: "clob_rewards_update". */ + ClobRewardsUpdateEvent: { + /** + * @description Type of change + * @enum {string} + */ + event_type?: "added" | "removed" | "updated"; + /** @description Affected market condition ID */ + condition_id?: string; + /** @description Full reward state (null for 'removed' events) */ + reward?: { + condition_id?: string; + rewards_config?: { + id?: number; + /** @description Reward token address (e.g. USDC) */ + asset_address?: string; + /** Format: date */ + start_date?: string; + /** Format: date */ + end_date?: string; + /** @description Daily reward rate in USDC */ + rate_per_day?: number; + /** @description Cumulative rewards distributed */ + total_rewards?: number; + }[]; + /** @description Max spread to qualify for rewards */ + rewards_max_spread?: number | null; + /** @description Min order size to qualify for rewards */ + rewards_min_size?: number | null; + /** @description Native (non-sponsored) daily rate */ + native_daily_rate?: number | null; + /** @description Sponsored daily rate */ + sponsored_daily_rate?: number | null; + /** @description Combined daily rate (native + sponsored) */ + total_daily_rate?: number | null; + /** @description Number of sponsors */ + sponsors_count?: number | null; + } | null; + /** @description Unix timestamp in milliseconds */ + timestamp_ms?: number; + }; + /** @description Server acknowledgement for a CLOB rewards subscription. Envelope type: "clob_rewards_stream_subscribe_response". */ + ClobRewardsSubscribeResponse: { + /** @description Accepted condition IDs */ + condition_ids?: string[]; + /** @description Whether subscribed to all changes */ + subscribe_all?: boolean; + /** @description Filter values that were rejected */ + rejected?: string[]; + }; + /** @description Subscribe to CLOB reward changes. Either provide specific condition_ids or set subscribe_all to true. */ + ClobRewardsSubscribeMessage: { + /** @enum {string} */ + action: "subscribe" | "unsubscribe_all"; + /** @description Condition IDs to watch for reward changes. */ + condition_ids?: string[]; + /** @description If true, receive ALL reward changes across all markets. Overrides condition_ids. */ + subscribe_all?: boolean; + }; + /** @description Server-pushed event: full CLOB orderbook snapshot for an outcome token. Envelope type: "order_book_update". Delivered whenever the book changes for a subscribed condition or position. */ + OrderBookUpdateEvent: { + /** @description Hex token ID (position / outcome token) */ + asset_id: string; + /** @description Condition ID (hex) */ + market: string; + /** @description Bid levels sorted best-first (highest price first) */ + bids: components["schemas"]["OrderBookLevel"][]; + /** @description Ask levels sorted best-first (lowest price first) */ + asks: components["schemas"]["OrderBookLevel"][]; + /** + * Format: int64 + * @description Unix milliseconds from CLOB message + */ + timestamp: number; + /** @description Orderbook content hash — identical hash means no change */ + hash: string; + /** @description Best bid price (0–1) */ + best_bid?: number | null; + /** @description Best ask price (0–1) */ + best_ask?: number | null; + /** @description (best_bid + best_ask) / 2 */ + mid_price?: number | null; + /** @description best_ask − best_bid */ + spread?: number | null; + /** @description Total USD value of all bid levels */ + bid_liquidity_usd?: number | null; + /** @description Total USD value of all ask levels */ + ask_liquidity_usd?: number | null; + /** @description Number of bid price levels */ + bid_levels?: number | null; + /** @description Number of ask price levels */ + ask_levels?: number | null; + }; + /** @description A single price level: [price_string, size_string] */ + OrderBookLevel: string[]; + /** @description Subscribe to the trades stream. No filters = subscribe to all trades. */ + TradesStreamSubscribeMessage: { + /** @enum {string} */ + action: "subscribe" | "unsubscribe_all"; + /** @description 64-char hex condition IDs (with or without 0x prefix) */ + condition_ids?: string[]; + /** @description Market slugs */ + market_slugs?: string[]; + /** @description Event slugs — subscribes to all markets under each event */ + event_slugs?: string[]; + /** @description ERC-1155 outcome token IDs (decimal or hex strings) */ + position_ids?: string[]; + /** @description Trader wallet addresses (lowercase 0x-prefixed) */ + traders?: string[]; + /** @description Only receive events of these types. Empty array = all types. */ + trade_types?: ("OrderFilled" | "OrdersMatched" | "Redemption" | "Merge" | "Split" | "Cancelled" | "PositionsConverted" | "Initialization" | "Proposal" | "Dispute" | "Settled" | "Resolution" | "ConditionResolution" | "Reset" | "Flag" | "Unflag" | "Pause" | "Unpause" | "ManualResolution" | "NegRiskOutcomeReported" | "RegisterToken" | "Approval")[]; + /** + * @description Trade status filter: "confirmed" (default) = on-chain only, "pending" = mempool only, "all" = both + * @enum {string} + */ + status?: "confirmed" | "pending" | "all"; + /** @description Explicitly subscribe to all trades. Also implicitly true when no filters are provided. */ + subscribe_all?: boolean; + }; + /** @description Server acknowledgement for a trades stream subscription */ + TradesStreamSubscribeResponse: { + condition_ids?: string[]; + market_slugs?: string[]; + event_slugs?: string[]; + position_ids?: string[]; + traders?: string[]; + trade_types?: string[]; + /** @enum {string} */ + status?: "confirmed" | "pending" | "all"; + subscribe_all?: boolean; + /** @description Filter values that were rejected (invalid format or unknown type) */ + rejected?: string[]; + }; + /** + * @description Server-pushed event. Discriminated by `trade_type` — each variant only includes relevant fields. + * + * Envelope: `{"type": "trade_stream_update", "room_id": "polymarket_trades", "status": "confirmed"|"pending", "data": {...}}` + * + * **Pending trades:** `block`, `confirmed_at`, `log_index`, `block_index` are absent. `received_at` (milliseconds) is included instead. For OrderFilled/OrdersMatched, `order_hash`, `taker`, `fee`, `fee_shares`, `fee_pct` are also absent. + */ + TradeStreamEvent: { + /** @enum {string} */ + trade_type: "OrderFilled" | "OrdersMatched"; + id: string; + hash: string; + /** @description Absent for pending trades */ + block?: number; + /** @description Unix seconds. Absent for pending trades */ + confirmed_at?: number; + /** @description Unix milliseconds. Present for pending trades only */ + received_at?: number; + /** @description Absent for pending trades */ + log_index?: number; + /** @description Absent for pending trades */ + block_index?: number; + /** @description Absent for pending trades */ + order_hash?: string; + trader: { + address?: string; + name?: string | null; + pseudonym?: string | null; + profile_image?: string | null; + x_username?: string | null; + verified_badge?: boolean; + }; + /** @description Absent for pending trades */ + taker?: string; + /** @enum {string} */ + side?: "Buy" | "Sell"; + condition_id?: string | null; + position_id?: string; + outcome?: string | null; + outcome_index?: number | null; + question?: string | null; + image_url?: string | null; + slug?: string | null; + event_slug?: string | null; + usd_amount?: number; + shares_amount?: number; + price?: number; + probability?: number | null; + /** @description Absent for pending trades */ + fee?: number; + /** @description Absent for pending trades */ + fee_shares?: number; + /** @description Absent for pending trades */ + fee_pct?: number; + exchange: number; + } | { + /** @enum {string} */ + trade_type: "Redemption"; + id: string; + hash: string; + block?: number; + confirmed_at?: number; + received_at?: number; + log_index?: number; + block_index?: number; + trader: { + address?: string; + name?: string | null; + pseudonym?: string | null; + profile_image?: string | null; + x_username?: string | null; + verified_badge?: boolean; + }; + condition_id?: string | null; + outcome?: string | null; + outcome_index?: number | null; + question?: string | null; + image_url?: string | null; + slug?: string | null; + event_slug?: string | null; + usd_amount?: number; + winning_outcome_index?: number | null; + position_details?: { + position_id?: string; + outcome_index?: number; + amount?: string; + }[]; + exchange: number; + } | { + /** @enum {string} */ + trade_type: "Merge"; + id: string; + hash: string; + block?: number; + confirmed_at?: number; + received_at?: number; + log_index?: number; + block_index?: number; + trader: { + address?: string; + name?: string | null; + pseudonym?: string | null; + profile_image?: string | null; + x_username?: string | null; + verified_badge?: boolean; + }; + condition_id?: string | null; + question?: string | null; + image_url?: string | null; + slug?: string | null; + event_slug?: string | null; + usd_amount?: number; + position_details?: { + position_id?: string; + outcome_index?: number; + amount?: string; + }[]; + exchange: number; + } | { + /** @enum {string} */ + trade_type: "Split"; + id: string; + hash: string; + block?: number; + confirmed_at?: number; + received_at?: number; + log_index?: number; + block_index?: number; + trader: { + address?: string; + name?: string | null; + pseudonym?: string | null; + profile_image?: string | null; + x_username?: string | null; + verified_badge?: boolean; + }; + condition_id?: string | null; + question?: string | null; + image_url?: string | null; + slug?: string | null; + event_slug?: string | null; + usd_amount?: number; + position_details?: { + position_id?: string; + outcome_index?: number; + amount?: string; + }[]; + exchange: number; + } | { + /** @enum {string} */ + trade_type: "PositionsConverted"; + id: string; + hash: string; + block?: number; + confirmed_at?: number; + received_at?: number; + log_index?: number; + block_index?: number; + trader: { + address?: string; + name?: string | null; + pseudonym?: string | null; + profile_image?: string | null; + x_username?: string | null; + verified_badge?: boolean; + }; + market_id?: string; + index_set?: string; + shares_amount?: number; + exchange: number; + } | { + /** @enum {string} */ + trade_type: "Cancelled"; + id: string; + hash: string; + block?: number; + confirmed_at?: number; + received_at?: number; + log_index?: number; + block_index?: number; + order_hash?: string; + question?: string | null; + image_url?: string | null; + slug?: string | null; + event_slug?: string | null; + exchange: number; + } | { + /** @enum {string} */ + trade_type: "Initialization" | "Proposal" | "Dispute" | "Settled" | "Resolution" | "ConditionResolution" | "Reset" | "Flag" | "Unflag" | "Pause" | "Unpause" | "ManualResolution" | "NegRiskOutcomeReported"; + id: string; + hash: string; + block?: number; + confirmed_at?: number; + received_at?: number; + log_index?: number; + block_index?: number; + oracle_contract: string; + condition_id: string; + question?: string | null; + image_url?: string | null; + slug?: string | null; + event_slug?: string | null; + assertion_id?: string | null; + proposer?: string | null; + disputer?: string | null; + proposed_outcome?: string | null; + settled_price?: number | null; + disputed?: boolean | null; + settlement_resolution?: boolean | null; + bond?: string | null; + expiration_time?: number | null; + creator?: string | null; + reward_token?: string | null; + reward?: string | null; + proposal_bond?: string | null; + } | { + /** @enum {string} */ + trade_type: "RegisterToken"; + id: string; + hash: string; + block?: number; + confirmed_at?: number; + received_at?: number; + log_index?: number; + block_index?: number; + condition_id: string; + token0?: string; + token1?: string; + question?: string | null; + image_url?: string | null; + slug?: string | null; + event_slug?: string | null; + exchange: number; + } | { + /** @enum {string} */ + trade_type: "Approval"; + id: string; + hash: string; + block?: number; + confirmed_at?: number; + received_at?: number; + log_index?: number; + block_index?: number; + trader: { + address?: string; + name?: string | null; + pseudonym?: string | null; + profile_image?: string | null; + x_username?: string | null; + verified_badge?: boolean; + }; + operator?: string; + approved?: boolean; + question?: string | null; + image_url?: string | null; + slug?: string | null; + event_slug?: string | null; + exchange: number; + }; + /** @description Subscribe to the asset prices stream. Empty asset_symbols = all assets. */ + AssetPricesSubscribeMessage: { + /** @enum {string} */ + action: "subscribe" | "unsubscribe_all"; + /** @description Uppercase asset symbols (e.g. "BTC", "ETH"). Empty = subscribe to all. */ + asset_symbols?: string[]; + }; + /** @description Server acknowledgement for an asset prices subscription */ + AssetPricesSubscribeResponse: { + /** @description Accepted symbols. Empty array means subscribed to all symbols. */ + asset_symbols?: string[]; + }; + /** @description Server-pushed event: a crypto-asset price tick. Envelope type: "asset_price_tick". */ + AssetPriceTickEvent: { + /** @description Always "asset_price_tick" */ + event_type: string; + /** @description Uppercase asset symbol (e.g. "BTC") */ + symbol: string; + /** @description Current price in USD */ + price: number; + /** + * Format: int64 + * @description Event timestamp in Unix milliseconds + */ + timestamp_ms: number; + /** + * Format: int64 + * @description Publish timestamp in Unix milliseconds + */ + published_at: number; + }; + /** @description Server-pushed event: candle open or close for a crypto asset. Envelope type: "asset_price_window_update". Delivered from both `polymarket_asset_prices` and `polymarket_asset_window_updates` rooms. */ + AssetPriceWindowUpdateEvent: { + /** @description Always "asset_price_window_update" */ + event_type: string; + /** @description Uppercase asset symbol (e.g. "BTC") */ + symbol: string; + /** @description Candle size / timeframe (e.g. "5m", "1h", "1d") */ + variant: string; + /** + * Format: int64 + * @description Candle start in Unix milliseconds + */ + start_time: number; + /** + * Format: int64 + * @description Candle end in Unix milliseconds + */ + end_time: number; + /** @description Candle open price in USD */ + open_price: number; + /** @description Candle close price in USD */ + close_price: number; + /** @description "open" = candle starting, "close" = candle finalised */ + update_type: string; + /** + * Format: int64 + * @description Publish timestamp in Unix milliseconds + */ + published_at: number; + }; + /** @description Subscribe to the asset window updates stream. At least one of asset_symbols or timeframes must be non-empty. */ + AssetWindowUpdatesSubscribeMessage: { + /** @enum {string} */ + action: "subscribe" | "unsubscribe_all"; + /** @description Uppercase asset symbols (e.g. "BTC", "ETH") */ + asset_symbols?: string[]; + /** @description Candle sizes to filter by. "1d" and "24h" are treated as equivalent. */ + timeframes?: ("5m" | "15m" | "1h" | "4h" | "1d" | "24h")[]; + }; + /** @description Server acknowledgement for an asset window updates subscription */ + AssetWindowUpdatesSubscribeResponse: { + asset_symbols?: string[]; + timeframes?: string[]; + /** @description Set if the subscription was rejected (e.g. no filters provided) */ + error?: string | null; + }; + /** @description Server-pushed event from the polymarket_asset_window_updates room. Same payload as AssetPriceWindowUpdateEvent. Envelope type: "asset_price_window_update". */ + AssetWindowUpdateEvent: components["schemas"]["AssetPriceWindowUpdateEvent"]; + /** @description Subscribe to the market metrics stream. condition_ids is required and must be non-empty. */ + MarketMetricsSubscribeMessage: { + /** @enum {string} */ + action: "subscribe" | "unsubscribe_all"; + /** @description 64-char hex condition IDs (with or without 0x prefix) */ + condition_ids: string[]; + }; + /** @description Server acknowledgement for a market metrics subscription */ + MarketMetricsSubscribeResponse: { + condition_ids?: string[]; + /** @description Condition IDs that were rejected (invalid format) */ + rejected?: string[]; + /** @description Set if the entire subscription was rejected */ + error?: string | null; + }; + /** @description Server-pushed event: metrics update for one timeframe of a condition. Envelope type: "market_metrics_update". One event is emitted per timeframe window on each update. */ + MarketMetricsEvent: { + /** @description 64-char hex condition ID */ + condition_id: string; + /** @enum {string} */ + timeframe: "1m" | "5m" | "30m" | "1h" | "6h" | "24h" | "7d" | "30d"; + /** + * Format: int64 + * @description Optional event timestamp (Unix seconds) + */ + timestamp?: number | null; + /** @description USD volume in this timeframe window (decimal string) */ + usd_volume: string; + /** @description Total fees in this window */ + fees: number; + /** + * Format: int64 + * @description Number of transactions + */ + txns: number; + /** Format: int64 */ + unique_traders: number; + }; + /** @description Subscribe to the event metrics stream. event_slugs is required and must be non-empty. */ + EventMetricsSubscribeMessage: { + /** @enum {string} */ + action: "subscribe" | "unsubscribe_all"; + /** @description Event slugs (lowercase) */ + event_slugs: string[]; + }; + /** @description Server acknowledgement for an event metrics subscription */ + EventMetricsSubscribeResponse: { + event_slugs?: string[]; + rejected?: string[]; + error?: string | null; + }; + /** @description Server-pushed event: aggregated metrics update for one timeframe of an event. Envelope type: "event_metrics_update". One event is emitted per timeframe window on each update. */ + EventMetricsEvent: { + event_slug: string; + /** @enum {string} */ + timeframe: "1m" | "5m" | "30m" | "1h" | "6h" | "24h" | "7d" | "30d"; + /** + * Format: int64 + * @description Optional event timestamp (Unix seconds) + */ + timestamp?: number | null; + /** @description USD volume in this timeframe window (decimal string) */ + usd_volume: string; + fees: number; + /** Format: int64 */ + txns: number; + /** Format: int64 */ + unique_traders: number; + }; + /** @description Subscribe to the position metrics stream. position_ids is required and must be non-empty. */ + PositionMetricsSubscribeMessage: { + /** @enum {string} */ + action: "subscribe" | "unsubscribe_all"; + /** @description ERC-1155 outcome token IDs (decimal or hex strings) */ + position_ids: string[]; + }; + /** @description Server acknowledgement for a position metrics subscription */ + PositionMetricsSubscribeResponse: { + position_ids?: string[]; + rejected?: string[]; + error?: string | null; + }; + /** @description Server-pushed event: metrics update for one timeframe of an outcome token. Envelope type: "position_metrics_update". One event is emitted per timeframe window on each update. */ + PositionMetricsEvent: { + /** @description 64-char hex condition ID */ + condition_id: string; + /** @description ERC-1155 token ID (decimal string) */ + position_id: string; + /** @description Outcome name (e.g. "Yes") */ + outcome?: string | null; + outcome_index?: number | null; + /** @enum {string} */ + timeframe: "1m" | "5m" | "30m" | "1h" | "6h" | "24h" | "7d" | "30d"; + /** + * Format: int64 + * @description Optional event timestamp (Unix seconds) + */ + timestamp?: number | null; + /** @description Total USD volume (decimal string) */ + usd_volume: string; + /** @description USD buy volume (decimal string) */ + usd_buy_volume: string; + /** @description USD sell volume (decimal string) */ + usd_sell_volume: string; + fees: number; + /** Format: int64 */ + txns: number; + /** Format: int64 */ + buys: number; + /** Format: int64 */ + sells: number; + /** Format: int64 */ + unique_traders: number; + /** @description OHLC open price (0–1) */ + price_open: number; + /** @description OHLC close price (0–1) */ + price_close: number; + /** @description OHLC high price (0–1) */ + price_high: number; + /** @description OHLC low price (0–1) */ + price_low: number; + /** @description Implied probability at open (0–1) */ + probability_open: number; + /** @description Implied probability at close (0–1) */ + probability_close: number; + /** @description Highest implied probability in window (0–1) */ + probability_high: number; + /** @description Lowest implied probability in window (0–1) */ + probability_low: number; + /** Format: int64 */ + historical_confirmed_at: number; + /** Format: int64 */ + latest_confirmed_at: number; + /** Format: int64 */ + latest_block: number; + }; + /** @description Subscribe to the trader PnL stream. traders is required and must be non-empty. */ + TraderPnlSubscribeMessage: { + /** @enum {string} */ + action: "subscribe" | "unsubscribe_all"; + /** @description EVM wallet addresses */ + traders: string[]; + }; + /** @description Server acknowledgement for a trader PnL subscription */ + TraderPnlSubscribeResponse: { + traders?: string[]; + rejected?: string[]; + error?: string | null; + }; + /** @description Server-pushed event: global (portfolio-level) PnL update for a trader. Envelope type: "trader_global_pnl_update". */ + TraderGlobalPnlEvent: { + /** @description Trader EVM wallet address */ + trader: string; + /** @description Total realized PnL in USD (decimal string) */ + realized_pnl_usd: string; + /** Format: int64 */ + events_traded?: number | null; + /** Format: int64 */ + markets_traded?: number | null; + /** Format: int64 */ + total_buys?: number | null; + /** Format: int64 */ + total_sells?: number | null; + /** Format: int64 */ + total_redemptions?: number | null; + /** Format: int64 */ + total_merges?: number | null; + /** @description Total USD volume (decimal string) */ + total_volume_usd: string; + buy_volume_usd: string; + sell_volume_usd: string; + redemption_volume_usd: string; + merge_volume_usd: string; + /** Format: int64 */ + markets_won?: number | null; + /** Format: int64 */ + markets_lost?: number | null; + /** @description Win rate percentage (decimal string) */ + market_win_rate_pct: string; + avg_pnl_per_market: string; + avg_pnl_per_trade: string; + avg_hold_time_seconds: string; + total_fees: string; + best_trade_pnl_usd: string; + best_trade_condition_id?: string | null; + worst_trade_pnl_usd: string; + worst_trade_condition_id?: string | null; + /** + * Format: int64 + * @description Unix seconds + */ + first_trade_at?: number | null; + /** + * Format: int64 + * @description Unix seconds + */ + last_trade_at?: number | null; + /** Format: int64 */ + timestamp?: number | null; + /** @description "1d", "7d", "30d", or "lifetime" */ + timeframe?: string | null; + }; + /** @description Server-pushed event: per-market PnL update for a trader. Envelope type: "trader_market_pnl_update". */ + TraderMarketPnlEvent: { + trader: string; + /** @description 64-char hex condition ID */ + condition_id: string; + event_slug?: string | null; + /** Format: int64 */ + outcomes_traded?: number | null; + /** Format: int64 */ + total_buys?: number | null; + /** Format: int64 */ + total_sells?: number | null; + /** Format: int64 */ + total_redemptions?: number | null; + /** Format: int64 */ + total_merges?: number | null; + /** @description Total buy volume in USD (decimal string) */ + buy_usd: string; + sell_usd: string; + redemption_usd: string; + merge_usd: string; + /** @description Realized PnL in USD (decimal string) */ + realized_pnl_usd: string; + /** Format: int64 */ + winning_outcomes?: number | null; + total_fees: string; + /** + * Format: int64 + * @description Unix seconds + */ + first_trade_at?: number | null; + /** + * Format: int64 + * @description Unix seconds + */ + last_trade_at?: number | null; + /** Format: int64 */ + timestamp?: number | null; + /** @description "1d", "7d", "30d", or "lifetime" */ + timeframe?: string | null; + }; + /** @description Server-pushed event: per-event PnL update for a trader. Envelope type: "trader_event_pnl_update". */ + TraderEventPnlEvent: { + trader: string; + event_slug: string; + /** Format: int64 */ + markets_traded?: number | null; + /** Format: int64 */ + outcomes_traded?: number | null; + /** Format: int64 */ + total_buys?: number | null; + /** Format: int64 */ + total_sells?: number | null; + /** Format: int64 */ + total_redemptions?: number | null; + /** Format: int64 */ + total_merges?: number | null; + /** @description Total USD volume (decimal string) */ + total_volume_usd: string; + buy_usd: string; + sell_usd: string; + redemption_usd: string; + merge_usd: string; + /** @description Realized PnL in USD (decimal string) */ + realized_pnl_usd: string; + /** Format: int64 */ + winning_markets?: number | null; + /** Format: int64 */ + losing_markets?: number | null; + total_fees: string; + /** + * Format: int64 + * @description Unix seconds + */ + first_trade_at?: number | null; + /** + * Format: int64 + * @description Unix seconds + */ + last_trade_at?: number | null; + /** Format: int64 */ + timestamp?: number | null; + /** @description "1d", "7d", "30d", or "lifetime" */ + timeframe?: string | null; + }; + /** @description Subscribe to the trader positions stream. traders is required and must be non-empty. */ + TraderPositionsSubscribeMessage: { + /** @enum {string} */ + action: "subscribe" | "unsubscribe_all"; + /** @description EVM wallet addresses */ + traders: string[]; + }; + /** @description Server acknowledgement for a trader positions subscription */ + TraderPositionsSubscribeResponse: { + traders?: string[]; + rejected?: string[]; + error?: string | null; + }; + /** @description Server-pushed event: full position snapshot for a tracked trader. Envelope type: "trader_position_update". Pushed whenever a position's PnL changes. */ + TraderPositionUpdateEvent: { + /** @description Trader EVM wallet address */ + trader: string; + /** @description ERC-1155 token ID (decimal string) */ + position_id?: string | null; + condition_id?: string | null; + market_slug?: string | null; + event_slug?: string | null; + /** @description Market title / question */ + title?: string | null; + image_url?: string | null; + /** @description Outcome name (e.g. "Yes") */ + outcome?: string | null; + outcome_index?: number | null; + /** @description True if this outcome resolved as winner */ + won?: boolean | null; + /** Format: int64 */ + total_buys?: number | null; + /** Format: int64 */ + total_sells?: number | null; + total_shares_bought?: number | null; + total_shares_sold?: number | null; + total_buy_usd?: number | null; + total_sell_usd?: number | null; + redemption_usd?: number | null; + /** @description Average entry price (0–1) */ + avg_entry_price?: number | null; + /** @description Average exit price (0–1) */ + avg_exit_price?: number | null; + realized_pnl_usd?: number | null; + total_fees?: number | null; + /** + * Format: int64 + * @description Unix seconds + */ + first_trade_at?: number | null; + /** + * Format: int64 + * @description Unix seconds + */ + last_trade_at?: number | null; + /** @description Current ERC-1155 token balance */ + current_shares_balance?: number | null; + /** @description Realized PnL as a percentage of cost basis */ + realized_pnl_pct?: number | null; + }; + /** @description Subscribe to the accounts stream. `wallets` is required. Share balance updates (`accounts_update`) are always delivered. Set `include_usdce` or `include_matic` to also receive those balance streams. */ + AccountsSubscribeMessage: { + /** @enum {string} */ + action: "subscribe" | "unsubscribe_all"; + /** @description EVM wallet addresses */ + wallets: string[]; + /** @description Also stream USDCe collateral balance updates for subscribed wallets */ + include_usdce?: boolean; + /** @description Also stream MATIC gas balance updates for subscribed wallets */ + include_matic?: boolean; + }; + /** @description Server acknowledgement for an accounts subscription */ + AccountsSubscribeResponse: { + wallets?: string[]; + /** @description Addresses rejected (invalid format) */ + rejected?: string[]; + include_usdce?: boolean; + include_matic?: boolean; + error?: string | null; + }; + /** @description Server-pushed event: ERC-1155 outcome token balance change for a wallet. Envelope type: "accounts_update". */ + AccountsUpdateEvent: { + /** @description Wallet address */ + wallet: string; + /** @description ERC-1155 outcome token ID (decimal string) */ + position_id: string; + /** @description Current token balance (decimal string) */ + balance: string; + /** Format: int64 */ + block_number: number; + /** + * Format: int64 + * @description Unix seconds + */ + updated_at: number; + /** @description Condition ID — omitted when not available */ + condition_id?: string; + /** @description Event slug — omitted when not available */ + event_slug?: string; + }; + /** @description Server-pushed event: USDCe collateral balance change for a wallet. Envelope type: "usdce_update". Only delivered when `include_usdce: true`. */ + UsdceUpdateEvent: { + /** @description Wallet address (0x-prefixed hex) */ + address: string; + /** @description USDCe contract address — omitted when not available */ + token_address?: string; + /** @description Current USDCe balance (decimal string) — omitted when not available */ + balance?: string; + /** Format: uint64 */ + block_number: number; + /** + * Format: int64 + * @description Unix seconds + */ + updated_at: number; + }; + /** @description Server-pushed event: MATIC native balance change for a wallet. Envelope type: "matic_update". Only delivered when `include_matic: true`. */ + MaticUpdateEvent: { + /** @description Wallet address (0x-prefixed hex) */ + address: string; + /** @description Native token address — omitted when not available */ + token_address?: string; + /** @description Current MATIC balance (decimal string) — omitted when not available */ + balance?: string; + /** Format: uint64 */ + block_number: number; + /** + * Format: int64 + * @description Unix seconds + */ + updated_at: number; + }; + /** @description Subscribe to the order book stream. At least one filter is required. Maximum 500 combined condition_ids + position_ids per client. No `type` field is needed — the server routes by room_id. */ + OrderBookSubscribeMessage: { + /** @enum {string} */ + action: "subscribe" | "unsubscribe_all"; + /** @description Condition IDs (markets). All positions within each market are delivered. */ + condition_ids?: string[]; + /** @description Token / asset IDs (individual outcome positions, hex strings). */ + position_ids?: string[]; + }; + /** @description Server acknowledgement for an order book subscription. Envelope type: "order_book_stream_subscribe_response". */ + OrderBookSubscribeResponse: { + /** @description Accepted condition IDs */ + condition_ids?: string[]; + /** @description Accepted position IDs */ + position_ids?: string[]; + /** @description Filter values that were rejected (invalid format or limit exceeded) */ + rejected?: string[]; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export type operations = Record; diff --git a/src/index.ts b/src/index.ts index 2bb51ae..2ae9bb3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,7 @@ export type { StructClientConfig } from "./client.js"; export { HttpClient } from "./http.js"; export { StructError, HttpError, NetworkError, TimeoutError, WebSocketError, WebSocketClosedError } from "./errors.js"; export { StructWebSocket } from "./ws.js"; +export { StructAlertsWebSocket } from "./ws-alerts.js"; export { paginate } from "./paginate.js"; export type { Venue } from "./types/common.js"; export type * from "./types/index.js"; diff --git a/src/types/http.ts b/src/types/http.ts index 7bc5914..adeebd9 100644 --- a/src/types/http.ts +++ b/src/types/http.ts @@ -2,6 +2,7 @@ export interface RetryConfig { maxRetries?: number; initialDelayMs?: number; maxDelayMs?: number; + maxPendingMessages?: number; } export interface RequestHookInfo { diff --git a/src/types/index.ts b/src/types/index.ts index c2b6543..6ae04b2 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -12,6 +12,13 @@ export type { operations as WebhookOperations, paths as WebhookPaths, } from "../generated/webhooks.js"; +export type { WsSchemas, WsAlertSchemas } from "./ws-helpers.js"; +export type { + components as WsComponents, +} from "../generated/ws.js"; +export type { + components as WsAlertComponents, +} from "../generated/ws-alerts.js"; import type { Schemas, OperationQuery } from "./helpers.js"; import type { WebhookSchemas, WebhookOperationQuery, WebhookOperationRequestBody } from "./webhook-helpers.js"; @@ -456,11 +463,63 @@ export type { Address, PaginationParams, SortParams, Venue } from "./common.js"; export type { ConnectionState, StructWebSocketConfig, - PredictionTrade, - EnrichedPredictionTrade, - PredictionMarketMetadata, - PredictionWalletTrackingAlert, + AlertsWebSocketEventMap, + WsRoomId, + WsFiltersOptionalRoom, + WsFiltersRequiredRoom, WebSocketEventMap, - WebSocketMessage, - WebSocketServerMessage, + WsSubscriptionMap, + WsSubscribeResponseMap, + TradesSubscribeFilters, + WsTradeType, + WsTradeStatus, + WsAssetTimeframe, + AssetPricesSubscribeFilters, + AssetWindowUpdatesSubscribeFilters, + MarketMetricsSubscribeFilters, + EventMetricsSubscribeFilters, + PositionMetricsSubscribeFilters, + TraderPnlSubscribeFilters, + AccountsSubscribeFilters, + OrderBookSubscribeFilters, + TraderPositionsSubscribeFilters, + TraderPositionsSubscribeResponse, + TraderPositionUpdateEvent, + ClobRewardsSubscribeFilters, + ClobRewardsUpdateEvent, + ClobRewardsSubscribeResponse, + WsAlertSubscribedResponse, + WsAlertUnsubscribedResponse, + WsAlertErrorResponse, + WsAlertEventType, + WsAlertSubscribeMessage, + WsAlertUnsubscribeMessage, + WsAlertEventPayload, + WsAlertSubscribeMap, + WsAlertEventDataMap, + WsAlertEventName, + TradeStreamEvent, + AssetPriceTickEvent, + AssetPriceWindowUpdateEvent, + AssetWindowUpdateEvent, + MarketMetricsEvent, + EventMetricsEvent, + PositionMetricsEvent, + TraderGlobalPnlEvent, + TraderMarketPnlEvent, + TraderEventPnlEvent, + AccountsUpdateEvent, + UsdceUpdateEvent, + MaticUpdateEvent, + WsOrderBookLevel, + OrderBookUpdateEvent, + TradesStreamSubscribeResponse, + AssetPricesSubscribeResponse, + AssetWindowUpdatesSubscribeResponse, + MarketMetricsSubscribeResponse, + EventMetricsSubscribeResponse, + PositionMetricsSubscribeResponse, + TraderPnlSubscribeResponse, + AccountsSubscribeResponse, + OrderBookSubscribeResponse, } from "./ws.js"; diff --git a/src/types/ws-helpers.ts b/src/types/ws-helpers.ts new file mode 100644 index 0000000..2dafb5e --- /dev/null +++ b/src/types/ws-helpers.ts @@ -0,0 +1,11 @@ +import type { components } from "../generated/ws.js"; +import type { + components as alertComponents, + WsAlertSubscribeMap, + WsAlertEventDataMap, + WsAlertEventName, +} from "../generated/ws-alerts.js"; + +export type WsSchemas = components["schemas"]; +export type WsAlertSchemas = alertComponents["schemas"]; +export type { WsAlertSubscribeMap, WsAlertEventDataMap, WsAlertEventName }; diff --git a/src/types/ws.ts b/src/types/ws.ts index 4ae0594..5f13ff4 100644 --- a/src/types/ws.ts +++ b/src/types/ws.ts @@ -1,115 +1,155 @@ -import type { Address } from "./common.js"; import type { RetryConfig } from "./http.js"; +import type { + WsSchemas, + WsAlertSchemas, + WsAlertSubscribeMap, + WsAlertEventDataMap, + WsAlertEventName, +} from "./ws-helpers.js"; export type ConnectionState = "disconnected" | "connecting" | "connected" | "reconnecting"; export interface StructWebSocketConfig { apiKey: string; jwt?: string; + getJwt?: () => string | undefined; baseUrl?: string; reconnect?: RetryConfig; + subscribeTimeout?: number; } -export interface PredictionTrade { - id: string; - hash: string; - chain_id: number; - block: number; - confirmed_at: number; - log_index: number; - block_index?: number; - order_hash?: string; - trader: Address; - taker: Address; - side: number; - condition_id: string | null; - position_id: string; - outcome: string | null; - outcome_index: number | null; - question: string | null; - slug: string | null; - event_slug?: string | null; - usd_amount: string; - shares_amount: string; - price: number; - probability: number | null; - fee?: string; - exchange?: number; - trade_type?: number; -} +export type WsRoomId = + | "polymarket_trades" + | "polymarket_asset_prices" + | "polymarket_asset_window_updates" + | "polymarket_market_metrics" + | "polymarket_event_metrics" + | "polymarket_position_metrics" + | "polymarket_trader_pnl" + | "polymarket_trader_positions" + | "polymarket_accounts" + | "polymarket_order_book" + | "polymarket_clob_rewards"; -export interface EnrichedPredictionTrade { - id: string; - hash: string; - block: number; - confirmed_at: number; - trader: Address; - side: number; - position_id: string; - condition_id: string | null; - outcome: string | null; - outcome_index: number | null; - question: string; - slug: string; - event_slug: string; - image_url: string | null; - market_id: string | null; - title: string | null; - usd_amount: string; - shares_amount: string; - price: number; - probability: number | null; - smart_money_score: number; - insider_score: number; - categories: string[]; -} +export type WsFiltersOptionalRoom = "polymarket_trades" | "polymarket_asset_prices" | "polymarket_clob_rewards"; +export type WsFiltersRequiredRoom = Exclude; -export interface PredictionMarketMetadata { - slug: string | null; - question: string | null; - outcome: string | null; - outcome_index?: number | null; - image_url?: string | null; -} +export type TradesSubscribeFilters = Omit; +export type AssetPricesSubscribeFilters = Omit; +export type AssetWindowUpdatesSubscribeFilters = Omit; +export type MarketMetricsSubscribeFilters = Omit; +export type EventMetricsSubscribeFilters = Omit; +export type PositionMetricsSubscribeFilters = Omit; +export type TraderPnlSubscribeFilters = Omit; +export type AccountsSubscribeFilters = Omit; +export type OrderBookSubscribeFilters = Omit; +export type TraderPositionsSubscribeFilters = Omit; +export type ClobRewardsSubscribeFilters = Omit; -export interface PredictionWalletTrackingAlert { - is_buy: boolean; - trader: Address; - condition_id: string | null; - position_id: string; - usd_amount: string; - shares_amount: string; - price: number; - probability: number | null; - metadata: PredictionMarketMetadata | null; - confirmed_at: number; -} +export type WsTradeType = NonNullable[number]; +export type WsTradeStatus = NonNullable; +export type WsAssetTimeframe = NonNullable[number]; + +export type TradeStreamEvent = WsSchemas["TradeStreamEvent"]; +export type AssetPriceTickEvent = WsSchemas["AssetPriceTickEvent"]; +export type AssetPriceWindowUpdateEvent = WsSchemas["AssetPriceWindowUpdateEvent"]; +export type AssetWindowUpdateEvent = WsSchemas["AssetWindowUpdateEvent"]; +export type MarketMetricsEvent = WsSchemas["MarketMetricsEvent"]; +export type EventMetricsEvent = WsSchemas["EventMetricsEvent"]; +export type PositionMetricsEvent = WsSchemas["PositionMetricsEvent"]; +export type TraderGlobalPnlEvent = WsSchemas["TraderGlobalPnlEvent"]; +export type TraderMarketPnlEvent = WsSchemas["TraderMarketPnlEvent"]; +export type TraderEventPnlEvent = WsSchemas["TraderEventPnlEvent"]; +export type AccountsUpdateEvent = WsSchemas["AccountsUpdateEvent"]; +export type UsdceUpdateEvent = WsSchemas["UsdceUpdateEvent"]; +export type MaticUpdateEvent = WsSchemas["MaticUpdateEvent"]; +export type WsOrderBookLevel = WsSchemas["OrderBookLevel"]; +export type OrderBookUpdateEvent = WsSchemas["OrderBookUpdateEvent"]; +export type TraderPositionUpdateEvent = WsSchemas["TraderPositionUpdateEvent"]; +export type TraderPositionsSubscribeResponse = WsSchemas["TraderPositionsSubscribeResponse"]; +export type ClobRewardsUpdateEvent = WsSchemas["ClobRewardsUpdateEvent"]; +export type ClobRewardsSubscribeResponse = WsSchemas["ClobRewardsSubscribeResponse"]; +export type WsAlertSubscribeMessage = WsAlertSchemas["WsAlertSubscribeMessage"]; +export type WsAlertUnsubscribeMessage = WsAlertSchemas["WsAlertUnsubscribeMessage"]; +export type WsAlertEventPayload = WsAlertSchemas["WsAlertEventPayload"]; +export type WsAlertSubscribedResponse = WsAlertSchemas["WsAlertSubscribedResponse"]; +export type WsAlertUnsubscribedResponse = WsAlertSchemas["WsAlertUnsubscribedResponse"]; +export type WsAlertErrorResponse = WsAlertSchemas["WsAlertErrorResponse"]; +export type WsAlertEventType = WsAlertSchemas["WsAlertEventType"]; +export type { WsAlertSubscribeMap, WsAlertEventDataMap, WsAlertEventName }; + +export type TradesStreamSubscribeResponse = WsSchemas["TradesStreamSubscribeResponse"]; +export type AssetPricesSubscribeResponse = WsSchemas["AssetPricesSubscribeResponse"]; +export type AssetWindowUpdatesSubscribeResponse = WsSchemas["AssetWindowUpdatesSubscribeResponse"]; +export type MarketMetricsSubscribeResponse = WsSchemas["MarketMetricsSubscribeResponse"]; +export type EventMetricsSubscribeResponse = WsSchemas["EventMetricsSubscribeResponse"]; +export type PositionMetricsSubscribeResponse = WsSchemas["PositionMetricsSubscribeResponse"]; +export type TraderPnlSubscribeResponse = WsSchemas["TraderPnlSubscribeResponse"]; +export type AccountsSubscribeResponse = WsSchemas["AccountsSubscribeResponse"]; +export type OrderBookSubscribeResponse = WsSchemas["OrderBookSubscribeResponse"]; export interface WebSocketEventMap { - market_trade: PredictionTrade; - whale_trade: EnrichedPredictionTrade; - smart_money_trade: EnrichedPredictionTrade; - insider_trade: EnrichedPredictionTrade; - wallet_tracking_alert: PredictionWalletTrackingAlert; - conditions_tracking_alert: PredictionTrade; + trade_stream_update: TradeStreamEvent; + asset_price_tick: AssetPriceTickEvent; + asset_price_window_update: AssetPriceWindowUpdateEvent; + asset_window_update: AssetWindowUpdateEvent; + market_metrics_update: MarketMetricsEvent; + event_metrics_update: EventMetricsEvent; + position_metrics_update: PositionMetricsEvent; + trader_global_pnl_update: TraderGlobalPnlEvent; + trader_market_pnl_update: TraderMarketPnlEvent; + trader_event_pnl_update: TraderEventPnlEvent; + accounts_update: AccountsUpdateEvent; + usdce_update: UsdceUpdateEvent; + matic_update: MaticUpdateEvent; + order_book_update: OrderBookUpdateEvent; + trader_position_update: TraderPositionUpdateEvent; + clob_rewards_update: ClobRewardsUpdateEvent; connected: void; disconnected: { code: number; reason: string }; reconnecting: { attempt: number }; + reconnect_failed: Error; + auth_failed: Error; error: Error; + warning: Error; } -export interface WebSocketMessage { - type: string; - room_id?: string; - action?: string; - message?: Record; - wallet_addresses?: string[]; - condition_ids?: string[]; +export interface WsSubscriptionMap { + polymarket_trades: TradesSubscribeFilters; + polymarket_asset_prices: AssetPricesSubscribeFilters; + polymarket_asset_window_updates: AssetWindowUpdatesSubscribeFilters; + polymarket_market_metrics: MarketMetricsSubscribeFilters; + polymarket_event_metrics: EventMetricsSubscribeFilters; + polymarket_position_metrics: PositionMetricsSubscribeFilters; + polymarket_trader_pnl: TraderPnlSubscribeFilters; + polymarket_trader_positions: TraderPositionsSubscribeFilters; + polymarket_accounts: AccountsSubscribeFilters; + polymarket_order_book: OrderBookSubscribeFilters; + polymarket_clob_rewards: ClobRewardsSubscribeFilters; } -export interface WebSocketServerMessage { - type: string; - data?: unknown; - room_id?: string; - message?: string; +export interface WsSubscribeResponseMap { + polymarket_trades: TradesStreamSubscribeResponse; + polymarket_asset_prices: AssetPricesSubscribeResponse; + polymarket_asset_window_updates: AssetWindowUpdatesSubscribeResponse; + polymarket_market_metrics: MarketMetricsSubscribeResponse; + polymarket_event_metrics: EventMetricsSubscribeResponse; + polymarket_position_metrics: PositionMetricsSubscribeResponse; + polymarket_trader_pnl: TraderPnlSubscribeResponse; + polymarket_trader_positions: TraderPositionsSubscribeResponse; + polymarket_accounts: AccountsSubscribeResponse; + polymarket_order_book: OrderBookSubscribeResponse; + polymarket_clob_rewards: ClobRewardsSubscribeResponse; } + +export type AlertsWebSocketEventMap = { + [E in WsAlertEventName]: { event: E; timestamp: number; data: WsAlertEventDataMap[E] }; +} & { + connected: void; + disconnected: { code: number; reason: string }; + reconnecting: { attempt: number }; + reconnect_failed: Error; + auth_failed: Error; + error: Error; + warning: Error; +}; diff --git a/src/ws-alerts.ts b/src/ws-alerts.ts new file mode 100644 index 0000000..2c13a19 --- /dev/null +++ b/src/ws-alerts.ts @@ -0,0 +1,308 @@ +import { WebSocketTransport, buildWebSocketUrl } from "./ws-transport.js"; +import { WebSocketError } from "./errors.js"; +import type { + ConnectionState, + StructWebSocketConfig, + AlertsWebSocketEventMap, + WsAlertSubscribedResponse, +} from "./types/ws.js"; +import type { WsAlertSubscribeMap, WsAlertEventName } from "./types/ws-helpers.js"; + +const DEFAULT_BASE_URL = "wss://api.struct.to"; +const PING_INTERVAL_MS = 30_000; +const DEFAULT_SUBSCRIBE_TIMEOUT_MS = 10_000; + +type Listener = (payload: T) => void; + +interface PendingSubscribe { + resolve: (data: unknown) => void; + reject: (err: Error) => void; + timer: ReturnType | null; +} + +export class StructAlertsWebSocket { + private readonly transport: WebSocketTransport; + private readonly listeners = new Map>>(); + private readonly subscriptions = new Map>(); + private readonly pendingSubscribes = new Map(); + private readonly subscribeTimeout: number; + private pingTimer: ReturnType | null = null; + private pongTimer: ReturnType | null = null; + private isEmittingListenerError = false; + + constructor(config: StructWebSocketConfig) { + this.subscribeTimeout = config.subscribeTimeout ?? DEFAULT_SUBSCRIBE_TIMEOUT_MS; + const getUrl = () => buildWebSocketUrl("/ws/alerts", { + apiKey: config.apiKey, + jwt: config.getJwt?.() ?? config.jwt, + baseUrl: config.baseUrl, + }, DEFAULT_BASE_URL); + + this.transport = new WebSocketTransport( + getUrl, + config.reconnect ?? {}, + { + onOpen: () => this.handleOpen(), + onClose: (code, reason) => this.handleClose(code, reason), + onError: (error) => this.emit("error", error), + onMessage: (data) => this.handleMessage(data), + onReconnecting: (attempt) => this.emit("reconnecting", { attempt }), + onReconnectFailed: (error) => this.emit("reconnect_failed", error), + onAuthFailed: (error) => this.emit("auth_failed", error), + onWarning: (warning) => this.emit("warning", warning), + }, + ); + } + + get state(): ConnectionState { + return this.transport.state; + } + + connect(): Promise { + return this.transport.connect(); + } + + disconnect(): void { + this.stopPing(); + for (const [, pending] of this.pendingSubscribes) { + this.clearSubscribeTimer(pending); + pending.reject(new WebSocketError("Disconnected")); + } + this.pendingSubscribes.clear(); + this.subscriptions.clear(); + this.transport.disconnect(); + } + + on(event: K, listener: Listener): () => void { + let set = this.listeners.get(event as string); + if (!set) { + set = new Set(); + this.listeners.set(event as string, set); + } + set.add(listener); + return () => { set.delete(listener); }; + } + + off(event: K, listener: Listener): void { + this.listeners.get(event as string)?.delete(listener); + } + + once(event: K, listener: Listener): () => void { + const wrapper = (payload: AlertsWebSocketEventMap[K]) => { + this.off(event, wrapper); + listener(payload); + }; + return this.on(event, wrapper); + } + + removeAllListeners(event?: keyof AlertsWebSocketEventMap): void { + if (event) { + this.listeners.delete(event as string); + } else { + this.listeners.clear(); + } + } + + subscribe( + event: E, + filters: Omit, + ): Promise { + const message = { op: "subscribe", event, ...filters } as Record; + + const existing = this.pendingSubscribes.get(event); + if (existing) { + this.clearSubscribeTimer(existing); + existing.reject(new WebSocketError("Superseded by new subscription")); + } + + this.subscriptions.set(event, message); + this.rebuildReplay(); + + return new Promise((resolve, reject) => { + const pending: PendingSubscribe = { + resolve: resolve as (data: unknown) => void, + reject, + timer: null, + }; + this.pendingSubscribes.set(event, pending); + if (this.sendSubscription(message)) { + this.armSubscribeTimer(event, pending); + } + }); + } + + unsubscribe(event: WsAlertEventName): void { + const sub = this.subscriptions.get(event); + if (!sub) return; + + const pending = this.pendingSubscribes.get(event); + if (pending) { + this.clearSubscribeTimer(pending); + pending.reject(new WebSocketError("Unsubscribed")); + this.pendingSubscribes.delete(event); + } + + this.subscriptions.delete(event); + this.rebuildReplay(); + if (this.state === "connected") { + this.transport.sendNow({ ...sub, op: "unsubscribe" }); + } + } + + private rebuildReplay(): void { + this.transport.clearReplayMessages(); + for (const [, message] of this.subscriptions) { + this.transport.addReplayMessage(message); + } + } + + private handleOpen(): void { + this.startPing(); + this.restartPendingSubscribes(); + this.emit("connected", undefined as never); + } + + private handleClose(code: number, reason: string): void { + this.stopPing(); + this.pausePendingSubscribes(); + this.emit("disconnected", { code, reason }); + } + + private handleMessage(raw: unknown): void { + const msg = raw as { op?: string; type?: string; event?: string; error?: string; subscription_id?: string; timestamp?: number; data?: unknown }; + if (!msg || typeof msg !== "object") return; + if (msg.op === "pong" || msg.type === "pong") { + this.clearPongTimer(); + return; + } + + if (msg.error) { + const error = new WebSocketError(msg.error); + if (msg.event) { + const pending = this.pendingSubscribes.get(msg.event as WsAlertEventName); + if (pending) { + this.clearSubscribeTimer(pending); + this.pendingSubscribes.delete(msg.event as WsAlertEventName); + pending.reject(error); + } + } + this.emit("error", error); + return; + } + + if (msg.op === "subscribed" && msg.event) { + const pending = this.pendingSubscribes.get(msg.event as WsAlertEventName); + if (pending) { + this.clearSubscribeTimer(pending); + this.pendingSubscribes.delete(msg.event as WsAlertEventName); + pending.resolve({ op: "subscribed", event: msg.event, subscription_id: msg.subscription_id }); + } + return; + } + + if (msg.op === "unsubscribed") return; + + if (msg.event && msg.data !== undefined) { + this.emit(msg.event as keyof AlertsWebSocketEventMap, { + event: msg.event, + timestamp: msg.timestamp, + data: msg.data, + } as never); + } + } + + private emit(event: K, payload: AlertsWebSocketEventMap[K]): void { + const set = this.listeners.get(event as string); + if (!set) return; + for (const fn of set) { + try { + (fn as Listener)(payload); + } catch (error) { + this.handleListenerError(error); + } + } + } + + private startPing(): void { + this.stopPing(); + this.pingTimer = setInterval(() => { + if (this.transport.sendNow({ type: "ping" })) { + this.armPongTimer(); + } + }, PING_INTERVAL_MS); + } + + private stopPing(): void { + if (this.pingTimer !== null) { + clearInterval(this.pingTimer); + this.pingTimer = null; + } + this.clearPongTimer(); + } + + private sendSubscription(message: Record): boolean { + if (this.state !== "connected") return false; + return this.transport.sendNow(message); + } + + private restartPendingSubscribes(): void { + for (const [event, pending] of this.pendingSubscribes) { + if (pending.timer === null) { + this.armSubscribeTimer(event, pending); + } + } + } + + private pausePendingSubscribes(): void { + for (const [, pending] of this.pendingSubscribes) { + this.clearSubscribeTimer(pending); + } + } + + private armSubscribeTimer(event: WsAlertEventName, pending: PendingSubscribe): void { + this.clearSubscribeTimer(pending); + pending.timer = setTimeout(() => { + pending.timer = null; + this.pendingSubscribes.delete(event); + pending.reject(new WebSocketError(`Subscribe to alert ${event} timed out`)); + }, this.subscribeTimeout); + } + + private clearSubscribeTimer(pending: PendingSubscribe): void { + if (pending.timer !== null) { + clearTimeout(pending.timer); + pending.timer = null; + } + } + + private armPongTimer(): void { + if (this.pongTimer !== null) return; + this.pongTimer = setTimeout(() => { + this.pongTimer = null; + this.transport.close(4000, "pong timeout"); + }, PING_INTERVAL_MS * 2); + } + + private clearPongTimer(): void { + if (this.pongTimer !== null) { + clearTimeout(this.pongTimer); + this.pongTimer = null; + } + } + + private handleListenerError(error: unknown): void { + const normalizedError = error instanceof Error + ? error + : new WebSocketError("WebSocket listener threw", { cause: error }); + if (!this.isEmittingListenerError && (this.listeners.get("error")?.size ?? 0) > 0) { + this.isEmittingListenerError = true; + try { + this.emit("error", normalizedError); + return; + } finally { + this.isEmittingListenerError = false; + } + } + console.error(normalizedError); + } +} diff --git a/src/ws-transport.ts b/src/ws-transport.ts index 82d8c59..682846d 100644 --- a/src/ws-transport.ts +++ b/src/ws-transport.ts @@ -1,9 +1,54 @@ import { WebSocketError, WebSocketClosedError } from "./errors.js"; -import type { ConnectionState, WebSocketMessage } from "./types/ws.js"; +import type { ConnectionState } from "./types/ws.js"; import type { RetryConfig } from "./types/http.js"; const DEFAULT_INITIAL_DELAY_MS = 1_000; const DEFAULT_MAX_DELAY_MS = 30_000; +const DEFAULT_MAX_PENDING_MESSAGES = 256; +const AUTH_FAILURE_CLOSE_CODES = new Set([1008, 4401]); + +interface QueuedMessage { + message: Record; +} + +interface ReplayMessageGroup { + messages: QueuedMessage[]; +} + +interface ConnectWaiter { + resolve: () => void; + reject: (err: Error) => void; +} + +export function buildWebSocketUrl( + path: string, + config: { apiKey: string; jwt?: string; baseUrl?: string }, + defaultBaseUrl: string, +): string { + const base = trimTrailingSlashes(config.baseUrl ?? defaultBaseUrl); + const wsBase = toWebSocketBaseUrl(base); + const params = new URLSearchParams({ "api-key": config.apiKey }); + if (config.jwt) params.set("token", config.jwt); + return `${wsBase}${path}?${params.toString()}`; +} + +function trimTrailingSlashes(value: string): string { + let end = value.length; + while (end > 0 && value.charCodeAt(end - 1) === 47) { + end--; + } + return end === value.length ? value : value.slice(0, end); +} + +function toWebSocketBaseUrl(baseUrl: string): string { + if (baseUrl.startsWith("https://")) { + return `wss://${baseUrl.slice("https://".length)}`; + } + if (baseUrl.startsWith("http://")) { + return `ws://${baseUrl.slice("http://".length)}`; + } + return baseUrl; +} export interface WebSocketTransportCallbacks { onOpen: () => void; @@ -11,6 +56,9 @@ export interface WebSocketTransportCallbacks { onError: (error: Error) => void; onMessage: (data: unknown) => void; onReconnecting: (attempt: number) => void; + onReconnectFailed: (error: Error) => void; + onAuthFailed: (error: WebSocketClosedError) => void; + onWarning: (warning: Error) => void; } export class WebSocketTransport { @@ -19,14 +67,15 @@ export class WebSocketTransport { private reconnectTimer: ReturnType | null = null; private reconnectAttempt = 0; private intentionalClose = false; - private readonly pendingMessages: WebSocketMessage[] = []; - private readonly replayMessages: WebSocketMessage[] = []; - private readonly url: string; + private readonly connectWaiters = new Set(); + private readonly pendingMessages: QueuedMessage[] = []; + private readonly replayMessages: ReplayMessageGroup[] = []; + private readonly getUrl: () => string; private readonly retry: RetryConfig; private readonly callbacks: WebSocketTransportCallbacks; - constructor(url: string, retry: RetryConfig, callbacks: WebSocketTransportCallbacks) { - this.url = url; + constructor(url: string | (() => string), retry: RetryConfig, callbacks: WebSocketTransportCallbacks) { + this.getUrl = typeof url === "function" ? url : () => url; this.retry = retry; this.callbacks = callbacks; } @@ -35,17 +84,18 @@ export class WebSocketTransport { return this._state; } - connect(): void { - if (this._state === "connected" || this._state === "connecting" || this._state === "reconnecting") { - return; - } - if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) { - return; + connect(): Promise { + if (this._state === "connected") return Promise.resolve(); + const promise = this.createConnectPromise(); + if (this._state === "connecting" || this._state === "reconnecting") { + return promise; } + this.intentionalClose = false; this.clearReconnectTimer(); this.setState("connecting"); this.createSocket(); + return promise; } disconnect(): void { @@ -53,6 +103,7 @@ export class WebSocketTransport { this.clearReconnectTimer(); this.pendingMessages.length = 0; this.replayMessages.length = 0; + this.rejectConnect(new WebSocketClosedError(1000, "client disconnect")); if (this.ws) { this.ws.close(1000, "client disconnect"); this.ws = null; @@ -60,37 +111,59 @@ export class WebSocketTransport { this.setState("disconnected"); } - send(message: WebSocketMessage): void { - if (this._state === "connected" && this.ws?.readyState === WebSocket.OPEN) { - this.ws.send(JSON.stringify(message)); - } else { - this.pendingMessages.push(message); - } + send(message: Record): boolean { + const queuedMessage: QueuedMessage = { message }; + if (this.sendMessage(queuedMessage)) return true; + this.enqueueMessage(this.pendingMessages, queuedMessage, "pending"); + return false; } - addReplayMessage(message: WebSocketMessage): void { - this.replayMessages.push(message); + sendNow(message: Record): boolean { + return this.sendMessage({ message }); } - removeReplayMessages(predicate: (msg: WebSocketMessage) => boolean): void { - for (let i = this.replayMessages.length - 1; i >= 0; i--) { - if (predicate(this.replayMessages[i]!)) { - this.replayMessages.splice(i, 1); - } - } + addReplayMessage(message: Record): void { + this.addReplayMessages([message]); + } + + addReplayMessages(messages: Record[]): void { + this.enqueueReplayMessages(messages.map((message) => ({ message }))); } clearReplayMessages(): void { this.replayMessages.length = 0; } + close(code: number, reason: string): void { + this.ws?.close(code, reason); + } + + private createConnectPromise(): Promise { + return new Promise((resolve, reject) => { + this.connectWaiters.add({ resolve, reject }); + }); + } + + private resolveConnect(): void { + for (const waiter of this.connectWaiters) { + waiter.resolve(); + } + this.connectWaiters.clear(); + } + + private rejectConnect(error: Error): void { + for (const waiter of this.connectWaiters) { + waiter.reject(error); + } + this.connectWaiters.clear(); + } + private createSocket(): void { try { - this.ws = new WebSocket(this.url); + this.ws = new WebSocket(this.getUrl()); } catch (err) { - this.callbacks.onError( - new WebSocketError("Failed to create WebSocket", { cause: err }), - ); + const error = new WebSocketError("Failed to create WebSocket", { cause: err }); + this.callbacks.onError(error); this.scheduleReconnect(); return; } @@ -100,6 +173,7 @@ export class WebSocketTransport { this.reconnectAttempt = 0; this.replaySubscriptions(); this.flushPendingMessages(); + this.resolveConnect(); this.callbacks.onOpen(); }; @@ -110,7 +184,15 @@ export class WebSocketTransport { this.callbacks.onClose(event.code, event.reason); return; } + + const error = new WebSocketClosedError(event.code, event.reason); this.callbacks.onClose(event.code, event.reason); + if (this.isAuthFailure(event.code)) { + this.setState("disconnected"); + this.rejectConnect(error); + this.callbacks.onAuthFailed(error); + return; + } this.scheduleReconnect(); }; @@ -129,29 +211,26 @@ export class WebSocketTransport { } private replaySubscriptions(): void { - for (const msg of this.replayMessages) { - if (this.ws?.readyState === WebSocket.OPEN) { - this.ws.send(JSON.stringify(msg)); + for (const replayGroup of this.replayMessages) { + for (const queuedMessage of replayGroup.messages) { + this.sendMessage(queuedMessage); } } } private flushPendingMessages(): void { while (this.pendingMessages.length > 0) { - const msg = this.pendingMessages.shift()!; - if (this.ws?.readyState === WebSocket.OPEN) { - this.ws.send(JSON.stringify(msg)); - } + this.sendMessage(this.pendingMessages.shift()!); } } private scheduleReconnect(): void { const maxRetries = this.retry.maxRetries ?? Infinity; if (this.reconnectAttempt >= maxRetries) { + const error = new WebSocketClosedError(1006, "Max reconnection attempts reached"); this.setState("disconnected"); - this.callbacks.onError( - new WebSocketClosedError(1006, "Max reconnection attempts reached"), - ); + this.rejectConnect(error); + this.callbacks.onReconnectFailed(error); return; } @@ -178,6 +257,42 @@ export class WebSocketTransport { } } + private sendMessage(queuedMessage: QueuedMessage): boolean { + if (this.ws?.readyState !== WebSocket.OPEN) return false; + this.ws.send(JSON.stringify(queuedMessage.message)); + return true; + } + + private enqueueMessage(queue: QueuedMessage[], queuedMessage: QueuedMessage, queueName: "pending" | "replay"): void { + queue.push(queuedMessage); + const maxPendingMessages = this.retry.maxPendingMessages ?? DEFAULT_MAX_PENDING_MESSAGES; + if (queue.length > maxPendingMessages) { + queue.shift(); + this.callbacks.onWarning( + new WebSocketError( + `WebSocket ${queueName} queue exceeded ${maxPendingMessages} messages; dropped oldest message`, + ), + ); + } + } + + private enqueueReplayMessages(messages: QueuedMessage[]): void { + this.replayMessages.push({ messages }); + const maxPendingMessages = this.retry.maxPendingMessages ?? DEFAULT_MAX_PENDING_MESSAGES; + if (this.replayMessages.length > maxPendingMessages) { + this.replayMessages.shift(); + this.callbacks.onWarning( + new WebSocketError( + `WebSocket replay queue exceeded ${maxPendingMessages} entries; dropped oldest replay entry`, + ), + ); + } + } + + private isAuthFailure(code: number): boolean { + return AUTH_FAILURE_CLOSE_CODES.has(code); + } + private setState(state: ConnectionState): void { this._state = state; } diff --git a/src/ws.ts b/src/ws.ts index cadca8f..c2b86d3 100644 --- a/src/ws.ts +++ b/src/ws.ts @@ -1,35 +1,48 @@ -import { WebSocketTransport } from "./ws-transport.js"; +import { WebSocketTransport, buildWebSocketUrl } from "./ws-transport.js"; +import { WebSocketError } from "./errors.js"; import type { ConnectionState, StructWebSocketConfig, WebSocketEventMap, - WebSocketMessage, - WebSocketServerMessage, + WsRoomId, + WsFiltersOptionalRoom, + WsFiltersRequiredRoom, + WsSubscriptionMap, + WsSubscribeResponseMap, } from "./types/ws.js"; -import type { Address } from "./types/common.js"; -const DEFAULT_BASE_URL = "https://api.struct.to/v1"; +const DEFAULT_BASE_URL = "wss://api.struct.to"; +const PING_INTERVAL_MS = 30_000; +const DEFAULT_SUBSCRIBE_TIMEOUT_MS = 10_000; type Listener = (payload: T) => void; +interface PendingSubscribe { + resolve: (data: unknown) => void; + reject: (err: Error) => void; + timer: ReturnType | null; +} + export class StructWebSocket { private readonly transport: WebSocketTransport; - private readonly listeners = new Map>(); - private readonly activeMarkets = new Set(); - private readonly activeMarketPositions = new Set(); - private readonly activeWallets = new Set(); - private readonly activeConditions = new Set(); - private readonly activeSpecialRooms = new Set(); + private readonly listeners = new Map>>(); + private readonly subscriptions = new Map>(); + private readonly pendingSubscribes = new Map(); + private readonly subscribeTimeout: number; + private pingTimer: ReturnType | null = null; + private pongTimer: ReturnType | null = null; + private isEmittingListenerError = false; constructor(config: StructWebSocketConfig) { - const httpBase = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, ""); - const wsBase = httpBase.replace(/^https:\/\//, "wss://").replace(/^http:\/\//, "ws://"); - const url = config.jwt - ? `${wsBase}?api-key=${encodeURIComponent(config.apiKey)}&token=${encodeURIComponent(config.jwt)}` - : `${wsBase}?token=${encodeURIComponent(config.apiKey)}`; + this.subscribeTimeout = config.subscribeTimeout ?? DEFAULT_SUBSCRIBE_TIMEOUT_MS; + const getUrl = () => buildWebSocketUrl("/ws", { + apiKey: config.apiKey, + jwt: config.getJwt?.() ?? config.jwt, + baseUrl: config.baseUrl, + }, DEFAULT_BASE_URL); this.transport = new WebSocketTransport( - url, + getUrl, config.reconnect ?? {}, { onOpen: () => this.handleOpen(), @@ -37,6 +50,9 @@ export class StructWebSocket { onError: (error) => this.emit("error", error), onMessage: (data) => this.handleMessage(data), onReconnecting: (attempt) => this.emit("reconnecting", { attempt }), + onReconnectFailed: (error) => this.emit("reconnect_failed", error), + onAuthFailed: (error) => this.emit("auth_failed", error), + onWarning: (warning) => this.emit("warning", warning), }, ); } @@ -45,35 +61,36 @@ export class StructWebSocket { return this.transport.state; } - connect(): void { - this.transport.connect(); + connect(): Promise { + return this.transport.connect(); } disconnect(): void { - this.activeMarkets.clear(); - this.activeMarketPositions.clear(); - this.activeWallets.clear(); - this.activeConditions.clear(); - this.activeSpecialRooms.clear(); + this.stopPing(); + for (const [, pending] of this.pendingSubscribes) { + this.clearSubscribeTimer(pending); + pending.reject(new WebSocketError("Disconnected")); + } + this.pendingSubscribes.clear(); + this.subscriptions.clear(); this.transport.disconnect(); } - on(event: K, listener: Listener): this { + on(event: K, listener: Listener): () => void { let set = this.listeners.get(event as string); if (!set) { set = new Set(); this.listeners.set(event as string, set); } set.add(listener); - return this; + return () => { set.delete(listener); }; } - off(event: K, listener: Listener): this { + off(event: K, listener: Listener): void { this.listeners.get(event as string)?.delete(listener); - return this; } - once(event: K, listener: Listener): this { + once(event: K, listener: Listener): () => void { const wrapper = (payload: WebSocketEventMap[K]) => { this.off(event, wrapper); listener(payload); @@ -81,142 +98,114 @@ export class StructWebSocket { return this.on(event, wrapper); } - subscribeMarket(conditionId: string): void { - if (this.activeMarkets.has(conditionId)) return; - this.activeMarkets.add(conditionId); - const msg = this.marketSubscribeMessage(conditionId); - this.transport.addReplayMessage(msg); - this.transport.send(msg); - } - - unsubscribeMarket(conditionId: string): void { - if (!this.activeMarkets.has(conditionId)) return; - this.activeMarkets.delete(conditionId); - const msg: WebSocketMessage = { - type: "unsubscribe_market", - room_id: `market_${conditionId}`, - message: {}, - }; - this.transport.removeReplayMessages( - (m) => m.type === "subscribe_market" && m.room_id === `market_${conditionId}`, - ); - this.transport.send(msg); - } - - subscribeMarketByPosition(positionId: string): void { - if (this.activeMarketPositions.has(positionId)) return; - this.activeMarketPositions.add(positionId); - const msg = this.marketPositionSubscribeMessage(positionId); - this.transport.addReplayMessage(msg); - this.transport.send(msg); - } - - unsubscribeMarketByPosition(positionId: string): void { - if (!this.activeMarketPositions.has(positionId)) return; - this.activeMarketPositions.delete(positionId); - const msg: WebSocketMessage = { - type: "unsubscribe_market", - room_id: `market_position_${positionId}`, - message: {}, - }; - this.transport.removeReplayMessages( - (m) => m.type === "subscribe_market" && m.room_id === `market_position_${positionId}`, - ); - this.transport.send(msg); - } - - trackWallets(addresses: Address[]): void { - const uniqueAddresses = [...new Set(addresses)]; - const added = uniqueAddresses.filter((addr) => !this.activeWallets.has(addr)); - if (added.length === 0) return; - for (const addr of added) this.activeWallets.add(addr); - const msg = this.walletTrackMessage("subscribe", added); - this.rebuildWalletReplay(); - this.transport.send(msg); - } - - untrackWallets(addresses: Address[]): void { - const uniqueAddresses = [...new Set(addresses)]; - const removed = uniqueAddresses.filter((addr) => this.activeWallets.has(addr)); - if (removed.length === 0) return; - for (const addr of removed) this.activeWallets.delete(addr); - const msg = this.walletTrackMessage("unsubscribe", removed); - this.rebuildWalletReplay(); - this.transport.send(msg); - } - - subscribeWhaleTrades(): void { - this.subscribeSpecialRoom("whale_trades"); - } - - unsubscribeWhaleTrades(): void { - this.unsubscribeSpecialRoom("whale_trades"); - } - - subscribeSmartMoneyTrades(): void { - this.subscribeSpecialRoom("smart_money_trades"); + removeAllListeners(event?: keyof WebSocketEventMap): void { + if (event) { + this.listeners.delete(event as string); + } else { + this.listeners.clear(); + } } - unsubscribeSmartMoneyTrades(): void { - this.unsubscribeSpecialRoom("smart_money_trades"); - } + subscribe(room: R, filters?: WsSubscriptionMap[R]): Promise; + subscribe(room: R, filters: WsSubscriptionMap[R]): Promise; + subscribe(room: R, filters?: WsSubscriptionMap[R]): Promise { + const resolvedFilters = (filters ?? {}) as Record; + const isNewRoom = !this.subscriptions.has(room); - subscribeInsiderTrades(): void { - this.subscribeSpecialRoom("insider_trades"); - } + const existing = this.pendingSubscribes.get(room); + if (existing) { + this.clearSubscribeTimer(existing); + existing.reject(new WebSocketError("Superseded by new subscription")); + } - unsubscribeInsiderTrades(): void { - this.unsubscribeSpecialRoom("insider_trades"); - } + this.subscriptions.set(room, resolvedFilters); + this.rebuildReplay(); + + return new Promise((resolve, reject) => { + const pending: PendingSubscribe = { + resolve: resolve as (data: unknown) => void, + reject, + timer: null, + }; + this.pendingSubscribes.set(room, pending); + if (this.sendSubscription(room, resolvedFilters, isNewRoom)) { + this.armSubscribeTimer(room, pending); + } + }); + } + + unsubscribe(room: WsRoomId): void { + if (!this.subscriptions.has(room)) return; + + const pending = this.pendingSubscribes.get(room); + if (pending) { + this.clearSubscribeTimer(pending); + pending.reject(new WebSocketError("Unsubscribed")); + this.pendingSubscribes.delete(room); + } - trackConditions(conditionIds: string[]): void { - const uniqueConditionIds = [...new Set(conditionIds)]; - const added = uniqueConditionIds.filter((id) => !this.activeConditions.has(id)); - if (added.length === 0) return; - for (const id of added) this.activeConditions.add(id); - const msg = this.conditionsTrackMessage("subscribe", added); - this.rebuildConditionsReplay(); - this.transport.send(msg); + this.subscriptions.delete(room); + this.rebuildReplay(); + if (this.state === "connected") { + this.transport.sendNow({ + type: "room_message", + payload: { room_id: room, message: { action: "unsubscribe_all" } }, + }); + this.transport.sendNow({ type: "leave_room", payload: { room_id: room } }); + } } - untrackConditions(conditionIds: string[]): void { - const uniqueConditionIds = [...new Set(conditionIds)]; - const removed = uniqueConditionIds.filter((id) => this.activeConditions.has(id)); - if (removed.length === 0) return; - for (const id of removed) this.activeConditions.delete(id); - const msg = this.conditionsTrackMessage("unsubscribe", removed); - this.rebuildConditionsReplay(); - this.transport.send(msg); + private rebuildReplay(): void { + this.transport.clearReplayMessages(); + for (const [roomId, filters] of this.subscriptions) { + this.transport.addReplayMessages([ + { type: "join_room", payload: { room_id: roomId } }, + { + type: "room_message", + payload: { room_id: roomId, message: { action: "subscribe", ...filters } }, + }, + ]); + } } private handleOpen(): void { + this.startPing(); + this.restartPendingSubscribes(); this.emit("connected", undefined as never); } private handleClose(code: number, reason: string): void { + this.stopPing(); + this.pausePendingSubscribes(); this.emit("disconnected", { code, reason }); } private handleMessage(raw: unknown): void { - const msg = raw as WebSocketServerMessage; - if (!msg || typeof msg !== "object") return; - - const roomId = msg.room_id ?? ""; - const type = msg.type; - - if (type === "market_trade" || roomId.startsWith("market_")) { - this.emit("market_trade", msg.data as never); - } else if (type === "whale_trade" || roomId === "whale_trades") { - this.emit("whale_trade", msg.data as never); - } else if (type === "smart_money_trade" || roomId === "smart_money_trades") { - this.emit("smart_money_trade", msg.data as never); - } else if (type === "insider_trade" || roomId === "insider_trades") { - this.emit("insider_trade", msg.data as never); - } else if (type === "wallet_tracking_alert") { - this.emit("wallet_tracking_alert", msg.data as never); - } else if (type === "conditions_tracking_alert") { - this.emit("conditions_tracking_alert", msg.data as never); + const msg = raw as { type?: string; room_id?: string; data?: unknown }; + if (!msg || typeof msg !== "object" || !msg.type) return; + if (msg.type === "pong") { + this.clearPongTimer(); + return; } + + if (msg.type === "subscribed" && msg.room_id) { + const pending = this.pendingSubscribes.get(msg.room_id as WsRoomId); + if (pending) { + this.clearSubscribeTimer(pending); + this.pendingSubscribes.delete(msg.room_id as WsRoomId); + const subscribeError = this.getSubscribeError(msg.data); + if (subscribeError) { + const error = new WebSocketError(subscribeError); + pending.reject(error); + this.emit("error", error); + } else { + pending.resolve(msg.data); + } + } + return; + } + + this.emit(msg.type as keyof WebSocketEventMap, msg.data as never); } private emit(event: K, payload: WebSocketEventMap[K]): void { @@ -225,59 +214,106 @@ export class StructWebSocket { for (const fn of set) { try { (fn as Listener)(payload); - } catch {} + } catch (error) { + this.handleListenerError(error); + } } } - private marketSubscribeMessage(conditionId: string): WebSocketMessage { - return { type: "subscribe_market", room_id: `market_${conditionId}`, message: {} }; + private startPing(): void { + this.stopPing(); + this.pingTimer = setInterval(() => { + if (this.transport.sendNow({ type: "ping" })) { + this.armPongTimer(); + } + }, PING_INTERVAL_MS); } - private marketPositionSubscribeMessage(positionId: string): WebSocketMessage { - return { type: "subscribe_market", room_id: `market_position_${positionId}`, message: {} }; + private stopPing(): void { + if (this.pingTimer !== null) { + clearInterval(this.pingTimer); + this.pingTimer = null; + } + this.clearPongTimer(); } - private walletTrackMessage(action: string, addresses: Address[]): WebSocketMessage { - return { type: "wallet_tracking", action, wallet_addresses: addresses }; + private sendSubscription(room: WsRoomId, filters: Record, joinRoom: boolean): boolean { + if (this.state !== "connected") return false; + let sent = true; + if (joinRoom) { + sent = this.transport.sendNow({ type: "join_room", payload: { room_id: room } }) && sent; + } + sent = this.transport.sendNow({ + type: "room_message", + payload: { room_id: room, message: { action: "subscribe", ...filters } }, + }) && sent; + return sent; + } + + private restartPendingSubscribes(): void { + for (const [room, pending] of this.pendingSubscribes) { + if (pending.timer === null) { + this.armSubscribeTimer(room, pending); + } + } } - private conditionsTrackMessage(action: string, conditionIds: string[]): WebSocketMessage { - return { type: "conditions_tracking", action, condition_ids: conditionIds }; + private pausePendingSubscribes(): void { + for (const [, pending] of this.pendingSubscribes) { + this.clearSubscribeTimer(pending); + } } - private subscribeSpecialRoom(roomId: string): void { - if (this.activeSpecialRooms.has(roomId)) return; - this.activeSpecialRooms.add(roomId); - const msg: WebSocketMessage = { type: "subscribe_market", room_id: roomId, message: {} }; - this.transport.addReplayMessage(msg); - this.transport.send(msg); + private armSubscribeTimer(room: WsRoomId, pending: PendingSubscribe): void { + this.clearSubscribeTimer(pending); + pending.timer = setTimeout(() => { + pending.timer = null; + this.pendingSubscribes.delete(room); + pending.reject(new WebSocketError(`Subscribe to ${room} timed out`)); + }, this.subscribeTimeout); } - private unsubscribeSpecialRoom(roomId: string): void { - if (!this.activeSpecialRooms.has(roomId)) return; - this.activeSpecialRooms.delete(roomId); - const msg: WebSocketMessage = { type: "unsubscribe_market", room_id: roomId, message: {} }; - this.transport.removeReplayMessages( - (m) => m.type === "subscribe_market" && m.room_id === roomId, - ); - this.transport.send(msg); + private clearSubscribeTimer(pending: PendingSubscribe): void { + if (pending.timer !== null) { + clearTimeout(pending.timer); + pending.timer = null; + } } - private rebuildWalletReplay(): void { - this.transport.removeReplayMessages((m) => m.type === "wallet_tracking"); - if (this.activeWallets.size > 0) { - this.transport.addReplayMessage( - this.walletTrackMessage("subscribe", [...this.activeWallets]), - ); + private armPongTimer(): void { + if (this.pongTimer !== null) return; + this.pongTimer = setTimeout(() => { + this.pongTimer = null; + this.transport.close(4000, "pong timeout"); + }, PING_INTERVAL_MS * 2); + } + + private clearPongTimer(): void { + if (this.pongTimer !== null) { + clearTimeout(this.pongTimer); + this.pongTimer = null; } } - private rebuildConditionsReplay(): void { - this.transport.removeReplayMessages((m) => m.type === "conditions_tracking"); - if (this.activeConditions.size > 0) { - this.transport.addReplayMessage( - this.conditionsTrackMessage("subscribe", [...this.activeConditions]), - ); + private getSubscribeError(data: unknown): string | null { + if (!data || typeof data !== "object") return null; + const error = (data as { error?: unknown }).error; + return typeof error === "string" && error.length > 0 ? error : null; + } + + private handleListenerError(error: unknown): void { + const normalizedError = error instanceof Error + ? error + : new WebSocketError("WebSocket listener threw", { cause: error }); + if (!this.isEmittingListenerError && (this.listeners.get("error")?.size ?? 0) > 0) { + this.isEmittingListenerError = true; + try { + this.emit("error", normalizedError); + return; + } finally { + this.isEmittingListenerError = false; + } } + console.error(normalizedError); } } diff --git a/tests/integration.test.ts b/tests/integration.test.ts index 433ce40..87e8406 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -7,6 +7,7 @@ import { Namespace, PlatformNamespace } from "../src/namespaces/index.js"; import { methodMeta, type MethodConfig } from "./integration.meta.js"; const API_KEY = Bun.env.STRUCT_API_KEY ?? ""; +const RUN_INTEGRATION_TESTS = Bun.env.STRUCT_RUN_INTEGRATION_TESTS === "1"; const REPORT_PATH = "logs/integration-report.md"; type OpenAPISpec = { @@ -354,7 +355,7 @@ function resolveParams(params: Record, setupData: SetupData): R const EXCLUDED_METHODS = new Set(["constructor", "get", "post", "put", "delete"]); -describe.skipIf(!API_KEY)("integration", () => { +describe.skipIf(!API_KEY || !RUN_INTEGRATION_TESTS)("integration", () => { const client = new StructClient({ apiKey: API_KEY, onRequest: (info) => { diff --git a/tests/setup/fake-websocket.ts b/tests/setup/fake-websocket.ts new file mode 100644 index 0000000..b6627ac --- /dev/null +++ b/tests/setup/fake-websocket.ts @@ -0,0 +1,86 @@ +export class FakeWebSocket { + static readonly CONNECTING = 0; + static readonly OPEN = 1; + static readonly CLOSING = 2; + static readonly CLOSED = 3; + + static originalWebSocket: typeof globalThis.WebSocket | undefined; + static readonly instances: FakeWebSocket[] = []; + + readonly url: string; + readyState = FakeWebSocket.CONNECTING; + readonly sent: string[] = []; + onopen: ((event: Event) => void) | null = null; + onclose: ((event: CloseEvent) => void) | null = null; + onerror: ((event: Event) => void) | null = null; + onmessage: ((event: MessageEvent) => void) | null = null; + + constructor(url: string) { + this.url = url; + FakeWebSocket.instances.push(this); + } + + send(data: string): void { + if (this.readyState !== FakeWebSocket.OPEN) { + throw new Error("FakeWebSocket is not open"); + } + this.sent.push(data); + } + + close(code = 1000, reason = ""): void { + if (this.readyState === FakeWebSocket.CLOSED) return; + this.readyState = FakeWebSocket.CLOSING; + this.readyState = FakeWebSocket.CLOSED; + this.onclose?.({ code, reason } as CloseEvent); + } + + open(): void { + if (this.readyState !== FakeWebSocket.CONNECTING) return; + this.readyState = FakeWebSocket.OPEN; + this.onopen?.({ type: "open" } as Event); + } + + serverClose(code = 1006, reason = ""): void { + this.close(code, reason); + } + + serverError(): void { + this.onerror?.({ type: "error" } as Event); + } + + serverSend(data: unknown): void { + this.onmessage?.({ + data: typeof data === "string" ? data : JSON.stringify(data), + } as MessageEvent); + } + + jsonMessages(): Record[] { + return this.sent.map((message) => JSON.parse(message) as Record); + } +} + +export function installFakeWebSocket(): void { + FakeWebSocket.originalWebSocket ??= globalThis.WebSocket; + FakeWebSocket.instances.length = 0; + globalThis.WebSocket = FakeWebSocket as unknown as typeof WebSocket; +} + +export function restoreFakeWebSocket(): void { + if (FakeWebSocket.originalWebSocket) { + globalThis.WebSocket = FakeWebSocket.originalWebSocket; + } + FakeWebSocket.instances.length = 0; +} + +export function latestSocket(): FakeWebSocket { + const socket = FakeWebSocket.instances.at(-1); + if (!socket) { + throw new Error("Expected a FakeWebSocket instance"); + } + return socket; +} + +export async function flushMicrotasks(): Promise { + await Promise.resolve(); + await Promise.resolve(); +} diff --git a/tests/ws-alerts.test.ts b/tests/ws-alerts.test.ts new file mode 100644 index 0000000..6fba809 --- /dev/null +++ b/tests/ws-alerts.test.ts @@ -0,0 +1,166 @@ +import { afterEach, beforeEach, describe, expect, jest, mock, test } from "bun:test"; +import { StructAlertsWebSocket } from "../src/ws-alerts.js"; +import { WebSocketError } from "../src/errors.js"; +import { + flushMicrotasks, + installFakeWebSocket, + latestSocket, + restoreFakeWebSocket, + FakeWebSocket, +} from "./setup/fake-websocket.js"; + +let originalRandom: typeof Math.random; + +beforeEach(() => { + installFakeWebSocket(); + jest.useFakeTimers(); + originalRandom = Math.random; + Math.random = () => 0; +}); + +afterEach(() => { + Math.random = originalRandom; + jest.useRealTimers(); + restoreFakeWebSocket(); +}); + +async function getRejectedError(promise: Promise): Promise { + try { + await promise; + throw new Error("Expected promise to reject"); + } catch (error) { + return error as Error; + } +} + +describe("StructAlertsWebSocket", () => { + test("sends a pre-connect alert subscription only once on first open", async () => { + const ws = new StructAlertsWebSocket({ apiKey: "api-key" }); + const subscribePromise = ws.subscribe("asset_price_tick", {}); + const connectPromise = ws.connect(); + const socket = latestSocket(); + + socket.open(); + await connectPromise; + expect(socket.jsonMessages()).toEqual([{ op: "subscribe", event: "asset_price_tick" }]); + + socket.serverSend({ + op: "subscribed", + event: "asset_price_tick", + subscription_id: "00000000-0000-0000-0000-000000000000", + }); + await expect(subscribePromise).resolves.toEqual({ + op: "subscribed", + event: "asset_price_tick", + subscription_id: "00000000-0000-0000-0000-000000000000", + }); + }); + + test("pauses alert subscribe timeouts during reconnect", async () => { + const ws = new StructAlertsWebSocket({ + apiKey: "api-key", + subscribeTimeout: 100, + reconnect: { maxRetries: 1, initialDelayMs: 10, maxDelayMs: 10 }, + }); + + const connectPromise = ws.connect(); + const firstSocket = latestSocket(); + firstSocket.open(); + await connectPromise; + + const subscribePromise = ws.subscribe("asset_price_tick", {}); + firstSocket.serverClose(1006, "offline"); + + jest.advanceTimersByTime(10); + const secondSocket = latestSocket(); + let rejected: Error | undefined; + subscribePromise.catch((error) => { + rejected = error as Error; + }); + + jest.advanceTimersByTime(200); + await flushMicrotasks(); + expect(rejected).toBeUndefined(); + + secondSocket.open(); + await flushMicrotasks(); + expect(secondSocket.jsonMessages()).toEqual([{ op: "subscribe", event: "asset_price_tick" }]); + + secondSocket.serverSend({ + op: "subscribed", + event: "asset_price_tick", + subscription_id: "00000000-0000-0000-0000-000000000000", + }); + await expect(subscribePromise).resolves.toEqual({ + op: "subscribed", + event: "asset_price_tick", + subscription_id: "00000000-0000-0000-0000-000000000000", + }); + }); + + test("rejects pending alert subscriptions immediately on error frames", async () => { + const ws = new StructAlertsWebSocket({ apiKey: "api-key" }); + const errors: Error[] = []; + ws.on("error", (error) => { + errors.push(error); + }); + + const connectPromise = ws.connect(); + const socket = latestSocket(); + socket.open(); + await connectPromise; + + const subscribePromise = ws.subscribe("asset_price_tick", {}); + socket.serverSend({ + event: "asset_price_tick", + error: "invalid alert filter", + }); + + const error = await getRejectedError(subscribePromise); + expect(error).toBeInstanceOf(WebSocketError); + expect(error.message).toContain("invalid alert filter"); + expect(errors).toHaveLength(1); + }); + + test("sends type-based pings and reconnects when pong is missing", async () => { + const ws = new StructAlertsWebSocket({ + apiKey: "api-key", + reconnect: { maxRetries: 1, initialDelayMs: 10, maxDelayMs: 10 }, + }); + const onReconnecting = mock((_payload: { attempt: number }) => {}); + ws.on("reconnecting", onReconnecting); + + const connectPromise = ws.connect(); + const firstSocket = latestSocket(); + firstSocket.open(); + await connectPromise; + + jest.advanceTimersByTime(30_000); + expect(firstSocket.jsonMessages().at(-1)).toEqual({ type: "ping" }); + + jest.advanceTimersByTime(60_000); + await flushMicrotasks(); + expect(onReconnecting).toHaveBeenCalledTimes(1); + expect(firstSocket.readyState).toBe(FakeWebSocket.CLOSED); + + jest.advanceTimersByTime(10); + expect(FakeWebSocket.instances).toHaveLength(2); + }); + + test("surfaces listener exceptions through the error event", async () => { + const ws = new StructAlertsWebSocket({ apiKey: "api-key" }); + const onError = mock((_error: Error) => {}); + + ws.on("error", onError); + ws.on("connected", () => { + throw new Error("alerts listener exploded"); + }); + + const connectPromise = ws.connect(); + latestSocket().open(); + await connectPromise; + + expect(onError).toHaveBeenCalledTimes(1); + expect((onError.mock.calls[0]![0] as Error).message).toBe("alerts listener exploded"); + }); +}); diff --git a/tests/ws-transport.test.ts b/tests/ws-transport.test.ts new file mode 100644 index 0000000..3f8fdc3 --- /dev/null +++ b/tests/ws-transport.test.ts @@ -0,0 +1,192 @@ +import { afterEach, beforeEach, describe, expect, jest, mock, test } from "bun:test"; +import { WebSocketClosedError } from "../src/errors.js"; +import { WebSocketTransport, buildWebSocketUrl } from "../src/ws-transport.js"; +import { + flushMicrotasks, + installFakeWebSocket, + latestSocket, + restoreFakeWebSocket, + FakeWebSocket, +} from "./setup/fake-websocket.js"; + +let originalRandom: typeof Math.random; + +beforeEach(() => { + installFakeWebSocket(); + jest.useFakeTimers(); + originalRandom = Math.random; + Math.random = () => 0; +}); + +afterEach(() => { + Math.random = originalRandom; + jest.useRealTimers(); + restoreFakeWebSocket(); +}); + +function createCallbacks() { + return { + onOpen: mock(() => {}), + onClose: mock((_code: number, _reason: string) => {}), + onError: mock((_error: Error) => {}), + onMessage: mock((_data: unknown) => {}), + onReconnecting: mock((_attempt: number) => {}), + onReconnectFailed: mock((_error: Error) => {}), + onAuthFailed: mock((_error: WebSocketClosedError) => {}), + onWarning: mock((_warning: Error) => {}), + }; +} + +async function getRejectedError(promise: Promise): Promise { + try { + await promise; + throw new Error("Expected promise to reject"); + } catch (error) { + return error as Error; + } +} + +describe("WebSocketTransport", () => { + test("buildWebSocketUrl normalizes trailing slashes and websocket protocol", () => { + expect( + buildWebSocketUrl("/ws", { apiKey: "api-key", baseUrl: "https://api.struct.to///" }, "http://ignored"), + ).toBe("wss://api.struct.to/ws?api-key=api-key"); + + expect( + buildWebSocketUrl("/ws", { apiKey: "api-key", baseUrl: "http://localhost:3000/" }, "https://ignored"), + ).toBe("ws://localhost:3000/ws?api-key=api-key"); + + expect( + buildWebSocketUrl("/ws", { apiKey: "api-key", baseUrl: "wss://stream.struct.to/" }, "https://ignored"), + ).toBe("wss://stream.struct.to/ws?api-key=api-key"); + }); + + test("rebuilds the websocket URL on reconnect and resolves connect after a retry", async () => { + let jwt = "jwt-1"; + const callbacks = createCallbacks(); + const transport = new WebSocketTransport( + () => buildWebSocketUrl("/ws", { apiKey: "api-key", jwt }, "https://api.struct.to"), + { maxRetries: 2, initialDelayMs: 10, maxDelayMs: 10 }, + callbacks, + ); + + const connectPromise = transport.connect(); + const firstSocket = latestSocket(); + expect(firstSocket.url).toContain("token=jwt-1"); + + firstSocket.serverClose(1006, "offline"); + await flushMicrotasks(); + expect(callbacks.onReconnecting).toHaveBeenCalledTimes(1); + + jwt = "jwt-2"; + jest.advanceTimersByTime(10); + const secondSocket = latestSocket(); + expect(secondSocket).not.toBe(firstSocket); + expect(secondSocket.url).toContain("token=jwt-2"); + + secondSocket.open(); + await connectPromise; + expect(transport.state).toBe("connected"); + }); + + test("emits reconnect_failed and rejects connect after retries are exhausted", async () => { + const callbacks = createCallbacks(); + const transport = new WebSocketTransport( + "wss://api.struct.to/ws", + { maxRetries: 1, initialDelayMs: 10, maxDelayMs: 10 }, + callbacks, + ); + + const connectPromise = transport.connect(); + latestSocket().serverClose(1006, "offline"); + jest.advanceTimersByTime(10); + latestSocket().serverClose(1006, "still offline"); + + const error = await getRejectedError(connectPromise); + expect(error).toBeInstanceOf(WebSocketClosedError); + expect(callbacks.onReconnectFailed).toHaveBeenCalledTimes(1); + expect(callbacks.onAuthFailed).not.toHaveBeenCalled(); + expect(transport.state).toBe("disconnected"); + }); + + test("rejects pending connect and cancels retry when disconnected during reconnect", async () => { + const callbacks = createCallbacks(); + const transport = new WebSocketTransport( + "wss://api.struct.to/ws", + { maxRetries: 2, initialDelayMs: 10, maxDelayMs: 10 }, + callbacks, + ); + + const connectPromise = transport.connect(); + latestSocket().serverClose(1006, "offline"); + transport.disconnect(); + + const error = await getRejectedError(connectPromise); + expect(error).toBeInstanceOf(WebSocketClosedError); + + jest.advanceTimersByTime(20); + expect(FakeWebSocket.instances).toHaveLength(1); + expect(transport.state).toBe("disconnected"); + }); + + test("caps the pending queue and drops the oldest message", async () => { + const callbacks = createCallbacks(); + const transport = new WebSocketTransport( + "wss://api.struct.to/ws", + { maxPendingMessages: 2 }, + callbacks, + ); + + transport.send({ id: 1 }); + transport.send({ id: 2 }); + transport.send({ id: 3 }); + + expect(callbacks.onWarning).toHaveBeenCalledTimes(1); + + const connectPromise = transport.connect(); + const socket = latestSocket(); + socket.open(); + await connectPromise; + + expect(socket.jsonMessages()).toEqual([{ id: 2 }, { id: 3 }]); + }); + + test("caps the replay queue by replay entry so grouped replays stay intact", async () => { + const callbacks = createCallbacks(); + const transport = new WebSocketTransport( + "wss://api.struct.to/ws", + { maxPendingMessages: 1 }, + callbacks, + ); + + transport.addReplayMessages([{ id: "join-1" }, { id: "subscribe-1" }]); + transport.addReplayMessages([{ id: "join-2" }, { id: "subscribe-2" }]); + + expect(callbacks.onWarning).toHaveBeenCalledTimes(1); + + const connectPromise = transport.connect(); + const socket = latestSocket(); + socket.open(); + await connectPromise; + + expect(socket.jsonMessages()).toEqual([{ id: "join-2" }, { id: "subscribe-2" }]); + }); + + test("treats auth close codes as terminal failures", async () => { + const callbacks = createCallbacks(); + const transport = new WebSocketTransport( + "wss://api.struct.to/ws", + { maxRetries: 3, initialDelayMs: 10, maxDelayMs: 10 }, + callbacks, + ); + + const connectPromise = transport.connect(); + latestSocket().serverClose(1008, "token expired"); + + const error = await getRejectedError(connectPromise); + expect(error).toBeInstanceOf(WebSocketClosedError); + expect(callbacks.onAuthFailed).toHaveBeenCalledTimes(1); + expect(callbacks.onReconnecting).not.toHaveBeenCalled(); + expect(transport.state).toBe("disconnected"); + }); +}); diff --git a/tests/ws.test.ts b/tests/ws.test.ts new file mode 100644 index 0000000..8619103 --- /dev/null +++ b/tests/ws.test.ts @@ -0,0 +1,244 @@ +import { afterEach, beforeEach, describe, expect, jest, mock, test } from "bun:test"; +import { StructWebSocket } from "../src/ws.js"; +import { WebSocketError } from "../src/errors.js"; +import { + flushMicrotasks, + installFakeWebSocket, + latestSocket, + restoreFakeWebSocket, + FakeWebSocket, +} from "./setup/fake-websocket.js"; + +let originalRandom: typeof Math.random; + +beforeEach(() => { + installFakeWebSocket(); + jest.useFakeTimers(); + originalRandom = Math.random; + Math.random = () => 0; +}); + +afterEach(() => { + Math.random = originalRandom; + jest.useRealTimers(); + restoreFakeWebSocket(); +}); + +async function getRejectedError(promise: Promise): Promise { + try { + await promise; + throw new Error("Expected promise to reject"); + } catch (error) { + return error as Error; + } +} + +describe("StructWebSocket", () => { + test("sends a pre-connect subscription only once on first open", async () => { + const ws = new StructWebSocket({ apiKey: "api-key" }); + const subscribePromise = ws.subscribe("polymarket_trades"); + const connectPromise = ws.connect(); + const socket = latestSocket(); + + socket.open(); + await connectPromise; + expect(socket.jsonMessages()).toEqual([ + { type: "join_room", payload: { room_id: "polymarket_trades" } }, + { + type: "room_message", + payload: { + room_id: "polymarket_trades", + message: { action: "subscribe" }, + }, + }, + ]); + + socket.serverSend({ + type: "subscribed", + room_id: "polymarket_trades", + data: { subscribe_all: true }, + }); + await expect(subscribePromise).resolves.toEqual({ subscribe_all: true }); + }); + + test("keeps room replays intact when the replay queue is capped", async () => { + const ws = new StructWebSocket({ + apiKey: "api-key", + reconnect: { maxPendingMessages: 1 }, + }); + const warnings: Error[] = []; + ws.on("warning", (warning) => { + warnings.push(warning); + }); + + const subscribePromise = ws.subscribe("polymarket_trades"); + const connectPromise = ws.connect(); + const socket = latestSocket(); + + socket.open(); + await connectPromise; + expect(socket.jsonMessages()).toEqual([ + { type: "join_room", payload: { room_id: "polymarket_trades" } }, + { + type: "room_message", + payload: { + room_id: "polymarket_trades", + message: { action: "subscribe" }, + }, + }, + ]); + expect(warnings).toEqual([]); + + socket.serverSend({ + type: "subscribed", + room_id: "polymarket_trades", + data: { subscribe_all: true }, + }); + await expect(subscribePromise).resolves.toEqual({ subscribe_all: true }); + }); + + test("pauses subscribe timeout during reconnect and resolves after replayed ack", async () => { + const ws = new StructWebSocket({ + apiKey: "api-key", + subscribeTimeout: 100, + reconnect: { maxRetries: 1, initialDelayMs: 10, maxDelayMs: 10 }, + }); + + const connectPromise = ws.connect(); + const firstSocket = latestSocket(); + firstSocket.open(); + await connectPromise; + + const subscribePromise = ws.subscribe("polymarket_trades"); + firstSocket.serverClose(1006, "offline"); + + jest.advanceTimersByTime(10); + const secondSocket = latestSocket(); + let rejected: Error | undefined; + subscribePromise.catch((error) => { + rejected = error as Error; + }); + + jest.advanceTimersByTime(200); + await flushMicrotasks(); + expect(rejected).toBeUndefined(); + + secondSocket.open(); + await flushMicrotasks(); + expect(secondSocket.jsonMessages()).toEqual([ + { type: "join_room", payload: { room_id: "polymarket_trades" } }, + { + type: "room_message", + payload: { + room_id: "polymarket_trades", + message: { action: "subscribe" }, + }, + }, + ]); + + secondSocket.serverSend({ + type: "subscribed", + room_id: "polymarket_trades", + data: { subscribe_all: true }, + }); + await expect(subscribePromise).resolves.toEqual({ subscribe_all: true }); + }); + + test("does not queue stale unsubscribe messages while reconnecting", async () => { + const ws = new StructWebSocket({ + apiKey: "api-key", + reconnect: { maxRetries: 1, initialDelayMs: 10, maxDelayMs: 10 }, + }); + + const connectPromise = ws.connect(); + const firstSocket = latestSocket(); + firstSocket.open(); + await connectPromise; + + const subscribePromise = ws.subscribe("polymarket_trades"); + firstSocket.serverSend({ + type: "subscribed", + room_id: "polymarket_trades", + data: { subscribe_all: true }, + }); + await subscribePromise; + + firstSocket.serverClose(1006, "offline"); + ws.unsubscribe("polymarket_trades"); + + jest.advanceTimersByTime(10); + const secondSocket = latestSocket(); + secondSocket.open(); + await flushMicrotasks(); + + expect(secondSocket.jsonMessages()).toEqual([]); + }); + + test("rejects subscribe promises immediately when the ack contains an error", async () => { + const ws = new StructWebSocket({ apiKey: "api-key" }); + const errors: Error[] = []; + ws.on("error", (error) => { + errors.push(error); + }); + + const connectPromise = ws.connect(); + const socket = latestSocket(); + socket.open(); + await connectPromise; + + const subscribePromise = ws.subscribe("polymarket_trades"); + socket.serverSend({ + type: "subscribed", + room_id: "polymarket_trades", + data: { error: "bad filters" }, + }); + + const error = await getRejectedError(subscribePromise); + expect(error).toBeInstanceOf(WebSocketError); + expect(error.message).toContain("bad filters"); + expect(errors).toHaveLength(1); + expect(errors[0]?.message).toContain("bad filters"); + }); + + test("surfaces listener exceptions through the error event", async () => { + const ws = new StructWebSocket({ apiKey: "api-key" }); + const onError = mock((_error: Error) => {}); + + ws.on("error", onError); + ws.on("connected", () => { + throw new Error("listener exploded"); + }); + + const connectPromise = ws.connect(); + latestSocket().open(); + await connectPromise; + + expect(onError).toHaveBeenCalledTimes(1); + expect((onError.mock.calls[0]![0] as Error).message).toBe("listener exploded"); + }); + + test("closes and reconnects when pong is missing", async () => { + const ws = new StructWebSocket({ + apiKey: "api-key", + reconnect: { maxRetries: 1, initialDelayMs: 10, maxDelayMs: 10 }, + }); + const onReconnecting = mock((_payload: { attempt: number }) => {}); + ws.on("reconnecting", onReconnecting); + + const connectPromise = ws.connect(); + const firstSocket = latestSocket(); + firstSocket.open(); + await connectPromise; + + jest.advanceTimersByTime(30_000); + expect(firstSocket.jsonMessages().at(-1)).toEqual({ type: "ping" }); + + jest.advanceTimersByTime(60_000); + await flushMicrotasks(); + expect(onReconnecting).toHaveBeenCalledTimes(1); + expect(firstSocket.readyState).toBe(FakeWebSocket.CLOSED); + + jest.advanceTimersByTime(10); + expect(FakeWebSocket.instances).toHaveLength(2); + }); +});