UNPKG

@proton/ccxt

Version:

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

1,124 lines (1,122 loc) 120 kB
// ---------------------------------------------------------------------------- // PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN: // https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code // EDIT THE CORRESPONDENT .ts FILE INSTEAD // --------------------------------------------------------------------------- import Exchange from './abstract/bitmex.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'; import { sha256 } from './static_dependencies/noble-hashes/sha256.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, 'certified': true, '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, 'fetchCurrencies': true, 'fetchDepositAddress': true, 'fetchDepositAddresses': false, 'fetchDepositAddressesByNetwork': false, 'fetchDepositsWithdrawals': 'emulated', 'fetchDepositWithdrawalFee': 'emulated', 'fetchDepositWithdrawalFees': true, '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, 'chat': 5, 'chat/channels': 5, 'chat/connected': 5, 'chat/pinned': 5, 'funding': 5, 'instrument': 5, 'instrument/active': 5, 'instrument/activeAndIndices': 5, 'instrument/activeIntervals': 5, 'instrument/compositeIndex': 5, 'instrument/indices': 5, 'instrument/usdVolume': 5, 'insurance': 5, 'leaderboard': 5, 'liquidation': 5, 'orderBook/L2': 5, 'porl/nonce': 5, 'quote': 5, 'quote/bucketed': 5, 'schema': 5, 'schema/websocketHelp': 5, 'settlement': 5, 'stats': 5, 'stats/history': 5, 'stats/historyUSD': 5, 'trade': 5, 'trade/bucketed': 5, 'wallet/assets': 5, 'wallet/networks': 5, }, }, 'private': { 'get': { 'apiKey': 5, 'execution': 5, 'execution/tradeHistory': 5, 'globalNotification': 5, 'leaderboard/name': 5, 'order': 5, 'porl/snapshots': 5, 'position': 5, 'user': 5, 'user/affiliateStatus': 5, 'user/checkReferralCode': 5, 'user/commission': 5, 'user/depositAddress': 5, 'user/executionHistory': 5, 'user/margin': 5, 'user/quoteFillRatio': 5, 'user/quoteValueRatio': 5, 'user/tradingVolume': 5, 'user/wallet': 5, 'user/walletHistory': 5, 'user/walletSummary': 5, 'userEvent': 5, }, 'post': { 'chat': 5, 'guild/join': 5, 'guild/leave': 5, 'order': 1, 'order/cancelAllAfter': 5, 'order/closePosition': 5, 'position/isolate': 1, 'position/leverage': 1, 'position/riskLimit': 5, 'position/transferMargin': 1, 'user/cancelWithdrawal': 5, 'user/communicationToken': 5, 'user/confirmEmail': 5, 'user/confirmWithdrawal': 5, 'user/logout': 5, 'user/preferences': 5, 'user/requestWithdrawal': 5, }, 'put': { 'order': 1, }, 'delete': { '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, 'oldPrecision': false, 'networks': { 'BTC': 'btc', 'ETH': 'eth', 'ERC20': 'eth', 'BEP20': 'bsc', 'BSC': 'bsc', 'TRC20': 'tron', 'TRON': 'tron', 'TRX': 'tron', 'AVALANCHEC': 'avax', 'NEAR': 'near', 'TEZOS': 'xtz', 'XTZ': 'xtz', 'POLKADOT': 'dot', 'DOT': 'dot', 'SOLANA': 'sol', 'SOL': 'sol', 'CARDANO': 'ada', }, 'networksById': { 'btc': 'BTC', 'eth': 'ERC20', 'bsc': 'BEP20', 'tron': 'TRC20', 'avax': 'AVALANCHEC', 'near': 'NEAR', 'xtz': 'TEZOS', 'dot': 'POLKADOT', 'sol': 'SOLANA', 'ada': 'CARDANO', }, }, 'commonCurrencies': { 'USDt': 'USDT', 'XBt': 'BTC', 'XBT': 'BTC', 'Gwei': 'ETH', 'GWEI': 'ETH', 'LAMP': 'SOL', 'LAMp': 'SOL', }, }); } async fetchCurrencies(params = {}) { /** * @method * @name bitmex#fetchCurrencies * @description fetches all available currencies on an exchange * @param {object} params extra parameters specific to the mexc3 api endpoint * @returns {object} an associative dictionary of currencies */ const response = await this.publicGetWalletAssets(params); // // { // "XBt": { // "asset": "XBT", // "currency": "XBt", // "majorCurrency": "XBT", // "name": "Bitcoin", // "currencyType": "Crypto", // "scale": "8", // // "mediumPrecision": "8", // // "shorterPrecision": "4", // // "symbol": "₿", // // "weight": "1", // // "tickLog": "0", // "enabled": true, // "isMarginCurrency": true, // "minDepositAmount": "10000", // "minWithdrawalAmount": "1000", // "maxWithdrawalAmount": "100000000000000", // "networks": [ // { // "asset": "btc", // "tokenAddress": "", // "depositEnabled": true, // "withdrawalEnabled": true, // "withdrawalFee": "20000", // "minFee": "20000", // "maxFee": "10000000" // } // ] // }, // } // const result = {}; for (let i = 0; i < response.length; i++) { const currency = response[i]; const asset = this.safeString(currency, 'asset'); const code = this.safeCurrencyCode(asset); const id = this.safeString(currency, 'currency'); const name = this.safeString(currency, 'name'); const chains = this.safeValue(currency, 'networks', []); let depositEnabled = false; let withdrawEnabled = false; const networks = {}; const scale = this.safeString(currency, 'scale'); const precisionString = this.parsePrecision(scale); const precision = this.parseNumber(precisionString); for (let j = 0; j < chains.length; j++) { const chain = chains[j]; const networkId = this.safeString(chain, 'asset'); const network = this.networkIdToCode(networkId); const withdrawalFeeRaw = this.safeString(chain, 'withdrawalFee'); const withdrawalFee = this.parseNumber(Precise.stringMul(withdrawalFeeRaw, precisionString)); const isDepositEnabled = this.safeValue(chain, 'depositEnabled', false); const isWithdrawEnabled = this.safeValue(chain, 'withdrawalEnabled', false); const active = (isDepositEnabled && isWithdrawEnabled); if (isDepositEnabled) { depositEnabled = true; } if (isWithdrawEnabled) { withdrawEnabled = true; } networks[network] = { 'info': chain, 'id': networkId, 'network': network, 'active': active, 'deposit': isDepositEnabled, 'withdraw': isWithdrawEnabled, 'fee': withdrawalFee, 'precision': undefined, 'limits': { 'withdraw': { 'min': undefined, 'max': undefined, }, 'deposit': { 'min': undefined, 'max': undefined, }, }, }; } const currencyEnabled = this.safeValue(currency, 'enabled'); const currencyActive = currencyEnabled || (depositEnabled || withdrawEnabled); const minWithdrawalString = this.safeString(currency, 'minWithdrawalAmount'); const minWithdrawal = this.parseNumber(Precise.stringMul(minWithdrawalString, precisionString)); const maxWithdrawalString = this.safeString(currency, 'maxWithdrawalAmount'); const maxWithdrawal = this.parseNumber(Precise.stringMul(maxWithdrawalString, precisionString)); const minDepositString = this.safeString(currency, 'minDepositAmount'); const minDeposit = this.parseNumber(Precise.stringMul(minDepositString, precisionString)); result[code] = { 'id': id, 'code': code, 'info': currency, 'name': name, 'active': currencyActive, 'deposit': depositEnabled, 'withdraw': withdrawEnabled, 'fee': undefined, 'precision': precision, 'limits': { 'amount': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': minWithdrawal, 'max': maxWithdrawal, }, 'deposit': { 'min': minDeposit, 'max': undefined, }, }, 'networks': networks, }; } return result; } convertFromRealAmount(code, amount) { const currency = this.currency(code); const precision = this.safeString(currency, 'precision'); const amountString = this.numberToString(amount); const finalAmount = Precise.stringDiv(amountString, precision); return this.parseNumber(finalAmount); } convertToRealAmount(code, amount) { const currency = this.currency(code); const precision = this.safeString(currency, 'precision'); const amountString = this.numberToString(amount); const finalAmount = Precise.stringMul(amountString, precision); return this.parseNumber(finalAmount); } amountToPrecision(symbol, amount) { symbol = this.safeSymbol(symbol); const market = this.market(symbol); const oldPrecision = this.safeValue(this.options, 'oldPrecision'); if (market['spot'] && !oldPrecision) { amount = this.convertFromRealAmount(market['base'], amount); } return super.amountToPrecision(symbol, amount); } convertFromRawQuantity(symbol, rawQuantity, currencySide = 'base') { if (this.safeValue(this.options, 'oldPrecision')) { return this.parseNumber(rawQuantity); } symbol = this.safeSymbol(symbol); const marketExists = this.inArray(symbol, this.symbols); if (!marketExists) { return this.parseNumber(rawQuantity); } const market = this.market(symbol); if (market['spot']) { return this.convertToRealAmount(market[currencySide], rawQuantity); } return this.parseNumber(rawQuantity); } convertFromRawCost(symbol, rawQuantity) { return this.convertFromRawQuantity(symbol, rawQuantity, 'quote'); } 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", // can be empty for spot markets // "underlying": "LTC", // "quoteCurrency": "USDT", // "underlyingSymbol": "LTCT=", // can be empty for spot markets // "reference": "BMEX", // "referenceSymbol": ".BLTCT", // can be empty for spot markets // "calcInterval": null, // "publishInterval": null, // "publishTime": null, // "maxOrderQty": 1000000000, // "maxPrice": 1000000, // "lotSize": 1000, // "tickSize": 0.01, // "multiplier": 100, // "settlCurrency": "USDt", // can be empty for spot markets // "underlyingToPositionMultiplier": 10000, // "underlyingToSettleMultiplier": null, // "quoteToSettleMultiplier": 1000000, // "isQuanto": false, // "isInverse": false, // "initMargin": 0.03, // "maintMargin": 0.015, // "riskLimit": 1000000000000, // can be null for spot markets // "riskStep": 1000000000000, // can be null for spot markets // "limit": null, // "capped": false, // "taxed": true, // "deleverage": true, // "makerFee": -0.0001, // "takerFee": 0.0005, // "settlementFee": 0, // "insuranceFee": 0, // "fundingBaseSymbol": ".LTCBON8H", // can be empty for spot markets // "fundingQuoteSymbol": ".USDTBON8H", // can be empty for spot markets // "fundingPremiumSymbol": ".LTCUSDTPI8H", // can be empty for spot markets // "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); // 'positionCurrency' may be empty ("", as Bitmex currently returns for ETHUSD) // so let's take the settlCurrency first and then adjust if needed const typ = this.safeString(market, 'typ'); // type definitions at: https://www.bitmex.com/api/explorer/#!/Instrument/Instrument_get const types = { 'FFWCSX': 'swap', 'FFWCSF': 'swap', 'IFXXXP': 'spot', 'FFCCSX': 'future', 'MRBXXX': 'index', 'MRCXXX': 'index', 'MRFXXX': 'index', 'MRRXXX': 'index', 'MRIXXX': 'index', }; const type = this.safeString(types, typ, typ); const swap = type === 'swap'; const future = type === 'future'; const spot = type === 'spot'; const contract = swap || future; let contractSize = undefined; const index = type === 'index'; const isInverse = this.safeValue(market, 'isInverse'); // this is true when BASE and SETTLE are same, i.e. BTC/XXX:BTC const isQuanto = this.safeValue(market, 'isQuanto'); // this is true when BASE and SETTLE are different, i.e. AXS/XXX:BTC const linear = contract ? (!isInverse && !isQuanto) : undefined; const status = this.safeString(market, 'state'); const active = status !== 'Unlisted'; let expiry = undefined; let expiryDatetime = undefined; let symbol = undefined; if (spot) { symbol = base + '/' + quote; } else if (contract) { symbol = base + '/' + quote + ':' + settle; const multiplierString = Precise.stringAbs(this.safeString(market, 'multiplier')); if (linear) { contractSize = this.parseNumber(Precise.stringDiv('1', market['underlyingToPositionMultiplier'])); } else { contractSize = this.parseNumber(multiplierString); } if (future) { expiryDatetime = this.safeString(market, 'expiry'); expiry = this.parse8601(expiryDatetime); symbol = symbol + '-' + this.yymmdd(expiry); } } else { // for index/exotic markets, default to id symbol = id; } const positionId = this.safeString2(market, 'positionCurrency', 'underlying'); const position = this.safeCurrencyCode(positionId); const positionIsQuote = (position === quote); const maxOrderQty = this.safeNumber(market, 'maxOrderQty'); const initMargin = this.safeString(market, 'initMargin', '1'); const maxLeverage = this.parseNumber(Precise.stringDiv('1', initMargin)); result.push({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'settle': settle, 'baseId': baseId, 'quoteId': quoteId, 'settleId': settleId, 'type': type, 'spot': spot, 'margin': false, 'swap': swap, 'future': future, 'option': false, 'index': index, 'active': active, 'contract': contract, 'linear': linear, 'inverse': isInverse, 'quanto': isQuanto, 'taker': this.safeNumber(market, 'takerFee'), 'maker': this.safeNumber(market, 'makerFee'), 'contractSize': contractSize, '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(); const free = this.safeString(balance, 'availableMargin'); const total = this.safeString(balance, 'marginBalance'); account['free'] = this.convertToRealAmount(code, free); account['total'] = this.convertToRealAmount(code, 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.convertFromRawQuantity(symbol, this.safeString(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); const amountString = this.safeString(item, 'amount'); let amount = this.convertToRealAmount(code, amountString); 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 = this.convertToRealAmount(code, feeCost); } const fee = { 'cost': feeCost, 'currency': code, }; let after = this.safeNumber(item, 'walletBalance'); if (after !== undefined) { after = this.convertToRealAmount(code, after); } const before = this.parseNumber(Precise.stringSub(this.numberToString(after), this.numberToString(amount))); let direction = undefined; if (Precise.stringLt(amountString, '0')) { direction = 'out'; amount = this.convertToRealAmount(code, Precise.stringAbs(amountString)); } 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': referenceI