Real Project Integration Guide

Copy Markdown View Source

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"}
  ]
end

Before publishing, or when testing a source checkout directly, use a local path dependency instead:

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

Supervision 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:

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_auth into stream_orders/4, stream_balance/1, or related private stream helpers.
  • Handle both task result messages ({ref, result}) and :DOWN monitor 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:

  1. Append raw websocket events and websocket API responses.
  2. Normalize to unified structures with Ccxt.StructurePersistence.
  3. Upsert current-state tables for mutable structures.
  4. Append history/event rows for mutable streams.

The schema manifest is:

priv/ccxt_structures/binance_pro_structures.json

Runtime 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.exs

Treat 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 releaseElixirPreviewCheck passes 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/0 after shutdown.