uxs 0.6.1
A unified crypto exchange websocket
Requires Python: >=3.5.3

# 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 streamer 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}})
```