UNPKG

remsed

Version:

A JavaScript cryptocurrency trading library with support for fairdesk.com

1,144 lines (1,142 loc) 126 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 './base/Exchange.js'; import { TICK_SIZE } from './base/functions/number.js'; import { AuthenticationError, BadRequest, DDoSProtection, ExchangeError, ExchangeNotAvailable, InsufficientFunds, InvalidOrder, OrderNotFound, PermissionDenied, ArgumentsRequired, BadSymbol } from './base/errors.js'; import { Precise } from './base/Precise.js'; // --------------------------------------------------------------------------- export default class bitmex extends Exchange { describe() { return this.deepExtend(super.describe(), { 'id': 'bitmex', 'name': 'BitMEX', 'countries': ['SC'], 'version': 'v1', 'userAgent': undefined, // cheapest endpoints are 10 requests per second (trading) // 10 per second => rateLimit = 1000ms / 10 = 100ms // 120 per minute => 2 per second => weight = 5 (authenticated) // 30 per minute => 0.5 per second => weight = 20 (unauthenticated) 'rateLimit': 100, 'pro': true, 'has': { 'CORS': undefined, 'spot': true, 'margin': false, 'swap': true, 'future': true, 'option': false, 'addMargin': undefined, 'cancelAllOrders': true, 'cancelOrder': true, 'cancelOrders': true, 'createOrder': true, 'createReduceOnlyOrder': true, 'editOrder': true, 'fetchBalance': true, 'fetchClosedOrders': true, 'fetchDepositAddress': true, 'fetchDepositAddresses': false, 'fetchDepositAddressesByNetwork': false, 'fetchFundingHistory': false, 'fetchFundingRate': false, 'fetchFundingRateHistory': true, 'fetchFundingRates': true, 'fetchIndexOHLCV': false, 'fetchLedger': true, 'fetchLeverage': false, 'fetchLeverageTiers': false, 'fetchMarketLeverageTiers': false, 'fetchMarkets': true, 'fetchMarkOHLCV': false, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrders': true, 'fetchPosition': false, 'fetchPositions': true, 'fetchPositionsRisk': false, 'fetchPremiumIndexOHLCV': false, 'fetchTicker': true, 'fetchTickers': true, 'fetchTrades': true, 'fetchTransactions': 'emulated', 'fetchTransfer': false, 'fetchTransfers': false, 'reduceMargin': undefined, 'setLeverage': true, 'setMargin': undefined, 'setMarginMode': true, 'setPositionMode': false, 'transfer': false, 'withdraw': true, }, 'timeframes': { '1m': '1m', '5m': '5m', '1h': '1h', '1d': '1d', }, 'urls': { 'test': { 'public': 'https://testnet.bitmex.com', 'private': 'https://testnet.bitmex.com', }, 'logo': 'https://user-images.githubusercontent.com/1294454/27766319-f653c6e6-5ed4-11e7-933d-f0bc3699ae8f.jpg', 'api': { 'public': 'https://www.bitmex.com', 'private': 'https://www.bitmex.com', }, 'www': 'https://www.bitmex.com', 'doc': [ 'https://www.bitmex.com/app/apiOverview', 'https://github.com/BitMEX/api-connectors/tree/master/official-http', ], 'fees': 'https://www.bitmex.com/app/fees', 'referral': 'https://www.bitmex.com/register/upZpOX', }, 'api': { 'public': { 'get': { 'announcement': 5, 'announcement/urgent': 5, 'funding': 5, 'instrument': 5, 'instrument/active': 5, 'instrument/activeAndIndices': 5, 'instrument/activeIntervals': 5, 'instrument/compositeIndex': 5, 'instrument/indices': 5, 'insurance': 5, 'leaderboard': 5, 'liquidation': 5, 'orderBook': 5, 'orderBook/L2': 5, 'quote': 5, 'quote/bucketed': 5, 'schema': 5, 'schema/websocketHelp': 5, 'settlement': 5, 'stats': 5, 'stats/history': 5, 'trade': 5, 'trade/bucketed': 5, 'wallet/assets': 5, 'wallet/networks': 5, }, }, 'private': { 'get': { 'apiKey': 5, 'chat': 5, 'chat/channels': 5, 'chat/connected': 5, 'execution': 5, 'execution/tradeHistory': 5, 'notification': 5, 'order': 5, 'position': 5, 'user': 5, 'user/affiliateStatus': 5, 'user/checkReferralCode': 5, 'user/commission': 5, 'user/depositAddress': 5, 'user/executionHistory': 5, 'user/margin': 5, 'user/minWithdrawalFee': 5, 'user/wallet': 5, 'user/walletHistory': 5, 'user/walletSummary': 5, 'wallet/assets': 5, 'wallet/networks': 5, 'userEvent': 5, }, 'post': { 'apiKey': 5, 'apiKey/disable': 5, 'apiKey/enable': 5, 'chat': 5, 'order': 1, 'order/bulk': 5, 'order/cancelAllAfter': 5, 'order/closePosition': 5, 'position/isolate': 1, 'position/leverage': 1, 'position/riskLimit': 5, 'position/transferMargin': 1, 'user/cancelWithdrawal': 5, 'user/confirmEmail': 5, 'user/confirmEnableTFA': 5, 'user/confirmWithdrawal': 5, 'user/disableTFA': 5, 'user/logout': 5, 'user/logoutAll': 5, 'user/preferences': 5, 'user/requestEnableTFA': 5, 'user/requestWithdrawal': 5, }, 'put': { 'order': 1, 'order/bulk': 5, 'user': 5, }, 'delete': { 'apiKey': 5, 'order': 1, 'order/all': 1, }, }, }, 'exceptions': { 'exact': { 'Invalid API Key.': AuthenticationError, 'This key is disabled.': PermissionDenied, 'Access Denied': PermissionDenied, 'Duplicate clOrdID': InvalidOrder, 'orderQty is invalid': InvalidOrder, 'Invalid price': InvalidOrder, 'Invalid stopPx for ordType': InvalidOrder, }, 'broad': { 'Signature not valid': AuthenticationError, 'overloaded': ExchangeNotAvailable, 'Account has insufficient Available Balance': InsufficientFunds, 'Service unavailable': ExchangeNotAvailable, 'Server Error': ExchangeError, 'Unable to cancel order due to existing state': InvalidOrder, 'We require all new traders to verify': PermissionDenied, // {"message":"We require all new traders to verify their identity before their first deposit. Please visit bitmex.com/verify to complete the process.","name":"HTTPError"} }, }, 'precisionMode': TICK_SIZE, 'options': { // https://blog.bitmex.com/api_announcement/deprecation-of-api-nonce-header/ // https://github.com/ccxt/ccxt/issues/4789 'api-expires': 5, 'fetchOHLCVOpenTimestamp': true, 'networks': { 'BTC': 'btc', 'ETH': 'eth', 'BSC': 'bsc', 'BNB': 'bsc', 'TRON': 'tron', 'ERC20': 'eth', 'BEP20': 'bsc', 'TRC20': 'tron', 'TRX': 'tron', 'AVAX': 'avax', 'NEAR': 'near', 'XTZ': 'xtz', 'DOT': 'dot', 'SOL': 'sol', }, 'networksById': { 'btc': 'BTC', 'eth': 'ERC20', 'bsc': 'BSC', 'tron': 'TRX', 'avax': 'AVAX', 'near': 'NEAR', 'xtz': 'XTZ', 'dot': 'DOT', 'sol': 'SOL', }, }, 'commonCurrencies': { 'USDt': 'USDT', 'XBt': 'BTC', 'XBT': 'BTC', 'Gwei': 'ETH', 'GWEI': 'ETH', 'LAMP': 'SOL', 'LAMp': 'SOL', }, }); } async fetchMarkets(params = {}) { /** * @method * @name bitmex#fetchMarkets * @description retrieves data on all markets for bitmex * @param {object} params extra parameters specific to the exchange api endpoint * @returns {[object]} an array of objects representing market data */ const response = await this.publicGetInstrumentActiveAndIndices(params); // // { // "symbol": "LTCUSDT", // "rootSymbol": "LTC", // "state": "Open", // "typ": "FFWCSX", // "listing": "2021-11-10T04:00:00.000Z", // "front": "2021-11-10T04:00:00.000Z", // "expiry": null, // "settle": null, // "listedSettle": null, // "relistInterval": null, // "inverseLeg": "", // "sellLeg": "", // "buyLeg": "", // "optionStrikePcnt": null, // "optionStrikeRound": null, // "optionStrikePrice": null, // "optionMultiplier": null, // "positionCurrency": "LTC", // "underlying": "LTC", // "quoteCurrency": "USDT", // "underlyingSymbol": "LTCT=", // "reference": "BMEX", // "referenceSymbol": ".BLTCT", // "calcInterval": null, // "publishInterval": null, // "publishTime": null, // "maxOrderQty": 1000000000, // "maxPrice": 1000000, // "lotSize": 1000, // "tickSize": 0.01, // "multiplier": 100, // "settlCurrency": "USDt", // "underlyingToPositionMultiplier": 10000, // "underlyingToSettleMultiplier": null, // "quoteToSettleMultiplier": 1000000, // "isQuanto": false, // "isInverse": false, // "initMargin": 0.03, // "maintMargin": 0.015, // "riskLimit": 1000000000000, // "riskStep": 1000000000000, // "limit": null, // "capped": false, // "taxed": true, // "deleverage": true, // "makerFee": -0.0001, // "takerFee": 0.0005, // "settlementFee": 0, // "insuranceFee": 0, // "fundingBaseSymbol": ".LTCBON8H", // "fundingQuoteSymbol": ".USDTBON8H", // "fundingPremiumSymbol": ".LTCUSDTPI8H", // "fundingTimestamp": "2022-01-14T20:00:00.000Z", // "fundingInterval": "2000-01-01T08:00:00.000Z", // "fundingRate": 0.0001, // "indicativeFundingRate": 0.0001, // "rebalanceTimestamp": null, // "rebalanceInterval": null, // "openingTimestamp": "2022-01-14T17:00:00.000Z", // "closingTimestamp": "2022-01-14T18:00:00.000Z", // "sessionInterval": "2000-01-01T01:00:00.000Z", // "prevClosePrice": 138.511, // "limitDownPrice": null, // "limitUpPrice": null, // "bankruptLimitDownPrice": null, // "bankruptLimitUpPrice": null, // "prevTotalVolume": 12699024000, // "totalVolume": 12702160000, // "volume": 3136000, // "volume24h": 114251000, // "prevTotalTurnover": 232418052349000, // "totalTurnover": 232463353260000, // "turnover": 45300911000, // "turnover24h": 1604331340000, // "homeNotional24h": 11425.1, // "foreignNotional24h": 1604331.3400000003, // "prevPrice24h": 135.48, // "vwap": 140.42165, // "highPrice": 146.42, // "lowPrice": 135.08, // "lastPrice": 144.36, // "lastPriceProtected": 144.36, // "lastTickDirection": "MinusTick", // "lastChangePcnt": 0.0655, // "bidPrice": 143.75, // "midPrice": 143.855, // "askPrice": 143.96, // "impactBidPrice": 143.75, // "impactMidPrice": 143.855, // "impactAskPrice": 143.96, // "hasLiquidity": true, // "openInterest": 38103000, // "openValue": 547963053300, // "fairMethod": "FundingRate", // "fairBasisRate": 0.1095, // "fairBasis": 0.004, // "fairPrice": 143.811, // "markMethod": "FairPrice", // "markPrice": 143.811, // "indicativeTaxRate": null, // "indicativeSettlePrice": 143.807, // "optionUnderlyingPrice": null, // "settledPriceAdjustmentRate": null, // "settledPrice": null, // "timestamp": "2022-01-14T17:49:55.000Z" // } // const result = []; for (let i = 0; i < response.length; i++) { const market = response[i]; const id = this.safeString(market, 'symbol'); const baseId = this.safeString(market, 'underlying'); const quoteId = this.safeString(market, 'quoteCurrency'); const settleId = this.safeString(market, 'settlCurrency', ''); const base = this.safeCurrencyCode(baseId); const quote = this.safeCurrencyCode(quoteId); const settle = this.safeCurrencyCode(settleId); const basequote = baseId + quoteId; const swap = (id === basequote); // 'positionCurrency' may be empty ("", as Bitmex currently returns for ETHUSD) // so let's take the settlCurrency first and then adjust if needed let type = undefined; let future = false; let prediction = false; let index = false; let symbol = base + '/' + quote + ':' + settle; const expiryDatetime = this.safeString(market, 'expiry'); const expiry = this.parse8601(expiryDatetime); const inverse = this.safeValue(market, 'isInverse'); const status = this.safeString(market, 'state'); let active = status !== 'Unlisted'; if (swap) { type = 'swap'; } else if (id.indexOf('B_') >= 0) { prediction = true; type = 'prediction'; symbol = id; } else if (expiry !== undefined) { future = true; type = 'future'; symbol = symbol + '-' + this.yymmdd(expiry); } else { index = true; type = 'index'; symbol = id; active = false; } const positionId = this.safeString2(market, 'positionCurrency', 'underlying'); const position = this.safeCurrencyCode(positionId); const positionIsQuote = (position === quote); const maxOrderQty = this.safeNumber(market, 'maxOrderQty'); const contract = !index; const initMargin = this.safeString(market, 'initMargin', '1'); const maxLeverage = this.parseNumber(Precise.stringDiv('1', initMargin)); const multiplierString = Precise.stringAbs(this.safeString(market, 'multiplier')); result.push({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'settle': settle, 'baseId': baseId, 'quoteId': quoteId, 'settleId': settleId, 'type': type, 'spot': false, 'margin': false, 'swap': swap, 'future': future, 'option': false, 'prediction': prediction, 'index': index, 'active': active, 'contract': contract, 'linear': contract ? !inverse : undefined, 'inverse': contract ? inverse : undefined, 'taker': this.safeNumber(market, 'takerFee'), 'maker': this.safeNumber(market, 'makerFee'), 'contractSize': this.parseNumber(multiplierString), 'expiry': expiry, 'expiryDatetime': expiryDatetime, 'strike': this.safeNumber(market, 'optionStrikePrice'), 'optionType': undefined, 'precision': { 'amount': this.safeNumber(market, 'lotSize'), 'price': this.safeNumber(market, 'tickSize'), 'quote': this.safeNumber(market, 'tickSize'), 'base': this.safeNumber(market, 'tickSize'), }, 'limits': { 'leverage': { 'min': contract ? this.parseNumber('1') : undefined, 'max': contract ? maxLeverage : undefined, }, 'amount': { 'min': undefined, 'max': positionIsQuote ? undefined : maxOrderQty, }, 'price': { 'min': undefined, 'max': this.safeNumber(market, 'maxPrice'), }, 'cost': { 'min': undefined, 'max': positionIsQuote ? maxOrderQty : undefined, }, }, 'info': market, }); } return result; } parseBalance(response) { // // [ // { // "account":1455728, // "currency":"XBt", // "riskLimit":1000000000000, // "prevState":"", // "state":"", // "action":"", // "amount":263542, // "pendingCredit":0, // "pendingDebit":0, // "confirmedDebit":0, // "prevRealisedPnl":0, // "prevUnrealisedPnl":0, // "grossComm":0, // "grossOpenCost":0, // "grossOpenPremium":0, // "grossExecCost":0, // "grossMarkValue":0, // "riskValue":0, // "taxableMargin":0, // "initMargin":0, // "maintMargin":0, // "sessionMargin":0, // "targetExcessMargin":0, // "varMargin":0, // "realisedPnl":0, // "unrealisedPnl":0, // "indicativeTax":0, // "unrealisedProfit":0, // "syntheticMargin":null, // "walletBalance":263542, // "marginBalance":263542, // "marginBalancePcnt":1, // "marginLeverage":0, // "marginUsedPcnt":0, // "excessMargin":263542, // "excessMarginPcnt":1, // "availableMargin":263542, // "withdrawableMargin":263542, // "timestamp":"2020-08-03T12:01:01.246Z", // "grossLastValue":0, // "commission":null // } // ] // const result = { 'info': response }; for (let i = 0; i < response.length; i++) { const balance = response[i]; const currencyId = this.safeString(balance, 'currency'); const code = this.safeCurrencyCode(currencyId); const account = this.account(); let free = this.safeString(balance, 'availableMargin'); let total = this.safeString(balance, 'marginBalance'); if (code !== 'USDT') { // tmp fix until this PR gets merged // https://github.com/ccxt/ccxt/pull/15311 const symbol = code + '_USDT'; const market = this.safeMarket(symbol); const info = this.safeValue(market, 'info', {}); const multiplier = this.safeString(info, 'underlyingToPositionMultiplier'); if (multiplier !== undefined) { free = Precise.stringDiv(free, multiplier); total = Precise.stringDiv(total, multiplier); } else { free = Precise.stringDiv(free, '1e8'); total = Precise.stringDiv(total, '1e8'); } } else { free = Precise.stringDiv(free, '1e6'); total = Precise.stringDiv(total, '1e6'); } account['free'] = free; account['total'] = total; result[code] = account; } return this.safeBalance(result); } async fetchBalance(params = {}) { /** * @method * @name bitmex#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 bitmex api endpoint * @returns {object} a [balance structure]{@link https://docs.ccxt.com/en/latest/manual.html?#balance-structure} */ await this.loadMarkets(); const request = { 'currency': 'all', }; const response = await this.privateGetUserMargin(this.extend(request, params)); // // [ // { // "account":1455728, // "currency":"XBt", // "riskLimit":1000000000000, // "prevState":"", // "state":"", // "action":"", // "amount":263542, // "pendingCredit":0, // "pendingDebit":0, // "confirmedDebit":0, // "prevRealisedPnl":0, // "prevUnrealisedPnl":0, // "grossComm":0, // "grossOpenCost":0, // "grossOpenPremium":0, // "grossExecCost":0, // "grossMarkValue":0, // "riskValue":0, // "taxableMargin":0, // "initMargin":0, // "maintMargin":0, // "sessionMargin":0, // "targetExcessMargin":0, // "varMargin":0, // "realisedPnl":0, // "unrealisedPnl":0, // "indicativeTax":0, // "unrealisedProfit":0, // "syntheticMargin":null, // "walletBalance":263542, // "marginBalance":263542, // "marginBalancePcnt":1, // "marginLeverage":0, // "marginUsedPcnt":0, // "excessMargin":263542, // "excessMarginPcnt":1, // "availableMargin":263542, // "withdrawableMargin":263542, // "timestamp":"2020-08-03T12:01:01.246Z", // "grossLastValue":0, // "commission":null // } // ] // return this.parseBalance(response); } async fetchOrderBook(symbol, limit = undefined, params = {}) { /** * @method * @name bitmex#fetchOrderBook * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data * @param {string} symbol unified symbol of the market to fetch the order book for * @param {int|undefined} limit the maximum amount of order book entries to return * @param {object} params extra parameters specific to the bitmex api endpoint * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols */ await this.loadMarkets(); const market = this.market(symbol); const request = { 'symbol': market['id'], }; if (limit !== undefined) { request['depth'] = limit; } const response = await this.publicGetOrderBookL2(this.extend(request, params)); const result = { 'symbol': symbol, 'bids': [], 'asks': [], 'timestamp': undefined, 'datetime': undefined, 'nonce': undefined, }; for (let i = 0; i < response.length; i++) { const order = response[i]; const side = (order['side'] === 'Sell') ? 'asks' : 'bids'; const amount = this.safeNumber(order, 'size'); const price = this.safeNumber(order, 'price'); // https://github.com/ccxt/ccxt/issues/4926 // https://github.com/ccxt/ccxt/issues/4927 // the exchange sometimes returns null price in the orderbook if (price !== undefined) { result[side].push([price, amount]); } } result['bids'] = this.sortBy(result['bids'], 0, true); result['asks'] = this.sortBy(result['asks'], 0); return result; } async fetchOrder(id, symbol = undefined, params = {}) { /** * @method * @name bitmex#fetchOrder * @description fetches information on an order made by the user * @param {string|undefined} symbol unified symbol of the market the order was made in * @param {object} params extra parameters specific to the bitmex api endpoint * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ const filter = { 'filter': { 'orderID': id, }, }; const response = await this.fetchOrders(symbol, undefined, undefined, this.deepExtend(filter, params)); const numResults = response.length; if (numResults === 1) { return response[0]; } throw new OrderNotFound(this.id + ': The order ' + id + ' not found.'); } async fetchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { /** * @method * @name bitmex#fetchOrders * @description fetches information on multiple orders made by the user * @param {string|undefined} symbol unified market symbol of the market orders were made in * @param {int|undefined} since the earliest time in ms to fetch orders for * @param {int|undefined} limit the maximum number of orde structures to retrieve * @param {object} params extra parameters specific to the bitmex api endpoint * @returns {[object]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} */ await this.loadMarkets(); let market = undefined; let request = {}; if (symbol !== undefined) { market = this.market(symbol); request['symbol'] = market['id']; } if (since !== undefined) { request['startTime'] = this.iso8601(since); } if (limit !== undefined) { request['count'] = limit; } request = this.deepExtend(request, params); // why the hassle? urlencode in python is kinda broken for nested dicts. // E.g. self.urlencode({"filter": {"open": True}}) will return "filter={'open':+True}" // Bitmex doesn't like that. Hence resorting to this hack. if ('filter' in request) { request['filter'] = this.json(request['filter']); } const response = await this.privateGetOrder(request); return this.parseOrders(response, market, since, limit); } async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { /** * @method * @name bitmex#fetchOpenOrders * @description fetch all unfilled currently open orders * @param {string|undefined} symbol unified market symbol * @param {int|undefined} since the earliest time in ms to fetch open orders for * @param {int|undefined} limit the maximum number of open orders structures to retrieve * @param {object} params extra parameters specific to the bitmex api endpoint * @returns {[object]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} */ const request = { 'filter': { 'open': true, }, }; return await this.fetchOrders(symbol, since, limit, this.deepExtend(request, params)); } async fetchClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { /** * @method * @name bitmex#fetchClosedOrders * @description fetches information on multiple closed orders made by the user * @param {string|undefined} symbol unified market symbol of the market orders were made in * @param {int|undefined} since the earliest time in ms to fetch orders for * @param {int|undefined} limit the maximum number of orde structures to retrieve * @param {object} params extra parameters specific to the bitmex api endpoint * @returns {[object]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} */ // Bitmex barfs if you set 'open': false in the filter... const orders = await this.fetchOrders(symbol, since, limit, params); return this.filterBy(orders, 'status', 'closed'); } async fetchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) { /** * @method * @name bitmex#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 bitmex api endpoint * @returns {[object]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure} */ await this.loadMarkets(); let market = undefined; let request = {}; if (symbol !== undefined) { market = this.market(symbol); request['symbol'] = market['id']; } if (since !== undefined) { request['startTime'] = this.iso8601(since); } if (limit !== undefined) { request['count'] = limit; } request = this.deepExtend(request, params); // why the hassle? urlencode in python is kinda broken for nested dicts. // E.g. self.urlencode({"filter": {"open": True}}) will return "filter={'open':+True}" // Bitmex doesn't like that. Hence resorting to this hack. if ('filter' in request) { request['filter'] = this.json(request['filter']); } const response = await this.privateGetExecutionTradeHistory(request); // // [ // { // "execID": "string", // "orderID": "string", // "clOrdID": "string", // "clOrdLinkID": "string", // "account": 0, // "symbol": "string", // "side": "string", // "lastQty": 0, // "lastPx": 0, // "underlyingLastPx": 0, // "lastMkt": "string", // "lastLiquidityInd": "string", // "simpleOrderQty": 0, // "orderQty": 0, // "price": 0, // "displayQty": 0, // "stopPx": 0, // "pegOffsetValue": 0, // "pegPriceType": "string", // "currency": "string", // "settlCurrency": "string", // "execType": "string", // "ordType": "string", // "timeInForce": "string", // "execInst": "string", // "contingencyType": "string", // "exDestination": "string", // "ordStatus": "string", // "triggered": "string", // "workingIndicator": true, // "ordRejReason": "string", // "simpleLeavesQty": 0, // "leavesQty": 0, // "simpleCumQty": 0, // "cumQty": 0, // "avgPx": 0, // "commission": 0, // "tradePublishIndicator": "string", // "multiLegReportingType": "string", // "text": "string", // "trdMatchID": "string", // "execCost": 0, // "execComm": 0, // "homeNotional": 0, // "foreignNotional": 0, // "transactTime": "2019-03-05T12:47:02.762Z", // "timestamp": "2019-03-05T12:47:02.762Z" // } // ] // return this.parseTrades(response, market, since, limit); } parseLedgerEntryType(type) { const types = { 'Withdrawal': 'transaction', 'RealisedPNL': 'margin', 'UnrealisedPNL': 'margin', 'Deposit': 'transaction', 'Transfer': 'transfer', 'AffiliatePayout': 'referral', }; return this.safeString(types, type, type); } parseLedgerEntry(item, currency = undefined) { // // { // transactID: "69573da3-7744-5467-3207-89fd6efe7a47", // account: 24321, // currency: "XBt", // transactType: "Withdrawal", // "AffiliatePayout", "Transfer", "Deposit", "RealisedPNL", ... // amount: -1000000, // fee: 300000, // transactStatus: "Completed", // "Canceled", ... // address: "1Ex4fkF4NhQaQdRWNoYpqiPbDBbq18Kdd9", // tx: "3BMEX91ZhhKoWtsH9QRb5dNXnmnGpiEetA", // text: "", // transactTime: "2017-03-21T20:05:14.388Z", // walletBalance: 0, // balance after // marginBalance: null, // timestamp: "2017-03-22T13:09:23.514Z" // } // // ButMEX returns the unrealized pnl from the wallet history endpoint. // The unrealized pnl transaction has an empty timestamp. // It is not related to historical pnl it has status set to "Pending". // Therefore it's not a part of the history at all. // https://github.com/ccxt/ccxt/issues/6047 // // { // "transactID":"00000000-0000-0000-0000-000000000000", // "account":121210, // "currency":"XBt", // "transactType":"UnrealisedPNL", // "amount":-5508, // "fee":0, // "transactStatus":"Pending", // "address":"XBTUSD", // "tx":"", // "text":"", // "transactTime":null, # ←---------------------------- null // "walletBalance":139198767, // "marginBalance":139193259, // "timestamp":null # ←---------------------------- null // } // const id = this.safeString(item, 'transactID'); const account = this.safeString(item, 'account'); const referenceId = this.safeString(item, 'tx'); const referenceAccount = undefined; const type = this.parseLedgerEntryType(this.safeString(item, 'transactType')); const currencyId = this.safeString(item, 'currency'); const code = this.safeCurrencyCode(currencyId, currency); let amount = this.safeNumber(item, 'amount'); if (amount !== undefined) { amount = amount / 100000000; } let timestamp = this.parse8601(this.safeString(item, 'transactTime')); if (timestamp === undefined) { // https://github.com/ccxt/ccxt/issues/6047 // set the timestamp to zero, 1970 Jan 1 00:00:00 // for unrealized pnl and other transactions without a timestamp timestamp = 0; // see comments above } let feeCost = this.safeNumber(item, 'fee', 0); if (feeCost !== undefined) { feeCost = feeCost / 100000000; } const fee = { 'cost': feeCost, 'currency': code, }; let after = this.safeNumber(item, 'walletBalance'); if (after !== undefined) { after = after / 100000000; } const before = this.sum(after, -amount); let direction = undefined; if (amount < 0) { direction = 'out'; amount = Math.abs(amount); } else { direction = 'in'; } const status = this.parseTransactionStatus(this.safeString(item, 'transactStatus')); return { 'id': id, 'info': item, 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'direction': direction, 'account': account, 'referenceId': referenceId, 'referenceAccount': referenceAccount, 'type': type, 'currency': code, 'amount': amount, 'before': before, 'after': after, 'status': status, 'fee': fee, }; } async fetchLedger(code = undefined, since = undefined, limit = undefined, params = {}) { /** * @method * @name bitmex#fetchLedger * @description fetch the history of changes, actions done by the user or operations that altered balance of the user * @param {string|undefined} code unified currency code, default is undefined * @param {int|undefined} since timestamp in ms of the earliest ledger entry, default is undefined * @param {int|undefined} limit max number of ledger entrys to return, default is undefined * @param {object} params extra parameters specific to the bitmex api endpoint * @returns {object} a [ledger structure]{@link https://docs.ccxt.com/#/?id=ledger-structure} */ await this.loadMarkets(); let currency = undefined; if (code !== undefined) { currency = this.currency(code); } const request = { // 'start': 123, }; // // if (since !== undefined) { // // date-based pagination not supported // } // if (limit !== undefined) { request['count'] = limit; } const response = await this.privateGetUserWalletHistory(this.extend(request, params)); // // [ // { // transactID: "69573da3-7744-5467-3207-89fd6efe7a47", // account: 24321, // currency: "XBt", // transactType: "Withdrawal", // "AffiliatePayout", "Transfer", "Deposit", "RealisedPNL", ... // amount: -1000000, // fee: 300000, // transactStatus: "Completed", // "Canceled", ... // address: "1Ex4fkF4NhQaQdRWNoYpqiPbDBbq18Kdd9", // tx: "3BMEX91ZhhKoWtsH9QRb5dNXnmnGpiEetA", // text: "", // transactTime: "2017-03-21T20:05:14.388Z", // walletBalance: 0, // balance after // marginBalance: null, // timestamp: "2017-03-22T13:09:23.514Z" // } // ] // return this.parseLedger(response, currency, since, limit); } async fetchTransactions(code = undefined, since = undefined, limit = undefined, params = {}) { /** * @method * @name bitmex#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 bitmex api endpoint * @returns {object} a list of [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} */ await this.loadMarkets(); const request = { 'currency': 'all', // 'start': 123, }; // // if (since !== undefined) { // // date-based pagination not supported // } // if (limit !== undefined) { request['count'] = limit; } const response = await this.privateGetUserWalletHistory(this.extend(request, params)); const transactions = this.filterByArray(response, 'transactType', ['Withdrawal', 'Deposit'], false); let currency = undefined; if (code !== undefined) { currency = this.currency(code); } return this.parseTransactions(transactions, currency, since, limit); } parseTransactionStatus(status) { const statuses = { 'Canceled': 'canceled', 'Completed': 'ok', 'Pending': 'pending', }; return this.safeString(statuses, status, status); } parseTransaction(transaction, currency = undefined) { // // { // 'transactID': 'ffe699c2-95ee-4c13-91f9-0faf41daec25', // 'account': 123456, // 'currency': 'XBt', // 'network':'', // 'transactType': 'Withdrawal', // 'amount': -100100000, // 'fee': 100000, // 'transactStatus': 'Completed', // 'address': '385cR5DM96n1HvBDMzLHPYcw89fZAXULJP', // 'tx': '3BMEXabcdefghijklmnopqrstuvwxyz123', // 'text': '', // 'transactTime': '2019-01-02T01:00:00.000Z', // 'walletBalance': 99900000, // 'marginBalance': None, // 'timestamp': '2019-01-02T13:00:00.000Z' // } // const currencyId = this.safeString(transaction, 'currency'); currency = this.safeCurrency(currencyId, currency); // For deposits, transactTime == timestamp // For withdrawals, transactTime is submission, timestamp is processed const transactTime = this.parse8601(this.safeString(transaction, 'transactTime')); const timestamp = this.parse8601(this.safeString(transaction, 'timestamp')); const type = this.safeStringLower(transaction, 'transactType'); // Deposits have no from address or to address, withdrawals have both let address = undefined; let addressFrom = undefined; let addressTo = undefined; if (type === 'withdrawal') { address = this.safeString(transaction, 'address'); addressFrom = this.safeString(transaction, 'tx'); addressTo = address; } let amountString = this.safeString(transaction, 'amount'); const scale = (currency['code'] === 'BTC') ? '1e8' : '1e6'; amountString = Precise.stringDiv(Precise.stringAbs(amountString), scale); let feeCostString = this.safeString(transaction, 'fee'); feeCostString = Precise.stringDiv(feeCostString, scale); let status = this.safeString(transaction, 'transactStatus'); if (status !== undefined) { status = this.parseTransactionStatus(status); } return { 'info': transaction, 'id': this.safeString(transaction, 'transactID'), 'txid': this.safeString(transaction, 'tx'), 'type': type, 'currency': currency['code'], 'network': this.safeString(transaction, 'status'), 'amount': this.parseNumber(amountString), 'status': status, 'timestamp': transactTime, 'datetime': this.iso8601(transactTime), 'address': address, 'addressFrom': addressFrom, 'addressTo': addressTo, 'tag': undefined, 'tagFrom': undefined, 'tagTo': undefined, 'updated': timestamp, 'comment': undefined, 'fee': { 'currency': currency['code'], 'cost': this.parseNumber(feeCostString), 'rate': undefined, }, }; } async fetchTicker(symbol, params = {}) { /** * @method * @name bitmex#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 bitmex api endpoint * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure} */ await this.loadMarkets(); const market = this.market(symbol); const tickers = await this.fetchTickers([market['symbol']], params); const ticker = this.safeValue(tickers, market['symbol']); if (ticker === undefined) { throw new BadSymbol(this.id + ' fetchTicker() symbol ' + symbol + ' not found'); } return ticker; } async fetchTickers(symbols = undefined, params = {}) { /** * @method * @name bitmex#fetchTickers * @description fetches pr