# -*- coding: utf-8 -*- # 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 from ccxt.base.exchange import Exchange from ccxt.abstract.xt import ImplicitAPI import hashlib from ccxt.base.types import Any, Currencies, Currency, DepositAddress, Int, LedgerEntry, LeverageTier, LeverageTiers, MarginModification, Market, Num, Order, OrderSide, OrderType, Position, Str, Tickers, FundingRate, Transaction, TransferEntry from typing import List from ccxt.base.errors import ExchangeError from ccxt.base.errors import AuthenticationError from ccxt.base.errors import PermissionDenied from ccxt.base.errors import ArgumentsRequired from ccxt.base.errors import BadRequest from ccxt.base.errors import BadSymbol from ccxt.base.errors import InsufficientFunds from ccxt.base.errors import InvalidOrder from ccxt.base.errors import NotSupported from ccxt.base.errors import NetworkError from ccxt.base.errors import RateLimitExceeded from ccxt.base.errors import OnMaintenance from ccxt.base.errors import RequestTimeout from ccxt.base.decimal_to_precision import TICK_SIZE from ccxt.base.precise import Precise class xt(Exchange, ImplicitAPI): def describe(self) -> Any: return self.deep_extend(super(xt, self).describe(), { 'id': 'xt', 'name': 'XT', 'countries': ['SC'], # Seychelles # spot api ratelimits are None, 10/s/ip, 50/s/ip, 100/s/ip or 200/s/ip # futures 3 requests per second => 1000ms / (100 * 3.33) = 3.003(get assets -> fetchMarkets & fetchCurrencies) # futures 10 requests per second => 1000ms / (100 * 1) = 10(all other) # futures 1000 times per minute for each single IP -> Otherwise account locked for 10min 'rateLimit': 100, 'version': 'v4', 'certified': False, 'pro': True, 'has': { 'CORS': False, 'spot': True, 'margin': True, 'swap': True, 'future': True, 'option': False, 'addMargin': True, 'borrowMargin': False, 'cancelAllOrders': True, 'cancelOrder': True, 'cancelOrders': True, 'createDepositAddress': False, 'createMarketBuyOrderWithCost': True, 'createMarketSellOrderWithCost': False, 'createOrder': True, 'createPostOnlyOrder': False, 'createReduceOnlyOrder': True, 'editOrder': True, 'fetchAccounts': False, 'fetchBalance': True, 'fetchBidsAsks': True, 'fetchBorrowInterest': False, 'fetchBorrowRate': False, 'fetchBorrowRateHistories': False, 'fetchBorrowRateHistory': False, 'fetchBorrowRatesPerSymbol': False, 'fetchCanceledOrders': True, 'fetchClosedOrders': True, 'fetchCurrencies': True, 'fetchDeposit': False, 'fetchDepositAddress': True, 'fetchDepositAddresses': False, 'fetchDepositAddressesByNetwork': False, 'fetchDeposits': True, 'fetchDepositWithdrawals': False, 'fetchDepositWithdrawFee': False, 'fetchDepositWithdrawFees': False, 'fetchFundingHistory': True, 'fetchFundingInterval': True, 'fetchFundingIntervals': False, 'fetchFundingRate': True, 'fetchFundingRateHistory': True, 'fetchFundingRates': False, 'fetchIndexOHLCV': False, 'fetchL3OrderBook': False, 'fetchLedger': True, 'fetchLedgerEntry': False, 'fetchLeverage': False, 'fetchLeverageTiers': True, 'fetchMarketLeverageTiers': True, 'fetchMarkets': True, 'fetchMarkOHLCV': False, 'fetchMyTrades': True, 'fetchOHLCV': True, 'fetchOpenInterest': False, 'fetchOpenInterestHistory': False, 'fetchOpenOrders': True, 'fetchOrder': True, 'fetchOrderBook': True, 'fetchOrderBooks': False, 'fetchOrders': True, 'fetchOrdersByStatus': True, 'fetchOrderTrades': False, 'fetchPosition': True, 'fetchPositions': True, 'fetchPremiumIndexOHLCV': False, 'fetchSettlementHistory': False, 'fetchStatus': False, 'fetchTicker': True, 'fetchTickers': True, 'fetchTime': True, 'fetchTrades': True, 'fetchTradingFee': False, 'fetchTradingFees': False, 'fetchTradingLimits': False, 'fetchTransactionFee': False, 'fetchTransactionFees': False, 'fetchTransactions': False, 'fetchTransfer': False, 'fetchTransfers': False, 'fetchWithdrawal': False, 'fetchWithdrawals': True, 'fetchWithdrawalWhitelist': False, 'reduceMargin': True, 'repayMargin': False, 'setLeverage': True, 'setMargin': False, 'setMarginMode': True, 'setPositionMode': False, 'signIn': False, 'transfer': True, 'withdraw': True, }, 'precisionMode': TICK_SIZE, 'urls': { 'logo': 'https://user-images.githubusercontent.com/14319357/232636712-466df2fc-560a-4ca4-aab2-b1d954a58e24.jpg', 'api': { 'spot': 'https://sapi.xt.com', 'linear': 'https://fapi.xt.com', 'inverse': 'https://dapi.xt.com', 'user': 'https://api.xt.com', }, 'www': 'https://xt.com', 'referral': 'https://www.xt.com/en/accounts/register?ref=9PTM9VW', 'doc': [ 'https://doc.xt.com/', 'https://github.com/xtpub/api-doc', ], 'fees': 'https://www.xt.com/en/rate', }, 'api': { 'public': { 'spot': { 'get': { 'currencies': 1, 'depth': 10, 'kline': 1, 'symbol': 1, # 1 for a single symbol 'ticker': 1, # 1 for a single symbol 'ticker/book': 1, # 1 for a single symbol 'ticker/price': 1, # 1 for a single symbol 'ticker/24h': 1, # 1 for a single symbol 'time': 1, 'trade/history': 1, 'trade/recent': 1, 'wallet/support/currency': 1, }, }, 'linear': { 'get': { 'future/market/v1/public/contract/risk-balance': 1, 'future/market/v1/public/contract/open-interest': 1, 'future/market/v1/public/leverage/bracket/detail': 1, 'future/market/v1/public/leverage/bracket/list': 1, 'future/market/v1/public/q/agg-ticker': 1, 'future/market/v1/public/q/agg-tickers': 1, 'future/market/v1/public/q/deal': 1, 'future/market/v1/public/q/depth': 1, 'future/market/v1/public/q/funding-rate': 1, 'future/market/v1/public/q/funding-rate-record': 1, 'future/market/v1/public/q/index-price': 1, 'future/market/v1/public/q/kline': 1, 'future/market/v1/public/q/mark-price': 1, 'future/market/v1/public/q/symbol-index-price': 1, 'future/market/v1/public/q/symbol-mark-price': 1, 'future/market/v1/public/q/ticker': 1, 'future/market/v1/public/q/tickers': 1, 'future/market/v1/public/symbol/coins': 3.33, 'future/market/v1/public/symbol/detail': 3.33, 'future/market/v1/public/symbol/list': 1, }, }, 'inverse': { 'get': { 'future/market/v1/public/contract/risk-balance': 1, 'future/market/v1/public/contract/open-interest': 1, 'future/market/v1/public/leverage/bracket/detail': 1, 'future/market/v1/public/leverage/bracket/list': 1, 'future/market/v1/public/q/agg-ticker': 1, 'future/market/v1/public/q/agg-tickers': 1, 'future/market/v1/public/q/deal': 1, 'future/market/v1/public/q/depth': 1, 'future/market/v1/public/q/funding-rate': 1, 'future/market/v1/public/q/funding-rate-record': 1, 'future/market/v1/public/q/index-price': 1, 'future/market/v1/public/q/kline': 1, 'future/market/v1/public/q/mark-price': 1, 'future/market/v1/public/q/symbol-index-price': 1, 'future/market/v1/public/q/symbol-mark-price': 1, 'future/market/v1/public/q/ticker': 1, 'future/market/v1/public/q/tickers': 1, 'future/market/v1/public/symbol/coins': 3.33, 'future/market/v1/public/symbol/detail': 3.33, 'future/market/v1/public/symbol/list': 1, }, }, }, 'private': { 'spot': { 'get': { 'balance': 1, 'balances': 1, 'batch-order': 1, 'deposit/address': 1, 'deposit/history': 1, 'history-order': 1, 'open-order': 1, 'order': 1, 'order/{orderId}': 1, 'trade': 1, 'withdraw/history': 1, }, 'post': { 'order': 0.2, 'withdraw': 10, 'balance/transfer': 1, 'balance/account/transfer': 1, 'ws-token': 1, }, 'delete': { 'batch-order': 1, 'open-order': 1, 'order/{orderId}': 1, }, 'put': { 'order/{orderId}': 1, }, }, 'linear': { 'get': { 'future/trade/v1/entrust/plan-detail': 1, 'future/trade/v1/entrust/plan-list': 1, 'future/trade/v1/entrust/plan-list-history': 1, 'future/trade/v1/entrust/profit-detail': 1, 'future/trade/v1/entrust/profit-list': 1, 'future/trade/v1/order/detail': 1, 'future/trade/v1/order/list': 1, 'future/trade/v1/order/list-history': 1, 'future/trade/v1/order/trade-list': 1, 'future/user/v1/account/info': 1, 'future/user/v1/balance/bills': 1, 'future/user/v1/balance/detail': 1, 'future/user/v1/balance/funding-rate-list': 1, 'future/user/v1/balance/list': 1, 'future/user/v1/position/adl': 1, 'future/user/v1/position/list': 1, 'future/user/v1/user/collection/list': 1, 'future/user/v1/user/listen-key': 1, }, 'post': { 'future/trade/v1/entrust/cancel-all-plan': 1, 'future/trade/v1/entrust/cancel-all-profit-stop': 1, 'future/trade/v1/entrust/cancel-plan': 1, 'future/trade/v1/entrust/cancel-profit-stop': 1, 'future/trade/v1/entrust/create-plan': 1, 'future/trade/v1/entrust/create-profit': 1, 'future/trade/v1/entrust/update-profit-stop': 1, 'future/trade/v1/order/cancel': 1, 'future/trade/v1/order/cancel-all': 1, 'future/trade/v1/order/create': 1, 'future/trade/v1/order/create-batch': 1, 'future/trade/v1/order/update': 1, 'future/user/v1/account/open': 1, 'future/user/v1/position/adjust-leverage': 1, 'future/user/v1/position/auto-margin': 1, 'future/user/v1/position/close-all': 1, 'future/user/v1/position/margin': 1, 'future/user/v1/user/collection/add': 1, 'future/user/v1/user/collection/cancel': 1, 'future/user/v1/position/change-type': 1, }, }, 'inverse': { 'get': { 'future/trade/v1/entrust/plan-detail': 1, 'future/trade/v1/entrust/plan-list': 1, 'future/trade/v1/entrust/plan-list-history': 1, 'future/trade/v1/entrust/profit-detail': 1, 'future/trade/v1/entrust/profit-list': 1, 'future/trade/v1/order/detail': 1, 'future/trade/v1/order/list': 1, 'future/trade/v1/order/list-history': 1, 'future/trade/v1/order/trade-list': 1, 'future/user/v1/account/info': 1, 'future/user/v1/balance/bills': 1, 'future/user/v1/balance/detail': 1, 'future/user/v1/balance/funding-rate-list': 1, 'future/user/v1/balance/list': 1, 'future/user/v1/position/adl': 1, 'future/user/v1/position/list': 1, 'future/user/v1/user/collection/list': 1, 'future/user/v1/user/listen-key': 1, }, 'post': { 'future/trade/v1/entrust/cancel-all-plan': 1, 'future/trade/v1/entrust/cancel-all-profit-stop': 1, 'future/trade/v1/entrust/cancel-plan': 1, 'future/trade/v1/entrust/cancel-profit-stop': 1, 'future/trade/v1/entrust/create-plan': 1, 'future/trade/v1/entrust/create-profit': 1, 'future/trade/v1/entrust/update-profit-stop': 1, 'future/trade/v1/order/cancel': 1, 'future/trade/v1/order/cancel-all': 1, 'future/trade/v1/order/create': 1, 'future/trade/v1/order/create-batch': 1, 'future/trade/v1/order/update': 1, 'future/user/v1/account/open': 1, 'future/user/v1/position/adjust-leverage': 1, 'future/user/v1/position/auto-margin': 1, 'future/user/v1/position/close-all': 1, 'future/user/v1/position/margin': 1, 'future/user/v1/user/collection/add': 1, 'future/user/v1/user/collection/cancel': 1, }, }, 'user': { 'get': { 'user/account': 1, 'user/account/api-key': 1, }, 'post': { 'user/account': 1, 'user/account/api-key': 1, }, 'put': { 'user/account/api-key': 1, }, 'delete': { 'user/account/{apiKeyId}': 1, }, }, }, }, 'fees': { 'spot': { 'tierBased': True, 'percentage': True, 'maker': self.parse_number('0.002'), 'taker': self.parse_number('0.002'), 'tiers': { 'maker': [ [self.parse_number('0'), self.parse_number('0.002')], [self.parse_number('5000'), self.parse_number('0.0018')], [self.parse_number('10000'), self.parse_number('0.0016')], [self.parse_number('20000'), self.parse_number('0.0014')], [self.parse_number('50000'), self.parse_number('0.0012')], [self.parse_number('150000'), self.parse_number('0.0010')], [self.parse_number('300000'), self.parse_number('0.0008')], [self.parse_number('600000'), self.parse_number('0.0007')], [self.parse_number('1200000'), self.parse_number('0.0006')], [self.parse_number('2500000'), self.parse_number('0.0005')], [self.parse_number('6000000'), self.parse_number('0.0004')], [self.parse_number('15000000'), self.parse_number('0.0003')], [self.parse_number('30000000'), self.parse_number('0.0002')], ], 'taker': [ [self.parse_number('0'), self.parse_number('0.002')], [self.parse_number('5000'), self.parse_number('0.0018')], [self.parse_number('10000'), self.parse_number('0.0016')], [self.parse_number('20000'), self.parse_number('0.0014')], [self.parse_number('50000'), self.parse_number('0.0012')], [self.parse_number('150000'), self.parse_number('0.0010')], [self.parse_number('300000'), self.parse_number('0.0008')], [self.parse_number('600000'), self.parse_number('0.0007')], [self.parse_number('1200000'), self.parse_number('0.0006')], [self.parse_number('2500000'), self.parse_number('0.0005')], [self.parse_number('6000000'), self.parse_number('0.0004')], [self.parse_number('15000000'), self.parse_number('0.0003')], [self.parse_number('30000000'), self.parse_number('0.0002')], ], }, }, 'contract': { 'tierBased': True, 'percentage': True, 'maker': self.parse_number('0.0004'), 'taker': self.parse_number('0.0006'), 'tiers': { 'maker': [ [self.parse_number('0'), self.parse_number('0.0004')], [self.parse_number('200000'), self.parse_number('0.00038')], [self.parse_number('1000000'), self.parse_number('0.00036')], [self.parse_number('5000000'), self.parse_number('0.00034')], [self.parse_number('10000000'), self.parse_number('0.00032')], [self.parse_number('15000000'), self.parse_number('0.00028')], [self.parse_number('30000000'), self.parse_number('0.00024')], [self.parse_number('50000000'), self.parse_number('0.0002')], [self.parse_number('100000000'), self.parse_number('0.00016')], [self.parse_number('300000000'), self.parse_number('0.00012')], [self.parse_number('500000000'), self.parse_number('0.00008')], ], 'taker': [ [self.parse_number('0'), self.parse_number('0.0006')], [self.parse_number('200000'), self.parse_number('0.000588')], [self.parse_number('1000000'), self.parse_number('0.00057')], [self.parse_number('5000000'), self.parse_number('0.00054')], [self.parse_number('10000000'), self.parse_number('0.00051')], [self.parse_number('15000000'), self.parse_number('0.00048')], [self.parse_number('30000000'), self.parse_number('0.00045')], [self.parse_number('50000000'), self.parse_number('0.00045')], [self.parse_number('100000000'), self.parse_number('0.00036')], [self.parse_number('300000000'), self.parse_number('0.00033')], [self.parse_number('500000000'), self.parse_number('0.0003')], ], }, }, }, 'exceptions': { 'exact': { '400': NetworkError, # {"returnCode":1,"msgInfo":"failure","error":{"code":"400","msg":"Connection refused: /10.0.26.71:8080"},"result":null} '404': ExchangeError, # interface does not exist '429': RateLimitExceeded, # The request is too frequent, please control the request rate according to the speed limit requirement '500': ExchangeError, # Service exception '502': ExchangeError, # Gateway exception '503': OnMaintenance, # Service unavailable, please try again later 'AUTH_001': AuthenticationError, # missing request header xt-validate-appkey 'AUTH_002': AuthenticationError, # missing request header xt-validate-timestamp 'AUTH_003': AuthenticationError, # missing request header xt-validate-recvwindow 'AUTH_004': AuthenticationError, # bad request header xt-validate-recvwindow 'AUTH_005': AuthenticationError, # missing request header xt-validate-algorithms 'AUTH_006': AuthenticationError, # bad request header xt-validate-algorithms 'AUTH_007': AuthenticationError, # missing request header xt-validate-signature 'AUTH_101': AuthenticationError, # ApiKey does not exist 'AUTH_102': AuthenticationError, # ApiKey is not activated 'AUTH_103': AuthenticationError, # Signature error, {"rc":1,"mc":"AUTH_103","ma":[],"result":null} 'AUTH_104': AuthenticationError, # Unbound IP request 'AUTH_105': AuthenticationError, # outdated message 'AUTH_106': PermissionDenied, # Exceeded apikey permission 'SYMBOL_001': BadSymbol, # Symbol not exist 'SYMBOL_002': BadSymbol, # Symbol offline 'SYMBOL_003': BadSymbol, # Symbol suspend trading 'SYMBOL_004': BadSymbol, # Symbol country disallow trading 'SYMBOL_005': BadSymbol, # The symbol does not support trading via API 'ORDER_001': InvalidOrder, # Platform rejection 'ORDER_002': InsufficientFunds, # insufficient funds 'ORDER_003': InvalidOrder, # Trading Pair Suspended 'ORDER_004': InvalidOrder, # no transaction 'ORDER_005': InvalidOrder, # Order not exist 'ORDER_006': InvalidOrder, # Too many open orders 'ORDER_007': PermissionDenied, # The sub-account has no transaction authority 'ORDER_F0101': InvalidOrder, # Trigger Price Filter - Min 'ORDER_F0102': InvalidOrder, # Trigger Price Filter - Max 'ORDER_F0103': InvalidOrder, # Trigger Price Filter - Step Value 'ORDER_F0201': InvalidOrder, # Trigger Quantity Filter - Min 'ORDER_F0202': InvalidOrder, # Trigger Quantity Filter - Max 'ORDER_F0203': InvalidOrder, # Trigger Quantity Filter - Step Value 'ORDER_F0301': InvalidOrder, # Trigger QUOTE_QTY Filter - Min Value 'ORDER_F0401': InvalidOrder, # Trigger PROTECTION_ONLINE Filter 'ORDER_F0501': InvalidOrder, # Trigger PROTECTION_LIMIT Filter - Buy Max Deviation 'ORDER_F0502': InvalidOrder, # Trigger PROTECTION_LIMIT Filter - Sell Max Deviation 'ORDER_F0601': InvalidOrder, # Trigger PROTECTION_MARKET Filter 'COMMON_001': ExchangeError, # The user does not exist 'COMMON_002': ExchangeError, # System busy, please try it later 'COMMON_003': BadRequest, # Operation failed, please try it later 'CURRENCY_001': BadRequest, # Information of currency is abnormal 'DEPOSIT_001': BadRequest, # Deposit is not open 'DEPOSIT_002': PermissionDenied, # The current account security level is low, please bind any two security verifications in mobile phone/email/Google Authenticator before deposit 'DEPOSIT_003': BadRequest, # The format of address is incorrect, please enter again 'DEPOSIT_004': BadRequest, # The address is already exists, please enter again 'DEPOSIT_005': BadRequest, # Can not find the address of offline wallet 'DEPOSIT_006': BadRequest, # No deposit address, please try it later 'DEPOSIT_007': BadRequest, # Address is being generated, please try it later 'DEPOSIT_008': BadRequest, # Deposit is not available 'WITHDRAW_001': BadRequest, # Withdraw is not open 'WITHDRAW_002': BadRequest, # The withdrawal address is invalid 'WITHDRAW_003': PermissionDenied, # The current account security level is low, please bind any two security verifications in mobile phone/email/Google Authenticator before withdraw 'WITHDRAW_004': BadRequest, # The withdrawal address is not added 'WITHDRAW_005': BadRequest, # The withdrawal address cannot be empty 'WITHDRAW_006': BadRequest, # Memo cannot be empty 'WITHDRAW_008': PermissionDenied, # Risk control is triggered, withdraw of self currency is not currently supported 'WITHDRAW_009': PermissionDenied, # Withdraw failed, some hasattr(self, assets) withdraw are restricted by T+1 withdraw 'WITHDRAW_010': BadRequest, # The precision of withdrawal is invalid 'WITHDRAW_011': InsufficientFunds, # free balance is not enough 'WITHDRAW_012': PermissionDenied, # Withdraw failed, your remaining withdrawal limit today is not enough 'WITHDRAW_013': PermissionDenied, # Withdraw failed, your remaining withdrawal limit today is not enough, the withdrawal amount can be increased by completing a higher level of real-name authentication 'WITHDRAW_014': BadRequest, # This withdrawal address cannot be used in the internal transfer function, please cancel the internal transfer function before submitting 'WITHDRAW_015': BadRequest, # The withdrawal amount is not enough to deduct the handling fee 'WITHDRAW_016': BadRequest, # This withdrawal address is already exists 'WITHDRAW_017': BadRequest, # This withdrawal has been processed and cannot be canceled 'WITHDRAW_018': BadRequest, # Memo must be a number 'WITHDRAW_019': BadRequest, # Memo is incorrect, please enter again 'WITHDRAW_020': PermissionDenied, # Your withdrawal amount has reached the upper limit for today, please try it tomorrow 'WITHDRAW_021': PermissionDenied, # Your withdrawal amount has reached the upper limit for today, you can only withdraw up to {0} self time 'WITHDRAW_022': BadRequest, # Withdrawal amount must be greater than {0} 'WITHDRAW_023': BadRequest, # Withdrawal amount must be less than {0} 'WITHDRAW_024': BadRequest, # Withdraw is not supported 'WITHDRAW_025': BadRequest, # Please create a FIO address in the deposit page 'FUND_001': BadRequest, # Duplicate request(a bizId can only be requested once) 'FUND_002': InsufficientFunds, # Insufficient account balance 'FUND_003': BadRequest, # Transfer operations are not supported(for example, sub-accounts do not support financial transfers) 'FUND_004': ExchangeError, # Unfreeze failed 'FUND_005': PermissionDenied, # Transfer prohibited 'FUND_014': BadRequest, # The transfer-in account id and transfer-out account ID cannot be the same 'FUND_015': BadRequest, # From and to business types cannot be the same 'FUND_016': BadRequest, # Leverage transfer, symbol cannot be empty 'FUND_017': BadRequest, # Parameter error 'FUND_018': BadRequest, # Invalid freeze record 'FUND_019': BadRequest, # Freeze users not equal 'FUND_020': BadRequest, # Freeze currency are not equal 'FUND_021': BadRequest, # Operation not supported 'FUND_022': BadRequest, # Freeze record does not exist 'FUND_044': BadRequest, # The maximum length of the amount is 113 and cannot exceed the limit 'TRANSFER_001': BadRequest, # Duplicate request(a bizId can only be requested once) 'TRANSFER_002': InsufficientFunds, # Insufficient account balance 'TRANSFER_003': BadRequest, # User not registered 'TRANSFER_004': PermissionDenied, # The currency is not allowed to be transferred 'TRANSFER_005': PermissionDenied, # The user’s currency is not allowed to be transferred 'TRANSFER_006': PermissionDenied, # Transfer prohibited 'TRANSFER_007': RequestTimeout, # Request timed out 'TRANSFER_008': BadRequest, # Transferring to a leveraged account is abnormal 'TRANSFER_009': BadRequest, # Departing from a leveraged account is abnormal 'TRANSFER_010': PermissionDenied, # Leverage cleared, transfer prohibited 'TRANSFER_011': PermissionDenied, # Leverage with borrowing, transfer prohibited 'TRANSFER_012': PermissionDenied, # Currency transfer prohibited 'symbol_not_support_trading_via_api': BadSymbol, # {"returnCode":1,"msgInfo":"failure","error":{"code":"symbol_not_support_trading_via_api","msg":"The symbol does not support trading via API"},"result":null} 'open_order_min_nominal_value_limit': InvalidOrder, # {"returnCode":1,"msgInfo":"failure","error":{"code":"open_order_min_nominal_value_limit","msg":"Exceeds the minimum notional value of a single order"},"result":null} 'insufficient_balance': InsufficientFunds, }, 'broad': { 'The symbol does not support trading via API': BadSymbol, # {"returnCode":1,"msgInfo":"failure","error":{"code":"symbol_not_support_trading_via_api","msg":"The symbol does not support trading via API"},"result":null} 'Exceeds the minimum notional value of a single order': InvalidOrder, # {"returnCode":1,"msgInfo":"failure","error":{"code":"open_order_min_nominal_value_limit","msg":"Exceeds the minimum notional value of a single order"},"result":null} 'insufficient balance': InsufficientFunds, }, }, 'timeframes': { '1m': '1m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', # spot only '2h': '2h', # spot only '4h': '4h', '6h': '6h', # spot only '8h': '8h', # spot only '1d': '1d', '3d': '3d', # spot only '1w': '1w', '1M': '1M', # spot only }, 'commonCurrencies': {}, 'options': { 'adjustForTimeDifference': False, 'timeDifference': 0, 'accountsById': { 'spot': 'SPOT', 'leverage': 'LEVER', 'finance': 'FINANCE', 'swap': 'FUTURES_U', 'future': 'FUTURES_U', 'linear': 'FUTURES_U', 'inverse': 'FUTURES_C', }, 'networks': { 'ERC20': 'Ethereum', 'TRC20': 'Tron', 'BEP20': 'BNB Smart Chain', 'BEP2': 'BNB-BEP2', 'ETH': 'Ethereum', 'TRON': 'Tron', 'BNB': 'BNB Smart Chain', 'AVAX': 'AVAX C-Chain', 'GAL': 'GAL(FT)', 'ALEO': 'ALEO(IOU)', 'BTC': 'Bitcoin', 'XT': 'XT Smart Chain', 'ETC': 'Ethereum Classic', 'MATIC': 'Polygon', 'LTC': 'Litecoin', 'BTS': 'BitShares', 'XRP': 'Ripple', 'XLM': 'Stellar Network', 'ADA': 'Cardano', 'XWC': 'XWC-XWC', 'DOGE': 'dogecoin', 'DCR': 'Decred', 'SC': 'Siacoin', 'XTZ': 'Tezos', 'ZEC': 'Zcash', 'XMR': 'Monero', 'LSK': 'Lisk', 'ATOM': 'Cosmos', 'ONT': 'Ontology', 'ALGO': 'Algorand', 'SOL': 'SOL-SOL', 'DOT': 'Polkadot', 'ZEN': 'Horizen', 'FIL': 'Filecoin', 'CHZ': 'chz', 'ICP': 'Internet Computer', 'KSM': 'Kusama', 'LUNA': 'Terra', 'THETA': 'Theta Token', 'FTM': 'Fantom', 'VET': 'VeChain', 'NEAR': 'NEAR Protocol', 'ONE': 'Harmony', 'KLAY': 'Klaytn', 'AR': 'Arweave', 'CELT': 'OKT', 'EGLD': 'Elrond eGold', 'CRO': 'CRO-CRONOS', 'BCH': 'Bitcoin Cash', 'GLMR': 'Moonbeam', 'LOOP': 'LOOP-LRC', 'REI': 'REI Network', 'ASTR': 'Astar Network', 'OP': 'OPT', 'MMT': 'MMT-MMT', 'TBC': 'TBC-TBC', 'OMAX': 'OMAX-OMAX CHAIN', 'GMMT': 'GMMT chain', 'ZIL': 'Zilliqa', }, 'networksById': { 'Ethereum': 'ERC20', 'Tron': 'TRC20', 'BNB Smart Chain': 'BEP20', 'BNB-BEP2': 'BEP2', 'Bitcoin': 'BTC', 'XT Smart Chain': 'XT', 'Ethereum Classic': 'ETC', 'Polygon': 'MATIC', 'Litecoin': 'LTC', 'BitShares': 'BTS', 'Ripple': 'XRP', 'Stellar Network': 'XLM', 'Cardano': 'ADA', 'XWC-XWC': 'XWC', 'dogecoin': 'DOGE', 'Decred': 'DCR', 'Siacoin': 'SC', 'Tezos': 'XTZ', 'Zcash': 'ZEC', 'Monero': 'XMR', 'Lisk': 'LSK', 'Cosmos': 'ATOM', 'Ontology': 'ONT', 'Algorand': 'ALGO', 'SOL-SOL': 'SOL', 'Polkadot': 'DOT', 'Horizen': 'ZEN', 'Filecoin': 'FIL', 'chz': 'CHZ', 'Internet Computer': 'ICP', 'Kusama': 'KSM', 'Terra': 'LUNA', 'Theta Token': 'THETA', 'Fantom': 'FTM', 'VeChain': 'VET', 'AVAX C-Chain': 'AVAX', 'NEAR Protocol': 'NEAR', 'Harmony': 'ONE', 'Klaytn': 'KLAY', 'Arweave': 'AR', 'OKT': 'CELT', 'Elrond eGold': 'EGLD', 'CRO-CRONOS': 'CRO', 'Bitcoin Cash': 'BCH', 'Moonbeam': 'GLMR', 'LOOP-LRC': 'LOOP', 'REI Network': 'REI', 'Astar Network': 'ASTR', 'GAL(FT)': 'GAL', 'ALEO(IOU)': 'ALEO', 'OPT': 'OP', 'MMT-MMT': 'MMT', 'TBC-TBC': 'TBC', 'OMAX-OMAX CHAIN': 'OMAX', 'GMMT chain': 'GMMT', 'Zilliqa': 'ZIL', }, 'createMarketBuyOrderRequiresPrice': True, 'recvWindow': '5000', # in milliseconds, spot only }, 'features': { 'default': { 'sandbox': False, 'createOrder': { 'marginMode': False, 'triggerPrice': False, 'triggerDirection': False, 'triggerPriceType': None, 'stopLossPrice': False, 'takeProfitPrice': False, 'attachedStopLossTakeProfit': None, 'timeInForce': { 'IOC': True, 'FOK': True, 'PO': True, 'GTD': False, }, 'hedged': False, 'trailing': False, 'leverage': False, 'marketBuyByCost': True, 'marketBuyRequiresPrice': False, 'selfTradePrevention': False, 'iceberg': False, }, 'createOrders': None, 'fetchMyTrades': { 'marginMode': True, 'limit': 100, 'daysBack': 100000, # todo 'untilDays': 100000, # todo 'marketType': True, 'subType': True, 'symbolRequired': False, }, 'fetchOrder': { 'marginMode': False, 'trigger': True, # todo TPSL kind 'trailing': False, 'marketType': True, 'subType': True, 'symbolRequired': False, }, 'fetchOpenOrders': { 'marginMode': True, 'limit': 100, 'trigger': True, # todo TPSL 'trailing': False, 'marketType': True, 'subType': True, 'symbolRequired': False, }, 'fetchOrders': { 'marginMode': True, 'limit': 100, 'daysBack': 100000, # todo 'untilDays': 100000, # todo 'trigger': True, # todo TPSL 'trailing': False, 'marketType': True, 'subType': True, 'symbolRequired': False, }, 'fetchClosedOrders': { 'marginMode': True, 'limit': 100, 'daysBack': 100000, # todo 'daysBackCanceled': 1, # todo 'untilDays': 100000, # todo 'trigger': True, # todo TPSL 'trailing': False, 'marketType': True, 'subType': True, 'symbolRequired': False, }, 'fetchOHLCV': { 'limit': 1000, # todo for derivatives }, }, 'spot': { 'extends': 'default', }, 'forDerivatives': { 'extends': 'default', 'createOrder': { 'triggerPrice': True, # todo 'triggerPriceType': { 'last': True, 'mark': True, 'index': True, }, 'stopLossPrice': True, 'takeProfitPrice': True, }, 'fetchMyTrades': { 'daysBack': None, 'untilDays': None, }, }, 'swap': { 'linear': { 'extends': 'forDerivatives', }, 'inverse': { 'extends': 'forDerivatives', }, }, 'future': { 'linear': { 'extends': 'forDerivatives', }, 'inverse': { 'extends': 'forDerivatives', }, }, }, }) def nonce(self): return self.milliseconds() - self.options['timeDifference'] def fetch_time(self, params={}) -> Int: """ fetches the current integer timestamp in milliseconds from the xt server https://doc.xt.com/#market1serverInfo :param dict params: extra parameters specific to the xt api endpoint :returns int: the current integer timestamp in milliseconds from the xt server """ response = self.publicSpotGetTime(params) # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": { # "serverTime": 1677823301643 # } # } # data = self.safe_value(response, 'result') return self.safe_integer(data, 'serverTime') def fetch_currencies(self, params={}) -> Currencies: """ fetches all available currencies on an exchange https://doc.xt.com/#deposit_withdrawalsupportedCurrenciesGet :param dict params: extra parameters specific to the xt api endpoint :returns dict: an associative dictionary of currencies """ promisesRaw = [self.publicSpotGetWalletSupportCurrency(params), self.publicSpotGetCurrencies(params)] chainsResponse, currenciesResponse = promisesRaw # # currencies # # { # "time": "1686626116145", # "version": "5dbbb2f2527c22b2b2e3b47187ef13d1", # "currencies": [ # { # "id": "2", # "currency": "btc", # "fullName": "Bitcoin", # "logo": "https://a.static-global.com/1/currency/btc.png", # "cmcLink": "https://coinmarketcap.com/currencies/bitcoin/", # "weight": "99999", # "maxPrecision": "10", # "depositStatus": "1", # "withdrawStatus": "1", # "convertEnabled": "1", # "transferEnabled": "1", # "isChainExist": "1", # "plates": [152] # }, # ], # } # # # chains # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": [ # { # "currency": "btc", # "supportChains": [ # { # "chain": "Bitcoin", # "depositEnabled": True, # "withdrawEnabled": True, # "withdrawFeeAmount": 0.0009, # "withdrawMinAmount": 0.0005, # "depositFeeRate": 0 # }, # ] # }, # ] # } # # note: individual network's full data is available on per-currency endpoint: https://www.xt.com/sapi/v4/balance/public/currency/11 # chainsData = self.safe_value(chainsResponse, 'result', []) currenciesResult = self.safe_value(currenciesResponse, 'result', []) currenciesData = self.safe_value(currenciesResult, 'currencies', []) chainsDataIndexed = self.index_by(chainsData, 'currency') result = {} for i in range(0, len(currenciesData)): entry = currenciesData[i] currencyId = self.safe_string(entry, 'currency') code = self.safe_currency_code(currencyId) networkEntry = self.safe_value(chainsDataIndexed, currencyId, {}) rawNetworks = self.safe_value(networkEntry, 'supportChains', []) networks = {} for j in range(0, len(rawNetworks)): rawNetwork = rawNetworks[j] networkId = self.safe_string(rawNetwork, 'chain') networkCode = self.network_id_to_code(networkId, code) networks[networkCode] = { 'info': rawNetwork, 'id': networkId, 'network': networkCode, 'name': None, 'active': None, 'fee': self.safe_number(rawNetwork, 'withdrawFeeAmount'), 'precision': None, 'deposit': self.safe_bool(rawNetwork, 'depositEnabled'), 'withdraw': self.safe_bool(rawNetwork, 'withdrawEnabled'), 'limits': { 'amount': { 'min': None, 'max': None, }, 'withdraw': { 'min': self.safe_number(rawNetwork, 'withdrawMinAmount'), 'max': None, }, 'deposit': { 'min': None, 'max': None, }, }, } typeRaw = self.safe_string(entry, 'type') type: Str = None if typeRaw == 'FT': type = 'crypto' else: type = 'other' result[code] = self.safe_currency_structure({ 'info': entry, 'id': currencyId, 'code': code, 'name': self.safe_string(entry, 'fullName'), 'active': None, 'fee': None, 'precision': self.parse_number(self.parse_precision(self.safe_string(entry, 'maxPrecision'))), 'deposit': self.safe_string(entry, 'depositStatus') == '1', 'withdraw': self.safe_string(entry, 'withdrawStatus') == '1', 'networks': networks, 'type': type, 'limits': { 'amount': { 'min': None, 'max': None, }, 'withdraw': { 'min': None, 'max': None, }, 'deposit': { 'min': None, 'max': None, }, }, }) return result def fetch_markets(self, params={}) -> List[Market]: """ retrieves data on all markets for xt https://doc.xt.com/#market2symbol https://doc.xt.com/#futures_quotesgetSymbols :param dict params: extra parameters specific to the xt api endpoint :returns dict[]: an array of objects representing market data """ if self.options['adjustForTimeDifference']: self.load_time_difference() promisesUnresolved = [ self.fetch_spot_markets(params), self.fetch_swap_and_future_markets(params), ] promises = promisesUnresolved spotMarkets = promises[0] swapAndFutureMarkets = promises[1] return self.array_concat(spotMarkets, swapAndFutureMarkets) def fetch_spot_markets(self, params={}): response = self.publicSpotGetSymbol(params) # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": { # "time": 1677881368812, # "version": "abb101d1543e54bee40687b135411ba0", # "symbols": [ # { # "id": 640, # "symbol": "xt_usdt", # "state": "ONLINE", # "stateTime": 1554048000000, # "tradingEnabled": True, # "openapiEnabled": True, # "nextStateTime": null, # "nextState": null, # "depthMergePrecision": 5, # "baseCurrency": "xt", # "baseCurrencyPrecision": 8, # "baseCurrencyId": 128, # "quoteCurrency": "usdt", # "quoteCurrencyPrecision": 8, # "quoteCurrencyId": 11, # "pricePrecision": 4, # "quantityPrecision": 2, # "orderTypes": ["LIMIT","MARKET"], # "timeInForces": ["GTC","IOC"], # "displayWeight": 10002, # "displayLevel": "FULL", # "plates": [], # "filters":[ # { # "filter": "QUOTE_QTY", # "min": "1" # }, # { # "filter": "PROTECTION_LIMIT", # "buyMaxDeviation": "0.8", # "sellMaxDeviation": "4" # }, # { # "filter": "PROTECTION_MARKET", # "maxDeviation": "0.02" # } # ] # }, # ] # } # } # data = self.safe_value(response, 'result', {}) symbols = self.safe_value(data, 'symbols', []) return self.parse_markets(symbols) def fetch_swap_and_future_markets(self, params={}): markets = [self.publicLinearGetFutureMarketV1PublicSymbolList(params), self.publicInverseGetFutureMarketV1PublicSymbolList(params)] # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": [ # { # "id": 52, # "symbolGroupId": 71, # "symbol": "xt_usdt", # "pair": "xt_usdt", # "contractType": "PERPETUAL", # "productType": "perpetual", # "predictEventType": null, # "underlyingType": "U_BASED", # "contractSize": "1", # "tradeSwitch": True, # "isDisplay": True, # "isOpenApi": False, # "state": 0, # "initLeverage": 20, # "initPositionType": "CROSSED", # "baseCoin": "xt", # "quoteCoin": "usdt", # "baseCoinPrecision": 8, # "baseCoinDisplayPrecision": 4, # "quoteCoinPrecision": 8, # "quoteCoinDisplayPrecision": 4, # "quantityPrecision": 0, # "pricePrecision": 4, # "supportOrderType": "LIMIT,MARKET", # "supportTimeInForce": "GTC,FOK,IOC,GTX", # "supportEntrustType": "TAKE_PROFIT,STOP,TAKE_PROFIT_MARKET,STOP_MARKET,TRAILING_STOP_MARKET", # "supportPositionType": "CROSSED,ISOLATED", # "minQty": "1", # "minNotional": "5", # "maxNotional": "20000000", # "multiplierDown": "0.1", # "multiplierUp": "0.1", # "maxOpenOrders": 200, # "maxEntrusts": 200, # "makerFee": "0.0004", # "takerFee": "0.0006", # "liquidationFee": "0.01", # "marketTakeBound": "0.1", # "depthPrecisionMerge": 5, # "labels": ["HOT"], # "onboardDate": 1657101601000, # "enName": "XTUSDT ", # "cnName": "XTUSDT", # "minStepPrice": "0.0001", # "minPrice": null, # "maxPrice": null, # "deliveryDate": 1669879634000, # "deliveryPrice": null, # "deliveryCompletion": False, # "cnDesc": null, # "enDesc": null # }, # ] # } # swapAndFutureMarkets = self.array_concat(self.safe_value(markets[0], 'result', []), self.safe_value(markets[1], 'result', [])) return self.parse_markets(swapAndFutureMarkets) def parse_markets(self, markets): result = [] for i in range(0, len(markets)): result.append(self.parse_market(markets[i])) return result def parse_market(self, market: dict) -> Market: # # spot # # { # "id": 640, # "symbol": "xt_usdt", # "state": "ONLINE", # "stateTime": 1554048000000, # "tradingEnabled": True, # "openapiEnabled": True, # "nextStateTime": null, # "nextState": null, # "depthMergePrecision": 5, # "baseCurrency": "xt", # "baseCurrencyPrecision": 8, # "baseCurrencyId": 128, # "quoteCurrency": "usdt", # "quoteCurrencyPrecision": 8, # "quoteCurrencyId": 11, # "pricePrecision": 4, # "quantityPrecision": 2, # "orderTypes": ["LIMIT","MARKET"], # "timeInForces": ["GTC","IOC"], # "displayWeight": 10002, # "displayLevel": "FULL", # "plates": [], # "filters":[ # { # "filter": "QUOTE_QTY", # "min": "1" # }, # { # "filter": "PRICE", # "min": null, # "max": null, # "tickSize": null # }, # { # "filter": "QUANTITY", # "min": null, # "max": null, # "tickSize": null # }, # { # "filter": "PROTECTION_LIMIT", # "buyMaxDeviation": "0.8", # "sellMaxDeviation": "4" # }, # { # "filter": "PROTECTION_MARKET", # "maxDeviation": "0.02" # }, # { # "filter": "PROTECTION_ONLINE", # "durationSeconds": "300", # "maxPriceMultiple": "5" # }, # ] # } # # swap and future # # { # "id": 52, # "symbolGroupId": 71, # "symbol": "xt_usdt", # "pair": "xt_usdt", # "contractType": "PERPETUAL", # "productType": "perpetual", # "predictEventType": null, # "underlyingType": "U_BASED", # "contractSize": "1", # "tradeSwitch": True, # "isDisplay": True, # "isOpenApi": False, # "state": 0, # "initLeverage": 20, # "initPositionType": "CROSSED", # "baseCoin": "xt", # "quoteCoin": "usdt", # "baseCoinPrecision": 8, # "baseCoinDisplayPrecision": 4, # "quoteCoinPrecision": 8, # "quoteCoinDisplayPrecision": 4, # "quantityPrecision": 0, # "pricePrecision": 4, # "supportOrderType": "LIMIT,MARKET", # "supportTimeInForce": "GTC,FOK,IOC,GTX", # "supportEntrustType": "TAKE_PROFIT,STOP,TAKE_PROFIT_MARKET,STOP_MARKET,TRAILING_STOP_MARKET", # "supportPositionType": "CROSSED,ISOLATED", # "minQty": "1", # "minNotional": "5", # "maxNotional": "20000000", # "multiplierDown": "0.1", # "multiplierUp": "0.1", # "maxOpenOrders": 200, # "maxEntrusts": 200, # "makerFee": "0.0004", # "takerFee": "0.0006", # "liquidationFee": "0.01", # "marketTakeBound": "0.1", # "depthPrecisionMerge": 5, # "labels": ["HOT"], # "onboardDate": 1657101601000, # "enName": "XTUSDT ", # "cnName": "XTUSDT", # "minStepPrice": "0.0001", # "minPrice": null, # "maxPrice": null, # "deliveryDate": 1669879634000, # "deliveryPrice": null, # "deliveryCompletion": False, # "cnDesc": null, # "enDesc": null # } # id = self.safe_string(market, 'symbol') baseId = self.safe_string_2(market, 'baseCurrency', 'baseCoin') quoteId = self.safe_string_2(market, 'quoteCurrency', 'quoteCoin') base = self.safe_currency_code(baseId) quote = self.safe_currency_code(quoteId) state = self.safe_string(market, 'state') symbol = base + '/' + quote filters = self.safe_value(market, 'filters', []) minAmount = None maxAmount = None minCost = None maxCost = None minPrice = None maxPrice = None amountPrecision = None for i in range(0, len(filters)): entry = filters[i] filter = self.safe_string(entry, 'filter') if filter == 'QUANTITY': minAmount = self.safe_number(entry, 'min') maxAmount = self.safe_number(entry, 'max') amountPrecision = self.safe_number(entry, 'tickSize') if filter == 'QUOTE_QTY': minCost = self.safe_number(entry, 'min') if filter == 'PRICE': minPrice = self.safe_number(entry, 'min') maxPrice = self.safe_number(entry, 'max') if amountPrecision is None: amountPrecision = self.parse_number(self.parse_precision(self.safe_string(market, 'quantityPrecision'))) underlyingType = self.safe_string(market, 'underlyingType') linear = None inverse = None settleId = None settle = None expiry = None future = False swap = False contract = False spot = True type = 'spot' if underlyingType == 'U_BASED': symbol = symbol + ':' + quote settleId = baseId settle = quote linear = True inverse = False elif underlyingType == 'COIN_BASED': symbol = symbol + ':' + base settleId = baseId settle = base linear = False inverse = True if underlyingType is not None: expiry = self.safe_integer(market, 'deliveryDate') productType = self.safe_string(market, 'productType') if productType != 'perpetual': symbol = symbol + '-' + self.yymmdd(expiry) type = 'future' future = True else: type = 'swap' swap = True minAmount = self.safe_number(market, 'minQty') minCost = self.safe_number(market, 'minNotional') maxCost = self.safe_number(market, 'maxNotional') minPrice = self.safe_number(market, 'minPrice') maxPrice = self.safe_number(market, 'maxPrice') contract = True spot = False isActive = False if contract: isActive = self.safe_value(market, 'isOpenApi', False) else: if (state == 'ONLINE') and (self.safe_value(market, 'tradingEnabled')) and (self.safe_value(market, 'openapiEnabled')): isActive = True return self.safe_market_structure({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'settle': settle, 'baseId': baseId, 'quoteId': quoteId, 'settleId': settleId, 'type': type, 'spot': spot, 'margin': None, 'swap': swap, 'future': future, 'option': False, 'active': isActive, 'contract': contract, 'linear': linear, 'inverse': inverse, 'taker': self.safe_number(market, 'takerFee'), 'maker': self.safe_number(market, 'makerFee'), 'contractSize': self.safe_number(market, 'contractSize'), 'expiry': expiry, 'expiryDatetime': self.iso8601(expiry), 'strike': None, 'optionType': None, 'precision': { 'price': self.parse_number(self.parse_precision(self.safe_string(market, 'pricePrecision'))), 'amount': amountPrecision, 'base': self.parse_number(self.parse_precision(self.safe_string(market, 'baseCoinPrecision'))), 'quote': self.parse_number(self.parse_precision(self.safe_string(market, 'quoteCoinPrecision'))), }, 'limits': { 'leverage': { 'min': self.parse_number('1'), 'max': None, }, 'amount': { 'min': minAmount, 'max': maxAmount, }, 'price': { 'min': minPrice, 'max': maxPrice, }, 'cost': { 'min': minCost, 'max': maxCost, }, }, 'info': market, }) def fetch_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}): """ fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market https://doc.xt.com/#market4kline https://doc.xt.com/#futures_quotesgetKLine :param str symbol: unified symbol of the market to fetch OHLCV data for :param str timeframe: the length of time each candle represents :param int [since]: timestamp in ms of the earliest candle to fetch :param int [limit]: the maximum amount of candles to fetch :param dict params: extra parameters specific to the xt api endpoint :param int [params.until]: timestamp in ms of the latest candle to fetch :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params) :returns int[][]: A list of candles ordered, open, high, low, close, volume """ self.load_markets() paginate = False paginate, params = self.handle_option_and_params(params, 'fetchOHLCV', 'paginate', False) if paginate: return self.fetch_paginated_call_deterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 1000) market = self.market(symbol) request = { 'symbol': market['id'], 'interval': self.safe_string(self.timeframes, timeframe, timeframe), } if since is not None: request['startTime'] = since if limit is not None: request['limit'] = limit else: request['limit'] = 1000 until = self.safe_integer(params, 'until') params = self.omit(params, ['until']) if until is not None: request['endTime'] = until response = None if market['linear']: response = self.publicLinearGetFutureMarketV1PublicQKline(self.extend(request, params)) elif market['inverse']: response = self.publicInverseGetFutureMarketV1PublicQKline(self.extend(request, params)) else: response = self.publicSpotGetKline(self.extend(request, params)) # # spot # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": [ # { # "t": 1678167720000, # "o": "22467.85", # "c": "22465.87", # "h": "22468.86", # "l": "22465.21", # "q": "1.316656", # "v": "29582.73018498" # }, # ] # } # # swap and future # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": [ # { # "s": "btc_usdt", # "p": "btc_usdt", # "t": 1678168020000, # "o": "22450.0", # "c": "22441.5", # "h": "22450.0", # "l": "22441.5", # "a": "312931", # "v": "702461.58895" # }, # ] # } # ohlcvs = self.safe_value(response, 'result', []) return self.parse_ohlcvs(ohlcvs, market, timeframe, since, limit) def parse_ohlcv(self, ohlcv, market: Market = None) -> list: # # spot # # { # "t": 1678167720000, # "o": "22467.85", # "c": "22465.87", # "h": "22468.86", # "l": "22465.21", # "q": "1.316656", # "v": "29582.73018498" # } # # swap and future # # { # "s": "btc_usdt", # "p": "btc_usdt", # "t": 1678168020000, # "o": "22450.0", # "c": "22441.5", # "h": "22450.0", # "l": "22441.5", # "a": "312931", # "v": "702461.58895" # } # volumeIndex = 'v' if (market['inverse']) else 'a' return [ self.safe_integer(ohlcv, 't'), self.safe_number(ohlcv, 'o'), self.safe_number(ohlcv, 'h'), self.safe_number(ohlcv, 'l'), self.safe_number(ohlcv, 'c'), self.safe_number_2(ohlcv, 'q', volumeIndex), ] def fetch_order_book(self, symbol: str, limit: Int = None, params={}): """ https://doc.xt.com/#market3depth https://doc.xt.com/#futures_quotesgetDepth fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data :param str symbol: unified market symbol to fetch the order book for :param int [limit]: the maximum amount of order book entries to return :param dict params: extra parameters specific to the xt api endpoint :returns dict: A dictionary of `order book structures ` indexed by market symbols """ self.load_markets() market = self.market(symbol) request = { 'symbol': market['id'], } response = None if market['spot']: if limit is not None: request['limit'] = min(limit, 500) response = self.publicSpotGetDepth(self.extend(request, params)) else: if limit is not None: request['level'] = min(limit, 50) else: request['level'] = 50 if market['linear']: response = self.publicLinearGetFutureMarketV1PublicQDepth(self.extend(request, params)) elif market['inverse']: response = self.publicInverseGetFutureMarketV1PublicQDepth(self.extend(request, params)) # # spot # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": { # "timestamp": 1678169975184, # "lastUpdateId": 1675333221812, # "bids": [ # ["22444.51", "0.129887"], # ["22444.49", "0.114245"], # ["22444.30", "0.225956"] # ], # "asks": [ # ["22446.19", "0.095330"], # ["22446.24", "0.224413"], # ["22446.28", "0.329095"] # ] # } # } # # swap and future # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": { # "t": 1678170311005, # "s": "btc_usdt", # "u": 471694545627, # "b": [ # ["22426", "198623"], # ["22423.5", "80295"], # ["22423", "163580"] # ], # "a": [ # ["22427", "3417"], # ["22428.5", "43532"], # ["22429", "119"] # ] # } # } # orderBook = self.safe_value(response, 'result', {}) timestamp = self.safe_integer_2(orderBook, 'timestamp', 't') if market['spot']: ob = self.parse_order_book(orderBook, symbol, timestamp) ob['nonce'] = self.safe_integer(orderBook, 'lastUpdateId') return ob swapOb = self.parse_order_book(orderBook, symbol, timestamp, 'b', 'a') swapOb['nonce'] = self.safe_integer_2(orderBook, 'u', 'lastUpdateId') return swapOb def fetch_ticker(self, symbol: str, params={}): """ fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market https://doc.xt.com/#market10ticker24h https://doc.xt.com/#futures_quotesgetAggTicker :param str symbol: unified market symbol to fetch the ticker for :param dict params: extra parameters specific to the xt api endpoint :returns dict: a `ticker structure ` """ self.load_markets() market = self.market(symbol) request = { 'symbol': market['id'], } response = None if market['linear']: response = self.publicLinearGetFutureMarketV1PublicQAggTicker(self.extend(request, params)) elif market['inverse']: response = self.publicInverseGetFutureMarketV1PublicQAggTicker(self.extend(request, params)) else: response = self.publicSpotGetTicker24h(self.extend(request, params)) # # spot # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": [ # { # "s": "btc_usdt", # "t": 1678172693931, # "cv": "34.00", # "cr": "0.0015", # "o": "22398.05", # "l": "22323.72", # "h": "22600.50", # "c": "22432.05", # "q": "7962.256931", # "v": "178675209.47416856" # } # ] # } # # swap and future # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": { # "t": 1678172848572, # "s": "btc_usdt", # "c": "22415.5", # "h": "22590.0", # "l": "22310.0", # "a": "623654031", # "v": "1399166074.31675", # "o": "22381.5", # "r": "0.0015", # "i": "22424.5", # "m": "22416.5", # "bp": "22415", # "ap": "22415.5" # } # } # ticker = self.safe_value(response, 'result') if market['spot']: return self.parse_ticker(ticker[0], market) return self.parse_ticker(ticker, market) def fetch_tickers(self, symbols: List[str] = None, params={}) -> Tickers: """ fetches price tickers for multiple markets, statistical calculations with the information calculated over the past 24 hours each market https://doc.xt.com/#market10ticker24h https://doc.xt.com/#futures_quotesgetAggTickers :param str [symbols]: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned :param dict params: extra parameters specific to the xt api endpoint :returns dict: an array of `ticker structures ` """ self.load_markets() market = None if symbols is not None: symbols = self.market_symbols(symbols) market = self.market(symbols[0]) request = {} type = None subType = None response = None type, params = self.handle_market_type_and_params('fetchTickers', market, params) subType, params = self.handle_sub_type_and_params('fetchTickers', market, params) if subType == 'inverse': response = self.publicInverseGetFutureMarketV1PublicQAggTickers(self.extend(request, params)) elif (subType == 'linear') or (type == 'swap') or (type == 'future'): response = self.publicLinearGetFutureMarketV1PublicQAggTickers(self.extend(request, params)) else: response = self.publicSpotGetTicker24h(self.extend(request, params)) # # spot # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": [ # { # "s": "btc_usdt", # "t": 1678172693931, # "cv": "34.00", # "cr": "0.0015", # "o": "22398.05", # "l": "22323.72", # "h": "22600.50", # "c": "22432.05", # "q": "7962.256931", # "v": "178675209.47416856" # } # ] # } # # swap and future # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": [ # { # "t": 1680738775108, # "s": "badger_usdt", # "c": "2.7176", # "h": "2.7917", # "l": "2.6818", # "a": "88332", # "v": "242286.3520", # "o": "2.7422", # "r": "-0.0089", # "i": "2.7155", # "m": "2.7161", # "bp": "2.7152", # "ap": "2.7176" # }, # ] # } # tickers = self.safe_value(response, 'result', []) result = {} for i in range(0, len(tickers)): ticker = self.parse_ticker(tickers[i], market) symbol = ticker['symbol'] result[symbol] = ticker return self.filter_by_array(result, 'symbol', symbols) def fetch_bids_asks(self, symbols: List[str] = None, params={}): """ fetches the bid and ask price and volume for multiple markets https://doc.xt.com/#market9tickerBook :param str [symbols]: unified symbols of the markets to fetch the bids and asks for, all markets are returned if not assigned :param dict params: extra parameters specific to the xt api endpoint :returns dict: a dictionary of `ticker structures ` """ self.load_markets() symbols = self.market_symbols(symbols) request = {} market = None if symbols is not None: market = self.market(symbols[0]) subType = None subType, params = self.handle_sub_type_and_params('fetchBidsAsks', market, params) if subType is not None: raise NotSupported(self.id + ' fetchBidsAsks() is not available for swap and future markets, only spot markets are supported') response = self.publicSpotGetTickerBook(self.extend(request, params)) # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": [ # { # "s": "kas_usdt", # "t": 1679539891853, # "ap": "0.016298", # "aq": "5119.09", # "bp": "0.016290", # "bq": "135.37" # }, # ] # } # tickers = self.safe_value(response, 'result', []) return self.parse_tickers(tickers, symbols) def parse_ticker(self, ticker, market=None): # # spot: fetchTicker, fetchTickers # # { # "s": "btc_usdt", # "t": 1678172693931, # "cv": "34.00", # "cr": "0.0015", # "o": "22398.05", # "l": "22323.72", # "h": "22600.50", # "c": "22432.05", # "q": "7962.256931", # "v": "178675209.47416856" # } # # swap and future: fetchTicker, fetchTickers # # { # "t": 1678172848572, # "s": "btc_usdt", # "c": "22415.5", # "h": "22590.0", # "l": "22310.0", # "a": "623654031", # "v": "1399166074.31675", # "o": "22381.5", # "r": "0.0015", # "i": "22424.5", # "m": "22416.5", # "bp": "22415", # "ap": "22415.5" # } # # fetchBidsAsks # # { # "s": "kas_usdt", # "t": 1679539891853, # "ap": "0.016298", # "aq": "5119.09", # "bp": "0.016290", # "bq": "135.37" # } # marketId = self.safe_string(ticker, 's') marketType = market['type'] if (market is not None) else None hasSpotKeys = ('cv' in ticker) or ('aq' in ticker) if marketType is None: marketType = 'spot' if hasSpotKeys else 'contract' market = self.safe_market(marketId, market, '_', marketType) symbol = market['symbol'] timestamp = self.safe_integer(ticker, 't') percentage = self.safe_string_2(ticker, 'cr', 'r') if percentage is not None: percentage = Precise.string_mul(percentage, '100') return self.safe_ticker({ 'symbol': symbol, 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), 'high': self.safe_number(ticker, 'h'), 'low': self.safe_number(ticker, 'l'), 'bid': self.safe_number(ticker, 'bp'), 'bidVolume': self.safe_number(ticker, 'bq'), 'ask': self.safe_number(ticker, 'ap'), 'askVolume': self.safe_number(ticker, 'aq'), 'vwap': None, 'open': self.safe_string(ticker, 'o'), 'close': self.safe_string(ticker, 'c'), 'last': self.safe_string(ticker, 'c'), 'previousClose': None, 'change': self.safe_number(ticker, 'cv'), 'percentage': self.parse_number(percentage), 'average': None, 'baseVolume': None, 'quoteVolume': self.safe_number_2(ticker, 'a', 'v'), 'info': ticker, }, market) def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}): """ get the list of most recent trades for a particular symbol https://doc.xt.com/#market5tradeRecent https://doc.xt.com/#futures_quotesgetDeal :param str symbol: unified market symbol to fetch trades for :param int [since]: timestamp in ms of the earliest trade to fetch :param int [limit]: the maximum amount of trades to fetch :param dict params: extra parameters specific to the xt api endpoint :returns dict[]: a list of `trade structures ` """ self.load_markets() market = self.market(symbol) request = { 'symbol': market['id'], } response = None if market['spot']: if limit is not None: request['limit'] = limit response = self.publicSpotGetTradeRecent(self.extend(request, params)) else: if limit is not None: request['num'] = limit if market['linear']: response = self.publicLinearGetFutureMarketV1PublicQDeal(self.extend(request, params)) elif market['inverse']: response = self.publicInverseGetFutureMarketV1PublicQDeal(self.extend(request, params)) # # spot # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": [ # { # "i": 203530723141917063, # "t": 1678227505815, # "p": "22038.81", # "q": "0.000978", # "v": "21.55395618", # "b": True # }, # ] # } # # swap and future # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": [ # { # "t": 1678227683897, # "s": "btc_usdt", # "p": "22031", # "a": "1067", # "m": "BID" # }, # ] # } # trades = self.safe_value(response, 'result', []) return self.parse_trades(trades, market) def fetch_my_trades(self, symbol: str = None, since: Int = None, limit: Int = None, params={}): """ fetch all trades made by the user https://doc.xt.com/#tradetradeGet https://doc.xt.com/#futures_ordergetTrades :param str [symbol]: unified market symbol to fetch trades for :param int [since]: timestamp in ms of the earliest trade to fetch :param int [limit]: the maximum amount of trades to fetch :param dict params: extra parameters specific to the xt api endpoint :returns dict[]: a list of `trade structures ` """ self.load_markets() request = {} market = None if symbol is not None: market = self.market(symbol) request['symbol'] = market['id'] if since is not None: request['startTime'] = since type = None subType = None response = None type, params = self.handle_market_type_and_params('fetchMyTrades', market, params) subType, params = self.handle_sub_type_and_params('fetchMyTrades', market, params) if (subType is not None) or (type == 'swap') or (type == 'future'): if limit is not None: request['size'] = limit if subType == 'inverse': response = self.privateInverseGetFutureTradeV1OrderTradeList(self.extend(request, params)) else: response = self.privateLinearGetFutureTradeV1OrderTradeList(self.extend(request, params)) else: marginMode = None marginMode, params = self.handle_margin_mode_and_params('fetchMyTrades', params) marginOrSpotRequest = 'LEVER' if (marginMode is not None) else 'SPOT' request['bizType'] = marginOrSpotRequest if limit is not None: request['limit'] = limit response = self.privateSpotGetTrade(self.extend(request, params)) # # spot and margin # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": { # "hasPrev": False, # "hasNext": False, # "items": [ # { # "symbol": "btc_usdt", # "tradeId": "206906233569974658", # "orderId": "206906233178463488", # "orderSide": "SELL", # "orderType": "MARKET", # "bizType": "SPOT", # "time": 1679032290215, # "price": "25703.46", # "quantity": "0.000099", # "quoteQty": "2.54464254", # "baseCurrency": "btc", # "quoteCurrency": "usdt", # "fee": "0.00508929", # "feeCurrency": "usdt", # "takerMaker": "TAKER" # }, # ] # } # } # # swap and future # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": { # "page": 1, # "ps": 10, # "total": 2, # "items": [ # { # "orderId": "207260566170987200", # "execId": "207260566790603265", # "symbol": "btc_usdt", # "quantity": "13", # "price": "27368", # "fee": "0.02134704", # "feeCoin": "usdt", # "timestamp": 1679116769838, # "takerMaker": "TAKER" # }, # ] # } # } # data = self.safe_value(response, 'result', {}) trades = self.safe_value(data, 'items', []) return self.parse_trades(trades, market, since, limit) def parse_trade(self, trade, market=None): # # spot: fetchTrades # # { # "i": 203530723141917063, # "t": 1678227505815, # "p": "22038.81", # "q": "0.000978", # "v": "21.55395618", # "b": True # } # # spot: watchTrades # # { # s: 'btc_usdt', # i: '228825383103928709', # t: 1684258222702, # p: '27003.65', # q: '0.000796', # b: True # } # # spot: watchMyTrades # # { # "s": "btc_usdt", # symbol # "t": 1656043204763, # time # "i": "6316559590087251233", # tradeId # "oi": "6216559590087220004", # orderId # "p": "30000", # trade price # "q": "3", # qty quantity # "v": "90000" # volume trade amount # } # # swap and future: fetchTrades # # { # "t": 1678227683897, # "s": "btc_usdt", # "p": "22031", # "a": "1067", # "m": "BID" # } # # spot: fetchMyTrades # # { # "symbol": "btc_usdt", # "tradeId": "206906233569974658", # "orderId": "206906233178463488", # "orderSide": "SELL", # "orderType": "MARKET", # "bizType": "SPOT", # "time": 1679032290215, # "price": "25703.46", # "quantity": "0.000099", # "quoteQty": "2.54464254", # "baseCurrency": "btc", # "quoteCurrency": "usdt", # "fee": "0.00508929", # "feeCurrency": "usdt", # "takerMaker": "TAKER" # } # # swap and future: fetchMyTrades # # { # "orderId": "207260566170987200", # "execId": "207260566790603265", # "symbol": "btc_usdt", # "quantity": "13", # "price": "27368", # "fee": "0.02134704", # "feeCoin": "usdt", # "timestamp": 1679116769838, # "takerMaker": "TAKER" # } # # contract watchMyTrades # # { # "symbol": 'btc_usdt', # "orderSide": 'SELL', # "positionSide": 'LONG', # "orderId": '231485367663419328', # "price": '27152.7', # "quantity": '33', # "marginUnfrozen": '2.85318000', # "timestamp": 1684892412565 # } # # watchMyTrades(ws, swap) # # { # 'fee': '0.04080840', # 'isMaker': False, # 'marginUnfrozen': '0.75711984', # 'orderId': '376172779053188416', # 'orderSide': 'BUY', # 'positionSide': 'LONG', # 'price': '3400.70', # 'quantity': '2', # 'symbol': 'eth_usdt', # 'timestamp': 1719388579622 # } # marketId = self.safe_string_2(trade, 's', 'symbol') marketType = market['type'] if (market is not None) else None hasSpotKeys = ('b' in trade) or ('bizType' in trade) or ('oi' in trade) if marketType is None: marketType = 'spot' if hasSpotKeys else 'contract' market = self.safe_market(marketId, market, '_', marketType) side = None takerOrMaker = None isBuyerMaker = self.safe_bool(trade, 'b') if isBuyerMaker is not None: side = 'sell' if isBuyerMaker else 'buy' takerOrMaker = 'taker' # public trades always taker else: takerMaker = self.safe_string_lower(trade, 'takerMaker') if takerMaker is not None: takerOrMaker = takerMaker else: isMaker = self.safe_bool(trade, 'isMaker') if isMaker is not None: takerOrMaker = 'maker' if isMaker else 'taker' orderSide = self.safe_string_lower(trade, 'orderSide') if orderSide is not None: side = orderSide else: bidOrAsk = self.safe_string(trade, 'm') if bidOrAsk is not None: side = 'buy' if (bidOrAsk == 'BID') else 'sell' timestamp = self.safe_integer_n(trade, ['t', 'time', 'timestamp']) quantity = self.safe_string_2(trade, 'q', 'quantity') amount = None if marketType == 'spot': amount = quantity else: if quantity is None: amount = Precise.string_mul(self.safe_string(trade, 'a'), self.number_to_string(market['contractSize'])) else: amount = Precise.string_mul(quantity, self.number_to_string(market['contractSize'])) return self.safe_trade({ 'info': trade, 'id': self.safe_string_n(trade, ['i', 'tradeId', 'execId']), 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), 'symbol': market['symbol'], 'order': self.safe_string_2(trade, 'orderId', 'oi'), 'type': self.safe_string_lower(trade, 'orderType'), 'side': side, 'takerOrMaker': takerOrMaker, 'price': self.safe_string_2(trade, 'p', 'price'), 'amount': amount, 'cost': None, 'fee': { 'currency': self.safe_currency_code(self.safe_string_2(trade, 'feeCurrency', 'feeCoin')), 'cost': self.safe_string(trade, 'fee'), }, }, market) def fetch_balance(self, params={}): """ query for balance and get the amount of funds available for trading or funds locked in orders https://doc.xt.com/#balancebalancesGet https://doc.xt.com/#futures_usergetBalances :param dict params: extra parameters specific to the xt api endpoint :returns dict: a `balance structure ` """ self.load_markets() type = None subType = None response = None type, params = self.handle_market_type_and_params('fetchBalance', None, params) subType, params = self.handle_sub_type_and_params('fetchBalance', None, params) isContractWallet = ((type == 'swap') or (type == 'future')) if subType == 'inverse': response = self.privateInverseGetFutureUserV1BalanceList(params) elif (subType == 'linear') or isContractWallet: response = self.privateLinearGetFutureUserV1BalanceList(params) else: response = self.privateSpotGetBalances(params) # # spot # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": { # "totalUsdtAmount": "31.75931133", # "totalBtcAmount": "0.00115951", # "assets": [ # { # "currency": "usdt", # "currencyId": 11, # "frozenAmount": "0.03834082", # "availableAmount": "31.70995965", # "totalAmount": "31.74830047", # "convertBtcAmount": "0.00115911", # "convertUsdtAmount": "31.74830047" # }, # ] # } # } # # swap and future # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": [ # { # "coin": "usdt", # "walletBalance": "19.29849875", # "openOrderMarginFrozen": "0", # "isolatedMargin": "0.709475", # "crossedMargin": "0", # "availableBalance": "18.58902375", # "bonus": "0", # "coupon":"0" # } # ] # } # balances = None if (subType is not None) or isContractWallet: balances = self.safe_value(response, 'result', []) else: data = self.safe_value(response, 'result', {}) balances = self.safe_value(data, 'assets', []) return self.parse_balance(balances) def parse_balance(self, response): # # spot # # { # "currency": "usdt", # "currencyId": 11, # "frozenAmount": "0.03834082", # "availableAmount": "31.70995965", # "totalAmount": "31.74830047", # "convertBtcAmount": "0.00115911", # "convertUsdtAmount": "31.74830047" # } # # swap and future # # { # "coin": "usdt", # "walletBalance": "19.29849875", # "openOrderMarginFrozen": "0", # "isolatedMargin": "0.709475", # "crossedMargin": "0", # "availableBalance": "18.58902375", # "bonus": "0", # "coupon":"0" # } # result = {'info': response} for i in range(0, len(response)): balance = response[i] currencyId = self.safe_string_2(balance, 'currency', 'coin') code = self.safe_currency_code(currencyId) account = self.account() free = self.safe_string_2(balance, 'availableAmount', 'availableBalance') used = self.safe_string(balance, 'frozenAmount') total = self.safe_string_2(balance, 'totalAmount', 'walletBalance') if used is None: crossedAndIsolatedMargin = Precise.string_add(self.safe_string(balance, 'crossedMargin'), self.safe_string(balance, 'isolatedMargin')) used = Precise.string_add(self.safe_string(balance, 'openOrderMarginFrozen'), crossedAndIsolatedMargin) account['free'] = free account['used'] = used account['total'] = total result[code] = account return self.safe_balance(result) def create_market_buy_order_with_cost(self, symbol: str, cost: float, params={}): """ https://doc.xt.com/#orderorderPost create a market buy order by providing the symbol and cost :param str symbol: unified symbol of the market to create an order in :param float cost: how much you want to trade in units of the quote currency :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: an `order structure ` """ self.load_markets() market = self.market(symbol) if not market['spot']: raise NotSupported(self.id + ' createMarketBuyOrderWithCost() supports spot orders only') return self.create_order(symbol, 'market', 'buy', cost, 1, params) def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}): """ create a trade order https://doc.xt.com/#orderorderPost https://doc.xt.com/#futures_ordercreate https://doc.xt.com/#futures_entrustcreatePlan https://doc.xt.com/#futures_entrustcreateProfit :param str symbol: unified symbol of the market to create an order in :param str type: 'market' or 'limit' :param str side: 'buy' or 'sell' :param float amount: how much you want to trade in units of the base currency :param float [price]: the price to fulfill the order, in units of the quote currency, can be ignored in market orders :param dict params: extra parameters specific to the xt api endpoint :param str [params.timeInForce]: 'GTC', 'IOC', 'FOK' or 'GTX' :param str [params.entrustType]: 'TAKE_PROFIT', 'STOP', 'TAKE_PROFIT_MARKET', 'STOP_MARKET', 'TRAILING_STOP_MARKET', required if stopPrice is defined, currently isn't functioning on xt's side :param str [params.triggerPriceType]: 'INDEX_PRICE', 'MARK_PRICE', 'LATEST_PRICE', required if stopPrice is defined :param float [params.triggerPrice]: price to trigger a stop order :param float [params.stopPrice]: alias for triggerPrice :param float [params.stopLoss]: price to set a stop-loss on an open position :param float [params.takeProfit]: price to set a take-profit on an open position :returns dict: an `order structure ` """ self.load_markets() market = self.market(symbol) symbol = market['symbol'] if market['spot']: return self.create_spot_order(symbol, type, side, amount, price, params) else: return self.create_contract_order(symbol, type, side, amount, price, params) def create_spot_order(self, symbol: str, type, side, amount, price=None, params={}): self.load_markets() market = self.market(symbol) request = { 'symbol': market['id'], 'side': side.upper(), 'type': type.upper(), } timeInForce = None marginMode = None marginMode, params = self.handle_margin_mode_and_params('createOrder', params) marginOrSpotRequest = 'LEVER' if (marginMode is not None) else 'SPOT' request['bizType'] = marginOrSpotRequest if type == 'market': timeInForce = self.safe_string_upper(params, 'timeInForce', 'FOK') if side == 'buy': cost = self.safe_string(params, 'cost') params = self.omit(params, 'cost') createMarketBuyOrderRequiresPrice = self.safe_bool(self.options, 'createMarketBuyOrderRequiresPrice', True) if createMarketBuyOrderRequiresPrice: if price is None and (cost is None): raise InvalidOrder(self.id + ' createOrder() requires a price argument or cost in params for market buy orders on spot markets to calculate the total amount to spend(amount * price), alternatively set the createMarketBuyOrderRequiresPrice option to False and pass in the cost to spend into the amount parameter') else: amountString = self.number_to_string(amount) priceString = self.number_to_string(price) costCalculated: Str = None if price is not None: costCalculated = Precise.string_mul(amountString, priceString) else: costCalculated = cost request['quoteQty'] = self.cost_to_precision(symbol, costCalculated) else: amountCost = cost if (cost is not None) else amount request['quoteQty'] = self.cost_to_precision(symbol, amountCost) else: timeInForce = self.safe_string_upper(params, 'timeInForce', 'GTC') request['price'] = self.price_to_precision(symbol, price) if (side == 'sell') or (type == 'limit'): request['quantity'] = self.amount_to_precision(symbol, amount) request['timeInForce'] = timeInForce response = self.privateSpotPostOrder(self.extend(request, params)) # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": { # "orderId": "204371980095156544" # } # } # order = self.safe_value(response, 'result', {}) return self.parse_order(order, market) def create_contract_order(self, symbol: str, type, side, amount, price=None, params={}): self.load_markets() market = self.market(symbol) request = { 'symbol': market['id'], 'origQty': self.amount_to_precision(symbol, amount), } timeInForce = self.safe_string_upper(params, 'timeInForce') if timeInForce is not None: request['timeInForce'] = timeInForce reduceOnly = self.safe_value(params, 'reduceOnly', False) if side == 'buy': requestType = 'SHORT' if (reduceOnly) else 'LONG' request['positionSide'] = requestType else: requestType = 'LONG' if (reduceOnly) else 'SHORT' request['positionSide'] = requestType response = None triggerPrice = self.safe_number_2(params, 'triggerPrice', 'stopPrice') stopLoss = self.safe_number_2(params, 'stopLoss', 'triggerStopPrice') takeProfit = self.safe_number_2(params, 'takeProfit', 'triggerProfitPrice') isTrigger = (triggerPrice is not None) isStopLoss = (stopLoss is not None) isTakeProfit = (takeProfit is not None) if price is not None: if not (isStopLoss) and not (isTakeProfit): request['price'] = self.price_to_precision(symbol, price) if isTrigger: request['timeInForce'] = self.safe_string_upper(params, 'timeInForce', 'GTC') request['triggerPriceType'] = self.safe_string(params, 'triggerPriceType', 'LATEST_PRICE') request['orderSide'] = side.upper() request['stopPrice'] = self.price_to_precision(symbol, triggerPrice) entrustType = 'STOP_MARKET' if (type == 'market') else 'STOP' request['entrustType'] = entrustType params = self.omit(params, 'triggerPrice') if market['linear']: response = self.privateLinearPostFutureTradeV1EntrustCreatePlan(self.extend(request, params)) elif market['inverse']: response = self.privateInversePostFutureTradeV1EntrustCreatePlan(self.extend(request, params)) elif isStopLoss or isTakeProfit: if isStopLoss: request['triggerStopPrice'] = self.price_to_precision(symbol, stopLoss) else: request['triggerProfitPrice'] = self.price_to_precision(symbol, takeProfit) params = self.omit(params, ['stopLoss', 'takeProfit']) if market['linear']: response = self.privateLinearPostFutureTradeV1EntrustCreateProfit(self.extend(request, params)) elif market['inverse']: response = self.privateInversePostFutureTradeV1EntrustCreateProfit(self.extend(request, params)) else: request['orderSide'] = side.upper() request['orderType'] = type.upper() if market['linear']: response = self.privateLinearPostFutureTradeV1OrderCreate(self.extend(request, params)) elif market['inverse']: response = self.privateInversePostFutureTradeV1OrderCreate(self.extend(request, params)) # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": "206410760006650176" # } # return self.parse_order(response, market) def fetch_order(self, id: str, symbol: str = None, params={}): """ fetches information on an order made by the user https://doc.xt.com/#orderorderGet https://doc.xt.com/#futures_ordergetById https://doc.xt.com/#futures_entrustgetPlanById https://doc.xt.com/#futures_entrustgetProfitById :param str id: order id :param str [symbol]: unified symbol of the market the order was made in :param dict params: extra parameters specific to the xt api endpoint :param bool [params.trigger]: if the order is a trigger order or not :param bool [params.stopLossTakeProfit]: if the order is a stop-loss or take-profit order :returns dict: An `order structure ` """ self.load_markets() market = None if symbol is not None: market = self.market(symbol) request = {} type = None subType = None response = None type, params = self.handle_market_type_and_params('fetchOrder', market, params) subType, params = self.handle_sub_type_and_params('fetchOrder', market, params) trigger = self.safe_value(params, 'stop') stopLossTakeProfit = self.safe_value(params, 'stopLossTakeProfit') if trigger: request['entrustId'] = id elif stopLossTakeProfit: request['profitId'] = id else: request['orderId'] = id if trigger: params = self.omit(params, 'stop') if subType == 'inverse': response = self.privateInverseGetFutureTradeV1EntrustPlanDetail(self.extend(request, params)) else: response = self.privateLinearGetFutureTradeV1EntrustPlanDetail(self.extend(request, params)) elif stopLossTakeProfit: params = self.omit(params, 'stopLossTakeProfit') if subType == 'inverse': response = self.privateInverseGetFutureTradeV1EntrustProfitDetail(self.extend(request, params)) else: response = self.privateLinearGetFutureTradeV1EntrustProfitDetail(self.extend(request, params)) elif subType == 'inverse': response = self.privateInverseGetFutureTradeV1OrderDetail(self.extend(request, params)) elif (subType == 'linear') or (type == 'swap') or (type == 'future'): response = self.privateLinearGetFutureTradeV1OrderDetail(self.extend(request, params)) else: response = self.privateSpotGetOrderOrderId(self.extend(request, params)) # # spot # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": { # "symbol": "btc_usdt", # "orderId": "207505997850909952", # "clientOrderId": null, # "baseCurrency": "btc", # "quoteCurrency": "usdt", # "side": "BUY", # "type": "LIMIT", # "timeInForce": "GTC", # "price": "20000.00", # "origQty": "0.001000", # "origQuoteQty": "20.00", # "executedQty": "0.000000", # "leavingQty": "0.001000", # "tradeBase": "0.000000", # "tradeQuote": "0.00", # "avgPrice": null, # "fee": null, # "feeCurrency": null, # "closed": False, # "state": "NEW", # "time": 1679175285162, # "updatedTime": 1679175285255 # } # } # # swap and future # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": { # "orderId": "211451874783183936", # "clientOrderId": null, # "symbol": "btc_usdt", # "orderType": "LIMIT", # "orderSide": "BUY", # "positionSide": "LONG", # "timeInForce": "GTC", # "closePosition": False, # "price": "20000", # "origQty": "10", # "avgPrice": "0", # "executedQty": "0", # "marginFrozen": "1.34533334", # "remark": null, # "triggerProfitPrice": null, # "triggerStopPrice": null, # "sourceId": null, # "sourceType": "DEFAULT", # "forceClose": False, # "closeProfit": null, # "state": "NEW", # "createdTime": 1680116055693, # "updatedTime": 1680116055693 # } # } # # trigger # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": { # "entrustId": "216300248132756992", # "symbol": "btc_usdt", # "entrustType": "STOP", # "orderSide": "SELL", # "positionSide": "SHORT", # "timeInForce": "GTC", # "closePosition": null, # "price": "20000", # "origQty": "1", # "stopPrice": "19000", # "triggerPriceType": "LATEST_PRICE", # "state": "NOT_TRIGGERED", # "marketOrderLevel": null, # "createdTime": 1681271998064, # "updatedTime": 1681271998064, # "ordinary": False # } # } # # stop-loss and take-profit # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": { # "profitId": "216306213226230400", # "symbol": "btc_usdt", # "positionSide": "LONG", # "origQty": "1", # "triggerPriceType": "LATEST_PRICE", # "triggerProfitPrice": null, # "triggerStopPrice": "20000", # "entryPrice": null, # "positionSize": null, # "isolatedMargin": null, # "executedQty": null, # "avgPrice": null, # "positionType": "ISOLATED", # "state": "NOT_TRIGGERED", # "createdTime": 1681273420039 # } # } # order = self.safe_value(response, 'result', {}) return self.parse_order(order, market) def fetch_orders(self, symbol: str = None, since: Int = None, limit: Int = None, params={}): """ fetches information on multiple orders made by the user https://doc.xt.com/#orderhistoryOrderGet https://doc.xt.com/#futures_ordergetHistory https://doc.xt.com/#futures_entrustgetPlanHistory :param str [symbol]: unified market symbol of the market the orders were made in :param int [since]: timestamp in ms of the earliest order :param int [limit]: the maximum number of order structures to retrieve :param dict params: extra parameters specific to the xt api endpoint :param bool [params.trigger]: if the order is a trigger order or not :returns dict[]: a list of `order structures ` """ self.load_markets() request = {} market = None if symbol is not None: market = self.market(symbol) request['symbol'] = market['id'] if since is not None: request['startTime'] = since if limit is not None: request['limit'] = limit type = None subType = None response = None type, params = self.handle_market_type_and_params('fetchOrders', market, params) subType, params = self.handle_sub_type_and_params('fetchOrders', market, params) trigger = self.safe_value_2(params, 'trigger', 'stop') if trigger: params = self.omit(params, ['trigger', 'stop']) if subType == 'inverse': response = self.privateInverseGetFutureTradeV1EntrustPlanListHistory(self.extend(request, params)) else: response = self.privateLinearGetFutureTradeV1EntrustPlanListHistory(self.extend(request, params)) elif subType == 'inverse': response = self.privateInverseGetFutureTradeV1OrderListHistory(self.extend(request, params)) elif (subType == 'linear') or (type == 'swap') or (type == 'future'): response = self.privateLinearGetFutureTradeV1OrderListHistory(self.extend(request, params)) else: marginMode = None marginMode, params = self.handle_margin_mode_and_params('fetchOrders', params) marginOrSpotRequest = 'LEVER' if (marginMode is not None) else 'SPOT' request['bizType'] = marginOrSpotRequest response = self.privateSpotGetHistoryOrder(self.extend(request, params)) # # spot and margin # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": { # "hasPrev": False, # "hasNext": True, # "items": [ # { # "symbol": "btc_usdt", # "orderId": "207505997850909952", # "clientOrderId": null, # "baseCurrency": "btc", # "quoteCurrency": "usdt", # "side": "BUY", # "type": "LIMIT", # "timeInForce": "GTC", # "price": "20000.00", # "origQty": "0.001000", # "origQuoteQty": "20.00", # "executedQty": "0.000000", # "leavingQty": "0.000000", # "tradeBase": "0.000000", # "tradeQuote": "0.00", # "avgPrice": null, # "fee": null, # "feeCurrency": null, # "closed": True, # "state": "CANCELED", # "time": 1679175285162, # "updatedTime": 1679175488492 # }, # ] # } # } # # swap and future # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": { # "hasPrev": False, # "hasNext": True, # "items": [ # { # "orderId": "207519546930995456", # "clientOrderId": null, # "symbol": "btc_usdt", # "orderType": "LIMIT", # "orderSide": "BUY", # "positionSide": "LONG", # "timeInForce": "GTC", # "closePosition": False, # "price": "20000", # "origQty": "100", # "avgPrice": "0", # "executedQty": "0", # "marginFrozen": "4.12", # "remark": null, # "triggerProfitPrice": null, # "triggerStopPrice": null, # "sourceId": null, # "sourceType": "DEFAULT", # "forceClose": False, # "closeProfit": null, # "state": "CANCELED", # "createdTime": 1679178515689, # "updatedTime": 1679180096172 # }, # ] # } # } # # stop # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": { # "hasPrev": False, # "hasNext": False, # "items": [ # { # "entrustId": "216300248132756992", # "symbol": "btc_usdt", # "entrustType": "STOP", # "orderSide": "SELL", # "positionSide": "SHORT", # "timeInForce": "GTC", # "closePosition": null, # "price": "20000", # "origQty": "1", # "stopPrice": "19000", # "triggerPriceType": "LATEST_PRICE", # "state": "USER_REVOCATION", # "marketOrderLevel": null, # "createdTime": 1681271998064, # "updatedTime": 1681273188674, # "ordinary": False # }, # ] # } # } # data = self.safe_value(response, 'result', {}) orders = self.safe_value(data, 'items', []) return self.parse_orders(orders, market, since, limit) def fetch_orders_by_status(self, status, symbol: str = None, since: Int = None, limit: Int = None, params={}): self.load_markets() request = {} market = None if symbol is not None: market = self.market(symbol) request['symbol'] = market['id'] if limit is not None: request['size'] = limit if since is not None: request['startTime'] = since type = None subType = None response = None type, params = self.handle_market_type_and_params('fetchOrdersByStatus', market, params) subType, params = self.handle_sub_type_and_params('fetchOrdersByStatus', market, params) trigger = self.safe_bool_2(params, 'stop', 'trigger') stopLossTakeProfit = self.safe_value(params, 'stopLossTakeProfit') if status == 'open': if trigger or stopLossTakeProfit: request['state'] = 'NOT_TRIGGERED' elif type == 'swap': request['state'] = 'UNFINISHED' # NEW & PARTIALLY_FILLED elif status == 'closed': if trigger or stopLossTakeProfit: request['state'] = 'TRIGGERED' else: request['state'] = 'FILLED' elif status == 'canceled': if trigger or stopLossTakeProfit: request['state'] = 'USER_REVOCATION' else: request['state'] = 'CANCELED' else: request['state'] = status if trigger or stopLossTakeProfit or (subType is not None) or (type == 'swap') or (type == 'future'): if since is not None: request['startTime'] = since if limit is not None: request['size'] = limit if trigger: params = self.omit(params, ['stop', 'trigger']) if subType == 'inverse': response = self.privateInverseGetFutureTradeV1EntrustPlanList(self.extend(request, params)) else: response = self.privateLinearGetFutureTradeV1EntrustPlanList(self.extend(request, params)) elif stopLossTakeProfit: params = self.omit(params, 'stopLossTakeProfit') if subType == 'inverse': response = self.privateInverseGetFutureTradeV1EntrustProfitList(self.extend(request, params)) else: response = self.privateLinearGetFutureTradeV1EntrustProfitList(self.extend(request, params)) elif (subType is not None) or (type == 'swap') or (type == 'future'): if subType == 'inverse': response = self.privateInverseGetFutureTradeV1OrderList(self.extend(request, params)) else: response = self.privateLinearGetFutureTradeV1OrderList(self.extend(request, params)) else: marginMode = None marginMode, params = self.handle_margin_mode_and_params('fetchOrdersByStatus', params) marginOrSpotRequest = 'LEVER' if (marginMode is not None) else 'SPOT' request['bizType'] = marginOrSpotRequest if status != 'open': if since is not None: request['startTime'] = since if limit is not None: request = self.omit(request, 'size') request['limit'] = limit response = self.privateSpotGetHistoryOrder(self.extend(request, params)) else: response = self.privateSpotGetOpenOrder(self.extend(request, params)) # # spot and margin # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": { # "hasPrev": False, # "hasNext": True, # "items": [ # { # "symbol": "btc_usdt", # "orderId": "207505997850909952", # "clientOrderId": null, # "baseCurrency": "btc", # "quoteCurrency": "usdt", # "side": "BUY", # "type": "LIMIT", # "timeInForce": "GTC", # "price": "20000.00", # "origQty": "0.001000", # "origQuoteQty": "20.00", # "executedQty": "0.000000", # "leavingQty": "0.000000", # "tradeBase": "0.000000", # "tradeQuote": "0.00", # "avgPrice": null, # "fee": null, # "feeCurrency": null, # "closed": True, # "state": "CANCELED", # "time": 1679175285162, # "updatedTime": 1679175488492 # }, # ] # } # } # # spot and margin: fetchOpenOrders # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": [ # { # "symbol": "eth_usdt", # "orderId": "208249323222264320", # "clientOrderId": null, # "baseCurrency": "eth", # "quoteCurrency": "usdt", # "side": "BUY", # "type": "LIMIT", # "timeInForce": "GTC", # "price": "1300.00", # "origQty": "0.0032", # "origQuoteQty": "4.16", # "executedQty": "0.0000", # "leavingQty": "0.0032", # "tradeBase": "0.0000", # "tradeQuote": "0.00", # "avgPrice": null, # "fee": null, # "feeCurrency": null, # "closed": False, # "state": "NEW", # "time": 1679352507741, # "updatedTime": 1679352507869 # }, # ] # } # # swap and future # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": { # "page": 1, # "ps": 10, # "total": 25, # "items": [ # { # "orderId": "207519546930995456", # "clientOrderId": null, # "symbol": "btc_usdt", # "orderType": "LIMIT", # "orderSide": "BUY", # "positionSide": "LONG", # "timeInForce": "GTC", # "closePosition": False, # "price": "20000", # "origQty": "100", # "avgPrice": "0", # "executedQty": "0", # "marginFrozen": "4.12", # "remark": null, # "triggerProfitPrice": null, # "triggerStopPrice": null, # "sourceId": null, # "sourceType": "DEFAULT", # "forceClose": False, # "closeProfit": null, # "state": "CANCELED", # "createdTime": 1679178515689, # "updatedTime": 1679180096172 # }, # ] # } # } # # stop # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": { # "page": 1, # "ps": 3, # "total": 8, # "items": [ # { # "entrustId": "216300248132756992", # "symbol": "btc_usdt", # "entrustType": "STOP", # "orderSide": "SELL", # "positionSide": "SHORT", # "timeInForce": "GTC", # "closePosition": null, # "price": "20000", # "origQty": "1", # "stopPrice": "19000", # "triggerPriceType": "LATEST_PRICE", # "state": "USER_REVOCATION", # "marketOrderLevel": null, # "createdTime": 1681271998064, # "updatedTime": 1681273188674, # "ordinary": False # }, # ] # } # } # # stop-loss and take-profit # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": { # "page": 1, # "ps": 3, # "total": 2, # "items": [ # { # "profitId": "216306213226230400", # "symbol": "btc_usdt", # "positionSide": "LONG", # "origQty": "1", # "triggerPriceType": "LATEST_PRICE", # "triggerProfitPrice": null, # "triggerStopPrice": "20000", # "entryPrice": "0", # "positionSize": "0", # "isolatedMargin": "0", # "executedQty": "0", # "avgPrice": null, # "positionType": "ISOLATED", # "state": "USER_REVOCATION", # "createdTime": 1681273420039 # }, # ] # } # } # orders = [] resultDict = self.safe_dict(response, 'result') if resultDict is not None: orders = self.safe_list(resultDict, 'items', []) else: orders = self.safe_list(response, 'result') return self.parse_orders(orders, market, since, limit) def fetch_open_orders(self, symbol: str = None, since: Int = None, limit: Int = None, params={}): """ fetch all unfilled currently open orders https://doc.xt.com/#orderopenOrderGet https://doc.xt.com/#futures_ordergetOrders https://doc.xt.com/#futures_entrustgetPlan https://doc.xt.com/#futures_entrustgetProfit :param str [symbol]: unified market symbol of the market the orders were made in :param int [since]: timestamp in ms of the earliest order :param int [limit]: the maximum number of open order structures to retrieve :param dict params: extra parameters specific to the xt api endpoint :param bool [params.trigger]: if the order is a trigger order or not :param bool [params.stopLossTakeProfit]: if the order is a stop-loss or take-profit order :returns dict[]: a list of `order structures ` """ return self.fetch_orders_by_status('open', symbol, since, limit, params) def fetch_closed_orders(self, symbol: str = None, since: Int = None, limit: Int = None, params={}): """ fetches information on multiple closed orders made by the user https://doc.xt.com/#orderhistoryOrderGet https://doc.xt.com/#futures_ordergetOrders https://doc.xt.com/#futures_entrustgetPlan https://doc.xt.com/#futures_entrustgetProfit :param str [symbol]: unified market symbol of the market the orders were made in :param int [since]: timestamp in ms of the earliest order :param int [limit]: the maximum number of order structures to retrieve :param dict params: extra parameters specific to the xt api endpoint :param bool [params.trigger]: if the order is a trigger order or not :param bool [params.stopLossTakeProfit]: if the order is a stop-loss or take-profit order :returns dict[]: a list of `order structures ` """ return self.fetch_orders_by_status('closed', symbol, since, limit, params) def fetch_canceled_orders(self, symbol: str = None, since: Int = None, limit: Int = None, params={}): """ fetches information on multiple canceled orders made by the user https://doc.xt.com/#orderhistoryOrderGet https://doc.xt.com/#futures_ordergetOrders https://doc.xt.com/#futures_entrustgetPlan https://doc.xt.com/#futures_entrustgetProfit :param str [symbol]: unified market symbol of the market the orders were made in :param int [since]: timestamp in ms of the earliest order :param int [limit]: the maximum number of order structures to retrieve :param dict params: extra parameters specific to the xt api endpoint :param bool [params.trigger]: if the order is a trigger order or not :param bool [params.stopLossTakeProfit]: if the order is a stop-loss or take-profit order :returns dict: a list of `order structures ` """ return self.fetch_orders_by_status('canceled', symbol, since, limit, params) def cancel_order(self, id: str, symbol: str = None, params={}): """ cancels an open order https://doc.xt.com/#orderorderDel https://doc.xt.com/#futures_ordercancel https://doc.xt.com/#futures_entrustcancelPlan https://doc.xt.com/#futures_entrustcancelProfit :param str id: order id :param str [symbol]: unified symbol of the market the order was made in :param dict params: extra parameters specific to the xt api endpoint :param bool [params.trigger]: if the order is a trigger order or not :param bool [params.stopLossTakeProfit]: if the order is a stop-loss or take-profit order :returns dict: An `order structure ` """ self.load_markets() market = None if symbol is not None: market = self.market(symbol) request = {} type = None subType = None response = None type, params = self.handle_market_type_and_params('cancelOrder', market, params) subType, params = self.handle_sub_type_and_params('cancelOrder', market, params) trigger = self.safe_value_2(params, 'trigger', 'stop') stopLossTakeProfit = self.safe_value(params, 'stopLossTakeProfit') if trigger: request['entrustId'] = id elif stopLossTakeProfit: request['profitId'] = id else: request['orderId'] = id if trigger: params = self.omit(params, ['trigger', 'stop']) if subType == 'inverse': response = self.privateInversePostFutureTradeV1EntrustCancelPlan(self.extend(request, params)) else: response = self.privateLinearPostFutureTradeV1EntrustCancelPlan(self.extend(request, params)) elif stopLossTakeProfit: params = self.omit(params, 'stopLossTakeProfit') if subType == 'inverse': response = self.privateInversePostFutureTradeV1EntrustCancelProfitStop(self.extend(request, params)) else: response = self.privateLinearPostFutureTradeV1EntrustCancelProfitStop(self.extend(request, params)) elif subType == 'inverse': response = self.privateInversePostFutureTradeV1OrderCancel(self.extend(request, params)) elif (subType == 'linear') or (type == 'swap') or (type == 'future'): response = self.privateLinearPostFutureTradeV1OrderCancel(self.extend(request, params)) else: response = self.privateSpotDeleteOrderOrderId(self.extend(request, params)) # # spot # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": { # "cancelId": "208322474307982720" # } # } # # swap and future # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": "208319789679471616" # } # isContractResponse = ((subType is not None) or (type == 'swap') or (type == 'future')) order = response if isContractResponse else self.safe_value(response, 'result', {}) return self.parse_order(order, market) def cancel_all_orders(self, symbol: str = None, params={}): """ cancel all open orders in a market https://doc.xt.com/#orderopenOrderDel https://doc.xt.com/#futures_ordercancelBatch https://doc.xt.com/#futures_entrustcancelPlanBatch https://doc.xt.com/#futures_entrustcancelProfitBatch :param str [symbol]: unified market symbol of the market to cancel orders in :param dict params: extra parameters specific to the xt api endpoint :param bool [params.trigger]: if the order is a trigger order or not :param bool [params.stopLossTakeProfit]: if the order is a stop-loss or take-profit order :returns dict[]: a list of `order structures ` """ self.load_markets() request = {} market = None if symbol is not None: market = self.market(symbol) request['symbol'] = market['id'] type = None subType = None response = None type, params = self.handle_market_type_and_params('cancelAllOrders', market, params) subType, params = self.handle_sub_type_and_params('cancelAllOrders', market, params) trigger = self.safe_value_2(params, 'trigger', 'stop') stopLossTakeProfit = self.safe_value(params, 'stopLossTakeProfit') if trigger: params = self.omit(params, ['trigger', 'stop']) if subType == 'inverse': response = self.privateInversePostFutureTradeV1EntrustCancelAllPlan(self.extend(request, params)) else: response = self.privateLinearPostFutureTradeV1EntrustCancelAllPlan(self.extend(request, params)) elif stopLossTakeProfit: params = self.omit(params, 'stopLossTakeProfit') if subType == 'inverse': response = self.privateInversePostFutureTradeV1EntrustCancelAllProfitStop(self.extend(request, params)) else: response = self.privateLinearPostFutureTradeV1EntrustCancelAllProfitStop(self.extend(request, params)) elif subType == 'inverse': response = self.privateInversePostFutureTradeV1OrderCancelAll(self.extend(request, params)) elif (subType == 'linear') or (type == 'swap') or (type == 'future'): response = self.privateLinearPostFutureTradeV1OrderCancelAll(self.extend(request, params)) else: marginMode = None marginMode, params = self.handle_margin_mode_and_params('cancelAllOrders', params) marginOrSpotRequest = 'LEVER' if (marginMode is not None) else 'SPOT' request['bizType'] = marginOrSpotRequest response = self.privateSpotDeleteOpenOrder(self.extend(request, params)) # # spot and margin # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": null # } # # swap and future # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": True # } # return [ self.safe_order(response), ] def cancel_orders(self, ids: List[str], symbol: str = None, params={}) -> List[Order]: """ cancel multiple orders https://doc.xt.com/#orderbatchOrderDel :param str[] ids: order ids :param str [symbol]: unified market symbol of the market to cancel orders in :param dict params: extra parameters specific to the xt api endpoint :returns dict[]: a list of `order structures ` """ self.load_markets() request = { 'orderIds': ids, } market = None if symbol is not None: market = self.market(symbol) subType = None subType, params = self.handle_sub_type_and_params('cancelOrders', market, params) if subType is not None: raise NotSupported(self.id + ' cancelOrders() does not support swap and future orders, only spot orders are accepted') response = self.privateSpotDeleteBatchOrder(self.extend(request, params)) # # spot # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": null # } # return [ self.safe_order(response), ] def parse_order(self, order, market=None): # # spot: createOrder # # { # "orderId": "204371980095156544" # } # # spot: cancelOrder # # { # "cancelId": "208322474307982720" # } # # swap and future: createOrder, cancelOrder, editOrder # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": "206410760006650176" # } # # spot: fetchOrder, fetchOrders, fetchOpenOrders, fetchClosedOrders, fetchCanceledOrders, fetchOrdersByStatus # # { # "symbol": "btc_usdt", # "orderId": "207505997850909952", # "clientOrderId": null, # "baseCurrency": "btc", # "quoteCurrency": "usdt", # "side": "BUY", # "type": "LIMIT", # "timeInForce": "GTC", # "price": "20000.00", # "origQty": "0.001000", # "origQuoteQty": "20.00", # "executedQty": "0.000000", # "leavingQty": "0.001000", # "tradeBase": "0.000000", # "tradeQuote": "0.00", # "avgPrice": null, # "fee": null, # "feeCurrency": null, # "closed": False, # "state": "NEW", # "time": 1679175285162, # "updatedTime": 1679175285255 # } # # swap and future: fetchOrder, fetchOrders, fetchOpenOrders, fetchClosedOrders, fetchCanceledOrders, fetchOrdersByStatus # # { # "orderId": "207519546930995456", # "clientOrderId": null, # "symbol": "btc_usdt", # "orderType": "LIMIT", # "orderSide": "BUY", # "positionSide": "LONG", # "timeInForce": "GTC", # "closePosition": False, # "price": "20000", # "origQty": "100", # "avgPrice": "0", # "executedQty": "0", # "marginFrozen": "4.12", # "remark": null, # "triggerProfitPrice": null, # "triggerStopPrice": null, # "sourceId": null, # "sourceType": "DEFAULT", # "forceClose": False, # "closeProfit": null, # "state": "CANCELED", # "createdTime": 1679178515689, # "updatedTime": 1679180096172 # } # # trigger: fetchOrder, fetchOrders, fetchOpenOrders, fetchClosedOrders, fetchCanceledOrders, fetchOrdersByStatus # # { # "entrustId": "216300248132756992", # "symbol": "btc_usdt", # "entrustType": "STOP", # "orderSide": "SELL", # "positionSide": "SHORT", # "timeInForce": "GTC", # "closePosition": null, # "price": "20000", # "origQty": "1", # "stopPrice": "19000", # "triggerPriceType": "LATEST_PRICE", # "state": "NOT_TRIGGERED", # "marketOrderLevel": null, # "createdTime": 1681271998064, # "updatedTime": 1681271998064, # "ordinary": False # } # # stop-loss and take-profit: fetchOrder, fetchOpenOrders, fetchClosedOrders, fetchCanceledOrders, fetchOrdersByStatus # # { # "profitId": "216306213226230400", # "symbol": "btc_usdt", # "positionSide": "LONG", # "origQty": "1", # "triggerPriceType": "LATEST_PRICE", # "triggerProfitPrice": null, # "triggerStopPrice": "20000", # "entryPrice": null, # "positionSize": null, # "isolatedMargin": null, # "executedQty": null, # "avgPrice": null, # "positionType": "ISOLATED", # "state": "NOT_TRIGGERED", # "createdTime": 1681273420039 # } # # spot editOrder # # { # "orderId": "484203027161892224", # "modifyId": "484203544105344000", # "clientModifyId": null # } # marketId = self.safe_string(order, 'symbol') marketType = ('result' in order) or 'contract' if ('positionSide' in order) else 'spot' market = self.safe_market(marketId, market, None, marketType) symbol = self.safe_symbol(marketId, market, None, marketType) timestamp = self.safe_integer_2(order, 'time', 'createdTime') quantity = self.safe_number(order, 'origQty') amount = quantity if (marketType == 'spot') else Precise.string_mul(self.number_to_string(quantity), self.number_to_string(market['contractSize'])) filledQuantity = self.safe_number(order, 'executedQty') filled = filledQuantity if (marketType == 'spot') else Precise.string_mul(self.number_to_string(filledQuantity), self.number_to_string(market['contractSize'])) lastUpdatedTimestamp = self.safe_integer(order, 'updatedTime') return self.safe_order({ 'info': order, 'id': self.safe_string_n(order, ['orderId', 'result', 'cancelId', 'entrustId', 'profitId']), 'clientOrderId': self.safe_string_2(order, 'clientOrderId', 'clientModifyId'), 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), 'lastTradeTimestamp': lastUpdatedTimestamp, 'lastUpdateTimestamp': lastUpdatedTimestamp, 'symbol': symbol, 'type': self.safe_string_lower_2(order, 'type', 'orderType'), 'timeInForce': self.safe_string(order, 'timeInForce'), 'postOnly': None, 'side': self.safe_string_lower_2(order, 'side', 'orderSide'), 'price': self.safe_number(order, 'price'), 'triggerPrice': self.safe_number(order, 'stopPrice'), 'stopLoss': self.safe_number(order, 'triggerStopPrice'), 'takeProfit': self.safe_number(order, 'triggerProfitPrice'), 'amount': amount, 'filled': filled, 'remaining': self.safe_number(order, 'leavingQty'), 'cost': None, 'average': self.safe_number(order, 'avgPrice'), 'status': self.parse_order_status(self.safe_string(order, 'state')), 'fee': { 'currency': self.safe_currency_code(self.safe_string(order, 'feeCurrency')), 'cost': self.safe_number(order, 'fee'), }, 'trades': None, }, market) def parse_order_status(self, status): statuses = { 'NEW': 'open', 'PARTIALLY_FILLED': 'open', 'FILLED': 'closed', 'CANCELED': 'canceled', 'REJECTED': 'rejected', 'EXPIRED': 'expired', 'UNFINISHED': 'open', 'NOT_TRIGGERED': 'open', 'TRIGGERING': 'open', 'TRIGGERED': 'closed', 'USER_REVOCATION': 'canceled', 'PLATFORM_REVOCATION': 'rejected', 'HISTORY': 'expired', } return self.safe_string(statuses, status, status) def fetch_ledger(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[LedgerEntry]: """ fetch the history of changes, actions done by the user or operations that altered the balance of the user https://doc.xt.com/#futures_usergetBalanceBill :param str [code]: unified currency code :param int [since]: timestamp in ms of the earliest ledger entry :param int [limit]: max number of ledger entries to return :param dict params: extra parameters specific to the xt api endpoint :returns dict: a `ledger structure ` """ self.load_markets() request = {} currency = None if code is not None: currency = self.currency(code) if since is not None: request['startTime'] = since if limit is not None: request['limit'] = limit type = None subType = None response = None type, params = self.handle_market_type_and_params('fetchLedger', None, params) subType, params = self.handle_sub_type_and_params('fetchLedger', None, params) if subType == 'inverse': response = self.privateInverseGetFutureUserV1BalanceBills(self.extend(request, params)) elif (subType == 'linear') or (type == 'swap') or (type == 'future'): response = self.privateLinearGetFutureUserV1BalanceBills(self.extend(request, params)) else: raise NotSupported(self.id + ' fetchLedger() does not support spot transactions, only swap and future wallet transactions are supported') # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": { # "hasPrev": False, # "hasNext": False, # "items": [ # { # "id": "207260567109387524", # "coin": "usdt", # "symbol": "btc_usdt", # "type": "FEE", # "amount": "-0.0213", # "side": "SUB", # "afterAmount": null, # "createdTime": 1679116769914 # }, # ] # } # } # data = self.safe_value(response, 'result', {}) ledger = self.safe_value(data, 'items', []) return self.parse_ledger(ledger, currency, since, limit) def parse_ledger_entry(self, item, currency=None) -> LedgerEntry: # # { # "id": "207260567109387524", # "coin": "usdt", # "symbol": "btc_usdt", # "type": "FEE", # "amount": "-0.0213", # "side": "SUB", # "afterAmount": null, # "createdTime": 1679116769914 # } # side = self.safe_string(item, 'side') direction = 'in' if (side == 'ADD') else 'out' currencyId = self.safe_string(item, 'coin') currency = self.safe_currency(currencyId, currency) timestamp = self.safe_integer(item, 'createdTime') return self.safe_ledger_entry({ 'info': item, 'id': self.safe_string(item, 'id'), 'direction': direction, 'account': None, 'referenceId': None, 'referenceAccount': None, 'type': self.parse_ledger_entry_type(self.safe_string(item, 'type')), 'currency': self.safe_currency_code(currencyId, currency), 'amount': self.safe_number(item, 'amount'), 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), 'before': None, 'after': self.safe_number(item, 'afterAmount'), 'status': None, 'fee': { 'currency': None, 'cost': None, }, }, currency) def parse_ledger_entry_type(self, type): ledgerType = { 'EXCHANGE': 'transfer', 'CLOSE_POSITION': 'trade', 'TAKE_OVER': 'trade', 'MERGE': 'trade', 'QIANG_PING_MANAGER': 'fee', 'FUND': 'fee', 'FEE': 'fee', 'ADL': 'auto-deleveraging', } return self.safe_string(ledgerType, type, type) def fetch_deposit_address(self, code: str, params={}) -> DepositAddress: """ fetch the deposit address for a currency associated with self account https://doc.xt.com/#deposit_withdrawaldepositAddressGet :param str code: unified currency code :param dict params: extra parameters specific to the xt api endpoint :param str params['network']: required network id :returns dict: an `address structure ` """ self.load_markets() networkCode = None networkCode, params = self.handle_network_code_and_params(params) currency = self.currency(code) networkId = self.network_code_to_id(networkCode, code) self.check_required_argument('fetchDepositAddress', networkId, 'network') request = { 'currency': currency['id'], 'chain': networkId, } response = self.privateSpotGetDepositAddress(self.extend(request, params)) # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": { # "address": "0x7f7173cf29d3846d20ca5a3aec1120b93dbd157a", # "memo": "" # } # } # result = self.safe_value(response, 'result', {}) return self.parse_deposit_address(result, currency) def parse_deposit_address(self, depositAddress, currency=None) -> DepositAddress: # # { # "address": "0x7f7173cf29d3846d20ca5a3aec1120b93dbd157a", # "memo": "" # } # address = self.safe_string(depositAddress, 'address') self.check_address(address) return { 'info': depositAddress, 'currency': self.safe_currency_code(None, currency), 'network': None, 'address': address, 'tag': self.safe_string(depositAddress, 'memo'), } def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}): """ fetch all deposits made to an account https://doc.xt.com/#deposit_withdrawalhistoryDepositGet :param str [code]: unified currency code :param int [since]: the earliest time in ms to fetch deposits for :param int [limit]: the maximum number of transaction structures to retrieve :param dict params: extra parameters specific to the xt api endpoint :returns dict[]: a list of `transaction structures ` """ self.load_markets() request = {} currency = None if code is not None: currency = self.currency(code) request['currency'] = currency['id'] if since is not None: request['startTime'] = since if limit is not None: request['limit'] = limit # default 10, max 200 response = self.privateSpotGetDepositHistory(self.extend(request, params)) # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": { # "hasPrev": False, # "hasNext": False, # "items": [ # { # "id": 170368702, # "currency": "usdt", # "chain": "Ethereum", # "memo": "", # "status": "SUCCESS", # "amount": "31.792528", # "confirmations": 12, # "transactionId": "0x90b8487c258b81b85e15e461b1839c49d4d8e6e9de4c1adb658cd47d4f5c5321", # "address": "0x7f7172cf29d3846d30ca5a3aec1120b92dbd150b", # "fromAddr": "0x7830c87c02e56aff27fa9ab1241711331fa86f58", # "createdTime": 1678491442000 # }, # ] # } # } # data = self.safe_value(response, 'result', {}) deposits = self.safe_value(data, 'items', []) return self.parse_transactions(deposits, currency, since, limit, params) def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}): """ fetch all withdrawals made from an account https://doc.xt.com/#deposit_withdrawalwithdrawHistory :param str [code]: unified currency code :param int [since]: the earliest time in ms to fetch withdrawals for :param int [limit]: the maximum number of transaction structures to retrieve :param dict params: extra parameters specific to the xt api endpoint :returns dict[]: a list of `transaction structures ` """ self.load_markets() request = {} currency = None if code is not None: currency = self.currency(code) request['currency'] = currency['id'] if since is not None: request['startTime'] = since if limit is not None: request['limit'] = limit # default 10, max 200 response = self.privateSpotGetWithdrawHistory(self.extend(request, params)) # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": { # "hasPrev": False, # "hasNext": False, # "items": [ # { # "id": 950898, # "currency": "usdt", # "chain": "Tron", # "address": "TGB2vxTjiqraVZBy7YHXF8V3CSMVhQKcaf", # "memo": "", # "status": "SUCCESS", # "amount": "5", # "fee": "2", # "confirmations": 6, # "transactionId": "c36e230b879842b1d7afd19d15ee1a866e26eaa0626e367d6f545d2932a15156", # "createdTime": 1680049062000 # } # ] # } # } # data = self.safe_value(response, 'result', {}) withdrawals = self.safe_value(data, 'items', []) return self.parse_transactions(withdrawals, currency, since, limit, params) def withdraw(self, code: str, amount: float, address: str, tag: Str = None, params={}) -> Transaction: """ make a withdrawal https://doc.xt.com/#deposit_withdrawalwithdraw :param str code: unified currency code :param float amount: the amount to withdraw :param str address: the address to withdraw to :param str [tag]: :param dict params: extra parameters specific to the xt api endpoint :returns dict: a `transaction structure ` """ self.check_address(address) self.load_markets() currency = self.currency(code) tag, params = self.handle_withdraw_tag_and_params(tag, params) networkCode = None networkCode, params = self.handle_network_code_and_params(params) networkIdsByCodes = self.safe_value(self.options, 'networks', {}) networkId = self.safe_string_2(networkIdsByCodes, networkCode, code, code) request = { 'currency': currency['id'], 'chain': networkId, 'amount': self.currency_to_precision(code, amount), 'address': address, } if tag is not None: request['memo'] = tag response = self.privateSpotPostWithdraw(self.extend(request, params)) # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": { # "id": 950898 # } # } # result = self.safe_value(response, 'result', {}) return self.parse_transaction(result, currency) def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction: # # fetchDeposits # # { # "id": 170368702, # "currency": "usdt", # "chain": "Ethereum", # "memo": "", # "status": "SUCCESS", # "amount": "31.792528", # "confirmations": 12, # "transactionId": "0x90b8487c258b81b85e15e461b1839c49d4d8e6e9de4c1adb658cd47d4f5c5321", # "address": "0x7f7172cf29d3846d30ca5a3aec1120b92dbd150b", # "fromAddr": "0x7830c87c02e56aff27fa9ab1241711331fa86f58", # "createdTime": 1678491442000 # } # # fetchWithdrawals # # { # "id": 950898, # "currency": "usdt", # "chain": "Tron", # "address": "TGB2vxTjiqraVZBy7YHXF8V3CSMVhQKcaf", # "memo": "", # "status": "SUCCESS", # "amount": "5", # "fee": "2", # "confirmations": 6, # "transactionId": "c36e230b879842b1d7afd19d15ee1a866e26eaa0626e367d6f545d2932a15156", # "createdTime": 1680049062000 # } # # withdraw # # { # "id": 950898 # } # type = 'deposit' if ('fromAddr' in transaction) else 'withdraw' timestamp = self.safe_integer(transaction, 'createdTime') address = self.safe_string(transaction, 'address') memo = self.safe_string(transaction, 'memo') currencyCode = self.safe_currency_code(self.safe_string(transaction, 'currency'), currency) fee = self.safe_number(transaction, 'fee') feeCurrency = currencyCode if (fee is not None) else None networkId = self.safe_string(transaction, 'chain') return { 'info': transaction, 'id': self.safe_string(transaction, 'id'), 'txid': self.safe_string(transaction, 'transactionId'), 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), 'updated': None, 'addressFrom': self.safe_string(transaction, 'fromAddr'), 'addressTo': address, 'address': address, 'tagFrom': None, 'tagTo': None, 'tag': memo, 'type': type, 'amount': self.safe_number(transaction, 'amount'), 'currency': currencyCode, 'network': self.network_id_to_code(networkId, currencyCode), 'status': self.parse_transaction_status(self.safe_string(transaction, 'status')), 'comment': memo, 'fee': { 'currency': feeCurrency, 'cost': fee, 'rate': None, }, 'internal': None, } def parse_transaction_status(self, status): statuses = { 'SUBMIT': 'pending', 'REVIEW': 'pending', 'AUDITED': 'pending', 'PENDING': 'pending', 'CANCEL': 'canceled', 'FAIL': 'failed', 'SUCCESS': 'ok', } return self.safe_string(statuses, status, status) def set_leverage(self, leverage: int, symbol: str = None, params={}): """ set the level of leverage for a market https://doc.xt.com/#futures_useradjustLeverage :param float leverage: the rate of leverage :param str symbol: unified market symbol :param dict params: extra parameters specific to the xt api endpoint :param str params['positionSide']: 'LONG' or 'SHORT' :returns dict: response from the exchange """ if symbol is None: raise ArgumentsRequired(self.id + ' setLeverage() requires a symbol argument') positionSide = self.safe_string(params, 'positionSide') self.check_required_argument('setLeverage', positionSide, 'positionSide', ['LONG', 'SHORT']) if (leverage < 1) or (leverage > 125): raise BadRequest(self.id + ' setLeverage() leverage should be between 1 and 125') self.load_markets() market = self.market(symbol) if not (market['contract']): raise BadSymbol(self.id + ' setLeverage() supports contract markets only') request = { 'symbol': market['id'], 'positionSide': positionSide, 'leverage': leverage, } subType = None subType, params = self.handle_sub_type_and_params('setLeverage', market, params) response = None if subType == 'inverse': response = self.privateInversePostFutureUserV1PositionAdjustLeverage(self.extend(request, params)) else: response = self.privateLinearPostFutureUserV1PositionAdjustLeverage(self.extend(request, params)) # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": null # } # return response def add_margin(self, symbol: str, amount: float, params={}): """ add margin to a position https://doc.xt.com/#futures_useradjustMargin :param str symbol: unified market symbol :param float amount: amount of margin to add :param dict params: extra parameters specific to the xt api endpoint :param str params['positionSide']: 'LONG' or 'SHORT' :returns dict: a `margin structure ` """ return self.modify_margin_helper(symbol, amount, 'ADD', params) def reduce_margin(self, symbol: str, amount: float, params={}): """ remove margin from a position https://doc.xt.com/#futures_useradjustMargin :param str symbol: unified market symbol :param float amount: the amount of margin to remove :param dict params: extra parameters specific to the xt api endpoint :param str params['positionSide']: 'LONG' or 'SHORT' :returns dict: a `margin structure ` """ return self.modify_margin_helper(symbol, amount, 'SUB', params) def modify_margin_helper(self, symbol: str, amount, addOrReduce, params={}) -> MarginModification: positionSide = self.safe_string(params, 'positionSide') self.check_required_argument('setLeverage', positionSide, 'positionSide', ['LONG', 'SHORT']) self.load_markets() market = self.market(symbol) request = { 'symbol': market['id'], 'margin': amount, 'type': addOrReduce, 'positionSide': positionSide, } subType = None subType, params = self.handle_sub_type_and_params('modifyMarginHelper', market, params) response = None if subType == 'inverse': response = self.privateInversePostFutureUserV1PositionMargin(self.extend(request, params)) else: response = self.privateLinearPostFutureUserV1PositionMargin(self.extend(request, params)) # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": null # } # return self.parse_margin_modification(response, market) def parse_margin_modification(self, data, market=None) -> MarginModification: return { 'info': data, 'type': None, 'amount': None, 'code': None, 'symbol': self.safe_symbol(None, market), 'status': None, 'marginMode': None, 'total': None, 'timestamp': None, 'datetime': None, } def fetch_leverage_tiers(self, symbols: List[str] = None, params={}) -> LeverageTiers: """ retrieve information on the maximum leverage for different trade sizes https://doc.xt.com/#futures_quotesgetLeverageBrackets :param str [symbols]: a list of unified market symbols :param dict params: extra parameters specific to the xt api endpoint :returns dict: a dictionary of `leverage tiers structures ` """ self.load_markets() subType = None subType, params = self.handle_sub_type_and_params('fetchLeverageTiers', None, params) response = None if subType == 'inverse': response = self.publicInverseGetFutureMarketV1PublicLeverageBracketList(params) else: response = self.publicLinearGetFutureMarketV1PublicLeverageBracketList(params) # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": [ # { # "symbol": "rad_usdt", # "leverageBrackets": [ # { # "symbol": "rad_usdt", # "bracket": 1, # "maxNominalValue": "5000", # "maintMarginRate": "0.025", # "startMarginRate": "0.05", # "maxStartMarginRate": null, # "maxLeverage": "20", # "minLeverage": "1" # }, # ] # }, # ] # } # data = self.safe_value(response, 'result', []) symbols = self.market_symbols(symbols) return self.parse_leverage_tiers(data, symbols, 'symbol') def parse_leverage_tiers(self, response, symbols=None, marketIdKey=None) -> LeverageTiers: # # { # "symbol": "rad_usdt", # "leverageBrackets": [ # { # "symbol": "rad_usdt", # "bracket": 1, # "maxNominalValue": "5000", # "maintMarginRate": "0.025", # "startMarginRate": "0.05", # "maxStartMarginRate": null, # "maxLeverage": "20", # "minLeverage": "1" # }, # ] # } # result = {} for i in range(0, len(response)): entry = response[i] marketId = self.safe_string(entry, 'symbol') market = self.safe_market(marketId, None, '_', 'contract') symbol = self.safe_symbol(marketId, market) if symbols is not None: if self.in_array(symbol, symbols): result[symbol] = self.parse_market_leverage_tiers(entry, market) else: result[symbol] = self.parse_market_leverage_tiers(response[i], market) return result def fetch_market_leverage_tiers(self, symbol: str, params={}) -> List[LeverageTier]: """ retrieve information on the maximum leverage for different trade sizes of a single market https://doc.xt.com/#futures_quotesgetLeverageBracket :param str symbol: unified market symbol :param dict params: extra parameters specific to the xt api endpoint :returns dict: a `leverage tiers structure ` """ self.load_markets() market = self.market(symbol) request = { 'symbol': market['id'], } subType = None subType, params = self.handle_sub_type_and_params('fetchMarketLeverageTiers', market, params) response = None if subType == 'inverse': response = self.publicInverseGetFutureMarketV1PublicLeverageBracketDetail(self.extend(request, params)) else: response = self.publicLinearGetFutureMarketV1PublicLeverageBracketDetail(self.extend(request, params)) # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": { # "symbol": "btc_usdt", # "leverageBrackets": [ # { # "symbol": "btc_usdt", # "bracket": 1, # "maxNominalValue": "500000", # "maintMarginRate": "0.004", # "startMarginRate": "0.008", # "maxStartMarginRate": null, # "maxLeverage": "125", # "minLeverage": "1" # }, # ] # } # } # data = self.safe_value(response, 'result', {}) return self.parse_market_leverage_tiers(data, market) def parse_market_leverage_tiers(self, info, market=None) -> List[LeverageTier]: # # { # "symbol": "rad_usdt", # "leverageBrackets": [ # { # "symbol": "rad_usdt", # "bracket": 1, # "maxNominalValue": "5000", # "maintMarginRate": "0.025", # "startMarginRate": "0.05", # "maxStartMarginRate": null, # "maxLeverage": "20", # "minLeverage": "1" # }, # ] # } # tiers = [] brackets = self.safe_value(info, 'leverageBrackets', []) for i in range(0, len(brackets)): tier = brackets[i] marketId = self.safe_string(info, 'symbol') market = self.safe_market(marketId, market, '_', 'contract') tiers.append({ 'tier': self.safe_integer(tier, 'bracket'), 'symbol': self.safe_symbol(marketId, market, '_', 'contract'), 'currency': market['settle'], 'minNotional': self.safe_number(brackets[i - 1], 'maxNominalValue', 0), 'maxNotional': self.safe_number(tier, 'maxNominalValue'), 'maintenanceMarginRate': self.safe_number(tier, 'maintMarginRate'), 'maxLeverage': self.safe_number(tier, 'maxLeverage'), 'info': tier, }) return tiers def fetch_funding_rate_history(self, symbol: str = None, since: Int = None, limit: Int = None, params={}): """ fetches historical funding rates https://doc.xt.com/#futures_quotesgetFundingRateRecord :param str [symbol]: unified symbol of the market to fetch the funding rate history for :param int [since]: timestamp in ms of the earliest funding rate to fetch :param int [limit]: the maximum amount of [funding rate structures] to fetch :param dict params: extra parameters specific to the xt api endpoint :param bool params['paginate']: True/false whether to use the pagination helper to aumatically paginate through the results :returns dict[]: a list of `funding rate structures ` """ if symbol is None: raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument') self.load_markets() paginate = False paginate, params = self.handle_option_and_params(params, 'fetchFundingRateHistory', 'paginate') if paginate: return self.fetch_paginated_call_cursor('fetchFundingRateHistory', symbol, since, limit, params, 'id', 'id', 1, 200) market = self.market(symbol) if not market['swap']: raise BadSymbol(self.id + ' fetchFundingRateHistory() supports swap contracts only') request = { 'symbol': market['id'], } if limit is not None: request['limit'] = limit else: request['limit'] = 200 # max subType = None subType, params = self.handle_sub_type_and_params('fetchFundingRateHistory', market, params) response = None if subType == 'inverse': response = self.publicInverseGetFutureMarketV1PublicQFundingRateRecord(self.extend(request, params)) else: response = self.publicLinearGetFutureMarketV1PublicQFundingRateRecord(self.extend(request, params)) # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": { # "hasPrev": False, # "hasNext": True, # "items": [ # { # "id": "210441653482221888", # "symbol": "btc_usdt", # "fundingRate": "0.000057", # "createdTime": 1679875200000, # "collectionInternal": 28800 # }, # ] # } # } # result = self.safe_value(response, 'result', {}) items = self.safe_value(result, 'items', []) rates = [] for i in range(0, len(items)): entry = items[i] marketId = self.safe_string(entry, 'symbol') symbolInner = self.safe_symbol(marketId, market) timestamp = self.safe_integer(entry, 'createdTime') rates.append({ 'info': entry, 'symbol': symbolInner, 'fundingRate': self.safe_number(entry, 'fundingRate'), 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), }) sorted = self.sort_by(rates, 'timestamp') return self.filter_by_symbol_since_limit(sorted, market['symbol'], since, limit) def fetch_funding_interval(self, symbol: str, params={}) -> FundingRate: """ fetch the current funding rate interval https://doc.xt.com/#futures_quotesgetFundingRate :param str symbol: unified market symbol :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a `funding rate structure ` """ return self.fetch_funding_rate(symbol, params) def fetch_funding_rate(self, symbol: str, params={}) -> FundingRate: """ fetch the current funding rate https://doc.xt.com/#futures_quotesgetFundingRate :param str symbol: unified market symbol :param dict params: extra parameters specific to the xt api endpoint :returns dict: a `funding rate structure ` """ self.load_markets() market = self.market(symbol) if not market['swap']: raise BadSymbol(self.id + ' fetchFundingRate() supports swap contracts only') request = { 'symbol': market['id'], } subType = None subType, params = self.handle_sub_type_and_params('fetchFundingRate', market, params) response = None if subType == 'inverse': response = self.publicInverseGetFutureMarketV1PublicQFundingRate(self.extend(request, params)) else: response = self.publicLinearGetFutureMarketV1PublicQFundingRate(self.extend(request, params)) # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": { # "symbol": "btc_usdt", # "fundingRate": "0.000086", # "nextCollectionTime": 1680307200000, # "collectionInternal": 8 # } # } # result = self.safe_value(response, 'result', {}) return self.parse_funding_rate(result, market) def parse_funding_rate(self, contract, market=None) -> FundingRate: # # { # "symbol": "btc_usdt", # "fundingRate": "0.000086", # "nextCollectionTime": 1680307200000, # "collectionInternal": 8 # } # marketId = self.safe_string(contract, 'symbol') symbol = self.safe_symbol(marketId, market, '_', 'swap') timestamp = self.safe_integer(contract, 'nextCollectionTime') interval = self.safe_string(contract, 'collectionInternal') if interval is not None: interval = interval + 'h' return { 'info': contract, 'symbol': symbol, 'markPrice': None, 'indexPrice': None, 'interestRate': None, 'estimatedSettlePrice': None, 'timestamp': None, 'datetime': None, 'fundingRate': self.safe_number(contract, 'fundingRate'), 'fundingTimestamp': timestamp, 'fundingDatetime': self.iso8601(timestamp), 'nextFundingRate': None, 'nextFundingTimestamp': None, 'nextFundingDatetime': None, 'previousFundingRate': None, 'previousFundingTimestamp': None, 'previousFundingDatetime': None, 'interval': interval, } def fetch_funding_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}): """ fetch the funding history https://doc.xt.com/#futures_usergetFunding :param str symbol: unified market symbol :param int [since]: the starting timestamp in milliseconds :param int [limit]: the number of entries to return :param dict params: extra parameters specific to the xt api endpoint :returns dict[]: a list of `funding history structures ` """ self.load_markets() market = self.market(symbol) if not market['swap']: raise BadSymbol(self.id + ' fetchFundingHistory() supports swap contracts only') request = { 'symbol': market['id'], } if since is not None: request['startTime'] = since if limit is not None: request['limit'] = limit subType = None subType, params = self.handle_sub_type_and_params('fetchFundingHistory', market, params) response = None if subType == 'inverse': response = self.privateInverseGetFutureUserV1BalanceFundingRateList(self.extend(request, params)) else: response = self.privateLinearGetFutureUserV1BalanceFundingRateList(self.extend(request, params)) # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": { # "hasPrev": False, # "hasNext": False, # "items": [ # { # "id": "210804044057280512", # "symbol": "btc_usdt", # "cast": "-0.0013", # "coin": "usdt", # "positionSide": "SHORT", # "createdTime": 1679961600653 # }, # ] # } # } # data = self.safe_value(response, 'result', {}) items = self.safe_value(data, 'items', []) result = [] for i in range(0, len(items)): entry = items[i] result.append(self.parse_funding_history(entry, market)) sorted = self.sort_by(result, 'timestamp') return self.filter_by_since_limit(sorted, since, limit) def parse_funding_history(self, contract, market=None): # # { # "id": "210804044057280512", # "symbol": "btc_usdt", # "cast": "-0.0013", # "coin": "usdt", # "positionSide": "SHORT", # "createdTime": 1679961600653 # } # marketId = self.safe_string(contract, 'symbol') symbol = self.safe_symbol(marketId, market, '_', 'swap') currencyId = self.safe_string(contract, 'coin') code = self.safe_currency_code(currencyId) timestamp = self.safe_integer(contract, 'createdTime') return { 'info': contract, 'symbol': symbol, 'code': code, 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), 'id': self.safe_string(contract, 'id'), 'amount': self.safe_number(contract, 'cast'), } def fetch_position(self, symbol: str, params={}): """ fetch data on a single open contract trade position https://doc.xt.com/#futures_usergetPosition :param str symbol: unified market symbol of the market the position is held in :param dict params: extra parameters specific to the xt api endpoint :returns dict: a `position structure ` """ self.load_markets() market = self.market(symbol) request = { 'symbol': market['id'], } subType = None subType, params = self.handle_sub_type_and_params('fetchPosition', market, params) response = None if subType == 'inverse': response = self.privateInverseGetFutureUserV1PositionList(self.extend(request, params)) else: response = self.privateLinearGetFutureUserV1PositionList(self.extend(request, params)) # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": [ # { # "symbol": "btc_usdt", # "positionType": "ISOLATED", # "positionSide": "SHORT", # "contractType": "PERPETUAL", # "positionSize": "10", # "closeOrderSize": "0", # "availableCloseSize": "10", # "entryPrice": "27060", # "openOrderSize": "0", # "isolatedMargin": "1.0824", # "openOrderMarginFrozen": "0", # "realizedProfit": "-0.00130138", # "autoMargin": False, # "leverage": 25 # }, # ] # } # positions = self.safe_value(response, 'result', []) for i in range(0, len(positions)): entry = positions[i] marketId = self.safe_string(entry, 'symbol') marketInner = self.safe_market(marketId, None, None, 'contract') positionSize = self.safe_string(entry, 'positionSize') if positionSize != '0': return self.parse_position(entry, marketInner) return None def fetch_positions(self, symbols: List[str] = None, params={}) -> List[Position]: """ fetch all open positions https://doc.xt.com/#futures_usergetPosition :param str [symbols]: list of unified market symbols, not supported with xt :param dict params: extra parameters specific to the xt api endpoint :returns dict[]: a list of `position structure ` """ self.load_markets() subType = None subType, params = self.handle_sub_type_and_params('fetchPositions', None, params) response = None if subType == 'inverse': response = self.privateInverseGetFutureUserV1PositionList(params) else: response = self.privateLinearGetFutureUserV1PositionList(params) # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": [ # { # "symbol": "btc_usdt", # "positionType": "ISOLATED", # "positionSide": "SHORT", # "contractType": "PERPETUAL", # "positionSize": "10", # "closeOrderSize": "0", # "availableCloseSize": "10", # "entryPrice": "27060", # "openOrderSize": "0", # "isolatedMargin": "1.0824", # "openOrderMarginFrozen": "0", # "realizedProfit": "-0.00130138", # "autoMargin": False, # "leverage": 25 # }, # ] # } # positions = self.safe_value(response, 'result', []) result = [] for i in range(0, len(positions)): entry = positions[i] marketId = self.safe_string(entry, 'symbol') marketInner = self.safe_market(marketId, None, None, 'contract') result.append(self.parse_position(entry, marketInner)) return self.filter_by_array_positions(result, 'symbol', symbols, False) def parse_position(self, position, market=None): # # { # "symbol": "btc_usdt", # "positionType": "ISOLATED", # "positionSide": "SHORT", # "contractType": "PERPETUAL", # "positionSize": "10", # "closeOrderSize": "0", # "availableCloseSize": "10", # "entryPrice": "27060", # "openOrderSize": "0", # "isolatedMargin": "1.0824", # "openOrderMarginFrozen": "0", # "realizedProfit": "-0.00130138", # "autoMargin": False, # "leverage": 25 # } # marketId = self.safe_string(position, 'symbol') market = self.safe_market(marketId, market, None, 'contract') symbol = self.safe_symbol(marketId, market, None, 'contract') positionType = self.safe_string(position, 'positionType') marginMode = 'cross' if (positionType == 'CROSSED') else 'isolated' collateral = self.safe_number(position, 'isolatedMargin') return self.safe_position({ 'info': position, 'id': None, 'symbol': symbol, 'timestamp': None, 'datetime': None, 'hedged': None, 'side': self.safe_string_lower(position, 'positionSide'), 'contracts': self.safe_number(position, 'positionSize'), 'contractSize': market['contractSize'], 'entryPrice': self.safe_number(position, 'entryPrice'), 'markPrice': None, 'notional': None, 'leverage': self.safe_integer(position, 'leverage'), 'collateral': collateral, 'initialMargin': collateral, 'maintenanceMargin': None, 'initialMarginPercentage': None, 'maintenanceMarginPercentage': None, 'unrealizedPnl': None, 'liquidationPrice': None, 'marginMode': marginMode, 'percentage': None, 'marginRatio': None, }) def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry: """ transfer currency internally between wallets on the same account https://doc.xt.com/#transfersubTransferPost :param str code: unified currency code :param float amount: amount to transfer :param str fromAccount: account to transfer from - spot, swap, leverage, finance :param str toAccount: account to transfer to - spot, swap, leverage, finance :param dict params: extra parameters specific to the whitebit api endpoint :returns dict: a `transfer structure ` """ self.load_markets() currency = self.currency(code) accountsByType = self.safe_value(self.options, 'accountsById') fromAccountId = self.safe_string(accountsByType, fromAccount, fromAccount) toAccountId = self.safe_string(accountsByType, toAccount, toAccount) amountString = self.currency_to_precision(code, amount) request = { 'bizId': self.uuid(), 'currency': currency['id'], 'amount': amountString, 'from': fromAccountId, 'to': toAccountId, } response = self.privateSpotPostBalanceTransfer(self.extend(request, params)) # # { # info: {rc: '0', mc: 'SUCCESS', ma: [], result: '226971333791398656'}, # id: '226971333791398656', # timestamp: None, # datetime: None, # currency: None, # amount: None, # fromAccount: None, # toAccount: None, # status: None # } # return self.parse_transfer(response, currency) def parse_transfer(self, transfer, currency=None): return { 'info': transfer, 'id': self.safe_string(transfer, 'result'), 'timestamp': None, 'datetime': None, 'currency': None, 'amount': None, 'fromAccount': None, 'toAccount': None, 'status': None, } def set_margin_mode(self, marginMode: str, symbol: Str = None, params={}): """ set margin mode to 'cross' or 'isolated' https://doc.xt.com/#futures_userchangePositionType :param str marginMode: 'cross' or 'isolated' :param str [symbol]: required :param dict [params]: extra parameters specific to the exchange API endpoint :param str [params.positionSide]: *required* "long" or "short" :returns dict: response from the exchange """ if symbol is None: raise ArgumentsRequired(self.id + ' setMarginMode() requires a symbol argument') self.load_markets() market = self.market(symbol) if market['spot']: raise BadSymbol(self.id + ' setMarginMode() supports contract markets only') marginMode = marginMode.lower() if marginMode != 'isolated' and marginMode != 'cross': raise BadRequest(self.id + ' setMarginMode() marginMode argument should be isolated or cross') if marginMode == 'cross': marginMode = 'CROSSED' else: marginMode = 'ISOLATED' posSide = self.safe_string_upper(params, 'positionSide') if posSide is None: raise ArgumentsRequired(self.id + ' setMarginMode() requires a positionSide parameter, either "LONG" or "SHORT"') request: dict = { 'positionType': marginMode, 'positionSide': posSide, 'symbol': market['id'], } response = self.privateLinearPostFutureUserV1PositionChangeType(self.extend(request, params)) # # { # "error": { # "code": "", # "msg": "" # }, # "msgInfo": "", # "result": {}, # "returnCode": 0 # } # return response # unify return type def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}) -> Order: """ cancels an order and places a new order https://doc.xt.com/#orderorderUpdate https://doc.xt.com/#futures_orderupdate https://doc.xt.com/#futures_entrustupdateProfit :param str id: order id :param str symbol: unified symbol of the market to create an order in :param str type: 'market' or 'limit' :param str side: 'buy' or 'sell' :param float amount: how much of the currency you want to trade in units of the base currency :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders :param dict [params]: extra parameters specific to the exchange API endpoint :param float [params.stopLoss]: price to set a stop-loss on an open position :param float [params.takeProfit]: price to set a take-profit on an open position :returns dict: an `order structure ` """ if amount is None: raise ArgumentsRequired(self.id + ' editOrder() requires an amount argument') self.load_markets() market = self.market(symbol) request = {} stopLoss = self.safe_number_2(params, 'stopLoss', 'triggerStopPrice') takeProfit = self.safe_number_2(params, 'takeProfit', 'triggerProfitPrice') params = self.omit(params, ['stopLoss', 'takeProfit']) isStopLoss = (stopLoss is not None) isTakeProfit = (takeProfit is not None) if isStopLoss or isTakeProfit: request['profitId'] = id else: request['orderId'] = id request['price'] = self.price_to_precision(symbol, price) response = None if market['swap']: if isStopLoss: request['triggerStopPrice'] = self.price_to_precision(symbol, stopLoss) elif takeProfit is not None: request['triggerProfitPrice'] = self.price_to_precision(symbol, takeProfit) else: request['origQty'] = self.amount_to_precision(symbol, amount) subType = None subType, params = self.handle_sub_type_and_params('editOrder', market, params) if subType == 'inverse': if isStopLoss or isTakeProfit: response = self.privateInversePostFutureTradeV1EntrustUpdateProfitStop(self.extend(request, params)) else: response = self.privateInversePostFutureTradeV1OrderUpdate(self.extend(request, params)) # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": "483869474947826752" # } # else: if isStopLoss or isTakeProfit: response = self.privateLinearPostFutureTradeV1EntrustUpdateProfitStop(self.extend(request, params)) else: response = self.privateLinearPostFutureTradeV1OrderUpdate(self.extend(request, params)) # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": "483869474947826752" # } # else: request['quantity'] = self.amount_to_precision(symbol, amount) response = self.privateSpotPutOrderOrderId(self.extend(request, params)) # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": { # "orderId": "484203027161892224", # "modifyId": "484203544105344000", # "clientModifyId": null # } # } # result = response if (market['swap']) else self.safe_dict(response, 'result', {}) return self.parse_order(result, market) def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody): # # spot: error # # { # "rc": 1, # "mc": "AUTH_103", # "ma": [], # "result": null # } # # spot: success # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": [] # } # # swap and future: error # # { # "returnCode": 1, # "msgInfo": "failure", # "error": { # "code": "403", # "msg": "invalid signature" # }, # "result": null # } # # swap and future: success # # { # "returnCode": 0, # "msgInfo": "success", # "error": null, # "result": null # } # # other: # # { # "rc": 0, # "mc": "SUCCESS", # "ma": [], # "result": {} # } # # {"returnCode":1,"msgInfo":"failure","error":{"code":"insufficient_balance","msg":"insufficient balance","args":[]},"result":null} # # status = self.safe_string_upper_2(response, 'msgInfo', 'mc') if status is not None and status != 'SUCCESS': feedback = self.id + ' ' + body error = self.safe_value(response, 'error', {}) spotErrorCode = self.safe_string(response, 'mc') errorCode = self.safe_string(error, 'code', spotErrorCode) spotMessage = self.safe_string(response, 'msgInfo') message = self.safe_string(error, 'msg', spotMessage) self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback) self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback) raise ExchangeError(feedback) return None def sign(self, path, api=[], method='GET', params={}, headers=None, body=None): signed = api[0] == 'private' endpoint = api[1] request = '/' + self.implode_params(path, params) payload = None if (endpoint == 'spot') or (endpoint == 'user'): if signed: payload = '/' + self.version + request else: payload = '/' + self.version + '/public' + request else: payload = request url = self.urls['api'][endpoint] + payload query = self.omit(params, self.extract_params(path)) urlencoded = self.urlencode(self.keysort(query)) headers = { 'Content-Type': 'application/json', } if signed: self.check_required_credentials() defaultRecvWindow = self.safe_string(self.options, 'recvWindow') recvWindow = self.safe_string(query, 'recvWindow', defaultRecvWindow) timestamp = self.number_to_string(self.nonce()) body = query if (payload == '/v4/order') or (payload == '/future/trade/v1/order/create') or (payload == '/future/trade/v1/entrust/create-plan') or (payload == '/future/trade/v1/entrust/create-profit') or (payload == '/future/trade/v1/order/create-batch'): id = 'CCXT' if payload.find('future') > -1: body['clientMedia'] = id else: body['media'] = id isUndefinedBody = ((method == 'GET') or (path == 'order/{orderId}') or (path == 'ws-token')) if (method == 'PUT') and (endpoint == 'spot'): isUndefinedBody = False body = None if isUndefinedBody else self.json(body) payloadString = None if (endpoint == 'spot') or (endpoint == 'user'): payloadString = 'xt-validate-algorithms=HmacSHA256&xt-validate-appkey=' + self.apiKey + '&xt-validate-recvwindow=' + recvWindow + '&xt-validate-t' + 'imestamp=' + timestamp if isUndefinedBody: if urlencoded: url += '?' + urlencoded payloadString += '#' + method + '#' + payload + '#' + self.rawencode(self.keysort(query)) else: payloadString += '#' + method + '#' + payload else: payloadString += '#' + method + '#' + payload + '#' + body headers['xt-validate-algorithms'] = 'HmacSHA256' headers['xt-validate-recvwindow'] = recvWindow else: payloadString = 'xt-validate-appkey=' + self.apiKey + '&xt-validate-t' + 'imestamp=' + timestamp # we can't glue timestamp, breaks in php if method == 'GET': if urlencoded: url += '?' + urlencoded payloadString += '#' + payload + '#' + urlencoded else: payloadString += '#' + payload else: payloadString += '#' + payload + '#' + body signature = self.hmac(self.encode(payloadString), self.encode(self.secret), hashlib.sha256) headers['xt-validate-appkey'] = self.apiKey headers['xt-validate-timestamp'] = timestamp headers['xt-validate-signature'] = signature else: if urlencoded: url += '?' + urlencoded return {'url': url, 'method': method, 'body': body, 'headers': headers}