# -*- 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 import json from ccxt.base.types import Any, Int, OrderBook, Ticker, Trade from ccxt.async_support.base.ws.client import Client from typing import List class ndax(ccxt.async_support.ndax): def describe(self) -> Any: return self.deep_extend(super(ndax, self).describe(), { 'has': { 'ws': True, 'watchOrderBook': True, 'watchTrades': True, 'watchTradesForSymbols': False, 'watchTicker': True, 'watchOHLCV': True, }, 'urls': { 'test': { 'ws': 'wss://ndaxmarginstaging.cdnhop.net:10456/WSAdminGatewa/', }, 'api': { 'ws': 'wss://api.ndax.io/WSGateway', }, }, # 'options': { # 'tradesLimit': 1000, # 'ordersLimit': 1000, # 'OHLCVLimit': 1000, # }, }) def request_id(self): requestId = self.sum(self.safe_integer(self.options, 'requestId', 0), 1) self.options['requestId'] = requestId return requestId async def watch_ticker(self, symbol: str, params={}) -> Ticker: """ watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market https://apidoc.ndax.io/#subscribelevel1 :param str symbol: unified symbol of the market to fetch the ticker for :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a `ticker structure ` """ omsId = self.safe_integer(self.options, 'omsId', 1) await self.load_markets() market = self.market(symbol) name = 'SubscribeLevel1' messageHash = name + ':' + market['id'] url = self.urls['api']['ws'] requestId = self.request_id() payload: dict = { 'OMSId': omsId, 'InstrumentId': int(market['id']), # conditionally optional # 'Symbol': market['info']['symbol'], # conditionally optional } request: dict = { 'm': 0, # message type, 0 request, 1 reply, 2 subscribe, 3 event, unsubscribe, 5 error 'i': requestId, # sequence number identifies an individual request or request-and-response pair, to your application 'n': name, # function name is the name of the function being called or that the server is responding to, the server echoes your call 'o': self.json(payload), # JSON-formatted string containing the data being sent with the message } message = self.extend(request, params) return await self.watch(url, messageHash, message, messageHash) def handle_ticker(self, client: Client, message): payload = self.safe_value(message, 'o', {}) # # { # "OMSId": 1, # "InstrumentId": 1, # "BestBid": 6423.57, # "BestOffer": 6436.53, # "LastTradedPx": 6423.57, # "LastTradedQty": 0.96183964, # "LastTradeTime": 1534862990343, # "SessionOpen": 6249.64, # "SessionHigh": 11111, # "SessionLow": 4433, # "SessionClose": 6249.64, # "Volume": 0.96183964, # "CurrentDayVolume": 3516.31668185, # "CurrentDayNumTrades": 8529, # "CurrentDayPxChange": 173.93, # "CurrentNotional": 0.0, # "Rolling24HrNotional": 0.0, # "Rolling24HrVolume": 4319.63870783, # "Rolling24NumTrades": 10585, # "Rolling24HrPxChange": -0.4165607307408487, # "TimeStamp": "1534862990358" # } # ticker = self.parse_ticker(payload) symbol = ticker['symbol'] market = self.market(symbol) self.tickers[symbol] = ticker name = 'SubscribeLevel1' messageHash = name + ':' + market['id'] client.resolve(ticker, messageHash) 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 https://apidoc.ndax.io/#subscribetrades :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 ` """ omsId = self.safe_integer(self.options, 'omsId', 1) await self.load_markets() market = self.market(symbol) symbol = market['symbol'] name = 'SubscribeTrades' messageHash = name + ':' + market['id'] url = self.urls['api']['ws'] requestId = self.request_id() payload: dict = { 'OMSId': omsId, 'InstrumentId': int(market['id']), # conditionally optional 'IncludeLastCount': 100, # the number of previous trades to retrieve in the immediate snapshot, 100 by default } request: dict = { 'm': 0, # message type, 0 request, 1 reply, 2 subscribe, 3 event, unsubscribe, 5 error 'i': requestId, # sequence number identifies an individual request or request-and-response pair, to your application 'n': name, # function name is the name of the function being called or that the server is responding to, the server echoes your call 'o': self.json(payload), # JSON-formatted string containing the data being sent with the message } message = self.extend(request, params) trades = await self.watch(url, messageHash, message, messageHash) if self.newUpdates: limit = trades.getLimit(symbol, limit) return self.filter_by_since_limit(trades, since, limit, 'timestamp', True) def handle_trades(self, client: Client, message): payload = self.safe_value(message, 'o', []) # # initial snapshot # # [ # [ # 6913253, # 0 TradeId # 8, # 1 ProductPairCode # 0.03340802, # 2 Quantity # 19116.08, # 3 Price # 2543425077, # 4 Order1 # 2543425482, # 5 Order2 # 1606935922416, # 6 Tradetime # 0, # 7 Direction # 1, # 8 TakerSide # 0, # 9 BlockTrade # 0, # 10 Either Order1ClientId or Order2ClientId # ] # ] # name = 'SubscribeTrades' updates: dict = {} for i in range(0, len(payload)): trade = self.parse_trade(payload[i]) symbol = trade['symbol'] tradesArray = self.safe_value(self.trades, symbol) if tradesArray is None: limit = self.safe_integer(self.options, 'tradesLimit', 1000) tradesArray = ArrayCache(limit) tradesArray.append(trade) self.trades[symbol] = tradesArray updates[symbol] = True symbols = list(updates.keys()) for i in range(0, len(symbols)): symbol = symbols[i] market = self.market(symbol) messageHash = name + ':' + market['id'] tradesArray = self.safe_value(self.trades, symbol) client.resolve(tradesArray, messageHash) async def watch_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}) -> List[list]: """ watches historical candlestick data containing the open, high, low, and close price, and the volume of a market https://apidoc.ndax.io/#subscribeticker :param str symbol: unified symbol of the market to fetch OHLCV data for :param str timeframe: the length of time each candle represents :param int [since]: timestamp in ms of the earliest candle to fetch :param int [limit]: the maximum amount of candles to fetch :param dict [params]: extra parameters specific to the exchange API endpoint :returns int[][]: A list of candles ordered, open, high, low, close, volume """ omsId = self.safe_integer(self.options, 'omsId', 1) await self.load_markets() market = self.market(symbol) symbol = market['symbol'] name = 'SubscribeTicker' messageHash = name + ':' + timeframe + ':' + market['id'] url = self.urls['api']['ws'] requestId = self.request_id() payload: dict = { 'OMSId': omsId, 'InstrumentId': int(market['id']), # conditionally optional 'Interval': int(self.safe_string(self.timeframes, timeframe, timeframe)), 'IncludeLastCount': 100, # the number of previous candles to retrieve in the immediate snapshot, 100 by default } request: dict = { 'm': 0, # message type, 0 request, 1 reply, 2 subscribe, 3 event, unsubscribe, 5 error 'i': requestId, # sequence number identifies an individual request or request-and-response pair, to your application 'n': name, # function name is the name of the function being called or that the server is responding to, the server echoes your call 'o': self.json(payload), # JSON-formatted string containing the data being sent with the message } message = self.extend(request, params) ohlcv = await self.watch(url, messageHash, message, messageHash) if self.newUpdates: limit = ohlcv.getLimit(symbol, limit) return self.filter_by_since_limit(ohlcv, since, limit, 0, True) def handle_ohlcv(self, client: Client, message): # # { # "m": 1, # "i": 1, # "n": "SubscribeTicker", # "o": [[1608284160000,23113.52,23070.88,23075.76,23075.39,162.44964300,23075.38,23075.39,8,1608284100000]], # } # payload = self.safe_value(message, 'o', []) # # [ # [ # 1501603632000, # 0 DateTime # 2700.33, # 1 High # 2687.01, # 2 Low # 2687.01, # 3 Open # 2687.01, # 4 Close # 24.86100992, # 5 Volume # 0, # 6 Inside Bid Price # 2870.95, # 7 Inside Ask Price # 1 # 8 InstrumentId # 1608290188062.7678, # 9 candle timestamp # ] # ] # updates: dict = {} for i in range(0, len(payload)): ohlcv = payload[i] marketId = self.safe_string(ohlcv, 8) market = self.safe_market(marketId) symbol = market['symbol'] updates[marketId] = {} self.ohlcvs[symbol] = self.safe_value(self.ohlcvs, symbol, {}) keys = list(self.timeframes.keys()) for j in range(0, len(keys)): timeframe = keys[j] interval = self.safe_string(self.timeframes, timeframe, timeframe) duration = int(interval) * 1000 timestamp = self.safe_integer(ohlcv, 0) parsed = [ self.parse_to_int((timestamp / duration) * duration), self.safe_float(ohlcv, 3), self.safe_float(ohlcv, 1), self.safe_float(ohlcv, 2), self.safe_float(ohlcv, 4), self.safe_float(ohlcv, 5), ] stored = self.safe_value(self.ohlcvs[symbol], timeframe, []) length = len(stored) if length and (parsed[0] == stored[length - 1][0]): previous = stored[length - 1] stored[length - 1] = [ parsed[0], previous[1], max(parsed[1], previous[1]), min(parsed[2], previous[2]), parsed[4], self.sum(parsed[5], previous[5]), ] updates[marketId][timeframe] = True else: if length and (parsed[0] < stored[length - 1][0]): continue else: stored.append(parsed) limit = self.safe_integer(self.options, 'OHLCVLimit', 1000) if length >= limit: stored.pop(0) updates[marketId][timeframe] = True self.ohlcvs[symbol][timeframe] = stored name = 'SubscribeTicker' marketIds = list(updates.keys()) for i in range(0, len(marketIds)): marketId = marketIds[i] timeframes = list(updates[marketId].keys()) for j in range(0, len(timeframes)): timeframe = timeframes[j] messageHash = name + ':' + timeframe + ':' + marketId market = self.safe_market(marketId) symbol = market['symbol'] stored = self.safe_value(self.ohlcvs[symbol], timeframe, []) client.resolve(stored, messageHash) 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 https://apidoc.ndax.io/#subscribelevel2 :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 ` indexed by market symbols """ omsId = self.safe_integer(self.options, 'omsId', 1) await self.load_markets() market = self.market(symbol) symbol = market['symbol'] name = 'SubscribeLevel2' messageHash = name + ':' + market['id'] url = self.urls['api']['ws'] requestId = self.request_id() limit = 100 if (limit is None) else limit payload: dict = { 'OMSId': omsId, 'InstrumentId': int(market['id']), # conditionally optional # 'Symbol': market['info']['symbol'], # conditionally optional 'Depth': limit, # default 100 } request: dict = { 'm': 0, # message type, 0 request, 1 reply, 2 subscribe, 3 event, unsubscribe, 5 error 'i': requestId, # sequence number identifies an individual request or request-and-response pair, to your application 'n': name, # function name is the name of the function being called or that the server is responding to, the server echoes your call 'o': self.json(payload), # JSON-formatted string containing the data being sent with the message } subscription: dict = { 'id': requestId, 'messageHash': messageHash, 'name': name, 'symbol': symbol, 'marketId': market['id'], 'method': self.handle_order_book_subscription, 'limit': limit, 'params': params, } message = self.extend(request, params) orderbook = await self.watch(url, messageHash, message, messageHash, subscription) return orderbook.limit() def handle_order_book(self, client: Client, message): # # { # "m": 3, # "i": 2, # "n": "Level2UpdateEvent", # "o": [[2,1,1608208308265,0,20782.49,1,25000,8,1,1]] # } # payload = self.safe_value(message, 'o', []) # # [ # 0, # 0 MDUpdateId # 1, # 1 Number of Unique Accounts # 123, # 2 ActionDateTime in Posix format X 1000 # 0, # 3 ActionType 0(New), 1(Update), 2(Delete) # 0.0, # 4 LastTradePrice # 0, # 5 Number of Orders # 0.0, # 6 Price # 0, # 7 ProductPairCode # 0.0, # 8 Quantity # 0, # 9 Side # ], # firstBidAsk = self.safe_value(payload, 0, []) marketId = self.safe_string(firstBidAsk, 7) if marketId is None: return market = self.safe_market(marketId) symbol = market['symbol'] orderbook = self.safe_value(self.orderbooks, symbol) if orderbook is None: return timestamp = None nonce = None for i in range(0, len(payload)): bidask = payload[i] if timestamp is None: timestamp = self.safe_integer(bidask, 2) else: newTimestamp = self.safe_integer(bidask, 2) timestamp = max(timestamp, newTimestamp) if nonce is None: nonce = self.safe_integer(bidask, 0) else: newNonce = self.safe_integer(bidask, 0) nonce = max(nonce, newNonce) # 0 new, 1 update, 2 remove type = self.safe_integer(bidask, 3) price = self.safe_float(bidask, 6) amount = self.safe_float(bidask, 8) side = self.safe_integer(bidask, 9) # 0 buy, 1 sell, 2 short reserved for future use, 3 unknown orderbookSide = orderbook['bids'] if (side == 0) else orderbook['asks'] # 0 new, 1 update, 2 remove if type == 0: orderbookSide.store(price, amount) elif type == 1: orderbookSide.store(price, amount) elif type == 2: orderbookSide.store(price, 0) orderbook['nonce'] = nonce orderbook['timestamp'] = timestamp orderbook['datetime'] = self.iso8601(timestamp) name = 'SubscribeLevel2' messageHash = name + ':' + marketId self.orderbooks[symbol] = orderbook client.resolve(orderbook, messageHash) def handle_order_book_subscription(self, client: Client, message, subscription): # # { # "m": 1, # "i": 1, # "n": "SubscribeLevel2", # "o": [[1,1,1608204295901,0,20782.49,1,18200,8,1,0]] # } # payload = self.safe_value(message, 'o', []) # # [ # [ # 0, # 0 MDUpdateId # 1, # 1 Number of Unique Accounts # 123, # 2 ActionDateTime in Posix format X 1000 # 0, # 3 ActionType 0(New), 1(Update), 2(Delete) # 0.0, # 4 LastTradePrice # 0, # 5 Number of Orders # 0.0, # 6 Price # 0, # 7 ProductPairCode # 0.0, # 8 Quantity # 0, # 9 Side # ], # ] # symbol = self.safe_string(subscription, 'symbol') snapshot = self.parse_order_book(payload, symbol) limit = self.safe_integer(subscription, 'limit') orderbook = self.order_book(snapshot, limit) self.orderbooks[symbol] = orderbook messageHash = self.safe_string(subscription, 'messageHash') client.resolve(orderbook, messageHash) def handle_subscription_status(self, client: Client, message): # # { # "m": 1, # "i": 1, # "n": "SubscribeLevel2", # "o": "[[1,1,1608204295901,0,20782.49,1,18200,8,1,0]]" # } # subscriptionsById = self.index_by(client.subscriptions, 'id') id = self.safe_integer(message, 'i') subscription = self.safe_value(subscriptionsById, id) if subscription is not None: method = self.safe_value(subscription, 'method') if method is not None: method(client, message, subscription) def handle_message(self, client: Client, message): # # { # "m": 0, # message type, 0 request, 1 reply, 2 subscribe, 3 event, unsubscribe, 5 error # "i": 0, # sequence number identifies an individual request or request-and-response pair, to your application # "n":"function name", # function name is the name of the function being called or that the server is responding to, the server echoes your call # "o":"payload", # JSON-formatted string containing the data being sent with the message # } # # { # "m": 1, # "i": 1, # "n": "SubscribeLevel2", # "o": "[[1,1,1608204295901,0,20782.49,1,18200,8,1,0]]" # } # # { # "m": 3, # "i": 2, # "n": "Level2UpdateEvent", # "o": "[[2,1,1608208308265,0,20782.49,1,25000,8,1,1]]" # } # payload = self.safe_string(message, 'o') if payload is None: return message['o'] = json.loads(payload) methods: dict = { 'SubscribeLevel2': self.handle_subscription_status, 'SubscribeLevel1': self.handle_ticker, 'Level2UpdateEvent': self.handle_order_book, 'Level1UpdateEvent': self.handle_ticker, 'SubscribeTrades': self.handle_trades, 'TradeDataUpdateEvent': self.handle_trades, 'SubscribeTicker': self.handle_ohlcv, 'TickerDataUpdateEvent': self.handle_ohlcv, } event = self.safe_string(message, 'n') method = self.safe_value(methods, event) if method is not None: method(client, message)