asyncapi: 3.0.0
info:
  title: Blink Realtime API v1
  version: 1.0.0
  description: >
    Realtime market and account data. Clients must send a WebSocket or application frame
    at least once every two minutes.

servers:
  production:
    host: api-v1-ab.blink.trade
    pathname: /v1/ws
    protocol: wss
    description: Production WebSocket.
    x-keepalive-timeout-ms: 120000

channels:
  marketData:
    address: /v1/ws
    description: Market-data streams.
    messages:
      hello: { $ref: "#/components/messages/Hello" }
      subscribe: { $ref: "#/components/messages/Subscribe" }
      unsubscribe: { $ref: "#/components/messages/Unsubscribe" }
      unsubscribed: { $ref: "#/components/messages/Unsubscribed" }
      update: { $ref: "#/components/messages/Update" }
      error: { $ref: "#/components/messages/Error" }

operations:
  sendMarketDataControl:
    action: send
    channel: { $ref: "#/channels/marketData" }
    summary: Send stream commands.
  receiveMarketData:
    action: receive
    channel: { $ref: "#/channels/marketData" }
    summary: Receive stream frames.

components:
  messages:
    Hello:
      name: hello
      payload: { $ref: "#/components/schemas/HelloFrame" }
    Subscribe:
      name: subscribe
      payload: { $ref: "#/components/schemas/SubscribeCommand" }
    Unsubscribe:
      name: unsubscribe
      payload: { $ref: "#/components/schemas/UnsubscribeCommand" }
    Unsubscribed:
      name: unsubscribed
      payload: { $ref: "#/components/schemas/UnsubscribedFrame" }
    Update:
      name: update
      payload: { $ref: "#/components/schemas/UpdateFrame" }
    Error:
      name: error
      payload: { $ref: "#/components/schemas/ErrorFrame" }

  schemas:
    Channel:
      oneOf:
        - $ref: "#/components/schemas/BboChannel"
        - $ref: "#/components/schemas/OrderbookChannel"
        - $ref: "#/components/schemas/TradesChannel"
        - $ref: "#/components/schemas/CandleChannel"
        - $ref: "#/components/schemas/MarketsChannel"
        - $ref: "#/components/schemas/MarketStatsChannel"
        - $ref: "#/components/schemas/FundingRatesChannel"
        - $ref: "#/components/schemas/AccountsChannel"
        - $ref: "#/components/schemas/AccountChannel"

    BboChannel:
      type: string
      pattern: "^bbo/(spot|perp)/[0-9]+$"
      examples: [bbo/perp/1]

    OrderbookChannel:
      type: string
      pattern: "^orderbook/(spot|perp)/[0-9]+$"
      examples: [orderbook/perp/1]

    TradesChannel:
      type: string
      pattern: "^trades/(spot|perp)/[0-9]+$"
      examples: [trades/perp/1]

    CandleChannel:
      type: string
      pattern: "^(candles/(spot|perp)/[0-9]+|mark-candles/[0-9]+)/(1m|5m|15m|1h|4h|1d)$"
      examples: [candles/perp/1/1m, mark-candles/1/1m]

    MarketsChannel:
      type: string
      const: markets

    MarketStatsChannel:
      type: string
      pattern: "^market-stats/(spot|perp)(/[0-9]+)?$"
      examples: [market-stats/perp, market-stats/perp/1]

    FundingRatesChannel:
      type: string
      pattern: "^funding-rates/[0-9]+$"
      examples: [funding-rates/1]

    AccountsChannel:
      type: string
      pattern: "^accounts/[1-9A-HJ-NP-Za-km-z]{32,44}$"
      examples: [accounts/11111111111111111111111111111111]

    AccountChannel:
      type: string
      pattern: "^account/[1-9A-HJ-NP-Za-km-z]{32,44}/[0-9]+$"
      examples: [account/11111111111111111111111111111111/0]

    TimestampNs:
      type: string
      pattern: "^[0-9]+$"
      description: Unix epoch time in nanoseconds.

    Decimal:
      type: string
      pattern: '^-?[0-9]+(?:\.[0-9]+)?$'

    UnsignedInteger:
      type: string
      pattern: "^[0-9]+$"

    SignedInteger:
      type: string
      pattern: "^-?[0-9]+$"

    NullableSequence:
      description: Global transaction sequence encoded as a decimal string.
      oneOf:
        - $ref: "#/components/schemas/UnsignedInteger"
        - type: "null"

    PriceLevel:
      type: object
      additionalProperties: false
      required: [price_ticks, size_base_lots]
      properties:
        price_ticks: { $ref: "#/components/schemas/UnsignedInteger" }
        size_base_lots: { $ref: "#/components/schemas/UnsignedInteger" }

    NullableBboLevel:
      oneOf:
        - $ref: "#/components/schemas/PriceLevel"
        - type: "null"

    Bbo:
      type: object
      additionalProperties: false
      required: [bid, ask]
      properties:
        bid: { $ref: "#/components/schemas/NullableBboLevel" }
        ask: { $ref: "#/components/schemas/NullableBboLevel" }

    Orderbook:
      type: object
      additionalProperties: false
      required: [bids, asks]
      properties:
        bids:
          type: array
          items: { $ref: "#/components/schemas/PriceLevel" }
        asks:
          type: array
          items: { $ref: "#/components/schemas/PriceLevel" }

    Trade:
      type: object
      additionalProperties: false
      required: [timestamp_ns, price_ticks, size_base_lots, taker_is_bid]
      properties:
        timestamp_ns: { $ref: "#/components/schemas/TimestampNs" }
        price_ticks: { $ref: "#/components/schemas/UnsignedInteger" }
        size_base_lots: { $ref: "#/components/schemas/UnsignedInteger" }
        taker_is_bid: { type: boolean }

    LastCandle:
      type: object
      additionalProperties: false
      required:
        [
          open_time_ms,
          open,
          high,
          low,
          close,
          volume_base,
          volume_quote,
          trades,
        ]
      properties:
        open_time_ms: { type: integer, format: int64 }
        open: { $ref: "#/components/schemas/Decimal" }
        high: { $ref: "#/components/schemas/Decimal" }
        low: { $ref: "#/components/schemas/Decimal" }
        close: { $ref: "#/components/schemas/Decimal" }
        volume_base: { $ref: "#/components/schemas/Decimal" }
        volume_quote: { $ref: "#/components/schemas/Decimal" }
        trades: { type: integer, minimum: 0 }

    MarkCandle:
      type: object
      additionalProperties: false
      required: [open_time_ms, open, high, low, close, samples]
      properties:
        open_time_ms: { type: integer, format: int64 }
        open: { $ref: "#/components/schemas/Decimal" }
        high: { $ref: "#/components/schemas/Decimal" }
        low: { $ref: "#/components/schemas/Decimal" }
        close: { $ref: "#/components/schemas/Decimal" }
        samples: { type: integer, minimum: 0 }

    Candle:
      oneOf:
        - $ref: "#/components/schemas/LastCandle"
        - $ref: "#/components/schemas/MarkCandle"

    Market:
      $ref: "./openapi.json#/components/schemas/Market"

    MarginTier:
      $ref: "./openapi.json#/components/schemas/MarginTier"

    MarketStats:
      $ref: "./openapi.json#/components/schemas/MarketStats"

    FundingRate:
      $ref: "./openapi.json#/components/schemas/FundingRate"

    Subaccount:
      $ref: "./openapi.json#/components/schemas/Subaccount"

    Balance:
      $ref: "./openapi.json#/components/schemas/Balance"

    Position:
      $ref: "./openapi.json#/components/schemas/Position"

    Order:
      $ref: "./openapi.json#/components/schemas/Order"

    Fill:
      $ref: "./openapi.json#/components/schemas/Fill"

    FundingPayment:
      $ref: "./openapi.json#/components/schemas/FundingPayment"

    Leverage:
      type: object
      additionalProperties: false
      required: [market_id, leverage]
      properties:
        market_id: { type: integer, format: int32, minimum: 0 }
        leverage: { type: integer, format: int32, minimum: 1 }

    AccountCatalogEntry:
      type: object
      additionalProperties: false
      required: [subaccount_id, status, created_at_ns]
      properties:
        subaccount_id: { type: integer, format: int32, minimum: 0 }
        status: { type: string, enum: [active, restricted, closed] }
        created_at_ns: { $ref: "#/components/schemas/TimestampNs" }

    AccountDelegation:
      type: object
      additionalProperties: false
      required: [subaccount_id, delegate, permissions]
      properties:
        subaccount_id: { type: integer, format: int32, minimum: 0 }
        delegate: { type: string }
        permissions:
          type: array
          items: { type: string }
        expires_at_ns: { $ref: "#/components/schemas/TimestampNs" }
        name: { type: string }

    HelloFrame:
      type: object
      additionalProperties: false
      required: [type, protocol_version, keepalive_timeout_ms]
      properties:
        type: { type: string, const: hello }
        protocol_version: { type: integer, const: 1 }
        keepalive_timeout_ms:
          type: integer
          const: 120000
          description: Maximum time between frames sent by the client.

    SubscribeCommand:
      type: object
      additionalProperties: false
      required: [type, channel]
      properties:
        type: { type: string, const: subscribe }
        channel: { $ref: "#/components/schemas/Channel" }

    UnsubscribeCommand:
      type: object
      additionalProperties: false
      required: [type, channel]
      properties:
        type: { type: string, const: unsubscribe }
        channel: { $ref: "#/components/schemas/Channel" }

    UnsubscribedFrame:
      type: object
      additionalProperties: false
      required: [type, channel, timestamp_ns]
      properties:
        type: { type: string, const: unsubscribed }
        channel: { $ref: "#/components/schemas/Channel" }
        timestamp_ns: { $ref: "#/components/schemas/TimestampNs" }

    UpdateFrame:
      oneOf:
        - $ref: "#/components/schemas/BboUpdateFrame"
        - $ref: "#/components/schemas/OrderbookUpdateFrame"
        - $ref: "#/components/schemas/TradeUpdateFrame"
        - $ref: "#/components/schemas/CandleUpdateFrame"
        - $ref: "#/components/schemas/MarketsUpdateFrame"
        - $ref: "#/components/schemas/MarketStatsUpdateFrame"
        - $ref: "#/components/schemas/FundingRatesUpdateFrame"
        - $ref: "#/components/schemas/AccountsUpdateFrame"
        - $ref: "#/components/schemas/AccountUpdateFrame"

    BboUpdateFrame:
      type: object
      additionalProperties: false
      required: [type, channel, timestamp_ns, bbo]
      properties:
        type: { type: string, const: update }
        channel: { $ref: "#/components/schemas/BboChannel" }
        timestamp_ns: { $ref: "#/components/schemas/TimestampNs" }
        bbo: { $ref: "#/components/schemas/Bbo" }

    OrderbookUpdateFrame:
      type: object
      additionalProperties: false
      required:
        [type, channel, timestamp_ns, previous_sequence, sequence, orderbook]
      properties:
        type: { type: string, const: update }
        channel: { $ref: "#/components/schemas/OrderbookChannel" }
        timestamp_ns: { $ref: "#/components/schemas/TimestampNs" }
        previous_sequence: { $ref: "#/components/schemas/NullableSequence" }
        sequence: { $ref: "#/components/schemas/NullableSequence" }
        orderbook: { $ref: "#/components/schemas/Orderbook" }

    TradeUpdateFrame:
      type: object
      additionalProperties: false
      required:
        [type, channel, timestamp_ns, previous_sequence, sequence, trades]
      properties:
        type: { type: string, const: update }
        channel: { $ref: "#/components/schemas/TradesChannel" }
        timestamp_ns: { $ref: "#/components/schemas/TimestampNs" }
        previous_sequence: { $ref: "#/components/schemas/NullableSequence" }
        sequence: { $ref: "#/components/schemas/NullableSequence" }
        trades:
          type: array
          items: { $ref: "#/components/schemas/Trade" }

    CandleUpdateFrame:
      type: object
      additionalProperties: false
      required: [type, channel, timestamp_ns, candles]
      properties:
        type: { type: string, const: update }
        channel: { $ref: "#/components/schemas/CandleChannel" }
        timestamp_ns: { $ref: "#/components/schemas/TimestampNs" }
        candles:
          type: array
          maxItems: 2
          items: { $ref: "#/components/schemas/Candle" }

    MarketsUpdateFrame:
      type: object
      additionalProperties: false
      required: [type, channel, timestamp_ns, previous_sequence, sequence, markets]
      properties:
        type: { type: string, const: update }
        channel: { $ref: "#/components/schemas/MarketsChannel" }
        timestamp_ns: { $ref: "#/components/schemas/TimestampNs" }
        previous_sequence: { $ref: "#/components/schemas/NullableSequence" }
        sequence: { $ref: "#/components/schemas/NullableSequence" }
        markets:
          type: array
          items: { $ref: "#/components/schemas/Market" }

    MarketStatsUpdateFrame:
      type: object
      additionalProperties: false
      required:
        [type, channel, timestamp_ns, previous_sequence, sequence, market_stats]
      properties:
        type: { type: string, const: update }
        channel: { $ref: "#/components/schemas/MarketStatsChannel" }
        timestamp_ns: { $ref: "#/components/schemas/TimestampNs" }
        previous_sequence: { $ref: "#/components/schemas/NullableSequence" }
        sequence: { $ref: "#/components/schemas/NullableSequence" }
        market_stats:
          type: array
          items: { $ref: "#/components/schemas/MarketStats" }

    FundingRatesUpdateFrame:
      type: object
      additionalProperties: false
      required:
        [type, channel, timestamp_ns, previous_sequence, sequence, funding_rates]
      properties:
        type: { type: string, const: update }
        channel: { $ref: "#/components/schemas/FundingRatesChannel" }
        timestamp_ns: { $ref: "#/components/schemas/TimestampNs" }
        previous_sequence: { $ref: "#/components/schemas/NullableSequence" }
        sequence: { $ref: "#/components/schemas/NullableSequence" }
        funding_rates:
          type: array
          maxItems: 200
          items: { $ref: "#/components/schemas/FundingRate" }

    AccountsUpdateFrame:
      type: object
      additionalProperties: false
      required:
        [type, channel, timestamp_ns, previous_sequence, sequence, subaccounts, delegations]
      properties:
        type: { type: string, const: update }
        channel: { $ref: "#/components/schemas/AccountsChannel" }
        timestamp_ns: { $ref: "#/components/schemas/TimestampNs" }
        previous_sequence: { $ref: "#/components/schemas/NullableSequence" }
        sequence: { $ref: "#/components/schemas/NullableSequence" }
        subaccounts:
          type: array
          items: { $ref: "#/components/schemas/AccountCatalogEntry" }
        delegations:
          type: array
          items: { $ref: "#/components/schemas/AccountDelegation" }

    AccountUpdateFrame:
      type: object
      additionalProperties: false
      required: [type, channel, timestamp_ns, previous_sequence, sequence]
      anyOf:
        - required: [account]
        - required: [balances]
        - required: [positions]
        - required: [leverages]
        - required: [orders]
        - required: [fills]
        - required: [funding_payments]
      properties:
        type: { type: string, const: update }
        channel: { $ref: "#/components/schemas/AccountChannel" }
        timestamp_ns: { $ref: "#/components/schemas/TimestampNs" }
        previous_sequence: { $ref: "#/components/schemas/NullableSequence" }
        sequence: { $ref: "#/components/schemas/NullableSequence" }
        account: { $ref: "#/components/schemas/Subaccount" }
        balances:
          type: array
          items: { $ref: "#/components/schemas/Balance" }
        positions:
          type: array
          items: { $ref: "#/components/schemas/Position" }
        leverages:
          type: array
          items: { $ref: "#/components/schemas/Leverage" }
        orders:
          type: array
          items: { $ref: "#/components/schemas/Order" }
        fills:
          type: array
          items: { $ref: "#/components/schemas/Fill" }
        funding_payments:
          type: array
          items: { $ref: "#/components/schemas/FundingPayment" }

    ErrorFrame:
      type: object
      additionalProperties: false
      required: [type, code, message, timestamp_ns]
      properties:
        type: { type: string, const: error }
        channel: { $ref: "#/components/schemas/Channel" }
        code:
          type: integer
          enum: [400, 404, 409, 503]
          description: >
            400 invalid command or channel; 404 unknown resource; 409
            duplicate subscription; 503 stream unavailable.
        message: { type: string }
        timestamp_ns: { $ref: "#/components/schemas/TimestampNs" }
