sfccxt
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges
813 lines (804 loc) • 175 kB
JavaScript
'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¤cy=USDT
'phemex-user/wallets/tradeAccountDetail', // ?bizCode=¤cy=&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>"eQtyEv=<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