# `CCXT.WS.Config`
[🔗](https://github.com/ZenHive/ccxt_client/blob/main/lib/ccxt/ws/config.ex#L1)

Per-exchange WebSocket configuration.

T94 ships WS configuration for the priority-tier universe using the extended
shape that routes WS subscription patterns (`CCXT.WS.Subscription`) and WS
auth patterns (`CCXT.WS.Auth`). Replaces T92's 3-canary
`subscribe_builder: {mod, fun}` with `subscription_pattern` + `auth_pattern`
dispatch atoms.

**Count:** 13 exchanges — every entry here has a matching `CCXT.Registry`
module. T102 (2026-04-19) removed three orphan entries (`bingx`, `bitget`,
`mexc`) whose `Registry` lookups raised because no spec file exists for them
under the current tier1/tier2/dex scope. Add them back via
`mix ccxt_extract.update --exchange <id>` + a re-register in Registry before
reinstating their config here.

WS URLs currently live in this consumer-side map because JSON specs don't
yet surface a clean `urls.api.ws` field. A follow-up ccxt_extract task will
emit `urls.api.ws.{public,private,sandbox_public,sandbox_private}` from the
JS AST; when it lands, this module can be replaced by a one-line read from
`exchange.spec["urls"]["api"]["ws"]`. (Prior to schema 3.0.0 the raw AST was
available under `structure.ws_methods`; T117 dropped that payload to fit the
Hex size cap — the upstream WS-URL task supersedes that path.)

## Entry shape

    %{
      public_url: String.t() | nil,           # may contain `{hostname}` placeholder; nil = requires REST pre-call
      public_url_sandbox: String.t() | nil,
      private_url: String.t() | nil,
      private_url_sandbox: String.t() | nil,
      heartbeat: %{type: :ping | :deribit | :custom, interval: pos_integer() | nil},
      subscription_pattern: atom(),           # routed via CCXT.WS.Subscription
      subscription_config: map(),
      auth_pattern: atom() | nil,             # routed via CCXT.WS.Auth; nil = public-only wiring
      auth_config: map()
    }

## Known URL limitations (MVP — single URL per public/private slot)

Several exchanges expose per-product URLs (spot vs linear/inverse swap vs
option). The current shape captures one representative URL per
`{section, sandbox}` slot; product routing will land in a follow-up task
once the adapter layer needs it.

- `bybit`  — linear-only (`/v5/public/linear`). spot/inverse/option need routing.
- `binance` — spot-only. USDⓈ-M futures (`fstream.binance.com`), COIN-M
  (`dstream.binance.com`), and portfolio margin (`/pm/ws`) need routing.
- `htx` / `huobi` — spot-only (`api.huobi.pro/ws`). Contract/swap endpoints
  live on `api.hbdm.vn`.
- `aster` — spot-only. Swap on `fstream.asterdex.com`.
- `kucoin` — URLs are fully dynamic (REST `POST /api/v1/bullet-{public,private}`
  returns the WSS host + token). Recorded as `nil` until the adapter layer
  wires the pre-call.

# `for_exchange`

```elixir
@spec for_exchange(String.t()) :: map() | nil
```

Returns the WS config map for the given exchange id, or nil if unsupported.

# `supported?`

```elixir
@spec supported?(String.t()) :: boolean()
```

Returns true if this exchange has a WS config entry.

# `supported_exchanges`

```elixir
@spec supported_exchanges() :: [String.t()]
```

Returns all supported exchange ids.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
