UNPKG

ccxt

Version:

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

1,013 lines (1,010 loc) • 40 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 ascendexRest from '../ascendex.js'; import { AuthenticationError, NetworkError } from '../base/errors.js'; import { ArrayCache, ArrayCacheByTimestamp, ArrayCacheBySymbolById } from '../base/ws/Cache.js'; import { sha256 } from '../static_dependencies/noble-hashes/sha256.js'; // --------------------------------------------------------------------------- export default class ascendex extends ascendexRest { describe() { return this.deepExtend(super.describe(), { 'has': { 'ws': true, 'watchBalance': true, 'watchOHLCV': true, 'watchOrderBook': true, 'watchOrders': true, 'watchTicker': false, 'watchTrades': true, 'watchTradesForSymbols': true, }, 'urls': { 'api': { 'ws': { 'public': 'wss://ascendex.com:443/api/pro/v2/stream', 'private': 'wss://ascendex.com:443/{accountGroup}/api/pro/v2/stream', }, }, 'test': { 'ws': { 'public': 'wss://api-test.ascendex-sandbox.com:443/api/pro/v2/stream', 'private': 'wss://api-test.ascendex-sandbox.com:443/{accountGroup}/api/pro/v2/stream', }, }, }, 'options': { 'tradesLimit': 1000, 'ordersLimit': 1000, 'OHLCVLimit': 1000, 'categoriesAccount': { 'cash': 'spot', 'futures': 'swap', 'margin': 'margin', }, }, }); } async watchPublic(messageHash, params = {}) { const url = this.urls['api']['ws']['public']; const id = this.nonce(); const request = { 'id': id.toString(), 'op': 'sub', }; const message = this.extend(request, params); return await this.watch(url, messageHash, message, messageHash); } async watchPublicMultiple(messageHashes, params = {}) { const url = this.urls['api']['ws']['public']; const id = this.nonce(); const request = { 'id': id.toString(), 'op': 'sub', }; const message = this.extend(request, params); return await this.watchMultiple(url, messageHashes, message, messageHashes); } async watchPrivate(channel, messageHash, params = {}) { await this.loadAccounts(); const accountGroup = this.safeString(this.options, 'account-group'); let url = this.urls['api']['ws']['private']; url = this.implodeParams(url, { 'accountGroup': accountGroup }); const id = this.nonce(); const request = { 'id': id.toString(), 'op': 'sub', 'ch': channel, }; const message = this.extend(request, params); await this.authenticate(url, params); return await this.watch(url, messageHash, message, channel); } /** * @method * @name ascendex#watchOHLCV * @description watches historical candlestick data containing the open, high, low, and close price, and the volume of a market * @see https://ascendex.github.io/ascendex-pro-api/#channel-bar-data * @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']; if ((limit === undefined) || (limit > 1440)) { limit = 100; } const interval = this.safeString(this.timeframes, timeframe, timeframe); const channel = 'bar' + ':' + interval + ':' + market['id']; params = { 'ch': channel, }; const ohlcv = await this.watchPublic(channel, params); if (this.newUpdates) { limit = ohlcv.getLimit(symbol, limit); } return this.filterBySinceLimit(ohlcv, since, limit, 0, true); } handleOHLCV(client, message) { // // { // "m": "bar", // "s": "ASD/USDT", // "data": { // "i": "1", // "ts": 1575398940000, // "o": "0.04993", // "c": "0.04970", // "h": "0.04993", // "l": "0.04970", // "v": "8052" // } // } // const marketId = this.safeString(message, 's'); const symbol = this.safeSymbol(marketId); const channel = this.safeString(message, 'm'); const data = this.safeValue(message, 'data', {}); const interval = this.safeString(data, 'i'); const messageHash = channel + ':' + interval + ':' + marketId; const timeframe = this.findTimeframe(interval); const market = this.market(symbol); const parsed = this.parseOHLCV(message, market); 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(parsed); client.resolve(stored, messageHash); return message; } /** * @method * @name ascendex#watchTrades * @description get the list of most recent trades for a particular symbol * @see https://ascendex.github.io/ascendex-pro-api/#channel-market-trades * @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 ascendex#watchTradesForSymbols * @description get the list of most recent trades for a list of symbols * @see https://ascendex.github.io/ascendex-pro-api/#channel-market-trades * @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 {string} [params.name] the name of the method to call, 'trade' or 'aggTrade', default is 'trade' * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades} */ async watchTradesForSymbols(symbols, since = undefined, limit = undefined, params = {}) { await this.loadMarkets(); symbols = this.marketSymbols(symbols, undefined, false, true, true); const marketIds = []; const messageHashes = []; if (symbols !== undefined) { for (let i = 0; i < symbols.length; i++) { const market = this.market(symbols[i]); marketIds.push(market['id']); messageHashes.push('trades:' + market['id']); } } const channel = 'trades:' + marketIds.join(','); params = this.extend(params, { 'ch': channel, }); const trades = await this.watchPublicMultiple(messageHashes, params); if (this.newUpdates) { const first = this.safeValue(trades, 0); const tradeSymbol = this.safeString(first, 'symbol'); limit = trades.getLimit(tradeSymbol, limit); } return this.filterBySinceLimit(trades, since, limit, 'timestamp', true); } handleTrades(client, message) { // // { // "m": "trades", // "symbol": "BTC/USDT", // "data": [ // { // "p": "40744.28", // "q": "0.00150", // "ts": 1647514330758, // "bm": true, // "seqnum": 72057633465800320 // } // ] // } // const marketId = this.safeString(message, 'symbol'); const symbol = this.safeSymbol(marketId); const channel = this.safeString(message, 'm'); const messageHash = channel + ':' + marketId; const market = this.market(symbol); let rawData = this.safeValue(message, 'data'); if (rawData === undefined) { rawData = []; } const trades = this.parseTrades(rawData, market); let tradesArray = this.safeValue(this.trades, symbol); if (tradesArray === undefined) { const limit = this.safeInteger(this.options, 'tradesLimit', 1000); tradesArray = new ArrayCache(limit); } for (let i = 0; i < trades.length; i++) { tradesArray.append(trades[i]); } this.trades[symbol] = tradesArray; client.resolve(tradesArray, messageHash); } /** * @method * @name ascendex#watchOrderBook * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data * @see https://ascendex.github.io/ascendex-pro-api/#channel-level-2-order-book-updates * @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 channel = 'depth' + ':' + market['id']; params = this.extend(params, { 'ch': channel, }); const orderbook = await this.watchPublic(channel, params); return orderbook.limit(); } async watchOrderBookSnapshot(symbol, limit = undefined, params = {}) { await this.loadMarkets(); const market = this.market(symbol); const action = 'depth-snapshot'; const channel = action + ':' + market['id']; params = this.extend(params, { 'action': action, 'args': { 'symbol': market['id'], }, 'op': 'req', }); const orderbook = await this.watchPublic(channel, params); return orderbook.limit(); } async fetchOrderBookSnapshot(symbol, limit = undefined, params = {}) { const restOrderBook = await this.fetchRestOrderBookSafe(symbol, limit, params); if (!(symbol in this.orderbooks)) { this.orderbooks[symbol] = this.orderBook(); } const orderbook = this.orderbooks[symbol]; orderbook.reset(restOrderBook); return orderbook; } handleOrderBookSnapshot(client, message) { // // { // "m": "depth", // "symbol": "BTC/USDT", // "data": { // "ts": 1647520500149, // "seqnum": 28590487626, // "asks": [ // [Array], [Array], [Array], // [Array], [Array], [Array], // ], // "bids": [ // [Array], [Array], [Array], // [Array], [Array], [Array], // ] // } // } // const marketId = this.safeString(message, 'symbol'); const symbol = this.safeSymbol(marketId); const channel = this.safeString(message, 'm'); const messageHash = channel + ':' + symbol; const orderbook = this.orderbooks[symbol]; const data = this.safeValue(message, 'data'); const snapshot = this.parseOrderBook(data, symbol); snapshot['nonce'] = this.safeInteger(data, 'seqnum'); orderbook.reset(snapshot); // unroll the accumulated deltas const messages = orderbook.cache; for (let i = 0; i < messages.length; i++) { const messageItem = messages[i]; this.handleOrderBookMessage(client, messageItem, orderbook); } this.orderbooks[symbol] = orderbook; client.resolve(orderbook, messageHash); } handleOrderBook(client, message) { // // { // "m": "depth", // "symbol": "BTC/USDT", // "data": { // "ts": 1647515136144, // "seqnum": 28590470736, // "asks": [ [Array], [Array] ], // "bids": [ [Array], [Array], [Array], [Array], [Array], [Array] ] // } // } // const channel = this.safeString(message, 'm'); const marketId = this.safeString(message, 'symbol'); const symbol = this.safeSymbol(marketId); const messageHash = channel + ':' + marketId; if (!(symbol in this.orderbooks)) { this.orderbooks[symbol] = this.orderBook({}); } const orderbook = this.orderbooks[symbol]; if (orderbook['nonce'] === undefined) { orderbook.cache.push(message); } else { this.handleOrderBookMessage(client, message, orderbook); client.resolve(orderbook, messageHash); } } handleDelta(bookside, delta) { // // ["40990.47","0.01619"], // const price = this.safeFloat(delta, 0); const amount = this.safeFloat(delta, 1); bookside.store(price, amount); } handleDeltas(bookside, deltas) { for (let i = 0; i < deltas.length; i++) { this.handleDelta(bookside, deltas[i]); } } handleOrderBookMessage(client, message, orderbook) { // // { // "m":"depth", // "symbol":"BTC/USDT", // "data":{ // "ts":1647527417715, // "seqnum":28590257013, // "asks":[ // ["40990.47","0.01619"], // ["41021.21","0"], // ["41031.59","0.06096"] // ], // "bids":[ // ["40990.46","0.76114"], // ["40985.18","0"] // ] // } // } // const data = this.safeValue(message, 'data', {}); const seqNum = this.safeInteger(data, 'seqnum'); if (seqNum > orderbook['nonce']) { const asks = this.safeValue(data, 'asks', []); const bids = this.safeValue(data, 'bids', []); this.handleDeltas(orderbook['asks'], asks); this.handleDeltas(orderbook['bids'], bids); orderbook['nonce'] = seqNum; const timestamp = this.safeInteger(data, 'ts'); orderbook['timestamp'] = timestamp; orderbook['datetime'] = this.iso8601(timestamp); } return orderbook; } /** * @method * @name ascendex#watchBalance * @description watch balance and get the amount of funds available for trading or funds locked in orders * @see https://ascendex.github.io/ascendex-pro-api/#channel-order-and-balance * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure} */ async watchBalance(params = {}) { await this.loadMarkets(); const [type, query] = this.handleMarketTypeAndParams('watchBalance', undefined, params); let channel = undefined; let messageHash = undefined; if ((type === 'spot') || (type === 'margin')) { const accountCategories = this.safeValue(this.options, 'accountCategories', {}); let accountCategory = this.safeString(accountCategories, type, 'cash'); // cash, margin, accountCategory = accountCategory.toUpperCase(); channel = 'order:' + accountCategory; // order and balance share the same channel messageHash = 'balance:' + type; } else { channel = 'futures-account-update'; messageHash = 'balance:swap'; } return await this.watchPrivate(channel, messageHash, query); } handleBalance(client, message) { // // cash account // // { // "m": "balance", // "accountId": "cshQtyfq8XLAA9kcf19h8bXHbAwwoqEo", // "ac": "CASH", // "data": { // "a" : "USDT", // "sn": 8159798, // "tb": "600", // "ab": "600" // } // } // // margin account // // { // "m": "balance", // "accountId": "marOxpKJV83dxTRx0Eyxpa0gxc4Txt0P", // "ac": "MARGIN", // "data": { // "a" : "USDT", // "sn" : 8159802, // "tb" : "400", // total Balance // "ab" : "400", // available balance // "brw": "0", // borrowws // "int": "0" // interest // } // } // // futures // { // "m" : "futures-account-update", // message // "e" : "ExecutionReport", // event type // "t" : 1612508562129, // time // "acc" : "futures-account-id", // account ID // "at" : "FUTURES", // account type // "sn" : 23128, // sequence number, // "id" : "r177710001cbU3813942147C5kbFGOan", // "col": [ // { // "a": "USDT", // asset code // "b": "1000000", // balance // "f": "1" // discount factor // } // ], // (...) // const channel = this.safeString(message, 'm'); let result = undefined; let type = undefined; if ((channel === 'order') || (channel === 'futures-order')) { const data = this.safeValue(message, 'data'); const marketId = this.safeString(data, 's'); const market = this.safeMarket(marketId); const baseAccount = this.account(); baseAccount['free'] = this.safeString(data, 'bab'); baseAccount['total'] = this.safeString(data, 'btb'); const quoteAccount = this.account(); quoteAccount['free'] = this.safeString(data, 'qab'); quoteAccount['total'] = this.safeString(data, 'qtb'); if (market['contract']) { type = 'swap'; result = this.safeValue(this.balance, type, {}); } else { type = market['type']; result = this.safeValue(this.balance, type, {}); } result[market['base']] = baseAccount; result[market['quote']] = quoteAccount; } else { const accountType = this.safeStringLower2(message, 'ac', 'at'); const categoriesAccounts = this.safeValue(this.options, 'categoriesAccount'); type = this.safeString(categoriesAccounts, accountType, 'spot'); result = this.safeValue(this.balance, type, {}); const data = this.safeValue(message, 'data'); let balances = undefined; if (data === undefined) { balances = this.safeValue(message, 'col'); } else { balances = [data]; } for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const code = this.safeCurrencyCode(this.safeString(balance, 'a')); const account = this.account(); account['free'] = this.safeString(balance, 'ab'); account['total'] = this.safeString2(balance, 'tb', 'b'); result[code] = account; } } const messageHash = 'balance' + ':' + type; client.resolve(this.safeBalance(result), messageHash); } /** * @method * @name ascendex#watchOrders * @see https://ascendex.github.io/ascendex-pro-api/#channel-order-and-balance * @description watches information on multiple orders made by the user * @param {string} symbol unified market symbol of the market orders were made in * @param {int} [since] the earliest time in ms to fetch orders for * @param {int} [limit] the maximum number of order structures to retrieve * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} */ async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets(); let market = undefined; if (symbol !== undefined) { market = this.market(symbol); symbol = market['symbol']; } const [type, query] = this.handleMarketTypeAndParams('watchOrders', market, params); let messageHash = undefined; let channel = undefined; if (type !== 'spot' && type !== 'margin') { channel = 'futures-order'; messageHash = 'order:FUTURES'; } else { const accountCategories = this.safeValue(this.options, 'accountCategories', {}); let accountCategory = this.safeString(accountCategories, type, 'cash'); // cash, margin accountCategory = accountCategory.toUpperCase(); messageHash = 'order' + ':' + accountCategory; channel = messageHash; } if (symbol !== undefined) { messageHash = messageHash + ':' + symbol; } const orders = await this.watchPrivate(channel, messageHash, query); if (this.newUpdates) { limit = orders.getLimit(symbol, limit); } return this.filterBySymbolSinceLimit(orders, symbol, since, limit, true); } handleOrder(client, message) { // // spot order // { // "m": "order", // "accountId": "cshF5SlR9ukAXoDOuXbND4dVpBMw9gzH", // "ac": "CASH", // "data": { // "sn": 19399016185, // "orderId": "r17f9d7983faU7223046196CMlrj3bfC", // "s": "LTC/USDT", // "ot": "Limit", // "t": 1647614461160, // "p": "50", // "q": "0.1", // "sd": "Buy", // "st": "New", // "ap": "0", // "cfq": "0", // "sp": '', // "err": '', // "btb": "0", // "bab": "0", // "qtb": "8", // "qab": "2.995", // "cf": "0", // "fa": "USDT", // "ei": "NULL_VAL" // } // } // // futures order // { // "m": "futures-order", // "sn": 19399927636, // "e": "ExecutionReport", // "a": "futF5SlR9ukAXoDOuXbND4dVpBMw9gzH", // account id // "ac": "FUTURES", // "t": 1647622515434, // last execution time // (...) // } // const accountType = this.safeString(message, 'ac'); const messageHash = 'order:' + accountType; const data = this.safeValue(message, 'data', message); const order = this.parseWsOrder(data); if (this.orders === undefined) { const limit = this.safeInteger(this.options, 'ordersLimit', 1000); this.orders = new ArrayCacheBySymbolById(limit); } const orders = this.orders; orders.append(order); const symbolMessageHash = messageHash + ':' + order['symbol']; client.resolve(orders, symbolMessageHash); client.resolve(orders, messageHash); } parseWsOrder(order, market = undefined) { // // spot order // { // "sn": 19399016185, //sequence number // "orderId": "r17f9d7983faU7223046196CMlrj3bfC", // "s": "LTC/USDT", // "ot": "Limit", // order type // "t": 1647614461160, // last execution timestamp // "p": "50", // price // "q": "0.1", // quantity // "sd": "Buy", // side // "st": "New", // status // "ap": "0", // average fill price // "cfq": "0", // cumulated fill quantity // "sp": '', // stop price // "err": '', // "btb": "0", // base asset total balance // "bab": "0", // base asset available balance // "qtb": "8", // quote asset total balance // "qab": "2.995", // quote asset available balance // "cf": "0", // cumulated commission // "fa": "USDT", // fee asset // "ei": "NULL_VAL" // } // // futures order // { // "m": "futures-order", // "sn": 19399927636, // "e": "ExecutionReport", // "a": "futF5SlR9ukAXoDOuXbND4dVpBMw9gzH", // account id // "ac": "FUTURES", // "t": 1647622515434, // last execution time // "ct": 1647622515413, // order creation time // "orderId": "r17f9df469b1U7223046196Okf5Kbmd", // "sd": "Buy", // side // "ot": "Limit", // order type // "ei": "NULL_VAL", // "q": "1", // quantity // "p": "50", //price // "sp": "0", // stopPrice // "spb": '', // stopTrigger // "s": "LTC-PERP", // symbol // "st": "New", // state // "err": '', // "lp": "0", // last filled price // "lq": "0", // last filled quantity (base asset) // "ap": "0", // average filled price // "cfq": "0", // cummulative filled quantity (base asset) // "f": "0", // commission fee of the current execution // "cf": "0", // cumulative commission fee // "fa": "USDT", // fee asset // "psl": "0", // "pslt": "market", // "ptp": "0", // "ptpt": "market" // } // const status = this.parseOrderStatus(this.safeString(order, 'st')); const marketId = this.safeString(order, 's'); const timestamp = this.safeInteger(order, 't'); const symbol = this.safeSymbol(marketId, market, '/'); const lastTradeTimestamp = this.safeInteger(order, 't'); const price = this.safeString(order, 'p'); const amount = this.safeString(order, 'q'); const average = this.safeString(order, 'ap'); const filled = this.safeString(order, 'cfq'); const id = this.safeString(order, 'orderId'); const type = this.safeStringLower(order, 'ot'); const side = this.safeStringLower(order, 'sd'); const feeCost = this.safeNumber(order, 'cf'); let fee = undefined; if (feeCost !== undefined) { const feeCurrencyId = this.safeString(order, 'fa'); const feeCurrencyCode = this.safeCurrencyCode(feeCurrencyId); fee = { 'cost': feeCost, 'currency': feeCurrencyCode, }; } const stopPrice = this.parseNumber(this.omitZero(this.safeString(order, 'sp'))); return this.safeOrder({ 'info': order, 'id': id, 'clientOrderId': undefined, 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'symbol': symbol, 'type': type, 'timeInForce': undefined, 'postOnly': undefined, 'side': side, 'price': price, 'stopPrice': stopPrice, 'triggerPrice': stopPrice, 'amount': amount, 'cost': undefined, 'average': average, 'filled': filled, 'remaining': undefined, 'status': status, 'fee': fee, 'trades': undefined, }, market); } handleErrorMessage(client, message) { // // { // "m": "disconnected", // "code": 100005, // "reason": "INVALID_WS_REQUEST_DATA", // "info": "Session is disconnected due to missing pong message from the client" // } // const errorCode = this.safeInteger(message, 'code'); try { if (errorCode !== undefined) { const feedback = this.id + ' ' + this.json(message); this.throwExactlyMatchedException(this.exceptions['exact'], errorCode, feedback); const messageString = this.safeValue(message, 'message'); if (messageString !== undefined) { this.throwBroadlyMatchedException(this.exceptions['broad'], messageString, feedback); } } return false; } catch (e) { if (e instanceof AuthenticationError) { const messageHash = 'authenticated'; client.reject(e, messageHash); if (messageHash in client.subscriptions) { delete client.subscriptions[messageHash]; } } else { client.reject(e); } return true; } } handleAuthenticate(client, message) { // // { m: "auth", id: "1647605234", code: 0 } // const messageHash = 'authenticated'; client.resolve(message, messageHash); } handleMessage(client, message) { if (this.handleErrorMessage(client, message)) { return; } // // { m: "ping", hp: 3 } // // { m: "sub", ch: "bar:BTC/USDT", code: 0 } // // { m: 'sub', id: "1647515701", ch: "depth:BTC/USDT", code: 0 } // // { m: "connected", type: "unauth" } // // { m: "auth", id: "1647605234", code: 0 } // // order or balance sub // { // "m": "sub", // "id": "1647605952", // "ch": "order:cshF5SlR9ukAXoDOuXbND4dVpBMw9gzH", or futures-order // "code": 0 // } // // ohlcv // { // "m": "bar", // "s": "BTC/USDT", // "data": { // "i": "1", // "ts": 1647510060000, // "o": "40813.93", // "c": "40804.57", // "h": "40814.21", // "l": "40804.56", // "v": "0.01537" // } // } // // trades // // { // "m": "trades", // "symbol": "BTC/USDT", // "data": [ // { // "p": "40762.26", // "q": "0.01500", // "ts": 1647514306759, // "bm": true, // "seqnum": 72057633465795180 // } // ] // } // // orderbook deltas // // { // "m":"depth", // "symbol":"BTC/USDT", // "data":{ // "ts":1647527417715, // "seqnum":28590257013, // "asks":[ // ["40990.47","0.01619"], // ["41021.21","0"], // ["41031.59","0.06096"] // ], // "bids":[ // ["40990.46","0.76114"], // ["40985.18","0"] // ] // } // } // // orderbook snapshot // { // "m": "depth-snapshot", // "symbol": "BTC/USDT", // "data": { // "ts": 1647525938513, // "seqnum": 28590504772, // "asks": [ // [Array], [Array], [Array], [Array], [Array], [Array], [Array], // [Array], [Array], [Array], [Array], [Array], [Array], [Array], // [Array], [Array], [Array], [Array], [Array], [Array], [Array], // (...) // ] // } // // spot order update // { // "m": "order", // "accountId": "cshQtyfq8XLAA9kcf19h8bXHbAwwoqDo", // "ac": "CASH", // "data": { // "s": "BTC/USDT", // "sn": 8159711, // "sd": "Buy", // "ap": "0", // "bab": "2006.5974027", // "btb": "2006.5974027", // "cf": "0", // "cfq": "0", // (...) // } // } // future order update // { // "m": "futures-order", // "sn": 19404258063, // "e": "ExecutionReport", // "a": "futF5SlR9ukAXoDOuXbND4dVpBMw9gzH", // "ac": "FUTURES", // "t": 1647681792543, // "ct": 1647622515413, // "orderId": "r17f9df469b1U7223046196Okf5KbmdL", // (...) // "ptpt": "None" // } // // balance update cash // { // "m": "balance", // "accountId": "cshQtyfq8XLAA9kcf19h8bXHbAwwoqDo", // "ac": "CASH", // "data": { // "a" : "USDT", // "sn": 8159798, // "tb": "600", // "ab": "600" // } // } // // balance update margin // { // "m": "balance", // "accountId": "marOxpKJV83dxTRx0Eyxpa0gxc4Txt0P", // "ac": "MARGIN", // "data": { // "a" : "USDT", // "sn" : 8159802, // "tb" : "400", // "ab" : "400", // "brw": "0", // "int": "0" // } // } // const subject = this.safeString(message, 'm'); const methods = { 'ping': this.handlePing, 'auth': this.handleAuthenticate, 'sub': this.handleSubscriptionStatus, 'depth': this.handleOrderBook, 'depth-snapshot': this.handleOrderBookSnapshot, 'trades': this.handleTrades, 'bar': this.handleOHLCV, 'balance': this.handleBalance, 'futures-account-update': this.handleBalance, }; const method = this.safeValue(methods, subject); if (method !== undefined) { method.call(this, client, message); } if ((subject === 'order') || (subject === 'futures-order')) { // this.handleOrder (client, message); // balance updates may be in the order structure // they may also be standalone balance updates related to account transfers this.handleOrder(client, message); if (subject === 'order') { this.handleBalance(client, message); } } } handleSubscriptionStatus(client, message) { // // { m: "sub", ch: "bar:BTC/USDT", code: 0 } // // { m: 'sub', id: "1647515701", ch: "depth:BTC/USDT", code: 0 } // const channel = this.safeString(message, 'ch', ''); if (channel.indexOf('depth') > -1 && !(channel.indexOf('depth-snapshot') > -1)) { this.handleOrderBookSubscription(client, message); } return message; } handleOrderBookSubscription(client, message) { const channel = this.safeString(message, 'ch'); const parts = channel.split(':'); const marketId = parts[1]; const market = this.safeMarket(marketId); const symbol = market['symbol']; if (symbol in this.orderbooks) { delete this.orderbooks[symbol]; } this.orderbooks[symbol] = this.orderBook({}); if (this.options['defaultType'] === 'swap' || market['contract']) { this.spawn(this.fetchOrderBookSnapshot, symbol); } else { this.spawn(this.watchOrderBookSnapshot, symbol); } } async pong(client, message) { // // { m: "ping", hp: 3 } // try { await client.send({ 'op': 'pong', 'hp': this.safeInteger(message, 'hp') }); } catch (e) { const error = new NetworkError(this.id + ' handlePing failed with error ' + this.json(e)); client.reset(error); } } handlePing(client, message) { this.spawn(this.pong, client, message); } async authenticate(url, params = {}) { this.checkRequiredCredentials(); const messageHash = 'authenticated'; const client = this.client(url); let future = this.safeValue(client.subscriptions, messageHash); if (future === undefined) { const timestamp = this.milliseconds().toString(); const urlParts = url.split('/'); const partsLength = urlParts.length; const path = this.safeString(urlParts, partsLength - 1); const version = this.safeString(urlParts, partsLength - 2); const auth = timestamp + '+' + version + '/' + path; const secret = this.base64ToBinary(this.secret); const signature = this.hmac(this.encode(auth), secret, sha256, 'base64'); const request = { 'op': 'auth', 'id': this.nonce().toString(), 't': timestamp, 'key': this.apiKey, 'sig': signature, }; future = await this.watch(url, messageHash, this.extend(request, params), messageHash); client.subscriptions[messageHash] = future; } return future; } }