Elixir target for CCXT, generated from the upstream TypeScript sources.

Current package status: 0.1.0-binance-pro-preview. The mature slice is Binance-first: REST raw/unified generation exists for Binance, and the CCXT Pro websocket target covers Binance Pro high-level methods with generated or runtime-owned semantics.

This preview package is ready for controlled Binance REST and Binance Pro integration work. It is not yet a broad multi-exchange Elixir release.

What Is Included

  • Binance unified REST methods in the Ccxt.Exchanges.Binance module, generated from ts/src/binance.ts through the AST/IR Elixir target.
  • Binance raw REST endpoint metadata in the Ccxt.Raw.Binance module, generated from describe().api.
  • Runtime HTTP execution through the Ccxt.HttpExecutor module.
  • Binance Pro websocket methods in the Ccxt.Pro.Binance module, with generated methods plus runtime-owned websocket/cache/auth lifecycle semantics.
  • CCXT-style Pro instance configuration through Ccxt.Pro.binance/1.
  • Structure normalization and database planning helpers through Ccxt.StructureSchema, Ccxt.StructureNormalizer, Ccxt.StructurePersistence, and Ccxt.RawPayload.
  • Release, live testing, production integration, cache parity, lifecycle, and structure coverage docs under doc/.

Install

Use the published preview package from a consuming project:

def deps do
  [
    {:ccxt, "== 0.1.0-binance-pro-preview"}
  ]
end

Then fetch and compile from the consuming project:

mix deps.get
mix compile

When working from a local CCXT checkout before publishing, use a path dependency instead:

def deps do
  [
    {:ccxt, path: "../ccxt/elixir"}
  ]
end

When working inside the CCXT checkout itself:

cd ccxt/elixir
mix deps.get
mix compile

Binance REST Quick Start

Public REST calls can be made directly through the generated Binance REST module:

{:ok, timestamp} = Ccxt.Exchanges.Binance.fetch_time()

{:ok, ticker} =
  Ccxt.Exchanges.Binance.fetch_ticker("BTC/USDT",
    binance_env: "prod"
  )

IO.inspect({timestamp, ticker.symbol, ticker.last})

Private REST calls use the active Binance credential environment:

BINANCE_API_KEY=...
BINANCE_API_SECRET=...
BINANCE_ENV=prod
{:ok, balance} =
  Ccxt.Exchanges.Binance.fetch_balance(%{},
    # optional per-request routing override
    binance_env: "prod"
  )

BINANCE_ENV defaults to production. Supported aliases are:

  • production: prod, production, mainnet
  • testnet: test, testnet, sandbox
  • demo: demo

Per-call binance_env: "demo" or binance_env: "testnet" overrides the process environment for generated REST calls.

Raw REST Endpoints

Use the raw Binance endpoint module when you need direct Binance endpoint metadata instead of a CCXT unified parser:

{:ok, endpoint} = Ccxt.Raw.Binance.public_get_time()
{:ok, body} = Ccxt.HttpExecutor.fetch(endpoint, %{}, binance_env: "prod")

For signed raw endpoints, pass params to the generated raw endpoint and keep credentials in the process environment:

{:ok, endpoint} = Ccxt.Raw.Binance.private_get_account(recvWindow: 10_000)
{:ok, account} = Ccxt.HttpExecutor.fetch(endpoint, %{}, binance_env: "prod")

Raw endpoint helpers return %Ccxt.RawEndpoint{} metadata. Ccxt.HttpExecutor owns URL routing, signing, timestamp, recvWindow, and response decoding.

REST Risk Boundary

Read-only REST methods are suitable for ordinary live smoke with read-only keys. State-changing methods such as withdraw, transfer, borrow, repay, convert, gift-code, and production-only order paths require the risk policy in doc/binance-live-testing.md. Demo/testnet should be used where Binance supports the namespace; production-only mutations should be dry-run or manually confirmed first.

Binance Pro Quick Start

Public Binance Pro streams do not require API keys:

{:ok, ticker} =
  Ccxt.Pro.Binance.watch_ticker("BTC/USDT",
    binance_env: "prod",
    timeout: 30_000
  )

IO.inspect({ticker.symbol, ticker.last, ticker.datetime})

watch_* methods follow CCXT Pro semantics: each call waits for and returns the next matching message. The websocket subscription remains active on the shared connection after the call returns.

For repeated Elixir consumption, use stream_* wrappers:

Ccxt.Pro.Binance.stream_ticker("BTC/USDT",
  binance_env: "prod",
  timeout: 30_000,
  unwatch_on_halt: true
)
|> Enum.take(10)
|> Enum.each(fn
  {:ok, ticker} -> IO.inspect({ticker.symbol, ticker.last, ticker.datetime})
  {:error, reason} -> IO.inspect(reason, label: "ticker_error")
end)

Close a shared websocket connection explicitly when you want to release it:

url = Ccxt.Pro.Binance.get_ws_url("spot", "public", "prod") <> "/stream"
:ok = Ccxt.Pro.close_connection(url)

Or inspect all active Pro connections:

Ccxt.Pro.connections()

Instance Configuration

The generated Pro target supports a CCXT-style instance model through Ccxt.Pro.binance/1:

binance =
  Ccxt.Pro.binance(%{
    apiKey: System.fetch_env!("BINANCE_PROD_API_KEY"),
    secret: System.fetch_env!("BINANCE_PROD_API_SECRET"),
    binanceEnv: "prod",
    options: %{recvWindow: 5_000}
  })

{:ok, balance} = Ccxt.Pro.Binance.fetch_balance_ws(binance)

Instance credentials and options are merged into each call. Per-call options override instance options:

{:ok, ticker} =
  Ccxt.Pro.Binance.watch_ticker(binance, "BTC/USDT",
    timeout: 30_000,
    binance_env: "prod"
  )

Accepted config keys:

  • apiKey or api_key
  • secret or api_secret
  • binanceEnv or binance_env
  • options

Binance Pro Credentials

For private Pro methods, prefer explicit instance credentials. Environment credentials are also supported for tests and examples:

BINANCE_PROD_API_KEY=...
BINANCE_PROD_API_SECRET=...
BINANCE_TESTNET_API_KEY=...
BINANCE_TESTNET_API_SECRET=...
BINANCE_DEMO_API_KEY=...
BINANCE_DEMO_API_SECRET=...
BINANCE_PRO_ENV=prod

BINANCE_PRO_ENV selects the default Pro environment used by examples and live tests. Method calls can also pass binance_env: "prod", "testnet", "sandbox", or "demo".

Private Pro credential lookup is environment-specific. It does not silently fall back from production keys to demo keys or from demo keys to production keys.

Public Market Streams

Common public methods:

  • watch_ticker/2, watch_tickers/2
  • watch_trades/4, watch_trades_for_symbols/4
  • watch_order_book/3, watch_order_book_for_symbols/3
  • watch_ohlcv/5, watch_ohlcv_for_symbols/4
  • watch_bids_asks/2
  • watch_mark_price/2, watch_mark_prices/2
  • watch_liquidations/4, watch_liquidations_for_symbols/4

Elixir stream wrappers:

  • stream_ticker/2
  • stream_trades/4
  • stream_order_book/3
  • stream_ohlcv/5

Use unwatch_on_halt: true when a stream should send the matching Binance unsubscribe request as the enumerable halts. Use Ccxt.Pro.close_connection/1 when the whole websocket connection should be shut down.

Private Websocket API

Read-only signed websocket API methods include:

  • fetch_balance_ws/1
  • fetch_position_ws/2
  • fetch_positions_ws/2
  • fetch_order_ws/3
  • fetch_orders_ws/4
  • fetch_closed_orders_ws/4
  • fetch_open_orders_ws/4
  • fetch_my_trades_ws/4

Order mutation websocket API methods are available but require explicit risk control in live testing:

  • create_order_ws/6
  • edit_order_ws/7
  • cancel_order_ws/3
  • cancel_all_orders_ws/2

Production order tests are gated behind CCXT_PRO_ENABLE_PROD_ORDER_LIVE=true and use non-marketable orders with immediate cancel where possible. cancel_all_orders_ws/2 remains manual-confirmed only because it can cancel unrelated user orders on the same symbol.

Private Event Streams

Private user-data stream methods include:

  • watch_balance/1
  • watch_orders/4
  • watch_positions/4
  • watch_my_trades/4
  • watch_my_liquidations/4
  • watch_my_liquidations_for_symbols/4

Elixir stream wrappers:

  • stream_balance/1
  • stream_orders/4
  • stream_positions/4
  • stream_my_trades/4

These wrappers repeat the generated private watch_* calls. They do not imply a private unwatch_* API. Use Ccxt.Pro.close_connection/1 for explicit private connection shutdown.

Telemetry And Debugging

Attach a simple IEx logger:

:ok = Ccxt.Pro.attach_debug_logger()

Detach it:

:ok = Ccxt.Pro.detach_debug_logger()

The runtime emits telemetry under [:ccxt, :pro, ...] for connection lifecycle, subscription sends, request resolution/rejection, message receive, cache append, message-hash routing, and explicit close operations.

Examples

Run examples from ccxt/elixir:

mix run examples/pro_watch_ticker.exs
mix run examples/pro_unwatch_ticker.exs
mix run examples/pro_stream_ticker.exs
mix run examples/pro_ticker_worker.exs
mix run examples/pro_public_market_streams.exs
mix run examples/pro_connection_info.exs
mix run examples/pro_close_connection.exs
mix run examples/pro_debug_logger.exs
mix run examples/pro_private_readonly.exs
mix run examples/pro_safe_order_lifecycle.exs
mix run examples/pro_order_event_stream.exs
mix run examples/pro_order_event_worker.exs
mix run examples/pro_soak_smoke.exs
mix run examples/pro_structure_persistence.exs

Public examples do not require API keys. Private examples load ../../.env when it exists and require the matching Binance environment credentials.

pro_ticker_worker.exs is the production-style OTP example. It defines a GenServer that owns the latest ticker state and a supervised stream task that consumes Ccxt.Pro.Binance.stream_ticker/2. Run a short public session with:

CCXT_PRO_WORKER_SECONDS=10 mix run examples/pro_ticker_worker.exs

In a real application, put its Task.Supervisor and worker in your supervision tree and set print?: false; the worker can then expose latest/1 or snapshot/1 to the rest of the app.

pro_order_event_worker.exs is the matching private-stream OTP example. It authenticates a Binance user-data stream once, passes the resulting ws_auth into Ccxt.Pro.Binance.stream_orders/4, and consumes private order updates with newUpdates: true:

CCXT_PRO_ORDER_EVENT_WORKER_SECONDS=30 mix run examples/pro_order_event_worker.exs

It does not place orders by itself. Run it while another process creates, edits, or cancels orders if you want to see order events arrive.

Manual Binance Testing

The REST Elixir runtime reads Binance credentials from process environment variables:

BINANCE_API_KEY=...
BINANCE_API_SECRET=...
BINANCE_ENV=prod

BINANCE_ENV is optional and defaults to production. Supported aliases are prod, production, and mainnet for production; test, testnet, and sandbox for Binance testnet; and demo for Binance demo URLs.

The same routing can also be selected per request with the :binance_env option:

Ccxt.Exchanges.Binance.fetch_time([], binance_env: "demo")
Ccxt.Exchanges.Binance.fetch_balance([], binance_env: "testnet")

For private methods, use API keys created for the selected Binance environment. Production keys, testnet keys, and demo keys are not interchangeable.

Consumer Smoke

smoke/consumer_app is a minimal external Mix project that depends on this package exactly like a real application. By default it uses the repository path:

{:ccxt, path: "../.."}

It verifies that a consumer project can fetch dependencies, compile, start an application, instantiate Ccxt.Pro.binance/1, call public watch_ticker/2, consume public stream_ticker/2, and close the shared websocket connection.

Run the path dependency smoke from the CCXT repository root:

npm run testElixirConsumer

Run the packaged consumer smoke before a release:

npm run testElixirPackageConsumer

That gate builds the local Hex package, unpacks it into a temporary directory, copies smoke/consumer_app into a clean temporary consumer project, points the consumer dependency at the unpacked package with CCXT_CONSUMER_CCXT_PATH, and runs the same public Binance Pro watch/stream/close smoke against the package payload.

The smoke uses public Binance market data and does not require API keys. It is tagged as :live inside the consumer app so plain mix test in that app does not hit the network by accident.

Validation Gates

From the CCXT repository root:

npm run pre-transpile-elixir
npm run transpileElixir
npm run transpileElixirPro
npm run assertElixirIR
npm run assertElixirGenerated
npm run assertElixirPro
npm run assertElixirStructureSchemas
npm run checkElixir
npm run docsElixir
npm run buildElixirPackage
npm run testElixirConsumer
npm run testElixirPackageConsumer
npm run releaseElixirPreviewCheck
npm run testElixirProSoak
npm run testElixirProLongSoak

The normal gate is:

npm run checkElixir

Build a local Hex package dry-run without publishing:

npm run buildElixirPackage

Run the release preview gate before handing off a package:

npm run releaseElixirPreviewCheck

This runs checkElixir, regenerates docs, builds and unpacks the package in a temporary directory, and runs the packaged consumer smoke against public Binance market data.

Soak gates are opt-in. The long soak defaults to 900 seconds:

npm run testElixirProLongSoak

For a short sanity run:

CCXT_PRO_LONG_SOAK_SECONDS=5 CCXT_PRO_LONG_SOAK_MIN_UPDATES=1 npm run testElixirProLongSoak

API Reference And Release Notes

  • doc/binance-pro-api-reference.md documents the supported public surface and return shapes.
  • doc/real-project-integration.md shows the OTP supervision, credentials, telemetry, retry, and database integration shape for application projects.
  • doc/release-checklist.md records the final local, live, package, soak, and evidence gates to run before release.
  • doc/release-0.1.0-binance-pro-preview.md records the preview release boundary, validation evidence, and known limitations.
  • doc/ccxt-pro-elixir-target.md is the traceability and maturity record.
  • doc/ccxt-pro-cache-parity.md tracks CCXT Pro cache-class parity.
  • doc/ccxt-pro-lifecycle-coverage.md and doc/ccxt-pro-structure-coverage.md are generated coverage matrices.
  • doc/ccxt-structure-schema.md explains the raw payload, unified structure, Ecto template, and persistence-planning layer.
  • doc/binance-live-testing.md records REST/live-test environment coverage and production-only mutation policy.