UNPKG

ccxt

Version:

A cryptocurrency trading API with more than 100 exchanges in JavaScript / TypeScript / Python / C# / PHP / Go

1,107 lines • 115 kB
// --------------------------------------------------------------------------- import bitgetRest from '../bitget.js'; import { AuthenticationError, BadRequest, ArgumentsRequired, ChecksumError, ExchangeError, RateLimitExceeded, UnsubscribeError } from '../base/errors.js'; import { Precise } from '../base/Precise.js'; import { ArrayCache, ArrayCacheBySymbolById, ArrayCacheBySymbolBySide, ArrayCacheByTimestamp } from '../base/ws/Cache.js'; import { sha256 } from '../static_dependencies/noble-hashes/sha256.js'; // --------------------------------------------------------------------------- /** * @class bitget * @augments Exchange * @description watching delivery future markets is not yet implemented (perpertual future & swap is implemented) */ export default class bitget extends bitgetRest { describe() { return this.deepExtend(super.describe(), { 'has': { 'ws': true, 'createOrderWs': false, 'editOrderWs': false, 'fetchOpenOrdersWs': false, 'fetchOrderWs': false, 'cancelOrderWs': false, 'cancelOrdersWs': false, 'cancelAllOrdersWs': false, 'watchBalance': true, 'watchMyTrades': true, 'watchOHLCV': true, 'watchOHLCVForSymbols': false, 'watchOrderBook': true, 'watchOrderBookForSymbols': true, 'watchOrders': true, 'watchTicker': true, 'watchTickers': true, 'watchBidsAsks': true, 'watchTrades': true, 'watchTradesForSymbols': true, 'watchPositions': true, }, 'urls': { 'api': { 'ws': { 'public': 'wss://ws.bitget.com/v2/ws/public', 'private': 'wss://ws.bitget.com/v2/ws/private', 'utaPublic': 'wss://ws.bitget.com/v3/ws/public', 'utaPrivate': 'wss://ws.bitget.com/v3/ws/private', }, 'demo': { 'public': 'wss://wspap.bitget.com/v2/ws/public', 'private': 'wss://wspap.bitget.com/v2/ws/private', 'utaPublic': 'wss://wspap.bitget.com/v3/ws/public', 'utaPrivate': 'wss://wspap.bitget.com/v3/ws/private', }, }, }, 'options': { 'tradesLimit': 1000, 'OHLCVLimit': 1000, // WS timeframes differ from REST timeframes 'timeframes': { '1m': '1m', '3m': '3m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1H', '4h': '4H', '6h': '6H', '12h': '12H', '1d': '1D', '1w': '1W', }, 'watchOrderBook': { 'checksum': true, }, 'watchTrades': { 'ignoreDuplicates': true, }, }, 'streaming': { 'ping': this.ping, }, 'exceptions': { 'ws': { 'exact': { '30001': BadRequest, '30002': AuthenticationError, '30003': BadRequest, '30004': AuthenticationError, '30005': AuthenticationError, '30006': RateLimitExceeded, '30007': RateLimitExceeded, '30011': AuthenticationError, '30012': AuthenticationError, '30013': AuthenticationError, '30014': BadRequest, '30015': AuthenticationError, '30016': BadRequest, // { event: 'error', code: 30016, msg: 'Param error' } }, 'broad': {}, }, }, }); } getInstType(market, uta = false, params = {}) { if ((uta === undefined) || !uta) { [uta, params] = this.handleOptionAndParams(params, 'getInstType', 'uta', false); } let instType = undefined; if (market === undefined) { [instType, params] = this.handleProductTypeAndParams(undefined, params); } else if ((market['swap']) || (market['future'])) { [instType, params] = this.handleProductTypeAndParams(market, params); } else { instType = 'SPOT'; } let instypeAux = undefined; [instypeAux, params] = this.handleOptionAndParams(params, 'getInstType', 'instType', instType); instType = instypeAux; if (uta) { instType = instType.toLowerCase(); } return [instType, params]; } /** * @method * @name bitget#watchTicker * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market * @see https://www.bitget.com/api-doc/spot/websocket/public/Tickers-Channel * @see https://www.bitget.com/api-doc/contract/websocket/public/Tickers-Channel * @see https://www.bitget.com/api-doc/uta/websocket/public/Tickers-Channel * @param {string} symbol unified symbol of the market to watch the ticker for * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {boolean} [params.uta] set to true for the unified trading account (uta), defaults to false * @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); symbol = market['symbol']; const messageHash = 'ticker:' + symbol; let instType = undefined; let uta = undefined; [uta, params] = this.handleOptionAndParams(params, 'watchTicker', 'uta', false); [instType, params] = this.getInstType(market, uta, params); const args = { 'instType': instType, }; const topicOrChannel = uta ? 'topic' : 'channel'; const symbolOrInstId = uta ? 'symbol' : 'instId'; args[topicOrChannel] = 'ticker'; args[symbolOrInstId] = market['id']; return await this.watchPublic(messageHash, args, params); } /** * @method * @name bitget#unWatchTicker * @description unsubscribe from the ticker channel * @see https://www.bitget.com/api-doc/spot/websocket/public/Tickers-Channel * @see https://www.bitget.com/api-doc/contract/websocket/public/Tickers-Channel * @param {string} symbol unified symbol of the market to unwatch the ticker for * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {any} status of the unwatch request */ async unWatchTicker(symbol, params = {}) { await this.loadMarkets(); return await this.unWatchChannel(symbol, 'ticker', 'ticker', params); } /** * @method * @name bitget#watchTickers * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list * @see https://www.bitget.com/api-doc/spot/websocket/public/Tickers-Channel * @see https://www.bitget.com/api-doc/contract/websocket/public/Tickers-Channel * @see https://www.bitget.com/api-doc/uta/websocket/public/Tickers-Channel * @param {string[]} symbols unified symbol of the market to watch the tickers for * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {boolean} [params.uta] set to true for the unified trading account (uta), defaults to false * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure} */ async watchTickers(symbols = undefined, params = {}) { await this.loadMarkets(); symbols = this.marketSymbols(symbols, undefined, false); const market = this.market(symbols[0]); let instType = undefined; let uta = undefined; [uta, params] = this.handleOptionAndParams(params, 'watchTickers', 'uta', false); [instType, params] = this.getInstType(market, uta, params); const topics = []; const messageHashes = []; for (let i = 0; i < symbols.length; i++) { const symbol = symbols[i]; const marketInner = this.market(symbol); const args = { 'instType': instType, }; const topicOrChannel = uta ? 'topic' : 'channel'; const symbolOrInstId = uta ? 'symbol' : 'instId'; args[topicOrChannel] = 'ticker'; args[symbolOrInstId] = marketInner['id']; topics.push(args); messageHashes.push('ticker:' + symbol); } const tickers = await this.watchPublicMultiple(messageHashes, topics, params); if (this.newUpdates) { const result = {}; result[tickers['symbol']] = tickers; return result; } return this.filterByArray(this.tickers, 'symbol', symbols); } handleTicker(client, message) { // // default // // { // "action": "snapshot", // "arg": { // "instType": "SPOT", // "channel": "ticker", // "instId": "BTCUSDT" // }, // "data": [ // { // "instId": "BTCUSDT", // "lastPr": "43528.19", // "open24h": "42267.78", // "high24h": "44490.00", // "low24h": "41401.53", // "change24h": "0.03879", // "bidPr": "43528", // "askPr": "43528.01", // "bidSz": "0.0334", // "askSz": "0.1917", // "baseVolume": "15002.4216", // "quoteVolume": "648006446.7164", // "openUtc": "44071.18", // "changeUtc24h": "-0.01232", // "ts": "1701842994338" // } // ], // "ts": 1701842994341 // } // // uta // // { // "action": "snapshot", // "arg": { "instType": "spot", topic: "ticker", symbol: "BTCUSDT" }, // "data": [ // { // "highPrice24h": "120255.61", // "lowPrice24h": "116145.88", // "openPrice24h": "118919.38", // "lastPrice": "119818.83", // "turnover24h": "215859996.272276", // "volume24h": "1819.756798", // "bid1Price": "119811.26", // "ask1Price": "119831.18", // "bid1Size": "0.008732", // "ask1Size": "0.004297", // "price24hPcnt": "0.02002" // } // ], // "ts": 1753230479687 // } // this.handleBidAsk(client, message); const ticker = this.parseWsTicker(message); const symbol = ticker['symbol']; this.tickers[symbol] = ticker; const messageHash = 'ticker:' + symbol; client.resolve(ticker, messageHash); } parseWsTicker(message, market = undefined) { // // spot // // { // "action": "snapshot", // "arg": { // "instType": "SPOT", // "channel": "ticker", // "instId": "BTCUSDT" // }, // "data": [ // { // "instId": "BTCUSDT", // "lastPr": "43528.19", // "open24h": "42267.78", // "high24h": "44490.00", // "low24h": "41401.53", // "change24h": "0.03879", // "bidPr": "43528", // "askPr": "43528.01", // "bidSz": "0.0334", // "askSz": "0.1917", // "baseVolume": "15002.4216", // "quoteVolume": "648006446.7164", // "openUtc": "44071.18", // "changeUtc24h": "-0.01232", // "ts": "1701842994338" // } // ], // "ts": 1701842994341 // } // // contract // // { // "action": "snapshot", // "arg": { // "instType": "USDT-FUTURES", // "channel": "ticker", // "instId": "BTCUSDT" // }, // "data": [ // { // "instId": "BTCUSDT", // "lastPr": "43480.4", // "bidPr": "43476.3", // "askPr": "43476.8", // "bidSz": "0.1", // "askSz": "3.055", // "open24h": "42252.3", // "high24h": "44518.2", // "low24h": "41387.0", // "change24h": "0.03875", // "fundingRate": "0.000096", // "nextFundingTime": "1701849600000", // "markPrice": "43476.4", // "indexPrice": "43478.4", // "holdingAmount": "50670.787", // "baseVolume": "120187.104", // "quoteVolume": "5167385048.693", // "openUtc": "44071.4", // "symbolType": "1", // "symbol": "BTCUSDT", // "deliveryPrice": "0", // "ts": "1701843962811" // } // ], // "ts": 1701843962812 // } // // uta // // { // "action": "snapshot", // "arg": { "instType": "spot", topic: "ticker", symbol: "BTCUSDT" }, // "data": [ // { // "highPrice24h": "120255.61", // "lowPrice24h": "116145.88", // "openPrice24h": "118919.38", // "lastPrice": "119818.83", // "turnover24h": "215859996.272276", // "volume24h": "1819.756798", // "bid1Price": "119811.26", // "ask1Price": "119831.18", // "bid1Size": "0.008732", // "ask1Size": "0.004297", // "price24hPcnt": "0.02002" // } // ], // "ts": 1753230479687 // } // const arg = this.safeValue(message, 'arg', {}); const data = this.safeValue(message, 'data', []); const ticker = this.safeValue(data, 0, {}); const utaTimestamp = this.safeInteger(message, 'ts'); const timestamp = this.safeInteger(ticker, 'ts', utaTimestamp); const instType = this.safeStringLower(arg, 'instType'); const marketType = (instType === 'spot') ? 'spot' : 'contract'; const utaMarketId = this.safeString(arg, 'symbol'); const marketId = this.safeString(ticker, 'instId', utaMarketId); market = this.safeMarket(marketId, market, undefined, marketType); const close = this.safeString2(ticker, 'lastPr', 'lastPrice'); const changeDecimal = this.safeString(ticker, 'change24h', ''); const change = this.safeString(ticker, 'price24hPcnt', Precise.stringMul(changeDecimal, '100')); return this.safeTicker({ 'symbol': market['symbol'], 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'high': this.safeString2(ticker, 'high24h', 'highPrice24h'), 'low': this.safeString2(ticker, 'low24h', 'lowPrice24h'), 'bid': this.safeString2(ticker, 'bidPr', 'bid1Price'), 'bidVolume': this.safeString2(ticker, 'bidSz', 'bid1Size'), 'ask': this.safeString2(ticker, 'askPr', 'ask1Price'), 'askVolume': this.safeString2(ticker, 'askSz', 'ask1Size'), 'vwap': undefined, 'open': this.safeString2(ticker, 'open24h', 'openPrice24h'), 'close': close, 'last': close, 'previousClose': undefined, 'change': undefined, 'percentage': change, 'average': undefined, 'baseVolume': this.safeString2(ticker, 'baseVolume', 'volume24h'), 'quoteVolume': this.safeString2(ticker, 'quoteVolume', 'turnover24h'), 'info': ticker, }, market); } /** * @method * @name bitget#watchBidsAsks * @description watches best bid & ask for symbols * @see https://www.bitget.com/api-doc/spot/websocket/public/Tickers-Channel * @see https://www.bitget.com/api-doc/contract/websocket/public/Tickers-Channel * @see https://www.bitget.com/api-doc/uta/websocket/public/Tickers-Channel * @param {string[]} symbols unified symbol of the market to fetch the ticker for * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {boolean} [params.uta] set to true for the unified trading account (uta), defaults to false * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure} */ async watchBidsAsks(symbols = undefined, params = {}) { await this.loadMarkets(); symbols = this.marketSymbols(symbols, undefined, false); const market = this.market(symbols[0]); let instType = undefined; let uta = undefined; [uta, params] = this.handleOptionAndParams(params, 'watchBidsAsks', 'uta', false); [instType, params] = this.getInstType(market, uta, params); const topics = []; const messageHashes = []; for (let i = 0; i < symbols.length; i++) { const symbol = symbols[i]; const marketInner = this.market(symbol); const args = { 'instType': instType, }; const topicOrChannel = uta ? 'topic' : 'channel'; const symbolOrInstId = uta ? 'symbol' : 'instId'; args[topicOrChannel] = 'ticker'; args[symbolOrInstId] = marketInner['id']; topics.push(args); messageHashes.push('bidask:' + symbol); } const tickers = await this.watchPublicMultiple(messageHashes, topics, params); if (this.newUpdates) { const result = {}; result[tickers['symbol']] = tickers; return result; } return this.filterByArray(this.bidsasks, 'symbol', symbols); } handleBidAsk(client, message) { const ticker = this.parseWsBidAsk(message); const symbol = ticker['symbol']; this.bidsasks[symbol] = ticker; const messageHash = 'bidask:' + symbol; client.resolve(ticker, messageHash); } parseWsBidAsk(message, market = undefined) { const arg = this.safeValue(message, 'arg', {}); const data = this.safeValue(message, 'data', []); const ticker = this.safeValue(data, 0, {}); const utaTimestamp = this.safeInteger(message, 'ts'); const timestamp = this.safeInteger(ticker, 'ts', utaTimestamp); const instType = this.safeStringLower(arg, 'instType'); const marketType = (instType === 'spot') ? 'spot' : 'contract'; const utaMarketId = this.safeString(arg, 'symbol'); const marketId = this.safeString(ticker, 'instId', utaMarketId); market = this.safeMarket(marketId, market, undefined, marketType); return this.safeTicker({ 'symbol': market['symbol'], 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'ask': this.safeString2(ticker, 'askPr', 'ask1Price'), 'askVolume': this.safeString2(ticker, 'askSz', 'ask1Size'), 'bid': this.safeString2(ticker, 'bidPr', 'bid1Price'), 'bidVolume': this.safeString2(ticker, 'bidSz', 'bid1Size'), 'info': ticker, }, market); } /** * @method * @name bitget#watchOHLCV * @description watches historical candlestick data containing the open, high, low, close price, and the volume of a market * @see https://www.bitget.com/api-doc/spot/websocket/public/Candlesticks-Channel * @see https://www.bitget.com/api-doc/contract/websocket/public/Candlesticks-Channel * @see https://www.bitget.com/api-doc/uta/websocket/public/Candlesticks-Channel * @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 * @param {boolean} [params.uta] set to true for the unified trading account (uta), defaults to false * @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); symbol = market['symbol']; const timeframes = this.safeValue(this.options, 'timeframes'); const interval = this.safeString(timeframes, timeframe); let messageHash = undefined; let instType = undefined; let uta = undefined; [uta, params] = this.handleOptionAndParams(params, 'watchOHLCV', 'uta', false); [instType, params] = this.getInstType(market, uta, params); const args = { 'instType': instType, }; if (uta) { args['topic'] = 'kline'; args['symbol'] = market['id']; args['interval'] = interval; params['uta'] = true; messageHash = 'kline:' + symbol; } else { args['channel'] = 'candle' + interval; args['instId'] = market['id']; messageHash = 'candles:' + timeframe + ':' + symbol; } const ohlcv = await this.watchPublic(messageHash, args, params); if (this.newUpdates) { limit = ohlcv.getLimit(symbol, limit); } return this.filterBySinceLimit(ohlcv, since, limit, 0, true); } /** * @method * @name bitget#unWatchOHLCV * @description unsubscribe from the ohlcv channel * @see https://www.bitget.com/api-doc/spot/websocket/public/Candlesticks-Channel * @see https://www.bitget.com/api-doc/contract/websocket/public/Candlesticks-Channel * @see https://www.bitget.com/api-doc/uta/websocket/public/Candlesticks-Channel * @param {string} symbol unified symbol of the market to unwatch the ohlcv for * @param {string} [timeframe] the period for the ratio, default is 1 minute * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {boolean} [params.uta] set to true for the unified trading account (uta), defaults to false * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols */ async unWatchOHLCV(symbol, timeframe = '1m', params = {}) { await this.loadMarkets(); const timeframes = this.safeDict(this.options, 'timeframes'); const interval = this.safeString(timeframes, timeframe); let channel = undefined; let market = undefined; if (symbol !== undefined) { market = this.market(symbol); } let instType = undefined; let messageHash = undefined; let uta = undefined; [uta, params] = this.handleOptionAndParams(params, 'unWatchOHLCV', 'uta', false); [instType, params] = this.getInstType(market, uta, params); const args = { 'instType': instType, }; if (uta) { channel = 'kline'; args['topic'] = channel; args['symbol'] = market['id']; args['interval'] = interval; params['uta'] = true; params['interval'] = interval; messageHash = channel + symbol; } else { channel = 'candle' + interval; args['channel'] = channel; args['instId'] = market['id']; messageHash = 'candles:' + interval; } return await this.unWatchChannel(symbol, channel, messageHash, params); } handleOHLCV(client, message) { // // { // "action": "snapshot", // "arg": { // "instType": "SPOT", // "channel": "candle1m", // "instId": "BTCUSDT" // }, // "data": [ // [ // "1701871620000", // "44080.23", // "44080.23", // "44028.5", // "44028.51", // "9.9287", // "437404.105512", // "437404.105512" // ], // [ // "1701871680000", // "44028.51", // "44108.11", // "44028.5", // "44108.11", // "17.139", // "755436.870643", // "755436.870643" // ], // ], // "ts": 1701901610417 // } // // uta // // { // "action": "snapshot", // "arg": { // "instType": "usdt-futures", // "topic": "kline", // "symbol": "BTCUSDT", // "interval": "1m" // }, // "data": [ // { // "start": "1755564480000", // "open": "116286", // "close": "116256.2", // "high": "116310.2", // "low": "116232.8", // "volume": "39.7062", // "turnover": "4616746.46654" // }, // ], // "ts": 1755594421877 // } // const arg = this.safeValue(message, 'arg', {}); const instType = this.safeStringLower(arg, 'instType'); const marketType = (instType === 'spot') ? 'spot' : 'contract'; const marketId = this.safeString2(arg, 'instId', 'symbol'); const market = this.safeMarket(marketId, undefined, undefined, marketType); const symbol = market['symbol']; this.ohlcvs[symbol] = this.safeValue(this.ohlcvs, symbol, {}); const channel = this.safeString2(arg, 'channel', 'topic'); let interval = this.safeString(arg, 'interval'); let isUta = undefined; if (interval === undefined) { isUta = false; interval = channel.replace('candle', ''); } else { isUta = true; } const timeframes = this.safeValue(this.options, 'timeframes'); const timeframe = this.findTimeframe(interval, timeframes); let stored = this.safeValue(this.ohlcvs[symbol], timeframe); if (stored === undefined) { const limit = this.safeInteger(this.options, 'OHLCVLimit', 1000); stored = new ArrayCacheByTimestamp(limit); this.ohlcvs[symbol][timeframe] = stored; } const data = this.safeValue(message, 'data', []); for (let i = 0; i < data.length; i++) { const parsed = this.parseWsOHLCV(data[i], market); stored.append(parsed); } let messageHash = undefined; if (isUta) { messageHash = 'kline:' + symbol; } else { messageHash = 'candles:' + timeframe + ':' + symbol; } client.resolve(stored, messageHash); } parseWsOHLCV(ohlcv, market = undefined) { // // [ // "1701871620000", // timestamp // "44080.23", // open // "44080.23", // high // "44028.5", // low // "44028.51", // close // "9.9287", // base volume // "437404.105512", // quote volume // "437404.105512" // USDT volume // ] // // uta // // { // "start": "1755564480000", // "open": "116286", // "close": "116256.2", // "high": "116310.2", // "low": "116232.8", // "volume": "39.7062", // "turnover": "4616746.46654" // } // const volumeIndex = (market['inverse']) ? 6 : 5; return [ this.safeInteger2(ohlcv, 'start', 0), this.safeNumber2(ohlcv, 'open', 1), this.safeNumber2(ohlcv, 'high', 2), this.safeNumber2(ohlcv, 'low', 3), this.safeNumber2(ohlcv, 'close', 4), this.safeNumber2(ohlcv, 'volume', volumeIndex), ]; } /** * @method * @name bitget#watchOrderBook * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data * @see https://www.bitget.com/api-doc/spot/websocket/public/Depth-Channel * @see https://www.bitget.com/api-doc/contract/websocket/public/Order-Book-Channel * @see https://www.bitget.com/api-doc/uta/websocket/public/Order-Book-Channel * @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 * @param {boolean} [params.uta] set to true for the unified trading account (uta), defaults to false * @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 = {}) { return await this.watchOrderBookForSymbols([symbol], limit, params); } /** * @method * @name bitget#unWatchOrderBook * @description unsubscribe from the orderbook channel * @see https://www.bitget.com/api-doc/spot/websocket/public/Depth-Channel * @see https://www.bitget.com/api-doc/contract/websocket/public/Order-Book-Channel * @see https://www.bitget.com/api-doc/uta/websocket/public/Order-Book-Channel * @param {string} symbol unified symbol of the market to fetch the order book for * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {int} [params.limit] orderbook limit, default is undefined * @param {boolean} [params.uta] set to true for the unified trading account (uta), defaults to false * @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(); let channel = 'books'; const limit = this.safeInteger(params, 'limit'); if ((limit === 1) || (limit === 5) || (limit === 15) || (limit === 50)) { params = this.omit(params, 'limit'); channel += limit.toString(); } return await this.unWatchChannel(symbol, channel, 'orderbook', params); } async unWatchChannel(symbol, channel, messageHashTopic, params = {}) { await this.loadMarkets(); const market = this.market(symbol); const messageHash = 'unsubscribe:' + messageHashTopic + ':' + market['symbol']; let instType = undefined; let uta = undefined; [uta, params] = this.handleOptionAndParams(params, 'unWatchChannel', 'uta', false); [instType, params] = this.getInstType(market, uta, params); const args = { 'instType': instType, }; if (uta) { args['topic'] = channel; args['symbol'] = market['id']; args['interval'] = this.safeString(params, 'interval', '1m'); params['uta'] = true; params = this.omit(params, 'interval'); } else { args['channel'] = channel; args['instId'] = market['id']; } return await this.unWatchPublic(messageHash, args, params); } /** * @method * @name bitget#watchOrderBookForSymbols * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data * @see https://www.bitget.com/api-doc/spot/websocket/public/Depth-Channel * @see https://www.bitget.com/api-doc/contract/websocket/public/Order-Book-Channel * @see https://www.bitget.com/api-doc/uta/websocket/public/Order-Book-Channel * @param {string[]} symbols unified array of symbols * @param {int} [limit] the maximum amount of order book entries to return * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {boolean} [params.uta] set to true for the unified trading account (uta), defaults to false * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols */ async watchOrderBookForSymbols(symbols, limit = undefined, params = {}) { await this.loadMarkets(); symbols = this.marketSymbols(symbols); let channel = 'books'; let incrementalFeed = true; if ((limit === 1) || (limit === 5) || (limit === 15) || (limit === 50)) { channel += limit.toString(); incrementalFeed = false; } const topics = []; const messageHashes = []; let uta = undefined; [uta, params] = this.handleOptionAndParams(params, 'watchOrderBookForSymbols', 'uta', false); for (let i = 0; i < symbols.length; i++) { const symbol = symbols[i]; const market = this.market(symbol); let instType = undefined; [instType, params] = this.getInstType(market, uta, params); const args = { 'instType': instType, }; const topicOrChannel = uta ? 'topic' : 'channel'; const symbolOrInstId = uta ? 'symbol' : 'instId'; args[topicOrChannel] = channel; args[symbolOrInstId] = market['id']; topics.push(args); messageHashes.push('orderbook:' + symbol); } if (uta) { params['uta'] = true; } const orderbook = await this.watchPublicMultiple(messageHashes, topics, params); if (incrementalFeed) { return orderbook.limit(); } else { return orderbook; } } handleOrderBook(client, message) { // // { // "action":"snapshot", // "arg":{ // "instType":"SPOT", // "channel":"books5", // "instId":"BTCUSDT" // }, // "data":[ // { // "asks":[ // ["21041.11","0.0445"], // ["21041.16","0.0411"], // ["21041.21","0.0421"], // ["21041.26","0.0811"], // ["21041.65","1.9465"] // ], // "bids":[ // ["21040.76","0.0417"], // ["21040.71","0.0434"], // ["21040.66","0.1141"], // ["21040.61","0.3004"], // ["21040.60","1.3357"] // ], // "checksum": -1367582038, // "ts":"1656413855484" // } // ] // } // // { // "action": "snapshot", // "arg": { "instType": "usdt-futures", "topic": "books", "symbol": "BTCUSDT" }, // "data": [ // { // "a": [Array], // "b": [Array], // "checksum": 0, // "pseq": 0, // "seq": "1343064377779269632", // "ts": "1755937421270" // } // ], // "ts": 1755937421337 // } // const arg = this.safeValue(message, 'arg'); const channel = this.safeString2(arg, 'channel', 'topic'); const instType = this.safeStringLower(arg, 'instType'); const marketType = (instType === 'spot') ? 'spot' : 'contract'; const marketId = this.safeString2(arg, 'instId', 'symbol'); const market = this.safeMarket(marketId, undefined, undefined, marketType); const symbol = market['symbol']; const messageHash = 'orderbook:' + symbol; const data = this.safeValue(message, 'data'); const rawOrderBook = this.safeValue(data, 0); const timestamp = this.safeInteger(rawOrderBook, 'ts'); const incrementalBook = channel === 'books'; if (incrementalBook) { // storedOrderBook = this.safeValue (this.orderbooks, symbol); if (!(symbol in this.orderbooks)) { // const ob = this.orderBook ({}); const ob = this.countedOrderBook({}); ob['symbol'] = symbol; this.orderbooks[symbol] = ob; } const storedOrderBook = this.orderbooks[symbol]; const asks = this.safeList2(rawOrderBook, 'asks', 'a', []); const bids = this.safeList2(rawOrderBook, 'bids', 'b', []); this.handleDeltas(storedOrderBook['asks'], asks); this.handleDeltas(storedOrderBook['bids'], bids); storedOrderBook['timestamp'] = timestamp; storedOrderBook['datetime'] = this.iso8601(timestamp); const checksum = this.handleOption('watchOrderBook', 'checksum', true); const isSnapshot = this.safeString(message, 'action') === 'snapshot'; // snapshot does not have a checksum if (!isSnapshot && checksum) { const storedAsks = storedOrderBook['asks']; const storedBids = storedOrderBook['bids']; const asksLength = storedAsks.length; const bidsLength = storedBids.length; const payloadArray = []; for (let i = 0; i < 25; i++) { if (i < bidsLength) { payloadArray.push(storedBids[i][2][0]); payloadArray.push(storedBids[i][2][1]); } if (i < asksLength) { payloadArray.push(storedAsks[i][2][0]); payloadArray.push(storedAsks[i][2][1]); } } const payload = payloadArray.join(':'); const calculatedChecksum = this.crc32(payload, true); const responseChecksum = this.safeInteger(rawOrderBook, 'checksum'); if (calculatedChecksum !== responseChecksum) { // if (messageHash in client.subscriptions) { // // delete client.subscriptions[messageHash]; // // delete this.orderbooks[symbol]; // } this.spawn(this.handleCheckSumError, client, symbol, messageHash); return; } } } else { const orderbook = this.orderBook({}); const parsedOrderbook = this.parseOrderBook(rawOrderBook, symbol, timestamp); orderbook.reset(parsedOrderbook); this.orderbooks[symbol] = orderbook; } client.resolve(this.orderbooks[symbol], messageHash); } async handleCheckSumError(client, symbol, messageHash) { await this.unWatchOrderBook(symbol); const error = new ChecksumError(this.id + ' ' + this.orderbookChecksumMessage(symbol)); client.reject(error, messageHash); } handleDelta(bookside, delta) { const bidAsk = this.parseBidAsk(delta, 0, 1); // we store the string representations in the orderbook for checksum calculation // this simplifies the code for generating checksums as we do not need to do any complex number transformations bidAsk.push(delta); bookside.storeArray(bidAsk); } handleDeltas(bookside, deltas) { for (let i = 0; i < deltas.length; i++) { this.handleDelta(bookside, deltas[i]); } } /** * @method * @name bitget#watchTrades * @description get the list of most recent trades for a particular symbol * @see https://www.bitget.com/api-doc/spot/websocket/public/Trades-Channel * @see https://www.bitget.com/api-doc/contract/websocket/public/New-Trades-Channel * @see https://www.bitget.com/api-doc/uta/websocket/public/New-Trades-Channel * @param {string} symbol unified symbol of the market to fetch trades for * @param {int} [since] timestamp in ms of the earliest trade to fetch * @param {int} [limit] the maximum amount of trades to fetch * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {boolean} [params.uta] set to true for the unified trading account (uta), defaults to false * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades} */ async watchTrades(symbol, since = undefined, limit = undefined, params = {}) { return await this.watchTradesForSymbols([symbol], since, limit, params); } /** * @method * @name bitget#watchTradesForSymbols * @description get the list of most recent trades for a particular symbol * @see https://www.bitget.com/api-doc/spot/websocket/public/Trades-Channel * @see https://www.bitget.com/api-doc/contract/websocket/public/New-Trades-Channel * @see https://www.bitget.com/api-doc/uta/websocket/public/New-Trades-Channel * @param {string[]} symbols unified symbol of the market to fetch trades for * @param {int} [since] timestamp in ms of the earliest trade to fetch * @param {int} [limit] the maximum amount of trades to fetch * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {boolean} [params.uta] set to true for the unified trading account (uta), defaults to false * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades} */ async watchTradesForSymbols(symbols, since = undefined, limit = undefined, params = {}) { const symbolsLength = symbols.length; if (symbolsLength === 0) { throw new ArgumentsRequired(this.id + ' watchTradesForSymbols() requires a non-empty array of symbols'); } await this.loadMarkets(); symbols = this.marketSymbols(symbols); let uta = undefined; [uta, params] = this.handleOptionAndParams(params, 'watchTradesForSymbols', 'uta', false); const topics = []; const messageHashes = []; for (let i = 0; i < symbols.length; i++) { const symbol = symbols[i]; const market = this.market(symbol); let instType = undefined; [instType, params] = this.getInstType(market, uta, params); const args = { 'instType': instType, }; const topicOrChannel = uta ? 'topic' : 'channel'; const symbolOrInstId = uta ? 'symbol' : 'instId'; args[topicOrChannel] = uta ? 'publicTrade' : 'trade'; args[symbolOrInstId] = market['id']; topics.push(args); messageHashes.push('trade:' + symbol); } if (uta) { params['uta'] = true; } const trades = await this.watchPublicMultiple(messageHashes, topics, params); if (this.newUpdates) { const first = this.safeValue(trades, 0); const tradeSymbol = this.safeString(first, 'symbol'); limit = trades.getLimit(tradeSymbol, 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 bitget#unWatchTrades * @description unsubscribe from the trades channel * @see https://www.bitget.com/api-doc/spot/websocket/public/Trades-Channel * @see https://www.bitget.com/api-doc/contract/websocket/public/New-Trades-Channel * @see https://www.bitget.com/api-doc/uta/websocket/public/New-Trades-Channel * @param {string} symbol unified symbol of the market to unwatch the trades for * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {boolean} [params.uta] set to true for the unified trading account (uta), defaults to false * @returns {any} status of the unwatch request */ async unWatchTrades(symbol, params = {}) { let uta = undefined; [uta, params] = this.handleOptionAndParams(params, 'unWatchTrades', 'uta', false); const channelTopic = uta ? 'publicTrade' : 'trade'; return await this.unWatchChannel(symbol, channelTopic, 'trade', params); } handleTrades(client, message) { // // { // "action": "snapshot", // "arg": { "instType": "SPOT", "channel": "trade", "instId": "BTCUSDT" }, // "data": [ // { // "ts": "1701910980366", // "price": "43854.01", // "size": "0.0535", // "side": "buy", // "tradeId": "1116461060594286593" // }, // ], // "ts": 1701910980730 // } // // uta // // { // "action": "snapshot", // "arg": { "instType": "spot", "topic": "publicTrade", "symbol": "BTCUSDT" }, // "data": [ // { // "T": "1756287827920", // "P": "110878.5", // "v": "0.07", // "S": "buy", // "L": "1344534089797185550" // "i": "1344534089797185549" // }, // ], // "ts": 1701910980730 // } // const arg = this.safeValue(message, 'arg', {}); const instType = this.safeStringLower(arg, 'instType'); const marketType = (instType === 'spot') ? 'spot' : 'contract'; const marketId = this.safeString2(arg, 'instId', 'symbol'); const market = this.safeMarket(marketId, undefined, undefined, marketType); const symbol = market['symbol']; let stored = this.safeValue(this.trades, symbol); if (stored === undefined) { const limit = this.safeInteger(this.options, 'tradesLimit', 1000); stored = new ArrayCache(limit); this.trades[symbol] = stored; } const data = this.safeList(message, 'data', []); const length = data.length; // fix chronological order by reversing for (let i = 0; i < length; i++) { const index = length - i - 1; const rawTrade = data[index]; const parsed = this.parseWsTrade(rawTrade, market); stored.append(parsed); } const messageHash = 'trade:' + symbol; client.resolve(stored, messageHash); } parseWsTrade(trade, market = undefined) { // // { // "ts": "1701910980366", // "price": "43854.01", // "size": "0.0535", // "side": "buy", //