ccxt
Version:
1,165 lines (1,163 loc) • 87.9 kB
JavaScript
// ----------------------------------------------------------------------------
// 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-