UNPKG

@proton/ccxt

Version:

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

822 lines (819 loc) 33.7 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 blockchaincomRest from '../blockchaincom.js'; import { NotSupported, AuthenticationError, ExchangeError } from '../base/errors.js'; import { ArrayCache, ArrayCacheBySymbolById, ArrayCacheByTimestamp } from '../base/ws/Cache.js'; // --------------------------------------------------------------------------- export default class blockchaincom extends blockchaincomRest { describe() { return this.deepExtend(super.describe(), { 'has': { 'ws': true, 'watchBalance': true, 'watchTicker': true, 'watchTickers': false, 'watchTrades': true, 'watchMyTrades': false, 'watchOrders': true, 'watchOrderBook': true, 'watchOHLCV': true, }, 'urls': { 'api': { 'ws': 'wss://ws.blockchain.info/mercury-gateway/v1/ws', }, }, 'options': { 'ws': { 'options': { 'headers': { 'Origin': 'https://exchange.blockchain.com', }, }, 'noOriginHeader': false, }, 'sequenceNumbers': {}, }, 'streaming': {}, 'exceptions': {}, 'timeframes': { '1m': '60', '5m': '300', '15m': '900', '1h': '3600', '6h': '21600', '1d': '86400', }, }); } async watchBalance(params = {}) { /** * @method * @name blockchaincom#watchBalance * @description query for balance and get the amount of funds available for trading or funds locked in orders * @see https://exchange.blockchain.com/api/#balances * @param {object} params extra parameters specific to the blockchaincom api endpoint * @returns {object} a [balance structure]{@link https://docs.ccxt.com/en/latest/manual.html?#balance-structure} */ await this.authenticate(params); const messageHash = 'balance'; const url = this.urls['api']['ws']; const subscribe = { 'action': 'subscribe', 'channel': 'balances', }; const request = this.deepExtend(subscribe, params); return await this.watch(url, messageHash, request, messageHash, request); } handleBalance(client, message) { // // subscribed // { // seqnum: 1, // event: 'subscribed', // channel: 'balances', // local_currency: 'USD', // batching: false // } // snapshot // { // "seqnum": 2, // "event": "snapshot", // "channel": "balances", // "balances": [ // { // "currency": "BTC", // "balance": 0.00366963, // "available": 0.00266963, // "balance_local": 38.746779155, // "available_local": 28.188009155, // "rate": 10558.77 // }, // ... // ], // "total_available_local": 65.477864168, // "total_balance_local": 87.696634168 // } // const event = this.safeString(message, 'event'); if (event === 'subscribed') { return message; } const result = { 'info': message }; const balances = this.safeValue(message, 'balances', []); for (let i = 0; i < balances.length; i++) { const entry = balances[i]; const currencyId = this.safeString(entry, 'currency'); const code = this.safeCurrencyCode(currencyId); const account = this.account(); account['free'] = this.safeNumber(entry, 'available'); account['total'] = this.safeNumber(entry, 'balance'); result[code] = account; } const messageHash = 'balance'; this.balance = result; client.resolve(this.balance, messageHash); } async watchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { /** * @method * @name blockchaincom#watchOHLCV * @description watches historical candlestick data containing the open, high, low, and close price, and the volume of a market. * @see https://exchange.blockchain.com/api/#prices * @param {string} symbol unified symbol of the market to fetch OHLCV data for * @param {string} timeframe the length of time each candle represents. Allows '1m', '5m', '15m', '1h', '6h' '1d'. Can only watch one timeframe per symbol. * @param {int|undefined} since timestamp in ms of the earliest candle to fetch * @param {int|undefined} limit the maximum amount of candles to fetch * @param {object} params extra parameters specific to the bitfinex2 api endpoint * @returns {[[int]]} A list of candles ordered as timestamp, open, high, low, close, volume */ await this.loadMarkets(); const market = this.market(symbol); symbol = market['symbol']; const interval = this.safeString(this.timeframes, timeframe, timeframe); const messageHash = 'ohlcv:' + symbol; let request = { 'action': 'subscribe', 'channel': 'prices', 'symbol': market['id'], 'granularity': this.parseNumber(interval), }; request = this.deepExtend(request, params); const url = this.urls['api']['ws']; const ohlcv = await this.watch(url, messageHash, request, messageHash, request); if (this.newUpdates) { limit = ohlcv.getLimit(symbol, limit); } return this.filterBySinceLimit(ohlcv, since, limit, 0, true); } handleOHLCV(client, message) { // // subscribed // { // seqnum: 0, // event: 'subscribed', // channel: 'prices', // symbol: 'BTC-USDT', // granularity: 60 // } // // updated // { // seqnum: 1, // event: 'updated', // channel: 'prices', // symbol: 'BTC-USD', // price: [ 1660085580000, 23185.215, 23185.935, 23164.79, 23169.97, 0 ] // } // const event = this.safeString(message, 'event'); if (event === 'subscribed') { return message; } else if (event === 'rejected') { throw new ExchangeError(this.id + ' ' + this.json(message)); } else if (event === 'updated') { const marketId = this.safeString(message, 'symbol'); const symbol = this.safeSymbol(marketId, undefined, '-'); const messageHash = 'ohlcv:' + symbol; const request = this.safeValue(client.subscriptions, messageHash); const timeframeId = this.safeNumber(request, 'granularity'); const timeframe = this.findTimeframe(timeframeId); const ohlcv = this.safeValue(message, 'price', []); this.ohlcvs[symbol] = this.safeValue(this.ohlcvs, symbol, {}); 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; } stored.append(ohlcv); client.resolve(stored, messageHash); } else { throw new NotSupported(this.id + ' ' + this.json(message)); } } async watchTicker(symbol, params = {}) { /** * @method * @name blockchaincom#watchTicker * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market * @see https://exchange.blockchain.com/api/#ticker * @param {string} symbol unified symbol of the market to fetch the ticker for * @param {object} params extra parameters specific to the blockchaincom api endpoint * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure} */ await this.loadMarkets(); const market = this.market(symbol); symbol = market['symbol']; const url = this.urls['api']['ws']; const messageHash = 'ticker:' + symbol; let request = { 'action': 'subscribe', 'channel': 'ticker', 'symbol': market['id'], }; request = this.deepExtend(request, params); return await this.watch(url, messageHash, request, messageHash); } handleTicker(client, message) { // // subscribed // { // seqnum: 0, // event: 'subscribed', // channel: 'ticker', // symbol: 'BTC-USD' // } // snapshot // { // seqnum: 1, // event: 'snapshot', // channel: 'ticker', // symbol: 'BTC-USD', // price_24h: 23071.4, // volume_24h: 236.28398636, // last_trade_price: 23936.4, // mark_price: 23935.335240262 // } // update // { // seqnum: 2, // event: 'updated', // channel: 'ticker', // symbol: 'BTC-USD', // mark_price: 23935.242443617 // } // const event = this.safeString(message, 'event'); const marketId = this.safeString(message, 'symbol'); const market = this.safeMarket(marketId); const symbol = market['symbol']; let ticker = undefined; if (event === 'subscribed') { return message; } else if (event === 'snapshot') { ticker = this.parseTicker(message, market); } else if (event === 'updated') { const lastTicker = this.safeValue(this.tickers, symbol); ticker = this.parseWsUpdatedTicker(message, lastTicker, market); } const messageHash = 'ticker:' + symbol; this.tickers[symbol] = ticker; client.resolve(ticker, messageHash); } parseWsUpdatedTicker(ticker, lastTicker = undefined, market = undefined) { // // { // seqnum: 2, // event: 'updated', // channel: 'ticker', // symbol: 'BTC-USD', // mark_price: 23935.242443617 // } // const marketId = this.safeString(ticker, 'symbol'); const symbol = this.safeSymbol(marketId, undefined, '-'); const last = this.safeString(ticker, 'mark_price'); return this.safeTicker({ 'symbol': symbol, 'timestamp': undefined, 'datetime': undefined, 'high': undefined, 'low': undefined, 'bid': undefined, 'bidVolume': undefined, 'ask': undefined, 'askVolume': undefined, 'vwap': undefined, 'open': this.safeString(lastTicker, 'open'), 'close': undefined, 'last': last, 'previousClose': this.safeString(lastTicker, 'close'), 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': this.safeString(lastTicker, 'baseVolume'), 'quoteVolume': undefined, 'info': this.extend(this.safeValue(lastTicker, 'info', {}), ticker), }, market); } async watchTrades(symbol, since = undefined, limit = undefined, params = {}) { /** * @method * @name blockchaincom#watchTrades * @description get the list of most recent trades for a particular symbol * @see https://exchange.blockchain.com/api/#trades * @param {string} symbol unified symbol of the market to fetch trades for * @param {int|undefined} since timestamp in ms of the earliest trade to fetch * @param {int|undefined} limit the maximum amount of trades to fetch * @param {object} params extra parameters specific to the blockchaincom api endpoint * @returns {[object]} a list of [trade structures]{@link https://docs.ccxt.com/en/latest/manual.html?#public-trades} */ await this.loadMarkets(); const market = this.market(symbol); symbol = market['symbol']; const url = this.urls['api']['ws']; const messageHash = 'trades:' + symbol; let request = { 'action': 'subscribe', 'channel': 'trades', 'symbol': market['id'], }; request = this.deepExtend(request, params); const trades = await this.watch(url, messageHash, request, messageHash, request); return this.filterBySinceLimit(trades, since, limit, 'timestamp', true); } handleTrades(client, message) { // // subscribed // { // seqnum: 0, // event: 'subscribed', // channel: 'trades', // symbol: 'BTC-USDT' // } // updates // { // seqnum: 1, // event: 'updated', // channel: 'trades', // symbol: 'BTC-USDT', // timestamp: '2022-08-08T17:23:48.163096Z', // side: 'sell', // qty: 0.083523, // price: 23940.67, // trade_id: '563078810223444' // } // const event = this.safeString(message, 'event'); if (event !== 'updated') { return message; } const marketId = this.safeString(message, 'symbol'); const symbol = this.safeSymbol(marketId); const market = this.safeMarket(marketId); const messageHash = 'trades:' + 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 parsed = this.parseWsTrade(message, market); stored.append(parsed); this.trades[symbol] = stored; client.resolve(this.trades[symbol], messageHash); } parseWsTrade(trade, market = undefined) { // // { // seqnum: 1, // event: 'updated', // channel: 'trades', // symbol: 'BTC-USDT', // timestamp: '2022-08-08T17:23:48.163096Z', // side: 'sell', // qty: 0.083523, // price: 23940.67, // trade_id: '563078810223444' // } // const marketId = this.safeString(trade, 'symbol'); const datetime = this.safeString(trade, 'timestamp'); return this.safeTrade({ 'id': this.safeString(trade, 'trade_id'), 'timestamp': this.parse8601(datetime), 'datetime': datetime, 'symbol': this.safeSymbol(marketId, market, '-'), 'order': undefined, 'type': undefined, 'side': this.safeString(trade, 'side'), 'takerOrMaker': undefined, 'price': this.safeString(trade, 'price'), 'amount': this.safeString(trade, 'qty'), 'cost': undefined, 'fee': undefined, 'info': trade, }, market); } async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { /** * @method * @name blockchaincom#fetchOrders * @description watches information on multiple orders made by the user * @see https://exchange.blockchain.com/api/#mass-order-status-request-ordermassstatusrequest * @param {string|undefined} symbol unified market symbol of the market orders were made in * @param {int|undefined} since the earliest time in ms to fetch orders for * @param {int|undefined} limit the maximum number of orde structures to retrieve * @param {object} params extra parameters specific to the blockchaincom api endpoint * @returns {[object]} a list of [order structures]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure} */ await this.loadMarkets(); await this.authenticate(); if (symbol !== undefined) { const market = this.market(symbol); symbol = market['symbol']; } const url = this.urls['api']['ws']; const message = { 'action': 'subscribe', 'channel': 'trading', }; const messageHash = 'orders'; const request = this.deepExtend(message, params); const orders = await this.watch(url, messageHash, request, messageHash); if (this.newUpdates) { limit = orders.getLimit(symbol, limit); } return this.filterBySymbolSinceLimit(orders, symbol, since, limit); } handleOrders(client, message) { // // { // seqnum: 1, // event: 'rejected', // channel: 'trading', // text: 'Not subscribed to channel' // } // snapshot // { // seqnum: 2, // event: 'snapshot', // channel: 'trading', // orders: [ // { // orderID: '562965341621940', // gwOrderId: 181011136260, // clOrdID: '016caf67f7a94508webd', // symbol: 'BTC-USD', // side: 'sell', // ordType: 'limit', // orderQty: 0.000675, // leavesQty: 0.000675, // cumQty: 0, // avgPx: 0, // ordStatus: 'open', // timeInForce: 'GTC', // text: 'New order', // execType: '0', // execID: '21415965325', // transactTime: '2022-08-08T23:31:00.550795Z', // msgType: 8, // lastPx: 0, // lastShares: 0, // tradeId: '0', // fee: 0, // price: 30000, // marginOrder: false, // closePositionOrder: false // } // ], // positions: [] // } // update // { // seqnum: 3, // event: 'updated', // channel: 'trading', // orderID: '562965341621940', // gwOrderId: 181011136260, // clOrdID: '016caf67f7a94508webd', // symbol: 'BTC-USD', // side: 'sell', // ordType: 'limit', // orderQty: 0.000675, // leavesQty: 0.000675, // cumQty: 0, // avgPx: 0, // ordStatus: 'cancelled', // timeInForce: 'GTC', // text: 'Canceled by User', // execType: '4', // execID: '21416034921', // transactTime: '2022-08-08T23:33:25.727785Z', // msgType: 8, // lastPx: 0, // lastShares: 0, // tradeId: '0', // fee: 0, // price: 30000, // marginOrder: false, // closePositionOrder: false // } // const event = this.safeString(message, 'event'); const messageHash = 'orders'; const cachedOrders = this.orders; if (cachedOrders === undefined) { const limit = this.safeInteger(this.options, 'ordersLimit', 1000); this.orders = new ArrayCacheBySymbolById(limit); } if (event === 'subscribed') { return message; } else if (event === 'rejected') { throw new ExchangeError(this.id + ' ' + this.json(message)); } else if (event === 'snapshot') { const orders = this.safeValue(message, 'orders', []); for (let i = 0; i < orders.length; i++) { const order = orders[i]; const parsedOrder = this.parseWsOrder(order); cachedOrders.append(parsedOrder); } } else if (event === 'updated') { const parsedOrder = this.parseWsOrder(message); cachedOrders.append(parsedOrder); } this.orders = cachedOrders; client.resolve(this.orders, messageHash); } parseWsOrder(order, market = undefined) { // // { // seqnum: 3, // event: 'updated', // channel: 'trading', // orderID: '562965341621940', // gwOrderId: 181011136260, // clOrdID: '016caf67f7a94508webd', // symbol: 'BTC-USD', // side: 'sell', // ordType: 'limit', // orderQty: 0.000675, // leavesQty: 0.000675, // cumQty: 0, // avgPx: 0, // ordStatus: 'cancelled', // timeInForce: 'GTC', // text: 'Canceled by User', // execType: '4', // execID: '21416034921', // transactTime: '2022-08-08T23:33:25.727785Z', // msgType: 8, // lastPx: 0, // lastShares: 0, // tradeId: '0', // fee: 0, // price: 30000, // marginOrder: false, // closePositionOrder: false // } // const datetime = this.safeString(order, 'transactTime'); const status = this.safeString(order, 'ordStatus'); const marketId = this.safeString(order, 'symbol'); market = this.safeMarket(marketId, market); const tradeId = this.safeString(order, 'tradeId'); const trades = []; if (tradeId !== '0') { trades.push({ 'id': tradeId }); } return this.safeOrder({ 'id': this.safeString(order, 'orderID'), 'clientOrderId': this.safeString(order, 'clOrdID'), 'datetime': datetime, 'timestamp': this.parse8601(datetime), 'status': this.parseWsOrderStatus(status), 'symbol': this.safeSymbol(marketId, market), 'type': this.safeString(order, 'ordType'), 'timeInForce': this.safeString(order, 'timeInForce'), 'postOnly': this.safeString(order, 'execInst') === 'ALO', 'side': this.safeString(order, 'side'), 'price': this.safeString(order, 'price'), 'stopPrice': this.safeString(order, 'stopPx'), 'cost': undefined, 'amount': this.safeString(order, 'orderQty'), 'filled': this.safeString(order, 'cumQty'), 'remaining': this.safeString(order, 'leavesQty'), 'trades': trades, 'fee': { 'rate': undefined, 'cost': this.safeNumber(order, 'fee'), 'currency': this.safeString(market, 'quote'), }, 'info': order, 'lastTradeTimestamp': undefined, 'average': this.safeString(order, 'avgPx'), }, market); } parseWsOrderStatus(status) { const statuses = { 'pending': 'open', 'open': 'open', 'rejected': 'rejected', 'cancelled': 'canceled', 'filled': 'closed', 'partial': 'open', 'expired': 'expired', }; return this.safeString(statuses, status, status); } async watchOrderBook(symbol, limit = undefined, params = {}) { /** * @method * @name blockchaincom#watchOrderBook * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data * @see https://exchange.blockchain.com/api/#l2-order-book * @param {string} symbol unified symbol of the market to fetch the order book for * @param {int|undefined} limit the maximum amount of order book entries to return * @param {objectConstructor} params extra parameters specific to the blockchaincom api endpoint * @param {string|undefined} params.type accepts l2 or l3 for level 2 or level 3 order book * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/en/latest/manual.html#order-book-structure} indexed by market symbols */ await this.loadMarkets(); const market = this.market(symbol); const url = this.urls['api']['ws']; const type = this.safeString(params, 'type', 'l2'); params = this.omit(params, 'type'); const messageHash = 'orderbook:' + symbol + ':' + type; const subscribe = { 'action': 'subscribe', 'channel': type, 'symbol': market['id'], }; const request = this.deepExtend(subscribe, params); const orderbook = await this.watch(url, messageHash, request, messageHash); return orderbook.limit(); } handleOrderBook(client, message) { // // subscribe // { // seqnum: 0, // event: 'subscribed', // channel: 'l2', // symbol: 'BTC-USDT', // batching: false // } // snapshot // { // seqnum: 1, // event: 'snapshot', // channel: 'l2', // symbol: 'BTC-USDT', // bids: [ // { num: 1, px: 0.01, qty: 22 }, // ], // asks: [ // { num: 1, px: 23840.26, qty: 0.25 }, // ], // timestamp: '2022-08-08T22:03:19.071870Z' // } // update // { // seqnum: 2, // event: 'updated', // channel: 'l2', // symbol: 'BTC-USDT', // bids: [], // asks: [ { num: 1, px: 23855.06, qty: 1.04786347 } ], // timestamp: '2022-08-08T22:03:19.014680Z' // } // const event = this.safeString(message, 'event'); const type = this.safeString(message, 'channel'); const marketId = this.safeString(message, 'symbol'); const symbol = this.safeSymbol(marketId); const messageHash = 'orderbook:' + symbol + ':' + type; const datetime = this.safeString(message, 'timestamp'); const timestamp = this.parse8601(datetime); let storedOrderBook = this.safeValue(this.orderbooks, symbol); if (storedOrderBook === undefined) { storedOrderBook = this.countedOrderBook({}); this.orderbooks[symbol] = storedOrderBook; } if (event === 'subscribed') { return message; } else if (event === 'snapshot') { const snapshot = this.parseCountedOrderBook(message, symbol, timestamp, 'bids', 'asks', 'px', 'qty', 'num'); storedOrderBook.reset(snapshot); } else if (event === 'updated') { const asks = this.safeValue(message, 'asks', []); const bids = this.safeValue(message, 'bids', []); this.handleDeltas(storedOrderBook['asks'], asks); this.handleDeltas(storedOrderBook['bids'], bids); storedOrderBook['timestamp'] = timestamp; storedOrderBook['datetime'] = datetime; } else { throw new NotSupported(this.id + ' watchOrderBook() does not support ' + event + ' yet'); } client.resolve(storedOrderBook, messageHash); } parseCountedBidAsk(bidAsk, priceKey = 0, amountKey = 1, countKey = 2) { const price = this.safeNumber(bidAsk, priceKey); const amount = this.safeNumber(bidAsk, amountKey); const count = this.safeNumber(bidAsk, countKey); return [price, amount, count]; } parseCountedBidsAsks(bidasks, priceKey = 0, amountKey = 1, countKey = 2) { bidasks = this.toArray(bidasks); const result = []; for (let i = 0; i < bidasks.length; i++) { result.push(this.parseCountedBidAsk(bidasks[i], priceKey, amountKey, countKey)); } return result; } parseCountedOrderBook(orderbook, symbol, timestamp = undefined, bidsKey = 'bids', asksKey = 'asks', priceKey = 0, amountKey = 1, countKey = 2) { const bids = this.parseCountedBidsAsks(this.safeValue(orderbook, bidsKey, []), priceKey, amountKey, countKey); const asks = this.parseCountedBidsAsks(this.safeValue(orderbook, asksKey, []), priceKey, amountKey, countKey); return { 'symbol': symbol, 'bids': this.sortBy(bids, 0, true), 'asks': this.sortBy(asks, 0), 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'nonce': undefined, }; } handleDelta(bookside, delta) { const array = this.parseCountedBidAsk(delta, 'px', 'qty', 'num'); bookside.storeArray(array); } handleDeltas(bookside, deltas) { for (let i = 0; i < deltas.length; i++) { this.handleDelta(bookside, deltas[i]); } } checkSequenceNumber(client, message) { const seqnum = this.safeInteger(message, 'seqnum', 0); const channel = this.safeString(message, 'channel', ''); const sequenceNumbersByChannel = this.safeValue(this.options, 'sequenceNumbers', {}); const lastSeqnum = this.safeInteger(sequenceNumbersByChannel, channel); if (lastSeqnum === undefined) { this.options['sequenceNumbers'][channel] = seqnum; } else { if (seqnum !== lastSeqnum + 1) { throw new ExchangeError(this.id + ' ' + channel + ' seqnum ' + seqnum + ' is not the expected ' + (lastSeqnum + 1)); } this.options['sequenceNumbers'][channel] = seqnum; } } handleMessage(client, message) { this.checkSequenceNumber(client, message); const channel = this.safeString(message, 'channel'); const handlers = { 'ticker': this.handleTicker, 'trades': this.handleTrades, 'prices': this.handleOHLCV, 'l2': this.handleOrderBook, 'l3': this.handleOrderBook, 'auth': this.handleAuthenticationMessage, 'balances': this.handleBalance, 'trading': this.handleOrders, }; const handler = this.safeValue(handlers, channel); if (handler !== undefined) { return handler.call(this, client, message); } throw new NotSupported(this.id + ' received an unsupported message: ' + this.json(message)); } handleAuthenticationMessage(client, message) { // // { // seqnum: 0, // event: 'subscribed', // channel: 'auth', // readOnly: false // } // const event = this.safeString(message, 'event'); if (event !== 'subscribed') { throw new AuthenticationError(this.id + ' received an authentication error: ' + this.json(message)); } const future = this.safeValue(client.futures, 'authenticated'); if (future !== undefined) { future.resolve(true); } } authenticate(params = {}) { const url = this.urls['api']['ws']; const client = this.client(url); const messageHash = 'authenticated'; const future = client.future(messageHash); const isAuthenticated = this.safeValue(client.subscriptions, messageHash); if (isAuthenticated === undefined) { this.checkRequiredCredentials(); const request = { 'action': 'subscribe', 'channel': 'auth', 'token': this.secret, }; return this.watch(url, messageHash, this.extend(request, params), messageHash); } return future; } }