# CCXT Pro Elixir Target
This document records the current Binance-first CCXT Pro Elixir generation path.
## Goal
Build an extensible Elixir target for CCXT Pro from `ts/src/pro/binance.ts`.
The target should move toward TypeScript AST to IR to Elixir code generation,
while websocket lifecycle, subscriptions, caches, and resolve/reject semantics
live in a reusable Elixir Pro runtime.
This document is Binance-first. The current goal is to make Binance Pro
production-mature before generalizing to another exchange. Binance-specific
logic should stay in metadata, URL selection, signing, and routing; method
coverage, websocket lifecycle, caches, parser output, and request/waiter
semantics should remain reusable runtime or IR concepts.
## Maturity Checklist
The Elixir Pro target is considered mature only when each item below has
current evidence from generated files, manifest assertions, tests, or live
smoke runs:
- Instance model: `Ccxt.Pro.binance/1` and `Ccxt.Pro.Binance.new/1` accept
CCXT-style `apiKey`, `secret`, `options`, and `binanceEnv`/`binance_env`;
exchange-first wrappers merge instance credentials/options with call options
so users do not need to pass full opts to every method.
- Source coverage: `pro_manifest.json` records all `107` methods from
`ts/src/pro/binance.ts`, with `unsupportedMethods: []`, source spans,
generated/runtime-owned status, `renderPlan`, and runtime contracts.
- IR coverage: public watch, private watch, unwatch, authenticate,
listenKey/listenToken, websocket API request, signing, parser, cache,
handler, and reconnect behavior must be represented by structured
`irSummary.nodes` or explicit runtime contracts. Complex branches must not be
patched into output with regex/string fallbacks.
- Runtime coverage: `runtimeSemanticCoverage` must include the required
features for OTP Registry/DynamicSupervisor ownership, websocket reconnect,
message-hash routing, subscription/unwatch lifecycle, request-id routing,
websocket API response routing, listenKey auth/keepalive, ArrayCache,
OrderBook, balance, positions, order/trade caches, public/private event
routing, parser output, and waiter error rejection. `Ccxt.ProLifecycleCoverage`
exposes this evidence as a runtime coverage matrix, and
`doc/ccxt-pro-cache-parity.md` tracks specialized cache-class parity, and
`doc/ccxt-pro-lifecycle-coverage.md` records the current human-readable view.
- Live-test policy: public streams run as ordinary live smoke; private
read-only websocket API calls use `BINANCE_PROD_API_KEY` /
`BINANCE_PROD_API_SECRET` or demo credentials where supported; prod-only
order mutation tests require `CCXT_PRO_ENABLE_PROD_ORDER_LIVE=true`, place a
non-marketable order, and cancel it immediately.
- Risk boundary: Binance Pro currently exposes order mutations only
(`createOrderWs`, `editOrderWs`, `cancelOrderWs`, `cancelAllOrdersWs`) as
state-changing methods. Withdraw, transfer, borrow, repay, convert, and gift
code APIs are REST-surface risks, not Pro manifest methods; they remain
dry-run/signature/manual-confirmation only.
- Stable gates: `npm run assertElixirPro`, `npm run checkElixir`, and
`mix compile --warnings-as-errors` must pass against the current worktree.
Live smoke remains env-gated and must report whether a branch executed,
skipped for missing credentials, or recorded an expected Binance permission
limitation.
## Completion Audit Snapshot
Current evidence for the Binance-only Pro target:
- Instance model: `Ccxt.Pro.binance/1`, `Ccxt.Pro.Binance.new/1`, and
exchange-first wrappers are generated. `pro_manifest.json` records `52`
required exchange-callable public methods and `52` wrapped methods, with
`getPrivateWsUrl` explicitly excluded as a websocket URL helper. ExUnit
asserts wrapper coverage and no-opts instance calls for signed helpers.
- Source coverage: `pro_manifest.json` records `107` Binance Pro TypeScript
methods, split into `66` generated methods and `41` runtime-owned methods,
with `unsupportedMethods: []`.
- Traceability: every manifest method records `source.path/startLine/endLine`,
`irSummary.nodes`, and generated/runtime-owned lowering status. Generated
methods have `renderPlan` entries; runtime-owned methods have runtime
contracts.
- IR/codegen discipline: `npm run assertElixirPro` fails on unsupported source
methods, missing render plans, missing runtime contracts, generated drift,
incomplete exchange wrappers, or missing runtime semantic features. The
broader `npm run checkElixir` gate also asserts the non-Pro selected Elixir
target has no fallback markers or regex/string-style source rewrites.
- Runtime semantics: `runtimeSemanticCoverage` records `18` required features
and current evidence entries for OTP registry/dynamic supervisor ownership,
websocket reconnect, message-hash routing, subscription/unwatch lifecycle,
request-id routing, websocket API response routing, listenKey auth/keepalive,
ArrayCache, OrderBook, balance, positions, order/trade caches,
public/private event routing, parser output, and waiter rejection.
`Ccxt.ProLifecycleCoverage.summary/0` reports `18/18` required features
covered with `125` evidence entries.
- Live/demo/prod policy: public streams and signed read-only websocket API calls
run through env-gated live smoke. Demo read-only websocket API calls run under
`demo_private_live`. Production order mutation is isolated behind
`CCXT_PRO_ENABLE_PROD_ORDER_LIVE=true`, uses a non-marketable spot limit
order, and immediately cancels it. Event-driven private watchers are covered
deterministically through fake-connection/cache/snapshot tests instead of
unstable live event waits.
- Method-level evidence: `methodCoverageMatrix` records one row for every
Binance Pro source method, including generated/runtime owner, unit evidence,
live evidence, risk class, and skip/manual reason. The matrix is part of the
generated manifest and is asserted by ExUnit.
- Risk inventory: Binance Pro mutation surface is limited to `createOrderWs`,
`editOrderWs`, `cancelOrderWs`, and `cancelAllOrdersWs`; manifest tests assert
that withdraw, transfer, borrow, repay, convert, and gift-code methods are not
Pro methods. `createOrderWs`, `editOrderWs`, `cancelOrderWs`, `fetchOrderWs`,
and `watchOrders` are covered by gated non-marketable production live tests.
`cancelAllOrdersWs` remains manual-confirmed only because cancel-all can
cancel unrelated user orders on the same symbol.
- Manual-only boundary: `watchMyTrades` requires a real fill event and therefore
remains `manual-fill-only`; the non-marketable production order policy
intentionally cannot prove fills. Private liquidation watchers are
`event-driven-private-unit-only` because waiting for a real liquidation is not
deterministic or safe as an always-on live gate.
- Binance-only scope: the original broader target mentioned a second Pro
exchange as a generalization proof, but the active scope is explicitly
Binance-only. No second-exchange work is required for the current goal.
Completion boundary:
- The current Binance-first goal is complete when the generated manifest records
`107/107` source methods, `unsupportedMethods: []`, a synchronized
`methodCoverageMatrix`, passing unit/generation gates, ordinary live smoke,
and gated non-marketable production order smoke.
- Manual-only rows are acceptable only when the matrix records their risk and
skip reason. At this snapshot those rows are exactly `watchMyTrades`
(`manual-fill-only`) and `cancelAllOrdersWs`
(`manual-confirmed-cancel-all-only`).
- Re-run the live gates whenever credentials, Binance permissions, network
restrictions, signing runtime, or websocket runtime behavior changes.
## Current Architecture
The runtime is OTP-based:
- `Ccxt.Application` starts the application supervision tree.
- `Ccxt.Pro.Supervisor` starts `Ccxt.Pro.Registry` and `Ccxt.Pro.ConnectionSupervisor`.
- `Ccxt.Pro.connection/1` returns one dynamically supervised connection process per websocket URL.
- `Ccxt.Pro.connections/0`, `Ccxt.Pro.connection_info/1`, and
`Ccxt.Pro.close_connection/1` provide Elixir runtime introspection and
explicit connection lifecycle control for IEx/manual testing. These helpers
do not change CCXT Pro `unWatch*` semantics: `unWatch*` still only sends
`UNSUBSCRIBE` and waits for the exchange acknowledgement.
- CCXT-style `:verbose` can be passed through generated watch/request options;
it toggles connection-level websocket send/message/close logging on the
reused URL connection. The Elixir runtime also emits `:telemetry` events
under `[:ccxt, :pro, ...]`, and `Ccxt.Pro.attach_debug_logger/0` /
`Ccxt.Pro.detach_debug_logger/0` provide an IEx-friendly event logger.
- `Ccxt.Pro.Binance.stream_ticker/2`, `stream_trades/4`,
`stream_order_book/3`, and `stream_ohlcv/5` are Elixir-native convenience
wrappers over generated `watch_*` methods; they keep CCXT's one-message watch
semantics intact while letting callers consume repeated public updates with
`Enum.take/2`, `Stream.each/2`, or other `Enumerable` tooling.
- Private watcher convenience wrappers are available as `stream_balance/1`,
`stream_orders/4`, `stream_positions/4`, and `stream_my_trades/4`. These
repeat the generated private `watch_*` methods and intentionally do not imply
a private `unwatch_*` API; use `Ccxt.Pro.close_connection/1` for explicit
connection shutdown.
- `Ccxt.Pro.Connection` owns websocket subscriptions, message hash routing, waiter resolution, and disconnect rejection.
- `Ccxt.Pro.Connection.handle_disconnect/2` returns the WebSockex reconnect
directive and rejects pending waiters with `{:disconnected, reason}`; the
lifecycle coverage matrix exposes this as `websocket-process-reconnect` with
`reject-pending-waiters`.
- `Ccxt.Pro.ArrayCache` provides a reusable bounded newest-first cache used by
public ticker/trade/OHLCV streams, private order/trade/liquidation stream
retention, and order book pre-snapshot delta buffers. Subscriptions can pass
`:cache_limit` to configure retention per message hash.
- Private stream subscriptions can also carry `:snapshot_payloads`; the
connection runtime preloads those payloads into the private cache and resolves
matching waiters immediately. Generated private watch helpers also support
`:fetch_balance_snapshot`/`:fetchBalanceSnapshot` and
`:fetch_positions_snapshot`/`:fetchPositionsSnapshot`; those option branches
can use an injectable `:snapshot_fetcher`, an injectable REST `:rest_fetcher`,
or the generated high-level REST methods.
Manual examples live under `elixir/examples/`:
- `pro_watch_ticker.exs`
- `pro_unwatch_ticker.exs`
- `pro_connection_info.exs`
- `pro_close_connection.exs`
- `pro_debug_logger.exs`
- `pro_stream_ticker.exs`
- `pro_ticker_worker.exs`
- `pro_public_market_streams.exs`
- `pro_private_readonly.exs`
- `pro_safe_order_lifecycle.exs`
- `pro_order_event_stream.exs`
- `pro_order_event_worker.exs`
- `pro_soak_smoke.exs`
- `pro_structure_persistence.exs`
Run examples from `ccxt/elixir` with `mix run examples/<name>.exs`. Public
examples do not require API keys. Private examples load `../../.env` when it is
present and require production credentials. Order examples require
`CCXT_PRO_ENABLE_PROD_ORDER_LIVE=true` and use non-marketable spot limit orders
with immediate cleanup.
Production public-stream soak is deliberately opt-in and does not require API
keys:
```sh
cd ..
npm run testElixirProSoak
npm run testElixirProLongSoak
```
The soak gate runs `test/ccxt_pro_binance_soak_test.exs` with the `:soak` tag.
It validates a public order-book snapshot, consumes `stream_ticker/2` for
`CCXT_PRO_SOAK_SECONDS` seconds, checks maximum ticker update gaps through
`CCXT_PRO_SOAK_MAX_GAP_MS`, verifies connection introspection, validates
explicit close/reopen behavior for a public ticker watch, verifies supervised
restart after an abnormal connection process exit, and then closes all Pro
websocket connections. The default symbol is `BTC/USDT`; override it with
`BINANCE_PRO_SOAK_SYMBOL`. Because soak is an operational stability gate,
ordinary `mix test` and `npm run checkElixir` exclude it by default.
The long soak gate runs `test/ccxt_pro_binance_long_soak_test.exs` with the
`:soak_long` tag. It defaults to `CCXT_PRO_LONG_SOAK_SECONDS=900` and records
public ticker update count, maximum update gap, telemetry message count,
connection start count, disconnect count, terminated count, and cleanup status.
For a quick sanity run, override `CCXT_PRO_LONG_SOAK_SECONDS=5` and
`CCXT_PRO_LONG_SOAK_MIN_UPDATES=1`.
The generator is currently an AST-driven Pro slice:
- `build/elixirProTranspiler.ts` reads `ts/src/pro/binance.ts` with the TypeScript compiler API.
- The generator builds a `ProMethodModel` for every TypeScript class method.
- Each method model records structured source span
(`source.path/startLine/endLine`), async/static/visibility, parameters, return
type, body AST summary, method category, AST-derived `irSummary`, and lowering
status.
- Every source method now has non-empty structured `irSummary.nodes`; ExUnit
asserts this for all 107 Binance Pro TypeScript methods.
- Lowering coverage is inferred from `ProMethodModel` category plus AST/body
signals such as calls, awaits, returns, and statement shape; it no longer
depends on a hand-maintained required-method/lowering list.
- Generated `elixir/lib/ccxt/pro/binance.ex` includes source trace lines for each method.
- Generated `elixir/lib/ccxt/pro_manifest.json` records coverage, generated
methods, runtime-owned methods, unsupported methods, unsupported reasons,
structured source trace, `renderPlan` records for generated methods, and
`runtimeContract` records for runtime-owned methods.
- Generated method blocks in `renderBinancePro` are now selected through the
manifest-backed `renderPlan`; inline metadata helpers remain in the module
scaffold, while non-inline generated methods must resolve through a renderer
entry before they can be emitted. Non-inline renderer names are dispatched
through `METHOD_RENDERER_REGISTRY`, so the manifest renderer key is the actual
generation entrypoint. The generated module insertion points read
`renderSections.main/parser`, which are derived from non-inline `renderPlan`
entries and TypeScript source order rather than a hand-maintained method
order list.
- Runtime-owned method contracts record the owning Elixir module/function, the
contract kind, and the dispatch/cache/signing signals that connect the
TypeScript method to reusable Pro runtime behavior.
- Websocket API request methods expose AST-derived sub-IR signals and nodes in the
manifest, including signed request routing, request waiter routing,
order-payload create/edit/cancel variants, option routing, market-symbol
routing, and response filter nodes.
- Public/private stream, unsubscribe, helper, cache, and orderbook methods also
expose structured `irSummary.nodes`, including watch single/multiple nodes,
channel families, private user-data auth nodes, cache nodes, unsubscribe
families, snapshot preload nodes, and orderbook snapshot/delta cache nodes.
- Auth/listenKey, parser, websocket API response handler, public/private event
handler, and top-level dispatch methods also expose structured nodes for
signing, listen-token routing, parser branches, response handlers, event
dispatch, waiter rejection, and cache updates.
- `npm run assertElixirPro` verifies every TypeScript source method is lowered,
every manifest method has structured source trace matching its AST span, every
generated method has a render plan, every render entrypoint method is backed by
a non-inline render plan entry, every non-inline generated method is consumed
by the module render entrypoint, every non-inline render plan entry resolves
through `METHOD_RENDERER_REGISTRY`, the registry has no unused renderer keys,
every runtime-owned method has a complete runtime contract, and compares
generated output plus manifest byte-for-byte to prevent manual drift.
The current generator uses AST-derived IR summaries for coverage and source
trace. Generated method bodies are emitted through manifest-backed renderer
entries, and stabilized payload/cache/auth/parser/router behavior is also
recorded as smaller reusable Pro IR nodes. Further work can keep splitting
large renderer internals into finer IR nodes, but that is now an incremental
refinement rather than a source-method coverage gap. The public watch renderer slice
emits `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_liquidations/4`, and
`watch_liquidations_for_symbols/4` from shared Pro IR specs rather than inline
method templates. The ticker-family public stream methods, including mark
prices and bids/asks, are covered through generated
`watch_multi_ticker_helper/5` plus a shared ticker-family wrapper renderer for
`watch_tickers/2`, `watch_mark_prices/2`, and `watch_bids_asks/2`; the helper
delegates public watch-any subscription, cache registration, and parser
market-type selection to a typed ticker-family subscription node. The public
`unwatch_*` methods are covered by shared unsubscribe wrapper, channel-family,
and ticker-stream renderers. The first websocket API request renderer slice
emits market-data request methods including `fetch_order_book_ws/3`,
`fetch_ohlcv_ws/5`, `fetch_ticker_ws/2`, and `fetch_trades_ws/4`, plus signed
helper query methods including `fetch_balance_ws/1`, `fetch_positions_ws/2`,
`fetch_order_ws/3`, and `fetch_open_orders_ws/4`. The shared websocket API
request renderer now delegates request setup, payload, request mode, and
response rendering to `WsApiRequestNodeIrSpec`; its dispatcher now delegates to
dedicated signed-waiter, unsigned request, and signed-helper request renderers,
covering signed waiter, unsigned connection, unsigned helper, and signed helper
request modes. The
websocket API wrapper renderer covers `fetch_position_ws/2` and
`fetch_closed_orders_ws/4`.
Validation-aware websocket API request rendering covers `fetch_orders_ws/4`
and `fetch_my_trades_ws/4`, plus cancel mutations `cancel_order_ws/3` and
`cancel_all_orders_ws/2`; these methods render guarded validation branches plus
typed signed-request payload/response nodes through the validated request IR.
Signed order mutation rendering now covers
`create_order_ws/6` and `edit_order_ws/7`, including test/SOR/algo-order method
selection, spot cancel-replace, and contract modify-order payload selection;
their signed request/response path is centralized through a generated order
mutation request helper, and the generated order mutation methods render their
success path through typed payload-wrapper, pre-request, and signed-request IR
nodes rather than an opaque method-body line block. Edit-order spot/swap
payload selection is centralized through generated order payload helpers. Order payload helper
generation is driven by a composed payload-helper IR spec with typed child
specs for order-id payloads, create-order payload delegation, order request
market stubs, spot cancel-replace payloads, contract modify payloads, base
order payloads, option/camel-case params, and conditional-order detection. The
manifest also records AST-derived websocket API sub-IR signals and structured
nodes such as
`order-payload:create`, `order-payload:spot-cancel-replace`,
`order-payload:contract-modify`, `order-payload:cancel`,
`request:signed-waiter`, `request:unsigned-connection`,
`request:unsigned-helper`, `request:signed-helper`,
`validation:guarded-request`, `request:signed-validated`,
`order-mutation:success`, `request:signed-order-mutation`,
`payload-shape:book-depth`, `payload-shape:ohlcv-query`,
`payload-shape:my-trades-query`, `payload-field:symbol`,
`payload-field:order-id`, `payload-field:return-rate-limits`,
`response-filter:positions`, and `response-filter:symbol-since-limit`. Stream,
unsubscribe, helper, cache, and orderbook methods also record structured nodes
such as `watch:single`, `watch:multiple`, `channel-family:trades`,
`subscription:public-single`, `request:public-watch`,
`subscription:public-multiple`, `request:public-watch-any`,
`auth:user-data-stream`, `cache:snapshot-preload`, `unsubscribe:orderbook`,
`snapshot:balance`, `snapshot:positions`, `request:snapshot-fetch`,
`payload:unsubscribe`, `request:unsubscribe-waiter`, and
`orderbook:snapshot-delta-merge`. Auth, parser, and handler methods record
nodes such as `auth:signature`, `auth:listen-token`, `parser:trade`,
`auth-signing:hmac-sha256`, `auth-signing:rsa-sha256`,
`auth-signing:eddsa-ed25519`, `credential:api-key`,
`payload:sorted-rawencode`,
`metadata-field:capabilities`, `metadata-field:urls`,
`metadata-field:timeframes`, `request-counter:per-url`,
`runtime-state:options-request-id`, `request-id:increment`,
`auth-subscription:request`, `response:subscription-id`,
`auth-route:spot-signature`, `auth-route:listen-key`,
`auth-route:listen-token`, `auth-route:unsupported`,
`listen-token-lifecycle:subscribe-ack`,
`listen-token-lifecycle:renewal`,
`listen-key-keepalive:supported`, `listen-key-keepalive:unsupported`,
`request:rest-keepalive`,
`routing:public-websocket-url`, `routing:private-websocket-url`,
`routing:stream-path`, `routing-dimension:market-type`,
`routing-dimension:listen-key`,
`parser-branch:public-private`, `parser-branch:ticker-family`,
`parser-wrapper:safe_trade`, `parser-wrapper:safe_ticker`,
`parser-wrapper:safe_order`, `parser-wrapper:safe_liquidation`,
`parser-field:liquidation-order`, `parser-field:contracts`,
`parser-field:side`, `parser-field:hedged`, `handler:ws-api-response`,
`response:ws-api-success`, `error:status-error`, `request:request-id`,
`private-event:order`, `cache-engine:order-cache`,
`cache-engine:array-cache`, `cache-engine:balance-position-cache`,
`cache-class:array-cache`, `cache-class:order-cache`,
`cache-class:position-cache`, `cache-class:orderbook-cache`,
`cache-semantic:bounded-newest-first`, `cache-semantic:symbol-id-index`,
`cache-semantic:order-trade-merge`, `cache-semantic:balance-delta`, and
`dispatch:request-channel-private-error`,
`dispatch-branch:request-id`, `dispatch-branch:channel`,
`dispatch-branch:private-event`.
Auth/listenKey IR rendering covers spot websocket API signature subscription,
margin listenToken subscription, `authenticate/1` routing, renewal, and futures
listenKey keepalive; the signature and listenToken subscription methods render
their websocket API auth request and subscription-id response handling through a
typed auth subscription request node. `authenticate/1` routes through typed
auth router branch specs for spot signature, futures listenKey, margin
listenToken, and unsupported branches. `keep_alive_listen_key/3` renders its
supported futures endpoint fetch and unsupported branch through a typed
listenKey keepalive request node. Shared private watch IR rendering covers private balance,
orders, positions, my trades, and my liquidation stream methods, with private
payload fetches delegated to a typed private watch payload/request node; the single
symbol private liquidation wrapper uses the same single-from-multiple wrapper
IR as the public mark-price wrapper. Structured
parser IR rendering now covers the field-mapping parser shape used by
`parse_ws_liquidation/2`, `parse_ws_order/2`, and `parse_ws_position/1`;
conditional parser IR rendering covers the branch shapes used by
`parse_ws_ticker/3` and `parse_ws_trade/2`.
## Covered Public Websocket Methods
Generated Binance Pro methods currently covered:
- `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_liquidations/4`
- `watch_liquidations_for_symbols/4`
- `watch_mark_price/2`
- `watch_mark_prices/2`
- `watch_bids_asks/2`
- `watch_multi_ticker_helper/5`
- `unwatch_ticker/2`
- `unwatch_tickers/2`
- `unwatch_mark_price/2`
- `unwatch_mark_prices/2`
- `unwatch_order_book/2`
- `unwatch_order_book_for_symbols/2`
- `unwatch_trades/2`
- `unwatch_trades_for_symbols/2`
- `unwatch_ohlcv/3`
- `unwatch_ohlcv_for_symbols/2`
- `fetch_ohlcv_ws/5`
- `fetch_ticker_ws/2`
- `fetch_order_book_ws/3`
- `fetch_balance_ws/1`
- `fetch_position_ws/2`
- `fetch_positions_ws/2`
- `cancel_order_ws/3`
- `cancel_all_orders_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`
- `fetch_trades_ws/4`
- `create_order_ws/6`
- `edit_order_ws/7`
- `sign_params/2`
- `ensure_user_data_stream_ws_subscribe_signature/2`
- `ensure_user_data_stream_ws_subscribe_listen_token/2`
- `renew_listen_token/1`
- `authenticate/1`
- `get_private_ws_url/3`
- `keep_alive_listen_key/3`
- `watch_my_liquidations/4`
- `watch_my_liquidations_for_symbols/4`
- `watch_balance/1`
- `watch_orders/4`
- `watch_positions/4`
- `watch_my_trades/4`
- `parse_ws_order/2`
- `parse_ws_position/1`
Current TypeScript source-method trace coverage:
- `107/107` Binance Pro methods are currently bound to generated Elixir output
or explicit runtime-owned Pro behavior.
- Current manifest split is `66` generated methods and `41` runtime-owned
methods.
- `publicStream` coverage is `14/14`.
- `unsubscribe` coverage is `10/10`.
- `auth` coverage is `8/8`; covered methods include the HMAC branch of
`signParams` and the spot `userDataStream.subscribe.signature`
authentication path, futures listenKey creation/keepalive helpers, and margin
listenToken subscription/renewal. The subscription, routing, renewal, and
keepalive methods are emitted through shared auth/listenKey IR renderers.
- `privateStream` coverage is `6/6`; covered methods include private balance,
orders, positions, my trades, and my liquidations streams. These methods are
emitted through shared private watch IR renderers.
- `parser` coverage is `5/5`; `parse_ws_liquidation/2` and
`parse_ws_position/1` are emitted through structured parser IR, while
`parse_ws_ticker/3` and `parse_ws_trade/2` are emitted through conditional
parser IR. `parse_ws_order/2` is also emitted through structured parser IR.
- `handler` coverage is `26/26`; AST call signals classify handler contracts
into public stream handlers, private stream handlers, unsubscribe
acknowledgements, order book cache handlers, websocket API response handlers,
and a small top-level event dispatch set. These runtime-owned contracts are
recorded in the manifest with owner functions such as `dispatch/3`,
`dispatch_private/2`, `handle_orderbook_delta/3`,
`resolve_or_reject_request_message/3`, and `handle_frame/2`.
- `wsApi` coverage is `24/24`; covered methods now include public market-data
requests, signed balance/position/order/trade query requests, signed
cancel-order requests, and signed create/edit order requests.
- `helper` coverage is `14/14`; metadata/url/category/account-type/market-type
helpers are generated, while snapshot/cache helpers are explicit
runtime-owned behavior with manifest runtime contracts.
- Coverage can be inspected with `npm run inspectElixirPro`.
- `npm run inspectElixirPro` reads TypeScript AST-derived `ProMethodModel`
records and groups coverage by method category.
- `elixir/lib/ccxt/pro_manifest.json` records `irSummary` for every source
method; tests assert that all 107 methods have non-empty AST-derived IR
signals, non-empty structured IR nodes, structured source trace
(`source.path/startLine/endLine`), MethodModel metadata, and no unsupported
entries.
- The same manifest records `methodCoverageMatrix` for every source method,
including source span, category, generated/runtime owner, renderer or runtime
owner function, unit evidence, live evidence, risk class, and skip reason.
Tests assert the matrix covers `107/107` methods and stays synchronized with
the source-method manifest.
- The same manifest records a `renderPlan` for every generated method; tests
assert that the render plan and generated method set are identical. The
manifest also records source-derived `renderSections.main/parser` for module
emission; tests assert those sections match the non-inline render plan.
- The generator assert also checks that the method blocks emitted by
`renderBinancePro` are resolved from `renderPlan`, rather than from a separate
hand-maintained required-method or render-order source; non-inline render
plan entries that are not consumed by a module render section fail the assert
path, and renderer registry drift is rejected in both directions.
- Runtime-owned source methods also record `runtimeContract`; generator assert
and ExUnit both require complete contract metadata for those methods.
- `Ccxt.ProLifecycleCoverage` groups `runtimeSemanticCoverage` into required
lifecycle feature rows, owner modules/functions, source methods, contract
kinds, and signals. `doc/ccxt-pro-lifecycle-coverage.md` is the rendered
matrix for review.
- This number is method-trace coverage, not runtime behavior quality; several
traced handler/helper methods are represented by shared runtime behavior.
- `requestId` remains runtime-owned because the TypeScript method mutates
instance-local `options.requestId[url]` counters. The current Elixir Pro
module is stateless at the exchange-function boundary, so request id ownership
stays with websocket request/waiter runtime behavior until exchange instance
state is modeled explicitly. The Pro IR still records the counter shape as
`request-counter:per-url`, `runtime-state:options-request-id`,
`request-id:increment`, and `waiter:message-hash`.
The generator tracks these TypeScript source methods:
- `watchTicker`
- `watchLiquidations`
- `watchLiquidationsForSymbols`
- `handleLiquidation`
- `parseWsLiquidation`
- `watchTickers`
- `watchMultiTickerHelper`
- `parseWsTicker`
- `watchOrderBook`
- `watchOrderBookForSymbols`
- `fetchOrderBookSnapshot`
- `handleDelta`
- `handleDeltas`
- `handleOrderBookMessage`
- `handleOrderBook`
- `handleOrderBookSubscription`
- `unWatchOrderBookForSymbols`
- `unWatchOrderBook`
- `unWatchTradesForSymbols`
- `unWatchTrades`
- `watchTrades`
- `watchTradesForSymbols`
- `parseWsTrade`
- `handleTrade`
- `watchOHLCV`
- `watchOHLCVForSymbols`
- `unWatchOHLCVForSymbols`
- `unWatchOHLCV`
- `handleOHLCV`
- `fetchOHLCVWs`
- `handleFetchOHLCV`
- `fetchTickerWs`
- `handleTickerWs`
- `fetchOrderBookWs`
- `handleFetchOrderBook`
- `fetchBalanceWs`
- `handleBalanceWs`
- `handleAccountStatusWs`
- `fetchPositionWs`
- `fetchPositionsWs`
- `handlePositionsWs`
- `cancelOrderWs`
- `cancelAllOrdersWs`
- `fetchOrderWs`
- `fetchOrdersWs`
- `fetchClosedOrdersWs`
- `fetchOpenOrdersWs`
- `handleOrderWs`
- `handleOrdersWs`
- `fetchMyTradesWs`
- `fetchTradesWs`
- `handleTradesWs`
- `createOrderWs`
- `editOrderWs`
- `handleEditOrderWs`
- `signParams`
- `getPrivateWsUrl`
- `ensureUserDataStreamWsSubscribeSignature`
- `handleUserDataStreamSubscribe`
- `authenticate`
- `keepAliveListenKey`
- `watchMarkPrice`
- `watchMarkPrices`
- `unWatchTickers`
- `unWatchMarkPrices`
- `unWatchMarkPrice`
- `unWatchTicker`
- `watchBidsAsks`
- `handleBidsAsks`
- `handleTickers`
- `handleMarkPrices`
- `handleTickersAndBidsAsks`
- `getWsUrl`
- `stream`
- `watchMyLiquidations`
- `watchMyLiquidationsForSymbols`
- `watchBalance`
- `watchOrders`
- `watchPositions`
- `watchMyTrades`
- `parseWsOrder`
- `parseWsPosition`
- `handleMyLiquidation`
- `handleBalance`
- `handleOrderUpdate`
- `handlePositions`
- `handleMyTrade`
- `handleOrder`
- `handleAcountUpdate`
## Runtime Semantics Implemented
Current websocket dispatch supports:
- `24hrMiniTicker`
- `24hrTicker`
- `trade`
- `aggTrade`
- `kline`
- `markPrice_kline`
- `indexPrice_kline`
- `depthUpdate`
- `markPriceUpdate`
- `forceOrder`
- book ticker payloads without an `e` event field
- Binance `UNSUBSCRIBE` acknowledgement frames
- Binance private user-data events: `executionReport`, `outboundAccountPosition`,
`balanceUpdate`, `ACCOUNT_UPDATE`, and `ORDER_TRADE_UPDATE`
- Binance websocket API request frames with `status != 200` or an `error` body
are rejected by request id.
- Invalid websocket JSON rejects all pending stream/request waiters, so callers
do not remain blocked after a malformed frame. Request and unsubscribe waiters
unwrap their internal waiter tags and report errors with the original caller
ref, matching `request/3` and `unwatch_any/3` receive contracts.
- Binance `UNSUBSCRIBE` error acknowledgements reject the unwatch caller instead
of reporting a successful unsubscribe.
- Binance `eventStreamTerminated` user-data events reject matching private
stream waiters instead of leaving callers blocked.
Current parser coverage:
- ticker: mini ticker, full 24h ticker, book ticker, mark price update
- liquidation: futures `forceOrder` payloads
- tickers: individual ticker update maps and all-market ticker arrays
- trade: public trade, aggregate trade, private execution trade payload
- order book: REST snapshot plus websocket depth delta merge
- OHLCV: kline payload to `[timestamp, open, high, low, close, volume]`
- private order: spot `executionReport` and futures `ORDER_TRADE_UPDATE`
- private position: futures account update position entries
Current cache behavior is intentionally minimal:
- `watch_ticker/2` returns the latest parsed ticker update.
- `watch_tickers/2` returns a one-update `%{symbol => ticker}` map for symbol subscriptions and parses all-market ticker arrays.
- `watch_trades/4` and `watch_trades_for_symbols/4` subscribe all requested
trade channels through cached `watch_any`; raw trade payloads are retained in
`Ccxt.Pro.ArrayCache` and parsed/filtered by `since` and `limit`.
- `watch_ohlcv/5` and `watch_ohlcv_for_symbols/4` subscribe requested kline
channels through cached `watch_any`; raw kline payloads are retained in
`Ccxt.Pro.ArrayCache` and parsed/filtered by `since` and `limit`.
- `watch_tickers/2`, `watch_mark_price/2`, `watch_mark_prices/2`, and
`watch_bids_asks/2` use cached ticker-family `watch_any`; raw ticker payloads
are retained in `Ccxt.Pro.ArrayCache` and parsed into symbol-keyed ticker
maps.
- Public ticker, trade, and OHLCV cache retention is covered by offline
connection tests that assert per-message-hash `ArrayCache` storage and
newest-first bounded semantics.
- `watch_liquidations/4` and `watch_liquidations_for_symbols/4` return one
liquidation update when Binance emits a matching `forceOrder` event; the
`ForSymbols` path subscribes all requested futures `forceOrder` channels
through `watch_any`.
- `watch_order_book_for_symbols/3` registers all requested order book channels,
loads snapshots per symbol, and returns the first normalized order book that
reaches a valid snapshot/delta state. Snapshot-time websocket delta buffers
use `Ccxt.Pro.ArrayCache` with optional per-subscription `:cache_limit`.
- `watch_balance/1`, `watch_orders/4`, `watch_positions/4`,
`watch_my_trades/4`, and `watch_my_liquidations/4` authenticate the user-data
stream, reuse the supervised websocket connection, and wait on CCXT message
hashes. Balance, positions, orders, my trades, and my liquidations are merged
into runtime private cache structures before watcher resolution. Private
order/trade/liquidation list retention now uses `Ccxt.Pro.ArrayCache` with
bounded newest-first append semantics and optional per-subscription
`:cache_limit`.
- Private event-driven watchers are not part of the always-on live smoke
because they depend on future account events that may not occur during a
deterministic timeout. `watch_balance/1`, `watch_orders/4`,
`watch_positions/4`, `watch_my_trades/4`, `watch_my_liquidations/4`, and
`watch_my_liquidations_for_symbols/4` are covered by deterministic
fake-connection tests, cache payload tests, snapshot preload tests, and
message-hash routing tests instead. The Pro unit suite asserts these watcher
calls stay out of `test/ccxt_pro_binance_live_test.exs`.
- `fetch_ohlcv_ws/5` sends a Binance websocket API `klines` request and parses
the response `result` into OHLCV arrays.
- `fetch_ticker_ws/2` and `fetch_order_book_ws/3` send signed Binance futures
websocket API requests using generated `sign_params/2`.
- `fetch_balance_ws/1`, `fetch_position_ws/2`, `fetch_positions_ws/2`,
`fetch_order_ws/3`, `fetch_orders_ws/4`, `fetch_open_orders_ws/4`,
`fetch_closed_orders_ws/4`, and `fetch_my_trades_ws/4` send signed websocket
API read requests and parse the response through shared ws-api helpers.
- `cancel_order_ws/3` and `cancel_all_orders_ws/2` are generated and covered by
offline signed request/response tests. They are excluded from the always-on
live smoke because they mutate order state and need a user-confirmed target
order or a freshly created test order.
- `fetch_trades_ws/4` sends the unsigned websocket API public historical trades
request and is included in the live smoke.
- `create_order_ws/6` and `edit_order_ws/7` are generated and covered by offline
signed request/response tests. Real production order mutation is gated by
`CCXT_PRO_ENABLE_PROD_ORDER_LIVE=true`; when enabled, the live test builds a
deliberately non-marketable spot limit buy from the current production order
book, submits it, and immediately cancels it. Without that explicit flag the
test reports that no production order was sent.
- Prod-only funding or account-state mutations that are not order placement
remain out of live execution. Withdraw, transfer, borrow, repay, convert, and
gift-code flows should be covered only by inventory lists, dry-run/signature
rendering, or request-shape tests unless they receive a separate manual
confirmation for the exact operation.
- The Binance Pro manifest does not include withdraw, transfer, borrow, repay,
convert, or gift-code methods. Its current mutation surface is limited to
`create_order_ws/6`, `edit_order_ws/7`, `cancel_order_ws/3`, and
`cancel_all_orders_ws/2`; an offline manifest test asserts this risk
inventory.
- Non-Pro Binance production-only mutation classes are covered by
`test/ccxt_binance_prod_only_dry_run_test.exs` with fake credentials and no
HTTP execution. That dry-run suite renders signed production request shapes
and asserts demo rejection for withdraw, transfer, futures transfer, gift-code
create/redeem, convert accept/transfer, margin borrow/repay, portfolio margin
borrow/repay, simple earn redeem, option order, portfolio margin order, and
portfolio margin leverage.
- `sign_params/2` implements the TypeScript `signParams` signing branches:
HMAC SHA256 for ordinary API secrets, RSA SHA256 for long PEM private keys,
and Ed25519 for short Binance EdDSA PEM private keys. The manifest records
credential requirements, timestamp/recvWindow payload extension, sorted
rawencode canonicalization, and all three signing algorithms.
- `ensure_user_data_stream_ws_subscribe_signature/2` sends the signed spot
websocket API `userDataStream.subscribe.signature` request and returns the
subscription id.
- `ensure_user_data_stream_ws_subscribe_listen_token/2` creates a margin
listenToken through `sapiPostUserListenToken`, subscribes it through websocket
API `userDataStream.subscribe.listenToken`, and returns subscription metadata.
- `renew_listen_token/1` reuses the margin listenToken subscription path with
explicit parameters, matching the TypeScript renewal wrapper at the current
stateless Elixir boundary.
- `authenticate/1` currently covers the TypeScript spot signature branch,
margin listenToken branch, and listenKey branches for future, delivery, and
portfolio-margin streams.
- `get_private_ws_url/3` generates Binance private websocket URLs for
listenKey-backed future, delivery, and portfolio-margin streams. Public and
private websocket URL helpers now record typed routing nodes with market type,
category, environment, stream hash, and listenKey dimensions in the manifest.
- `keep_alive_listen_key/3` implements listenKey keepalive requests for future,
delivery, and portfolio-margin streams.
- All generated `unwatch_*` public methods send Binance `UNSUBSCRIBE` and wait
for the request acknowledgement.
- `watch_order_book/3` keeps local order book state in the websocket connection process.
The cache-class parity milestone now has runtime owners for the Binance Pro
cache semantics used so far. `Ccxt.Pro.IndexedArrayCache` owns the reusable
`symbol -> key` cache behavior behind `ArrayCacheBySymbolById` and
`ArrayCacheBySymbolBySide` style caches. `Ccxt.Pro.TimestampArrayCache` owns the
`ArrayCacheByTimestamp` style timestamp replacement cache used by OHLCV streams.
`Ccxt.Pro.PositionCache` wires symbol+side indexing into Binance position
streams, and `Ccxt.Pro.CacheUpdates` owns CCXT Pro `getLimit`-style
`newUpdates` counters. `Ccxt.Pro.Connection` wires those counters into public
cached streams and private list/order streams when callers pass
`newUpdates: true` or `new_updates: true`.
Order book state currently follows the Binance/CCXT Pro sequence rules:
- Deltas received before the REST snapshot are buffered.
- The REST snapshot initializes bids, asks, and nonce from `lastUpdateId`.
- Spot deltas with `u <= nonce` are dropped.
- The first valid spot delta must satisfy `U - 1 <= nonce` and `u - 1 >= nonce`.
- Later spot deltas must satisfy `U - 1 == previous u`.
- Futures deltas use `U/u/pu` continuity; cached futures deltas can bridge the
REST snapshot and later deltas must continue from the previous update id.
- Sequence gaps reject the waiter with a checksum-style error, matching the TypeScript `watchOrderBook` behavior where checksum mode turns sequence inconsistency into `ChecksumError`.
- When checksum mode is disabled, the same sequence inconsistency is surfaced
as `:sequence_gap` for testable non-checksum order book subscriptions.
## Verification
Standard gate:
```sh
npm run checkElixir
```
Pro generation stability:
```sh
npm run assertElixirPro
```
This gate fails if any TypeScript method is unsupported, if any runtime-owned
method lacks a complete runtime contract, or if generated files drift from the
AST-derived output.
Public Binance Pro live smoke:
```sh
cd elixir
mix test --include live --exclude prod_order_live \
test/ccxt_pro_binance_live_test.exs --timeout 180000
```
The public stream live tests use Binance websocket streams and do not require
API keys. Event-driven liquidation streams are covered by a stable live
`forceOrder` subscribe/unsubscribe acknowledgement test, while actual forced
order payload parsing stays in deterministic parser and fake-connection tests
because Binance only emits payloads when a real forced-order event occurs.
Signed websocket API live tests run when `BINANCE_PROD_API_KEY` /
`BINANCE_PROD_API_SECRET` are available. The Pro live test credential check
intentionally does not fall back to generic `BINANCE_API_KEY` /
`BINANCE_API_SECRET`, because those may point at demo credentials and mask
production permission failures.
The signed read-only websocket API smoke covers `fetch_balance_ws/1`,
`fetch_position_ws/2`, `fetch_positions_ws/2`, `fetch_open_orders_ws/4`,
`fetch_orders_ws/4`, `fetch_closed_orders_ws/4`, and `fetch_my_trades_ws/4`.
These calls inspect account/order/trade state and do not create, edit, cancel,
transfer, borrow, repay, convert, or withdraw.
The always-on live smoke does not wait for a real liquidation payload; it
verifies the public `forceOrder` subscription lifecycle against Binance and
keeps payload shape assertions deterministic.
Demo private read-only smoke can be run on its own:
```sh
cd elixir
mix test --exclude live --include demo_private_live \
test/ccxt_pro_binance_live_test.exs --timeout 180000
```
This verifies the demo read-only websocket API matrix for
`fetch_balance_ws/1`, `fetch_positions_ws/2`, `fetch_open_orders_ws/4`,
`fetch_orders_ws/4`, `fetch_closed_orders_ws/4`, and `fetch_my_trades_ws/4`.
The test accepts successful demo responses or explicit Binance demo
unsupported/credential/permission responses. It is read-only and does not place
orders or move funds.
Production order mutation smoke is deliberately opt-in:
```sh
cd elixir
CCXT_PRO_ENABLE_PROD_ORDER_LIVE=true \
BINANCE_PROD_ORDER_AMOUNT=0.0002 \
mix test --exclude live --include prod_order_live \
test/ccxt_pro_binance_live_test.exs --timeout 120000
```
That test uses production credentials, places a non-marketable spot limit buy,
and cancels the created order immediately. It is meant only for the prod-only
order-placement class; non-order funding mutations stay dry-run/request-shape
only.
Latest verified run, 2026-06-11:
- `npm run inspectElixirPro`: `107/107` Binance Pro source methods covered,
split as `66` generated and `41` runtime-owned.
- `npm run assertElixirPro`: generated output stable.
- `mix test test/ccxt_pro_binance_test.exs test/ccxt_pro_binance_soak_test.exs`:
`114 tests, 0 failures (4 excluded)`; this includes HMAC SHA256, RSA SHA256,
Ed25519 `signParams` branch coverage, Pro runtime connection lifecycle helper
coverage, and proof that the `:soak` gate stays opt-in.
- `mix compile --warnings-as-errors`: passed.
- `npm run checkElixir`: `509 tests, 0 failures (60 excluded)`.
- `CCXT_PRO_SOAK_SECONDS=5 CCXT_PRO_SOAK_MIN_UPDATES=1 mix test --include soak test/ccxt_pro_binance_soak_test.exs --timeout 120000`:
`3 tests, 0 failures`. This short public-stream run validated order-book
snapshots before/after a ticker stream, repeated ticker updates, connection
introspection, ticker unwatch-on-halt, explicit close/reopen, supervised
restart after abnormal process exit, and websocket cleanup.
- `CCXT_PRO_LONG_SOAK_SECONDS=5 CCXT_PRO_LONG_SOAK_MIN_UPDATES=1 npm run testElixirProLongSoak`:
`1 test, 0 failures`. This short long-soak sanity run records ticker update
count, maximum update gap, connection starts, disconnects, terminations,
telemetry message count, and explicit websocket cleanup.
- `CCXT_PRO_WORKER_SECONDS=5 mix run examples/pro_ticker_worker.exs`: received
`3` public ticker updates for `BTC/USDT`, returned a GenServer snapshot with
`last_error: nil`, and stopped the worker with explicit websocket cleanup.
- `BINANCE_PRO_ENV=prod CCXT_PRO_ORDER_EVENT_WORKER_SECONDS=90 mix run
examples/pro_order_event_worker.exs` while
`CCXT_PRO_ENABLE_PROD_ORDER_LIVE=true BINANCE_PROD_ORDER_SYMBOL=BTC/USDT
BINANCE_PROD_ORDER_AMOUNT=0.0002 mix run examples/pro_safe_order_lifecycle.exs`
was running: the private worker received `3` order-event updates for a
non-marketable production spot order lifecycle covering create/open, cancel
of the original order, and cancel of the edited order. The final worker
snapshot retained recent orders and the retry path surfaced Binance `-2035
User Data Stream subscription already active`, which is an exchange
lifecycle condition after an active user-data stream subscription.
- `npm run testElixirConsumer`: compiled `smoke/consumer_app` as an external
path dependency consumer using `{:ccxt, path: "../.."}` and ran `1` live
public Binance Pro test with `0` failures. This verifies
`Ccxt.Pro.binance/1`, `watch_ticker/2`, `stream_ticker/2`, and
`Ccxt.Pro.close_connection/1` outside the package project.
- `npm run buildElixirPackage`: built `ccxt-0.1.0-binance-pro-preview.tar`
with package metadata and the intended runtime/docs payload. A follow-up
`mix hex.build --unpack` succeeded and confirmed the package includes
`lib`, `priv`, `doc`, `mix.exs`, and `README.md`, including
`doc/real-project-integration.md` and `doc/release-checklist.md`, while
excluding `test`, `examples`, and `smoke`.
- `npm run testElixirPackageConsumer`: built and unpacked the local Hex package
into a temporary directory, copied `smoke/consumer_app` into a clean temporary
consumer project, set `CCXT_CONSUMER_CCXT_PATH` to the unpacked package, and
ran the same public Binance Pro watch/stream/close smoke successfully from the
packaged payload.
- `npm run releaseElixirPreviewCheck`: release-preview aggregation gate that
runs `checkElixir`, `docsElixir`, and `testElixirPackageConsumer` in order.
Latest local run completed all three phases, including docs generation and
packaged consumer smoke with `1` test and `0` failures.
- `npm run testElixirProLongSoak`: `1 test, 0 failures` over `901.0` seconds.
This default production public ticker soak used `BTC/USDT` on `prod` for
`900` seconds and recorded `883` ticker updates, `1767` websocket messages,
`2992` ms maximum update gap, `2353` ms first update latency, `1` connection
start, `0` disconnects, `0` terminations, and explicit websocket cleanup.
- `mix test --include live --exclude prod_order_live test/ccxt_pro_binance_live_test.exs --timeout 180000`:
`34 tests, 0 failures`. This run covered the public single-symbol and
multi-symbol ticker, trade, OHLCV, order book, mark-price, bids/asks, and
unsubscribe smoke paths, plus live `forceOrder` subscribe/unsubscribe ACK
coverage for the liquidation stream. It also covered signed read-only
balance, position, open-orders, all-orders, closed-orders, and my-trades
websocket API calls when matching credentials were available, or recorded the
expected Binance credential / permission reason.
- `CCXT_PRO_ENABLE_PROD_ORDER_LIVE=true BINANCE_PROD_ORDER_SYMBOL=BTC/USDT BINANCE_PROD_ORDER_AMOUNT=0.0002 mix test --only prod_order_live test/ccxt_pro_binance_live_test.exs --timeout 180000`:
`3 tests, 0 failures (31 excluded)`. The gated production order smoke placed
non-marketable spot limit orders and verified create, fetch, edit, cancel,
and `stream_orders/4` execution-report event handling before cleanup.
## Remaining Public Scope
All TypeScript methods categorized as public websocket streams are now covered
in the current manifest.
## Remaining Private Scope
All TypeScript methods categorized as private websocket streams are now covered
in the current manifest. Balance, positions, orders, my trades, and my
liquidations now maintain lightweight runtime private cache state across
user-data events. Spot/margin `balanceUpdate` events add the websocket delta to
the existing cached free balance, matching the TypeScript
`Precise.stringAdd(previousValue, delta)` branch. Futures/delivery
`ACCOUNT_UPDATE` position entries inherit the websocket event timestamp so
`watch_positions/4` can return parsed positions with `timestamp` and
`datetime`, matching the TypeScript `handlePositions` timestamp assignment.
`Ccxt.Pro.PositionCache` owns the symbol+side-indexed position cache,
per-symbol payload view, and flattened values view used by
`watch_positions/4`.
Private order caches now merge trade execution metadata by order id: TRADE
events are routed through `Ccxt.Pro.OrderCache`, attach cached raw trades and
accumulated fee metadata to the cached order payload, and generated
`parse_ws_order/2` emits unified orders with `trades` and `fee`. The same
runtime component exposes a `hashmap/1` view keyed by `symbol -> order id`,
matching the lookup shape used by CCXT Pro order caches while preserving list
payload compatibility for current watchers.
Order/trade/liquidation list retention is now routed through
`Ccxt.Pro.ArrayCache`. Private stream subscriptions support per-message-hash
`:cache_limit`. Private stream subscriptions also support injectable
`:snapshot_payloads`, which preload balance/position caches and immediately
resolve matching private watch waiters. The generated private watch helpers now
wire the TypeScript `fetchBalanceSnapshot` and `fetchPositionsSnapshot` option
branches through `:snapshot_fetcher`, `:rest_fetcher`, or generated high-level
REST methods. Remaining cache work is deeper specialized cache variants beyond
the cache classes already represented in the manifest.
## Binance Pro Coverage Status
`ts/src/pro/binance.ts` source-method coverage is complete for Binance. The
remaining work listed here is incremental parity refinement and should continue
to be implemented as generic capabilities rather than one-off method templates:
### Stream Helper Completion Matrix
The Elixir-only `stream_*` helpers are generated convenience wrappers around
the generated `watch_*` methods. They repeatedly await the matching CCXT Pro
watch method and call the matching public `unwatch_*` method when the enumerable
halts where Binance exposes an unsubscribe channel. Private account streams use
the Binance user data stream lifecycle and runtime connection cleanup instead
of method-specific unsubscribe calls.
| Stream helper | Watch source | Unit coverage | Live coverage | Notes |
| --- | --- | --- | --- | --- |
| `stream_ticker/2,3` | `watch_ticker/2` | fake repeated updates | prod public live | Public stream; `Enum.take/2` receives repeated ticker updates. |
| `stream_trades/4,5` | `watch_trades/4` | fake repeated updates | prod public live | Public stream; no funds risk. |
| `stream_order_book/3,4` | `watch_order_book/3` | fake repeated snapshots | prod public live | Public stream; uses generated order-book watch path. |
| `stream_ohlcv/5,6` | `watch_ohlcv/5` | fake repeated candles | prod public live | Public stream; no funds risk. |
| `stream_balance/1,2` | `watch_balance/1` | fake private cache updates | prod private read-only smoke | Private stream; live can report credential, permission, or active subscription state. |
| `stream_positions/4,5` | `watch_positions/4` | fake private cache updates | prod private read-only smoke | Private futures stream; live depends on futures permission and account state. |
| `stream_orders/4,5` | `watch_orders/4` | fake repeated order events | gated prod live order event | Live proof uses `CCXT_PRO_ENABLE_PROD_ORDER_LIVE=true`, a non-marketable order, and immediate cancel. |
| `stream_my_trades/4,5` | `watch_my_trades/4` | fake repeated trade events | manual-fill only | Non-marketable orders intentionally do not emit trade fills; live proof requires an explicitly accepted real fill or remains fake/unit evidence. |
Current stream completion status is therefore complete for generated API
surface, repeated enumerable behavior, public live behavior, read-only private
smoke behavior, and gated order-event behavior. The only intentionally
ungated live gap is `stream_my_trades/4,5`, because proving it on Binance
requires a real executed trade rather than the non-marketable production order
policy.
- `unsubscribe`: the now-covered `un_watch_*` channel families are generated
through shared unsubscribe wrapper, channel-family, and ticker-stream
renderers. Direct channel-family unsubscribe methods delegate the
`unwatch_channels/5` waiter request to a typed unsubscribe request node;
ticker and mark-price unsubscribe wrappers delegate their
`unwatch_ticker_stream/6` call through a typed ticker-stream unsubscribe
node. The shared `unwatch_channels/5` helper is rendered through a typed
payload/waiter node that owns the Binance `UNSUBSCRIBE` message, generated
request id, public websocket URL, and `Ccxt.Pro.Connection.unwatch_any/3`
call.
- `publicStream`: source-method coverage is complete. `watch_ticker/2`,
`watch_tickers/2`, `watch_trades/4`, `watch_trades_for_symbols/4`,
`watch_ohlcv/5`, `watch_ohlcv_for_symbols/4`, `watch_liquidations/4`,
`watch_liquidations_for_symbols/4`, `watch_mark_price/2`,
`watch_mark_prices/2`, `watch_bids_asks/2`, and
`watch_multi_ticker_helper/5` are covered by shared public
watch/watchMultiple/ticker-family renderers. The single and multiple public
watch renderers now delegate channel/message-hash/subscription/watch-call
rendering to typed public subscription nodes for `watch_ticker/2`,
`watch_trades_for_symbols/4`, `watch_ohlcv_for_symbols/4`, and
`watch_liquidations_for_symbols/4`; `watch_multi_ticker_helper/5` delegates
ticker-family public watch-any subscription, cache registration, and parser
market-type selection to a typed subscription node while preserving its
unsubscribe branch. `watch_order_book/3` and
`watch_order_book_for_symbols/3` are covered by orderbook watch renderers,
including multi-symbol channel registration and per-symbol snapshot loading.
- `wsApi`: source-method coverage is complete. `fetch_order_book_ws/3`,
`fetch_ohlcv_ws/5`, `fetch_ticker_ws/2`, and `fetch_trades_ws/4` are covered
by the shared websocket API request renderer, whose typed request node covers
signed waiter, unsigned connection, and unsigned helper request modes through
dedicated request-mode renderers.
`fetch_balance_ws/1`,
`fetch_positions_ws/2`, `fetch_order_ws/3`, and `fetch_open_orders_ws/4` use
the same renderer through signed helper request mode. `fetch_position_ws/2`
and `fetch_closed_orders_ws/4` are covered by the websocket API wrapper
renderer; wrapper nodes now record their delegated ws-api method, list result
shape, and response filter semantics. `fetch_orders_ws/4` and
`fetch_my_trades_ws/4` are covered by
validation-aware websocket API request rendering, as are `cancel_order_ws/3`
and `cancel_all_orders_ws/2`; their success path now uses typed payload,
signed-request, and response IR nodes after guarded validation branches.
`create_order_ws/6` and `edit_order_ws/7` are
covered by signed order mutation IR rendering, with shared signed
request/response handling for order mutation results, typed success/request
IR nodes for the generated mutation method body, and shared edit-order payload
routing for spot cancel-replace versus contract modify-order. The
runtime-owned websocket API response handlers now record typed
success-response, status-error, and request-id waiter nodes in the manifest.
The
payload-helper renderer now owns ignored option keys, Binance camel-case key
mappings, and conditional-order option key detection. The manifest records
AST-derived sub-IR signals and structured nodes for signed requests, request
waiters, ordinary websocket API request modes, guarded validation requests,
websocket API payload shapes/fields, create-order payload delegation, spot
cancel-replace, contract modify-order, signed order mutation requests, cancel
payloads, and response filters. Stream, unsubscribe, and
cache helpers also expose structured nodes for watch mode, channel family,
private auth, cache, snapshot preload, and orderbook snapshot/delta behavior.
Auth/listenKey, parser, websocket API response, event handler, and top-level
dispatch methods also expose structured nodes in the same manifest format.
The payload helper renderer now consumes a
composed typed IR spec and delegates to smaller reusable render nodes for
order-id payloads, create-order request delegation, edit payload branch
selection, base order payloads, option mapping, and conditional-order
detection. Further improvements should keep moving renderer-specific internals
into explicit IR specs where a reusable pattern is clear.
- `auth`: websocket signature subscription, listenToken subscription, renewal,
authenticate routing, and listenKey keepalive are covered by shared
auth/listenKey IR renderers. The signature and listenToken subscription
methods now use typed request/result nodes for websocket API auth
subscription and subscription-id response handling. `authenticate/1` now uses
typed router branch specs rather than an opaque branch-line block, and
`keep_alive_listen_key/3` uses a typed keepalive request spec for supported
endpoint fetch versus unsupported types. The signer runtime now covers the
HMAC SHA256, RSA SHA256, and Ed25519 branches used by Binance Pro
`signParams`.
- `privateStream`: the now-covered private stream methods are generated through
shared private watch IR renderers. Their `watch_private_payload/3` calls are
now rendered through a typed private watch payload/request node. Balance,
positions, orders, my trades, and
my liquidations consume runtime private cache payloads. The single-symbol
private liquidation method is also covered by the shared single-from-multiple
wrapper IR. Spot/margin balance delta updates now accumulate against existing
cached free balances, and `Ccxt.Pro.PositionCache` stores symbol-indexed
position entries with event timestamps for parsed `watch_positions/4`
results. Private order cache entries use
`Ccxt.Pro.OrderCache` to merge TRADE execution metadata into the generated
order parser output, including cached trades and accumulated fee metadata,
and expose a symbol/order-id index view; private order handlers now record
`cache-engine:order-cache`, `cache-class:order-cache`,
`cache-semantic:symbol-id-index`, and
`cache-semantic:order-trade-merge` in the manifest. Private trade/liquidation list retention uses the reusable
`Ccxt.Pro.ArrayCache` runtime component with per-subscription `:cache_limit`.
Their handlers record `cache-engine:array-cache`,
`cache-class:array-cache`, and `cache-semantic:bounded-newest-first`, while
account handlers record `cache-engine:balance-position-cache`,
`cache-class:position-cache`, `cache-semantic:symbol-index`,
`cache-semantic:side-index`, and `cache-semantic:balance-delta`.
Private watch subscriptions can preload snapshot payloads into the runtime
cache and resolve the matching waiter immediately, matching the cache warm-up
role of `setBalanceCache`/`setPositionsCache`. Generated watch helpers now
support the TypeScript `fetchBalanceSnapshot` and `fetchPositionsSnapshot`
option branches with injectable snapshot fetchers for offline tests, optional
REST fetchers, and generated high-level REST fallback; the snapshot preload
helpers are now rendered through typed preload/fetch nodes that record
balance and positions snapshot fetch semantics in the manifest. Runtime-owned
cache helper methods also record snapshot targets, REST snapshot requests,
cache-engine nodes, cache-class nodes, and cache semantic nodes for balance,
positions, and orderbook snapshot caches.
The next private-stream
improvement is broader watcher wiring for the new indexed cache primitives
and broader live coverage where credentials permit it.
- `parser`: simple field-mapping parsers are covered by structured parser IR,
and ticker/trade are covered by conditional parser IR. Parser manifest nodes
now record conditional branch names, safe wrapper usage, and order/trade cache
merge semantics. The parser category has no remaining inline method template;
future parser work should improve field
parity with the TypeScript implementation where current Elixir behavior is
intentionally narrower.
- `cache`: public ticker-family streams, public trades/OHLCV, and private
order/trade/liquidation list retention now use `Ccxt.Pro.ArrayCache`. Order
book pre-snapshot delta buffers use the same bounded append semantics.
Balance cache updates handle both full account-position payloads and
incremental `balanceUpdate` deltas, while position cache entries preserve
event timestamps for parser output through `Ccxt.Pro.PositionCache`.
`Ccxt.Pro.CacheUpdates` owns CCXT Pro `allNewUpdates`,
`newUpdatesBySymbol`, nested id/side, and timestamp counter semantics without
changing default list-shaped watcher payloads. Public cached watch streams and
private list/order watchers can opt into that incremental window with
`newUpdates: true` or `new_updates: true`.
`Ccxt.Pro.OrderCache` merges private
trade metadata by order id before generated parsing. Private snapshot preload
is available through subscription-level `:snapshot_payloads`, and generated
private watch helpers can fetch balance/position snapshots before
subscription registration. Remaining cache work is any exchange-specific
specialized cache variant beyond Binance, plus future private payload shapes
that can safely expose incremental windows without changing non-list parser
inputs.
Current cache-owned TypeScript methods are explicitly represented by runtime
contracts in `pro_manifest.json`.
- `Pro IR`: continue splitting large renderer internals into smaller Pro IR
nodes as patterns stabilize, especially for deeper payload helper internals
and specialized cache-class variants.