openapi: 3.1.0
info:
  title: Funding Perpetuals API
  version: 1.0.0
  description: >-
    Programmatic access to Funding Perpetuals paper-trading challenge accounts.
    API trades use the same real order books and simulated execution engine as
    the web trading interface.
  contact:
    url: https://fundingperpetuals.com/support
servers:
  - url: https://developers.fundingperpetuals.com
    description: Production
security:
  - bearerAuth: []
tags:
  - name: General
  - name: Accounts
  - name: Markets
  - name: Positions
  - name: Orders
  - name: Fills
  - name: Strategy Orders
paths:
  /v1:
    get:
      operationId: getApiInformation
      summary: Get API information
      tags: [General]
      security: []
      responses:
        "200":
          description: API name, version, and documentation URL.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: object
                    required: [name, version, documentation]
                    properties:
                      name: { type: string, example: Funding Perpetuals API }
                      version: { type: string, example: v1 }
                      documentation:
                        type: string
                        format: uri
                        example: https://docs.fundingperpetuals.com
  /v1/accounts:
    get:
      operationId: listAccounts
      summary: List challenge accounts
      description: Returns the authenticated user's non-archived challenge accounts.
      tags: [Accounts]
      responses:
        "200":
          description: Challenge accounts.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: array
                    items: { $ref: "#/components/schemas/Account" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /v1/accounts/{account_id}:
    get:
      operationId: getAccount
      summary: Get a challenge account
      tags: [Accounts]
      parameters:
        - $ref: "#/components/parameters/AccountId"
      responses:
        "200":
          description: Challenge account detail.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data: { $ref: "#/components/schemas/Account" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /v1/markets:
    get:
      operationId: listMarkets
      summary: List tradable markets
      description: Returns the selected execution venue for each currently tradable instrument.
      tags: [Markets]
      security: []
      responses:
        "200":
          description: Tradable markets.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: array
                    items: { $ref: "#/components/schemas/Market" }
  /v1/markets/{market_id}:
    get:
      operationId: getMarket
      summary: Get a tradable market
      tags: [Markets]
      parameters:
        - $ref: "#/components/parameters/MarketId"
      responses:
        "200":
          description: Market detail and execution capabilities.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data: { $ref: "#/components/schemas/Market" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /v1/markets/{market_id}/quote:
    get:
      operationId: getMarketQuote
      summary: Get an execution quote
      description: >-
        Returns the best bid, best ask, and midpoint from a fresh authoritative
        execution book. URL-encode the pipe in a market ID when it is used in
        the path.
      tags: [Markets]
      parameters:
        - $ref: "#/components/parameters/MarketId"
      responses:
        "200":
          description: Current execution quote.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data: { $ref: "#/components/schemas/Quote" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "503":
          description: A fresh quote is temporarily unavailable.
          headers:
            Retry-After: { $ref: "#/components/headers/RetryAfter" }
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ErrorEnvelope" }
  /v1/positions:
    get:
      operationId: listOpenPositions
      summary: List open positions
      tags: [Positions]
      parameters:
        - $ref: "#/components/parameters/AccountIdQuery"
        - name: status
          in: query
          description: Open positions by default, or cursor-paginated closed positions.
          schema: { type: string, enum: [open, closed], default: open }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 100, default: 50 }
        - name: cursor
          in: query
          schema: { type: string, maxLength: 1024 }
      responses:
        "200":
          description: Open positions for the account.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: array
                    items: { $ref: "#/components/schemas/Position" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /v1/positions/{position_id}:
    get:
      operationId: getPosition
      summary: Get a position
      tags: [Positions]
      parameters:
        - $ref: "#/components/parameters/PositionId"
      responses:
        "200":
          description: Open or closed position detail.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data: { $ref: "#/components/schemas/Position" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /v1/positions/{position_id}/close:
    post:
      operationId: closePosition
      summary: Close part or all of a position
      description: >-
        Resolves the current position side, leverage, and margin mode on the
        server and submits a reduce-only market, limit, or conditional order.
        Omit size to close the complete current position.
      tags: [Positions]
      parameters:
        - $ref: "#/components/parameters/PositionId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/ClosePositionRequest" }
      responses:
        "200":
          description: Idempotent replay.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/OrderEnvelope" }
        "201":
          description: Close order accepted.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/OrderEnvelope" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "409": { $ref: "#/components/responses/Conflict" }
        "422": { $ref: "#/components/responses/TradeRejected" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /v1/positions/{position_id}/exit-orders:
    put:
      operationId: replacePositionExitOrders
      summary: Atomically update position TP/SL orders
      description: >-
        Applies a stale-snapshot-guarded batch of place, modify, and cancel
        operations. New opposite-group legs can be paired as OCO orders.
      tags: [Positions]
      parameters:
        - $ref: "#/components/parameters/PositionId"
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/ExitOrderBatchRequest" }
      responses:
        "200":
          description: Exit orders updated.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data: { $ref: "#/components/schemas/Position" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "409": { $ref: "#/components/responses/Conflict" }
        "422": { $ref: "#/components/responses/TradeRejected" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /v1/positions/{position_id}/isolated-margin:
    put:
      operationId: setPositionIsolatedMargin
      summary: Set the position's extra isolated margin
      description: >-
        Sets a desired total rather than applying a delta, making an identical
        retry safe. Reductions use a fresh authoritative book and reject when
        the new liquidation risk would be unsafe.
      tags: [Positions]
      parameters:
        - $ref: "#/components/parameters/PositionId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required: [extra_margin]
              properties:
                extra_margin: { type: number, minimum: 0 }
      responses:
        "200":
          description: Isolated margin target applied.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data: { $ref: "#/components/schemas/Position" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "409": { $ref: "#/components/responses/Conflict" }
        "422": { $ref: "#/components/responses/TradeRejected" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /v1/orders:
    get:
      operationId: listOrders
      summary: List recent orders
      description: Returns newest orders first with a bounded opaque cursor.
      tags: [Orders]
      parameters:
        - $ref: "#/components/parameters/AccountIdQuery"
        - name: limit
          in: query
          description: Orders per page.
          schema: { type: integer, minimum: 1, maximum: 100, default: 50 }
        - name: cursor
          in: query
          description: Opaque cursor returned as `next_cursor` by the previous page.
          schema: { type: string, maxLength: 1024 }
        - name: status
          in: query
          description: Set to `working` for the complete bounded pending and resting set.
          schema: { type: string, enum: [working] }
      responses:
        "200":
          description: A page of orders.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/OrderPage" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "503":
          description: A complete working-order snapshot is temporarily unavailable.
          headers:
            Retry-After: { $ref: "#/components/headers/RetryAfter" }
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ErrorEnvelope" }
    post:
      operationId: createOrder
      summary: Place a standard or advanced order
      description: >-
        Places market, limit, chase, stop, take, take-profit, or external
        conditional orders through the authoritative paper-trading engine. The
        idempotency key is committed transactionally with the order. Omit type
        for the original market-order behavior.
      tags: [Orders]
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CreateOrderRequest" }
            examples:
              buyBitcoin:
                summary: Buy BTC with cross margin
                value:
                  type: market
                  account_id: j57abc123
                  market_id: binance|BTCUSDT
                  side: buy
                  size: 0.001
                  expected_price: 118000
                  leverage: 2
                  margin_mode: cross
      responses:
        "200":
          description: Idempotent replay returning the order's current state.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data: { $ref: "#/components/schemas/Order" }
        "201":
          description: Order accepted on the first completed request.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data: { $ref: "#/components/schemas/Order" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "409":
          description: The idempotency key was already used for a different order payload.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ErrorEnvelope" }
        "413":
          description: Request body exceeds 16 KiB.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ErrorEnvelope" }
        "415":
          description: Request body is not sent as application/json.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ErrorEnvelope" }
        "422":
          description: Order rejected by a trading rule.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/TradeRejectionEnvelope" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /v1/orders/{order_id}:
    get:
      operationId: getOrder
      summary: Get an order
      tags: [Orders]
      parameters:
        - $ref: "#/components/parameters/OrderId"
      responses:
        "200":
          description: Order detail.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data: { $ref: "#/components/schemas/Order" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
    patch:
      operationId: modifyOrder
      summary: Modify a working order
      description: Reprices a modifiable order and optionally changes its size.
      tags: [Orders]
      parameters:
        - $ref: "#/components/parameters/OrderId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required: [price]
              properties:
                price: { type: number, exclusiveMinimum: 0 }
                size: { type: number, exclusiveMinimum: 0 }
      responses:
        "200":
          description: Modification applied or queued behind an armed order.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/OrderEnvelope" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "409": { $ref: "#/components/responses/Conflict" }
        "422": { $ref: "#/components/responses/TradeRejected" }
        "429": { $ref: "#/components/responses/RateLimited" }
    delete:
      operationId: cancelOrder
      summary: Cancel a working order
      description: Cancels a resting order or queues cancellation for an armed order.
      tags: [Orders]
      parameters:
        - $ref: "#/components/parameters/OrderId"
      responses:
        "200":
          description: Cancellation accepted.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: object
                    required: [id, canceled]
                    properties:
                      id: { type: string }
                      canceled: { type: boolean, const: true }
                      queued: { type: boolean }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "409":
          description: The order is terminal or managed by copy trading.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ErrorEnvelope" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /v1/orders/{order_id}/attached-exits:
    put:
      operationId: updateAttachedExits
      summary: Update TP/SL attached to a resting limit entry
      description: Set a leg with a positive price or remove it with null.
      tags: [Orders]
      parameters:
        - $ref: "#/components/parameters/OrderId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              properties:
                take_profit_price:
                  oneOf:
                    [{ type: number, exclusiveMinimum: 0 }, { type: "null" }]
                stop_loss_price:
                  oneOf:
                    [{ type: number, exclusiveMinimum: 0 }, { type: "null" }]
              minProperties: 1
      responses:
        "200":
          description: Attached exits updated.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/OrderEnvelope" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "409": { $ref: "#/components/responses/Conflict" }
        "422": { $ref: "#/components/responses/TradeRejected" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /v1/fills:
    get:
      operationId: listFills
      summary: List fills in settlement order
      tags: [Fills]
      parameters:
        - $ref: "#/components/parameters/AccountIdQuery"
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 100, default: 50 }
        - name: cursor
          in: query
          schema: { type: string, maxLength: 1024 }
      responses:
        "200":
          description: Cursor-paginated filled orders, newest settlement first.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/OrderPage" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /v1/twap-orders:
    get:
      operationId: listActiveTwapOrders
      summary: List active TWAP and VWAP orders
      tags: [Strategy Orders]
      parameters:
        - $ref: "#/components/parameters/AccountIdQuery"
      responses:
        "200":
          description: Running and canceling scheduled orders.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: array
                    items: { $ref: "#/components/schemas/TwapOrder" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
    post:
      operationId: createTwapOrder
      summary: Create a TWAP or VWAP order
      tags: [Strategy Orders]
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CreateTwapOrderRequest" }
      responses:
        "200":
          description: Idempotent replay.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/TwapOrderEnvelope" }
        "201":
          description: Scheduled order created.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/TwapOrderEnvelope" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "409": { $ref: "#/components/responses/Conflict" }
        "422": { $ref: "#/components/responses/TradeRejected" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /v1/twap-orders/{twap_order_id}:
    get:
      operationId: getTwapOrder
      summary: Get a TWAP or VWAP order
      tags: [Strategy Orders]
      parameters:
        - $ref: "#/components/parameters/TwapOrderId"
      responses:
        "200":
          description: Scheduled order detail.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/TwapOrderEnvelope" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
    delete:
      operationId: cancelTwapOrder
      summary: Stop future TWAP or VWAP slices
      tags: [Strategy Orders]
      parameters:
        - $ref: "#/components/parameters/TwapOrderId"
      responses:
        "200":
          description: Current scheduled-order state.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/TwapOrderEnvelope" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "409": { $ref: "#/components/responses/Conflict" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /v1/scaled-orders:
    get:
      operationId: listScaledOrders
      summary: List scaled-order parents
      tags: [Strategy Orders]
      parameters:
        - $ref: "#/components/parameters/AccountIdQuery"
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 100, default: 50 }
        - name: cursor
          in: query
          schema: { type: string, maxLength: 1024 }
      responses:
        "200":
          description: Cursor-paginated scaled orders.
          content:
            application/json:
              schema:
                type: object
                required: [data, has_more]
                properties:
                  data:
                    type: array
                    items: { $ref: "#/components/schemas/ScaledOrder" }
                  has_more: { type: boolean }
                  next_cursor: { type: string }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
    post:
      operationId: createScaledOrder
      summary: Create a scaled price ladder
      tags: [Strategy Orders]
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CreateScaledOrderRequest" }
      responses:
        "200":
          description: Idempotent replay.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ScaledOrderEnvelope" }
        "201":
          description: Scaled order created.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ScaledOrderEnvelope" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "409": { $ref: "#/components/responses/Conflict" }
        "422": { $ref: "#/components/responses/TradeRejected" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /v1/scaled-orders/{scaled_order_id}:
    get:
      operationId: getScaledOrder
      summary: Get a scaled order and its rungs
      tags: [Strategy Orders]
      parameters:
        - $ref: "#/components/parameters/ScaledOrderId"
      responses:
        "200":
          description: Scaled-order detail.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ScaledOrderEnvelope" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
    delete:
      operationId: cancelScaledOrder
      summary: Cancel a scaled order and its working rungs
      tags: [Strategy Orders]
      parameters:
        - $ref: "#/components/parameters/ScaledOrderId"
      responses:
        "200":
          description: Current scaled-order state and child cancellation counts.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ScaledOrderEnvelope" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "409": { $ref: "#/components/responses/Conflict" }
        "429": { $ref: "#/components/responses/RateLimited" }
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Funding Perpetuals API key
      description: API key beginning with `fp_live_`.
  headers:
    RequestId:
      description: Stable identifier for support and log correlation.
      schema: { type: string, format: uuid }
    RetryAfter:
      description: Seconds to wait before retrying.
      schema: { type: integer, minimum: 1 }
  parameters:
    AccountId:
      name: account_id
      in: path
      required: true
      schema: { type: string }
    AccountIdQuery:
      name: account_id
      in: query
      required: true
      description: Challenge account ID returned by `GET /v1/accounts`.
      schema: { type: string }
    MarketId:
      name: market_id
      in: path
      required: true
      description: URL-encoded market ID, such as `binance%7CBTCUSDT`.
      schema: { type: string }
    OrderId:
      name: order_id
      in: path
      required: true
      schema: { type: string }
    PositionId:
      name: position_id
      in: path
      required: true
      schema: { type: string }
    TwapOrderId:
      name: twap_order_id
      in: path
      required: true
      schema: { type: string }
    ScaledOrderId:
      name: scaled_order_id
      in: path
      required: true
      schema: { type: string }
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: Unique printable key for this intended order. Reuse it only for an identical retry.
      schema: { type: string, minLength: 1, maxLength: 128 }
  responses:
    BadRequest:
      description: Invalid request.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/ErrorEnvelope" }
    Unauthorized:
      description: Missing, invalid, or revoked API key.
      headers:
        WWW-Authenticate:
          schema: { type: string }
      content:
        application/json:
          schema: { $ref: "#/components/schemas/ErrorEnvelope" }
    NotFound:
      description: Resource not found or not owned by the authenticated user.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/ErrorEnvelope" }
    Conflict:
      description: The request conflicts with current state or an idempotency record.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/ErrorEnvelope" }
    TradeRejected:
      description: The trading engine rejected the requested operation.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/TradeRejectionEnvelope" }
    RateLimited:
      description: A per-key, per-IP, or deployment request budget is exhausted.
      headers:
        Retry-After: { $ref: "#/components/headers/RetryAfter" }
      content:
        application/json:
          schema: { $ref: "#/components/schemas/ErrorEnvelope" }
  schemas:
    Account:
      type: object
      required:
        [
          id,
          account_number,
          name,
          stage,
          status,
          starting_balance,
          balance,
          created_at,
        ]
      properties:
        id: { type: string }
        account_number: { type: string }
        name: { type: string }
        stage: { type: string, enum: [evaluation, funded] }
        status: { type: string, enum: [active, passed, failed, closed] }
        starting_balance: { type: number }
        balance: { type: number }
        created_at:
          { type: number, description: Unix timestamp in milliseconds. }
    Market:
      type: object
      required: [market_id, provider, symbol, coin, size_decimals, max_leverage]
      properties:
        market_id: { type: string, example: binance|BTCUSDT }
        provider:
          { type: string, enum: [binance, bybit, hyperliquid, lighter, kraken] }
        symbol: { type: string, example: BTC }
        coin: { type: string, example: BTCUSDT }
        size_decimals: { type: integer, minimum: 0 }
        max_leverage: { type: number, minimum: 1 }
        market_type: { type: string }
        category: { type: string }
        trading_enabled: { type: boolean }
        reduce_only: { type: boolean }
    Quote:
      type: object
      required: [status, market_id, provider, symbol, coin, bid, ask, mid, time]
      properties:
        status: { type: string, const: ok }
        market_id: { type: string }
        provider: { type: string }
        symbol: { type: string }
        coin: { type: string }
        bid: { type: number }
        ask: { type: number }
        mid: { type: number }
        time: { type: number, description: Venue timestamp in milliseconds. }
    Position:
      type: object
      required:
        [
          id,
          account_id,
          market_id,
          provider,
          symbol,
          coin,
          side,
          size,
          entry_price,
          leverage,
          margin_mode,
          isolated_margin_extra,
          status,
          opened_at,
        ]
      properties:
        id: { type: string }
        account_id: { type: string }
        market_id: { type: string }
        provider: { type: string }
        symbol: { type: string }
        coin: { type: string }
        side: { type: string, enum: [long, short] }
        size: { type: number }
        entry_price: { type: number }
        leverage: { type: number }
        margin_mode: { type: string, enum: [cross, isolated] }
        liquidation_price: { type: number }
        isolated_margin_extra: { type: number }
        status: { type: string, enum: [open, closed] }
        opened_at: { type: number }
        closed_at: { type: number }
        size_decimals: { type: integer, minimum: 0 }
        exit_price: { type: number }
        realized_pnl: { type: number }
        realized_margin_basis: { type: number }
        fees: { type: number }
        funding: { type: number }
    Order:
      type: object
      required:
        [
          id,
          account_id,
          market_id,
          provider,
          symbol,
          coin,
          type,
          mode,
          side,
          size,
          expected_price,
          leverage,
          margin_mode,
          reduce_only,
          status,
          requested_at,
        ]
      properties:
        id: { type: string }
        account_id: { type: string }
        market_id: { type: string }
        provider: { type: string }
        symbol: { type: string }
        coin: { type: string }
        type:
          type: string
          enum:
            [
              market,
              limit,
              stop_market,
              stop_limit,
              take_market,
              take_limit,
              take_profit,
              liquidation,
              close_out,
            ]
        mode:
          type: string
          enum:
            [
              market,
              limit,
              chase,
              stop_market,
              stop_limit,
              take_market,
              take_limit,
              take_profit,
              conditional,
              liquidation,
              close_out,
            ]
        side: { type: string, enum: [buy, sell] }
        size: { type: number }
        initial_size: { type: number }
        filled_size: { type: number }
        expected_price: { type: number }
        fill_price: { type: number }
        trigger_price: { type: number }
        limit_price: { type: number }
        notional: { type: number }
        fee: { type: number }
        realized_pnl: { type: number }
        leverage: { type: number }
        margin_mode: { type: string, enum: [cross, isolated] }
        reduce_only: { type: boolean }
        time_in_force: { type: string, enum: [gtc, ioc, fok, gtd] }
        gtd_expires_at: { type: number }
        attached_take_profit_price: { type: number }
        attached_stop_loss_price: { type: number }
        chase_offset: { type: number }
        chase_cap_price: { type: number }
        trigger_market_id: { type: string }
        trigger_direction: { type: string, enum: [above, below] }
        target_position_id: { type: string }
        liquidity_role: { type: string, enum: [maker, taker] }
        partial_of_order_id: { type: string }
        partial_fill_root_order_id: { type: string }
        twap_order_id: { type: string }
        scaled_order_id: { type: string }
        status:
          {
            type: string,
            enum: [pending, filled, rejected, expired, resting, canceled],
          }
        reject_reason: { type: string }
        reject_rule_id: { type: string }
        requested_at: { type: number }
        settled_at: { type: number }
        canceled_at: { type: number }
    OrderPage:
      type: object
      required: [data, has_more]
      properties:
        data:
          type: array
          items: { $ref: "#/components/schemas/Order" }
        has_more: { type: boolean }
        next_cursor: { type: string }
    CreateOrderRequest:
      type: object
      additionalProperties: false
      required: [account_id, market_id, side, size, leverage, margin_mode]
      properties:
        type:
          type: string
          default: market
          enum:
            [
              market,
              limit,
              chase,
              stop_market,
              stop_limit,
              take_market,
              take_limit,
              take_profit,
              conditional,
            ]
        account_id: { type: string }
        market_id: { type: string, example: binance|BTCUSDT }
        side: { type: string, enum: [buy, sell] }
        size: { type: number, exclusiveMinimum: 0 }
        expected_price:
          type: number
          exclusiveMinimum: 0
          description: Midpoint observed by the client. Server execution never trusts this value for pricing.
        leverage: { type: number, exclusiveMinimum: 0 }
        margin_mode: { type: string, enum: [cross, isolated] }
        slippage_tolerance_bps: { type: number, exclusiveMinimum: 0 }
        reduce_only: { type: boolean, default: false }
        take_profit_price: { type: number, exclusiveMinimum: 0 }
        stop_loss_price: { type: number, exclusiveMinimum: 0 }
        trigger_price: { type: number, exclusiveMinimum: 0 }
        limit_price: { type: number, exclusiveMinimum: 0 }
        time_in_force: { type: string, enum: [ioc, fok, gtd] }
        gtd_expires_at: { type: number }
        chase_offset: { type: number, minimum: 0 }
        chase_cap_price: { type: number, exclusiveMinimum: 0 }
        marketable_limit_acknowledged: { type: boolean }
        trigger_market_id: { type: string }
        trigger_direction: { type: string, enum: [above, below] }
        target_position_id: { type: string }
    OrderEnvelope:
      type: object
      required: [data]
      properties:
        data: { $ref: "#/components/schemas/Order" }
        queued: { type: boolean }
    ClosePositionRequest:
      type: object
      additionalProperties: false
      properties:
        type:
          { type: string, enum: [market, limit, conditional], default: market }
        size:
          type: number
          exclusiveMinimum: 0
          description: Omit to close the complete current position.
        expected_price: { type: number, exclusiveMinimum: 0 }
        slippage_tolerance_bps: { type: number, exclusiveMinimum: 0 }
        limit_price: { type: number, exclusiveMinimum: 0 }
        trigger_price: { type: number, exclusiveMinimum: 0 }
        trigger_market_id: { type: string }
        trigger_direction: { type: string, enum: [above, below] }
        marketable_limit_acknowledged: { type: boolean }
    ExitOrderBatchRequest:
      type: object
      additionalProperties: false
      required: [expected_position_size, expected_orders, operations]
      properties:
        expected_position_size: { type: number, exclusiveMinimum: 0 }
        expected_orders:
          type: array
          items:
            type: object
            additionalProperties: false
            required: [order_id, price, size]
            properties:
              order_id: { type: string }
              execution_type: { type: string, enum: [market, limit] }
              price: { type: number, exclusiveMinimum: 0 }
              size: { type: number, exclusiveMinimum: 0 }
        operations:
          type: array
          items:
            oneOf:
              - type: object
                additionalProperties: false
                required: [kind, order_id]
                properties:
                  kind: { type: string, const: cancel }
                  order_id: { type: string }
              - type: object
                additionalProperties: false
                required: [kind, order_id, price]
                properties:
                  kind: { type: string, const: modify }
                  order_id: { type: string }
                  price: { type: number, exclusiveMinimum: 0 }
                  size: { type: number, exclusiveMinimum: 0 }
              - type: object
                additionalProperties: false
                required: [kind, group, price, size]
                properties:
                  kind: { type: string, const: place }
                  group: { type: string, enum: [tp, sl] }
                  execution_type: { type: string, enum: [market, limit] }
                  price: { type: number, exclusiveMinimum: 0 }
                  size: { type: number, exclusiveMinimum: 0 }
                  oco_pair_with_operation_index: { type: integer, minimum: 0 }
    CreateTwapOrderRequest:
      type: object
      additionalProperties: false
      required:
        [
          account_id,
          market_id,
          side,
          size,
          expected_price,
          leverage,
          margin_mode,
          duration_minutes,
        ]
      properties:
        account_id: { type: string }
        market_id: { type: string }
        strategy: { type: string, enum: [twap, vwap], default: twap }
        side: { type: string, enum: [buy, sell] }
        size: { type: number, exclusiveMinimum: 0 }
        expected_price: { type: number, exclusiveMinimum: 0 }
        leverage: { type: number, exclusiveMinimum: 0 }
        margin_mode: { type: string, enum: [cross, isolated] }
        duration_minutes: { type: integer, minimum: 5, maximum: 1440 }
        slice_interval_seconds:
          type: integer
          minimum: 30
          maximum: 43200
          description: At most half the selected duration, with a global 12-hour ceiling.
        randomize: { type: boolean, default: false }
        reduce_only: { type: boolean, default: false }
        trigger_price: { type: number, exclusiveMinimum: 0 }
        max_price: { type: number, exclusiveMinimum: 0 }
        min_price: { type: number, exclusiveMinimum: 0 }
    TwapOrder:
      type: object
      required:
        [
          id,
          account_id,
          market_id,
          strategy,
          side,
          total_size,
          filled_size,
          leverage,
          margin_mode,
          reduce_only,
          duration_minutes,
          slice_interval_seconds,
          status,
          started_at,
          end_at,
        ]
      properties:
        id: { type: string }
        account_id: { type: string }
        market_id: { type: string }
        provider: { type: string }
        symbol: { type: string }
        coin: { type: string }
        strategy: { type: string, enum: [twap, vwap] }
        side: { type: string, enum: [buy, sell] }
        total_size: { type: number }
        filled_size: { type: number }
        size_decimals: { type: integer }
        average_price: { type: number }
        leverage: { type: number }
        margin_mode: { type: string, enum: [cross, isolated] }
        reduce_only: { type: boolean }
        duration_minutes: { type: integer }
        slice_interval_seconds: { type: integer }
        slice_count: { type: integer }
        slices_attempted: { type: integer }
        randomize: { type: boolean }
        trigger_price: { type: number }
        max_price: { type: number }
        min_price: { type: number }
        status: { type: string }
        current_child_order_id: { type: string }
        activated_at: { type: number }
        started_at: { type: number }
        end_at: { type: number }
        next_run_at: { type: number }
        canceled_at: { type: number }
        completed_at: { type: number }
        completion_reason: { type: string }
    TwapOrderEnvelope:
      type: object
      required: [data]
      properties:
        data: { $ref: "#/components/schemas/TwapOrder" }
    CreateScaledOrderRequest:
      type: object
      additionalProperties: false
      required:
        [
          account_id,
          market_id,
          side,
          size,
          start_price,
          end_price,
          rung_count,
          distribution,
          leverage,
          margin_mode,
        ]
      properties:
        account_id: { type: string }
        market_id: { type: string }
        side: { type: string, enum: [buy, sell] }
        size: { type: number, exclusiveMinimum: 0 }
        start_price: { type: number, exclusiveMinimum: 0 }
        end_price: { type: number, exclusiveMinimum: 0 }
        rung_count: { type: integer, minimum: 2, maximum: 10 }
        distribution: { type: string, enum: [flat, ascending, descending] }
        leverage: { type: number, exclusiveMinimum: 0 }
        margin_mode: { type: string, enum: [cross, isolated] }
        reduce_only: { type: boolean, default: false }
        marketable_limit_acknowledged: { type: boolean }
    ScaledOrder:
      type: object
      required:
        [
          id,
          account_id,
          market_id,
          side,
          total_size,
          rung_count,
          rungs,
          status,
          started_at,
        ]
      properties:
        id: { type: string }
        account_id: { type: string }
        market_id: { type: string }
        provider: { type: string }
        symbol: { type: string }
        coin: { type: string }
        side: { type: string, enum: [buy, sell] }
        total_size: { type: number }
        size_decimals: { type: integer }
        leverage: { type: number }
        margin_mode: { type: string, enum: [cross, isolated] }
        reduce_only: { type: boolean }
        distribution: { type: string, enum: [flat, ascending, descending] }
        start_price: { type: number }
        end_price: { type: number }
        rung_count: { type: integer }
        placed_count: { type: integer }
        rejected_count: { type: integer }
        rungs:
          type: array
          items:
            type: object
            required: [price, size]
            properties:
              price: { type: number }
              size: { type: number }
              liquidity_role: { type: string, enum: [maker, taker] }
              order_id: { type: string }
              reject_reason: { type: string }
        status: { type: string }
        started_at: { type: number }
        completed_at: { type: number }
        canceled_at: { type: number }
    ScaledOrderEnvelope:
      type: object
      required: [data]
      properties:
        data: { $ref: "#/components/schemas/ScaledOrder" }
    Error:
      type: object
      required: [code, message, request_id]
      properties:
        code: { type: string }
        message: { type: string }
        request_id: { type: string, format: uuid }
    ErrorEnvelope:
      type: object
      required: [error]
      properties:
        error: { $ref: "#/components/schemas/Error" }
    TradeRejection:
      allOf:
        - $ref: "#/components/schemas/Error"
        - type: object
          properties:
            order_id: { type: string }
            rule_id: { type: string }
            details: { type: object, additionalProperties: true }
    TradeRejectionEnvelope:
      type: object
      required: [error]
      properties:
        error: { $ref: "#/components/schemas/TradeRejection" }
