UNPKG

ccxt

Version:

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

1,166 lines (1,163 loc) • 46.5 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 bitfinexRest from '../bitfinex.js'; import { Precise } from '../base/Precise.js'; import { ExchangeError, AuthenticationError, ChecksumError } from '../base/errors.js'; import { ArrayCache, ArrayCacheBySymbolById, ArrayCacheByTimestamp } from '../base/ws/Cache.js'; import { sha384 } from '../static_dependencies/noble-hashes/sha512.js'; // --------------------------------------------------------------------------- export default class bitfinex extends bitfinexRest { describe() { return this.deepExtend(super.describe(), { 'has': { 'ws': true, 'watchTicker': true, 'watchTickers': false, 'watchOrderBook': true, 'watchTrades': true, 'watchTradesForSymbols': false, 'watchMyTrades': true, 'watchBalance': true, 'watchOHLCV': true, 'watchOrders': true, }, 'urls': { 'api': { 'ws': { 'public': 'wss://api-pub.bitfinex.com/ws/2', 'private': 'wss://api.bitfinex.com/ws/2', }, }, }, 'options': { 'watchOrderBook': { 'prec': 'P0', 'freq': 'F0', 'checksum': true, }, 'ordersLimit': 1000, }, }); } async subscribe(channel, symbol, params = {}) { await this.loadMarkets(); const market = this.market(symbol); const marketId = market['id']; const url = this.urls['api']['ws']['public']; const client = this.client(url); const messageHash = channel + ':' + marketId; const request = { 'event': 'subscribe', 'channel': channel, 'symbol': marketId, }; const result = await this.watch(url, messageHash, this.deepExtend(request, params), messageHash, { 'checksum': false }); const checksum = this.safeBool(this.options, 'checksum', true); if (checksum && !client.subscriptions[messageHash]['checksum'] && (channel === 'book')) { client.subscriptions[messageHash]['checksum'] = true; await client.send({ 'event': 'conf', 'flags': 131072, }); } return result; } async subscribePrivate(messageHash) { await this.loadMarkets(); await this.authenticate(); const url = this.urls['api']['ws']['private']; return await this.watch(url, messageHash, undefined, 1); } /** * @method * @name bitfinex#watchOHLCV * @description watches historical candlestick data containing the open, high, low, and close price, and the volume of a market * @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 interval = this.safeString(this.timeframes, timeframe, timeframe); const channel = 'candles'; const key = 'trade:' + interval + ':' + market['id']; const messageHash = channel + ':' + interval + ':' + market['id']; const request = { 'event': 'subscribe', 'channel': channel, 'key': key, }; const url = this.urls['api']['ws']['public']; // not using subscribe here because this message has a different format const ohlcv = await this.watch(url, messageHash, this.deepExtend(request, params), messageHash); if (this.newUpdates) { limit = ohlcv.getLimit(symbol, limit); } return this.filterBySinceLimit(ohlcv, since, limit, 0, true); } handleOHLCV(client, message, subscription) { // // initial snapshot // [ // 341527, // channel id // [ // [ // 1654705860000, // timestamp // 1802.6, // open // 1800.3, // close // 1802.8, // high // 1800.3, // low // 86.49588236 // volume // ], // [ // 1654705800000, // 1803.6, // 1802.6, // 1804.9, // 1802.3, // 74.6348086 // ], // [ // 1654705740000, // 1802.5, // 1803.2, // 1804.4, // 1802.4, // 23.61801085 // ] // ] // ] // // update // [ // 211171, // [ // 1654705680000, // 1801, // 1802.4, // 1802.9, // 1800.4, // 23.91911091 // ] // ] // const data = this.safeValue(message, 1, []); let ohlcvs = undefined; const first = this.safeValue(data, 0); if (Array.isArray(first)) { // snapshot ohlcvs = data; } else { // update ohlcvs = [data]; } const channel = this.safeValue(subscription, 'channel'); const key = this.safeString(subscription, 'key'); const keyParts = key.split(':'); const interval = this.safeString(keyParts, 1); let marketId = key; marketId = marketId.replace('trade:', ''); marketId = marketId.replace(interval + ':', ''); const market = this.safeMarket(marketId); const timeframe = this.findTimeframe(interval); const symbol = market['symbol']; const messageHash = channel + ':' + interval + ':' + marketId; 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; } const ohlcvsLength = ohlcvs.length; for (let i = 0; i < ohlcvsLength; i++) { const ohlcv = ohlcvs[ohlcvsLength - i - 1]; const parsed = this.parseOHLCV(ohlcv, market); stored.append(parsed); } client.resolve(stored, messageHash); } /** * @method * @name bitfinex#watchTrades * @description get the list of most recent trades for a particular symbol * @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 = {}) { const trades = await this.subscribe('trades', symbol, params); if (this.newUpdates) { limit = trades.getLimit(symbol, limit); } return this.filterBySinceLimit(trades, since, limit, 'timestamp', true); } /** * @method * @name bitfinex#watchMyTrades * @description watches information on multiple trades made by the user * @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 * @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(); let messageHash = 'myTrade'; if (symbol !== undefined) { const market = this.market(symbol); messageHash += ':' + market['id']; } const trades = await this.subscribePrivate(messageHash); if (this.newUpdates) { limit = trades.getLimit(symbol, limit); } return this.filterBySymbolSinceLimit(trades, symbol, since, limit, true); } /** * @method * @name bitfinex#watchTicker * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market * @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 = {}) { return await this.subscribe('ticker', symbol, params); } handleMyTrade(client, message, subscription = {}) { // // trade execution // [ // 0, // "te", // or tu // [ // 1133411090, // "tLTCUST", // 1655110144598, // 97084883506, // 0.1, // 42.821, // "EXCHANGE MARKET", // 42.799, // -1, // null, // null, // 1655110144596 // ] // ] // const name = 'myTrade'; const data = this.safeValue(message, 2); const trade = this.parseWsTrade(data); const symbol = trade['symbol']; const market = this.market(symbol); const messageHash = name + ':' + market['id']; if (this.myTrades === undefined) { const limit = this.safeInteger(this.options, 'tradesLimit', 1000); this.myTrades = new ArrayCacheBySymbolById(limit); } const tradesArray = this.myTrades; tradesArray.append(trade); this.myTrades = tradesArray; // generic subscription client.resolve(tradesArray, name); // specific subscription client.resolve(tradesArray, messageHash); } handleTrades(client, message, subscription) { // // initial snapshot // // [ // 188687, // channel id // [ // [ 1128060675, 1654701572690, 0.00217533, 1815.3 ], // id, mts, amount, price // [ 1128060665, 1654701551231, -0.00280472, 1814.1 ], // [ 1128060664, 1654701550996, -0.00364444, 1814.1 ], // [ 1128060656, 1654701527730, -0.00265203, 1814.2 ], // [ 1128060647, 1654701505193, 0.00262395, 1815.2 ], // [ 1128060642, 1654701484656, -0.13411443, 1816 ], // [ 1128060641, 1654701484656, -0.00088557, 1816 ], // [ 1128060639, 1654701478326, -0.002, 1816 ], // ] // ] // update // // [ // 360141, // "te", // [ // 1128060969, // id // 1654702500098, // mts // 0.00325131, // amount positive buy, negative sell // 1818.5, // price // ], // ] // // const channel = this.safeValue(subscription, 'channel'); const marketId = this.safeString(subscription, 'symbol'); const market = this.safeMarket(marketId); const messageHash = channel + ':' + marketId; const tradesLimit = this.safeInteger(this.options, 'tradesLimit', 1000); const symbol = market['symbol']; let stored = this.safeValue(this.trades, symbol); if (stored === undefined) { stored = new ArrayCache(tradesLimit); this.trades[symbol] = stored; } const messageLength = message.length; if (messageLength === 2) { // initial snapshot const trades = this.safeList(message, 1, []); // needs to be reversed to make chronological order const length = trades.length; for (let i = 0; i < length; i++) { const index = length - i - 1; const parsed = this.parseWsTrade(trades[index], market); stored.append(parsed); } } else { // update const type = this.safeString(message, 1); if (type === 'tu') { // don't resolve for a duplicate update // since te and tu updates are duplicated on the public stream return; } const trade = this.safeValue(message, 2, []); const parsed = this.parseWsTrade(trade, market); stored.append(parsed); } client.resolve(stored, messageHash); } parseWsTrade(trade, market = undefined) { // // [ // 1128060969, // id // 1654702500098, // mts // 0.00325131, // amount positive buy, negative sell // 1818.5, // price // ] // // trade execution // // [ // 1133411090, // id // "tLTCUST", // symbol // 1655110144598, // create ms // 97084883506, // order id // 0.1, // amount // 42.821, // price // "EXCHANGE MARKET", // order type // 42.799, // order price // -1, // maker // null, // fee // null, // fee currency // 1655110144596 // cid // ] // // trade update // // [ // 1133411090, // "tLTCUST", // 1655110144598, // 97084883506, // 0.1, // 42.821, // "EXCHANGE MARKET", // 42.799, // -1, // -0.0002, // "LTC", // 1655110144596 // ] // const numFields = trade.length; const isPublic = numFields <= 8; let marketId = (!isPublic) ? this.safeString(trade, 1) : undefined; market = this.safeMarket(marketId, market); const createdKey = isPublic ? 1 : 2; const priceKey = isPublic ? 3 : 5; const amountKey = isPublic ? 2 : 4; marketId = market['id']; let type = this.safeString(trade, 6); if (type !== undefined) { if (type.indexOf('LIMIT') > -1) { type = 'limit'; } else if (type.indexOf('MARKET') > -1) { type = 'market'; } } const orderId = (!isPublic) ? this.safeString(trade, 3) : undefined; const id = this.safeString(trade, 0); const timestamp = this.safeInteger(trade, createdKey); const price = this.safeString(trade, priceKey); const amountString = this.safeString(trade, amountKey); const amount = this.parseNumber(Precise.stringAbs(amountString)); let side = undefined; if (amount !== undefined) { side = Precise.stringGt(amountString, '0') ? 'buy' : 'sell'; } const symbol = this.safeSymbol(marketId, market); const feeValue = this.safeString(trade, 9); let fee = undefined; if (feeValue !== undefined) { const currencyId = this.safeString(trade, 10); const code = this.safeCurrencyCode(currencyId); fee = { 'cost': feeValue, 'currency': code, }; } const maker = this.safeInteger(trade, 8); let takerOrMaker = undefined; if (maker !== undefined) { takerOrMaker = (maker === -1) ? 'taker' : 'maker'; } return this.safeTrade({ 'info': trade, 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'symbol': symbol, 'id': id, 'order': orderId, 'type': type, 'takerOrMaker': takerOrMaker, 'side': side, 'price': price, 'amount': amount, 'cost': undefined, 'fee': fee, }, market); } handleTicker(client, message, subscription) { // // [ // 340432, // channel ID // [ // 236.62, // 1 BID float Price of last highest bid // 9.0029, // 2 BID_SIZE float Size of the last highest bid // 236.88, // 3 ASK float Price of last lowest ask // 7.1138, // 4 ASK_SIZE float Size of the last lowest ask // -1.02, // 5 DAILY_CHANGE float Amount that the last price has changed since yesterday // 0, // 6 DAILY_CHANGE_PERC float Amount that the price has changed expressed in percentage terms // 236.52, // 7 LAST_PRICE float Price of the last trade. // 5191.36754297, // 8 VOLUME float Daily volume // 250.01, // 9 HIGH float Daily high // 220.05, // 10 LOW float Daily low // ] // ] // const ticker = this.safeValue(message, 1); const marketId = this.safeString(subscription, 'symbol'); const market = this.safeMarket(marketId); const symbol = this.safeSymbol(marketId); const parsed = this.parseWsTicker(ticker, market); const channel = 'ticker'; const messageHash = channel + ':' + marketId; this.tickers[symbol] = parsed; client.resolve(parsed, messageHash); } parseWsTicker(ticker, market = undefined) { // // [ // 236.62, // 1 BID float Price of last highest bid // 9.0029, // 2 BID_SIZE float Size of the last highest bid // 236.88, // 3 ASK float Price of last lowest ask // 7.1138, // 4 ASK_SIZE float Size of the last lowest ask // -1.02, // 5 DAILY_CHANGE float Amount that the last price has changed since yesterday // 0, // 6 DAILY_CHANGE_PERC float Amount that the price has changed expressed in percentage terms // 236.52, // 7 LAST_PRICE float Price of the last trade. // 5191.36754297, // 8 VOLUME float Daily volume // 250.01, // 9 HIGH float Daily high // 220.05, // 10 LOW float Daily low // ] // market = this.safeMarket(undefined, market); const symbol = market['symbol']; const last = this.safeString(ticker, 6); const change = this.safeString(ticker, 4); return this.safeTicker({ 'symbol': symbol, 'timestamp': undefined, 'datetime': undefined, 'high': this.safeString(ticker, 8), 'low': this.safeString(ticker, 9), 'bid': this.safeString(ticker, 0), 'bidVolume': this.safeString(ticker, 1), 'ask': this.safeString(ticker, 2), 'askVolume': this.safeString(ticker, 3), 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': change, 'percentage': this.safeString(ticker, 5), 'average': undefined, 'baseVolume': this.safeString(ticker, 7), 'quoteVolume': undefined, 'info': ticker, }, market); } /** * @method * @name bitfinex#watchOrderBook * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data * @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 = {}) { if (limit !== undefined) { if ((limit !== 25) && (limit !== 100)) { throw new ExchangeError(this.id + ' watchOrderBook limit argument must be undefined, 25 or 100'); } } const options = this.safeValue(this.options, 'watchOrderBook', {}); const prec = this.safeString(options, 'prec', 'P0'); const freq = this.safeString(options, 'freq', 'F0'); const request = { 'prec': prec, 'freq': freq, // string, frequency of updates 'F0' = realtime, 'F1' = 2 seconds, default is 'F0' }; if (limit !== undefined) { request['len'] = limit; // string, number of price points, '25', '100', default = '25' } const orderbook = await this.subscribe('book', symbol, this.deepExtend(request, params)); return orderbook.limit(); } handleOrderBook(client, message, subscription) { // // first message (snapshot) // // [ // 18691, // channel id // [ // [ 7364.8, 10, 4.354802 ], // price, count, size > 0 = bid // [ 7364.7, 1, 0.00288831 ], // [ 7364.3, 12, 0.048 ], // [ 7364.9, 3, -0.42028976 ], // price, count, size < 0 = ask // [ 7365, 1, -0.25 ], // [ 7365.5, 1, -0.00371937 ], // ] // ] // // subsequent updates // // [ // 358169, // channel id // [ // 1807.1, // price // 0, // cound // 1 // size // ] // ] // const marketId = this.safeString(subscription, 'symbol'); const symbol = this.safeSymbol(marketId); const channel = 'book'; const messageHash = channel + ':' + marketId; const prec = this.safeString(subscription, 'prec', 'P0'); const isRaw = (prec === 'R0'); // if it is an initial snapshot if (!(symbol in this.orderbooks)) { const limit = this.safeInteger(subscription, 'len'); if (isRaw) { // raw order books this.orderbooks[symbol] = this.indexedOrderBook({}, limit); } else { // P0, P1, P2, P3, P4 this.orderbooks[symbol] = this.countedOrderBook({}, limit); } const orderbook = this.orderbooks[symbol]; if (isRaw) { const deltas = message[1]; for (let i = 0; i < deltas.length; i++) { const delta = deltas[i]; const delta2 = delta[2]; const size = (delta2 < 0) ? -delta2 : delta2; const side = (delta2 < 0) ? 'asks' : 'bids'; const bookside = orderbook[side]; const idString = this.safeString(delta, 0); const price = this.safeFloat(delta, 1); bookside.storeArray([price, size, idString]); } } else { const deltas = message[1]; for (let i = 0; i < deltas.length; i++) { const delta = deltas[i]; const amount = this.safeNumber(delta, 2); const counter = this.safeNumber(delta, 1); const price = this.safeNumber(delta, 0); const size = (amount < 0) ? -amount : amount; const side = (amount < 0) ? 'asks' : 'bids'; const bookside = orderbook[side]; bookside.storeArray([price, size, counter]); } } orderbook['symbol'] = symbol; client.resolve(orderbook, messageHash); } else { const orderbook = this.orderbooks[symbol]; const deltas = message[1]; const orderbookItem = this.orderbooks[symbol]; if (isRaw) { const price = this.safeString(deltas, 1); const deltas2 = deltas[2]; const size = (deltas2 < 0) ? -deltas2 : deltas2; const side = (deltas2 < 0) ? 'asks' : 'bids'; const bookside = orderbookItem[side]; // price = 0 means that you have to remove the order from your book const amount = Precise.stringGt(price, '0') ? size : '0'; const idString = this.safeString(deltas, 0); bookside.storeArray([this.parseNumber(price), this.parseNumber(amount), idString]); } else { const amount = this.safeString(deltas, 2); const counter = this.safeString(deltas, 1); const price = this.safeString(deltas, 0); const size = Precise.stringLt(amount, '0') ? Precise.stringNeg(amount) : amount; const side = Precise.stringLt(amount, '0') ? 'asks' : 'bids'; const bookside = orderbookItem[side]; bookside.storeArray([this.parseNumber(price), this.parseNumber(size), this.parseNumber(counter)]); } client.resolve(orderbook, messageHash); } } handleChecksum(client, message, subscription) { // // [ 173904, "cs", -890884919 ] // const marketId = this.safeString(subscription, 'symbol'); const symbol = this.safeSymbol(marketId); const channel = 'book'; const messageHash = channel + ':' + marketId; const book = this.safeValue(this.orderbooks, symbol); if (book === undefined) { return; } const depth = 25; // covers the first 25 bids and asks const stringArray = []; const bids = book['bids']; const asks = book['asks']; const prec = this.safeString(subscription, 'prec', 'P0'); const isRaw = (prec === 'R0'); const idToCheck = isRaw ? 2 : 0; // pepperoni pizza from bitfinex for (let i = 0; i < depth; i++) { const bid = this.safeValue(bids, i); const ask = this.safeValue(asks, i); if (bid !== undefined) { stringArray.push(this.numberToString(bids[i][idToCheck])); stringArray.push(this.numberToString(bids[i][1])); } if (ask !== undefined) { stringArray.push(this.numberToString(asks[i][idToCheck])); const aski1 = asks[i][1]; stringArray.push(this.numberToString(-aski1)); } } const payload = stringArray.join(':'); const localChecksum = this.crc32(payload, true); const responseChecksum = this.safeInteger(message, 2); if (responseChecksum !== localChecksum) { delete client.subscriptions[messageHash]; delete this.orderbooks[symbol]; const checksum = this.handleOption('watchOrderBook', 'checksum', true); if (checksum) { const error = new ChecksumError(this.id + ' ' + this.orderbookChecksumMessage(symbol)); client.reject(error, messageHash); } } } /** * @method * @name bitfinex#watchBalance * @description watch balance and get the amount of funds available for trading or funds locked in orders * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {str} [params.type] spot or contract if not provided this.options['defaultType'] is used * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure} */ async watchBalance(params = {}) { await this.loadMarkets(); const balanceType = this.safeString(params, 'wallet', 'exchange'); // exchange, margin params = this.omit(params, 'wallet'); const messageHash = 'balance:' + balanceType; return await this.subscribePrivate(messageHash); } handleBalance(client, message, subscription) { // // snapshot (exchange + margin together) // [ // 0, // "ws", // [ // [ // "exchange", // "LTC", // 0.05479727, // 0, // null, // "Trading fees for 0.05 LTC (LTCUST) @ 51.872 on BFX (0.2%)", // null, // ] // [ // "margin", // "USTF0", // 11.960650700086292, // 0, // null, // "Trading fees for 0.1 LTCF0 (LTCF0:USTF0) @ 51.844 on BFX (0.065%)", // null, // ], // ], // ] // // spot // [ // 0, // "wu", // [ // "exchange", // "LTC", // currency // 0.06729727, // wallet balance // 0, // unsettled balance // 0.06729727, // available balance might be null // "Exchange 0.4 LTC for UST @ 65.075", // { // "reason": "TRADE", // "order_id": 96596397973, // "order_id_oppo": 96596632735, // "trade_price": "65.075", // "trade_amount": "-0.4", // "order_cid": 1654636218766, // "order_gid": null // } // ] // ] // // margin // // [ // "margin", // "USTF0", // 11.960650700086292, // total // 0, // 6.776250700086292, // available // "Trading fees for 0.1 LTCF0 (LTCF0:USTF0) @ 51.844 on BFX (0.065%)", // null // ] // const updateType = this.safeValue(message, 1); let data = undefined; if (updateType === 'ws') { data = this.safeValue(message, 2); } else { data = [this.safeValue(message, 2)]; } const updatedTypes = {}; for (let i = 0; i < data.length; i++) { const rawBalance = data[i]; const currencyId = this.safeString(rawBalance, 1); const code = this.safeCurrencyCode(currencyId); const balance = this.parseWsBalance(rawBalance); const balanceType = this.safeString(rawBalance, 0); const oldBalance = this.safeValue(this.balance, balanceType, {}); oldBalance[code] = balance; oldBalance['info'] = message; this.balance[balanceType] = this.safeBalance(oldBalance); updatedTypes[balanceType] = true; } const updatesKeys = Object.keys(updatedTypes); for (let i = 0; i < updatesKeys.length; i++) { const type = updatesKeys[i]; const messageHash = 'balance:' + type; client.resolve(this.balance[type], messageHash); } } parseWsBalance(balance) { // // [ // "exchange", // "LTC", // 0.05479727, // balance // 0, // null, // available null if not calculated yet // "Trading fees for 0.05 LTC (LTCUST) @ 51.872 on BFX (0.2%)", // null, // ] // const totalBalance = this.safeString(balance, 2); const availableBalance = this.safeString(balance, 4); const account = this.account(); if (availableBalance !== undefined) { account['free'] = availableBalance; } account['total'] = totalBalance; return account; } handleSystemStatus(client, message) { // // { // "event": "info", // "version": 2, // "serverId": "e293377e-7bb7-427e-b28c-5db045b2c1d1", // "platform": { status: 1 }, // 1 for operative, 0 for maintenance // } // return message; } handleSubscriptionStatus(client, message) { // // { // "event": "subscribed", // "channel": "book", // "chanId": 67473, // "symbol": "tBTCUSD", // "prec": "P0", // "freq": "F0", // "len": "25", // "pair": "BTCUSD" // } // const channelId = this.safeString(message, 'chanId'); client.subscriptions[channelId] = message; return message; } async authenticate(params = {}) { const url = this.urls['api']['ws']['private']; const client = this.client(url); const messageHash = 'authenticated'; const future = client.future(messageHash); const authenticated = this.safeValue(client.subscriptions, messageHash); if (authenticated === undefined) { const nonce = this.milliseconds(); const payload = 'AUTH' + nonce.toString(); const signature = this.hmac(this.encode(payload), this.encode(this.secret), sha384, 'hex'); const event = 'auth'; const request = { 'apiKey': this.apiKey, 'authSig': signature, 'authNonce': nonce, 'authPayload': payload, 'event': event, }; const message = this.extend(request, params); this.watch(url, messageHash, message, messageHash); } return await future; } handleAuthenticationMessage(client, message) { const messageHash = 'authenticated'; const status = this.safeString(message, 'status'); if (status === 'OK') { // we resolve the future here permanently so authentication only happens once const future = this.safeValue(client.futures, messageHash); future.resolve(true); } else { const error = new AuthenticationError(this.json(message)); client.reject(error, messageHash); // allows further authentication attempts if (messageHash in client.subscriptions) { delete client.subscriptions[messageHash]; } } } /** * @method * @name bitfinex#watchOrders * @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 messageHash = 'orders'; if (symbol !== undefined) { const market = this.market(symbol); messageHash += ':' + market['id']; } const orders = await this.subscribePrivate(messageHash); if (this.newUpdates) { limit = orders.getLimit(symbol, limit); } return this.filterBySymbolSinceLimit(orders, symbol, since, limit, true); } handleOrders(client, message, subscription) { // // limit order // [ // 0, // "on", // ou or oc // [ // 96923856256, // order id // null, // gid // 1655029337026, // cid // "tLTCUST", // symbol // 1655029337027, // created timestamp // 1655029337029, // updated timestamp // 0.1, // amount // 0.1, // amount_orig // "EXCHANGE LIMIT", // order type // null, // type_prev // null, // mts_tif // null, // placeholder // 0, // flags // "ACTIVE", // status // null, // null, // 30, // price // 0, // price average // 0, // price_trailling // 0, // price_aux_limit // null, // null, // null, // 0, // notify // 0, // null, // null, // null, // "BFX", // null, // null, // ] // ] // const data = this.safeValue(message, 2, []); const messageType = this.safeString(message, 1); if (this.orders === undefined) { const limit = this.safeInteger(this.options, 'ordersLimit', 1000); this.orders = new ArrayCacheBySymbolById(limit); } const orders = this.orders; const symbolIds = {}; if (messageType === 'os') { const snapshotLength = data.length; if (snapshotLength === 0) { return; } for (let i = 0; i < data.length; i++) { const value = data[i]; const parsed = this.parseWsOrder(value); const symbol = parsed['symbol']; symbolIds[symbol] = true; orders.append(parsed); } } else { const parsed = this.parseWsOrder(data); orders.append(parsed); const symbol = parsed['symbol']; symbolIds[symbol] = true; } const name = 'orders'; client.resolve(this.orders, name); const keys = Object.keys(symbolIds); for (let i = 0; i < keys.length; i++) { const symbol = keys[i]; const market = this.market(symbol); const messageHash = name + ':' + market['id']; client.resolve(this.orders, messageHash); } } parseWsOrderStatus(status) { const statuses = { 'ACTIVE': 'open', 'CANCELED': 'canceled', 'EXECUTED': 'closed', 'PARTIALLY': 'open', }; return this.safeString(statuses, status, status); } parseWsOrder(order, market = undefined) { // // [ // 97084883506, // order id // null, // 1655110144596, // clientOrderId // "tLTCUST", // symbol // 1655110144596, // created timestamp // 1655110144598, // updated timestamp // 0, // amount // 0.1, // amount_orig negative if sell order // "EXCHANGE MARKET", // type // null, // null, // null, // 0, // "EXECUTED @ 42.821(0.1)", // status // null, // null, // 42.799, // price // 42.821, // price average // 0, // price trailling // 0, // price_aux_limit // null, // null, // null, // 0, // 0, // null, // null, // null, // "BFX", // null, // null, // {} // ] // const id = this.safeString(order, 0); const clientOrderId = this.safeString(order, 1); const marketId = this.safeString(order, 3); const symbol = this.safeSymbol(marketId); market = this.safeMarket(symbol); let amount = this.safeString(order, 7); let side = 'buy'; if (Precise.stringLt(amount, '0')) { amount = Precise.stringAbs(amount); side = 'sell'; } const remaining = Precise.stringAbs(this.safeString(order, 6)); let type = this.safeString(order, 8); if (type.indexOf('LIMIT') > -1) { type = 'limit'; } else if (type.indexOf('MARKET') > -1) { type = 'market'; } const rawState = this.safeString(order, 13); const stateParts = rawState.split(' '); const trimmedStatus = this.safeString(stateParts, 0); const status = this.parseWsOrderStatus(trimmedStatus); const price = this.safeString(order, 16); const timestamp = this.safeInteger2(order, 5, 4); const average = this.safeString(order, 17); const stopPrice = this.omitZero(this.safeString(order, 18)); return this.safeOrder({ 'info': order, 'id': id, 'clientOrderId': clientOrderId, 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'lastTradeTimestamp': undefined, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'stopPrice': stopPrice, 'triggerPrice': stopPrice, 'average': average, 'amount': amount, 'remaining': remaining, 'filled': undefined, 'status': status, 'fee': undefined, 'cost': undefined, 'trades': undefined, }, market); } handleMessage(client, message) { const channelId = this.safeString(message, 0); // // [ // 1231, // "hb", // ] // // auth message // { // "event": "auth", // "status": "OK", // "chanId": 0, // "userId": 3159883, // "auth_id": "ac7108e7-2f26-424d-9982-c24700dc02ca", // "caps": { // "orders": { read: 1, write: 1 }, // "account": { read: 1, write: 1 }, // "funding": { read: 1, write: 1 }, // "history": { read: 1, write: 0 }, // "wallets": { read: 1, write: 1 }, // "withdraw": { read: 0, write: 1 }, // "positions": { read: 1, write: 1 }, // "ui_withdraw": { read: 0, write: 0 } // } // } // if (Array.isArray(message)) { if (message[1] === 'hb') { return; // skip heartbeats within subscription channels for now } const subscription = this.safeValue(client.subscriptions, channelId, {}); const channel = this.safeString(subscription, 'channel'); const name = this.safeString(message, 1); const publicMethods = { 'book': this.handleOrderBook, 'cs': this.handleChecksum, 'candles': this.handleOHLCV, 'ticker': this.handleTicker, 'trades': this.handleTrades, }; const privateMethods = { 'os': this.handleOrders, 'ou': this.handleOrders, 'on': this.handleOrders, 'oc': this.handleOrders, 'wu': this.handleBalance, 'ws': this.handleBalance, 'tu': this.handleMyTrade, }; let method = undefined; if (channelId === '0') { method = this.safeValue(privateMethods, name); } else { method = this.safeValue2(publicMethods, name, channel); } if (method !== undefined) { method.call(this, client, message, subscription); } } else { const event = this.safeString(message, 'event'); if (event !== undefined) { const methods = { 'info': this.handleSystemStatus, 'subscribed': this.handleSubscriptionStatus, 'auth': this.handleAuthenticationMessage, }; const method = this.safeValue(methods, event); if (method !== undefined) { method.call(this, client, message); } } } } }