Skip to content

Latest commit

 

History

352 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

bourse

Elixir client library for eleven provider-authored exchange integrations generated from owned JSON specs via compile-time macros.

Status: the runtime support contract is the eleven venues listed below.

Installation

def deps do
  [
    {:bourse, "~> 0.7"}
  ]
end

Lighter private signing

Lighter private endpoints use a host-native helper compiled from the packaged Go and C sources. Install Go 1.23.1 and a C compiler, then build the helper in the consuming Mix project before making private Lighter calls or assembling a release:

mix ccxt.build_lighter_signer

Public Lighter market-data calls do not require the helper. The package does not ship prebuilt native binaries.

Quick Start

# Public market data (no credentials)
{:ok, exchange} = Bourse.Exchange.new("bybit")
{:ok, ticker} = Bourse.fetch_ticker(exchange, "BTC/USDT")

# Authenticated private call
{:ok, exchange} =
  Bourse.Exchange.new("bybit",
    api_key: System.fetch_env!("BYBIT_API_KEY"),
    secret: System.fetch_env!("BYBIT_API_SECRET")
  )

{:ok, balance} = Bourse.fetch_balance(exchange)

Supported Exchanges

Runtime support is closed and invariant across builds. Every supported venue loads one complete owned spec; the version-pinned CCXT corpus retained in the source repository is reference material for authoring and compatibility tests, not runtime support.

Exchange Role Markets
alpaca US-equity data and paper-trading venue US equities, news, FX rates
binance Perp-hedge venue (spot) Spot
binancecoinm Perp-hedge venue (COIN-M) Inverse perpetuals and dated futures
binanceusdm Perp-hedge venue (USDⓈ-M) Linear perpetuals
bybit Options venue Spot, linear/inverse perps, options
coinbaseexchange Public market-data venue ETH-USD and other spot products
deribit Options venue Options, futures, spot
derive Options venue (DEX) On-chain options on Optimism
hyperliquid Perp-hedge venue (DEX) On-chain perpetuals
lighter Perp-hedge venue (DEX) On-chain perpetuals
okx Options venue Spot, swaps, options
Bourse.Registry.exchanges()
#=> ["alpaca", "binance", "binancecoinm", "binanceusdm", "bybit",
#=>  "coinbaseexchange", "deribit", "derive", "hyperliquid", "lighter", "okx"]

Bourse.Exchange.new("kraken")
#=> {:error, {:unsupported_exchange, "kraken"}}

Migrating from ccxt_client

bourse is the breaking rename of the former ccxt_client package. Change the dependency name to :bourse, replace the CCXT module prefix with Bourse, move application configuration from :ccxt_client to :bourse, and rename app-specific CCXT_* environment variables to BOURSE_*.

Consumers that cannot migrate in the same release can remain on the final CCXT-namespaced line with {:ccxt_client, "~> 0.6.1"} until their namespace change lands. Every ccxt_client release is retired as renamed, so that dependency resolves with a deprecation warning and receives no further updates.

Two Contracts

bourse exposes two API surfaces with different stability guarantees.

Raw Endpoints — Stable

Per-exchange generated functions that pass through to the exchange API unchanged. Signing, rate limiting, circuit breakers, and transport are handled; the response body is returned as-is.

{:ok, exchange} = Bourse.Exchange.new("bybit", api_key: key, secret: secret)
{:ok, response} = Bourse.Bybit.public_get_v5_market_tickers(exchange, %{category: "spot"})

Recommended for agents, automated trading, and any consumer that wants to interpret exchange responses directly.

Unified API — Evolving

Standardized cross-exchange methods return the unified structs declared by each owned venue spec where that method is supported.

{:ok, ticker} = Bourse.fetch_ticker(exchange, "BTC/USDT")

Capability and routing introspection are available through Bourse.Exchange.has?/2 and each generated module's __unified_endpoints__/0.

WebSocket — Early

{:ok, ws} = Bourse.WS.connect(exchange, :public)
:ok = Bourse.WS.subscribe(ws, ["tickers.BTCUSDT"])
# Messages arrive at the calling process as {:websocket_message, decoded_map}
Bourse.WS.close(ws)

Thin wrapper over zen_websocket driven by authored per-exchange subscription and auth patterns. Callers pass exchange-native channel strings.

A :private connection authenticates before it is returned, so a socket you hold is one the venue accepted:

{:ok, ws} = Bourse.WS.connect(exchange, :private)
:ok = Bourse.WS.subscribe(ws, ["order"])

A rejected handshake closes the socket and returns the venue's reason rather than a connection that would deliver nothing.

The binance futures venues are the exception to "authenticate the open socket": their credential is a listen key issued over REST and carried in the URL, so connect/3 obtains it first and authenticate: false is refused — there is no later handshake to run. binanceusdm and binancecoinm issue separate keys for separate streams. derive has no authored handshake yet and connects without one.

Known Caveats

Consumer-facing gotchas not obvious from the API signatures. Full context in CLAUDE.md.

  • has?/2 is the derived callable surface, not the provider-support declaration. Provider support lives on venue_support/2 (and generated __venue_support__/0); that is where order-type flags such as createMarketOrder stay. A callable method with an incomplete mapping returns {:ok, %Bourse.RawResponse{}} instead of a normalized struct. Cross-check __unified_endpoints__/0 when choosing among multiple native endpoint families.

  • Signing is a closed authored contract. HMAC venues execute their authored deterministic recipes. Derive, Hyperliquid, and Lighter use bundled first-party signers. Consumers cannot register a reference-only exchange or inject a long-tail signer. Derive and Hyperliquid expect the EVM private key in credentials.secret; Lighter uses its documented API signing key.

  • Sandbox semantics differ. Credentialed venues resolve an authored sandbox; some swap the host (Alpaca paper trading), some add a header to the production host (OKX simulated trading), and some change signed payload material (Lighter also switches chain id). coinbaseexchange is deliberately public-only and uses its production public host. A venue's sandbox may additionally be read-only or region-restricted for signed writes, which surfaces as a business error rather than a transport failure.

Discovery

Bourse.describe()                      # Library overview
Bourse.describe(Bourse, :fetch_ticker) # Method signature + params + errors + return shape
Bourse.MCP.tools()                     # MCP tool definitions for agent autodiscovery
Bourse.Registry.exchanges()            # List the eleven runtime exchange ids

Per-exchange introspection (generated on every exchange module):

Bourse.Bybit.__spec__()               # Raw spec map (describe output)
Bourse.Bybit.__endpoints__()          # List of %{path, method, authenticated, weight, ...}
Bourse.Bybit.__signing__()            # %{pattern: :hmac_sha256_headers, config: %{...}}
Bourse.Bybit.__unified_endpoints__()  # Unified-method → endpoint-config mapping

Documentation

  • CHANGELOG.md — completed tasks and known limitations
  • BUGS.md — the consumer bug queue: what has been reported, what is already fixed, and where to file
  • CLAUDE.md — architecture, design decisions, internal conventions
  • CONTRIBUTING.md — how to land code and authored-spec fixes

Authoring Exchange Changes

This section is for work inside the source repository. The authoring and audit Mix tasks named below are repo-internal and are not part of the published package; only mix ccxt.build_lighter_signer ships to consumers.

For a supported venue, author the owned spec or normalization slice against the provider's API behavior and provider-owned documentation, then add the branch it covers to priv/venues/<venue>/authority/rest_read_contract.json and keep mix ccxt.verify_rest_read_contracts green — that lane calls the venue's own host, so a claim is graded by the provider, not by a stored response. CCXT and ccxt-distill are a pinned third-party extraction; they do not establish venue semantics.

Adding an unsupported venue is hand-authored work — there is no promotion task. It means writing the owned document, registering the venue, and earning live REST-read contract coverage for every critical operation; copying a reference spec into the repository does not make it runtime-supported. See CONTRIBUTING.md and docs/authored-specs.md.

Development

Checks for work inside the source repository. The suite is provider-live: it calls the venues' testnet/demo/public hosts and raises when their credentials are absent, so there is no offline run.

mix deps.get
mix test.json --quiet
mix ccxt.verify_rest_read_contracts
mix dialyzer.json --quiet
mix credo --strict --format json

License

MIT — see LICENSE.

About

Elixir client for ten provider-authored cryptocurrency exchange integrations (Alpaca, Binance spot/COIN-M/USD-M, Bybit, Deribit, Derive, Hyperliquid, Lighter, OKX)

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages