ccxt 4.3.44__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/oxfun.py ADDED
@@ -0,0 +1,2772 @@
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.oxfun import ImplicitAPI
8
+ import hashlib
9
+ from ccxt.base.types import Account, Balances, Bool, Currencies, Currency, Int, Market, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, 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 AccountNotEnabled
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 MarketClosed
18
+ from ccxt.base.errors import InsufficientFunds
19
+ from ccxt.base.errors import InvalidOrder
20
+ from ccxt.base.errors import OrderNotFound
21
+ from ccxt.base.errors import NotSupported
22
+ from ccxt.base.errors import OperationFailed
23
+ from ccxt.base.errors import NetworkError
24
+ from ccxt.base.errors import RateLimitExceeded
25
+ from ccxt.base.errors import RequestTimeout
26
+ from ccxt.base.decimal_to_precision import TICK_SIZE
27
+ from ccxt.base.precise import Precise
28
+
29
+
30
+ class oxfun(Exchange, ImplicitAPI):
31
+
32
+ def describe(self):
33
+ return self.deep_extend(super(oxfun, self).describe(), {
34
+ 'id': 'oxfun',
35
+ 'name': 'Oxfun',
36
+ 'countries': ['PA'], # Panama todo check
37
+ 'version': 'v3',
38
+ 'rateLimit': 120, # 100 requests per second and 25000 per 5 minutes
39
+ 'pro': True,
40
+ 'has': {
41
+ 'CORS': None,
42
+ 'spot': True,
43
+ 'margin': False,
44
+ 'swap': True,
45
+ 'future': False,
46
+ 'option': False,
47
+ 'addMargin': False,
48
+ 'cancelAllOrders': True,
49
+ 'cancelOrder': True,
50
+ 'cancelOrders': True,
51
+ 'closeAllPositions': False,
52
+ 'closePosition': False,
53
+ 'createDepositAddress': False,
54
+ 'createMarketBuyOrderWithCost': True,
55
+ 'createMarketOrderWithCost': False,
56
+ 'createMarketSellOrderWithCost': False,
57
+ 'createOrder': True,
58
+ 'createOrders': True,
59
+ 'createPostOnlyOrder': True,
60
+ 'createReduceOnlyOrder': False,
61
+ 'createStopLimitOrder': True,
62
+ 'createStopMarketOrder': True,
63
+ 'createStopOrder': True,
64
+ 'deposit': False,
65
+ 'editOrder': False,
66
+ 'fetchAccounts': True,
67
+ 'fetchBalance': True,
68
+ 'fetchBidsAsks': False,
69
+ 'fetchBorrowInterest': False,
70
+ 'fetchBorrowRateHistories': False,
71
+ 'fetchBorrowRateHistory': False,
72
+ 'fetchCanceledOrders': False,
73
+ 'fetchClosedOrder': False,
74
+ 'fetchClosedOrders': False,
75
+ 'fetchCrossBorrowRate': False,
76
+ 'fetchCrossBorrowRates': False,
77
+ 'fetchCurrencies': True,
78
+ 'fetchDeposit': False,
79
+ 'fetchDepositAddress': False,
80
+ 'fetchDepositAddresses': False,
81
+ 'fetchDepositAddressesByNetwork': False,
82
+ 'fetchDeposits': True,
83
+ 'fetchDepositWithdrawFee': False,
84
+ 'fetchDepositWithdrawFees': False,
85
+ 'fetchFundingHistory': True,
86
+ 'fetchFundingRate': False,
87
+ 'fetchFundingRateHistory': True,
88
+ 'fetchFundingRates': True,
89
+ 'fetchIndexOHLCV': False,
90
+ 'fetchIsolatedBorrowRate': False,
91
+ 'fetchIsolatedBorrowRates': False,
92
+ 'fetchL3OrderBook': False,
93
+ 'fetchLedger': False,
94
+ 'fetchLeverage': False,
95
+ 'fetchLeverageTiers': True,
96
+ 'fetchMarketLeverageTiers': False,
97
+ 'fetchMarkets': True,
98
+ 'fetchMarkOHLCV': False,
99
+ 'fetchMyTrades': True,
100
+ 'fetchOHLCV': True,
101
+ 'fetchOpenInterestHistory': False,
102
+ 'fetchOpenOrder': False,
103
+ 'fetchOpenOrders': True,
104
+ 'fetchOrder': True,
105
+ 'fetchOrderBook': True,
106
+ 'fetchOrderBooks': False,
107
+ 'fetchOrders': False,
108
+ 'fetchOrderTrades': False,
109
+ 'fetchPosition': False,
110
+ 'fetchPositionHistory': False,
111
+ 'fetchPositionMode': False,
112
+ 'fetchPositions': True,
113
+ 'fetchPositionsForSymbol': False,
114
+ 'fetchPositionsHistory': False,
115
+ 'fetchPositionsRisk': False,
116
+ 'fetchPremiumIndexOHLCV': False,
117
+ 'fetchStatus': False,
118
+ 'fetchTicker': True,
119
+ 'fetchTickers': True,
120
+ 'fetchTime': False,
121
+ 'fetchTrades': True,
122
+ 'fetchTradingFee': False,
123
+ 'fetchTradingFees': False,
124
+ 'fetchTradingLimits': False,
125
+ 'fetchTransactionFee': False,
126
+ 'fetchTransactionFees': False,
127
+ 'fetchTransactions': False,
128
+ 'fetchTransfers': True,
129
+ 'fetchWithdrawal': False,
130
+ 'fetchWithdrawals': True,
131
+ 'fetchWithdrawalWhitelist': False,
132
+ 'reduceMargin': False,
133
+ 'repayCrossMargin': False,
134
+ 'repayIsolatedMargin': False,
135
+ 'setLeverage': False,
136
+ 'setMargin': False,
137
+ 'setMarginMode': False,
138
+ 'setPositionMode': False,
139
+ 'signIn': False,
140
+ 'transfer': True,
141
+ 'withdraw': True,
142
+ 'ws': True,
143
+ },
144
+ 'timeframes': {
145
+ '1m': '60s',
146
+ '5m': '300s',
147
+ '15m': '900s',
148
+ '30m': '1800s',
149
+ '1h': '3600s',
150
+ '2h': '7200s',
151
+ '4h': '14400s',
152
+ '1d': '86400s',
153
+ },
154
+ 'urls': {
155
+ 'logo': 'https://github.com/ccxt/ccxt/assets/43336371/9c7114b3-ec32-4cf7-a716-f807d7d071cd',
156
+ 'referral': 'https://ox.fun/register?shareAccountId=5ZUD4a7G',
157
+ 'api': {
158
+ 'public': 'https://api.ox.fun',
159
+ 'private': 'https://api.ox.fun',
160
+ },
161
+ 'test': {
162
+ 'public': 'https://stgapi.ox.fun',
163
+ 'private': 'https://stgapi.ox.fun',
164
+ },
165
+ 'www': 'https://ox.fun/',
166
+ 'doc': 'https://docs.ox.fun/',
167
+ 'fees': 'https://support.ox.fun/en/articles/8819866-trading-fees',
168
+ },
169
+ 'api': {
170
+ 'public': {
171
+ 'get': {
172
+ 'v3/markets': 1,
173
+ 'v3/assets': 1,
174
+ 'v3/tickers': 1,
175
+ 'v3/funding/estimates': 1,
176
+ 'v3/candles': 1,
177
+ 'v3/depth': 1,
178
+ 'v3/markets/operational': 1,
179
+ 'v3/exchange-trades': 1,
180
+ 'v3/funding/rates': 1,
181
+ 'v3/leverage/tiers': 1,
182
+ },
183
+ },
184
+ 'private': {
185
+ 'get': {
186
+ 'v3/account': 1,
187
+ 'v3/account/names': 1,
188
+ 'v3/wallet': 1, # retruns only FUNDING in OX
189
+ 'v3/transfer': 1,
190
+ 'v3/balances': 1,
191
+ 'v3/positions': 1,
192
+ 'v3/funding': 1,
193
+ 'v3/deposit-addresses': 1,
194
+ 'v3/deposit': 1,
195
+ 'v3/withdrawal-addresses': 1,
196
+ 'v3/withdrawal': 1,
197
+ 'v3/withdrawal-fees': 1,
198
+ 'v3/orders/status': 1,
199
+ 'v3/orders/working': 1,
200
+ 'v3/trades': 1,
201
+ },
202
+ 'post': {
203
+ 'v3/transfer': 1,
204
+ 'v3/withdrawal': 1,
205
+ 'v3/orders/place': 1,
206
+ },
207
+ 'delete': {
208
+ 'v3/orders/cancel': 1,
209
+ 'v3/orders/cancel-all': 1,
210
+ },
211
+ },
212
+ },
213
+ 'fees': {
214
+ 'trading': {
215
+ 'tierBased': True,
216
+ 'percentage': True,
217
+ 'maker': self.parse_number('0.00020'),
218
+ 'taker': self.parse_number('0.00070'),
219
+ 'tiers': {
220
+ 'maker': [
221
+ [self.parse_number('0'), self.parse_number('0.00020')],
222
+ [self.parse_number('2500000'), self.parse_number('0.00010')],
223
+ [self.parse_number('25000000'), self.parse_number('0')],
224
+ ],
225
+ 'taker': [
226
+ [self.parse_number('0'), self.parse_number('0.00070')],
227
+ [self.parse_number('2500000'), self.parse_number('0.00050')],
228
+ [self.parse_number('25000000'), self.parse_number('0.00040')],
229
+ ],
230
+ },
231
+ },
232
+ },
233
+ 'precisionMode': TICK_SIZE,
234
+ # exchange-specific options
235
+ 'options': {
236
+ 'sandboxMode': False,
237
+ 'networks': {
238
+ 'BTC': 'Bitcoin',
239
+ 'ERC20': 'Ethereum',
240
+ 'AVAX': 'Avalanche',
241
+ 'SOL': 'Solana',
242
+ 'ARB': 'Arbitrum',
243
+ 'MATIC': 'Polygon',
244
+ 'FTM': 'Fantom',
245
+ 'BNB': 'BNBSmartChain',
246
+ 'OPTIMISM': 'Optimism',
247
+ },
248
+ 'networksById': {
249
+ 'Bitcoin': 'BTC',
250
+ 'Ethereum': 'ERC20',
251
+ 'Avalanche': 'AVAX',
252
+ 'Solana': 'SOL',
253
+ 'Arbitrum': 'ARB',
254
+ 'Polygon': 'MATIC',
255
+ 'Fantom': 'FTM',
256
+ 'Base': 'BASE',
257
+ 'BNBSmartChain': 'BNB',
258
+ 'Optimism': 'OPTIMISM',
259
+ },
260
+ },
261
+ 'exceptions': {
262
+ 'exact': {
263
+ '-0010': OperationFailed, # {"event":null,"success":false,"message":"Validation failed","code":"0010","data":null} - failed transfer
264
+ '-429': RateLimitExceeded, # Rate limit reached
265
+ '-05001': AuthenticationError, # Your operation authority is invalid
266
+ '-10001': ExchangeError, # General networking failure
267
+ '-20000': BadRequest, # Signature is invalid
268
+ '-20001': BadRequest, # "success":false,"code":"20001","message":"marketCode is invalid"
269
+ '-20002': BadRequest, # Unexpected error, please check if your request data complies with the specification.
270
+ '-20003': NotSupported, # Unrecognized operation
271
+ '-20005': AuthenticationError, # Already logged in
272
+ '-20006': BadRequest, # Quantity must be greater than zero
273
+ '-20007': AuthenticationError, # You are accessing server too rapidly
274
+ '-20008': BadRequest, # clientOrderId must be greater than zero if provided
275
+ '-20009': BadRequest, # JSON data format is invalid
276
+ '-20010': ArgumentsRequired, # Either clientOrderId or orderId is required
277
+ '-20011': ArgumentsRequired, # marketCode is required
278
+ '-20012': ArgumentsRequired, # side is required
279
+ '-20013': ArgumentsRequired, # orderType is required
280
+ '-20014': BadRequest, # clientOrderId is not long type
281
+ '-20015': BadSymbol, # marketCode is invalid
282
+ '-20016': BadRequest, # side is invalid
283
+ '-20017': BadRequest, # orderType is invalid
284
+ '-20018': BadRequest, # timeInForce is invalid
285
+ '-20019': BadRequest, # orderId is invalid
286
+ '-20020': BadRequest, # stopPrice or limitPrice is invalid
287
+ '-20021': BadRequest, # price is invalid
288
+ '-20022': ArgumentsRequired, # price is required for LIMIT order
289
+ '-20023': ArgumentsRequired, # timestamp is required
290
+ '-20024': ExchangeError, # timestamp exceeds the threshold
291
+ '-20025': AuthenticationError, # API key is invalid
292
+ '-20026': BadRequest, # Token is invalid or expired
293
+ '-20027': BadRequest, # The length of the message exceeds the maximum length
294
+ '-20028': BadRequest, # price or stopPrice or limitPrice must be greater than zero
295
+ '-20029': BadRequest, # stopPrice must be less than limitPrice for Buy Stop Order
296
+ '-20030': BadRequest, # limitPrice must be less than stopPrice for Sell Stop Order
297
+ '-20031': MarketClosed, # The marketCode is closed for trading temporarily
298
+ '-20032': NetworkError, # Failed to submit due to timeout in server side
299
+ '-20033': BadRequest, # triggerType is invalid
300
+ '-20034': BadRequest, # The size of tag must be less than 32
301
+ '-20050': ExchangeError, # selfTradePreventionMode is invalid
302
+ '-30001': BadRequest, # {"success":false,"code":"30001","message":"Required parameter 'marketCode' is missing"}
303
+ '-35034': AuthenticationError, # {"success":false,"code":"35034","message":"Wallet API is not functioning properly, please try again or contact support."}
304
+ '-35046': AuthenticationError, # {"success":false,"code":"35046","message":"Error. Please refresh the page."}
305
+ '-40001': ExchangeError, # Alert from the server
306
+ '-50001': ExchangeError, # Unknown server error
307
+ '-300001': AccountNotEnabled, # Invalid account status xxx, please contact administration if any questions
308
+ '-300011': InvalidOrder, # Repo market orders are not allowed during the auction window
309
+ '-300012': InvalidOrder, # Repo bids above 0 and offers below 0 are not allowed during the auction window
310
+ '-100005': OrderNotFound, # Open order not found
311
+ '-100006': InvalidOrder, # Open order is not owned by the user
312
+ '-100008': BadRequest, # Quantity cannot be less than the quantity increment xxx
313
+ '-100015': NetworkError, # recvWindow xxx has expired
314
+ '-710001': ExchangeError, # System failure, exception thrown -> xxx
315
+ '-710002': BadRequest, # The price is lower than the minimum
316
+ '-710003': BadRequest, # The price is higher than the maximum
317
+ '-710004': BadRequest, # Position quantity exceeds the limit
318
+ '-710005': InsufficientFunds, # Insufficient margin
319
+ '-710006': InsufficientFunds, # Insufficient balance
320
+ '-710007': InsufficientFunds, # Insufficient position
321
+ '-000101': NetworkError, # Internal server is unavailable temporary, try again later
322
+ '-000201': NetworkError, # Trade service is busy, try again later
323
+ },
324
+ 'broad': {
325
+ '-20001': OperationFailed, # Operation failed, please contact system administrator
326
+ '-200050': RequestTimeout, # The market is not active
327
+ },
328
+ },
329
+ })
330
+
331
+ def fetch_markets(self, params={}) -> List[Market]:
332
+ """
333
+ retrieves data on all markets for bitmex
334
+ :see: https://docs.ox.fun/?json#get-v3-markets
335
+ :param dict [params]: extra parameters specific to the exchange API endpoint
336
+ :returns dict[]: an array of objects representing market data
337
+ """
338
+ responseFromMarkets, responseFromTickers = [self.publicGetV3Markets(params), self.publicGetV3Tickers(params)]
339
+ marketsFromMarkets = self.safe_list(responseFromMarkets, 'data', [])
340
+ #
341
+ # {
342
+ # success: True,
343
+ # data: [
344
+ # {
345
+ # marketCode: 'OX-USD-SWAP-LIN',
346
+ # name: 'OX/USD Perp',
347
+ # referencePair: 'OX/USDT',
348
+ # base: 'OX',
349
+ # counter: 'USD',
350
+ # type: 'FUTURE',
351
+ # tickSize: '0.00001',
352
+ # minSize: '1',
353
+ # listedAt: '1704766320000',
354
+ # upperPriceBound: '0.02122',
355
+ # lowerPriceBound: '0.01142',
356
+ # markPrice: '0.01632',
357
+ # indexPrice: '0.01564',
358
+ # lastUpdatedAt: '1714762235569'
359
+ # },
360
+ # {
361
+ # marketCode: 'BTC-USD-SWAP-LIN',
362
+ # name: 'BTC/USD Perp',
363
+ # referencePair: 'BTC/USDT',
364
+ # base: 'BTC',
365
+ # counter: 'USD',
366
+ # type: 'FUTURE',
367
+ # tickSize: '1',
368
+ # minSize: '0.0001',
369
+ # listedAt: '1704686640000',
370
+ # upperPriceBound: '67983',
371
+ # lowerPriceBound: '55621',
372
+ # markPrice: '61802',
373
+ # indexPrice: '61813',
374
+ # lastUpdatedAt: '1714762234765'
375
+ # },
376
+ # {
377
+ # "marketCode": "MILK-OX",
378
+ # "name": "MILK/OX",
379
+ # "referencePair": "MILK/OX",
380
+ # "base": "MILK",
381
+ # "counter": "OX",
382
+ # "type": "SPOT",
383
+ # "tickSize": "0.0001",
384
+ # "minSize": "1",
385
+ # "listedAt": "1706608500000",
386
+ # "upperPriceBound": "1.0000",
387
+ # "lowerPriceBound": "-1.0000",
388
+ # "markPrice": "0.0269",
389
+ # "indexPrice": "0.0269",
390
+ # "lastUpdatedAt": "1714757402185"
391
+ # },
392
+ # ...
393
+ # ]
394
+ # }
395
+ #
396
+ marketsFromTickers = self.safe_list(responseFromTickers, 'data', [])
397
+ #
398
+ # {
399
+ # "success": True,
400
+ # "data": [
401
+ # {
402
+ # "marketCode": "DYM-USD-SWAP-LIN",
403
+ # "markPrice": "3.321",
404
+ # "open24h": "3.315",
405
+ # "high24h": "3.356",
406
+ # "low24h": "3.255",
407
+ # "volume24h": "0",
408
+ # "currencyVolume24h": "0",
409
+ # "openInterest": "1768.1",
410
+ # "lastTradedPrice": "3.543",
411
+ # "lastTradedQuantity": "1.0",
412
+ # "lastUpdatedAt": "1714853388102"
413
+ # },
414
+ # ...
415
+ # ]
416
+ # }
417
+ #
418
+ markets = self.array_concat(marketsFromMarkets, marketsFromTickers)
419
+ return self.parse_markets(markets)
420
+
421
+ def parse_markets(self, markets) -> List[Market]:
422
+ marketIds = []
423
+ result = []
424
+ for i in range(0, len(markets)):
425
+ market = markets[i]
426
+ marketId = self.safe_string(market, 'marketCode')
427
+ if not (self.in_array(marketId, marketIds)):
428
+ marketIds.append(marketId)
429
+ result.append(self.parse_market(market))
430
+ return result
431
+
432
+ def parse_market(self, market) -> Market:
433
+ id = self.safe_string(market, 'marketCode', '')
434
+ parts = id.split('-')
435
+ baseId = self.safe_string(parts, 0)
436
+ quoteId = self.safe_string(parts, 1)
437
+ base = self.safe_currency_code(baseId)
438
+ quote = self.safe_currency_code(quoteId)
439
+ symbol = base + '/' + quote
440
+ type = self.safe_string_lower(market, 'type', 'spot') # markets from v3/tickers are spot and have no type
441
+ settleId: Str = None
442
+ settle: Str = None
443
+ isFuture = (type == 'future') # the exchange has only perpetual futures
444
+ if isFuture:
445
+ type = 'swap'
446
+ settleId = 'OX'
447
+ settle = self.safe_currency_code('OX')
448
+ symbol = symbol + ':' + settle
449
+ isSpot = type == 'spot'
450
+ return self.safe_market_structure({
451
+ 'id': id,
452
+ 'numericId': None,
453
+ 'symbol': symbol,
454
+ 'base': base,
455
+ 'quote': quote,
456
+ 'settle': settle,
457
+ 'baseId': baseId,
458
+ 'quoteId': quoteId,
459
+ 'settleId': settleId,
460
+ 'type': type,
461
+ 'spot': isSpot,
462
+ 'margin': False,
463
+ 'swap': isFuture,
464
+ 'future': False,
465
+ 'option': False,
466
+ 'active': True,
467
+ 'contract': isFuture,
468
+ 'linear': True if isFuture else None,
469
+ 'inverse': False if isFuture else None,
470
+ 'taker': self.fees['trading']['taker'],
471
+ 'maker': self.fees['trading']['maker'],
472
+ 'contractSize': 1 if isFuture else None,
473
+ 'expiry': None,
474
+ 'expiryDatetime': None,
475
+ 'strike': None,
476
+ 'optionType': None,
477
+ 'precision': {
478
+ 'amount': None, # todo find it out
479
+ 'price': self.safe_number(market, 'tickSize'),
480
+ },
481
+ 'limits': {
482
+ 'leverage': {
483
+ 'min': None,
484
+ 'max': None,
485
+ },
486
+ 'amount': {
487
+ 'min': self.safe_number(market, 'minSize'),
488
+ 'max': None,
489
+ },
490
+ 'price': {
491
+ 'min': None,
492
+ 'max': None,
493
+ },
494
+ 'cost': {
495
+ 'min': None,
496
+ 'max': None,
497
+ },
498
+ },
499
+ 'created': self.safe_integer(market, 'listedAt'),
500
+ 'index': None,
501
+ 'info': market,
502
+ })
503
+
504
+ def fetch_currencies(self, params={}) -> Currencies:
505
+ """
506
+ fetches all available currencies on an exchange
507
+ :see: https://docs.ox.fun/?json#get-v3-assets
508
+ :param dict [params]: extra parameters specific to the exchange API endpoint
509
+ :returns dict: an associative dictionary of currencies
510
+ """
511
+ response = self.publicGetV3Assets(params)
512
+ #
513
+ # {
514
+ # "success": True,
515
+ # "data": [
516
+ # {
517
+ # "asset": "OX",
518
+ # "isCollateral": True,
519
+ # "loanToValue": "1.000000000",
520
+ # "loanToValueFactor": "0.000000000",
521
+ # "networkList": [
522
+ # {
523
+ # "network": "BNBSmartChain",
524
+ # "tokenId": "0x78a0A62Fba6Fb21A83FE8a3433d44C73a4017A6f",
525
+ # "transactionPrecision": "18",
526
+ # "isWithdrawalFeeChargedToUser": True,
527
+ # "canDeposit": True,
528
+ # "canWithdraw": False,
529
+ # "minDeposit": "0.00010",
530
+ # "minWithdrawal": "0.00010"
531
+ # },
532
+ # {
533
+ # "network": "Polygon",
534
+ # "tokenId": "0x78a0A62Fba6Fb21A83FE8a3433d44C73a4017A6f",
535
+ # "transactionPrecision": "18",
536
+ # "isWithdrawalFeeChargedToUser": True,
537
+ # "canDeposit": True,
538
+ # "canWithdraw": False,
539
+ # "minDeposit": "0.00010",
540
+ # "minWithdrawal": "0.00010"
541
+ # },
542
+ # {
543
+ # "network": "Arbitrum",
544
+ # "tokenId": "0xba0Dda8762C24dA9487f5FA026a9B64b695A07Ea",
545
+ # "transactionPrecision": "18",
546
+ # "isWithdrawalFeeChargedToUser": True,
547
+ # "canDeposit": True,
548
+ # "canWithdraw": True,
549
+ # "minDeposit": "0.00010",
550
+ # "minWithdrawal": "0.00010"
551
+ # },
552
+ # {
553
+ # "network": "Ethereum",
554
+ # "tokenId": "0xba0Dda8762C24dA9487f5FA026a9B64b695A07Ea",
555
+ # "transactionPrecision": "18",
556
+ # "isWithdrawalFeeChargedToUser": True,
557
+ # "canDeposit": True,
558
+ # "canWithdraw": True,
559
+ # "minDeposit": "0.00010",
560
+ # "minWithdrawal": "0.00010"
561
+ # },
562
+ # {
563
+ # "network": "Arbitrum",
564
+ # "tokenId": "0x78a0A62Fba6Fb21A83FE8a3433d44C73a4017A6f",
565
+ # "transactionPrecision": "18",
566
+ # "isWithdrawalFeeChargedToUser": True,
567
+ # "canDeposit": True,
568
+ # "canWithdraw": False,
569
+ # "minDeposit": "0.00010",
570
+ # "minWithdrawal": "0.00010"
571
+ # },
572
+ # {
573
+ # "network": "Avalanche",
574
+ # "tokenId": "0x78a0A62Fba6Fb21A83FE8a3433d44C73a4017A6f",
575
+ # "transactionPrecision": "18",
576
+ # "isWithdrawalFeeChargedToUser": True,
577
+ # "canDeposit": True,
578
+ # "canWithdraw": False,
579
+ # "minDeposit": "0.00010",
580
+ # "minWithdrawal": "0.00010"
581
+ # },
582
+ # {
583
+ # "network": "Solana",
584
+ # "tokenId": "DV3845GEAVXfwpyVGGgWbqBVCtzHdCXNCGfcdboSEuZz",
585
+ # "transactionPrecision": "8",
586
+ # "isWithdrawalFeeChargedToUser": True,
587
+ # "canDeposit": True,
588
+ # "canWithdraw": True,
589
+ # "minDeposit": "0.00010",
590
+ # "minWithdrawal": "0.00010"
591
+ # },
592
+ # {
593
+ # "network": "Ethereum",
594
+ # "tokenId": "0x78a0A62Fba6Fb21A83FE8a3433d44C73a4017A6f",
595
+ # "transactionPrecision": "18",
596
+ # "isWithdrawalFeeChargedToUser": True,
597
+ # "canDeposit": True,
598
+ # "canWithdraw": False,
599
+ # "minDeposit": "0.00010",
600
+ # "minWithdrawal": "0.00010"
601
+ # }
602
+ # ]
603
+ # },
604
+ # {
605
+ # "asset": "BTC",
606
+ # "isCollateral": True,
607
+ # "loanToValue": "0.950000000",
608
+ # "loanToValueFactor": "0.000000000",
609
+ # "networkList": [
610
+ # {
611
+ # "network": "Bitcoin",
612
+ # "transactionPrecision": "8",
613
+ # "isWithdrawalFeeChargedToUser": True,
614
+ # "canDeposit": True,
615
+ # "canWithdraw": True,
616
+ # "minDeposit": "0.00010",
617
+ # "minWithdrawal": "0.00010"
618
+ # }
619
+ # ]
620
+ # },
621
+ # {
622
+ # "asset": "USDT.ARB",
623
+ # "isCollateral": True,
624
+ # "loanToValue": "0.950000000",
625
+ # "loanToValueFactor": "0.000000000",
626
+ # "networkList": [
627
+ # {
628
+ # "network": "Arbitrum",
629
+ # "tokenId": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
630
+ # "transactionPrecision": "18",
631
+ # "isWithdrawalFeeChargedToUser": True,
632
+ # "canDeposit": True,
633
+ # "canWithdraw": True,
634
+ # "minDeposit": "0.00010",
635
+ # "minWithdrawal": "0.00010"
636
+ # }
637
+ # ]
638
+ # },
639
+ # ...
640
+ # ]
641
+ # }
642
+ #
643
+ data = self.safe_list(response, 'data', [])
644
+ result: dict = {}
645
+ for i in range(0, len(data)):
646
+ currency = data[i]
647
+ fullId = self.safe_string(currency, 'asset', '')
648
+ parts = fullId.split('.')
649
+ id = parts[0]
650
+ code = self.safe_currency_code(id)
651
+ networks: dict = {}
652
+ chains = self.safe_list(currency, 'networkList', [])
653
+ currencyMaxPrecision: Str = None
654
+ currencyDepositEnabled: Bool = None
655
+ currencyWithdrawEnabled: Bool = None
656
+ for j in range(0, len(chains)):
657
+ chain = chains[j]
658
+ networkId = self.safe_string(chain, 'network')
659
+ networkCode = self.network_id_to_code(networkId)
660
+ deposit = self.safe_bool(chain, 'canDeposit')
661
+ withdraw = self.safe_bool(chain, 'canWithdraw')
662
+ active = (deposit and withdraw)
663
+ minDeposit = self.safe_string(chain, 'minDeposit')
664
+ minWithdrawal = self.safe_string(chain, 'minWithdrawal')
665
+ precision = self.parse_precision(self.safe_string(chain, 'transactionPrecision'))
666
+ networks[networkCode] = {
667
+ 'id': networkId,
668
+ 'network': networkCode,
669
+ 'margin': None,
670
+ 'deposit': deposit,
671
+ 'withdraw': withdraw,
672
+ 'active': active,
673
+ 'fee': None,
674
+ 'precision': self.parse_number(precision),
675
+ 'limits': {
676
+ 'deposit': {
677
+ 'min': minDeposit,
678
+ 'max': None,
679
+ },
680
+ 'withdraw': {
681
+ 'min': minWithdrawal,
682
+ 'max': None,
683
+ },
684
+ },
685
+ 'info': chain,
686
+ }
687
+ if (currencyDepositEnabled is None) or deposit:
688
+ currencyDepositEnabled = deposit
689
+ if (currencyWithdrawEnabled is None) or withdraw:
690
+ currencyWithdrawEnabled = withdraw
691
+ if (currencyMaxPrecision is None) or Precise.string_gt(currencyMaxPrecision, precision):
692
+ currencyMaxPrecision = precision
693
+ if code in result:
694
+ # checking for specific ids.ARB
695
+ networks = self.extend(result[code]['networks'], networks)
696
+ result[code] = {
697
+ 'id': id,
698
+ 'code': code,
699
+ 'name': None,
700
+ 'type': None,
701
+ 'active': None,
702
+ 'deposit': currencyDepositEnabled,
703
+ 'withdraw': currencyWithdrawEnabled,
704
+ 'fee': None,
705
+ 'precision': self.parse_number(currencyMaxPrecision),
706
+ 'limits': {
707
+ 'amount': {
708
+ 'min': None,
709
+ 'max': None,
710
+ },
711
+ 'withdraw': {
712
+ 'min': None,
713
+ 'max': None,
714
+ },
715
+ },
716
+ 'networks': networks,
717
+ 'info': currency,
718
+ }
719
+ return result
720
+
721
+ def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
722
+ """
723
+ fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
724
+ :see: https://docs.ox.fun/?json#get-v3-tickers
725
+ :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
726
+ :param dict [params]: extra parameters specific to the exchange API endpoint
727
+ :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
728
+ """
729
+ self.load_markets()
730
+ symbols = self.market_symbols(symbols)
731
+ response = self.publicGetV3Tickers(params)
732
+ #
733
+ # {
734
+ # "success": True,
735
+ # "data": [
736
+ # {
737
+ # "marketCode": "NII-USDT",
738
+ # "markPrice": "0",
739
+ # "open24h": "0",
740
+ # "high24h": "0",
741
+ # "low24h": "0",
742
+ # "volume24h": "0",
743
+ # "currencyVolume24h": "0",
744
+ # "openInterest": "0",
745
+ # "lastTradedPrice": "0",
746
+ # "lastTradedQuantity": "0",
747
+ # "lastUpdatedAt": "1714853388621"
748
+ # },
749
+ # {
750
+ # "marketCode": "GEC-USDT",
751
+ # "markPrice": "0",
752
+ # "open24h": "0",
753
+ # "high24h": "0",
754
+ # "low24h": "0",
755
+ # "volume24h": "0",
756
+ # "currencyVolume24h": "0",
757
+ # "openInterest": "0",
758
+ # "lastTradedPrice": "0",
759
+ # "lastTradedQuantity": "0",
760
+ # "lastUpdatedAt": "1714853388621"
761
+ # },
762
+ # {
763
+ # "marketCode": "DYM-USD-SWAP-LIN",
764
+ # "markPrice": "3.321",
765
+ # "open24h": "3.315",
766
+ # "high24h": "3.356",
767
+ # "low24h": "3.255",
768
+ # "volume24h": "0",
769
+ # "currencyVolume24h": "0",
770
+ # "openInterest": "1768.1",
771
+ # "lastTradedPrice": "3.543",
772
+ # "lastTradedQuantity": "1.0",
773
+ # "lastUpdatedAt": "1714853388102"
774
+ # },
775
+ # ...
776
+ # ]
777
+ # }
778
+ #
779
+ tickers = self.safe_list(response, 'data', [])
780
+ return self.parse_tickers(tickers, symbols)
781
+
782
+ def fetch_ticker(self, symbol: str, params={}) -> Ticker:
783
+ """
784
+ fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
785
+ :see: https://docs.ox.fun/?json#get-v3-tickers
786
+ :param str symbol: unified symbol of the market to fetch the ticker for
787
+ :param dict [params]: extra parameters specific to the exchange API endpoint
788
+ :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
789
+ """
790
+ self.load_markets()
791
+ market = self.market(symbol)
792
+ request: dict = {
793
+ 'marketCode': market['id'],
794
+ }
795
+ response = self.publicGetV3Tickers(self.extend(request, params))
796
+ #
797
+ # {
798
+ # "success": True,
799
+ # "data": [
800
+ # {
801
+ # "marketCode": "BTC-USD-SWAP-LIN",
802
+ # "markPrice": "64276",
803
+ # "open24h": "63674",
804
+ # "high24h": "64607",
805
+ # "low24h": "62933",
806
+ # "volume24h": "306317655.80000",
807
+ # "currencyVolume24h": "48.06810",
808
+ # "openInterest": "72.39250",
809
+ # "lastTradedPrice": "64300.0",
810
+ # "lastTradedQuantity": "1.0",
811
+ # "lastUpdatedAt": "1714925196034"
812
+ # }
813
+ # ]
814
+ # }
815
+ #
816
+ data = self.safe_list(response, 'data', [])
817
+ ticker = self.safe_dict(data, 0, {})
818
+ return self.parse_ticker(ticker, market)
819
+
820
+ def parse_ticker(self, ticker, market: Market = None) -> Ticker:
821
+ #
822
+ # {
823
+ # "marketCode": "BTC-USD-SWAP-LIN",
824
+ # "markPrice": "64276",
825
+ # "open24h": "63674",
826
+ # "high24h": "64607",
827
+ # "low24h": "62933",
828
+ # "volume24h": "306317655.80000",
829
+ # "currencyVolume24h": "48.06810",
830
+ # "openInterest": "72.39250",
831
+ # "lastTradedPrice": "64300.0",
832
+ # "lastTradedQuantity": "1.0",
833
+ # "lastUpdatedAt": "1714925196034"
834
+ # }
835
+ #
836
+ timestamp = self.safe_integer(ticker, 'lastUpdatedAt')
837
+ marketId = self.safe_string(ticker, 'marketCode')
838
+ market = self.safe_market(marketId, market)
839
+ symbol = market['symbol']
840
+ last = self.safe_string(ticker, 'lastTradedPrice')
841
+ return self.safe_ticker({
842
+ 'symbol': symbol,
843
+ 'timestamp': timestamp,
844
+ 'datetime': self.iso8601(timestamp),
845
+ 'high': self.safe_string(ticker, 'high24h'),
846
+ 'low': self.safe_string(ticker, 'low24h'),
847
+ 'bid': None,
848
+ 'bidVolume': None,
849
+ 'ask': None,
850
+ 'askVolume': None,
851
+ 'vwap': None,
852
+ 'open': self.safe_string(ticker, 'open24h'),
853
+ 'close': last,
854
+ 'last': last,
855
+ 'previousClose': None,
856
+ 'change': None,
857
+ 'percentage': None,
858
+ 'average': None,
859
+ 'baseVolume': self.safe_string(ticker, 'currencyVolume24h'),
860
+ 'quoteVolume': None, # the exchange returns cost in OX
861
+ 'info': ticker,
862
+ }, market)
863
+
864
+ def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
865
+ """
866
+ fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
867
+ :see: https://docs.ox.fun/?json#get-v3-candles
868
+ :param str symbol: unified symbol of the market to fetch OHLCV data for
869
+ :param str timeframe: the length of time each candle represents
870
+ :param int [since]: timestamp in ms of the earliest candle to fetch(default 24 hours ago)
871
+ :param int [limit]: the maximum amount of candles to fetch(default 200, max 500)
872
+ :param dict [params]: extra parameters specific to the exchange API endpoint
873
+ :param int [params.until]: timestamp in ms of the latest candle to fetch(default now)
874
+ :returns int[][]: A list of candles ordered, open, high, low, close, volume
875
+ """
876
+ self.load_markets()
877
+ market = self.market(symbol)
878
+ timeframe = self.safe_string(self.timeframes, timeframe, timeframe)
879
+ request: dict = {
880
+ 'marketCode': market['id'],
881
+ 'timeframe': timeframe,
882
+ }
883
+ if since is not None:
884
+ request['startTime'] = since # startTime and endTime must be within 7 days of each other
885
+ if limit is not None:
886
+ request['limit'] = limit
887
+ until = self.safe_integer(params, 'until')
888
+ if until is not None:
889
+ request['endTime'] = until
890
+ params = self.omit(params, 'until')
891
+ elif since is not None:
892
+ request['endTime'] = self.sum(since, 7 * 24 * 60 * 60 * 1000) # for the exchange not to raise an exception if since is younger than 7 days
893
+ response = self.publicGetV3Candles(self.extend(request, params))
894
+ #
895
+ # {
896
+ # "success": True,
897
+ # "timeframe": "3600s",
898
+ # "data": [
899
+ # {
900
+ # "open": "0.03240000",
901
+ # "high": "0.03240000",
902
+ # "low": "0.03240000",
903
+ # "close": "0.03240000",
904
+ # "volume": "0",
905
+ # "currencyVolume": "0",
906
+ # "openedAt": "1714906800000"
907
+ # },
908
+ # {
909
+ # "open": "0.03240000",
910
+ # "high": "0.03240000",
911
+ # "low": "0.03240000",
912
+ # "close": "0.03240000",
913
+ # "volume": "0",
914
+ # "currencyVolume": "0",
915
+ # "openedAt": "1714903200000"
916
+ # },
917
+ # ...
918
+ # ]
919
+ # }
920
+ #
921
+ result = self.safe_list(response, 'data', [])
922
+ return self.parse_ohlcvs(result, market, timeframe, since, limit)
923
+
924
+ def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
925
+ #
926
+ # {
927
+ # "open": "0.03240000",
928
+ # "high": "0.03240000",
929
+ # "low": "0.03240000",
930
+ # "close": "0.03240000",
931
+ # "volume": "0",
932
+ # "currencyVolume": "0",
933
+ # "openedAt": "1714906800000"
934
+ # }
935
+ #
936
+ return [
937
+ self.safe_integer(ohlcv, 'openedAt'),
938
+ self.safe_number(ohlcv, 'open'),
939
+ self.safe_number(ohlcv, 'high'),
940
+ self.safe_number(ohlcv, 'low'),
941
+ self.safe_number(ohlcv, 'close'),
942
+ self.safe_number(ohlcv, 'currencyVolume'),
943
+ ]
944
+
945
+ def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
946
+ """
947
+ fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
948
+ :see: https://docs.ox.fun/?json#get-v3-depth
949
+ :param str symbol: unified symbol of the market to fetch the order book for
950
+ :param int [limit]: the maximum amount of order book entries to return(default 5, max 100)
951
+ :param dict [params]: extra parameters specific to the exchange API endpoint
952
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
953
+ """
954
+ self.load_markets()
955
+ market = self.market(symbol)
956
+ request: dict = {
957
+ 'marketCode': market['id'],
958
+ }
959
+ if limit is not None:
960
+ request['level'] = limit
961
+ response = self.publicGetV3Depth(self.extend(request, params))
962
+ #
963
+ # {
964
+ # "success": True,
965
+ # "level": "5",
966
+ # "data": {
967
+ # "marketCode": "BTC-USD-SWAP-LIN",
968
+ # "lastUpdatedAt": "1714933499266",
969
+ # "asks": [
970
+ # [64073.0, 8.4622],
971
+ # [64092.0, 8.1912],
972
+ # [64111.0, 8.0669],
973
+ # [64130.0, 11.7195],
974
+ # [64151.0, 10.1798]
975
+ # ],
976
+ # "bids": [
977
+ # [64022.0, 10.1292],
978
+ # [64003.0, 8.1619],
979
+ # [64000.0, 1.0],
980
+ # [63984.0, 12.7724],
981
+ # [63963.0, 11.0073]
982
+ # ]
983
+ # }
984
+ # }
985
+ #
986
+ data = self.safe_dict(response, 'data', {})
987
+ timestamp = self.safe_integer(data, 'lastUpdatedAt')
988
+ return self.parse_order_book(data, market['symbol'], timestamp)
989
+
990
+ def fetch_funding_rates(self, symbols: Strings = None, params={}):
991
+ """
992
+ :see: https://docs.ox.fun/?json#get-v3-funding-estimates
993
+ fetch the current funding rates
994
+ :param str[] symbols: unified market symbols
995
+ :param dict [params]: extra parameters specific to the exchange API endpoint
996
+ :returns Order[]: an array of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-structure>`
997
+ """
998
+ self.load_markets()
999
+ symbols = self.market_symbols(symbols)
1000
+ response = self.publicGetV3FundingEstimates(params)
1001
+ #
1002
+ # {
1003
+ # "success": True,
1004
+ # "data": [
1005
+ # {
1006
+ # "marketCode": "OX-USD-SWAP-LIN",
1007
+ # "fundingAt": "1715515200000",
1008
+ # "estFundingRate": "0.000200000"
1009
+ # },
1010
+ # {
1011
+ # "marketCode": "BTC-USD-SWAP-LIN",
1012
+ # "fundingAt": "1715515200000",
1013
+ # "estFundingRate": "0.000003"
1014
+ # },
1015
+ # ...
1016
+ # ]
1017
+ # }
1018
+ #
1019
+ data = self.safe_list(response, 'data', [])
1020
+ result = self.parse_funding_rates(data)
1021
+ return self.filter_by_array(result, 'symbol', symbols)
1022
+
1023
+ def parse_funding_rate(self, fundingRate, market: Market = None):
1024
+ #
1025
+ # {
1026
+ # "marketCode": "OX-USD-SWAP-LIN",
1027
+ # "fundingAt": "1715515200000",
1028
+ # "estFundingRate": "0.000200000"
1029
+ # },
1030
+ #
1031
+ #
1032
+ symbol = self.safe_string(fundingRate, 'marketCode')
1033
+ market = self.market(symbol)
1034
+ estFundingRateTimestamp = self.safe_integer(fundingRate, 'fundingAt')
1035
+ return {
1036
+ 'info': fundingRate,
1037
+ 'symbol': market['symbol'],
1038
+ 'markPrice': None,
1039
+ 'indexPrice': None,
1040
+ 'interestRate': self.parse_number('0'),
1041
+ 'estimatedSettlePrice': None,
1042
+ 'timestamp': estFundingRateTimestamp,
1043
+ 'datetime': self.iso8601(estFundingRateTimestamp),
1044
+ 'fundingRate': self.safe_number(fundingRate, 'estFundingRate'),
1045
+ 'fundingTimestamp': None,
1046
+ 'fundingDatetime': None,
1047
+ 'nextFundingRate': None,
1048
+ 'nextFundingTimestamp': None,
1049
+ 'nextFundingDatetime': None,
1050
+ 'previousFundingRate': None,
1051
+ 'previousFundingTimestamp': None,
1052
+ 'previousFundingDatetime': None,
1053
+ }
1054
+
1055
+ def fetch_funding_rate_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
1056
+ """
1057
+ Fetches the history of funding rates
1058
+ :see: https://docs.ox.fun/?json#get-v3-funding-rates
1059
+ :param str symbol: unified symbol of the market to fetch trades for
1060
+ :param int [since]: timestamp in ms of the earliest trade to fetch(default 24 hours ago)
1061
+ :param int [limit]: the maximum amount of trades to fetch(default 200, max 500)
1062
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1063
+ :param int [params.until]: timestamp in ms of the latest trade to fetch(default now)
1064
+ :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
1065
+ """
1066
+ self.load_markets()
1067
+ market = self.market(symbol)
1068
+ request: dict = {
1069
+ 'marketCode': market['id'],
1070
+ }
1071
+ if since is not None:
1072
+ request['startTime'] = since # startTime and endTime must be within 7 days of each other
1073
+ if limit is not None:
1074
+ request['limit'] = limit
1075
+ until = self.safe_integer(params, 'until')
1076
+ if until is not None:
1077
+ request['endTime'] = until
1078
+ params = self.omit(params, 'until')
1079
+ response = self.publicGetV3FundingRates(self.extend(request, params))
1080
+ #
1081
+ # {
1082
+ # success: True,
1083
+ # data: [
1084
+ # {
1085
+ # marketCode: 'NEAR-USD-SWAP-LIN',
1086
+ # fundingRate: '-0.000010000',
1087
+ # createdAt: '1715428870755'
1088
+ # },
1089
+ # {
1090
+ # marketCode: 'ENA-USD-SWAP-LIN',
1091
+ # fundingRate: '0.000150000',
1092
+ # createdAt: '1715428868616'
1093
+ # },
1094
+ # ...
1095
+ # }
1096
+ #
1097
+ data = self.safe_list(response, 'data', [])
1098
+ return self.parse_funding_rate_histories(data, market, since, limit)
1099
+
1100
+ def parse_funding_rate_history(self, info, market: Market = None):
1101
+ #
1102
+ # {
1103
+ # success: True,
1104
+ # data: [
1105
+ # {
1106
+ # marketCode: 'NEAR-USD-SWAP-LIN',
1107
+ # fundingRate: '-0.000010000',
1108
+ # createdAt: '1715428870755'
1109
+ # },
1110
+ # {
1111
+ # marketCode: 'ENA-USD-SWAP-LIN',
1112
+ # fundingRate: '0.000150000',
1113
+ # createdAt: '1715428868616'
1114
+ # },
1115
+ # ...
1116
+ # }
1117
+ #
1118
+ marketId = self.safe_string(info, 'marketCode')
1119
+ market = self.safe_market(marketId, market)
1120
+ symbol = market['symbol']
1121
+ timestamp = self.safe_integer(info, 'createdAt')
1122
+ return {
1123
+ 'info': info,
1124
+ 'symbol': symbol,
1125
+ 'fundingRate': self.safe_number(info, 'fundingRate'),
1126
+ 'timestamp': timestamp,
1127
+ 'datetime': self.iso8601(timestamp),
1128
+ }
1129
+
1130
+ def fetch_funding_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
1131
+ """
1132
+ fetches the history of funding payments
1133
+ :see: https://docs.ox.fun/?json#get-v3-funding
1134
+ :param str symbol: unified symbol of the market to fetch trades for
1135
+ :param int [since]: timestamp in ms of the earliest trade to fetch(default 24 hours ago)
1136
+ :param int [limit]: the maximum amount of trades to fetch(default 200, max 500)
1137
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1138
+ :param int [params.until]: timestamp in ms of the latest trade to fetch(default now)
1139
+ :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
1140
+ """
1141
+ self.load_markets()
1142
+ market = self.market(symbol)
1143
+ request: dict = {
1144
+ 'marketCode': market['id'],
1145
+ }
1146
+ if since is not None:
1147
+ request['startTime'] = since # startTime and endTime must be within 7 days of each other
1148
+ if limit is not None:
1149
+ request['limit'] = limit
1150
+ until = self.safe_integer(params, 'until')
1151
+ if until is not None:
1152
+ request['endTime'] = until
1153
+ params = self.omit(params, 'until')
1154
+ response = self.privateGetV3Funding(self.extend(request, params))
1155
+ #
1156
+ # {
1157
+ # success: True,
1158
+ # data: [
1159
+ # {
1160
+ # id: '966709913041305605',
1161
+ # marketCode: 'ETH-USD-SWAP-LIN',
1162
+ # payment: '-0.00430822',
1163
+ # fundingRate: '0.000014',
1164
+ # position: '0.001',
1165
+ # indexPrice: '3077.3',
1166
+ # createdAt: '1715086852890'
1167
+ # },
1168
+ # {
1169
+ # id: '966698111997509637',
1170
+ # marketCode: 'ETH-USD-SWAP-LIN',
1171
+ # payment: '-0.0067419',
1172
+ # fundingRate: '0.000022',
1173
+ # position: '0.001',
1174
+ # indexPrice: '3064.5',
1175
+ # createdAt: '1715083251516'
1176
+ # },
1177
+ # ...
1178
+ # ]
1179
+ # }
1180
+ #
1181
+ result = self.safe_list(response, 'data', [])
1182
+ return self.parse_incomes(result, market, since, limit)
1183
+
1184
+ def parse_income(self, income, market: Market = None):
1185
+ #
1186
+ # {
1187
+ # id: '966709913041305605',
1188
+ # marketCode: 'ETH-USD-SWAP-LIN',
1189
+ # payment: '-0.00430822',
1190
+ # fundingRate: '0.000014',
1191
+ # position: '0.001',
1192
+ # indexPrice: '3077.3',
1193
+ # createdAt: '1715086852890'
1194
+ # },
1195
+ #
1196
+ marketId = self.safe_string(income, 'marketCode')
1197
+ symbol = self.safe_symbol(marketId, market)
1198
+ amount = self.safe_number(income, 'payment')
1199
+ code = self.safe_currency_code('OX')
1200
+ id = self.safe_string(income, 'id')
1201
+ timestamp = self.safe_timestamp(income, 'createdAt')
1202
+ rate = self.safe_number(income, 'fundingRate')
1203
+ return {
1204
+ 'info': income,
1205
+ 'symbol': symbol,
1206
+ 'code': code,
1207
+ 'timestamp': timestamp,
1208
+ 'datetime': self.iso8601(timestamp),
1209
+ 'id': id,
1210
+ 'amount': amount,
1211
+ 'rate': rate,
1212
+ }
1213
+
1214
+ def fetch_leverage_tiers(self, symbols: Strings = None, params={}):
1215
+ """
1216
+ retrieve information on the maximum leverage, and maintenance margin for trades of varying trade sizes, if a market has a leverage tier of 0, then the leverage tiers cannot be obtained for self market
1217
+ :see: https://docs.ox.fun/?json#get-v3-leverage-tiers
1218
+ :param str[] [symbols]: list of unified market symbols
1219
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1220
+ :returns dict: a dictionary of `leverage tiers structures <https://docs.ccxt.com/#/?id=leverage-tiers-structure>`, indexed by market symbols
1221
+ """
1222
+ self.load_markets()
1223
+ response = self.publicGetV3LeverageTiers(params)
1224
+ #
1225
+ # {
1226
+ # success: True,
1227
+ # data: [
1228
+ # {
1229
+ # marketCode: 'SOL-USD-SWAP-LIN',
1230
+ # tiers: [
1231
+ # {
1232
+ # tier: '1',
1233
+ # leverage: '10',
1234
+ # positionFloor: '0',
1235
+ # positionCap: '200000000',
1236
+ # initialMargin: '0.1',
1237
+ # maintenanceMargin: '0.05',
1238
+ # maintenanceAmount: '0'
1239
+ # },
1240
+ # {
1241
+ # tier: '2',
1242
+ # leverage: '5',
1243
+ # positionFloor: '200000000',
1244
+ # positionCap: '280000000',
1245
+ # initialMargin: '0.2',
1246
+ # maintenanceMargin: '0.1',
1247
+ # maintenanceAmount: '7000000'
1248
+ # },
1249
+ # {
1250
+ # tier: '3',
1251
+ # leverage: '4',
1252
+ # positionFloor: '280000000',
1253
+ # positionCap: '460000000',
1254
+ # initialMargin: '0.25',
1255
+ # maintenanceMargin: '0.125',
1256
+ # maintenanceAmount: '14000000'
1257
+ # },
1258
+ # ...
1259
+ # ]
1260
+ # },
1261
+ # ...
1262
+ # ]
1263
+ # }
1264
+ #
1265
+ data = self.safe_list(response, 'data', [])
1266
+ return self.parse_leverage_tiers(data, symbols, 'marketCode')
1267
+
1268
+ def parse_market_leverage_tiers(self, info, market: Market = None):
1269
+ #
1270
+ # {
1271
+ # marketCode: 'SOL-USD-SWAP-LIN',
1272
+ # tiers: [
1273
+ # {
1274
+ # tier: '1',
1275
+ # leverage: '10',
1276
+ # positionFloor: '0',
1277
+ # positionCap: '200000000',
1278
+ # initialMargin: '0.1',
1279
+ # maintenanceMargin: '0.05',
1280
+ # maintenanceAmount: '0'
1281
+ # ...
1282
+ # ]
1283
+ # },
1284
+ #
1285
+ marketId = self.safe_string(info, 'marketCode')
1286
+ market = self.safe_market(marketId, market)
1287
+ listOfTiers = self.safe_list(info, 'tiers', [])
1288
+ tiers = []
1289
+ for j in range(0, len(listOfTiers)):
1290
+ tier = listOfTiers[j]
1291
+ tiers.append({
1292
+ 'tier': self.safe_number(tier, 'tier'),
1293
+ 'currency': market['settle'],
1294
+ 'minNotional': self.safe_number(tier, 'positionFloor'),
1295
+ 'maxNotional': self.safe_number(tier, 'positionCap'),
1296
+ 'maintenanceMarginRate': self.safe_number(tier, 'maintenanceMargin'),
1297
+ 'maxLeverage': self.safe_number(tier, 'leverage'),
1298
+ 'info': tier,
1299
+ })
1300
+ return tiers
1301
+
1302
+ def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
1303
+ """
1304
+ get the list of most recent trades for a particular symbol
1305
+ :see: https://docs.ox.fun/?json#get-v3-exchange-trades
1306
+ :param str symbol: unified symbol of the market to fetch trades for
1307
+ :param int [since]: timestamp in ms of the earliest trade to fetch(default 24 hours ago)
1308
+ :param int [limit]: the maximum amount of trades to fetch(default 200, max 500)
1309
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1310
+ :param int [params.until]: timestamp in ms of the latest trade to fetch(default now)
1311
+ :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
1312
+ """
1313
+ self.load_markets()
1314
+ market = self.market(symbol)
1315
+ request: dict = {
1316
+ 'marketCode': market['id'],
1317
+ }
1318
+ if since is not None:
1319
+ request['startTime'] = since # startTime and endTime must be within 7 days of each other
1320
+ if limit is not None:
1321
+ request['limit'] = limit
1322
+ until = self.safe_integer(params, 'until')
1323
+ if until is not None:
1324
+ request['endTime'] = until
1325
+ params = self.omit(params, 'until')
1326
+ elif since is not None:
1327
+ request['endTime'] = self.sum(since, 7 * 24 * 60 * 60 * 1000) # for the exchange not to raise an exception if since is younger than 7 days
1328
+ response = self.publicGetV3ExchangeTrades(self.extend(request, params))
1329
+ #
1330
+ # {
1331
+ # "success": True,
1332
+ # "data": [
1333
+ # {
1334
+ # "marketCode": "BTC-USD-SWAP-LIN",
1335
+ # "matchPrice": "63900",
1336
+ # "matchQuantity": "1",
1337
+ # "side": "SELL",
1338
+ # "matchType": "TAKER",
1339
+ # "matchedAt": "1714934112352"
1340
+ # },
1341
+ # ...
1342
+ # ]
1343
+ # }
1344
+ #
1345
+ data = self.safe_list(response, 'data', [])
1346
+ return self.parse_trades(data, market, since, limit)
1347
+
1348
+ def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
1349
+ """
1350
+ fetch all trades made by the user
1351
+ :see: https://docs.ox.fun/?json#get-v3-trades
1352
+ :param str symbol: unified market symbol
1353
+ :param int [since]: the earliest time in ms to fetch trades for
1354
+ :param int [limit]: the maximum amount of trades to fetch(default 200, max 500)
1355
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1356
+ :param int [params.until]: timestamp in ms of the latest trade to fetch(default now)
1357
+ :returns Trade[]: a list of `trade structures <https://github.com/ccxt/ccxt/wiki/Manual#trade-structure>`
1358
+ """
1359
+ self.load_markets()
1360
+ request: dict = {}
1361
+ market: Market = None
1362
+ if symbol is not None:
1363
+ market = self.market(symbol)
1364
+ request['marketCode'] = market['id']
1365
+ if since is not None: # startTime and endTime must be within 7 days of each other
1366
+ request['startTime'] = since
1367
+ if limit is not None:
1368
+ request['limit'] = limit
1369
+ until = self.safe_integer(params, 'until')
1370
+ if until is not None:
1371
+ request['endTime'] = until
1372
+ params = self.omit(params, 'until')
1373
+ elif since is not None:
1374
+ request['endTime'] = self.sum(since, 7 * 24 * 60 * 60 * 1000) # for the exchange not to raise an exception if since is younger than 7 days
1375
+ response = self.privateGetV3Trades(self.extend(request, params))
1376
+ #
1377
+ # {
1378
+ # "success": True,
1379
+ # "data": [
1380
+ # {
1381
+ # "orderId": "1000104903698",
1382
+ # "clientOrderId": "1715000260094",
1383
+ # "matchId": "400017129522773178",
1384
+ # "marketCode": "ETH-USD-SWAP-LIN",
1385
+ # "side": "BUY",
1386
+ # "matchedQuantity": "0.001",
1387
+ # "matchPrice": "3100.2",
1388
+ # "total": "310.02",
1389
+ # "orderMatchType": "MAKER",
1390
+ # "feeAsset": "OX",
1391
+ # "fee": "0.062004",
1392
+ # "source": "0",
1393
+ # "matchedAt": "1715000267420"
1394
+ # }
1395
+ # ]
1396
+ # }
1397
+ #
1398
+ result = self.safe_list(response, 'data', [])
1399
+ return self.parse_trades(result, market, since, limit)
1400
+
1401
+ def parse_trade(self, trade, market: Market = None) -> Trade:
1402
+ #
1403
+ # public fetchTrades
1404
+ #
1405
+ # {
1406
+ # "marketCode": "BTC-USD-SWAP-LIN",
1407
+ # "matchPrice": "63900",
1408
+ # "matchQuantity": "1",
1409
+ # "side": "SELL",
1410
+ # "matchType": "TAKER",
1411
+ # "matchedAt": "1714934112352"
1412
+ # }
1413
+ #
1414
+ #
1415
+ # private fetchMyTrades
1416
+ #
1417
+ # {
1418
+ # "orderId": "1000104903698",
1419
+ # "clientOrderId": "1715000260094",
1420
+ # "matchId": "400017129522773178",
1421
+ # "marketCode": "ETH-USD-SWAP-LIN",
1422
+ # "side": "BUY",
1423
+ # "matchedQuantity": "0.001",
1424
+ # "matchPrice": "3100.2",
1425
+ # "total": "310.02",
1426
+ # "orderMatchType": "MAKER",
1427
+ # "feeAsset": "OX",
1428
+ # "fee": "0.062004",
1429
+ # "source": "0",
1430
+ # "matchedAt": "1715000267420"
1431
+ # }
1432
+ #
1433
+ marketId = self.safe_string(trade, 'marketCode')
1434
+ market = self.safe_market(marketId, market)
1435
+ symbol = market['symbol']
1436
+ timestamp = self.safe_integer(trade, 'matchedAt')
1437
+ fee = {
1438
+ 'cost': self.safe_string(trade, 'fee'),
1439
+ 'currency': self.safe_currency_code(self.safe_string(trade, 'feeAsset')),
1440
+ }
1441
+ return self.safe_trade({
1442
+ 'id': self.safe_string(trade, 'matchId'),
1443
+ 'timestamp': timestamp,
1444
+ 'datetime': self.iso8601(timestamp),
1445
+ 'symbol': symbol,
1446
+ 'type': None,
1447
+ 'order': self.safe_string(trade, 'orderId'),
1448
+ 'side': self.safe_string_lower(trade, 'side'),
1449
+ 'takerOrMaker': self.safe_string_lower_2(trade, 'matchType', 'orderMatchType'),
1450
+ 'price': self.safe_string(trade, 'matchPrice'),
1451
+ 'amount': self.safe_string_2(trade, 'matchQuantity', 'matchedQuantity'),
1452
+ 'cost': None, # the exchange returns total cost in OX
1453
+ 'fee': fee,
1454
+ 'info': trade,
1455
+ }, market)
1456
+
1457
+ def fetch_balance(self, params={}) -> Balances:
1458
+ """
1459
+ query for balance and get the amount of funds available for trading or funds locked in orders
1460
+ :see: https://docs.ox.fun/?json#get-v3-balances
1461
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1462
+ :param str [params.asset]: currency id, if empty the exchange returns info about all currencies
1463
+ :param str [params.subAcc]: Name of sub account. If no subAcc is given, then the response contains only the account linked to the API-Key.
1464
+ :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
1465
+ """
1466
+ self.load_markets()
1467
+ response = self.privateGetV3Balances(params)
1468
+ #
1469
+ # {
1470
+ # "success": True,
1471
+ # "data": [
1472
+ # {
1473
+ # "accountId": "106490",
1474
+ # "name": "main",
1475
+ # "balances": [
1476
+ # {
1477
+ # "asset": "OX",
1478
+ # "total": "-7.55145065000",
1479
+ # "available": "-71.16445065000",
1480
+ # "reserved": "0",
1481
+ # "lastUpdatedAt": "1715000448946"
1482
+ # },
1483
+ # {
1484
+ # "asset": "ETH",
1485
+ # "total": "0.01",
1486
+ # "available": "0.01",
1487
+ # "reserved": "0",
1488
+ # "lastUpdatedAt": "1714914512750"
1489
+ # },
1490
+ # ...
1491
+ # ]
1492
+ # },
1493
+ # ...
1494
+ # ]
1495
+ # }
1496
+ #
1497
+ data = self.safe_list(response, 'data', [])
1498
+ balance = data[0]
1499
+ subAcc = self.safe_string(params, 'subAcc')
1500
+ if subAcc is not None:
1501
+ for i in range(0, len(data)):
1502
+ b = data[i]
1503
+ name = self.safe_string(b, 'name')
1504
+ if name == subAcc:
1505
+ balance = b
1506
+ break
1507
+ return self.parse_balance(balance)
1508
+
1509
+ def parse_balance(self, balance) -> Balances:
1510
+ #
1511
+ # {
1512
+ # "accountId": "106490",
1513
+ # "name": "main",
1514
+ # "balances": [
1515
+ # {
1516
+ # "asset": "OX",
1517
+ # "total": "-7.55145065000",
1518
+ # "available": "-71.16445065000",
1519
+ # "reserved": "0",
1520
+ # "lastUpdatedAt": "1715000448946"
1521
+ # },
1522
+ # {
1523
+ # "asset": "ETH",
1524
+ # "total": "0.01",
1525
+ # "available": "0.01",
1526
+ # "reserved": "0",
1527
+ # "lastUpdatedAt": "1714914512750"
1528
+ # },
1529
+ # ...
1530
+ # ]
1531
+ # }
1532
+ #
1533
+ result: dict = {
1534
+ 'info': balance,
1535
+ }
1536
+ balances = self.safe_list(balance, 'balances', [])
1537
+ for i in range(0, len(balances)):
1538
+ balanceEntry = balances[i]
1539
+ currencyId = self.safe_string(balanceEntry, 'asset')
1540
+ code = self.safe_currency_code(currencyId)
1541
+ account = self.account()
1542
+ account['total'] = self.safe_string(balanceEntry, 'total')
1543
+ account['free'] = self.safe_string(balanceEntry, 'available')
1544
+ account['used'] = self.safe_string(balanceEntry, 'reserved')
1545
+ result[code] = account
1546
+ return self.safe_balance(result)
1547
+
1548
+ def fetch_accounts(self, params={}) -> List[Account]:
1549
+ """
1550
+ fetch subaccounts associated with a profile
1551
+ :see: https://docs.ox.fun/?json#get-v3-account-names
1552
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1553
+ :returns dict: a dictionary of `account structures <https://docs.ccxt.com/#/?id=account-structure>` indexed by the account type
1554
+ """
1555
+ self.load_markets()
1556
+ # self endpoint can only be called using API keys paired with the parent account! Returns all active subaccounts.
1557
+ response = self.privateGetV3AccountNames(params)
1558
+ #
1559
+ # {
1560
+ # "success": True,
1561
+ # "data": [
1562
+ # {
1563
+ # "accountId": "106526",
1564
+ # "name": "testSubAccount"
1565
+ # },
1566
+ # ...
1567
+ # ]
1568
+ # }
1569
+ #
1570
+ data = self.safe_list(response, 'data', [])
1571
+ return self.parse_accounts(data, params)
1572
+
1573
+ def parse_account(self, account):
1574
+ #
1575
+ # {
1576
+ # "accountId": "106526",
1577
+ # "name": "testSubAccount"
1578
+ # },
1579
+ #
1580
+ return {
1581
+ 'id': self.safe_string(account, 'accountId'),
1582
+ 'type': None,
1583
+ 'code': None,
1584
+ 'info': account,
1585
+ }
1586
+
1587
+ def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
1588
+ """
1589
+ transfer currency internally between wallets on the same account
1590
+ :see: https://docs.ox.fun/?json#post-v3-transfer
1591
+ :param str code: unified currency code
1592
+ :param float amount: amount to transfer
1593
+ :param str fromAccount: account id to transfer from
1594
+ :param str toAccount: account id to transfer to
1595
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1596
+ :returns dict: a `transfer structure <https://docs.ccxt.com/#/?id=transfer-structure>`
1597
+ """
1598
+ # transferring funds between sub-accounts is restricted to API keys linked to the parent account.
1599
+ self.load_markets()
1600
+ currency = self.currency(code)
1601
+ request: dict = {
1602
+ 'asset': currency['id'],
1603
+ 'quantity': self.currency_to_precision(code, amount),
1604
+ 'fromAccount': fromAccount,
1605
+ 'toAccount': toAccount,
1606
+ }
1607
+ response = self.privatePostV3Transfer(self.extend(request, params))
1608
+ #
1609
+ # {
1610
+ # timestamp: 1715430036267,
1611
+ # datetime: '2024-05-11T12:20:36.267Z',
1612
+ # currency: 'OX',
1613
+ # amount: 10,
1614
+ # fromAccount: '106464',
1615
+ # toAccount: '106570',
1616
+ # info: {
1617
+ # asset: 'OX',
1618
+ # quantity: '10',
1619
+ # fromAccount: '106464',
1620
+ # toAccount: '106570',
1621
+ # transferredAt: '1715430036267'
1622
+ # }
1623
+ # }
1624
+ #
1625
+ data = self.safe_dict(response, 'data', {})
1626
+ return self.parse_transfer(data, currency)
1627
+
1628
+ def fetch_transfers(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
1629
+ """
1630
+ fetch a history of internal transfers made on an account
1631
+ :see: https://docs.ox.fun/?json#get-v3-transfer
1632
+ :param str code: unified currency code of the currency transferred
1633
+ :param int [since]: the earliest time in ms to fetch transfers for(default 24 hours ago)
1634
+ :param int [limit]: the maximum number of transfer structures to retrieve(default 50, max 200)
1635
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1636
+ :param int [params.until]: the latest time in ms to fetch transfers for(default time now)
1637
+ :returns dict[]: a list of `transfer structures <https://docs.ccxt.com/#/?id=transfer-structure>`
1638
+ """
1639
+ # API keys linked to the parent account can get all account transfers, while API keys linked to a sub-account can only see transfers where the sub-account is either the "fromAccount" or "toAccount"
1640
+ self.load_markets()
1641
+ request: dict = {}
1642
+ currency: Currency = None
1643
+ if code is not None:
1644
+ currency = self.currency(code)
1645
+ request['asset'] = currency['id']
1646
+ if since is not None:
1647
+ request['startTime'] = since # startTime and endTime must be within 7 days of each other
1648
+ if limit is not None:
1649
+ request['limit'] = limit
1650
+ until = self.safe_integer(params, 'until')
1651
+ if until is not None:
1652
+ request['endTime'] = until
1653
+ params = self.omit(params, 'until')
1654
+ elif since is not None:
1655
+ request['endTime'] = self.sum(since, 7 * 24 * 60 * 60 * 1000) # for the exchange not to raise an exception if since is younger than 7 days
1656
+ response = self.privateGetV3Transfer(self.extend(request, params))
1657
+ #
1658
+ # {
1659
+ # "success": True,
1660
+ # "data": [
1661
+ # {
1662
+ # "asset": "USDT",
1663
+ # "quantity": "5",
1664
+ # "fromAccount": "106490",
1665
+ # "toAccount": "106526",
1666
+ # "id": "966706320886267905",
1667
+ # "status": "COMPLETED",
1668
+ # "transferredAt": "1715085756708"
1669
+ # },
1670
+ # ...
1671
+ # ]
1672
+ # }
1673
+ #
1674
+ data = self.safe_list(response, 'data', [])
1675
+ return self.parse_transfers(data, currency, since, limit)
1676
+
1677
+ def parse_transfer(self, transfer, currency: Currency = None):
1678
+ #
1679
+ # fetchTransfers
1680
+ #
1681
+ # {
1682
+ # "asset": "USDT",
1683
+ # "quantity": "5",
1684
+ # "fromAccount": "106490",
1685
+ # "toAccount": "106526",
1686
+ # "id": "966706320886267905",
1687
+ # "status": "COMPLETED",
1688
+ # "transferredAt": "1715085756708"
1689
+ # }
1690
+ #
1691
+ timestamp = self.safe_integer(transfer, 'transferredAt')
1692
+ currencyId = self.safe_string(transfer, 'asset')
1693
+ return {
1694
+ 'id': self.safe_string(transfer, 'id'),
1695
+ 'timestamp': timestamp,
1696
+ 'datetime': self.iso8601(timestamp),
1697
+ 'currency': self.safe_currency_code(currencyId, currency),
1698
+ 'amount': self.safe_number(transfer, 'quantity'),
1699
+ 'fromAccount': self.safe_string(transfer, 'fromAccount'),
1700
+ 'toAccount': self.safe_string(transfer, 'toAccount'),
1701
+ 'status': self.parse_transfer_status(self.safe_string(transfer, 'status')),
1702
+ 'info': transfer,
1703
+ }
1704
+
1705
+ def parse_transfer_status(self, status):
1706
+ statuses: dict = {
1707
+ 'COMPLETED': 'ok',
1708
+ }
1709
+ return self.safe_string(statuses, status, status)
1710
+
1711
+ def fetch_deposit_address(self, code: str, params={}):
1712
+ """
1713
+ fetch the deposit address for a currency associated with self account
1714
+ :see: https://docs.ox.fun/?json#get-v3-deposit-addresses
1715
+ :param str code: unified currency code
1716
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1717
+ :param str [params.network]: network for fetch deposit address
1718
+ :returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
1719
+ """
1720
+ networkCode = self.safe_string(params, 'network')
1721
+ networkId = self.network_code_to_id(networkCode, code)
1722
+ if networkId is None:
1723
+ raise BadRequest(self.id + ' fetchDepositAddress() require network parameter')
1724
+ self.load_markets()
1725
+ currency = self.currency(code)
1726
+ request: dict = {
1727
+ 'asset': currency['id'],
1728
+ 'network': networkId,
1729
+ }
1730
+ params = self.omit(params, 'network')
1731
+ response = self.privateGetV3DepositAddresses(self.extend(request, params))
1732
+ #
1733
+ # {"success":true,"data":{"address":"0x998dEc76151FB723963Bd8AFD517687b38D33dE8"}}
1734
+ #
1735
+ data = self.safe_dict(response, 'data', {})
1736
+ return self.parse_deposit_address(data, currency)
1737
+
1738
+ def parse_deposit_address(self, depositAddress, currency: Currency = None):
1739
+ #
1740
+ # {"address":"0x998dEc76151FB723963Bd8AFD517687b38D33dE8"}
1741
+ #
1742
+ address = self.safe_string(depositAddress, 'address')
1743
+ self.check_address(address)
1744
+ return {
1745
+ 'currency': currency['code'],
1746
+ 'address': address,
1747
+ 'tag': None,
1748
+ 'network': None,
1749
+ 'info': depositAddress,
1750
+ }
1751
+
1752
+ def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
1753
+ """
1754
+ fetch all deposits made to an account
1755
+ :see: https://docs.ox.fun/?json#get-v3-deposit
1756
+ :param str code: unified currency code of the currency transferred
1757
+ :param int [since]: the earliest time in ms to fetch transfers for(default 24 hours ago)
1758
+ :param int [limit]: the maximum number of transfer structures to retrieve(default 50, max 200)
1759
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1760
+ :param int [params.until]: the latest time in ms to fetch transfers for(default time now)
1761
+ :returns dict[]: a list of `transfer structures <https://docs.ccxt.com/#/?id=transfer-structure>`
1762
+ """
1763
+ self.load_markets()
1764
+ request: dict = {}
1765
+ currency: Currency = None
1766
+ if code is not None:
1767
+ currency = self.currency(code)
1768
+ request['asset'] = currency['id']
1769
+ if since is not None:
1770
+ request['startTime'] = since # startTime and endTime must be within 7 days of each other
1771
+ if limit is not None:
1772
+ request['limit'] = limit
1773
+ until = self.safe_integer(params, 'until')
1774
+ if until is not None:
1775
+ request['endTime'] = until
1776
+ params = self.omit(params, 'until')
1777
+ response = self.privateGetV3Deposit(self.extend(request, params))
1778
+ #
1779
+ # {
1780
+ # "success": True,
1781
+ # "data": [
1782
+ # {
1783
+ # "asset":"USDC",
1784
+ # "network":"Ethereum",
1785
+ # "address": "0x998dEc76151FB723963Bd8AFD517687b38D33dE8",
1786
+ # "quantity":"50",
1787
+ # "id":"5914",
1788
+ # "status": "COMPLETED",
1789
+ # "txId":"0xf5e79663830a0c6f94d46638dcfbc134566c12facf1832396f81ecb55d3c75dc",
1790
+ # "creditedAt":"1714821645154"
1791
+ # }
1792
+ # ]
1793
+ # }
1794
+ #
1795
+ data = self.safe_list(response, 'data', [])
1796
+ for i in range(0, len(data)):
1797
+ data[i]['type'] = 'deposit'
1798
+ return self.parse_transactions(data, currency, since, limit)
1799
+
1800
+ def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
1801
+ """
1802
+ fetch all withdrawals made from an account
1803
+ :see: https://docs.ox.fun/?json#get-v3-withdrawal
1804
+ :param str code: unified currency code of the currency transferred
1805
+ :param int [since]: the earliest time in ms to fetch transfers for(default 24 hours ago)
1806
+ :param int [limit]: the maximum number of transfer structures to retrieve(default 50, max 200)
1807
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1808
+ :param int [params.until]: the latest time in ms to fetch transfers for(default time now)
1809
+ :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
1810
+ """
1811
+ self.load_markets()
1812
+ request: dict = {}
1813
+ currency: Currency = None
1814
+ if code is not None:
1815
+ currency = self.currency(code)
1816
+ request['asset'] = currency['id']
1817
+ if since is not None:
1818
+ request['startTime'] = since # startTime and endTime must be within 7 days of each other
1819
+ if limit is not None:
1820
+ request['limit'] = limit
1821
+ until = self.safe_integer(params, 'until')
1822
+ if until is not None:
1823
+ request['endTime'] = until
1824
+ params = self.omit(params, 'until')
1825
+ response = self.privateGetV3Withdrawal(self.extend(request, params))
1826
+ #
1827
+ # {
1828
+ # success: True,
1829
+ # data: [
1830
+ # {
1831
+ # id: '968163212989431811',
1832
+ # asset: 'OX',
1833
+ # network: 'Arbitrum',
1834
+ # address: '0x90fc1fB49a4ED8f485dd02A2a1Cf576897f6Bfc9',
1835
+ # quantity: '11.7444',
1836
+ # fee: '1.744400000',
1837
+ # status: 'COMPLETED',
1838
+ # txId: '0xe96b2d128b737fdbca927edf355cff42202e65b0fb960e64ffb9bd68c121f69f',
1839
+ # requestedAt: '1715530365450',
1840
+ # completedAt: '1715530527000'
1841
+ # }
1842
+ # ]
1843
+ # }
1844
+ #
1845
+ data = self.safe_list(response, 'data', [])
1846
+ for i in range(0, len(data)):
1847
+ data[i]['type'] = 'withdrawal'
1848
+ return self.parse_transactions(data, currency, since, limit)
1849
+
1850
+ def parse_transactions(self, transactions, currency: Currency = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
1851
+ result = []
1852
+ for i in range(0, len(transactions)):
1853
+ transactions[i] = self.extend(transactions[i], params)
1854
+ transaction = self.parse_transaction(transactions[i], currency)
1855
+ result.append(transaction)
1856
+ result = self.sort_by(result, 'timestamp')
1857
+ code = currency['code'] if (currency is not None) else None
1858
+ return self.filter_by_currency_since_limit(result, code, since, limit)
1859
+
1860
+ def parse_transaction(self, transaction, currency: Currency = None) -> Transaction:
1861
+ #
1862
+ # fetchDeposits
1863
+ # {
1864
+ # "asset":"USDC",
1865
+ # "network":"Ethereum",
1866
+ # "address": "0x998dEc76151FB723963Bd8AFD517687b38D33dE8",
1867
+ # "quantity":"50",
1868
+ # "id":"5914",
1869
+ # "status": "COMPLETED",
1870
+ # "txId":"0xf5e79663830a0c6f94d46638dcfbc134566c12facf1832396f81ecb55d3c75dc",
1871
+ # "creditedAt":"1714821645154"
1872
+ # }
1873
+ #
1874
+ # fetchWithdrawals
1875
+ # {
1876
+ # id: '968163212989431811',
1877
+ # asset: 'OX',
1878
+ # network: 'Arbitrum',
1879
+ # address: '0x90fc1fB49a4ED8f485dd02A2a1Cf576897f6Bfc9',
1880
+ # quantity: '11.7444',
1881
+ # fee: '1.744400000',
1882
+ # status: 'COMPLETED',
1883
+ # txId: '0xe96b2d128b737fdbca927edf355cff42202e65b0fb960e64ffb9bd68c121f69f',
1884
+ # requestedAt: '1715530365450',
1885
+ # completedAt: '1715530527000'
1886
+ # }
1887
+ #
1888
+ # withdraw
1889
+ # {
1890
+ # "id": "968364664449302529",
1891
+ # "asset": "OX",
1892
+ # "network": "Arbitrum",
1893
+ # "address": "0x90fc1fB49a4ED8f485dd02A2a1Cf576897f6Bfc9",
1894
+ # "quantity": "10",
1895
+ # "externalFee": False,
1896
+ # "fee": "1.6728",
1897
+ # "status": "PENDING",
1898
+ # "requestedAt": "1715591843616"
1899
+ # }
1900
+ #
1901
+ id = self.safe_string(transaction, 'id')
1902
+ type = self.safe_string(transaction, 'type')
1903
+ transaction = self.omit(transaction, 'type')
1904
+ address: Str = None
1905
+ addressTo: Str = None
1906
+ status: Str = None
1907
+ if type == 'deposit':
1908
+ address = self.safe_string(transaction, 'address')
1909
+ status = self.parse_deposit_status(self.safe_string(transaction, 'status'))
1910
+ elif type == 'withdrawal':
1911
+ addressTo = self.safe_string(transaction, 'address')
1912
+ status = self.parse_withdrawal_status(self.safe_string(transaction, 'status'))
1913
+ txid = self.safe_string(transaction, 'txId')
1914
+ currencyId = self.safe_string(transaction, 'asset')
1915
+ code = self.safe_currency_code(currencyId, currency)
1916
+ network = self.safe_string(transaction, 'network')
1917
+ networkCode = self.network_id_to_code(network)
1918
+ timestamp = self.safe_integer_2(transaction, 'creditedAt', 'requestedAt')
1919
+ amount = self.safe_number(transaction, 'quantity')
1920
+ feeCost = self.safe_number(transaction, 'fee')
1921
+ fee = None
1922
+ if feeCost is not None:
1923
+ fee = {
1924
+ 'cost': feeCost,
1925
+ 'currency': code,
1926
+ }
1927
+ return {
1928
+ 'info': transaction,
1929
+ 'id': id,
1930
+ 'txid': txid,
1931
+ 'timestamp': timestamp,
1932
+ 'datetime': self.iso8601(timestamp),
1933
+ 'network': networkCode,
1934
+ 'address': address,
1935
+ 'addressTo': addressTo,
1936
+ 'addressFrom': None,
1937
+ 'tag': None,
1938
+ 'tagTo': None,
1939
+ 'tagFrom': None,
1940
+ 'type': type,
1941
+ 'amount': amount,
1942
+ 'currency': code,
1943
+ 'status': status,
1944
+ 'updated': None,
1945
+ 'internal': None,
1946
+ 'comment': None,
1947
+ 'fee': fee,
1948
+ }
1949
+
1950
+ def parse_deposit_status(self, status):
1951
+ statuses: dict = {
1952
+ 'COMPLETED': 'ok',
1953
+ }
1954
+ return self.safe_string(statuses, status, status)
1955
+
1956
+ def parse_withdrawal_status(self, status):
1957
+ statuses: dict = {
1958
+ 'COMPLETED': 'ok',
1959
+ 'PROCESSING': 'pending',
1960
+ 'IN SWEEPING': 'pending',
1961
+ 'PENDING': 'pending',
1962
+ 'ON HOLD': 'pending',
1963
+ 'CANCELED': 'canceled',
1964
+ 'FAILED': 'failed',
1965
+ }
1966
+ return self.safe_string(statuses, status, status)
1967
+
1968
+ def withdraw(self, code: str, amount: float, address: str, tag=None, params={}):
1969
+ """
1970
+ make a withdrawal
1971
+ :see: https://docs.bitflex.com/spot#withdraw
1972
+ :param str code: unified currency code
1973
+ :param float amount: the amount to withdraw
1974
+ :param str address: the address to withdraw to
1975
+ :param str tag:
1976
+ :param str [params.network]: network for withdraw
1977
+ :param bool [params.externalFee]: if False, then the fee is taken from the quantity, also with the burn fee for asset SOLO
1978
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1979
+ *
1980
+ * EXCHANGE SPECIFIC PARAMETERS
1981
+ :param str [params.tfaType]: GOOGLE, or AUTHY_SECRET, or YUBIKEY, for 2FA
1982
+ :param str [params.code]: 2FA code
1983
+ :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
1984
+ """
1985
+ tag, params = self.handle_withdraw_tag_and_params(tag, params)
1986
+ self.load_markets()
1987
+ currency = self.currency(code)
1988
+ stringAmount = self.currency_to_precision(code, amount)
1989
+ request: dict = {
1990
+ 'asset': currency['id'],
1991
+ 'address': address,
1992
+ 'quantity': stringAmount,
1993
+ }
1994
+ if tag is not None:
1995
+ request['memo'] = tag
1996
+ networkCode: Str = None
1997
+ networkCode, params = self.handle_network_code_and_params(params)
1998
+ if networkCode is not None:
1999
+ request['network'] = self.network_code_to_id(networkCode)
2000
+ request['externalFee'] = False
2001
+ response = self.privatePostV3Withdrawal(self.extend(request, params))
2002
+ #
2003
+ # {
2004
+ # "success": True,
2005
+ # "data": {
2006
+ # "id": "968364664449302529",
2007
+ # "asset": "OX",
2008
+ # "network": "Arbitrum",
2009
+ # "address": "0x90fc1fB49a4ED8f485dd02A2a1Cf576897f6Bfc9",
2010
+ # "quantity": "10",
2011
+ # "externalFee": False,
2012
+ # "fee": "1.6728",
2013
+ # "status": "PENDING",
2014
+ # "requestedAt": "1715591843616"
2015
+ # }
2016
+ # }
2017
+ #
2018
+ data = self.safe_dict(response, 'data', {})
2019
+ data['type'] = 'withdrawal'
2020
+ return self.parse_transaction(data, currency)
2021
+
2022
+ def fetch_positions(self, symbols: Strings = None, params={}):
2023
+ """
2024
+ fetch all open positions
2025
+ :see: https://docs.ox.fun/?json#get-v3-positions
2026
+ :param str[]|None symbols: list of unified market symbols
2027
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2028
+ :param boolean [params.subAcc]:
2029
+ :returns dict[]: a list of `position structure <https://docs.ccxt.com/#/?id=position-structure>`
2030
+ """
2031
+ # Calling self endpoint using an API key pair linked to the parent account with the parameter "subAcc"
2032
+ # allows the caller to include positions of additional sub-accounts in the response.
2033
+ # This feature does not work when using API key pairs linked to a sub-account
2034
+ self.load_markets()
2035
+ symbols = self.market_symbols(symbols)
2036
+ response = self.privateGetV3Positions(params)
2037
+ #
2038
+ # {
2039
+ # "success": True,
2040
+ # "data": [
2041
+ # {
2042
+ # "accountId": "106490",
2043
+ # "name": "main",
2044
+ # "positions": [
2045
+ # {
2046
+ # "marketCode": "BTC-USD-SWAP-LIN",
2047
+ # "baseAsset": "BTC",
2048
+ # "counterAsset": "USD",
2049
+ # "position": "0.00010",
2050
+ # "entryPrice": "64300.0",
2051
+ # "markPrice": "63278",
2052
+ # "positionPnl": "-10.1900",
2053
+ # "estLiquidationPrice": "0",
2054
+ # "lastUpdatedAt": "1714915841448"
2055
+ # },
2056
+ # ...
2057
+ # ]
2058
+ # },
2059
+ # {
2060
+ # "accountId": "106526",
2061
+ # "name": "testSubAccount",
2062
+ # "positions": [
2063
+ # {
2064
+ # "marketCode": "ETH-USD-SWAP-LIN",
2065
+ # "baseAsset": "ETH",
2066
+ # "counterAsset": "USD",
2067
+ # "position": "0.001",
2068
+ # "entryPrice": "3080.5",
2069
+ # "markPrice": "3062.0",
2070
+ # "positionPnl": "-1.8500",
2071
+ # "estLiquidationPrice": "0",
2072
+ # "lastUpdatedAt": "1715089678013"
2073
+ # },
2074
+ # ...
2075
+ # ]
2076
+ # }
2077
+ # ]
2078
+ # }
2079
+ #
2080
+ data = self.safe_list(response, 'data', [])
2081
+ allPositions = []
2082
+ for i in range(0, len(data)):
2083
+ account = data[i]
2084
+ positions = self.safe_list(account, 'positions', [])
2085
+ allPositions = self.array_concat(allPositions, positions)
2086
+ return self.parse_positions(allPositions, symbols)
2087
+
2088
+ def parse_position(self, position, market: Market = None):
2089
+ #
2090
+ # {
2091
+ # "marketCode": "ETH-USD-SWAP-LIN",
2092
+ # "baseAsset": "ETH",
2093
+ # "counterAsset": "USD",
2094
+ # "position": "0.001",
2095
+ # "entryPrice": "3080.5",
2096
+ # "markPrice": "3062.0",
2097
+ # "positionPnl": "-1.8500",
2098
+ # "estLiquidationPrice": "0",
2099
+ # "lastUpdatedAt": "1715089678013"
2100
+ # }
2101
+ #
2102
+ marketId = self.safe_string(position, 'marketCode')
2103
+ market = self.safe_market(marketId, market)
2104
+ return self.safe_position({
2105
+ 'info': position,
2106
+ 'id': None,
2107
+ 'symbol': market['symbol'],
2108
+ 'notional': None,
2109
+ 'marginMode': 'cross',
2110
+ 'liquidationPrice': self.safe_number(position, 'estLiquidationPrice'),
2111
+ 'entryPrice': self.safe_number(position, 'entryPrice'),
2112
+ 'unrealizedPnl': self.safe_number(position, 'positionPnl'),
2113
+ 'realizedPnl': None,
2114
+ 'percentage': None,
2115
+ 'contracts': self.safe_number(position, 'position'),
2116
+ 'contractSize': None,
2117
+ 'markPrice': self.safe_number(position, 'markPrice'),
2118
+ 'lastPrice': None,
2119
+ 'side': None,
2120
+ 'hedged': None,
2121
+ 'timestamp': None,
2122
+ 'datetime': None,
2123
+ 'lastUpdateTimestamp': self.safe_integer(position, 'lastUpdatedAt'),
2124
+ 'maintenanceMargin': None,
2125
+ 'maintenanceMarginPercentage': None,
2126
+ 'collateral': None,
2127
+ 'initialMargin': None,
2128
+ 'initialMarginPercentage': None,
2129
+ 'leverage': None,
2130
+ 'marginRatio': None,
2131
+ 'stopLossPrice': None,
2132
+ 'takeProfitPrice': None,
2133
+ })
2134
+
2135
+ def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}) -> Order:
2136
+ """
2137
+ create a trade order
2138
+ :see: https://docs.ox.fun/?json#post-v3-orders-place
2139
+ :param str symbol: unified symbol of the market to create an order in
2140
+ :param str type: 'market', 'limit', 'STOP_LIMIT' or 'STOP_MARKET'
2141
+ :param str side: 'buy' or 'sell'
2142
+ :param float amount: how much of currency you want to trade in units of base currency
2143
+ :param float [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
2144
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2145
+ :param int [params.clientOrderId]: a unique id for the order
2146
+ :param int [params.timestamp]: in milliseconds. If an order reaches the matching engine and the current timestamp exceeds timestamp + recvWindow, then the order will be rejected.
2147
+ :param int [params.recvWindow]: in milliseconds. If an order reaches the matching engine and the current timestamp exceeds timestamp + recvWindow, then the order will be rejected. If timestamp is provided without recvWindow, then a default recvWindow of 1000ms is used.
2148
+ :param str [params.responseType]: FULL or ACK
2149
+ :param float [params.cost]: the quote quantity that can be used alternative for the amount for market buy orders
2150
+ :param float [params.triggerPrice]: The price at which a trigger order is triggered at
2151
+ :param float [params.limitPrice]: Limit price for the STOP_LIMIT order
2152
+ :param bool [params.postOnly]: if True, the order will only be posted if it will be a maker order
2153
+ :param str [params.timeInForce]: GTC(default), IOC, FOK, PO, MAKER_ONLY or MAKER_ONLY_REPRICE(reprices order to the best maker only price if the specified price were to lead to a taker trade)
2154
+ :param str [params.selfTradePreventionMode]: NONE, EXPIRE_MAKER, EXPIRE_TAKER or EXPIRE_BOTH for more info check here {@link https://docs.ox.fun/?json#self-trade-prevention-modes}
2155
+ :param str [params.displayQuantity]: for an iceberg order, pass both quantity and displayQuantity fields in the order request
2156
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2157
+ """
2158
+ self.load_markets()
2159
+ request: dict = {
2160
+ 'responseType': self.safe_string(params, 'responseType', 'FULL'),
2161
+ 'timestamp': self.safe_integer(params, 'timestamp', self.milliseconds()),
2162
+ }
2163
+ params = self.omit(params, ['responseType', 'timestamp'])
2164
+ recvWindow = self.safe_integer(params, 'recvWindow')
2165
+ if recvWindow is not None:
2166
+ request['recvWindow'] = recvWindow
2167
+ params = self.omit(params, 'recvWindow')
2168
+ orderRequest = self.create_order_request(symbol, type, side, amount, price, params)
2169
+ request['orders'] = [orderRequest]
2170
+ response = self.privatePostV3OrdersPlace(request)
2171
+ #
2172
+ # accepted market order responseType FULL
2173
+ # {
2174
+ # "success": True,
2175
+ # "data": [
2176
+ # {
2177
+ # "notice": "OrderMatched",
2178
+ # "accountId": "106490",
2179
+ # "orderId": "1000109901865",
2180
+ # "submitted": True,
2181
+ # "clientOrderId": "0",
2182
+ # "marketCode": "OX-USDT",
2183
+ # "status": "FILLED",
2184
+ # "side": "SELL",
2185
+ # "isTriggered": False,
2186
+ # "quantity": "150.0",
2187
+ # "amount": "0.0",
2188
+ # "remainQuantity": "0.0",
2189
+ # "matchId": "100017047880451399",
2190
+ # "matchPrice": "0.01465",
2191
+ # "matchQuantity": "150.0",
2192
+ # "feeInstrumentId": "USDT",
2193
+ # "fees": "0.0015382500",
2194
+ # "orderType": "MARKET",
2195
+ # "createdAt": "1715592472236",
2196
+ # "lastMatchedAt": "1715592472200",
2197
+ # "displayQuantity": "150.0"
2198
+ # }
2199
+ # ]
2200
+ # }
2201
+ #
2202
+ # accepted limit order responseType FULL
2203
+ # {
2204
+ # "success": True,
2205
+ # "data": [
2206
+ # {
2207
+ # "notice": "OrderOpened",
2208
+ # "accountId": "106490",
2209
+ # "orderId": "1000111482406",
2210
+ # "submitted": True,
2211
+ # "clientOrderId": "0",
2212
+ # "marketCode": "ETH-USD-SWAP-LIN",
2213
+ # "status": "OPEN",
2214
+ # "side": "SELL",
2215
+ # "price": "4000.0",
2216
+ # "isTriggered": False,
2217
+ # "quantity": "0.01",
2218
+ # "amount": "0.0",
2219
+ # "orderType": "LIMIT",
2220
+ # "timeInForce": "GTC",
2221
+ # "createdAt": "1715763507682",
2222
+ # "displayQuantity": "0.01"
2223
+ # }
2224
+ # ]
2225
+ # }
2226
+ #
2227
+ # accepted order responseType ACK
2228
+ # {
2229
+ # "success": True,
2230
+ # "data": [
2231
+ # {
2232
+ # "accountId": "106490",
2233
+ # "orderId": "1000109892193",
2234
+ # "submitted": True,
2235
+ # "marketCode": "OX-USDT",
2236
+ # "side": "BUY",
2237
+ # "price": "0.01961",
2238
+ # "isTriggered": False,
2239
+ # "quantity": "100",
2240
+ # "orderType": "MARKET",
2241
+ # "timeInForce": "IOC",
2242
+ # "createdAt": "1715591529057",
2243
+ # "selfTradePreventionMode": "NONE"
2244
+ # }
2245
+ # ]
2246
+ # }
2247
+ #
2248
+ # rejected order(balance insufficient)
2249
+ # {
2250
+ # "success": True,
2251
+ # "data": [
2252
+ # {
2253
+ # "code": "710001",
2254
+ # "message": "System failure, exception thrown -> null",
2255
+ # "submitted": False,
2256
+ # "marketCode": "OX-USDT",
2257
+ # "side": "BUY",
2258
+ # "price": "0.01961",
2259
+ # "amount": "100",
2260
+ # "orderType": "MARKET",
2261
+ # "timeInForce": "IOC",
2262
+ # "createdAt": "1715591678835",
2263
+ # "source": 11,
2264
+ # "selfTradePreventionMode": "NONE"
2265
+ # }
2266
+ # ]
2267
+ # }
2268
+ #
2269
+ # rejected order(bad request)
2270
+ # {
2271
+ # "success": True,
2272
+ # "data": [
2273
+ # {
2274
+ # "code": "20044",
2275
+ # "message": "Amount is not supported for self order type",
2276
+ # "submitted": False,
2277
+ # "marketCode": "OX-USDT",
2278
+ # "side": "SELL",
2279
+ # "amount": "200",
2280
+ # "orderType": "MARKET",
2281
+ # "createdAt": "1715592079986",
2282
+ # "source": 11
2283
+ # }
2284
+ # ]
2285
+ # }
2286
+ #
2287
+ data = self.safe_list(response, 'data', [])
2288
+ order = self.safe_dict(data, 0, {})
2289
+ return self.parse_order(order)
2290
+
2291
+ def create_orders(self, orders: List[OrderRequest], params={}) -> List[Order]:
2292
+ """
2293
+ create a list of trade orders
2294
+ :see: https://docs.ox.fun/?json#post-v3-orders-place
2295
+ :param Array orders: list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params
2296
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2297
+ :param int [params.timestamp]: *for all orders* in milliseconds. If orders reach the matching engine and the current timestamp exceeds timestamp + recvWindow, then all orders will be rejected.
2298
+ :param int [params.recvWindow]: *for all orders* in milliseconds. If orders reach the matching engine and the current timestamp exceeds timestamp + recvWindow, then all orders will be rejected. If timestamp is provided without recvWindow, then a default recvWindow of 1000ms is used.
2299
+ :param str [params.responseType]: *for all orders* FULL or ACK
2300
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2301
+ """
2302
+ self.load_markets()
2303
+ ordersRequests = []
2304
+ for i in range(0, len(orders)):
2305
+ rawOrder = orders[i]
2306
+ symbol = self.safe_string(rawOrder, 'symbol')
2307
+ type = self.safe_string(rawOrder, 'type')
2308
+ side = self.safe_string(rawOrder, 'side')
2309
+ amount = self.safe_number(rawOrder, 'amount')
2310
+ price = self.safe_number(rawOrder, 'price')
2311
+ orderParams = self.safe_dict(rawOrder, 'params', {})
2312
+ orderRequest = self.create_order_request(symbol, type, side, amount, price, orderParams)
2313
+ ordersRequests.append(orderRequest)
2314
+ request: dict = {
2315
+ 'responseType': 'FULL',
2316
+ 'timestamp': self.milliseconds(),
2317
+ 'orders': ordersRequests,
2318
+ }
2319
+ response = self.privatePostV3OrdersPlace(self.extend(request, params))
2320
+ data = self.safe_list(response, 'data', [])
2321
+ return self.parse_orders(data)
2322
+
2323
+ def create_order_request(self, symbol: str, type: str, side: str, amount, price=None, params={}):
2324
+ """
2325
+ :param str symbol: unified symbol of the market to create an order in
2326
+ :param str type: 'market', 'limit', 'STOP_LIMIT' or 'STOP_MARKET'
2327
+ :param str side: 'buy' or 'sell'
2328
+ :param float amount: how much of currency you want to trade in units of base currency
2329
+ :param float [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
2330
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2331
+ :param int [params.clientOrderId]: a unique id for the order
2332
+ :param float [params.cost]: the quote quantity that can be used alternative for the amount for market buy orders
2333
+ :param float [params.triggerPrice]: The price at which a trigger order is triggered at
2334
+ :param float [params.limitPrice]: Limit price for the STOP_LIMIT order
2335
+ :param bool [params.postOnly]: if True, the order will only be posted if it will be a maker order
2336
+ :param str [params.timeInForce]: GTC(default), IOC, FOK, PO, MAKER_ONLY or MAKER_ONLY_REPRICE(reprices order to the best maker only price if the specified price were to lead to a taker trade)
2337
+ :param str [params.selfTradePreventionMode]: NONE, EXPIRE_MAKER, EXPIRE_TAKER or EXPIRE_BOTH for more info check here {@link https://docs.ox.fun/?json#self-trade-prevention-modes}
2338
+ :param str [params.displayQuantity]: for an iceberg order, pass both quantity and displayQuantity fields in the order request
2339
+ """
2340
+ market = self.market(symbol)
2341
+ request: dict = {
2342
+ 'marketCode': market['id'],
2343
+ 'side': side.upper(),
2344
+ 'source': 1000,
2345
+ }
2346
+ cost = self.safe_string_2(params, 'cost', 'amount')
2347
+ if cost is not None:
2348
+ request['amount'] = cost # todo costToPrecision
2349
+ params = self.omit(params, ['cost', 'amount'])
2350
+ else:
2351
+ request['quantity'] = amount # todo amountToPrecision
2352
+ triggerPrice = self.safe_string_2(params, 'triggerPrice', 'stopPrice')
2353
+ orderType = type.upper()
2354
+ if triggerPrice is not None:
2355
+ if orderType == 'MARKET':
2356
+ orderType = 'STOP_MARKET'
2357
+ elif orderType == 'LIMIT':
2358
+ orderType = 'STOP_LIMIT'
2359
+ request['stopPrice'] = triggerPrice # todo priceToPrecision
2360
+ params = self.omit(params, ['triggerPrice', 'stopPrice'])
2361
+ request['orderType'] = orderType
2362
+ if orderType == 'STOP_LIMIT':
2363
+ request['limitPrice'] = price # todo priceToPrecision
2364
+ elif price is not None:
2365
+ request['price'] = price # todo priceToPrecision
2366
+ postOnly: Bool = None
2367
+ isMarketOrder = (orderType == 'MARKET') or (orderType == 'STOP_MARKET')
2368
+ postOnly, params = self.handle_post_only(isMarketOrder, False, params)
2369
+ timeInForce = self.safe_string_upper(params, 'timeInForce')
2370
+ if postOnly and (timeInForce != 'MAKER_ONLY_REPRICE'):
2371
+ request['timeInForce'] = 'MAKER_ONLY'
2372
+ return self.extend(request, params)
2373
+
2374
+ def create_market_buy_order_with_cost(self, symbol: str, cost: float, params={}):
2375
+ """
2376
+ create a market buy order by providing the symbol and cost
2377
+ :see: https://open.big.one/docs/spot_orders.html#create-order
2378
+ :param str symbol: unified symbol of the market to create an order in
2379
+ :param float cost: how much you want to trade in units of the quote currency
2380
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2381
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2382
+ """
2383
+ self.load_markets()
2384
+ market = self.market(symbol)
2385
+ if not market['spot']:
2386
+ raise NotSupported(self.id + ' createMarketBuyOrderWithCost() supports spot orders only')
2387
+ request: dict = {
2388
+ 'cost': cost,
2389
+ }
2390
+ return self.create_order(symbol, 'market', 'buy', None, None, self.extend(request, params))
2391
+
2392
+ def fetch_order(self, id: str, symbol: Str = None, params={}) -> Order:
2393
+ """
2394
+ :see: https://docs.ox.fun/?json#get-v3-orders-status
2395
+ fetches information on an order made by the user
2396
+ :param str id: a unique id for the order
2397
+ :param str [symbol]: not used by oxfun fetchOrder
2398
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2399
+ :param int [params.clientOrderId]: the client order id of the order
2400
+ :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2401
+ """
2402
+ self.load_markets()
2403
+ request: dict = {
2404
+ 'orderId': id,
2405
+ }
2406
+ response = self.privateGetV3OrdersStatus(self.extend(request, params))
2407
+ #
2408
+ # {
2409
+ # "success": True,
2410
+ # "data": {
2411
+ # "orderId": "1000111762980",
2412
+ # "clientOrderId": "0",
2413
+ # "marketCode": "ETH-USD-SWAP-LIN",
2414
+ # "status": "OPEN",
2415
+ # "side": "BUY",
2416
+ # "price": "2700.0",
2417
+ # "isTriggered": False,
2418
+ # "remainQuantity": "0.01",
2419
+ # "totalQuantity": "0.01",
2420
+ # "amount": "0",
2421
+ # "displayQuantity": "0.01",
2422
+ # "cumulativeMatchedQuantity": "0",
2423
+ # "orderType": "STOP_LIMIT",
2424
+ # "timeInForce": "GTC",
2425
+ # "source": "11",
2426
+ # "createdAt": "1715794191277"
2427
+ # }
2428
+ # }
2429
+ #
2430
+ data = self.safe_dict(response, 'data', {})
2431
+ return self.parse_order(data)
2432
+
2433
+ def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
2434
+ """
2435
+ fetch all unfilled currently open orders
2436
+ :see: https://docs.ox.fun/?json#get-v3-orders-working
2437
+ :param str symbol: unified market symbol
2438
+ :param int [since]: the earliest time in ms to fetch open orders for
2439
+ :param int [limit]: the maximum number of open orders structures to retrieve
2440
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2441
+ :param int [params.orderId]: a unique id for the order
2442
+ :param int [params.clientOrderId]: the client order id of the order
2443
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2444
+ """
2445
+ self.load_markets()
2446
+ request: dict = {}
2447
+ market: Market = None
2448
+ if symbol is not None:
2449
+ market = self.market(symbol)
2450
+ response = self.privateGetV3OrdersWorking(self.extend(request, params))
2451
+ data = self.safe_list(response, 'data', [])
2452
+ return self.parse_orders(data, market, since, limit)
2453
+
2454
+ def cancel_order(self, id: str, symbol: Str = None, params={}):
2455
+ """
2456
+ cancels an open order
2457
+ :see: https://docs.ox.fun/?json#delete-v3-orders-cancel
2458
+ :param str id: order id
2459
+ :param str symbol: unified symbol of the market the order was made in
2460
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2461
+ :param int [params.clientOrderId]: a unique id for the order
2462
+ :param int [params.timestamp]: in milliseconds
2463
+ :param int [params.recvWindow]: in milliseconds
2464
+ :param str [params.responseType]: 'FULL' or 'ACK'
2465
+ :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
2466
+ """
2467
+ if symbol is None:
2468
+ raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
2469
+ market = self.market(symbol)
2470
+ marketId = market['id']
2471
+ request: dict = {
2472
+ 'timestamp': self.milliseconds(),
2473
+ 'responseType': 'FULL',
2474
+ }
2475
+ orderRequest = {
2476
+ 'marketCode': marketId,
2477
+ 'orderId': id,
2478
+ }
2479
+ clientOrderId = self.safe_integer(params, 'clientOrderId')
2480
+ if clientOrderId is not None:
2481
+ orderRequest['clientOrderId'] = clientOrderId
2482
+ request['orders'] = [orderRequest]
2483
+ response = self.privateDeleteV3OrdersCancel(self.extend(request, params))
2484
+ data = self.safe_list(response, 'data', [])
2485
+ order = self.safe_dict(data, 0, {})
2486
+ return self.parse_order(order)
2487
+
2488
+ def cancel_all_orders(self, symbol: Str = None, params={}):
2489
+ """
2490
+ cancel all open orders
2491
+ :see: https://docs.ox.fun/?json#delete-v3-orders-cancel-all
2492
+ :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
2493
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2494
+ :returns dict: response from exchange
2495
+ """
2496
+ request: dict = {}
2497
+ if symbol is not None:
2498
+ market = self.market(symbol)
2499
+ request['marketCode'] = market['id']
2500
+ #
2501
+ # {
2502
+ # "success": True,
2503
+ # "data": {"notice": "Orders queued for cancelation"}
2504
+ # }
2505
+ #
2506
+ # {
2507
+ # "success": True,
2508
+ # "data": {"notice": "No working orders found"}
2509
+ # }
2510
+ #
2511
+ return self.privateDeleteV3OrdersCancelAll(self.extend(request, params))
2512
+
2513
+ def cancel_orders(self, ids: List[str], symbol: Str = None, params={}):
2514
+ """
2515
+ cancel multiple orders
2516
+ :see: https://docs.ox.fun/?json#delete-v3-orders-cancel
2517
+ :param str[] ids: order ids
2518
+ :param str [symbol]: unified market symbol
2519
+ :param dict [params]: extra parameters specific to the exchange API endpoint
2520
+ :param int [params.timestamp]: in milliseconds
2521
+ :param int [params.recvWindow]: in milliseconds
2522
+ :param str [params.responseType]: 'FULL' or 'ACK'
2523
+ :returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
2524
+ """
2525
+ if symbol is None:
2526
+ raise ArgumentsRequired(self.id + ' cancelOrders() requires a symbol argument')
2527
+ self.load_markets()
2528
+ market = self.market(symbol)
2529
+ marketId = market['id']
2530
+ request: dict = {
2531
+ 'timestamp': self.milliseconds(),
2532
+ 'responseType': 'FULL',
2533
+ }
2534
+ orders = []
2535
+ for i in range(0, len(ids)):
2536
+ order = {
2537
+ 'marketCode': marketId,
2538
+ 'orderId': ids[i],
2539
+ }
2540
+ orders.append(order)
2541
+ request['orders'] = orders
2542
+ response = self.privateDeleteV3OrdersCancel(self.extend(request, params))
2543
+ data = self.safe_list(response, 'data', [])
2544
+ return self.parse_orders(data, market)
2545
+
2546
+ def parse_order(self, order, market: Market = None) -> Order:
2547
+ #
2548
+ # accepted market order responseType FULL
2549
+ # {
2550
+ # "notice": "OrderMatched",
2551
+ # "accountId": "106490",
2552
+ # "orderId": "1000109901865",
2553
+ # "submitted": True,
2554
+ # "clientOrderId": "0",
2555
+ # "marketCode": "OX-USDT",
2556
+ # "status": "FILLED",
2557
+ # "side": "SELL",
2558
+ # "isTriggered": False,
2559
+ # "quantity": "150.0",
2560
+ # "amount": "0.0",
2561
+ # "remainQuantity": "0.0",
2562
+ # "matchId": "100017047880451399",
2563
+ # "matchPrice": "0.01465",
2564
+ # "matchQuantity": "150.0",
2565
+ # "feeInstrumentId": "USDT",
2566
+ # "fees": "0.0015382500",
2567
+ # "orderType": "MARKET",
2568
+ # "createdAt": "1715592472236",
2569
+ # "lastMatchedAt": "1715592472200",
2570
+ # "displayQuantity": "150.0"
2571
+ # }
2572
+ #
2573
+ # accepted limit order responseType FULL
2574
+ # {
2575
+ # "notice": "OrderOpened",
2576
+ # "accountId": "106490",
2577
+ # "orderId": "1000111482406",
2578
+ # "submitted": True,
2579
+ # "clientOrderId": "0",
2580
+ # "marketCode": "ETH-USD-SWAP-LIN",
2581
+ # "status": "OPEN",
2582
+ # "side": "SELL",
2583
+ # "price": "4000.0",
2584
+ # "isTriggered": False,
2585
+ # "quantity": "0.01",
2586
+ # "amount": "0.0",
2587
+ # "orderType": "LIMIT",
2588
+ # "timeInForce": "GTC",
2589
+ # "createdAt": "1715763507682",
2590
+ # "displayQuantity": "0.01"
2591
+ # }
2592
+ #
2593
+ # accepted order responseType ACK
2594
+ # {
2595
+ # "accountId": "106490",
2596
+ # "orderId": "1000109892193",
2597
+ # "submitted": True,
2598
+ # "marketCode": "OX-USDT",
2599
+ # "side": "BUY",
2600
+ # "price": "0.01961",
2601
+ # "isTriggered": False,
2602
+ # "quantity": "100",
2603
+ # "orderType": "MARKET",
2604
+ # "timeInForce": "IOC",
2605
+ # "createdAt": "1715591529057",
2606
+ # "selfTradePreventionMode": "NONE"
2607
+ # }
2608
+ #
2609
+ # rejected order(balance insufficient)
2610
+ # {
2611
+ # "code": "710001",
2612
+ # "message": "System failure, exception thrown -> null",
2613
+ # "submitted": False,
2614
+ # "marketCode": "OX-USDT",
2615
+ # "side": "BUY",
2616
+ # "price": "0.01961",
2617
+ # "amount": "100",
2618
+ # "orderType": "MARKET",
2619
+ # "timeInForce": "IOC",
2620
+ # "createdAt": "1715591678835",
2621
+ # "source": 11,
2622
+ # "selfTradePreventionMode": "NONE"
2623
+ # }
2624
+ #
2625
+ # rejected order(bad request)
2626
+ # {
2627
+ # "code": "20044",
2628
+ # "message": "Amount is not supported for self order type",
2629
+ # "submitted": False,
2630
+ # "marketCode": "OX-USDT",
2631
+ # "side": "SELL",
2632
+ # "amount": "200",
2633
+ # "orderType": "MARKET",
2634
+ # "createdAt": "1715592079986",
2635
+ # "source": 11
2636
+ # }
2637
+ #
2638
+ # fetchOrder
2639
+ # {
2640
+ # "orderId": "1000111762980",
2641
+ # "clientOrderId": "0",
2642
+ # "marketCode": "ETH-USD-SWAP-LIN",
2643
+ # "status": "OPEN",
2644
+ # "side": "BUY",
2645
+ # "price": "2700.0",
2646
+ # "isTriggered": False,
2647
+ # "remainQuantity": "0.01",
2648
+ # "totalQuantity": "0.01",
2649
+ # "amount": "0",
2650
+ # "displayQuantity": "0.01",
2651
+ # "cumulativeMatchedQuantity": "0",
2652
+ # "orderType": "STOP_LIMIT",
2653
+ # "timeInForce": "GTC",
2654
+ # "source": "11",
2655
+ # "createdAt": "1715794191277"
2656
+ # }
2657
+ #
2658
+ marketId = self.safe_string(order, 'marketCode')
2659
+ market = self.safe_market(marketId, market)
2660
+ timestamp = self.safe_integer(order, 'createdAt')
2661
+ fee = None
2662
+ feeCurrency = self.safe_string(order, 'feeInstrumentId')
2663
+ if feeCurrency is not None:
2664
+ fee = {
2665
+ 'currency': self.safe_currency_code(feeCurrency),
2666
+ 'cost': self.safe_number(order, 'fees'),
2667
+ }
2668
+ status = self.safe_string(order, 'status')
2669
+ code = self.safe_integer(order, 'code') # rejected orders have code of the error
2670
+ if code is not None:
2671
+ status = 'rejected'
2672
+ triggerPrice = self.safe_string(order, 'stopPrice')
2673
+ return self.safe_order({
2674
+ 'id': self.safe_string(order, 'orderId'),
2675
+ 'clientOrderId': self.safe_string(order, 'clientOrderId'),
2676
+ 'timestamp': timestamp,
2677
+ 'datetime': self.iso8601(timestamp),
2678
+ 'lastTradeTimestamp': self.safe_integer(order, 'lastMatchedAt'),
2679
+ 'lastUpdateTimestamp': self.safe_integer(order, 'lastModifiedAt'),
2680
+ 'status': self.parse_order_status(status),
2681
+ 'symbol': market['symbol'],
2682
+ 'type': self.parse_order_type(self.safe_string(order, 'orderType')),
2683
+ 'timeInForce': self.parse_order_time_in_force(self.safe_string(order, 'timeInForce')), # only for limit orders
2684
+ 'side': self.safe_string_lower(order, 'side'),
2685
+ 'price': self.safe_string_n(order, ['price', 'matchPrice', 'limitPrice']),
2686
+ 'average': None,
2687
+ 'amount': self.safe_string_2(order, 'totalQuantity', 'quantity'),
2688
+ 'filled': self.safe_string_2(order, 'cumulativeMatchedQuantity', 'matchQuantity'),
2689
+ 'remaining': self.safe_string(order, 'remainQuantity'),
2690
+ 'triggerPrice': triggerPrice,
2691
+ 'stopLossPrice': triggerPrice,
2692
+ 'cost': self.omit_zero(self.safe_string(order, 'amount')),
2693
+ 'trades': None,
2694
+ 'fee': fee,
2695
+ 'info': order,
2696
+ }, market)
2697
+
2698
+ def parse_order_status(self, status):
2699
+ statuses: dict = {
2700
+ 'OPEN': 'open',
2701
+ 'PARTIALLY_FILLED': 'open',
2702
+ 'PARTIAL_FILL': 'open',
2703
+ 'FILLED': 'closed',
2704
+ 'CANCELED': 'canceled',
2705
+ 'CANCELED_BY_USER': 'canceled',
2706
+ 'CANCELED_BY_MAKER_ONLY': 'rejected',
2707
+ 'CANCELED_BY_FOK': 'rejected',
2708
+ 'CANCELED_ALL_BY_IOC': 'rejected',
2709
+ 'CANCELED_PARTIAL_BY_IOC': 'canceled',
2710
+ 'CANCELED_BY_SELF_TRADE_PROTECTION': 'rejected',
2711
+ }
2712
+ return self.safe_string(statuses, status, status)
2713
+
2714
+ def parse_order_type(self, type):
2715
+ types: dict = {
2716
+ 'LIMIT': 'limit',
2717
+ 'STOP_LIMIT': 'limit',
2718
+ 'MARKET': 'market',
2719
+ 'STOP_MARKET': 'market',
2720
+ }
2721
+ return self.safe_string(types, type, type)
2722
+
2723
+ def parse_order_time_in_force(self, type):
2724
+ types: dict = {
2725
+ 'GTC': 'GTC',
2726
+ 'IOC': 'IOC',
2727
+ 'FOK': 'FOK',
2728
+ 'MAKER_ONLY': 'PO',
2729
+ 'MAKER_ONLY_REPRICE': 'PO',
2730
+ }
2731
+ return self.safe_string(types, type, type)
2732
+
2733
+ def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
2734
+ baseUrl = self.urls['api'][api]
2735
+ url = baseUrl + '/' + path
2736
+ queryString = ''
2737
+ if method == 'GET':
2738
+ queryString = self.urlencode(params)
2739
+ if len(queryString) != 0:
2740
+ url += '?' + queryString
2741
+ if api == 'private':
2742
+ self.check_required_credentials()
2743
+ timestamp = self.milliseconds()
2744
+ isoDatetime = self.iso8601(timestamp)
2745
+ datetimeParts = isoDatetime.split('.')
2746
+ datetime = datetimeParts[0]
2747
+ nonce = self.nonce()
2748
+ urlParts = baseUrl.split('//')
2749
+ if (method == 'POST') or (method == 'DELETE'):
2750
+ body = self.json(params)
2751
+ queryString = body
2752
+ msgString = datetime + '\n' + str(nonce) + '\n' + method + '\n' + urlParts[1] + '\n/' + path + '\n' + queryString
2753
+ signature = self.hmac(self.encode(msgString), self.encode(self.secret), hashlib.sha256, 'base64')
2754
+ headers = {
2755
+ 'Content-Type': 'application/json',
2756
+ 'AccessKey': self.apiKey,
2757
+ 'Timestamp': datetime,
2758
+ 'Signature': signature,
2759
+ 'Nonce': nonce,
2760
+ }
2761
+ return {'url': url, 'method': method, 'body': body, 'headers': headers}
2762
+
2763
+ def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
2764
+ if response is None:
2765
+ return None
2766
+ if code != 200:
2767
+ responseCode = self.safe_string(response, 'code', None)
2768
+ feedback = self.id + ' ' + body
2769
+ self.throw_broadly_matched_exception(self.exceptions['broad'], body, feedback)
2770
+ self.throw_exactly_matched_exception(self.exceptions['exact'], responseCode, feedback)
2771
+ raise ExchangeError(feedback)
2772
+ return None