asyncapi: 3.0.0
info:
  title: True Markets Market Data WebSocket
  description: |
    ## Overview
    This API streams real-time market data over a single WebSocket connection. Clients subscribe to one or more topics and receive batched price candles, trending assets, surging assets, and weighted reference prices as they update.

    ## Topics
    Subscribe to specific topics, or to the `all` wildcard alias to receive every stream:
    - `price_candles` — OHLC price candles
    - `trending_assets` — trending assets
    - `surging_assets` — surging assets
    - `reference_prices` — weighted reference prices
    - `all` — wildcard alias; receive all of the above

    Per-instrument topics are delivered only to clients that subscribe to them by name, never to `all`, and the current value is sent as soon as the subscription lands:
    - `price.{SYMBOL}` — live price with 1h and 24h change (perps: `price.{SYMBOL}-PERP`)
    - `candles.{SYMBOL}.{interval}` — one candle series, interval one of `30s`, `1h`, `4h`, `24h`
    - `reference.{SYMBOL}` — weighted reference bid/mid/ask for the `{SYMBOL}|USD` pair
    - `orderbook.{venue}.{pair}` — full order-book depth

    Symbols are case-insensitive and keyed upper-case. A connection may hold a limited number of topics; topics over the limit are rejected with `topic_limit_exceeded`.

    `all` is a subscription convenience, not a stream of its own. Every pushed message carries the **concrete** topic it belongs to in `topics` (e.g. `["price_candles"]`), even when delivered to an `all` subscriber. Because `all` is a wildcard, unsubscribing an individual topic while subscribed to `all` has no effect — to drop a single topic, subscribe to explicit topics and unsubscribe them individually.

    ## Authentication
    All channels are public — no API key, signature, or other credentials are required.

    ## Numeric Precision
    Prices, market caps, and other numeric values are returned as strings to preserve full decimal precision and avoid floating-point rounding.

    ## Links
    - 📧 **Support**: [support@truemarkets.co](mailto:support@truemarkets.co)

    ## Change Log
    | Version | Date       | Notes                                                              |
    |---------|------------|--------------------------------------------------------------------|
    | v0.0.1  | 2026-05-07 | Initial release.                                                   |
    | v0.0.2  | 2026-06-26 | Topic-based subscriptions; stream messages carry the concrete topic. |
    | v0.0.3  | 2026-10-19 | Per-symbol `price`, `candles` and `reference` topics; per-connection topic limit. |
    | v0.0.4  | 2026-10-20 | Price candles carry `product_type` (`spot` or `perp`).              |
  contact:
    email: support@truemarkets.co
  version: "v0.0.4"
servers:
  production:
    $ref: "#/components/servers/production"
  uat:
    $ref: "#/components/servers/uat"
channels:
  v1:
    address: /v1/defi/market
    messages:
      subscription:
        $ref: "#/components/messages/subscription"
      subscriptionResponse:
        $ref: "#/components/messages/subscriptionResponse"
      priceCandles:
        $ref: "#/components/messages/priceCandles"
      trendingAssets:
        $ref: "#/components/messages/trendingAssets"
      surgingAssets:
        $ref: "#/components/messages/surgingAssets"
      referencePrices:
        $ref: "#/components/messages/referencePrices"
      orderbook:
        $ref: "#/components/messages/orderbook"
      price:
        $ref: "#/components/messages/price"
      candles:
        $ref: "#/components/messages/candles"
      reference:
        $ref: "#/components/messages/reference"
      error:
        $ref: "#/components/messages/error"
operations:
  subscribe:
    action: send
    description: "Message sent to server indicating which topics the client wants to subscribe to or unsubscribe from."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/subscription"
  subscriptionResponse:
    action: receive
    description: "Acknowledgement sent from server in response to a subscribe or unsubscribe request."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/subscriptionResponse"
  priceCandles:
    action: receive
    description: "Batch of OHLC price candles for tracked assets across the standard intervals."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/priceCandles"
  trendingAssets:
    action: receive
    description: "Batch of trending assets ranked by recent vs. historical trading volume."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/trendingAssets"
  surgingAssets:
    action: receive
    description: "Batch of assets with significant price movement over 1h, 4h, and 24h windows."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/surgingAssets"
  referencePrices:
    action: receive
    description: "Batch of weighted reference prices computed from multiple exchange BBOs."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/referencePrices"
  orderbook:
    action: receive
    description: "Full order-book depth snapshot for a subscribed orderbook.{venue}.{pair} topic."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/orderbook"
  price:
    action: receive
    description: "Live price for a subscribed price.{SYMBOL} topic."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/price"
  candles:
    action: receive
    description: "One candle for a subscribed candles.{SYMBOL}.{interval} topic."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/candles"
  reference:
    action: receive
    description: "Weighted reference price for a subscribed reference.{SYMBOL} topic."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/reference"
  error:
    action: receive
    description: "Server-side error message describing why a request was rejected."
    channel:
      $ref: "#/channels/v1"
    messages:
      - $ref: "#/channels/v1/messages/error"
components:
  servers:
    production:
      host: api.truemarkets.co
      protocol: wss
      description: Production
    uat:
      host: api.uat.truemarkets.co
      protocol: wss
      description: UAT (sandbox)
  schemas:
    Topic:
      type: string
      description: |
        Topic identifier. `all` is a wildcard alias for every stream (per-instrument
        topics excluded). Per-instrument topics are parameterized:
        `orderbook.{venue}.{pair}` (e.g. `orderbook.hyperliquid-perp.BTC-USD`),
        `price.{SYMBOL}` (e.g. `price.BTC`, `price.BTC-PERP`),
        `candles.{SYMBOL}.{interval}` (e.g. `candles.ETH.1h`) and
        `reference.{SYMBOL}` (e.g. `reference.BTC`). They are subscribable but not
        part of the fixed enum below and are never delivered to `all` subscribers.
      enum:
        - all
        - price_candles
        - trending_assets
        - surging_assets
        - reference_prices
      example: "price_candles"
    RejectedTopic:
      type: object
      description: "A requested topic that could not be parsed, with the reason."
      required:
        - topic
        - reason
      properties:
        topic:
          type: string
          description: "The rejected raw topic string as sent by the client."
          example: "orderbook.hyperliquid.HYPE-USD"
        reason:
          type: string
          description: "Why the topic was rejected."
          enum:
            - empty_topic
            - unknown_channel
            - unsupported_channel
            - invalid_topic_format
            - topic_limit_exceeded
            - unclassified
          example: "unsupported_channel"
    PriceCandle:
      type: object
      description: "OHLC price data for a single asset and interval."
      properties:
        symbol:
          type: string
          description: "Asset symbol."
          example: "SOL"
        address:
          type: string
          description: "Asset contract address."
          example: "So11111111111111111111111111111111111111112"
        chain:
          type: string
          description: "Blockchain the asset lives on."
          example: "solana"
        product_type:
          type: string
          enum: [spot, perp]
          description: "How the asset trades. A spot and a perp listing can share a symbol (e.g. `NVDA`); this tells them apart."
          example: "spot"
        interval:
          type: string
          description: "Candle interval (e.g. `1h`, `4h`, `24h`)."
          example: "1h"
        open:
          type: string
          description: "Opening price for the interval."
          example: "100.50"
        high:
          type: string
          description: "Highest price during the interval."
          example: "102.00"
        low:
          type: string
          description: "Lowest price during the interval."
          example: "99.00"
        close:
          type: string
          description: "Closing price for the interval."
          example: "101.25"
    TrendingAsset:
      type: object
      description: "Trending asset with recent vs. historical volume metrics."
      properties:
        symbol:
          type: string
          example: "SOL"
        address:
          type: string
          example: "So11111111111111111111111111111111111111112"
        chain:
          type: string
          example: "solana"
        market_cap:
          type: string
          description: "Market capitalization in USD."
          example: "50000000000"
        trending_ratio:
          type: string
          description: "Recent volume divided by historical volume."
          example: "1.5"
        recent_volume:
          type: string
          description: "Recent trading volume."
          example: "1000000"
        historical_volume:
          type: string
          description: "Historical trading volume baseline."
          example: "800000"
    SurgingAsset:
      type: object
      description: "Asset with significant price movement over the trailing 1h, 4h, and 24h windows."
      properties:
        symbol:
          type: string
          example: "SOL"
        address:
          type: string
          example: "So11111111111111111111111111111111111111112"
        chain:
          type: string
          example: "solana"
        price_change_pct_1h:
          type: string
          description: "Price change percentage over the last 1 hour."
          example: "5.25"
        price_change_pct_4h:
          type: string
          description: "Price change percentage over the last 4 hours."
          example: "10.50"
        price_change_pct_24h:
          type: string
          description: "Price change percentage over the last 24 hours."
          example: "15.75"
        price_1h_ago:
          type: string
          description: "Price 1 hour ago."
          example: "95.00"
        price_4h_ago:
          type: string
          description: "Price 4 hours ago."
          example: "90.00"
        price_24h_ago:
          type: string
          description: "Price 24 hours ago."
          example: "85.00"
        current_price:
          type: string
          description: "Current price."
          example: "100.00"
    SourceBBO:
      type: object
      description: "Best bid and offer from a single exchange contributing to a reference price."
      properties:
        source:
          type: string
          description: "Exchange the BBO was sourced from."
          example: "coinbase"
        bid:
          type: string
          example: "100.49"
        ask:
          type: string
          example: "100.51"
        mid_price:
          type: string
          example: "100.50"
        weight_pct:
          type: string
          description: "Percentage weight applied to this source when computing the reference price."
          example: "0.5"
        timestamp:
          type: string
          format: date-time
          description: "RFC 3339 timestamp when the BBO was observed."
          example: "2026-05-07T12:00:00Z"
    ReferencePrice:
      type: object
      description: "Weighted average reference price computed from multiple exchange BBOs. A configured spot symbol (e.g. a tokenized stock) with no exchange BBO is priced from its latest 30s price candle close instead, with bid, ask and mid equal and a single source."
      properties:
        symbol:
          type: string
          example: "SOL"
        mid_price:
          type: string
          description: "Weighted mid price."
          example: "100.50"
        bid:
          type: string
          description: "Weighted best bid."
          example: "100.49"
        ask:
          type: string
          description: "Weighted best ask."
          example: "100.51"
        sources:
          type: array
          description: "Per-exchange BBOs that contributed to the weighted price."
          items:
            $ref: "#/components/schemas/SourceBBO"
        timestamp:
          type: string
          format: date-time
          description: "RFC 3339 timestamp when the reference price was computed."
          example: "2026-05-07T12:00:00Z"
    OrderbookLevel:
      type: object
      description: "A single order-book price level. Values are decimal strings to preserve precision."
      properties:
        px:
          type: string
          description: "Price at this level."
          example: "67.40"
        sz:
          type: string
          description: "Total size resting at this level."
          example: "12.5"
        n:
          type: integer
          description: "Number of orders at this level."
          example: 3
    OrderbookUpdate:
      type: object
      description: "A full order-book depth snapshot for one pair; each update replaces the prior book."
      properties:
        venue:
          type: string
          description: "Order-book venue."
          example: "hyperliquid-spot"
        pair:
          type: string
          description: "Display pair."
          example: "HYPE-USDC"
        coin:
          type: string
          description: "Venue wire identifier for the pair."
          example: "@107"
        bids:
          type: array
          description: "Bid levels, best (highest price) first, truncated to the configured depth."
          items:
            $ref: "#/components/schemas/OrderbookLevel"
        asks:
          type: array
          description: "Ask levels, best (lowest price) first, truncated to the configured depth."
          items:
            $ref: "#/components/schemas/OrderbookLevel"
        event_timestamp:
          type: string
          format: date-time
          description: "Venue-reported book time."
          example: "2026-05-07T12:00:00Z"
        occurred_at:
          type: string
          format: date-time
          description: "Time the producer emitted the event."
          example: "2026-05-07T12:00:00Z"
    LivePrice:
      type: object
      description: "A symbol's displayed price and its change against the 1h and 24h candle opens."
      required:
        - symbol
        - asset_class
        - price
        - timestamp
      properties:
        symbol:
          type: string
          example: "BTC"
        asset_class:
          type: string
          enum: [spot, perp]
          example: "spot"
        price:
          type: string
          description: "Reference mid for spot symbols with a fresh reference price, else the 24h candle close."
          example: "67250.5"
        change_1h:
          type: string
          description: "Percent change from the 1h candle open, two decimals. Omitted without a 1h candle."
          example: "0.42"
        change_24h:
          type: string
          description: "Percent change from the 24h candle open, two decimals. Omitted without a 24h candle."
          example: "-1.37"
        timestamp:
          type: string
          format: date-time
          example: "2026-10-19T12:00:00Z"
  messages:
    subscription:
      summary: "Subscribe or unsubscribe from topics."
      payload:
        type: object
        required:
          - type
          - topics
        properties:
          type:
            type: string
            description: "Action the request is intending to take."
            enum:
              - subscribe
              - unsubscribe
            example: "subscribe"
          topics:
            type: array
            description: "Topics to subscribe to or unsubscribe from, e.g. `[\"price_candles\"]`, `[\"price_candles\",\"surging_assets\"]`, or `[\"all\"]`."
            items:
              $ref: "#/components/schemas/Topic"
    subscriptionResponse:
      summary: "Acknowledgement of a subscribe or unsubscribe request. Partial-apply: accepted topics are always applied; rejected topics set status to `error` without closing the connection."
      payload:
        type: object
        required:
          - type
          - status
          - timestamp
        properties:
          type:
            type: string
            enum:
              - subscribe
              - unsubscribe
            example: "subscribe"
          status:
            type: string
            description: "`success` when every requested topic was accepted; `error` when one or more were rejected (accepted topics are still applied)."
            enum:
              - success
              - error
            example: "success"
          timestamp:
            type: integer
            format: int64
            description: "Unix timestamp (seconds) when the response was generated."
            example: 1234567890
          message:
            type: string
            description: "Human-readable description of the result."
            example: "Successfully subscribed to topics"
          topics:
            type: array
            description: "Accepted (applied) topics by canonical key. Omitted when none were accepted."
            items:
              $ref: "#/components/schemas/Topic"
          rejected:
            type: array
            description: "Topics that could not be parsed, with reasons. Present only when status is `error`."
            items:
              $ref: "#/components/schemas/RejectedTopic"
    priceCandles:
      summary: "Batch of OHLC price candles for tracked assets."
      payload:
        type: object
        required:
          - type
          - timestamp
          - topics
          - data
        properties:
          type:
            type: string
            enum: [price_candles]
          timestamp:
            type: integer
            format: int64
            description: "Unix timestamp (seconds) when the message was generated."
            example: 1234567890
          topics:
            type: array
            description: "The concrete topic this message belongs to, e.g. `[\"price_candles\"]` — even for `all` subscribers."
            items:
              $ref: "#/components/schemas/Topic"
          data:
            type: array
            items:
              $ref: "#/components/schemas/PriceCandle"
    trendingAssets:
      summary: "Batch of trending assets."
      payload:
        type: object
        required:
          - type
          - timestamp
          - topics
          - data
        properties:
          type:
            type: string
            enum: [trending_assets]
          timestamp:
            type: integer
            format: int64
            example: 1234567890
          topics:
            type: array
            description: "The concrete topic this message belongs to, e.g. `[\"trending_assets\"]` — even for `all` subscribers."
            items:
              $ref: "#/components/schemas/Topic"
          data:
            type: array
            items:
              $ref: "#/components/schemas/TrendingAsset"
    surgingAssets:
      summary: "Batch of surging assets."
      payload:
        type: object
        required:
          - type
          - timestamp
          - topics
          - data
        properties:
          type:
            type: string
            enum: [surging_assets]
          timestamp:
            type: integer
            format: int64
            example: 1234567890
          topics:
            type: array
            description: "The concrete topic this message belongs to, e.g. `[\"surging_assets\"]` — even for `all` subscribers."
            items:
              $ref: "#/components/schemas/Topic"
          data:
            type: array
            items:
              $ref: "#/components/schemas/SurgingAsset"
    referencePrices:
      summary: "Batch of weighted reference prices."
      payload:
        type: object
        required:
          - type
          - timestamp
          - topics
          - data
        properties:
          type:
            type: string
            enum: [reference_prices]
          timestamp:
            type: integer
            format: int64
            example: 1234567890
          topics:
            type: array
            description: "The concrete topic this message belongs to, e.g. `[\"reference_prices\"]` — even for `all` subscribers."
            items:
              $ref: "#/components/schemas/Topic"
          data:
            type: array
            items:
              $ref: "#/components/schemas/ReferencePrice"
    orderbook:
      summary: "Full order-book depth snapshot for a subscribed pair."
      payload:
        type: object
        required:
          - type
          - timestamp
          - topics
          - data
        properties:
          type:
            type: string
            enum: [orderbook]
          timestamp:
            type: integer
            format: int64
            example: 1234567890
          topics:
            type: array
            description: "The concrete topic this message belongs to, e.g. `[\"orderbook.hyperliquid-spot.HYPE-USDC\"]`."
            items:
              type: string
          data:
            $ref: "#/components/schemas/OrderbookUpdate"
    price:
      summary: "Live price for one symbol."
      payload:
        type: object
        required: [type, timestamp, topics, data]
        properties:
          type:
            type: string
            enum: [price]
          timestamp:
            type: integer
            format: int64
            example: 1234567890
          topics:
            type: array
            description: "The concrete topic, e.g. `[\"price.BTC\"]`."
            items:
              type: string
          data:
            $ref: "#/components/schemas/LivePrice"
    candles:
      summary: "One candle for one symbol and interval."
      payload:
        type: object
        required: [type, timestamp, topics, data]
        properties:
          type:
            type: string
            enum: [candles]
          timestamp:
            type: integer
            format: int64
            example: 1234567890
          topics:
            type: array
            description: "The concrete topic, e.g. `[\"candles.ETH.1h\"]`."
            items:
              type: string
          data:
            $ref: "#/components/schemas/PriceCandle"
    reference:
      summary: "Weighted reference price for one symbol."
      payload:
        type: object
        required: [type, timestamp, topics, data]
        properties:
          type:
            type: string
            enum: [reference]
          timestamp:
            type: integer
            format: int64
            example: 1234567890
          topics:
            type: array
            description: "The concrete topic, e.g. `[\"reference.BTC\"]`."
            items:
              type: string
          data:
            $ref: "#/components/schemas/ReferencePrice"
    error:
      summary: "Server-side error for a malformed request (e.g. invalid JSON, empty topics, or an unexpected message type)."
      payload:
        type: object
        required:
          - type
          - status
          - timestamp
        properties:
          type:
            type: string
            enum: [error]
          status:
            type: string
            enum: [error]
            example: "error"
          timestamp:
            type: integer
            format: int64
            example: 1234567890
          message:
            type: string
            description: "Human-readable error description."
            example: "invalid client message format"
