# CCXT Elixir
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:
```elixir
def deps do
[
{:ccxt, "== 0.1.0-binance-pro-preview"}
]
end
```
Then fetch and compile from the consuming project:
```sh
mix deps.get
mix compile
```
When working from a local CCXT checkout before publishing, use a path
dependency instead:
```elixir
def deps do
[
{:ccxt, path: "../ccxt/elixir"}
]
end
```
When working inside the CCXT checkout itself:
```sh
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:
```elixir
{: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:
```sh
BINANCE_API_KEY=...
BINANCE_API_SECRET=...
BINANCE_ENV=prod
```
```elixir
{: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:
```elixir
{: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:
```elixir
{: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:
```elixir
{: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:
```elixir
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:
```elixir
url = Ccxt.Pro.Binance.get_ws_url("spot", "public", "prod") <> "/stream"
:ok = Ccxt.Pro.close_connection(url)
```
Or inspect all active Pro connections:
```elixir
Ccxt.Pro.connections()
```
## Instance Configuration
The generated Pro target supports a CCXT-style instance model through
`Ccxt.Pro.binance/1`:
```elixir
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:
```elixir
{: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:
```sh
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:
```elixir
:ok = Ccxt.Pro.attach_debug_logger()
```
Detach it:
```elixir
: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`:
```sh
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:
```sh
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`:
```sh
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:
```sh
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:
```elixir
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:
```elixir
{: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:
```sh
npm run testElixirConsumer
```
Run the packaged consumer smoke before a release:
```sh
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:
```sh
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:
```sh
npm run checkElixir
```
Build a local Hex package dry-run without publishing:
```sh
npm run buildElixirPackage
```
Run the release preview gate before handing off a package:
```sh
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:
```sh
npm run testElixirProLongSoak
```
For a short sanity run:
```sh
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.