UNPKG

sfccxt

Version:

A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges

1,168 lines (1,147 loc) 123 kB
const Exchange = require ('./base/Exchange'); const { TICK_SIZE } = require ('./base/functions/number'); const Precise = require ('./base/Precise'); const { BadSymbol, BadRequest, OnMaintenance, AccountSuspended, PermissionDenied, ExchangeError, RateLimitExceeded, ExchangeNotAvailable, OrderNotFound, InsufficientFunds, InvalidOrder, AuthenticationError, ArgumentsRequired, NotSupported } = require ('./base/errors'); module.exports = class hitbtc3 extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'hitbtc3', 'name': 'HitBTC', 'countries': [ 'HK' ], // 300 requests per second => 1000ms / 300 = 3.333 (Trading: placing, replacing, deleting) // 30 requests per second => ( 1000ms / rateLimit ) / 30 = cost = 10 (Market Data and other Public Requests) // 20 requests per second => ( 1000ms / rateLimit ) / 20 = cost = 15 (All Other) 'rateLimit': 3.333, // TODO: optimize https://api.hitbtc.com/#rate-limiting 'version': '3', 'pro': true, 'has': { 'CORS': false, 'spot': true, 'margin': true, 'swap': true, 'future': false, 'option': undefined, 'addMargin': true, 'cancelAllOrders': true, 'cancelOrder': true, 'createDepositAddress': true, 'createOrder': true, 'createReduceOnlyOrder': true, 'createStopLimitOrder': true, 'createStopMarketOrder': true, 'createStopOrder': true, 'editOrder': true, 'fetchBalance': true, 'fetchBorrowRate': undefined, 'fetchBorrowRateHistories': undefined, 'fetchBorrowRateHistory': undefined, 'fetchBorrowRates': undefined, 'fetchClosedOrders': true, 'fetchCurrencies': true, 'fetchDepositAddress': true, 'fetchDeposits': true, 'fetchFundingHistory': undefined, 'fetchFundingRate': true, 'fetchFundingRateHistory': true, 'fetchFundingRates': false, 'fetchIndexOHLCV': true, 'fetchLeverage': true, 'fetchLeverageTiers': undefined, 'fetchMarketLeverageTiers': undefined, 'fetchMarkets': true, 'fetchMarkOHLCV': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenOrder': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrderBooks': true, 'fetchOrders': false, 'fetchOrderTrades': true, 'fetchPosition': true, 'fetchPositions': true, 'fetchPremiumIndexOHLCV': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchTrades': true, 'fetchTradingFee': true, 'fetchTradingFees': true, 'fetchTransactions': true, 'fetchWithdrawals': true, 'reduceMargin': true, 'setLeverage': true, 'setMarginMode': false, 'setPositionMode': false, 'transfer': true, 'withdraw': true, }, 'precisionMode': TICK_SIZE, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/27766555-8eaec20e-5edc-11e7-9c5b-6dc69fc42f5e.jpg', 'test': { 'public': 'https://api.demo.hitbtc.com', 'private': 'https://api.demo.hitbtc.com', }, 'api': { 'public': 'https://api.hitbtc.com/api/3', 'private': 'https://api.hitbtc.com/api/3', }, 'www': 'https://hitbtc.com', 'referral': 'https://hitbtc.com/?ref_id=5a5d39a65d466', 'doc': [ 'https://api.hitbtc.com', 'https://github.com/hitbtc-com/hitbtc-api/blob/master/APIv2.md', ], 'fees': [ 'https://hitbtc.com/fees-and-limits', 'https://support.hitbtc.com/hc/en-us/articles/115005148605-Fees-and-limits', ], }, 'api': { 'public': { 'get': { 'public/currency': 10, 'public/symbol': 10, 'public/ticker': 10, 'public/price/rate': 10, 'public/trades': 10, 'public/orderbook': 10, 'public/candles': 10, 'public/futures/info': 10, 'public/futures/history/funding': 10, 'public/futures/candles/index_price': 10, 'public/futures/candles/mark_price': 10, 'public/futures/candles/premium_index': 10, 'public/futures/candles/open_interest': 10, }, }, 'private': { 'get': { 'spot/balance': 15, 'spot/order': 15, 'spot/order/{client_order_id}': 15, 'spot/fee': 15, 'spot/fee/{symbol}': 15, 'spot/history/order': 15, 'spot/history/trade': 15, 'margin/account': 15, 'margin/account/isolated/{symbol}': 15, 'margin/order': 15, 'margin/order/{client_order_id}': 15, 'margin/history/clearing': 15, 'margin/history/order': 15, 'margin/history/positions': 15, 'margin/history/trade': 15, 'futures/balance': 15, 'futures/account': 15, 'futures/account/isolated/{symbol}': 15, 'futures/order': 15, 'futures/order/{client_order_id}': 15, 'futures/fee': 15, 'futures/fee/{symbol}': 15, 'futures/history/clearing': 15, 'futures/history/order': 15, 'futures/history/positions': 15, 'futures/history/trade': 15, 'wallet/balance': 15, 'wallet/crypto/address': 15, 'wallet/crypto/address/recent-deposit': 15, 'wallet/crypto/address/recent-withdraw': 15, 'wallet/crypto/address/check-mine': 15, 'wallet/transactions': 15, 'wallet/crypto/check-offchain-available': 15, 'wallet/crypto/fee/estimate': 15, 'sub-account': 15, 'sub-account/acl': 15, 'sub-account/balance/{subAccID}': 15, 'sub-account/crypto/address/{subAccID}/{currency}': 15, }, 'post': { 'spot/order': 1, 'margin/order': 1, 'futures/order': 1, 'wallet/convert': 15, 'wallet/crypto/address': 15, 'wallet/crypto/withdraw': 15, 'wallet/transfer': 15, 'sub-account/freeze': 15, 'sub-account/activate': 15, 'sub-account/transfer': 15, 'sub-account/acl': 15, }, 'patch': { 'spot/order/{client_order_id}': 1, 'margin/order/{client_order_id}': 1, 'futures/order/{client_order_id}': 1, }, 'delete': { 'spot/order': 1, 'spot/order/{client_order_id}': 1, 'margin/position': 1, 'margin/position/isolated/{symbol}': 1, 'margin/order': 1, 'margin/order/{client_order_id}': 1, 'futures/position': 1, 'futures/position/isolated/{symbol}': 1, 'futures/order': 1, 'futures/order/{client_order_id}': 1, 'wallet/crypto/withdraw/{id}': 1, }, 'put': { 'margin/account/isolated/{symbol}': 1, 'futures/account/isolated/{symbol}': 1, 'wallet/crypto/withdraw/{id}': 1, }, }, }, 'fees': { 'trading': { 'tierBased': true, 'percentage': true, 'taker': this.parseNumber ('0.0009'), 'maker': this.parseNumber ('0.0009'), 'tiers': { 'maker': [ [ this.parseNumber ('0'), this.parseNumber ('0.0009') ], [ this.parseNumber ('10'), this.parseNumber ('0.0007') ], [ this.parseNumber ('100'), this.parseNumber ('0.0006') ], [ this.parseNumber ('500'), this.parseNumber ('0.0005') ], [ this.parseNumber ('1000'), this.parseNumber ('0.0003') ], [ this.parseNumber ('5000'), this.parseNumber ('0.0002') ], [ this.parseNumber ('10000'), this.parseNumber ('0.0001') ], [ this.parseNumber ('20000'), this.parseNumber ('0') ], [ this.parseNumber ('50000'), this.parseNumber ('-0.0001') ], [ this.parseNumber ('100000'), this.parseNumber ('-0.0001') ], ], 'taker': [ [ this.parseNumber ('0'), this.parseNumber ('0.0009') ], [ this.parseNumber ('10'), this.parseNumber ('0.0008') ], [ this.parseNumber ('100'), this.parseNumber ('0.0007') ], [ this.parseNumber ('500'), this.parseNumber ('0.0007') ], [ this.parseNumber ('1000'), this.parseNumber ('0.0006') ], [ this.parseNumber ('5000'), this.parseNumber ('0.0006') ], [ this.parseNumber ('10000'), this.parseNumber ('0.0005') ], [ this.parseNumber ('20000'), this.parseNumber ('0.0004') ], [ this.parseNumber ('50000'), this.parseNumber ('0.0003') ], [ this.parseNumber ('100000'), this.parseNumber ('0.0002') ], ], }, }, }, 'timeframes': { '1m': 'M1', '3m': 'M3', '5m': 'M5', '15m': 'M15', '30m': 'M30', // default '1h': 'H1', '4h': 'H4', '1d': 'D1', '1w': 'D7', '1M': '1M', }, 'exceptions': { 'exact': { '429': RateLimitExceeded, '500': ExchangeError, '503': ExchangeNotAvailable, '504': ExchangeNotAvailable, '600': PermissionDenied, '800': ExchangeError, '1002': AuthenticationError, '1003': PermissionDenied, '1004': AuthenticationError, '1005': AuthenticationError, '2001': BadSymbol, '2002': BadRequest, '2003': BadRequest, '2010': BadRequest, '2011': BadRequest, '2012': BadRequest, '2020': BadRequest, '2022': BadRequest, '10001': BadRequest, '10021': AccountSuspended, '10022': BadRequest, '20001': InsufficientFunds, '20002': OrderNotFound, '20003': ExchangeError, '20004': ExchangeError, '20005': ExchangeError, '20006': ExchangeError, '20007': ExchangeError, '20008': InvalidOrder, '20009': InvalidOrder, '20010': OnMaintenance, '20011': ExchangeError, '20012': ExchangeError, '20014': ExchangeError, '20016': ExchangeError, '20031': ExchangeError, '20032': ExchangeError, '20033': ExchangeError, '20034': ExchangeError, '20040': ExchangeError, '20041': ExchangeError, '20042': ExchangeError, '20043': ExchangeError, '20044': PermissionDenied, '20045': InvalidOrder, '20080': ExchangeError, '21001': ExchangeError, '21003': AccountSuspended, '21004': AccountSuspended, }, 'broad': {}, }, 'options': { 'networks': { 'ETH': 'USDT20', 'ERC20': 'USDT20', 'TRX': 'USDTRX', 'TRC20': 'USDTRX', 'OMNI': 'USDT', }, 'accountsByType': { 'spot': 'spot', 'funding': 'wallet', 'future': 'derivatives', }, }, 'commonCurrencies': { 'AUTO': 'Cube', 'BCC': 'BCC', // initial symbol for Bitcoin Cash, now inactive 'BDP': 'BidiPass', 'BET': 'DAO.Casino', 'BIT': 'BitRewards', 'BOX': 'BOX Token', 'CPT': 'Cryptaur', // conflict with CPT = Contents Protocol https://github.com/ccxt/ccxt/issues/4920 and https://github.com/ccxt/ccxt/issues/6081 'GET': 'Themis', 'GMT': 'GMT Token', 'HSR': 'HC', 'IQ': 'IQ.Cash', 'LNC': 'LinkerCoin', 'PLA': 'PlayChip', 'PNT': 'Penta', 'SBTC': 'Super Bitcoin', 'STEPN': 'GMT', 'STX': 'STOX', 'TV': 'Tokenville', 'USD': 'USDT', 'XMT': 'MTL', 'XPNT': 'PNT', }, }); } nonce () { return this.milliseconds (); } async fetchMarkets (params = {}) { /** * @method * @name hitbtc3#fetchMarkets * @description retrieves data on all markets for hitbtc3 * @param {object} params extra parameters specific to the exchange api endpoint * @returns {[object]} an array of objects representing market data */ const response = await this.publicGetPublicSymbol (params); // // { // "AAVEUSDT_PERP":{ // "type":"futures", // "expiry":null, // "underlying":"AAVE", // "base_currency":null, // "quote_currency":"USDT", // "quantity_increment":"0.01", // "tick_size":"0.001", // "take_rate":"0.0005", // "make_rate":"0.0002", // "fee_currency":"USDT", // "margin_trading":true, // "max_initial_leverage":"50.00" // }, // "MANAUSDT":{ // "type":"spot", // "base_currency":"MANA", // "quote_currency":"USDT", // "quantity_increment":"1", // "tick_size":"0.0000001", // "take_rate":"0.0025", // "make_rate":"0.001", // "fee_currency":"USDT", // "margin_trading":true, // "max_initial_leverage":"5.00" // }, // } // const result = []; const ids = Object.keys (response); for (let i = 0; i < ids.length; i++) { const id = ids[i]; const market = this.safeValue (response, id); const marketType = this.safeString (market, 'type'); const expiry = this.safeInteger (market, 'expiry'); const contract = (marketType === 'futures'); const spot = (marketType === 'spot'); const marginTrading = this.safeValue (market, 'margin_trading', false); const margin = spot && marginTrading; const future = (expiry !== undefined); const swap = (contract && !future); const option = false; const baseId = this.safeString2 (market, 'base_currency', 'underlying'); const quoteId = this.safeString (market, 'quote_currency'); const feeCurrencyId = this.safeString (market, 'fee_currency'); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const feeCurrency = this.safeCurrencyCode (feeCurrencyId); let settleId = undefined; let settle = undefined; let symbol = base + '/' + quote; let type = 'spot'; let contractSize = undefined; let linear = undefined; let inverse = undefined; if (contract) { contractSize = this.parseNumber ('1'); settleId = feeCurrencyId; settle = this.safeCurrencyCode (settleId); linear = ((quote !== undefined) && (quote === settle)); inverse = !linear; symbol = symbol + ':' + settle; if (future) { symbol = symbol + '-' + expiry; type = 'future'; } else { type = 'swap'; } } const lotString = this.safeString (market, 'quantity_increment'); const stepString = this.safeString (market, 'tick_size'); const lot = this.parseNumber (lotString); const step = this.parseNumber (stepString); result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'settle': settle, 'baseId': baseId, 'quoteId': quoteId, 'settleId': settleId, 'type': type, 'spot': spot, 'margin': margin, 'swap': swap, 'future': future, 'option': option, 'active': true, 'contract': contract, 'linear': linear, 'inverse': inverse, 'taker': this.safeNumber (market, 'take_rate'), 'maker': this.safeNumber (market, 'make_rate'), 'contractSize': contractSize, 'expiry': expiry, 'expiryDatetime': undefined, 'strike': undefined, 'optionType': undefined, 'feeCurrency': feeCurrency, 'precision': { 'amount': lot, 'price': step, }, 'limits': { 'leverage': { 'min': this.parseNumber ('1'), 'max': this.safeNumber (market, 'max_initial_leverage', 1), }, 'amount': { 'min': lot, 'max': undefined, }, 'price': { 'min': step, 'max': undefined, }, 'cost': { 'min': this.parseNumber (Precise.stringMul (lotString, stepString)), 'max': undefined, }, }, 'info': market, }); } return result; } async fetchCurrencies (params = {}) { /** * @method * @name hitbtc3#fetchCurrencies * @description fetches all available currencies on an exchange * @param {object} params extra parameters specific to the hitbtc3 api endpoint * @returns {object} an associative dictionary of currencies */ const response = await this.publicGetPublicCurrency (params); // // { // "WEALTH": { // "full_name": "ConnectWealth", // "payin_enabled": false, // "payout_enabled": false, // "transfer_enabled": true, // "precision_transfer": "0.001", // "networks": [ // { // "network": "ETH", // "protocol": "ERC20", // "default": true, // "payin_enabled": false, // "payout_enabled": false, // "precision_payout": "0.001", // "payout_fee": "0.016800000000", // "payout_is_payment_id": false, // "payin_payment_id": false, // "payin_confirmations": "2" // } // ] // } // } // const result = {}; const currencies = Object.keys (response); for (let i = 0; i < currencies.length; i++) { const currencyId = currencies[i]; const code = this.safeCurrencyCode (currencyId); const entry = response[currencyId]; const name = this.safeString (entry, 'full_name'); const precision = this.safeNumber (entry, 'precision_transfer'); const payinEnabled = this.safeValue (entry, 'payin_enabled', false); const payoutEnabled = this.safeValue (entry, 'payout_enabled', false); const transferEnabled = this.safeValue (entry, 'transfer_enabled', false); const active = payinEnabled && payoutEnabled && transferEnabled; const rawNetworks = this.safeValue (entry, 'networks', []); const networks = {}; let fee = undefined; let depositEnabled = undefined; let withdrawEnabled = undefined; for (let j = 0; j < rawNetworks.length; j++) { const rawNetwork = rawNetworks[j]; const networkId = this.safeString2 (rawNetwork, 'protocol', 'network'); const network = this.safeNetwork (networkId); fee = this.safeNumber (rawNetwork, 'payout_fee'); const networkPrecision = this.safeNumber (rawNetwork, 'precision_payout'); const payinEnabledNetwork = this.safeValue (entry, 'payin_enabled', false); const payoutEnabledNetwork = this.safeValue (entry, 'payout_enabled', false); const activeNetwork = payinEnabledNetwork && payoutEnabledNetwork; if (payinEnabledNetwork && !depositEnabled) { depositEnabled = true; } else if (!payinEnabledNetwork) { depositEnabled = false; } if (payoutEnabledNetwork && !withdrawEnabled) { withdrawEnabled = true; } else if (!payoutEnabledNetwork) { withdrawEnabled = false; } networks[network] = { 'info': rawNetwork, 'id': networkId, 'network': network, 'fee': fee, 'active': activeNetwork, 'deposit': payinEnabledNetwork, 'withdraw': payoutEnabledNetwork, 'precision': networkPrecision, 'limits': { 'withdraw': { 'min': undefined, 'max': undefined, }, }, }; } const networksKeys = Object.keys (networks); const networksLength = networksKeys.length; result[code] = { 'info': entry, 'code': code, 'id': currencyId, 'precision': precision, 'name': name, 'active': active, 'deposit': depositEnabled, 'withdraw': withdrawEnabled, 'networks': networks, 'fee': (networksLength <= 1) ? fee : undefined, 'limits': { 'amount': { 'min': undefined, 'max': undefined, }, }, }; } return result; } safeNetwork (networkId) { if (networkId === undefined) { return undefined; } else { return networkId.toUpperCase (); } } async createDepositAddress (code, params = {}) { /** * @method * @name hitbtc3#createDepositAddress * @description create a currency deposit address * @param {string} code unified currency code of the currency for the deposit address * @param {object} params extra parameters specific to the hitbtc api endpoint * @returns {object} an [address structure]{@link https://docs.ccxt.com/en/latest/manual.html#address-structure} */ await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const network = this.safeStringUpper (params, 'network'); if ((network !== undefined) && (code === 'USDT')) { const networks = this.safeValue (this.options, 'networks'); const parsedNetwork = this.safeString (networks, network); if (parsedNetwork !== undefined) { request['currency'] = parsedNetwork; } params = this.omit (params, 'network'); } const response = await this.privatePostWalletCryptoAddress (this.extend (request, params)); // // {"currency":"ETH","address":"0xd0d9aea60c41988c3e68417e2616065617b7afd3"} // const currencyId = this.safeString (response, 'currency'); return { 'currency': this.safeCurrencyCode (currencyId), 'address': this.safeString (response, 'address'), 'tag': this.safeString (response, 'payment_id'), 'network': undefined, 'info': response, }; } async fetchDepositAddress (code, params = {}) { /** * @method * @name hitbtc3#fetchDepositAddress * @description fetch the deposit address for a currency associated with this account * @param {string} code unified currency code * @param {object} params extra parameters specific to the hitbtc3 api endpoint * @returns {object} an [address structure]{@link https://docs.ccxt.com/en/latest/manual.html#address-structure} */ await this.loadMarkets (); const currency = this.currency (code); const request = { 'currency': currency['id'], }; const network = this.safeStringUpper (params, 'network'); if ((network !== undefined) && (code === 'USDT')) { const networks = this.safeValue (this.options, 'networks'); const parsedNetwork = this.safeString (networks, network); if (parsedNetwork !== undefined) { request['currency'] = parsedNetwork; } params = this.omit (params, 'network'); } const response = await this.privateGetWalletCryptoAddress (this.extend (request, params)); // // [{"currency":"ETH","address":"0xd0d9aea60c41988c3e68417e2616065617b7afd3"}] // const firstAddress = this.safeValue (response, 0); const address = this.safeString (firstAddress, 'address'); const currencyId = this.safeString (firstAddress, 'currency'); const tag = this.safeString (firstAddress, 'payment_id'); const parsedCode = this.safeCurrencyCode (currencyId); return { 'info': response, 'address': address, 'tag': tag, 'code': parsedCode, // kept here for backward-compatibility, but will be removed soon 'currency': parsedCode, 'network': undefined, }; } parseBalance (response) { const result = { 'info': response }; for (let i = 0; i < response.length; i++) { const entry = response[i]; const currencyId = this.safeString (entry, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeString (entry, 'available'); account['used'] = this.safeString (entry, 'reserved'); result[code] = account; } return this.safeBalance (result); } async fetchBalance (params = {}) { /** * @method * @name hitbtc3#fetchBalance * @description query for balance and get the amount of funds available for trading or funds locked in orders * @param {object} params extra parameters specific to the hitbtc3 api endpoint * @returns {object} a [balance structure]{@link https://docs.ccxt.com/en/latest/manual.html?#balance-structure} */ const type = this.safeStringLower (params, 'type', 'spot'); params = this.omit (params, [ 'type' ]); const accountsByType = this.safeValue (this.options, 'accountsByType', {}); const account = this.safeString (accountsByType, type, type); let response = undefined; if (account === 'wallet') { response = await this.privateGetWalletBalance (params); } else if (account === 'spot') { response = await this.privateGetSpotBalance (params); } else if (account === 'derivatives') { response = await this.privateGetFuturesBalance (params); } else { const keys = Object.keys (accountsByType); throw new BadRequest (this.id + ' fetchBalance() type parameter must be one of ' + keys.join (', ')); } // // [ // { // "currency": "PAXG", // "available": "0", // "reserved": "0", // "reserved_margin": "0", // }, // ... // ] // return this.parseBalance (response); } async fetchTicker (symbol, params = {}) { /** * @method * @name hitbtc3#fetchTicker * @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market * @param {string} symbol unified symbol of the market to fetch the ticker for * @param {object} params extra parameters specific to the hitbtc3 api endpoint * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure} */ const response = await this.fetchTickers ([ symbol ], params); return this.safeValue (response, symbol); } async fetchTickers (symbols = undefined, params = {}) { /** * @method * @name hitbtc3#fetchTickers * @description fetches price tickers for multiple markets, statistical calculations with the information calculated over the past 24 hours each market * @param {[string]|undefined} 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 hitbtc3 api endpoint * @returns {object} an array of [ticker structures]{@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure} */ await this.loadMarkets (); symbols = this.marketSymbols (symbols); const request = {}; if (symbols !== undefined) { const marketIds = this.marketIds (symbols); const delimited = marketIds.join (','); request['symbols'] = delimited; } const response = await this.publicGetPublicTicker (this.extend (request, params)); // // { // "BTCUSDT": { // "ask": "63049.06", // "bid": "63046.41", // "last": "63048.36", // "low": "62010.00", // "high": "66657.99", // "open": "64839.75", // "volume": "15272.13278", // "volume_quote": "976312127.6277998", // "timestamp": "2021-10-22T04:25:47.573Z" // } // } // const result = {}; const keys = Object.keys (response); for (let i = 0; i < keys.length; i++) { const marketId = keys[i]; const market = this.safeMarket (marketId); const symbol = market['symbol']; const entry = response[marketId]; result[symbol] = this.parseTicker (entry, market); } return this.filterByArray (result, 'symbol', symbols); } parseTicker (ticker, market = undefined) { // // { // "ask": "62756.01", // "bid": "62754.09", // "last": "62755.87", // "low": "62010.00", // "high": "66657.99", // "open": "65089.27", // "volume": "16719.50366", // "volume_quote": "1063422878.8156828", // "timestamp": "2021-10-22T07:29:14.585Z" // } // const timestamp = this.parse8601 (ticker['timestamp']); const symbol = this.safeSymbol (undefined, market); const baseVolume = this.safeString (ticker, 'volume'); const quoteVolume = this.safeString (ticker, 'volume_quote'); const open = this.safeString (ticker, 'open'); const last = this.safeString (ticker, 'last'); return this.safeTicker ({ 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeString (ticker, 'high'), 'low': this.safeString (ticker, 'low'), 'bid': this.safeString (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeString (ticker, 'ask'), 'askVolume': undefined, 'vwap': undefined, 'open': open, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }, market); } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { /** * @method * @name hitbtc3#fetchTrades * @description get the list of most recent trades for a particular symbol * @param {string} symbol unified symbol of the market to fetch trades for * @param {int|undefined} since timestamp in ms of the earliest trade to fetch * @param {int|undefined} limit the maximum amount of trades to fetch * @param {object} params extra parameters specific to the hitbtc3 api endpoint * @returns {[object]} a list of [trade structures]{@link https://docs.ccxt.com/en/latest/manual.html?#public-trades} */ await this.loadMarkets (); let market = undefined; const request = {}; if (symbol !== undefined) { market = this.market (symbol); // symbol is optional for hitbtc fetchTrades request['symbols'] = market['id']; } if (limit !== undefined) { request['limit'] = limit; } if (since !== undefined) { request['from'] = since; } const response = await this.publicGetPublicTrades (this.extend (request, params)); const marketIds = Object.keys (response); let trades = []; for (let i = 0; i < marketIds.length; i++) { const marketId = marketIds[i]; const market = this.market (marketId); const rawTrades = response[marketId]; const parsed = this.parseTrades (rawTrades, market); trades = this.arrayConcat (trades, parsed); } return trades; } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { /** * @method * @name hitbtc3#fetchMyTrades * @description fetch all trades made by the user * @param {string|undefined} symbol unified market symbol * @param {int|undefined} since the earliest time in ms to fetch trades for * @param {int|undefined} limit the maximum number of trades structures to retrieve * @param {object} params extra parameters specific to the hitbtc3 api endpoint * @param {string|undefined} params.marginMode 'cross' or 'isolated' only 'isolated' is supported * @param {bool|undefined} params.margin true for fetching margin trades * @returns {[object]} a list of [trade structures]{@link https://docs.ccxt.com/en/latest/manual.html#trade-structure} */ await this.loadMarkets (); let market = undefined; const request = {}; if (symbol !== undefined) { market = this.market (symbol); request['symbol'] = market['id']; } if (limit !== undefined) { request['limit'] = limit; } if (since !== undefined) { request['from'] = since; } let marketType = undefined; [ marketType, params ] = this.handleMarketTypeAndParams ('fetchMyTrades', market, params); let method = this.getSupportedMapping (marketType, { 'spot': 'privateGetSpotHistoryTrade', 'swap': 'privateGetFuturesHistoryTrade', 'margin': 'privateGetMarginHistoryTrade', }); const [ marginMode, query ] = this.handleMarginModeAndParams ('fetchMyTrades', params); if (marginMode !== undefined) { method = 'privateGetMarginHistoryTrade'; } const response = await this[method] (this.extend (request, query)); return this.parseTrades (response, market, since, limit); } parseTrade (trade, market = undefined) { // // createOrder (market) // // { // id: '1569252895', // position_id: '0', // quantity: '10', // price: '0.03919424', // fee: '0.000979856000', // timestamp: '2022-01-25T19:38:36.153Z', // taker: true // } // // fetchTrades // // { // id: 974786185, // price: '0.032462', // qty: '0.3673', // side: 'buy', // timestamp: '2020-10-16T12:57:39.846Z' // } // // fetchMyTrades spot // // { // id: 277210397, // clientOrderId: '6e102f3e7f3f4e04aeeb1cdc95592f1a', // orderId: 28102855393, // symbol: 'ETHBTC', // side: 'sell', // quantity: '0.002', // price: '0.073365', // fee: '0.000000147', // timestamp: '2018-04-28T18:39:55.345Z', // taker: true // } // // fetchMyTrades swap and margin // // { // "id": 4718564, // "order_id": 58730811958, // "client_order_id": "475c47d97f867f09726186eb22b4c3d4", // "symbol": "BTCUSDT_PERP", // "side": "sell", // "quantity": "0.0001", // "price": "41118.51", // "fee": "0.002055925500", // "timestamp": "2022-03-17T05:23:17.795Z", // "taker": true, // "position_id": 2350122, // "pnl": "0.002255000000", // "liquidation": false // } // const timestamp = this.parse8601 (trade['timestamp']); const marketId = this.safeString (trade, 'symbol'); market = this.safeMarket (marketId, market); const symbol = market['symbol']; let fee = undefined; const feeCostString = this.safeString (trade, 'fee'); const taker = this.safeValue (trade, 'taker'); let takerOrMaker = undefined; if (taker !== undefined) { takerOrMaker = taker ? 'taker' : 'maker'; } if (feeCostString !== undefined) { const info = this.safeValue (market, 'info', {}); const feeCurrency = this.safeString (info, 'fee_currency'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrency); fee = { 'cost': feeCostString, 'currency': feeCurrencyCode, }; } // we use clientOrderId as the order id with this exchange intentionally // because most of their endpoints will require clientOrderId // explained here: https://github.com/ccxt/ccxt/issues/5674 const orderId = this.safeString2 (trade, 'clientOrderId', 'client_order_id'); const priceString = this.safeString (trade, 'price'); const amountString = this.safeString2 (trade, 'quantity', 'qty'); const side = this.safeString (trade, 'side'); const id = this.safeString (trade, 'id'); return this.safeTrade ({ 'info': trade, 'id': id, 'order': orderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': side, 'takerOrMaker': takerOrMaker, 'price': priceString, 'amount': amountString, 'cost': undefined, 'fee': fee, }, market); } async fetchTransactionsHelper (types, code, since, limit, params) { await this.loadMarkets (); const request = { 'types': types, }; let currency = undefined; if (code !== undefined) { currency = this.currency (code); request['currencies'] = currency['id']; } if (since !== undefined) { request['from'] = this.iso8601 (since); } if (limit !== undefined) { request['limit'] = limit; } const response = await this.privateGetWalletTransactions (this.extend (request, params)); // // [ // { // "id": "101609495", // "created_at": "2018-03-06T22:05:06.507Z", // "updated_at": "2018-03-06T22:11:45.03Z", // "status": "SUCCESS", // "type": "DEPOSIT", // "subtype": "BLOCKCHAIN", // "native": { // "tx_id": "e20b0965-4024-44d0-b63f-7fb8996a6706", // "index": "881652766", // "currency": "ETH", // "amount": "0.01418088", // "hash": "d95dbbff3f9234114f1211ab0ba2a94f03f394866fd5749d74a1edab80e6c5d3", // "address": "0xd9259302c32c0a0295d86a39185c9e14f6ba0a0d", // "confirmations": "20", // "senders": [ // "0x243bec9256c9a3469da22103891465b47583d9f1" // ] // } // } // ] // return this.parseTransactions (response, currency, since, limit, params); } parseTransactionStatus (status) { const statuses = { 'PENDING': 'pending', 'FAILED': 'failed', 'SUCCESS': 'ok', }; return this.safeString (statuses, status, status); } parseTransactionType (type) { const types = { 'DEPOSIT': 'deposit', 'WITHDRAW': 'withdrawal', }; return this.safeString (types, type, type); } parseTransaction (transaction, currency = undefined) { // // transaction // // { // "id": "101609495", // "created_at": "2018-03-06T22:05:06.507Z", // "updated_at": "2018-03-06T22:11:45.03Z", // "status": "SUCCESS", // "type": "DEPOSIT", // DEPOSIT, WITHDRAW, .. // "subtype": "BLOCKCHAIN", // "native": { // "tx_id": "e20b0965-4024-44d0-b63f-7fb8996a6706", // "index": "881652766", // "currency": "ETH", // "amount": "0.01418088", // "hash": "d95dbbff3f9234114f1211ab0ba2a94f03f394866fd5749d74a1edab80e6c5d3", // "address": "0xd9259302c32c0a0295d86a39185c9e14f6ba0a0d", // "confirmations": "20", // "senders": [ // "0x243bec9256c9a3469da22103891465b47583d9f1" // ], // "fee": "1.22" // only for WITHDRAW // } // } // // withdraw // // { // "id":"084cfcd5-06b9-4826-882e-fdb75ec3625d" // } // const id = this.safeString (transaction, 'id'); const timestamp = this.parse8601 (this.safeString (transaction, 'created_at')); const updated = this.parse8601 (this.safeString (transaction, 'updated_at')); const type = this.parseTransactionType (this.safeString (transaction, 'type')); const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); const native = this.safeValue (transaction, 'native', {}); const currencyId = this.safeString (native, 'currency'); const code = this.safeCurrencyCode (currencyId); const txhash = this.safeString (native, 'hash'); const address = this.safeString (native, 'address'); const addressTo = address; const tag = this.safeString (native, 'payment_id'); const tagTo = tag; const sender = this.safeValue (native, 'senders'); const addressFrom = this.safeString (sender, 0); const amount = this.safeNumber (native, 'amount'); let fee = undefined; const feeCost = this.safeNumber (native, 'fee'); if (feeCost !== undefined) { fee = { 'currency': code, 'cost': feeCost, }; } return { 'info': transaction, 'id': id, 'txid': txhash, 'code': code, // kept here for backward-compatibility, but will be removed soon 'currency': code, 'amount': amount, 'network': undefined, 'address': address, 'addressFrom': addressFrom, 'addressTo': addressTo, 'tag': tag, 'tagFrom': undefined, 'tagTo': tagTo, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'updated': updated, 'status': status, 'type': type, 'fee': fee, }; } async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { /** * @method * @name hitbtc3#fetchTransactions * @description fetch history of deposits and withdrawals * @param {string|undefined} code unified currency code for the currency of the transactions, default is undefined * @param {int|undefined} since timestamp in ms of the earliest transaction, default is undefined * @param {int|undefined} limit max number of transactions to return, default is undefined * @param {object} params extra parameters specific to the hitbtc3 api endpoint * @returns {object} a list of [transaction structure]{@link https://docs.ccxt.com/en/latest/manual.html#transaction-structure} */ return await this.fetchTransactionsHelper ('DEPOSIT,WITHDRAW', code, since, limit, params); } async fetchDeposits (