ccxt
Version:
1,142 lines (1,140 loc) • 139 kB
JavaScript
// ----------------------------------------------------------------------------
// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
// EDIT THE CORRESPONDENT .ts FILE INSTEAD
// ---------------------------------------------------------------------------
import Exchange from './abstract/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';
import { totp } from './base/functions/totp.js';
// ---------------------------------------------------------------------------
/**
* @class bitmex
* @augments Exchange
*/
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,
'cancelAllOrdersAfter': true,
'cancelOrder': true,
'cancelOrders': true,
'closeAllPositions': false,
'closePosition': true,
'createOrder': true,
'createReduceOnlyOrder': true,
'createStopOrder': true,
'createTrailingAmountOrder': true,
'createTriggerOrder': true,
'editOrder': true,
'fetchBalance': true,
'fetchClosedOrders': true,
'fetchCurrencies': true,
'fetchDepositAddress': true,
'fetchDepositAddresses': false,
'fetchDepositAddressesByNetwork': false,
'fetchDepositsWithdrawals': 'emulated',
'fetchDepositWithdrawFee': 'emulated',
'fetchDepositWithdrawFees': true,
'fetchFundingHistory': false,
'fetchFundingRate': 'emulated',
'fetchFundingRateHistory': true,
'fetchFundingRates': true,
'fetchIndexOHLCV': false,
'fetchLedger': true,
'fetchLeverage': 'emulated',
'fetchLeverages': true,
'fetchLeverageTiers': false,
'fetchLiquidations': true,
'fetchMarginAdjustmentHistory': false,
'fetchMarketLeverageTiers': false,
'fetchMarkets': true,
'fetchMarkOHLCV': false,
'fetchMyLiquidations': false,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenOrders': true,
'fetchOrder': true,
'fetchOrderBook': true,
'fetchOrders': true,
'fetchPosition': false,
'fetchPositionHistory': false,
'fetchPositions': true,
'fetchPositionsHistory': false,
'fetchPositionsRisk': false,
'fetchPremiumIndexOHLCV': false,
'fetchTicker': true,
'fetchTickers': true,
'fetchTrades': true,
'fetchTransactions': 'emulated',
'fetchTransfer': false,
'fetchTransfers': false,
'index': true,
'reduceMargin': undefined,
'sandbox': true,
'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://github.com/user-attachments/assets/c78425ab-78d5-49d6-bd14-db7734798f04',
'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': {
'url': 'https://www.bitmex.com/app/register/NZTR1q',
'discount': 0.1,
},
},
'api': {
'public': {
'get': {
'announcement': 5,
'announcement/urgent': 5,
'chat': 5,
'chat/channels': 5,
'chat/connected': 5,
'chat/pinned': 5,
'funding': 5,
'guild': 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': {
'address': 5,
'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/csa': 5,
'user/depositAddress': 5,
'user/executionHistory': 5,
'user/getWalletTransferAccounts': 5,
'user/margin': 5,
'user/quoteFillRatio': 5,
'user/quoteValueRatio': 5,
'user/staking': 5,
'user/staking/instruments': 5,
'user/staking/tiers': 5,
'user/tradingVolume': 5,
'user/unstakingRequests': 5,
'user/wallet': 5,
'user/walletHistory': 5,
'user/walletSummary': 5,
'userAffiliates': 5,
'userEvent': 5,
},
'post': {
'address': 5,
'chat': 5,
'guild': 5,
'guild/archive': 5,
'guild/join': 5,
'guild/kick': 5,
'guild/leave': 5,
'guild/sharesTrades': 5,
'order': 1,
'order/cancelAllAfter': 5,
'order/closePosition': 5,
'position/isolate': 1,
'position/leverage': 1,
'position/riskLimit': 5,
'position/transferMargin': 1,
'user/addSubaccount': 5,
'user/cancelWithdrawal': 5,
'user/communicationToken': 5,
'user/confirmEmail': 5,
'user/confirmWithdrawal': 5,
'user/logout': 5,
'user/preferences': 5,
'user/requestWithdrawal': 5,
'user/unstakingRequests': 5,
'user/updateSubaccount': 5,
'user/walletTransfer': 5,
},
'put': {
'guild': 5,
'order': 1,
},
'delete': {
'order': 1,
'order/all': 1,
'user/unstakingRequests': 5,
},
},
},
'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,
'Account is restricted': PermissionDenied, // {"error":{"message":"Account is restricted","name":"HTTPError"}}
},
'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',
'ERC20': 'eth',
'BEP20': 'bsc',
'TRC20': 'tron',
'AVAXC': 'avax',
'NEAR': 'near',
'XTZ': 'xtz',
'DOT': 'dot',
'SOL': 'sol',
'ADA': 'ada',
},
},
'features': {
'default': {
'sandbox': true,
'createOrder': {
'marginMode': true,
'triggerPrice': true,
'triggerPriceType': {
'last': true,
'mark': true,
},
'triggerDirection': true,
'stopLossPrice': false,
'takeProfitPrice': false,
'attachedStopLossTakeProfit': undefined,
'timeInForce': {
'IOC': true,
'FOK': true,
'PO': true,
'GTD': false,
},
'hedged': false,
'trailing': true,
'marketBuyRequiresPrice': false,
'marketBuyByCost': false,
// exchange-supported features
// 'selfTradePrevention': true,
// 'twap': false,
// 'iceberg': false,
// 'oco': false,
},
'createOrders': undefined,
'fetchMyTrades': {
'marginMode': false,
'limit': 500,
'daysBack': undefined,
'untilDays': 1000000,
'symbolRequired': false,
},
'fetchOrder': {
'marginMode': false,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOpenOrders': {
'marginMode': false,
'limit': 500,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOrders': {
'marginMode': false,
'limit': 500,
'daysBack': undefined,
'untilDays': 1000000,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchClosedOrders': {
'marginMode': false,
'limit': 500,
'daysBack': undefined,
'daysBackCanceled': undefined,
'untilDays': 1000000,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOHLCV': {
'limit': 10000,
},
},
'spot': {
'extends': 'default',
'createOrder': {
'triggerPriceType': {
'index': false,
},
},
},
'derivatives': {
'extends': 'default',
'createOrder': {
'triggerPriceType': {
'index': true,
},
},
},
'swap': {
'linear': {
'extends': 'derivatives',
},
'inverse': {
'extends': 'derivatives',
},
},
'future': {
'linear': {
'extends': 'derivatives',
},
'inverse': {
'extends': 'derivatives',
},
},
},
'commonCurrencies': {
'USDt': 'USDT',
'XBt': 'BTC',
'XBT': 'BTC',
'Gwei': 'ETH',
'GWEI': 'ETH',
'LAMP': 'SOL',
'LAMp': 'SOL',
},
});
}
/**
* @method
* @name bitmex#fetchCurrencies
* @description fetches all available currencies on an exchange
* @see https://www.bitmex.com/api/explorer/#!/Wallet/Wallet_getAssetsConfig
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an associative dictionary of currencies
*/
async fetchCurrencies(params = {}) {
const response = await this.publicGetWalletAssets(params);
//
// {
// "XBt": {
// "asset": "XBT",
// "currency": "XBt",
// "majorCurrency": "XBT",
// "name": "Bitcoin",
// "currencyType": "Crypto",
// "scale": "8",
// // "mediumPrecision": "8",
// // "shorterPrecision": "4",
// // "symbol": "₿",
// // "tickLog": "0",
// // "weight": "1",
// "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.safeBool(chain, 'depositEnabled', false);
const isWithdrawEnabled = this.safeBool(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));
const isCrypto = this.safeString(currency, 'currencyType') === 'Crypto';
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,
'type': isCrypto ? 'crypto' : 'other',
};
}
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) {
if (code === undefined) {
return amount;
}
else if (amount === undefined) {
return undefined;
}
const currency = this.currency(code);
const precision = this.safeString(currency, 'precision');
return Precise.stringMul(amount, precision);
}
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.parseNumber(this.convertToRealAmount(market[currencySide], rawQuantity));
}
return this.parseNumber(rawQuantity);
}
convertFromRawCost(symbol, rawQuantity) {
return this.convertFromRawQuantity(symbol, rawQuantity, 'quote');
}
/**
* @method
* @name bitmex#fetchMarkets
* @description retrieves data on all markets for bitmex
* @see https://www.bitmex.com/api/explorer/#!/Instrument/Instrument_getActive
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} an array of objects representing market data
*/
async fetchMarkets(params = {}) {
const response = await this.publicGetInstrumentActive(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"
// }
// ]
//
return this.parseMarkets(response);
}
parseMarket(market) {
const id = this.safeString(market, 'symbol');
let baseId = this.safeString(market, 'underlying');
let quoteId = this.safeString(market, 'quoteCurrency');
const settleId = this.safeString(market, 'settlCurrency');
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
let type;
let swap = false;
let spot = false;
let future = false;
if (typ === 'FFWCSX') {
type = 'swap';
swap = true;
}
else if (typ === 'IFXXXP') {
type = 'spot';
spot = true;
}
else if (typ === 'FFCCSX') {
type = 'future';
future = true;
}
else if (typ === 'FFICSX') {
// prediction markets (without any volume)
quoteId = baseId;
baseId = this.safeString(market, 'rootSymbol');
type = 'future';
future = true;
}
const base = this.safeCurrencyCode(baseId);
const quote = this.safeCurrencyCode(quoteId);
const contract = swap || future;
let contractSize = undefined;
let isInverse = this.safeValue(market, 'isInverse'); // this is true when BASE and SETTLE are same, i.e. BTC/XXX:BTC
let isQuanto = this.safeValue(market, 'isQuanto'); // this is true when BASE and SETTLE are different, i.e. AXS/XXX:BTC
let linear = contract ? (!isInverse && !isQuanto) : undefined;
const status = this.safeString(market, 'state');
const active = status === 'Open'; // Open, Settled, Unlisted
let expiry = undefined;
let expiryDatetime = undefined;
let symbol = undefined;
if (spot) {
symbol = base + '/' + quote;
}
else if (contract) {
symbol = base + '/' + quote + ':' + settle;
if (linear) {
const multiplierString = this.safeString2(market, 'underlyingToPositionMultiplier', 'underlyingToSettleMultiplier');
contractSize = this.parseNumber(Precise.stringDiv('1', multiplierString));
}
else {
const multiplierString = Precise.stringAbs(this.safeString(market, 'multiplier'));
contractSize = this.parseNumber(multiplierString);
}
expiryDatetime = this.safeString(market, 'expiry');
expiry = this.parse8601(expiryDatetime);
if (expiry !== undefined) {
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));
// subtype should be undefined for spot markets
if (spot) {
isInverse = undefined;
isQuanto = undefined;
linear = undefined;
}
return {
'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,
'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'),
},
'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,
},
},
'created': undefined,
'info': market,
};
}
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);
}
/**
* @method
* @name bitmex#fetchBalance
* @description query for balance and get the amount of funds available for trading or funds locked in orders
* @see https://www.bitmex.com/api/explorer/#!/User/User_getMargin
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
*/
async fetchBalance(params = {}) {
await this.loadMarkets();
const 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);
}
/**
* @method
* @name bitmex#fetchOrderBook
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://www.bitmex.com/api/explorer/#!/OrderBook/OrderBook_getL2
* @param {string} symbol unified symbol of the market to fetch the order book for
* @param {int} [limit] the maximum amount of order book entries to return
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
*/
async fetchOrderBook(symbol, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'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) {
const resultSide = result[side];
resultSide.push([price, amount]);
}
}
result['bids'] = this.sortBy(result['bids'], 0, true);
result['asks'] = this.sortBy(result['asks'], 0);
return result;
}
/**
* @method
* @name bitmex#fetchOrder
* @description fetches information on an order made by the user
* @see https://www.bitmex.com/api/explorer/#!/Order/Order_getOrders
* @param {string} id the order id
* @param {string} symbol unified symbol of the market the order was made in
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async fetchOrder(id, symbol = undefined, params = {}) {
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.');
}
/**
* @method
* @name bitmex#fetchOrders
* @see https://www.bitmex.com/api/explorer/#!/Order/Order_getOrders
* @description fetches information on multiple orders made by the user
* @param {string} symbol unified market symbol of the market orders were made in
* @param {int} [since] the earliest time in ms to fetch orders for
* @param {int} [limit] the maximum number of order structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.until] the earliest time in ms to fetch orders for
* @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
* @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async fetchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
let paginate = false;
[paginate, params] = this.handleOptionAndParams(params, 'fetchOrders', 'paginate');
if (paginate) {
return await this.fetchPaginatedCallDynamic('fetchOrders', symbol, since, limit, params, 100);
}
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;
}
const until = this.safeInteger2(params, 'until', 'endTime');
if (until !== undefined) {
params = this.omit(params, ['until']);
request['endTime'] = this.iso8601(until);
}
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);
}
/**
* @method
* @name bitmex#fetchOpenOrders
* @description fetch all unfilled currently open orders
* @see https://www.bitmex.com/api/explorer/#!/Order/Order_getOrders
* @param {string} symbol unified market symbol
* @param {int} [since] the earliest time in ms to fetch open orders for
* @param {int} [limit] the maximum number of open orders structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
const request = {
'filter': {
'open': true,
},
};
return await this.fetchOrders(symbol, since, limit, this.deepExtend(request, params));
}
/**
* @method
* @name bitmex#fetchClosedOrders
* @description fetches information on multiple closed orders made by the user
* @see https://www.bitmex.com/api/explorer/#!/Order/Order_getOrders
* @param {string} symbol unified market symbol of the market orders were made in
* @param {int} [since] the earliest time in ms to fetch orders for
* @param {int} [limit] the maximum number of order structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async fetchClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
// Bitmex barfs if you set 'open': false in the filter...
const orders = await this.fetchOrders(symbol, since, limit, params);
return this.filterByArray(orders, 'status', ['closed', 'canceled'], false);
}
/**
* @method
* @name bitmex#fetchMyTrades
* @description fetch all trades made by the user
* @see https://www.bitmex.com/api/explorer/#!/Execution/Execution_getTradeHistory
* @param {string} symbol unified market symbol
* @param {int} [since] the earliest time in ms to fetch trades for
* @param {int} [limit] the maximum number of trades structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
* @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
*/
async fetchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
let paginate = false;
[paginate, params] = this.handleOptionAndParams(params, 'fetchMyTrades', 'paginate');
if (paginate) {
return await this.fetchPaginatedCallDynamic('fetchMyTrades', symbol, since, limit, params, 100);
}
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'] = Math.min(500, limit);
}
const until = this.safeInteger2(params, 'until', 'endTime');
if (until !== undefined) {
params = this.omit(params, ['until']);
request['endTime'] = this.iso8601(un