CCXT Client

Elixir client for 100+ cryptocurrency exchanges. This is a standalone package generated from ccxt_ex.

Status

This is an early release (v0.x). Here's what works and what needs testing:

The signing implementations are correct to the spec, but most exchanges still need real-world validation with actual API credentials. This is where you can help.

We'd love your help testing. If you use an exchange we haven't validated yet:

  1. Try public endpoints first (fetch_ticker, fetch_order_book)
  2. Then try authenticated endpoints with testnet/sandbox credentials
  3. Open an issue with what worked and what didn't

Every exchange you test helps the whole community.

Installation

def deps do
  [
    {:ccxt_client, "~> 0.3"}
  ]
end

Usage

# Get ticker from any exchange
{:ok, ticker} = CCXT.Binance.fetch_ticker("BTC/USDT")
{:ok, ticker} = CCXT.Bybit.fetch_ticker("BTC/USDT")
{:ok, ticker} = CCXT.OKX.fetch_ticker("BTC/USDT")

# Authenticated requests
credentials = CCXT.Credentials.new(api_key: "...", secret: "...")
{:ok, balance} = CCXT.Binance.fetch_balance(credentials)

WebSocket Streaming

Stream real-time market data over WebSocket connections.

Basic connection (Client)

spec = CCXT.Bybit.__ccxt_spec__()
{:ok, client} = CCXT.WS.Client.connect(spec, [:public, :spot])

{:ok, sub} = CCXT.Bybit.WS.watch_ticker_subscription("BTC/USDT")
:ok = CCXT.WS.Client.subscribe(client, sub)

receive do
  {:websocket_message, data} -> Jason.decode!(data)
end

Managed connections (Adapter)

For long-running or authenticated streams, use the adapter — a GenServer that handles reconnection, subscription restoration, and auth state automatically:

{:ok, adapter} = CCXT.Bybit.WS.Adapter.start_link(
  url_path: [:public, :spot],
  handler: fn msg -> IO.inspect(msg, label: "ws") end
)

{:ok, sub} = CCXT.Bybit.WS.watch_ticker_subscription("BTC/USDT")
:ok = CCXT.Bybit.WS.Adapter.subscribe(adapter, sub)

The adapter handles automatically:

The CCXT.WS.Client is stateless and does not reconnect or restore subscriptions — use it for short-lived connections where you manage lifecycle yourself.

For the full Client vs Adapter decision guide and detailed reconnection behavior, see llms.txt section 12 (WebSocket Architecture).

Configuration

Configure the client via application config (:ccxt_client).

config :ccxt_client,
  recv_window_ms: 10_000,
  request_timeout_ms: 60_000,
  rate_limit_cleanup_interval_ms: 120_000,
  rate_limit_max_age_ms: 120_000,
  retry_policy: :safe_transient,
  debug: false,
  broker_id: "MY_APP_BROKER"

config :ccxt_client, :circuit_breaker,
  enabled: true,
  max_failures: 5,
  window_ms: 10_000,
  reset_ms: 15_000

Top-level keys

Key Type Default Description
recv_window_mspos_integer5000 Request timestamp validity window (rejects stale requests).
request_timeout_mspos_integer30000 HTTP request timeout in milliseconds.
extraction_timeout_mspos_integer30000 Per-exchange extraction timeout used by mix tasks. Notes: Only used by mix tasks, not runtime requests.
rate_limit_cleanup_interval_mspos_integer60000 Interval for cleaning up old rate limit timestamps.
rate_limit_max_age_mspos_integer60000 Maximum age for rate limit request timestamps.
retry_policyretry_policy:safe_transient (test: false) Req retry policy for HTTP requests. Notes: In :test, default is false (no retries).
debugbooleanfalse Log exceptions with full stack traces. Notes: May log sensitive data; use only in development.
broker_idstringnil Optional broker identifier appended to requests.

Circuit breaker keys

Key Type Default Description
enabledbooleantrue Enable or disable the circuit breaker.
max_failurespos_integer5 Failures before circuit opens (0 disables).
window_mspos_integer10000 Time window for counting failures.
reset_mspos_integer15000 Time before circuit resets (closes).

Machine-readable config spec

[
  {
    "default": 5000,
    "type": "pos_integer",
    "path": [
      "recv_window_ms"
    ],
    "description": "Request timestamp validity window (rejects stale requests).",
    "key": "recv_window_ms",
    "examples": [
      "config :ccxt_client, recv_window_ms: 10_000"
    ]
  },
  {
    "default": 30000,
    "type": "pos_integer",
    "path": [
      "request_timeout_ms"
    ],
    "description": "HTTP request timeout in milliseconds.",
    "key": "request_timeout_ms",
    "examples": [
      "config :ccxt_client, request_timeout_ms: 60_000"
    ]
  },
  {
    "default": 30000,
    "type": "pos_integer",
    "path": [
      "extraction_timeout_ms"
    ],
    "description": "Per-exchange extraction timeout used by mix tasks.",
    "key": "extraction_timeout_ms",
    "examples": [
      "config :ccxt_client, extraction_timeout_ms: 60_000"
    ],
    "notes": [
      "Only used by mix tasks, not runtime requests."
    ]
  },
  {
    "default": 60000,
    "type": "pos_integer",
    "path": [
      "rate_limit_cleanup_interval_ms"
    ],
    "description": "Interval for cleaning up old rate limit timestamps.",
    "key": "rate_limit_cleanup_interval_ms",
    "examples": [
      "config :ccxt_client, rate_limit_cleanup_interval_ms: 120_000"
    ]
  },
  {
    "default": 60000,
    "type": "pos_integer",
    "path": [
      "rate_limit_max_age_ms"
    ],
    "description": "Maximum age for rate limit request timestamps.",
    "key": "rate_limit_max_age_ms",
    "examples": [
      "config :ccxt_client, rate_limit_max_age_ms: 120_000"
    ]
  },
  {
    "default": "safe_transient",
    "type": "retry_policy",
    "path": [
      "retry_policy"
    ],
    "description": "Req retry policy for HTTP requests.",
    "key": "retry_policy",
    "examples": [
      "config :ccxt_client, retry_policy: :safe_transient"
    ],
    "notes": [
      "In :test, default is false (no retries)."
    ],
    "default_test": false
  },
  {
    "default": false,
    "type": "boolean",
    "path": [
      "debug"
    ],
    "description": "Log exceptions with full stack traces.",
    "key": "debug",
    "examples": [
      "config :ccxt_client, debug: true"
    ],
    "notes": [
      "May log sensitive data; use only in development."
    ]
  },
  {
    "default": null,
    "type": "string",
    "path": [
      "broker_id"
    ],
    "description": "Optional broker identifier appended to requests.",
    "key": "broker_id",
    "examples": [
      "config :ccxt_client, broker_id: \"MY_APP_BROKER\""
    ]
  },
  {
    "default": true,
    "type": "boolean",
    "path": [
      "circuit_breaker",
      "enabled"
    ],
    "description": "Enable or disable the circuit breaker.",
    "key": "circuit_breaker",
    "examples": [
      "config :ccxt_client, :circuit_breaker, enabled: true"
    ]
  },
  {
    "default": 5,
    "type": "pos_integer",
    "path": [
      "circuit_breaker",
      "max_failures"
    ],
    "description": "Failures before circuit opens (0 disables).",
    "key": "circuit_breaker",
    "examples": [
      "config :ccxt_client, :circuit_breaker, max_failures: 5"
    ]
  },
  {
    "default": 10000,
    "type": "pos_integer",
    "path": [
      "circuit_breaker",
      "window_ms"
    ],
    "description": "Time window for counting failures.",
    "key": "circuit_breaker",
    "examples": [
      "config :ccxt_client, :circuit_breaker, window_ms: 10_000"
    ]
  },
  {
    "default": 15000,
    "type": "pos_integer",
    "path": [
      "circuit_breaker",
      "reset_ms"
    ],
    "description": "Time before circuit resets (closes).",
    "key": "circuit_breaker",
    "examples": [
      "config :ccxt_client, :circuit_breaker, reset_ms: 15_000"
    ]
  }
]

Selective Compilation

By default, all bundled exchanges are compiled. To compile only a subset:

config :ccxt_client, exchanges: [:binance, :bybit, :okx]

Pass a list of exchange atoms or strings. Unlisted exchanges become stub modules (no API functions generated), reducing compile time and binary size.

Scaffolding Exchange Modules

Generate exchange modules in your application's namespace:

mix ccxt.gen binance kraken       # Generate specific exchanges
mix ccxt.gen --list               # Show available specs with tier info
mix ccxt.gen --tier1              # Generate all Tier 1 exchanges
mix ccxt.gen --all                # All available exchanges
mix ccxt.gen --force              # Overwrite existing files
mix ccxt.gen --namespace Trading  # Custom module namespace

This creates modules like:

defmodule MyApp.Exchanges.Binance do
  use CCXT.Generator, spec: "binance"
end

Discovering Available Specs

List bundled exchange specs at runtime:

CCXT.Exchange.Discovery.available_specs()
# => ["binance", "bybit", "deribit", ...]

CCXT.Exchange.Discovery.available_spec?("binance")
# => true

Supported Exchanges

This package supports 100+ exchanges. See the lib/ccxt/exchanges/ directory for the full list.

Generated Package

This package was generated using:

mix ccxt.sync --all --output ../ccxt_client

To regenerate with updated exchange specs, run the above command from the ccxt_ex project directory.