ccxt 4.4.80__py2.py3-none-any.whl → 4.4.85__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ccxt/__init__.py +1 -5
- ccxt/abstract/blofin.py +8 -0
- ccxt/abstract/btcbox.py +1 -0
- ccxt/apex.py +21 -30
- ccxt/ascendex.py +1 -1
- ccxt/async_support/__init__.py +1 -5
- ccxt/async_support/apex.py +21 -30
- ccxt/async_support/ascendex.py +1 -1
- ccxt/async_support/base/exchange.py +26 -3
- ccxt/async_support/base/ws/cache.py +6 -1
- ccxt/async_support/bigone.py +17 -14
- ccxt/async_support/bingx.py +13 -32
- ccxt/async_support/bitfinex.py +61 -48
- ccxt/async_support/bitget.py +7 -4
- ccxt/async_support/bitrue.py +14 -32
- ccxt/async_support/bitso.py +33 -0
- ccxt/async_support/bitstamp.py +33 -0
- ccxt/async_support/blofin.py +145 -14
- ccxt/async_support/btcbox.py +25 -5
- ccxt/async_support/bybit.py +20 -39
- ccxt/async_support/cex.py +2 -4
- ccxt/async_support/coinbase.py +56 -42
- ccxt/async_support/coinbaseexchange.py +141 -32
- ccxt/async_support/coincatch.py +14 -67
- ccxt/async_support/coinex.py +28 -29
- ccxt/async_support/coinlist.py +17 -16
- ccxt/async_support/coinmetro.py +20 -11
- ccxt/async_support/coinone.py +8 -10
- ccxt/async_support/coinsph.py +124 -2
- ccxt/async_support/cryptocom.py +109 -2
- ccxt/async_support/cryptomus.py +42 -80
- ccxt/async_support/delta.py +75 -36
- ccxt/async_support/derive.py +46 -10
- ccxt/async_support/ellipx.py +175 -77
- ccxt/async_support/gate.py +1 -1
- ccxt/async_support/gemini.py +3 -4
- ccxt/async_support/hitbtc.py +56 -65
- ccxt/async_support/htx.py +2 -2
- ccxt/async_support/hyperliquid.py +15 -2
- ccxt/async_support/kraken.py +27 -23
- ccxt/async_support/kucoinfutures.py +5 -0
- ccxt/async_support/lbank.py +1 -1
- ccxt/async_support/okx.py +1 -2
- ccxt/async_support/oxfun.py +21 -1
- ccxt/async_support/paradex.py +120 -4
- ccxt/base/errors.py +6 -0
- ccxt/base/exchange.py +40 -3
- ccxt/base/types.py +3 -0
- ccxt/bigone.py +17 -14
- ccxt/bingx.py +13 -32
- ccxt/bitfinex.py +61 -48
- ccxt/bitget.py +7 -4
- ccxt/bitrue.py +14 -32
- ccxt/bitso.py +33 -0
- ccxt/bitstamp.py +33 -0
- ccxt/blofin.py +145 -14
- ccxt/btcbox.py +24 -5
- ccxt/bybit.py +20 -39
- ccxt/cex.py +2 -4
- ccxt/coinbase.py +56 -42
- ccxt/coinbaseexchange.py +141 -32
- ccxt/coincatch.py +14 -67
- ccxt/coinex.py +28 -29
- ccxt/coinlist.py +17 -16
- ccxt/coinmetro.py +20 -11
- ccxt/coinone.py +8 -10
- ccxt/coinsph.py +124 -2
- ccxt/cryptocom.py +109 -2
- ccxt/cryptomus.py +42 -80
- ccxt/delta.py +75 -36
- ccxt/derive.py +46 -10
- ccxt/ellipx.py +175 -77
- ccxt/gate.py +1 -1
- ccxt/gemini.py +3 -4
- ccxt/hitbtc.py +56 -65
- ccxt/htx.py +2 -2
- ccxt/hyperliquid.py +15 -2
- ccxt/kraken.py +27 -23
- ccxt/kucoinfutures.py +5 -0
- ccxt/lbank.py +1 -1
- ccxt/okx.py +1 -2
- ccxt/oxfun.py +21 -1
- ccxt/paradex.py +120 -4
- ccxt/pro/__init__.py +69 -3
- ccxt/pro/binance.py +31 -33
- ccxt/pro/bithumb.py +5 -3
- ccxt/pro/coinbase.py +1 -1
- ccxt/pro/hyperliquid.py +10 -2
- ccxt/pro/kraken.py +249 -79
- ccxt/pro/mexc.py +252 -7
- ccxt/pro/poloniex.py +6 -2
- {ccxt-4.4.80.dist-info → ccxt-4.4.85.dist-info}/METADATA +7 -11
- {ccxt-4.4.80.dist-info → ccxt-4.4.85.dist-info}/RECORD +96 -104
- ccxt/abstract/bl3p.py +0 -19
- ccxt/abstract/idex.py +0 -26
- ccxt/async_support/base/ws/fast_client.py +0 -97
- ccxt/async_support/bl3p.py +0 -543
- ccxt/async_support/idex.py +0 -1889
- ccxt/bl3p.py +0 -543
- ccxt/idex.py +0 -1889
- ccxt/pro/idex.py +0 -687
- {ccxt-4.4.80.dist-info → ccxt-4.4.85.dist-info}/LICENSE.txt +0 -0
- {ccxt-4.4.80.dist-info → ccxt-4.4.85.dist-info}/WHEEL +0 -0
- {ccxt-4.4.80.dist-info → ccxt-4.4.85.dist-info}/top_level.txt +0 -0
ccxt/pro/idex.py
DELETED
@@ -1,687 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
|
3
|
-
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
4
|
-
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
5
|
-
|
6
|
-
import ccxt.async_support
|
7
|
-
from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById, ArrayCacheByTimestamp
|
8
|
-
from ccxt.base.types import Any, Int, Order, OrderBook, Str, Ticker, Trade
|
9
|
-
from ccxt.async_support.base.ws.client import Client
|
10
|
-
from typing import List
|
11
|
-
from ccxt.base.errors import InvalidNonce
|
12
|
-
from ccxt.base.precise import Precise
|
13
|
-
|
14
|
-
|
15
|
-
class idex(ccxt.async_support.idex):
|
16
|
-
|
17
|
-
def describe(self) -> Any:
|
18
|
-
return self.deep_extend(super(idex, self).describe(), {
|
19
|
-
'has': {
|
20
|
-
'ws': True,
|
21
|
-
'watchOrderBook': True,
|
22
|
-
'watchTrades': True,
|
23
|
-
'watchOHLCV': True,
|
24
|
-
'watchTicker': True,
|
25
|
-
'watchTickers': False, # for now
|
26
|
-
'watchOrders': True,
|
27
|
-
'watchTransactions': True,
|
28
|
-
},
|
29
|
-
'urls': {
|
30
|
-
'test': {
|
31
|
-
'ws': 'wss://websocket-matic.idex.io/v1',
|
32
|
-
},
|
33
|
-
'api': {},
|
34
|
-
},
|
35
|
-
'options': {
|
36
|
-
'tradesLimit': 1000,
|
37
|
-
'ordersLimit': 1000,
|
38
|
-
'OHLCVLimit': 1000,
|
39
|
-
'watchOrderBookLimit': 1000, # default limit
|
40
|
-
'orderBookSubscriptions': {},
|
41
|
-
'token': None,
|
42
|
-
'watchOrderBook': {
|
43
|
-
'maxRetries': 3,
|
44
|
-
},
|
45
|
-
'fetchOrderBookSnapshotMaxAttempts': 10,
|
46
|
-
'fetchOrderBookSnapshotMaxDelay': 10000, # raise if there are no orders in 10 seconds
|
47
|
-
},
|
48
|
-
})
|
49
|
-
|
50
|
-
async def subscribe(self, subscribeObject, messageHash, subscription=True):
|
51
|
-
url = self.urls['test']['ws']
|
52
|
-
request: dict = {
|
53
|
-
'method': 'subscribe',
|
54
|
-
'subscriptions': [
|
55
|
-
subscribeObject,
|
56
|
-
],
|
57
|
-
}
|
58
|
-
return await self.watch(url, messageHash, request, messageHash, subscription)
|
59
|
-
|
60
|
-
async def subscribe_private(self, subscribeObject, messageHash):
|
61
|
-
token = await self.authenticate()
|
62
|
-
url = self.urls['test']['ws']
|
63
|
-
request: dict = {
|
64
|
-
'method': 'subscribe',
|
65
|
-
'token': token,
|
66
|
-
'subscriptions': [
|
67
|
-
subscribeObject,
|
68
|
-
],
|
69
|
-
}
|
70
|
-
return await self.watch(url, messageHash, request, messageHash)
|
71
|
-
|
72
|
-
async def watch_ticker(self, symbol: str, params={}) -> Ticker:
|
73
|
-
"""
|
74
|
-
watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
|
75
|
-
|
76
|
-
https://api-docs-v4.idex.io/#tickers
|
77
|
-
|
78
|
-
:param str symbol: unified symbol of the market to fetch the ticker for
|
79
|
-
:param dict [params]: extra parameters specific to the exchange API endpoint
|
80
|
-
:returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
|
81
|
-
"""
|
82
|
-
await self.load_markets()
|
83
|
-
market = self.market(symbol)
|
84
|
-
name = 'tickers'
|
85
|
-
subscribeObject: dict = {
|
86
|
-
'name': name,
|
87
|
-
'markets': [market['id']],
|
88
|
-
}
|
89
|
-
messageHash = name + ':' + market['id']
|
90
|
-
return await self.subscribe(self.extend(subscribeObject, params), messageHash)
|
91
|
-
|
92
|
-
def handle_ticker(self, client: Client, message):
|
93
|
-
# {type: "tickers",
|
94
|
-
# "data":
|
95
|
-
# {m: "DIL-ETH",
|
96
|
-
# "t": 1599213946045,
|
97
|
-
# "o": "0.09699020",
|
98
|
-
# "h": "0.10301548",
|
99
|
-
# "l": "0.09577222",
|
100
|
-
# "c": "0.09907311",
|
101
|
-
# "Q": "1.32723120",
|
102
|
-
# "v": "297.80667468",
|
103
|
-
# "q": "29.52142669",
|
104
|
-
# "P": "2.14",
|
105
|
-
# "n": 197,
|
106
|
-
# "a": "0.09912245",
|
107
|
-
# "b": "0.09686980",
|
108
|
-
# "u": 5870}}
|
109
|
-
type = self.safe_string(message, 'type')
|
110
|
-
data = self.safe_value(message, 'data')
|
111
|
-
marketId = self.safe_string(data, 'm')
|
112
|
-
symbol = self.safe_symbol(marketId)
|
113
|
-
messageHash = type + ':' + marketId
|
114
|
-
timestamp = self.safe_integer(data, 't')
|
115
|
-
close = self.safe_string(data, 'c')
|
116
|
-
percentage = self.safe_string(data, 'P')
|
117
|
-
change = None
|
118
|
-
if (percentage is not None) and (close is not None):
|
119
|
-
change = Precise.string_mul(close, percentage)
|
120
|
-
ticker = self.safe_ticker({
|
121
|
-
'symbol': symbol,
|
122
|
-
'timestamp': timestamp,
|
123
|
-
'datetime': self.iso8601(timestamp),
|
124
|
-
'high': self.safe_string(data, 'h'),
|
125
|
-
'low': self.safe_string(data, 'l'),
|
126
|
-
'bid': self.safe_string(data, 'b'),
|
127
|
-
'bidVolume': None,
|
128
|
-
'ask': self.safe_string(data, 'a'),
|
129
|
-
'askVolume': None,
|
130
|
-
'vwap': None,
|
131
|
-
'open': self.safe_string(data, 'o'),
|
132
|
-
'close': close,
|
133
|
-
'last': close,
|
134
|
-
'previousClose': None,
|
135
|
-
'change': change,
|
136
|
-
'percentage': percentage,
|
137
|
-
'average': None,
|
138
|
-
'baseVolume': self.safe_string(data, 'v'),
|
139
|
-
'quoteVolume': self.safe_string(data, 'q'),
|
140
|
-
'info': message,
|
141
|
-
})
|
142
|
-
client.resolve(ticker, messageHash)
|
143
|
-
|
144
|
-
async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
145
|
-
"""
|
146
|
-
get the list of most recent trades for a particular symbol
|
147
|
-
|
148
|
-
https://api-docs-v4.idex.io/#trades
|
149
|
-
|
150
|
-
:param str symbol: unified symbol of the market to fetch trades for
|
151
|
-
:param int [since]: timestamp in ms of the earliest trade to fetch
|
152
|
-
:param int [limit]: the maximum amount of trades to fetch
|
153
|
-
:param dict [params]: extra parameters specific to the exchange API endpoint
|
154
|
-
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
|
155
|
-
"""
|
156
|
-
await self.load_markets()
|
157
|
-
market = self.market(symbol)
|
158
|
-
symbol = market['symbol']
|
159
|
-
name = 'trades'
|
160
|
-
subscribeObject: dict = {
|
161
|
-
'name': name,
|
162
|
-
'markets': [market['id']],
|
163
|
-
}
|
164
|
-
messageHash = name + ':' + market['id']
|
165
|
-
trades = await self.subscribe(subscribeObject, messageHash)
|
166
|
-
if self.newUpdates:
|
167
|
-
limit = trades.getLimit(symbol, limit)
|
168
|
-
return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
|
169
|
-
|
170
|
-
def handle_trade(self, client: Client, message):
|
171
|
-
type = self.safe_string(message, 'type')
|
172
|
-
data = self.safe_value(message, 'data')
|
173
|
-
marketId = self.safe_string(data, 'm')
|
174
|
-
messageHash = type + ':' + marketId
|
175
|
-
trade = self.parse_ws_trade(data)
|
176
|
-
keys = list(self.trades.keys())
|
177
|
-
length = len(keys)
|
178
|
-
if length == 0:
|
179
|
-
limit = self.safe_integer(self.options, 'tradesLimit')
|
180
|
-
self.trades = ArrayCacheBySymbolById(limit)
|
181
|
-
trades = self.trades
|
182
|
-
trades.append(trade)
|
183
|
-
client.resolve(trades, messageHash)
|
184
|
-
|
185
|
-
def parse_ws_trade(self, trade, market=None):
|
186
|
-
# public trades
|
187
|
-
# {m: "DIL-ETH",
|
188
|
-
# "i": "897ecae6-4b75-368a-ac00-be555e6ad65f",
|
189
|
-
# "p": "0.09696995",
|
190
|
-
# "q": "2.00000000",
|
191
|
-
# "Q": "0.19393990",
|
192
|
-
# "t": 1599504616247,
|
193
|
-
# "s": "buy",
|
194
|
-
# "u": 6620}
|
195
|
-
# private trades
|
196
|
-
# {i: "ee253d78-88be-37ed-a61c-a36395c2ce48",
|
197
|
-
# "p": "0.09925382",
|
198
|
-
# "q": "0.15000000",
|
199
|
-
# "Q": "0.01488807",
|
200
|
-
# "t": 1599499129369,
|
201
|
-
# "s": "sell",
|
202
|
-
# "u": 6603,
|
203
|
-
# "f": "0.00030000",
|
204
|
-
# "a": "DIL",
|
205
|
-
# "g": "0.00856110",
|
206
|
-
# "l": "maker",
|
207
|
-
# "S": "pending"}
|
208
|
-
marketId = self.safe_string(trade, 'm')
|
209
|
-
symbol = self.safe_symbol(marketId)
|
210
|
-
id = self.safe_string(trade, 'i')
|
211
|
-
price = self.safe_string(trade, 'p')
|
212
|
-
amount = self.safe_string(trade, 'q')
|
213
|
-
cost = self.safe_string(trade, 'Q')
|
214
|
-
timestamp = self.safe_integer(trade, 't')
|
215
|
-
side = self.safe_string(trade, 's')
|
216
|
-
fee = {
|
217
|
-
'currency': self.safe_string(trade, 'a'),
|
218
|
-
'cost': self.safe_string(trade, 'f'),
|
219
|
-
}
|
220
|
-
takerOrMarker = self.safe_string(trade, 'l')
|
221
|
-
return self.safe_trade({
|
222
|
-
'info': trade,
|
223
|
-
'timestamp': timestamp,
|
224
|
-
'datetime': self.iso8601(timestamp),
|
225
|
-
'symbol': symbol,
|
226
|
-
'id': id,
|
227
|
-
'order': None,
|
228
|
-
'type': None,
|
229
|
-
'takerOrMaker': takerOrMarker,
|
230
|
-
'side': side,
|
231
|
-
'price': price,
|
232
|
-
'amount': amount,
|
233
|
-
'cost': cost,
|
234
|
-
'fee': fee,
|
235
|
-
})
|
236
|
-
|
237
|
-
async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
|
238
|
-
"""
|
239
|
-
watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
240
|
-
|
241
|
-
https://api-docs-v4.idex.io/#candles
|
242
|
-
|
243
|
-
:param str symbol: unified symbol of the market to fetch OHLCV data for
|
244
|
-
:param str timeframe: the length of time each candle represents
|
245
|
-
:param int [since]: timestamp in ms of the earliest candle to fetch
|
246
|
-
:param int [limit]: the maximum amount of candles to fetch
|
247
|
-
:param dict [params]: extra parameters specific to the exchange API endpoint
|
248
|
-
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
249
|
-
"""
|
250
|
-
await self.load_markets()
|
251
|
-
market = self.market(symbol)
|
252
|
-
symbol = market['symbol']
|
253
|
-
name = 'candles'
|
254
|
-
interval = self.safe_string(self.timeframes, timeframe, timeframe)
|
255
|
-
subscribeObject: dict = {
|
256
|
-
'name': name,
|
257
|
-
'markets': [market['id']],
|
258
|
-
'interval': interval,
|
259
|
-
}
|
260
|
-
messageHash = name + ':' + market['id']
|
261
|
-
ohlcv = await self.subscribe(subscribeObject, messageHash)
|
262
|
-
if self.newUpdates:
|
263
|
-
limit = ohlcv.getLimit(symbol, limit)
|
264
|
-
return self.filter_by_since_limit(ohlcv, since, limit, 0, True)
|
265
|
-
|
266
|
-
def handle_ohlcv(self, client: Client, message):
|
267
|
-
# {type: "candles",
|
268
|
-
# "data":
|
269
|
-
# {m: "DIL-ETH",
|
270
|
-
# "t": 1599477340109,
|
271
|
-
# "i": "1m",
|
272
|
-
# "s": 1599477300000,
|
273
|
-
# "e": 1599477360000,
|
274
|
-
# "o": "0.09911040",
|
275
|
-
# "h": "0.09911040",
|
276
|
-
# "l": "0.09911040",
|
277
|
-
# "c": "0.09911040",
|
278
|
-
# "v": "0.15000000",
|
279
|
-
# "n": 1,
|
280
|
-
# "u": 6531}}
|
281
|
-
type = self.safe_string(message, 'type')
|
282
|
-
data = self.safe_value(message, 'data')
|
283
|
-
marketId = self.safe_string(data, 'm')
|
284
|
-
messageHash = type + ':' + marketId
|
285
|
-
parsed = [
|
286
|
-
self.safe_integer(data, 's'),
|
287
|
-
self.safe_float(data, 'o'),
|
288
|
-
self.safe_float(data, 'h'),
|
289
|
-
self.safe_float(data, 'l'),
|
290
|
-
self.safe_float(data, 'c'),
|
291
|
-
self.safe_float(data, 'v'),
|
292
|
-
]
|
293
|
-
symbol = self.safe_symbol(marketId)
|
294
|
-
interval = self.safe_string(data, 'i')
|
295
|
-
timeframe = self.find_timeframe(interval)
|
296
|
-
# TODO: move to base class
|
297
|
-
self.ohlcvs[symbol] = self.safe_value(self.ohlcvs, symbol, {})
|
298
|
-
stored = self.safe_value(self.ohlcvs[symbol], timeframe)
|
299
|
-
if stored is None:
|
300
|
-
limit = self.safe_integer(self.options, 'OHLCVLimit', 1000)
|
301
|
-
stored = ArrayCacheByTimestamp(limit)
|
302
|
-
self.ohlcvs[symbol][timeframe] = stored
|
303
|
-
stored.append(parsed)
|
304
|
-
client.resolve(stored, messageHash)
|
305
|
-
|
306
|
-
def handle_subscribe_message(self, client: Client, message):
|
307
|
-
# {
|
308
|
-
# "type": "subscriptions",
|
309
|
-
# "subscriptions": [
|
310
|
-
# {
|
311
|
-
# "name": "l2orderbook",
|
312
|
-
# "markets": [
|
313
|
-
# "DIL-ETH"
|
314
|
-
# ]
|
315
|
-
# }
|
316
|
-
# ]
|
317
|
-
# }
|
318
|
-
subscriptions = self.safe_value(message, 'subscriptions')
|
319
|
-
for i in range(0, len(subscriptions)):
|
320
|
-
subscription = subscriptions[i]
|
321
|
-
name = self.safe_string(subscription, 'name')
|
322
|
-
if name == 'l2orderbook':
|
323
|
-
markets = self.safe_value(subscription, 'markets')
|
324
|
-
for j in range(0, len(markets)):
|
325
|
-
marketId = markets[j]
|
326
|
-
orderBookSubscriptions = self.safe_value(self.options, 'orderBookSubscriptions', {})
|
327
|
-
if not (marketId in orderBookSubscriptions):
|
328
|
-
symbol = self.safe_symbol(marketId)
|
329
|
-
if not (symbol in self.orderbooks):
|
330
|
-
orderbook = self.counted_order_book({})
|
331
|
-
# orderbook.cache = [] # cache is never used?
|
332
|
-
self.orderbooks[symbol] = orderbook
|
333
|
-
self.spawn(self.fetch_order_book_snapshot, client, symbol)
|
334
|
-
break
|
335
|
-
|
336
|
-
async def fetch_order_book_snapshot(self, client, symbol, params={}):
|
337
|
-
orderbook = self.orderbooks[symbol]
|
338
|
-
market = self.market(symbol)
|
339
|
-
messageHash = 'l2orderbook' + ':' + market['id']
|
340
|
-
subscription = client.subscriptions[messageHash]
|
341
|
-
if not subscription['fetchingOrderBookSnapshot']:
|
342
|
-
subscription['startTime'] = self.milliseconds()
|
343
|
-
subscription['fetchingOrderBookSnapshot'] = True
|
344
|
-
maxAttempts = self.safe_integer(self.options, 'fetchOrderBookSnapshotMaxAttempts', 10)
|
345
|
-
maxDelay = self.safe_integer(self.options, 'fetchOrderBookSnapshotMaxDelay', 10000)
|
346
|
-
try:
|
347
|
-
limit = self.safe_integer(subscription, 'limit', 0)
|
348
|
-
# 3. Request a level-2 order book snapshot for the market from the REST API Order Books endpoint with limit set to 0.
|
349
|
-
snapshot = await self.fetch_rest_order_book_safe(symbol, limit)
|
350
|
-
firstBuffered = self.safe_value(orderbook.cache, 0)
|
351
|
-
firstData = self.safe_value(firstBuffered, 'data')
|
352
|
-
firstNonce = self.safe_integer(firstData, 'u')
|
353
|
-
length = len(orderbook.cache)
|
354
|
-
lastBuffered = self.safe_value(orderbook.cache, length - 1)
|
355
|
-
lastData = self.safe_value(lastBuffered, 'data')
|
356
|
-
lastNonce = self.safe_integer(lastData, 'u')
|
357
|
-
bothExist = (firstNonce is not None) and (lastNonce is not None)
|
358
|
-
# ensure the snapshot is inside the range of our cached messages
|
359
|
-
# for example if the snapshot nonce is 100
|
360
|
-
# the first nonce must be less than or equal to 101 and the last nonce must be greater than 101
|
361
|
-
if bothExist and (firstNonce <= snapshot['nonce'] + 1) and (lastNonce > snapshot['nonce']):
|
362
|
-
orderbook.reset(snapshot)
|
363
|
-
for i in range(0, len(orderbook.cache)):
|
364
|
-
message = orderbook.cache[i]
|
365
|
-
data = self.safe_value(message, 'data')
|
366
|
-
u = self.safe_integer(data, 'u')
|
367
|
-
if u > orderbook['nonce']:
|
368
|
-
# 5. Discard all order book update messages with sequence numbers less than or equal to the snapshot sequence number.
|
369
|
-
# 6. Apply the remaining buffered order book update messages and any incoming order book update messages to the order book snapshot.
|
370
|
-
self.handle_order_book_message(client, message, orderbook)
|
371
|
-
subscription['fetchingOrderBookSnapshot'] = False
|
372
|
-
client.resolve(orderbook, messageHash)
|
373
|
-
else:
|
374
|
-
# 4. If the sequence in the order book snapshot is less than the sequence of the
|
375
|
-
# first buffered order book update message, discard the order book snapshot and retry step 3.
|
376
|
-
# self will continue to recurse until we have a buffered message
|
377
|
-
# since updates the order book endpoint depend on order events
|
378
|
-
# so it will eventually raise if there are no orders on a pair
|
379
|
-
subscription['numAttempts'] = subscription['numAttempts'] + 1
|
380
|
-
timeElapsed = self.milliseconds() - subscription['startTime']
|
381
|
-
maxAttemptsValid = subscription['numAttempts'] < maxAttempts
|
382
|
-
timeElapsedValid = timeElapsed < maxDelay
|
383
|
-
if maxAttemptsValid and timeElapsedValid:
|
384
|
-
self.delay(self.rateLimit, self.fetch_order_book_snapshot, client, symbol)
|
385
|
-
else:
|
386
|
-
endpart = ' in ' + str(maxAttempts) + ' attempts' if (not maxAttemptsValid) else ' after ' + str(maxDelay) + ' milliseconds'
|
387
|
-
raise InvalidNonce(self.id + ' failed to synchronize WebSocket feed with the snapshot for symbol ' + symbol + endpart)
|
388
|
-
except Exception as e:
|
389
|
-
subscription['fetchingOrderBookSnapshot'] = False
|
390
|
-
client.reject(e, messageHash)
|
391
|
-
|
392
|
-
async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
|
393
|
-
"""
|
394
|
-
watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
|
395
|
-
|
396
|
-
https://api-docs-v4.idex.io/#l2-order-book
|
397
|
-
|
398
|
-
:param str symbol: unified symbol of the market to fetch the order book for
|
399
|
-
:param int [limit]: the maximum amount of order book entries to return
|
400
|
-
:param dict [params]: extra parameters specific to the exchange API endpoint
|
401
|
-
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
|
402
|
-
"""
|
403
|
-
await self.load_markets()
|
404
|
-
market = self.market(symbol)
|
405
|
-
name = 'l2orderbook'
|
406
|
-
subscribeObject: dict = {
|
407
|
-
'name': name,
|
408
|
-
'markets': [market['id']],
|
409
|
-
}
|
410
|
-
messageHash = name + ':' + market['id']
|
411
|
-
subscription: dict = {
|
412
|
-
'fetchingOrderBookSnapshot': False,
|
413
|
-
'numAttempts': 0,
|
414
|
-
'startTime': None,
|
415
|
-
}
|
416
|
-
if limit is None:
|
417
|
-
subscription['limit'] = 1000
|
418
|
-
else:
|
419
|
-
subscription['limit'] = limit
|
420
|
-
# 1. Connect to the WebSocket API endpoint and subscribe to the L2 Order Book for the target market.
|
421
|
-
orderbook = await self.subscribe(subscribeObject, messageHash, subscription)
|
422
|
-
return orderbook.limit()
|
423
|
-
|
424
|
-
def handle_order_book(self, client: Client, message):
|
425
|
-
data = self.safe_value(message, 'data')
|
426
|
-
marketId = self.safe_string(data, 'm')
|
427
|
-
symbol = self.safe_symbol(marketId)
|
428
|
-
orderbook = self.orderbooks[symbol]
|
429
|
-
if orderbook['nonce'] is None:
|
430
|
-
# 2. Buffer the incoming order book update subscription messages.
|
431
|
-
orderbook.cache.append(message)
|
432
|
-
else:
|
433
|
-
self.handle_order_book_message(client, message, orderbook)
|
434
|
-
|
435
|
-
def handle_order_book_message(self, client: Client, message, orderbook):
|
436
|
-
# {
|
437
|
-
# "type": "l2orderbook",
|
438
|
-
# "data": {
|
439
|
-
# "m": "DIL-ETH",
|
440
|
-
# "t": 1600197205037,
|
441
|
-
# "u": 94116643,
|
442
|
-
# "b": [
|
443
|
-
# [
|
444
|
-
# "0.09662187",
|
445
|
-
# "0.00000000",
|
446
|
-
# 0
|
447
|
-
# ]
|
448
|
-
# ],
|
449
|
-
# "a": []
|
450
|
-
# }
|
451
|
-
# }
|
452
|
-
type = self.safe_string(message, 'type')
|
453
|
-
data = self.safe_value(message, 'data')
|
454
|
-
marketId = self.safe_string(data, 'm')
|
455
|
-
messageHash = type + ':' + marketId
|
456
|
-
nonce = self.safe_integer(data, 'u')
|
457
|
-
timestamp = self.safe_integer(data, 't')
|
458
|
-
bids = self.safe_value(data, 'b')
|
459
|
-
asks = self.safe_value(data, 'a')
|
460
|
-
self.handle_deltas(orderbook['bids'], bids)
|
461
|
-
self.handle_deltas(orderbook['asks'], asks)
|
462
|
-
orderbook['nonce'] = nonce
|
463
|
-
orderbook['timestamp'] = timestamp
|
464
|
-
orderbook['datetime'] = self.iso8601(timestamp)
|
465
|
-
client.resolve(orderbook, messageHash)
|
466
|
-
|
467
|
-
def handle_delta(self, bookside, delta):
|
468
|
-
price = self.safe_float(delta, 0)
|
469
|
-
amount = self.safe_float(delta, 1)
|
470
|
-
count = self.safe_integer(delta, 2)
|
471
|
-
bookside.storeArray([price, amount, count])
|
472
|
-
|
473
|
-
def handle_deltas(self, bookside, deltas):
|
474
|
-
for i in range(0, len(deltas)):
|
475
|
-
self.handle_delta(bookside, deltas[i])
|
476
|
-
|
477
|
-
async def authenticate(self, params={}):
|
478
|
-
time = self.seconds()
|
479
|
-
lastAuthenticatedTime = self.safe_integer(self.options, 'lastAuthenticatedTime', 0)
|
480
|
-
if time - lastAuthenticatedTime > 900:
|
481
|
-
request: dict = {
|
482
|
-
'wallet': self.walletAddress,
|
483
|
-
'nonce': self.uuidv1(),
|
484
|
-
}
|
485
|
-
response = await self.privateGetWsToken(self.extend(request, params))
|
486
|
-
self.options['lastAuthenticatedTime'] = time
|
487
|
-
self.options['token'] = self.safe_string(response, 'token')
|
488
|
-
return self.options['token']
|
489
|
-
|
490
|
-
async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
491
|
-
"""
|
492
|
-
watches information on multiple orders made by the user
|
493
|
-
|
494
|
-
https://api-docs-v4.idex.io/#orders
|
495
|
-
|
496
|
-
:param str symbol: unified market symbol of the market orders were made in
|
497
|
-
:param int [since]: the earliest time in ms to fetch orders for
|
498
|
-
:param int [limit]: the maximum number of order structures to retrieve
|
499
|
-
:param dict [params]: extra parameters specific to the exchange API endpoint
|
500
|
-
:returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
501
|
-
"""
|
502
|
-
await self.load_markets()
|
503
|
-
name = 'orders'
|
504
|
-
subscribeObject: dict = {
|
505
|
-
'name': name,
|
506
|
-
}
|
507
|
-
messageHash = name
|
508
|
-
if symbol is not None:
|
509
|
-
symbol = self.symbol(symbol)
|
510
|
-
marketId = self.market_id(symbol)
|
511
|
-
subscribeObject['markets'] = [marketId]
|
512
|
-
messageHash = name + ':' + marketId
|
513
|
-
orders = await self.subscribe_private(subscribeObject, messageHash)
|
514
|
-
if self.newUpdates:
|
515
|
-
limit = orders.getLimit(symbol, limit)
|
516
|
-
return self.filter_by_since_limit(orders, since, limit, 'timestamp', True)
|
517
|
-
|
518
|
-
def handle_order(self, client: Client, message):
|
519
|
-
# {
|
520
|
-
# "type": "orders",
|
521
|
-
# "data": {
|
522
|
-
# "m": "DIL-ETH",
|
523
|
-
# "i": "8f75dd30-f12d-11ea-b63c-df3381b4b5b4",
|
524
|
-
# "w": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1",
|
525
|
-
# "t": 1599498857138,
|
526
|
-
# "T": 1599498857092,
|
527
|
-
# "x": "fill",
|
528
|
-
# "X": "filled",
|
529
|
-
# "u": 67695627,
|
530
|
-
# "o": "limit",
|
531
|
-
# "S": "buy",
|
532
|
-
# "q": "0.15000000",
|
533
|
-
# "z": "0.15000000",
|
534
|
-
# "Z": "0.01486286",
|
535
|
-
# "v": "0.09908573",
|
536
|
-
# "p": "1.00000000",
|
537
|
-
# "f": "gtc",
|
538
|
-
# "V": "2",
|
539
|
-
# "F": [
|
540
|
-
# {
|
541
|
-
# "i": "5cdc6d14-bc35-3279-ab5e-40d654ca1523",
|
542
|
-
# "p": "0.09908577",
|
543
|
-
# "q": "0.15000000",
|
544
|
-
# "Q": "0.01486286",
|
545
|
-
# "t": 1599498857092,
|
546
|
-
# "s": "sell",
|
547
|
-
# "u": 6600,
|
548
|
-
# "f": "0.00030000",
|
549
|
-
# "a": "DIL",
|
550
|
-
# "g": "0.00856977",
|
551
|
-
# "l": "maker",
|
552
|
-
# "S": "pending"
|
553
|
-
# }
|
554
|
-
# ]
|
555
|
-
# }
|
556
|
-
# }
|
557
|
-
type = self.safe_string(message, 'type')
|
558
|
-
order = self.safe_value(message, 'data')
|
559
|
-
marketId = self.safe_string(order, 'm')
|
560
|
-
symbol = self.safe_symbol(marketId)
|
561
|
-
timestamp = self.safe_integer(order, 't')
|
562
|
-
fills = self.safe_value(order, 'F', [])
|
563
|
-
trades = []
|
564
|
-
for i in range(0, len(fills)):
|
565
|
-
trades.append(self.parse_ws_trade(fills[i]))
|
566
|
-
id = self.safe_string(order, 'i')
|
567
|
-
side = self.safe_string(order, 's')
|
568
|
-
orderType = self.safe_string(order, 'o')
|
569
|
-
amount = self.safe_string(order, 'q')
|
570
|
-
filled = self.safe_string(order, 'z')
|
571
|
-
average = self.safe_string(order, 'v')
|
572
|
-
price = self.safe_string(order, 'price', average) # for market orders
|
573
|
-
rawStatus = self.safe_string(order, 'X')
|
574
|
-
status = self.parse_order_status(rawStatus)
|
575
|
-
fee = {
|
576
|
-
'currency': None,
|
577
|
-
'cost': None,
|
578
|
-
}
|
579
|
-
lastTrade = None
|
580
|
-
for i in range(0, len(trades)):
|
581
|
-
lastTrade = trades[i]
|
582
|
-
fee['currency'] = lastTrade['fee']['currency']
|
583
|
-
stringLastTradeFee = lastTrade['fee']['cost']
|
584
|
-
fee['cost'] = Precise.string_add(fee['cost'], stringLastTradeFee)
|
585
|
-
lastTradeTimestamp = self.safe_integer(lastTrade, 'timestamp')
|
586
|
-
parsedOrder = self.safe_order({
|
587
|
-
'info': message,
|
588
|
-
'id': id,
|
589
|
-
'clientOrderId': None,
|
590
|
-
'timestamp': timestamp,
|
591
|
-
'datetime': self.iso8601(timestamp),
|
592
|
-
'lastTradeTimestamp': lastTradeTimestamp,
|
593
|
-
'symbol': symbol,
|
594
|
-
'type': orderType,
|
595
|
-
'side': side,
|
596
|
-
'price': self.parse_number(price),
|
597
|
-
'stopPrice': None,
|
598
|
-
'triggerPrice': None,
|
599
|
-
'amount': self.parse_number(amount),
|
600
|
-
'cost': None,
|
601
|
-
'average': self.parse_number(average),
|
602
|
-
'filled': self.parse_number(filled),
|
603
|
-
'remaining': None,
|
604
|
-
'status': status,
|
605
|
-
'fee': fee,
|
606
|
-
'trades': trades,
|
607
|
-
})
|
608
|
-
if self.orders is None:
|
609
|
-
limit = self.safe_integer(self.options, 'ordersLimit', 1000)
|
610
|
-
self.orders = ArrayCacheBySymbolById(limit)
|
611
|
-
orders = self.orders
|
612
|
-
orders.append(parsedOrder)
|
613
|
-
symbolSpecificMessageHash = type + ':' + marketId
|
614
|
-
client.resolve(orders, symbolSpecificMessageHash)
|
615
|
-
client.resolve(orders, type)
|
616
|
-
|
617
|
-
async def watch_transactions(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
|
618
|
-
await self.load_markets()
|
619
|
-
name = 'balances'
|
620
|
-
subscribeObject: dict = {
|
621
|
-
'name': name,
|
622
|
-
}
|
623
|
-
messageHash = name
|
624
|
-
if code is not None:
|
625
|
-
messageHash = name + ':' + code
|
626
|
-
transactions = await self.subscribe_private(subscribeObject, messageHash)
|
627
|
-
if self.newUpdates:
|
628
|
-
limit = transactions.getLimit(code, limit)
|
629
|
-
return self.filter_by_since_limit(transactions, since, limit, 'timestamp')
|
630
|
-
|
631
|
-
def handle_transaction(self, client: Client, message):
|
632
|
-
# Update Speed: Real time, updates on any deposit or withdrawal of the wallet
|
633
|
-
# {type: "balances",
|
634
|
-
# "data":
|
635
|
-
# {w: "0x0AB991497116f7F5532a4c2f4f7B1784488628e1",
|
636
|
-
# "a": "ETH",
|
637
|
-
# "q": "0.11198667",
|
638
|
-
# "f": "0.11198667",
|
639
|
-
# "l": "0.00000000",
|
640
|
-
# "d": "0.00"}}
|
641
|
-
type = self.safe_string(message, 'type')
|
642
|
-
data = self.safe_value(message, 'data')
|
643
|
-
currencyId = self.safe_string(data, 'a')
|
644
|
-
messageHash = type + ':' + currencyId
|
645
|
-
code = self.safe_currency_code(currencyId)
|
646
|
-
address = self.safe_string(data, 'w')
|
647
|
-
transaction: dict = {
|
648
|
-
'info': message,
|
649
|
-
'id': None,
|
650
|
-
'currency': code,
|
651
|
-
'amount': None,
|
652
|
-
'address': address,
|
653
|
-
'addressTo': None,
|
654
|
-
'addressFrom': None,
|
655
|
-
'tag': None,
|
656
|
-
'tagTo': None,
|
657
|
-
'tagFrom': None,
|
658
|
-
'status': 'ok',
|
659
|
-
'type': None,
|
660
|
-
'updated': None,
|
661
|
-
'txid': None,
|
662
|
-
'timestamp': None,
|
663
|
-
'datetime': None,
|
664
|
-
'fee': None,
|
665
|
-
}
|
666
|
-
if not (code in self.transactions):
|
667
|
-
limit = self.safe_integer(self.options, 'transactionsLimit', 1000)
|
668
|
-
self.transactions[code] = ArrayCache(limit)
|
669
|
-
transactions = self.transactions[code]
|
670
|
-
transactions.append(transaction)
|
671
|
-
client.resolve(transactions, messageHash)
|
672
|
-
client.resolve(transactions, type)
|
673
|
-
|
674
|
-
def handle_message(self, client: Client, message):
|
675
|
-
type = self.safe_string(message, 'type')
|
676
|
-
methods: dict = {
|
677
|
-
'tickers': self.handle_ticker,
|
678
|
-
'trades': self.handle_trade,
|
679
|
-
'subscriptions': self.handle_subscribe_message,
|
680
|
-
'candles': self.handle_ohlcv,
|
681
|
-
'l2orderbook': self.handle_order_book,
|
682
|
-
'balances': self.handle_transaction,
|
683
|
-
'orders': self.handle_order,
|
684
|
-
}
|
685
|
-
if type in methods:
|
686
|
-
method = methods[type]
|
687
|
-
method(client, message)
|
File without changes
|
File without changes
|