Skip to content

ynzheng/uxs

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

UXS - Unified eXchange Streaming

A unified crypto exchange websocket

This free to use python library is ccxt based. I use it for personal trading and data feeds. Pull requests are welcome, although I'll yet have to make some sort of guide on how to implement new exchanges.

DISCLAIMER: Use it at your own risk! I take no responsibility for any losses occurred!

Installation (python 3.5.3+ required):

pip install --upgrade uxs

Latest development version:

pip install git+https://github.com/binares/uxs.git

Supported exchanges:

ticker all_tickers orderbook l3 ohlcv trades balance order fill position
binance + + + + + + + p
binancefu + + + + + + + p +
bitmex p + + p p + o o +
bittrex + + + + + + + +
bw + + + + + p p
coinbene + + + + + p p
coindcx p p + p + + p +
gateiofu + p
hitbtc + p + p p p + +
kraken + p + + + + +
krakenfu + p + p + + + + +
kucoin + + + p + + o o
luno p p w + p w p o o
poloniex p + + p p + + +
southxchange p p + p + p p

Note that there aren't separate subscription channels for balance, order, fill (your trade) and position - they all belong under account: xs.subscribe_to_account(). However some exchanges like bitmex require you to subscribe to each market directly for order and fill updates: xs.subscribe_to_own_market(symbol) (but you'll still want to also subscribe to account, as it contains balance and position updates).

+ - direct streaming
p - emulated via polling (fetch_balance, fetch_tickers etc)
w - emulated via streaming (l3 -> orderbook, l3 -> trades)
o - must be subscribed to own_market

Test environments are currently offered for: binancefu, bitmex, krakenfu and kucoin (kraken also offers some sort of non-sandboxed test env). For using them add {'test': True} to the config, and make sure you have created a sandbox account.

Simple usage:

import uxs
import asyncio

xs = uxs.binance({'apiKey': '', 'secret': ''})
xs.subscribe_to_orderbook('BTC/USDT')
# Subscribes to balance, order, fill and position updates
xs.subscribe_to_account()
xs.start()

asyncio.get_event_loop().run_forever()

ExchangeSocket

All exchange classes inherit from uxs.ExchangeSocket. Note that uxs.ExchangeSocket itself isn't a subclass of ccxt.Exchange, i.e. uxs.binance !=~ ccxt.async_support.binance. Its corresponding (asynchronous) ccxt Exchange instance is accessible under .api: uxs.binance.api =~ ccxt.async_support.binance. The .api object's class is a wrapped one through, with some extra attributes added for caching data, rounding up/down, calculating payout and altering the markets data (e.g. for personalized fees).

ExchangeSocket does borrow some ccxt.async_support.Exchange methods: create_order, edit_order, cancel_order will normally evoke the same method of ccxt class, unless the exchange supports websocket trading (hitbtc). And fetch_ticker, fetch_tickers, fetch_order_book, fetch_ohlcv, fetch_trades, fetch_balance, fetch_order, fetch_orders, fetch_open_orders, fetch_closed_orders, which may in some cases be evoked through websocket.

The included examples show how to wait for updates, use callbacks, trade, cache fetch results and store tokens in a file / password encrypted file.

Subscriptions

# Subscribe

xs.subscribe_to_ticker(symbol)

xs.subscribe_to_all_tickers()

xs.subscribe_to_orderbook(symbol)

xs.subscribe_to_l3(symbol)

xs.subscribe_to_ohlcv(symbol, timeframe)

xs.subscribe_to_trades(symbol)

xs.subscribe_to_account()

xs.subscribe_to_own_market(symbol)

# Unsubscribe

xs.unsubscibe_to_{channel}(**params)

# Shortened

xs.sub_to_{channel}(**params)

xs.unsub_to_{channel}(**params)

# Dynamically

xs.subscribe_to({'_': channel, **params})

xs.unsubscribe_to({'_': channel, **params})

# Subscription object

s = xs.get_subscription({'_': channel, **params}) # for example {'_': 'orderbook', 'symbol': 'BTC/USDT'}
# or
s = xs.get_subscription((channel, *params)) # for example ('orderbook', 'BTC/USDT')

s.state # 0=inactive, 1=active

await s.wait_till_active() # wait till the subscription becomes active
# or
await xs.wait_till_subscription_active({'_': channel, **params})

Data structures

These are attributes of ExchangeSocket instance (xs). The objects in these dicts are equivalent to those of ccxt.Exchange (except for position, which isn't yet implemented by ccxt).

xs.tickers[symbol]

xs.orderbooks[symbol]

xs.l3_books[symbol]

xs.ohlcv[symbol][timeframe]

xs.trades[symbol]

xs.balances[currency]

xs.orders[order_id]

xs.open_orders[order_id]

xs.fills[order_id]

xs.positions[symbol]

An order also contains 'payout' keyword, which is the current received amount in target currency.

The structures are updated on spot. Bids/asks are inserted directly into the existing list, dict values are updated but the dict objects' id never changes. That includes all sub-level dicts (orders, fills, ...), and even the 'info' dicts (but not the other dicts like 'fee': {'cost': .. , 'currency': ..}). So for any time spanning operation (await create_order()), or if you're accessing the data from another thread, there is a real possibility that the dict has been updated in the meanwhile. To ensure that you'll still have access to the old values, make a (deep)copy of the structure before. Also avoid looping over a structure while for example creating an order (in the loop), as the dict/list size might change, and python throws an error (in dict case).

# Do NOT do this:
for o in xs.open_orders.values():
    await xs.cancel_order(o['id'], o['symbol'])

# Instead:
for o in list(xs.open_orders.values()):
    await xs.cancel_order(o['id'], o['symbol'])

Note that once a subscription feed is lost/unsubbed, the associated real-time data is automatically deleted. This it to prevent the user using outdated data, and by default includes these channels: all_tickers, ticker, orderbook. E.g. once ('orderbook', 'ETH/BTC') is lost, xs.orderbooks['ETH/BTC'] is deleted. Account relevant data is not deleted, as you might want to cancel the orders / close the positions.

To change it initiate an exchange like this:

# specific channel
uxs.binance({'channels': {'orderbook': {'delete_data_on_unsub': False}}})

Wait on stream event

# See example c / docstring for description
await xs.wait_on(stream, id=-1)

Add stream event callback

# See example d for description
xs.add_callback(cb, stream, id=None)

xs.create_order

xs.create_order automatically rounds the price down for buy orders, and up for sell orders. The amount is also rounded, always down.

Logging

Initialize with verbose value

  • 0.5 - create/edit/cancel order
  • 1 - connection, subscription, send, fetch/polling events + above
  • 2 - ping + some inner mechanisms + above
  • 3 - recv events + above

depending on how detailed log you want. Unexpected errors (not connectivity related) are logged in any case.

uxs.binance({'verbose': 1})

If you just want to see recv output without including 0.5, 1 and 2, init with

uxs.binance({'connection_defaults': {'handle': print}})

About

A unified crypto exchange websocket

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Python 100.0%