CCXT.Exchange (ccxt_client v0.6.1)

Copy Markdown View Source

Exchange configuration struct and constructor.

Holds everything needed to make API calls for a specific exchange instance: resolved base URLs, rate limits, credentials, capabilities, and lean spec data.

This is a pure data struct — no process. Rate limiting, HTTP execution, and signing are handled by other modules that receive %Exchange{} as input.

Examples

# Public-only (no credentials)
{:ok, exchange} = CCXT.Exchange.new("bybit")
exchange.base_urls
#=> %{"public" => "https://api.bybit.com", ...}

# With credentials
{:ok, exchange} = CCXT.Exchange.new("bybit", api_key: "abc", secret: "xyz")
exchange.credentials
#=> %CCXT.Credentials{api_key: "abc", secret: "xyz", ...}

# Sandbox mode (uses testnet URLs)
{:ok, exchange} = CCXT.Exchange.new("bybit", sandbox: true)

Summary

Functions

Generates introspection functions and endpoint wrappers from a spec ID.

Macro entry point: use CCXT.Exchange, spec: "bybit" generates an exchange module.

Pre-computes a flat list of endpoint configs from the nested API tree.

Builds quoted endpoint wrapper functions from a list of endpoint configs.

Builds the quoted module body from prepared generate data.

Builds unified method mapping from spec data and pre-computed endpoint configs.

Classifies signing pattern and builds @external_resource AST for parent specs.

Checks if the exchange supports a given capability.

Creates an exchange configuration from an exchange ID and options.

Creates an exchange configuration, raising on error.

Prepares all compile-time data for the generator macro.

Returns the exchange's tier string, or nil if the spec omits it.

Types

error_body_check()

@type error_body_check() :: %{
  field: String.t() | nil,
  field2: String.t() | nil,
  roles: [error_body_role()],
  sentinel_values: [sentinel_value()]
}

error_body_role()

@type error_body_role() :: :error_code | :status_sentinel

sentinel_operator()

@type sentinel_operator() :: String.t()

sentinel_value()

@type sentinel_value() :: %{operator: sentinel_operator(), value: String.t()}

t()

@type t() :: %CCXT.Exchange{
  base_urls: map(),
  broad_error_patterns: %{required(String.t()) => CCXT.Error.error_type()},
  common_currencies: %{required(String.t()) => String.t()},
  credentials: CCXT.Credentials.t() | nil,
  error_body_checks: [error_body_check()],
  error_code_fields: [String.t()],
  error_codes: %{required(String.t()) => CCXT.Error.error_type()},
  has: %{required(String.t()) => boolean() | String.t()},
  hostname: String.t() | nil,
  http_exceptions: %{required(String.t()) => CCXT.Error.error_type()},
  id: String.t(),
  module: module() | nil,
  name: String.t(),
  options: map(),
  outbound_aliases: %{required(String.t()) => String.t()},
  rate_limit_ms: number(),
  request_defaults: %{required(String.t()) => %{required(String.t()) => term()}},
  required_credentials: %{required(String.t()) => boolean()},
  sandbox: boolean(),
  signing_config: map(),
  signing_pattern: CCXT.Signing.pattern() | nil,
  spec: map(),
  symbol_patterns: %{required(atom()) => CCXT.Symbol.pattern_config()},
  tier: String.t() | nil
}

Functions

__generate__(spec_id)

(macro)

Generates introspection functions and endpoint wrappers from a spec ID.

__using__(opts)

(macro)

Macro entry point: use CCXT.Exchange, spec: "bybit" generates an exchange module.

build_endpoint_configs(api_tree, url_prefixes \\ %{}, authenticated_sections \\ [])

@spec build_endpoint_configs(map(), map(), [String.t()]) :: [map()]

Pre-computes a flat list of endpoint configs from the nested API tree.

Called at compile time by __generate__/1. Recursively traverses the spec's API tree until it finds HTTP method keys (get, post, etc.), then extracts endpoint configs from the level below.

Handles three spec patterns:

  • Standard: %{visibility => %{method => %{path => weight}}}
  • Deep nesting: %{api_type => %{version => %{visibility => %{method => ...}}}}
  • Array endpoints: %{... => %{method => [list_of_paths]}}

All intermediate keys above the HTTP method become the :sections list.

Examples

CCXT.Exchange.build_endpoint_configs(%{
  "public" => %{"get" => %{"v5/market/tickers" => 5}},
  "private" => %{"post" => %{"v5/order/create" => 2.5}}
})
#=> [
#=>   %{name: :public_get_v5_market_tickers, method: :get,
#=>     path: "v5/market/tickers", sections: ["public"], weight: 5},
#=>   %{name: :private_post_v5_order_create, method: :post,
#=>     path: "v5/order/create", sections: ["private"], weight: 2.5}
#=> ]

build_endpoint_functions(endpoint_configs)

@spec build_endpoint_functions([map()]) :: [Macro.t()]

Builds quoted endpoint wrapper functions from a list of endpoint configs.

Called at compile time by __generate__/1. Each generated function embeds its endpoint config as a literal and delegates to CCXT.Dispatch.call/4.

Example

For a config %{name: :public_get_v5_market_tickers, ...}, generates:

def public_get_v5_market_tickers(exchange, params \\ %{}, opts \\ [])
def public_get_v5_market_tickers(%CCXT.Exchange{} = exchange, params, opts) do
  CCXT.Dispatch.call(exchange, %{...}, params, opts)
end

build_module_body(data, opts \\ [])

@spec build_module_body(
  map(),
  keyword()
) :: Macro.t()

Builds the quoted module body from prepared generate data.

Called by both __generate__/1 (macro path) and CCXT.Exchanges (Module.create path) to ensure a single source of truth.

Options

  • :moduledoc — optional @moduledoc string to inject. The macro path leaves this to the caller; CCXT.Exchanges provides one per exchange.

build_unified_method_mapping(spec, endpoint_configs)

@spec build_unified_method_mapping(map(), [map()]) :: %{required(atom()) => [map()]}

Builds unified method mapping from spec data and pre-computed endpoint configs.

classify_and_track_parent(spec, exchange_id)

@spec classify_and_track_parent(map(), String.t()) :: {atom(), map(), [Macro.t()]}

Classifies signing pattern and builds @external_resource AST for parent specs.

has?(exchange, capability)

@spec has?(t(), String.t()) :: boolean()

Checks if the exchange supports a given capability.

Returns true for capabilities marked true or "emulated" in the spec. Returns false for false, "__undefined", or missing capabilities.

Capability names use camelCase strings matching the CCXT spec (e.g., "fetchTicker", "createOrder").

Examples

CCXT.Exchange.has?(exchange, "fetchTicker")
#=> true

CCXT.Exchange.has?(exchange, "fetchFundingRateHistory")
#=> false

new(exchange_id, opts \\ [])

@spec new(
  String.t() | atom(),
  keyword()
) :: {:ok, t()} | {:error, term()}

Creates an exchange configuration from an exchange ID and options.

Loads the spec, resolves base URLs (with hostname interpolation and sandbox/testnet switching), and optionally builds credentials.

Options

  • :api_key - API key string (builds credentials automatically)
  • :secret - API secret string (builds credentials automatically)
  • :password - API password (OKX, KuCoin)
  • :uid - User ID
  • :credentials - Pre-built %CCXT.Credentials{} (overrides key/secret opts)
  • :sandbox - Use testnet URLs (default: false)
  • :hostname - Override the default hostname
  • :options - Exchange-specific options map

Examples

{:ok, exchange} = CCXT.Exchange.new("bybit")
{:ok, exchange} = CCXT.Exchange.new("okx", api_key: "k", secret: "s", password: "p")
{:error, :missing_secret} = CCXT.Exchange.new("bybit", api_key: "k")

new!(exchange_id, opts \\ [])

@spec new!(
  String.t() | atom(),
  keyword()
) :: t()

Creates an exchange configuration, raising on error.

Examples

exchange = CCXT.Exchange.new!("bybit")
exchange = CCXT.Exchange.new!("bybit", api_key: "abc", secret: "xyz")

prepare_generate_data(spec_id)

@spec prepare_generate_data(String.t()) :: map()

Prepares all compile-time data for the generator macro.

tier(exchange)

@spec tier(t()) :: String.t() | nil

Returns the exchange's tier string, or nil if the spec omits it.

Values mirror priv/priority_tiers.json from ccxt_extract: "tier1", "tier2", "dex", "tier3", "unclassified". Specs emitted before ccxt_extract schema 1.8.0 (2026-04-14) have no tier field and return nil.

Examples

CCXT.Exchange.tier(CCXT.Exchange.new!("bybit"))
#=> "tier1"