ccxt 4.2.36 → 4.2.38
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 +8 -7
- package/dist/ccxt.browser.js +3085 -602
- package/dist/ccxt.browser.min.js +7 -7
- package/dist/cjs/ccxt.js +4 -1
- package/dist/cjs/src/abstract/blofin.js +9 -0
- 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/blofin.js +2103 -0
- package/dist/cjs/src/bybit.js +13 -4
- package/dist/cjs/src/pro/gemini.js +190 -3
- package/dist/cjs/src/pro/kucoin.js +93 -40
- package/js/ccxt.d.ts +5 -2
- package/js/ccxt.js +4 -2
- package/js/src/abstract/blofin.d.ts +36 -0
- package/js/src/abstract/blofin.js +11 -0
- 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/blofin.d.ts +124 -0
- package/js/src/blofin.js +2104 -0
- 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/js/src/pro/kucoin.js +93 -40
- package/package.json +2 -2
package/js/src/bybit.js
CHANGED
|
@@ -3741,10 +3741,22 @@ export default class bybit extends Exchange {
|
|
|
3741
3741
|
if (isStopLoss) {
|
|
3742
3742
|
const slTriggerPrice = this.safeValue2(stopLoss, 'triggerPrice', 'stopPrice', stopLoss);
|
|
3743
3743
|
request['stopLoss'] = this.priceToPrecision(symbol, slTriggerPrice);
|
|
3744
|
+
const slLimitPrice = this.safeValue(stopLoss, 'price');
|
|
3745
|
+
if (slLimitPrice !== undefined) {
|
|
3746
|
+
request['tpslMode'] = 'Partial';
|
|
3747
|
+
request['slOrderType'] = 'Limit';
|
|
3748
|
+
request['slLimitPrice'] = this.priceToPrecision(symbol, slLimitPrice);
|
|
3749
|
+
}
|
|
3744
3750
|
}
|
|
3745
3751
|
if (isTakeProfit) {
|
|
3746
3752
|
const tpTriggerPrice = this.safeValue2(takeProfit, 'triggerPrice', 'stopPrice', takeProfit);
|
|
3747
3753
|
request['takeProfit'] = this.priceToPrecision(symbol, tpTriggerPrice);
|
|
3754
|
+
const tpLimitPrice = this.safeValue(takeProfit, 'price');
|
|
3755
|
+
if (tpLimitPrice !== undefined) {
|
|
3756
|
+
request['tpslMode'] = 'Partial';
|
|
3757
|
+
request['tpOrderType'] = 'Limit';
|
|
3758
|
+
request['tpLimitPrice'] = this.priceToPrecision(symbol, tpLimitPrice);
|
|
3759
|
+
}
|
|
3748
3760
|
}
|
|
3749
3761
|
}
|
|
3750
3762
|
if (market['spot']) {
|
|
@@ -6026,9 +6038,6 @@ export default class bybit extends Exchange {
|
|
|
6026
6038
|
if (timestamp === undefined) {
|
|
6027
6039
|
timestamp = this.safeIntegerN(position, ['updatedTime', 'updatedAt']);
|
|
6028
6040
|
}
|
|
6029
|
-
// default to cross of USDC margined positions
|
|
6030
|
-
const tradeMode = this.safeInteger(position, 'tradeMode', 0);
|
|
6031
|
-
const marginMode = tradeMode ? 'isolated' : 'cross';
|
|
6032
6041
|
let collateralString = this.safeString(position, 'positionBalance');
|
|
6033
6042
|
const entryPrice = this.omitZero(this.safeString2(position, 'entryPrice', 'avgPrice'));
|
|
6034
6043
|
const liquidationPrice = this.omitZero(this.safeString(position, 'liqPrice'));
|
|
@@ -6092,7 +6101,7 @@ export default class bybit extends Exchange {
|
|
|
6092
6101
|
'markPrice': this.safeNumber(position, 'markPrice'),
|
|
6093
6102
|
'lastPrice': undefined,
|
|
6094
6103
|
'collateral': this.parseNumber(collateralString),
|
|
6095
|
-
'marginMode':
|
|
6104
|
+
'marginMode': undefined,
|
|
6096
6105
|
'side': side,
|
|
6097
6106
|
'percentage': undefined,
|
|
6098
6107
|
'stopLossPrice': this.safeNumber2(position, 'stop_loss', 'stopLoss'),
|
package/js/src/pro/gemini.d.ts
CHANGED
|
@@ -4,13 +4,18 @@ import Client from '../base/ws/Client.js';
|
|
|
4
4
|
export default class gemini extends geminiRest {
|
|
5
5
|
describe(): any;
|
|
6
6
|
watchTrades(symbol: string, since?: Int, limit?: Int, params?: {}): Promise<Trade[]>;
|
|
7
|
+
watchTradesForSymbols(symbols: string[], since?: Int, limit?: Int, params?: {}): Promise<Trade[]>;
|
|
7
8
|
parseWsTrade(trade: any, market?: any): Trade;
|
|
8
9
|
handleTrade(client: Client, message: any): void;
|
|
9
10
|
handleTrades(client: Client, message: any): void;
|
|
11
|
+
handleTradesForMultidata(client: Client, trades: any, timestamp: Int): void;
|
|
10
12
|
watchOHLCV(symbol: string, timeframe?: string, since?: Int, limit?: Int, params?: {}): Promise<OHLCV[]>;
|
|
11
13
|
handleOHLCV(client: Client, message: any): any;
|
|
12
14
|
watchOrderBook(symbol: string, limit?: Int, params?: {}): Promise<OrderBook>;
|
|
13
15
|
handleOrderBook(client: Client, message: any): void;
|
|
16
|
+
watchOrderBookForSymbols(symbols: string[], limit?: Int, params?: {}): Promise<OrderBook>;
|
|
17
|
+
helperForWatchMultipleConstruct(itemHashName: string, symbols: string[], params?: {}): Promise<any>;
|
|
18
|
+
handleOrderBookForMultidata(client: Client, rawOrderBookChanges: any, timestamp: Int, nonce: Int): void;
|
|
14
19
|
handleL2Updates(client: Client, message: any): void;
|
|
15
20
|
watchOrders(symbol?: Str, since?: Int, limit?: Int, params?: {}): Promise<Order[]>;
|
|
16
21
|
handleHeartbeat(client: Client, message: any): any;
|
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/js/src/pro/kucoin.js
CHANGED
|
@@ -40,6 +40,7 @@ export default class kucoin extends kucoinRest {
|
|
|
40
40
|
'watchOrderBook': {
|
|
41
41
|
'snapshotDelay': 5,
|
|
42
42
|
'snapshotMaxRetries': 3,
|
|
43
|
+
'method': '/market/level2', // '/spotMarket/level2Depth5' or '/spotMarket/level2Depth50'
|
|
43
44
|
},
|
|
44
45
|
},
|
|
45
46
|
'streaming': {
|
|
@@ -441,10 +442,15 @@ export default class kucoin extends kucoinRest {
|
|
|
441
442
|
/**
|
|
442
443
|
* @method
|
|
443
444
|
* @name kucoin#watchOrderBook
|
|
445
|
+
* @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level1-bbo-market-data
|
|
446
|
+
* @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level2-market-data
|
|
447
|
+
* @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level2-5-best-ask-bid-orders
|
|
448
|
+
* @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level2-50-best-ask-bid-orders
|
|
444
449
|
* @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
|
|
445
450
|
* @param {string} symbol unified symbol of the market to fetch the order book for
|
|
446
451
|
* @param {int} [limit] the maximum amount of order book entries to return
|
|
447
452
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
453
|
+
* @param {string} [params.method] either '/market/level2' or '/spotMarket/level2Depth5' or '/spotMarket/level2Depth50' default is '/market/level2'
|
|
448
454
|
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
|
|
449
455
|
*/
|
|
450
456
|
//
|
|
@@ -468,10 +474,15 @@ export default class kucoin extends kucoinRest {
|
|
|
468
474
|
/**
|
|
469
475
|
* @method
|
|
470
476
|
* @name kucoin#watchOrderBookForSymbols
|
|
477
|
+
* @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level1-bbo-market-data
|
|
478
|
+
* @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level2-market-data
|
|
479
|
+
* @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level2-5-best-ask-bid-orders
|
|
480
|
+
* @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level2-50-best-ask-bid-orders
|
|
471
481
|
* @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
|
|
472
482
|
* @param {string[]} symbols unified array of symbols
|
|
473
483
|
* @param {int} [limit] the maximum amount of order book entries to return
|
|
474
484
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
485
|
+
* @param {string} [params.method] either '/market/level2' or '/spotMarket/level2Depth5' or '/spotMarket/level2Depth50' default is '/market/level2'
|
|
475
486
|
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
|
|
476
487
|
*/
|
|
477
488
|
const symbolsLength = symbols.length;
|
|
@@ -479,28 +490,36 @@ export default class kucoin extends kucoinRest {
|
|
|
479
490
|
throw new ArgumentsRequired(this.id + ' watchOrderBookForSymbols() requires a non-empty array of symbols');
|
|
480
491
|
}
|
|
481
492
|
if (limit !== undefined) {
|
|
482
|
-
if ((limit !== 20) && (limit !== 100)) {
|
|
483
|
-
throw new ExchangeError(this.id + " watchOrderBook 'limit' argument must be undefined, 20 or 100");
|
|
493
|
+
if ((limit !== 20) && (limit !== 100) && (limit !== 50) && (limit !== 5)) {
|
|
494
|
+
throw new ExchangeError(this.id + " watchOrderBook 'limit' argument must be undefined, 5, 20, 50 or 100");
|
|
484
495
|
}
|
|
485
496
|
}
|
|
486
497
|
await this.loadMarkets();
|
|
487
498
|
symbols = this.marketSymbols(symbols);
|
|
488
499
|
const marketIds = this.marketIds(symbols);
|
|
489
500
|
const url = await this.negotiate(false);
|
|
490
|
-
|
|
501
|
+
let method = undefined;
|
|
502
|
+
[method, params] = this.handleOptionAndParams(params, 'watchOrderBook', 'method', '/market/level2');
|
|
503
|
+
if ((limit === 5) || (limit === 50)) {
|
|
504
|
+
method = '/spotMarket/level2Depth' + limit.toString();
|
|
505
|
+
}
|
|
506
|
+
const topic = method + ':' + marketIds.join(',');
|
|
491
507
|
const messageHashes = [];
|
|
492
508
|
const subscriptionHashes = [];
|
|
493
509
|
for (let i = 0; i < symbols.length; i++) {
|
|
494
510
|
const symbol = symbols[i];
|
|
495
511
|
messageHashes.push('orderbook:' + symbol);
|
|
496
512
|
const marketId = marketIds[i];
|
|
497
|
-
subscriptionHashes.push('
|
|
513
|
+
subscriptionHashes.push(method + ':' + marketId);
|
|
514
|
+
}
|
|
515
|
+
let subscription = {};
|
|
516
|
+
if (method === '/market/level2') { // other streams return the entire orderbook, so we don't need to fetch the snapshot through REST
|
|
517
|
+
subscription = {
|
|
518
|
+
'method': this.handleOrderBookSubscription,
|
|
519
|
+
'symbols': symbols,
|
|
520
|
+
'limit': limit,
|
|
521
|
+
};
|
|
498
522
|
}
|
|
499
|
-
const subscription = {
|
|
500
|
-
'method': this.handleOrderBookSubscription,
|
|
501
|
-
'symbols': symbols,
|
|
502
|
-
'limit': limit,
|
|
503
|
-
};
|
|
504
523
|
const orderbook = await this.subscribeMultiple(url, messageHashes, topic, subscriptionHashes, params, subscription);
|
|
505
524
|
return orderbook.limit();
|
|
506
525
|
}
|
|
@@ -524,41 +543,74 @@ export default class kucoin extends kucoinRest {
|
|
|
524
543
|
// }
|
|
525
544
|
// }
|
|
526
545
|
//
|
|
546
|
+
// {
|
|
547
|
+
// "topic": "/spotMarket/level2Depth5:BTC-USDT",
|
|
548
|
+
// "type": "message",
|
|
549
|
+
// "data": {
|
|
550
|
+
// "asks": [
|
|
551
|
+
// [
|
|
552
|
+
// "42815.6",
|
|
553
|
+
// "1.24016245"
|
|
554
|
+
// ]
|
|
555
|
+
// ],
|
|
556
|
+
// "bids": [
|
|
557
|
+
// [
|
|
558
|
+
// "42815.5",
|
|
559
|
+
// "0.08652716"
|
|
560
|
+
// ]
|
|
561
|
+
// ],
|
|
562
|
+
// "timestamp": 1707204474018
|
|
563
|
+
// },
|
|
564
|
+
// "subject": "level2"
|
|
565
|
+
// }
|
|
566
|
+
//
|
|
527
567
|
const data = this.safeValue(message, 'data');
|
|
528
|
-
const
|
|
568
|
+
const subject = this.safeString(message, 'subject');
|
|
569
|
+
const topic = this.safeString(message, 'topic');
|
|
570
|
+
const topicParts = topic.split(':');
|
|
571
|
+
const topicSymbol = this.safeString(topicParts, 1);
|
|
572
|
+
const topicChannel = this.safeString(topicParts, 0);
|
|
573
|
+
const marketId = this.safeString(data, 'symbol', topicSymbol);
|
|
529
574
|
const symbol = this.safeSymbol(marketId, undefined, '-');
|
|
530
575
|
const messageHash = 'orderbook:' + symbol;
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
const cacheLength = storedOrderBook.cache.length;
|
|
536
|
-
const topic = this.safeString(message, 'topic');
|
|
537
|
-
const topicParts = topic.split(':');
|
|
538
|
-
const topicSymbol = this.safeString(topicParts, 1);
|
|
539
|
-
const topicChannel = this.safeString(topicParts, 0);
|
|
540
|
-
const subscriptions = Object.keys(client.subscriptions);
|
|
541
|
-
let subscription = undefined;
|
|
542
|
-
for (let i = 0; i < subscriptions.length; i++) {
|
|
543
|
-
const key = subscriptions[i];
|
|
544
|
-
if ((key.indexOf(topicSymbol) >= 0) && (key.indexOf(topicChannel) >= 0)) {
|
|
545
|
-
subscription = client.subscriptions[key];
|
|
546
|
-
break;
|
|
547
|
-
}
|
|
576
|
+
let orderbook = this.safeDict(this.orderbooks, symbol);
|
|
577
|
+
if (subject === 'level2') {
|
|
578
|
+
if (orderbook === undefined) {
|
|
579
|
+
orderbook = this.orderBook();
|
|
548
580
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
if (cacheLength === snapshotDelay) {
|
|
552
|
-
this.spawn(this.loadOrderBook, client, messageHash, symbol, limit, {});
|
|
581
|
+
else {
|
|
582
|
+
orderbook.reset();
|
|
553
583
|
}
|
|
554
|
-
|
|
555
|
-
return;
|
|
584
|
+
orderbook['symbol'] = symbol;
|
|
556
585
|
}
|
|
557
|
-
else
|
|
558
|
-
|
|
586
|
+
else {
|
|
587
|
+
const nonce = this.safeInteger(orderbook, 'nonce');
|
|
588
|
+
const deltaEnd = this.safeInteger2(data, 'sequenceEnd', 'timestamp');
|
|
589
|
+
if (nonce === undefined) {
|
|
590
|
+
const cacheLength = orderbook.cache.length;
|
|
591
|
+
const subscriptions = Object.keys(client.subscriptions);
|
|
592
|
+
let subscription = undefined;
|
|
593
|
+
for (let i = 0; i < subscriptions.length; i++) {
|
|
594
|
+
const key = subscriptions[i];
|
|
595
|
+
if ((key.indexOf(topicSymbol) >= 0) && (key.indexOf(topicChannel) >= 0)) {
|
|
596
|
+
subscription = client.subscriptions[key];
|
|
597
|
+
break;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
const limit = this.safeInteger(subscription, 'limit');
|
|
601
|
+
const snapshotDelay = this.handleOption('watchOrderBook', 'snapshotDelay', 5);
|
|
602
|
+
if (cacheLength === snapshotDelay) {
|
|
603
|
+
this.spawn(this.loadOrderBook, client, messageHash, symbol, limit, {});
|
|
604
|
+
}
|
|
605
|
+
orderbook.cache.push(data);
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
else if (nonce >= deltaEnd) {
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
559
611
|
}
|
|
560
|
-
this.handleDelta(
|
|
561
|
-
client.resolve(
|
|
612
|
+
this.handleDelta(orderbook, data);
|
|
613
|
+
client.resolve(orderbook, messageHash);
|
|
562
614
|
}
|
|
563
615
|
getCacheIndex(orderbook, cache) {
|
|
564
616
|
const firstDelta = this.safeValue(cache, 0);
|
|
@@ -578,11 +630,11 @@ export default class kucoin extends kucoinRest {
|
|
|
578
630
|
return cache.length;
|
|
579
631
|
}
|
|
580
632
|
handleDelta(orderbook, delta) {
|
|
581
|
-
|
|
582
|
-
|
|
633
|
+
const timestamp = this.safeInteger2(delta, 'time', 'timestamp');
|
|
634
|
+
orderbook['nonce'] = this.safeInteger(delta, 'sequenceEnd', timestamp);
|
|
583
635
|
orderbook['timestamp'] = timestamp;
|
|
584
636
|
orderbook['datetime'] = this.iso8601(timestamp);
|
|
585
|
-
const changes = this.safeValue(delta, 'changes');
|
|
637
|
+
const changes = this.safeValue(delta, 'changes', delta);
|
|
586
638
|
const bids = this.safeValue(changes, 'bids', []);
|
|
587
639
|
const asks = this.safeValue(changes, 'asks', []);
|
|
588
640
|
const storedBids = orderbook['bids'];
|
|
@@ -996,6 +1048,7 @@ export default class kucoin extends kucoinRest {
|
|
|
996
1048
|
}
|
|
997
1049
|
const subject = this.safeString(message, 'subject');
|
|
998
1050
|
const methods = {
|
|
1051
|
+
'level2': this.handleOrderBook,
|
|
999
1052
|
'trade.l2update': this.handleOrderBook,
|
|
1000
1053
|
'trade.ticker': this.handleTicker,
|
|
1001
1054
|
'trade.snapshot': this.handleTicker,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccxt",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.38",
|
|
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",
|