UNPKG

ccxt

Version:

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

781 lines (778 loc) • 34.6 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 bullishRest from '../bullish.js'; import { ArrayCache, ArrayCacheBySymbolById, ArrayCacheBySymbolBySide } from '../base/ws/Cache.js'; import { ExchangeError } from '../base/errors.js'; // --------------------------------------------------------------------------- export default class bullish extends bullishRest { describe() { return this.deepExtend(super.describe(), { 'has': { 'ws': true, 'watchTicker': true, 'watchTickers': false, 'watchOrderBook': true, 'watchOrders': true, 'watchTrades': true, 'watchPositions': true, 'watchMyTrades': true, 'watchBalance': true, 'watchOHLCV': false, }, 'urls': { 'api': { 'ws': { 'public': 'wss://api.exchange.bullish.com', 'private': 'wss://api.exchange.bullish.com/trading-api/v1/private-data', }, }, 'test': { 'ws': { 'public': 'wss://api.simnext.bullish-test.com', 'private': 'wss://api.simnext.bullish-test.com/trading-api/v1/private-data', }, }, }, 'options': { 'ws': { 'cookies': {}, }, }, 'streaming': { 'ping': this.ping, 'keepAlive': 99000, // disconnect after 100 seconds of inactivity }, }); } requestId() { const requestId = this.sum(this.safeInteger(this.options, 'requestId', 0), 1); this.options['requestId'] = requestId; return requestId; } ping(client) { // bullish does not support built-in ws protocol-level ping-pong // https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#overview--keep-websocket-open const id = this.requestId().toString(); return { 'jsonrpc': '2.0', 'type': 'command', 'method': 'keepalivePing', 'params': {}, 'id': id, }; } handlePong(client, message) { // // { // "id": "7", // "jsonrpc": "2.0", // "result": { // "responseCodeName": "OK", // "responseCode": "200", // "message": "Keep alive pong" // } // } // client.lastPong = this.milliseconds(); return message; // current line is for transpilation compatibility } async watchPublic(url, messageHash, request = {}, params = {}) { const id = this.requestId().toString(); const message = { 'jsonrpc': '2.0', 'type': 'command', 'method': 'subscribe', 'params': request, 'id': id, }; const fullUrl = this.urls['api']['ws']['public'] + url; return await this.watch(fullUrl, messageHash, this.deepExtend(message, params), messageHash); } async watchPrivate(messageHash, subscribeHash, request = {}, params = {}) { const url = this.urls['api']['ws']['private']; const token = await this.handleToken(); const cookies = { 'JWT_COOKIE': token, }; this.options['ws']['cookies'] = cookies; const id = this.requestId().toString(); const message = { 'jsonrpc': '2.0', 'type': 'command', 'method': 'subscribe', 'params': request, 'id': id, }; const result = await this.watch(url, messageHash, this.deepExtend(message, params), subscribeHash); return result; } /** * @method * @name bullish#watchTrades * @description get the list of most recent trades for a particular symbol * @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#overview--unified-anonymous-trades-websocket-unauthenticated * @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 = {}) { await this.loadMarkets(); const market = this.market(symbol); const messageHash = 'trades::' + market['symbol']; const url = '/trading-api/v1/market-data/trades'; const request = { 'topic': 'anonymousTrades', 'symbol': market['id'], }; const trades = await this.watchPublic(url, messageHash, request, params); if (this.newUpdates) { limit = trades.getLimit(symbol, limit); } return this.filterBySinceLimit(trades, since, limit, 'timestamp', true); } handleTrades(client, message) { // // { // "type": "snapshot", // "dataType": "V1TAAnonymousTradeUpdate", // "data": { // "trades": [ // { // "tradeId": "100086000000609304", // "isTaker": true, // "price": "104889.2063", // "createdAtTimestamp": "1749124509118", // "quantity": "0.01000000", // "publishedAtTimestamp": "1749124531466", // "side": "BUY", // "createdAtDatetime": "2025-06-05T11:55:09.118Z", // "symbol": "BTCUSDC" // } // ], // "createdAtTimestamp": "1749124509118", // "publishedAtTimestamp": "1749124531466", // "symbol": "BTCUSDC" // } // } // const data = this.safeDict(message, 'data', {}); const marketId = this.safeString(data, 'symbol'); const symbol = this.safeSymbol(marketId); const market = this.market(symbol); const rawTrades = this.safeList(data, 'trades', []); const trades = this.parseTrades(rawTrades, market); 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]; for (let i = 0; i < trades.length; i++) { tradesArray.append(trades[i]); } this.trades[symbol] = tradesArray; const messageHash = 'trades::' + market['symbol']; client.resolve(tradesArray, messageHash); } /** * @method * @name bullish#watchTicker * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market * @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#overview--anonymous-market-data-price-tick-unauthenticated * @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); symbol = market['symbol']; const url = this.urls['api']['ws']['public'] + '/trading-api/v1/market-data/tick/' + market['id']; const messageHash = 'ticker::' + symbol; return await this.watch(url, messageHash, params, messageHash); // no need to send a subscribe message, the server sends a ticker update on connect } handleTicker(client, message) { // // { // "type": "update", // "dataType": "V1TATickerResponse", // "data": { // "askVolume": "0.00100822", // "average": "104423.1806", // "baseVolume": "472.83799258", // "bestAsk": "104324.6000", // "bestBid": "104324.5000", // "bidVolume": "0.00020146", // "change": "-198.4864", // "close": "104323.9374", // "createdAtTimestamp": "1749132838951", // "publishedAtTimestamp": "1749132838955", // "high": "105966.6577", // "last": "104323.9374", // "lastTradeDatetime": "2025-06-05T14:13:56.111Z", // "lastTradeSize": "0.02396100", // "low": "104246.6662", // "open": "104522.4238", // "percentage": "-0.19", // "quoteVolume": "49662592.6712", // "symbol": "BTC-USDC-PERP", // "type": "ticker", // "vwap": "105030.6996", // "currentPrice": "104324.7747", // "ammData": [ // { // "feeTierId": "1", // "currentPrice": "104324.7747", // "baseReservesQuantity": "8.27911366", // "quoteReservesQuantity": "1067283.0234", // "bidSpreadFee": "0.00000000", // "askSpreadFee": "0.00000000" // } // ], // "createdAtDatetime": "2025-06-05T14:13:58.951Z", // "markPrice": "104289.6884", // "fundingRate": "-0.000192", // "openInterest": "92.24146651" // } // } // const updateType = this.safeString(message, 'type', ''); const data = this.safeDict(message, 'data', {}); const marketId = this.safeString(data, 'symbol'); const market = this.safeMarket(marketId); const symbol = market['symbol']; let parsed = undefined; if ((updateType === 'snapshot')) { parsed = this.parseTicker(data, market); } else if (updateType === 'update') { const ticker = this.safeDict(this.tickers, symbol, {}); const rawTicker = this.safeDict(ticker, 'info', {}); const merged = this.extend(rawTicker, data); parsed = this.parseTicker(merged, market); } this.tickers[symbol] = parsed; const messageHash = 'ticker::' + symbol; client.resolve(this.tickers[symbol], messageHash); } /** * @method * @name bullish#watchOrderBook * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data * @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#overview--multi-orderbook-websocket-unauthenticated * @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); const url = '/trading-api/v1/market-data/orderbook'; const messageHash = 'orderbook::' + market['symbol']; const request = { 'topic': 'l2Orderbook', 'symbol': market['id'], }; const orderbook = await this.watchPublic(url, messageHash, request, params); return orderbook.limit(); } handleOrderBook(client, message) { // // { // "type": "snapshot", // "dataType": "V1TALevel2", // "data": { // "timestamp": "1749372632028", // "bids": [ // "105523.3000", // "0.00046045", // ], // "asks": [ // "105523.4000", // "0.00117112", // ], // "publishedAtTimestamp": "1749372632073", // "datetime": "2025-06-08T08:50:32.028Z", // "sequenceNumberRange": [ 1967862061, 1967862062 ], // "symbol": "BTCUSDC" // } // } // // current channel is 'l2Orderbook' which returns only snapshots const data = this.safeDict(message, 'data', {}); const marketId = this.safeString(data, 'symbol'); const symbol = this.safeSymbol(marketId); const messageHash = 'orderbook::' + symbol; const timestamp = this.safeInteger(data, 'timestamp'); if (!(symbol in this.orderbooks)) { this.orderbooks[symbol] = this.orderBook(); } const orderbook = this.orderbooks[symbol]; const bids = this.separateBidsOrAsks(this.safeList(data, 'bids', [])); const asks = this.separateBidsOrAsks(this.safeList(data, 'asks', [])); const snapshot = { 'bids': bids, 'asks': asks, }; const parsed = this.parseOrderBook(snapshot, symbol, timestamp); const sequenceNumberRange = this.safeList(data, 'sequenceNumberRange', []); if (sequenceNumberRange.length > 0) { const lastIndex = sequenceNumberRange.length - 1; parsed['nonce'] = this.safeInteger(sequenceNumberRange, lastIndex); } orderbook.reset(parsed); this.orderbooks[symbol] = orderbook; client.resolve(orderbook, messageHash); } separateBidsOrAsks(entry) { const result = []; // 300 = '54885.0000000' // 301 = '0.06141566' // 302 ='53714.0000000' for (let i = 0; i < entry.length; i++) { if (i % 2 !== 0) { continue; } const price = this.safeString(entry, i); const amount = this.safeString(entry, i + 1); result.push([price, amount]); } return result; } /** * @method * @name bullish#watchOrders * @description watches information on multiple orders made by the user * @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#overview--private-data-websocket-authenticated * @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 * @param {string} [params.tradingAccountId] the trading account id to fetch entries for * @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(); const subscribeHash = 'orders'; let messageHash = subscribeHash; if (symbol !== undefined) { symbol = this.symbol(symbol); messageHash = messageHash + '::' + symbol; } const request = { 'topic': 'orders', }; const tradingAccountId = this.safeString(params, 'tradingAccountId'); if (tradingAccountId !== undefined) { request['tradingAccountId'] = tradingAccountId; params = this.omit(params, 'tradingAccountId'); } const orders = await this.watchPrivate(messageHash, subscribeHash, request, params); if (this.newUpdates) { limit = orders.getLimit(symbol, limit); } return this.filterBySymbolSinceLimit(orders, symbol, since, limit, true); } handleOrders(client, message) { // snapshot // { // "type": "snapshot", // "tradingAccountId": "111309424211255", // "dataType": "V1TAOrder", // "data": [ ... ] // could be an empty list or a list of orders // } // // update // { // "type": "update", // "tradingAccountId": "111309424211255", // "dataType": "V1TAOrder", // "data": { // "status": "OPEN", // "createdAtTimestamp": "1751893427971", // "quoteFee": "0.000000", // "stopPrice": null, // "quantityFilled": "0.00000000", // "handle": null, // "clientOrderId": null, // "quantity": "0.10000000", // "margin": false, // "side": "BUY", // "createdAtDatetime": "2025-07-07T13:03:47.971Z", // "isLiquidation": false, // "borrowedQuoteQuantity": null, // "borrowedBaseQuantity": null, // "timeInForce": "GTC", // "borrowedQuantity": null, // "baseFee": "0.000000", // "quoteAmount": "0.0000000", // "price": "0.0000000", // "statusReason": "Order accepted", // "type": "MKT", // "statusReasonCode": 6014, // "allowBorrow": false, // "orderId": "862317981870850049", // "publishedAtTimestamp": "1751893427975", // "symbol": "ETHUSDT", // "averageFillPrice": null // } // } // const type = this.safeString(message, 'type'); let rawOrders = []; if (type === 'update') { const data = this.safeDict(message, 'data', {}); rawOrders.push(data); // update is a single order } else { rawOrders = this.safeList(message, 'data', []); // snapshot is a list of orders } if (rawOrders.length > 0) { if (this.orders === undefined) { const limit = this.safeInteger(this.options, 'ordersLimit', 1000); this.orders = new ArrayCacheBySymbolById(limit); } const orders = this.orders; const symbols = {}; for (let i = 0; i < rawOrders.length; i++) { const rawOrder = rawOrders[i]; const parsedOrder = this.parseOrder(rawOrder); orders.append(parsedOrder); const symbol = this.safeString(parsedOrder, 'symbol'); symbols[symbol] = true; } const messageHash = 'orders'; client.resolve(orders, messageHash); const keys = Object.keys(symbols); for (let i = 0; i < keys.length; i++) { const hashSymbol = keys[i]; const symbolMessageHash = messageHash + '::' + hashSymbol; client.resolve(this.orders, symbolMessageHash); } } } /** * @method * @name bullish#watchMyTrades * @description watches information on multiple trades made by the user * @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#overview--private-data-websocket-authenticated * @param {string} symbol unified market symbol of the market trades were made in * @param {int} [since] the earliest time in ms to fetch trades for * @param {int} [limit] the maximum number of trade structures to retrieve * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {string} [params.tradingAccountId] the trading account id to fetch entries for * @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(); const subscribeHash = 'myTrades'; let messageHash = subscribeHash; if (symbol !== undefined) { symbol = this.symbol(symbol); messageHash += '::' + symbol; } const request = { 'topic': 'trades', }; const tradingAccountId = this.safeString(params, 'tradingAccountId'); if (tradingAccountId !== undefined) { request['tradingAccountId'] = tradingAccountId; params = this.omit(params, 'tradingAccountId'); } const trades = await this.watchPrivate(messageHash, subscribeHash, request, params); if (this.newUpdates) { limit = trades.getLimit(symbol, limit); } return this.filterBySinceLimit(trades, since, limit, 'timestamp', true); } handleMyTrades(client, message) { // // snapshot // { // "type": "snapshot", // "tradingAccountId": "111309424211255", // "dataType": "V1TATrade", // "data": [ ... ] // could be an empty list or a list of trades // } // // update // { // "type": "update", // "tradingAccountId": "111309424211255", // "dataType": "V1TATrade", // "data": { // "clientOtcTradeId": null, // "tradeId": "100203000003940164", // "baseFee": "0.00000000", // "isTaker": true, // "quoteAmount": "253.6012195", // "price": "2536.0121950", // "createdAtTimestamp": "1751914859840", // "quoteFee": "0.0000000", // "tradeRebateAmount": null, // "tradeRebateAssetSymbol": null, // "handle": null, // "otcTradeId": null, // "otcMatchId": null, // "orderId": "862407873644725249", // "quantity": "0.10000000", // "publishedAtTimestamp": "1751914859843", // "side": "SELL", // "createdAtDatetime": "2025-07-07T19:00:59.840Z", // "symbol": "ETHUSDT" // } // } // const type = this.safeString(message, 'type'); let rawTrades = []; if (type === 'update') { const data = this.safeDict(message, 'data', {}); rawTrades.push(data); // update is a single trade } else { rawTrades = this.safeList(message, 'data', []); // snapshot is a list of trades } if (rawTrades.length > 0) { if (this.myTrades === undefined) { const limit = this.safeInteger(this.options, 'tradesLimit', 1000); this.myTrades = new ArrayCacheBySymbolById(limit); } const trades = this.myTrades; const symbols = {}; for (let i = 0; i < rawTrades.length; i++) { const rawTrade = rawTrades[i]; const parsedTrade = this.parseTrade(rawTrade); trades.append(parsedTrade); const symbol = this.safeString(parsedTrade, 'symbol'); symbols[symbol] = true; } const messageHash = 'myTrades'; client.resolve(trades, messageHash); const keys = Object.keys(symbols); for (let i = 0; i < keys.length; i++) { const hashSymbol = keys[i]; const symbolMessageHash = messageHash + '::' + hashSymbol; client.resolve(this.myTrades, symbolMessageHash); } } } /** * @method * @name bullish#watchBalance * @description watch balance and get the amount of funds available for trading or funds locked in orders * @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#overview--private-data-websocket-authenticated * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {string} [params.tradingAccountId] the trading account id to fetch entries for * @returns {object} a [balance structure]{@link https://docs.ccxt.com/?id=balance-structure} */ async watchBalance(params = {}) { await this.loadMarkets(); const request = { 'topic': 'assetAccounts', }; let messageHash = 'balance'; const tradingAccountId = this.safeString(params, 'tradingAccountId'); if (tradingAccountId !== undefined) { params = this.omit(params, 'tradingAccountId'); request['tradingAccountId'] = tradingAccountId; messageHash += '::' + tradingAccountId; } return await this.watchPrivate(messageHash, messageHash, request, params); } handleBalance(client, message) { // // snapshot // { // "type": "snapshot", // "tradingAccountId": "111309424211255", // "dataType": "V1TAAssetAccount", // "data": [ // { // "updatedAtTimestamp": "1751989627509", // "borrowedQuantity": "0.0000", // "tradingAccountId": "111309424211255", // "loanedQuantity": "0.0000", // "lockedQuantity": "0.0000", // "assetId": "5", // "assetSymbol": "USDC", // "publishedAtTimestamp": "1751989627512", // "availableQuantity": "999672939.8767", // "updatedAtDatetime": "2025-07-08T15:47:07.509Z" // } // ] // } // // update // { // "type": "update", // "tradingAccountId": "111309424211255", // "dataType": "V1TAAssetAccount", // "data": { // "updatedAtTimestamp": "1751989627509", // "borrowedQuantity": "0.0000", // "tradingAccountId": "111309424211255", // "loanedQuantity": "0.0000", // "lockedQuantity": "0.0000", // "assetId": "5", // "assetSymbol": "USDC", // "publishedAtTimestamp": "1751989627512", // "availableQuantity": "999672939.8767", // "updatedAtDatetime": "2025-07-08T15:47:07.509Z" // } // } // const tradingAccountId = this.safeString(message, 'tradingAccountId'); if (!(tradingAccountId in this.balance)) { this.balance[tradingAccountId] = {}; } const messageType = this.safeString(message, 'type'); if (messageType === 'snapshot') { const data = this.safeList(message, 'data', []); this.balance[tradingAccountId] = this.parseBalance(data); } else { const data = this.safeDict(message, 'data', {}); const assetId = this.safeString(data, 'assetSymbol'); const account = this.account(); account['total'] = this.safeString(data, 'availableQuantity'); account['used'] = this.safeString(data, 'lockedQuantity'); const code = this.safeCurrencyCode(assetId); this.balance[tradingAccountId][code] = account; this.balance[tradingAccountId]['info'] = message; this.balance[tradingAccountId] = this.safeBalance(this.balance[tradingAccountId]); } const messageHash = 'balance'; const tradingAccountIdHash = '::' + tradingAccountId; client.resolve(this.balance[tradingAccountId], messageHash); client.resolve(this.balance[tradingAccountId], messageHash + tradingAccountIdHash); } /** * @method * @name bullish#watchPositions * @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#overview--private-data-websocket-authenticated * @description watch all open positions * @param {string[]} [symbols] list of unified market symbols * @param {int} [since] the earliest time in ms to fetch positions for * @param {int} [limit] the maximum number of positions to retrieve * @param {object} params extra parameters specific to the exchange API endpoint * @returns {object[]} a list of [position structure]{@link https://docs.ccxt.com/en/latest/manual.html#position-structure} */ async watchPositions(symbols = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets(); const subscribeHash = 'positions'; let messageHash = subscribeHash; if (!this.isEmpty(symbols)) { symbols = this.marketSymbols(symbols); messageHash += '::' + symbols.join(','); } const request = { 'topic': 'derivativesPositionsV2', }; const positions = await this.watchPrivate(messageHash, subscribeHash, request, params); if (this.newUpdates) { return positions; } return this.filterBySymbolsSinceLimit(positions, symbols, since, limit, true); } handlePositions(client, message) { // exchange does not return messages for sandbox mode // current method is implemented blindly // todo: check if this works with not-sandbox mode const messageType = this.safeString(message, 'type'); let rawPositions = []; if (messageType === 'update') { const data = this.safeDict(message, 'data', {}); rawPositions.push(data); } else { rawPositions = this.safeList(message, 'data', []); } if (this.positions === undefined) { this.positions = new ArrayCacheBySymbolBySide(); } const positions = this.positions; const newPositions = []; for (let i = 0; i < rawPositions.length; i++) { const rawPosition = rawPositions[i]; const position = this.parsePosition(rawPosition); positions.append(position); newPositions.push(position); } const messageHashes = this.findMessageHashes(client, 'positions::'); for (let i = 0; i < messageHashes.length; i++) { const messageHash = messageHashes[i]; const parts = messageHash.split('::'); const symbolsString = parts[1]; const symbols = symbolsString.split(','); const symbolPositions = this.filterByArray(newPositions, 'symbol', symbols, false); if (!this.isEmpty(symbolPositions)) { client.resolve(symbolPositions, messageHash); } } client.resolve(positions, 'positions'); } handleErrorMessage(client, message) { // // { // "data": { // "errorCode": 401, // "errorCodeName": "UNAUTHORIZED", // "message": "Unable to authenticate; JWT is missing/invalid or unauthorised to access account" // }, // "dataType": "V1TAErrorResponse", // "type": "error" // } // const data = this.safeDict(message, 'data', {}); const feedback = this.id + ' ' + this.json(data); try { const errorCode = this.safeString(data, 'errorCode'); const errorCodeName = this.safeString(data, 'errorCodeName'); this.throwExactlyMatchedException(this.exceptions['exact'], errorCode, feedback); this.throwBroadlyMatchedException(this.exceptions['broad'], errorCodeName, feedback); throw new ExchangeError(feedback); // unknown message } catch (e) { client.reject(e); } } handleMessage(client, message) { const dataType = this.safeString(message, 'dataType'); const result = this.safeDict(message, 'result'); if (result !== undefined) { const response = this.safeString(result, 'message'); if (response === 'Keep alive pong') { this.handlePong(client, message); } } else if (dataType !== undefined) { if (dataType === 'V1TAAnonymousTradeUpdate') { this.handleTrades(client, message); } if (dataType === 'V1TATickerResponse') { this.handleTicker(client, message); } if (dataType === 'V1TALevel2') { this.handleOrderBook(client, message); } if (dataType === 'V1TAOrder') { this.handleOrders(client, message); } if (dataType === 'V1TATrade') { this.handleMyTrades(client, message); } if (dataType === 'V1TAAssetAccount') { this.handleBalance(client, message); } if (dataType === 'V1TAErrorResponse') { this.handleErrorMessage(client, message); } } } }