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