CCXT Pro Elixir Target

Copy Markdown View Source

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:

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:

npm run checkElixir

Pro generation stability:

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:

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:

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:

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 helperWatch sourceUnit coverageLive coverageNotes
stream_ticker/2,3watch_ticker/2fake repeated updatesprod public livePublic stream; Enum.take/2 receives repeated ticker updates.
stream_trades/4,5watch_trades/4fake repeated updatesprod public livePublic stream; no funds risk.
stream_order_book/3,4watch_order_book/3fake repeated snapshotsprod public livePublic stream; uses generated order-book watch path.
stream_ohlcv/5,6watch_ohlcv/5fake repeated candlesprod public livePublic stream; no funds risk.
stream_balance/1,2watch_balance/1fake private cache updatesprod private read-only smokePrivate stream; live can report credential, permission, or active subscription state.
stream_positions/4,5watch_positions/4fake private cache updatesprod private read-only smokePrivate futures stream; live depends on futures permission and account state.
stream_orders/4,5watch_orders/4fake repeated order eventsgated prod live order eventLive proof uses CCXT_PRO_ENABLE_PROD_ORDER_LIVE=true, a non-marketable order, and immediate cancel.
stream_my_trades/4,5watch_my_trades/4fake repeated trade eventsmanual-fill onlyNon-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.