All notable changes to this project are documented here.
The format follows Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
0.2.0 - 2026-08-06
Fixed
Findings from the 2026-08-04 live venue sweep, which compared this client against CCXT JS endpoint by endpoint on the same testnets. Each was generalized to the defect class rather than patched per venue.
- Unified reads returned raw venue envelopes, collapsed multi-row responses to a single row, or keyed results by un-normalized venue symbols. A whole-surface contract guard now holds every unified read to the same shape.
fetch_canceled_orders/2,fetch_closed_orders/2andfetch_orders/2returned identical, unfiltered rows.- Unified read parsing raised on legitimate venue responses instead of returning a typed error.
- Authored enum slices rejected real venue values; a single unmapped order status disabled four Hyperliquid read methods.
- Field maps were present but inert: populated venue fields arrived as
nil, and one scalar parse dropped the year from a timestamp. - Time-window request params (
since,limit) did not reach the venue on every venue that accepts them. Bourse.WS.subscribe/2reported success when the venue rejected the subscription, and its return shape varied by venue.- Funding cadence came from an authored constant rather than observed venue data — Deribit was recorded as 8h for an hourly venue, overstating funding roughly eightfold in anything that multiplied by it.
Reported by consumers against the published package:
Bourse.Testnetexited the calling process when the registry was not running. Because the registry is deliberately not an application child, a consumer callingregister_all_from_env/1from its owntest_helper.exslost its entire suite before a single test ran. Writes now return{:error, :not_started}and reads raise anArgumentErrornamingstart_link/1, instead of aGenServerexit and an opaque ETS badarg.- Derive's ticker mapped
high,low,changeandpercentagefrom astatsobject the venue publishes on neither its demo nor its production host, and documents nowhere — an inherited carve whose only surviving evidence was a January 2025 sample. The four fields are recorded as absent, registered as carveC-T560d.
Packaging and attribution, found while auditing the extraction:
- The tarball shipped Bourse.Spec.Promotion and its two helpers — 1,049 lines
of repo-internal tooling that reads deliberately unpackaged reality manifests,
so in a consumer project it could only fail on missing files.
Path.wildcard/1yields directory entries, Hex expands a listed directory recursively, andlib/bourse/specmatched no exclusion prefix. Directory entries are now dropped outright rather than excluded one prefix at a time, and the guard asserts against the built tarball, where the expansion is actually visible. - The tarball also shipped the oracle / recording / replay / drift cluster, which
reads
test/fixtures/**andpriv/reference_cache/— neither of them packaged. Two of those modules namedReq.Plug, which exists only from req 0.7 and only behind theonly: [:dev, :test]:plugdependency, so a consumer resolving~> 0.6.1compiled the package with undefined-module warnings. The cluster is now excluded from both the tarball and hexdocs, and a new gate scans every shipped module's AST for references to dependencies a consumer may not have.
Changed
Bourse.Testnetno longer starts as a child ofBourse.Application. It is a credential registry for sandbox testing and has no place in a consumer's always-on supervision tree; callers that want it start it explicitly.
Added
Bourse.Testnet.started?/0, so a caller can ask whether the registry is running rather than discover it from a failure.NOTICE, shipped in the package. The authored venue specs still carry method and return descriptions taken verbatim from CCXT,docs.ccxt.comlinks included; CCXT is MIT, whose terms require the copyright notice to travel with that text. No such notice was ever tracked, in this repository or its predecessor — publishing the package is what made the omission consequential.A CI workflow running the offline gate — format, warnings-as-errors, Credo, Doctor, Sobelow, the offline suite, the reality oracle, the documentation claims,
deps.auditand Dialyzer. Until now those ran only on the maintainer's host and through dispatch review, so an outside pull request and a fresh clone had no gate at all.This repository.
bourseis extracted from the working repo it grew up in, which stays behind as the private authoring workbenchbourse-workbench. Carried over: the client (Bourse.Exchange/Dispatch/HTTP/Signing/Symbol/Unified/WSplus the unified response structs), the ten authored runtime specs, the verification layer (the ccxt.oracle_gate Mix task, the recorded response and accepted-request evidence, live drift checking), the spec-authoring and venue-promotion tooling, the authority corpus and its validators, and the trading domain layer.Left in the workbench: the complete version-pinned CCXT reference corpus (110 documents), the classification tooling and corpus-wide audits that can only be answered against it, and the task roadmap with its CHANGELOG gate. This repository carries a 15-document reference slice covering the supported venues, which its own offline tests read; both manifests pin the same upstream revision, so the two copies are checkable rather than silently divergent.
0.1.0 - 2026-08-03
First hex.pm release as bourse, succeeding the retired ccxt_client package.
Published before this repository existed, from the tree that is now the private
bourse-workbench history — there is no v0.1.0 tag here.
Added
- Ten provider-authored venue integrations —
alpaca,binance,binancecoinm,binanceusdm,bybit,deribit,derive,hyperliquid,lighter,okx— each generated at compile time from one complete owned JSON spec. Runtime support is a closed set: constructing any other exchange fails withunsupported_exchange. - Two API surfaces. Raw per-exchange endpoint functions pass exchange responses
through unchanged with signing, rate limiting, circuit breaking, and transport
handled. The unified
BourseAPI adds cross-exchange methods returning normalized structs, with bang variants and machine-readable descriptions. - Signing for every supported venue, including first-party signers for the three
DEX venues: EIP-712 for Derive, msgpack action hashing for Hyperliquid, and a
zk-Schnorr Port helper for Lighter.
Bourse.Signingdispatches the authored recipes; no signing behavior is inferred at runtime. - WebSocket support via
Bourse.WS— a thin wrapper overzen_websocketdriven by authored per-exchange subscription and auth patterns. - Discovery and agent integration:
Bourse.describe/0-2for method signatures, parameters, errors, and return shapes, plusBourse.MCP.tools/0for MCP tool autodiscovery. - Operational layers: per-credential weighted rate limiting with response-header
feedback, per-exchange circuit breakers, telemetry events, and sandbox
resolution for all ten venues via
Bourse.Exchange.new/2.
Changed
- Renamed from
ccxt_clienttobourse, with theCCXT.*namespace becomingBourse.*. See the migration notes in the README. - Interpretive judgment moved out of the runtime and into the authored specs. The heuristic signing classifier, symbol pattern inference, and long-tail fallback are removed; the runtime reads authored fields instead of guessing.
- Correctness is verified against recorded venue reality — registered response recordings, accepted-request goldens, and recorded exchange errors — rather than against third-party client behavior.
Fixed
- Alpaca
fetch_ohlcv/3-4had no working call shape: the default path returned an empty list, failing silently as success, and the documentedsinceoption produced an HTTP 400. The authored request slice now emits a real dated window.
Packaging
- The published package carries the library and the ten authored specs. The
repo-internal authoring and audit tooling is not shipped;
mix ccxt.build_lighter_signer, the prerequisite for private Lighter calls, is the one task consumers receive.