275 lines
11 KiB
Python
275 lines
11 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
|
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
|
|
|
import ccxt.async_support
|
|
from ccxt.async_support.base.ws.cache import ArrayCache
|
|
from ccxt.base.types import Any, Int, OrderBook, Trade
|
|
from ccxt.async_support.base.ws.client import Client
|
|
from typing import List
|
|
from ccxt.base.errors import NotSupported
|
|
from ccxt.base.errors import ChecksumError
|
|
|
|
|
|
class independentreserve(ccxt.async_support.independentreserve):
|
|
|
|
def describe(self) -> Any:
|
|
return self.deep_extend(super(independentreserve, self).describe(), {
|
|
'has': {
|
|
'ws': True,
|
|
'watchBalance': False,
|
|
'watchTicker': False,
|
|
'watchTickers': False,
|
|
'watchTrades': True,
|
|
'watchTradesForSymbols': False,
|
|
'watchMyTrades': False,
|
|
'watchOrders': False,
|
|
'watchOrderBook': True,
|
|
'watchOHLCV': False,
|
|
},
|
|
'urls': {
|
|
'api': {
|
|
'ws': 'wss://websockets.independentreserve.com',
|
|
},
|
|
},
|
|
'options': {
|
|
'watchOrderBook': {
|
|
'checksum': True, # TODO: currently only working for snapshot
|
|
},
|
|
},
|
|
'streaming': {
|
|
},
|
|
'exceptions': {
|
|
},
|
|
})
|
|
|
|
async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
|
"""
|
|
get the list of most recent trades for a particular symbol
|
|
:param str symbol: unified symbol of the market to fetch trades for
|
|
:param int [since]: timestamp in ms of the earliest trade to fetch
|
|
:param int [limit]: the maximum amount of trades to fetch
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
|
|
"""
|
|
await self.load_markets()
|
|
market = self.market(symbol)
|
|
symbol = market['symbol']
|
|
url = self.urls['api']['ws'] + '?subscribe=ticker-' + market['base'] + '-' + market['quote']
|
|
messageHash = 'trades:' + symbol
|
|
trades = await self.watch(url, messageHash, None, messageHash)
|
|
return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
|
|
|
|
def handle_trades(self, client: Client, message):
|
|
#
|
|
# {
|
|
# "Channel": "ticker-btc-usd",
|
|
# "Nonce": 130,
|
|
# "Data": {
|
|
# "TradeGuid": "7a669f2a-d564-472b-8493-6ef982eb1e96",
|
|
# "Pair": "btc-aud",
|
|
# "TradeDate": "2023-02-12T10:04:13.0804889+11:00",
|
|
# "Price": 31640,
|
|
# "Volume": 0.00079029,
|
|
# "BidGuid": "ba8a78b5-be69-4d33-92bb-9df0daa6314e",
|
|
# "OfferGuid": "27d20270-f21f-4c25-9905-152e70b2f6ec",
|
|
# "Side": "Buy"
|
|
# },
|
|
# "Time": 1676156653111,
|
|
# "Event": "Trade"
|
|
# }
|
|
#
|
|
data = self.safe_value(message, 'Data', {})
|
|
marketId = self.safe_string(data, 'Pair')
|
|
symbol = self.safe_symbol(marketId, None, '-')
|
|
messageHash = 'trades:' + symbol
|
|
stored = self.safe_value(self.trades, symbol)
|
|
if stored is None:
|
|
limit = self.safe_integer(self.options, 'tradesLimit', 1000)
|
|
stored = ArrayCache(limit)
|
|
self.trades[symbol] = stored
|
|
trade = self.parse_ws_trade(data)
|
|
stored.append(trade)
|
|
self.trades[symbol] = stored
|
|
client.resolve(self.trades[symbol], messageHash)
|
|
|
|
def parse_ws_trade(self, trade, market=None):
|
|
#
|
|
# {
|
|
# "TradeGuid": "2f316718-0d0b-4e33-a30c-c2c06f3cfb34",
|
|
# "Pair": "xbt-aud",
|
|
# "TradeDate": "2023-02-12T09:22:35.4207494+11:00",
|
|
# "Price": 31573.8,
|
|
# "Volume": 0.05,
|
|
# "BidGuid": "adb63d74-4c02-47f9-9cc3-f287e3b48ab6",
|
|
# "OfferGuid": "b94d9bc4-addd-4633-a18f-69cf7e1b6f47",
|
|
# "Side": "Buy"
|
|
# }
|
|
#
|
|
datetime = self.safe_string(trade, 'TradeDate')
|
|
marketId = self.safe_string(market, 'Pair')
|
|
return self.safe_trade({
|
|
'info': trade,
|
|
'id': self.safe_string(trade, 'TradeGuid'),
|
|
'order': self.safe_string(trade, 'orderNo'),
|
|
'symbol': self.safe_symbol(marketId, market, '-'),
|
|
'side': self.safe_string_lower(trade, 'Side'),
|
|
'type': None,
|
|
'takerOrMaker': None,
|
|
'price': self.safe_string(trade, 'Price'),
|
|
'amount': self.safe_string(trade, 'Volume'),
|
|
'cost': None,
|
|
'fee': None,
|
|
'timestamp': self.parse8601(datetime),
|
|
'datetime': datetime,
|
|
}, market)
|
|
|
|
async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
|
|
"""
|
|
watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
|
|
:param str symbol: unified symbol of the market to fetch the order book for
|
|
:param int [limit]: the maximum amount of order book entries to return
|
|
:param dict [params]: extra parameters specific to the exchange API endpoint
|
|
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
|
|
"""
|
|
await self.load_markets()
|
|
market = self.market(symbol)
|
|
symbol = market['symbol']
|
|
if limit is None:
|
|
limit = 100
|
|
limitString = self.number_to_string(limit)
|
|
url = self.urls['api']['ws'] + '/orderbook/' + limitString + '?subscribe=' + market['base'] + '-' + market['quote']
|
|
messageHash = 'orderbook:' + symbol + ':' + limitString
|
|
subscription: dict = {
|
|
'receivedSnapshot': False,
|
|
}
|
|
orderbook = await self.watch(url, messageHash, None, messageHash, subscription)
|
|
return orderbook.limit()
|
|
|
|
def handle_order_book(self, client: Client, message):
|
|
#
|
|
# {
|
|
# "Channel": "orderbook/1/eth/aud",
|
|
# "Data": {
|
|
# "Bids": [
|
|
# {
|
|
# "Price": 2198.09,
|
|
# "Volume": 0.16143952,
|
|
# },
|
|
# ],
|
|
# "Offers": [
|
|
# {
|
|
# "Price": 2201.25,
|
|
# "Volume": 15,
|
|
# },
|
|
# ],
|
|
# "Crc32": 1519697650,
|
|
# },
|
|
# "Time": 1676150558254,
|
|
# "Event": "OrderBookSnapshot",
|
|
# }
|
|
#
|
|
event = self.safe_string(message, 'Event')
|
|
channel = self.safe_string(message, 'Channel')
|
|
parts = channel.split('/')
|
|
depth = self.safe_string(parts, 1)
|
|
baseId = self.safe_string(parts, 2)
|
|
quoteId = self.safe_string(parts, 3)
|
|
base = self.safe_currency_code(baseId)
|
|
quote = self.safe_currency_code(quoteId)
|
|
symbol = base + '/' + quote
|
|
orderBook = self.safe_dict(message, 'Data', {})
|
|
messageHash = 'orderbook:' + symbol + ':' + depth
|
|
subscription = self.safe_value(client.subscriptions, messageHash, {})
|
|
receivedSnapshot = self.safe_bool(subscription, 'receivedSnapshot', False)
|
|
timestamp = self.safe_integer(message, 'Time')
|
|
# orderbook = self.safe_value(self.orderbooks, symbol)
|
|
if not (symbol in self.orderbooks):
|
|
self.orderbooks[symbol] = self.order_book({})
|
|
orderbook = self.orderbooks[symbol]
|
|
if event == 'OrderBookSnapshot':
|
|
snapshot = self.parse_order_book(orderBook, symbol, timestamp, 'Bids', 'Offers', 'Price', 'Volume')
|
|
orderbook.reset(snapshot)
|
|
subscription['receivedSnapshot'] = True
|
|
else:
|
|
asks = self.safe_list(orderBook, 'Offers', [])
|
|
bids = self.safe_list(orderBook, 'Bids', [])
|
|
self.handle_deltas(orderbook['asks'], asks)
|
|
self.handle_deltas(orderbook['bids'], bids)
|
|
orderbook['timestamp'] = timestamp
|
|
orderbook['datetime'] = self.iso8601(timestamp)
|
|
checksum = self.handle_option('watchOrderBook', 'checksum', True)
|
|
if checksum and receivedSnapshot:
|
|
storedAsks = orderbook['asks']
|
|
storedBids = orderbook['bids']
|
|
asksLength = len(storedAsks)
|
|
bidsLength = len(storedBids)
|
|
payload = ''
|
|
for i in range(0, 10):
|
|
if i < bidsLength:
|
|
payload = payload + self.value_to_checksum(storedBids[i][0]) + self.value_to_checksum(storedBids[i][1])
|
|
for i in range(0, 10):
|
|
if i < asksLength:
|
|
payload = payload + self.value_to_checksum(storedAsks[i][0]) + self.value_to_checksum(storedAsks[i][1])
|
|
calculatedChecksum = self.crc32(payload, True)
|
|
responseChecksum = self.safe_integer(orderBook, 'Crc32')
|
|
if calculatedChecksum != responseChecksum:
|
|
error = ChecksumError(self.id + ' ' + self.orderbook_checksum_message(symbol))
|
|
del client.subscriptions[messageHash]
|
|
del self.orderbooks[symbol]
|
|
client.reject(error, messageHash)
|
|
return
|
|
if receivedSnapshot:
|
|
client.resolve(orderbook, messageHash)
|
|
|
|
def value_to_checksum(self, value):
|
|
result = format(value, '.8f')
|
|
result = result.replace('.', '')
|
|
# remove leading zeros
|
|
result = self.parse_number(result)
|
|
result = self.number_to_string(result)
|
|
return result
|
|
|
|
def handle_delta(self, bookside, delta):
|
|
bidAsk = self.parse_bid_ask(delta, 'Price', 'Volume')
|
|
bookside.storeArray(bidAsk)
|
|
|
|
def handle_deltas(self, bookside, deltas):
|
|
for i in range(0, len(deltas)):
|
|
self.handle_delta(bookside, deltas[i])
|
|
|
|
def handle_heartbeat(self, client: Client, message):
|
|
#
|
|
# {
|
|
# "Time": 1676156208182,
|
|
# "Event": "Heartbeat"
|
|
# }
|
|
#
|
|
return message
|
|
|
|
def handle_subscriptions(self, client: Client, message):
|
|
#
|
|
# {
|
|
# "Data": ["ticker-btc-sgd"],
|
|
# "Time": 1676157556223,
|
|
# "Event": "Subscriptions"
|
|
# }
|
|
#
|
|
return message
|
|
|
|
def handle_message(self, client: Client, message):
|
|
event = self.safe_string(message, 'Event')
|
|
handlers: dict = {
|
|
'Subscriptions': self.handle_subscriptions,
|
|
'Heartbeat': self.handle_heartbeat,
|
|
'Trade': self.handle_trades,
|
|
'OrderBookSnapshot': self.handle_order_book,
|
|
'OrderBookChange': self.handle_order_book,
|
|
}
|
|
handler = self.safe_value(handlers, event)
|
|
if handler is not None:
|
|
handler(client, message)
|
|
return
|
|
raise NotSupported(self.id + ' received an unsupported message: ' + self.json(message))
|