@kraken-crypto/ccxt
Version:
A cryptocurrency trading API with more than 100 exchanges in JavaScript / TypeScript / Python / C# / PHP / Go
1,135 lines (1,132 loc) • 69.2 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var bingx$1 = require('../bingx.js');
var errors = require('../base/errors.js');
var Cache = require('../base/ws/Cache.js');
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
class bingx extends bingx$1["default"] {
describe() {
return this.deepExtend(super.describe(), {
'has': {
'ws': true,
'watchTrades': true,
'watchTradesForSymbols': false,
'watchOrderBook': true,
'watchOrderBookForSymbols': true,
'watchOHLCV': true,
'watchOHLCVForSymbols': false,
'watchOrders': true,
'watchMyTrades': true,
'watchTicker': true,
'watchTickers': false,
'watchBalance': true,
'unWatchOHLCV': true,
'unWatchOrderBook': true,
'unWatchTicker': true,
'unWatchTrades': true,
},
'urls': {
'api': {
'ws': {
'spot': 'wss://open-api-ws.bingx.com/market',
'linear': 'wss://open-api-swap.bingx.com/swap-market',
'inverse': 'wss://open-api-cswap-ws.bingx.com/market',
},
},
},
'options': {
'listenKeyRefreshRate': 3540000,
'ws': {
'gunzip': true,
},
'swap': {
'timeframes': {
'1m': '1m',
'3m': '3m',
'5m': '5m',
'15m': '15m',
'30m': '30m',
'1h': '1h',
'2h': '2h',
'4h': '4h',
'6h': '6h',
'12h': '12h',
'1d': '1d',
'3d': '3d',
'1w': '1w',
'1M': '1M',
},
},
'spot': {
'timeframes': {
'1m': '1min',
'5m': '5min',
'15m': '15min',
'30m': '30min',
'1h': '60min',
'1d': '1day',
},
},
'watchBalance': {
'fetchBalanceSnapshot': true,
'awaitBalanceSnapshot': false, // whether to wait for the balance snapshot before providing updates
},
'watchOrderBook': {
'depth': 100, // 5, 10, 20, 50, 100
// 'interval': 500, // 100, 200, 500, 1000
},
'watchTrades': {
'ignoreDuplicates': true,
},
},
'streaming': {
'keepAlive': 1800000, // 30 minutes
},
});
}
async unWatch(messageHash, subMessageHash, subscribeHash, dataType, topic, market, methodName, params = {}) {
let marketType = undefined;
let subType = undefined;
let url = undefined;
[marketType, params] = this.handleMarketTypeAndParams(methodName, market, params);
[subType, params] = this.handleSubTypeAndParams(methodName, market, params, 'linear');
if (marketType === 'swap') {
url = this.safeString(this.urls['api']['ws'], subType);
}
else {
url = this.safeString(this.urls['api']['ws'], marketType);
}
const id = this.uuid();
const request = {
'id': id,
'dataType': dataType,
'reqType': 'unsub',
};
const symbols = [];
if (market !== undefined) {
symbols.push(market['symbol']);
}
const subscription = {
'unsubscribe': true,
'id': id,
'subMessageHashes': [subMessageHash],
'messageHashes': [messageHash],
'symbols': symbols,
'topic': topic,
};
const symbolsAndTimeframes = this.safeList(params, 'symbolsAndTimeframes');
if (symbolsAndTimeframes !== undefined) {
subscription['symbolsAndTimeframes'] = symbolsAndTimeframes;
params = this.omit(params, 'symbolsAndTimeframes');
}
return await this.watch(url, messageHash, this.extend(request, params), subscribeHash, subscription);
}
/**
* @method
* @name bingx#watchTicker
* @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
* @see https://bingx-api.github.io/docs/#/en-us/spot/socket/market.html#Subscribe%20to%2024-hour%20Price%20Change
* @see https://bingx-api.github.io/docs/#/en-us/swapV2/socket/market.html#Subscribe%20to%2024-hour%20price%20changes
* @see https://bingx-api.github.io/docs/#/en-us/cswap/socket/market.html#Subscribe%20to%2024-Hour%20Price%20Change
* @param {string} symbol unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async watchTicker(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
let marketType = undefined;
let subType = undefined;
let url = undefined;
[marketType, params] = this.handleMarketTypeAndParams('watchTicker', market, params);
[subType, params] = this.handleSubTypeAndParams('watchTicker', market, params, 'linear');
if (marketType === 'swap') {
url = this.safeString(this.urls['api']['ws'], subType);
}
else {
url = this.safeString(this.urls['api']['ws'], marketType);
}
const dataType = market['id'] + '@ticker';
const messageHash = this.getMessageHash('ticker', market['symbol']);
const uuid = this.uuid();
const request = {
'id': uuid,
'dataType': dataType,
};
if (marketType === 'swap') {
request['reqType'] = 'sub';
}
const subscription = {
'unsubscribe': false,
'id': uuid,
};
return await this.watch(url, messageHash, this.extend(request, params), messageHash, subscription);
}
/**
* @method
* @name bingx#unWatchTicker
* @description unWatches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
* @see https://bingx-api.github.io/docs/#/en-us/spot/socket/market.html#Subscribe%20to%2024-hour%20Price%20Change
* @see https://bingx-api.github.io/docs/#/en-us/swapV2/socket/market.html#Subscribe%20to%2024-hour%20price%20changes
* @see https://bingx-api.github.io/docs/#/en-us/cswap/socket/market.html#Subscribe%20to%2024-Hour%20Price%20Change
* @param {string} symbol unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async unWatchTicker(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const dataType = market['id'] + '@ticker';
const subMessageHash = this.getMessageHash('ticker', market['symbol']);
const messageHash = 'unsubscribe::' + subMessageHash;
const topic = 'ticker';
const methodName = 'unWatchTicker';
return await this.unWatch(messageHash, subMessageHash, messageHash, dataType, topic, market, methodName, params);
}
handleTicker(client, message) {
//
// swap
//
// {
// "code": 0,
// "dataType": "BTC-USDT@ticker",
// "data": {
// "e": "24hTicker",
// "E": 1706498923556,
// "s": "BTC-USDT",
// "p": "346.4",
// "P": "0.82",
// "c": "42432.5",
// "L": "0.0529",
// "h": "42855.4",
// "l": "41578.3",
// "v": "64310.9754",
// "q": "2728360284.15",
// "o": "42086.1",
// "O": 1706498922655,
// "C": 1706498883023,
// "A": "42437.8",
// "a": "1.4160",
// "B": "42437.1",
// "b": "2.5747"
// }
// }
//
// spot
//
// {
// "code": 0,
// "timestamp": 1706506795473,
// "data": {
// "e": "24hTicker",
// "E": 1706506795472,
// "s": "BTC-USDT",
// "p": -372.12,
// "P": "-0.87%",
// "o": 42548.95,
// "h": 42696.1,
// "l": 41621.29,
// "c": 42176.83,
// "v": 4943.33,
// "q": 208842236.5,
// "O": 1706420395472,
// "C": 1706506795472,
// "A": 42177.23,
// "a": 5.14484,
// "B": 42176.38,
// "b": 5.36117
// }
// }
//
const data = this.safeValue(message, 'data', {});
const marketId = this.safeString(data, 's');
// const marketId = messageHash.split('@')[0];
const isSwap = client.url.indexOf('swap') >= 0;
const marketType = isSwap ? 'swap' : 'spot';
const market = this.safeMarket(marketId, undefined, undefined, marketType);
const symbol = market['symbol'];
const ticker = this.parseWsTicker(data, market);
this.tickers[symbol] = ticker;
client.resolve(ticker, this.getMessageHash('ticker', symbol));
if (this.safeString(message, 'dataType') === 'all@ticker') {
client.resolve(ticker, this.getMessageHash('ticker'));
}
}
parseWsTicker(message, market = undefined) {
//
// {
// "e": "24hTicker",
// "E": 1706498923556,
// "s": "BTC-USDT",
// "p": "346.4",
// "P": "0.82",
// "c": "42432.5",
// "L": "0.0529",
// "h": "42855.4",
// "l": "41578.3",
// "v": "64310.9754",
// "q": "2728360284.15",
// "o": "42086.1",
// "O": 1706498922655,
// "C": 1706498883023,
// "A": "42437.8",
// "a": "1.4160",
// "B": "42437.1",
// "b": "2.5747"
// }
//
const timestamp = this.safeInteger(message, 'C');
const marketId = this.safeString(message, 's');
market = this.safeMarket(marketId, market);
const close = this.safeString(message, 'c');
return this.safeTicker({
'symbol': market['symbol'],
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'high': this.safeString(message, 'h'),
'low': this.safeString(message, 'l'),
'bid': this.safeString(message, 'B'),
'bidVolume': this.safeString(message, 'b'),
'ask': this.safeString(message, 'A'),
'askVolume': this.safeString(message, 'a'),
'vwap': undefined,
'open': this.safeString(message, 'o'),
'close': close,
'last': close,
'previousClose': undefined,
'change': this.safeString(message, 'p'),
'percentage': undefined,
'average': undefined,
'baseVolume': this.safeString(message, 'v'),
'quoteVolume': this.safeString(message, 'q'),
'info': message,
}, market);
}
getOrderBookLimitByMarketType(marketType, limit = undefined) {
if (limit === undefined) {
limit = 100;
}
else {
if (marketType === 'swap' || marketType === 'future') {
limit = this.findNearestCeiling([5, 10, 20, 50, 100], limit);
}
else if (marketType === 'spot') {
limit = this.findNearestCeiling([20, 100], limit);
}
}
return limit;
}
getMessageHash(unifiedChannel, symbol = undefined, extra = undefined) {
let hash = unifiedChannel;
if (symbol !== undefined) {
hash += '::' + symbol;
}
else {
hash += 's'; // tickers, orderbooks, ohlcvs ...
}
if (extra !== undefined) {
hash += '::' + extra;
}
return hash;
}
/**
* @method
* @name bingx#watchTrades
* @description watches information on multiple trades made in a market
* @see https://bingx-api.github.io/docs/#/en-us/spot/socket/market.html#Subscription%20transaction%20by%20transaction
* @see https://bingx-api.github.io/docs/#/en-us/swapV2/socket/market.html#Subscribe%20the%20Latest%20Trade%20Detail
* @see https://bingx-api.github.io/docs/#/en-us/cswap/socket/market.html#Subscription%20transaction%20by%20transaction
* @param {string} symbol unified market symbol of the market orders were made in
* @param {int} [since] the earliest time in ms to fetch orders for
* @param {int} [limit] the maximum number of order structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async watchTrades(symbol, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
symbol = market['symbol'];
let marketType = undefined;
let subType = undefined;
let url = undefined;
[marketType, params] = this.handleMarketTypeAndParams('watchTrades', market, params);
[subType, params] = this.handleSubTypeAndParams('watchTrades', market, params, 'linear');
if (marketType === 'swap') {
url = this.safeString(this.urls['api']['ws'], subType);
}
else {
url = this.safeString(this.urls['api']['ws'], marketType);
}
const rawHash = market['id'] + '@trade';
const messageHash = 'trade::' + symbol;
const uuid = this.uuid();
const request = {
'id': uuid,
'dataType': rawHash,
};
if (marketType === 'swap') {
request['reqType'] = 'sub';
}
const subscription = {
'unsubscribe': false,
'id': uuid,
};
const trades = await this.watch(url, messageHash, this.extend(request, params), messageHash, subscription);
if (this.newUpdates) {
limit = trades.getLimit(symbol, limit);
}
const result = this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
if (this.handleOption('watchTrades', 'ignoreDuplicates', true)) {
let filtered = this.removeRepeatedTradesFromArray(result);
filtered = this.sortBy(filtered, 'timestamp');
return filtered;
}
return result;
}
/**
* @method
* @name bingx#unWatchTrades
* @description unsubscribes from the trades channel
* @see https://bingx-api.github.io/docs/#/en-us/spot/socket/market.html#Subscription%20transaction%20by%20transaction
* @see https://bingx-api.github.io/docs/#/en-us/swapV2/socket/market.html#Subscribe%20the%20Latest%20Trade%20Detail
* @see https://bingx-api.github.io/docs/#/en-us/cswap/socket/market.html#Subscription%20transaction%20by%20transaction
* @param {string} symbol unified symbol of the market to fetch trades for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.name] the name of the method to call, 'trade' or 'aggTrade', default is 'trade'
* @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
*/
async unWatchTrades(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const dataType = market['id'] + '@trade';
const subMessageHash = this.getMessageHash('trade', market['symbol']);
const messageHash = 'unsubscribe::' + subMessageHash;
const topic = 'trades';
const methodName = 'unWatchTrades';
return await this.unWatch(messageHash, subMessageHash, messageHash, dataType, topic, market, methodName, params);
}
handleTrades(client, message) {
//
// spot: first snapshot
//
// {
// "id": "d83b78ce-98be-4dc2-b847-12fe471b5bc5",
// "code": 0,
// "msg": "SUCCESS",
// "timestamp": 1690214699854
// }
//
// spot: subsequent updates
//
// {
// "code": 0,
// "data": {
// "E": 1690214529432,
// "T": 1690214529386,
// "e": "trade",
// "m": true,
// "p": "29110.19",
// "q": "0.1868",
// "s": "BTC-USDT",
// "t": "57903921"
// },
// "dataType": "BTC-USDT@trade",
// "success": true
// }
//
// linear swap: first snapshot
//
// {
// "id": "2aed93b1-6e1e-4038-aeba-f5eeaec2ca48",
// "code": 0,
// "msg": '',
// "dataType": '',
// "data": null
// }
//
// linear swap: subsequent updates
//
// {
// "code": 0,
// "dataType": "BTC-USDT@trade",
// "data": [
// {
// "q": "0.0421",
// "p": "29023.5",
// "T": 1690221401344,
// "m": false,
// "s": "BTC-USDT"
// },
// ...
// ]
// }
//
// inverse swap: first snapshot
//
// {
// "code": 0,
// "id": "a2e482ca-f71b-42f8-a83a-8ff85a713e64",
// "msg": "SUCCESS",
// "timestamp": 1722920589426
// }
//
// inverse swap: subsequent updates
//
// {
// "code": 0,
// "dataType": "BTC-USD@trade",
// "data": {
// "e": "trade",
// "E": 1722920589665,
// "s": "BTC-USD",
// "t": "39125001",
// "p": "55360.0",
// "q": "1",
// "T": 1722920589582,
// "m": false
// }
// }
//
const data = this.safeValue(message, 'data', []);
const rawHash = this.safeString(message, 'dataType');
const marketId = rawHash.split('@')[0];
const isSwap = client.url.indexOf('swap') >= 0;
const marketType = isSwap ? 'swap' : 'spot';
const market = this.safeMarket(marketId, undefined, undefined, marketType);
const symbol = market['symbol'];
const messageHash = 'trade::' + symbol;
let trades = undefined;
if (Array.isArray(data)) {
trades = this.parseTrades(data, market);
}
else {
trades = [this.parseTrade(data, market)];
}
let stored = this.safeValue(this.trades, symbol);
if (stored === undefined) {
const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
stored = new Cache.ArrayCache(limit);
this.trades[symbol] = stored;
}
for (let j = 0; j < trades.length; j++) {
stored.append(trades[j]);
}
client.resolve(stored, messageHash);
}
/**
* @method
* @name bingx#watchOrderBook
* @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://bingx-api.github.io/docs/#/en-us/spot/socket/market.html#Subscribe%20Market%20Depth%20Data
* @see https://bingx-api.github.io/docs/#/en-us/swapV2/socket/market.html#Subscribe%20Market%20Depth%20Data
* @see https://bingx-api.github.io/docs/#/en-us/cswap/socket/market.html#Subscribe%20to%20Limited%20Depth
* @param {string} symbol unified symbol of the market to fetch the order book for
* @param {int} [limit] the maximum amount of order book entries to return
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
*/
async watchOrderBook(symbol, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
let marketType = undefined;
let subType = undefined;
let url = undefined;
[marketType, params] = this.handleMarketTypeAndParams('watchOrderBook', market, params);
[subType, params] = this.handleSubTypeAndParams('watchOrderBook', market, params, 'linear');
if (marketType === 'swap') {
url = this.safeString(this.urls['api']['ws'], subType);
}
else {
url = this.safeString(this.urls['api']['ws'], marketType);
}
const options = this.safeDict(this.options, 'watchOrderBook', {});
const depth = this.safeInteger(options, 'depth', 100);
const subscriptionHash = market['id'] + '@' + 'depth' + this.numberToString(depth);
const messageHash = this.getMessageHash('orderbook', market['symbol']);
const uuid = this.uuid();
const request = {
'id': uuid,
'dataType': subscriptionHash,
};
if (marketType === 'swap') {
request['reqType'] = 'sub';
}
let subscriptionArgs = {};
if (market['inverse']) {
subscriptionArgs = {
'id': uuid,
'unsubscribe': false,
'count': limit,
'params': params,
};
}
else {
subscriptionArgs = {
'id': uuid,
'unsubscribe': false,
'level': limit,
'params': params,
};
}
const orderbook = await this.watch(url, messageHash, this.deepExtend(request, params), subscriptionHash, subscriptionArgs);
return orderbook.limit();
}
/**
* @method
* @name bingx#unWatchOrderBook
* @description unWatches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://bingx-api.github.io/docs/#/en-us/spot/socket/market.html#Subscribe%20Market%20Depth%20Data
* @see https://bingx-api.github.io/docs/#/en-us/swapV2/socket/market.html#Subscribe%20Market%20Depth%20Data
* @see https://bingx-api.github.io/docs/#/en-us/cswap/socket/market.html#Subscribe%20to%20Limited%20Depth
* @param {string} symbol unified symbol of the market
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
*/
async unWatchOrderBook(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const options = this.safeDict(this.options, 'watchOrderBook', {});
const depth = this.safeInteger(options, 'depth', 100);
const subMessageHash = market['id'] + '@' + 'depth' + this.numberToString(depth);
const messageHash = 'unsubscribe::' + subMessageHash;
const topic = 'orderbook';
const methodName = 'unWatchOrderBook';
return await this.unWatch(messageHash, subMessageHash, messageHash, subMessageHash, topic, market, methodName, params);
}
/**
* @method
* @name bingx#watchOrderBookForSymbols
* @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://bingx-api.github.io/docs/#/en-us/swapV2/socket/market.html#Subscribe%20Market%20Depth%20Data
* @param {string[]} symbols unified array of symbols
* @param {int} [limit] the maximum amount of order book entries to return, must be one of: 5, 10, 20, 50, 100 (default is 100)
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.interval] the interval for orderbook updates, must be one of: 100, 200, 500, 1000. If not specified, defaults to 200ms for BTC-USDT and ETH-USDT, 500ms for all other contracts
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
* @remarks It seems the interval parameter does not take effect - BingX automatically sets the push interval based on the symbol (200ms for BTC-USDT and ETH-USDT, 500ms for other contracts)
*/
async watchOrderBookForSymbols(symbols, limit = undefined, params = {}) {
await this.loadMarkets();
symbols = this.marketSymbols(symbols, undefined, true, true, false);
let firstMarket = undefined;
let marketType = undefined;
let subType = undefined;
if (symbols.length > 0) {
firstMarket = this.market(symbols[0]);
}
[marketType, params] = this.handleMarketTypeAndParams('watchOrderBookForSymbols', firstMarket, params);
[subType, params] = this.handleSubTypeAndParams('watchOrderBookForSymbols', firstMarket, params, 'linear');
if (marketType === 'spot') {
throw new errors.NotSupported(this.id + ' watchOrderBookForSymbols is not supported for spot markets');
}
if (subType === 'inverse') {
throw new errors.NotSupported(this.id + ' watchOrderBookForSymbols is not supported for inverse markets');
}
limit = this.getOrderBookLimitByMarketType(marketType, limit);
if (limit !== undefined && ![5, 10, 20, 50, 100].includes(limit)) {
throw new errors.BadRequest(this.id + ' watchOrderBookForSymbols limit must be one of: 5, 10, 20, 50, 100');
}
let interval = undefined;
[interval, params] = this.handleOptionAndParams(params, 'watchOrderBookForSymbols', 'interval', undefined);
if (interval !== undefined && ![100, 200, 500, 1000].includes(interval)) {
throw new errors.BadRequest(this.id + ' watchOrderBookForSymbols interval must be one of: 100, 200, 500, 1000');
}
const url = this.safeString(this.urls['api']['ws'], subType);
const promises = [];
for (let i = 0; i < symbols.length; i++) {
const symbol = symbols[i];
const market = this.market(symbol);
const marketId = market['id'];
// If interval is not specified, set it based on symbol: 200ms for BTC-USDT and ETH-USDT, 500ms for others
if (interval === undefined) {
interval = (marketId === 'BTC-USDT' || marketId === 'ETH-USDT') ? 200 : 500;
}
const channelName = 'depth' + limit.toString() + '@' + interval.toString() + 'ms';
const subscriptionHash = marketId + '@' + channelName;
const messageHash = this.getMessageHash('orderbook', market['symbol']);
const uuid = this.uuid();
const request = {
'id': uuid,
'reqType': 'sub',
'dataType': subscriptionHash,
};
const subscription = {
'unsubscribe': false,
'id': uuid,
'limit': limit,
'interval': interval,
'params': params,
};
promises.push(this.watch(url, messageHash, this.deepExtend(request, params), subscriptionHash, subscription));
}
const orderbook = await Promise.race(promises);
return orderbook.limit();
}
handleDelta(bookside, delta) {
const price = this.safeFloat2(delta, 0, 'p');
const amount = this.safeFloat2(delta, 1, 'a');
bookside.store(price, amount);
}
handleOrderBook(client, message) {
//
// spot
//
// {
// "code":0,
// "data":
// {
// "asks":[
// ["84119.73","0.000011"],
// ["84116.52","0.000014"],
// ["84116.40","0.000039"]
// ],
// "bids":[
// ["83656.98","2.570805"],
// ["83655.51","0.000347"],
// ["83654.59","0.000082"]
// ],
// "lastUpdateId":13565694850
// },
// "dataType":"BTC-USDT@depth100",
// "success":true,
// "timestamp":1743241379958
// }
//
// linear swap
//
// {
// "code":0,
// "dataType":"BTC-USDT@depth100@500ms",
// "ts":1743241563651,
// "data":
// {
// "bids":[
// ["83363.2","0.1908"],
// ["83360.0","0.0003"],
// ["83356.5","0.0245"],
// ],
// "asks":[
// ["83495.0","0.0024"],
// ["83490.0","0.0001"],
// ["83488.0","0.0004"],
// ]
// }
// }
//
// inverse swap
//
// {
// "code":0,
// "dataType":"BTC-USD@depth100",
// "data":{
// "symbol":"BTC-USD",
// "bids":[
// {"p":"83411.2","a":"2.979216","v":"2485.0"},
// {"p":"83411.1","a":"1.592114","v":"1328.0"},
// {"p":"83410.8","a":"2.656730","v":"2216.0"},
// ],
// "asks":[
// {"p":"88200.0","a":"0.344671","v":"304.0"},
// {"p":"88023.8","a":"0.045442","v":"40.0"},
// {"p":"88001.0","a":"0.003409","v":"3.0"},
// ],
// "aggPrecision":"0.1",
// "timestamp":1743242290710
// }
// }
//
const data = this.safeDict(message, 'data', {});
const dataType = this.safeString(message, 'dataType');
const parts = dataType.split('@');
const firstPart = parts[0];
const isAllEndpoint = (firstPart === 'all');
const marketId = this.safeString(data, 'symbol', firstPart);
const isSwap = client.url.indexOf('swap') >= 0;
const marketType = isSwap ? 'swap' : 'spot';
const market = this.safeMarket(marketId, undefined, undefined, marketType);
const symbol = market['symbol'];
let orderbook = this.safeValue(this.orderbooks, symbol);
if (orderbook === undefined) {
// const limit = [ 5, 10, 20, 50, 100 ]
const subscriptionHash = dataType;
const subscription = client.subscriptions[subscriptionHash];
const limit = this.safeInteger(subscription, 'limit');
this.orderbooks[symbol] = this.orderBook({}, limit);
}
orderbook = this.orderbooks[symbol];
let snapshot = undefined;
let timestamp = this.safeInteger2(message, 'timestamp', 'ts');
timestamp = this.safeInteger2(data, 'timestamp', 'ts', timestamp);
if (market['inverse']) {
snapshot = this.parseOrderBook(data, symbol, timestamp, 'bids', 'asks', 'p', 'a');
}
else {
snapshot = this.parseOrderBook(data, symbol, timestamp, 'bids', 'asks', 0, 1);
}
const nonce = this.safeInteger(data, 'lastUpdateId');
snapshot['nonce'] = nonce;
orderbook.reset(snapshot);
const messageHash = this.getMessageHash('orderbook', symbol);
client.resolve(orderbook, messageHash);
// resolve for "all"
if (isAllEndpoint) {
const messageHashForAll = this.getMessageHash('orderbook');
client.resolve(orderbook, messageHashForAll);
}
}
parseWsOHLCV(ohlcv, market = undefined) {
//
// {
// "c": "28909.0",
// "o": "28915.4",
// "h": "28915.4",
// "l": "28896.1",
// "v": "27.6919",
// "T": 1696687499999,
// "t": 1696687440000
// }
//
// for spot, opening-time (t) is used instead of closing-time (T), to be compatible with fetchOHLCV
// for linear swap, (T) is the opening time
let timestamp = (market['spot']) ? 't' : 'T';
if (market['swap']) {
timestamp = (market['inverse']) ? 't' : 'T';
}
return [
this.safeInteger(ohlcv, timestamp),
this.safeNumber(ohlcv, 'o'),
this.safeNumber(ohlcv, 'h'),
this.safeNumber(ohlcv, 'l'),
this.safeNumber(ohlcv, 'c'),
this.safeNumber(ohlcv, 'v'),
];
}
handleOHLCV(client, message) {
//
// spot:
//
// {
// "code": 0,
// "data": {
// "E": 1696687498608,
// "K": {
// "T": 1696687499999,
// "c": "27917.829",
// "h": "27918.427",
// "i": "1min",
// "l": "27917.7",
// "n": 262,
// "o": "27917.91",
// "q": "25715.359197",
// "s": "BTC-USDT",
// "t": 1696687440000,
// "v": "0.921100"
// },
// "e": "kline",
// "s": "BTC-USDT"
// },
// "dataType": "BTC-USDT@kline_1min",
// "success": true
// }
//
// linear swap:
//
// {
// "code": 0,
// "dataType": "BTC-USDT@kline_1m",
// "s": "BTC-USDT",
// "data": [
// {
// "c": "28909.0",
// "o": "28915.4",
// "h": "28915.4",
// "l": "28896.1",
// "v": "27.6919",
// "T": 1690907580000
// }
// ]
// }
//
// inverse swap:
//
// {
// "code": 0,
// "timestamp": 1723769354547,
// "dataType": "BTC-USD@kline_1m",
// "data": {
// "t": 1723769340000,
// "o": 57485.1,
// "c": 57468,
// "l": 57464.9,
// "h": 57485.1,
// "a": 0.189663,
// "v": 109,
// "u": 92,
// "s": "BTC-USD"
// }
// }
//
const isSwap = client.url.indexOf('swap') >= 0;
const dataType = this.safeString(message, 'dataType');
const parts = dataType.split('@');
const firstPart = parts[0];
const isAllEndpoint = (firstPart === 'all');
const marketId = this.safeString(message, 's', firstPart);
const marketType = isSwap ? 'swap' : 'spot';
const market = this.safeMarket(marketId, undefined, undefined, marketType);
let candles = undefined;
if (isSwap) {
if (market['inverse']) {
candles = [this.safeDict(message, 'data', {})];
}
else {
candles = this.safeList(message, 'data', []);
}
}
else {
const data = this.safeDict(message, 'data', {});
candles = [this.safeDict(data, 'K', {})];
}
const symbol = market['symbol'];
this.ohlcvs[symbol] = this.safeValue(this.ohlcvs, symbol, {});
const rawTimeframe = dataType.split('_')[1];
const marketOptions = this.safeDict(this.options, marketType);
const timeframes = this.safeDict(marketOptions, 'timeframes', {});
const unifiedTimeframe = this.findTimeframe(rawTimeframe, timeframes);
if (this.safeValue(this.ohlcvs[symbol], rawTimeframe) === undefined) {
const subscriptionHash = dataType;
const subscription = client.subscriptions[subscriptionHash];
const limit = this.safeInteger(subscription, 'limit');
this.ohlcvs[symbol][unifiedTimeframe] = new Cache.ArrayCacheByTimestamp(limit);
}
const stored = this.ohlcvs[symbol][unifiedTimeframe];
for (let i = 0; i < candles.length; i++) {
const candle = candles[i];
const parsed = this.parseWsOHLCV(candle, market);
stored.append(parsed);
}
const resolveData = [symbol, unifiedTimeframe, stored];
const messageHash = this.getMessageHash('ohlcv', symbol, unifiedTimeframe);
client.resolve(resolveData, messageHash);
// resolve for "all"
if (isAllEndpoint) {
const messageHashForAll = this.getMessageHash('ohlcv', undefined, unifiedTimeframe);
client.resolve(resolveData, messageHashForAll);
}
}
/**
* @method
* @name bingx#watchOHLCV
* @description watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @see https://bingx-api.github.io/docs/#/en-us/spot/socket/market.html#K-line%20Streams
* @see https://bingx-api.github.io/docs/#/en-us/swapV2/socket/market.html#Subscribe%20K-Line%20Data
* @see https://bingx-api.github.io/docs/#/en-us/cswap/socket/market.html#Subscribe%20to%20Latest%20Trading%20Pair%20K-Line
* @param {string} symbol unified symbol of the market to fetch OHLCV data for
* @param {string} timeframe the length of time each candle represents
* @param {int} [since] timestamp in ms of the earliest candle to fetch
* @param {int} [limit] the maximum amount of candles to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
*/
async watchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
let marketType = undefined;
let subType = undefined;
let url = undefined;
[marketType, params] = this.handleMarketTypeAndParams('watchOHLCV', market, params);
[subType, params] = this.handleSubTypeAndParams('watchOHLCV', market, params, 'linear');
if (marketType === 'swap') {
url = this.safeString(this.urls['api']['ws'], subType);
}
else {
url = this.safeString(this.urls['api']['ws'], marketType);
}
if (url === undefined) {
throw new errors.BadRequest(this.id + ' watchOHLCV is not supported for ' + marketType + ' markets.');
}
const options = this.safeValue(this.options, marketType, {});
const timeframes = this.safeValue(options, 'timeframes', {});
const rawTimeframe = this.safeString(timeframes, timeframe, timeframe);
const messageHash = this.getMessageHash('ohlcv', market['symbol'], timeframe);
const subscriptionHash = market['id'] + '@kline_' + rawTimeframe;
const uuid = this.uuid();
const request = {
'id': uuid,
'dataType': subscriptionHash,
};
if (marketType === 'swap') {
request['reqType'] = 'sub';
}
const subscriptionArgs = {
'id': uuid,
'unsubscribe': false,
'interval': rawTimeframe,
'params': params,
};
const result = await this.watch(url, messageHash, this.extend(request, params), subscriptionHash, subscriptionArgs);
const ohlcv = result[2];
if (this.newUpdates) {
limit = ohlcv.getLimit(symbol, limit);
}
return this.filterBySinceLimit(ohlcv, since, limit, 0, true);
}
/**
* @method
* @name bingx#unWatchOHLCV
* @description unWatches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @see https://bingx-api.github.io/docs/#/en-us/spot/socket/market.html#K-line%20Streams
* @see https://bingx-api.github.io/docs/#/en-us/swapV2/socket/market.html#Subscribe%20K-Line%20Data
* @see https://bingx-api.github.io/docs/#/en-us/cswap/socket/market.html#Subscribe%20to%20Latest%20Trading%20Pair%20K-Line
* @param {string} symbol unified symbol of the market to fetch OHLCV data for
* @param {string} timeframe the length of time each candle represents
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
*/
async unWatchOHLCV(symbol, timeframe = '1m', params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const options = this.safeValue(this.options, market['type'], {});
const timeframes = this.safeValue(options, 'timeframes', {});
const rawTimeframe = this.safeString(timeframes, timeframe, timeframe);
const subMessageHash = market['id'] + '@kline_' + rawTimeframe;
const messageHash = 'unsubscribe::' + subMessageHash;
const topic = 'ohlcv';
const methodName = 'unWatchOHLCV';
const symbolsAndTimeframes = [[market['symbol'], timeframe]];
params['symbolsAndTimeframes'] = symbolsAndTimeframes;
return await this.unWatch(messageHash, subMessageHash, messageHash, subMessageHash, topic, market, methodName, params);
}
/**
* @method
* @name bingx#watchOrders
* @description watches information on multiple orders made by the user
* @see https://bingx-api.github.io/docs/#/en-us/spot/socket/account.html#Subscription%20order%20update%20data
* @see https://bingx-api.github.io/docs/#/en-us/swapV2/socket/account.html#Order%20update%20push
* @see https://bingx-api.github.io/docs/#/en-us/cswap/socket/account.html#Order%20update%20push
* @param {string} [symbol] unified market symbol of the market orders are made in
* @param {int} [since] the earliest time in ms to watch orders for
* @param {int} [limit] the maximum number of order structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
await this.authenticate();
let type = undefined;
let subType = undefined;
let market = undefined;
if (symbol !== undefined) {
market = this.market(symbol);
symbol = market['symbol'];
}
[type, params] = this.handleMarketTypeAndParams('watchOrders', market, params);
[subType, params] = this.handleSubTypeAndParams('watchOrders', market, params, 'linear');
const isSpot = (type === 'spot');
const spotHash = 'spot:private';
const swapHash = 'swap:private';
const subscriptionHash = isSpot ? spotHash : swapHash;
const spotMessageHash = 'spot:order';
const swapMessageHash = 'swap:order';
let messageHash = isSpot ? spotMessageHash : swapMessageHash;
if (market !== undefined) {
messageHash += ':' + symbol;
}
const uuid = this.uuid();
let baseUrl = undefined;
let request = undefined;
if (type === 'swap') {
if (subType === 'inverse') {
throw new errors.NotSupported(this.id + ' watchOrders is not supported for inverse swap markets yet');
}
baseUrl = this.safeString(this.urls['api']['ws'], subType);
}
else {
baseUrl = this.safeString(this.urls['api']['ws'], type);
request = {
'id': uuid,
'reqType': 'sub',
'dataType': 'spot.executionReport',
};
}
const url = baseUrl + '?listenKey=' + this.options['listenKey'];
const subscription = {
'unsubscribe': false,
'id': uuid,
};
const orders = await this.watch(url, messageHash, request, subscriptionHash, subscription);
if (this.newUpdates) {
limit = orders.getLimit(symbol, limit);
}
return this.filterBySymbolSinceLimit(orders, symbol, since, limit, true);
}
/**
* @method
* @name bingx#watchMyTrades
* @description watches information on multiple trades made by the user
* @see https://bingx-api.github.io/docs/#/en-us/spot/socket/account.html#Subscription%20order%20update%20data
* @see https://bingx-api.github.io/docs/#/en-us/swapV2/socket/account.html#Order%20update%20push
* @see https://bingx-api.github.io/docs/#/en-us/cswap/socket/account.html#Order%20update%20push
* @param {string} [symbol] unified market symbol of the market the trades are made in
* @param {int} [since] the earliest time in ms to watch trades for
* @param {int} [limit] the maximum number of trade structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
*/
async watchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
await this.authenticate();
let type = undefined;
let subType = undefined;
let market = undefined;
if (symbol !== undefined) {
market = this.market(symbol);
symbol = market['symbol'];
}
[type, params] = this.handleMarketTypeAndParams('watchMyTrades', market, params);
[subType, params] = this.handleSubTypeAndParams('watchMyTrades', market, params, 'linear');
const isSpot = (type === 'spot');
const spotHash = 'spot:private';
const swapHash = 'swap:private';
const subscriptionHash = isSpot ? spotHash : swapHash;
const spotMessageHash = 'spot:mytrades';
const swapMessageHash = 'swap:mytrades';
let messageHash = isSpot ? spotMessageHash : swapMessageHash;
if (market !== undefined) {
messageHash += ':' + symbol;
}
const uuid = this.uuid();
let baseUrl = undefined;
let request = undefined;
if (type === 'swap') {
if (subType === 'inverse') {
throw new errors.NotSupported(this.id + ' watchMyTrades is not supported for inverse swap markets yet');
}
baseUrl = this.safeString(this.urls['api']['ws'], subType);
}
else {
baseUrl = this.safeString(this.urls['api']['ws'], type);
request = {
'id': uuid,
'reqType': 'sub',
'dataType': 'spot.executionReport',
};
}
const url = baseUrl + '?listenKey=' + this.options['listenKey'];
const subscription = {
'unsubscribe': false,
'id': uuid,
};
const trades = await this.watch(url, messageHash, request, subscriptionHash, subscription);
if (this.newUpdates) {
limit = trades.getLimit(symbol, limit);
}
return this.filterBySymbolSinceLimit(trades, symbol, since, limit, true);
}
/**
* @method
* @name bingx#watchBalance
* @description query for balance and get the amount of funds available for trading or funds locked in orders
* @see https://bingx-api.github.io/docs/#/en-us/spot/socket/account.html#Subscription%20account%20balance%20push
* @see h