This guide shows how to embed the Binance-first Elixir CCXT Pro target in an
OTP application. The examples under examples/ are runnable scripts; this
document is the application-facing shape.
Dependency
After publishing, use the preview package version:
defp deps do
[
{:ccxt, "== 0.1.0-binance-pro-preview"}
]
endBefore publishing, or when testing a source checkout directly, use a local path dependency instead:
defp deps do
[
{:ccxt, path: "../ccxt/elixir"}
]
endSupervision Tree
Keep websocket stream enumeration in a task, not inside a GenServer callback.
The worker owns state; the task blocks on stream_*:
children = [
Ccxt.Pro.Supervisor,
{Task.Supervisor, name: MyApp.BinanceStreams.TaskSupervisor},
{MyApp.BinanceTickerWorker,
name: MyApp.BinanceTickerWorker,
symbol: "BTC/USDT",
binance_env: "prod",
task_supervisor: MyApp.BinanceStreams.TaskSupervisor}
]
Supervisor.start_link(children, strategy: :one_for_one)The included examples/pro_ticker_worker.exs and
examples/pro_order_event_worker.exs show the complete public and private
worker patterns.
Instance Configuration
For code that wants an exchange-like instance shape, use Ccxt.Pro.binance/1:
exchange =
Ccxt.Pro.binance(%{
apiKey: System.fetch_env!("BINANCE_PROD_API_KEY"),
secret: System.fetch_env!("BINANCE_PROD_API_SECRET"),
options: %{defaultType: "spot"}
})Generated high-level functions also accept keyword options:
Ccxt.Pro.Binance.watch_ticker("BTC/USDT",
binance_env: "prod",
timeout: 30_000
)Use one environment per process invocation. Keep production, demo, and testnet keys distinct; do not rely on generic fallback keys to pick the active account.
Credentials
Recommended runtime environment names:
BINANCE_PROD_API_KEY=...
BINANCE_PROD_API_SECRET=...
BINANCE_PRO_ENV=prod
For scripts and tests in this repository, .env can hold multiple key
families:
BINANCE_DEMO_API_KEY=...
BINANCE_DEMO_API_SECRET=...
BINANCE_TESTNET_API_KEY=...
BINANCE_TESTNET_API_SECRET=...
BINANCE_PROD_API_KEY=...
BINANCE_PROD_API_SECRET=...
In a production app, load secrets through the application's normal secret manager and pass them into config or process environment before starting workers. Avoid logging credentials and raw signed URLs.
Telemetry
The runtime emits telemetry events under [:ccxt, :pro, ...]. Attach a handler
once during application startup:
:telemetry.attach_many(
"my-app-ccxt-pro-logger",
[
[:ccxt, :pro, :connection, :started],
[:ccxt, :pro, :connection, :closed],
[:ccxt, :pro, :message, :received],
[:ccxt, :pro, :message_hash, :resolved],
[:ccxt, :pro, :request, :resolved],
[:ccxt, :pro, :request, :rejected]
],
fn event, measurements, metadata, _config ->
Logger.debug("ccxt_pro event=#{inspect(event)} measurements=#{inspect(measurements)} metadata=#{inspect(metadata)}")
end,
nil
)Useful production metrics:
- open connection count from
Ccxt.Pro.connections/0 - first update latency
- maximum gap between updates
- reconnect count
- request rejection count
- waiter count from
Ccxt.Pro.connection_info/1 - cache sizes for high-volume streams
Worker Retry And Shutdown
For public streams:
- Start the stream in
handle_continue/2. - Consume the enumerable in a supervised task.
- Send parsed updates back to the GenServer.
- On stop, terminate the task and call
Ccxt.Pro.close_connection/1.
For private streams:
- Authenticate once with
Ccxt.Pro.Binance.authenticate/1. - Pass returned
ws_authintostream_orders/4,stream_balance/1, or related private stream helpers. - Handle both task result messages (
{ref, result}) and:DOWNmonitor messages. - Dismiss the matching monitor with
Process.demonitor(ref, [:flush])when the task result arrives first. - Close the private websocket connection before retrying.
Backoff should be owned by the worker. Start with a small retry such as 5 seconds and increase it for repeated auth or permission failures.
Database Schema Path
Do not design database tables directly from raw Binance payloads only. Store both raw payloads and CCXT unified structures:
- Append raw websocket events and websocket API responses.
- Normalize to unified structures with
Ccxt.StructurePersistence. - Upsert current-state tables for mutable structures.
- Append history/event rows for mutable streams.
The schema manifest is:
priv/ccxt_structures/binance_pro_structures.jsonRuntime access:
Ccxt.StructureSchema.manifest()
Ccxt.StructureSchema.structure!("order")
Ccxt.StructureSchema.fields!("ticker")Generated Ecto templates:
priv/ccxt_structures/generated/binance_pro_ecto_schemas.exs
priv/ccxt_structures/generated/binance_pro_migration.exsTreat generated templates as a starting point for the application repo. Review table names, indexes, retention, decimal precision, and partitioning before running migrations in production.
Operational Checks
Before promoting an application release:
npm run releaseElixirPreviewCheckpasses on the dependency checkout.- Public live smoke passes for the production region where the app runs.
- Private read-only live smoke passes with the exact key permissions used by the app.
- If order placement is enabled, a gated non-marketable production order smoke passes and confirms cleanup.
- Long soak metrics are within the app's websocket freshness requirements.
- Telemetry events are visible in the app's logs or metrics backend.
- Workers stop cleanly and leave no open
Ccxt.Pro.connections/0after shutdown.