Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Crypto

The integration with crypto exchanges is done by using the CCXT package. It provides both market data as well as broker functionality from these exchanges.


Supported Exchanges

CCXT supports 100+ exchanges. Some popular ones include:

ExchangeClass
Binanceccxt.binance()
Coinbaseccxt.coinbase()
Krakenccxt.kraken()
Bybitccxt.bybit()
OKXccxt.okx()
import ccxt

# List first 10 exchanges
print(ccxt.exchanges[:10])
['alpaca', 'apex', 'aster', 'backpack', 'bequant', 'bigone', 'binance', 'binancecoinm', 'binanceus', 'binanceusdm']

Market Data

CCXT provides access to historical OHLCV data, order books, and live ticker feeds from hundreds of cryptocurrency exchanges.

The following example shows a back test over historical data gotten from Binance exchange. This uses a public API, so no trading account with Binance is required for this example.

import ccxt
import roboquant as rq
from roboquant.feeds.crypto import CryptoFeed
from roboquant.common.monetary import USDT

rq.set_light_style()

exchange = ccxt.binance()
# exchange = ccxt.kraken()  # or any other exchange supported by ccxt
feed = CryptoFeed(exchange, "BTC/USDT", "ETH/USDT", start_date="2020-01-01 00:00:00", interval="1d")

strategy = rq.strategies.EMACrossover()
trader = rq.traders.FlexTrader(size_fractions=4, max_order_pct=0.2, max_position_pct=0.5, shorting=True)
broker = rq.brokers.SimBroker(deposit=10_000@USDT)
account = rq.run(feed, strategy, trader=trader, broker=broker)
print(account)

for asset in feed.assets():
    feed.plot(asset, trades=account.trades)
ERROR:roboquant.feeds.crypto:Error retrieving symbol=BTC/USDT
Traceback (most recent call last):
  File "/home/runner/work/roboquant.py/roboquant.py/.venv/lib/python3.12/site-packages/ccxt/base/exchange.py", line 687, in fetch
    response.raise_for_status()
  File "/home/runner/work/_temp/setup-uv-cache/archive-v0/JZUQWwThcZ-Yrkeu/lib/python3.12/site-packages/requests/models.py", line 1167, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 451 Client Error:  for url: https://api.binance.com/api/v3/exchangeInfo

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/runner/work/roboquant.py/roboquant.py/roboquant/feeds/crypto.py", line 52, in __init__
    rows: list[list[Any]] = exchange.fetch_ohlcv(
                            ^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/roboquant.py/roboquant.py/.venv/lib/python3.12/site-packages/ccxt/binance.py", line 4709, in fetch_ohlcv
    self.load_markets()
  File "/home/runner/work/roboquant.py/roboquant.py/.venv/lib/python3.12/site-packages/ccxt/base/exchange.py", line 1839, in load_markets
    markets = self.fetch_markets(params)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/roboquant.py/roboquant.py/.venv/lib/python3.12/site-packages/ccxt/binance.py", line 3241, in fetch_markets
    promisesRaw.append(self.publicGetExchangeInfo(params))
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/roboquant.py/roboquant.py/.venv/lib/python3.12/site-packages/ccxt/base/types.py", line 35, in unbound_method
    return _self.request(self.path, self.api, self.method, params, config=self.config)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/roboquant.py/roboquant.py/.venv/lib/python3.12/site-packages/ccxt/binance.py", line 11819, in request
    response = self.fetch2(path, api, method, params, headers, body, config)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/roboquant.py/roboquant.py/.venv/lib/python3.12/site-packages/ccxt/base/exchange.py", line 5562, in fetch2
    raise e
  File "/home/runner/work/roboquant.py/roboquant.py/.venv/lib/python3.12/site-packages/ccxt/base/exchange.py", line 5545, in fetch2
    response = self.fetch(request['url'], request['method'], request['headers'], request['body'])
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/roboquant.py/roboquant.py/.venv/lib/python3.12/site-packages/ccxt/base/exchange.py", line 705, in fetch
    self.handle_http_status_code(http_status_code, http_status_text, url, method, http_response)
  File "/home/runner/work/roboquant.py/roboquant.py/.venv/lib/python3.12/site-packages/ccxt/base/exchange.py", line 2015, in handle_http_status_code
    raise ErrorClass(self.id + ' ' + method + ' ' + url + ' ' + codeAsString + ' ' + reason + ' ' + body)
ccxt.base.errors.ExchangeNotAvailable: binance GET https://api.binance.com/api/v3/exchangeInfo 451  {
  "code": 0,
  "msg": "Service unavailable from a restricted location according to 'b. Eligibility' in https://www.binance.com/en/terms. Please contact customer service if you believe you received this message in error."
}
ERROR:roboquant.feeds.crypto:Error retrieving symbol=ETH/USDT
Traceback (most recent call last):
  File "/home/runner/work/roboquant.py/roboquant.py/.venv/lib/python3.12/site-packages/ccxt/base/exchange.py", line 687, in fetch
    response.raise_for_status()
  File "/home/runner/work/_temp/setup-uv-cache/archive-v0/JZUQWwThcZ-Yrkeu/lib/python3.12/site-packages/requests/models.py", line 1167, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 451 Client Error:  for url: https://api.binance.com/api/v3/exchangeInfo

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/runner/work/roboquant.py/roboquant.py/roboquant/feeds/crypto.py", line 52, in __init__
    rows: list[list[Any]] = exchange.fetch_ohlcv(
                            ^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/roboquant.py/roboquant.py/.venv/lib/python3.12/site-packages/ccxt/binance.py", line 4709, in fetch_ohlcv
    self.load_markets()
  File "/home/runner/work/roboquant.py/roboquant.py/.venv/lib/python3.12/site-packages/ccxt/base/exchange.py", line 1839, in load_markets
    markets = self.fetch_markets(params)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/roboquant.py/roboquant.py/.venv/lib/python3.12/site-packages/ccxt/binance.py", line 3241, in fetch_markets
    promisesRaw.append(self.publicGetExchangeInfo(params))
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/roboquant.py/roboquant.py/.venv/lib/python3.12/site-packages/ccxt/base/types.py", line 35, in unbound_method
    return _self.request(self.path, self.api, self.method, params, config=self.config)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/roboquant.py/roboquant.py/.venv/lib/python3.12/site-packages/ccxt/binance.py", line 11819, in request
    response = self.fetch2(path, api, method, params, headers, body, config)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/roboquant.py/roboquant.py/.venv/lib/python3.12/site-packages/ccxt/base/exchange.py", line 5562, in fetch2
    raise e
  File "/home/runner/work/roboquant.py/roboquant.py/.venv/lib/python3.12/site-packages/ccxt/base/exchange.py", line 5545, in fetch2
    response = self.fetch(request['url'], request['method'], request['headers'], request['body'])
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/roboquant.py/roboquant.py/.venv/lib/python3.12/site-packages/ccxt/base/exchange.py", line 705, in fetch
    self.handle_http_status_code(http_status_code, http_status_text, url, method, http_response)
  File "/home/runner/work/roboquant.py/roboquant.py/.venv/lib/python3.12/site-packages/ccxt/base/exchange.py", line 2015, in handle_http_status_code
    raise ErrorClass(self.id + ' ' + method + ' ' + url + ' ' + codeAsString + ' ' + reason + ' ' + body)
ccxt.base.errors.ExchangeNotAvailable: binance GET https://api.binance.com/api/v3/exchangeInfo 451  {
  "code": 0,
  "msg": "Service unavailable from a restricted location according to 'b. Eligibility' in https://www.binance.com/en/terms. Please contact customer service if you believe you received this message in error."
}
buying power : 10,000@USDT
cash         : 10,000@USDT
equity       : 10,000@USDT
positions    : none
trades       : 0
mkt value    : 0@USDT
orders       : none
last update  : 1900-01-01 00:00:00+00:00

Broker Integration

For broker integration you typically need a trading account with the Exchange/Broker you want to use.

  from dotenv import load_env
  load_env()

  key = os.getenv("ALPACA_API_KEY")
  secret = os.getenv("ALPACA_SECRET")
  assert(key is not None and secret is not None), "ALPACA_API_KEY and ALPACA_SECRET must be set"
  alpaca = ccxt.alpaca({
      "apiKey": key,
      "secret": secret
  })
  alpaca.set_sandbox_mode(True)
  broker = CryptoBroker(alpaca)
  account = broker.sync()