UNPKG

ccxt

Version:

A JavaScript / TypeScript / Python / C# / PHP cryptocurrency trading library with support for 100+ exchanges

805 lines (802 loc) • 34.1 kB
// ---------------------------------------------------------------------------- // PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN: // https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code // EDIT THE CORRESPONDENT .ts FILE INSTEAD // --------------------------------------------------------------------------- import coinbaseinternationalRest from '../coinbaseinternational.js'; import { AuthenticationError, ExchangeError, NotSupported } from '../base/errors.js'; import { sha256 } from '../static_dependencies/noble-hashes/sha256.js'; import { ArrayCache, ArrayCacheByTimestamp } from '../base/ws/Cache.js'; // --------------------------------------------------------------------------- export default class coinbaseinternational extends coinbaseinternationalRest { describe() { return this.deepExtend(super.describe(), { 'has': { 'ws': true, 'watchTrades': true, 'watchTradesForSymbols': true, 'watchOrderBook': true, 'watchOrderBookForSymbols': true, 'watchTicker': true, 'watchBalance': false, 'watchMyTrades': false, 'watchOHLCV': true, 'watchOHLCVForSymbols': false, 'watchOrders': false, 'watchOrdersForSymbols': false, 'watchPositions': false, 'watchTickers': true, 'createOrderWs': false, 'editOrderWs': false, 'cancelOrderWs': false, 'cancelOrdersWs': false, 'cancelAllOrdersWs': false, 'fetchOrderWs': false, 'fetchOrdersWs': false, 'fetchBalanceWs': false, 'fetchMyTradesWs': false, }, 'urls': { 'api': { 'ws': 'wss://ws-md.international.coinbase.com', }, 'test': { 'ws': 'wss://ws-md.n5e2.coinbase.com', }, }, 'options': { 'watchTicker': { 'channel': 'LEVEL1', // 'INSTRUMENTS' or 'RISK' }, 'tradesLimit': 1000, 'ordersLimit': 1000, 'myTradesLimit': 1000, 'timeframes': { '1m': 'CANDLES_ONE_MINUTE', '5m': 'CANDLES_FIVE_MINUTES', '30m': 'CANDLES_THIRTY_MINUTES', '1h': 'CANDLES_ONE_HOUR', '2h': 'CANDLES_TWO_HOURS', '1d': 'CANDLES_ONE_DAY', }, }, 'exceptions': { 'exact': { 'Unable to authenticate': AuthenticationError, }, }, }); } /** * @ignore * @method * @description subscribes to a websocket channel * @see https://docs.cloud.coinbase.com/intx/docs/websocket-overview#subscribe * @param {string} name the name of the channel * @param {string[]} [symbols] unified market symbol * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} subscription to a websocket channel */ async subscribe(name, symbols = undefined, params = {}) { await this.loadMarkets(); this.checkRequiredCredentials(); let market = undefined; let messageHash = name; let productIds = undefined; if (symbols === undefined) { symbols = this.getActiveSymbols(); } const symbolsLength = symbols.length; const messageHashes = []; if (symbolsLength > 1) { const parsedSymbols = this.marketSymbols(symbols); const marketIds = this.marketIds(parsedSymbols); productIds = marketIds; for (let i = 0; i < parsedSymbols.length; i++) { messageHashes.push(name + '::' + parsedSymbols[i]); } // messageHash = messageHash + '::' + parsedSymbols.join (','); } else if (symbolsLength === 1) { market = this.market(symbols[0]); messageHash = name + '::' + market['symbol']; productIds = [market['id']]; } const url = this.urls['api']['ws']; if (url === undefined) { throw new NotSupported(this.id + ' is not supported in sandbox environment'); } const timestamp = this.nonce().toString(); const auth = timestamp + this.apiKey + 'CBINTLMD' + this.password; const signature = this.hmac(this.encode(auth), this.base64ToBinary(this.secret), sha256, 'base64'); const subscribe = { 'type': 'SUBSCRIBE', // 'product_ids': productIds, 'channels': [name], 'time': timestamp, 'key': this.apiKey, 'passphrase': this.password, 'signature': signature, }; if (productIds !== undefined) { subscribe['product_ids'] = productIds; } if (symbolsLength > 1) { return await this.watchMultiple(url, messageHashes, this.extend(subscribe, params), messageHashes); } return await this.watch(url, messageHash, this.extend(subscribe, params), messageHash); } /** * @ignore * @method * @description subscribes to a websocket channel using watchMultiple * @see https://docs.cloud.coinbase.com/intx/docs/websocket-overview#subscribe * @param {string} name the name of the channel * @param {string|string[]} [symbols] unified market symbol * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} subscription to a websocket channel */ async subscribeMultiple(name, symbols = undefined, params = {}) { await this.loadMarkets(); this.checkRequiredCredentials(); if (this.isEmpty(symbols)) { symbols = this.symbols; } else { symbols = this.marketSymbols(symbols); } const messageHashes = []; const productIds = []; for (let i = 0; i < symbols.length; i++) { const marketId = this.marketId(symbols[i]); const symbol = this.symbol(marketId); productIds.push(marketId); messageHashes.push(name + '::' + symbol); } const url = this.urls['api']['ws']; if (url === undefined) { throw new NotSupported(this.id + ' is not supported in sandbox environment'); } const timestamp = this.numberToString(this.seconds()); const auth = timestamp + this.apiKey + 'CBINTLMD' + this.password; const signature = this.hmac(this.encode(auth), this.base64ToBinary(this.secret), sha256, 'base64'); const subscribe = { 'type': 'SUBSCRIBE', 'time': timestamp, 'product_ids': productIds, 'channels': [name], 'key': this.apiKey, 'passphrase': this.password, 'signature': signature, }; return await this.watchMultiple(url, messageHashes, this.extend(subscribe, params), messageHashes); } /** * @method * @name coinbaseinternational#watchFundingRate * @description watch the current funding rate * @see https://docs.cloud.coinbase.com/intx/docs/websocket-channels#funding-channel * @param {string} symbol unified market symbol * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a [funding rate structure]{@link https://docs.ccxt.com/#/?id=funding-rate-structure} */ async watchFundingRate(symbol, params = {}) { await this.loadMarkets(); return await this.subscribe('RISK', [symbol], params); } /** * @method * @name coinbaseinternational#watchFundingRates * @description watch the funding rate for multiple markets * @see https://docs.cloud.coinbase.com/intx/docs/websocket-channels#funding-channel * @param {string[]|undefined} symbols list of unified market symbols * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a dictionary of [funding rates structures]{@link https://docs.ccxt.com/#/?id=funding-rates-structure}, indexe by market symbols */ async watchFundingRates(symbols, params = {}) { await this.loadMarkets(); const fundingRate = await this.subscribeMultiple('RISK', symbols, params); const symbol = this.safeString(fundingRate, 'symbol'); if (this.newUpdates) { const result = {}; result[symbol] = fundingRate; return result; } return this.filterByArray(this.fundingRates, 'symbol', symbols); } /** * @method * @name coinbaseinternational#watchTicker * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market * @see https://docs.cloud.coinbase.com/intx/docs/websocket-channels#instruments-channel * @param {string} [symbol] unified symbol of the market to fetch the ticker for * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {string} [params.channel] the channel to watch, 'LEVEL1' or 'INSTRUMENTS', default is 'LEVEL1' * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure} */ async watchTicker(symbol, params = {}) { await this.loadMarkets(); let channel = undefined; [channel, params] = this.handleOptionAndParams(params, 'watchTicker', 'channel', 'LEVEL1'); return await this.subscribe(channel, [symbol], params); } getActiveSymbols() { const symbols = this.symbols; const output = []; for (let i = 0; i < symbols.length; i++) { const symbol = symbols[i]; const market = this.markets[symbol]; if (market['active']) { output.push(symbol); } } return output; } /** * @method * @name coinbaseinternational#watchTickers * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market * @see https://docs.cloud.coinbase.com/intx/docs/websocket-channels#instruments-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 {string} [params.channel] the channel to watch, 'LEVEL1' or 'INSTRUMENTS', default is 'INSTLEVEL1UMENTS' * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure} */ async watchTickers(symbols = undefined, params = {}) { await this.loadMarkets(); let channel = undefined; [channel, params] = this.handleOptionAndParams(params, 'watchTickers', 'channel', 'LEVEL1'); const ticker = await this.subscribe(channel, symbols, params); if (this.newUpdates) { const result = {}; result[ticker['symbol']] = ticker; return result; } return this.filterByArray(this.tickers, 'symbol', symbols); } handleInstrument(client, message) { // // { // "sequence": 1, // "product_id": "ETH-PERP", // "instrument_type": "PERP", // "base_asset_name": "ETH", // "quote_asset_name": "USDC", // "base_increment": "0.0001", // "quote_increment": "0.01", // "avg_daily_quantity": "43.0", // "avg_daily_volume": "80245.2", // "total_30_day_quantity":"1443.0", // "total_30_day_volume":"3040449.0", // "total_24_hour_quantity":"48.1", // "total_24_hour_volume":"101348.3", // "base_imf": "0.2", // "min_quantity": "0.0001", // "position_size_limit": "500", // "funding_interval": "60000000000", // "trading_state": "trading", // "last_update_time": "2023-05-04T11:16:33.016Z", // "time": "2023-05-10T14:58:47.000Z", // "channel":"INSTRUMENTS", // "type":"SNAPSHOT" // } const ticker = this.parseWsInstrument(message); const channel = this.safeString(message, 'channel'); client.resolve(ticker, channel); client.resolve(ticker, channel + '::' + ticker['symbol']); } parseWsInstrument(ticker, market = undefined) { // // { // "sequence": 1, // "product_id": "ETH-PERP", // "instrument_type": "PERP", // "base_asset_name": "ETH", // "quote_asset_name": "USDC", // "base_increment": "0.0001", // "quote_increment": "0.01", // "avg_daily_quantity": "43.0", // "avg_daily_volume": "80245.2", // "total_30_day_quantity":"1443.0", // "total_30_day_volume":"3040449.0", // "total_24_hour_quantity":"48.1", // "total_24_hour_volume":"101348.3", // "base_imf": "0.2", // "min_quantity": "0.0001", // "position_size_limit": "500", // "funding_interval": "60000000000", // "trading_state": "trading", // "last_update_time": "2023-05-04T11:16:33.016Z", // "time": "2023-05-10T14:58:47.000Z", // "channel":"INSTRUMENTS", // "type":"SNAPSHOT" // } // instruments // { // sequence: 0, // instrument_type: 'PERP', // instrument_mode: 'standard', // base_asset_name: 'BTC', // quote_asset_name: 'USDC', // base_increment: '0.0001', // quote_increment: '0.1', // avg_daily_quantity: '502.8845', // avg_daily_volume: '3.1495242961566668E7', // total30_day_quantity: '15086.535', // total30_day_volume: '9.44857288847E8', // total24_hour_quantity: '5.0', // total24_hour_volume: '337016.5', // base_imf: '0.1', // min_quantity: '0.0001', // position_size_limit: '800', // funding_interval: '3600000000000', // trading_state: 'trading', // last_updated_time: '2024-07-30T15:00:00Z', // default_initial_margin: '0.2', // base_asset_multiplier: '1.0', // channel: 'INSTRUMENTS', // type: 'SNAPSHOT', // time: '2024-07-30T15:26:56.766Z', // } // const marketId = this.safeString(ticker, 'product_id'); const datetime = this.safeString(ticker, 'time'); return this.safeTicker({ 'info': ticker, 'symbol': this.safeSymbol(marketId, market, '-'), 'timestamp': this.parse8601(datetime), 'datetime': datetime, 'high': undefined, 'low': undefined, 'bid': undefined, 'bidVolume': undefined, 'ask': undefined, 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': undefined, 'last': undefined, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeString2(ticker, 'total_24_hour_quantity', 'total24_hour_quantity'), 'quoteVolume': this.safeString2(ticker, 'total_24_hour_volume', 'total24_hour_volume'), }); } handleTicker(client, message) { // // snapshot // { // "sequence": 0, // "product_id": "BTC-PERP", // "time": "2023-05-10T14:58:47.000Z", // "bid_price": "28787.8", // "bid_qty": "0.466", // One side book // "channel": "LEVEL1", // "type": "SNAPSHOT" // } // update // { // "sequence": 1, // "product_id": "BTC-PERP", // "time": "2023-05-10T14:58:47.547Z", // "bid_price": "28787.8", // "bid_qty": "0.466", // "ask_price": "28788.8", // "ask_qty": "1.566", // "channel": "LEVEL1", // "type": "UPDATE" // } // const ticker = this.parseWsTicker(message); const channel = this.safeString(message, 'channel'); client.resolve(ticker, channel); client.resolve(ticker, channel + '::' + ticker['symbol']); } parseWsTicker(ticker, market = undefined) { // // { // "sequence": 1, // "product_id": "BTC-PERP", // "time": "2023-05-10T14:58:47.547Z", // "bid_price": "28787.8", // "bid_qty": "0.466", // "ask_price": "28788.8", // "ask_qty": "1.566", // "channel": "LEVEL1", // "type": "UPDATE" // } // const datetime = this.safeString(ticker, 'time'); const marketId = this.safeString(ticker, 'product_id'); return this.safeTicker({ 'info': ticker, 'symbol': this.safeSymbol(marketId, market), 'timestamp': this.parse8601(datetime), 'datetime': datetime, 'bid': this.safeNumber(ticker, 'bid_price'), 'bidVolume': this.safeNumber(ticker, 'bid_qty'), 'ask': this.safeNumber(ticker, 'ask_price'), 'askVolume': this.safeNumber(ticker, 'ask_qty'), 'high': undefined, 'low': undefined, 'open': undefined, 'close': undefined, 'last': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'vwap': undefined, 'baseVolume': undefined, 'quoteVolume': undefined, 'previousClose': undefined, }); } /** * @method * @name coinbaseinternational#watchOHLCV * @description watches historical candlestick data containing the open, high, low, close price, and the volume of a market * @see https://docs.cdp.coinbase.com/intx/docs/websocket-channels#candles-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 * @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 options = this.safeDict(this.options, 'timeframes', {}); const interval = this.safeString(options, timeframe, timeframe); const ohlcv = await this.subscribe(interval, [symbol], params); if (this.newUpdates) { limit = ohlcv.getLimit(symbol, limit); } return this.filterBySinceLimit(ohlcv, since, limit, 0, true); } handleOHLCV(client, message) { // // { // "sequence": 0, // "product_id": "BTC-PERP", // "channel": "CANDLES_ONE_MINUTE", // "type": "SNAPSHOT", // "candles": [ // { // "time": "2023-05-10T14:58:47.000Z", // "low": "28787.8", // "high": "28788.8", // "open": "28788.8", // "close": "28787.8", // "volume": "0.466" // }, // ] // } // const messageHash = this.safeString(message, 'channel'); const marketId = this.safeString(message, 'product_id'); const market = this.safeMarket(marketId); const symbol = market['symbol']; const timeframe = this.findTimeframe(messageHash); this.ohlcvs[symbol] = this.safeValue(this.ohlcvs, symbol, {}); if (this.safeValue(this.ohlcvs[symbol], timeframe) === undefined) { const limit = this.safeInteger(this.options, 'OHLCVLimit', 1000); this.ohlcvs[symbol][timeframe] = new ArrayCacheByTimestamp(limit); } const stored = this.ohlcvs[symbol][timeframe]; const data = this.safeList(message, 'candles', []); for (let i = 0; i < data.length; i++) { const tick = data[i]; const parsed = this.parseOHLCV(tick, market); stored.append(parsed); } client.resolve(stored, messageHash + '::' + symbol); } /** * @method * @name coinbaseinternational#watchTrades * @description get the list of most recent trades for a particular symbol * @see https://docs.cloud.coinbase.com/intx/docs/websocket-channels#match-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 * @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 coinbaseinternational#watchTradesForSymbols * @description get the list of most recent trades for a list of symbols * @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 * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades} */ async watchTradesForSymbols(symbols, since = undefined, limit = undefined, params = {}) { await this.loadMarkets(); symbols = this.marketSymbols(symbols, undefined, false, true, true); const trades = await this.subscribeMultiple('MATCH', symbols, params); if (this.newUpdates) { const first = this.safeDict(trades, 0); const tradeSymbol = this.safeString(first, 'symbol'); limit = trades.getLimit(tradeSymbol, limit); } return this.filterBySinceLimit(trades, since, limit, 'timestamp', true); } handleTrade(client, message) { // // { // "sequence": 0, // "product_id": "BTC-PERP", // "time": "2023-05-10T14:58:47.002Z", // "match_id": "177101110052388865", // "trade_qty": "0.006", // "aggressor_side": "BUY", // "trade_price": "28833.1", // "channel": "MATCH", // "type": "UPDATE" // } // const trade = this.parseWsTrade(message); const symbol = trade['symbol']; const channel = this.safeString(message, 'channel'); if (!(symbol in this.trades)) { const limit = this.safeInteger(this.options, 'tradesLimit', 1000); const tradesArrayCache = new ArrayCache(limit); this.trades[symbol] = tradesArrayCache; } const tradesArray = this.trades[symbol]; tradesArray.append(trade); this.trades[symbol] = tradesArray; client.resolve(tradesArray, channel); client.resolve(tradesArray, channel + '::' + trade['symbol']); return message; } parseWsTrade(trade, market = undefined) { // // { // "sequence": 0, // "product_id": "BTC-PERP", // "time": "2023-05-10T14:58:47.002Z", // "match_id": "177101110052388865", // "trade_qty": "0.006", // "aggressor_side": "BUY", // "trade_price": "28833.1", // "channel": "MATCH", // "type": "UPDATE" // } const marketId = this.safeString2(trade, 'symbol', 'product_id'); const datetime = this.safeString(trade, 'time'); return this.safeTrade({ 'info': trade, 'id': this.safeString(trade, 'match_id'), 'order': undefined, 'timestamp': this.parse8601(datetime), 'datetime': datetime, 'symbol': this.safeSymbol(marketId, market), 'type': undefined, 'side': this.safeStringLower(trade, 'agressor_side'), 'takerOrMaker': undefined, 'price': this.safeString(trade, 'trade_price'), 'amount': this.safeString(trade, 'trade_qty'), 'cost': undefined, 'fee': undefined, }); } /** * @method * @name coinbaseinternational#watchOrderBook * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data * @see https://docs.cloud.coinbase.com/intx/docs/websocket-channels#level2-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 * @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 coinbaseinternational#watchOrderBook * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data * @see https://docs.cloud.coinbase.com/intx/docs/websocket-channels#level2-channel * @param {string[]} symbols * @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 watchOrderBookForSymbols(symbols, limit = undefined, params = {}) { await this.loadMarkets(); return await this.subscribeMultiple('LEVEL2', symbols, params); } handleOrderBook(client, message) { // // snapshot // { // "sequence": 0, // "product_id": "BTC-PERP", // "time": "2023-05-10T14:58:47.000Z", // "bids": [ // ["29100", "0.02"], // ["28950", "0.01"], // ["28900", "0.01"] // ], // "asks": [ // ["29267.8", "18"], // ["29747.6", "18"], // ["30227.4", "9"] // ], // "channel": "LEVEL2", // "type": "SNAPSHOT", // } // update // { // "sequence": 1, // "product_id": "BTC-PERP", // "time": "2023-05-10T14:58:47.375Z", // "changes": [ // [ // "BUY", // "28787.7", // "6" // ] // ], // "channel": "LEVEL2", // "type": "UPDATE" // } // const type = this.safeString(message, 'type'); const marketId = this.safeString(message, 'product_id'); const symbol = this.safeSymbol(marketId); const datetime = this.safeString(message, 'time'); const channel = this.safeString(message, 'channel'); if (!(symbol in this.orderbooks)) { const limit = this.safeInteger(this.options, 'watchOrderBookLimit', 1000); this.orderbooks[symbol] = this.orderBook({}, limit); } const orderbook = this.orderbooks[symbol]; if (type === 'SNAPSHOT') { const parsedSnapshot = this.parseOrderBook(message, symbol, undefined, 'bids', 'asks'); orderbook.reset(parsedSnapshot); orderbook['symbol'] = symbol; } else { const changes = this.safeList(message, 'changes', []); this.handleDeltas(orderbook, changes); } orderbook['nonce'] = this.safeInteger(message, 'sequence'); orderbook['datetime'] = datetime; orderbook['timestamp'] = this.parse8601(datetime); this.orderbooks[symbol] = orderbook; client.resolve(orderbook, channel + '::' + symbol); } handleDelta(orderbook, delta) { const rawSide = this.safeStringLower(delta, 0); const side = (rawSide === 'buy') ? 'bids' : 'asks'; const price = this.safeFloat(delta, 1); const amount = this.safeFloat(delta, 2); const bookside = orderbook[side]; bookside.store(price, amount); } handleDeltas(orderbook, deltas) { for (let i = 0; i < deltas.length; i++) { this.handleDelta(orderbook, deltas[i]); } } handleSubscriptionStatus(client, message) { // // { // "channels": [ // { // "name": "MATCH", // "product_ids": [ // "BTC-PERP", // "ETH-PERP" // ] // }, // { // "name": "INSTRUMENTS", // "product_ids": [ // "BTC-PERP", // "ETH-PERP" // ] // } // ], // "authenticated": true, // "channel": "SUBSCRIPTIONS", // "type": "SNAPSHOT", // "time": "2023-05-30T16:53:46.847Z" // } // return message; } handleFundingRate(client, message) { // // snapshot // { // "sequence": 0, // "product_id": "BTC-PERP", // "time": "2023-05-10T14:58:47.000Z", // "funding_rate": "0.001387", // "is_final": true, // "channel": "FUNDING", // "type": "SNAPSHOT" // } // update // { // "sequence": 1, // "product_id": "BTC-PERP", // "time": "2023-05-10T15:00:00.000Z", // "funding_rate": "0.001487", // "is_final": false, // "channel": "FUNDING", // "type": "UPDATE" // } // const channel = this.safeString(message, 'channel'); const fundingRate = this.parseFundingRate(message); this.fundingRates[fundingRate['symbol']] = fundingRate; client.resolve(fundingRate, channel + '::' + fundingRate['symbol']); } handleErrorMessage(client, message) { // // { // message: 'Failed to subscribe', // reason: 'Unable to authenticate', // channel: 'SUBSCRIPTIONS', // type: 'REJECT' // } // const type = this.safeString(message, 'type'); if (type !== 'REJECT') { return false; } const reason = this.safeString(message, 'reason'); const errMsg = this.safeString(message, 'message'); try { const feedback = this.id + ' ' + errMsg + reason; this.throwExactlyMatchedException(this.exceptions['exact'], reason, feedback); this.throwBroadlyMatchedException(this.exceptions['broad'], reason, feedback); throw new ExchangeError(feedback); } catch (e) { client.reject(e); } return true; } handleMessage(client, message) { if (this.handleErrorMessage(client, message)) { return; } const channel = this.safeString(message, 'channel', ''); const methods = { 'SUBSCRIPTIONS': this.handleSubscriptionStatus, 'INSTRUMENTS': this.handleInstrument, 'LEVEL1': this.handleTicker, 'MATCH': this.handleTrade, 'LEVEL2': this.handleOrderBook, 'FUNDING': this.handleFundingRate, 'RISK': this.handleTicker, }; const type = this.safeString(message, 'type'); if (type === 'error') { const errorMessage = this.safeString(message, 'message'); throw new ExchangeError(errorMessage); } if (channel.indexOf('CANDLES') > -1) { this.handleOHLCV(client, message); } const method = this.safeValue(methods, channel); if (method !== undefined) { method.call(this, client, message); } } }