Elixir client library for cryptocurrency exchanges, generated from CCXT specs via compile-time macros.
Status: 0.x — raw endpoint surface is stable; unified translation layer is evolving.
def deps do
[
{:ccxt_client, "~> 0.6"}
]
end# Public market data (no credentials)
{:ok, exchange} = CCXT.Exchange.new("bybit")
{:ok, %{status: 200, body: body}} = CCXT.fetch_ticker(exchange, "BTC/USDT")
# Authenticated private call
{:ok, exchange} =
CCXT.Exchange.new("bybit",
api_key: System.fetch_env!("BYBIT_API_KEY"),
secret: System.fetch_env!("BYBIT_API_SECRET")
)
{:ok, %{status: 200, body: balances}} = CCXT.fetch_balance(exchange)The full vendored catalog (~110 exchanges, from the ccxt-distill corpus) compiles by default — every exchange supports public market data, and REST signing is wired for the large majority. Consumers can trim the compiled set to shrink their build:
# config/config.exs (compile-time)
config :ccxt_client, exchanges: [:deribit, :bybit, :okx]config :ccxt_client, exchanges: :all (the default) compiles the whole catalog. CCXT.Registry.exchanges/0 returns whatever is currently compiled.
The venues below have first-class signing support and are exercised by the integration suite:
| Exchange | Role | Markets |
|---|---|---|
binance |
Perp-hedge venue (spot) | Spot |
binanceusdm |
Perp-hedge venue (USDⓈ-M) | Linear perpetuals |
bybit |
Options venue | Spot, linear/inverse perps, options |
deribit |
Options venue | Options, futures, spot |
derive |
Options venue (DEX) | On-chain options on Optimism |
hyperliquid |
Perp-hedge venue (DEX) | On-chain perpetuals |
okx |
Options venue | Spot, swaps, options |
CCXT.Registry.exchanges()
#=> ["aftermath", "alpaca", "apex", ...] # full catalog by defaultccxt_client exposes two API surfaces with different stability guarantees.
Per-exchange generated functions that pass through to the exchange API unchanged. Signing, rate limiting, circuit breakers, and transport are handled; the response body is returned as-is.
{:ok, exchange} = CCXT.Exchange.new("bybit", api_key: key, secret: secret)
{:ok, response} = CCXT.Bybit.public_get_v5_market_tickers(exchange, %{category: "spot"})Recommended for agents, automated trading, and any consumer that wants to interpret exchange responses directly.
Standardized cross-exchange methods. Today most methods still return the raw HTTP response map (%{status, body, headers}); normalized structs arrive with Phase 5 parsers (see ROADMAP.md — gated on upstream ccxt-distill normalization data).
{:ok, %{status: 200, body: body}} = CCXT.fetch_time(exchange)Zero-arg public methods (fetch_time, fetch_status, fetch_currencies, fetch_markets) are reliable across the supported set. Symbol-bearing methods (fetch_ticker, fetch_order_book, fetch_trades) have per-exchange gaps — some exchanges need market-category params that the unified layer doesn't yet inject. Prefer the raw surface for those until Phase 5 lands.
{:ok, ws} = CCXT.WS.connect(exchange, :public)
:ok = CCXT.WS.subscribe(ws, ["tickers.BTCUSDT"])
# Messages arrive at the calling process as {:websocket_message, decoded_map}
CCXT.WS.close(ws)Thin wrapper over zen_websocket driven by per-exchange subscription + auth patterns. Among the first-class venues, binance, bybit, okx, and deribit have WS config entries today; derive and hyperliquid are REST-first DEX venues. Callers pass pre-formatted exchange-native channel strings; spec-driven channel formatting is T97 (upstream-gated). See ROADMAP.md for the WebSocket track status.
Consumer-facing gotchas not obvious from the API signatures. Full context in CLAUDE.md and ROADMAP.md.
-
has?/2is advertising, not a dispatch guarantee.CCXT.has?(exchange, "fetchX")reflects what upstream CCXT declares the exchange supports in principle — it does not promise thatccxt_clienthas an endpoint mapping wired for that method today (matches CCXT JS semantics: endpoint map is the dispatch gate,hasis advertising). When in doubt, cross-check withCCXT.<Exchange>.__unified_endpoints__/0; for production workloads prefer the raw per-exchange surface. -
DEX signing is bundled for
deriveandhyperliquid; other:customexchanges need a consumer-supplied module.deriveandhyperliquidresolve toCCXT.Signing.DeriveandCCXT.Signing.Hyperliquidfrom the v4 signing recipe. Exchanges that classify as:customraiseArgumentErroron private endpoints unless you provide acustom_module:implementingCCXT.Signing.Behaviour; exchanges whose signing recipe is unresolved upstream return a clear{:error, {:unsupported_signing, _}}. Public endpoints work regardless. Example:{:ok, ex} = CCXT.Exchange.new("my_custom_exchange", api_key: "…", secret: "…", signing: %{pattern: :custom, custom_module: MyApp.CustomExchangeSigner} )
DEX signers expect wallet-style credentials: put the EVM private key in
credentials.secretforderive/hyperliquid. -
Testnet coverage varies by exchange. Many exchanges ship testnet URL data in their specs; many don't.
Exchange.new(id, sandbox: true)returns{:error, :no_testnet_data}when the spec has no testnet catalog. Use production URLs with small-size orders and strict risk limits when no testnet exists.
CCXT.describe() # Library overview
CCXT.describe(CCXT, :fetch_ticker) # Method signature + params + errors + return shape
CCXT.MCP.tools() # MCP tool definitions for agent autodiscovery
CCXT.Registry.exchanges() # List compiled exchange idsPer-exchange introspection (generated on every exchange module):
CCXT.Bybit.__spec__() # Raw spec map (describe output)
CCXT.Bybit.__endpoints__() # List of %{path, method, authenticated, weight, ...}
CCXT.Bybit.__signing__() # %{pattern: :hmac_sha256_headers, config: %{...}}
CCXT.Bybit.__unified_endpoints__() # Unified-method → endpoint-config mapping- ROADMAP.md — active work, priorities, upstream dependencies
- CHANGELOG.md — completed tasks and known limitations
- CLAUDE.md — architecture, design decisions, internal conventions
- CONTRIBUTING.md — how to land fixes, including exchange overrides
When an exchange behaves incorrectly (wrong URL, wrong field name, missing auth section, bad error sentinel), fix it in the spec via an upstream override rather than patching ccxt_client. Overrides live in the ccxt-distill repo under overrides/<exchange>.json and apply at any JSON Pointer. Every overridden path is tagged "override" in the spec's top-level _provenance map, so consumers can see exactly which fields diverge from raw extraction.
See CONTRIBUTING.md § Exchange Overrides and ccxt-distill/CONSUMING.md for the authoritative format and workflow.
mix deps.get
mix test.json --quiet
mix dialyzer.json --quiet
mix credo --strict --format jsonMIT — see LICENSE.