4917 lines
212 KiB
Python
4917 lines
212 KiB
Python
# -*- 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 <https://docs.ccxt.com/en/latest/manual.html#order-book-structure>` 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 <https://docs.ccxt.com/en/latest/manual.html#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 <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
|
||
"""
|
||
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 <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
|
||
"""
|
||
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 <https://docs.ccxt.com/en/latest/manual.html?#public-trades>`
|
||
"""
|
||
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 <https://docs.ccxt.com/en/latest/manual.html?#public-trades>`
|
||
"""
|
||
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 <https://docs.ccxt.com/en/latest/manual.html?#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 <https://docs.ccxt.com/#/?id=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 <https://docs.ccxt.com/en/latest/manual.html#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 <https://docs.ccxt.com/en/latest/manual.html#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 <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
|
||
"""
|
||
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 <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
|
||
"""
|
||
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 <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
|
||
"""
|
||
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 <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
|
||
"""
|
||
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 <https://docs.ccxt.com/en/latest/manual.html#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 <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
|
||
"""
|
||
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 <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
|
||
"""
|
||
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 <https://docs.ccxt.com/en/latest/manual.html#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 <https://docs.ccxt.com/en/latest/manual.html#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 <https://docs.ccxt.com/en/latest/manual.html#transaction-structure>`
|
||
"""
|
||
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 <https://docs.ccxt.com/en/latest/manual.html#transaction-structure>`
|
||
"""
|
||
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 <https://docs.ccxt.com/en/latest/manual.html#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 <https://docs.ccxt.com/#/?id=add-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 <https://docs.ccxt.com/#/?id=reduce-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 <https://docs.ccxt.com/#/?id=leverage-tiers-structure>`
|
||
"""
|
||
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 <https://docs.ccxt.com/#/?id=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 <https://docs.ccxt.com/en/latest/manual.html?#funding-rate-history-structure>`
|
||
"""
|
||
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 <https://docs.ccxt.com/#/?id=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 <https://docs.ccxt.com/#/?id=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 <https://docs.ccxt.com/#/?id=funding-history-structure>`
|
||
"""
|
||
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 <https://docs.ccxt.com/#/?id=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 <https://docs.ccxt.com/#/?id=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 <https://docs.ccxt.com/#/?id=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 <https://docs.ccxt.com/#/?id=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}
|