{
  "openapi": "3.0.3",
  "info": {
    "title": "PerpDB API",
    "version": "1.0.0-rc.1",
    "license": {
      "name": "Proprietary",
      "url": "https://perpdb.xyz/terms"
    }
  },
  "servers": [
    {
      "url": "https://api.perpdb.xyz",
      "description": "Production"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "name": "Protocols",
      "description": "Supported protocols and their capabilities, plus cross-protocol aggregations (stats snapshot and timeseries).\n"
    },
    {
      "name": "GMTrade",
      "description": "GMTrade (Solana GMX v2 fork) — isolated margin."
    },
    {
      "name": "Jupiter",
      "description": "Jupiter Perpetuals — isolated margin."
    },
    {
      "name": "Streams",
      "description": "Real-time WebSocket feed of position lifecycle fills. One scope per connection, and the scope is the URL: `/v1/stream/{protocol}/fills`, optionally narrowed with `?market=` and/or `?address=` (naming both intersects them). The socket is read-only, and every frame is `{ data: … }` (or `{ error: … }`); a plain GET on the same URL echoes the resolved scope, so a URL can be validated without opening a socket. Premium plan only, capped at 50 concurrent connections per account. Delivered messages do not consume credits.\n"
    },
    {
      "name": "MCP",
      "description": "Model Context Protocol for AI agents (streamable HTTP at `/v1/mcp`). Pro and Premium plans; tool calls are metered like the REST endpoints they wrap.\n"
    }
  ],
  "paths": {
    "/v1/protocols": {
      "get": {
        "operationId": "listProtocols",
        "summary": "List supported protocols",
        "description": "Protocols available on this API.",
        "tags": [
          "Protocols"
        ],
        "responses": {
          "200": {
            "description": "Supported protocols.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ProtocolInfo"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/protocols/stats": {
      "get": {
        "operationId": "getProtocolsStats",
        "summary": "Cross-protocol stats",
        "description": "Current open interest, 24h volume, 24h fees, and 24h liquidation volume, broken out per protocol.\n",
        "tags": [
          "Protocols"
        ],
        "responses": {
          "200": {
            "description": "Cross-protocol stats.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Per-protocol breakdown; one entry per tracked protocol.",
                      "items": {
                        "$ref": "#/components/schemas/ProtocolOverview"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/gmtrade/markets": {
      "get": {
        "operationId": "listGmtradeMarkets",
        "summary": "List GMTrade markets",
        "description": "The tracked GMTrade market set — identifiers only. Rows are per market variant (uniquely identified by market_variant); variants sharing a grouped market are summed by history endpoints. The top-level markets array lists the distinct grouping keys those endpoints accept. For price, open interest, rates, and volume, use /v1/gmtrade/markets/stats.\n",
        "tags": [
          "GMTrade"
        ],
        "responses": {
          "200": {
            "description": "Tracked markets.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "markets"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/GmtradeMarketRef"
                      }
                    },
                    "markets": {
                      "type": "array",
                      "description": "Distinct index symbols across the tracked markets — the grouping keys other endpoints accept via ?market=.\n",
                      "items": {
                        "type": "string"
                      },
                      "example": [
                        "BTC-PERP",
                        "ETH-PERP",
                        "SOL-PERP"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/gmtrade/markets/stats": {
      "get": {
        "operationId": "getGmtradeMarketsStats",
        "summary": "GMTrade market stats",
        "description": "All active GMTrade markets with current price, open interest, net per-side funding rates, and 24h volume. Multiple market variants can share one grouped market; rows are per market variant. For just the tracked market identifiers, use /v1/gmtrade/markets.\n",
        "tags": [
          "GMTrade"
        ],
        "responses": {
          "200": {
            "description": "Active markets.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/GmtradeMarket"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/gmtrade/liquidations/search": {
      "get": {
        "operationId": "listGmtradeLiquidations",
        "summary": "Liquidations search",
        "description": "GMTrade liquidation events, newest first. Filter with `market` (matches all market variants of an index token) or `market_variant` (a single variant by its native identifier). Up to 500 rows per page.\n\nWalk history with **cursor pagination**: each full page returns a `next_cursor`; pass it back as `cursor` to fetch the next (older) page, until `next_cursor` is `null`. The walk is bounded by your plan's history lookback — a capped tier stops at its horizon (the page simply ends early). Each event carries the `position_id` of the lifecycle it terminated.\n",
        "tags": [
          "GMTrade"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Market"
          },
          {
            "$ref": "#/components/parameters/MarketVariant"
          },
          {
            "$ref": "#/components/parameters/From"
          },
          {
            "$ref": "#/components/parameters/To"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Liquidation events, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/GmtradeLiquidation"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true,
                      "description": "Opaque token for the next (older) page; `null` when the last page (or the tier lookback floor) is reached.\n"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/gmtrade/liquidations/history": {
      "get": {
        "operationId": "getGmtradeLiquidationHistory",
        "summary": "Liquidation history",
        "description": "Time-bucketed GMTrade liquidation volume. Filter with `market` (sums market variants) or omit for protocol-wide totals.\n",
        "tags": [
          "GMTrade"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Market"
          },
          {
            "$ref": "#/components/parameters/IntervalCoarse"
          },
          {
            "$ref": "#/components/parameters/From"
          },
          {
            "$ref": "#/components/parameters/To"
          }
        ],
        "responses": {
          "200": {
            "description": "Bucketed liquidation volume, oldest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "interval",
                    "from",
                    "to",
                    "data"
                  ],
                  "properties": {
                    "interval": {
                      "type": "string",
                      "example": "1h"
                    },
                    "from": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Effective window start — echoes the default/tier-clamped bound when `from` was omitted or clamped."
                    },
                    "to": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Effective window end (now, when `to` was omitted)."
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/GmtradeLiquidationBucket"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/gmtrade/open-interest": {
      "get": {
        "operationId": "getGmtradeOpenInterest",
        "summary": "Current open interest",
        "description": "Latest open interest snapshot per GMTrade market.",
        "tags": [
          "GMTrade"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MarketVariant"
          }
        ],
        "responses": {
          "200": {
            "description": "Current open interest per market.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/GmtradeOpenInterest"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/gmtrade/open-interest/history": {
      "get": {
        "operationId": "getGmtradeOpenInterestHistory",
        "summary": "Open interest history",
        "description": "Time-bucketed GMTrade open interest. Filter with `market` (sums market variants) or omit for protocol-wide totals. History depth: **30 days** on Trial & Starter, **90 days** on Pro, **full history** on Premium. GMTrade coverage reaches back to protocol launch (March 2025); data before May 2026 is reconstructed from the on-chain fill journal (validated against observed snapshots).\n",
        "tags": [
          "GMTrade"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Market"
          },
          {
            "$ref": "#/components/parameters/IntervalFine"
          },
          {
            "$ref": "#/components/parameters/From"
          },
          {
            "$ref": "#/components/parameters/To"
          }
        ],
        "responses": {
          "200": {
            "description": "Bucketed open interest, oldest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "interval",
                    "from",
                    "to",
                    "data"
                  ],
                  "properties": {
                    "interval": {
                      "type": "string",
                      "example": "1h"
                    },
                    "from": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Effective window start — echoes the default/tier-clamped bound when `from` was omitted or clamped."
                    },
                    "to": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Effective window end (now, when `to` was omitted)."
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/GmtradeOpenInterestBucket"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/gmtrade/volume/history": {
      "get": {
        "operationId": "getGmtradeVolumeHistory",
        "summary": "Volume history",
        "description": "Time-bucketed GMTrade trading volume and fees, aggregated from the fill journal. Filter with `market` (sums market variants) or omit for protocol-wide totals. Buckets with no fills are omitted. History depth: **30 days** on Trial & Starter, **90 days** on Pro, **full history** on Premium. Coverage reaches back to protocol launch (March 2025).\n",
        "tags": [
          "GMTrade"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Market"
          },
          {
            "$ref": "#/components/parameters/IntervalFine"
          },
          {
            "$ref": "#/components/parameters/From"
          },
          {
            "$ref": "#/components/parameters/To"
          }
        ],
        "responses": {
          "200": {
            "description": "Bucketed volume and fees, oldest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "interval",
                    "from",
                    "to",
                    "data"
                  ],
                  "properties": {
                    "interval": {
                      "type": "string",
                      "example": "1h"
                    },
                    "from": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Effective window start — echoes the default/tier-clamped bound when `from` was omitted or clamped."
                    },
                    "to": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Effective window end (now, when `to` was omitted)."
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/GmtradeVolumeBucket"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/gmtrade/funding-rates": {
      "get": {
        "operationId": "getGmtradeFundingRates",
        "summary": "Current funding rates",
        "description": "Latest net funding rate per GMTrade market, per side. Folds the signed funding leg together with the per-side borrowing leg into one rate: positive = the side receives, negative = it pays.\n",
        "tags": [
          "GMTrade"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MarketVariant"
          }
        ],
        "responses": {
          "200": {
            "description": "Current funding rate per market.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/GmtradeFundingRate"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/gmtrade/funding-rates/history": {
      "get": {
        "operationId": "getGmtradeFundingRateHistory",
        "summary": "Funding rate history",
        "description": "Time-bucketed net GMTrade funding rates per side (funding folded with borrowing, bucket average). Rates are per market variant (each collateral pool carries its own rates), so `market_variant` is required — there is no cross-variant aggregate. History is capped at **30 days** on every plan.\n",
        "tags": [
          "GMTrade"
        ],
        "parameters": [
          {
            "name": "market_variant",
            "in": "query",
            "required": true,
            "description": "Market variant in GMTrade's native format (a single collateral pool).",
            "schema": {
              "type": "string",
              "example": "SOL/USD[WSOL-USDC]"
            }
          },
          {
            "$ref": "#/components/parameters/IntervalCoarse"
          },
          {
            "$ref": "#/components/parameters/From"
          },
          {
            "$ref": "#/components/parameters/To"
          }
        ],
        "responses": {
          "200": {
            "description": "Bucketed funding rates, oldest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "interval",
                    "from",
                    "to",
                    "data"
                  ],
                  "properties": {
                    "interval": {
                      "type": "string",
                      "example": "1h"
                    },
                    "from": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Effective window start — echoes the default/tier-clamped bound when `from` was omitted or clamped."
                    },
                    "to": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Effective window end (now, when `to` was omitted)."
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/GmtradeFundingRateBucket"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/gmtrade/accounts/stats": {
      "get": {
        "operationId": "getGmtradeAccountsStats",
        "summary": "Accounts stats",
        "description": "Aggregate statistics across all GMTrade accounts.",
        "tags": [
          "GMTrade"
        ],
        "responses": {
          "200": {
            "description": "Aggregate account statistics.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AccountsSummary"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/gmtrade/accounts/search": {
      "get": {
        "operationId": "listGmtradeAccounts",
        "summary": "Accounts search",
        "description": "GMTrade trading accounts with lifetime stats, sortable. Up to 500 rows per page.",
        "tags": [
          "GMTrade"
        ],
        "parameters": [
          {
            "name": "sort_by",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "net_pnl",
                "volume",
                "win_rate",
                "positions",
                "current_notional"
              ],
              "default": "net_pnl"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            }
          },
          {
            "name": "min_trades",
            "in": "query",
            "required": false,
            "description": "Exclude accounts with fewer closed trades than this.",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          },
          {
            "name": "has_active_positions",
            "in": "query",
            "required": false,
            "description": "When true, only accounts with at least one open position.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Return only accounts whose address starts with this prefix, case-insensitive (4-44 alphanumeric characters).",
            "schema": {
              "type": "string",
              "minLength": 4,
              "maxLength": 44,
              "pattern": "^[a-zA-Z0-9]+$"
            }
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ],
        "responses": {
          "200": {
            "description": "Scored accounts.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/GmtradeAccount"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/gmtrade/accounts/{address}": {
      "get": {
        "operationId": "getGmtradeAccount",
        "summary": "Account stats",
        "description": "Lifetime GMTrade stats for a single wallet. Open positions are served (paginated, with live fields) by GET /open-positions/search?address=; historical positions by GET /accounts/{address}/history.\n",
        "tags": [
          "GMTrade"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Address"
          }
        ],
        "responses": {
          "200": {
            "description": "Account stats.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GmtradeAccount"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/gmtrade/accounts/{address}/history": {
      "get": {
        "operationId": "listGmtradeAccountHistory",
        "summary": "Account history",
        "description": "Historical positions for a GMTrade account: closed and liquidated position lifecycles, newest first. Open positions are not returned here — fetch them from GET /v1/gmtrade/open-positions/search?address={address}. Closed lifecycles older than your plan's lookback window are omitted (see \"Data retention\"). Up to 500 rows per page.\n",
        "tags": [
          "GMTrade"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Address"
          },
          {
            "$ref": "#/components/parameters/Market"
          },
          {
            "$ref": "#/components/parameters/MarketVariant"
          },
          {
            "name": "sort_by",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "opened_at",
                "closed_at",
                "size",
                "pnl",
                "leverage"
              ],
              "default": "opened_at"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            }
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ],
        "responses": {
          "200": {
            "description": "Historical positions, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/GmtradePositionHistoryItem"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/gmtrade/open-positions/search": {
      "get": {
        "operationId": "listGmtradeOpenPositions",
        "summary": "Open positions search",
        "description": "Currently open GMTrade positions across all accounts, sortable and filterable. Closed and liquidated positions are served by the account history resource (GET /accounts/{address}/history). Up to 500 rows per page.\n",
        "tags": [
          "GMTrade"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MarketVariant"
          },
          {
            "$ref": "#/components/parameters/Market"
          },
          {
            "name": "address",
            "in": "query",
            "required": false,
            "description": "Filter to one wallet address.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "side",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "long",
                "short"
              ]
            }
          },
          {
            "name": "min_size_usd",
            "in": "query",
            "required": false,
            "description": "Exclude positions smaller than this notional.",
            "schema": {
              "type": "number",
              "minimum": 0
            }
          },
          {
            "name": "sort_by",
            "in": "query",
            "required": false,
            "description": "liq_proximity sorts by distance between mark price and liquidation price (closest to liquidation first when order=asc); positions without a computable liquidation price are excluded.\n",
            "schema": {
              "type": "string",
              "enum": [
                "size",
                "pnl",
                "collateral",
                "leverage",
                "opened_at",
                "liq_proximity"
              ],
              "default": "size"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            }
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ],
        "responses": {
          "200": {
            "description": "Open positions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/GmtradePosition"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    },
                    "meta": {
                      "type": "object",
                      "description": "Set-level freshness for the whole filtered result (not just the current page). Each position's own update time is its `updated_at`; positions that traded recently are refreshed by the fill stream, idle ones by the periodic full poll.\n",
                      "properties": {
                        "oldest_updated": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true,
                          "description": "Oldest per-position `updated_at` across all matching open positions — nothing in the set is staler than this.\n"
                        },
                        "last_full_refresh": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true,
                          "description": "Timestamp of the most recent full poll re-mark represented in the set (poll-sourced rows only).\n"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/gmtrade/open-positions/stats": {
      "get": {
        "operationId": "getGmtradeOpenPositionsStats",
        "summary": "Open positions stats",
        "description": "Long/short split from latest snapshots, summed across a grouped market's variants when `market` is set. Omit `market` for a protocol-wide aggregate.",
        "tags": [
          "GMTrade"
        ],
        "parameters": [
          {
            "name": "market",
            "in": "query",
            "required": false,
            "description": "Grouped market symbol, e.g. SOL-PERP. Omit for protocol-wide totals.",
            "schema": {
              "type": "string"
            },
            "example": "SOL-PERP"
          }
        ],
        "responses": {
          "200": {
            "description": "Position stats for the grouped market or protocol.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GmtradePositionsSummary"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/gmtrade/positions/{address}/{id}": {
      "get": {
        "operationId": "getGmtradePosition",
        "summary": "Position stats",
        "description": "One GMTrade position lifecycle's record: its aggregates plus, for an open lifecycle, live mark price / liquidation price / unrealized PnL. A reused on-chain position spans several open→close lifecycles; `id` is the surrogate `position_id` that selects one of them. The fill-event timeline is paginated separately at `/v1/gmtrade/positions/{address}/{id}/lifecycle`. A stable, pollable resource.\n",
        "tags": [
          "GMTrade"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Address"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Surrogate position_id (position_history primary key).",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The position stats.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GmtradePositionHistoryItem"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/gmtrade/positions/{address}/{id}/lifecycle": {
      "get": {
        "operationId": "listGmtradePositionLifecycle",
        "summary": "Position lifecycle",
        "description": "The fill-event timeline for one GMTrade position lifecycle, paginated and sortable. Split out from the position stats so a long-running lifecycle with thousands of fills stays cheap to poll. Events are scoped server-side to the lifecycle's window; `id` is the surrogate `position_id` (same key as the record and the deep link). Chronological by default (`order=asc`). Up to 500 rows per page.\n",
        "tags": [
          "GMTrade"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Address"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Surrogate position_id (position_history primary key).",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "sort_by",
            "in": "query",
            "required": false,
            "description": "`time` (fill timestamp) or `size` (absolute notional delta).",
            "schema": {
              "type": "string",
              "enum": [
                "time",
                "size"
              ],
              "default": "time"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "asc"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Filter to a single event type. The values partition the timeline, so `increase` excludes the lifecycle's opening fill and `decrease` excludes its closing one — ask for those by name (`open` / `close`).\n",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "increase",
                "decrease",
                "close",
                "liquidation"
              ]
            }
          },
          {
            "name": "include_collateral_events",
            "in": "query",
            "required": false,
            "description": "GMTrade collateral edits arrive as increase/decrease events with `size_delta_usd = 0`. By default the timeline omits them and shows only actual fills (liquidations always included). Set `true` to include the collateral-only events.\n",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ],
        "responses": {
          "200": {
            "description": "Fill events for the lifecycle.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/GmtradePositionEvent"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/jupiter/markets": {
      "get": {
        "operationId": "listJupiterMarkets",
        "summary": "List Jupiter markets",
        "description": "The tracked Jupiter Perpetuals market set — identifiers only. For price, open interest, rates, and volume, use /v1/jupiter/markets/stats.\n",
        "tags": [
          "Jupiter"
        ],
        "responses": {
          "200": {
            "description": "Tracked markets.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/JupiterMarketRef"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/jupiter/markets/stats": {
      "get": {
        "operationId": "getJupiterMarketsStats",
        "summary": "Jupiter market stats",
        "description": "All Jupiter Perpetuals markets with current price, open interest, per-side funding rates, and 24h volume. For just the tracked market identifiers, use /v1/jupiter/markets.\n",
        "tags": [
          "Jupiter"
        ],
        "responses": {
          "200": {
            "description": "Active markets.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/JupiterMarket"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/jupiter/liquidations/search": {
      "get": {
        "operationId": "listJupiterLiquidations",
        "summary": "Liquidations search",
        "description": "Jupiter liquidation events, newest first. Filter with `market`. Up to 500 rows per page.\n\nWalk history with **cursor pagination**: each full page returns a `next_cursor`; pass it back as `cursor` to fetch the next (older) page, until `next_cursor` is `null`. The walk is bounded by your plan's history lookback — a capped tier stops at its horizon (the page simply ends early). Each event carries the `position_id` of the lifecycle it terminated.\n",
        "tags": [
          "Jupiter"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Market"
          },
          {
            "$ref": "#/components/parameters/From"
          },
          {
            "$ref": "#/components/parameters/To"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Liquidation events, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/JupiterLiquidation"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true,
                      "description": "Opaque token for the next (older) page; `null` when the last page (or the tier lookback floor) is reached.\n"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/jupiter/liquidations/history": {
      "get": {
        "operationId": "getJupiterLiquidationHistory",
        "summary": "Liquidation history",
        "description": "Time-bucketed Jupiter liquidation volume. Filter with `market` or omit for protocol-wide totals.\n",
        "tags": [
          "Jupiter"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Market"
          },
          {
            "$ref": "#/components/parameters/IntervalCoarse"
          },
          {
            "$ref": "#/components/parameters/From"
          },
          {
            "$ref": "#/components/parameters/To"
          }
        ],
        "responses": {
          "200": {
            "description": "Bucketed liquidation volume, oldest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "interval",
                    "from",
                    "to",
                    "data"
                  ],
                  "properties": {
                    "interval": {
                      "type": "string",
                      "example": "1h"
                    },
                    "from": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Effective window start — echoes the default/tier-clamped bound when `from` was omitted or clamped."
                    },
                    "to": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Effective window end (now, when `to` was omitted)."
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/JupiterLiquidationBucket"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/jupiter/open-interest": {
      "get": {
        "operationId": "getJupiterOpenInterest",
        "summary": "Current open interest",
        "description": "Latest open interest snapshot per Jupiter market.",
        "tags": [
          "Jupiter"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Market"
          }
        ],
        "responses": {
          "200": {
            "description": "Current open interest per market.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/JupiterOpenInterest"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/jupiter/open-interest/history": {
      "get": {
        "operationId": "getJupiterOpenInterestHistory",
        "summary": "Open interest history",
        "description": "Time-bucketed Jupiter open interest. Filter with `market` or omit for protocol-wide totals. History depth: **30 days** on Trial & Starter, **90 days** on Pro, **full history** on Premium. Jupiter coverage reaches back to protocol launch (July 2023); data before April 2026 is reconstructed from the on-chain fill journal (validated against observed snapshots).\n",
        "tags": [
          "Jupiter"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Market"
          },
          {
            "$ref": "#/components/parameters/IntervalFine"
          },
          {
            "$ref": "#/components/parameters/From"
          },
          {
            "$ref": "#/components/parameters/To"
          }
        ],
        "responses": {
          "200": {
            "description": "Bucketed open interest, oldest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "interval",
                    "from",
                    "to",
                    "data"
                  ],
                  "properties": {
                    "interval": {
                      "type": "string",
                      "example": "1h"
                    },
                    "from": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Effective window start — echoes the default/tier-clamped bound when `from` was omitted or clamped."
                    },
                    "to": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Effective window end (now, when `to` was omitted)."
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/JupiterOpenInterestBucket"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/jupiter/volume/history": {
      "get": {
        "operationId": "getJupiterVolumeHistory",
        "summary": "Volume history",
        "description": "Time-bucketed Jupiter trading volume and fees, aggregated from the fill journal. Filter with `market` or omit for protocol-wide totals. Buckets with no fills are omitted. History depth: **30 days** on Trial & Starter, **90 days** on Pro, **full history** on Premium. Coverage reaches back to protocol launch (July 2023).\n",
        "tags": [
          "Jupiter"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Market"
          },
          {
            "$ref": "#/components/parameters/IntervalFine"
          },
          {
            "$ref": "#/components/parameters/From"
          },
          {
            "$ref": "#/components/parameters/To"
          }
        ],
        "responses": {
          "200": {
            "description": "Bucketed volume and fees, oldest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "interval",
                    "from",
                    "to",
                    "data"
                  ],
                  "properties": {
                    "interval": {
                      "type": "string",
                      "example": "1h"
                    },
                    "from": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Effective window start — echoes the default/tier-clamped bound when `from` was omitted or clamped."
                    },
                    "to": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Effective window end (now, when `to` was omitted)."
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/JupiterVolumeBucket"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/jupiter/funding-rates": {
      "get": {
        "operationId": "getJupiterFundingRates",
        "summary": "Current funding rates",
        "description": "Latest net funding rate per Jupiter market, per side. Jupiter has no funding mechanism — only a borrow fee — so the funding rate is the negated per-side borrow rate (≤ 0, i.e. the side always pays).\n",
        "tags": [
          "Jupiter"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Market"
          }
        ],
        "responses": {
          "200": {
            "description": "Current funding rates per market.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/JupiterFundingRate"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/jupiter/funding-rates/history": {
      "get": {
        "operationId": "getJupiterFundingRateHistory",
        "summary": "Funding rate history",
        "description": "Time-bucketed net Jupiter funding rates per side (negated per-side borrow, bucket average). History is capped at **30 days** on every plan.\n",
        "tags": [
          "Jupiter"
        ],
        "parameters": [
          {
            "name": "market",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "SOL-PERP"
            }
          },
          {
            "$ref": "#/components/parameters/IntervalCoarse"
          },
          {
            "$ref": "#/components/parameters/From"
          },
          {
            "$ref": "#/components/parameters/To"
          }
        ],
        "responses": {
          "200": {
            "description": "Bucketed funding rates, oldest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "interval",
                    "from",
                    "to",
                    "data"
                  ],
                  "properties": {
                    "interval": {
                      "type": "string",
                      "example": "1h"
                    },
                    "from": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Effective window start — echoes the default/tier-clamped bound when `from` was omitted or clamped."
                    },
                    "to": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Effective window end (now, when `to` was omitted)."
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/JupiterFundingRateBucket"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/jupiter/accounts/stats": {
      "get": {
        "operationId": "getJupiterAccountsStats",
        "summary": "Accounts stats",
        "description": "Aggregate statistics across all Jupiter accounts.",
        "tags": [
          "Jupiter"
        ],
        "responses": {
          "200": {
            "description": "Aggregate account statistics.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AccountsSummary"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/jupiter/accounts/search": {
      "get": {
        "operationId": "listJupiterAccounts",
        "summary": "Accounts search",
        "description": "Jupiter trading accounts with lifetime stats, sortable. Up to 500 rows per page.",
        "tags": [
          "Jupiter"
        ],
        "parameters": [
          {
            "name": "sort_by",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "net_pnl",
                "volume",
                "win_rate",
                "positions",
                "current_notional"
              ],
              "default": "net_pnl"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            }
          },
          {
            "name": "min_trades",
            "in": "query",
            "required": false,
            "description": "Exclude accounts with fewer closed trades than this.",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          },
          {
            "name": "min_volume_usd",
            "in": "query",
            "required": false,
            "description": "Exclude accounts with less lifetime volume than this.",
            "schema": {
              "type": "number",
              "minimum": 0
            }
          },
          {
            "name": "has_active_positions",
            "in": "query",
            "required": false,
            "description": "When true, only accounts with at least one open position.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Return only accounts whose address starts with this prefix, case-insensitive (4-44 alphanumeric characters).",
            "schema": {
              "type": "string",
              "minLength": 4,
              "maxLength": 44,
              "pattern": "^[a-zA-Z0-9]+$"
            }
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ],
        "responses": {
          "200": {
            "description": "Scored accounts.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/JupiterAccount"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/jupiter/accounts/{address}": {
      "get": {
        "operationId": "getJupiterAccount",
        "summary": "Account stats",
        "description": "Lifetime Jupiter stats for a single wallet. Open positions are served (paginated, with live fields) by GET /open-positions/search?address=; historical positions by GET /accounts/{address}/history.\n",
        "tags": [
          "Jupiter"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Address"
          }
        ],
        "responses": {
          "200": {
            "description": "Account stats.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/JupiterAccount"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/jupiter/accounts/{address}/history": {
      "get": {
        "operationId": "listJupiterAccountHistory",
        "summary": "Account history",
        "description": "Historical positions for a Jupiter account: closed and liquidated position lifecycles, newest first. Open positions are not returned here — fetch them from GET /v1/jupiter/open-positions/search?address={address}. Closed lifecycles older than your plan's lookback window are omitted (see \"Data retention\"). Up to 500 rows per page.\n",
        "tags": [
          "Jupiter"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Address"
          },
          {
            "$ref": "#/components/parameters/Market"
          },
          {
            "name": "sort_by",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "opened_at",
                "closed_at",
                "size",
                "pnl",
                "leverage"
              ],
              "default": "opened_at"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            }
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ],
        "responses": {
          "200": {
            "description": "Historical positions, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/JupiterPositionHistoryItem"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/jupiter/open-positions/search": {
      "get": {
        "operationId": "listJupiterOpenPositions",
        "summary": "Open positions search",
        "description": "Currently open Jupiter positions across all accounts, sortable and filterable. Closed and liquidated positions are served by the account history resource (GET /accounts/{address}/history). Up to 500 rows per page.\n",
        "tags": [
          "Jupiter"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Market"
          },
          {
            "name": "address",
            "in": "query",
            "required": false,
            "description": "Filter to one wallet address.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "side",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "long",
                "short"
              ]
            }
          },
          {
            "name": "min_size_usd",
            "in": "query",
            "required": false,
            "description": "Exclude positions smaller than this notional.",
            "schema": {
              "type": "number",
              "minimum": 0
            }
          },
          {
            "name": "sort_by",
            "in": "query",
            "required": false,
            "description": "liq_proximity sorts by distance between mark price and liquidation price (closest to liquidation first when order=asc); positions without a computable liquidation price are excluded.\n",
            "schema": {
              "type": "string",
              "enum": [
                "size",
                "pnl",
                "collateral",
                "leverage",
                "opened_at",
                "liq_proximity"
              ],
              "default": "size"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            }
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ],
        "responses": {
          "200": {
            "description": "Open positions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/JupiterPosition"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    },
                    "meta": {
                      "type": "object",
                      "description": "Set-level freshness for the whole filtered result (not just the current page). Each position's own update time is its `updated_at`; positions that traded recently are refreshed by the fill stream, idle ones by the periodic full poll.\n",
                      "properties": {
                        "oldest_updated": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true,
                          "description": "Oldest per-position `updated_at` across all matching open positions — nothing in the set is staler than this.\n"
                        },
                        "last_full_refresh": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true,
                          "description": "Timestamp of the most recent full poll re-mark represented in the set (poll-sourced rows only).\n"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/jupiter/open-positions/stats": {
      "get": {
        "operationId": "getJupiterOpenPositionsStats",
        "summary": "Open positions stats",
        "description": "Long/short split from latest snapshots. Pass `market` to scope to one `market`; omit for a protocol-wide aggregate (`market`: `__all__`).",
        "tags": [
          "Jupiter"
        ],
        "parameters": [
          {
            "name": "market",
            "in": "query",
            "required": false,
            "description": "Market symbol, e.g. SOL-PERP. Omit for protocol-wide totals.",
            "schema": {
              "type": "string"
            },
            "example": "SOL-PERP"
          }
        ],
        "responses": {
          "200": {
            "description": "Position stats for the market or protocol.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/JupiterPositionsSummary"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/jupiter/positions/{address}/{id}": {
      "get": {
        "operationId": "getJupiterPosition",
        "summary": "Position stats",
        "description": "One Jupiter position lifecycle's record: its aggregates plus, for an open lifecycle, live mark price / liquidation price / unrealized PnL. A reused position PDA spans several open→close lifecycles; `id` is the surrogate `position_id` that selects one of them. The fill-event timeline is paginated separately at `/v1/jupiter/positions/{address}/{id}/lifecycle`. A stable, pollable resource.\n",
        "tags": [
          "Jupiter"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Address"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Surrogate position_id (position_history primary key).",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The position stats.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/JupiterPositionHistoryItem"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/jupiter/positions/{address}/{id}/lifecycle": {
      "get": {
        "operationId": "listJupiterPositionLifecycle",
        "summary": "Position lifecycle",
        "description": "The fill-event timeline for one Jupiter position lifecycle, paginated and sortable. Split out from the position stats so a long-running lifecycle with thousands of fills stays cheap to poll. Events are scoped server-side to the lifecycle's window; `id` is the surrogate `position_id` (same key as the record and the deep link). Chronological by default (`order=asc`). Up to 500 rows per page.\n",
        "tags": [
          "Jupiter"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Address"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Surrogate position_id (position_history primary key).",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "sort_by",
            "in": "query",
            "required": false,
            "description": "`time` (fill timestamp) or `size` (absolute notional delta).",
            "schema": {
              "type": "string",
              "enum": [
                "time",
                "size"
              ],
              "default": "time"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "asc"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Filter to a single event type. The values partition the timeline, so `increase` excludes the lifecycle's opening fill and `decrease` excludes its closing one — ask for those by name (`open` / `close`).\n",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "increase",
                "decrease",
                "close",
                "liquidation"
              ]
            }
          },
          {
            "name": "include_collateral_events",
            "in": "query",
            "required": false,
            "description": "Jupiter routes collateral deposits, withdrawals, and borrow-fee settlements through the same increase/decrease events as trades, with `size_delta_usd = 0`. By default the timeline omits them and shows only actual fills (liquidations always included). Set `true` to include the collateral-only events.\n",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ],
        "responses": {
          "200": {
            "description": "Fill events for the lifecycle.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/JupiterPositionEvent"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/stream/{protocol}/fills": {
      "get": {
        "operationId": "streamFills",
        "summary": "Lifecycle fills (WebSocket)",
        "description": "**WebSocket** endpoint (`wss://…/v1/stream/{protocol}/fills`). This path is a protocol upgrade, not a REST resource — a `GET` with the standard `Upgrade: websocket` handshake headers. Authenticate by sending your `x-api-key` as a request header on the upgrade (the key is never accepted as a query parameter).\n\nThe scope IS the URL, so a connection is its subscription: the socket is **read-only** (client frames are ignored), there is no subscribe protocol, and errors are plain HTTP statuses on the handshake — `400` for an unknown protocol, `401`/`403` for auth/plan. There is no acknowledgement frame either: the handshake's `101 Switching Protocols` is the acknowledgement, and what it confirms is the URL you requested. Events flow from there. To watch several scopes, open several connections.\n\nNarrow the stream with the optional `market` and `address` query parameters; naming both intersects them (that trader, in that market). A plain HTTP `GET` on the same URL (no `Upgrade` header) echoes the resolved scope with `200`, so you can validate a URL without opening a socket, and `GET /v1/stream` returns the directory.\n\nEvery event frame is a fill in its position-lifecycle context. `event` says what happened: `open` | `increase` | `decrease` | `close` | `liquidation`. Every frame is tagged with the **`position_id`** surrogate the REST surface exposes at `/v1/{protocol}/positions/{address}/{id}` — frames carry the fill's own facts, and a lifecycle's aggregate outcome (entry/exit price, peak size, net PnL) is fetched from that REST record. A liquidation is simply a frame with `event: \"liquidation\"` — there is no separate liquidation feed to correlate.\n\nCollateral-only events (deposits, withdrawals, borrow-fee settlements — `size_delta_usd = 0` that neither opens nor closes a lifecycle) are not streamed, mirroring the REST position-history default.\n\n**Included with the Premium plan.** Upgrade requests from other plans are rejected with `403`. Delivered messages do not consume credits. With connection = scope, **the only limit is the connection cap**: **50** concurrent connections per account (shared across all of the account's API keys). A connect beyond the cap is accepted and then immediately closed with WebSocket close code `4029`.\n\n### Scope\nScope is the protocol in the path plus two optional filters, mirroring the per-protocol REST paths:\n``` wss://api.perpdb.xyz/v1/stream/gmtrade/fills wss://api.perpdb.xyz/v1/stream/gmtrade/fills?market=SOL-PERP wss://api.perpdb.xyz/v1/stream/jupiter/fills?address=9xQ… wss://api.perpdb.xyz/v1/stream/gmtrade/fills?market=SOL-PERP&address=9xQ… ```\n| Scope | URL | |---|---| | One protocol | `/v1/stream/{protocol}/fills` | | One market | `…/fills?market=<market>` | | One account | `…/fills?address=<address>` | | One account in one market | `…/fills?market=<market>&address=<address>` |\n- `{protocol}` is one of `gmtrade`, `jupiter` (and `drift`, `phoenix`,\n  `hyperliquid` as they come online); it is lower-cased. There is\n  deliberately no cross-protocol scope — a new protocol going live never\n  changes what an existing connection receives.\n\n- `market` accepts a grouped `market` (`SOL-PERP`) or a native\n  `market_variant` (`SOL/USD[WSOL-USDC]`) — both forms match, so a grouped\n  market covers every collateral-pool variant. `address` is an owner\n  address. Both are matched **exactly** (case-sensitive), and naming both\n  narrows to their intersection.\n\n- There is no server-side event-type filtering — pick the scope you want\n  and filter locally on `event` (total platform fill volume is on the\n  order of one event per second, so even a full protocol scope is light).\n\n\n### Delivery semantics\nEach event is delivered **once** per connection. This is a **best-effort** feed: the underlying transport is fire-and-forget and lossy (events are not replayed across a server restart or a client reconnect — and reconnecting a scoped socket rebuilds its subscription by construction). The durable source of truth remains the REST endpoints. Every frame carries enough identity (`tx`, `trade_id`, `position_id`, `position_size_usd`) to dedupe and to reconcile against REST after a gap.\n\n### The `fill` event\n`event` is the lifecycle-aware fill action: `open` is the lifecycle's first fill, `increase` / `decrease` are mid-lifecycle changes, and `close` / `liquidation` are terminal. A lifecycle opened and terminated by a single fill emits only its terminal event. `position_id` is the lifecycle surrogate and is always present - producers resolve it atomically from the same transaction that persists the lifecycle record. `size_delta_usd` is signed (`+` increase, `−` decrease). `realized_pnl_usd` is the PnL realized by **this fill**; lifecycle aggregates come from the REST record at `position_id`. `entry_price` is the position's running VWAP entry after this fill — it moves on increases, stays flat on decreases, and equals the REST record's `entry_price_vwap` on the final fill — so unrealized PnL is computable from the stream alone: `(mark − entry_price) × position_size_usd`.\nFees are disjoint buckets on one basis with the REST surface: `fee_usd` is the **order (trading) fee alone** — identical to the REST timeline's `fee_usd`, to the position record's `fees_usd`, and to what feeds `fees_24h_usd`, so fees sum cleanly from fill to lifecycle to rollup. `borrowing_fee_usd` and `liquidation_fee_usd` carry the other charges every protocol levies, and `fee_total_usd` is the all-in sum.\nThe basis is shared; the field list is per-protocol. A charge a protocol does not levy is **absent** from its frames rather than present and always null — every connection is protocol-scoped, so a frame only carries the fee fields its own protocol has (see the per-protocol notes below).\nPer-protocol differences (matching the REST fills surface):\n- **gmtrade** — `market_variant` is\n  the native collateral-pool variant the fill executed on; `trade_id` is\n  the protocol-native fill identity. Carries `funding_fee_usd`; carries no\n  `price_impact_fee_usd`, because GMX price impact is a separate signed\n  value that can be a rebate and so is not a fee.\n- **jupiter** — `market_variant` and\n  `trade_id` are always `null` (Jupiter has no market variants and dedupes\n  fills on `tx`). Carries `price_impact_fee_usd`, the impact component of\n  `fee_usd` rather than an addend; carries no `funding_fee_usd`, having no\n  funding mechanism.\n\nEvery frame uses the same envelope as the REST surface — `{ \"data\": … }` for a fill, `{ \"error\": { \"code\", \"message\" } }` for the one error the socket reports (the connection cap, sent just before a `4029` close). One feed means one shape, so there is no type tag to switch on.\nA mid-lifecycle scale-in:\n```json {\n  \"data\": {\n    \"protocol\": \"gmtrade\",\n    \"market\": \"SOL-PERP\",\n    \"market_variant\": \"SOL/USD[WSOL-USDC]\",\n    \"position_id\": 403867588,\n    \"event\": \"increase\",\n    \"side\": \"long\",\n    \"size_delta_usd\": 1250.0,\n    \"position_size_usd\": 3750.0,\n    \"price\": 168.42,\n    \"entry_price\": 165.13,\n    \"collateral_delta_usd\": 250.0,\n    \"realized_pnl_usd\": null,\n    \"fee_usd\": 0.63,\n    \"borrowing_fee_usd\": 0.11,\n    \"funding_fee_usd\": 0.04,\n    \"liquidation_fee_usd\": null,\n    \"fee_total_usd\": 0.78,\n    \"trade_id\": 91823,\n    \"address\": \"9xQ…\",\n    \"tx\": \"5xY…\",\n    \"time\": \"2026-07-06T12:34:56.000Z\"\n  }\n} ```\nA liquidation closing the lifecycle:\n```json {\n  \"data\": {\n    \"protocol\": \"jupiter\",\n    \"market\": \"SOL-PERP\",\n    \"market_variant\": null,\n    \"position_id\": 403867588,\n    \"event\": \"liquidation\",\n    \"side\": \"long\",\n    \"size_delta_usd\": -12500.0,\n    \"position_size_usd\": 0.0,\n    \"price\": 133.4,\n    \"entry_price\": 148.2,\n    \"collateral_delta_usd\": -1250.0,\n    \"realized_pnl_usd\": -1180.5,\n    \"fee_usd\": 12.5,\n    \"price_impact_fee_usd\": 1.8,\n    \"borrowing_fee_usd\": 3.2,\n    \"liquidation_fee_usd\": 4.4,\n    \"fee_total_usd\": 20.1,\n    \"trade_id\": null,\n    \"address\": \"9xQ…\",\n    \"tx\": \"5jk…\",\n    \"time\": \"2026-07-06T12:34:56.000Z\"\n  }\n} ```\n",
        "tags": [
          "Streams"
        ],
        "parameters": [
          {
            "name": "protocol",
            "in": "path",
            "required": true,
            "description": "The protocol this connection streams.",
            "schema": {
              "type": "string",
              "enum": [
                "gmtrade",
                "jupiter"
              ],
              "example": "gmtrade"
            }
          },
          {
            "name": "market",
            "in": "query",
            "required": false,
            "description": "Optional. Narrow the stream to one market — either a grouped `market` (`SOL-PERP`) or a native `market_variant` (`SOL/USD[WSOL-USDC]`); both forms match.\n",
            "schema": {
              "type": "string",
              "example": "SOL-PERP"
            }
          },
          {
            "name": "address",
            "in": "query",
            "required": false,
            "description": "Optional. Narrow the stream to one owner address.",
            "schema": {
              "type": "string",
              "example": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"
            }
          }
        ],
        "responses": {
          "101": {
            "description": "Switching Protocols — the WebSocket handshake succeeded. This IS the acknowledgement: the socket delivers `fill` frames from here, with no subscription echo, because the scope it confirmed is the URL you requested.\n"
          },
          "200": {
            "description": "Plain GET (no upgrade) — echoes the resolved scope, so a client can validate protocol and filters without opening a socket.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "protocol": {
                      "type": "string"
                    },
                    "market": {
                      "type": "string",
                      "nullable": true
                    },
                    "address": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown protocol."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/v1/mcp": {
      "post": {
        "operationId": "mcp",
        "summary": "MCP endpoint (streamable HTTP)",
        "description": "Model Context Protocol endpoint for AI agents. Connect any MCP-capable client using this URL with the `x-api-key` header — the server speaks MCP's streamable-HTTP transport (JSON-RPC over POST). Available on Pro and Premium plans. Each tool call is metered against the same rate limit and monthly credit quota as the REST endpoint it wraps. Tool discovery happens in-protocol (`tools/list`); the request/response bodies below are JSON-RPC envelopes, not REST resources.\n",
        "tags": [
          "MCP"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "JSON-RPC 2.0 request envelope (MCP streamable HTTP)."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC 2.0 response envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "API key (prefix: ak_...). Created from the PerpDB account modal (API Keys tab)."
      }
    },
    "schemas": {
      "Protocol": {
        "type": "string",
        "description": "Protocol identifier.",
        "enum": [
          "gmtrade",
          "jupiter"
        ]
      },
      "ProtocolInfo": {
        "type": "object",
        "required": [
          "id",
          "name",
          "chain"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/Protocol"
          },
          "name": {
            "type": "string",
            "example": "GMTrade"
          },
          "chain": {
            "type": "string",
            "example": "solana"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Machine-readable error code.",
                "example": "invalid_parameter"
              },
              "message": {
                "type": "string",
                "example": "interval must be one of: 1h, 4h, 1d"
              }
            }
          }
        }
      },
      "ProtocolOverview": {
        "type": "object",
        "description": "One protocol's slice of the cross-protocol stats.",
        "required": [
          "protocol"
        ],
        "properties": {
          "protocol": {
            "$ref": "#/components/schemas/Protocol"
          },
          "open_interest_usd": {
            "type": "number",
            "description": "Current open interest across the protocol's markets.",
            "example": 412300000
          },
          "volume_24h_usd": {
            "type": "number",
            "description": "Traded volume over the trailing 24h.",
            "example": 1875000000
          },
          "fees_24h_usd": {
            "type": "number",
            "description": "Trading fees over the trailing 24h (excludes funding/borrowing).",
            "example": 562500
          },
          "liquidations_24h_usd": {
            "type": "number",
            "description": "Liquidated notional over the trailing 24h.",
            "example": 8400000
          },
          "markets": {
            "type": "integer",
            "description": "Number of active markets.",
            "example": 68
          }
        }
      },
      "GmtradeMarketRef": {
        "type": "object",
        "required": [
          "market_variant",
          "market"
        ],
        "description": "A tracked market variant, identifiers only.",
        "properties": {
          "market_variant": {
            "type": "string",
            "description": "Unique market identifier.",
            "example": "SOL/USD[WSOL-USDC]"
          },
          "market": {
            "type": "string",
            "description": "Index token symbol shared by market variants (history grouping key).",
            "example": "SOL-PERP"
          }
        }
      },
      "GmtradeMarket": {
        "type": "object",
        "required": [
          "market_variant",
          "market"
        ],
        "properties": {
          "market_variant": {
            "type": "string",
            "description": "Unique market identifier.",
            "example": "SOL/USD[WSOL-USDC]"
          },
          "market": {
            "type": "string",
            "description": "Index token symbol shared by market variants.",
            "example": "SOL-PERP"
          },
          "index_price": {
            "type": "number"
          },
          "open_interest_long_usd": {
            "type": "number"
          },
          "open_interest_short_usd": {
            "type": "number"
          },
          "funding_rate_long": {
            "type": "number",
            "description": "Net hourly funding rate for long positions: GMTrade's signed funding leg minus the long borrowing rate, folded into one figure. Positive = longs receive, negative = longs pay. Annualize by ×24×365.\n"
          },
          "funding_rate_short": {
            "type": "number",
            "description": "Net hourly funding rate for short positions (funding leg minus the short borrowing rate); same sign convention as funding_rate_long.\n"
          },
          "volume_24h_usd": {
            "type": "number"
          },
          "fees_24h_usd": {
            "type": "number",
            "description": "Trailing-24h trading fees (USD); excludes funding/borrowing."
          },
          "liquidations_24h_usd": {
            "type": "number",
            "description": "Trailing-24h liquidation volume (USD) for this market."
          }
        }
      },
      "Side": {
        "type": "string",
        "description": "Position side.",
        "enum": [
          "long",
          "short"
        ]
      },
      "GmtradeLiquidation": {
        "type": "object",
        "required": [
          "time",
          "market_variant",
          "side",
          "size_usd",
          "price",
          "tx"
        ],
        "properties": {
          "time": {
            "type": "string",
            "format": "date-time"
          },
          "market_variant": {
            "type": "string",
            "example": "SOL/USD[WSOL-USDC]"
          },
          "market": {
            "type": "string",
            "example": "SOL-PERP"
          },
          "side": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Side"
              }
            ],
            "description": "Side of the liquidated position."
          },
          "size_usd": {
            "type": "number",
            "description": "Notional size liquidated."
          },
          "collateral_usd": {
            "type": "number"
          },
          "price": {
            "type": "number",
            "description": "Index price at liquidation."
          },
          "address": {
            "type": "string",
            "description": "Liquidated wallet address."
          },
          "tx": {
            "type": "string",
            "description": "Transaction signature."
          },
          "position_id": {
            "type": "integer",
            "nullable": true,
            "description": "Lifecycle surrogate of the position this liquidation terminated (the same id served at `/v1/gmtrade/positions/{address}/{id}`). Resolved to the lifecycle active at the liquidation time, so a reused position PDA maps to the correct segment. `null` if the fill could not be attributed to a lifecycle.\n",
            "example": 403867588
          }
        }
      },
      "GmtradeLiquidationBucket": {
        "type": "object",
        "required": [
          "time"
        ],
        "properties": {
          "time": {
            "type": "string",
            "format": "date-time",
            "description": "Bucket start."
          },
          "count": {
            "type": "integer"
          },
          "long_usd": {
            "type": "number",
            "description": "Liquidated long notional in bucket."
          },
          "short_usd": {
            "type": "number"
          },
          "total_usd": {
            "type": "number"
          }
        }
      },
      "GmtradeOpenInterest": {
        "type": "object",
        "required": [
          "market_variant",
          "time",
          "long_usd",
          "short_usd",
          "total_usd"
        ],
        "properties": {
          "market_variant": {
            "type": "string",
            "example": "SOL/USD[WSOL-USDC]"
          },
          "market": {
            "type": "string",
            "example": "SOL-PERP"
          },
          "time": {
            "type": "string",
            "format": "date-time",
            "description": "Snapshot time."
          },
          "long_usd": {
            "type": "number"
          },
          "short_usd": {
            "type": "number"
          },
          "total_usd": {
            "type": "number"
          }
        }
      },
      "GmtradeOpenInterestBucket": {
        "type": "object",
        "required": [
          "time"
        ],
        "properties": {
          "time": {
            "type": "string",
            "format": "date-time",
            "description": "Bucket start."
          },
          "long_usd": {
            "type": "number",
            "description": "Open interest at end of bucket."
          },
          "short_usd": {
            "type": "number"
          },
          "total_usd": {
            "type": "number"
          }
        }
      },
      "GmtradeVolumeBucket": {
        "type": "object",
        "required": [
          "time"
        ],
        "properties": {
          "time": {
            "type": "string",
            "format": "date-time",
            "description": "Bucket start."
          },
          "volume_usd": {
            "type": "number",
            "description": "Traded notional in the bucket (sum of absolute size deltas; liquidations carry no size delta)."
          },
          "fees_usd": {
            "type": "number",
            "description": "Trading fees collected in the bucket (order-fee basis, excludes funding/borrow)."
          }
        }
      },
      "GmtradeFundingRate": {
        "type": "object",
        "required": [
          "market_variant",
          "time"
        ],
        "properties": {
          "market_variant": {
            "type": "string",
            "example": "SOL/USD[WSOL-USDC]"
          },
          "time": {
            "type": "string",
            "format": "date-time",
            "description": "Snapshot time."
          },
          "funding_rate_long": {
            "type": "number",
            "description": "Net hourly funding rate for longs: the signed funding leg minus the long borrowing rate. Positive = longs receive, negative = longs pay.\n"
          },
          "funding_rate_short": {
            "type": "number",
            "description": "Net hourly funding rate for shorts; same sign convention."
          }
        }
      },
      "GmtradeFundingRateBucket": {
        "type": "object",
        "required": [
          "time"
        ],
        "properties": {
          "time": {
            "type": "string",
            "format": "date-time",
            "description": "Bucket start."
          },
          "funding_rate_long": {
            "type": "number",
            "description": "Bucket-average net hourly funding rate for longs."
          },
          "funding_rate_short": {
            "type": "number",
            "description": "Bucket-average net hourly funding rate for shorts."
          }
        }
      },
      "AccountsSummary": {
        "type": "object",
        "description": "Aggregate statistics across all of a protocol's accounts.",
        "required": [
          "total_accounts"
        ],
        "properties": {
          "total_accounts": {
            "type": "integer"
          },
          "accounts_with_open_positions": {
            "type": "integer"
          },
          "total_positions_ever": {
            "type": "integer",
            "description": "Lifetime position count summed across accounts."
          },
          "total_active_positions": {
            "type": "integer",
            "description": "Currently-open position count summed across accounts."
          },
          "total_volume_usd": {
            "type": "number"
          },
          "avg_net_pnl_usd": {
            "type": "number",
            "description": "Mean net PnL per account."
          },
          "total_profits_usd": {
            "type": "number",
            "description": "Sum of net PnL over profitable accounts."
          },
          "total_losses_usd": {
            "type": "number",
            "description": "Sum of net PnL over losing accounts (negative)."
          },
          "avg_win_rate": {
            "type": "number",
            "nullable": true,
            "description": "Mean win rate as a 0..1 fraction."
          }
        }
      },
      "AccountBase": {
        "type": "object",
        "description": "Account stats shared by every protocol. Each protocol extends this schema with protocol-specific fields (see JupiterAccount).\n",
        "required": [
          "address"
        ],
        "properties": {
          "address": {
            "type": "string",
            "description": "Wallet address (base58)."
          },
          "trades": {
            "type": "integer",
            "description": "Closed positions counted as trades."
          },
          "wins": {
            "type": "integer"
          },
          "losses": {
            "type": "integer"
          },
          "win_rate": {
            "type": "number",
            "description": "wins / trades (0..1)."
          },
          "volume_usd": {
            "type": "number",
            "description": "Lifetime traded notional."
          },
          "net_pnl_usd": {
            "type": "number",
            "description": "Lifetime realized PnL net of fees."
          },
          "positions": {
            "type": "integer",
            "description": "Lifetime positions opened."
          },
          "open_positions": {
            "type": "integer",
            "description": "Currently-open positions."
          },
          "first_trade_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_trade_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "GmtradeAccount": {
        "description": "GMTrade account with extended lifetime stats.",
        "allOf": [
          {
            "$ref": "#/components/schemas/AccountBase"
          },
          {
            "type": "object",
            "properties": {
              "realized_pnl_usd": {
                "type": "number",
                "description": "Lifetime realized PnL before fees."
              },
              "fees_usd": {
                "type": "number",
                "description": "Lifetime trading fees paid."
              },
              "funding_fees_usd": {
                "type": "number",
                "description": "Lifetime funding paid."
              },
              "funding_earned_usd": {
                "type": "number",
                "description": "Lifetime funding received. Paid and received are separate one-sided on-chain buckets — a net funding loss shows as funding_fees_usd > funding_earned_usd, never a negative earned.\n"
              },
              "borrowing_fees_usd": {
                "type": "number"
              },
              "liquidation_fees_usd": {
                "type": "number",
                "description": "Lifetime liquidation fees paid."
              },
              "avg_leverage": {
                "type": "number"
              },
              "max_leverage": {
                "type": "number"
              },
              "avg_position_size_usd": {
                "type": "number"
              },
              "max_position_size_usd": {
                "type": "number"
              },
              "avg_position_duration_hours": {
                "type": "number"
              },
              "open_notional_usd": {
                "type": "number",
                "description": "Total notional of currently open positions."
              },
              "markets_traded": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "example": [
                  "SOL-PERP",
                  "BTC-PERP"
                ]
              },
              "market_variants_traded": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "example": [
                  "SOL/USD[WSOL-USDC]",
                  "BTC/USD[WBTC]"
                ]
              }
            }
          }
        ]
      },
      "Pagination": {
        "type": "object",
        "required": [
          "limit",
          "offset",
          "total"
        ],
        "properties": {
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "total": {
            "type": "integer",
            "description": "Total rows matching the query, ignoring limit/offset."
          }
        }
      },
      "PositionBase": {
        "type": "object",
        "description": "Fields shared by positions on every protocol. Each protocol extends this schema with protocol-specific fields (e.g. GmtradePosition adds borrowing and funding fee accruals). Shared field names mean the same thing on every protocol. Size and collateral are NOT in the base: open-position resources carry live current_size_usd / current_collateral_usd, while lifecycle records carry the peaks (max_size_usd / max_collateral_usd).\n",
        "required": [
          "protocol",
          "market",
          "address",
          "side",
          "entry_price",
          "opened_at"
        ],
        "properties": {
          "protocol": {
            "$ref": "#/components/schemas/Protocol"
          },
          "market": {
            "type": "string",
            "description": "Grouped market identifier (Jupiter \"SOL-PERP\"; GMTrade \"SOL/USD\", summing all variants). GMTrade rows additionally carry `market_variant` for the single collateral pool (\"SOL/USD[WSOL-USDC]\").\n"
          },
          "address": {
            "type": "string",
            "description": "Owner wallet address (base58)."
          },
          "side": {
            "$ref": "#/components/schemas/Side"
          },
          "entry_price": {
            "type": "number"
          },
          "opened_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "GmtradePositionHistoryItem": {
        "description": "A single GMTrade position lifecycle (open through close or liquidation). Lifecycle records carry the PEAKS reached over the lifecycle (max_size_usd / max_collateral_usd); an open lifecycle additionally carries live current_size_usd / current_collateral_usd from the latest snapshot. entry_price is the size-weighted (VWAP) entry.\n",
        "allOf": [
          {
            "$ref": "#/components/schemas/PositionBase"
          },
          {
            "type": "object",
            "required": [
              "status",
              "position_id"
            ],
            "properties": {
              "max_size_usd": {
                "type": "number",
                "description": "Maximum notional (USD) reached over the lifecycle."
              },
              "current_size_usd": {
                "type": "number",
                "nullable": true,
                "description": "Current position size (notional, USD) from the latest snapshot. Only populated for an open lifecycle via the position-history endpoint.\n"
              },
              "max_collateral_usd": {
                "type": "number",
                "description": "Peak collateral (USD) held at any point in the lifecycle."
              },
              "current_collateral_usd": {
                "type": "number",
                "nullable": true,
                "description": "Current on-chain collateral from the latest snapshot. Sits at or below max_collateral_usd once margin has been withdrawn. Only populated for an open lifecycle via the position-history endpoint.\n"
              },
              "unsettled_borrowing_usd": {
                "type": "number",
                "nullable": true,
                "description": "Borrowing fee (USD) accrued since the last on-chain settle. current_collateral_usd is POSTED collateral; net current collateral = posted − unsettled_borrowing_usd − unsettled_funding_usd + claimable_funding_usd. Only populated for an open lifecycle.\n"
              },
              "unsettled_funding_usd": {
                "type": "number",
                "nullable": true,
                "description": "Funding OWED (USD) accrued since the last settle (paying side only; 0 while on the receiving side). Only populated for an open lifecycle.\n"
              },
              "claimable_funding_usd": {
                "type": "number",
                "nullable": true,
                "description": "Funding receivable (USD) accrued since the last settle (both payout-token halves summed). Only populated for an open lifecycle.\n"
              },
              "position_id": {
                "type": "integer",
                "description": "Surrogate id uniquely identifying this position lifecycle (a reused on-chain position spans several). Stable per-position deep-link key.\n"
              },
              "status": {
                "type": "string",
                "enum": [
                  "open",
                  "closed",
                  "liquidated"
                ]
              },
              "market": {
                "type": "string",
                "example": "SOL-PERP"
              },
              "closed_at": {
                "type": "string",
                "format": "date-time",
                "description": "Absent while status is open."
              },
              "exit_price": {
                "type": "number",
                "description": "Size-weighted average exit price."
              },
              "max_leverage": {
                "type": "number",
                "description": "Peak leverage over the lifecycle."
              },
              "realized_pnl_usd": {
                "type": "number",
                "description": "Realized PnL before fees."
              },
              "fees_usd": {
                "type": "number",
                "description": "Trading fees paid over the lifecycle."
              },
              "liquidation_fees_usd": {
                "type": "number",
                "description": "Liquidation fees paid over the lifecycle."
              },
              "funding_fees_usd": {
                "type": "number"
              },
              "borrowing_fees_usd": {
                "type": "number"
              },
              "funding_earned_usd": {
                "type": "number",
                "description": "Funding received over the lifecycle (separate one-sided bucket from funding_fees_usd).\n"
              },
              "net_pnl_usd": {
                "type": "number",
                "description": "realized_pnl_usd minus all fees."
              },
              "fill_count": {
                "type": "integer"
              },
              "open_tx": {
                "type": "string",
                "description": "Transaction signature that opened the position."
              },
              "close_tx": {
                "type": "string",
                "description": "Transaction signature that closed it, if closed."
              },
              "unrealized_pnl_usd": {
                "type": "number",
                "nullable": true,
                "description": "Live unrealized PnL from the latest snapshot. Only populated for an open lifecycle via the position-history endpoint (null otherwise).\n"
              },
              "liquidation_price": {
                "type": "number",
                "nullable": true,
                "description": "Live liquidation price from the latest snapshot. Only populated for an open lifecycle via the position-history endpoint.\n"
              },
              "mark_price": {
                "type": "number",
                "nullable": true,
                "description": "Live mark (oracle) price from the latest snapshot. Only populated for an open lifecycle via the position-history endpoint.\n"
              },
              "updated_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "Timestamp of the snapshot the live fields (unrealized_pnl_usd, liquidation_price, mark_price, current_size_usd, current_collateral_usd) were taken from. Only populated for an open lifecycle via the position-history endpoint.\n"
              }
            }
          }
        ]
      },
      "OpenPositionLive": {
        "type": "object",
        "required": [
          "current_size_usd",
          "current_collateral_usd"
        ],
        "properties": {
          "current_size_usd": {
            "type": "number",
            "description": "Current position size (notional, USD) from the latest snapshot."
          },
          "current_collateral_usd": {
            "type": "number",
            "description": "Current on-chain collateral (USD) from the latest snapshot."
          },
          "leverage": {
            "type": "number",
            "description": "current_size_usd / current_collateral_usd at last update."
          }
        }
      },
      "GmtradePosition": {
        "description": "An open GMTrade position.",
        "allOf": [
          {
            "$ref": "#/components/schemas/PositionBase"
          },
          {
            "$ref": "#/components/schemas/OpenPositionLive"
          },
          {
            "type": "object",
            "properties": {
              "position_id": {
                "type": "integer",
                "nullable": true,
                "description": "Surrogate id of the position lifecycle this open snapshot belongs to (the containing `position_history` row). Deep-link target. Null under rebuild lag, when the containing lifecycle hasn't been written yet.\n"
              },
              "market": {
                "type": "string",
                "example": "SOL-PERP"
              },
              "max_size_usd": {
                "type": "number",
                "nullable": true,
                "description": "Maximum notional (USD) reached over the containing lifecycle. From the same lifecycle lookup as position_id — null under rebuild lag.\n"
              },
              "max_collateral_usd": {
                "type": "number",
                "nullable": true,
                "description": "Peak collateral (USD) held at any point in the containing lifecycle. Null under rebuild lag, like position_id.\n"
              },
              "unsettled_borrowing_usd": {
                "type": "number",
                "nullable": true,
                "description": "Borrowing fee (USD) accrued since the position's last on-chain settle. current_collateral_usd is the POSTED on-chain collateral; net current collateral = posted − unsettled_borrowing_usd − unsettled_funding_usd + claimable_funding_usd. Null briefly under write lag.\n"
              },
              "unsettled_funding_usd": {
                "type": "number",
                "nullable": true,
                "description": "Funding OWED (USD) accrued since the last settle — the paying side only; 0 when the position is on the receiving side. Null briefly under write lag.\n"
              },
              "claimable_funding_usd": {
                "type": "number",
                "nullable": true,
                "description": "Funding receivable (USD) accrued since the last settle (both payout-token halves summed). Null briefly under write lag.\n"
              },
              "mark_price": {
                "type": "number"
              },
              "liquidation_price": {
                "type": "number",
                "description": "Estimated liquidation price; null when not computable."
              },
              "unrealized_pnl_usd": {
                "type": "number"
              },
              "updated_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        ]
      },
      "PositionsSummaryBase": {
        "type": "object",
        "description": "Long/short split from latest snapshots. Shared figures; each protocol adds an echo of the `market` filter, null when the figures are summed protocol-wide.",
        "required": [
          "total_positions"
        ],
        "properties": {
          "snapshot_ts": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Snapshot timestamp the figures are drawn from."
          },
          "total_positions": {
            "type": "integer"
          },
          "long_positions": {
            "type": "integer"
          },
          "short_positions": {
            "type": "integer"
          },
          "long_notional_usd": {
            "type": "number"
          },
          "short_notional_usd": {
            "type": "number"
          },
          "long_upnl_usd": {
            "type": "number",
            "description": "Summed unrealized PnL (USD) of open longs. Positions whose mark is momentarily unknown are excluded from the sum."
          },
          "short_upnl_usd": {
            "type": "number",
            "description": "Summed unrealized PnL (USD) of open shorts. Positions whose mark is momentarily unknown are excluded from the sum."
          },
          "avg_leverage": {
            "type": "number",
            "nullable": true
          }
        }
      },
      "GmtradePositionsSummary": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PositionsSummaryBase"
          },
          {
            "type": "object",
            "properties": {
              "market": {
                "type": "string",
                "nullable": true,
                "description": "Echo of the `market` filter (variants summed); null = protocol-wide totals."
              }
            }
          }
        ]
      },
      "GmtradePositionEvent": {
        "type": "object",
        "description": "A lifecycle event on a position.",
        "required": [
          "time",
          "type",
          "tx"
        ],
        "properties": {
          "time": {
            "type": "string",
            "format": "date-time"
          },
          "type": {
            "type": "string",
            "description": "What this fill did to the position, in the same vocabulary the stream's `event` field uses: `open` is the lifecycle's first fill, `close` the decrease that took it to zero, and `liquidation` is terminal. The two boundary labels are derived from the lifecycle's own opening and closing transactions, so a mid-lifecycle `increase` or `decrease` never carries them, and a lifecycle that ended in a liquidation has no `close`.\n",
            "enum": [
              "open",
              "increase",
              "decrease",
              "close",
              "liquidation"
            ]
          },
          "size_delta_usd": {
            "type": "number",
            "description": "Signed notional change."
          },
          "position_size_usd": {
            "type": "number",
            "description": "Notional after this event."
          },
          "collateral_delta_usd": {
            "type": "number",
            "description": "Signed collateral change."
          },
          "price": {
            "type": "number",
            "description": "Execution price."
          },
          "entry_price": {
            "type": "number",
            "nullable": true,
            "description": "The position's running VWAP entry after this event — it moves on increases, stays flat on decreases, and equals the position record's entry_price_vwap on the final event. Null before the first priced increase.\n"
          },
          "pnl_delta_usd": {
            "type": "number",
            "description": "Realized PnL delta on this event, if any."
          },
          "fee_usd": {
            "type": "number",
            "description": "Order (trading) fee on this event, and only that — the same basis as the position record's fees_usd and the fees_24h_usd rollups, so per-event fees sum to the lifecycle and on up. Borrow, funding and liquidation charges are their own fields below; fee_total_usd is the all-in sum."
          },
          "borrowing_fee_usd": {
            "type": "number",
            "nullable": true,
            "description": "Borrow fee settled on this event; null when it carried none."
          },
          "funding_fee_usd": {
            "type": "number",
            "nullable": true,
            "description": "Funding fee settled on this event; null when it carried none."
          },
          "liquidation_fee_usd": {
            "type": "number",
            "nullable": true,
            "description": "Liquidation penalty fee (liquidation events only)."
          },
          "fee_total_usd": {
            "type": "number",
            "nullable": true,
            "description": "All-in cost of this event: fee_usd + borrowing_fee_usd + funding_fee_usd + liquidation_fee_usd."
          },
          "tx": {
            "type": "string",
            "description": "Transaction signature."
          }
        }
      },
      "JupiterMarketRef": {
        "type": "object",
        "required": [
          "market"
        ],
        "description": "A tracked market, identifier only.",
        "properties": {
          "market": {
            "type": "string",
            "description": "Unique market identifier.",
            "example": "SOL-PERP"
          }
        }
      },
      "JupiterMarket": {
        "type": "object",
        "required": [
          "market"
        ],
        "properties": {
          "market": {
            "type": "string",
            "description": "Unique market identifier.",
            "example": "SOL-PERP"
          },
          "index_price": {
            "type": "number",
            "description": "Oracle price of the index token."
          },
          "open_interest_long_usd": {
            "type": "number"
          },
          "open_interest_short_usd": {
            "type": "number"
          },
          "funding_rate_long": {
            "type": "number",
            "description": "Hourly funding rate for long positions. Jupiter has no funding mechanism — only a borrow fee — so this is the negated long borrow rate: ≤ 0, i.e. longs always pay. Annualize by ×24×365.\n"
          },
          "funding_rate_short": {
            "type": "number",
            "description": "Hourly funding rate for short positions: the negated short borrow rate (≤ 0, shorts always pay).\n"
          },
          "volume_24h_usd": {
            "type": "number"
          },
          "fees_24h_usd": {
            "type": "number",
            "description": "Trailing-24h trading fees (USD); excludes funding/borrowing."
          },
          "liquidations_24h_usd": {
            "type": "number",
            "description": "Trailing-24h liquidation volume (USD) for this market."
          }
        }
      },
      "JupiterLiquidation": {
        "type": "object",
        "required": [
          "time",
          "market",
          "side",
          "size_usd",
          "price",
          "tx"
        ],
        "properties": {
          "time": {
            "type": "string",
            "format": "date-time"
          },
          "market": {
            "type": "string",
            "example": "SOL-PERP"
          },
          "side": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Side"
              }
            ],
            "description": "Side of the liquidated position."
          },
          "size_usd": {
            "type": "number",
            "description": "Notional size liquidated."
          },
          "collateral_usd": {
            "type": "number"
          },
          "price": {
            "type": "number",
            "description": "Oracle price at liquidation."
          },
          "address": {
            "type": "string",
            "description": "Liquidated wallet address."
          },
          "tx": {
            "type": "string",
            "description": "Transaction signature."
          },
          "position_id": {
            "type": "integer",
            "nullable": true,
            "description": "Lifecycle surrogate of the position this liquidation terminated (the same id served at `/v1/jupiter/positions/{address}/{id}`). Resolved to the lifecycle active at the liquidation time, so a reused position PDA maps to the correct segment. `null` if the fill could not be attributed to a lifecycle.\n",
            "example": 403867588
          }
        }
      },
      "JupiterLiquidationBucket": {
        "type": "object",
        "required": [
          "time"
        ],
        "properties": {
          "time": {
            "type": "string",
            "format": "date-time",
            "description": "Bucket start."
          },
          "count": {
            "type": "integer"
          },
          "long_usd": {
            "type": "number",
            "description": "Liquidated long notional in bucket."
          },
          "short_usd": {
            "type": "number"
          },
          "total_usd": {
            "type": "number"
          }
        }
      },
      "JupiterOpenInterest": {
        "type": "object",
        "required": [
          "market",
          "time",
          "long_usd",
          "short_usd",
          "total_usd"
        ],
        "properties": {
          "market": {
            "type": "string",
            "example": "SOL-PERP"
          },
          "time": {
            "type": "string",
            "format": "date-time",
            "description": "Snapshot time."
          },
          "long_usd": {
            "type": "number"
          },
          "short_usd": {
            "type": "number"
          },
          "total_usd": {
            "type": "number"
          }
        }
      },
      "JupiterOpenInterestBucket": {
        "type": "object",
        "required": [
          "time"
        ],
        "properties": {
          "time": {
            "type": "string",
            "format": "date-time",
            "description": "Bucket start."
          },
          "long_usd": {
            "type": "number",
            "description": "Open interest at end of bucket."
          },
          "short_usd": {
            "type": "number"
          },
          "total_usd": {
            "type": "number"
          }
        }
      },
      "JupiterVolumeBucket": {
        "type": "object",
        "required": [
          "time"
        ],
        "properties": {
          "time": {
            "type": "string",
            "format": "date-time",
            "description": "Bucket start."
          },
          "volume_usd": {
            "type": "number",
            "description": "Traded notional in the bucket (sum of absolute size deltas; liquidations carry no size delta)."
          },
          "fees_usd": {
            "type": "number",
            "description": "Trading fees collected in the bucket (order-fee basis, excludes funding/borrow)."
          }
        }
      },
      "JupiterFundingRate": {
        "type": "object",
        "description": "Net hourly funding per side. Jupiter has no funding mechanism — only a borrow fee — so this is the negated per-side borrow rate: ≤ 0, i.e. the side always pays.\n",
        "required": [
          "market",
          "time"
        ],
        "properties": {
          "market": {
            "type": "string",
            "example": "SOL-PERP"
          },
          "time": {
            "type": "string",
            "format": "date-time",
            "description": "Snapshot time."
          },
          "funding_rate_long": {
            "type": "number",
            "description": "Hourly funding rate for longs (negated long borrow rate; ≤ 0).\n"
          },
          "funding_rate_short": {
            "type": "number",
            "description": "Hourly funding rate for shorts (negated short borrow rate; ≤ 0).\n"
          }
        }
      },
      "JupiterFundingRateBucket": {
        "type": "object",
        "required": [
          "time"
        ],
        "properties": {
          "time": {
            "type": "string",
            "format": "date-time",
            "description": "Bucket start."
          },
          "funding_rate_long": {
            "type": "number",
            "description": "Bucket-average hourly funding rate for longs (≤ 0)."
          },
          "funding_rate_short": {
            "type": "number",
            "description": "Bucket-average hourly funding rate for shorts (≤ 0)."
          }
        }
      },
      "JupiterAccount": {
        "description": "Jupiter account with extended lifetime stats.",
        "allOf": [
          {
            "$ref": "#/components/schemas/AccountBase"
          },
          {
            "type": "object",
            "properties": {
              "realized_pnl_usd": {
                "type": "number",
                "description": "Lifetime realized PnL before fees."
              },
              "fees_usd": {
                "type": "number",
                "description": "Lifetime order fees paid (base + price impact). Excludes borrow and liquidation fees — the three buckets are disjoint and net_pnl_usd = realized − fees − borrowing − liquidation."
              },
              "borrowing_fees_usd": {
                "type": "number",
                "description": "Lifetime borrow fees paid (Jupiter's hourly borrow charge; the on-chain events call it fundingFeeUsd but Jupiter has no funding mechanism — always a cost, never earned)."
              },
              "liquidation_fees_usd": {
                "type": "number",
                "description": "Lifetime liquidation penalty fees paid."
              },
              "avg_leverage": {
                "type": "number"
              },
              "max_leverage": {
                "type": "number"
              },
              "avg_position_size_usd": {
                "type": "number"
              },
              "max_position_size_usd": {
                "type": "number"
              },
              "avg_position_duration_hours": {
                "type": "number"
              },
              "open_notional_usd": {
                "type": "number",
                "description": "Total notional of currently open positions."
              },
              "markets_traded": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "example": [
                  "SOL-PERP",
                  "ETH-PERP"
                ]
              }
            }
          }
        ]
      },
      "JupiterPositionHistoryItem": {
        "description": "A single Jupiter position lifecycle (open through close/liquidation). Lifecycle records carry the PEAKS reached over the lifecycle (max_size_usd / max_collateral_usd); an open lifecycle additionally carries live current_size_usd / current_collateral_usd from the latest snapshot. entry_price is the size-weighted (VWAP) entry.\n",
        "allOf": [
          {
            "$ref": "#/components/schemas/PositionBase"
          },
          {
            "type": "object",
            "required": [
              "status",
              "position_id"
            ],
            "properties": {
              "max_size_usd": {
                "type": "number",
                "description": "Maximum notional (USD) reached over the lifecycle."
              },
              "current_size_usd": {
                "type": "number",
                "nullable": true,
                "description": "Current position size (notional, USD) from the latest snapshot. Only populated for an open lifecycle via the position-history endpoint.\n"
              },
              "max_collateral_usd": {
                "type": "number",
                "description": "Peak collateral (USD) held at any point in the lifecycle."
              },
              "current_collateral_usd": {
                "type": "number",
                "nullable": true,
                "description": "Current on-chain collateral from the latest snapshot. Sits at or below max_collateral_usd once margin has been withdrawn. Only populated for an open lifecycle via the position-history endpoint.\n"
              },
              "unsettled_borrowing_usd": {
                "type": "number",
                "nullable": true,
                "description": "Borrow fee (USD) accrued since the last on-chain settle — Jupiter's only fee accrual (no funding leg). current_collateral_usd is POSTED collateral; net current collateral = posted − unsettled_borrowing_usd. Only populated for an open lifecycle.\n"
              },
              "position_id": {
                "type": "integer",
                "description": "Surrogate id uniquely identifying this position lifecycle (a reused on-chain position spans several). Stable per-position deep-link key.\n"
              },
              "status": {
                "type": "string",
                "enum": [
                  "open",
                  "closed",
                  "liquidated"
                ]
              },
              "closed_at": {
                "type": "string",
                "format": "date-time",
                "description": "Absent while status is open."
              },
              "exit_price": {
                "type": "number",
                "description": "Size-weighted average exit price."
              },
              "max_leverage": {
                "type": "number",
                "description": "Peak leverage over the lifecycle."
              },
              "realized_pnl_usd": {
                "type": "number",
                "description": "Realized PnL before fees."
              },
              "fees_usd": {
                "type": "number",
                "description": "Order fees paid over the lifecycle (base + price impact). Excludes borrow and liquidation fees — the buckets are disjoint. For lifecycles fully before 2025-04-09 the on-chain events carried only a lumped fee, which is reported here."
              },
              "borrowing_fees_usd": {
                "type": "number",
                "description": "Borrow fees paid over the lifecycle (Jupiter's hourly borrow charge; on-chain \"fundingFeeUsd\" — Jupiter has no funding mechanism, so this is always a cost)."
              },
              "liquidation_fees_usd": {
                "type": "number",
                "description": "Liquidation penalty fees paid (liquidated lifecycles)."
              },
              "net_pnl_usd": {
                "type": "number",
                "description": "realized_pnl_usd − fees_usd − borrowing_fees_usd − liquidation_fees_usd."
              },
              "fill_count": {
                "type": "integer"
              },
              "open_tx": {
                "type": "string",
                "description": "Transaction signature that opened the position."
              },
              "close_tx": {
                "type": "string",
                "description": "Transaction signature that closed it, if closed."
              },
              "unrealized_pnl_usd": {
                "type": "number",
                "nullable": true,
                "description": "Live unrealized PnL from the latest snapshot. Only populated for an open lifecycle via the position-history endpoint (null otherwise).\n"
              },
              "liquidation_price": {
                "type": "number",
                "nullable": true,
                "description": "Live liquidation price from the latest snapshot. Only populated for an open lifecycle via the position-history endpoint.\n"
              },
              "mark_price": {
                "type": "number",
                "nullable": true,
                "description": "Live mark (oracle) price from the latest snapshot. Only populated for an open lifecycle via the position-history endpoint.\n"
              },
              "updated_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "Timestamp of the snapshot the live fields (unrealized_pnl_usd, liquidation_price, mark_price, current_size_usd, current_collateral_usd) were taken from. Only populated for an open lifecycle via the position-history endpoint.\n"
              }
            }
          }
        ]
      },
      "JupiterPosition": {
        "description": "An open Jupiter position.",
        "allOf": [
          {
            "$ref": "#/components/schemas/PositionBase"
          },
          {
            "$ref": "#/components/schemas/OpenPositionLive"
          },
          {
            "type": "object",
            "properties": {
              "position_id": {
                "type": "integer",
                "nullable": true,
                "description": "Surrogate id of the position lifecycle this open snapshot belongs to (the containing `position_history` row). Deep-link target. Null under rebuild lag, when the containing lifecycle hasn't been written yet.\n"
              },
              "max_size_usd": {
                "type": "number",
                "nullable": true,
                "description": "Maximum notional (USD) reached over the containing lifecycle. From the same lifecycle lookup as position_id — null under rebuild lag.\n"
              },
              "max_collateral_usd": {
                "type": "number",
                "nullable": true,
                "description": "Peak collateral (USD) held at any point in the containing lifecycle. Null under rebuild lag, like position_id.\n"
              },
              "unsettled_borrowing_usd": {
                "type": "number",
                "nullable": true,
                "description": "Borrow fee (USD) accrued since the position's last on-chain settle — Jupiter's only fee accrual (no funding leg). current_collateral_usd is the POSTED on-chain collateral; net current collateral = posted − unsettled_borrowing_usd. Null briefly under write lag.\n"
              },
              "mark_price": {
                "type": "number",
                "description": "Current oracle price."
              },
              "liquidation_price": {
                "type": "number",
                "description": "Estimated liquidation price; null when not computable."
              },
              "unrealized_pnl_usd": {
                "type": "number"
              },
              "updated_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        ]
      },
      "JupiterPositionsSummary": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PositionsSummaryBase"
          },
          {
            "type": "object",
            "properties": {
              "market": {
                "type": "string",
                "nullable": true,
                "description": "Echo of the `market` filter; null = protocol-wide totals."
              }
            }
          }
        ]
      },
      "JupiterPositionEvent": {
        "type": "object",
        "description": "A fill event on a position.",
        "required": [
          "time",
          "type",
          "tx"
        ],
        "properties": {
          "time": {
            "type": "string",
            "format": "date-time"
          },
          "type": {
            "type": "string",
            "description": "What this fill did to the position, in the same vocabulary the stream's `event` field uses: `open` is the lifecycle's first fill, `close` the decrease that took it to zero, and `liquidation` is terminal. The two boundary labels are derived from the lifecycle's own opening and closing transactions, so a mid-lifecycle `increase` or `decrease` never carries them, and a lifecycle that ended in a liquidation has no `close`.\n",
            "enum": [
              "open",
              "increase",
              "decrease",
              "close",
              "liquidation"
            ]
          },
          "size_delta_usd": {
            "type": "number",
            "description": "Signed notional change."
          },
          "position_size_usd": {
            "type": "number",
            "description": "Notional after this event."
          },
          "collateral_delta_usd": {
            "type": "number",
            "description": "Signed collateral change."
          },
          "price": {
            "type": "number",
            "description": "Execution price."
          },
          "entry_price": {
            "type": "number",
            "nullable": true,
            "description": "The position's running VWAP entry after this event — it moves on increases, stays flat on decreases, and equals the position record's entry_price_vwap on the final event. Null before the first priced increase.\n"
          },
          "pnl_delta_usd": {
            "type": "number",
            "description": "Realized PnL on this event, if any."
          },
          "fee_usd": {
            "type": "number",
            "description": "Order (trading) fee on this event, and only that — the same basis as the position record's fees_usd and the fees_24h_usd rollups, so per-event fees sum to the lifecycle and on up. Borrow, funding and liquidation charges are their own fields below; fee_total_usd is the all-in sum."
          },
          "price_impact_fee_usd": {
            "type": "number",
            "nullable": true,
            "description": "Price-impact component OF fee_usd (base fee = fee_usd − this), not an additional charge. Null on events before 2025-04-09 (the backfill era carried only the lumped on-chain fee)."
          },
          "borrowing_fee_usd": {
            "type": "number",
            "nullable": true,
            "description": "Borrow fee settled on this event (on-chain \"fundingFeeUsd\"; Jupiter has no funding mechanism)."
          },
          "liquidation_fee_usd": {
            "type": "number",
            "nullable": true,
            "description": "Liquidation penalty fee (liquidation events only)."
          },
          "fee_total_usd": {
            "type": "number",
            "description": "All-in cost of this event: fee_usd + borrowing_fee_usd + liquidation_fee_usd. Jupiter has no funding mechanism, so there is no funding charge to add — each protocol carries only the charges it actually levies."
          },
          "tx": {
            "type": "string",
            "description": "Transaction signature."
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing, invalid, or revoked API key.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limit exceeded for this API key's tier.",
        "headers": {
          "X-RateLimit-Limit": {
            "description": "Requests allowed in the current window.",
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Remaining": {
            "description": "Requests remaining in the current window.",
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Reset": {
            "description": "Unix timestamp (seconds) when the window resets.",
            "schema": {
              "type": "integer"
            }
          },
          "Retry-After": {
            "description": "Seconds to wait before retrying.",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "BadRequest": {
        "description": "Malformed or invalid request parameters.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "The requested entity does not exist.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The API key is valid but its plan does not permit this operation (e.g. live streams require a Pro or Premium plan).\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "parameters": {
      "Market": {
        "name": "market",
        "in": "query",
        "required": false,
        "description": "Grouped market symbol, comparable across protocols (e.g. \"SOL-PERP\"). On GMTrade this spans every collateral-pool variant of the market (history rows are summed across variants). Omit for protocol-wide results.\n",
        "schema": {
          "type": "string",
          "example": "SOL-PERP"
        }
      },
      "MarketVariant": {
        "name": "market_variant",
        "in": "query",
        "required": false,
        "description": "GMTrade only: one native collateral-pool variant of a market (e.g. \"SOL/USD[WSOL-USDC]\").\n",
        "schema": {
          "type": "string",
          "example": "SOL/USD[WSOL-USDC]"
        }
      },
      "From": {
        "name": "from",
        "in": "query",
        "required": false,
        "description": "Window start (inclusive). Defaults per endpoint. Floored to your plan's lookback window (see \"Data retention\"): an earlier value is silently clamped and the effective start is echoed back in the response.\n",
        "schema": {
          "type": "string",
          "format": "date-time"
        }
      },
      "To": {
        "name": "to",
        "in": "query",
        "required": false,
        "description": "Window end (exclusive). Defaults to now.",
        "schema": {
          "type": "string",
          "format": "date-time"
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "Opaque keyset pagination token from the previous page's `next_cursor`. Walks newest→oldest; omit for the first (newest) page. Hold `market`/`market_variant` and `from`/`to` constant across the walk — the token is bound to them and a mismatch is rejected.\n",
        "schema": {
          "type": "string"
        }
      },
      "IntervalCoarse": {
        "name": "interval",
        "in": "query",
        "required": false,
        "description": "Bucket size for time-series aggregation.",
        "schema": {
          "type": "string",
          "enum": [
            "1h",
            "4h",
            "1d"
          ],
          "default": "1h"
        }
      },
      "IntervalFine": {
        "name": "interval",
        "in": "query",
        "required": false,
        "description": "Bucket size for time-series aggregation.",
        "schema": {
          "type": "string",
          "enum": [
            "5m",
            "15m",
            "1h",
            "4h",
            "1d"
          ],
          "default": "1h"
        }
      },
      "Offset": {
        "name": "offset",
        "in": "query",
        "required": false,
        "description": "Row offset for pagination. Capped at 10000; deeper paging is rejected — narrow the result set with from/to instead.\n",
        "schema": {
          "type": "integer",
          "default": 0,
          "minimum": 0,
          "maximum": 10000
        }
      },
      "Address": {
        "name": "address",
        "in": "path",
        "required": true,
        "description": "Wallet address (base58).",
        "schema": {
          "type": "string"
        }
      }
    }
  },
  "x-tagGroups": [
    {
      "name": "Platform",
      "tags": [
        "Protocols"
      ]
    },
    {
      "name": "GMTrade",
      "tags": [
        "GMTrade"
      ]
    },
    {
      "name": "Jupiter",
      "tags": [
        "Jupiter"
      ]
    },
    {
      "name": "Streams",
      "tags": [
        "Streams"
      ]
    },
    {
      "name": "MCP",
      "tags": [
        "MCP"
      ]
    }
  ]
}