UNPKG

sfccxt

Version:

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

813 lines (804 loc) 175 kB
'use strict'; // ---------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeError, BadSymbol, AuthenticationError, InsufficientFunds, InvalidOrder, ArgumentsRequired, OrderNotFound, BadRequest, PermissionDenied, AccountSuspended, CancelPending, DDoSProtection, DuplicateOrderId, RateLimitExceeded } = require ('./base/errors'); const { TICK_SIZE } = require ('./base/functions/number'); const Precise = require ('./base/Precise'); // ---------------------------------------------------------------------------- module.exports = class phemex extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'phemex', 'name': 'Phemex', 'countries': [ 'CN' ], // China 'rateLimit': 100, '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': false, '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, 'fetchTrades': true, 'fetchTradingFee': false, 'fetchTradingFees': false, 'fetchTransfers': true, 'fetchWithdrawals': true, 'reduceMargin': false, 'setLeverage': true, 'setMargin': true, 'setMarginMode': true, 'setPositionMode': false, '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', 'public': 'https://testnet-api.phemex.com/exchange/public', 'private': 'https://testnet-api.phemex.com', }, 'api': { 'v1': 'https://{hostname}/v1', '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', }, 'api': { 'public': { 'get': [ 'cfg/v2/products', // spot + contracts 'cfg/fundingRates', 'products', // contracts only 'nomics/trades', // ?market=<symbol>&since=<since> 'md/kline', // ?from=1589811875&resolution=1800&symbol=sBTCUSDT&to=1592457935 ], }, 'v1': { 'get': [ 'md/orderbook', // ?symbol=<symbol>&id=<id> 'md/trade', // ?symbol=<symbol>&id=<id> 'md/ticker/24hr', // ?symbol=<symbol>&id=<id> 'md/ticker/24hr/all', // ?id=<id> 'md/spot/ticker/24hr', // ?symbol=<symbol>&id=<id> 'md/spot/ticker/24hr/all', // ?symbol=<symbol>&id=<id> 'exchange/public/products', // contracts only ], }, 'private': { 'get': [ // spot 'spot/orders/active', // ?symbol=<symbol>&orderID=<orderID> // 'spot/orders/active', // ?symbol=<symbol>&clOrDID=<clOrdID> 'spot/orders', // ?symbol=<symbol> 'spot/wallets', // ?currency=<currency> 'exchange/spot/order', // ?symbol=<symbol>&ordStatus=<ordStatus1,orderStatus2>ordType=<ordType1,orderType2>&start=<start>&end=<end>&limit=<limit>&offset=<offset> 'exchange/spot/order/trades', // ?symbol=<symbol>&start=<start>&end=<end>&limit=<limit>&offset=<offset> // swap 'accounts/accountPositions', // ?currency=<currency> 'accounts/positions', // ?currency=<currency> 'api-data/futures/funding-fees', // ?symbol=<symbol> 'orders/activeList', // ?symbol=<symbol> 'exchange/order/list', // ?symbol=<symbol>&start=<start>&end=<end>&offset=<offset>&limit=<limit>&ordStatus=<ordStatus>&withCount=<withCount> 'exchange/order', // ?symbol=<symbol>&orderID=<orderID1,orderID2> // 'exchange/order', // ?symbol=<symbol>&clOrdID=<clOrdID1,clOrdID2> 'exchange/order/trade', // ?symbol=<symbol>&start=<start>&end=<end>&limit=<limit>&offset=<offset>&withCount=<withCount> 'phemex-user/users/children', // ?offset=<offset>&limit=<limit>&withCount=<withCount> 'phemex-user/wallets/v2/depositAddress', // ?_t=1592722635531&currency=USDT 'phemex-user/wallets/tradeAccountDetail', // ?bizCode=&currency=&end=1642443347321&limit=10&offset=0&side=&start=1&type=4&withCount=true 'phemex-user/order/closedPositionList', // ?currency=USD&limit=10&offset=0&symbol=&withCount=true 'exchange/margins/transfer', // ?start=<start>&end=<end>&offset=<offset>&limit=<limit>&withCount=<withCount> 'exchange/wallets/confirm/withdraw', // ?code=<withdrawConfirmCode> 'exchange/wallets/withdrawList', // ?currency=<currency>&limit=<limit>&offset=<offset>&withCount=<withCount> 'exchange/wallets/depositList', // ?currency=<currency>&offset=<offset>&limit=<limit> 'exchange/wallets/v2/depositAddress', // ?currency=<currency> 'api-data/spots/funds', // ?currency=<currency>&start=<start>&end=<end>&limit=<limit>&offset=<offset> 'assets/convert', // ?startTime=<startTime>&endTime=<endTime>&limit=<limit>&offset=<offset> // transfer 'assets/transfer', // ?currency=<currency>&start=<start>&end=<end>&limit=<limit>&offset=<offset> 'assets/spots/sub-accounts/transfer', // ?currency=<currency>&start=<start>&end=<end>&limit=<limit>&offset=<offset> 'assets/futures/sub-accounts/transfer', // ?currency=<currency>&start=<start>&end=<end>&limit=<limit>&offset=<offset> 'assets/quote', // ?fromCurrency=<currency>&toCurrency=<currency>&amountEv=<amount> 'assets/convert', // ?fromCurrency=<currency>&toCurrency=<currency>&startTime=<start>&endTime=<end>&limit=<limit>&offset=<offset> ], 'post': [ // spot 'spot/orders', // swap 'orders', 'positions/assign', // ?symbol=<symbol>&posBalance=<posBalance>&posBalanceEv=<posBalanceEv> 'exchange/wallets/transferOut', 'exchange/wallets/transferIn', 'exchange/margins', 'exchange/wallets/createWithdraw', // ?otpCode=<otpCode> 'exchange/wallets/cancelWithdraw', 'exchange/wallets/createWithdrawAddress', // ?otpCode={optCode} // transfer 'assets/transfer', 'assets/spots/sub-accounts/transfer', // for sub-account only 'assets/futures/sub-accounts/transfer', // for sub-account only 'assets/universal-transfer', // for Main account only 'assets/convert', ], 'put': [ // spot 'spot/orders', // ?symbol=<symbol>&orderID=<orderID>&origClOrdID=<origClOrdID>&clOrdID=<clOrdID>&priceEp=<priceEp>&baseQtyEV=<baseQtyEV>&quoteQtyEv=<quoteQtyEv>&stopPxEp=<stopPxEp> // swap 'orders/replace', // ?symbol=<symbol>&orderID=<orderID>&origClOrdID=<origClOrdID>&clOrdID=<clOrdID>&price=<price>&priceEp=<priceEp>&orderQty=<orderQty>&stopPx=<stopPx>&stopPxEp=<stopPxEp>&takeProfit=<takeProfit>&takeProfitEp=<takeProfitEp>&stopLoss=<stopLoss>&stopLossEp=<stopLossEp>&pegOffsetValueEp=<pegOffsetValueEp>&pegPriceType=<pegPriceType> 'positions/leverage', // ?symbol=<symbol>&leverage=<leverage>&leverageEr=<leverageEr> 'positions/riskLimit', // ?symbol=<symbol>&riskLimit=<riskLimit>&riskLimitEv=<riskLimitEv> ], 'delete': [ // spot 'spot/orders', // ?symbol=<symbol>&orderID=<orderID> 'spot/orders/all', // ?symbol=<symbol>&untriggered=<untriggered> // 'spot/orders', // ?symbol=<symbol>&clOrdID=<clOrdID> // swap 'orders/cancel', // ?symbol=<symbol>&orderID=<orderID> 'orders', // ?symbol=<symbol>&orderID=<orderID1>,<orderID2>,<orderID3> 'orders/all', // ?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, // {"code":412,"msg":"Missing parameter - resolution","data":null} '6001': BadRequest, // {"error":{"code":6001,"message":"invalid argument"},"id":null,"result":null} // documented '19999': BadRequest, // REQUEST_IS_DUPLICATED Duplicated request ID '10001': DuplicateOrderId, // OM_DUPLICATE_ORDERID Duplicated order ID '10002': OrderNotFound, // OM_ORDER_NOT_FOUND Cannot find order ID '10003': CancelPending, // OM_ORDER_PENDING_CANCEL Cannot cancel while order is already in pending cancel status '10004': CancelPending, // OM_ORDER_PENDING_REPLACE Cannot cancel while order is already in pending cancel status '10005': CancelPending, // OM_ORDER_PENDING Cannot cancel while order is already in pending cancel status '11001': InsufficientFunds, // TE_NO_ENOUGH_AVAILABLE_BALANCE Insufficient available balance '11002': InvalidOrder, // TE_INVALID_RISK_LIMIT Invalid risk limit value '11003': InsufficientFunds, // TE_NO_ENOUGH_BALANCE_FOR_NEW_RISK_LIMIT Insufficient available balance '11004': InvalidOrder, // TE_INVALID_LEVERAGE invalid input or new leverage is over maximum allowed leverage '11005': InsufficientFunds, // TE_NO_ENOUGH_BALANCE_FOR_NEW_LEVERAGE Insufficient available balance '11006': ExchangeError, // TE_CANNOT_CHANGE_POSITION_MARGIN_WITHOUT_POSITION Position size is zero. Cannot change margin '11007': ExchangeError, // TE_CANNOT_CHANGE_POSITION_MARGIN_FOR_CROSS_MARGIN Cannot change margin under CrossMargin '11008': ExchangeError, // TE_CANNOT_REMOVE_POSITION_MARGIN_MORE_THAN_ADDED exceeds the maximum removable Margin '11009': ExchangeError, // TE_CANNOT_REMOVE_POSITION_MARGIN_DUE_TO_UNREALIZED_PNL exceeds the maximum removable Margin '11010': InsufficientFunds, // TE_CANNOT_ADD_POSITION_MARGIN_DUE_TO_NO_ENOUGH_AVAILABLE_BALANCE Insufficient available balance '11011': InvalidOrder, // TE_REDUCE_ONLY_ABORT Cannot accept reduce only order '11012': InvalidOrder, // TE_REPLACE_TO_INVALID_QTY Order quantity Error '11013': InvalidOrder, // TE_CONDITIONAL_NO_POSITION Position size is zero. Cannot determine conditional order's quantity '11014': InvalidOrder, // TE_CONDITIONAL_CLOSE_POSITION_WRONG_SIDE Close position conditional order has the same side '11015': InvalidOrder, // TE_CONDITIONAL_TRIGGERED_OR_CANCELED '11016': BadRequest, // TE_ADL_NOT_TRADING_REQUESTED_ACCOUNT Request is routed to the wrong trading engine '11017': ExchangeError, // TE_ADL_CANNOT_FIND_POSITION Cannot find requested position on current account '11018': ExchangeError, // TE_NO_NEED_TO_SETTLE_FUNDING The current account does not need to pay a funding fee '11019': ExchangeError, // TE_FUNDING_ALREADY_SETTLED The current account already pays the funding fee '11020': ExchangeError, // TE_CANNOT_TRANSFER_OUT_DUE_TO_BONUS Withdraw to wallet needs to remove all remaining bonus. However if bonus is used by position or order cost, withdraw fails. '11021': ExchangeError, // TE_INVALID_BONOUS_AMOUNT // Grpc command cannot be negative number Invalid bonus amount '11022': AccountSuspended, // TE_REJECT_DUE_TO_BANNED Account is banned '11023': ExchangeError, // TE_REJECT_DUE_TO_IN_PROCESS_OF_LIQ Account is in the process of liquidation '11024': ExchangeError, // TE_REJECT_DUE_TO_IN_PROCESS_OF_ADL Account is in the process of auto-deleverage '11025': BadRequest, // TE_ROUTE_ERROR Request is routed to the wrong trading engine '11026': ExchangeError, // TE_UID_ACCOUNT_MISMATCH '11027': BadSymbol, // TE_SYMBOL_INVALID Invalid number ID or name '11028': BadSymbol, // TE_CURRENCY_INVALID Invalid currency ID or name '11029': ExchangeError, // TE_ACTION_INVALID Unrecognized request type '11030': ExchangeError, // TE_ACTION_BY_INVALID '11031': DDoSProtection, // TE_SO_NUM_EXCEEDS Number of total conditional orders exceeds the max limit '11032': DDoSProtection, // TE_AO_NUM_EXCEEDS Number of total active orders exceeds the max limit '11033': DuplicateOrderId, // TE_ORDER_ID_DUPLICATE Duplicated order ID '11034': InvalidOrder, // TE_SIDE_INVALID Invalid side '11035': InvalidOrder, // TE_ORD_TYPE_INVALID Invalid OrderType '11036': InvalidOrder, // TE_TIME_IN_FORCE_INVALID Invalid TimeInForce '11037': InvalidOrder, // TE_EXEC_INST_INVALID Invalid ExecType '11038': InvalidOrder, // TE_TRIGGER_INVALID Invalid trigger type '11039': InvalidOrder, // TE_STOP_DIRECTION_INVALID Invalid stop direction type '11040': InvalidOrder, // TE_NO_MARK_PRICE Cannot get valid mark price to create conditional order '11041': InvalidOrder, // TE_NO_INDEX_PRICE Cannot get valid index price to create conditional order '11042': InvalidOrder, // TE_NO_LAST_PRICE Cannot get valid last market price to create conditional order '11043': InvalidOrder, // TE_RISING_TRIGGER_DIRECTLY Conditional order would be triggered immediately '11044': InvalidOrder, // TE_FALLING_TRIGGER_DIRECTLY Conditional order would be triggered immediately '11045': InvalidOrder, // TE_TRIGGER_PRICE_TOO_LARGE Conditional order trigger price is too high '11046': InvalidOrder, // TE_TRIGGER_PRICE_TOO_SMALL Conditional order trigger price is too low '11047': InvalidOrder, // TE_BUY_TP_SHOULD_GT_BASE TakeProfile BUY conditional order trigger price needs to be greater than reference price '11048': InvalidOrder, // TE_BUY_SL_SHOULD_LT_BASE StopLoss BUY condition order price needs to be less than the reference price '11049': InvalidOrder, // TE_BUY_SL_SHOULD_GT_LIQ StopLoss BUY condition order price needs to be greater than liquidation price or it will not trigger '11050': InvalidOrder, // TE_SELL_TP_SHOULD_LT_BASE TakeProfile SELL conditional order trigger price needs to be less than reference price '11051': InvalidOrder, // TE_SELL_SL_SHOULD_LT_LIQ StopLoss SELL condition order price needs to be less than liquidation price or it will not trigger '11052': InvalidOrder, // TE_SELL_SL_SHOULD_GT_BASE StopLoss SELL condition order price needs to be greater than the reference price '11053': InvalidOrder, // TE_PRICE_TOO_LARGE '11054': InvalidOrder, // TE_PRICE_WORSE_THAN_BANKRUPT Order price cannot be more aggressive than bankrupt price if this order has instruction to close a position '11055': InvalidOrder, // TE_PRICE_TOO_SMALL Order price is too low '11056': InvalidOrder, // TE_QTY_TOO_LARGE Order quantity is too large '11057': InvalidOrder, // TE_QTY_NOT_MATCH_REDUCE_ONLY Does not allow ReduceOnly order without position '11058': InvalidOrder, // TE_QTY_TOO_SMALL Order quantity is too small '11059': InvalidOrder, // TE_TP_SL_QTY_NOT_MATCH_POS Position size is zero. Cannot accept any TakeProfit or StopLoss order '11060': InvalidOrder, // TE_SIDE_NOT_CLOSE_POS TakeProfit or StopLoss order has wrong side. Cannot close position '11061': CancelPending, // TE_ORD_ALREADY_PENDING_CANCEL Repeated cancel request '11062': InvalidOrder, // TE_ORD_ALREADY_CANCELED Order is already canceled '11063': InvalidOrder, // TE_ORD_STATUS_CANNOT_CANCEL Order is not able to be canceled under current status '11064': InvalidOrder, // TE_ORD_ALREADY_PENDING_REPLACE Replace request is rejected because order is already in pending replace status '11065': InvalidOrder, // TE_ORD_REPLACE_NOT_MODIFIED Replace request does not modify any parameters of the order '11066': InvalidOrder, // TE_ORD_STATUS_CANNOT_REPLACE Order is not able to be replaced under current status '11067': InvalidOrder, // TE_CANNOT_REPLACE_PRICE Market conditional order cannot change price '11068': InvalidOrder, // TE_CANNOT_REPLACE_QTY Condtional order for closing position cannot change order quantity, since the order quantity is determined by position size already '11069': ExchangeError, // TE_ACCOUNT_NOT_IN_RANGE The account ID in the request is not valid or is not in the range of the current process '11070': BadSymbol, // TE_SYMBOL_NOT_IN_RANGE The symbol is invalid '11071': InvalidOrder, // TE_ORD_STATUS_CANNOT_TRIGGER '11072': InvalidOrder, // TE_TKFR_NOT_IN_RANGE The fee value is not valid '11073': InvalidOrder, // TE_MKFR_NOT_IN_RANGE The fee value is not valid '11074': InvalidOrder, // TE_CANNOT_ATTACH_TP_SL Order request cannot contain TP/SL parameters when the account already has positions '11075': InvalidOrder, // TE_TP_TOO_LARGE TakeProfit price is too large '11076': InvalidOrder, // TE_TP_TOO_SMALL TakeProfit price is too small '11077': InvalidOrder, // TE_TP_TRIGGER_INVALID Invalid trigger type '11078': InvalidOrder, // TE_SL_TOO_LARGE StopLoss price is too large '11079': InvalidOrder, // TE_SL_TOO_SMALL StopLoss price is too small '11080': InvalidOrder, // TE_SL_TRIGGER_INVALID Invalid trigger type '11081': InvalidOrder, // TE_RISK_LIMIT_EXCEEDS Total potential position breaches current risk limit '11082': InsufficientFunds, // TE_CANNOT_COVER_ESTIMATE_ORDER_LOSS The remaining balance cannot cover the potential unrealized PnL for this new order '11083': InvalidOrder, // TE_TAKE_PROFIT_ORDER_DUPLICATED TakeProfit order already exists '11084': InvalidOrder, // TE_STOP_LOSS_ORDER_DUPLICATED StopLoss order already exists '11085': DuplicateOrderId, // TE_CL_ORD_ID_DUPLICATE ClOrdId is duplicated '11086': InvalidOrder, // TE_PEG_PRICE_TYPE_INVALID PegPriceType is invalid '11087': InvalidOrder, // TE_BUY_TS_SHOULD_LT_BASE The trailing order's StopPrice should be less than the current last price '11088': InvalidOrder, // TE_BUY_TS_SHOULD_GT_LIQ The traling order's StopPrice should be greater than the current liquidation price '11089': InvalidOrder, // TE_SELL_TS_SHOULD_LT_LIQ The traling order's StopPrice should be greater than the current last price '11090': InvalidOrder, // TE_SELL_TS_SHOULD_GT_BASE The traling order's StopPrice should be less than the current liquidation price '11091': InvalidOrder, // TE_BUY_REVERT_VALUE_SHOULD_LT_ZERO The PegOffset should be less than zero '11092': InvalidOrder, // TE_SELL_REVERT_VALUE_SHOULD_GT_ZERO The PegOffset should be greater than zero '11093': InvalidOrder, // TE_BUY_TTP_SHOULD_ACTIVATE_ABOVE_BASE The activation price should be greater than the current last price '11094': InvalidOrder, // TE_SELL_TTP_SHOULD_ACTIVATE_BELOW_BASE The activation price should be less than the current last price '11095': InvalidOrder, // TE_TRAILING_ORDER_DUPLICATED A trailing order exists already '11096': InvalidOrder, // TE_CLOSE_ORDER_CANNOT_ATTACH_TP_SL An order to close position cannot have trailing instruction '11097': BadRequest, // TE_CANNOT_FIND_WALLET_OF_THIS_CURRENCY This crypto is not supported '11098': BadRequest, // TE_WALLET_INVALID_ACTION Invalid action on wallet '11099': ExchangeError, // TE_WALLET_VID_UNMATCHED Wallet operation request has a wrong wallet vid '11100': InsufficientFunds, // TE_WALLET_INSUFFICIENT_BALANCE Wallet has insufficient balance '11101': InsufficientFunds, // TE_WALLET_INSUFFICIENT_LOCKED_BALANCE Locked balance in wallet is not enough for unlock/withdraw request '11102': BadRequest, // TE_WALLET_INVALID_DEPOSIT_AMOUNT Deposit amount must be greater than zero '11103': BadRequest, // TE_WALLET_INVALID_WITHDRAW_AMOUNT Withdraw amount must be less than zero '11104': BadRequest, // TE_WALLET_REACHED_MAX_AMOUNT Deposit makes wallet exceed max amount allowed '11105': InsufficientFunds, // TE_PLACE_ORDER_INSUFFICIENT_BASE_BALANCE Insufficient funds in base wallet '11106': InsufficientFunds, // TE_PLACE_ORDER_INSUFFICIENT_QUOTE_BALANCE Insufficient funds in quote wallet '11107': ExchangeError, // TE_CANNOT_CONNECT_TO_REQUEST_SEQ TradingEngine failed to connect with CrossEngine '11108': InvalidOrder, // TE_CANNOT_REPLACE_OR_CANCEL_MARKET_ORDER Cannot replace/amend market order '11109': InvalidOrder, // TE_CANNOT_REPLACE_OR_CANCEL_IOC_ORDER Cannot replace/amend ImmediateOrCancel order '11110': InvalidOrder, // TE_CANNOT_REPLACE_OR_CANCEL_FOK_ORDER Cannot replace/amend FillOrKill order '11111': InvalidOrder, // TE_MISSING_ORDER_ID OrderId is missing '11112': InvalidOrder, // TE_QTY_TYPE_INVALID QtyType is invalid '11113': BadRequest, // TE_USER_ID_INVALID UserId is invalid '11114': InvalidOrder, // TE_ORDER_VALUE_TOO_LARGE Order value is too large '11115': InvalidOrder, // TE_ORDER_VALUE_TOO_SMALL Order value is too small '11116': InvalidOrder, // TE_BO_NUM_EXCEEDS Details: the total count of brakcet orders should equal or less than 5 '11117': InvalidOrder, // TE_BO_CANNOT_HAVE_BO_WITH_DIFF_SIDE Details: all bracket orders should have the same Side. '11118': InvalidOrder, // TE_BO_TP_PRICE_INVALID Details: bracker order take profit price is invalid '11119': InvalidOrder, // TE_BO_SL_PRICE_INVALID Details: bracker order stop loss price is invalid '11120': InvalidOrder, // TE_BO_SL_TRIGGER_PRICE_INVALID Details: bracker order stop loss trigger price is invalid '11121': InvalidOrder, // TE_BO_CANNOT_REPLACE Details: cannot replace bracket order. '11122': InvalidOrder, // TE_BO_BOTP_STATUS_INVALID Details: bracket take profit order status is invalid '11123': InvalidOrder, // TE_BO_CANNOT_PLACE_BOTP_OR_BOSL_ORDER Details: cannot place bracket take profit order '11124': InvalidOrder, // TE_BO_CANNOT_REPLACE_BOTP_OR_BOSL_ORDER Details: cannot place bracket stop loss order '11125': InvalidOrder, // TE_BO_CANNOT_CANCEL_BOTP_OR_BOSL_ORDER Details: cannot cancel bracket sl/tp order '11126': InvalidOrder, // TE_BO_DONOT_SUPPORT_API Details: doesn't support bracket order via API '11128': InvalidOrder, // TE_BO_INVALID_EXECINST Details: ExecInst value is invalid '11129': InvalidOrder, // TE_BO_MUST_BE_SAME_SIDE_AS_POS Details: bracket order should have the same side as position's side '11130': InvalidOrder, // TE_BO_WRONG_SL_TRIGGER_TYPE Details: bracket stop loss order trigger type is invalid '11131': InvalidOrder, // TE_BO_WRONG_TP_TRIGGER_TYPE Details: bracket take profit order trigger type is invalid '11132': InvalidOrder, // TE_BO_ABORT_BOSL_DUE_BOTP_CREATE_FAILED Details: cancel bracket stop loss order due failed to create take profit order. '11133': InvalidOrder, // TE_BO_ABORT_BOSL_DUE_BOPO_CANCELED Details: cancel bracket stop loss order due main order canceled. '11134': InvalidOrder, // TE_BO_ABORT_BOTP_DUE_BOPO_CANCELED Details: cancel bracket take profit order due main order canceled. // not documented '30000': BadRequest, // {"code":30000,"msg":"Please double check input arguments","data":null} '30018': BadRequest, // {"code":30018,"msg":"phemex.data.size.uplimt","data":null} '34003': PermissionDenied, // {"code":34003,"msg":"Access forbidden","data":null} '35104': InsufficientFunds, // {"code":35104,"msg":"phemex.spot.wallet.balance.notenough","data":null} '39995': RateLimitExceeded, // {"code": "39995","msg": "Too many requests."} '39996': PermissionDenied, // {"code": "39996","msg": "Access denied."} }, 'broad': { '401 Insufficient privilege': PermissionDenied, // {"code": "401","msg": "401 Insufficient privilege."} '401 Request IP mismatch': PermissionDenied, // {"code": "401","msg": "401 Request IP mismatch."} 'Failed to find api-key': AuthenticationError, // {"msg":"Failed to find api-key 1c5ec63fd-660d-43ea-847a-0d3ba69e106e","code":10500} 'Missing required parameter': BadRequest, // {"msg":"Missing required parameter","code":10500} 'API Signature verification failed': AuthenticationError, // {"msg":"API Signature verification failed.","code":10500} '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, // in seconds '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.safeNumber (market, 'lotSize'), '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", // "displaySymbol":"BTC / USDT", // "quoteCurrency":"USDT", // "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":20 // } // 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': 8, 'valueScale': 8, 'ratioScale': 8, '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":[ // {"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"}, // ], // "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", // "displaySymbol":"BTC / USDT", // "quoteCurrency":"USDT", // "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 // }, // ], // "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 riskLimitsById = this.indexBy (riskLimits, 'symbol'); const v1ProductsById = this.indexBy (v1ProductsData, 'symbol'); const result = []; for (let i = 0; i < products.length; i++) { let market = products[i]; const type = this.safeStringLower (market, 'type'); if (type === 'perpetual') { 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 { 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,"minValu