UNPKG

ccxt

Version:

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

1,165 lines (1,163 loc) • 87.9 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 Exchange from './abstract/coinmetro.js'; import { ArgumentsRequired, BadRequest, BadSymbol, InsufficientFunds, InvalidOrder, ExchangeError, OrderNotFound, PermissionDenied, RateLimitExceeded } from './base/errors.js'; import { TICK_SIZE } from './base/functions/number.js'; import { Precise } from './base/Precise.js'; // --------------------------------------------------------------------------- /** * @class coinmetro * @augments Exchange */ export default class coinmetro extends Exchange { 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, 'sandbox': true, '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': 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': TICK_SIZE, // exchange-specific options 'options': { 'currenciesByIdForParseMarket': undefined, 'currencyIdsListForParseMarket': ['QRDO'], 'skippedMarkets': ['VXVUSDT'], // broken markets which do not have enough info in API }, 'features': { 'spot': { 'sandbox': true, 'createOrder': { 'marginMode': true, 'triggerPrice': true, 'triggerPriceType': undefined, 'triggerDirection': false, 'stopLossPrice': false, 'takeProfitPrice': false, 'attachedStopLossTakeProfit': { 'triggerPriceType': undefined, 'price': false, }, 'timeInForce': { 'IOC': true, 'FOK': true, 'PO': false, 'GTD': true, }, 'hedged': false, 'trailing': false, 'leverage': false, 'marketBuyByCost': true, 'marketBuyRequiresPrice': false, 'selfTradePrevention': false, 'iceberg': true, }, 'createOrders': undefined, 'fetchMyTrades': { 'marginMode': false, 'limit': undefined, 'daysBack': 100000, 'untilDays': undefined, 'symbolRequired': false, }, 'fetchOrder': { 'marginMode': false, 'trigger': false, 'trailing': false, 'symbolRequired': false, }, 'fetchOpenOrders': { 'marginMode': false, 'limit': undefined, 'trigger': false, 'trailing': false, 'symbolRequired': false, }, 'fetchOrders': { 'marginMode': false, 'limit': undefined, 'daysBack': 100000, 'untilDays': undefined, 'trigger': false, 'trailing': false, 'symbolRequired': false, }, 'fetchClosedOrders': undefined, 'fetchOHLCV': { 'limit': 1000, }, }, 'swap': { 'linear': undefined, 'inverse': undefined, }, 'future': { 'linear': undefined, 'inverse': undefined, }, }, 'exceptions': { // https://trade-docs.coinmetro.co/?javascript--nodejs#message-codes 'exact': { 'Both buyingCurrency and sellingCurrency are required': InvalidOrder, 'One and only one of buyingQty and sellingQty is required': InvalidOrder, 'Invalid buyingCurrency': InvalidOrder, 'Invalid \'from\'': BadRequest, 'Invalid sellingCurrency': InvalidOrder, 'Invalid buyingQty': InvalidOrder, 'Invalid sellingQty': InvalidOrder, 'Insufficient balance': InsufficientFunds, 'Expiration date is in the past or too near in the future': InvalidOrder, 'Forbidden': PermissionDenied, 'Order Not Found': OrderNotFound, 'since must be a millisecond timestamp': BadRequest, 'This pair is disabled on margin': BadSymbol, // 422 Unprocessable Entity {"message":"This pair is disabled on margin"} }, 'broad': { 'accessing from a new IP': PermissionDenied, 'available to allocate as collateral': InsufficientFunds, 'At least': BadRequest, 'collateral is not allowed': BadRequest, 'Insufficient liquidity': InvalidOrder, 'Insufficient order size': InvalidOrder, 'Invalid quantity': InvalidOrder, 'Invalid Stop Loss': InvalidOrder, 'Invalid stop price!': InvalidOrder, 'Not enough balance': InsufficientFunds, 'Not enough margin': InsufficientFunds, 'orderType missing': BadRequest, 'Server Timeout': ExchangeError, 'Time in force has to be IOC or FOK for market orders': InvalidOrder, 'Too many attempts': RateLimitExceeded, // 429 Too Many Requests {"message":"Too many attempts. Try again in 3 seconds"} }, }, }); } /** * @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 */ async fetchCurrencies(params = {}) { 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 typeRaw = this.safeString(currency, 'type'); let type = undefined; if (typeRaw === 'coin' || typeRaw === 'token' || typeRaw === 'erc20') { type = 'crypto'; } else if (typeRaw === 'fiat') { type = 'fiat'; } let precisionDigits = this.safeString2(currency, 'digits', 'notabeneDecimals'); if (code === 'RENDER') { // RENDER is an exception (with broken info) precisionDigits = '4'; } result[code] = this.safeCurrencyStructure({ 'id': id, 'code': code, 'name': code, 'type': type, 'info': currency, 'active': this.safeBool(currency, 'canTrade'), 'deposit': this.safeBool(currency, 'canDeposit'), 'withdraw': this.safeBool(currency, 'canWithdraw'), 'fee': undefined, 'precision': this.parseNumber(this.parsePrecision(precisionDigits)), 'limits': { 'amount': { 'min': this.safeNumber(currency, 'minQty'), '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; const currentCurrencyIdsList = this.safeList(this.options, 'currencyIdsListForParseMarket', []); const currencyIdsList = Object.keys(currenciesById); for (let i = 0; i < currencyIdsList.length; i++) { currentCurrencyIdsList.push(currencyIdsList[i]); } this.options['currencyIdsListForParseMarket'] = currentCurrencyIdsList; } return result; } /** * @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 */ async fetchMarkets(params = {}) { const promises = []; promises.push(this.publicGetMarkets(params)); if (this.safeValue(this.options, 'currenciesByIdForParseMarket') === undefined) { promises.push(this.fetchCurrencies()); } const responses = await Promise.all(promises); const response = responses[0]; // // [ // { // "pair": "YFIEUR", // "precision": 5, // "margin": false // }, // { // "pair": "BTCEUR", // "precision": 2, // "margin": true // }, // ... // ] // const skippedMarkets = this.safeList(this.options, 'skippedMarkets', []); const result = []; for (let i = 0; i < response.length; i++) { const market = this.parseMarket(response[i]); if (this.inArray(market['id'], skippedMarkets)) { continue; } result.push(market); } return result; } 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': this.parseNumber(this.parsePrecision(this.safeString(market, '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', []); // Bubble sort by length (longest first) const currencyIdsLength = currencyIds.length; for (let i = 0; i < currencyIdsLength; i++) { for (let j = 0; j < currencyIdsLength - i - 1; j++) { const a = currencyIds[j]; const b = currencyIds[j + 1]; if (a.length < b.length) { currencyIds[j] = b; currencyIds[j + 1] = a; } } } for (let i = 0; i < currencyIds.length; i++) { const currencyId = currencyIds[i]; const entryIndex = marketId.indexOf(currencyId); if (entryIndex === 0) { 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 limits = this.safeValue(currency, 'limits', {}); const amountLimits = this.safeValue(limits, 'amount', {}); const minLimit = this.safeNumber(amountLimits, 'min'); const result = { 'precision': this.safeNumber(currency, 'precision'), 'minLimit': minLimit, }; return result; } /** * @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 */ async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { 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.safeInteger(params, 'until', until); if (until !== undefined) { params = this.omit(params, ['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.safeList(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'), ]; } /** * @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} */ async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) { 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.safeList(response, 'tickHistory', []); return this.parseTrades(tickHistory, market, since, limit); } /** * @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} */ async fetchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) { 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); } /** * @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 */ async fetchOrderBook(symbol, limit = undefined, params = {}) { 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; } /** * @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} */ async fetchTickers(symbols = undefined, params = {}) { 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); } /** * @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} */ async fetchBidsAsks(symbols = undefined, params = {}) { await this.loadMarkets(); const response = await this.publicGetExchangePrices(params); const latestPrices = this.safeList(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.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); } /** * @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#741a1dcc-7307-40d0-acca-28d003d1506a * @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 fetchBalance(params = {}) { await this.loadMarkets(); const response = await this.privateGetUsersWallets(params); const list = this.safeList(response, 'list', []); return this.parseBalance(list); } parseBalance(balances) { // // [ // { // "xcmLocks": [], // "xcmLockAmounts": [], // "refList": [], // "balanceHistory": [], // "_id": "5fecd3c998e75c2e4d63f7c3", // "currency": "BTC", // "label": "BTC", // "userId": "5fecd3c97fbfed1521db23bd", // "__v": 0, // "balance": 0.5, // "createdAt": "2020-12-30T19:23:53.646Z", // "disabled": false, // "updatedAt": "2020-12-30T19:23:53.653Z", // "reserved": 0, // "id": "5fecd3c998e75c2e4d63f7c3" // }, // ... // ] // const result = { 'info': balances, }; for (let i = 0; i < balances.length; i++) { const balanceEntry = this.safeDict(balances, i, {}); const currencyId = this.safeString(balanceEntry, 'currency'); const code = this.safeCurrencyCode(currencyId); const account = this.account(); account['total'] = this.safeString(balanceEntry, 'balance'); account['used'] = this.safeString(balanceEntry, 'reserved'); result[code] = account; } return this.safeBalance(result); } /** * @method * @name coinmetro#fetchLedger * @description fetch the history of changes, actions done by the user or operations that altered the 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 entries 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} */ async fetchLedger(code = undefined, since = undefined, limit = undefined, params = {}) { 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-