ccxt 4.3.65 → 4.3.67

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.
Files changed (48) hide show
  1. package/README.md +4 -3
  2. package/dist/ccxt.browser.min.js +2 -2
  3. package/dist/cjs/ccxt.js +1 -1
  4. package/dist/cjs/src/base/Exchange.js +2 -2
  5. package/dist/cjs/src/bingx.js +151 -19
  6. package/dist/cjs/src/bithumb.js +61 -17
  7. package/dist/cjs/src/hyperliquid.js +63 -7
  8. package/dist/cjs/src/independentreserve.js +0 -1
  9. package/dist/cjs/src/kraken.js +27 -0
  10. package/dist/cjs/src/pro/hyperliquid.js +103 -1
  11. package/dist/cjs/src/pro/okx.js +22 -9
  12. package/js/ccxt.d.ts +1 -1
  13. package/js/ccxt.js +1 -1
  14. package/js/src/abstract/bingx.d.ts +7 -0
  15. package/js/src/base/Exchange.d.ts +14 -13
  16. package/js/src/base/Exchange.js +2 -2
  17. package/js/src/bingx.js +151 -19
  18. package/js/src/bithumb.js +61 -17
  19. package/js/src/hyperliquid.d.ts +3 -1
  20. package/js/src/hyperliquid.js +63 -7
  21. package/js/src/independentreserve.js +1 -1
  22. package/js/src/kraken.d.ts +7 -0
  23. package/js/src/kraken.js +27 -0
  24. package/js/src/pro/bitget.d.ts +1 -1
  25. package/js/src/pro/bybit.d.ts +1 -1
  26. package/js/src/pro/coinone.d.ts +1 -1
  27. package/js/src/pro/currencycom.d.ts +1 -1
  28. package/js/src/pro/hollaex.d.ts +1 -1
  29. package/js/src/pro/hyperliquid.d.ts +5 -2
  30. package/js/src/pro/hyperliquid.js +103 -1
  31. package/js/src/pro/kucoin.d.ts +1 -1
  32. package/js/src/pro/kucoinfutures.d.ts +1 -1
  33. package/js/src/pro/mexc.d.ts +1 -1
  34. package/js/src/pro/okcoin.d.ts +1 -1
  35. package/js/src/pro/okx.d.ts +2 -2
  36. package/js/src/pro/okx.js +23 -10
  37. package/js/src/pro/oxfun.d.ts +1 -1
  38. package/js/src/pro/p2b.d.ts +1 -1
  39. package/js/src/pro/poloniex.d.ts +1 -1
  40. package/js/src/pro/whitebit.d.ts +1 -1
  41. package/package.json +1 -1
  42. package/dist/cjs/src/abstract/bittrex.js +0 -9
  43. package/dist/cjs/src/bittrex.js +0 -2308
  44. package/dist/cjs/src/pro/bittrex.js +0 -959
  45. package/js/src/bittrex.d.ts +0 -97
  46. package/js/src/bittrex.js +0 -2309
  47. package/js/src/pro/bittrex.d.ts +0 -69
  48. package/js/src/pro/bittrex.js +0 -960
@@ -1,959 +0,0 @@
1
- 'use strict';
2
-
3
- var bittrex$1 = require('../bittrex.js');
4
- var errors = require('../base/errors.js');
5
- var Cache = require('../base/ws/Cache.js');
6
- var sha512 = require('../static_dependencies/noble-hashes/sha512.js');
7
- var browser = require('../static_dependencies/fflake/browser.js');
8
-
9
- // ---------------------------------------------------------------------------
10
- // ---------------------------------------------------------------------------
11
- class bittrex extends bittrex$1 {
12
- describe() {
13
- return this.deepExtend(super.describe(), {
14
- 'has': {
15
- 'ws': true,
16
- 'cancelAllOrdersWs': false,
17
- 'cancelOrdersWs': false,
18
- 'cancelOrderWs': false,
19
- 'createOrderWs': false,
20
- 'editOrderWs': false,
21
- 'fetchBalanceWs': false,
22
- 'fetchOpenOrdersWs': false,
23
- 'fetchOrderWs': false,
24
- 'fetchTradesWs': false,
25
- 'watchBalance': true,
26
- 'watchHeartbeat': true,
27
- 'watchMyTrades': true,
28
- 'watchOHLCV': true,
29
- 'watchOrderBook': true,
30
- 'watchOrders': true,
31
- 'watchTicker': true,
32
- 'watchTickers': false,
33
- 'watchTrades': true,
34
- },
35
- 'urls': {
36
- 'api': {
37
- 'ws': 'wss://socket-v3.bittrex.com/signalr/connect',
38
- 'signalr': 'https://socket-v3.bittrex.com/signalr',
39
- },
40
- },
41
- 'api': {
42
- 'signalr': {
43
- 'get': [
44
- 'negotiate',
45
- 'start',
46
- ],
47
- },
48
- },
49
- 'options': {
50
- 'tradesLimit': 1000,
51
- 'OHLCVLimit': 1000,
52
- 'hub': 'c3',
53
- 'I': this.milliseconds(),
54
- 'watchOrderBook': {
55
- 'maxRetries': 3,
56
- },
57
- },
58
- 'exceptions': {
59
- 'exact': {
60
- 'INVALID_APIKEY': errors.AuthenticationError,
61
- 'UNAUTHORIZED_USER': errors.AuthenticationError,
62
- },
63
- },
64
- });
65
- }
66
- getSignalRUrl(negotiation) {
67
- const connectionToken = this.safeString(negotiation['response'], 'ConnectionToken');
68
- const query = this.extend(negotiation['request'], {
69
- 'connectionToken': connectionToken,
70
- // 'tid': this.milliseconds () % 10,
71
- });
72
- return this.urls['api']['ws'] + '?' + this.urlencode(query);
73
- }
74
- makeRequest(requestId, method, args) {
75
- const hub = this.safeString(this.options, 'hub', 'c3');
76
- return {
77
- 'H': hub,
78
- 'M': method,
79
- 'A': args,
80
- 'I': requestId, // invocation request id
81
- };
82
- }
83
- makeRequestToSubscribe(requestId, args) {
84
- const method = 'Subscribe';
85
- return this.makeRequest(requestId, method, args);
86
- }
87
- makeRequestToAuthenticate(requestId) {
88
- const timestamp = this.milliseconds();
89
- const uuid = this.uuid();
90
- const auth = timestamp.toString() + uuid;
91
- const signature = this.hmac(this.encode(auth), this.encode(this.secret), sha512.sha512);
92
- const args = [this.apiKey, timestamp, uuid, signature];
93
- const method = 'Authenticate';
94
- return this.makeRequest(requestId, method, args);
95
- }
96
- requestId() {
97
- // their support said that reqid must be an int32, not documented
98
- const reqid = this.sum(this.safeInteger(this.options, 'I', 0), 1);
99
- this.options['I'] = reqid;
100
- return reqid;
101
- }
102
- async sendRequestToSubscribe(negotiation, messageHash, subscription, params = {}) {
103
- const args = [messageHash];
104
- const requestId = this.requestId().toString();
105
- const request = this.makeRequestToSubscribe(requestId, [args]);
106
- subscription = this.extend({
107
- 'id': requestId,
108
- 'negotiation': negotiation,
109
- }, subscription);
110
- const url = this.getSignalRUrl(negotiation);
111
- return await this.watch(url, messageHash, request, messageHash, subscription);
112
- }
113
- async authenticate(params = {}) {
114
- this.checkRequiredCredentials();
115
- await this.loadMarkets();
116
- const request = await this.negotiate();
117
- return await this.sendRequestToAuthenticate(request, false, params);
118
- }
119
- async sendRequestToAuthenticate(negotiation, expired = false, params = {}) {
120
- const url = this.getSignalRUrl(negotiation);
121
- const client = this.client(url);
122
- const messageHash = 'authenticate';
123
- let future = this.safeValue(client.subscriptions, messageHash);
124
- if ((future === undefined) || expired) {
125
- future = client.future(messageHash);
126
- client.subscriptions[messageHash] = future;
127
- const requestId = this.requestId().toString();
128
- const request = this.makeRequestToAuthenticate(requestId);
129
- const subscription = {
130
- 'id': requestId,
131
- 'params': params,
132
- 'negotiation': negotiation,
133
- 'method': this.handleAuthenticate,
134
- };
135
- this.watch(url, messageHash, request, requestId, subscription);
136
- }
137
- return await future;
138
- }
139
- async sendAuthenticatedRequestToSubscribe(authentication, messageHash, params = {}) {
140
- const negotiation = this.safeValue(authentication, 'negotiation');
141
- const subscription = { 'params': params };
142
- return await this.sendRequestToSubscribe(negotiation, messageHash, subscription, params);
143
- }
144
- handleAuthenticate(client, message, subscription) {
145
- const requestId = this.safeString(subscription, 'id');
146
- if (requestId in client.subscriptions) {
147
- delete client.subscriptions[requestId];
148
- }
149
- client.resolve(subscription, 'authenticate');
150
- }
151
- async handleAuthenticationExpiringHelper() {
152
- const negotiation = await this.negotiate();
153
- return await this.sendRequestToAuthenticate(negotiation, true);
154
- }
155
- handleAuthenticationExpiring(client, message) {
156
- //
157
- // {
158
- // "C": "d-B1733F58-B,0|vT7,1|vT8,2|vBR,3",
159
- // "M": [ { H: "C3", M: "authenticationExpiring", A: [] } ]
160
- // }
161
- //
162
- // resend the authentication request and refresh the subscription
163
- //
164
- this.spawn(this.handleAuthenticationExpiringHelper);
165
- }
166
- createSignalRQuery(params = {}) {
167
- const hub = this.safeString(this.options, 'hub', 'c3');
168
- const hubs = [
169
- { 'name': hub },
170
- ];
171
- const ms = this.milliseconds();
172
- return this.extend({
173
- 'transport': 'webSockets',
174
- 'connectionData': this.json(hubs),
175
- 'clientProtocol': 1.5,
176
- '_': ms,
177
- 'tid': this.sum(ms % 10, 1), // random
178
- }, params);
179
- }
180
- async negotiate(params = {}) {
181
- const client = this.client(this.urls['api']['ws']);
182
- const messageHash = 'negotiate';
183
- let future = this.safeValue(client.subscriptions, messageHash);
184
- if (future === undefined) {
185
- future = client.future(messageHash);
186
- client.subscriptions[messageHash] = future;
187
- const request = this.createSignalRQuery(params);
188
- const response = await this.signalrGetNegotiate(this.extend(request, params));
189
- //
190
- // {
191
- // "Url": "/signalr/v1.1/signalr",
192
- // "ConnectionToken": "lT/sa19+FcrEb4W53On2v+Pcc3d4lVCHV5/WJtmQw1RQNQMpm7K78w/WnvfTN2EgwQopTUiFX1dioHN7Bd1p8jAbfdxrqf5xHAMntJfOrw1tON0O",
193
- // "ConnectionId": "a2afb0f7-346f-4f32-b7c7-01e04584b86a",
194
- // "KeepAliveTimeout": 20,
195
- // "DisconnectTimeout": 30,
196
- // "ConnectionTimeout": 110,
197
- // "TryWebSockets": true,
198
- // "ProtocolVersion": "1.5",
199
- // "TransportConnectTimeout": 5,
200
- // "LongPollDelay": 0
201
- // }
202
- //
203
- const result = {
204
- 'request': request,
205
- 'response': response,
206
- };
207
- client.resolve(result, messageHash);
208
- }
209
- return await future;
210
- }
211
- async start(negotiation, params = {}) {
212
- const connectionToken = this.safeString(negotiation['response'], 'ConnectionToken');
213
- const request = this.createSignalRQuery(this.extend(negotiation['request'], {
214
- 'connectionToken': connectionToken,
215
- }));
216
- return await this.signalrGetStart(request);
217
- }
218
- async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
219
- /**
220
- * @method
221
- * @name bittrex#watchOrders
222
- * @description watches information on multiple orders made by the user
223
- * @param {string} symbol unified market symbol of the market orders were made in
224
- * @param {int} [since] the earliest time in ms to fetch orders for
225
- * @param {int} [limit] the maximum number of orde structures to retrieve
226
- * @param {object} [params] extra parameters specific to the exchange API endpoint
227
- * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
228
- */
229
- await this.loadMarkets();
230
- if (symbol !== undefined) {
231
- symbol = this.symbol(symbol);
232
- }
233
- const authentication = await this.authenticate();
234
- const orders = await this.subscribeToOrders(authentication, params);
235
- if (this.newUpdates) {
236
- limit = orders.getLimit(symbol, limit);
237
- }
238
- return this.filterBySymbolSinceLimit(orders, symbol, since, limit, true);
239
- }
240
- async subscribeToOrders(authentication, params = {}) {
241
- const messageHash = 'order';
242
- return await this.sendAuthenticatedRequestToSubscribe(authentication, messageHash, params);
243
- }
244
- handleOrder(client, message) {
245
- //
246
- // {
247
- // "accountId": "2832c5c6-ac7a-493e-bc16-ebca06c73670",
248
- // "sequence": 41,
249
- // "delta": {
250
- // "id": "b91eff76-10eb-4382-834a-b753b770283e",
251
- // "marketSymbol": "BTC-USDT",
252
- // "direction": "BUY",
253
- // "type": "LIMIT",
254
- // "quantity": "0.01000000",
255
- // "limit": "3000.00000000",
256
- // "timeInForce": "GOOD_TIL_CANCELLED",
257
- // "fillQuantity": "0.00000000",
258
- // "commission": "0.00000000",
259
- // "proceeds": "0.00000000",
260
- // "status": "OPEN",
261
- // "createdAt": "2020-10-07T12:51:43.16Z",
262
- // "updatedAt": "2020-10-07T12:51:43.16Z"
263
- // }
264
- // }
265
- //
266
- const delta = this.safeValue(message, 'delta', {});
267
- const parsed = this.parseOrder(delta);
268
- if (this.orders === undefined) {
269
- const limit = this.safeInteger(this.options, 'ordersLimit', 1000);
270
- this.orders = new Cache.ArrayCacheBySymbolById(limit);
271
- }
272
- const orders = this.orders;
273
- orders.append(parsed);
274
- const messageHash = 'order';
275
- client.resolve(this.orders, messageHash);
276
- }
277
- async watchBalance(params = {}) {
278
- /**
279
- * @method
280
- * @name bittrex#watchBalance
281
- * @description watch balance and get the amount of funds available for trading or funds locked in orders
282
- * @param {object} [params] extra parameters specific to the exchange API endpoint
283
- * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
284
- */
285
- await this.loadMarkets();
286
- const authentication = await this.authenticate();
287
- return await this.subscribeToBalance(authentication, params);
288
- }
289
- async subscribeToBalance(authentication, params = {}) {
290
- const messageHash = 'balance';
291
- return await this.sendAuthenticatedRequestToSubscribe(authentication, messageHash, params);
292
- }
293
- handleBalance(client, message) {
294
- //
295
- // {
296
- // "accountId": "2832c5c6-ac7a-493e-bc16-ebca06c73670",
297
- // "sequence": 9,
298
- // "delta": {
299
- // "currencySymbol": "USDT",
300
- // "total": "32.88918476",
301
- // "available": "2.82918476",
302
- // "updatedAt": "2020-10-06T13:49:20.29Z"
303
- // }
304
- // }
305
- //
306
- const delta = this.safeValue(message, 'delta', {});
307
- this.balance['info'] = delta;
308
- const currencyId = this.safeString(delta, 'currencySymbol');
309
- const code = this.safeCurrencyCode(currencyId);
310
- const account = this.account();
311
- account['free'] = this.safeString(delta, 'available');
312
- account['total'] = this.safeString(delta, 'total');
313
- this.balance[code] = account;
314
- this.balance = this.safeBalance(this.balance);
315
- const messageHash = 'balance';
316
- client.resolve(this.balance, messageHash);
317
- }
318
- async watchHeartbeat(params = {}) {
319
- await this.loadMarkets();
320
- const negotiation = await this.negotiate();
321
- return await this.subscribeToHeartbeat(negotiation, params);
322
- }
323
- async subscribeToHeartbeat(negotiation, params = {}) {
324
- await this.loadMarkets();
325
- const url = this.getSignalRUrl(negotiation);
326
- const requestId = this.milliseconds().toString();
327
- const messageHash = 'heartbeat';
328
- const args = [messageHash];
329
- const request = this.makeRequestToSubscribe(requestId, [args]);
330
- const subscription = {
331
- 'id': requestId,
332
- 'params': params,
333
- 'negotiation': negotiation,
334
- };
335
- return await this.watch(url, messageHash, request, messageHash, subscription);
336
- }
337
- handleHeartbeat(client, message) {
338
- //
339
- // every 20 seconds (approx) if no other updates are sent
340
- //
341
- // {}
342
- //
343
- client.resolve(message, 'heartbeat');
344
- }
345
- async watchTicker(symbol, params = {}) {
346
- /**
347
- * @method
348
- * @name bittrex#watchTicker
349
- * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
350
- * @param {string} symbol unified symbol of the market to fetch the ticker for
351
- * @param {object} [params] extra parameters specific to the exchange API endpoint
352
- * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
353
- */
354
- await this.loadMarkets();
355
- const negotiation = await this.negotiate();
356
- return await this.subscribeToTicker(negotiation, symbol, params);
357
- }
358
- async subscribeToTicker(negotiation, symbol, params = {}) {
359
- await this.loadMarkets();
360
- const market = this.market(symbol);
361
- symbol = market['symbol'];
362
- const name = 'ticker';
363
- const messageHash = name + '_' + market['id'];
364
- const subscription = {
365
- 'marketId': market['id'],
366
- 'symbol': symbol,
367
- 'params': params,
368
- };
369
- return await this.sendRequestToSubscribe(negotiation, messageHash, subscription);
370
- }
371
- handleTicker(client, message) {
372
- //
373
- // summary subscription update
374
- //
375
- // ...
376
- //
377
- // ticker subscription update
378
- //
379
- // {
380
- // "symbol": "BTC-USDT",
381
- // "lastTradeRate": "10701.02140008",
382
- // "bidRate": "10701.02140007",
383
- // "askRate": "10705.71049998"
384
- // }
385
- //
386
- const ticker = this.parseTicker(message);
387
- const symbol = ticker['symbol'];
388
- const market = this.market(symbol);
389
- this.tickers[symbol] = ticker;
390
- const name = 'ticker';
391
- const messageHash = name + '_' + market['id'];
392
- client.resolve(ticker, messageHash);
393
- }
394
- async watchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
395
- /**
396
- * @method
397
- * @name bittrex#watchOHLCV
398
- * @description watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
399
- * @param {string} symbol unified symbol of the market to fetch OHLCV data for
400
- * @param {string} timeframe the length of time each candle represents
401
- * @param {int} [since] timestamp in ms of the earliest candle to fetch
402
- * @param {int} [limit] the maximum amount of candles to fetch
403
- * @param {object} [params] extra parameters specific to the exchange API endpoint
404
- * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
405
- */
406
- await this.loadMarkets();
407
- symbol = this.symbol(symbol);
408
- const negotiation = await this.negotiate();
409
- const ohlcv = await this.subscribeToOHLCV(negotiation, symbol, timeframe, params);
410
- if (this.newUpdates) {
411
- limit = ohlcv.getLimit(symbol, limit);
412
- }
413
- return this.filterBySinceLimit(ohlcv, since, limit, 0, true);
414
- }
415
- async subscribeToOHLCV(negotiation, symbol, timeframe = '1m', params = {}) {
416
- await this.loadMarkets();
417
- const market = this.market(symbol);
418
- const interval = this.safeString(this.timeframes, timeframe, timeframe);
419
- const name = 'candle';
420
- const messageHash = name + '_' + market['id'] + '_' + interval;
421
- const subscription = {
422
- 'symbol': symbol,
423
- 'timeframe': timeframe,
424
- 'messageHash': messageHash,
425
- 'params': params,
426
- };
427
- return await this.sendRequestToSubscribe(negotiation, messageHash, subscription);
428
- }
429
- handleOHLCV(client, message) {
430
- //
431
- // {
432
- // "sequence": 28286,
433
- // "marketSymbol": "BTC-USD",
434
- // "interval": "MINUTE_1",
435
- // "delta": {
436
- // "startsAt": "2020-10-05T18:52:00Z",
437
- // "open": "10706.62600000",
438
- // "high": "10706.62600000",
439
- // "low": "10703.25900000",
440
- // "close": "10703.26000000",
441
- // "volume": "0.86822264",
442
- // "quoteVolume": "9292.84594774"
443
- // }
444
- // }
445
- //
446
- const name = 'candle';
447
- const marketId = this.safeString(message, 'marketSymbol');
448
- const symbol = this.safeSymbol(marketId, undefined, '-');
449
- const interval = this.safeString(message, 'interval');
450
- const messageHash = name + '_' + marketId + '_' + interval;
451
- const timeframe = this.findTimeframe(interval);
452
- const delta = this.safeValue(message, 'delta', {});
453
- const parsed = this.parseOHLCV(delta);
454
- this.ohlcvs[symbol] = this.safeValue(this.ohlcvs, symbol, {});
455
- let stored = this.safeValue(this.ohlcvs[symbol], timeframe);
456
- if (stored === undefined) {
457
- const limit = this.safeInteger(this.options, 'OHLCVLimit', 1000);
458
- stored = new Cache.ArrayCacheByTimestamp(limit);
459
- this.ohlcvs[symbol][timeframe] = stored;
460
- }
461
- stored.append(parsed);
462
- client.resolve(stored, messageHash);
463
- }
464
- async watchTrades(symbol, since = undefined, limit = undefined, params = {}) {
465
- /**
466
- * @method
467
- * @name bittrex#watchTrades
468
- * @description get the list of most recent trades for a particular symbol
469
- * @param {string} symbol unified symbol of the market to fetch trades for
470
- * @param {int} [since] timestamp in ms of the earliest trade to fetch
471
- * @param {int} [limit] the maximum amount of trades to fetch
472
- * @param {object} [params] extra parameters specific to the exchange API endpoint
473
- * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
474
- */
475
- await this.loadMarkets();
476
- symbol = this.symbol(symbol);
477
- const negotiation = await this.negotiate();
478
- const trades = await this.subscribeToTrades(negotiation, symbol, params);
479
- if (this.newUpdates) {
480
- limit = trades.getLimit(symbol, limit);
481
- }
482
- return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
483
- }
484
- async subscribeToTrades(negotiation, symbol, params = {}) {
485
- await this.loadMarkets();
486
- const market = this.market(symbol);
487
- const name = 'trade';
488
- const messageHash = name + '_' + market['id'];
489
- const subscription = {
490
- 'symbol': symbol,
491
- 'messageHash': messageHash,
492
- 'params': params,
493
- };
494
- return await this.sendRequestToSubscribe(negotiation, messageHash, subscription);
495
- }
496
- handleTrades(client, message) {
497
- //
498
- // {
499
- // "deltas": [
500
- // {
501
- // "id": "5bf67885-a0a8-4c62-b73d-534e480e3332",
502
- // "executedAt": "2020-10-05T23:02:17.49Z",
503
- // "quantity": "0.00166790",
504
- // "rate": "10763.97000000",
505
- // "takerSide": "BUY"
506
- // }
507
- // ],
508
- // "sequence": 24391,
509
- // "marketSymbol": "BTC-USD"
510
- // }
511
- //
512
- const deltas = this.safeValue(message, 'deltas', []);
513
- const marketId = this.safeString(message, 'marketSymbol');
514
- const symbol = this.safeSymbol(marketId, undefined, '-');
515
- const market = this.market(symbol);
516
- const name = 'trade';
517
- const messageHash = name + '_' + marketId;
518
- let stored = this.safeValue(this.trades, symbol);
519
- if (stored === undefined) {
520
- const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
521
- stored = new Cache.ArrayCache(limit);
522
- }
523
- const trades = this.parseTrades(deltas, market);
524
- for (let i = 0; i < trades.length; i++) {
525
- stored.append(trades[i]);
526
- }
527
- this.trades[symbol] = stored;
528
- client.resolve(stored, messageHash);
529
- }
530
- async watchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
531
- /**
532
- * @method
533
- * @name bittrex#watchMyTrades
534
- * @description watches information on multiple trades made by the user
535
- * @param {string} symbol unified market symbol of the market trades were made in
536
- * @param {int} [since] the earliest time in ms to fetch trades for
537
- * @param {int} [limit] the maximum number of trade structures to retrieve
538
- * @param {object} [params] extra parameters specific to the exchange API endpoint
539
- * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure
540
- */
541
- await this.loadMarkets();
542
- if (symbol !== undefined) {
543
- symbol = this.symbol(symbol);
544
- }
545
- const authentication = await this.authenticate();
546
- const trades = await this.subscribeToMyTrades(authentication, params);
547
- if (this.newUpdates) {
548
- limit = trades.getLimit(symbol, limit);
549
- }
550
- return this.filterBySymbolSinceLimit(trades, symbol, since, limit, true);
551
- }
552
- async subscribeToMyTrades(authentication, params = {}) {
553
- const messageHash = 'execution';
554
- return await this.sendAuthenticatedRequestToSubscribe(authentication, messageHash, params);
555
- }
556
- handleMyTrades(client, message) {
557
- //
558
- // {
559
- // "accountId": "2832c5c6-ac7a-493e-bc16-ebca06c73670",
560
- // "sequence": 42,
561
- // "deltas": [
562
- // {
563
- // "id": "5bf67885-a0a8-4c62-b73d-534e480e3332",
564
- // "marketSymbol": "BTC-USDT",
565
- // "executedAt": "2020-10-05T23:02:17.49Z",
566
- // "quantity": "0.00166790",
567
- // "rate": "10763.97000000",
568
- // "orderId": "string (uuid)",
569
- // "commission": "0.00000000",
570
- // "isTaker": False
571
- // }
572
- // ]
573
- // }
574
- //
575
- const deltas = this.safeValue(message, 'deltas', {});
576
- const trades = this.parseTrades(deltas);
577
- let stored = this.myTrades;
578
- if (stored === undefined) {
579
- const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
580
- stored = new Cache.ArrayCacheBySymbolById(limit);
581
- this.myTrades = stored;
582
- }
583
- for (let i = 0; i < trades.length; i++) {
584
- stored.append(trades[i]);
585
- }
586
- const messageHash = 'execution';
587
- client.resolve(stored, messageHash);
588
- }
589
- async watchOrderBook(symbol, limit = undefined, params = {}) {
590
- /**
591
- * @method
592
- * @name bittrex#watchOrderBook
593
- * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
594
- * @param {string} symbol unified symbol of the market to fetch the order book for
595
- * @param {int} [limit] the maximum amount of order book entries to return
596
- * @param {object} [params] extra parameters specific to the exchange API endpoint
597
- * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
598
- */
599
- limit = (limit === undefined) ? 25 : limit; // 25 by default
600
- if ((limit !== 1) && (limit !== 25) && (limit !== 500)) {
601
- throw new errors.BadRequest(this.id + ' watchOrderBook() limit argument must be undefined, 1, 25 or 500, default is 25');
602
- }
603
- await this.loadMarkets();
604
- symbol = this.symbol(symbol);
605
- const negotiation = await this.negotiate();
606
- //
607
- // 1. Subscribe to the relevant socket streams
608
- // 2. Begin to queue up messages without processing them
609
- // 3. Call the equivalent v3 REST API and record both the results and the value of the returned Sequence header. Refer to the descriptions of individual streams to find the corresponding REST API. Note that you must call the REST API with the same parameters as you used to subscribed to the stream to get the right snapshot. For example, orderbook snapshots of different depths will have different sequence numbers.
610
- // 4. If the Sequence header is less than the sequence number of the first queued socket message received (unlikely), discard the results of step 3 and then repeat step 3 until this check passes.
611
- // 5. Discard all socket messages where the sequence number is less than or equal to the Sequence header retrieved from the REST call
612
- // 6. Apply the remaining socket messages in order on top of the results of the REST call. The objects received in the socket deltas have the same schemas as the objects returned by the REST API. Each socket delta is a snapshot of an object. The identity of the object is defined by a unique key made up of one or more fields in the message (see documentation of individual streams for details). To apply socket deltas to a local cache of data, simply replace the objects in the cache with those coming from the socket where the keys match.
613
- // 7. Continue to apply messages as they are received from the socket as long as sequence number on the stream is always increasing by 1 each message (Note: for private streams, the sequence number is scoped to a single account or subaccount).
614
- // 8. If a message is received that is not the next in order, return to step 2 in this process
615
- //
616
- const market = this.market(symbol);
617
- const name = 'orderbook';
618
- const messageHash = name + '_' + market['id'] + '_' + limit.toString();
619
- const subscription = {
620
- 'symbol': symbol,
621
- 'messageHash': messageHash,
622
- 'method': this.handleOrderBookSubscription,
623
- 'limit': limit,
624
- 'params': params,
625
- };
626
- const orderbook = await this.sendRequestToSubscribe(negotiation, messageHash, subscription);
627
- return orderbook.limit();
628
- }
629
- async fetchOrderBookSnapshot(client, message, subscription) {
630
- const symbol = this.safeString(subscription, 'symbol');
631
- const limit = this.safeInteger(subscription, 'limit');
632
- const messageHash = this.safeString(subscription, 'messageHash');
633
- try {
634
- // 2. Initiate a REST request to get the snapshot data of Level 2 order book.
635
- // todo: this is a synch blocking call in ccxt.php - make it async
636
- const snapshot = await this.fetchRestOrderBookSafe(symbol, limit);
637
- const orderbook = this.orderbooks[symbol];
638
- const messages = orderbook.cache;
639
- // make sure we have at least one delta before fetching the snapshot
640
- // otherwise we cannot synchronize the feed with the snapshot
641
- // and that will lead to a bidask cross as reported here
642
- // https://github.com/ccxt/ccxt/issues/6762
643
- const firstMessage = this.safeValue(messages, 0, {});
644
- const sequence = this.safeInteger(firstMessage, 'sequence');
645
- const nonce = this.safeInteger(snapshot, 'nonce');
646
- // if the received snapshot is earlier than the first cached delta
647
- // then we cannot align it with the cached deltas and we need to
648
- // retry synchronizing in maxAttempts
649
- if ((sequence !== undefined) && (nonce < sequence)) {
650
- const maxAttempts = this.handleOption('watchOrderBook', 'maxRetries', 3);
651
- let numAttempts = this.safeInteger(subscription, 'numAttempts', 0);
652
- // retry to syncrhonize if we haven't reached maxAttempts yet
653
- if (numAttempts < maxAttempts) {
654
- // safety guard
655
- if (messageHash in client.subscriptions) {
656
- numAttempts = this.sum(numAttempts, 1);
657
- subscription['numAttempts'] = numAttempts;
658
- client.subscriptions[messageHash] = subscription;
659
- this.spawn(this.fetchOrderBookSnapshot, client, message, subscription);
660
- }
661
- }
662
- else {
663
- // throw upon failing to synchronize in maxAttempts
664
- throw new errors.InvalidNonce(this.id + ' failed to synchronize WebSocket feed with the snapshot for symbol ' + symbol + ' in ' + maxAttempts.toString() + ' attempts');
665
- }
666
- }
667
- else {
668
- orderbook.reset(snapshot);
669
- // unroll the accumulated deltas
670
- // 3. Playback the cached Level 2 data flow.
671
- for (let i = 0; i < messages.length; i++) {
672
- const messageItem = messages[i];
673
- this.handleOrderBookMessage(client, messageItem, orderbook);
674
- }
675
- this.orderbooks[symbol] = orderbook;
676
- client.resolve(orderbook, messageHash);
677
- }
678
- }
679
- catch (e) {
680
- client.reject(e, messageHash);
681
- }
682
- }
683
- handleOrderBookSubscription(client, message, subscription) {
684
- const symbol = this.safeString(subscription, 'symbol');
685
- const limit = this.safeInteger(subscription, 'limit');
686
- if (symbol in this.orderbooks) {
687
- delete this.orderbooks[symbol];
688
- }
689
- this.orderbooks[symbol] = this.orderBook({}, limit);
690
- this.spawn(this.fetchOrderBookSnapshot, client, message, subscription);
691
- }
692
- handleDelta(bookside, delta) {
693
- //
694
- // {
695
- // "quantity": "0.05100000",
696
- // "rate": "10694.86410031"
697
- // }
698
- //
699
- const price = this.safeFloat(delta, 'rate');
700
- const amount = this.safeFloat(delta, 'quantity');
701
- bookside.store(price, amount);
702
- }
703
- handleDeltas(bookside, deltas) {
704
- //
705
- // [
706
- // { quantity: '0.05100000', rate: "10694.86410031" },
707
- // { quantity: "0", rate: "10665.72578226" }
708
- // ]
709
- //
710
- for (let i = 0; i < deltas.length; i++) {
711
- this.handleDelta(bookside, deltas[i]);
712
- }
713
- }
714
- handleOrderBook(client, message) {
715
- //
716
- // {
717
- // "marketSymbol": "BTC-USDT",
718
- // "depth": 25,
719
- // "sequence": 3009387,
720
- // "bidDeltas": [
721
- // { quantity: '0.05100000', rate: "10694.86410031" },
722
- // { quantity: "0", rate: "10665.72578226" }
723
- // ],
724
- // "askDeltas": []
725
- // }
726
- //
727
- const marketId = this.safeString(message, 'marketSymbol');
728
- const symbol = this.safeSymbol(marketId, undefined, '-');
729
- const limit = this.safeInteger(message, 'depth');
730
- let orderbook = this.safeValue(this.orderbooks, symbol);
731
- if (orderbook === undefined) {
732
- orderbook = this.orderBook({}, limit);
733
- }
734
- if (orderbook['nonce'] !== undefined) {
735
- this.handleOrderBookMessage(client, message, orderbook);
736
- }
737
- else {
738
- orderbook.cache.push(message);
739
- }
740
- }
741
- handleOrderBookMessage(client, message, orderbook) {
742
- //
743
- // {
744
- // "marketSymbol": "BTC-USDT",
745
- // "depth": 25,
746
- // "sequence": 3009387,
747
- // "bidDeltas": [
748
- // { quantity: '0.05100000', rate: "10694.86410031" },
749
- // { quantity: "0", rate: "10665.72578226" }
750
- // ],
751
- // "askDeltas": []
752
- // }
753
- //
754
- const marketId = this.safeString(message, 'marketSymbol');
755
- const depth = this.safeString(message, 'depth');
756
- const name = 'orderbook';
757
- const messageHash = name + '_' + marketId + '_' + depth;
758
- const nonce = this.safeInteger(message, 'sequence');
759
- if (nonce > orderbook['nonce']) {
760
- this.handleDeltas(orderbook['asks'], this.safeValue(message, 'askDeltas', []));
761
- this.handleDeltas(orderbook['bids'], this.safeValue(message, 'bidDeltas', []));
762
- orderbook['nonce'] = nonce;
763
- client.resolve(orderbook, messageHash);
764
- }
765
- return orderbook;
766
- }
767
- async handleSystemStatusHelper() {
768
- const negotiation = await this.negotiate();
769
- await this.start(negotiation);
770
- }
771
- handleSystemStatus(client, message) {
772
- // send signalR protocol start() call
773
- this.spawn(this.handleSystemStatusHelper);
774
- return message;
775
- }
776
- handleSubscriptionStatus(client, message) {
777
- //
778
- // success
779
- //
780
- // { R: [ { Success: true, ErrorCode: null } ], I: "1601891513224" }
781
- //
782
- // failure
783
- // todo add error handling and future rejections
784
- //
785
- // {
786
- // "I": "1601942374563",
787
- // "E": "There was an error invoking Hub method "c3.Authenticate"."
788
- // }
789
- //
790
- const I = this.safeString(message, 'I'); // noqa: E741
791
- let subscription = this.safeValue(client.subscriptions, I);
792
- if (subscription === undefined) {
793
- const subscriptionsById = this.indexBy(client.subscriptions, 'id');
794
- subscription = this.safeValue(subscriptionsById, I, {});
795
- }
796
- else {
797
- // clear if subscriptionHash === requestId (one-time request)
798
- delete client.subscriptions[I];
799
- }
800
- const method = this.safeValue(subscription, 'method');
801
- if (method === undefined) {
802
- client.resolve(message, I);
803
- }
804
- else {
805
- method.call(this, client, message, subscription);
806
- }
807
- return message;
808
- }
809
- handleErrorMessage(client, message) {
810
- //
811
- // {
812
- // "R": [{ Success: false, ErrorCode: "UNAUTHORIZED_USER" }, ... ],
813
- // "I": "1698601759267"
814
- // }
815
- // {
816
- // "R": { Success: false, ErrorCode: "INVALID_APIKEY" },
817
- // "I": "1698601759266"
818
- // }
819
- //
820
- const R = this.safeValue(message, 'R');
821
- if (R === undefined) {
822
- // Return there is no error
823
- return false;
824
- }
825
- const I = this.safeString(message, 'I');
826
- let errorCode = undefined;
827
- if (Array.isArray(R)) {
828
- for (let i = 0; i < R.length; i++) {
829
- const response = this.safeValue(R, i);
830
- const success = this.safeValue(response, 'Success', true);
831
- if (!success) {
832
- errorCode = this.safeString(response, 'ErrorCode');
833
- break;
834
- }
835
- }
836
- }
837
- else {
838
- const success = this.safeValue(R, 'Success', true);
839
- if (!success) {
840
- errorCode = this.safeString(R, 'ErrorCode');
841
- }
842
- }
843
- if (errorCode === undefined) {
844
- // Return there is no error
845
- return false;
846
- }
847
- const feedback = this.id + ' ' + errorCode;
848
- try {
849
- this.throwExactlyMatchedException(this.exceptions['exact'], errorCode, feedback);
850
- if (message !== undefined) {
851
- this.throwBroadlyMatchedException(this.exceptions['broad'], errorCode, feedback);
852
- }
853
- throw new errors.ExchangeError(feedback);
854
- }
855
- catch (e) {
856
- if (e instanceof errors.AuthenticationError) {
857
- client.reject(e, 'authenticate');
858
- }
859
- client.reject(e, I);
860
- }
861
- return true;
862
- }
863
- handleMessage(client, message) {
864
- //
865
- // subscription confirmation
866
- //
867
- // {
868
- // "R": [
869
- // { Success: true, ErrorCode: null }
870
- // ],
871
- // "I": "1601899375696"
872
- // }
873
- //
874
- // heartbeat subscription update
875
- //
876
- // {
877
- // "C": "d-6010FB90-B,0|o_b,0|o_c,2|8,1F4E",
878
- // "M": [
879
- // { H: "C3", M: "heartbeat", A: [] }
880
- // ]
881
- // }
882
- //
883
- // heartbeat empty message
884
- //
885
- // {}
886
- //
887
- // subscription update
888
- //
889
- // {
890
- // "C": "d-ED78B69D-E,0|rq4,0|rq5,2|puI,60C",
891
- // "M": [
892
- // {
893
- // "H": "C3",
894
- // "M": "ticker", // orderBook, trade, candle, balance, order
895
- // "A": [
896
- // "q1YqrsxNys9RslJyCnHWDQ12CVHSUcpJLC4JKUpMSQ1KLEkFShkamBsa6VkYm5paGJuZAhUkZaYgpAws9QwszAwsDY1MgFKJxdlIuiz0jM3MLIHATKkWAA=="
897
- // ]
898
- // }
899
- // ]
900
- // }
901
- //
902
- // authentication expiry notification
903
- //
904
- // {
905
- // "C": "d-B1733F58-B,0|vT7,1|vT8,2|vBR,3",
906
- // "M": [ { H: "C3", M: "authenticationExpiring", A: [] } ]
907
- // }
908
- //
909
- if (this.handleErrorMessage(client, message)) {
910
- return;
911
- }
912
- const methods = {
913
- 'authenticationExpiring': this.handleAuthenticationExpiring,
914
- 'order': this.handleOrder,
915
- 'balance': this.handleBalance,
916
- 'trade': this.handleTrades,
917
- 'candle': this.handleOHLCV,
918
- 'orderBook': this.handleOrderBook,
919
- 'heartbeat': this.handleHeartbeat,
920
- 'ticker': this.handleTicker,
921
- 'execution': this.handleMyTrades,
922
- };
923
- const M = this.safeValue(message, 'M', []);
924
- for (let i = 0; i < M.length; i++) {
925
- const methodType = this.safeValue(M[i], 'M');
926
- const method = this.safeValue(methods, methodType);
927
- if (method !== undefined) {
928
- if (methodType === 'heartbeat') {
929
- method.call(this, client, message);
930
- }
931
- else if (methodType === 'authenticationExpiring') {
932
- method.call(this, client, message);
933
- }
934
- else {
935
- const A = this.safeValue(M[i], 'A', []);
936
- for (let k = 0; k < A.length; k++) {
937
- const inflated = this.decode(browser.inflateSync(this.base64ToBinary(A[k])));
938
- const update = JSON.parse(inflated);
939
- method.call(this, client, update);
940
- }
941
- }
942
- }
943
- }
944
- // resolve invocations by request id
945
- if ('I' in message) {
946
- this.handleSubscriptionStatus(client, message);
947
- }
948
- if ('S' in message) {
949
- this.handleSystemStatus(client, message);
950
- }
951
- const keys = Object.keys(message);
952
- const numKeys = keys.length;
953
- if (numKeys < 1) {
954
- this.handleHeartbeat(client, message);
955
- }
956
- }
957
- }
958
-
959
- module.exports = bittrex;