UNPKG

@proton/ccxt

Version:

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

1,112 lines (1,110 loc) 195 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/phemex.js'; import { ExchangeError, BadSymbol, AuthenticationError, InsufficientFunds, InvalidOrder, ArgumentsRequired, OrderNotFound, BadRequest, PermissionDenied, AccountSuspended, CancelPending, DDoSProtection, DuplicateOrderId, RateLimitExceeded, NotSupported } from './base/errors.js'; import { Precise } from './base/Precise.js'; import { TICK_SIZE } from './base/functions/number.js'; import { sha256 } from './static_dependencies/noble-hashes/sha256.js'; // ---------------------------------------------------------------------------- export default class phemex extends Exchange { describe() { return this.deepExtend(super.describe(), { 'id': 'phemex', 'name': 'Phemex', 'countries': ['CN'], 'rateLimit': 120.5, 'version': 'v1', 'certified': false, 'pro': true, 'hostname': 'api.phemex.com', 'has': { 'CORS': undefined, 'spot': true, 'margin': false, 'swap': true, 'future': false, 'option': false, 'addMargin': false, 'cancelAllOrders': true, 'cancelOrder': true, 'createOrder': true, 'createReduceOnlyOrder': true, 'createStopLimitOrder': true, 'createStopMarketOrder': true, 'createStopOrder': true, 'editOrder': true, 'fetchBalance': true, 'fetchBorrowRate': false, 'fetchBorrowRateHistories': false, 'fetchBorrowRateHistory': false, 'fetchBorrowRates': false, 'fetchBorrowRatesPerSymbol': false, 'fetchClosedOrders': true, 'fetchCurrencies': true, 'fetchDepositAddress': true, 'fetchDeposits': true, 'fetchFundingHistory': true, 'fetchFundingRate': true, 'fetchFundingRateHistories': false, 'fetchFundingRateHistory': true, 'fetchFundingRates': false, 'fetchIndexOHLCV': false, 'fetchLeverage': false, 'fetchLeverageTiers': true, 'fetchMarketLeverageTiers': 'emulated', 'fetchMarkets': true, 'fetchMarkOHLCV': false, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrders': true, 'fetchPositions': true, 'fetchPositionsRisk': false, 'fetchPremiumIndexOHLCV': false, 'fetchTicker': true, 'fetchTickers': true, 'fetchTrades': true, 'fetchTradingFee': false, 'fetchTradingFees': false, 'fetchTransfers': true, 'fetchWithdrawals': true, 'reduceMargin': false, 'setLeverage': true, 'setMargin': true, 'setMarginMode': true, 'setPositionMode': true, 'transfer': true, 'withdraw': undefined, }, 'urls': { 'logo': 'https://user-images.githubusercontent.com/1294454/85225056-221eb600-b3d7-11ea-930d-564d2690e3f6.jpg', 'test': { 'v1': 'https://testnet-api.phemex.com/v1', 'v2': 'https://testnet-api.phemex.com', 'public': 'https://testnet-api.phemex.com/exchange/public', 'private': 'https://testnet-api.phemex.com', }, 'api': { 'v1': 'https://{hostname}/v1', 'v2': 'https://{hostname}', 'public': 'https://{hostname}/exchange/public', 'private': 'https://{hostname}', }, 'www': 'https://phemex.com', 'doc': 'https://github.com/phemex/phemex-api-docs', 'fees': 'https://phemex.com/fees-conditions', 'referral': { 'url': 'https://phemex.com/register?referralCode=EDNVJ', 'discount': 0.1, }, }, 'timeframes': { '1m': '60', '3m': '180', '5m': '300', '15m': '900', '30m': '1800', '1h': '3600', '2h': '7200', '3h': '10800', '4h': '14400', '6h': '21600', '12h': '43200', '1d': '86400', '1w': '604800', '1M': '2592000', '3M': '7776000', '1Y': '31104000', }, 'api': { 'public': { 'get': { 'cfg/v2/products': 5, 'cfg/fundingRates': 5, 'products': 5, 'nomics/trades': 5, 'md/kline': 5, 'md/v2/kline/list': 5, 'md/v2/kline': 5, 'md/v2/kline/last': 5, // perpetual ?symbol=<symbol>&resolution=<resolution>&limit=<limit> }, }, 'v1': { 'get': { 'md/orderbook': 5, 'md/trade': 5, 'md/ticker/24hr': 5, 'md/ticker/24hr/all': 5, 'md/spot/ticker/24hr': 5, 'md/spot/ticker/24hr/all': 5, 'exchange/public/products': 5, 'api-data/public/data/funding-rate-history': 5, }, }, 'v2': { 'get': { 'md/v2/orderbook': 5, 'md/v2/trade': 5, 'md/v2/ticker/24hr': 5, 'md/v2/ticker/24hr/all': 5, 'api-data/public/data/funding-rate-history': 5, }, }, 'private': { 'get': { // spot 'spot/orders/active': 1, // 'spot/orders/active': 5, // ?symbol=<symbol>&clOrDID=<clOrdID> 'spot/orders': 1, 'spot/wallets': 5, 'exchange/spot/order': 5, 'exchange/spot/order/trades': 5, 'exchange/order/v2/orderList': 5, 'exchange/order/v2/tradingList': 5, // swap 'accounts/accountPositions': 1, 'g-accounts/accountPositions': 1, 'accounts/positions': 25, 'api-data/futures/funding-fees': 5, 'api-data/g-futures/funding-fees': 5, 'api-data/futures/orders': 5, 'api-data/g-futures/orders': 5, 'api-data/futures/orders/by-order-id': 5, 'api-data/g-futures/orders/by-order-id': 5, 'api-data/futures/trades': 5, 'api-data/g-futures/trades': 5, 'api-data/futures/trading-fees': 5, 'api-data/g-futures/trading-fees': 5, 'g-orders/activeList': 1, 'orders/activeList': 1, 'exchange/order/list': 5, 'exchange/order': 5, // 'exchange/order': 5, // ?symbol=<symbol>&clOrdID=<clOrdID5,clOrdID2> 'exchange/order/trade': 5, 'phemex-user/users/children': 5, 'phemex-user/wallets/v2/depositAddress': 5, 'phemex-user/wallets/tradeAccountDetail': 5, 'phemex-user/order/closedPositionList': 5, 'exchange/margins/transfer': 5, 'exchange/wallets/confirm/withdraw': 5, 'exchange/wallets/withdrawList': 5, 'exchange/wallets/depositList': 5, 'exchange/wallets/v2/depositAddress': 5, 'api-data/spots/funds': 5, 'assets/convert': 5, // transfer 'assets/transfer': 5, 'assets/spots/sub-accounts/transfer': 5, 'assets/futures/sub-accounts/transfer': 5, 'assets/quote': 5, // ?fromCurrency=<currency>&toCurrency=<currency>&amountEv=<amount> }, 'post': { // spot 'spot/orders': 1, // swap 'orders': 1, 'g-orders': 1, 'positions/assign': 5, 'exchange/wallets/transferOut': 5, 'exchange/wallets/transferIn': 5, 'exchange/margins': 5, 'exchange/wallets/createWithdraw': 5, 'exchange/wallets/cancelWithdraw': 5, 'exchange/wallets/createWithdrawAddress': 5, // transfer 'assets/transfer': 5, 'assets/spots/sub-accounts/transfer': 5, 'assets/futures/sub-accounts/transfer': 5, 'assets/universal-transfer': 5, 'assets/convert': 5, }, 'put': { // spot 'spot/orders': 1, // swap 'orders/replace': 1, 'g-orders/replace': 1, 'positions/leverage': 5, 'g-positions/leverage': 5, 'g-positions/switch-pos-mode-sync': 5, 'positions/riskLimit': 5, // ?symbol=<symbol>&riskLimit=<riskLimit>&riskLimitEv=<riskLimitEv> }, 'delete': { // spot 'spot/orders': 2, 'spot/orders/all': 2, // 'spot/orders': 5, // ?symbol=<symbol>&clOrdID=<clOrdID> // swap 'orders/cancel': 1, 'orders': 1, 'orders/all': 3, 'g-orders/cancel': 1, 'g-orders': 1, 'g-orders/all': 3, // ?symbol=<symbol>&untriggered=<untriggered>&text=<text> }, }, }, 'precisionMode': TICK_SIZE, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'taker': this.parseNumber('0.001'), 'maker': this.parseNumber('0.001'), }, }, 'requiredCredentials': { 'apiKey': true, 'secret': true, }, 'exceptions': { 'exact': { // not documented '412': BadRequest, '6001': BadRequest, // documented '19999': BadRequest, '10001': DuplicateOrderId, '10002': OrderNotFound, '10003': CancelPending, '10004': CancelPending, '10005': CancelPending, '11001': InsufficientFunds, '11002': InvalidOrder, '11003': InsufficientFunds, '11004': InvalidOrder, '11005': InsufficientFunds, '11006': ExchangeError, '11007': ExchangeError, '11008': ExchangeError, '11009': ExchangeError, '11010': InsufficientFunds, '11011': InvalidOrder, '11012': InvalidOrder, '11013': InvalidOrder, '11014': InvalidOrder, '11015': InvalidOrder, '11016': BadRequest, '11017': ExchangeError, '11018': ExchangeError, '11019': ExchangeError, '11020': ExchangeError, '11021': ExchangeError, '11022': AccountSuspended, '11023': ExchangeError, '11024': ExchangeError, '11025': BadRequest, '11026': ExchangeError, '11027': BadSymbol, '11028': BadSymbol, '11029': ExchangeError, '11030': ExchangeError, '11031': DDoSProtection, '11032': DDoSProtection, '11033': DuplicateOrderId, '11034': InvalidOrder, '11035': InvalidOrder, '11036': InvalidOrder, '11037': InvalidOrder, '11038': InvalidOrder, '11039': InvalidOrder, '11040': InvalidOrder, '11041': InvalidOrder, '11042': InvalidOrder, '11043': InvalidOrder, '11044': InvalidOrder, '11045': InvalidOrder, '11046': InvalidOrder, '11047': InvalidOrder, '11048': InvalidOrder, '11049': InvalidOrder, '11050': InvalidOrder, '11051': InvalidOrder, '11052': InvalidOrder, '11053': InvalidOrder, '11054': InvalidOrder, '11055': InvalidOrder, '11056': InvalidOrder, '11057': InvalidOrder, '11058': InvalidOrder, '11059': InvalidOrder, '11060': InvalidOrder, '11061': CancelPending, '11062': InvalidOrder, '11063': InvalidOrder, '11064': InvalidOrder, '11065': InvalidOrder, '11066': InvalidOrder, '11067': InvalidOrder, '11068': InvalidOrder, '11069': ExchangeError, '11070': BadSymbol, '11071': InvalidOrder, '11072': InvalidOrder, '11073': InvalidOrder, '11074': InvalidOrder, '11075': InvalidOrder, '11076': InvalidOrder, '11077': InvalidOrder, '11078': InvalidOrder, '11079': InvalidOrder, '11080': InvalidOrder, '11081': InvalidOrder, '11082': InsufficientFunds, '11083': InvalidOrder, '11084': InvalidOrder, '11085': DuplicateOrderId, '11086': InvalidOrder, '11087': InvalidOrder, '11088': InvalidOrder, '11089': InvalidOrder, '11090': InvalidOrder, '11091': InvalidOrder, '11092': InvalidOrder, '11093': InvalidOrder, '11094': InvalidOrder, '11095': InvalidOrder, '11096': InvalidOrder, '11097': BadRequest, '11098': BadRequest, '11099': ExchangeError, '11100': InsufficientFunds, '11101': InsufficientFunds, '11102': BadRequest, '11103': BadRequest, '11104': BadRequest, '11105': InsufficientFunds, '11106': InsufficientFunds, '11107': ExchangeError, '11108': InvalidOrder, '11109': InvalidOrder, '11110': InvalidOrder, '11111': InvalidOrder, '11112': InvalidOrder, '11113': BadRequest, '11114': InvalidOrder, '11115': InvalidOrder, '11116': InvalidOrder, '11117': InvalidOrder, '11118': InvalidOrder, '11119': InvalidOrder, '11120': InvalidOrder, '11121': InvalidOrder, '11122': InvalidOrder, '11123': InvalidOrder, '11124': InvalidOrder, '11125': InvalidOrder, '11126': InvalidOrder, '11128': InvalidOrder, '11129': InvalidOrder, '11130': InvalidOrder, '11131': InvalidOrder, '11132': InvalidOrder, '11133': InvalidOrder, '11134': InvalidOrder, // not documented '30000': BadRequest, '30018': BadRequest, '34003': PermissionDenied, '35104': InsufficientFunds, '39995': RateLimitExceeded, '39996': PermissionDenied, // {"code": "39996","msg": "Access denied."} }, 'broad': { '401 Insufficient privilege': PermissionDenied, '401 Request IP mismatch': PermissionDenied, 'Failed to find api-key': AuthenticationError, 'Missing required parameter': BadRequest, 'API Signature verification failed': AuthenticationError, 'Api key not found': AuthenticationError, // {"msg":"Api key not found 698dc9e3-6faa-4910-9476-12857e79e198","code":"10500"} }, }, 'options': { 'brokerId': 'ccxt2022', 'x-phemex-request-expiry': 60, 'createOrderByQuoteRequiresPrice': true, 'networks': { 'TRC20': 'TRX', 'ERC20': 'ETH', }, 'defaultNetworks': { 'USDT': 'ETH', }, 'defaultSubType': 'linear', 'accountsByType': { 'spot': 'spot', 'swap': 'future', }, 'transfer': { 'fillResponseFromRequest': true, }, }, }); } parseSafeNumber(value = undefined) { if (value === undefined) { return value; } let parts = value.split(','); value = parts.join(''); parts = value.split(' '); return this.safeNumber(parts, 0); } parseSwapMarket(market) { // // { // "symbol":"BTCUSD", // "displaySymbol":"BTC / USD", // "indexSymbol":".BTC", // "markSymbol":".MBTC", // "fundingRateSymbol":".BTCFR", // "fundingRate8hSymbol":".BTCFR8H", // "contractUnderlyingAssets":"USD", // "settleCurrency":"BTC", // "quoteCurrency":"USD", // "contractSize":"1 USD", // "lotSize":1, // "tickSize":0.5, // "priceScale":4, // "ratioScale":8, // "pricePrecision":1, // "minPriceEp":5000, // "maxPriceEp":10000000000, // "maxOrderQty":1000000, // "type":"Perpetual", // "status":"Listed", // "tipOrderQty":1000000, // "steps":"50", // "riskLimits":[ // {"limit":100,"initialMargin":"1.0%","initialMarginEr":1000000,"maintenanceMargin":"0.5%","maintenanceMarginEr":500000}, // {"limit":150,"initialMargin":"1.5%","initialMarginEr":1500000,"maintenanceMargin":"1.0%","maintenanceMarginEr":1000000}, // {"limit":200,"initialMargin":"2.0%","initialMarginEr":2000000,"maintenanceMargin":"1.5%","maintenanceMarginEr":1500000}, // ], // "underlyingSymbol":".BTC", // "baseCurrency":"BTC", // "settlementCurrency":"BTC", // "valueScale":8, // "defaultLeverage":0, // "maxLeverage":100, // "initMarginEr":"1000000", // "maintMarginEr":"500000", // "defaultRiskLimitEv":10000000000, // "deleverage":true, // "makerFeeRateEr":-250000, // "takerFeeRateEr":750000, // "fundingInterval":8, // "marketUrl":"https://phemex.com/trade/BTCUSD", // "description":"BTCUSD is a BTC/USD perpetual contract priced on the .BTC Index. Each contract is worth 1 USD of Bitcoin. Funding is paid and received every 8 hours. At UTC time: 00:00, 08:00, 16:00.", // } // const id = this.safeString(market, 'symbol'); const baseId = this.safeString2(market, 'baseCurrency', 'contractUnderlyingAssets'); const quoteId = this.safeString(market, 'quoteCurrency'); const settleId = this.safeString(market, 'settleCurrency'); const base = this.safeCurrencyCode(baseId); const quote = this.safeCurrencyCode(quoteId); const settle = this.safeCurrencyCode(settleId); let inverse = false; if (settleId !== quoteId) { inverse = true; } const priceScale = this.safeInteger(market, 'priceScale'); const ratioScale = this.safeInteger(market, 'ratioScale'); const valueScale = this.safeInteger(market, 'valueScale'); const minPriceEp = this.safeString(market, 'minPriceEp'); const maxPriceEp = this.safeString(market, 'maxPriceEp'); const makerFeeRateEr = this.safeString(market, 'makerFeeRateEr'); const takerFeeRateEr = this.safeString(market, 'takerFeeRateEr'); const status = this.safeString(market, 'status'); const contractSizeString = this.safeString(market, 'contractSize', ' '); let contractSize = undefined; if (contractSizeString.indexOf(' ')) { // "1 USD" // "0.005 ETH" const parts = contractSizeString.split(' '); contractSize = this.parseNumber(parts[0]); } else { // "1.0" contractSize = this.parseNumber(contractSizeString); } return { 'id': id, 'symbol': base + '/' + quote + ':' + settle, 'base': base, 'quote': quote, 'settle': settle, 'baseId': baseId, 'quoteId': quoteId, 'settleId': settleId, 'type': 'swap', 'spot': false, 'margin': false, 'swap': true, 'future': false, 'option': false, 'active': status === 'Listed', 'contract': true, 'linear': !inverse, 'inverse': inverse, 'taker': this.parseNumber(this.fromEn(takerFeeRateEr, ratioScale)), 'maker': this.parseNumber(this.fromEn(makerFeeRateEr, ratioScale)), 'contractSize': contractSize, 'expiry': undefined, 'expiryDatetime': undefined, 'strike': undefined, 'optionType': undefined, 'priceScale': priceScale, 'valueScale': valueScale, 'ratioScale': ratioScale, 'precision': { 'amount': this.safeNumber2(market, 'lotSize', 'qtyStepSize'), 'price': this.safeNumber(market, 'tickSize'), }, 'limits': { 'leverage': { 'min': this.parseNumber('1'), 'max': this.safeNumber(market, 'maxLeverage'), }, 'amount': { 'min': undefined, 'max': undefined, }, 'price': { 'min': this.parseNumber(this.fromEn(minPriceEp, priceScale)), 'max': this.parseNumber(this.fromEn(maxPriceEp, priceScale)), }, 'cost': { 'min': undefined, 'max': this.parseNumber(this.safeString(market, 'maxOrderQty')), }, }, 'info': market, }; } parseSpotMarket(market) { // // { // "symbol":"sBTCUSDT", // "code":1001, // "displaySymbol":"BTC / USDT", // "quoteCurrency":"USDT", // "priceScale":8, // "ratioScale":8, // "pricePrecision":2, // "type":"Spot", // "baseCurrency":"BTC", // "baseTickSize":"0.000001 BTC", // "baseTickSizeEv":100, // "quoteTickSize":"0.01 USDT", // "quoteTickSizeEv":1000000, // "minOrderValue":"10 USDT", // "minOrderValueEv":1000000000, // "maxBaseOrderSize":"1000 BTC", // "maxBaseOrderSizeEv":100000000000, // "maxOrderValue":"5,000,000 USDT", // "maxOrderValueEv":500000000000000, // "defaultTakerFee":"0.001", // "defaultTakerFeeEr":100000, // "defaultMakerFee":"0.001", // "defaultMakerFeeEr":100000, // "baseQtyPrecision":6, // "quoteQtyPrecision":2, // "status":"Listed", // "tipOrderQty":2, // "description":"BTCUSDT is a BTC/USDT spot trading pair. Minimum order value is 1 USDT", // "leverage":5 // "valueScale":8, // }, // const type = this.safeStringLower(market, 'type'); const id = this.safeString(market, 'symbol'); const quoteId = this.safeString(market, 'quoteCurrency'); const baseId = this.safeString(market, 'baseCurrency'); const base = this.safeCurrencyCode(baseId); const quote = this.safeCurrencyCode(quoteId); const status = this.safeString(market, 'status'); const precisionAmount = this.parseSafeNumber(this.safeString(market, 'baseTickSize')); const precisionPrice = this.parseSafeNumber(this.safeString(market, 'quoteTickSize')); return { 'id': id, 'symbol': base + '/' + quote, 'base': base, 'quote': quote, 'settle': undefined, 'baseId': baseId, 'quoteId': quoteId, 'settleId': undefined, 'type': type, 'spot': true, 'margin': false, 'swap': false, 'future': false, 'option': false, 'active': status === 'Listed', 'contract': false, 'linear': undefined, 'inverse': undefined, 'taker': this.safeNumber(market, 'defaultTakerFee'), 'maker': this.safeNumber(market, 'defaultMakerFee'), 'contractSize': undefined, 'expiry': undefined, 'expiryDatetime': undefined, 'strike': undefined, 'optionType': undefined, 'priceScale': this.safeInteger(market, 'priceScale'), 'valueScale': this.safeInteger(market, 'valueScale'), 'ratioScale': this.safeInteger(market, 'ratioScale'), 'precision': { 'amount': precisionAmount, 'price': precisionPrice, }, 'limits': { 'leverage': { 'min': undefined, 'max': undefined, }, 'amount': { 'min': precisionAmount, 'max': this.parseSafeNumber(this.safeString(market, 'maxBaseOrderSize')), }, 'price': { 'min': precisionPrice, 'max': undefined, }, 'cost': { 'min': this.parseSafeNumber(this.safeString(market, 'minOrderValue')), 'max': this.parseSafeNumber(this.safeString(market, 'maxOrderValue')), }, }, 'info': market, }; } async fetchMarkets(params = {}) { /** * @method * @name phemex#fetchMarkets * @description retrieves data on all markets for phemex * @param {object} params extra parameters specific to the exchange api endpoint * @returns {[object]} an array of objects representing market data */ const v2Products = await this.publicGetCfgV2Products(params); // // { // "code":0, // "msg":"OK", // "data":{ // "ratioScale":8, // "currencies":[ // {"code":1,"currency":"BTC","valueScale":8,"minValueEv":1,"maxValueEv":5000000000000000000,"name":"Bitcoin"}, // {"code":2,"currency":"USD","valueScale":4,"minValueEv":1,"maxValueEv":500000000000000,"name":"USD"}, // {"code":3,"currency":"USDT","valueScale":8,"minValueEv":1,"maxValueEv":5000000000000000000,"name":"TetherUS"}, // ], // "products":[ // { // "symbol":"BTCUSD", // "displaySymbol":"BTC / USD", // "indexSymbol":".BTC", // "markSymbol":".MBTC", // "fundingRateSymbol":".BTCFR", // "fundingRate8hSymbol":".BTCFR8H", // "contractUnderlyingAssets":"USD", // "settleCurrency":"BTC", // "quoteCurrency":"USD", // "contractSize":1.0, // "lotSize":1, // "tickSize":0.5, // "priceScale":4, // "ratioScale":8, // "pricePrecision":1, // "minPriceEp":5000, // "maxPriceEp":10000000000, // "maxOrderQty":1000000, // "type":"Perpetual" // }, // { // "symbol":"sBTCUSDT", // "code":1001, // "displaySymbol":"BTC / USDT", // "quoteCurrency":"USDT", // "priceScale":8, // "ratioScale":8, // "pricePrecision":2, // "type":"Spot", // "baseCurrency":"BTC", // "baseTickSize":"0.000001 BTC", // "baseTickSizeEv":100, // "quoteTickSize":"0.01 USDT", // "quoteTickSizeEv":1000000, // "minOrderValue":"10 USDT", // "minOrderValueEv":1000000000, // "maxBaseOrderSize":"1000 BTC", // "maxBaseOrderSizeEv":100000000000, // "maxOrderValue":"5,000,000 USDT", // "maxOrderValueEv":500000000000000, // "defaultTakerFee":"0.001", // "defaultTakerFeeEr":100000, // "defaultMakerFee":"0.001", // "defaultMakerFeeEr":100000, // "baseQtyPrecision":6, // "quoteQtyPrecision":2, // "status":"Listed", // "tipOrderQty":2, // "description":"BTCUSDT is a BTC/USDT spot trading pair. Minimum order value is 1 USDT", // "leverage":5 // }, // ], // "riskLimits":[ // { // "symbol":"BTCUSD", // "steps":"50", // "riskLimits":[ // {"limit":100,"initialMargin":"1.0%","initialMarginEr":1000000,"maintenanceMargin":"0.5%","maintenanceMarginEr":500000}, // {"limit":150,"initialMargin":"1.5%","initialMarginEr":1500000,"maintenanceMargin":"1.0%","maintenanceMarginEr":1000000}, // {"limit":200,"initialMargin":"2.0%","initialMarginEr":2000000,"maintenanceMargin":"1.5%","maintenanceMarginEr":1500000}, // ] // }, // ], // "leverages":[ // {"initialMargin":"1.0%","initialMarginEr":1000000,"options":[1,2,3,5,10,25,50,100]}, // {"initialMargin":"1.5%","initialMarginEr":1500000,"options":[1,2,3,5,10,25,50,66]}, // {"initialMargin":"2.0%","initialMarginEr":2000000,"options":[1,2,3,5,10,25,33,50]}, // ] // } // } // const v1Products = await this.v1GetExchangePublicProducts(params); const v1ProductsData = this.safeValue(v1Products, 'data', []); // // { // "code":0, // "msg":"OK", // "data":[ // { // "symbol":"BTCUSD", // "underlyingSymbol":".BTC", // "quoteCurrency":"USD", // "baseCurrency":"BTC", // "settlementCurrency":"BTC", // "maxOrderQty":1000000, // "maxPriceEp":100000000000000, // "lotSize":1, // "tickSize":"0.5", // "contractSize":"1 USD", // "priceScale":4, // "ratioScale":8, // "valueScale":8, // "defaultLeverage":0, // "maxLeverage":100, // "initMarginEr":"1000000", // "maintMarginEr":"500000", // "defaultRiskLimitEv":10000000000, // "deleverage":true, // "makerFeeRateEr":-250000, // "takerFeeRateEr":750000, // "fundingInterval":8, // "marketUrl":"https://phemex.com/trade/BTCUSD", // "description":"BTCUSD is a BTC/USD perpetual contract priced on the .BTC Index. Each contract is worth 1 USD of Bitcoin. Funding is paid and received every 8 hours. At UTC time: 00:00, 08:00, 16:00.", // "type":"Perpetual" // }, // ] // } // const v2ProductsData = this.safeValue(v2Products, 'data', {}); const products = this.safeValue(v2ProductsData, 'products', []); const riskLimits = this.safeValue(v2ProductsData, 'riskLimits', []); const currencies = this.safeValue(v2ProductsData, 'currencies', []); const riskLimitsById = this.indexBy(riskLimits, 'symbol'); const v1ProductsById = this.indexBy(v1ProductsData, 'symbol'); const currenciesByCode = this.indexBy(currencies, 'currency'); const result = []; for (let i = 0; i < products.length; i++) { let market = products[i]; const type = this.safeStringLower(market, 'type'); if ((type === 'perpetual') || (type === 'perpetualv2')) { const id = this.safeString(market, 'symbol'); const riskLimitValues = this.safeValue(riskLimitsById, id, {}); market = this.extend(market, riskLimitValues); const v1ProductsValues = this.safeValue(v1ProductsById, id, {}); market = this.extend(market, v1ProductsValues); market = this.parseSwapMarket(market); } else { const baseCurrency = this.safeString(market, 'baseCurrency'); const currencyValues = this.safeValue(currenciesByCode, baseCurrency, {}); const valueScale = this.safeString(currencyValues, 'valueScale', '8'); market = this.extend(market, { 'valueScale': valueScale }); market = this.parseSpotMarket(market); } result.push(market); } return result; } async fetchCurrencies(params = {}) { /** * @method * @name phemex#fetchCurrencies * @description fetches all available currencies on an exchange * @param {object} params extra parameters specific to the phemex api endpoint * @returns {object} an associative dictionary of currencies */ const response = await this.publicGetCfgV2Products(params); // // { // "code":0, // "msg":"OK", // "data":{ // ..., // "currencies":[ // {"currency":"BTC","valueScale":8,"minValueEv":1,"maxValueEv":5000000000000000000,"name":"Bitcoin"}, // {"currency":"USD","valueScale":4,"minValueEv":1,"maxValueEv":500000000000000,"name":"USD"}, // {"currency":"USDT","valueScale":8,"minValueEv":1,"maxValueEv":5000000000000000000,"name":"TetherUS"}, // ], // ... // } // } const data = this.safeValue(response, 'data', {}); const currencies = this.safeValue(data, 'currencies', []); const result = {}; for (let i = 0; i < currencies.length; i++) { const currency = currencies[i]; const id = this.safeString(currency, 'currency'); const name = this.safeString(currency, 'name'); const code = this.safeCurrencyCode(id); const valueScaleString = this.safeString(currency, 'valueScale'); const valueScale = parseInt(valueScaleString); const minValueEv = this.safeString(currency, 'minValueEv'); const maxValueEv = this.safeString(currency, 'maxValueEv'); let minAmount = undefined; let maxAmount = undefined; let precision = undefined; if (valueScale !== undefined) { const precisionString = this.parsePrecision(valueScaleString); precision = this.parseNumber(precisionString); minAmount = this.parseNumber(Precise.stringMul(minValueEv, precisionString)); maxAmount = this.parseNumber(Precise.stringMul(maxValueEv, precisionString)); } result[code] = { 'id': id, 'info': currency, 'code': code, 'name': name, 'active': undefined, 'deposit': undefined, 'withdraw': undefined, 'fee': undefined, 'precision': precision, 'limits': { 'amount': { 'min': minAmount, 'max': maxAmount, }, 'withdraw': { 'min': undefined, 'max': undefined, }, }, 'valueScale': valueScale, 'networks': {}, }; } return result; } customParseBidAsk(bidask, priceKey = 0, amountKey = 1, market = undefined) { if (market === undefined) { throw new ArgumentsRequired(this.id + ' customParseBidAsk() requires a market argument'); } let amount = this.safeString(bidask, amountKey); if (market['spot']) { amount = this.fromEv(amount, market); } return [ this.parseNumber(this.fromEp(this.safeString(bidask, priceKey), market)), this.parseNumber(amount), ]; } customParseOrderBook(orderbook, symbol, timestamp = undefined, bidsKey = 'bids', asksKey = 'asks', priceKey = 0, amountKey = 1, market = undefined) { const result = { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'nonce': undefined, }; const sides = [bidsKey, asksKey]; for (let i = 0; i < sides.length; i++) { const side = sides[i]; const orders = []; const bidasks = this.safeValue(orderbook, side); for (let k = 0; k < bidasks.length; k++) { orders.push(this.customParseBidAsk(bidasks[k], priceKey, amountKey, market)); } result[side] = orders; } result[bidsKey] = this.sortBy(result[bidsKey], 0, true); result[asksKey] = this.sortBy(result[asksKey], 0); return result; } async fetchOrderBook(symbol, limit = undefined, params = {}) { /** * @method * @name phemex#fetchOrderBook * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data * @see https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#queryorderbook * @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 phemex 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'], // 'id': 123456789, // optional request id }; let method = 'v1GetMdOrderbook'; if (market['linear'] && market['settle'] === 'USDT') { method = 'v2GetMdV2Orderbook'; } const response = await this[method](this.extend(request, params)); // // { // "error": null, // "id": 0, // "result": { // "book": { // "asks": [ // [ 23415000000, 105262000 ], // [ 23416000000, 147914000 ], // [ 23419000000, 160914000 ], // ], // "bids": [ // [ 23360000000, 32995000 ], // [ 23359000000, 221887000 ], // [ 23356000000, 284599000 ], // ], // }, // "depth": 30, // "sequence": 1592059928, // "symbol": "sETHUSDT", // "timestamp": 1592387340020000955, // "type": "snapshot" // } // } // const result = this.safeValue(response, 'result', {}); const book = this.safeValue2(result, 'book', 'orderbook_p', {}); const timestamp = this.safeIntegerProduct(result, 'timestamp', 0.000001); const orderbook = this.customParseOrderBook(book, symbol, timestamp, 'bids', 'asks', 0, 1, market); orderbook['nonce'] = this.safeInteger(result, 'sequence'); return orderbook; } toEn(n, scale) { const stringN = n.toString(); const precise = new Precise(stringN); precise.decimals = precise.decimals - scale; precise.reduce(); const preciseString = precise.toString(); return this.parseToInt(preciseString); } toEv(amount, market = undefined) { if ((amount === undefined) || (market === undefined)) { return amount; } return this.toEn(amount, market['valueScale']); } toEp(price, market = undefined) { if ((price === undefined) || (market === undefined)) { return price; } return this.toEn(price, market['priceScale']); } fromEn(en, scale) { if (en === undefined) { return undefined; } const precise = new Precise(en); precise.decimals = this.sum(precise.decimals, scale); precise.reduce(); return precise.toString(); } fromEp(ep, market = undefined) { if ((ep === undefined) || (market === undefined)) { return ep; } return this.fromEn(ep, this.safeInteger(market, 'priceScale')); } fromEv(ev, market = undefined) { if ((ev === undefined) || (market === undefined)) { return ev; } return this.fromEn(ev, this.safeInteger(market, 'valueScale')); } fromEr(er, market = undefined) { if ((er === undefined) || (market === undefined)) { return er; } return this.fromEn(er, this.safeInteger(market, 'ratioScale')); } parseOHLCV(ohlcv, market = undefined) { // // [ // 1592467200, // timestamp // 300, // interval // 23376000000, // last // 23322000000, // open // 23381000000, // high // 23315000000, // low // 23367000000, // close // 208671000, // base volume // 48759063370, // quote volume // ] // let baseVolume = undefined; if ((market !== undefined) && market['spot']) { baseVolume = this.parseNumber(this.fromEv(this.safeString(ohlcv, 7), market)); } else { baseVolume = this.safeNumber(ohlcv, 7); } return [ this.safeTimestamp(ohlcv, 0), this.parseNumber(this.fromEp(this.safeString(ohlcv, 3), market)), this.parseNumber(this.fromEp(this.safeString(ohlcv, 4), market)), this.parseNumber(this.fromEp(this.safeString(ohlcv, 5), market)), this.parseNumber(this.fromEp(this.safeString(ohlcv, 6), market)), baseVolume, ]; } async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { /** * @method * @name phemex#fetchOHLCV * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market * @see https://github.com/phemex/phemex-api-docs/blob/master/Public-Hedged-Perpetual-API.md#querykline * @see https://github.com/phemex/phemex-api-docs/blob/master/Public-Contract-API-en.md#query-kline * @param {string} symbol unified symbol of the market to fetch OHLCV data for * @param {string} timeframe the length of time each candle represents * @param {int|undefined} since *emulated not supported by the exchange* timestamp in ms of the earliest candle to fetch * @param {int|undefined} limit the maximum amount of candles to fetch * @param {object} params extra parameters specific to the phemex api endpoint * @returns {[[int]]} A list of candles ordered as timestamp, open, high, low, close, volume */ await this.loadMarkets(); const market = this.market(symbol); const userLimit = limit; const request = { 'symbol': market['id'], 'resolution': this.safeString(this.timeframes, timeframe, timeframe), }; const possibleLimitValues = [5, 10, 50, 100, 500, 1000]; const maxLimit = 1000; if (limit === undefined && since === undefined) { limit = possibleLimitValues[5]; } if (since !== undefined) { // phemex also