ccxt 4.3.42__py2.py3-none-any.whl → 4.3.44__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ccxt might be problematic. Click here for more details.

@@ -0,0 +1,4454 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
4
+ # https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
5
+
6
+ from ccxt.async_support.base.exchange import Exchange
7
+ from ccxt.abstract.xt import ImplicitAPI
8
+ import asyncio
9
+ import hashlib
10
+ from ccxt.base.types import Currencies, Currency, Int, LeverageTier, MarginModification, Market, Num, Order, OrderSide, OrderType, Str, Tickers, Transaction, TransferEntry
11
+ from typing import List
12
+ from ccxt.base.errors import ExchangeError
13
+ from ccxt.base.errors import AuthenticationError
14
+ from ccxt.base.errors import PermissionDenied
15
+ from ccxt.base.errors import ArgumentsRequired
16
+ from ccxt.base.errors import BadRequest
17
+ from ccxt.base.errors import BadSymbol
18
+ from ccxt.base.errors import InsufficientFunds
19
+ from ccxt.base.errors import InvalidOrder
20
+ from ccxt.base.errors import NotSupported
21
+ from ccxt.base.errors import NetworkError
22
+ from ccxt.base.errors import RateLimitExceeded
23
+ from ccxt.base.errors import OnMaintenance
24
+ from ccxt.base.errors import RequestTimeout
25
+ from ccxt.base.decimal_to_precision import TICK_SIZE
26
+ from ccxt.base.precise import Precise
27
+
28
+
29
+ class xt(Exchange, ImplicitAPI):
30
+
31
+ def describe(self):
32
+ return self.deep_extend(super(xt, self).describe(), {
33
+ 'id': 'xt',
34
+ 'name': 'XT',
35
+ 'countries': ['SC'], # Seychelles
36
+ # spot api ratelimits are None, 10/s/ip, 50/s/ip, 100/s/ip or 200/s/ip
37
+ # futures 3 requests per second => 1000ms / (100 * 3.33) = 3.003(get assets -> fetchMarkets & fetchCurrencies)
38
+ # futures 10 requests per second => 1000ms / (100 * 1) = 10(all other)
39
+ # futures 1000 times per minute for each single IP -> Otherwise account locked for 10min
40
+ 'rateLimit': 100,
41
+ 'version': 'v4',
42
+ 'certified': True,
43
+ 'pro': False,
44
+ 'has': {
45
+ 'CORS': False,
46
+ 'spot': True,
47
+ 'margin': True,
48
+ 'swap': True,
49
+ 'future': True,
50
+ 'option': False,
51
+ 'addMargin': True,
52
+ 'borrowMargin': False,
53
+ 'cancelAllOrders': True,
54
+ 'cancelOrder': True,
55
+ 'cancelOrders': True,
56
+ 'createDepositAddress': False,
57
+ 'createMarketBuyOrderWithCost': True,
58
+ 'createMarketSellOrderWithCost': False,
59
+ 'createOrder': True,
60
+ 'createPostOnlyOrder': False,
61
+ 'createReduceOnlyOrder': True,
62
+ 'editOrder': False,
63
+ 'fetchAccounts': False,
64
+ 'fetchBalance': True,
65
+ 'fetchBidsAsks': True,
66
+ 'fetchBorrowInterest': False,
67
+ 'fetchBorrowRate': False,
68
+ 'fetchBorrowRateHistories': False,
69
+ 'fetchBorrowRateHistory': False,
70
+ 'fetchBorrowRatesPerSymbol': False,
71
+ 'fetchCanceledOrders': True,
72
+ 'fetchClosedOrders': True,
73
+ 'fetchCurrencies': True,
74
+ 'fetchDeposit': False,
75
+ 'fetchDepositAddress': True,
76
+ 'fetchDeposits': True,
77
+ 'fetchDepositWithdrawals': False,
78
+ 'fetchDepositWithdrawFee': False,
79
+ 'fetchDepositWithdrawFees': False,
80
+ 'fetchFundingHistory': True,
81
+ 'fetchFundingRate': True,
82
+ 'fetchFundingRateHistory': True,
83
+ 'fetchFundingRates': False,
84
+ 'fetchIndexOHLCV': False,
85
+ 'fetchL3OrderBook': False,
86
+ 'fetchLedger': True,
87
+ 'fetchLedgerEntry': False,
88
+ 'fetchLeverage': False,
89
+ 'fetchLeverageTiers': True,
90
+ 'fetchMarketLeverageTiers': True,
91
+ 'fetchMarkets': True,
92
+ 'fetchMarkOHLCV': False,
93
+ 'fetchMyTrades': True,
94
+ 'fetchOHLCV': True,
95
+ 'fetchOpenInterest': False,
96
+ 'fetchOpenInterestHistory': False,
97
+ 'fetchOpenOrders': True,
98
+ 'fetchOrder': True,
99
+ 'fetchOrderBook': True,
100
+ 'fetchOrderBooks': False,
101
+ 'fetchOrders': True,
102
+ 'fetchOrdersByStatus': True,
103
+ 'fetchOrderTrades': False,
104
+ 'fetchPosition': True,
105
+ 'fetchPositions': True,
106
+ 'fetchPremiumIndexOHLCV': False,
107
+ 'fetchSettlementHistory': False,
108
+ 'fetchStatus': False,
109
+ 'fetchTicker': True,
110
+ 'fetchTickers': True,
111
+ 'fetchTime': True,
112
+ 'fetchTrades': True,
113
+ 'fetchTradingFee': False,
114
+ 'fetchTradingFees': False,
115
+ 'fetchTradingLimits': False,
116
+ 'fetchTransactionFee': False,
117
+ 'fetchTransactionFees': False,
118
+ 'fetchTransactions': False,
119
+ 'fetchTransfer': False,
120
+ 'fetchTransfers': False,
121
+ 'fetchWithdrawal': False,
122
+ 'fetchWithdrawals': True,
123
+ 'fetchWithdrawalWhitelist': False,
124
+ 'reduceMargin': True,
125
+ 'repayMargin': False,
126
+ 'setLeverage': True,
127
+ 'setMargin': False,
128
+ 'setMarginMode': False,
129
+ 'setPositionMode': False,
130
+ 'signIn': False,
131
+ 'transfer': True,
132
+ 'withdraw': True,
133
+ },
134
+ 'precisionMode': TICK_SIZE,
135
+ 'urls': {
136
+ 'logo': 'https://user-images.githubusercontent.com/14319357/232636712-466df2fc-560a-4ca4-aab2-b1d954a58e24.jpg',
137
+ 'api': {
138
+ 'spot': 'https://sapi.xt.com',
139
+ 'linear': 'https://fapi.xt.com',
140
+ 'inverse': 'https://dapi.xt.com',
141
+ 'user': 'https://api.xt.com',
142
+ },
143
+ 'www': 'https://xt.com',
144
+ 'referral': 'https://www.xt.com/en/accounts/register?ref=9PTM9VW',
145
+ 'doc': [
146
+ 'https://doc.xt.com/',
147
+ 'https://github.com/xtpub/api-doc',
148
+ ],
149
+ 'fees': 'https://www.xt.com/en/rate',
150
+ },
151
+ 'api': {
152
+ 'public': {
153
+ 'spot': {
154
+ 'get': {
155
+ 'currencies': 1,
156
+ 'depth': 0.05,
157
+ 'kline': 0.1,
158
+ 'symbol': 1, # 0.1 for a single symbol
159
+ 'ticker': 1, # 0.1 for a single symbol
160
+ 'ticker/book': 1, # 0.1 for a single symbol
161
+ 'ticker/price': 1, # 0.1 for a single symbol
162
+ 'ticker/24h': 1, # 0.1 for a single symbol
163
+ 'time': 1,
164
+ 'trade/history': 0.1,
165
+ 'trade/recent': 0.1,
166
+ 'wallet/support/currency': 1,
167
+ },
168
+ },
169
+ 'linear': {
170
+ 'get': {
171
+ 'future/market/v1/public/contract/risk-balance': 1,
172
+ 'future/market/v1/public/contract/open-interest': 1,
173
+ 'future/market/v1/public/leverage/bracket/detail': 1,
174
+ 'future/market/v1/public/leverage/bracket/list': 1,
175
+ 'future/market/v1/public/q/agg-ticker': 1,
176
+ 'future/market/v1/public/q/agg-tickers': 1,
177
+ 'future/market/v1/public/q/deal': 1,
178
+ 'future/market/v1/public/q/depth': 1,
179
+ 'future/market/v1/public/q/funding-rate': 1,
180
+ 'future/market/v1/public/q/funding-rate-record': 1,
181
+ 'future/market/v1/public/q/index-price': 1,
182
+ 'future/market/v1/public/q/kline': 1,
183
+ 'future/market/v1/public/q/mark-price': 1,
184
+ 'future/market/v1/public/q/symbol-index-price': 1,
185
+ 'future/market/v1/public/q/symbol-mark-price': 1,
186
+ 'future/market/v1/public/q/ticker': 1,
187
+ 'future/market/v1/public/q/tickers': 1,
188
+ 'future/market/v1/public/symbol/coins': 3.33,
189
+ 'future/market/v1/public/symbol/detail': 3.33,
190
+ 'future/market/v1/public/symbol/list': 1,
191
+ },
192
+ },
193
+ 'inverse': {
194
+ 'get': {
195
+ 'future/market/v1/public/contract/risk-balance': 1,
196
+ 'future/market/v1/public/contract/open-interest': 1,
197
+ 'future/market/v1/public/leverage/bracket/detail': 1,
198
+ 'future/market/v1/public/leverage/bracket/list': 1,
199
+ 'future/market/v1/public/q/agg-ticker': 1,
200
+ 'future/market/v1/public/q/agg-tickers': 1,
201
+ 'future/market/v1/public/q/deal': 1,
202
+ 'future/market/v1/public/q/depth': 1,
203
+ 'future/market/v1/public/q/funding-rate': 1,
204
+ 'future/market/v1/public/q/funding-rate-record': 1,
205
+ 'future/market/v1/public/q/index-price': 1,
206
+ 'future/market/v1/public/q/kline': 1,
207
+ 'future/market/v1/public/q/mark-price': 1,
208
+ 'future/market/v1/public/q/symbol-index-price': 1,
209
+ 'future/market/v1/public/q/symbol-mark-price': 1,
210
+ 'future/market/v1/public/q/ticker': 1,
211
+ 'future/market/v1/public/q/tickers': 1,
212
+ 'future/market/v1/public/symbol/coins': 3.33,
213
+ 'future/market/v1/public/symbol/detail': 3.33,
214
+ 'future/market/v1/public/symbol/list': 1,
215
+ },
216
+ },
217
+ },
218
+ 'private': {
219
+ 'spot': {
220
+ 'get': {
221
+ 'balance': 1,
222
+ 'balances': 1,
223
+ 'batch-order': 1,
224
+ 'deposit/address': 1,
225
+ 'deposit/history': 1,
226
+ 'history-order': 1,
227
+ 'open-order': 1,
228
+ 'order': 1,
229
+ 'order/{orderId}': 1,
230
+ 'trade': 1,
231
+ 'withdraw/history': 1,
232
+ },
233
+ 'post': {
234
+ 'order': 0.2,
235
+ 'withdraw': 1,
236
+ 'balance/transfer': 1,
237
+ 'balance/account/transfer': 1,
238
+ },
239
+ 'delete': {
240
+ 'batch-order': 1,
241
+ 'open-order': 1,
242
+ 'order/{orderId}': 1,
243
+ },
244
+ },
245
+ 'linear': {
246
+ 'get': {
247
+ 'future/trade/v1/entrust/plan-detail': 1,
248
+ 'future/trade/v1/entrust/plan-list': 1,
249
+ 'future/trade/v1/entrust/plan-list-history': 1,
250
+ 'future/trade/v1/entrust/profit-detail': 1,
251
+ 'future/trade/v1/entrust/profit-list': 1,
252
+ 'future/trade/v1/order/detail': 1,
253
+ 'future/trade/v1/order/list': 1,
254
+ 'future/trade/v1/order/list-history': 1,
255
+ 'future/trade/v1/order/trade-list': 1,
256
+ 'future/user/v1/account/info': 1,
257
+ 'future/user/v1/balance/bills': 1,
258
+ 'future/user/v1/balance/detail': 1,
259
+ 'future/user/v1/balance/funding-rate-list': 1,
260
+ 'future/user/v1/balance/list': 1,
261
+ 'future/user/v1/position/adl': 1,
262
+ 'future/user/v1/position/list': 1,
263
+ 'future/user/v1/user/collection/list': 1,
264
+ 'future/user/v1/user/listen-key': 1,
265
+ },
266
+ 'post': {
267
+ 'future/trade/v1/entrust/cancel-all-plan': 1,
268
+ 'future/trade/v1/entrust/cancel-all-profit-stop': 1,
269
+ 'future/trade/v1/entrust/cancel-plan': 1,
270
+ 'future/trade/v1/entrust/cancel-profit-stop': 1,
271
+ 'future/trade/v1/entrust/create-plan': 1,
272
+ 'future/trade/v1/entrust/create-profit': 1,
273
+ 'future/trade/v1/entrust/update-profit-stop': 1,
274
+ 'future/trade/v1/order/cancel': 1,
275
+ 'future/trade/v1/order/cancel-all': 1,
276
+ 'future/trade/v1/order/create': 1,
277
+ 'future/trade/v1/order/create-batch': 1,
278
+ 'future/user/v1/account/open': 1,
279
+ 'future/user/v1/position/adjust-leverage': 1,
280
+ 'future/user/v1/position/auto-margin': 1,
281
+ 'future/user/v1/position/close-all': 1,
282
+ 'future/user/v1/position/margin': 1,
283
+ 'future/user/v1/user/collection/add': 1,
284
+ 'future/user/v1/user/collection/cancel': 1,
285
+ },
286
+ },
287
+ 'inverse': {
288
+ 'get': {
289
+ 'future/trade/v1/entrust/plan-detail': 1,
290
+ 'future/trade/v1/entrust/plan-list': 1,
291
+ 'future/trade/v1/entrust/plan-list-history': 1,
292
+ 'future/trade/v1/entrust/profit-detail': 1,
293
+ 'future/trade/v1/entrust/profit-list': 1,
294
+ 'future/trade/v1/order/detail': 1,
295
+ 'future/trade/v1/order/list': 1,
296
+ 'future/trade/v1/order/list-history': 1,
297
+ 'future/trade/v1/order/trade-list': 1,
298
+ 'future/user/v1/account/info': 1,
299
+ 'future/user/v1/balance/bills': 1,
300
+ 'future/user/v1/balance/detail': 1,
301
+ 'future/user/v1/balance/funding-rate-list': 1,
302
+ 'future/user/v1/balance/list': 1,
303
+ 'future/user/v1/position/adl': 1,
304
+ 'future/user/v1/position/list': 1,
305
+ 'future/user/v1/user/collection/list': 1,
306
+ 'future/user/v1/user/listen-key': 1,
307
+ },
308
+ 'post': {
309
+ 'future/trade/v1/entrust/cancel-all-plan': 1,
310
+ 'future/trade/v1/entrust/cancel-all-profit-stop': 1,
311
+ 'future/trade/v1/entrust/cancel-plan': 1,
312
+ 'future/trade/v1/entrust/cancel-profit-stop': 1,
313
+ 'future/trade/v1/entrust/create-plan': 1,
314
+ 'future/trade/v1/entrust/create-profit': 1,
315
+ 'future/trade/v1/entrust/update-profit-stop': 1,
316
+ 'future/trade/v1/order/cancel': 1,
317
+ 'future/trade/v1/order/cancel-all': 1,
318
+ 'future/trade/v1/order/create': 1,
319
+ 'future/trade/v1/order/create-batch': 1,
320
+ 'future/user/v1/account/open': 1,
321
+ 'future/user/v1/position/adjust-leverage': 1,
322
+ 'future/user/v1/position/auto-margin': 1,
323
+ 'future/user/v1/position/close-all': 1,
324
+ 'future/user/v1/position/margin': 1,
325
+ 'future/user/v1/user/collection/add': 1,
326
+ 'future/user/v1/user/collection/cancel': 1,
327
+ },
328
+ },
329
+ 'user': {
330
+ 'get': {
331
+ 'user/account': 1,
332
+ 'user/account/api-key': 1,
333
+ },
334
+ 'post': {
335
+ 'user/account': 1,
336
+ 'user/account/api-key': 1,
337
+ },
338
+ 'put': {
339
+ 'user/account/api-key': 1,
340
+ },
341
+ 'delete': {
342
+ 'user/account/{apikeyId}': 1,
343
+ },
344
+ },
345
+ },
346
+ },
347
+ 'fees': {
348
+ 'spot': {
349
+ 'tierBased': True,
350
+ 'percentage': True,
351
+ 'maker': self.parse_number('0.002'),
352
+ 'taker': self.parse_number('0.002'),
353
+ 'tiers': {
354
+ 'maker': [
355
+ [self.parse_number('0'), self.parse_number('0.002')],
356
+ [self.parse_number('5000'), self.parse_number('0.0018')],
357
+ [self.parse_number('10000'), self.parse_number('0.0016')],
358
+ [self.parse_number('20000'), self.parse_number('0.0014')],
359
+ [self.parse_number('50000'), self.parse_number('0.0012')],
360
+ [self.parse_number('150000'), self.parse_number('0.0010')],
361
+ [self.parse_number('300000'), self.parse_number('0.0008')],
362
+ [self.parse_number('600000'), self.parse_number('0.0007')],
363
+ [self.parse_number('1200000'), self.parse_number('0.0006')],
364
+ [self.parse_number('2500000'), self.parse_number('0.0005')],
365
+ [self.parse_number('6000000'), self.parse_number('0.0004')],
366
+ [self.parse_number('15000000'), self.parse_number('0.0003')],
367
+ [self.parse_number('30000000'), self.parse_number('0.0002')],
368
+ ],
369
+ 'taker': [
370
+ [self.parse_number('0'), self.parse_number('0.002')],
371
+ [self.parse_number('5000'), self.parse_number('0.0018')],
372
+ [self.parse_number('10000'), self.parse_number('0.0016')],
373
+ [self.parse_number('20000'), self.parse_number('0.0014')],
374
+ [self.parse_number('50000'), self.parse_number('0.0012')],
375
+ [self.parse_number('150000'), self.parse_number('0.0010')],
376
+ [self.parse_number('300000'), self.parse_number('0.0008')],
377
+ [self.parse_number('600000'), self.parse_number('0.0007')],
378
+ [self.parse_number('1200000'), self.parse_number('0.0006')],
379
+ [self.parse_number('2500000'), self.parse_number('0.0005')],
380
+ [self.parse_number('6000000'), self.parse_number('0.0004')],
381
+ [self.parse_number('15000000'), self.parse_number('0.0003')],
382
+ [self.parse_number('30000000'), self.parse_number('0.0002')],
383
+ ],
384
+ },
385
+ },
386
+ 'contract': {
387
+ 'tierBased': True,
388
+ 'percentage': True,
389
+ 'maker': self.parse_number('0.0004'),
390
+ 'taker': self.parse_number('0.0006'),
391
+ 'tiers': {
392
+ 'maker': [
393
+ [self.parse_number('0'), self.parse_number('0.0004')],
394
+ [self.parse_number('200000'), self.parse_number('0.00038')],
395
+ [self.parse_number('1000000'), self.parse_number('0.00036')],
396
+ [self.parse_number('5000000'), self.parse_number('0.00034')],
397
+ [self.parse_number('10000000'), self.parse_number('0.00032')],
398
+ [self.parse_number('15000000'), self.parse_number('0.00028')],
399
+ [self.parse_number('30000000'), self.parse_number('0.00024')],
400
+ [self.parse_number('50000000'), self.parse_number('0.0002')],
401
+ [self.parse_number('100000000'), self.parse_number('0.00016')],
402
+ [self.parse_number('300000000'), self.parse_number('0.00012')],
403
+ [self.parse_number('500000000'), self.parse_number('0.00008')],
404
+ ],
405
+ 'taker': [
406
+ [self.parse_number('0'), self.parse_number('0.0006')],
407
+ [self.parse_number('200000'), self.parse_number('0.000588')],
408
+ [self.parse_number('1000000'), self.parse_number('0.00057')],
409
+ [self.parse_number('5000000'), self.parse_number('0.00054')],
410
+ [self.parse_number('10000000'), self.parse_number('0.00051')],
411
+ [self.parse_number('15000000'), self.parse_number('0.00048')],
412
+ [self.parse_number('30000000'), self.parse_number('0.00045')],
413
+ [self.parse_number('50000000'), self.parse_number('0.00045')],
414
+ [self.parse_number('100000000'), self.parse_number('0.00036')],
415
+ [self.parse_number('300000000'), self.parse_number('0.00033')],
416
+ [self.parse_number('500000000'), self.parse_number('0.0003')],
417
+ ],
418
+ },
419
+ },
420
+ },
421
+ 'exceptions': {
422
+ 'exact': {
423
+ '400': NetworkError, # {"returnCode":1,"msgInfo":"failure","error":{"code":"400","msg":"Connection refused: /10.0.26.71:8080"},"result":null}
424
+ '404': ExchangeError, # interface does not exist
425
+ '429': RateLimitExceeded, # The request is too frequent, please control the request rate according to the speed limit requirement
426
+ '500': ExchangeError, # Service exception
427
+ '502': ExchangeError, # Gateway exception
428
+ '503': OnMaintenance, # Service unavailable, please try again later
429
+ 'AUTH_001': AuthenticationError, # missing request header xt-validate-appkey
430
+ 'AUTH_002': AuthenticationError, # missing request header xt-validate-timestamp
431
+ 'AUTH_003': AuthenticationError, # missing request header xt-validate-recvwindow
432
+ 'AUTH_004': AuthenticationError, # bad request header xt-validate-recvwindow
433
+ 'AUTH_005': AuthenticationError, # missing request header xt-validate-algorithms
434
+ 'AUTH_006': AuthenticationError, # bad request header xt-validate-algorithms
435
+ 'AUTH_007': AuthenticationError, # missing request header xt-validate-signature
436
+ 'AUTH_101': AuthenticationError, # ApiKey does not exist
437
+ 'AUTH_102': AuthenticationError, # ApiKey is not activated
438
+ 'AUTH_103': AuthenticationError, # Signature error, {"rc":1,"mc":"AUTH_103","ma":[],"result":null}
439
+ 'AUTH_104': AuthenticationError, # Unbound IP request
440
+ 'AUTH_105': AuthenticationError, # outdated message
441
+ 'AUTH_106': PermissionDenied, # Exceeded apikey permission
442
+ 'SYMBOL_001': BadSymbol, # Symbol not exist
443
+ 'SYMBOL_002': BadSymbol, # Symbol offline
444
+ 'SYMBOL_003': BadSymbol, # Symbol suspend trading
445
+ 'SYMBOL_004': BadSymbol, # Symbol country disallow trading
446
+ 'SYMBOL_005': BadSymbol, # The symbol does not support trading via API
447
+ 'ORDER_001': InvalidOrder, # Platform rejection
448
+ 'ORDER_002': InsufficientFunds, # insufficient funds
449
+ 'ORDER_003': InvalidOrder, # Trading Pair Suspended
450
+ 'ORDER_004': InvalidOrder, # no transaction
451
+ 'ORDER_005': InvalidOrder, # Order not exist
452
+ 'ORDER_006': InvalidOrder, # Too many open orders
453
+ 'ORDER_007': PermissionDenied, # The sub-account has no transaction authority
454
+ 'ORDER_F0101': InvalidOrder, # Trigger Price Filter - Min
455
+ 'ORDER_F0102': InvalidOrder, # Trigger Price Filter - Max
456
+ 'ORDER_F0103': InvalidOrder, # Trigger Price Filter - Step Value
457
+ 'ORDER_F0201': InvalidOrder, # Trigger Quantity Filter - Min
458
+ 'ORDER_F0202': InvalidOrder, # Trigger Quantity Filter - Max
459
+ 'ORDER_F0203': InvalidOrder, # Trigger Quantity Filter - Step Value
460
+ 'ORDER_F0301': InvalidOrder, # Trigger QUOTE_QTY Filter - Min Value
461
+ 'ORDER_F0401': InvalidOrder, # Trigger PROTECTION_ONLINE Filter
462
+ 'ORDER_F0501': InvalidOrder, # Trigger PROTECTION_LIMIT Filter - Buy Max Deviation
463
+ 'ORDER_F0502': InvalidOrder, # Trigger PROTECTION_LIMIT Filter - Sell Max Deviation
464
+ 'ORDER_F0601': InvalidOrder, # Trigger PROTECTION_MARKET Filter
465
+ 'COMMON_001': ExchangeError, # The user does not exist
466
+ 'COMMON_002': ExchangeError, # System busy, please try it later
467
+ 'COMMON_003': BadRequest, # Operation failed, please try it later
468
+ 'CURRENCY_001': BadRequest, # Information of currency is abnormal
469
+ 'DEPOSIT_001': BadRequest, # Deposit is not open
470
+ 'DEPOSIT_002': PermissionDenied, # The current account security level is low, please bind any two security verifications in mobile phone/email/Google Authenticator before deposit
471
+ 'DEPOSIT_003': BadRequest, # The format of address is incorrect, please enter again
472
+ 'DEPOSIT_004': BadRequest, # The address is already exists, please enter again
473
+ 'DEPOSIT_005': BadRequest, # Can not find the address of offline wallet
474
+ 'DEPOSIT_006': BadRequest, # No deposit address, please try it later
475
+ 'DEPOSIT_007': BadRequest, # Address is being generated, please try it later
476
+ 'DEPOSIT_008': BadRequest, # Deposit is not available
477
+ 'WITHDRAW_001': BadRequest, # Withdraw is not open
478
+ 'WITHDRAW_002': BadRequest, # The withdrawal address is invalid
479
+ 'WITHDRAW_003': PermissionDenied, # The current account security level is low, please bind any two security verifications in mobile phone/email/Google Authenticator before withdraw
480
+ 'WITHDRAW_004': BadRequest, # The withdrawal address is not added
481
+ 'WITHDRAW_005': BadRequest, # The withdrawal address cannot be empty
482
+ 'WITHDRAW_006': BadRequest, # Memo cannot be empty
483
+ 'WITHDRAW_008': PermissionDenied, # Risk control is triggered, withdraw of self currency is not currently supported
484
+ 'WITHDRAW_009': PermissionDenied, # Withdraw failed, some hasattr(self, assets) withdraw are restricted by T+1 withdraw
485
+ 'WITHDRAW_010': BadRequest, # The precision of withdrawal is invalid
486
+ 'WITHDRAW_011': InsufficientFunds, # free balance is not enough
487
+ 'WITHDRAW_012': PermissionDenied, # Withdraw failed, your remaining withdrawal limit today is not enough
488
+ '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
489
+ 'WITHDRAW_014': BadRequest, # This withdrawal address cannot be used in the internal transfer function, please cancel the internal transfer function before submitting
490
+ 'WITHDRAW_015': BadRequest, # The withdrawal amount is not enough to deduct the handling fee
491
+ 'WITHDRAW_016': BadRequest, # This withdrawal address is already exists
492
+ 'WITHDRAW_017': BadRequest, # This withdrawal has been processed and cannot be canceled
493
+ 'WITHDRAW_018': BadRequest, # Memo must be a number
494
+ 'WITHDRAW_019': BadRequest, # Memo is incorrect, please enter again
495
+ 'WITHDRAW_020': PermissionDenied, # Your withdrawal amount has reached the upper limit for today, please try it tomorrow
496
+ 'WITHDRAW_021': PermissionDenied, # Your withdrawal amount has reached the upper limit for today, you can only withdraw up to {0} self time
497
+ 'WITHDRAW_022': BadRequest, # Withdrawal amount must be greater than {0}
498
+ 'WITHDRAW_023': BadRequest, # Withdrawal amount must be less than {0}
499
+ 'WITHDRAW_024': BadRequest, # Withdraw is not supported
500
+ 'WITHDRAW_025': BadRequest, # Please create a FIO address in the deposit page
501
+ 'FUND_001': BadRequest, # Duplicate request(a bizId can only be requested once)
502
+ 'FUND_002': InsufficientFunds, # Insufficient account balance
503
+ 'FUND_003': BadRequest, # Transfer operations are not supported(for example, sub-accounts do not support financial transfers)
504
+ 'FUND_004': ExchangeError, # Unfreeze failed
505
+ 'FUND_005': PermissionDenied, # Transfer prohibited
506
+ 'FUND_014': BadRequest, # The transfer-in account id and transfer-out account ID cannot be the same
507
+ 'FUND_015': BadRequest, # From and to business types cannot be the same
508
+ 'FUND_016': BadRequest, # Leverage transfer, symbol cannot be empty
509
+ 'FUND_017': BadRequest, # Parameter error
510
+ 'FUND_018': BadRequest, # Invalid freeze record
511
+ 'FUND_019': BadRequest, # Freeze users not equal
512
+ 'FUND_020': BadRequest, # Freeze currency are not equal
513
+ 'FUND_021': BadRequest, # Operation not supported
514
+ 'FUND_022': BadRequest, # Freeze record does not exist
515
+ 'FUND_044': BadRequest, # The maximum length of the amount is 113 and cannot exceed the limit
516
+ 'TRANSFER_001': BadRequest, # Duplicate request(a bizId can only be requested once)
517
+ 'TRANSFER_002': InsufficientFunds, # Insufficient account balance
518
+ 'TRANSFER_003': BadRequest, # User not registered
519
+ 'TRANSFER_004': PermissionDenied, # The currency is not allowed to be transferred
520
+ 'TRANSFER_005': PermissionDenied, # The user’s currency is not allowed to be transferred
521
+ 'TRANSFER_006': PermissionDenied, # Transfer prohibited
522
+ 'TRANSFER_007': RequestTimeout, # Request timed out
523
+ 'TRANSFER_008': BadRequest, # Transferring to a leveraged account is abnormal
524
+ 'TRANSFER_009': BadRequest, # Departing from a leveraged account is abnormal
525
+ 'TRANSFER_010': PermissionDenied, # Leverage cleared, transfer prohibited
526
+ 'TRANSFER_011': PermissionDenied, # Leverage with borrowing, transfer prohibited
527
+ 'TRANSFER_012': PermissionDenied, # Currency transfer prohibited
528
+ '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}
529
+ '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}
530
+ },
531
+ 'broad': {
532
+ '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}
533
+ '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}
534
+ },
535
+ },
536
+ 'timeframes': {
537
+ '1m': '1m',
538
+ '5m': '5m',
539
+ '15m': '15m',
540
+ '30m': '30m',
541
+ '1h': '1h', # spot only
542
+ '2h': '2h', # spot only
543
+ '4h': '4h',
544
+ '6h': '6h', # spot only
545
+ '8h': '8h', # spot only
546
+ '1d': '1d',
547
+ '3d': '3d', # spot only
548
+ '1w': '1w',
549
+ '1M': '1M', # spot only
550
+ },
551
+ 'commonCurrencies': {},
552
+ 'options': {
553
+ 'adjustForTimeDifference': False,
554
+ 'timeDifference': 0,
555
+ 'accountsById': {
556
+ 'spot': 'SPOT',
557
+ 'leverage': 'LEVER',
558
+ 'finance': 'FINANCE',
559
+ 'swap': 'FUTURES_U',
560
+ 'future': 'FUTURES_U',
561
+ 'linear': 'FUTURES_U',
562
+ 'inverse': 'FUTURES_C',
563
+ },
564
+ 'networks': {
565
+ 'ERC20': 'Ethereum',
566
+ 'TRC20': 'Tron',
567
+ 'BEP20': 'BNB Smart Chain',
568
+ 'BEP2': 'BNB-BEP2',
569
+ 'ETH': 'Ethereum',
570
+ 'TRON': 'Tron',
571
+ 'BNB': 'BNB Smart Chain',
572
+ 'AVAX': 'AVAX C-Chain',
573
+ 'GAL': 'GAL(FT)',
574
+ 'ALEO': 'ALEO(IOU)',
575
+ 'BTC': 'Bitcoin',
576
+ 'XT': 'XT Smart Chain',
577
+ 'ETC': 'Ethereum Classic',
578
+ 'MATIC': 'Polygon',
579
+ 'LTC': 'Litecoin',
580
+ 'BTS': 'BitShares',
581
+ 'XRP': 'Ripple',
582
+ 'XLM': 'Stellar Network',
583
+ 'ADA': 'Cardano',
584
+ 'XWC': 'XWC-XWC',
585
+ 'DOGE': 'dogecoin',
586
+ 'DCR': 'Decred',
587
+ 'SC': 'Siacoin',
588
+ 'XTZ': 'Tezos',
589
+ 'ZEC': 'Zcash',
590
+ 'XMR': 'Monero',
591
+ 'LSK': 'Lisk',
592
+ 'ATOM': 'Cosmos',
593
+ 'ONT': 'Ontology',
594
+ 'ALGO': 'Algorand',
595
+ 'SOL': 'SOL-SOL',
596
+ 'DOT': 'Polkadot',
597
+ 'ZEN': 'Horizen',
598
+ 'FIL': 'Filecoin',
599
+ 'CHZ': 'chz',
600
+ 'ICP': 'Internet Computer',
601
+ 'KSM': 'Kusama',
602
+ 'LUNA': 'Terra',
603
+ 'THETA': 'Theta Token',
604
+ 'FTM': 'Fantom',
605
+ 'VET': 'VeChain',
606
+ 'NEAR': 'NEAR Protocol',
607
+ 'ONE': 'Harmony',
608
+ 'KLAY': 'Klaytn',
609
+ 'AR': 'Arweave',
610
+ 'CELT': 'OKT',
611
+ 'EGLD': 'Elrond eGold',
612
+ 'CRO': 'CRO-CRONOS',
613
+ 'BCH': 'Bitcoin Cash',
614
+ 'GLMR': 'Moonbeam',
615
+ 'LOOP': 'LOOP-LRC',
616
+ 'REI': 'REI Network',
617
+ 'ASTR': 'Astar Network',
618
+ 'OP': 'OPT',
619
+ 'MMT': 'MMT-MMT',
620
+ 'TBC': 'TBC-TBC',
621
+ 'OMAX': 'OMAX-OMAX CHAIN',
622
+ 'GMMT': 'GMMT chain',
623
+ 'ZIL': 'Zilliqa',
624
+ },
625
+ 'networksById': {
626
+ 'Ethereum': 'ERC20',
627
+ 'Tron': 'TRC20',
628
+ 'BNB Smart Chain': 'BEP20',
629
+ 'BNB-BEP2': 'BEP2',
630
+ 'Bitcoin': 'BTC',
631
+ 'XT Smart Chain': 'XT',
632
+ 'Ethereum Classic': 'ETC',
633
+ 'Polygon': 'MATIC',
634
+ 'Litecoin': 'LTC',
635
+ 'BitShares': 'BTS',
636
+ 'Ripple': 'XRP',
637
+ 'Stellar Network': 'XLM',
638
+ 'Cardano': 'ADA',
639
+ 'XWC-XWC': 'XWC',
640
+ 'dogecoin': 'DOGE',
641
+ 'Decred': 'DCR',
642
+ 'Siacoin': 'SC',
643
+ 'Tezos': 'XTZ',
644
+ 'Zcash': 'ZEC',
645
+ 'Monero': 'XMR',
646
+ 'Lisk': 'LSK',
647
+ 'Cosmos': 'ATOM',
648
+ 'Ontology': 'ONT',
649
+ 'Algorand': 'ALGO',
650
+ 'SOL-SOL': 'SOL',
651
+ 'Polkadot': 'DOT',
652
+ 'Horizen': 'ZEN',
653
+ 'Filecoin': 'FIL',
654
+ 'chz': 'CHZ',
655
+ 'Internet Computer': 'ICP',
656
+ 'Kusama': 'KSM',
657
+ 'Terra': 'LUNA',
658
+ 'Theta Token': 'THETA',
659
+ 'Fantom': 'FTM',
660
+ 'VeChain': 'VET',
661
+ 'AVAX C-Chain': 'AVAX',
662
+ 'NEAR Protocol': 'NEAR',
663
+ 'Harmony': 'ONE',
664
+ 'Klaytn': 'KLAY',
665
+ 'Arweave': 'AR',
666
+ 'OKT': 'CELT',
667
+ 'Elrond eGold': 'EGLD',
668
+ 'CRO-CRONOS': 'CRO',
669
+ 'Bitcoin Cash': 'BCH',
670
+ 'Moonbeam': 'GLMR',
671
+ 'LOOP-LRC': 'LOOP',
672
+ 'REI Network': 'REI',
673
+ 'Astar Network': 'ASTR',
674
+ 'GAL(FT)': 'GAL',
675
+ 'ALEO(IOU)': 'ALEO',
676
+ 'OPT': 'OP',
677
+ 'MMT-MMT': 'MMT',
678
+ 'TBC-TBC': 'TBC',
679
+ 'OMAX-OMAX CHAIN': 'OMAX',
680
+ 'GMMT chain': 'GMMT',
681
+ 'Zilliqa': 'ZIL',
682
+ },
683
+ 'createMarketBuyOrderRequiresPrice': True,
684
+ 'recvWindow': '5000', # in milliseconds, spot only
685
+ },
686
+ })
687
+
688
+ def nonce(self):
689
+ return self.milliseconds() - self.options['timeDifference']
690
+
691
+ async def fetch_time(self, params={}):
692
+ """
693
+ fetches the current integer timestamp in milliseconds from the xt server
694
+ :see: https://doc.xt.com/#market1serverInfo
695
+ :param dict params: extra parameters specific to the xt api endpoint
696
+ :returns int: the current integer timestamp in milliseconds from the xt server
697
+ """
698
+ response = await self.publicSpotGetTime(params)
699
+ #
700
+ # {
701
+ # "rc": 0,
702
+ # "mc": "SUCCESS",
703
+ # "ma": [],
704
+ # "result": {
705
+ # "serverTime": 1677823301643
706
+ # }
707
+ # }
708
+ #
709
+ data = self.safe_value(response, 'result')
710
+ return self.safe_integer(data, 'serverTime')
711
+
712
+ async def fetch_currencies(self, params={}) -> Currencies:
713
+ """
714
+ fetches all available currencies on an exchange
715
+ :see: https://doc.xt.com/#deposit_withdrawalsupportedCurrenciesGet
716
+ :param dict params: extra parameters specific to the xt api endpoint
717
+ :returns dict: an associative dictionary of currencies
718
+ """
719
+ promisesRaw = [self.publicSpotGetWalletSupportCurrency(params), self.publicSpotGetCurrencies(params)]
720
+ chainsResponse, currenciesResponse = await asyncio.gather(*promisesRaw)
721
+ #
722
+ # currencies
723
+ #
724
+ # {
725
+ # "time": "1686626116145",
726
+ # "version": "5dbbb2f2527c22b2b2e3b47187ef13d1",
727
+ # "currencies": [
728
+ # {
729
+ # "id": "2",
730
+ # "currency": "btc",
731
+ # "fullName": "Bitcoin",
732
+ # "logo": "https://a.static-global.com/1/currency/btc.png",
733
+ # "cmcLink": "https://coinmarketcap.com/currencies/bitcoin/",
734
+ # "weight": "99999",
735
+ # "maxPrecision": "10",
736
+ # "depositStatus": "1",
737
+ # "withdrawStatus": "1",
738
+ # "convertEnabled": "1",
739
+ # "transferEnabled": "1",
740
+ # "isChainExist": "1",
741
+ # "plates": [152]
742
+ # },
743
+ # ],
744
+ # }
745
+ #
746
+ #
747
+ # chains
748
+ #
749
+ # {
750
+ # "rc": 0,
751
+ # "mc": "SUCCESS",
752
+ # "ma": [],
753
+ # "result": [
754
+ # {
755
+ # "currency": "btc",
756
+ # "supportChains": [
757
+ # {
758
+ # "chain": "Bitcoin",
759
+ # "depositEnabled": True,
760
+ # "withdrawEnabled": True,
761
+ # "withdrawFeeAmount": 0.0009,
762
+ # "withdrawMinAmount": 0.0005,
763
+ # "depositFeeRate": 0
764
+ # },
765
+ # ]
766
+ # },
767
+ # ]
768
+ # }
769
+ #
770
+ # note: individual network's full data is available on per-currency endpoint: https://www.xt.com/sapi/v4/balance/public/currency/11
771
+ #
772
+ chainsData = self.safe_value(chainsResponse, 'result', [])
773
+ currenciesResult = self.safe_value(currenciesResponse, 'result', [])
774
+ currenciesData = self.safe_value(currenciesResult, 'currencies', [])
775
+ chainsDataIndexed = self.index_by(chainsData, 'currency')
776
+ result = {}
777
+ for i in range(0, len(currenciesData)):
778
+ entry = currenciesData[i]
779
+ currencyId = self.safe_string(entry, 'currency')
780
+ code = self.safe_currency_code(currencyId)
781
+ minPrecision = self.parse_number(self.parse_precision(self.safe_string(entry, 'maxPrecision')))
782
+ networkEntry = self.safe_value(chainsDataIndexed, currencyId, {})
783
+ rawNetworks = self.safe_value(networkEntry, 'supportChains', [])
784
+ networks = {}
785
+ minWithdrawString = None
786
+ minWithdrawFeeString = None
787
+ active = False
788
+ deposit = False
789
+ withdraw = False
790
+ for j in range(0, len(rawNetworks)):
791
+ rawNetwork = rawNetworks[j]
792
+ networkId = self.safe_string(rawNetwork, 'chain')
793
+ network = self.network_id_to_code(networkId)
794
+ depositEnabled = self.safe_value(rawNetwork, 'depositEnabled')
795
+ deposit = depositEnabled if (depositEnabled) else deposit
796
+ withdrawEnabled = self.safe_value(rawNetwork, 'withdrawEnabled')
797
+ withdraw = withdrawEnabled if (withdrawEnabled) else withdraw
798
+ networkActive = depositEnabled and withdrawEnabled
799
+ active = networkActive if (networkActive) else active
800
+ withdrawFeeString = self.safe_string(rawNetwork, 'withdrawFeeAmount')
801
+ if withdrawFeeString is not None:
802
+ minWithdrawFeeString = withdrawFeeString if (minWithdrawFeeString is None) else Precise.string_min(withdrawFeeString, minWithdrawFeeString)
803
+ minNetworkWithdrawString = self.safe_string(rawNetwork, 'withdrawMinAmount')
804
+ if minNetworkWithdrawString is not None:
805
+ minWithdrawString = minNetworkWithdrawString if (minWithdrawString is None) else Precise.string_min(minNetworkWithdrawString, minWithdrawString)
806
+ networks[network] = {
807
+ 'info': rawNetwork,
808
+ 'id': networkId,
809
+ 'network': network,
810
+ 'name': None,
811
+ 'active': networkActive,
812
+ 'fee': self.parse_number(withdrawFeeString),
813
+ 'precision': minPrecision,
814
+ 'deposit': depositEnabled,
815
+ 'withdraw': withdrawEnabled,
816
+ 'limits': {
817
+ 'amount': {
818
+ 'min': None,
819
+ 'max': None,
820
+ },
821
+ 'withdraw': {
822
+ 'min': self.parse_number(minNetworkWithdrawString),
823
+ 'max': None,
824
+ },
825
+ 'deposit': {
826
+ 'min': None,
827
+ 'max': None,
828
+ },
829
+ },
830
+ }
831
+ result[code] = {
832
+ 'info': entry,
833
+ 'id': currencyId,
834
+ 'code': code,
835
+ 'name': self.safe_string(entry, 'fullName'),
836
+ 'active': active,
837
+ 'fee': self.parse_number(minWithdrawFeeString),
838
+ 'precision': None,
839
+ 'deposit': deposit,
840
+ 'withdraw': withdraw,
841
+ 'networks': networks,
842
+ 'limits': {
843
+ 'amount': {
844
+ 'min': None,
845
+ 'max': None,
846
+ },
847
+ 'withdraw': {
848
+ 'min': self.parse_number(minWithdrawString),
849
+ 'max': None,
850
+ },
851
+ 'deposit': {
852
+ 'min': None,
853
+ 'max': None,
854
+ },
855
+ },
856
+ }
857
+ return result
858
+
859
+ async def fetch_markets(self, params={}) -> List[Market]:
860
+ """
861
+ retrieves data on all markets for xt
862
+ :see: https://doc.xt.com/#market2symbol
863
+ :see: https://doc.xt.com/#futures_quotesgetSymbols
864
+ :param dict params: extra parameters specific to the xt api endpoint
865
+ :returns dict[]: an array of objects representing market data
866
+ """
867
+ if self.options['adjustForTimeDifference']:
868
+ await self.load_time_difference()
869
+ promisesUnresolved = [
870
+ self.fetch_spot_markets(params),
871
+ self.fetch_swap_and_future_markets(params),
872
+ ]
873
+ promises = await asyncio.gather(*promisesUnresolved)
874
+ spotMarkets = promises[0]
875
+ swapAndFutureMarkets = promises[1]
876
+ return self.array_concat(spotMarkets, swapAndFutureMarkets)
877
+
878
+ async def fetch_spot_markets(self, params={}):
879
+ response = await self.publicSpotGetSymbol(params)
880
+ #
881
+ # {
882
+ # "rc": 0,
883
+ # "mc": "SUCCESS",
884
+ # "ma": [],
885
+ # "result": {
886
+ # "time": 1677881368812,
887
+ # "version": "abb101d1543e54bee40687b135411ba0",
888
+ # "symbols": [
889
+ # {
890
+ # "id": 640,
891
+ # "symbol": "xt_usdt",
892
+ # "state": "ONLINE",
893
+ # "stateTime": 1554048000000,
894
+ # "tradingEnabled": True,
895
+ # "openapiEnabled": True,
896
+ # "nextStateTime": null,
897
+ # "nextState": null,
898
+ # "depthMergePrecision": 5,
899
+ # "baseCurrency": "xt",
900
+ # "baseCurrencyPrecision": 8,
901
+ # "baseCurrencyId": 128,
902
+ # "quoteCurrency": "usdt",
903
+ # "quoteCurrencyPrecision": 8,
904
+ # "quoteCurrencyId": 11,
905
+ # "pricePrecision": 4,
906
+ # "quantityPrecision": 2,
907
+ # "orderTypes": ["LIMIT","MARKET"],
908
+ # "timeInForces": ["GTC","IOC"],
909
+ # "displayWeight": 10002,
910
+ # "displayLevel": "FULL",
911
+ # "plates": [],
912
+ # "filters":[
913
+ # {
914
+ # "filter": "QUOTE_QTY",
915
+ # "min": "1"
916
+ # },
917
+ # {
918
+ # "filter": "PROTECTION_LIMIT",
919
+ # "buyMaxDeviation": "0.8",
920
+ # "sellMaxDeviation": "4"
921
+ # },
922
+ # {
923
+ # "filter": "PROTECTION_MARKET",
924
+ # "maxDeviation": "0.02"
925
+ # }
926
+ # ]
927
+ # },
928
+ # ]
929
+ # }
930
+ # }
931
+ #
932
+ data = self.safe_value(response, 'result', {})
933
+ symbols = self.safe_value(data, 'symbols', [])
934
+ return self.parse_markets(symbols)
935
+
936
+ async def fetch_swap_and_future_markets(self, params={}):
937
+ markets = await asyncio.gather(*[self.publicLinearGetFutureMarketV1PublicSymbolList(params), self.publicInverseGetFutureMarketV1PublicSymbolList(params)])
938
+ #
939
+ # {
940
+ # "returnCode": 0,
941
+ # "msgInfo": "success",
942
+ # "error": null,
943
+ # "result": [
944
+ # {
945
+ # "id": 52,
946
+ # "symbolGroupId": 71,
947
+ # "symbol": "xt_usdt",
948
+ # "pair": "xt_usdt",
949
+ # "contractType": "PERPETUAL",
950
+ # "productType": "perpetual",
951
+ # "predictEventType": null,
952
+ # "underlyingType": "U_BASED",
953
+ # "contractSize": "1",
954
+ # "tradeSwitch": True,
955
+ # "isDisplay": True,
956
+ # "isOpenApi": False,
957
+ # "state": 0,
958
+ # "initLeverage": 20,
959
+ # "initPositionType": "CROSSED",
960
+ # "baseCoin": "xt",
961
+ # "quoteCoin": "usdt",
962
+ # "baseCoinPrecision": 8,
963
+ # "baseCoinDisplayPrecision": 4,
964
+ # "quoteCoinPrecision": 8,
965
+ # "quoteCoinDisplayPrecision": 4,
966
+ # "quantityPrecision": 0,
967
+ # "pricePrecision": 4,
968
+ # "supportOrderType": "LIMIT,MARKET",
969
+ # "supportTimeInForce": "GTC,FOK,IOC,GTX",
970
+ # "supportEntrustType": "TAKE_PROFIT,STOP,TAKE_PROFIT_MARKET,STOP_MARKET,TRAILING_STOP_MARKET",
971
+ # "supportPositionType": "CROSSED,ISOLATED",
972
+ # "minQty": "1",
973
+ # "minNotional": "5",
974
+ # "maxNotional": "20000000",
975
+ # "multiplierDown": "0.1",
976
+ # "multiplierUp": "0.1",
977
+ # "maxOpenOrders": 200,
978
+ # "maxEntrusts": 200,
979
+ # "makerFee": "0.0004",
980
+ # "takerFee": "0.0006",
981
+ # "liquidationFee": "0.01",
982
+ # "marketTakeBound": "0.1",
983
+ # "depthPrecisionMerge": 5,
984
+ # "labels": ["HOT"],
985
+ # "onboardDate": 1657101601000,
986
+ # "enName": "XTUSDT ",
987
+ # "cnName": "XTUSDT",
988
+ # "minStepPrice": "0.0001",
989
+ # "minPrice": null,
990
+ # "maxPrice": null,
991
+ # "deliveryDate": 1669879634000,
992
+ # "deliveryPrice": null,
993
+ # "deliveryCompletion": False,
994
+ # "cnDesc": null,
995
+ # "enDesc": null
996
+ # },
997
+ # ]
998
+ # }
999
+ #
1000
+ swapAndFutureMarkets = self.array_concat(self.safe_value(markets[0], 'result', []), self.safe_value(markets[1], 'result', []))
1001
+ return self.parse_markets(swapAndFutureMarkets)
1002
+
1003
+ def parse_markets(self, markets):
1004
+ result = []
1005
+ for i in range(0, len(markets)):
1006
+ result.append(self.parse_market(markets[i]))
1007
+ return result
1008
+
1009
+ def parse_market(self, market: dict) -> Market:
1010
+ #
1011
+ # spot
1012
+ #
1013
+ # {
1014
+ # "id": 640,
1015
+ # "symbol": "xt_usdt",
1016
+ # "state": "ONLINE",
1017
+ # "stateTime": 1554048000000,
1018
+ # "tradingEnabled": True,
1019
+ # "openapiEnabled": True,
1020
+ # "nextStateTime": null,
1021
+ # "nextState": null,
1022
+ # "depthMergePrecision": 5,
1023
+ # "baseCurrency": "xt",
1024
+ # "baseCurrencyPrecision": 8,
1025
+ # "baseCurrencyId": 128,
1026
+ # "quoteCurrency": "usdt",
1027
+ # "quoteCurrencyPrecision": 8,
1028
+ # "quoteCurrencyId": 11,
1029
+ # "pricePrecision": 4,
1030
+ # "quantityPrecision": 2,
1031
+ # "orderTypes": ["LIMIT","MARKET"],
1032
+ # "timeInForces": ["GTC","IOC"],
1033
+ # "displayWeight": 10002,
1034
+ # "displayLevel": "FULL",
1035
+ # "plates": [],
1036
+ # "filters":[
1037
+ # {
1038
+ # "filter": "QUOTE_QTY",
1039
+ # "min": "1"
1040
+ # },
1041
+ # {
1042
+ # "filter": "PRICE",
1043
+ # "min": null,
1044
+ # "max": null,
1045
+ # "tickSize": null
1046
+ # },
1047
+ # {
1048
+ # "filter": "QUANTITY",
1049
+ # "min": null,
1050
+ # "max": null,
1051
+ # "tickSize": null
1052
+ # },
1053
+ # {
1054
+ # "filter": "PROTECTION_LIMIT",
1055
+ # "buyMaxDeviation": "0.8",
1056
+ # "sellMaxDeviation": "4"
1057
+ # },
1058
+ # {
1059
+ # "filter": "PROTECTION_MARKET",
1060
+ # "maxDeviation": "0.02"
1061
+ # },
1062
+ # {
1063
+ # "filter": "PROTECTION_ONLINE",
1064
+ # "durationSeconds": "300",
1065
+ # "maxPriceMultiple": "5"
1066
+ # },
1067
+ # ]
1068
+ # }
1069
+ #
1070
+ # swap and future
1071
+ #
1072
+ # {
1073
+ # "id": 52,
1074
+ # "symbolGroupId": 71,
1075
+ # "symbol": "xt_usdt",
1076
+ # "pair": "xt_usdt",
1077
+ # "contractType": "PERPETUAL",
1078
+ # "productType": "perpetual",
1079
+ # "predictEventType": null,
1080
+ # "underlyingType": "U_BASED",
1081
+ # "contractSize": "1",
1082
+ # "tradeSwitch": True,
1083
+ # "isDisplay": True,
1084
+ # "isOpenApi": False,
1085
+ # "state": 0,
1086
+ # "initLeverage": 20,
1087
+ # "initPositionType": "CROSSED",
1088
+ # "baseCoin": "xt",
1089
+ # "quoteCoin": "usdt",
1090
+ # "baseCoinPrecision": 8,
1091
+ # "baseCoinDisplayPrecision": 4,
1092
+ # "quoteCoinPrecision": 8,
1093
+ # "quoteCoinDisplayPrecision": 4,
1094
+ # "quantityPrecision": 0,
1095
+ # "pricePrecision": 4,
1096
+ # "supportOrderType": "LIMIT,MARKET",
1097
+ # "supportTimeInForce": "GTC,FOK,IOC,GTX",
1098
+ # "supportEntrustType": "TAKE_PROFIT,STOP,TAKE_PROFIT_MARKET,STOP_MARKET,TRAILING_STOP_MARKET",
1099
+ # "supportPositionType": "CROSSED,ISOLATED",
1100
+ # "minQty": "1",
1101
+ # "minNotional": "5",
1102
+ # "maxNotional": "20000000",
1103
+ # "multiplierDown": "0.1",
1104
+ # "multiplierUp": "0.1",
1105
+ # "maxOpenOrders": 200,
1106
+ # "maxEntrusts": 200,
1107
+ # "makerFee": "0.0004",
1108
+ # "takerFee": "0.0006",
1109
+ # "liquidationFee": "0.01",
1110
+ # "marketTakeBound": "0.1",
1111
+ # "depthPrecisionMerge": 5,
1112
+ # "labels": ["HOT"],
1113
+ # "onboardDate": 1657101601000,
1114
+ # "enName": "XTUSDT ",
1115
+ # "cnName": "XTUSDT",
1116
+ # "minStepPrice": "0.0001",
1117
+ # "minPrice": null,
1118
+ # "maxPrice": null,
1119
+ # "deliveryDate": 1669879634000,
1120
+ # "deliveryPrice": null,
1121
+ # "deliveryCompletion": False,
1122
+ # "cnDesc": null,
1123
+ # "enDesc": null
1124
+ # }
1125
+ #
1126
+ id = self.safe_string(market, 'symbol')
1127
+ baseId = self.safe_string_2(market, 'baseCurrency', 'baseCoin')
1128
+ quoteId = self.safe_string_2(market, 'quoteCurrency', 'quoteCoin')
1129
+ base = self.safe_currency_code(baseId)
1130
+ quote = self.safe_currency_code(quoteId)
1131
+ state = self.safe_string(market, 'state')
1132
+ symbol = base + '/' + quote
1133
+ filters = self.safe_value(market, 'filters', [])
1134
+ minAmount = None
1135
+ maxAmount = None
1136
+ minCost = None
1137
+ maxCost = None
1138
+ minPrice = None
1139
+ maxPrice = None
1140
+ for i in range(0, len(filters)):
1141
+ entry = filters[i]
1142
+ filter = self.safe_string(entry, 'filter')
1143
+ if filter == 'QUANTITY':
1144
+ minAmount = self.safe_number(entry, 'min')
1145
+ maxAmount = self.safe_number(entry, 'max')
1146
+ if filter == 'QUOTE_QTY':
1147
+ minCost = self.safe_number(entry, 'min')
1148
+ if filter == 'PRICE':
1149
+ minPrice = self.safe_number(entry, 'min')
1150
+ maxPrice = self.safe_number(entry, 'max')
1151
+ underlyingType = self.safe_string(market, 'underlyingType')
1152
+ linear = None
1153
+ inverse = None
1154
+ settleId = None
1155
+ settle = None
1156
+ expiry = None
1157
+ future = False
1158
+ swap = False
1159
+ contract = False
1160
+ spot = True
1161
+ type = 'spot'
1162
+ if underlyingType == 'U_BASED':
1163
+ symbol = symbol + ':' + quote
1164
+ settleId = baseId
1165
+ settle = quote
1166
+ linear = True
1167
+ inverse = False
1168
+ elif underlyingType == 'COIN_BASED':
1169
+ symbol = symbol + ':' + base
1170
+ settleId = baseId
1171
+ settle = base
1172
+ linear = False
1173
+ inverse = True
1174
+ if underlyingType is not None:
1175
+ expiry = self.safe_integer(market, 'deliveryDate')
1176
+ productType = self.safe_string(market, 'productType')
1177
+ if productType != 'perpetual':
1178
+ symbol = symbol + '-' + self.yymmdd(expiry)
1179
+ type = 'future'
1180
+ future = True
1181
+ else:
1182
+ type = 'swap'
1183
+ swap = True
1184
+ minAmount = self.safe_number(market, 'minQty')
1185
+ minCost = self.safe_number(market, 'minNotional')
1186
+ maxCost = self.safe_number(market, 'maxNotional')
1187
+ minPrice = self.safe_number(market, 'minPrice')
1188
+ maxPrice = self.safe_number(market, 'maxPrice')
1189
+ contract = True
1190
+ spot = False
1191
+ isActive = False
1192
+ if contract:
1193
+ isActive = self.safe_value(market, 'isOpenApi', False)
1194
+ else:
1195
+ if (state == 'ONLINE') and (self.safe_value(market, 'tradingEnabled')) and (self.safe_value(market, 'openapiEnabled')):
1196
+ isActive = True
1197
+ return self.safe_market_structure({
1198
+ 'id': id,
1199
+ 'symbol': symbol,
1200
+ 'base': base,
1201
+ 'quote': quote,
1202
+ 'settle': settle,
1203
+ 'baseId': baseId,
1204
+ 'quoteId': quoteId,
1205
+ 'settleId': settleId,
1206
+ 'type': type,
1207
+ 'spot': spot,
1208
+ 'margin': None,
1209
+ 'swap': swap,
1210
+ 'future': future,
1211
+ 'option': False,
1212
+ 'active': isActive,
1213
+ 'contract': contract,
1214
+ 'linear': linear,
1215
+ 'inverse': inverse,
1216
+ 'taker': self.safe_number(market, 'takerFee'),
1217
+ 'maker': self.safe_number(market, 'makerFee'),
1218
+ 'contractSize': self.safe_number(market, 'contractSize'),
1219
+ 'expiry': expiry,
1220
+ 'expiryDatetime': self.iso8601(expiry),
1221
+ 'strike': None,
1222
+ 'optionType': None,
1223
+ 'precision': {
1224
+ 'price': self.parse_number(self.parse_precision(self.safe_string(market, 'pricePrecision'))),
1225
+ 'amount': self.parse_number(self.parse_precision(self.safe_string(market, 'quantityPrecision'))),
1226
+ 'base': self.parse_number(self.parse_precision(self.safe_string(market, 'baseCoinPrecision'))),
1227
+ 'quote': self.parse_number(self.parse_precision(self.safe_string(market, 'quoteCoinPrecision'))),
1228
+ },
1229
+ 'limits': {
1230
+ 'leverage': {
1231
+ 'min': self.parse_number('1'),
1232
+ 'max': None,
1233
+ },
1234
+ 'amount': {
1235
+ 'min': minAmount,
1236
+ 'max': maxAmount,
1237
+ },
1238
+ 'price': {
1239
+ 'min': minPrice,
1240
+ 'max': maxPrice,
1241
+ },
1242
+ 'cost': {
1243
+ 'min': minCost,
1244
+ 'max': maxCost,
1245
+ },
1246
+ },
1247
+ 'info': market,
1248
+ })
1249
+
1250
+ async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}):
1251
+ """
1252
+ fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
1253
+ :see: https://doc.xt.com/#market4kline
1254
+ :see: https://doc.xt.com/#futures_quotesgetKLine
1255
+ :param str symbol: unified symbol of the market to fetch OHLCV data for
1256
+ :param str timeframe: the length of time each candle represents
1257
+ :param int [since]: timestamp in ms of the earliest candle to fetch
1258
+ :param int [limit]: the maximum amount of candles to fetch
1259
+ :param dict params: extra parameters specific to the xt api endpoint
1260
+ :returns int[][]: A list of candles ordered, open, high, low, close, volume
1261
+ """
1262
+ await self.load_markets()
1263
+ market = self.market(symbol)
1264
+ request = {
1265
+ 'symbol': market['id'],
1266
+ 'interval': self.safe_string(self.timeframes, timeframe, timeframe),
1267
+ }
1268
+ if since is not None:
1269
+ request['startTime'] = since
1270
+ if limit is not None:
1271
+ request['limit'] = limit
1272
+ response = None
1273
+ if market['linear']:
1274
+ response = await self.publicLinearGetFutureMarketV1PublicQKline(self.extend(request, params))
1275
+ elif market['inverse']:
1276
+ response = await self.publicInverseGetFutureMarketV1PublicQKline(self.extend(request, params))
1277
+ else:
1278
+ response = await self.publicSpotGetKline(self.extend(request, params))
1279
+ #
1280
+ # spot
1281
+ #
1282
+ # {
1283
+ # "rc": 0,
1284
+ # "mc": "SUCCESS",
1285
+ # "ma": [],
1286
+ # "result": [
1287
+ # {
1288
+ # "t": 1678167720000,
1289
+ # "o": "22467.85",
1290
+ # "c": "22465.87",
1291
+ # "h": "22468.86",
1292
+ # "l": "22465.21",
1293
+ # "q": "1.316656",
1294
+ # "v": "29582.73018498"
1295
+ # },
1296
+ # ]
1297
+ # }
1298
+ #
1299
+ # swap and future
1300
+ #
1301
+ # {
1302
+ # "returnCode": 0,
1303
+ # "msgInfo": "success",
1304
+ # "error": null,
1305
+ # "result": [
1306
+ # {
1307
+ # "s": "btc_usdt",
1308
+ # "p": "btc_usdt",
1309
+ # "t": 1678168020000,
1310
+ # "o": "22450.0",
1311
+ # "c": "22441.5",
1312
+ # "h": "22450.0",
1313
+ # "l": "22441.5",
1314
+ # "a": "312931",
1315
+ # "v": "702461.58895"
1316
+ # },
1317
+ # ]
1318
+ # }
1319
+ #
1320
+ ohlcvs = self.safe_value(response, 'result', [])
1321
+ return self.parse_ohlcvs(ohlcvs, market, timeframe, since, limit)
1322
+
1323
+ def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
1324
+ #
1325
+ # spot
1326
+ #
1327
+ # {
1328
+ # "t": 1678167720000,
1329
+ # "o": "22467.85",
1330
+ # "c": "22465.87",
1331
+ # "h": "22468.86",
1332
+ # "l": "22465.21",
1333
+ # "q": "1.316656",
1334
+ # "v": "29582.73018498"
1335
+ # }
1336
+ #
1337
+ # swap and future
1338
+ #
1339
+ # {
1340
+ # "s": "btc_usdt",
1341
+ # "p": "btc_usdt",
1342
+ # "t": 1678168020000,
1343
+ # "o": "22450.0",
1344
+ # "c": "22441.5",
1345
+ # "h": "22450.0",
1346
+ # "l": "22441.5",
1347
+ # "a": "312931",
1348
+ # "v": "702461.58895"
1349
+ # }
1350
+ #
1351
+ volumeIndex = 'v' if (market['inverse']) else 'a'
1352
+ return [
1353
+ self.safe_integer(ohlcv, 't'),
1354
+ self.safe_number(ohlcv, 'o'),
1355
+ self.safe_number(ohlcv, 'h'),
1356
+ self.safe_number(ohlcv, 'l'),
1357
+ self.safe_number(ohlcv, 'c'),
1358
+ self.safe_number_2(ohlcv, volumeIndex, 'v'),
1359
+ ]
1360
+
1361
+ async def fetch_order_book(self, symbol: str, limit: Int = None, params={}):
1362
+ """
1363
+ :see: https://doc.xt.com/#market3depth
1364
+ :see: https://doc.xt.com/#futures_quotesgetDepth
1365
+ fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
1366
+ :param str symbol: unified market symbol to fetch the order book for
1367
+ :param int [limit]: the maximum amount of order book entries to return
1368
+ :param dict params: extra parameters specific to the xt api endpoint
1369
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/en/latest/manual.html#order-book-structure>` indexed by market symbols
1370
+ """
1371
+ await self.load_markets()
1372
+ market = self.market(symbol)
1373
+ request = {
1374
+ 'symbol': market['id'],
1375
+ }
1376
+ response = None
1377
+ if market['spot']:
1378
+ if limit is not None:
1379
+ request['limit'] = min(limit, 500)
1380
+ response = await self.publicSpotGetDepth(self.extend(request, params))
1381
+ else:
1382
+ if limit is not None:
1383
+ request['level'] = min(limit, 50)
1384
+ else:
1385
+ request['level'] = 50
1386
+ if market['linear']:
1387
+ response = await self.publicLinearGetFutureMarketV1PublicQDepth(self.extend(request, params))
1388
+ elif market['inverse']:
1389
+ response = await self.publicInverseGetFutureMarketV1PublicQDepth(self.extend(request, params))
1390
+ #
1391
+ # spot
1392
+ #
1393
+ # {
1394
+ # "rc": 0,
1395
+ # "mc": "SUCCESS",
1396
+ # "ma": [],
1397
+ # "result": {
1398
+ # "timestamp": 1678169975184,
1399
+ # "lastUpdateId": 1675333221812,
1400
+ # "bids": [
1401
+ # ["22444.51", "0.129887"],
1402
+ # ["22444.49", "0.114245"],
1403
+ # ["22444.30", "0.225956"]
1404
+ # ],
1405
+ # "asks": [
1406
+ # ["22446.19", "0.095330"],
1407
+ # ["22446.24", "0.224413"],
1408
+ # ["22446.28", "0.329095"]
1409
+ # ]
1410
+ # }
1411
+ # }
1412
+ #
1413
+ # swap and future
1414
+ #
1415
+ # {
1416
+ # "returnCode": 0,
1417
+ # "msgInfo": "success",
1418
+ # "error": null,
1419
+ # "result": {
1420
+ # "t": 1678170311005,
1421
+ # "s": "btc_usdt",
1422
+ # "u": 471694545627,
1423
+ # "b": [
1424
+ # ["22426", "198623"],
1425
+ # ["22423.5", "80295"],
1426
+ # ["22423", "163580"]
1427
+ # ],
1428
+ # "a": [
1429
+ # ["22427", "3417"],
1430
+ # ["22428.5", "43532"],
1431
+ # ["22429", "119"]
1432
+ # ]
1433
+ # }
1434
+ # }
1435
+ #
1436
+ orderBook = self.safe_value(response, 'result', {})
1437
+ timestamp = self.safe_integer_2(orderBook, 'timestamp', 't')
1438
+ if market['spot']:
1439
+ return self.parse_order_book(orderBook, symbol, timestamp)
1440
+ return self.parse_order_book(orderBook, symbol, timestamp, 'b', 'a')
1441
+
1442
+ async def fetch_ticker(self, symbol: str, params={}):
1443
+ """
1444
+ fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
1445
+ :see: https://doc.xt.com/#market10ticker24h
1446
+ :see: https://doc.xt.com/#futures_quotesgetAggTicker
1447
+ :param str symbol: unified market symbol to fetch the ticker for
1448
+ :param dict params: extra parameters specific to the xt api endpoint
1449
+ :returns dict: a `ticker structure <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
1450
+ """
1451
+ await self.load_markets()
1452
+ market = self.market(symbol)
1453
+ request = {
1454
+ 'symbol': market['id'],
1455
+ }
1456
+ response = None
1457
+ if market['linear']:
1458
+ response = await self.publicLinearGetFutureMarketV1PublicQAggTicker(self.extend(request, params))
1459
+ elif market['inverse']:
1460
+ response = await self.publicInverseGetFutureMarketV1PublicQAggTicker(self.extend(request, params))
1461
+ else:
1462
+ response = await self.publicSpotGetTicker24h(self.extend(request, params))
1463
+ #
1464
+ # spot
1465
+ #
1466
+ # {
1467
+ # "rc": 0,
1468
+ # "mc": "SUCCESS",
1469
+ # "ma": [],
1470
+ # "result": [
1471
+ # {
1472
+ # "s": "btc_usdt",
1473
+ # "t": 1678172693931,
1474
+ # "cv": "34.00",
1475
+ # "cr": "0.0015",
1476
+ # "o": "22398.05",
1477
+ # "l": "22323.72",
1478
+ # "h": "22600.50",
1479
+ # "c": "22432.05",
1480
+ # "q": "7962.256931",
1481
+ # "v": "178675209.47416856"
1482
+ # }
1483
+ # ]
1484
+ # }
1485
+ #
1486
+ # swap and future
1487
+ #
1488
+ # {
1489
+ # "returnCode": 0,
1490
+ # "msgInfo": "success",
1491
+ # "error": null,
1492
+ # "result": {
1493
+ # "t": 1678172848572,
1494
+ # "s": "btc_usdt",
1495
+ # "c": "22415.5",
1496
+ # "h": "22590.0",
1497
+ # "l": "22310.0",
1498
+ # "a": "623654031",
1499
+ # "v": "1399166074.31675",
1500
+ # "o": "22381.5",
1501
+ # "r": "0.0015",
1502
+ # "i": "22424.5",
1503
+ # "m": "22416.5",
1504
+ # "bp": "22415",
1505
+ # "ap": "22415.5"
1506
+ # }
1507
+ # }
1508
+ #
1509
+ ticker = self.safe_value(response, 'result')
1510
+ if market['spot']:
1511
+ return self.parse_ticker(ticker[0], market)
1512
+ return self.parse_ticker(ticker, market)
1513
+
1514
+ async def fetch_tickers(self, symbols: List[str] = None, params={}) -> Tickers:
1515
+ """
1516
+ fetches price tickers for multiple markets, statistical calculations with the information calculated over the past 24 hours each market
1517
+ :see: https://doc.xt.com/#market10ticker24h
1518
+ :see: https://doc.xt.com/#futures_quotesgetAggTickers
1519
+ :param str [symbols]: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
1520
+ :param dict params: extra parameters specific to the xt api endpoint
1521
+ :returns dict: an array of `ticker structures <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
1522
+ """
1523
+ await self.load_markets()
1524
+ market = None
1525
+ if symbols is not None:
1526
+ symbols = self.market_symbols(symbols)
1527
+ market = self.market(symbols[0])
1528
+ request = {}
1529
+ type = None
1530
+ subType = None
1531
+ response = None
1532
+ type, params = self.handle_market_type_and_params('fetchTickers', market, params)
1533
+ subType, params = self.handle_sub_type_and_params('fetchTickers', market, params)
1534
+ if subType == 'inverse':
1535
+ response = await self.publicInverseGetFutureMarketV1PublicQAggTickers(self.extend(request, params))
1536
+ elif (subType == 'linear') or (type == 'swap') or (type == 'future'):
1537
+ response = await self.publicLinearGetFutureMarketV1PublicQAggTickers(self.extend(request, params))
1538
+ else:
1539
+ response = await self.publicSpotGetTicker24h(self.extend(request, params))
1540
+ #
1541
+ # spot
1542
+ #
1543
+ # {
1544
+ # "rc": 0,
1545
+ # "mc": "SUCCESS",
1546
+ # "ma": [],
1547
+ # "result": [
1548
+ # {
1549
+ # "s": "btc_usdt",
1550
+ # "t": 1678172693931,
1551
+ # "cv": "34.00",
1552
+ # "cr": "0.0015",
1553
+ # "o": "22398.05",
1554
+ # "l": "22323.72",
1555
+ # "h": "22600.50",
1556
+ # "c": "22432.05",
1557
+ # "q": "7962.256931",
1558
+ # "v": "178675209.47416856"
1559
+ # }
1560
+ # ]
1561
+ # }
1562
+ #
1563
+ # swap and future
1564
+ #
1565
+ # {
1566
+ # "returnCode": 0,
1567
+ # "msgInfo": "success",
1568
+ # "error": null,
1569
+ # "result": [
1570
+ # {
1571
+ # "t": 1680738775108,
1572
+ # "s": "badger_usdt",
1573
+ # "c": "2.7176",
1574
+ # "h": "2.7917",
1575
+ # "l": "2.6818",
1576
+ # "a": "88332",
1577
+ # "v": "242286.3520",
1578
+ # "o": "2.7422",
1579
+ # "r": "-0.0089",
1580
+ # "i": "2.7155",
1581
+ # "m": "2.7161",
1582
+ # "bp": "2.7152",
1583
+ # "ap": "2.7176"
1584
+ # },
1585
+ # ]
1586
+ # }
1587
+ #
1588
+ tickers = self.safe_value(response, 'result', [])
1589
+ result = {}
1590
+ for i in range(0, len(tickers)):
1591
+ ticker = self.parse_ticker(tickers[i], market)
1592
+ symbol = ticker['symbol']
1593
+ result[symbol] = ticker
1594
+ return self.filter_by_array(result, 'symbol', symbols)
1595
+
1596
+ async def fetch_bids_asks(self, symbols: List[str] = None, params={}):
1597
+ """
1598
+ fetches the bid and ask price and volume for multiple markets
1599
+ :see: https://doc.xt.com/#market9tickerBook
1600
+ :param str [symbols]: unified symbols of the markets to fetch the bids and asks for, all markets are returned if not assigned
1601
+ :param dict params: extra parameters specific to the xt api endpoint
1602
+ :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
1603
+ """
1604
+ await self.load_markets()
1605
+ symbols = self.market_symbols(symbols)
1606
+ request = {}
1607
+ market = None
1608
+ if symbols is not None:
1609
+ market = self.market(symbols[0])
1610
+ subType = None
1611
+ subType, params = self.handle_sub_type_and_params('fetchBidsAsks', market, params)
1612
+ if subType is not None:
1613
+ raise NotSupported(self.id + ' fetchBidsAsks() is not available for swap and future markets, only spot markets are supported')
1614
+ response = await self.publicSpotGetTickerBook(self.extend(request, params))
1615
+ #
1616
+ # {
1617
+ # "rc": 0,
1618
+ # "mc": "SUCCESS",
1619
+ # "ma": [],
1620
+ # "result": [
1621
+ # {
1622
+ # "s": "kas_usdt",
1623
+ # "t": 1679539891853,
1624
+ # "ap": "0.016298",
1625
+ # "aq": "5119.09",
1626
+ # "bp": "0.016290",
1627
+ # "bq": "135.37"
1628
+ # },
1629
+ # ]
1630
+ # }
1631
+ #
1632
+ tickers = self.safe_value(response, 'result', [])
1633
+ return self.parse_tickers(tickers, symbols)
1634
+
1635
+ def parse_ticker(self, ticker, market=None):
1636
+ #
1637
+ # spot: fetchTicker, fetchTickers
1638
+ #
1639
+ # {
1640
+ # "s": "btc_usdt",
1641
+ # "t": 1678172693931,
1642
+ # "cv": "34.00",
1643
+ # "cr": "0.0015",
1644
+ # "o": "22398.05",
1645
+ # "l": "22323.72",
1646
+ # "h": "22600.50",
1647
+ # "c": "22432.05",
1648
+ # "q": "7962.256931",
1649
+ # "v": "178675209.47416856"
1650
+ # }
1651
+ #
1652
+ # swap and future: fetchTicker, fetchTickers
1653
+ #
1654
+ # {
1655
+ # "t": 1678172848572,
1656
+ # "s": "btc_usdt",
1657
+ # "c": "22415.5",
1658
+ # "h": "22590.0",
1659
+ # "l": "22310.0",
1660
+ # "a": "623654031",
1661
+ # "v": "1399166074.31675",
1662
+ # "o": "22381.5",
1663
+ # "r": "0.0015",
1664
+ # "i": "22424.5",
1665
+ # "m": "22416.5",
1666
+ # "bp": "22415",
1667
+ # "ap": "22415.5"
1668
+ # }
1669
+ #
1670
+ # fetchBidsAsks
1671
+ #
1672
+ # {
1673
+ # "s": "kas_usdt",
1674
+ # "t": 1679539891853,
1675
+ # "ap": "0.016298",
1676
+ # "aq": "5119.09",
1677
+ # "bp": "0.016290",
1678
+ # "bq": "135.37"
1679
+ # }
1680
+ #
1681
+ marketId = self.safe_string(ticker, 's')
1682
+ marketType = market['type'] if (market is not None) else None
1683
+ if marketType is None:
1684
+ marketType = ('cv' in ticker) or 'spot' if ('aq' in ticker) else 'contract'
1685
+ market = self.safe_market(marketId, market, '_', marketType)
1686
+ symbol = market['symbol']
1687
+ timestamp = self.safe_integer(ticker, 't')
1688
+ return self.safe_ticker({
1689
+ 'symbol': symbol,
1690
+ 'timestamp': timestamp,
1691
+ 'datetime': self.iso8601(timestamp),
1692
+ 'high': self.safe_number(ticker, 'h'),
1693
+ 'low': self.safe_number(ticker, 'l'),
1694
+ 'bid': self.safe_number(ticker, 'bp'),
1695
+ 'bidVolume': self.safe_number(ticker, 'bq'),
1696
+ 'ask': self.safe_number(ticker, 'ap'),
1697
+ 'askVolume': self.safe_number(ticker, 'aq'),
1698
+ 'vwap': None,
1699
+ 'open': self.safe_string(ticker, 'o'),
1700
+ 'close': self.safe_string(ticker, 'c'),
1701
+ 'last': self.safe_string(ticker, 'c'),
1702
+ 'previousClose': None,
1703
+ 'change': self.safe_number(ticker, 'cv'),
1704
+ 'percentage': self.safe_number_2(ticker, 'cr', 'r'),
1705
+ 'average': None,
1706
+ 'baseVolume': None,
1707
+ 'quoteVolume': self.safe_number_2(ticker, 'a', 'v'),
1708
+ 'info': ticker,
1709
+ }, market)
1710
+
1711
+ async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}):
1712
+ """
1713
+ get the list of most recent trades for a particular symbol
1714
+ :see: https://doc.xt.com/#market5tradeRecent
1715
+ :see: https://doc.xt.com/#futures_quotesgetDeal
1716
+ :param str symbol: unified market symbol to fetch trades for
1717
+ :param int [since]: timestamp in ms of the earliest trade to fetch
1718
+ :param int [limit]: the maximum amount of trades to fetch
1719
+ :param dict params: extra parameters specific to the xt api endpoint
1720
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/en/latest/manual.html?#public-trades>`
1721
+ """
1722
+ await self.load_markets()
1723
+ market = self.market(symbol)
1724
+ request = {
1725
+ 'symbol': market['id'],
1726
+ }
1727
+ response = None
1728
+ if market['spot']:
1729
+ if limit is not None:
1730
+ request['limit'] = limit
1731
+ response = await self.publicSpotGetTradeRecent(self.extend(request, params))
1732
+ else:
1733
+ if limit is not None:
1734
+ request['num'] = limit
1735
+ if market['linear']:
1736
+ response = await self.publicLinearGetFutureMarketV1PublicQDeal(self.extend(request, params))
1737
+ elif market['inverse']:
1738
+ response = await self.publicInverseGetFutureMarketV1PublicQDeal(self.extend(request, params))
1739
+ #
1740
+ # spot
1741
+ #
1742
+ # {
1743
+ # "rc": 0,
1744
+ # "mc": "SUCCESS",
1745
+ # "ma": [],
1746
+ # "result": [
1747
+ # {
1748
+ # "i": 203530723141917063,
1749
+ # "t": 1678227505815,
1750
+ # "p": "22038.81",
1751
+ # "q": "0.000978",
1752
+ # "v": "21.55395618",
1753
+ # "b": True
1754
+ # },
1755
+ # ]
1756
+ # }
1757
+ #
1758
+ # swap and future
1759
+ #
1760
+ # {
1761
+ # "returnCode": 0,
1762
+ # "msgInfo": "success",
1763
+ # "error": null,
1764
+ # "result": [
1765
+ # {
1766
+ # "t": 1678227683897,
1767
+ # "s": "btc_usdt",
1768
+ # "p": "22031",
1769
+ # "a": "1067",
1770
+ # "m": "BID"
1771
+ # },
1772
+ # ]
1773
+ # }
1774
+ #
1775
+ trades = self.safe_value(response, 'result', [])
1776
+ return self.parse_trades(trades, market)
1777
+
1778
+ async def fetch_my_trades(self, symbol: str = None, since: Int = None, limit: Int = None, params={}):
1779
+ """
1780
+ fetch all trades made by the user
1781
+ :see: https://doc.xt.com/#tradetradeGet
1782
+ :see: https://doc.xt.com/#futures_ordergetTrades
1783
+ :param str [symbol]: unified market symbol to fetch trades for
1784
+ :param int [since]: timestamp in ms of the earliest trade to fetch
1785
+ :param int [limit]: the maximum amount of trades to fetch
1786
+ :param dict params: extra parameters specific to the xt api endpoint
1787
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/en/latest/manual.html?#public-trades>`
1788
+ """
1789
+ await self.load_markets()
1790
+ request = {}
1791
+ market = None
1792
+ if symbol is not None:
1793
+ market = self.market(symbol)
1794
+ request['symbol'] = market['id']
1795
+ if since is not None:
1796
+ request['startTime'] = since
1797
+ type = None
1798
+ subType = None
1799
+ response = None
1800
+ type, params = self.handle_market_type_and_params('fetchMyTrades', market, params)
1801
+ subType, params = self.handle_sub_type_and_params('fetchMyTrades', market, params)
1802
+ if (subType is not None) or (type == 'swap') or (type == 'future'):
1803
+ if limit is not None:
1804
+ request['size'] = limit
1805
+ if subType == 'inverse':
1806
+ response = await self.privateInverseGetFutureTradeV1OrderTradeList(self.extend(request, params))
1807
+ else:
1808
+ response = await self.privateLinearGetFutureTradeV1OrderTradeList(self.extend(request, params))
1809
+ else:
1810
+ marginMode = None
1811
+ marginMode, params = self.handle_margin_mode_and_params('fetchMyTrades', params)
1812
+ marginOrSpotRequest = 'LEVER' if (marginMode is not None) else 'SPOT'
1813
+ request['bizType'] = marginOrSpotRequest
1814
+ if limit is not None:
1815
+ request['limit'] = limit
1816
+ response = await self.privateSpotGetTrade(self.extend(request, params))
1817
+ #
1818
+ # spot and margin
1819
+ #
1820
+ # {
1821
+ # "rc": 0,
1822
+ # "mc": "SUCCESS",
1823
+ # "ma": [],
1824
+ # "result": {
1825
+ # "hasPrev": False,
1826
+ # "hasNext": False,
1827
+ # "items": [
1828
+ # {
1829
+ # "symbol": "btc_usdt",
1830
+ # "tradeId": "206906233569974658",
1831
+ # "orderId": "206906233178463488",
1832
+ # "orderSide": "SELL",
1833
+ # "orderType": "MARKET",
1834
+ # "bizType": "SPOT",
1835
+ # "time": 1679032290215,
1836
+ # "price": "25703.46",
1837
+ # "quantity": "0.000099",
1838
+ # "quoteQty": "2.54464254",
1839
+ # "baseCurrency": "btc",
1840
+ # "quoteCurrency": "usdt",
1841
+ # "fee": "0.00508929",
1842
+ # "feeCurrency": "usdt",
1843
+ # "takerMaker": "TAKER"
1844
+ # },
1845
+ # ]
1846
+ # }
1847
+ # }
1848
+ #
1849
+ # swap and future
1850
+ #
1851
+ # {
1852
+ # "returnCode": 0,
1853
+ # "msgInfo": "success",
1854
+ # "error": null,
1855
+ # "result": {
1856
+ # "page": 1,
1857
+ # "ps": 10,
1858
+ # "total": 2,
1859
+ # "items": [
1860
+ # {
1861
+ # "orderId": "207260566170987200",
1862
+ # "execId": "207260566790603265",
1863
+ # "symbol": "btc_usdt",
1864
+ # "quantity": "13",
1865
+ # "price": "27368",
1866
+ # "fee": "0.02134704",
1867
+ # "feeCoin": "usdt",
1868
+ # "timestamp": 1679116769838,
1869
+ # "takerMaker": "TAKER"
1870
+ # },
1871
+ # ]
1872
+ # }
1873
+ # }
1874
+ #
1875
+ data = self.safe_value(response, 'result', {})
1876
+ trades = self.safe_value(data, 'items', [])
1877
+ return self.parse_trades(trades, market, since, limit)
1878
+
1879
+ def parse_trade(self, trade, market=None):
1880
+ #
1881
+ # spot: fetchTrades
1882
+ #
1883
+ # {
1884
+ # "i": 203530723141917063,
1885
+ # "t": 1678227505815,
1886
+ # "p": "22038.81",
1887
+ # "q": "0.000978",
1888
+ # "v": "21.55395618",
1889
+ # "b": True
1890
+ # }
1891
+ #
1892
+ # swap and future: fetchTrades
1893
+ #
1894
+ # {
1895
+ # "t": 1678227683897,
1896
+ # "s": "btc_usdt",
1897
+ # "p": "22031",
1898
+ # "a": "1067",
1899
+ # "m": "BID"
1900
+ # }
1901
+ #
1902
+ # spot: fetchMyTrades
1903
+ #
1904
+ # {
1905
+ # "symbol": "btc_usdt",
1906
+ # "tradeId": "206906233569974658",
1907
+ # "orderId": "206906233178463488",
1908
+ # "orderSide": "SELL",
1909
+ # "orderType": "MARKET",
1910
+ # "bizType": "SPOT",
1911
+ # "time": 1679032290215,
1912
+ # "price": "25703.46",
1913
+ # "quantity": "0.000099",
1914
+ # "quoteQty": "2.54464254",
1915
+ # "baseCurrency": "btc",
1916
+ # "quoteCurrency": "usdt",
1917
+ # "fee": "0.00508929",
1918
+ # "feeCurrency": "usdt",
1919
+ # "takerMaker": "TAKER"
1920
+ # }
1921
+ #
1922
+ # swap and future: fetchMyTrades
1923
+ #
1924
+ # {
1925
+ # "orderId": "207260566170987200",
1926
+ # "execId": "207260566790603265",
1927
+ # "symbol": "btc_usdt",
1928
+ # "quantity": "13",
1929
+ # "price": "27368",
1930
+ # "fee": "0.02134704",
1931
+ # "feeCoin": "usdt",
1932
+ # "timestamp": 1679116769838,
1933
+ # "takerMaker": "TAKER"
1934
+ # }
1935
+ #
1936
+ marketId = self.safe_string_2(trade, 's', 'symbol')
1937
+ marketType = market['type'] if (market is not None) else None
1938
+ if marketType is None:
1939
+ marketType = ('b' in trade) or 'spot' if ('bizType' in trade) else 'contract'
1940
+ market = self.safe_market(marketId, market, '_', marketType)
1941
+ bidOrAsk = self.safe_string(trade, 'm')
1942
+ side = self.safe_string_lower(trade, 'orderSide')
1943
+ if bidOrAsk is not None:
1944
+ side = 'buy' if (bidOrAsk == 'BID') else 'sell'
1945
+ buyerMaker = self.safe_value(trade, 'b')
1946
+ takerOrMaker = self.safe_string_lower(trade, 'takerMaker')
1947
+ if buyerMaker is not None:
1948
+ takerOrMaker = 'maker' if buyerMaker else 'taker'
1949
+ timestamp = self.safe_integer_n(trade, ['t', 'time', 'timestamp'])
1950
+ quantity = self.safe_string_2(trade, 'q', 'quantity')
1951
+ amount = None
1952
+ if marketType == 'spot':
1953
+ amount = quantity
1954
+ else:
1955
+ if quantity is None:
1956
+ amount = Precise.string_mul(self.safe_string(trade, 'a'), self.number_to_string(market['contractSize']))
1957
+ else:
1958
+ amount = Precise.string_mul(quantity, self.number_to_string(market['contractSize']))
1959
+ return self.safe_trade({
1960
+ 'info': trade,
1961
+ 'id': self.safe_string_n(trade, ['i', 'tradeId', 'execId']),
1962
+ 'timestamp': timestamp,
1963
+ 'datetime': self.iso8601(timestamp),
1964
+ 'symbol': market['symbol'],
1965
+ 'order': self.safe_string(trade, 'orderId'),
1966
+ 'type': self.safe_string_lower(trade, 'orderType'),
1967
+ 'side': side,
1968
+ 'takerOrMaker': takerOrMaker,
1969
+ 'price': self.safe_string_2(trade, 'p', 'price'),
1970
+ 'amount': amount,
1971
+ 'cost': None,
1972
+ 'fee': {
1973
+ 'currency': self.safe_currency_code(self.safe_string_2(trade, 'feeCurrency', 'feeCoin')),
1974
+ 'cost': self.safe_string(trade, 'fee'),
1975
+ 'rate': None,
1976
+ },
1977
+ }, market)
1978
+
1979
+ async def fetch_balance(self, params={}):
1980
+ """
1981
+ query for balance and get the amount of funds available for trading or funds locked in orders
1982
+ :see: https://doc.xt.com/#balancebalancesGet
1983
+ :see: https://doc.xt.com/#futures_usergetBalances
1984
+ :param dict params: extra parameters specific to the xt api endpoint
1985
+ :returns dict: a `balance structure <https://docs.ccxt.com/en/latest/manual.html?#balance-structure>`
1986
+ """
1987
+ await self.load_markets()
1988
+ type = None
1989
+ subType = None
1990
+ response = None
1991
+ type, params = self.handle_market_type_and_params('fetchBalance', None, params)
1992
+ subType, params = self.handle_sub_type_and_params('fetchBalance', None, params)
1993
+ isContractWallet = ((type == 'swap') or (type == 'future'))
1994
+ if subType == 'inverse':
1995
+ response = await self.privateInverseGetFutureUserV1BalanceList(params)
1996
+ elif (subType == 'linear') or isContractWallet:
1997
+ response = await self.privateLinearGetFutureUserV1BalanceList(params)
1998
+ else:
1999
+ response = await self.privateSpotGetBalances(params)
2000
+ #
2001
+ # spot
2002
+ #
2003
+ # {
2004
+ # "rc": 0,
2005
+ # "mc": "SUCCESS",
2006
+ # "ma": [],
2007
+ # "result": {
2008
+ # "totalUsdtAmount": "31.75931133",
2009
+ # "totalBtcAmount": "0.00115951",
2010
+ # "assets": [
2011
+ # {
2012
+ # "currency": "usdt",
2013
+ # "currencyId": 11,
2014
+ # "frozenAmount": "0.03834082",
2015
+ # "availableAmount": "31.70995965",
2016
+ # "totalAmount": "31.74830047",
2017
+ # "convertBtcAmount": "0.00115911",
2018
+ # "convertUsdtAmount": "31.74830047"
2019
+ # },
2020
+ # ]
2021
+ # }
2022
+ # }
2023
+ #
2024
+ # swap and future
2025
+ #
2026
+ # {
2027
+ # "returnCode": 0,
2028
+ # "msgInfo": "success",
2029
+ # "error": null,
2030
+ # "result": [
2031
+ # {
2032
+ # "coin": "usdt",
2033
+ # "walletBalance": "19.29849875",
2034
+ # "openOrderMarginFrozen": "0",
2035
+ # "isolatedMargin": "0.709475",
2036
+ # "crossedMargin": "0",
2037
+ # "availableBalance": "18.58902375",
2038
+ # "bonus": "0",
2039
+ # "coupon":"0"
2040
+ # }
2041
+ # ]
2042
+ # }
2043
+ #
2044
+ balances = None
2045
+ if (subType is not None) or isContractWallet:
2046
+ balances = self.safe_value(response, 'result', [])
2047
+ else:
2048
+ data = self.safe_value(response, 'result', {})
2049
+ balances = self.safe_value(data, 'assets', [])
2050
+ return self.parse_balance(balances)
2051
+
2052
+ def parse_balance(self, response):
2053
+ #
2054
+ # spot
2055
+ #
2056
+ # {
2057
+ # "currency": "usdt",
2058
+ # "currencyId": 11,
2059
+ # "frozenAmount": "0.03834082",
2060
+ # "availableAmount": "31.70995965",
2061
+ # "totalAmount": "31.74830047",
2062
+ # "convertBtcAmount": "0.00115911",
2063
+ # "convertUsdtAmount": "31.74830047"
2064
+ # }
2065
+ #
2066
+ # swap and future
2067
+ #
2068
+ # {
2069
+ # "coin": "usdt",
2070
+ # "walletBalance": "19.29849875",
2071
+ # "openOrderMarginFrozen": "0",
2072
+ # "isolatedMargin": "0.709475",
2073
+ # "crossedMargin": "0",
2074
+ # "availableBalance": "18.58902375",
2075
+ # "bonus": "0",
2076
+ # "coupon":"0"
2077
+ # }
2078
+ #
2079
+ result = {'info': response}
2080
+ for i in range(0, len(response)):
2081
+ balance = response[i]
2082
+ currencyId = self.safe_string_2(balance, 'currency', 'coin')
2083
+ code = self.safe_currency_code(currencyId)
2084
+ account = self.account()
2085
+ free = self.safe_string_2(balance, 'availableAmount', 'availableBalance')
2086
+ used = self.safe_string(balance, 'frozenAmount')
2087
+ total = self.safe_string_2(balance, 'totalAmount', 'walletBalance')
2088
+ if used is None:
2089
+ crossedAndIsolatedMargin = Precise.string_add(self.safe_string(balance, 'crossedMargin'), self.safe_string(balance, 'isolatedMargin'))
2090
+ used = Precise.string_add(self.safe_string(balance, 'openOrderMarginFrozen'), crossedAndIsolatedMargin)
2091
+ account['free'] = free
2092
+ account['used'] = used
2093
+ account['total'] = total
2094
+ result[code] = account
2095
+ return self.safe_balance(result)
2096
+
2097
+ async def create_market_buy_order_with_cost(self, symbol: str, cost: float, params={}):
2098
+ """
2099
+ :see: https://doc.xt.com/#orderorderPost
2100
+ create a market buy order by providing the symbol and cost
2101
+ :param str symbol: unified symbol of the market to create an order in
2102
+ :param float cost: how much you want to trade in units of the quote currency
2103
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2104
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2105
+ """
2106
+ await self.load_markets()
2107
+ market = self.market(symbol)
2108
+ if not market['spot']:
2109
+ raise NotSupported(self.id + ' createMarketBuyOrderWithCost() supports spot orders only')
2110
+ return await self.create_order(symbol, 'market', 'buy', cost, 1, params)
2111
+
2112
+ async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
2113
+ """
2114
+ create a trade order
2115
+ :see: https://doc.xt.com/#orderorderPost
2116
+ :see: https://doc.xt.com/#futures_ordercreate
2117
+ :see: https://doc.xt.com/#futures_entrustcreatePlan
2118
+ :see: https://doc.xt.com/#futures_entrustcreateProfit
2119
+ :param str symbol: unified symbol of the market to create an order in
2120
+ :param str type: 'market' or 'limit'
2121
+ :param str side: 'buy' or 'sell'
2122
+ :param float amount: how much you want to trade in units of the base currency
2123
+ :param float [price]: the price to fulfill the order, in units of the quote currency, can be ignored in market orders
2124
+ :param dict params: extra parameters specific to the xt api endpoint
2125
+ :param str [params.timeInForce]: 'GTC', 'IOC', 'FOK' or 'GTX'
2126
+ :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
2127
+ :param str [params.triggerPriceType]: 'INDEX_PRICE', 'MARK_PRICE', 'LATEST_PRICE', required if stopPrice is defined
2128
+ :param float [params.stopPrice]: price to trigger a stop order
2129
+ :param float [params.stopLoss]: price to set a stop-loss on an open position
2130
+ :param float [params.takeProfit]: price to set a take-profit on an open position
2131
+ :returns dict: an `order structure <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
2132
+ """
2133
+ await self.load_markets()
2134
+ market = self.market(symbol)
2135
+ symbol = market['symbol']
2136
+ if market['spot']:
2137
+ return await self.create_spot_order(symbol, type, side, amount, price, params)
2138
+ else:
2139
+ return await self.create_contract_order(symbol, type, side, amount, price, params)
2140
+
2141
+ async def create_spot_order(self, symbol: str, type, side, amount, price=None, params={}):
2142
+ await self.load_markets()
2143
+ market = self.market(symbol)
2144
+ request = {
2145
+ 'symbol': market['id'],
2146
+ 'side': side.upper(),
2147
+ 'type': type.upper(),
2148
+ }
2149
+ timeInForce = None
2150
+ marginMode = None
2151
+ marginMode, params = self.handle_margin_mode_and_params('createOrder', params)
2152
+ marginOrSpotRequest = 'LEVER' if (marginMode is not None) else 'SPOT'
2153
+ request['bizType'] = marginOrSpotRequest
2154
+ if type == 'market':
2155
+ timeInForce = self.safe_string_upper(params, 'timeInForce', 'FOK')
2156
+ if side == 'buy':
2157
+ cost = self.safe_string(params, 'cost')
2158
+ params = self.omit(params, 'cost')
2159
+ createMarketBuyOrderRequiresPrice = self.safe_bool(self.options, 'createMarketBuyOrderRequiresPrice', True)
2160
+ if createMarketBuyOrderRequiresPrice:
2161
+ if price is None and (cost is None):
2162
+ 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')
2163
+ else:
2164
+ amountString = self.number_to_string(amount)
2165
+ priceString = self.number_to_string(price)
2166
+ costCalculated: Str = None
2167
+ if price is not None:
2168
+ costCalculated = Precise.string_mul(amountString, priceString)
2169
+ else:
2170
+ costCalculated = cost
2171
+ request['quoteQty'] = self.cost_to_precision(symbol, costCalculated)
2172
+ else:
2173
+ amountCost = cost if (cost is not None) else amount
2174
+ request['quoteQty'] = self.cost_to_precision(symbol, amountCost)
2175
+ else:
2176
+ timeInForce = self.safe_string_upper(params, 'timeInForce', 'GTC')
2177
+ request['price'] = self.price_to_precision(symbol, price)
2178
+ if (side == 'sell') or (type == 'limit'):
2179
+ request['quantity'] = self.amount_to_precision(symbol, amount)
2180
+ request['timeInForce'] = timeInForce
2181
+ response = await self.privateSpotPostOrder(self.extend(request, params))
2182
+ #
2183
+ # {
2184
+ # "rc": 0,
2185
+ # "mc": "SUCCESS",
2186
+ # "ma": [],
2187
+ # "result": {
2188
+ # "orderId": "204371980095156544"
2189
+ # }
2190
+ # }
2191
+ #
2192
+ order = self.safe_value(response, 'result', {})
2193
+ return self.parse_order(order, market)
2194
+
2195
+ async def create_contract_order(self, symbol: str, type, side, amount, price=None, params={}):
2196
+ await self.load_markets()
2197
+ market = self.market(symbol)
2198
+ request = {
2199
+ 'symbol': market['id'],
2200
+ 'origQty': self.amount_to_precision(symbol, amount),
2201
+ }
2202
+ timeInForce = self.safe_string_upper(params, 'timeInForce')
2203
+ if timeInForce is not None:
2204
+ request['timeInForce'] = timeInForce
2205
+ reduceOnly = self.safe_value(params, 'reduceOnly', False)
2206
+ if side == 'buy':
2207
+ requestType = 'SHORT' if (reduceOnly) else 'LONG'
2208
+ request['positionSide'] = requestType
2209
+ else:
2210
+ requestType = 'LONG' if (reduceOnly) else 'SHORT'
2211
+ request['positionSide'] = requestType
2212
+ response = None
2213
+ triggerPrice = self.safe_number_2(params, 'triggerPrice', 'stopPrice')
2214
+ stopLoss = self.safe_number_2(params, 'stopLoss', 'triggerStopPrice')
2215
+ takeProfit = self.safe_number_2(params, 'takeProfit', 'triggerProfitPrice')
2216
+ isTrigger = (triggerPrice is not None)
2217
+ isStopLoss = (stopLoss is not None)
2218
+ isTakeProfit = (takeProfit is not None)
2219
+ if price is not None:
2220
+ if not (isStopLoss) and not (isTakeProfit):
2221
+ request['price'] = self.price_to_precision(symbol, price)
2222
+ if isTrigger:
2223
+ request['timeInForce'] = self.safe_string_upper(params, 'timeInForce', 'GTC')
2224
+ request['triggerPriceType'] = self.safe_string(params, 'triggerPriceType', 'LATEST_PRICE')
2225
+ request['orderSide'] = side.upper()
2226
+ request['stopPrice'] = self.price_to_precision(symbol, triggerPrice)
2227
+ entrustType = 'STOP_MARKET' if (type == 'market') else 'STOP'
2228
+ request['entrustType'] = entrustType
2229
+ params = self.omit(params, 'triggerPrice')
2230
+ if market['linear']:
2231
+ response = await self.privateLinearPostFutureTradeV1EntrustCreatePlan(self.extend(request, params))
2232
+ elif market['inverse']:
2233
+ response = await self.privateInversePostFutureTradeV1EntrustCreatePlan(self.extend(request, params))
2234
+ elif isStopLoss or isTakeProfit:
2235
+ if isStopLoss:
2236
+ request['triggerStopPrice'] = self.price_to_precision(symbol, stopLoss)
2237
+ else:
2238
+ request['triggerProfitPrice'] = self.price_to_precision(symbol, takeProfit)
2239
+ params = self.omit(params, ['stopLoss', 'takeProfit'])
2240
+ if market['linear']:
2241
+ response = await self.privateLinearPostFutureTradeV1EntrustCreateProfit(self.extend(request, params))
2242
+ elif market['inverse']:
2243
+ response = await self.privateInversePostFutureTradeV1EntrustCreateProfit(self.extend(request, params))
2244
+ else:
2245
+ request['orderSide'] = side.upper()
2246
+ request['orderType'] = type.upper()
2247
+ if market['linear']:
2248
+ response = await self.privateLinearPostFutureTradeV1OrderCreate(self.extend(request, params))
2249
+ elif market['inverse']:
2250
+ response = await self.privateInversePostFutureTradeV1OrderCreate(self.extend(request, params))
2251
+ #
2252
+ # {
2253
+ # "returnCode": 0,
2254
+ # "msgInfo": "success",
2255
+ # "error": null,
2256
+ # "result": "206410760006650176"
2257
+ # }
2258
+ #
2259
+ return self.parse_order(response, market)
2260
+
2261
+ async def fetch_order(self, id: str, symbol: str = None, params={}):
2262
+ """
2263
+ fetches information on an order made by the user
2264
+ :see: https://doc.xt.com/#orderorderGet
2265
+ :see: https://doc.xt.com/#futures_ordergetById
2266
+ :see: https://doc.xt.com/#futures_entrustgetPlanById
2267
+ :see: https://doc.xt.com/#futures_entrustgetProfitById
2268
+ :param str id: order id
2269
+ :param str [symbol]: unified symbol of the market the order was made in
2270
+ :param dict params: extra parameters specific to the xt api endpoint
2271
+ :param bool [params.stop]: if the order is a stop trigger order or not
2272
+ :param bool [params.stopLossTakeProfit]: if the order is a stop-loss or take-profit order
2273
+ :returns dict: An `order structure <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
2274
+ """
2275
+ await self.load_markets()
2276
+ market = None
2277
+ if symbol is not None:
2278
+ market = self.market(symbol)
2279
+ request = {}
2280
+ type = None
2281
+ subType = None
2282
+ response = None
2283
+ type, params = self.handle_market_type_and_params('fetchOrder', market, params)
2284
+ subType, params = self.handle_sub_type_and_params('fetchOrder', market, params)
2285
+ stop = self.safe_value(params, 'stop')
2286
+ stopLossTakeProfit = self.safe_value(params, 'stopLossTakeProfit')
2287
+ if stop:
2288
+ request['entrustId'] = id
2289
+ elif stopLossTakeProfit:
2290
+ request['profitId'] = id
2291
+ else:
2292
+ request['orderId'] = id
2293
+ if stop:
2294
+ params = self.omit(params, 'stop')
2295
+ if subType == 'inverse':
2296
+ response = await self.privateInverseGetFutureTradeV1EntrustPlanDetail(self.extend(request, params))
2297
+ else:
2298
+ response = await self.privateLinearGetFutureTradeV1EntrustPlanDetail(self.extend(request, params))
2299
+ elif stopLossTakeProfit:
2300
+ params = self.omit(params, 'stopLossTakeProfit')
2301
+ if subType == 'inverse':
2302
+ response = await self.privateInverseGetFutureTradeV1EntrustProfitDetail(self.extend(request, params))
2303
+ else:
2304
+ response = await self.privateLinearGetFutureTradeV1EntrustProfitDetail(self.extend(request, params))
2305
+ elif subType == 'inverse':
2306
+ response = await self.privateInverseGetFutureTradeV1OrderDetail(self.extend(request, params))
2307
+ elif (subType == 'linear') or (type == 'swap') or (type == 'future'):
2308
+ response = await self.privateLinearGetFutureTradeV1OrderDetail(self.extend(request, params))
2309
+ else:
2310
+ response = await self.privateSpotGetOrderOrderId(self.extend(request, params))
2311
+ #
2312
+ # spot
2313
+ #
2314
+ # {
2315
+ # "rc": 0,
2316
+ # "mc": "SUCCESS",
2317
+ # "ma": [],
2318
+ # "result": {
2319
+ # "symbol": "btc_usdt",
2320
+ # "orderId": "207505997850909952",
2321
+ # "clientOrderId": null,
2322
+ # "baseCurrency": "btc",
2323
+ # "quoteCurrency": "usdt",
2324
+ # "side": "BUY",
2325
+ # "type": "LIMIT",
2326
+ # "timeInForce": "GTC",
2327
+ # "price": "20000.00",
2328
+ # "origQty": "0.001000",
2329
+ # "origQuoteQty": "20.00",
2330
+ # "executedQty": "0.000000",
2331
+ # "leavingQty": "0.001000",
2332
+ # "tradeBase": "0.000000",
2333
+ # "tradeQuote": "0.00",
2334
+ # "avgPrice": null,
2335
+ # "fee": null,
2336
+ # "feeCurrency": null,
2337
+ # "closed": False,
2338
+ # "state": "NEW",
2339
+ # "time": 1679175285162,
2340
+ # "updatedTime": 1679175285255
2341
+ # }
2342
+ # }
2343
+ #
2344
+ # swap and future
2345
+ #
2346
+ # {
2347
+ # "returnCode": 0,
2348
+ # "msgInfo": "success",
2349
+ # "error": null,
2350
+ # "result": {
2351
+ # "orderId": "211451874783183936",
2352
+ # "clientOrderId": null,
2353
+ # "symbol": "btc_usdt",
2354
+ # "orderType": "LIMIT",
2355
+ # "orderSide": "BUY",
2356
+ # "positionSide": "LONG",
2357
+ # "timeInForce": "GTC",
2358
+ # "closePosition": False,
2359
+ # "price": "20000",
2360
+ # "origQty": "10",
2361
+ # "avgPrice": "0",
2362
+ # "executedQty": "0",
2363
+ # "marginFrozen": "1.34533334",
2364
+ # "remark": null,
2365
+ # "triggerProfitPrice": null,
2366
+ # "triggerStopPrice": null,
2367
+ # "sourceId": null,
2368
+ # "sourceType": "DEFAULT",
2369
+ # "forceClose": False,
2370
+ # "closeProfit": null,
2371
+ # "state": "NEW",
2372
+ # "createdTime": 1680116055693,
2373
+ # "updatedTime": 1680116055693
2374
+ # }
2375
+ # }
2376
+ #
2377
+ # trigger
2378
+ #
2379
+ # {
2380
+ # "returnCode": 0,
2381
+ # "msgInfo": "success",
2382
+ # "error": null,
2383
+ # "result": {
2384
+ # "entrustId": "216300248132756992",
2385
+ # "symbol": "btc_usdt",
2386
+ # "entrustType": "STOP",
2387
+ # "orderSide": "SELL",
2388
+ # "positionSide": "SHORT",
2389
+ # "timeInForce": "GTC",
2390
+ # "closePosition": null,
2391
+ # "price": "20000",
2392
+ # "origQty": "1",
2393
+ # "stopPrice": "19000",
2394
+ # "triggerPriceType": "LATEST_PRICE",
2395
+ # "state": "NOT_TRIGGERED",
2396
+ # "marketOrderLevel": null,
2397
+ # "createdTime": 1681271998064,
2398
+ # "updatedTime": 1681271998064,
2399
+ # "ordinary": False
2400
+ # }
2401
+ # }
2402
+ #
2403
+ # stop-loss and take-profit
2404
+ #
2405
+ # {
2406
+ # "returnCode": 0,
2407
+ # "msgInfo": "success",
2408
+ # "error": null,
2409
+ # "result": {
2410
+ # "profitId": "216306213226230400",
2411
+ # "symbol": "btc_usdt",
2412
+ # "positionSide": "LONG",
2413
+ # "origQty": "1",
2414
+ # "triggerPriceType": "LATEST_PRICE",
2415
+ # "triggerProfitPrice": null,
2416
+ # "triggerStopPrice": "20000",
2417
+ # "entryPrice": null,
2418
+ # "positionSize": null,
2419
+ # "isolatedMargin": null,
2420
+ # "executedQty": null,
2421
+ # "avgPrice": null,
2422
+ # "positionType": "ISOLATED",
2423
+ # "state": "NOT_TRIGGERED",
2424
+ # "createdTime": 1681273420039
2425
+ # }
2426
+ # }
2427
+ #
2428
+ order = self.safe_value(response, 'result', {})
2429
+ return self.parse_order(order, market)
2430
+
2431
+ async def fetch_orders(self, symbol: str = None, since: Int = None, limit: Int = None, params={}):
2432
+ """
2433
+ fetches information on multiple orders made by the user
2434
+ :see: https://doc.xt.com/#orderhistoryOrderGet
2435
+ :see: https://doc.xt.com/#futures_ordergetHistory
2436
+ :see: https://doc.xt.com/#futures_entrustgetPlanHistory
2437
+ :param str [symbol]: unified market symbol of the market the orders were made in
2438
+ :param int [since]: timestamp in ms of the earliest order
2439
+ :param int [limit]: the maximum number of order structures to retrieve
2440
+ :param dict params: extra parameters specific to the xt api endpoint
2441
+ :param bool [params.stop]: if the order is a stop trigger order or not
2442
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
2443
+ """
2444
+ await self.load_markets()
2445
+ request = {}
2446
+ market = None
2447
+ if symbol is not None:
2448
+ market = self.market(symbol)
2449
+ request['symbol'] = market['id']
2450
+ if since is not None:
2451
+ request['startTime'] = since
2452
+ if limit is not None:
2453
+ request['limit'] = limit
2454
+ type = None
2455
+ subType = None
2456
+ response = None
2457
+ type, params = self.handle_market_type_and_params('fetchOrders', market, params)
2458
+ subType, params = self.handle_sub_type_and_params('fetchOrders', market, params)
2459
+ stop = self.safe_value(params, 'stop')
2460
+ if stop:
2461
+ params = self.omit(params, 'stop')
2462
+ if subType == 'inverse':
2463
+ response = await self.privateInverseGetFutureTradeV1EntrustPlanListHistory(self.extend(request, params))
2464
+ else:
2465
+ response = await self.privateLinearGetFutureTradeV1EntrustPlanListHistory(self.extend(request, params))
2466
+ elif subType == 'inverse':
2467
+ response = await self.privateInverseGetFutureTradeV1OrderListHistory(self.extend(request, params))
2468
+ elif (subType == 'linear') or (type == 'swap') or (type == 'future'):
2469
+ response = await self.privateLinearGetFutureTradeV1OrderListHistory(self.extend(request, params))
2470
+ else:
2471
+ marginMode = None
2472
+ marginMode, params = self.handle_margin_mode_and_params('fetchOrders', params)
2473
+ marginOrSpotRequest = 'LEVER' if (marginMode is not None) else 'SPOT'
2474
+ request['bizType'] = marginOrSpotRequest
2475
+ response = await self.privateSpotGetHistoryOrder(self.extend(request, params))
2476
+ #
2477
+ # spot and margin
2478
+ #
2479
+ # {
2480
+ # "rc": 0,
2481
+ # "mc": "SUCCESS",
2482
+ # "ma": [],
2483
+ # "result": {
2484
+ # "hasPrev": False,
2485
+ # "hasNext": True,
2486
+ # "items": [
2487
+ # {
2488
+ # "symbol": "btc_usdt",
2489
+ # "orderId": "207505997850909952",
2490
+ # "clientOrderId": null,
2491
+ # "baseCurrency": "btc",
2492
+ # "quoteCurrency": "usdt",
2493
+ # "side": "BUY",
2494
+ # "type": "LIMIT",
2495
+ # "timeInForce": "GTC",
2496
+ # "price": "20000.00",
2497
+ # "origQty": "0.001000",
2498
+ # "origQuoteQty": "20.00",
2499
+ # "executedQty": "0.000000",
2500
+ # "leavingQty": "0.000000",
2501
+ # "tradeBase": "0.000000",
2502
+ # "tradeQuote": "0.00",
2503
+ # "avgPrice": null,
2504
+ # "fee": null,
2505
+ # "feeCurrency": null,
2506
+ # "closed": True,
2507
+ # "state": "CANCELED",
2508
+ # "time": 1679175285162,
2509
+ # "updatedTime": 1679175488492
2510
+ # },
2511
+ # ]
2512
+ # }
2513
+ # }
2514
+ #
2515
+ # swap and future
2516
+ #
2517
+ # {
2518
+ # "returnCode": 0,
2519
+ # "msgInfo": "success",
2520
+ # "error": null,
2521
+ # "result": {
2522
+ # "hasPrev": False,
2523
+ # "hasNext": True,
2524
+ # "items": [
2525
+ # {
2526
+ # "orderId": "207519546930995456",
2527
+ # "clientOrderId": null,
2528
+ # "symbol": "btc_usdt",
2529
+ # "orderType": "LIMIT",
2530
+ # "orderSide": "BUY",
2531
+ # "positionSide": "LONG",
2532
+ # "timeInForce": "GTC",
2533
+ # "closePosition": False,
2534
+ # "price": "20000",
2535
+ # "origQty": "100",
2536
+ # "avgPrice": "0",
2537
+ # "executedQty": "0",
2538
+ # "marginFrozen": "4.12",
2539
+ # "remark": null,
2540
+ # "triggerProfitPrice": null,
2541
+ # "triggerStopPrice": null,
2542
+ # "sourceId": null,
2543
+ # "sourceType": "DEFAULT",
2544
+ # "forceClose": False,
2545
+ # "closeProfit": null,
2546
+ # "state": "CANCELED",
2547
+ # "createdTime": 1679178515689,
2548
+ # "updatedTime": 1679180096172
2549
+ # },
2550
+ # ]
2551
+ # }
2552
+ # }
2553
+ #
2554
+ # stop
2555
+ #
2556
+ # {
2557
+ # "returnCode": 0,
2558
+ # "msgInfo": "success",
2559
+ # "error": null,
2560
+ # "result": {
2561
+ # "hasPrev": False,
2562
+ # "hasNext": False,
2563
+ # "items": [
2564
+ # {
2565
+ # "entrustId": "216300248132756992",
2566
+ # "symbol": "btc_usdt",
2567
+ # "entrustType": "STOP",
2568
+ # "orderSide": "SELL",
2569
+ # "positionSide": "SHORT",
2570
+ # "timeInForce": "GTC",
2571
+ # "closePosition": null,
2572
+ # "price": "20000",
2573
+ # "origQty": "1",
2574
+ # "stopPrice": "19000",
2575
+ # "triggerPriceType": "LATEST_PRICE",
2576
+ # "state": "USER_REVOCATION",
2577
+ # "marketOrderLevel": null,
2578
+ # "createdTime": 1681271998064,
2579
+ # "updatedTime": 1681273188674,
2580
+ # "ordinary": False
2581
+ # },
2582
+ # ]
2583
+ # }
2584
+ # }
2585
+ #
2586
+ data = self.safe_value(response, 'result', {})
2587
+ orders = self.safe_value(data, 'items', [])
2588
+ return self.parse_orders(orders, market, since, limit)
2589
+
2590
+ async def fetch_orders_by_status(self, status, symbol: str = None, since: Int = None, limit: Int = None, params={}):
2591
+ await self.load_markets()
2592
+ request = {}
2593
+ market = None
2594
+ if symbol is not None:
2595
+ market = self.market(symbol)
2596
+ request['symbol'] = market['id']
2597
+ type = None
2598
+ subType = None
2599
+ response = None
2600
+ type, params = self.handle_market_type_and_params('fetchOrdersByStatus', market, params)
2601
+ subType, params = self.handle_sub_type_and_params('fetchOrdersByStatus', market, params)
2602
+ stop = self.safe_value(params, 'stop')
2603
+ stopLossTakeProfit = self.safe_value(params, 'stopLossTakeProfit')
2604
+ if status == 'open':
2605
+ if stop or stopLossTakeProfit:
2606
+ request['state'] = 'NOT_TRIGGERED'
2607
+ elif subType is not None:
2608
+ request['state'] = 'NEW'
2609
+ elif status == 'closed':
2610
+ if stop or stopLossTakeProfit:
2611
+ request['state'] = 'TRIGGERED'
2612
+ else:
2613
+ request['state'] = 'FILLED'
2614
+ elif status == 'canceled':
2615
+ if stop or stopLossTakeProfit:
2616
+ request['state'] = 'USER_REVOCATION'
2617
+ else:
2618
+ request['state'] = 'CANCELED'
2619
+ else:
2620
+ request['state'] = status
2621
+ if stop or stopLossTakeProfit or (subType is not None) or (type == 'swap') or (type == 'future'):
2622
+ if since is not None:
2623
+ request['startTime'] = since
2624
+ if limit is not None:
2625
+ request['size'] = limit
2626
+ if stop:
2627
+ params = self.omit(params, 'stop')
2628
+ if subType == 'inverse':
2629
+ response = await self.privateInverseGetFutureTradeV1EntrustPlanList(self.extend(request, params))
2630
+ else:
2631
+ response = await self.privateLinearGetFutureTradeV1EntrustPlanList(self.extend(request, params))
2632
+ elif stopLossTakeProfit:
2633
+ params = self.omit(params, 'stopLossTakeProfit')
2634
+ if subType == 'inverse':
2635
+ response = await self.privateInverseGetFutureTradeV1EntrustProfitList(self.extend(request, params))
2636
+ else:
2637
+ response = await self.privateLinearGetFutureTradeV1EntrustProfitList(self.extend(request, params))
2638
+ elif (subType is not None) or (type == 'swap') or (type == 'future'):
2639
+ if subType == 'inverse':
2640
+ response = await self.privateInverseGetFutureTradeV1OrderList(self.extend(request, params))
2641
+ else:
2642
+ response = await self.privateLinearGetFutureTradeV1OrderList(self.extend(request, params))
2643
+ else:
2644
+ marginMode = None
2645
+ marginMode, params = self.handle_margin_mode_and_params('fetchOrdersByStatus', params)
2646
+ marginOrSpotRequest = 'LEVER' if (marginMode is not None) else 'SPOT'
2647
+ request['bizType'] = marginOrSpotRequest
2648
+ if status != 'open':
2649
+ if since is not None:
2650
+ request['startTime'] = since
2651
+ if limit is not None:
2652
+ request['limit'] = limit
2653
+ response = await self.privateSpotGetHistoryOrder(self.extend(request, params))
2654
+ else:
2655
+ response = await self.privateSpotGetOpenOrder(self.extend(request, params))
2656
+ #
2657
+ # spot and margin
2658
+ #
2659
+ # {
2660
+ # "rc": 0,
2661
+ # "mc": "SUCCESS",
2662
+ # "ma": [],
2663
+ # "result": {
2664
+ # "hasPrev": False,
2665
+ # "hasNext": True,
2666
+ # "items": [
2667
+ # {
2668
+ # "symbol": "btc_usdt",
2669
+ # "orderId": "207505997850909952",
2670
+ # "clientOrderId": null,
2671
+ # "baseCurrency": "btc",
2672
+ # "quoteCurrency": "usdt",
2673
+ # "side": "BUY",
2674
+ # "type": "LIMIT",
2675
+ # "timeInForce": "GTC",
2676
+ # "price": "20000.00",
2677
+ # "origQty": "0.001000",
2678
+ # "origQuoteQty": "20.00",
2679
+ # "executedQty": "0.000000",
2680
+ # "leavingQty": "0.000000",
2681
+ # "tradeBase": "0.000000",
2682
+ # "tradeQuote": "0.00",
2683
+ # "avgPrice": null,
2684
+ # "fee": null,
2685
+ # "feeCurrency": null,
2686
+ # "closed": True,
2687
+ # "state": "CANCELED",
2688
+ # "time": 1679175285162,
2689
+ # "updatedTime": 1679175488492
2690
+ # },
2691
+ # ]
2692
+ # }
2693
+ # }
2694
+ #
2695
+ # spot and margin: fetchOpenOrders
2696
+ #
2697
+ # {
2698
+ # "rc": 0,
2699
+ # "mc": "SUCCESS",
2700
+ # "ma": [],
2701
+ # "result": [
2702
+ # {
2703
+ # "symbol": "eth_usdt",
2704
+ # "orderId": "208249323222264320",
2705
+ # "clientOrderId": null,
2706
+ # "baseCurrency": "eth",
2707
+ # "quoteCurrency": "usdt",
2708
+ # "side": "BUY",
2709
+ # "type": "LIMIT",
2710
+ # "timeInForce": "GTC",
2711
+ # "price": "1300.00",
2712
+ # "origQty": "0.0032",
2713
+ # "origQuoteQty": "4.16",
2714
+ # "executedQty": "0.0000",
2715
+ # "leavingQty": "0.0032",
2716
+ # "tradeBase": "0.0000",
2717
+ # "tradeQuote": "0.00",
2718
+ # "avgPrice": null,
2719
+ # "fee": null,
2720
+ # "feeCurrency": null,
2721
+ # "closed": False,
2722
+ # "state": "NEW",
2723
+ # "time": 1679352507741,
2724
+ # "updatedTime": 1679352507869
2725
+ # },
2726
+ # ]
2727
+ # }
2728
+ #
2729
+ # swap and future
2730
+ #
2731
+ # {
2732
+ # "returnCode": 0,
2733
+ # "msgInfo": "success",
2734
+ # "error": null,
2735
+ # "result": {
2736
+ # "page": 1,
2737
+ # "ps": 10,
2738
+ # "total": 25,
2739
+ # "items": [
2740
+ # {
2741
+ # "orderId": "207519546930995456",
2742
+ # "clientOrderId": null,
2743
+ # "symbol": "btc_usdt",
2744
+ # "orderType": "LIMIT",
2745
+ # "orderSide": "BUY",
2746
+ # "positionSide": "LONG",
2747
+ # "timeInForce": "GTC",
2748
+ # "closePosition": False,
2749
+ # "price": "20000",
2750
+ # "origQty": "100",
2751
+ # "avgPrice": "0",
2752
+ # "executedQty": "0",
2753
+ # "marginFrozen": "4.12",
2754
+ # "remark": null,
2755
+ # "triggerProfitPrice": null,
2756
+ # "triggerStopPrice": null,
2757
+ # "sourceId": null,
2758
+ # "sourceType": "DEFAULT",
2759
+ # "forceClose": False,
2760
+ # "closeProfit": null,
2761
+ # "state": "CANCELED",
2762
+ # "createdTime": 1679178515689,
2763
+ # "updatedTime": 1679180096172
2764
+ # },
2765
+ # ]
2766
+ # }
2767
+ # }
2768
+ #
2769
+ # stop
2770
+ #
2771
+ # {
2772
+ # "returnCode": 0,
2773
+ # "msgInfo": "success",
2774
+ # "error": null,
2775
+ # "result": {
2776
+ # "page": 1,
2777
+ # "ps": 3,
2778
+ # "total": 8,
2779
+ # "items": [
2780
+ # {
2781
+ # "entrustId": "216300248132756992",
2782
+ # "symbol": "btc_usdt",
2783
+ # "entrustType": "STOP",
2784
+ # "orderSide": "SELL",
2785
+ # "positionSide": "SHORT",
2786
+ # "timeInForce": "GTC",
2787
+ # "closePosition": null,
2788
+ # "price": "20000",
2789
+ # "origQty": "1",
2790
+ # "stopPrice": "19000",
2791
+ # "triggerPriceType": "LATEST_PRICE",
2792
+ # "state": "USER_REVOCATION",
2793
+ # "marketOrderLevel": null,
2794
+ # "createdTime": 1681271998064,
2795
+ # "updatedTime": 1681273188674,
2796
+ # "ordinary": False
2797
+ # },
2798
+ # ]
2799
+ # }
2800
+ # }
2801
+ #
2802
+ # stop-loss and take-profit
2803
+ #
2804
+ # {
2805
+ # "returnCode": 0,
2806
+ # "msgInfo": "success",
2807
+ # "error": null,
2808
+ # "result": {
2809
+ # "page": 1,
2810
+ # "ps": 3,
2811
+ # "total": 2,
2812
+ # "items": [
2813
+ # {
2814
+ # "profitId": "216306213226230400",
2815
+ # "symbol": "btc_usdt",
2816
+ # "positionSide": "LONG",
2817
+ # "origQty": "1",
2818
+ # "triggerPriceType": "LATEST_PRICE",
2819
+ # "triggerProfitPrice": null,
2820
+ # "triggerStopPrice": "20000",
2821
+ # "entryPrice": "0",
2822
+ # "positionSize": "0",
2823
+ # "isolatedMargin": "0",
2824
+ # "executedQty": "0",
2825
+ # "avgPrice": null,
2826
+ # "positionType": "ISOLATED",
2827
+ # "state": "USER_REVOCATION",
2828
+ # "createdTime": 1681273420039
2829
+ # },
2830
+ # ]
2831
+ # }
2832
+ # }
2833
+ #
2834
+ isSpotOpenOrders = ((status == 'open') and (subType is None))
2835
+ data = self.safe_value(response, 'result', {})
2836
+ orders = self.safe_value(response, 'result', []) if isSpotOpenOrders else self.safe_value(data, 'items', [])
2837
+ return self.parse_orders(orders, market, since, limit)
2838
+
2839
+ async def fetch_open_orders(self, symbol: str = None, since: Int = None, limit: Int = None, params={}):
2840
+ """
2841
+ fetch all unfilled currently open orders
2842
+ :see: https://doc.xt.com/#orderopenOrderGet
2843
+ :see: https://doc.xt.com/#futures_ordergetOrders
2844
+ :see: https://doc.xt.com/#futures_entrustgetPlan
2845
+ :see: https://doc.xt.com/#futures_entrustgetProfit
2846
+ :param str [symbol]: unified market symbol of the market the orders were made in
2847
+ :param int [since]: timestamp in ms of the earliest order
2848
+ :param int [limit]: the maximum number of open order structures to retrieve
2849
+ :param dict params: extra parameters specific to the xt api endpoint
2850
+ :param bool [params.stop]: if the order is a stop trigger order or not
2851
+ :param bool [params.stopLossTakeProfit]: if the order is a stop-loss or take-profit order
2852
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
2853
+ """
2854
+ return await self.fetch_orders_by_status('open', symbol, since, limit, params)
2855
+
2856
+ async def fetch_closed_orders(self, symbol: str = None, since: Int = None, limit: Int = None, params={}):
2857
+ """
2858
+ fetches information on multiple closed orders made by the user
2859
+ :see: https://doc.xt.com/#orderhistoryOrderGet
2860
+ :see: https://doc.xt.com/#futures_ordergetOrders
2861
+ :see: https://doc.xt.com/#futures_entrustgetPlan
2862
+ :see: https://doc.xt.com/#futures_entrustgetProfit
2863
+ :param str [symbol]: unified market symbol of the market the orders were made in
2864
+ :param int [since]: timestamp in ms of the earliest order
2865
+ :param int [limit]: the maximum number of order structures to retrieve
2866
+ :param dict params: extra parameters specific to the xt api endpoint
2867
+ :param bool [params.stop]: if the order is a stop trigger order or not
2868
+ :param bool [params.stopLossTakeProfit]: if the order is a stop-loss or take-profit order
2869
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
2870
+ """
2871
+ return await self.fetch_orders_by_status('closed', symbol, since, limit, params)
2872
+
2873
+ async def fetch_canceled_orders(self, symbol: str = None, since: Int = None, limit: Int = None, params={}):
2874
+ """
2875
+ fetches information on multiple canceled orders made by the user
2876
+ :see: https://doc.xt.com/#orderhistoryOrderGet
2877
+ :see: https://doc.xt.com/#futures_ordergetOrders
2878
+ :see: https://doc.xt.com/#futures_entrustgetPlan
2879
+ :see: https://doc.xt.com/#futures_entrustgetProfit
2880
+ :param str [symbol]: unified market symbol of the market the orders were made in
2881
+ :param int [since]: timestamp in ms of the earliest order
2882
+ :param int [limit]: the maximum number of order structures to retrieve
2883
+ :param dict params: extra parameters specific to the xt api endpoint
2884
+ :param bool [params.stop]: if the order is a stop trigger order or not
2885
+ :param bool [params.stopLossTakeProfit]: if the order is a stop-loss or take-profit order
2886
+ :returns dict: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
2887
+ """
2888
+ return await self.fetch_orders_by_status('canceled', symbol, since, limit, params)
2889
+
2890
+ async def cancel_order(self, id: str, symbol: str = None, params={}):
2891
+ """
2892
+ cancels an open order
2893
+ :see: https://doc.xt.com/#orderorderDel
2894
+ :see: https://doc.xt.com/#futures_ordercancel
2895
+ :see: https://doc.xt.com/#futures_entrustcancelPlan
2896
+ :see: https://doc.xt.com/#futures_entrustcancelProfit
2897
+ :param str id: order id
2898
+ :param str [symbol]: unified symbol of the market the order was made in
2899
+ :param dict params: extra parameters specific to the xt api endpoint
2900
+ :param bool [params.stop]: if the order is a stop trigger order or not
2901
+ :param bool [params.stopLossTakeProfit]: if the order is a stop-loss or take-profit order
2902
+ :returns dict: An `order structure <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
2903
+ """
2904
+ await self.load_markets()
2905
+ market = None
2906
+ if symbol is not None:
2907
+ market = self.market(symbol)
2908
+ request = {}
2909
+ type = None
2910
+ subType = None
2911
+ response = None
2912
+ type, params = self.handle_market_type_and_params('cancelOrder', market, params)
2913
+ subType, params = self.handle_sub_type_and_params('cancelOrder', market, params)
2914
+ stop = self.safe_value(params, 'stop')
2915
+ stopLossTakeProfit = self.safe_value(params, 'stopLossTakeProfit')
2916
+ if stop:
2917
+ request['entrustId'] = id
2918
+ elif stopLossTakeProfit:
2919
+ request['profitId'] = id
2920
+ else:
2921
+ request['orderId'] = id
2922
+ if stop:
2923
+ params = self.omit(params, 'stop')
2924
+ if subType == 'inverse':
2925
+ response = await self.privateInversePostFutureTradeV1EntrustCancelPlan(self.extend(request, params))
2926
+ else:
2927
+ response = await self.privateLinearPostFutureTradeV1EntrustCancelPlan(self.extend(request, params))
2928
+ elif stopLossTakeProfit:
2929
+ params = self.omit(params, 'stopLossTakeProfit')
2930
+ if subType == 'inverse':
2931
+ response = await self.privateInversePostFutureTradeV1EntrustCancelProfitStop(self.extend(request, params))
2932
+ else:
2933
+ response = await self.privateLinearPostFutureTradeV1EntrustCancelProfitStop(self.extend(request, params))
2934
+ elif subType == 'inverse':
2935
+ response = await self.privateInversePostFutureTradeV1OrderCancel(self.extend(request, params))
2936
+ elif (subType == 'linear') or (type == 'swap') or (type == 'future'):
2937
+ response = await self.privateLinearPostFutureTradeV1OrderCancel(self.extend(request, params))
2938
+ else:
2939
+ response = await self.privateSpotDeleteOrderOrderId(self.extend(request, params))
2940
+ #
2941
+ # spot
2942
+ #
2943
+ # {
2944
+ # "rc": 0,
2945
+ # "mc": "SUCCESS",
2946
+ # "ma": [],
2947
+ # "result": {
2948
+ # "cancelId": "208322474307982720"
2949
+ # }
2950
+ # }
2951
+ #
2952
+ # swap and future
2953
+ #
2954
+ # {
2955
+ # "returnCode": 0,
2956
+ # "msgInfo": "success",
2957
+ # "error": null,
2958
+ # "result": "208319789679471616"
2959
+ # }
2960
+ #
2961
+ isContractResponse = ((subType is not None) or (type == 'swap') or (type == 'future'))
2962
+ order = response if isContractResponse else self.safe_value(response, 'result', {})
2963
+ return self.parse_order(order, market)
2964
+
2965
+ async def cancel_all_orders(self, symbol: str = None, params={}):
2966
+ """
2967
+ cancel all open orders in a market
2968
+ :see: https://doc.xt.com/#orderopenOrderDel
2969
+ :see: https://doc.xt.com/#futures_ordercancelBatch
2970
+ :see: https://doc.xt.com/#futures_entrustcancelPlanBatch
2971
+ :see: https://doc.xt.com/#futures_entrustcancelProfitBatch
2972
+ :param str [symbol]: unified market symbol of the market to cancel orders in
2973
+ :param dict params: extra parameters specific to the xt api endpoint
2974
+ :param bool [params.stop]: if the order is a stop trigger order or not
2975
+ :param bool [params.stopLossTakeProfit]: if the order is a stop-loss or take-profit order
2976
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
2977
+ """
2978
+ await self.load_markets()
2979
+ request = {}
2980
+ market = None
2981
+ if symbol is not None:
2982
+ market = self.market(symbol)
2983
+ request['symbol'] = market['id']
2984
+ type = None
2985
+ subType = None
2986
+ response = None
2987
+ type, params = self.handle_market_type_and_params('cancelAllOrders', market, params)
2988
+ subType, params = self.handle_sub_type_and_params('cancelAllOrders', market, params)
2989
+ stop = self.safe_value(params, 'stop')
2990
+ stopLossTakeProfit = self.safe_value(params, 'stopLossTakeProfit')
2991
+ if stop:
2992
+ params = self.omit(params, 'stop')
2993
+ if subType == 'inverse':
2994
+ response = await self.privateInversePostFutureTradeV1EntrustCancelAllPlan(self.extend(request, params))
2995
+ else:
2996
+ response = await self.privateLinearPostFutureTradeV1EntrustCancelAllPlan(self.extend(request, params))
2997
+ elif stopLossTakeProfit:
2998
+ params = self.omit(params, 'stopLossTakeProfit')
2999
+ if subType == 'inverse':
3000
+ response = await self.privateInversePostFutureTradeV1EntrustCancelAllProfitStop(self.extend(request, params))
3001
+ else:
3002
+ response = await self.privateLinearPostFutureTradeV1EntrustCancelAllProfitStop(self.extend(request, params))
3003
+ elif subType == 'inverse':
3004
+ response = await self.privateInversePostFutureTradeV1OrderCancelAll(self.extend(request, params))
3005
+ elif (subType == 'linear') or (type == 'swap') or (type == 'future'):
3006
+ response = await self.privateLinearPostFutureTradeV1OrderCancelAll(self.extend(request, params))
3007
+ else:
3008
+ marginMode = None
3009
+ marginMode, params = self.handle_margin_mode_and_params('cancelAllOrders', params)
3010
+ marginOrSpotRequest = 'LEVER' if (marginMode is not None) else 'SPOT'
3011
+ request['bizType'] = marginOrSpotRequest
3012
+ response = await self.privateSpotDeleteOpenOrder(self.extend(request, params))
3013
+ #
3014
+ # spot and margin
3015
+ #
3016
+ # {
3017
+ # "rc": 0,
3018
+ # "mc": "SUCCESS",
3019
+ # "ma": [],
3020
+ # "result": null
3021
+ # }
3022
+ #
3023
+ # swap and future
3024
+ #
3025
+ # {
3026
+ # "returnCode": 0,
3027
+ # "msgInfo": "success",
3028
+ # "error": null,
3029
+ # "result": True
3030
+ # }
3031
+ #
3032
+ return [
3033
+ self.safe_order(response),
3034
+ ]
3035
+
3036
+ async def cancel_orders(self, ids: List[str], symbol: str = None, params={}) -> List[Order]:
3037
+ """
3038
+ cancel multiple orders
3039
+ :see: https://doc.xt.com/#orderbatchOrderDel
3040
+ :param str[] ids: order ids
3041
+ :param str [symbol]: unified market symbol of the market to cancel orders in
3042
+ :param dict params: extra parameters specific to the xt api endpoint
3043
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
3044
+ """
3045
+ await self.load_markets()
3046
+ request = {
3047
+ 'orderIds': ids,
3048
+ }
3049
+ market = None
3050
+ if symbol is not None:
3051
+ market = self.market(symbol)
3052
+ subType = None
3053
+ subType, params = self.handle_sub_type_and_params('cancelOrders', market, params)
3054
+ if subType is not None:
3055
+ raise NotSupported(self.id + ' cancelOrders() does not support swap and future orders, only spot orders are accepted')
3056
+ response = await self.privateSpotDeleteBatchOrder(self.extend(request, params))
3057
+ #
3058
+ # spot
3059
+ #
3060
+ # {
3061
+ # "rc": 0,
3062
+ # "mc": "SUCCESS",
3063
+ # "ma": [],
3064
+ # "result": null
3065
+ # }
3066
+ #
3067
+ return [
3068
+ self.safe_order(response),
3069
+ ]
3070
+
3071
+ def parse_order(self, order, market=None):
3072
+ #
3073
+ # spot: createOrder
3074
+ #
3075
+ # {
3076
+ # "orderId": "204371980095156544"
3077
+ # }
3078
+ #
3079
+ # spot: cancelOrder
3080
+ #
3081
+ # {
3082
+ # "cancelId": "208322474307982720"
3083
+ # }
3084
+ #
3085
+ # swap and future: createOrder, cancelOrder
3086
+ #
3087
+ # {
3088
+ # "returnCode": 0,
3089
+ # "msgInfo": "success",
3090
+ # "error": null,
3091
+ # "result": "206410760006650176"
3092
+ # }
3093
+ #
3094
+ # spot: fetchOrder, fetchOrders, fetchOpenOrders, fetchClosedOrders, fetchCanceledOrders, fetchOrdersByStatus
3095
+ #
3096
+ # {
3097
+ # "symbol": "btc_usdt",
3098
+ # "orderId": "207505997850909952",
3099
+ # "clientOrderId": null,
3100
+ # "baseCurrency": "btc",
3101
+ # "quoteCurrency": "usdt",
3102
+ # "side": "BUY",
3103
+ # "type": "LIMIT",
3104
+ # "timeInForce": "GTC",
3105
+ # "price": "20000.00",
3106
+ # "origQty": "0.001000",
3107
+ # "origQuoteQty": "20.00",
3108
+ # "executedQty": "0.000000",
3109
+ # "leavingQty": "0.001000",
3110
+ # "tradeBase": "0.000000",
3111
+ # "tradeQuote": "0.00",
3112
+ # "avgPrice": null,
3113
+ # "fee": null,
3114
+ # "feeCurrency": null,
3115
+ # "closed": False,
3116
+ # "state": "NEW",
3117
+ # "time": 1679175285162,
3118
+ # "updatedTime": 1679175285255
3119
+ # }
3120
+ #
3121
+ # swap and future: fetchOrder, fetchOrders, fetchOpenOrders, fetchClosedOrders, fetchCanceledOrders, fetchOrdersByStatus
3122
+ #
3123
+ # {
3124
+ # "orderId": "207519546930995456",
3125
+ # "clientOrderId": null,
3126
+ # "symbol": "btc_usdt",
3127
+ # "orderType": "LIMIT",
3128
+ # "orderSide": "BUY",
3129
+ # "positionSide": "LONG",
3130
+ # "timeInForce": "GTC",
3131
+ # "closePosition": False,
3132
+ # "price": "20000",
3133
+ # "origQty": "100",
3134
+ # "avgPrice": "0",
3135
+ # "executedQty": "0",
3136
+ # "marginFrozen": "4.12",
3137
+ # "remark": null,
3138
+ # "triggerProfitPrice": null,
3139
+ # "triggerStopPrice": null,
3140
+ # "sourceId": null,
3141
+ # "sourceType": "DEFAULT",
3142
+ # "forceClose": False,
3143
+ # "closeProfit": null,
3144
+ # "state": "CANCELED",
3145
+ # "createdTime": 1679178515689,
3146
+ # "updatedTime": 1679180096172
3147
+ # }
3148
+ #
3149
+ # trigger: fetchOrder, fetchOrders, fetchOpenOrders, fetchClosedOrders, fetchCanceledOrders, fetchOrdersByStatus
3150
+ #
3151
+ # {
3152
+ # "entrustId": "216300248132756992",
3153
+ # "symbol": "btc_usdt",
3154
+ # "entrustType": "STOP",
3155
+ # "orderSide": "SELL",
3156
+ # "positionSide": "SHORT",
3157
+ # "timeInForce": "GTC",
3158
+ # "closePosition": null,
3159
+ # "price": "20000",
3160
+ # "origQty": "1",
3161
+ # "stopPrice": "19000",
3162
+ # "triggerPriceType": "LATEST_PRICE",
3163
+ # "state": "NOT_TRIGGERED",
3164
+ # "marketOrderLevel": null,
3165
+ # "createdTime": 1681271998064,
3166
+ # "updatedTime": 1681271998064,
3167
+ # "ordinary": False
3168
+ # }
3169
+ #
3170
+ # stop-loss and take-profit: fetchOrder, fetchOpenOrders, fetchClosedOrders, fetchCanceledOrders, fetchOrdersByStatus
3171
+ #
3172
+ # {
3173
+ # "profitId": "216306213226230400",
3174
+ # "symbol": "btc_usdt",
3175
+ # "positionSide": "LONG",
3176
+ # "origQty": "1",
3177
+ # "triggerPriceType": "LATEST_PRICE",
3178
+ # "triggerProfitPrice": null,
3179
+ # "triggerStopPrice": "20000",
3180
+ # "entryPrice": null,
3181
+ # "positionSize": null,
3182
+ # "isolatedMargin": null,
3183
+ # "executedQty": null,
3184
+ # "avgPrice": null,
3185
+ # "positionType": "ISOLATED",
3186
+ # "state": "NOT_TRIGGERED",
3187
+ # "createdTime": 1681273420039
3188
+ # }
3189
+ #
3190
+ marketId = self.safe_string(order, 'symbol')
3191
+ marketType = ('result' in order) or 'contract' if ('positionSide' in order) else 'spot'
3192
+ market = self.safe_market(marketId, market, None, marketType)
3193
+ symbol = self.safe_symbol(marketId, market, None, marketType)
3194
+ timestamp = self.safe_integer_2(order, 'time', 'createdTime')
3195
+ quantity = self.safe_number(order, 'origQty')
3196
+ amount = quantity if (marketType == 'spot') else Precise.string_mul(self.number_to_string(quantity), self.number_to_string(market['contractSize']))
3197
+ filledQuantity = self.safe_number(order, 'executedQty')
3198
+ filled = filledQuantity if (marketType == 'spot') else Precise.string_mul(self.number_to_string(filledQuantity), self.number_to_string(market['contractSize']))
3199
+ lastUpdatedTimestamp = self.safe_integer(order, 'updatedTime')
3200
+ return self.safe_order({
3201
+ 'info': order,
3202
+ 'id': self.safe_string_n(order, ['orderId', 'result', 'cancelId', 'entrustId', 'profitId']),
3203
+ 'clientOrderId': self.safe_string(order, 'clientOrderId'),
3204
+ 'timestamp': timestamp,
3205
+ 'datetime': self.iso8601(timestamp),
3206
+ 'lastTradeTimestamp': lastUpdatedTimestamp,
3207
+ 'lastUpdateTimestamp': lastUpdatedTimestamp,
3208
+ 'symbol': symbol,
3209
+ 'type': self.safe_string_lower_2(order, 'type', 'orderType'),
3210
+ 'timeInForce': self.safe_string(order, 'timeInForce'),
3211
+ 'postOnly': None,
3212
+ 'side': self.safe_string_lower_2(order, 'side', 'orderSide'),
3213
+ 'price': self.safe_number(order, 'price'),
3214
+ 'stopPrice': self.safe_number(order, 'stopPrice'),
3215
+ 'stopLoss': self.safe_number(order, 'triggerStopPrice'),
3216
+ 'takeProfit': self.safe_number(order, 'triggerProfitPrice'),
3217
+ 'amount': amount,
3218
+ 'filled': filled,
3219
+ 'remaining': self.safe_number(order, 'leavingQty'),
3220
+ 'cost': None,
3221
+ 'average': self.safe_number(order, 'avgPrice'),
3222
+ 'status': self.parse_order_status(self.safe_string(order, 'state')),
3223
+ 'fee': {
3224
+ 'currency': self.safe_currency_code(self.safe_string(order, 'feeCurrency')),
3225
+ 'cost': self.safe_number(order, 'fee'),
3226
+ },
3227
+ 'trades': None,
3228
+ }, market)
3229
+
3230
+ def parse_order_status(self, status):
3231
+ statuses = {
3232
+ 'NEW': 'open',
3233
+ 'PARTIALLY_FILLED': 'open',
3234
+ 'FILLED': 'closed',
3235
+ 'CANCELED': 'canceled',
3236
+ 'REJECTED': 'rejected',
3237
+ 'EXPIRED': 'expired',
3238
+ 'UNFINISHED': 'open',
3239
+ 'NOT_TRIGGERED': 'open',
3240
+ 'TRIGGERING': 'open',
3241
+ 'TRIGGERED': 'closed',
3242
+ 'USER_REVOCATION': 'canceled',
3243
+ 'PLATFORM_REVOCATION': 'rejected',
3244
+ 'HISTORY': 'expired',
3245
+ }
3246
+ return self.safe_string(statuses, status, status)
3247
+
3248
+ async def fetch_ledger(self, code: str = None, since: Int = None, limit: Int = None, params={}):
3249
+ """
3250
+ fetch the history of changes, actions done by the user or operations that altered the balance of the user
3251
+ :see: https://doc.xt.com/#futures_usergetBalanceBill
3252
+ :param str [code]: unified currency code
3253
+ :param int [since]: timestamp in ms of the earliest ledger entry
3254
+ :param int [limit]: max number of ledger entries to return
3255
+ :param dict params: extra parameters specific to the xt api endpoint
3256
+ :returns dict: a `ledger structure <https://docs.ccxt.com/en/latest/manual.html#ledger-structure>`
3257
+ """
3258
+ await self.load_markets()
3259
+ request = {}
3260
+ currency = None
3261
+ if code is not None:
3262
+ currency = self.currency(code)
3263
+ if since is not None:
3264
+ request['startTime'] = since
3265
+ if limit is not None:
3266
+ request['limit'] = limit
3267
+ type = None
3268
+ subType = None
3269
+ response = None
3270
+ type, params = self.handle_market_type_and_params('fetchLedger', None, params)
3271
+ subType, params = self.handle_sub_type_and_params('fetchLedger', None, params)
3272
+ if subType == 'inverse':
3273
+ response = await self.privateInverseGetFutureUserV1BalanceBills(self.extend(request, params))
3274
+ elif (subType == 'linear') or (type == 'swap') or (type == 'future'):
3275
+ response = await self.privateLinearGetFutureUserV1BalanceBills(self.extend(request, params))
3276
+ else:
3277
+ raise NotSupported(self.id + ' fetchLedger() does not support spot transactions, only swap and future wallet transactions are supported')
3278
+ #
3279
+ # {
3280
+ # "returnCode": 0,
3281
+ # "msgInfo": "success",
3282
+ # "error": null,
3283
+ # "result": {
3284
+ # "hasPrev": False,
3285
+ # "hasNext": False,
3286
+ # "items": [
3287
+ # {
3288
+ # "id": "207260567109387524",
3289
+ # "coin": "usdt",
3290
+ # "symbol": "btc_usdt",
3291
+ # "type": "FEE",
3292
+ # "amount": "-0.0213",
3293
+ # "side": "SUB",
3294
+ # "afterAmount": null,
3295
+ # "createdTime": 1679116769914
3296
+ # },
3297
+ # ]
3298
+ # }
3299
+ # }
3300
+ #
3301
+ data = self.safe_value(response, 'result', {})
3302
+ ledger = self.safe_value(data, 'items', [])
3303
+ return self.parse_ledger(ledger, currency, since, limit)
3304
+
3305
+ def parse_ledger_entry(self, item, currency=None):
3306
+ #
3307
+ # {
3308
+ # "id": "207260567109387524",
3309
+ # "coin": "usdt",
3310
+ # "symbol": "btc_usdt",
3311
+ # "type": "FEE",
3312
+ # "amount": "-0.0213",
3313
+ # "side": "SUB",
3314
+ # "afterAmount": null,
3315
+ # "createdTime": 1679116769914
3316
+ # }
3317
+ #
3318
+ side = self.safe_string(item, 'side')
3319
+ direction = 'in' if (side == 'ADD') else 'out'
3320
+ currencyId = self.safe_string(item, 'coin')
3321
+ timestamp = self.safe_integer(item, 'createdTime')
3322
+ return {
3323
+ 'id': self.safe_string(item, 'id'),
3324
+ 'direction': direction,
3325
+ 'account': None,
3326
+ 'referenceId': None,
3327
+ 'referenceAccount': None,
3328
+ 'type': self.parse_ledger_entry_type(self.safe_string(item, 'type')),
3329
+ 'currency': self.safe_currency_code(currencyId, currency),
3330
+ 'amount': self.safe_number(item, 'amount'),
3331
+ 'timestamp': timestamp,
3332
+ 'datetime': self.iso8601(timestamp),
3333
+ 'before': None,
3334
+ 'after': self.safe_number(item, 'afterAmount'),
3335
+ 'status': None,
3336
+ 'fee': {
3337
+ 'currency': None,
3338
+ 'cost': None,
3339
+ },
3340
+ 'info': item,
3341
+ }
3342
+
3343
+ def parse_ledger_entry_type(self, type):
3344
+ ledgerType = {
3345
+ 'EXCHANGE': 'transfer',
3346
+ 'CLOSE_POSITION': 'trade',
3347
+ 'TAKE_OVER': 'trade',
3348
+ 'MERGE': 'trade',
3349
+ 'QIANG_PING_MANAGER': 'fee',
3350
+ 'FUND': 'fee',
3351
+ 'FEE': 'fee',
3352
+ 'ADL': 'auto-deleveraging',
3353
+ }
3354
+ return self.safe_string(ledgerType, type, type)
3355
+
3356
+ async def fetch_deposit_address(self, code: str, params={}):
3357
+ """
3358
+ fetch the deposit address for a currency associated with self account
3359
+ :see: https://doc.xt.com/#deposit_withdrawaldepositAddressGet
3360
+ :param str code: unified currency code
3361
+ :param dict params: extra parameters specific to the xt api endpoint
3362
+ :param str params['network']: required network id
3363
+ :returns dict: an `address structure <https://docs.ccxt.com/en/latest/manual.html#address-structure>`
3364
+ """
3365
+ await self.load_markets()
3366
+ networkCode = None
3367
+ networkCode, params = self.handle_network_code_and_params(params)
3368
+ currency = self.currency(code)
3369
+ networkId = self.network_code_to_id(networkCode, code)
3370
+ self.check_required_argument('fetchDepositAddress', networkId, 'network')
3371
+ request = {
3372
+ 'currency': currency['id'],
3373
+ 'chain': networkId,
3374
+ }
3375
+ response = await self.privateSpotGetDepositAddress(self.extend(request, params))
3376
+ #
3377
+ # {
3378
+ # "rc": 0,
3379
+ # "mc": "SUCCESS",
3380
+ # "ma": [],
3381
+ # "result": {
3382
+ # "address": "0x7f7173cf29d3846d20ca5a3aec1120b93dbd157a",
3383
+ # "memo": ""
3384
+ # }
3385
+ # }
3386
+ #
3387
+ result = self.safe_value(response, 'result', {})
3388
+ return self.parse_deposit_address(result, currency)
3389
+
3390
+ def parse_deposit_address(self, depositAddress, currency=None):
3391
+ #
3392
+ # {
3393
+ # "address": "0x7f7173cf29d3846d20ca5a3aec1120b93dbd157a",
3394
+ # "memo": ""
3395
+ # }
3396
+ #
3397
+ address = self.safe_string(depositAddress, 'address')
3398
+ self.check_address(address)
3399
+ return {
3400
+ 'currency': self.safe_currency_code(None, currency),
3401
+ 'address': address,
3402
+ 'tag': self.safe_string(depositAddress, 'memo'),
3403
+ 'network': None,
3404
+ 'info': depositAddress,
3405
+ }
3406
+
3407
+ async def fetch_deposits(self, code: str = None, since: Int = None, limit: Int = None, params={}):
3408
+ """
3409
+ fetch all deposits made to an account
3410
+ :see: https://doc.xt.com/#deposit_withdrawalhistoryDepositGet
3411
+ :param str [code]: unified currency code
3412
+ :param int [since]: the earliest time in ms to fetch deposits for
3413
+ :param int [limit]: the maximum number of transaction structures to retrieve
3414
+ :param dict params: extra parameters specific to the xt api endpoint
3415
+ :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/en/latest/manual.html#transaction-structure>`
3416
+ """
3417
+ await self.load_markets()
3418
+ request = {}
3419
+ currency = None
3420
+ if code is not None:
3421
+ currency = self.currency(code)
3422
+ request['currency'] = currency['id']
3423
+ if since is not None:
3424
+ request['startTime'] = since
3425
+ if limit is not None:
3426
+ request['limit'] = limit # default 10, max 200
3427
+ response = await self.privateSpotGetDepositHistory(self.extend(request, params))
3428
+ #
3429
+ # {
3430
+ # "rc": 0,
3431
+ # "mc": "SUCCESS",
3432
+ # "ma": [],
3433
+ # "result": {
3434
+ # "hasPrev": False,
3435
+ # "hasNext": False,
3436
+ # "items": [
3437
+ # {
3438
+ # "id": 170368702,
3439
+ # "currency": "usdt",
3440
+ # "chain": "Ethereum",
3441
+ # "memo": "",
3442
+ # "status": "SUCCESS",
3443
+ # "amount": "31.792528",
3444
+ # "confirmations": 12,
3445
+ # "transactionId": "0x90b8487c258b81b85e15e461b1839c49d4d8e6e9de4c1adb658cd47d4f5c5321",
3446
+ # "address": "0x7f7172cf29d3846d30ca5a3aec1120b92dbd150b",
3447
+ # "fromAddr": "0x7830c87c02e56aff27fa9ab1241711331fa86f58",
3448
+ # "createdTime": 1678491442000
3449
+ # },
3450
+ # ]
3451
+ # }
3452
+ # }
3453
+ #
3454
+ data = self.safe_value(response, 'result', {})
3455
+ deposits = self.safe_value(data, 'items', [])
3456
+ return self.parse_transactions(deposits, currency, since, limit, params)
3457
+
3458
+ async def fetch_withdrawals(self, code: str = None, since: Int = None, limit: Int = None, params={}):
3459
+ """
3460
+ fetch all withdrawals made from an account
3461
+ :see: https://doc.xt.com/#deposit_withdrawalwithdrawHistory
3462
+ :param str [code]: unified currency code
3463
+ :param int [since]: the earliest time in ms to fetch withdrawals for
3464
+ :param int [limit]: the maximum number of transaction structures to retrieve
3465
+ :param dict params: extra parameters specific to the xt api endpoint
3466
+ :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/en/latest/manual.html#transaction-structure>`
3467
+ """
3468
+ await self.load_markets()
3469
+ request = {}
3470
+ currency = None
3471
+ if code is not None:
3472
+ currency = self.currency(code)
3473
+ request['currency'] = currency['id']
3474
+ if since is not None:
3475
+ request['startTime'] = since
3476
+ if limit is not None:
3477
+ request['limit'] = limit # default 10, max 200
3478
+ response = await self.privateSpotGetWithdrawHistory(self.extend(request, params))
3479
+ #
3480
+ # {
3481
+ # "rc": 0,
3482
+ # "mc": "SUCCESS",
3483
+ # "ma": [],
3484
+ # "result": {
3485
+ # "hasPrev": False,
3486
+ # "hasNext": False,
3487
+ # "items": [
3488
+ # {
3489
+ # "id": 950898,
3490
+ # "currency": "usdt",
3491
+ # "chain": "Tron",
3492
+ # "address": "TGB2vxTjiqraVZBy7YHXF8V3CSMVhQKcaf",
3493
+ # "memo": "",
3494
+ # "status": "SUCCESS",
3495
+ # "amount": "5",
3496
+ # "fee": "2",
3497
+ # "confirmations": 6,
3498
+ # "transactionId": "c36e230b879842b1d7afd19d15ee1a866e26eaa0626e367d6f545d2932a15156",
3499
+ # "createdTime": 1680049062000
3500
+ # }
3501
+ # ]
3502
+ # }
3503
+ # }
3504
+ #
3505
+ data = self.safe_value(response, 'result', {})
3506
+ withdrawals = self.safe_value(data, 'items', [])
3507
+ return self.parse_transactions(withdrawals, currency, since, limit, params)
3508
+
3509
+ async def withdraw(self, code: str, amount: float, address: str, tag=None, params={}):
3510
+ """
3511
+ make a withdrawal
3512
+ :see: https://doc.xt.com/#deposit_withdrawalwithdraw
3513
+ :param str code: unified currency code
3514
+ :param float amount: the amount to withdraw
3515
+ :param str address: the address to withdraw to
3516
+ :param str [tag]:
3517
+ :param dict params: extra parameters specific to the xt api endpoint
3518
+ :returns dict: a `transaction structure <https://docs.ccxt.com/en/latest/manual.html#transaction-structure>`
3519
+ """
3520
+ self.check_address(address)
3521
+ await self.load_markets()
3522
+ currency = self.currency(code)
3523
+ tag, params = self.handle_withdraw_tag_and_params(tag, params)
3524
+ networkCode = None
3525
+ networkCode, params = self.handle_network_code_and_params(params)
3526
+ networkIdsByCodes = self.safe_value(self.options, 'networks', {})
3527
+ networkId = self.safe_string_2(networkIdsByCodes, networkCode, code, code)
3528
+ request = {
3529
+ 'currency': currency['id'],
3530
+ 'chain': networkId,
3531
+ 'amount': self.currency_to_precision(code, amount),
3532
+ 'address': address,
3533
+ }
3534
+ if tag is not None:
3535
+ request['memo'] = tag
3536
+ response = await self.privateSpotPostWithdraw(self.extend(request, params))
3537
+ #
3538
+ # {
3539
+ # "rc": 0,
3540
+ # "mc": "SUCCESS",
3541
+ # "ma": [],
3542
+ # "result": {
3543
+ # "id": 950898
3544
+ # }
3545
+ # }
3546
+ #
3547
+ result = self.safe_value(response, 'result', {})
3548
+ return self.parse_transaction(result, currency)
3549
+
3550
+ def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
3551
+ #
3552
+ # fetchDeposits
3553
+ #
3554
+ # {
3555
+ # "id": 170368702,
3556
+ # "currency": "usdt",
3557
+ # "chain": "Ethereum",
3558
+ # "memo": "",
3559
+ # "status": "SUCCESS",
3560
+ # "amount": "31.792528",
3561
+ # "confirmations": 12,
3562
+ # "transactionId": "0x90b8487c258b81b85e15e461b1839c49d4d8e6e9de4c1adb658cd47d4f5c5321",
3563
+ # "address": "0x7f7172cf29d3846d30ca5a3aec1120b92dbd150b",
3564
+ # "fromAddr": "0x7830c87c02e56aff27fa9ab1241711331fa86f58",
3565
+ # "createdTime": 1678491442000
3566
+ # }
3567
+ #
3568
+ # fetchWithdrawals
3569
+ #
3570
+ # {
3571
+ # "id": 950898,
3572
+ # "currency": "usdt",
3573
+ # "chain": "Tron",
3574
+ # "address": "TGB2vxTjiqraVZBy7YHXF8V3CSMVhQKcaf",
3575
+ # "memo": "",
3576
+ # "status": "SUCCESS",
3577
+ # "amount": "5",
3578
+ # "fee": "2",
3579
+ # "confirmations": 6,
3580
+ # "transactionId": "c36e230b879842b1d7afd19d15ee1a866e26eaa0626e367d6f545d2932a15156",
3581
+ # "createdTime": 1680049062000
3582
+ # }
3583
+ #
3584
+ # withdraw
3585
+ #
3586
+ # {
3587
+ # "id": 950898
3588
+ # }
3589
+ #
3590
+ type = 'deposit' if ('fromAddr' in transaction) else 'withdraw'
3591
+ timestamp = self.safe_integer(transaction, 'createdTime')
3592
+ address = self.safe_string(transaction, 'address')
3593
+ memo = self.safe_string(transaction, 'memo')
3594
+ currencyCode = self.safe_currency_code(self.safe_string(transaction, 'currency'), currency)
3595
+ fee = self.safe_number(transaction, 'fee')
3596
+ feeCurrency = currencyCode if (fee is not None) else None
3597
+ networkId = self.safe_string(transaction, 'chain')
3598
+ return {
3599
+ 'info': transaction,
3600
+ 'id': self.safe_string(transaction, 'id'),
3601
+ 'txid': self.safe_string(transaction, 'transactionId'),
3602
+ 'timestamp': timestamp,
3603
+ 'datetime': self.iso8601(timestamp),
3604
+ 'updated': None,
3605
+ 'addressFrom': self.safe_string(transaction, 'fromAddr'),
3606
+ 'addressTo': address,
3607
+ 'address': address,
3608
+ 'tagFrom': None,
3609
+ 'tagTo': None,
3610
+ 'tag': memo,
3611
+ 'type': type,
3612
+ 'amount': self.safe_number(transaction, 'amount'),
3613
+ 'currency': currencyCode,
3614
+ 'network': self.network_id_to_code(networkId, currencyCode),
3615
+ 'status': self.parse_transaction_status(self.safe_string(transaction, 'status')),
3616
+ 'comment': memo,
3617
+ 'fee': {
3618
+ 'currency': feeCurrency,
3619
+ 'cost': fee,
3620
+ 'rate': None,
3621
+ },
3622
+ 'internal': None,
3623
+ }
3624
+
3625
+ def parse_transaction_status(self, status):
3626
+ statuses = {
3627
+ 'SUBMIT': 'pending',
3628
+ 'REVIEW': 'pending',
3629
+ 'AUDITED': 'pending',
3630
+ 'PENDING': 'pending',
3631
+ 'CANCEL': 'canceled',
3632
+ 'FAIL': 'failed',
3633
+ 'SUCCESS': 'ok',
3634
+ }
3635
+ return self.safe_string(statuses, status, status)
3636
+
3637
+ async def set_leverage(self, leverage: Int, symbol: str = None, params={}):
3638
+ """
3639
+ set the level of leverage for a market
3640
+ :see: https://doc.xt.com/#futures_useradjustLeverage
3641
+ :param float leverage: the rate of leverage
3642
+ :param str symbol: unified market symbol
3643
+ :param dict params: extra parameters specific to the xt api endpoint
3644
+ :param str params['positionSide']: 'LONG' or 'SHORT'
3645
+ :returns dict: response from the exchange
3646
+ """
3647
+ if symbol is None:
3648
+ raise ArgumentsRequired(self.id + ' setLeverage() requires a symbol argument')
3649
+ positionSide = self.safe_string(params, 'positionSide')
3650
+ self.check_required_argument('setLeverage', positionSide, 'positionSide', ['LONG', 'SHORT'])
3651
+ if (leverage < 1) or (leverage > 125):
3652
+ raise BadRequest(self.id + ' setLeverage() leverage should be between 1 and 125')
3653
+ await self.load_markets()
3654
+ market = self.market(symbol)
3655
+ if not (market['contract']):
3656
+ raise BadSymbol(self.id + ' setLeverage() supports contract markets only')
3657
+ request = {
3658
+ 'symbol': market['id'],
3659
+ 'positionSide': positionSide,
3660
+ 'leverage': leverage,
3661
+ }
3662
+ subType = None
3663
+ subType, params = self.handle_sub_type_and_params('setLeverage', market, params)
3664
+ response = None
3665
+ if subType == 'inverse':
3666
+ response = await self.privateInversePostFutureUserV1PositionAdjustLeverage(self.extend(request, params))
3667
+ else:
3668
+ response = await self.privateLinearPostFutureUserV1PositionAdjustLeverage(self.extend(request, params))
3669
+ #
3670
+ # {
3671
+ # "returnCode": 0,
3672
+ # "msgInfo": "success",
3673
+ # "error": null,
3674
+ # "result": null
3675
+ # }
3676
+ #
3677
+ return response
3678
+
3679
+ async def add_margin(self, symbol: str, amount: float, params={}):
3680
+ """
3681
+ add margin to a position
3682
+ :see: https://doc.xt.com/#futures_useradjustMargin
3683
+ :param str symbol: unified market symbol
3684
+ :param float amount: amount of margin to add
3685
+ :param dict params: extra parameters specific to the xt api endpoint
3686
+ :param str params['positionSide']: 'LONG' or 'SHORT'
3687
+ :returns dict: a `margin structure <https://docs.ccxt.com/#/?id=add-margin-structure>`
3688
+ """
3689
+ return await self.modify_margin_helper(symbol, amount, 'ADD', params)
3690
+
3691
+ async def reduce_margin(self, symbol: str, amount: float, params={}):
3692
+ """
3693
+ remove margin from a position
3694
+ :see: https://doc.xt.com/#futures_useradjustMargin
3695
+ :param str symbol: unified market symbol
3696
+ :param float amount: the amount of margin to remove
3697
+ :param dict params: extra parameters specific to the xt api endpoint
3698
+ :param str params['positionSide']: 'LONG' or 'SHORT'
3699
+ :returns dict: a `margin structure <https://docs.ccxt.com/#/?id=reduce-margin-structure>`
3700
+ """
3701
+ return await self.modify_margin_helper(symbol, amount, 'SUB', params)
3702
+
3703
+ async def modify_margin_helper(self, symbol: str, amount, addOrReduce, params={}) -> MarginModification:
3704
+ positionSide = self.safe_string(params, 'positionSide')
3705
+ self.check_required_argument('setLeverage', positionSide, 'positionSide', ['LONG', 'SHORT'])
3706
+ await self.load_markets()
3707
+ market = self.market(symbol)
3708
+ request = {
3709
+ 'symbol': market['id'],
3710
+ 'margin': amount,
3711
+ 'type': addOrReduce,
3712
+ 'positionSide': positionSide,
3713
+ }
3714
+ subType = None
3715
+ subType, params = self.handle_sub_type_and_params('modifyMarginHelper', market, params)
3716
+ response = None
3717
+ if subType == 'inverse':
3718
+ response = await self.privateInversePostFutureUserV1PositionMargin(self.extend(request, params))
3719
+ else:
3720
+ response = await self.privateLinearPostFutureUserV1PositionMargin(self.extend(request, params))
3721
+ #
3722
+ # {
3723
+ # "returnCode": 0,
3724
+ # "msgInfo": "success",
3725
+ # "error": null,
3726
+ # "result": null
3727
+ # }
3728
+ #
3729
+ return self.parse_margin_modification(response, market)
3730
+
3731
+ def parse_margin_modification(self, data, market=None) -> MarginModification:
3732
+ return {
3733
+ 'info': data,
3734
+ 'type': None,
3735
+ 'amount': None,
3736
+ 'code': None,
3737
+ 'symbol': self.safe_symbol(None, market),
3738
+ 'status': None,
3739
+ 'marginMode': None,
3740
+ 'total': None,
3741
+ 'timestamp': None,
3742
+ 'datetime': None,
3743
+ }
3744
+
3745
+ async def fetch_leverage_tiers(self, symbols: List[str] = None, params={}):
3746
+ """
3747
+ :see: https://doc.xt.com/#futures_quotesgetLeverageBrackets
3748
+ retrieve information on the maximum leverage for different trade sizes
3749
+ :param str [symbols]: a list of unified market symbols
3750
+ :param dict params: extra parameters specific to the xt api endpoint
3751
+ :returns dict: a dictionary of `leverage tiers structures <https://docs.ccxt.com/#/?id=leverage-tiers-structure>`
3752
+ """
3753
+ await self.load_markets()
3754
+ subType = None
3755
+ subType, params = self.handle_sub_type_and_params('fetchLeverageTiers', None, params)
3756
+ response = None
3757
+ if subType == 'inverse':
3758
+ response = await self.publicInverseGetFutureMarketV1PublicLeverageBracketList(params)
3759
+ else:
3760
+ response = await self.publicLinearGetFutureMarketV1PublicLeverageBracketList(params)
3761
+ #
3762
+ # {
3763
+ # "returnCode": 0,
3764
+ # "msgInfo": "success",
3765
+ # "error": null,
3766
+ # "result": [
3767
+ # {
3768
+ # "symbol": "rad_usdt",
3769
+ # "leverageBrackets": [
3770
+ # {
3771
+ # "symbol": "rad_usdt",
3772
+ # "bracket": 1,
3773
+ # "maxNominalValue": "5000",
3774
+ # "maintMarginRate": "0.025",
3775
+ # "startMarginRate": "0.05",
3776
+ # "maxStartMarginRate": null,
3777
+ # "maxLeverage": "20",
3778
+ # "minLeverage": "1"
3779
+ # },
3780
+ # ]
3781
+ # },
3782
+ # ]
3783
+ # }
3784
+ #
3785
+ data = self.safe_value(response, 'result', [])
3786
+ symbols = self.market_symbols(symbols)
3787
+ return self.parse_leverage_tiers(data, symbols, 'symbol')
3788
+
3789
+ def parse_leverage_tiers(self, response, symbols=None, marketIdKey=None):
3790
+ #
3791
+ # {
3792
+ # "symbol": "rad_usdt",
3793
+ # "leverageBrackets": [
3794
+ # {
3795
+ # "symbol": "rad_usdt",
3796
+ # "bracket": 1,
3797
+ # "maxNominalValue": "5000",
3798
+ # "maintMarginRate": "0.025",
3799
+ # "startMarginRate": "0.05",
3800
+ # "maxStartMarginRate": null,
3801
+ # "maxLeverage": "20",
3802
+ # "minLeverage": "1"
3803
+ # },
3804
+ # ]
3805
+ # }
3806
+ #
3807
+ result = {}
3808
+ for i in range(0, len(response)):
3809
+ entry = response[i]
3810
+ marketId = self.safe_string(entry, 'symbol')
3811
+ market = self.safe_market(marketId, None, '_', 'contract')
3812
+ symbol = self.safe_symbol(marketId, market)
3813
+ if symbols is not None:
3814
+ if self.in_array(symbol, symbols):
3815
+ result[symbol] = self.parse_market_leverage_tiers(entry, market)
3816
+ else:
3817
+ result[symbol] = self.parse_market_leverage_tiers(response[i], market)
3818
+ return result
3819
+
3820
+ async def fetch_market_leverage_tiers(self, symbol: str, params={}) -> List[LeverageTier]:
3821
+ """
3822
+ :see: https://doc.xt.com/#futures_quotesgetLeverageBracket
3823
+ retrieve information on the maximum leverage for different trade sizes of a single market
3824
+ :param str symbol: unified market symbol
3825
+ :param dict params: extra parameters specific to the xt api endpoint
3826
+ :returns dict: a `leverage tiers structure <https://docs.ccxt.com/#/?id=leverage-tiers-structure>`
3827
+ """
3828
+ await self.load_markets()
3829
+ market = self.market(symbol)
3830
+ request = {
3831
+ 'symbol': market['id'],
3832
+ }
3833
+ subType = None
3834
+ subType, params = self.handle_sub_type_and_params('fetchMarketLeverageTiers', market, params)
3835
+ response = None
3836
+ if subType == 'inverse':
3837
+ response = await self.publicInverseGetFutureMarketV1PublicLeverageBracketDetail(self.extend(request, params))
3838
+ else:
3839
+ response = await self.publicLinearGetFutureMarketV1PublicLeverageBracketDetail(self.extend(request, params))
3840
+ #
3841
+ # {
3842
+ # "returnCode": 0,
3843
+ # "msgInfo": "success",
3844
+ # "error": null,
3845
+ # "result": {
3846
+ # "symbol": "btc_usdt",
3847
+ # "leverageBrackets": [
3848
+ # {
3849
+ # "symbol": "btc_usdt",
3850
+ # "bracket": 1,
3851
+ # "maxNominalValue": "500000",
3852
+ # "maintMarginRate": "0.004",
3853
+ # "startMarginRate": "0.008",
3854
+ # "maxStartMarginRate": null,
3855
+ # "maxLeverage": "125",
3856
+ # "minLeverage": "1"
3857
+ # },
3858
+ # ]
3859
+ # }
3860
+ # }
3861
+ #
3862
+ data = self.safe_value(response, 'result', {})
3863
+ return self.parse_market_leverage_tiers(data, market)
3864
+
3865
+ def parse_market_leverage_tiers(self, info, market=None):
3866
+ #
3867
+ # {
3868
+ # "symbol": "rad_usdt",
3869
+ # "leverageBrackets": [
3870
+ # {
3871
+ # "symbol": "rad_usdt",
3872
+ # "bracket": 1,
3873
+ # "maxNominalValue": "5000",
3874
+ # "maintMarginRate": "0.025",
3875
+ # "startMarginRate": "0.05",
3876
+ # "maxStartMarginRate": null,
3877
+ # "maxLeverage": "20",
3878
+ # "minLeverage": "1"
3879
+ # },
3880
+ # ]
3881
+ # }
3882
+ #
3883
+ tiers = []
3884
+ brackets = self.safe_value(info, 'leverageBrackets', [])
3885
+ for i in range(0, len(brackets)):
3886
+ tier = brackets[i]
3887
+ marketId = self.safe_string(info, 'symbol')
3888
+ market = self.safe_market(marketId, market, '_', 'contract')
3889
+ tiers.append({
3890
+ 'tier': self.safe_integer(tier, 'bracket'),
3891
+ 'currency': market['settle'],
3892
+ 'minNotional': self.safe_number(brackets[i - 1], 'maxNominalValue', 0),
3893
+ 'maxNotional': self.safe_number(tier, 'maxNominalValue'),
3894
+ 'maintenanceMarginRate': self.safe_number(tier, 'maintMarginRate'),
3895
+ 'maxLeverage': self.safe_number(tier, 'maxLeverage'),
3896
+ 'info': tier,
3897
+ })
3898
+ return tiers
3899
+
3900
+ async def fetch_funding_rate_history(self, symbol: str = None, since: Int = None, limit: Int = None, params={}):
3901
+ """
3902
+ fetches historical funding rates
3903
+ :see: https://doc.xt.com/#futures_quotesgetFundingRateRecord
3904
+ :param str [symbol]: unified symbol of the market to fetch the funding rate history for
3905
+ :param int [since]: timestamp in ms of the earliest funding rate to fetch
3906
+ :param int [limit]: the maximum amount of [funding rate structures] to fetch
3907
+ :param dict params: extra parameters specific to the xt api endpoint
3908
+ :returns dict[]: a list of `funding rate structures <https://docs.ccxt.com/en/latest/manual.html?#funding-rate-history-structure>`
3909
+ """
3910
+ if symbol is None:
3911
+ raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument')
3912
+ await self.load_markets()
3913
+ market = self.market(symbol)
3914
+ if not market['swap']:
3915
+ raise BadSymbol(self.id + ' fetchFundingRateHistory() supports swap contracts only')
3916
+ request = {
3917
+ 'symbol': market['id'],
3918
+ }
3919
+ if limit is not None:
3920
+ request['limit'] = limit
3921
+ subType = None
3922
+ subType, params = self.handle_sub_type_and_params('fetchFundingRateHistory', market, params)
3923
+ response = None
3924
+ if subType == 'inverse':
3925
+ response = await self.publicInverseGetFutureMarketV1PublicQFundingRateRecord(self.extend(request, params))
3926
+ else:
3927
+ response = await self.publicLinearGetFutureMarketV1PublicQFundingRateRecord(self.extend(request, params))
3928
+ #
3929
+ # {
3930
+ # "returnCode": 0,
3931
+ # "msgInfo": "success",
3932
+ # "error": null,
3933
+ # "result": {
3934
+ # "hasPrev": False,
3935
+ # "hasNext": True,
3936
+ # "items": [
3937
+ # {
3938
+ # "id": "210441653482221888",
3939
+ # "symbol": "btc_usdt",
3940
+ # "fundingRate": "0.000057",
3941
+ # "createdTime": 1679875200000,
3942
+ # "collectionInternal": 28800
3943
+ # },
3944
+ # ]
3945
+ # }
3946
+ # }
3947
+ #
3948
+ result = self.safe_value(response, 'result', {})
3949
+ items = self.safe_value(result, 'items', [])
3950
+ rates = []
3951
+ for i in range(0, len(items)):
3952
+ entry = items[i]
3953
+ marketId = self.safe_string(entry, 'symbol')
3954
+ symbolInner = self.safe_symbol(marketId, market)
3955
+ timestamp = self.safe_integer(entry, 'createdTime')
3956
+ rates.append({
3957
+ 'info': entry,
3958
+ 'symbol': symbolInner,
3959
+ 'fundingRate': self.safe_number(entry, 'fundingRate'),
3960
+ 'timestamp': timestamp,
3961
+ 'datetime': self.iso8601(timestamp),
3962
+ })
3963
+ sorted = self.sort_by(rates, 'timestamp')
3964
+ return self.filter_by_symbol_since_limit(sorted, market['symbol'], since, limit)
3965
+
3966
+ async def fetch_funding_rate(self, symbol: str, params={}):
3967
+ """
3968
+ fetch the current funding rate
3969
+ :see: https://doc.xt.com/#futures_quotesgetFundingRate
3970
+ :param str symbol: unified market symbol
3971
+ :param dict params: extra parameters specific to the xt api endpoint
3972
+ :returns dict: a `funding rate structure <https://docs.ccxt.com/#/?id=funding-rate-structure>`
3973
+ """
3974
+ await self.load_markets()
3975
+ market = self.market(symbol)
3976
+ if not market['swap']:
3977
+ raise BadSymbol(self.id + ' fetchFundingRate() supports swap contracts only')
3978
+ request = {
3979
+ 'symbol': market['id'],
3980
+ }
3981
+ subType = None
3982
+ subType, params = self.handle_sub_type_and_params('fetchFundingRate', market, params)
3983
+ response = None
3984
+ if subType == 'inverse':
3985
+ response = await self.publicInverseGetFutureMarketV1PublicQFundingRate(self.extend(request, params))
3986
+ else:
3987
+ response = await self.publicLinearGetFutureMarketV1PublicQFundingRate(self.extend(request, params))
3988
+ #
3989
+ # {
3990
+ # "returnCode": 0,
3991
+ # "msgInfo": "success",
3992
+ # "error": null,
3993
+ # "result": {
3994
+ # "symbol": "btc_usdt",
3995
+ # "fundingRate": "0.000086",
3996
+ # "nextCollectionTime": 1680307200000,
3997
+ # "collectionInternal": 8
3998
+ # }
3999
+ # }
4000
+ #
4001
+ result = self.safe_value(response, 'result', {})
4002
+ return self.parse_funding_rate(result, market)
4003
+
4004
+ def parse_funding_rate(self, contract, market=None):
4005
+ #
4006
+ # {
4007
+ # "symbol": "btc_usdt",
4008
+ # "fundingRate": "0.000086",
4009
+ # "nextCollectionTime": 1680307200000,
4010
+ # "collectionInternal": 8
4011
+ # }
4012
+ #
4013
+ marketId = self.safe_string(contract, 'symbol')
4014
+ symbol = self.safe_symbol(marketId, market, '_', 'swap')
4015
+ timestamp = self.safe_integer(contract, 'nextCollectionTime')
4016
+ return {
4017
+ 'info': contract,
4018
+ 'symbol': symbol,
4019
+ 'markPrice': None,
4020
+ 'indexPrice': None,
4021
+ 'interestRate': None,
4022
+ 'estimatedSettlePrice': None,
4023
+ 'timestamp': None,
4024
+ 'datetime': None,
4025
+ 'fundingRate': self.safe_number(contract, 'fundingRate'),
4026
+ 'fundingTimestamp': timestamp,
4027
+ 'fundingDatetime': self.iso8601(timestamp),
4028
+ 'nextFundingRate': None,
4029
+ 'nextFundingTimestamp': None,
4030
+ 'nextFundingDatetime': None,
4031
+ 'previousFundingRate': None,
4032
+ 'previousFundingTimestamp': None,
4033
+ 'previousFundingDatetime': None,
4034
+ }
4035
+
4036
+ async def fetch_funding_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
4037
+ """
4038
+ fetch the funding history
4039
+ :see: https://doc.xt.com/#futures_usergetFunding
4040
+ :param str symbol: unified market symbol
4041
+ :param int [since]: the starting timestamp in milliseconds
4042
+ :param int [limit]: the number of entries to return
4043
+ :param dict params: extra parameters specific to the xt api endpoint
4044
+ :returns dict[]: a list of `funding history structures <https://docs.ccxt.com/#/?id=funding-history-structure>`
4045
+ """
4046
+ await self.load_markets()
4047
+ market = self.market(symbol)
4048
+ if not market['swap']:
4049
+ raise BadSymbol(self.id + ' fetchFundingHistory() supports swap contracts only')
4050
+ request = {
4051
+ 'symbol': market['id'],
4052
+ }
4053
+ if since is not None:
4054
+ request['startTime'] = since
4055
+ if limit is not None:
4056
+ request['limit'] = limit
4057
+ subType = None
4058
+ subType, params = self.handle_sub_type_and_params('fetchFundingHistory', market, params)
4059
+ response = None
4060
+ if subType == 'inverse':
4061
+ response = await self.privateInverseGetFutureUserV1BalanceFundingRateList(self.extend(request, params))
4062
+ else:
4063
+ response = await self.privateLinearGetFutureUserV1BalanceFundingRateList(self.extend(request, params))
4064
+ #
4065
+ # {
4066
+ # "returnCode": 0,
4067
+ # "msgInfo": "success",
4068
+ # "error": null,
4069
+ # "result": {
4070
+ # "hasPrev": False,
4071
+ # "hasNext": False,
4072
+ # "items": [
4073
+ # {
4074
+ # "id": "210804044057280512",
4075
+ # "symbol": "btc_usdt",
4076
+ # "cast": "-0.0013",
4077
+ # "coin": "usdt",
4078
+ # "positionSide": "SHORT",
4079
+ # "createdTime": 1679961600653
4080
+ # },
4081
+ # ]
4082
+ # }
4083
+ # }
4084
+ #
4085
+ data = self.safe_value(response, 'result', {})
4086
+ items = self.safe_value(data, 'items', [])
4087
+ result = []
4088
+ for i in range(0, len(items)):
4089
+ entry = items[i]
4090
+ result.append(self.parse_funding_history(entry, market))
4091
+ sorted = self.sort_by(result, 'timestamp')
4092
+ return self.filter_by_since_limit(sorted, since, limit)
4093
+
4094
+ def parse_funding_history(self, contract, market=None):
4095
+ #
4096
+ # {
4097
+ # "id": "210804044057280512",
4098
+ # "symbol": "btc_usdt",
4099
+ # "cast": "-0.0013",
4100
+ # "coin": "usdt",
4101
+ # "positionSide": "SHORT",
4102
+ # "createdTime": 1679961600653
4103
+ # }
4104
+ #
4105
+ marketId = self.safe_string(contract, 'symbol')
4106
+ symbol = self.safe_symbol(marketId, market, '_', 'swap')
4107
+ currencyId = self.safe_string(contract, 'coin')
4108
+ code = self.safe_currency_code(currencyId)
4109
+ timestamp = self.safe_integer(contract, 'createdTime')
4110
+ return {
4111
+ 'info': contract,
4112
+ 'symbol': symbol,
4113
+ 'code': code,
4114
+ 'timestamp': timestamp,
4115
+ 'datetime': self.iso8601(timestamp),
4116
+ 'id': self.safe_string(contract, 'id'),
4117
+ 'amount': self.safe_number(contract, 'cast'),
4118
+ }
4119
+
4120
+ async def fetch_position(self, symbol: str, params={}):
4121
+ """
4122
+ fetch data on a single open contract trade position
4123
+ :see: https://doc.xt.com/#futures_usergetPosition
4124
+ :param str symbol: unified market symbol of the market the position is held in
4125
+ :param dict params: extra parameters specific to the xt api endpoint
4126
+ :returns dict: a `position structure <https://docs.ccxt.com/#/?id=position-structure>`
4127
+ """
4128
+ await self.load_markets()
4129
+ market = self.market(symbol)
4130
+ request = {
4131
+ 'symbol': market['id'],
4132
+ }
4133
+ subType = None
4134
+ subType, params = self.handle_sub_type_and_params('fetchPosition', market, params)
4135
+ response = None
4136
+ if subType == 'inverse':
4137
+ response = await self.privateInverseGetFutureUserV1PositionList(self.extend(request, params))
4138
+ else:
4139
+ response = await self.privateLinearGetFutureUserV1PositionList(self.extend(request, params))
4140
+ #
4141
+ # {
4142
+ # "returnCode": 0,
4143
+ # "msgInfo": "success",
4144
+ # "error": null,
4145
+ # "result": [
4146
+ # {
4147
+ # "symbol": "btc_usdt",
4148
+ # "positionType": "ISOLATED",
4149
+ # "positionSide": "SHORT",
4150
+ # "contractType": "PERPETUAL",
4151
+ # "positionSize": "10",
4152
+ # "closeOrderSize": "0",
4153
+ # "availableCloseSize": "10",
4154
+ # "entryPrice": "27060",
4155
+ # "openOrderSize": "0",
4156
+ # "isolatedMargin": "1.0824",
4157
+ # "openOrderMarginFrozen": "0",
4158
+ # "realizedProfit": "-0.00130138",
4159
+ # "autoMargin": False,
4160
+ # "leverage": 25
4161
+ # },
4162
+ # ]
4163
+ # }
4164
+ #
4165
+ positions = self.safe_value(response, 'result', [])
4166
+ for i in range(0, len(positions)):
4167
+ entry = positions[i]
4168
+ marketId = self.safe_string(entry, 'symbol')
4169
+ marketInner = self.safe_market(marketId, None, None, 'contract')
4170
+ positionSize = self.safe_string(entry, 'positionSize')
4171
+ if positionSize != '0':
4172
+ return self.parse_position(entry, marketInner)
4173
+ return None
4174
+
4175
+ async def fetch_positions(self, symbols: List[str] = None, params={}):
4176
+ """
4177
+ fetch all open positions
4178
+ :see: https://doc.xt.com/#futures_usergetPosition
4179
+ :param str [symbols]: list of unified market symbols, not supported with xt
4180
+ :param dict params: extra parameters specific to the xt api endpoint
4181
+ :returns dict[]: a list of `position structure <https://docs.ccxt.com/#/?id=position-structure>`
4182
+ """
4183
+ await self.load_markets()
4184
+ subType = None
4185
+ subType, params = self.handle_sub_type_and_params('fetchPositions', None, params)
4186
+ response = None
4187
+ if subType == 'inverse':
4188
+ response = await self.privateInverseGetFutureUserV1PositionList(params)
4189
+ else:
4190
+ response = await self.privateLinearGetFutureUserV1PositionList(params)
4191
+ #
4192
+ # {
4193
+ # "returnCode": 0,
4194
+ # "msgInfo": "success",
4195
+ # "error": null,
4196
+ # "result": [
4197
+ # {
4198
+ # "symbol": "btc_usdt",
4199
+ # "positionType": "ISOLATED",
4200
+ # "positionSide": "SHORT",
4201
+ # "contractType": "PERPETUAL",
4202
+ # "positionSize": "10",
4203
+ # "closeOrderSize": "0",
4204
+ # "availableCloseSize": "10",
4205
+ # "entryPrice": "27060",
4206
+ # "openOrderSize": "0",
4207
+ # "isolatedMargin": "1.0824",
4208
+ # "openOrderMarginFrozen": "0",
4209
+ # "realizedProfit": "-0.00130138",
4210
+ # "autoMargin": False,
4211
+ # "leverage": 25
4212
+ # },
4213
+ # ]
4214
+ # }
4215
+ #
4216
+ positions = self.safe_value(response, 'result', [])
4217
+ result = []
4218
+ for i in range(0, len(positions)):
4219
+ entry = positions[i]
4220
+ marketId = self.safe_string(entry, 'symbol')
4221
+ marketInner = self.safe_market(marketId, None, None, 'contract')
4222
+ result.append(self.parse_position(entry, marketInner))
4223
+ return self.filter_by_array_positions(result, 'symbol', symbols, False)
4224
+
4225
+ def parse_position(self, position, market=None):
4226
+ #
4227
+ # {
4228
+ # "symbol": "btc_usdt",
4229
+ # "positionType": "ISOLATED",
4230
+ # "positionSide": "SHORT",
4231
+ # "contractType": "PERPETUAL",
4232
+ # "positionSize": "10",
4233
+ # "closeOrderSize": "0",
4234
+ # "availableCloseSize": "10",
4235
+ # "entryPrice": "27060",
4236
+ # "openOrderSize": "0",
4237
+ # "isolatedMargin": "1.0824",
4238
+ # "openOrderMarginFrozen": "0",
4239
+ # "realizedProfit": "-0.00130138",
4240
+ # "autoMargin": False,
4241
+ # "leverage": 25
4242
+ # }
4243
+ #
4244
+ marketId = self.safe_string(position, 'symbol')
4245
+ market = self.safe_market(marketId, market, None, 'contract')
4246
+ symbol = self.safe_symbol(marketId, market, None, 'contract')
4247
+ positionType = self.safe_string(position, 'positionType')
4248
+ marginMode = 'cross' if (positionType == 'CROSSED') else 'isolated'
4249
+ collateral = self.safe_number(position, 'isolatedMargin')
4250
+ return self.safe_position({
4251
+ 'info': position,
4252
+ 'id': None,
4253
+ 'symbol': symbol,
4254
+ 'timestamp': None,
4255
+ 'datetime': None,
4256
+ 'hedged': None,
4257
+ 'side': self.safe_string_lower(position, 'positionSide'),
4258
+ 'contracts': self.safe_number(position, 'positionSize'),
4259
+ 'contractSize': market['contractSize'],
4260
+ 'entryPrice': self.safe_number(position, 'entryPrice'),
4261
+ 'markPrice': None,
4262
+ 'notional': None,
4263
+ 'leverage': self.safe_integer(position, 'leverage'),
4264
+ 'collateral': collateral,
4265
+ 'initialMargin': collateral,
4266
+ 'maintenanceMargin': None,
4267
+ 'initialMarginPercentage': None,
4268
+ 'maintenanceMarginPercentage': None,
4269
+ 'unrealizedPnl': None,
4270
+ 'liquidationPrice': None,
4271
+ 'marginMode': marginMode,
4272
+ 'percentage': None,
4273
+ 'marginRatio': None,
4274
+ })
4275
+
4276
+ async def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
4277
+ """
4278
+ transfer currency internally between wallets on the same account
4279
+ :see: https://doc.xt.com/#transfersubTransferPost
4280
+ :param str code: unified currency code
4281
+ :param float amount: amount to transfer
4282
+ :param str fromAccount: account to transfer from - spot, swap, leverage, finance
4283
+ :param str toAccount: account to transfer to - spot, swap, leverage, finance
4284
+ :param dict params: extra parameters specific to the whitebit api endpoint
4285
+ :returns dict: a `transfer structure <https://docs.ccxt.com/#/?id=transfer-structure>`
4286
+ """
4287
+ await self.load_markets()
4288
+ currency = self.currency(code)
4289
+ accountsByType = self.safe_value(self.options, 'accountsById')
4290
+ fromAccountId = self.safe_string(accountsByType, fromAccount, fromAccount)
4291
+ toAccountId = self.safe_string(accountsByType, toAccount, toAccount)
4292
+ amountString = self.currency_to_precision(code, amount)
4293
+ request = {
4294
+ 'bizId': self.uuid(),
4295
+ 'currency': currency['id'],
4296
+ 'amount': amountString,
4297
+ 'from': fromAccountId,
4298
+ 'to': toAccountId,
4299
+ }
4300
+ response = await self.privateSpotPostBalanceTransfer(self.extend(request, params))
4301
+ #
4302
+ # {
4303
+ # info: {rc: '0', mc: 'SUCCESS', ma: [], result: '226971333791398656'},
4304
+ # id: '226971333791398656',
4305
+ # timestamp: None,
4306
+ # datetime: None,
4307
+ # currency: None,
4308
+ # amount: None,
4309
+ # fromAccount: None,
4310
+ # toAccount: None,
4311
+ # status: None
4312
+ # }
4313
+ #
4314
+ return self.parse_transfer(response, currency)
4315
+
4316
+ def parse_transfer(self, transfer, currency=None):
4317
+ return {
4318
+ 'info': transfer,
4319
+ 'id': self.safe_string(transfer, 'result'),
4320
+ 'timestamp': None,
4321
+ 'datetime': None,
4322
+ 'currency': None,
4323
+ 'amount': None,
4324
+ 'fromAccount': None,
4325
+ 'toAccount': None,
4326
+ 'status': None,
4327
+ }
4328
+
4329
+ def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
4330
+ #
4331
+ # spot: error
4332
+ #
4333
+ # {
4334
+ # "rc": 1,
4335
+ # "mc": "AUTH_103",
4336
+ # "ma": [],
4337
+ # "result": null
4338
+ # }
4339
+ #
4340
+ # spot: success
4341
+ #
4342
+ # {
4343
+ # "returnCode": 0,
4344
+ # "msgInfo": "success",
4345
+ # "error": null,
4346
+ # "result": []
4347
+ # }
4348
+ #
4349
+ # swap and future: error
4350
+ #
4351
+ # {
4352
+ # "returnCode": 1,
4353
+ # "msgInfo": "failure",
4354
+ # "error": {
4355
+ # "code": "403",
4356
+ # "msg": "invalid signature"
4357
+ # },
4358
+ # "result": null
4359
+ # }
4360
+ #
4361
+ # swap and future: success
4362
+ #
4363
+ # {
4364
+ # "returnCode": 0,
4365
+ # "msgInfo": "success",
4366
+ # "error": null,
4367
+ # "result": null
4368
+ # }
4369
+ #
4370
+ # other:
4371
+ #
4372
+ # {
4373
+ # "rc": 0,
4374
+ # "mc": "SUCCESS",
4375
+ # "ma": [],
4376
+ # "result": {}
4377
+ # }
4378
+ #
4379
+ status = self.safe_string_upper_2(response, 'msgInfo', 'mc')
4380
+ if status is not None and status != 'SUCCESS':
4381
+ feedback = self.id + ' ' + body
4382
+ error = self.safe_value(response, 'error', {})
4383
+ spotErrorCode = self.safe_string(response, 'mc')
4384
+ errorCode = self.safe_string(error, 'code', spotErrorCode)
4385
+ spotMessage = self.safe_string(response, 'msgInfo')
4386
+ message = self.safe_string(error, 'msg', spotMessage)
4387
+ self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
4388
+ self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
4389
+ raise ExchangeError(feedback)
4390
+ return None
4391
+
4392
+ def sign(self, path, api=[], method='GET', params={}, headers=None, body=None):
4393
+ signed = api[0] == 'private'
4394
+ endpoint = api[1]
4395
+ request = '/' + self.implode_params(path, params)
4396
+ payload = None
4397
+ if (endpoint == 'spot') or (endpoint == 'user'):
4398
+ if signed:
4399
+ payload = '/' + self.version + request
4400
+ else:
4401
+ payload = '/' + self.version + '/public' + request
4402
+ else:
4403
+ payload = request
4404
+ url = self.urls['api'][endpoint] + payload
4405
+ query = self.omit(params, self.extract_params(path))
4406
+ urlencoded = self.urlencode(self.keysort(query))
4407
+ headers = {
4408
+ 'Content-Type': 'application/json',
4409
+ }
4410
+ if signed:
4411
+ self.check_required_credentials()
4412
+ defaultRecvWindow = self.safe_string(self.options, 'recvWindow')
4413
+ recvWindow = self.safe_string(query, 'recvWindow', defaultRecvWindow)
4414
+ timestamp = self.number_to_string(self.nonce())
4415
+ body = query
4416
+ 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'):
4417
+ id = 'CCXT'
4418
+ if payload.find('future') > -1:
4419
+ body['clientMedia'] = id
4420
+ else:
4421
+ body['media'] = id
4422
+ isUndefinedBody = ((method == 'GET') or (path == 'order/{orderId}'))
4423
+ body = None if isUndefinedBody else self.json(body)
4424
+ payloadString = None
4425
+ if (endpoint == 'spot') or (endpoint == 'user'):
4426
+ payloadString = 'xt-validate-algorithms=HmacSHA256&xt-validate-appkey=' + self.apiKey + '&xt-validate-recvwindow=' + recvWindow + '&xt-validate-t' + 'imestamp=' + timestamp
4427
+ if isUndefinedBody:
4428
+ if urlencoded:
4429
+ url += '?' + urlencoded
4430
+ payloadString += '#' + method + '#' + payload + '#' + urlencoded
4431
+ else:
4432
+ payloadString += '#' + method + '#' + payload
4433
+ else:
4434
+ payloadString += '#' + method + '#' + payload + '#' + body
4435
+ headers['xt-validate-algorithms'] = 'HmacSHA256'
4436
+ headers['xt-validate-recvwindow'] = recvWindow
4437
+ else:
4438
+ payloadString = 'xt-validate-appkey=' + self.apiKey + '&xt-validate-t' + 'imestamp=' + timestamp # we can't glue timestamp, breaks in php
4439
+ if method == 'GET':
4440
+ if urlencoded:
4441
+ url += '?' + urlencoded
4442
+ payloadString += '#' + payload + '#' + urlencoded
4443
+ else:
4444
+ payloadString += '#' + payload
4445
+ else:
4446
+ payloadString += '#' + payload + '#' + body
4447
+ signature = self.hmac(self.encode(payloadString), self.encode(self.secret), hashlib.sha256)
4448
+ headers['xt-validate-appkey'] = self.apiKey
4449
+ headers['xt-validate-timestamp'] = timestamp
4450
+ headers['xt-validate-signature'] = signature
4451
+ else:
4452
+ if urlencoded:
4453
+ url += '?' + urlencoded
4454
+ return {'url': url, 'method': method, 'body': body, 'headers': headers}