ccxt 4.2.36 → 4.2.37
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.
- package/CONTRIBUTING.md +3 -2
- package/README.md +5 -5
- package/dist/ccxt.browser.js +495 -198
- package/dist/ccxt.browser.min.js +2 -2
- package/dist/cjs/ccxt.js +1 -1
- package/dist/cjs/src/binance.js +147 -65
- package/dist/cjs/src/bitfinex2.js +122 -122
- package/dist/cjs/src/bitmex.js +22 -3
- package/dist/cjs/src/bybit.js +13 -4
- package/dist/cjs/src/pro/gemini.js +190 -3
- package/js/ccxt.d.ts +1 -1
- package/js/ccxt.js +1 -1
- package/js/src/base/types.d.ts +2 -0
- package/js/src/binance.js +147 -65
- package/js/src/bitfinex2.js +122 -122
- package/js/src/bitmex.js +22 -3
- package/js/src/bybit.js +13 -4
- package/js/src/pro/gemini.d.ts +5 -0
- package/js/src/pro/gemini.js +191 -4
- package/package.json +2 -2
package/js/src/pro/gemini.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// ---------------------------------------------------------------------------
|
|
8
8
|
import geminiRest from '../gemini.js';
|
|
9
9
|
import { ArrayCache, ArrayCacheBySymbolById, ArrayCacheByTimestamp } from '../base/ws/Cache.js';
|
|
10
|
-
import { ExchangeError } from '../base/errors.js';
|
|
10
|
+
import { ExchangeError, NotSupported } from '../base/errors.js';
|
|
11
11
|
import { sha384 } from '../static_dependencies/noble-hashes/sha512.js';
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
13
|
export default class gemini extends geminiRest {
|
|
@@ -19,9 +19,11 @@ export default class gemini extends geminiRest {
|
|
|
19
19
|
'watchTicker': false,
|
|
20
20
|
'watchTickers': false,
|
|
21
21
|
'watchTrades': true,
|
|
22
|
+
'watchTradesForSymbols': true,
|
|
22
23
|
'watchMyTrades': false,
|
|
23
24
|
'watchOrders': true,
|
|
24
25
|
'watchOrderBook': true,
|
|
26
|
+
'watchOrderBookForSymbols': true,
|
|
25
27
|
'watchOHLCV': true,
|
|
26
28
|
},
|
|
27
29
|
'hostname': 'api.gemini.com',
|
|
@@ -70,7 +72,29 @@ export default class gemini extends geminiRest {
|
|
|
70
72
|
}
|
|
71
73
|
return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
|
|
72
74
|
}
|
|
75
|
+
async watchTradesForSymbols(symbols, since = undefined, limit = undefined, params = {}) {
|
|
76
|
+
/**
|
|
77
|
+
* @method
|
|
78
|
+
* @name gemini#watchTradesForSymbols
|
|
79
|
+
* @see https://docs.gemini.com/websocket-api/#multi-market-data
|
|
80
|
+
* @description get the list of most recent trades for a list of symbols
|
|
81
|
+
* @param {string[]} symbols unified symbol of the market to fetch trades for
|
|
82
|
+
* @param {int} [since] timestamp in ms of the earliest trade to fetch
|
|
83
|
+
* @param {int} [limit] the maximum amount of trades to fetch
|
|
84
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
85
|
+
* @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
|
|
86
|
+
*/
|
|
87
|
+
const trades = await this.helperForWatchMultipleConstruct('trades', symbols, params);
|
|
88
|
+
if (this.newUpdates) {
|
|
89
|
+
const first = this.safeList(trades, 0);
|
|
90
|
+
const tradeSymbol = this.safeString(first, 'symbol');
|
|
91
|
+
limit = trades.getLimit(tradeSymbol, limit);
|
|
92
|
+
}
|
|
93
|
+
return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
|
|
94
|
+
}
|
|
73
95
|
parseWsTrade(trade, market = undefined) {
|
|
96
|
+
//
|
|
97
|
+
// regular v2 trade
|
|
74
98
|
//
|
|
75
99
|
// {
|
|
76
100
|
// "type": "trade",
|
|
@@ -82,11 +106,31 @@ export default class gemini extends geminiRest {
|
|
|
82
106
|
// "side": "buy"
|
|
83
107
|
// }
|
|
84
108
|
//
|
|
109
|
+
// multi data trade
|
|
110
|
+
//
|
|
111
|
+
// {
|
|
112
|
+
// "type": "trade",
|
|
113
|
+
// "symbol": "ETHUSD",
|
|
114
|
+
// "tid": "1683002242170204", // this is not TS, but somewhat ID
|
|
115
|
+
// "price": "2299.24",
|
|
116
|
+
// "amount": "0.002662",
|
|
117
|
+
// "makerSide": "bid"
|
|
118
|
+
// }
|
|
119
|
+
//
|
|
85
120
|
const timestamp = this.safeInteger(trade, 'timestamp');
|
|
86
|
-
const id = this.
|
|
121
|
+
const id = this.safeString2(trade, 'event_id', 'tid');
|
|
87
122
|
const priceString = this.safeString(trade, 'price');
|
|
88
|
-
const amountString = this.
|
|
89
|
-
|
|
123
|
+
const amountString = this.safeString2(trade, 'quantity', 'amount');
|
|
124
|
+
let side = this.safeStringLower(trade, 'side');
|
|
125
|
+
if (side === undefined) {
|
|
126
|
+
const marketSide = this.safeStringLower(trade, 'makerSide');
|
|
127
|
+
if (marketSide === 'bid') {
|
|
128
|
+
side = 'sell';
|
|
129
|
+
}
|
|
130
|
+
else if (marketSide === 'ask') {
|
|
131
|
+
side = 'buy';
|
|
132
|
+
}
|
|
133
|
+
}
|
|
90
134
|
const marketId = this.safeStringLower(trade, 'symbol');
|
|
91
135
|
const symbol = this.safeSymbol(marketId, market);
|
|
92
136
|
return this.safeTrade({
|
|
@@ -186,6 +230,34 @@ export default class gemini extends geminiRest {
|
|
|
186
230
|
client.resolve(stored, messageHash);
|
|
187
231
|
}
|
|
188
232
|
}
|
|
233
|
+
handleTradesForMultidata(client, trades, timestamp) {
|
|
234
|
+
if (trades !== undefined) {
|
|
235
|
+
const tradesLimit = this.safeInteger(this.options, 'tradesLimit', 1000);
|
|
236
|
+
const storesForSymbols = {};
|
|
237
|
+
for (let i = 0; i < trades.length; i++) {
|
|
238
|
+
const marketId = trades[i]['symbol'];
|
|
239
|
+
const market = this.safeMarket(marketId.toLowerCase());
|
|
240
|
+
const symbol = market['symbol'];
|
|
241
|
+
const trade = this.parseWsTrade(trades[i], market);
|
|
242
|
+
trade['timestamp'] = timestamp;
|
|
243
|
+
trade['datetime'] = this.iso8601(timestamp);
|
|
244
|
+
let stored = this.safeValue(this.trades, symbol);
|
|
245
|
+
if (stored === undefined) {
|
|
246
|
+
stored = new ArrayCache(tradesLimit);
|
|
247
|
+
this.trades[symbol] = stored;
|
|
248
|
+
}
|
|
249
|
+
stored.append(trade);
|
|
250
|
+
storesForSymbols[symbol] = stored;
|
|
251
|
+
}
|
|
252
|
+
const symbols = Object.keys(storesForSymbols);
|
|
253
|
+
for (let i = 0; i < symbols.length; i++) {
|
|
254
|
+
const symbol = symbols[i];
|
|
255
|
+
const stored = storesForSymbols[symbol];
|
|
256
|
+
const messageHash = 'trades:' + symbol;
|
|
257
|
+
client.resolve(stored, messageHash);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
189
261
|
async watchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
|
|
190
262
|
/**
|
|
191
263
|
* @method
|
|
@@ -331,6 +403,93 @@ export default class gemini extends geminiRest {
|
|
|
331
403
|
this.orderbooks[symbol] = orderbook;
|
|
332
404
|
client.resolve(orderbook, messageHash);
|
|
333
405
|
}
|
|
406
|
+
async watchOrderBookForSymbols(symbols, limit = undefined, params = {}) {
|
|
407
|
+
/**
|
|
408
|
+
* @method
|
|
409
|
+
* @name gemini#watchOrderBookForSymbols
|
|
410
|
+
* @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
|
|
411
|
+
* @see https://docs.gemini.com/websocket-api/#multi-market-data
|
|
412
|
+
* @param {string[]} symbols unified array of symbols
|
|
413
|
+
* @param {int} [limit] the maximum amount of order book entries to return
|
|
414
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
415
|
+
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
|
|
416
|
+
*/
|
|
417
|
+
const orderbook = await this.helperForWatchMultipleConstruct('orderbook', symbols, params);
|
|
418
|
+
return orderbook.limit();
|
|
419
|
+
}
|
|
420
|
+
async helperForWatchMultipleConstruct(itemHashName, symbols, params = {}) {
|
|
421
|
+
await this.loadMarkets();
|
|
422
|
+
symbols = this.marketSymbols(symbols, undefined, false, true, true);
|
|
423
|
+
const firstMarket = this.market(symbols[0]);
|
|
424
|
+
if (!firstMarket['spot'] && !firstMarket['linear']) {
|
|
425
|
+
throw new NotSupported(this.id + ' watchMultiple supports only spot or linear-swap symbols');
|
|
426
|
+
}
|
|
427
|
+
const messageHashes = [];
|
|
428
|
+
const marketIds = [];
|
|
429
|
+
for (let i = 0; i < symbols.length; i++) {
|
|
430
|
+
const symbol = symbols[i];
|
|
431
|
+
const messageHash = itemHashName + ':' + symbol;
|
|
432
|
+
messageHashes.push(messageHash);
|
|
433
|
+
const market = this.market(symbol);
|
|
434
|
+
marketIds.push(market['id']);
|
|
435
|
+
}
|
|
436
|
+
const queryStr = marketIds.join(',');
|
|
437
|
+
let url = this.urls['api']['ws'] + '/v1/multimarketdata?symbols=' + queryStr + '&heartbeat=true&';
|
|
438
|
+
if (itemHashName === 'orderbook') {
|
|
439
|
+
url += 'trades=false&bids=true&offers=true';
|
|
440
|
+
}
|
|
441
|
+
else if (itemHashName === 'trades') {
|
|
442
|
+
url += 'trades=true&bids=false&offers=false';
|
|
443
|
+
}
|
|
444
|
+
return await this.watchMultiple(url, messageHashes, undefined);
|
|
445
|
+
}
|
|
446
|
+
handleOrderBookForMultidata(client, rawOrderBookChanges, timestamp, nonce) {
|
|
447
|
+
//
|
|
448
|
+
// rawOrderBookChanges
|
|
449
|
+
//
|
|
450
|
+
// [
|
|
451
|
+
// {
|
|
452
|
+
// delta: "4105123935484.817624",
|
|
453
|
+
// price: "0.000000001",
|
|
454
|
+
// reason: "initial", // initial|cancel|place
|
|
455
|
+
// remaining: "4105123935484.817624",
|
|
456
|
+
// side: "bid", // bid|ask
|
|
457
|
+
// symbol: "SHIBUSD",
|
|
458
|
+
// type: "change", // seems always change
|
|
459
|
+
// },
|
|
460
|
+
// ...
|
|
461
|
+
//
|
|
462
|
+
const marketId = rawOrderBookChanges[0]['symbol'];
|
|
463
|
+
const market = this.safeMarket(marketId.toLowerCase());
|
|
464
|
+
const symbol = market['symbol'];
|
|
465
|
+
const messageHash = 'orderbook:' + symbol;
|
|
466
|
+
let orderbook = this.safeDict(this.orderbooks, symbol);
|
|
467
|
+
if (orderbook === undefined) {
|
|
468
|
+
orderbook = this.orderBook();
|
|
469
|
+
}
|
|
470
|
+
const bids = orderbook['bids'];
|
|
471
|
+
const asks = orderbook['asks'];
|
|
472
|
+
for (let i = 0; i < rawOrderBookChanges.length; i++) {
|
|
473
|
+
const entry = rawOrderBookChanges[i];
|
|
474
|
+
const price = this.safeNumber(entry, 'price');
|
|
475
|
+
const size = this.safeNumber(entry, 'remaining');
|
|
476
|
+
const rawSide = this.safeString(entry, 'side');
|
|
477
|
+
if (rawSide === 'bid') {
|
|
478
|
+
bids.store(price, size);
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
asks.store(price, size);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
orderbook['bids'] = bids;
|
|
485
|
+
orderbook['asks'] = asks;
|
|
486
|
+
orderbook['symbol'] = symbol;
|
|
487
|
+
orderbook['nonce'] = nonce;
|
|
488
|
+
orderbook['timestamp'] = timestamp;
|
|
489
|
+
orderbook['datetime'] = this.iso8601(timestamp);
|
|
490
|
+
this.orderbooks[symbol] = orderbook;
|
|
491
|
+
client.resolve(orderbook, messageHash);
|
|
492
|
+
}
|
|
334
493
|
handleL2Updates(client, message) {
|
|
335
494
|
//
|
|
336
495
|
// {
|
|
@@ -411,6 +570,7 @@ export default class gemini extends geminiRest {
|
|
|
411
570
|
// "socket_sequence": 7
|
|
412
571
|
// }
|
|
413
572
|
//
|
|
573
|
+
client.lastPong = this.milliseconds();
|
|
414
574
|
return message;
|
|
415
575
|
}
|
|
416
576
|
handleSubscription(client, message) {
|
|
@@ -613,6 +773,33 @@ export default class gemini extends geminiRest {
|
|
|
613
773
|
if (method !== undefined) {
|
|
614
774
|
method.call(this, client, message);
|
|
615
775
|
}
|
|
776
|
+
// handle multimarketdata
|
|
777
|
+
if (type === 'update') {
|
|
778
|
+
const ts = this.safeInteger(message, 'timestampms', this.milliseconds());
|
|
779
|
+
const eventId = this.safeInteger(message, 'eventId');
|
|
780
|
+
const events = this.safeList(message, 'events');
|
|
781
|
+
const orderBookItems = [];
|
|
782
|
+
const collectedEventsOfTrades = [];
|
|
783
|
+
for (let i = 0; i < events.length; i++) {
|
|
784
|
+
const event = events[i];
|
|
785
|
+
const eventType = this.safeString(event, 'type');
|
|
786
|
+
const isOrderBook = (eventType === 'change') && ('side' in event) && this.inArray(event['side'], ['ask', 'bid']);
|
|
787
|
+
if (isOrderBook) {
|
|
788
|
+
orderBookItems.push(event);
|
|
789
|
+
}
|
|
790
|
+
else if (eventType === 'trade') {
|
|
791
|
+
collectedEventsOfTrades.push(events[i]);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
const lengthOb = orderBookItems.length;
|
|
795
|
+
if (lengthOb > 0) {
|
|
796
|
+
this.handleOrderBookForMultidata(client, orderBookItems, ts, eventId);
|
|
797
|
+
}
|
|
798
|
+
const lengthTrades = collectedEventsOfTrades.length;
|
|
799
|
+
if (lengthTrades > 0) {
|
|
800
|
+
this.handleTradesForMultidata(client, collectedEventsOfTrades, ts);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
616
803
|
}
|
|
617
804
|
async authenticate(params = {}) {
|
|
618
805
|
const url = this.safeString(params, 'url');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccxt",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.37",
|
|
4
4
|
"description": "A JavaScript / TypeScript / Python / C# / PHP cryptocurrency trading library with support for 100+ exchanges",
|
|
5
5
|
"unpkg": "dist/ccxt.browser.js",
|
|
6
6
|
"type": "module",
|
|
@@ -144,7 +144,7 @@
|
|
|
144
144
|
"as-table": "1.0.37",
|
|
145
145
|
"asciichart": "^1.5.25",
|
|
146
146
|
"assert": "^2.0.0",
|
|
147
|
-
"ast-transpiler": "^0.0.
|
|
147
|
+
"ast-transpiler": "^0.0.43",
|
|
148
148
|
"docsify": "^4.11.4",
|
|
149
149
|
"eslint": "8.22.0",
|
|
150
150
|
"eslint-config-airbnb-base": "15.0.0",
|