Current section
Files
Jump to
Current section
Files
ccxt_client
CHANGELOG.md
CHANGELOG.md
# Changelog
Completed roadmap tasks. For upcoming work, see ROADMAP.md.
---
## v0.3.0 (2026-02-24)
### Highlights
- **93% compile-time reduction** — New `Dispatch.call/5` shared runtime dispatcher replaces per-endpoint inline code generation. Binance module: 28.1s → 1.05s. Full suite: 35.7s → 2.5s.
- **CCXT.Health module** — Stateless exchange health checks: `ping/1`, `latency/1`, `status/2`, `all/1` (concurrent bulk check).
- **OHLCV timeframe translation** — Unified timeframes (`"1h"`, `"1d"`) automatically translated to exchange-native format for both REST and WebSocket.
- **Normalization QA sweep** — 54 contract tests across 5 tier1 exchanges × 3 endpoints. Parser coverage tests with hard assertions for tier1. No bugs found.
- **OrderBook metadata fix** — `timestamp`, `symbol`, `nonce`, `datetime` now correctly populated via fallback key lookups for exchanges using non-standard keys (Bybit, OKX, etc.).
- **CCXT v4.5.40 sync** — Updated specs and extractor data, including new `parseADLRank` method and Deribit funding rate improvements.
### Breaking Changes
None. All changes are additive or internal.
### New Features
- `CCXT.Health` — `ping/1`, `latency/1`, `all/1`, `status/2` for exchange health monitoring
- `CCXT.Generator.Dispatch` — Shared runtime dispatcher with pre-computed `@endpoint_configs`
- `CCXT.Generator.Helpers.translate_timeframe/2` — REST timeframe translation in `Dispatch.call/5` pipeline
- `CCXT.Generator.Helpers.translate_timeframe_value/2` — WS timeframe translation in generated OHLCV subscriptions
- Pipeline module — Single source of truth for default pipeline config (fixes normalization as path dep)
- `MappingCompiler` — `boolean_derivation` category and `safe_fn` override support
- `ResponseParser` — `{:bool_enum, true_value, false_value}` coercion for boolean-derived fields
- `ResponseCoercer` — Auto-injects `"info"` key before parsing for raw field population
### Bug Fixes
- OrderBook metadata (`timestamp`, `symbol`, `nonce`, `datetime`) nil for non-standard exchange keys
- OrderBook levels returning strings instead of floats
- Pipeline default not loaded when compiled as path dependency
- Dialyzer warnings from `Mix.env()` runtime call in `CCXT.Defaults`
- Doc warnings from ex_doc-incompatible link syntax
- Param precedence: `default_params` no longer overrides function args when `param_mappings` renames keys
### Performance
- Compile time: 35.7s → 2.5s (93% reduction)
- Binance module: 28.1s → 1.05s
- Root cause: 98% of .beam size was inline code per endpoint
- `strip_dead_weight/1` removes extraction-only data before `Macro.escape`
### Syncs
- CCXT v4.5.39 → v4.5.40
- New `parseADLRank` parse method (Binance, Bitmex, Bybit, Htx)
- Deribit `parseFundingRate` improvements (nextFundingRate, nextFundingTimestamp, nextFundingDatetime)
- Param precedence fix and symbol defaults removed from specs
- Spec cost/weight sync across all tier1 exchanges
- 107 exchanges bundled (synced for publish)
### Tests
- 54 normalization contract tests (5 exchanges × 3 endpoints + order book)
- 20 parser coverage tests with tier1 hard assertions
- 29 dispatch tests (config building, param merging, path interpolation, auth, OHLCV)
- 18 timeframe translation tests (REST + WS)
- 13 OrderBook metadata tests
- 11 Health module tests
---
## Fix: OrderBook metadata fields nil for non-standard keys (2026-02-24)
**What was done:**
- **Fallback key lookups in `OrderBook.from_map/1`** — `timestamp`, `symbol`, `nonce`, and `datetime` were all nil for exchanges using non-standard keys (e.g., Bybit: `"ts"`, `"s"`, `"u"`). Added `Safe.timestamp/2`, `Safe.string/2`, `Safe.integer/2` with fallback key lists.
- **`derive_datetime/1`** — New private helper derives ISO 8601 datetime string from millisecond timestamp via `DateTime.from_unix/2`.
- **Fallback key coverage**: `timestamp` ← `["timestamp", "ts", "T"]`, `symbol` ← `["s", "instrument_name", "instId"]`, `nonce` ← `["u", "lastUpdateId", "change_id", "seq"]`
- **Contract test assertions strengthened** — `assert_order_book_contract/1` now checks all metadata fields (timestamp, symbol, nonce, datetime) for correct types.
- **Bybit-specific contract test** — Verifies all metadata fields are populated from non-standard keys through the full normalization pipeline.
**Safe from sync:** `lib/ccxt/types/order_book.ex` is runtime code owned by this repo — not overwritten by `mix ccxt.sync`. When the extractor improves upstream, fallbacks become no-ops.
**Tests:** 13 new tests (12 unit in `order_book_test.exs`, 1 contract in `normalization_contract_test.exs`). 81 total, 0 failures.
**Live verified:** Bybit `fetch_order_book("BTC/USDT:USDT")` → `timestamp=1771929865793`, `symbol="BTCUSDT"`, `nonce=21077445`, `datetime="2026-02-24T10:44:25.793Z"`. Deribit `fetch_order_book("BTC-PERPETUAL")` → all fields populated (no regression).
---
## ccxt_ex Sync (2026-02-24)
**What was synced:**
- **CCXT v4.5.40** — Updated from v4.5.39
- **Deribit `parseFundingRate` improvements** — `nextFundingRate`, `nextFundingTimestamp`, `nextFundingDatetime` now populated (were `undefined`). `fundingRate` now uses `safeNumber2` for fallback key support.
- **New WS handler mappings** — `push.funding.rate` and `funding_data` channel handlers added
- **Parse methods** — 1542 → 1544 methods across 110 exchanges
- Updated extractor data: `ccxt_emulated_methods.json`, `ccxt_error_codes.json`, `ccxt_parse_methods.json`, `ccxt_symbol_formats.json`, `ccxt_ws_handler_mappings.json`
- Updated specs: Binance, Bitmex, Coinbase Exchange, Deribit, Gate, Htx, OKX
---
## Fix: Pre-commit Blocking Issues (2026-02-23)
**What was done:**
- **Dialyzer PLT** — Added `plt_add_apps: [:mix]` to `mix.exs` project config. Resolves 8 warnings for legitimate Mix usage in generator and mix task code.
- **Runtime `Mix.env()` removed** — `CCXT.Defaults.retry_policy/0` no longer calls `Mix.env()` at runtime (architectural violation, breaks releases). Now uses `Application.get_env/3` with `:safe_transient` default.
- **Per-environment config** — Added `config/test.exs` (`retry_policy: false`), `config/dev.exs`, `config/prod.exs`, and `import_config` in `config/config.exs`.
- **Doc warnings fixed** — Removed ex_doc-incompatible link syntax: ROADMAP.md link → plain text (not in `extras` list), backtick-quoted Pipeline module → plain text (has `@moduledoc false`).
**Verified:** 0 dialyzer warnings, 0 doc warnings, 2795 tests passed.
---
## ccxt_ex Sync (2026-02-23)
**What was synced:**
- **New `parseADLRank` parse method** — Auto-deleverage rank parsing added upstream for Binance, Bitmex, Bybit, Htx. Returns ADL type with `rank`, `rating`, `symbol`, `timestamp` fields. Exchange-specific field mappings (`adlRisk`, `adlQuantile`, `deleveragePercentile`, `adlRankIndicator`, `adl_risk_percent`).
- Updated extractor data: `ccxt_emulated_methods.json`, `ccxt_error_codes.json`, `ccxt_parse_methods.json`, `ccxt_symbol_formats.json`
- Updated specs: Binance, Bitmex, Bybit, Coinbase Exchange, Gate, Htx
---
## Task 228: OHLCV Timeframe Translation (2026-02-23)
**Completed** | [D:3/B:7 → Priority:2.33]
**What was done:**
- **`Helpers.translate_timeframe/2`** — Translates `:timeframe` value in a params map to exchange-native format (e.g., `"1h"` → `"60"` for Bybit). Called by `Dispatch.call/5` for all OHLCV endpoints, before param mapping. Empty-map guard for fast-path no-op.
- **`Helpers.translate_timeframe_value/2`** — Translates a single timeframe string. Called by generated WS OHLCV subscription functions. Simple `Map.get` with passthrough fallback.
- **`Dispatch.call/5` integration** — Timeframe translation runs after timestamp conversion and before path interpolation. Applied to all 4 OHLCV endpoints: `fetch_ohlcv`, `fetch_index_ohlcv`, `fetch_mark_ohlcv`, `fetch_premium_index_ohlcv`.
- **WS generator integration** — `generate_watch_functions/2` accepts optional `timeframes` map. OHLCV subscription functions call `translate_timeframe_value/2` before channel formatting.
- **`ohlcv?` flag expanded** — Now covers all 4 OHLCV endpoint variants (previously only `fetch_ohlcv`).
**Tests:** 8 new unit tests in `helpers_test.exs` (translate/passthrough/empty map for both functions). 5 tests in `dispatch_test.exs` (REST translation, passthrough, non-OHLCV unaffected). 5 tests in `generator_functions_test.exs` (WS translation, passthrough, default timeframe, empty map, non-OHLCV unaffected).
---
## Task 227: Compile-Time Performance — Shared Runtime Dispatch (2026-02-23)
**Completed** | [D:6/B:9 → Priority:1.5]
**What was done:**
- **`CCXT.Generator.Dispatch`** — New shared runtime dispatcher. Generated endpoint functions are now thin wrappers calling `Dispatch.call/5` instead of inlining ~30 lines of pipeline logic per endpoint (87-99 times per exchange). Pipeline: param merging → emulation dispatch → path interpolation → prefix handling → HTTP execution.
- **`build_configs/2` / `build_config/3`** — Called at compile time to pre-compute per-endpoint configuration (path style, reversed mappings, prefix strategy). Stored as `@endpoint_configs` module attribute, accessed via `__endpoint_config__/1`.
- **`strip_dead_weight/1`** — Removes extraction-only data (`parse_methods` ~170KB, `handle_errors_source` ~4KB) from specs before `Macro.escape`. Spec `.exs` files remain complete; stripping happens only at compile time.
- **Introspection deduplication** — Map-typed introspection functions (e.g., `__ccxt_raw_endpoints__/0`) now reference `@ccxt_spec` directly instead of each having its own `Macro.escape` literal. Only scalars (comment, certified, pro, dex, precision_mode) remain inlined.
- **Endpoints.ex simplification** — Removed ~250 lines of per-endpoint inline code generation (path interpolation, prefix building, client opts assembly, OHLCV conversion). All moved to `Dispatch`.
- **Updated extractor data** — `ccxt_emulated_methods.json`, `ccxt_error_codes.json`, `ccxt_parse_methods.json`, `ccxt_symbol_formats.json` synced from ccxt_ex.
- **Updated specs** — Binance, Bitmex, Coinbase Exchange, Deribit, Gate, Htx, OKX specs refreshed.
**Performance impact:**
- Before: 35.7s wall clock, Binance alone 28.1s
- After: 2.5s wall clock, Binance 1.05s — **93% reduction**
- Root cause: 98% of .beam size was inline code per endpoint, not spec data
**Tests:** 29 new tests in `dispatch_test.exs` (config building, param merging, path interpolation, prefix strategies, auth, OHLCV, emulation, coercer). 3 new tests in `generator_test.exs` for `__endpoint_config__/1`.
---
## ccxt_ex Sync (2026-02-22)
**What was synced:**
- **Param precedence fix:** New `strip_overridden_defaults/3` in `CCXT.Generator.Helpers` prevents `default_params` from overriding function args when `param_mappings` renames keys (e.g., `symbol` → `instId` on OKX). Called in generated endpoint functions before the merge chain.
- **Symbol defaults removed from specs:** Hardcoded `"symbol"` entries removed from `default_params` across all 9 tier1 exchange specs (binance, bitmex, bybit, coinbaseexchange, deribit, gate, kraken, kucoin, okx). Symbol now flows exclusively through `param_mappings`.
- **Updated extractor data:** `ccxt_parse_methods.json` (expanded parse method coverage), `ccxt_error_codes.json`, `ccxt_symbol_formats.json`, `ccxt_emulated_methods.json`
**Verified:** 2947 passed, 0 code failures (15 failures + 178 invalid all credential-related)
---
## Phase 5: Health & Monitoring
### CCXT.Health Module
**Completed** | `Health.latency/1` [D:1/B:7 → 7.0], `Health.ping/1` [D:2/B:8 → 4.0], `Health.all/1` [D:3/B:8 → 2.67], `Health.status/2` [D:3/B:7 → 2.33]
**What was done:**
- `CCXT.Health` module at `lib/ccxt/health.ex` — stateless, one-shot exchange health checks
- `ping/1` — checks exchange reachability via `fetch_time/1`, returns `:ok` or `{:error, Error.t()}`
- `latency/1` — wall-clock round-trip measurement in milliseconds (float), wraps `fetch_time/1` with `System.monotonic_time()`
- `all/1` — concurrent bulk health check via `Task.async_stream/3` with `on_timeout: :kill_task`, returns `%{exchange => :ok | {:error, term()}}`
- `status/2` — composite snapshot combining reachability, latency, and `CircuitBreaker.status/1`
- Private `resolve_module/1` validates exchange atom → module with `__ccxt_spec__/0` check
- 11 tests (6 unit, 5 integration) covering error paths, concurrency, and real exchange calls
**Design decisions:**
- Wall-clock measurement around `fetch_time` (not Finch telemetry) — simpler, gives total round-trip including signing and rate-limit wait
- No struct for status result — plain map keeps the API simple and extensible
- `resolve_module/1` checks `__ccxt_spec__/0` export to distinguish real exchange modules from other `CCXT.*` modules
---
## Task 225: Normalization QA Sweep (2026-02-22)
**Completed** | [D:5/B:10 → Priority:2.0]
**What was done:**
- **Normalization contract tests** (`test/ccxt/normalization_contract_test.exs`) — 54 tests covering 5 tier1 exchanges × 3 endpoints (ticker, trade, order) + 2 order book tests (Bybit with parser mapping, baseline without)
- **Parser coverage tests** (`test/ccxt/parser_coverage_test.exs`) — 20 tests: tier1 hard assertions, cross-validation (method_schemas ⊆ type_modules), coverage inventory, known exceptions
- **Contract assertions**: numeric fields are `number() | nil` (never strings), enum fields (`side`, `status`, `taker_or_maker`) are atoms (never strings/booleans), `timestamp` is `integer() | nil`, `raw` is always a map
- **List-shape contracts**: `:trades` and `:orders` coerce lists of raw maps to `[%Trade{}]` and `[%Order{}]`
- **Exchange-specific coercion tests**: Binance bool→side, Bybit isMaker→taker_or_maker, Deribit liquidity→taker_or_maker, string_lower normalization, string timestamp→integer
- **Known exceptions documented**: `:order_book` (unified keys), `:balance` (nested maps)
**Triage result:** No bugs found — pipeline works correctly for all tier1 exchanges.
**Coverage snapshot:** Binance/Bybit 76.7%, OKX/Gate 63.3%, Htx 53.3%, Deribit 43.3%, Kucoin 40%, Bitmex 36.7%, Kraken 26.7%, Coinbase 20%
**Run with:** `mix test.json --quiet --only normalization` and `mix test.json --quiet test/ccxt/parser_coverage_test.exs`
---
## Fix: OrderBook levels return strings instead of floats (2026-02-21)
**What was done:**
- **Level coercion** — `OrderBook.from_map/1` now coerces string bid/ask level values to floats via `Safe.to_number/1`. Previously, `fetch_order_book(normalize: true)` returned `[["67795.8", "1.636265"]]` instead of `[[67795.8, 1.636265]]`. Root cause: MappingCompiler's `type_to_coercion/1` fell through to `:value` (passthrough) for `[[number() | nil]]` schema type.
- **Raw field fix** — `book.raw` now uses precedence chain `raw || info || map` instead of always falling back to the enriched parsed map. Preserves original exchange payload.
- **Test update** — WS normalizer test updated to assert coerced floats instead of raw strings.
**Related to:** Task 225 (Normalization QA Sweep) — proactive fix before sweep
---
## Pipeline default for deps + maybe_coerce warning (2026-02-21)
**What was done:**
- **Pipeline default hardcoded** — CCXT.Pipeline module provides single source of truth for default pipeline config. Both REST (`CCXT.Generator`) and WS (`CCXT.WS.Generator`) generators use `CCXT.Pipeline.default()` as fallback instead of `[]`. Fixes normalization not working when ccxt_client is compiled as a path dependency (dep config files not loaded by parent app).
- **maybe_coerce warning** — `maybe_coerce/5` nil-coercer path now logs actionable warning with `response_type` when `normalize: true` (the default) is active but no coercer is configured.
**Verified:** 2649 tests, 0 failures (398 excluded)
---
## Task 224: Normalization — boolean_derivation, safe_fn, info injection (2026-02-21)
**What was done:**
- **MappingCompiler: `boolean_derivation` category** — Compiles P1 `boolean_derivation` entries to `{:bool_enum, true_value, false_value}` tagged tuple coercion (34 occurrences across exchanges, e.g., Binance `side` from `"m"` boolean)
- **MappingCompiler: `safe_fn` override** — `resolved_safe_accessor` entries with `safe_fn` field now override schema-derived coercion (e.g., Bybit `side` → `:string_lower`, `timestamp` → `:integer`)
- **ResponseParser: `{:bool_enum, tv, fv}` coercion** — New `apply_coercion/3` clause with dedicated `find_boolean/2` that correctly handles `false` as a found value (unlike `Safe.value` which treats `false` as falsy)
- **ResponseCoercer: info injection** — `coerce_single/3` injects `"info" => data` before parsing so `from_map`'s `:raw`/`:info` field gets populated. Doesn't overwrite existing `"info"` or `:info` keys
- **Helpers: capitalized side normalization** — Added `"Buy"`, `"Sell"`, `"Long"`, `"Short"` clauses to `normalize_side/1`
- **Typespecs** — Widened instruction type across 7 locations to accept tagged tuple coercion
**ccxt_ex:** Linked to Tasks 221-223 (P1 analysis improvements)
**Verified:** 2645 tests, 0 failures (398 excluded)
---
## v0.2.1 — Full Exchange Sync (2026-02-21)
**107 exchanges** now bundled (up from 10 tier1/tier2). Synced from CCXT v4.5.39.
- Full exchange sync: 97 new exchange modules, specs, and test files (433 files total)
- New WS methods: `watch_position_for_symbols`, `watch_private_multiple`, `watch_public`, `watch_multiple`, `watch_public_multiple`
- Updated extractor data: symbol formats, emulated methods, error codes, parse methods
- Fixed version references in README.md and llms.txt (`~> 0.1` → `~> 0.2`)
- Moved `TRADING_BACKLOG.md` to quantex as `ROADMAP.md`
**Verified:** 6650 tests, 3852 passed, 0 code failures (all failures credential-related)
---
## v0.2.0 — Trading Modules Removed (Breaking)
**Breaking:** `CCXT.Trading.*` modules (Funding, Greeks, Risk, Basis, Sizing,
Volatility, Options, Portfolio, PowerLaw, and helpers) have been removed.
These trading analytics have been extracted to a separate library.
ccxt_client is now a focused exchange-access library — HTTP, WebSocket,
signing, symbol normalization, and types only.
No changes to exchange API functionality.
---
## Spec Cost Sync + Test Improvement (2026-02-20)
**What was done:**
- **Spec cost/weight sync:** Updated rate limit costs across all 10 Tier 1 exchange specs (binance, bitmex, bybit, coinbaseexchange, deribit, gate, htx, kraken, kucoin, okx) from upstream CCXT
- **Test improvement:** `test_since_value/2` in `PublicEndpointsTest` now derives `since` requirements from endpoint spec metadata (`required_params`, `param_mappings`, path patterns) instead of hardcoding exchange IDs — resolves inline TODO
**Verified:** 3380 tests, 0 code failures (15 failures + 178 invalid all credential-related)
---
## ccxt_ex Sync (2026-02-20)
**What was synced:**
- **Bug #1 resolved:** `CCXT.Deribit.fetch_trades/1` now exists (optional param arity fix, Task 176)
- **Bug #2 resolved:** Deribit WS balance subscription builds correct channel (Task 177)
- **Bug #3 resolved:** Deribit WS ticker subscription includes interval suffix (Task 177)
- **Adapter refactor:** Monolithic generator functions decomposed into focused sub-generators (`_ast` naming convention)
- **Deribit spec updates:** `response_transformer` added to ~12 endpoints, `auth_required: true` on all private WS channel templates, updated symbol format samples, `api_sections` map
- **Generator updates:** Pipeline-based coercer/parser decoupling, version-override path prefix handling, dynamic app name resolution
**Verified:** 3380 tests, 0 code failures (15 failures + 178 invalid all credential-related)
---
## Phase 1: Response Quality & Documentation
### Feature #2: Symbol Precision Metadata
**Completed** | [D:4/B:8 → Priority:2.0]
**What was done:**
- `CCXT.MarketPrecision` module at `lib/ccxt/market_precision.ex`
- `from_market/2` — builds precision struct from a single market map
- `from_markets/2` — builds precision map for all symbols on an exchange
- `tradingview_price_format/1` — TradingView-compatible price format
- `decimal_places/1`, `increment_from_decimals/1` — precision mode conversions
- Handles all 3 CCXT precision modes: TICK_SIZE, DECIMALS, SIGNIFICANT_DIGITS
**Linked to:** ccxt_ex Task 178 (Normalized Market Precision Metadata)
---
## Pre-existing (included in v0.1.1)
The following roadmap items were found to already exist in the initial release:
- **`Symbol.normalize/2` + `Symbol.denormalize/2`** (Phase 17) — `CCXT.Symbol` provides bidirectional normalization
- **`Symbol.to_exchange_id/3` + `Symbol.from_exchange_id/3`** (Phase 17) — pattern-based exchange format conversion
- **`RateLimit` visibility** (Phase 20) — `CCXT.HTTP.RateLimiter` provides per-credential sliding window rate tracking
---
## v0.1.1 — Initial Release
- Elixir client for 110+ cryptocurrency exchanges
- 7 signing patterns covering 95%+ of exchanges
- Unified API: fetch_ticker, fetch_order_book, create_order, fetch_balance, etc.
- WebSocket support with automatic reconnection
- Type-safe response structs (Ticker, Order, Balance, Position, etc.)
- Bidirectional symbol normalization (unified ↔ exchange format)
- Circuit breaker and rate limiting
- Selective compilation (configure which exchanges to compile)
Generated from [ccxt_ex](https://github.com/ZenHive/ccxt_ex), extracted from
CCXT's 7+ years of accumulated exchange knowledge.