CCXT adapter

One venue class covering 100+ crypto exchanges: Binance, Coinbase, Kraken, OKX, Bybit, KuCoin.

horizon.venues.ccxt_venue.CcxtVenue wraps any CCXT-supported exchange behind Horizon’s Venue Protocol. CCXT unifies 100+ exchanges under one API shape; CcxtVenue normalizes it further into VenueOrder, VenueFill, VenueCapital.

Each exchange is a separate CcxtVenue instance. venue_name is ccxt:<exchange_id>.

What you get

  • Any CCXT exchange. CcxtVenue(exchange_id="binance") or "coinbase", "kraken", "okx", "bybit", "kucoin", etc.
  • Unified auth. API key + secret for all; plus passphrase for Coinbase, OKX, KuCoin, Bitget, Crypto.com. Resolution via Secrets under <exchange_id>.key / .secret / .passphrase.
  • Sandbox toggle. sandbox=True calls exchange.set_sandbox_mode(True) when the exchange supports it (Binance Futures testnet, OKX demo, Kraken futures demo).
  • Unified order status. open, closed, canceled, expired, rejected. PartiallyFilled derived from filled vs. amount.
  • Multi-stablecoin balance. Sums USDT, USDC, USD, BUSD, FDUSD, DAI.
  • Fill dedup by trade id. fetch_my_trades emits each fill once.

Quickstart: Binance testnet

python
from horizon.venues.ccxt_venue import CcxtVenue

v = CcxtVenue(
    exchange_id="binance",
    api_key="...", api_secret="...",
    sandbox=True,
)
v.connect()
print(v.balance())

from horizon.types import OrderAction
order = v.submit(OrderAction.place(
    market_id="BTC/USDT", side="buy",
    quantity=0.001, price=65000.0,
    client_order_id="coid_1",
))

Coinbase, OKX, KuCoin, and Bitget additionally require a passphrase:

python
v = CcxtVenue(
    exchange_id="okx",
    api_key="...", api_secret="...", api_passphrase="...",
    sandbox=True,
)

Symbology

CCXT uses BASE/QUOTE: "BTC/USDT", "ETH/USDC", "SOL/USDT". Derivatives on some exchanges use BASE/QUOTE:SETTLE, e.g. "BTC/USDT:USDT" for USDT-margined perps.

Pass asset_class=AssetClass.Perp at construction when the venue trades futures to mark positions correctly.

Order types

TypeOrderAction.order_typeNotes
Limit"limit"Requires price.
Market"market"Price ignored.
Stop"stop"stopPrice passed in params.
Stop-limit"stop_limit"stopPrice + price.

TIF maps to GTC, IOC, FOK, PO (post-only). client_order_id passes as clientOrderId; post_only and reduce_only as boolean params. Exchange support varies; unsupported params are often silently dropped by CCXT.

Amend

CCXT’s edit_order is supported by a subset of exchanges (Binance, OKX, Kraken). When exposed, CcxtVenue.amend(order_id, new_quantity=..., new_price=...) calls it. Otherwise raises NotImplementedError.

WebSocket feed (ccxt.pro)

ccxt.pro became MIT-licensed in late 2023 and ships alongside ccxt. CcxtLiveFeed wraps its watch_* coroutines in a dedicated asyncio loop running on a daemon thread.

python
from horizon.data.live import CcxtLiveFeed, SubscriptionKind

feed = CcxtLiveFeed(
    exchange_id="binance",
    api_key="...", api_secret="...",
    fill_hook=v.push_fill,
)
feed.subscribe(["BTC/USDT"], SubscriptionKind.Trades)
feed.subscribe(["BTC/USDT"], SubscriptionKind.OrderBook)

Channels: watch_trades, watch_ticker, watch_order_book, watch_my_trades. Public channels work without credentials; watch_my_trades requires auth.

Rate limiting

CCXT throttles client-side via enableRateLimit=True (the adapter sets this). Each exchange class carries a published rateLimit (ms between calls). Raise alerts via the Alerting module on reject streaks.

Pinning

CCXT ships frequent releases and sometimes breaks API details on minor versions. Pin tightly in production:

ccxt==4.4.86         # or whatever you tested

Status by phase

CapabilityStatus
One class covers 100+ exchangesShipped
Submit, cancel, cancel_all, amend (where supported)Shipped
Positions, balance, open_ordersShipped
WebSocket via ccxt.proShipped
Sandbox toggleShipped
Per-exchange fee and liquidity parsingUnified; custom subclass to tune per exchange

Related