UNPKG

@ango-ya/ccxt

Version:

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

1,150 lines (1,147 loc) 83.9 kB
'use strict'; var coinmetro$1 = require('./abstract/coinmetro.js'); var errors = require('./base/errors.js'); var number = require('./base/functions/number.js'); var Precise = require('./base/Precise.js'); // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- /** * @class coinmetro * @augments Exchange */ class coinmetro extends coinmetro$1 { describe() { return this.deepExtend(super.describe(), { 'id': 'coinmetro', 'name': 'Coinmetro', 'countries': ['EE'], 'version': 'v1', 'rateLimit': 200, 'certified': false, 'pro': false, 'has': { 'CORS': undefined, 'spot': true, 'margin': true, 'swap': false, 'future': false, 'option': false, 'addMargin': false, 'borrowCrossMargin': true, 'borrowIsolatedMargin': false, 'cancelAllOrders': false, 'cancelOrder': true, 'cancelOrders': false, 'closeAllPositions': false, 'closePosition': true, 'createDepositAddress': false, 'createOrder': true, 'createPostOnlyOrder': false, 'createReduceOnlyOrder': false, 'createStopLimitOrder': true, 'createStopMarketOrder': true, 'createStopOrder': true, 'deposit': false, 'editOrder': false, 'fetchAccounts': false, 'fetchBalance': true, 'fetchBidsAsks': true, 'fetchBorrowInterest': false, 'fetchBorrowRateHistories': false, 'fetchBorrowRateHistory': false, 'fetchCanceledAndClosedOrders': true, 'fetchCanceledOrders': false, 'fetchClosedOrder': false, 'fetchClosedOrders': false, 'fetchCrossBorrowRate': false, 'fetchCrossBorrowRates': false, 'fetchCurrencies': true, 'fetchDeposit': false, 'fetchDepositAddress': false, 'fetchDepositAddresses': false, 'fetchDepositAddressesByNetwork': false, 'fetchDeposits': false, 'fetchDepositsWithdrawals': false, 'fetchDepositWithdrawFee': false, 'fetchDepositWithdrawFees': false, 'fetchFundingHistory': false, 'fetchFundingRate': false, 'fetchFundingRateHistory': false, 'fetchFundingRates': false, 'fetchIndexOHLCV': false, 'fetchIsolatedBorrowRate': false, 'fetchIsolatedBorrowRates': false, 'fetchL3OrderBook': false, 'fetchLedger': true, 'fetchLeverage': false, 'fetchLeverageTiers': false, 'fetchMarketLeverageTiers': false, 'fetchMarkets': true, 'fetchMarkOHLCV': false, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenInterestHistory': false, 'fetchOpenOrder': false, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrderBooks': false, 'fetchOrders': false, 'fetchOrderTrades': false, 'fetchPosition': false, 'fetchPositions': false, 'fetchPositionsRisk': false, 'fetchPremiumIndexOHLCV': false, 'fetchStatus': false, 'fetchTicker': false, 'fetchTickers': true, 'fetchTime': false, 'fetchTrades': true, 'fetchTradingFee': false, 'fetchTradingFees': false, 'fetchTradingLimits': false, 'fetchTransactionFee': false, 'fetchTransactionFees': false, 'fetchTransactions': false, 'fetchTransfers': false, 'fetchWithdrawal': false, 'fetchWithdrawals': false, 'fetchWithdrawalWhitelist': false, 'reduceMargin': false, 'repayCrossMargin': false, 'repayIsolatedMargin': false, 'setLeverage': false, 'setMargin': false, 'setMarginMode': false, 'setPositionMode': false, 'signIn': false, 'transfer': false, 'withdraw': false, 'ws': false, }, 'timeframes': { '1m': '60000', '5m': '300000', '30m': '1800000', '4h': '14400000', '1d': '86400000', }, 'urls': { 'logo': 'https://github.com/ccxt/ccxt/assets/43336371/e86f87ec-6ba3-4410-962b-f7988c5db539', 'api': { 'public': 'https://api.coinmetro.com', 'private': 'https://api.coinmetro.com', }, 'test': { 'public': 'https://api.coinmetro.com/open', 'private': 'https://api.coinmetro.com/open', }, 'www': 'https://coinmetro.com/', 'doc': [ 'https://documenter.getpostman.com/view/3653795/SVfWN6KS', ], 'fees': 'https://help.coinmetro.com/hc/en-gb/articles/6844007317789-What-are-the-fees-on-Coinmetro-', 'referral': 'https://go.coinmetro.com/?ref=crypto24', }, 'api': { 'public': { 'get': { 'demo/temp': 1, 'exchange/candles/{pair}/{timeframe}/{from}/{to}': 3, 'exchange/prices': 1, 'exchange/ticks/{pair}/{from}': 3, 'assets': 1, 'markets': 1, 'exchange/book/{pair}': 3, 'exchange/bookUpdates/{pair}/{from}': 1, // not unified }, }, 'private': { 'get': { 'users/balances': 1, 'users/wallets/history/{since}': 1.67, 'exchange/orders/status/{orderID}': 1, 'exchange/orders/active': 1, 'exchange/orders/history/{since}': 1.67, 'exchange/fills/{since}': 1.67, 'exchange/margin': 1, // not unified }, 'post': { 'jwt': 1, 'jwtDevice': 1, 'devices': 1, 'jwt-read-only': 1, 'exchange/orders/create': 1, 'exchange/orders/modify/{orderID}': 1, 'exchange/swap': 1, 'exchange/swap/confirm/{swapId}': 1, 'exchange/orders/close/{orderID}': 1, 'exchange/orders/hedge': 1, // not unified }, 'put': { 'jwt': 1, 'exchange/orders/cancel/{orderID}': 1, 'users/margin/collateral': 1, 'users/margin/primary/{currency}': 1, // not unified }, }, }, 'requiredCredentials': { 'apiKey': false, 'secret': false, 'uid': true, 'token': true, }, 'fees': { 'trading': { 'feeSide': 'get', 'tierBased': false, 'percentage': true, 'taker': this.parseNumber('0.001'), 'maker': this.parseNumber('0'), }, }, 'precisionMode': number.DECIMAL_PLACES, // exchange-specific options 'options': { 'currenciesByIdForParseMarket': undefined, 'currencyIdsListForParseMarket': undefined, }, 'exceptions': { // https://trade-docs.coinmetro.co/?javascript--nodejs#message-codes 'exact': { 'Both buyingCurrency and sellingCurrency are required': errors.InvalidOrder, 'One and only one of buyingQty and sellingQty is required': errors.InvalidOrder, 'Invalid buyingCurrency': errors.InvalidOrder, 'Invalid \'from\'': errors.BadRequest, 'Invalid sellingCurrency': errors.InvalidOrder, 'Invalid buyingQty': errors.InvalidOrder, 'Invalid sellingQty': errors.InvalidOrder, 'Insufficient balance': errors.InsufficientFunds, 'Expiration date is in the past or too near in the future': errors.InvalidOrder, 'Forbidden': errors.PermissionDenied, 'Order Not Found': errors.OrderNotFound, 'since must be a millisecond timestamp': errors.BadRequest, 'This pair is disabled on margin': errors.BadSymbol, // 422 Unprocessable Entity {"message":"This pair is disabled on margin"} }, 'broad': { 'accessing from a new IP': errors.PermissionDenied, 'available to allocate as collateral': errors.InsufficientFunds, 'At least': errors.BadRequest, 'collateral is not allowed': errors.BadRequest, 'Insufficient liquidity': errors.InvalidOrder, 'Insufficient order size': errors.InvalidOrder, 'Invalid quantity': errors.InvalidOrder, 'Invalid Stop Loss': errors.InvalidOrder, 'Invalid stop price!': errors.InvalidOrder, 'Not enough balance': errors.InsufficientFunds, 'Not enough margin': errors.InsufficientFunds, 'orderType missing': errors.BadRequest, 'Server Timeout': errors.ExchangeError, 'Time in force has to be IOC or FOK for market orders': errors.InvalidOrder, 'Too many attempts': errors.RateLimitExceeded, // 429 Too Many Requests {"message":"Too many attempts. Try again in 3 seconds"} }, }, }); } async fetchCurrencies(params = {}) { /** * @method * @name coinmetro#fetchCurrencies * @description fetches all available currencies on an exchange * @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#d5876d43-a3fe-4479-8c58-24d0f044edfb * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} an associative dictionary of currencies */ const response = await this.publicGetAssets(params); // // [ // { // "symbol": "BTC", // "name": "Bitcoin", // "color": "#FFA500", // "type": "coin", // "canDeposit": true, // "canWithdraw": true, // "canTrade": true, // "notabeneDecimals": 8, // "canMarket": true, // "maxSwap": 10000, // "digits": 6, // "multiplier": 1000000, // "bookDigits": 8, // "bookMultiplier": 100000000, // "sentimentData": { // "sentiment": 51.59555555555555, // "interest": 1.127511216044664 // }, // "minQty": 0.0001 // }, // { // "symbol": "EUR", // "name": "Euro", // "color": "#1246FF", // "type": "fiat", // "canDeposit": true, // "canWithdraw": true, // "canTrade": true, // "canMarket": true, // "maxSwap": 10000, // "digits": 2, // "multiplier": 100, // "bookDigits": 3, // "bookMultiplier": 1000, // "minQty": 5 // } // ... // ] // const result = {}; for (let i = 0; i < response.length; i++) { const currency = response[i]; const id = this.safeString(currency, 'symbol'); const code = this.safeCurrencyCode(id); const withdraw = this.safeValue(currency, 'canWithdraw'); const deposit = this.safeValue(currency, 'canDeposit'); const canTrade = this.safeValue(currency, 'canTrade'); const active = canTrade ? withdraw : true; const precision = this.safeInteger(currency, 'digits'); const minAmount = this.safeNumber(currency, 'minQty'); result[code] = this.safeCurrencyStructure({ 'id': id, 'code': code, 'name': code, 'info': currency, 'active': active, 'deposit': deposit, 'withdraw': withdraw, 'fee': undefined, 'precision': precision, 'limits': { 'amount': { 'min': minAmount, 'max': undefined }, 'withdraw': { 'min': undefined, 'max': undefined }, }, 'networks': {}, }); } if (this.safeValue(this.options, 'currenciesByIdForParseMarket') === undefined) { const currenciesById = this.indexBy(result, 'id'); this.options['currenciesByIdForParseMarket'] = currenciesById; this.options['currencyIdsListForParseMarket'] = Object.keys(currenciesById); } return result; } async fetchMarkets(params = {}) { /** * @method * @name coinmetro#fetchMarkets * @description retrieves data on all markets for coinmetro * @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#9fd18008-338e-4863-b07d-722878a46832 * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} an array of objects representing market data */ const response = await this.publicGetMarkets(params); if (this.safeValue(this.options, 'currenciesByIdForParseMarket') === undefined) { await this.fetchCurrencies(); } // // [ // { // "pair": "PERPEUR", // "precision": 5, // "margin": false // }, // { // "pair": "PERPUSD", // "precision": 5, // "margin": false // }, // { // "pair": "YFIEUR", // "precision": 5, // "margin": false // }, // ... // ] // return this.parseMarkets(response); } parseMarket(market) { const id = this.safeString(market, 'pair'); const parsedMarketId = this.parseMarketId(id); const baseId = this.safeString(parsedMarketId, 'baseId'); const quoteId = this.safeString(parsedMarketId, 'quoteId'); const base = this.safeCurrencyCode(baseId); const quote = this.safeCurrencyCode(quoteId); const basePrecisionAndLimits = this.parseMarketPrecisionAndLimits(baseId); const quotePrecisionAndLimits = this.parseMarketPrecisionAndLimits(quoteId); const margin = this.safeBool(market, 'margin', false); const tradingFees = this.safeValue(this.fees, 'trading', {}); return this.safeMarketStructure({ 'id': id, 'symbol': base + '/' + quote, 'base': base, 'quote': quote, 'settle': undefined, 'baseId': baseId, 'quoteId': quoteId, 'settleId': undefined, 'type': 'spot', 'spot': true, 'margin': margin, 'swap': false, 'future': false, 'option': false, 'active': true, 'contract': false, 'linear': undefined, 'inverse': undefined, 'taker': this.safeNumber(tradingFees, 'taker'), 'maker': this.safeNumber(tradingFees, 'maker'), 'contractSize': undefined, 'expiry': undefined, 'expiryDatetime': undefined, 'strike': undefined, 'optionType': undefined, 'precision': { 'amount': basePrecisionAndLimits['precision'], 'price': quotePrecisionAndLimits['precision'], 'base': basePrecisionAndLimits['precision'], 'quote': quotePrecisionAndLimits['precision'], }, 'limits': { 'leverage': { 'min': undefined, 'max': undefined, }, 'amount': { 'min': basePrecisionAndLimits['minLimit'], 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': quotePrecisionAndLimits['minLimit'], 'max': undefined, }, }, 'created': undefined, 'info': market, }); } parseMarketId(marketId) { let baseId = undefined; let quoteId = undefined; const currencyIds = this.safeValue(this.options, 'currencyIdsListForParseMarket', []); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const entryIndex = marketId.indexOf(currencyId); if (entryIndex !== -1) { const restId = marketId.replace(currencyId, ''); if (this.inArray(restId, currencyIds)) { if (entryIndex === 0) { baseId = currencyId; quoteId = restId; } else { baseId = restId; quoteId = currencyId; } break; } } } const result = { 'baseId': baseId, 'quoteId': quoteId, }; return result; } parseMarketPrecisionAndLimits(currencyId) { const currencies = this.safeValue(this.options, 'currenciesByIdForParseMarket', {}); const currency = this.safeValue(currencies, currencyId, {}); const precision = this.safeInteger(currency, 'precision'); const limits = this.safeValue(currency, 'limits', {}); const amountLimits = this.safeValue(limits, 'amount', {}); const minLimit = this.safeNumber(amountLimits, 'min'); const result = { 'precision': precision, 'minLimit': minLimit, }; return result; } async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { /** * @method * @name coinmetro#fetchOHLCV * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market * @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#13cfb5bc-7bfb-4847-85e1-e0f35dfb3573 * @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 * @param {int} [params.until] the latest time in ms to fetch entries for * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume */ await this.loadMarkets(); const market = this.market(symbol); const request = { 'pair': market['id'], 'timeframe': this.safeString(this.timeframes, timeframe, timeframe), }; let until = undefined; if (since !== undefined) { request['from'] = since; if (limit !== undefined) { const duration = this.parseTimeframe(timeframe) * 1000; until = this.sum(since, duration * (limit)); } } else { request['from'] = ':from'; // this endpoint doesn't accept empty from and to params (setting them into the value described in the documentation) } until = this.safeInteger2(params, 'till', 'until', until); if (until !== undefined) { params = this.omit(params, ['till', 'until']); request['to'] = until; } else { request['to'] = ':to'; } const response = await this.publicGetExchangeCandlesPairTimeframeFromTo(this.extend(request, params)); // // { // "candleHistory": [ // { // "pair": "ETHUSDT", // "timeframe": 86400000, // "timestamp": 1697673600000, // "c": 1567.4409353098604, // "h": 1566.7514068472303, // "l": 1549.4563666936847, // "o": 1563.4490341395904, // "v": 0 // }, // { // "pair": "ETHUSDT", // "timeframe": 86400000, // "timestamp": 1697760000000, // "c": 1603.7831363339324, // "h": 1625.0356823666407, // "l": 1565.4629390011505, // "o": 1566.8387619426028, // "v": 0 // }, // ... // ] // } // const candleHistory = this.safeValue(response, 'candleHistory', []); return this.parseOHLCVs(candleHistory, market, timeframe, since, limit); } parseOHLCV(ohlcv, market = undefined) { return [ this.safeInteger(ohlcv, 'timestamp'), this.safeNumber(ohlcv, 'o'), this.safeNumber(ohlcv, 'h'), this.safeNumber(ohlcv, 'l'), this.safeNumber(ohlcv, 'c'), this.safeNumber(ohlcv, 'v'), ]; } async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) { /** * @method * @name coinmetro#fetchTrades * @description get the list of most recent trades for a particular symbol * @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#6ee5d698-06da-4570-8c84-914185e05065 * @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 (default 200, max 500) * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades} */ await this.loadMarkets(); const market = this.market(symbol); const request = { 'pair': market['id'], }; if (since !== undefined) { request['from'] = since; } else { // this endpoint accepts empty from param request['from'] = ''; } const response = await this.publicGetExchangeTicksPairFrom(this.extend(request, params)); // // { // "tickHistory": [ // { // "pair": "ETHUSDT", // "price": 2077.5623, // "qty": 0.002888, // "timestamp": 1700684689420, // "seqNum": 10644554718 // }, // { // "pair": "ETHUSDT", // "price": 2078.3848, // "qty": 0.003368, // "timestamp": 1700684738410, // "seqNum": 10644559561 // }, // { // "pair": "ETHUSDT", // "price": 2077.1513, // "qty": 0.00337, // "timestamp": 1700684816853, // "seqNum": 10644567113 // }, // ... // ] // } // const tickHistory = this.safeValue(response, 'tickHistory', []); return this.parseTrades(tickHistory, market, since, limit); } async fetchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) { /** * @method * @name coinmetro#fetchMyTrades * @description fetch all trades made by the user * @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#4d48ae69-8ee2-44d1-a268-71f84e557b7b * @param {string} symbol unified market symbol * @param {int} [since] the earliest time in ms to fetch trades for * @param {int} [limit] the maximum number of trades structures to retrieve (default 500, max 1000) * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure} */ await this.loadMarkets(); let market = undefined; if (symbol !== undefined) { market = this.market(symbol); } const request = {}; if (since !== undefined) { request['since'] = since; } else { // the exchange requires a value for the since param request['since'] = 0; } const response = await this.privateGetExchangeFillsSince(this.extend(request, params)); // // [ // { // "pair": "ETHUSDC", // "seqNumber": 10873722343, // "timestamp": 1702570610747, // "qty": 0.002, // "price": 2282, // "side": "buy", // "orderID": "65671262d93d9525ac009e36170257061073952c6423a8c5b4d6c" // }, // ... // ] // return this.parseTrades(response, market, since, limit); } parseTrade(trade, market = undefined) { // // fetchTrades // { // "pair": "ETHUSDT", // "price": 2077.1513, // "qty": 0.00337, // "timestamp": 1700684816853, // "seqNum": 10644567113 // }, // // fetchMyTrades // { // "pair": "ETHUSDC", // "seqNumber": 10873722343, // "timestamp": 1702570610747, // "qty": 0.002, // "price": 2282, // "side": "buy", // "orderID": "65671262d93d9525ac009e36170257061073952c6423a8c5b4d6c" // } // // fetchOrders // { // "_id": "657b31d360a9542449381bdc", // "seqNumber": 10873722343, // "timestamp": 1702570610747, // "qty": 0.002, // "price": 2282, // "side": "buy" // } // // { // "pair":"ETHUSDC", // "seqNumber":"10873722343", // "timestamp":"1702570610747", // "qty":"0.002", // "price":"2282", // "side":"buy", // "orderID":"65671262d93d9525ac009e36170257061073952c6423a8c5b4d6c", // "userID":"65671262d93d9525ac009e36" // } // const marketId = this.safeString2(trade, 'symbol', 'pair'); market = this.safeMarket(marketId, market); const symbol = market['symbol']; const id = this.safeStringN(trade, ['_id', 'seqNum', 'seqNumber']); const timestamp = this.safeInteger(trade, 'timestamp'); const priceString = this.safeString(trade, 'price'); const amountString = this.safeString(trade, 'qty'); const order = this.safeString(trade, 'orderID'); const side = this.safeString(trade, 'side'); return this.safeTrade({ 'id': id, 'order': order, 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'symbol': symbol, 'type': undefined, 'side': side, 'takerOrMaker': undefined, 'price': priceString, 'amount': amountString, 'cost': undefined, 'fee': undefined, 'info': trade, }, market); } async fetchOrderBook(symbol, limit = undefined, params = {}) { /** * @method * @name coinmetro#fetchOrderBook * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data * @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#26ad80d7-8c46-41b5-9208-386f439a8b87 * @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 (default 100, max 200) * @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 */ await this.loadMarkets(); const market = this.market(symbol); const request = { 'pair': market['id'], }; const response = await this.publicGetExchangeBookPair(this.extend(request, params)); // // { // "book": { // "pair": "ETHUSDT", // "seqNumber": 10800409239, // "ask": { // "2354.2861": 3.75, // "2354.3138": 19, // "2354.7538": 80, // "2355.5430": 260, // "2356.4611": 950, // "2361.7150": 1500, // "206194.0000": 0.01 // }, // "bid": { // "2352.6339": 3.75, // "2352.6002": 19, // "2352.2402": 80, // "2351.4582": 260, // "2349.3111": 950, // "2343.8601": 1500, // "1.0000": 5 // }, // "checksum": 2108177337 // } // } // const book = this.safeValue(response, 'book', {}); const rawBids = this.safeValue(book, 'bid', {}); const rawAsks = this.safeValue(book, 'ask', {}); const rawOrderbook = { 'bids': rawBids, 'asks': rawAsks, }; const orderbook = this.parseOrderBook(rawOrderbook, symbol); orderbook['nonce'] = this.safeInteger(book, 'seqNumber'); return orderbook; } parseBidsAsks(bidasks, priceKey = 0, amountKey = 1, countOrIdKey = 2) { const prices = Object.keys(bidasks); const result = []; for (let i = 0; i < prices.length; i++) { const priceString = this.safeString(prices, i); const price = this.safeNumber(prices, i); const volume = this.safeNumber(bidasks, priceString); result.push([price, volume]); } return result; } async fetchTickers(symbols = undefined, params = {}) { /** * @method * @name coinmetro#fetchTickers * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market * @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#6ecd1cd1-f162-45a3-8b3b-de690332a485 * @param {string[]} [symbols] unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure} */ await this.loadMarkets(); const response = await this.publicGetExchangePrices(params); // // { // "latestPrices": [ // { // "pair": "PERPEUR", // "timestamp": 1702549840393, // "price": 0.7899997816001223, // "qty": 1e-12, // "ask": 0.8, // "bid": 0.7799995632002446 // }, // { // "pair": "PERPUSD", // "timestamp": 1702549841973, // "price": 0.8615317721366659, // "qty": 1e-12, // "ask": 0.8742333599999257, // "bid": 0.8490376365388491 // }, // ... // ], // "24hInfo": [ // { // "delta": 0.25396444229149906, // "h": 0.78999978160012, // "l": 0.630001740844, // "v": 54.910000002833996, // "pair": "PERPEUR", // "sentimentData": { // "sentiment": 36.71333333333333, // "interest": 0.47430830039525695 // } // }, // { // "delta": 0.26915154078134096, // "h": 0.86220315458898, // "l": 0.67866757035154, // "v": 2.835000000000001e-9, // "pair": "PERPUSD", // "sentimentData": { // "sentiment": 36.71333333333333, // "interest": 0.47430830039525695 // } // }, // ... // ] // } // const latestPrices = this.safeValue(response, 'latestPrices', []); const twentyFourHInfos = this.safeValue(response, '24hInfo', []); const tickersObject = {}; // merging info from two lists into one for (let i = 0; i < latestPrices.length; i++) { const latestPrice = latestPrices[i]; const marketId = this.safeString(latestPrice, 'pair'); if (marketId !== undefined) { tickersObject[marketId] = latestPrice; } } for (let i = 0; i < twentyFourHInfos.length; i++) { const twentyFourHInfo = twentyFourHInfos[i]; const marketId = this.safeString(twentyFourHInfo, 'pair'); if (marketId !== undefined) { const latestPrice = this.safeValue(tickersObject, marketId, {}); tickersObject[marketId] = this.extend(twentyFourHInfo, latestPrice); } } const tickers = Object.values(tickersObject); return this.parseTickers(tickers, symbols); } async fetchBidsAsks(symbols = undefined, params = {}) { /** * @method * @name coinmetro#fetchBidsAsks * @description fetches the bid and ask price and volume for multiple markets * @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#6ecd1cd1-f162-45a3-8b3b-de690332a485 * @param {string[]} [symbols] unified symbols of the markets to fetch the bids and asks for, all markets are returned if not assigned * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure} */ await this.loadMarkets(); const response = await this.publicGetExchangePrices(params); const latestPrices = this.safeValue(response, 'latestPrices', []); return this.parseTickers(latestPrices, symbols); } parseTicker(ticker, market = undefined) { // // { // "pair": "PERPUSD", // "timestamp": 1702549841973, // "price": 0.8615317721366659, // "qty": 1e-12, // "ask": 0.8742333599999257, // "bid": 0.8490376365388491 // "delta": 0.26915154078134096, // "h": 0.86220315458898, // "l": 0.67866757035154, // "v": 2.835000000000001e-9, // "sentimentData": { // "sentiment": 36.71333333333333, // "interest": 0.47430830039525695 // } // } // const marketId = this.safeString(ticker, 'pair'); market = this.safeMarket(marketId, market); const timestamp = this.safeInteger(ticker, 'timestamp'); const bid = this.safeString(ticker, 'bid'); const ask = this.safeString(ticker, 'ask'); const high = this.safeString(ticker, 'h'); const low = this.safeString(ticker, 'l'); const last = this.safeString(ticker, 'price'); const baseVolume = this.safeString(ticker, 'v'); const delta = this.safeString(ticker, 'delta'); const percentage = Precise["default"].stringMul(delta, '100'); return this.safeTicker({ 'symbol': market['symbol'], 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'open': undefined, 'high': high, 'low': low, 'close': undefined, 'last': last, 'bid': bid, 'bidVolume': undefined, 'ask': ask, 'askVolume': undefined, 'vwap': undefined, 'previousClose': undefined, 'change': undefined, 'percentage': percentage, 'average': undefined, 'baseVolume': baseVolume, 'quoteVolume': undefined, 'info': ticker, }, market); } async fetchBalance(params = {}) { /** * @method * @name coinmetro#fetchBalance * @description query for balance and get the amount of funds available for trading or funds locked in orders * @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#698ae067-43dd-4e19-a0ac-d9ba91381816 * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure} */ await this.loadMarkets(); const response = await this.privateGetUsersBalances(params); return this.parseBalance(response); } parseBalance(response) { // // { // "USDC": { // "USDC": 99, // "EUR": 91.16, // "BTC": 0.002334 // }, // "XCM": { // "XCM": 0, // "EUR": 0, // "BTC": 0 // }, // "TOTAL": { // "EUR": 91.16, // "BTC": 0.002334 // }, // "REF": { // "XCM": 0, // "EUR": 0, // "BTC": 0 // } // } // const result = { 'info': response, }; const balances = this.omit(response, ['TOTAL', 'REF']); const currencyIds = Object.keys(balances); for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const code = this.safeCurrencyCode(currencyId); const account = this.account(); const currency = this.safeValue(balances, currencyId, {}); account['total'] = this.safeString(currency, currencyId); result[code] = account; } return this.safeBalance(result); } async fetchLedger(code = undefined, since = undefined, limit = undefined, params = {}) { /** * @method * @name coinmetro#fetchLedger * @description fetch the history of changes, actions done by the user or operations that altered balance of the user * @see https://documenter.getpostman.com/view/3653795/SVfWN6KS#4e7831f7-a0e7-4c3e-9336-1d0e5dcb15cf * @param {string} code unified currency code, default is undefined * @param {int} [since] timestamp in ms of the earliest ledger entry, default is undefined * @param {int} [limit] max number of ledger entrys to return (default 200, max 500) * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {int} [params.until] the latest time in ms to fetch entries for * @returns {object} a [ledger structure]{@link https://docs.ccxt.com/#/?id=ledger-structure} */ await this.loadMarkets(); const request = {}; if (since !== undefined) { request['since'] = since; } else { // this endpoint accepts empty since param request['since'] = ''; } let currency = undefined; if (code !== undefined) { currency = this.currency(code); } const response = await this.privateGetUsersWalletsHistorySince(this.extend(request, params)); // // { // "list": [ // { // "currency": "USDC", // "label": "USDC", // "userId": "65671262d93d9525ac009e36", // "balance": 0, // "disabled": false, // "balanceHistory": [ // { // "description": "Deposit - 657973a9b6eadf0f33d70100", // "JSONdata": { // "fees": 0, // "notes": "Via Crypto", // "txHash": "0x2e4875185b0f312d8e24b2d26d46bf9877db798b608ad2ff97b2b8bc7d8134e5", // "last4Digits": null, // "IBAN": null, // "alternativeChain": "polygon", // "referenceId": "657973a9b6eadf0f33d70100", // "status": "completed", // "tracked": true // }, // "amount": 99, // "timestamp": "2023-12-13T09:04:51.270Z", // "amountEUR": 91.79310117335974 // }, // { // "description": "Order 65671262d93d9525ac009e36170257061073952c6423a8c5b4d6c SeqNum 10873722342", // "JSONdata": { // "price": "2282.00 ETH/USDC", // "fees": 0, // "notes": "Order 3a8c5b4d6c" // }, // "amount": -4.564, // "timestamp": "2023-12-14T16:16:50.760Z", // "amountEUR": -4.150043849187587 // }, // ... // ] // }, // { // "currency": "ETH", // "label": "ETH", // "userId": "65671262d93d9525ac009e36", // "balance": 0, // "disabled": false, // "balanceHistory": [ // { // "description": "Order 65671262d93d9525ac009e36170257061073952c6423a8c5b4d6c SeqNum 10873722342", // "JSONdata": { // "price": "2282.00 ETH/USDC", // "fees": 0.000002, // "notes": "Order 3a8c5b4d6c" // }, // "amount": 0.001998, // "timestamp": "2023-12-14T16:16:50.761Z", // "amountEUR": 4.144849415806856 // }, // ... // ] // }, // { // "currency": "DOGE", // "label": "DOGE", // "userId": "65671262d93d9525ac009e36", // "balance": 0, // "disabled": false, // "balanceHistory": [ // { // "description": "Order 65671262d93d9525ac009e361702905785319b5d9016dc20736034d13ca6a - Swap", // "JSONdata": { // "swap": true, // "subtype": "swap", // "fees": 0, // "price": "0.0905469 DOGE/USDC", // "notes": "Swap 034d13ca6a" // }, // "amount": 70, // "timestamp": "2023-12-18T13:23:05.836Z", // "amountEUR": 5.643627624549227 // } // ] // }, // ... // ] // } // const ledgerByCurrencies = this.safeValue(response, 'list', []); const ledger = []; for (let i = 0; i < ledgerByCurrencies.length; i++) { const currencyLedger = ledgerByCurrencies[i]; const currencyId = this.safeString(currencyLedger, 'currency'); const balanceHistory = this.safeValue(currencyLedger, 'balanceHistory', []); for (let j = 0; j < balanceHistory.length; j++) { const rawLedgerEntry = balanceHistory[j]; rawLedgerEntry['currencyId'] = currencyId; ledger.push(rawLedgerEntry); } } return this.parseLedger(ledger, currency, since, limit); } parseLedgerEntry(item, currency = undefined) { const datetime = this.safeString(item, 'timestamp'); const currencyId = this.safeString(item, 'currencyId'); item = this.omit(item, 'currencyId'); currency = this.safeCurrency(currencyId, currency); const description = this.safeString(item, 'description', ''); const [type, referenceId] = this.parseLedgerEntryDescription(description); const JSONdata = this.safeValue(item, 'JSONdata', {}); const feeCost = this.safeString(JSONdata, 'fees'); const fee = { 'cost': feeCost, 'currency': undefined, }; let amount = this.safeString(item, 'amount'); let direction = undefined; if (amount !== undefined) { if (Precise["default"].stringLt(amount, '0')) { direction = 'out'; amount = Precise["default"].stringAbs(amount); } else if (Precise["default"].stringGt(amount, '0')) { direction = 'in'; } } return this.safeLedgerEntry({ 'info': item, 'id': undefined, 'timestamp': this.parse8601(datetime), 'datetime': datetime, 'direction': direction,