ccxt 4.4.70__py2.py3-none-any.whl → 4.4.71__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.
Files changed (51) hide show
  1. ccxt/__init__.py +1 -3
  2. ccxt/abstract/bingx.py +1 -0
  3. ccxt/abstract/bitmart.py +1 -0
  4. ccxt/abstract/poloniex.py +36 -0
  5. ccxt/async_support/__init__.py +1 -3
  6. ccxt/async_support/base/exchange.py +3 -3
  7. ccxt/async_support/binance.py +106 -101
  8. ccxt/async_support/bingx.py +64 -42
  9. ccxt/async_support/bitget.py +0 -3
  10. ccxt/async_support/bitmart.py +12 -1
  11. ccxt/async_support/bitopro.py +1 -0
  12. ccxt/async_support/bitrue.py +1 -0
  13. ccxt/async_support/cex.py +1 -0
  14. ccxt/async_support/coinbaseexchange.py +1 -0
  15. ccxt/async_support/deribit.py +1 -0
  16. ccxt/async_support/hashkey.py +4 -2
  17. ccxt/async_support/kraken.py +77 -5
  18. ccxt/async_support/kucoin.py +4 -2
  19. ccxt/async_support/mexc.py +8 -4
  20. ccxt/async_support/okx.py +58 -46
  21. ccxt/async_support/poloniex.py +1263 -85
  22. ccxt/async_support/whitebit.py +4 -2
  23. ccxt/base/exchange.py +23 -3
  24. ccxt/base/types.py +28 -0
  25. ccxt/binance.py +106 -101
  26. ccxt/bingx.py +64 -42
  27. ccxt/bitget.py +0 -3
  28. ccxt/bitmart.py +12 -1
  29. ccxt/bitopro.py +1 -0
  30. ccxt/bitrue.py +1 -0
  31. ccxt/cex.py +1 -0
  32. ccxt/coinbaseexchange.py +1 -0
  33. ccxt/deribit.py +1 -0
  34. ccxt/hashkey.py +4 -2
  35. ccxt/kraken.py +77 -5
  36. ccxt/kucoin.py +4 -2
  37. ccxt/mexc.py +8 -4
  38. ccxt/okx.py +58 -46
  39. ccxt/poloniex.py +1262 -85
  40. ccxt/pro/__init__.py +1 -3
  41. ccxt/pro/binance.py +102 -102
  42. ccxt/pro/bingx.py +62 -51
  43. ccxt/test/tests_async.py +1 -0
  44. ccxt/test/tests_sync.py +1 -0
  45. ccxt/whitebit.py +4 -2
  46. {ccxt-4.4.70.dist-info → ccxt-4.4.71.dist-info}/METADATA +6 -9
  47. {ccxt-4.4.70.dist-info → ccxt-4.4.71.dist-info}/RECORD +50 -51
  48. ccxt/abstract/poloniexfutures.py +0 -48
  49. {ccxt-4.4.70.dist-info → ccxt-4.4.71.dist-info}/LICENSE.txt +0 -0
  50. {ccxt-4.4.70.dist-info → ccxt-4.4.71.dist-info}/WHEEL +0 -0
  51. {ccxt-4.4.70.dist-info → ccxt-4.4.71.dist-info}/top_level.txt +0 -0
@@ -5,8 +5,9 @@
5
5
 
6
6
  from ccxt.async_support.base.exchange import Exchange
7
7
  from ccxt.abstract.poloniex import ImplicitAPI
8
+ import asyncio
8
9
  import hashlib
9
- from ccxt.base.types import Any, Balances, Currencies, Currency, DepositAddress, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFees, Transaction, TransferEntry
10
+ from ccxt.base.types import Any, Balances, Bool, Currencies, Currency, DepositAddress, Int, Leverage, MarginModification, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFees, Transaction, TransferEntry
10
11
  from typing import List
11
12
  from ccxt.base.errors import ExchangeError
12
13
  from ccxt.base.errors import AuthenticationError
@@ -41,21 +42,25 @@ class poloniex(Exchange, ImplicitAPI):
41
42
  'CORS': None,
42
43
  'spot': True,
43
44
  'margin': None, # has but not fully implemented
44
- 'swap': False,
45
- 'future': False,
45
+ 'swap': True,
46
+ 'future': True,
46
47
  'option': False,
48
+ 'addMargin': True,
47
49
  'cancelAllOrders': True,
48
50
  'cancelOrder': True,
51
+ 'cancelOrders': None, # not yet implemented, because RL is worse than cancelOrder
49
52
  'createDepositAddress': True,
50
53
  'createMarketBuyOrderWithCost': True,
51
54
  'createMarketOrderWithCost': False,
52
55
  'createMarketSellOrderWithCost': False,
53
56
  'createOrder': True,
57
+ 'createOrders': None, # not yet implemented, because RL is worse than createOrder
54
58
  'createStopOrder': True,
55
59
  'createTriggerOrder': True,
56
60
  'editOrder': True,
57
61
  'fetchBalance': True,
58
62
  'fetchClosedOrder': False,
63
+ 'fetchClosedOrders': True,
59
64
  'fetchCurrencies': True,
60
65
  'fetchDepositAddress': True,
61
66
  'fetchDepositAddresses': False,
@@ -69,20 +74,24 @@ class poloniex(Exchange, ImplicitAPI):
69
74
  'fetchFundingIntervals': False,
70
75
  'fetchFundingRate': False,
71
76
  'fetchFundingRateHistory': False,
72
- 'fetchFundingRates': False,
77
+ 'fetchFundingRates': None, # has but not implemented
78
+ 'fetchLedger': None, # has but not implemented
79
+ 'fetchLeverage': True,
80
+ 'fetchLiquidations': None, # has but not implemented
73
81
  'fetchMarginMode': False,
74
82
  'fetchMarkets': True,
75
83
  'fetchMyTrades': True,
76
84
  'fetchOHLCV': True,
77
85
  'fetchOpenInterestHistory': False,
78
86
  'fetchOpenOrder': False,
79
- 'fetchOpenOrders': True, # True endpoint for open orders
87
+ 'fetchOpenOrders': True,
80
88
  'fetchOrder': True,
81
89
  'fetchOrderBook': True,
82
90
  'fetchOrderBooks': False,
83
- 'fetchOrderTrades': True, # True endpoint for trades of a single open or closed order
91
+ 'fetchOrderTrades': True,
84
92
  'fetchPosition': False,
85
- 'fetchPositionMode': False,
93
+ 'fetchPositionMode': True,
94
+ 'fetchPositions': True,
86
95
  'fetchTicker': True,
87
96
  'fetchTickers': True,
88
97
  'fetchTime': True,
@@ -93,33 +102,37 @@ class poloniex(Exchange, ImplicitAPI):
93
102
  'fetchTransfer': False,
94
103
  'fetchTransfers': False,
95
104
  'fetchWithdrawals': True,
105
+ 'reduceMargin': True,
96
106
  'sandbox': True,
107
+ 'setLeverage': True,
108
+ 'setPositionMode': True,
97
109
  'transfer': True,
98
110
  'withdraw': True,
99
111
  },
100
112
  'timeframes': {
101
113
  '1m': 'MINUTE_1',
102
114
  '5m': 'MINUTE_5',
103
- '10m': 'MINUTE_10',
115
+ '10m': 'MINUTE_10', # not in swap
104
116
  '15m': 'MINUTE_15',
105
117
  '30m': 'MINUTE_30',
106
118
  '1h': 'HOUR_1',
107
119
  '2h': 'HOUR_2',
108
120
  '4h': 'HOUR_4',
109
- '6h': 'HOUR_6',
121
+ '6h': 'HOUR_6', # not in swap
110
122
  '12h': 'HOUR_12',
111
123
  '1d': 'DAY_1',
112
124
  '3d': 'DAY_3',
113
125
  '1w': 'WEEK_1',
114
- '1M': 'MONTH_1',
126
+ '1M': 'MONTH_1', # not in swap
115
127
  },
116
128
  'urls': {
117
129
  'logo': 'https://user-images.githubusercontent.com/1294454/27766817-e9456312-5ee6-11e7-9b3c-b628ca5626a5.jpg',
118
130
  'api': {
119
- 'rest': 'https://api.poloniex.com',
131
+ 'spot': 'https://api.poloniex.com',
132
+ 'swap': 'https://api.poloniex.com',
120
133
  },
121
134
  'test': {
122
- 'rest': 'https://sand-spot-api-gateway.poloniex.com',
135
+ 'spot': 'https://sand-spot-api-gateway.poloniex.com',
123
136
  },
124
137
  'www': 'https://www.poloniex.com',
125
138
  'doc': 'https://api-docs.poloniex.com/spot/',
@@ -206,6 +219,55 @@ class poloniex(Exchange, ImplicitAPI):
206
219
  'smartorders/{id}': 20,
207
220
  },
208
221
  },
222
+ 'swapPublic': {
223
+ 'get': {
224
+ # 300 calls / second
225
+ 'v3/market/allInstruments': 2 / 3,
226
+ 'v3/market/instruments': 2 / 3,
227
+ 'v3/market/orderBook': 2 / 3,
228
+ 'v3/market/candles': 10, # candles have differnt RL
229
+ 'v3/market/indexPriceCandlesticks': 10,
230
+ 'v3/market/premiumIndexCandlesticks': 10,
231
+ 'v3/market/markPriceCandlesticks': 10,
232
+ 'v3/market/trades': 2 / 3,
233
+ 'v3/market/liquidationOrder': 2 / 3,
234
+ 'v3/market/tickers': 2 / 3,
235
+ 'v3/market/markPrice': 2 / 3,
236
+ 'v3/market/indexPrice': 2 / 3,
237
+ 'v3/market/indexPriceComponents': 2 / 3,
238
+ 'v3/market/fundingRate': 2 / 3,
239
+ 'v3/market/openInterest': 2 / 3,
240
+ 'v3/market/insurance': 2 / 3,
241
+ 'v3/market/riskLimit': 2 / 3,
242
+ },
243
+ },
244
+ 'swapPrivate': {
245
+ 'get': {
246
+ 'v3/account/balance': 4,
247
+ 'v3/account/bills': 20,
248
+ 'v3/trade/order/opens': 20,
249
+ 'v3/trade/order/trades': 20,
250
+ 'v3/trade/order/history': 20,
251
+ 'v3/trade/position/opens': 20,
252
+ 'v3/trade/position/history': 20, # todo: method for self
253
+ 'v3/position/leverages': 20,
254
+ 'v3/position/mode': 20,
255
+ },
256
+ 'post': {
257
+ 'v3/trade/order': 4,
258
+ 'v3/trade/orders': 40,
259
+ 'v3/trade/position': 20,
260
+ 'v3/trade/positionAll': 100,
261
+ 'v3/position/leverage': 20,
262
+ 'v3/position/mode': 20,
263
+ 'v3/trade/position/margin': 20,
264
+ },
265
+ 'delete': {
266
+ 'v3/trade/order': 2,
267
+ 'v3/trade/batchOrders': 20,
268
+ 'v3/trade/allOrders': 20,
269
+ },
270
+ },
209
271
  },
210
272
  'fees': {
211
273
  'trading': {
@@ -254,6 +316,7 @@ class poloniex(Exchange, ImplicitAPI):
254
316
  'UST': 'USTC',
255
317
  },
256
318
  'options': {
319
+ 'defaultType': 'spot',
257
320
  'createMarketBuyOrderRequiresPrice': True,
258
321
  'networks': {
259
322
  'BEP20': 'BSC',
@@ -300,7 +363,7 @@ class poloniex(Exchange, ImplicitAPI):
300
363
  'timeInForce': {
301
364
  'IOC': True,
302
365
  'FOK': True,
303
- 'PO': False,
366
+ 'PO': True,
304
367
  'GTD': False,
305
368
  },
306
369
  'hedged': False,
@@ -311,7 +374,9 @@ class poloniex(Exchange, ImplicitAPI):
311
374
  'trailing': False,
312
375
  'iceberg': False,
313
376
  },
314
- 'createOrders': None,
377
+ 'createOrders': {
378
+ 'max': 20,
379
+ },
315
380
  'fetchMyTrades': {
316
381
  'marginMode': False,
317
382
  'limit': 1000,
@@ -341,13 +406,50 @@ class poloniex(Exchange, ImplicitAPI):
341
406
  'spot': {
342
407
  'extends': 'default',
343
408
  },
409
+ 'forContracts': {
410
+ 'extends': 'default',
411
+ 'createOrder': {
412
+ 'marginMode': True,
413
+ 'triggerPrice': False,
414
+ 'hedged': True,
415
+ 'stpMode': True, # todo
416
+ 'marketBuyByCost': False,
417
+ },
418
+ 'createOrders': {
419
+ 'max': 10,
420
+ },
421
+ 'fetchOpenOrders': {
422
+ 'limit': 100,
423
+ },
424
+ 'fetchClosedOrders': {
425
+ 'marginMode': False,
426
+ 'limit': 100,
427
+ 'daysBack': None,
428
+ 'daysBackCanceled': 1 / 6,
429
+ 'untilDays': None,
430
+ 'trigger': False,
431
+ 'trailing': False,
432
+ },
433
+ 'fetchMyTrades': {
434
+ 'limit': 100,
435
+ 'untilDays': 90,
436
+ },
437
+ },
344
438
  'swap': {
345
- 'linear': None,
346
- 'inverse': None,
439
+ 'linear': {
440
+ 'extends': 'forContracts',
441
+ },
442
+ 'inverse': {
443
+ 'extends': 'forContracts',
444
+ },
347
445
  },
348
446
  'future': {
349
- 'linear': None,
350
- 'inverse': None,
447
+ 'linear': {
448
+ 'extends': 'forContracts',
449
+ },
450
+ 'inverse': {
451
+ 'extends': 'forContracts',
452
+ },
351
453
  },
352
454
  },
353
455
  'precisionMode': TICK_SIZE,
@@ -467,6 +569,8 @@ class poloniex(Exchange, ImplicitAPI):
467
569
  })
468
570
 
469
571
  def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
572
+ #
573
+ # spot:
470
574
  #
471
575
  # [
472
576
  # [
@@ -487,6 +591,31 @@ class poloniex(Exchange, ImplicitAPI):
487
591
  # ]
488
592
  # ]
489
593
  #
594
+ # contract:
595
+ #
596
+ # [
597
+ # "84207.02",
598
+ # "84320.85",
599
+ # "84207.02",
600
+ # "84253.83",
601
+ # "3707.5395",
602
+ # "44",
603
+ # "14",
604
+ # "1740770040000",
605
+ # "1740770099999",
606
+ # ],
607
+ #
608
+ ohlcvLength = len(ohlcv)
609
+ isContract = ohlcvLength == 9
610
+ if isContract:
611
+ return [
612
+ self.safe_integer(ohlcv, 7),
613
+ self.safe_number(ohlcv, 2),
614
+ self.safe_number(ohlcv, 1),
615
+ self.safe_number(ohlcv, 0),
616
+ self.safe_number(ohlcv, 3),
617
+ self.safe_number(ohlcv, 5),
618
+ ]
490
619
  return [
491
620
  self.safe_integer(ohlcv, 12),
492
621
  self.safe_number(ohlcv, 2),
@@ -501,6 +630,7 @@ class poloniex(Exchange, ImplicitAPI):
501
630
  fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
502
631
 
503
632
  https://api-docs.poloniex.com/spot/api/public/market-data#candles
633
+ https://api-docs.poloniex.com/v3/futures/api/market/get-kline-data
504
634
 
505
635
  :param str symbol: unified symbol of the market to fetch OHLCV data for
506
636
  :param str timeframe: the length of time each candle represents
@@ -521,12 +651,37 @@ class poloniex(Exchange, ImplicitAPI):
521
651
  'symbol': market['id'],
522
652
  'interval': self.safe_string(self.timeframes, timeframe, timeframe),
523
653
  }
654
+ keyStart = 'startTime' if market['spot'] else 'sTime'
655
+ keyEnd = 'endTime' if market['spot'] else 'eTime'
524
656
  if since is not None:
525
- request['startTime'] = since
657
+ request[keyStart] = since
526
658
  if limit is not None:
527
659
  # limit should in between 100 and 500
528
660
  request['limit'] = limit
529
- request, params = self.handle_until_option('endTime', request, params)
661
+ request, params = self.handle_until_option(keyEnd, request, params)
662
+ if market['contract']:
663
+ if self.in_array(timeframe, ['10m', '1M']):
664
+ raise NotSupported(self.id + ' ' + timeframe + ' ' + market['type'] + ' fetchOHLCV is not supported')
665
+ responseRaw = await self.swapPublicGetV3MarketCandles(self.extend(request, params))
666
+ #
667
+ # {
668
+ # code: "200",
669
+ # msg: "Success",
670
+ # data: [
671
+ # [
672
+ # "84207.02",
673
+ # "84320.85",
674
+ # "84207.02",
675
+ # "84253.83",
676
+ # "3707.5395",
677
+ # "44",
678
+ # "14",
679
+ # "1740770040000",
680
+ # "1740770099999",
681
+ # ],
682
+ #
683
+ data = self.safe_list(responseRaw, 'data')
684
+ return self.parse_ohlcvs(data, market, timeframe, since, limit)
530
685
  response = await self.publicGetMarketsSymbolCandles(self.extend(request, params))
531
686
  #
532
687
  # [
@@ -562,10 +717,16 @@ class poloniex(Exchange, ImplicitAPI):
562
717
  retrieves data on all markets for poloniex
563
718
 
564
719
  https://api-docs.poloniex.com/spot/api/public/reference-data#symbol-information
720
+ https://api-docs.poloniex.com/v3/futures/api/market/get-all-product-info
565
721
 
566
722
  :param dict [params]: extra parameters specific to the exchange API endpoint
567
723
  :returns dict[]: an array of objects representing market data
568
724
  """
725
+ promises = [self.fetch_spot_markets(params), self.fetch_swap_markets(params)]
726
+ results = await asyncio.gather(*promises)
727
+ return self.array_concat(results[0], results[1])
728
+
729
+ async def fetch_spot_markets(self, params={}) -> List[Market]:
569
730
  markets = await self.publicGetMarkets(params)
570
731
  #
571
732
  # [
@@ -592,7 +753,57 @@ class poloniex(Exchange, ImplicitAPI):
592
753
  #
593
754
  return self.parse_markets(markets)
594
755
 
756
+ async def fetch_swap_markets(self, params={}) -> List[Market]:
757
+ # do similar per https://api-docs.poloniex.com/v3/futures/api/market/get-product-info
758
+ response = await self.swapPublicGetV3MarketAllInstruments(params)
759
+ #
760
+ # {
761
+ # "code": "200",
762
+ # "msg": "Success",
763
+ # "data": [
764
+ # {
765
+ # "symbol": "BNB_USDT_PERP",
766
+ # "bAsset": ".PBNBUSDT",
767
+ # "bCcy": "BNB",
768
+ # "qCcy": "USDT",
769
+ # "visibleStartTime": "1620390600000",
770
+ # "tradableStartTime": "1620390600000",
771
+ # "sCcy": "USDT",
772
+ # "tSz": "0.001",
773
+ # "pxScale": "0.001,0.01,0.1,1,10",
774
+ # "lotSz": "1",
775
+ # "minSz": "1",
776
+ # "ctVal": "0.1",
777
+ # "status": "OPEN",
778
+ # "oDate": "1620287590000",
779
+ # "maxPx": "1000000",
780
+ # "minPx": "0.001",
781
+ # "maxQty": "1000000",
782
+ # "minQty": "1",
783
+ # "maxLever": "50",
784
+ # "lever": "10",
785
+ # "ctType": "LINEAR",
786
+ # "alias": "",
787
+ # "iM": "0.02",
788
+ # "mM": "0.0115",
789
+ # "mR": "2000",
790
+ # "buyLmt": "",
791
+ # "sellLmt": "",
792
+ # "ordPxRange": "0.05",
793
+ # "marketMaxQty": "2800",
794
+ # "limitMaxQty": "1000000"
795
+ # },
796
+ #
797
+ markets = self.safe_list(response, 'data')
798
+ return self.parse_markets(markets)
799
+
595
800
  def parse_market(self, market: dict) -> Market:
801
+ if 'ctType' in market:
802
+ return self.parse_swap_market(market)
803
+ else:
804
+ return self.parse_spot_market(market)
805
+
806
+ def parse_spot_market(self, market: dict) -> Market:
596
807
  id = self.safe_string(market, 'symbol')
597
808
  baseId = self.safe_string(market, 'baseCurrencyName')
598
809
  quoteId = self.safe_string(market, 'quoteCurrencyName')
@@ -648,6 +859,113 @@ class poloniex(Exchange, ImplicitAPI):
648
859
  'info': market,
649
860
  }
650
861
 
862
+ def parse_swap_market(self, market: dict) -> Market:
863
+ #
864
+ # {
865
+ # "symbol": "BNB_USDT_PERP",
866
+ # "bAsset": ".PBNBUSDT",
867
+ # "bCcy": "BNB",
868
+ # "qCcy": "USDT",
869
+ # "visibleStartTime": "1620390600000",
870
+ # "tradableStartTime": "1620390600000",
871
+ # "sCcy": "USDT",
872
+ # "tSz": "0.001",
873
+ # "pxScale": "0.001,0.01,0.1,1,10",
874
+ # "lotSz": "1",
875
+ # "minSz": "1",
876
+ # "ctVal": "0.1",
877
+ # "status": "OPEN",
878
+ # "oDate": "1620287590000",
879
+ # "maxPx": "1000000",
880
+ # "minPx": "0.001",
881
+ # "maxQty": "1000000",
882
+ # "minQty": "1",
883
+ # "maxLever": "50",
884
+ # "lever": "10",
885
+ # "ctType": "LINEAR",
886
+ # "alias": "",
887
+ # "iM": "0.02",
888
+ # "mM": "0.0115",
889
+ # "mR": "2000",
890
+ # "buyLmt": "",
891
+ # "sellLmt": "",
892
+ # "ordPxRange": "0.05",
893
+ # "marketMaxQty": "2800",
894
+ # "limitMaxQty": "1000000"
895
+ # },
896
+ #
897
+ id = self.safe_string(market, 'symbol')
898
+ baseId = self.safe_string(market, 'bCcy')
899
+ quoteId = self.safe_string(market, 'qCcy')
900
+ settleId = self.safe_string(market, 'sCcy')
901
+ base = self.safe_currency_code(baseId)
902
+ quote = self.safe_currency_code(quoteId)
903
+ settle = self.safe_currency_code(settleId)
904
+ status = self.safe_string(market, 'status')
905
+ active = status == 'OPEN'
906
+ linear = market['ctType'] == 'LINEAR'
907
+ symbol = base + '/' + quote
908
+ if linear:
909
+ symbol += ':' + settle
910
+ else:
911
+ # actually, exchange does not have any inverse future now
912
+ symbol += ':' + base
913
+ alias = self.safe_string(market, 'alias')
914
+ type = 'swap'
915
+ if alias is not None:
916
+ type = 'future'
917
+ return {
918
+ 'id': id,
919
+ 'symbol': symbol,
920
+ 'base': base,
921
+ 'quote': quote,
922
+ 'settle': settle,
923
+ 'baseId': baseId,
924
+ 'quoteId': quoteId,
925
+ 'settleId': settleId,
926
+ 'type': 'future' if (type == 'future') else 'swap',
927
+ 'spot': False,
928
+ 'margin': False,
929
+ 'swap': type == 'swap',
930
+ 'future': type == 'future',
931
+ 'option': False,
932
+ 'active': active,
933
+ 'contract': True,
934
+ 'linear': linear,
935
+ 'inverse': not linear,
936
+ 'contractSize': self.safe_number(market, 'ctVal'),
937
+ 'expiry': None,
938
+ 'expiryDatetime': None,
939
+ 'strike': None,
940
+ 'optionType': None,
941
+ 'taker': self.safe_number(market, 'tFee'),
942
+ 'maker': self.safe_number(market, 'mFee'),
943
+ 'precision': {
944
+ 'amount': self.safe_number(market, 'lotSz'),
945
+ 'price': self.safe_number(market, 'tSz'),
946
+ },
947
+ 'limits': {
948
+ 'amount': {
949
+ 'min': self.safe_number(market, 'minSz'),
950
+ 'max': self.safe_number(market, 'limitMaxQty'),
951
+ },
952
+ 'price': {
953
+ 'min': self.safe_number(market, 'minPx'),
954
+ 'max': self.safe_number(market, 'maxPx'),
955
+ },
956
+ 'cost': {
957
+ 'min': None,
958
+ 'max': None,
959
+ },
960
+ 'leverage': {
961
+ 'max': self.safe_number(market, 'maxLever'),
962
+ 'min': None,
963
+ },
964
+ },
965
+ 'created': self.safe_integer(market, 'oDate'),
966
+ 'info': market,
967
+ }
968
+
651
969
  async def fetch_time(self, params={}) -> Int:
652
970
  """
653
971
  fetches the current integer timestamp in milliseconds from the exchange server
@@ -661,6 +979,8 @@ class poloniex(Exchange, ImplicitAPI):
661
979
  return self.safe_integer(response, 'serverTime')
662
980
 
663
981
  def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
982
+ #
983
+ # spot:
664
984
  #
665
985
  # {
666
986
  # "symbol" : "BTC_USDT",
@@ -683,36 +1003,56 @@ class poloniex(Exchange, ImplicitAPI):
683
1003
  # "markPrice" : "26444.11"
684
1004
  # }
685
1005
  #
686
- timestamp = self.safe_integer(ticker, 'ts')
687
- marketId = self.safe_string(ticker, 'symbol')
1006
+ # swap:
1007
+ #
1008
+ # {
1009
+ # "s": "XRP_USDT_PERP",
1010
+ # "o": "2.0503",
1011
+ # "l": "2.0066",
1012
+ # "h": "2.216",
1013
+ # "c": "2.1798",
1014
+ # "qty": "21090",
1015
+ # "amt": "451339.65",
1016
+ # "tC": "3267",
1017
+ # "sT": "1740736380000",
1018
+ # "cT": "1740822777559",
1019
+ # "dN": "XRP/USDT/PERP",
1020
+ # "dC": "0.0632",
1021
+ # "bPx": "2.175",
1022
+ # "bSz": "3",
1023
+ # "aPx": "2.1831",
1024
+ # "aSz": "111",
1025
+ # "mPx": "2.1798",
1026
+ # "iPx": "2.1834"
1027
+ # },
1028
+ #
1029
+ timestamp = self.safe_integer_2(ticker, 'ts', 'cT')
1030
+ marketId = self.safe_string_2(ticker, 'symbol', 's')
688
1031
  market = self.safe_market(marketId)
689
- close = self.safe_string(ticker, 'close')
690
- relativeChange = self.safe_string(ticker, 'dailyChange')
1032
+ relativeChange = self.safe_string_2(ticker, 'dailyChange', 'dc')
691
1033
  percentage = Precise.string_mul(relativeChange, '100')
692
- bidVolume = self.safe_string(ticker, 'bidQuantity')
693
- askVolume = self.safe_string(ticker, 'askQuantity')
694
1034
  return self.safe_ticker({
695
1035
  'id': marketId,
696
1036
  'symbol': market['symbol'],
697
1037
  'timestamp': timestamp,
698
1038
  'datetime': self.iso8601(timestamp),
699
- 'high': self.safe_string(ticker, 'high'),
700
- 'low': self.safe_string(ticker, 'low'),
701
- 'bid': self.safe_string(ticker, 'bid'),
702
- 'bidVolume': bidVolume,
703
- 'ask': self.safe_string(ticker, 'ask'),
704
- 'askVolume': askVolume,
1039
+ 'high': self.safe_string_2(ticker, 'high', 'h'),
1040
+ 'low': self.safe_string_2(ticker, 'low', 'l'),
1041
+ 'bid': self.safe_string_2(ticker, 'bid', 'bPx'),
1042
+ 'bidVolume': self.safe_string_2(ticker, 'bidQuantity', 'bSz'),
1043
+ 'ask': self.safe_string_2(ticker, 'ask', 'aPx'),
1044
+ 'askVolume': self.safe_string_2(ticker, 'askQuantity', 'aSz'),
705
1045
  'vwap': None,
706
- 'open': self.safe_string(ticker, 'open'),
707
- 'close': close,
708
- 'last': close,
1046
+ 'open': self.safe_string_2(ticker, 'open', 'o'),
1047
+ 'close': self.safe_string_2(ticker, 'close', 'c'),
709
1048
  'previousClose': None,
710
1049
  'change': None,
711
1050
  'percentage': percentage,
712
1051
  'average': None,
713
- 'baseVolume': self.safe_string(ticker, 'quantity'),
714
- 'quoteVolume': self.safe_string(ticker, 'amount'),
715
- 'markPrice': self.safe_string(ticker, 'markPrice'),
1052
+ 'baseVolume': self.safe_string_2(ticker, 'quantity', 'qty'),
1053
+ 'quoteVolume': self.safe_string_2(ticker, 'amount', 'amt'),
1054
+ 'markPrice': self.safe_string_2(ticker, 'markPrice', 'mPx'),
1055
+ 'indexPrice': self.safe_string(ticker, 'iPx'),
716
1056
  'info': ticker,
717
1057
  }, market)
718
1058
 
@@ -721,13 +1061,54 @@ class poloniex(Exchange, ImplicitAPI):
721
1061
  fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
722
1062
 
723
1063
  https://api-docs.poloniex.com/spot/api/public/market-data#ticker
1064
+ https://api-docs.poloniex.com/v3/futures/api/market/get-market-info
724
1065
 
725
1066
  :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
726
1067
  :param dict [params]: extra parameters specific to the exchange API endpoint
727
1068
  :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
728
1069
  """
729
1070
  await self.load_markets()
730
- symbols = self.market_symbols(symbols)
1071
+ market = None
1072
+ request: dict = {}
1073
+ if symbols is not None:
1074
+ symbols = self.market_symbols(symbols, None, True, True, False)
1075
+ symbolsLength = len(symbols)
1076
+ if symbolsLength > 0:
1077
+ market = self.market(symbols[0])
1078
+ if symbolsLength == 1:
1079
+ request['symbol'] = market['id']
1080
+ marketType = None
1081
+ marketType, params = self.handle_market_type_and_params('fetchTickers', market, params)
1082
+ if marketType == 'swap':
1083
+ responseRaw = await self.swapPublicGetV3MarketTickers(self.extend(request, params))
1084
+ #
1085
+ # {
1086
+ # "code": "200",
1087
+ # "msg": "Success",
1088
+ # "data": [
1089
+ # {
1090
+ # "s": "XRP_USDT_PERP",
1091
+ # "o": "2.0503",
1092
+ # "l": "2.0066",
1093
+ # "h": "2.216",
1094
+ # "c": "2.1798",
1095
+ # "qty": "21090",
1096
+ # "amt": "451339.65",
1097
+ # "tC": "3267",
1098
+ # "sT": "1740736380000",
1099
+ # "cT": "1740822777559",
1100
+ # "dN": "XRP/USDT/PERP",
1101
+ # "dC": "0.0632",
1102
+ # "bPx": "2.175",
1103
+ # "bSz": "3",
1104
+ # "aPx": "2.1831",
1105
+ # "aSz": "111",
1106
+ # "mPx": "2.1798",
1107
+ # "iPx": "2.1834"
1108
+ # },
1109
+ #
1110
+ data = self.safe_list(responseRaw, 'data')
1111
+ return self.parse_tickers(data, symbols)
731
1112
  response = await self.publicGetMarketsTicker24h(params)
732
1113
  #
733
1114
  # [
@@ -892,6 +1273,7 @@ class poloniex(Exchange, ImplicitAPI):
892
1273
  fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
893
1274
 
894
1275
  https://api-docs.poloniex.com/spot/api/public/market-data#ticker
1276
+ https://api-docs.poloniex.com/v3/futures/api/market/get-market-info
895
1277
 
896
1278
  :param str symbol: unified symbol of the market to fetch the ticker for
897
1279
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -902,6 +1284,9 @@ class poloniex(Exchange, ImplicitAPI):
902
1284
  request: dict = {
903
1285
  'symbol': market['id'],
904
1286
  }
1287
+ if market['contract']:
1288
+ tickers = await self.fetch_tickers([market['symbol']], params)
1289
+ return self.safe_dict(tickers, symbol)
905
1290
  response = await self.publicGetMarketsSymbolTicker24h(self.extend(request, params))
906
1291
  #
907
1292
  # {
@@ -931,6 +1316,8 @@ class poloniex(Exchange, ImplicitAPI):
931
1316
  #
932
1317
  # fetchTrades
933
1318
  #
1319
+ # spot:
1320
+ #
934
1321
  # {
935
1322
  # "id" : "60014521",
936
1323
  # "price" : "23162.94",
@@ -941,8 +1328,21 @@ class poloniex(Exchange, ImplicitAPI):
941
1328
  # "createTime" : 1659684602036
942
1329
  # }
943
1330
  #
1331
+ # swap:
1332
+ #
1333
+ # {
1334
+ # "id": "105807376",
1335
+ # "side": "buy",
1336
+ # "px": "84410.57",
1337
+ # "qty": "1",
1338
+ # "amt": "84.41057",
1339
+ # "cT": "1740777563557",
1340
+ # }
1341
+ #
944
1342
  # fetchMyTrades
945
1343
  #
1344
+ # spot:
1345
+ #
946
1346
  # {
947
1347
  # "id": "32164924331503616",
948
1348
  # "symbol": "LINK_USDT",
@@ -961,6 +1361,34 @@ class poloniex(Exchange, ImplicitAPI):
961
1361
  # "clientOrderId": "myOwnId-321"
962
1362
  # }
963
1363
  #
1364
+ # swap:
1365
+ #
1366
+ # {
1367
+ # "symbol": "BTC_USDT_PERP",
1368
+ # "trdId": "105813553",
1369
+ # "side": "SELL",
1370
+ # "type": "TRADE",
1371
+ # "mgnMode": "CROSS",
1372
+ # "ordType": "MARKET",
1373
+ # "clOrdId": "polo418912106147315112",
1374
+ # "role": "TAKER",
1375
+ # "px": "84704.9",
1376
+ # "qty": "1",
1377
+ # "cTime": "1740842829430",
1378
+ # "uTime": "1740842829450",
1379
+ # "feeCcy": "USDT",
1380
+ # "feeAmt": "0.04235245",
1381
+ # "deductCcy": "",
1382
+ # "deductAmt": "0",
1383
+ # "feeRate": "0.0005",
1384
+ # "id": "418912106342654592",
1385
+ # "posSide": "BOTH",
1386
+ # "ordId": "418912106147315112",
1387
+ # "qCcy": "USDT",
1388
+ # "value": "84.7049",
1389
+ # "actType": "TRADING"
1390
+ # },
1391
+ #
964
1392
  # fetchOrderTrades(taker trades)
965
1393
  #
966
1394
  # {
@@ -981,20 +1409,19 @@ class poloniex(Exchange, ImplicitAPI):
981
1409
  # "clientOrderId": ""
982
1410
  # }
983
1411
  #
984
- #
985
- id = self.safe_string_2(trade, 'id', 'tradeID')
986
- orderId = self.safe_string(trade, 'orderId')
987
- timestamp = self.safe_integer_2(trade, 'ts', 'createTime')
1412
+ id = self.safe_string_n(trade, ['id', 'tradeID', 'trdId'])
1413
+ orderId = self.safe_string_2(trade, 'orderId', 'ordId')
1414
+ timestamp = self.safe_integer_n(trade, ['ts', 'createTime', 'cT', 'cTime'])
988
1415
  marketId = self.safe_string(trade, 'symbol')
989
1416
  market = self.safe_market(marketId, market, '_')
990
1417
  symbol = market['symbol']
991
1418
  side = self.safe_string_lower_2(trade, 'side', 'takerSide')
992
1419
  fee = None
993
- priceString = self.safe_string(trade, 'price')
994
- amountString = self.safe_string(trade, 'quantity')
995
- costString = self.safe_string(trade, 'amount')
996
- feeCurrencyId = self.safe_string(trade, 'feeCurrency')
997
- feeCostString = self.safe_string(trade, 'feeAmount')
1420
+ priceString = self.safe_string_2(trade, 'price', 'px')
1421
+ amountString = self.safe_string_2(trade, 'quantity', 'qty')
1422
+ costString = self.safe_string_2(trade, 'amount', 'amt')
1423
+ feeCurrencyId = self.safe_string_2(trade, 'feeCurrency', 'feeCcy')
1424
+ feeCostString = self.safe_string_2(trade, 'feeAmount', 'feeAmt')
998
1425
  if feeCostString is not None:
999
1426
  feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
1000
1427
  fee = {
@@ -1008,9 +1435,9 @@ class poloniex(Exchange, ImplicitAPI):
1008
1435
  'datetime': self.iso8601(timestamp),
1009
1436
  'symbol': symbol,
1010
1437
  'order': orderId,
1011
- 'type': self.safe_string_lower(trade, 'type'),
1438
+ 'type': self.safe_string_lower_2(trade, 'ordType', 'type'), # ordType should take precedence
1012
1439
  'side': side,
1013
- 'takerOrMaker': self.safe_string_lower(trade, 'matchRole'),
1440
+ 'takerOrMaker': self.safe_string_lower_2(trade, 'matchRole', 'role'),
1014
1441
  'price': priceString,
1015
1442
  'amount': amountString,
1016
1443
  'cost': costString,
@@ -1022,6 +1449,7 @@ class poloniex(Exchange, ImplicitAPI):
1022
1449
  get the list of most recent trades for a particular symbol
1023
1450
 
1024
1451
  https://api-docs.poloniex.com/spot/api/public/market-data#trades
1452
+ https://api-docs.poloniex.com/v3/futures/api/market/get-execution-info
1025
1453
 
1026
1454
  :param str symbol: unified symbol of the market to fetch trades for
1027
1455
  :param int [since]: timestamp in ms of the earliest trade to fetch
@@ -1035,7 +1463,25 @@ class poloniex(Exchange, ImplicitAPI):
1035
1463
  'symbol': market['id'],
1036
1464
  }
1037
1465
  if limit is not None:
1038
- request['limit'] = limit
1466
+ request['limit'] = limit # max 1000, for spot & swap
1467
+ if market['contract']:
1468
+ response = await self.swapPublicGetV3MarketTrades(self.extend(request, params))
1469
+ #
1470
+ # {
1471
+ # code: "200",
1472
+ # msg: "Success",
1473
+ # data: [
1474
+ # {
1475
+ # id: "105807320", # descending order
1476
+ # side: "sell",
1477
+ # px: "84383.93",
1478
+ # qty: "1",
1479
+ # amt: "84.38393",
1480
+ # cT: "1740777074704",
1481
+ # },
1482
+ #
1483
+ tradesList = self.safe_list(response, 'data')
1484
+ return self.parse_trades(tradesList, market, since, limit)
1039
1485
  trades = await self.publicGetMarketsSymbolTrades(self.extend(request, params))
1040
1486
  #
1041
1487
  # [
@@ -1057,6 +1503,7 @@ class poloniex(Exchange, ImplicitAPI):
1057
1503
  fetch all trades made by the user
1058
1504
 
1059
1505
  https://api-docs.poloniex.com/spot/api/private/trade#trade-history
1506
+ https://api-docs.poloniex.com/v3/futures/api/trade/get-execution-details
1060
1507
 
1061
1508
  :param str symbol: unified market symbol
1062
1509
  :param int [since]: the earliest time in ms to fetch trades for
@@ -1074,15 +1521,57 @@ class poloniex(Exchange, ImplicitAPI):
1074
1521
  market: Market = None
1075
1522
  if symbol is not None:
1076
1523
  market = self.market(symbol)
1524
+ marketType = None
1525
+ marketType, params = self.handle_market_type_and_params('fetchMyTrades', market, params)
1526
+ isContract = self.in_array(marketType, ['swap', 'future'])
1077
1527
  request: dict = {
1078
1528
  # 'from': 12345678, # A 'trade Id'. The query begins at ‘from'.
1079
1529
  # 'direction': 'PRE', # PRE, NEXT The direction before or after ‘from'.
1080
1530
  }
1531
+ startKey = 'sTime' if isContract else 'startTime'
1532
+ endKey = 'eTime' if isContract else 'endTime'
1081
1533
  if since is not None:
1082
- request['startTime'] = since
1534
+ request[startKey] = since
1083
1535
  if limit is not None:
1084
1536
  request['limit'] = limit
1085
- request, params = self.handle_until_option('endTime', request, params)
1537
+ if isContract and symbol is not None:
1538
+ request['symbol'] = market['id']
1539
+ request, params = self.handle_until_option(endKey, request, params)
1540
+ if isContract:
1541
+ raw = await self.swapPrivateGetV3TradeOrderTrades(self.extend(request, params))
1542
+ #
1543
+ # {
1544
+ # "code": "200",
1545
+ # "msg": "",
1546
+ # "data": [
1547
+ # {
1548
+ # "symbol": "BTC_USDT_PERP",
1549
+ # "trdId": "105813553",
1550
+ # "side": "SELL",
1551
+ # "type": "TRADE",
1552
+ # "mgnMode": "CROSS",
1553
+ # "ordType": "MARKET",
1554
+ # "clOrdId": "polo418912106147315112",
1555
+ # "role": "TAKER",
1556
+ # "px": "84704.9",
1557
+ # "qty": "1",
1558
+ # "cTime": "1740842829430",
1559
+ # "uTime": "1740842829450",
1560
+ # "feeCcy": "USDT",
1561
+ # "feeAmt": "0.04235245",
1562
+ # "deductCcy": "",
1563
+ # "deductAmt": "0",
1564
+ # "feeRate": "0.0005",
1565
+ # "id": "418912106342654592",
1566
+ # "posSide": "BOTH",
1567
+ # "ordId": "418912106147315112",
1568
+ # "qCcy": "USDT",
1569
+ # "value": "84.7049",
1570
+ # "actType": "TRADING"
1571
+ # },
1572
+ #
1573
+ data = self.safe_list(raw, 'data')
1574
+ return self.parse_trades(data, market, since, limit)
1086
1575
  response = await self.privateGetTrades(self.extend(request, params))
1087
1576
  #
1088
1577
  # [
@@ -1143,7 +1632,9 @@ class poloniex(Exchange, ImplicitAPI):
1143
1632
  # "updateTime" : 16xxxxxxxxx36
1144
1633
  # }
1145
1634
  #
1146
- # fetchOpenOrders
1635
+ # fetchOpenOrders(and fetchClosedOrders same for contracts)
1636
+ #
1637
+ # spot:
1147
1638
  #
1148
1639
  # {
1149
1640
  # "id": "24993088082542592",
@@ -1164,14 +1655,60 @@ class poloniex(Exchange, ImplicitAPI):
1164
1655
  # "updateTime": 1646925216548
1165
1656
  # }
1166
1657
  #
1658
+ # contract:
1659
+ #
1660
+ # {
1661
+ # "symbol": "BTC_USDT_PERP",
1662
+ # "side": "BUY",
1663
+ # "type": "LIMIT",
1664
+ # "ordId": "418890767248232148",
1665
+ # "clOrdId": "polo418890767248232148",
1666
+ # "mgnMode": "CROSS",
1667
+ # "px": "81130.13",
1668
+ # "reduceOnly": False,
1669
+ # "lever": "20",
1670
+ # "state": "NEW",
1671
+ # "source": "WEB",
1672
+ # "timeInForce": "GTC",
1673
+ # "tpTrgPx": "",
1674
+ # "tpPx": "",
1675
+ # "tpTrgPxType": "",
1676
+ # "slTrgPx": "",
1677
+ # "slPx": "",
1678
+ # "slTrgPxType": "",
1679
+ # "avgPx": "0",
1680
+ # "execQty": "0",
1681
+ # "execAmt": "0",
1682
+ # "feeCcy": "",
1683
+ # "feeAmt": "0",
1684
+ # "deductCcy": "0",
1685
+ # "deductAmt": "0",
1686
+ # "stpMode": "NONE", # todo: selfTradePrevention
1687
+ # "cTime": "1740837741523",
1688
+ # "uTime": "1740840846882",
1689
+ # "sz": "1",
1690
+ # "posSide": "BOTH",
1691
+ # "qCcy": "USDT"
1692
+ # "cancelReason": "", # self field can only be in closed orders
1693
+ # },
1694
+ #
1167
1695
  # createOrder, editOrder
1168
1696
  #
1697
+ # spot:
1698
+ #
1169
1699
  # {
1170
1700
  # "id": "29772698821328896",
1171
1701
  # "clientOrderId": "1234Abc"
1172
1702
  # }
1173
1703
  #
1174
- timestamp = self.safe_integer_2(order, 'timestamp', 'createTime')
1704
+ # contract:
1705
+ #
1706
+ # {
1707
+ # "ordId":"418876147745775616",
1708
+ # "clOrdId":"polo418876147745775616"
1709
+ # }
1710
+ #
1711
+ timestamp = self.safe_integer_n(order, ['timestamp', 'createTime', 'cTime'])
1175
1712
  if timestamp is None:
1176
1713
  timestamp = self.parse8601(self.safe_string(order, 'date'))
1177
1714
  marketId = self.safe_string(order, 'symbol')
@@ -1181,16 +1718,16 @@ class poloniex(Exchange, ImplicitAPI):
1181
1718
  if resultingTrades is not None:
1182
1719
  if not isinstance(resultingTrades, list):
1183
1720
  resultingTrades = self.safe_value(resultingTrades, self.safe_string(market, 'id', marketId))
1184
- price = self.safe_string_2(order, 'price', 'rate')
1185
- amount = self.safe_string(order, 'quantity')
1186
- filled = self.safe_string(order, 'filledQuantity')
1721
+ price = self.safe_string_n(order, ['price', 'rate', 'px'])
1722
+ amount = self.safe_string_2(order, 'quantity', 'sz')
1723
+ filled = self.safe_string_2(order, 'filledQuantity', 'execQty')
1187
1724
  status = self.parse_order_status(self.safe_string(order, 'state'))
1188
1725
  side = self.safe_string_lower(order, 'side')
1189
1726
  rawType = self.safe_string(order, 'type')
1190
1727
  type = self.parse_order_type(rawType)
1191
- id = self.safe_string_n(order, ['orderNumber', 'id', 'orderId'])
1728
+ id = self.safe_string_n(order, ['orderNumber', 'id', 'orderId', 'ordId'])
1192
1729
  fee = None
1193
- feeCurrency = self.safe_string(order, 'tokenFeeCurrency')
1730
+ feeCurrency = self.safe_string_2(order, 'tokenFeeCurrency', 'feeCcy')
1194
1731
  feeCost: Str = None
1195
1732
  feeCurrencyCode: Str = None
1196
1733
  rate = self.safe_string(order, 'fee')
@@ -1199,14 +1736,18 @@ class poloniex(Exchange, ImplicitAPI):
1199
1736
  else:
1200
1737
  # poloniex accepts a 30% discount to pay fees in TRX
1201
1738
  feeCurrencyCode = self.safe_currency_code(feeCurrency)
1202
- feeCost = self.safe_string(order, 'tokenFee')
1739
+ feeCost = self.safe_string_2(order, 'tokenFee', 'feeAmt')
1203
1740
  if feeCost is not None:
1204
1741
  fee = {
1205
1742
  'rate': rate,
1206
1743
  'cost': feeCost,
1207
1744
  'currency': feeCurrencyCode,
1208
1745
  }
1209
- clientOrderId = self.safe_string(order, 'clientOrderId')
1746
+ clientOrderId = self.safe_string_2(order, 'clientOrderId', 'clOrdId')
1747
+ marginMode = self.safe_string_lower(order, 'mgnMode')
1748
+ reduceOnly = self.safe_bool(order, 'reduceOnly')
1749
+ leverage = self.safe_integer(order, 'lever')
1750
+ hedged = self.safe_string(order, 'posSide') != 'BOTH'
1210
1751
  return self.safe_order({
1211
1752
  'info': order,
1212
1753
  'id': id,
@@ -1218,23 +1759,28 @@ class poloniex(Exchange, ImplicitAPI):
1218
1759
  'symbol': symbol,
1219
1760
  'type': type,
1220
1761
  'timeInForce': self.safe_string(order, 'timeInForce'),
1221
- 'postOnly': None,
1762
+ 'postOnly': rawType == 'LIMIT_MAKER',
1222
1763
  'side': side,
1223
1764
  'price': price,
1224
1765
  'triggerPrice': self.safe_string_2(order, 'triggerPrice', 'stopPrice'),
1225
- 'cost': None,
1226
- 'average': self.safe_string(order, 'avgPrice'),
1766
+ 'cost': self.safe_string(order, 'execAmt'),
1767
+ 'average': self.safe_string_2(order, 'avgPrice', 'avgPx'),
1227
1768
  'amount': amount,
1228
1769
  'filled': filled,
1229
1770
  'remaining': None,
1230
1771
  'trades': resultingTrades,
1231
1772
  'fee': fee,
1773
+ 'marginMode': marginMode,
1774
+ 'reduceOnly': reduceOnly,
1775
+ 'leverage': leverage,
1776
+ 'hedged': hedged,
1232
1777
  }, market)
1233
1778
 
1234
1779
  def parse_order_type(self, status):
1235
1780
  statuses: dict = {
1236
1781
  'MARKET': 'market',
1237
1782
  'LIMIT': 'limit',
1783
+ 'LIMIT_MAKER': 'limit',
1238
1784
  'STOP-LIMIT': 'limit',
1239
1785
  'STOP-MARKET': 'market',
1240
1786
  }
@@ -1258,6 +1804,7 @@ class poloniex(Exchange, ImplicitAPI):
1258
1804
 
1259
1805
  https://api-docs.poloniex.com/spot/api/private/order#open-orders
1260
1806
  https://api-docs.poloniex.com/spot/api/private/smart-order#open-orders # trigger orders
1807
+ https://api-docs.poloniex.com/v3/futures/api/trade/get-current-orders
1261
1808
 
1262
1809
  :param str symbol: unified market symbol
1263
1810
  :param int [since]: the earliest time in ms to fetch open orders for
@@ -1272,12 +1819,57 @@ class poloniex(Exchange, ImplicitAPI):
1272
1819
  if symbol is not None:
1273
1820
  market = self.market(symbol)
1274
1821
  request['symbol'] = market['id']
1822
+ marketType = None
1823
+ marketType, params = self.handle_market_type_and_params('fetchOpenOrders', market, params)
1275
1824
  if limit is not None:
1276
- request['limit'] = limit
1825
+ max = 2000 if (marketType == 'spot') else 100
1826
+ request['limit'] = max(limit, max)
1277
1827
  isTrigger = self.safe_value_2(params, 'trigger', 'stop')
1278
1828
  params = self.omit(params, ['trigger', 'stop'])
1279
1829
  response = None
1280
- if isTrigger:
1830
+ if not market['spot']:
1831
+ raw = await self.swapPrivateGetV3TradeOrderOpens(self.extend(request, params))
1832
+ #
1833
+ # {
1834
+ # "code": "200",
1835
+ # "msg": "",
1836
+ # "data": [
1837
+ # {
1838
+ # "symbol": "BTC_USDT_PERP",
1839
+ # "side": "BUY",
1840
+ # "type": "LIMIT",
1841
+ # "ordId": "418890767248232148",
1842
+ # "clOrdId": "polo418890767248232148",
1843
+ # "mgnMode": "CROSS",
1844
+ # "px": "81130.13",
1845
+ # "reduceOnly": False,
1846
+ # "lever": "20",
1847
+ # "state": "NEW",
1848
+ # "source": "WEB",
1849
+ # "timeInForce": "GTC",
1850
+ # "tpTrgPx": "",
1851
+ # "tpPx": "",
1852
+ # "tpTrgPxType": "",
1853
+ # "slTrgPx": "",
1854
+ # "slPx": "",
1855
+ # "slTrgPxType": "",
1856
+ # "avgPx": "0",
1857
+ # "execQty": "0",
1858
+ # "execAmt": "0",
1859
+ # "feeCcy": "",
1860
+ # "feeAmt": "0",
1861
+ # "deductCcy": "0",
1862
+ # "deductAmt": "0",
1863
+ # "stpMode": "NONE",
1864
+ # "cTime": "1740837741523",
1865
+ # "uTime": "1740840846882",
1866
+ # "sz": "1",
1867
+ # "posSide": "BOTH",
1868
+ # "qCcy": "USDT"
1869
+ # },
1870
+ #
1871
+ response = self.safe_list(raw, 'data')
1872
+ elif isTrigger:
1281
1873
  response = await self.privateGetSmartorders(self.extend(request, params))
1282
1874
  else:
1283
1875
  response = await self.privateGetOrders(self.extend(request, params))
@@ -1307,6 +1899,78 @@ class poloniex(Exchange, ImplicitAPI):
1307
1899
  extension: dict = {'status': 'open'}
1308
1900
  return self.parse_orders(response, market, since, limit, extension)
1309
1901
 
1902
+ async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
1903
+ """
1904
+
1905
+ https://api-docs.poloniex.com/v3/futures/api/trade/get-order-history
1906
+
1907
+ fetches information on multiple closed orders made by the user
1908
+ :param str symbol: unified market symbol of the market orders were made in
1909
+ :param int [since]: the earliest time in ms to fetch orders for
1910
+ :param int [limit]: the maximum number of order structures to retrieve
1911
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1912
+ :param int [params.until]: timestamp in ms of the latest entry
1913
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
1914
+ """
1915
+ await self.load_markets()
1916
+ market = None
1917
+ request: dict = {}
1918
+ if symbol is not None:
1919
+ market = self.market(symbol)
1920
+ request['symbol'] = market['id']
1921
+ marketType = None
1922
+ marketType, params = self.handle_market_type_and_params('fetchClosedOrders', market, params, 'swap')
1923
+ if marketType == 'spot':
1924
+ raise NotSupported(self.id + ' fetchClosedOrders() is not supported for spot markets yet')
1925
+ if limit is not None:
1926
+ request['limit'] = min(200, limit)
1927
+ if since is not None:
1928
+ request['sTime'] = since
1929
+ request, params = self.handle_until_option('eTime', request, params)
1930
+ response = await self.swapPrivateGetV3TradeOrderHistory(self.extend(request, params))
1931
+ #
1932
+ # {
1933
+ # "code": "200",
1934
+ # "msg": "",
1935
+ # "data": [
1936
+ # {
1937
+ # "symbol": "BTC_USDT_PERP",
1938
+ # "side": "SELL",
1939
+ # "type": "MARKET",
1940
+ # "ordId": "418912106147315712",
1941
+ # "clOrdId": "polo418912106147315712",
1942
+ # "mgnMode": "CROSS",
1943
+ # "px": "0",
1944
+ # "sz": "2",
1945
+ # "lever": "20",
1946
+ # "state": "FILLED",
1947
+ # "cancelReason": "",
1948
+ # "source": "WEB",
1949
+ # "reduceOnly": "true",
1950
+ # "timeInForce": "GTC",
1951
+ # "tpTrgPx": "",
1952
+ # "tpPx": "",
1953
+ # "tpTrgPxType": "",
1954
+ # "slTrgPx": "",
1955
+ # "slPx": "",
1956
+ # "slTrgPxType": "",
1957
+ # "avgPx": "84705.56",
1958
+ # "execQty": "2",
1959
+ # "execAmt": "169.41112",
1960
+ # "feeCcy": "USDT",
1961
+ # "feeAmt": "0.08470556",
1962
+ # "deductCcy": "0",
1963
+ # "deductAmt": "0",
1964
+ # "stpMode": "NONE",
1965
+ # "cTime": "1740842829116",
1966
+ # "uTime": "1740842829130",
1967
+ # "posSide": "BOTH",
1968
+ # "qCcy": "USDT"
1969
+ # },
1970
+ #
1971
+ data = self.safe_list(response, 'data', [])
1972
+ return self.parse_orders(data, market, since, limit)
1973
+
1310
1974
  async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
1311
1975
  """
1312
1976
  create a trade order
@@ -1326,19 +1990,23 @@ class poloniex(Exchange, ImplicitAPI):
1326
1990
  """
1327
1991
  await self.load_markets()
1328
1992
  market = self.market(symbol)
1329
- if not market['spot']:
1330
- raise NotSupported(self.id + ' createOrder() does not support ' + market['type'] + ' orders, only spot orders are accepted')
1331
1993
  request: dict = {
1332
1994
  'symbol': market['id'],
1333
- 'side': side,
1334
- # 'timeInForce': timeInForce,
1995
+ 'side': side.upper(), # uppercase, both for spot & swap
1996
+ # 'timeInForce': timeInForce, # matches unified values
1335
1997
  # 'accountType': 'SPOT',
1336
1998
  # 'amount': amount,
1337
1999
  }
1338
2000
  triggerPrice = self.safe_number_2(params, 'stopPrice', 'triggerPrice')
1339
2001
  request, params = self.order_request(symbol, type, side, amount, request, price, params)
1340
2002
  response = None
1341
- if triggerPrice is not None:
2003
+ if market['swap'] or market['future']:
2004
+ responseInitial = await self.swapPrivatePostV3TradeOrder(self.extend(request, params))
2005
+ #
2006
+ # {"code":200,"msg":"Success","data":{"ordId":"418876147745775616","clOrdId":"polo418876147745775616"}}
2007
+ #
2008
+ response = self.safe_dict(responseInitial, 'data')
2009
+ elif triggerPrice is not None:
1342
2010
  response = await self.privatePostSmartorders(self.extend(request, params))
1343
2011
  else:
1344
2012
  response = await self.privatePostOrders(self.extend(request, params))
@@ -1348,19 +2016,31 @@ class poloniex(Exchange, ImplicitAPI):
1348
2016
  # "clientOrderId" : ""
1349
2017
  # }
1350
2018
  #
1351
- response = self.extend(response, {
1352
- 'type': type,
1353
- 'side': side,
1354
- })
1355
2019
  return self.parse_order(response, market)
1356
2020
 
1357
2021
  def order_request(self, symbol, type, side, amount, request, price=None, params={}):
2022
+ triggerPrice = self.safe_number_2(params, 'stopPrice', 'triggerPrice')
2023
+ market = self.market(symbol)
2024
+ if market['contract']:
2025
+ marginMode = None
2026
+ marginMode, params = self.handle_param_string(params, 'marginMode')
2027
+ if marginMode is not None:
2028
+ self.check_required_argument('createOrder', marginMode, 'marginMode', ['cross', 'isolated'])
2029
+ request['mgnMode'] = marginMode.upper()
2030
+ hedged = None
2031
+ hedged, params = self.handle_param_string(params, 'hedged')
2032
+ if hedged:
2033
+ if marginMode is None:
2034
+ raise ArgumentsRequired(self.id + ' createOrder() requires a marginMode parameter "cross" or "isolated" for hedged orders')
2035
+ if not ('posSide' in params):
2036
+ raise ArgumentsRequired(self.id + ' createOrder() requires a posSide parameter "LONG" or "SHORT" for hedged orders')
1358
2037
  upperCaseType = type.upper()
1359
2038
  isMarket = upperCaseType == 'MARKET'
1360
2039
  isPostOnly = self.is_post_only(isMarket, upperCaseType == 'LIMIT_MAKER', params)
1361
- triggerPrice = self.safe_number_2(params, 'stopPrice', 'triggerPrice')
1362
2040
  params = self.omit(params, ['postOnly', 'triggerPrice', 'stopPrice'])
1363
2041
  if triggerPrice is not None:
2042
+ if not market['spot']:
2043
+ raise InvalidOrder(self.id + ' createOrder() does not support trigger orders for ' + market['type'] + ' markets')
1364
2044
  upperCaseType = 'STOP' if (price is None) else 'STOP_LIMIT'
1365
2045
  request['stopPrice'] = triggerPrice
1366
2046
  elif isPostOnly:
@@ -1375,7 +2055,7 @@ class poloniex(Exchange, ImplicitAPI):
1375
2055
  params = self.omit(params, 'cost')
1376
2056
  if cost is not None:
1377
2057
  quoteAmount = self.cost_to_precision(symbol, cost)
1378
- elif createMarketBuyOrderRequiresPrice:
2058
+ elif createMarketBuyOrderRequiresPrice and market['spot']:
1379
2059
  if price is None:
1380
2060
  raise InvalidOrder(self.id + ' createOrder() requires the price argument for market buy orders to calculate the total cost to spend(amount * price), alternatively set the createMarketBuyOrderRequiresPrice option or param to False and pass the cost to spend(quote quantity) in the amount argument')
1381
2061
  else:
@@ -1385,12 +2065,16 @@ class poloniex(Exchange, ImplicitAPI):
1385
2065
  quoteAmount = self.cost_to_precision(symbol, costRequest)
1386
2066
  else:
1387
2067
  quoteAmount = self.cost_to_precision(symbol, amount)
1388
- request['amount'] = quoteAmount
2068
+ amountKey = 'amount' if market['spot'] else 'sz'
2069
+ request[amountKey] = quoteAmount
1389
2070
  else:
1390
- request['quantity'] = self.amount_to_precision(symbol, amount)
2071
+ amountKey = 'quantity' if market['spot'] else 'sz'
2072
+ request[amountKey] = self.amount_to_precision(symbol, amount)
1391
2073
  else:
1392
- request['quantity'] = self.amount_to_precision(symbol, amount)
1393
- request['price'] = self.price_to_precision(symbol, price)
2074
+ amountKey = 'quantity' if market['spot'] else 'sz'
2075
+ request[amountKey] = self.amount_to_precision(symbol, amount)
2076
+ priceKey = 'price' if market['spot'] else 'px'
2077
+ request[priceKey] = self.price_to_precision(symbol, price)
1394
2078
  clientOrderId = self.safe_string(params, 'clientOrderId')
1395
2079
  if clientOrderId is not None:
1396
2080
  request['clientOrderId'] = clientOrderId
@@ -1456,7 +2140,25 @@ class poloniex(Exchange, ImplicitAPI):
1456
2140
  # @returns {object} An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
1457
2141
  #
1458
2142
  await self.load_markets()
2143
+ if symbol is None:
2144
+ raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
2145
+ market = self.market(symbol)
1459
2146
  request: dict = {}
2147
+ if not market['spot']:
2148
+ request['symbol'] = market['id']
2149
+ request['ordId'] = id
2150
+ raw = await self.swapPrivateDeleteV3TradeOrder(self.extend(request, params))
2151
+ #
2152
+ # {
2153
+ # "code": "200",
2154
+ # "msg": "Success",
2155
+ # "data": {
2156
+ # "ordId": "418886099910612040",
2157
+ # "clOrdId": "polo418886099910612040"
2158
+ # }
2159
+ # }
2160
+ #
2161
+ return self.parse_order(self.safe_dict(raw, 'data'))
1460
2162
  clientOrderId = self.safe_value(params, 'clientOrderId')
1461
2163
  if clientOrderId is not None:
1462
2164
  id = clientOrderId
@@ -1485,6 +2187,7 @@ class poloniex(Exchange, ImplicitAPI):
1485
2187
 
1486
2188
  https://api-docs.poloniex.com/spot/api/private/order#cancel-all-orders
1487
2189
  https://api-docs.poloniex.com/spot/api/private/smart-order#cancel-all-orders # trigger orders
2190
+ https://api-docs.poloniex.com/v3/futures/api/trade/cancel-all-orders - contract markets
1488
2191
 
1489
2192
  :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
1490
2193
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -1502,9 +2205,29 @@ class poloniex(Exchange, ImplicitAPI):
1502
2205
  request['symbols'] = [
1503
2206
  market['id'],
1504
2207
  ]
2208
+ response = None
2209
+ marketType = None
2210
+ marketType, params = self.handle_market_type_and_params('cancelAllOrders', market, params)
2211
+ if marketType == 'swap' or marketType == 'future':
2212
+ raw = await self.swapPrivateDeleteV3TradeAllOrders(self.extend(request, params))
2213
+ #
2214
+ # {
2215
+ # "code": "200",
2216
+ # "msg": "Success",
2217
+ # "data": [
2218
+ # {
2219
+ # "code": "200",
2220
+ # "msg": "Success",
2221
+ # "ordId": "418885787866388511",
2222
+ # "clOrdId": "polo418885787866388511"
2223
+ # }
2224
+ # ]
2225
+ # }
2226
+ #
2227
+ response = self.safe_list(raw, 'data')
2228
+ return self.parse_orders(response, market)
1505
2229
  isTrigger = self.safe_value_2(params, 'trigger', 'stop')
1506
2230
  params = self.omit(params, ['trigger', 'stop'])
1507
- response = None
1508
2231
  if isTrigger:
1509
2232
  response = await self.privateDeleteSmartorders(self.extend(request, params))
1510
2233
  else:
@@ -1546,6 +2269,14 @@ class poloniex(Exchange, ImplicitAPI):
1546
2269
  request: dict = {
1547
2270
  'id': id,
1548
2271
  }
2272
+ market = None
2273
+ if symbol is not None:
2274
+ market = self.market(symbol)
2275
+ request['symbol'] = market['id']
2276
+ marketType = None
2277
+ marketType, params = self.handle_market_type_and_params('fetchOrder', market, params)
2278
+ if marketType != 'spot':
2279
+ raise NotSupported(self.id + ' fetchOrder() is not supported for ' + marketType + ' markets yet')
1549
2280
  isTrigger = self.safe_value_2(params, 'trigger', 'stop')
1550
2281
  params = self.omit(params, ['trigger', 'stop'])
1551
2282
  response = None
@@ -1632,6 +2363,22 @@ class poloniex(Exchange, ImplicitAPI):
1632
2363
  'timestamp': None,
1633
2364
  'datetime': None,
1634
2365
  }
2366
+ # for swap
2367
+ if not isinstance(response, list):
2368
+ ts = self.safe_integer(response, 'uTime')
2369
+ result['timestamp'] = ts
2370
+ result['datetime'] = self.iso8601(ts)
2371
+ details = self.safe_list(response, 'details', [])
2372
+ for i in range(0, len(details)):
2373
+ balance = details[i]
2374
+ currencyId = self.safe_string(balance, 'ccy')
2375
+ code = self.safe_currency_code(currencyId)
2376
+ account = self.account()
2377
+ account['total'] = self.safe_string(balance, 'avail')
2378
+ account['used'] = self.safe_string(balance, 'im')
2379
+ result[code] = account
2380
+ return self.safe_balance(result)
2381
+ # for spot
1635
2382
  for i in range(0, len(response)):
1636
2383
  account = self.safe_value(response, i, {})
1637
2384
  balances = self.safe_value(account, 'balances')
@@ -1650,11 +2397,55 @@ class poloniex(Exchange, ImplicitAPI):
1650
2397
  query for balance and get the amount of funds available for trading or funds locked in orders
1651
2398
 
1652
2399
  https://api-docs.poloniex.com/spot/api/private/account#all-account-balances
2400
+ https://api-docs.poloniex.com/v3/futures/api/account/balance
1653
2401
 
1654
2402
  :param dict [params]: extra parameters specific to the exchange API endpoint
1655
2403
  :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
1656
2404
  """
1657
2405
  await self.load_markets()
2406
+ marketType = None
2407
+ marketType, params = self.handle_market_type_and_params('fetchBalance', None, params)
2408
+ if marketType != 'spot':
2409
+ responseRaw = await self.swapPrivateGetV3AccountBalance(params)
2410
+ #
2411
+ # {
2412
+ # "code": "200",
2413
+ # "msg": "",
2414
+ # "data": {
2415
+ # "state": "NORMAL",
2416
+ # "eq": "9.98571622",
2417
+ # "isoEq": "0",
2418
+ # "im": "0",
2419
+ # "mm": "0",
2420
+ # "mmr": "0",
2421
+ # "upl": "0",
2422
+ # "availMgn": "9.98571622",
2423
+ # "cTime": "1738093601775",
2424
+ # "uTime": "1740829116236",
2425
+ # "details": [
2426
+ # {
2427
+ # "ccy": "USDT",
2428
+ # "eq": "9.98571622",
2429
+ # "isoEq": "0",
2430
+ # "avail": "9.98571622",
2431
+ # "trdHold": "0",
2432
+ # "upl": "0",
2433
+ # "isoAvail": "0",
2434
+ # "isoHold": "0",
2435
+ # "isoUpl": "0",
2436
+ # "im": "0",
2437
+ # "mm": "0",
2438
+ # "mmr": "0",
2439
+ # "imr": "0",
2440
+ # "cTime": "1740829116236",
2441
+ # "uTime": "1740829116236"
2442
+ # }
2443
+ # ]
2444
+ # }
2445
+ # }
2446
+ #
2447
+ data = self.safe_dict(responseRaw, 'data', {})
2448
+ return self.parse_balance(data)
1658
2449
  request: dict = {
1659
2450
  'accountType': 'SPOT',
1660
2451
  }
@@ -1714,6 +2505,7 @@ class poloniex(Exchange, ImplicitAPI):
1714
2505
  fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
1715
2506
 
1716
2507
  https://api-docs.poloniex.com/spot/api/public/market-data#order-book
2508
+ https://api-docs.poloniex.com/v3/futures/api/market/get-order-book
1717
2509
 
1718
2510
  :param str symbol: unified symbol of the market to fetch the order book for
1719
2511
  :param int [limit]: the maximum amount of order book entries to return
@@ -1727,6 +2519,25 @@ class poloniex(Exchange, ImplicitAPI):
1727
2519
  }
1728
2520
  if limit is not None:
1729
2521
  request['limit'] = limit # The default value of limit is 10. Valid limit values are: 5, 10, 20, 50, 100, 150.
2522
+ if market['contract']:
2523
+ request['limit'] = self.find_nearest_ceiling([5, 10, 20, 100, 150], limit)
2524
+ if market['contract']:
2525
+ responseRaw = await self.swapPublicGetV3MarketOrderBook(self.extend(request, params))
2526
+ #
2527
+ # {
2528
+ # "code": 200,
2529
+ # "data": {
2530
+ # "asks": [["58700", "9934"], ..],
2531
+ # "bids": [["58600", "9952"], ..],
2532
+ # "s": "100",
2533
+ # "ts": 1719974138333
2534
+ # },
2535
+ # "msg": "Success"
2536
+ # }
2537
+ #
2538
+ data = self.safe_dict(responseRaw, 'data', {})
2539
+ ts = self.safe_integer(data, 'ts')
2540
+ return self.parse_order_book(data, symbol, ts)
1730
2541
  response = await self.publicGetMarketsSymbolOrderBook(self.extend(request, params))
1731
2542
  #
1732
2543
  # {
@@ -2300,14 +3111,381 @@ class poloniex(Exchange, ImplicitAPI):
2300
3111
  },
2301
3112
  }
2302
3113
 
3114
+ async def set_leverage(self, leverage: Int, symbol: Str = None, params={}):
3115
+ """
3116
+ set the level of leverage for a market
3117
+
3118
+ https://api-docs.poloniex.com/v3/futures/api/positions/set-leverage
3119
+
3120
+ :param int leverage: the rate of leverage
3121
+ :param str symbol: unified market symbol
3122
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3123
+ :param str [params.marginMode]: 'cross' or 'isolated'
3124
+ :returns dict: response from the exchange
3125
+ """
3126
+ if symbol is None:
3127
+ raise ArgumentsRequired(self.id + ' setLeverage() requires a symbol argument')
3128
+ await self.load_markets()
3129
+ market = self.market(symbol)
3130
+ marginMode = None
3131
+ marginMode, params = self.handle_margin_mode_and_params('setLeverage', params)
3132
+ if marginMode is None:
3133
+ raise ArgumentsRequired(self.id + ' setLeverage() requires a marginMode parameter "cross" or "isolated"')
3134
+ hedged: Bool = None
3135
+ hedged, params = self.handle_param_bool(params, 'hedged', False)
3136
+ if hedged:
3137
+ if not ('posSide' in params):
3138
+ raise ArgumentsRequired(self.id + ' setLeverage() requires a posSide parameter for hedged mode: "LONG" or "SHORT"')
3139
+ request: dict = {
3140
+ 'lever': leverage,
3141
+ 'mgnMode': marginMode.upper(),
3142
+ 'symbol': market['id'],
3143
+ }
3144
+ response = await self.swapPrivatePostV3PositionLeverage(self.extend(request, params))
3145
+ return response
3146
+
3147
+ async def fetch_leverage(self, symbol: str, params={}) -> Leverage:
3148
+ """
3149
+ fetch the set leverage for a market
3150
+
3151
+ https://api-docs.poloniex.com/v3/futures/api/positions/get-leverages
3152
+
3153
+ :param str symbol: unified market symbol
3154
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3155
+ :returns dict: a `leverage structure <https://docs.ccxt.com/#/?id=leverage-structure>`
3156
+ """
3157
+ await self.load_markets()
3158
+ market = self.market(symbol)
3159
+ request: dict = {
3160
+ 'symbol': market['id'],
3161
+ }
3162
+ marginMode = None
3163
+ marginMode, params = self.handle_margin_mode_and_params('fetchLeverage', params)
3164
+ if marginMode is None:
3165
+ raise ArgumentsRequired(self.id + ' fetchLeverage() requires a marginMode parameter "cross" or "isolated"')
3166
+ request['mgnMode'] = marginMode.upper()
3167
+ response = await self.swapPrivateGetV3PositionLeverages(self.extend(request, params))
3168
+ #
3169
+ # for one-way mode:
3170
+ #
3171
+ # {
3172
+ # "code": "200",
3173
+ # "msg": "",
3174
+ # "data": [
3175
+ # {
3176
+ # "symbol": "BTC_USDT_PERP",
3177
+ # "lever": "10",
3178
+ # "mgnMode": "CROSS",
3179
+ # "posSide": "BOTH"
3180
+ # }
3181
+ # ]
3182
+ # }
3183
+ #
3184
+ # for hedge:
3185
+ #
3186
+ # {
3187
+ # "code": "200",
3188
+ # "msg": "",
3189
+ # "data": [
3190
+ # {
3191
+ # "symbol": "BTC_USDT_PERP",
3192
+ # "lever": "20",
3193
+ # "mgnMode": "CROSS",
3194
+ # "posSide": "SHORT"
3195
+ # },
3196
+ # {
3197
+ # "symbol": "BTC_USDT_PERP",
3198
+ # "lever": "20",
3199
+ # "mgnMode": "CROSS",
3200
+ # "posSide": "LONG"
3201
+ # }
3202
+ # ]
3203
+ # }
3204
+ #
3205
+ return self.parse_leverage(response, market)
3206
+
3207
+ def parse_leverage(self, leverage: dict, market: Market = None) -> Leverage:
3208
+ shortLeverage: Int = None
3209
+ longLeverage: Int = None
3210
+ marketId: Str = None
3211
+ marginMode: Str = None
3212
+ data = self.safe_list(leverage, 'data')
3213
+ for i in range(0, len(data)):
3214
+ entry = data[i]
3215
+ marketId = self.safe_string(entry, 'symbol')
3216
+ marginMode = self.safe_string(entry, 'mgnMode')
3217
+ lever = self.safe_integer(entry, 'lever')
3218
+ posSide = self.safe_string(entry, 'posSide')
3219
+ if posSide == 'LONG':
3220
+ longLeverage = lever
3221
+ elif posSide == 'SHORT':
3222
+ shortLeverage = lever
3223
+ else:
3224
+ longLeverage = lever
3225
+ shortLeverage = lever
3226
+ return {
3227
+ 'info': leverage,
3228
+ 'symbol': self.safe_symbol(marketId, market),
3229
+ 'marginMode': marginMode,
3230
+ 'longLeverage': longLeverage,
3231
+ 'shortLeverage': shortLeverage,
3232
+ }
3233
+
3234
+ async def fetch_position_mode(self, symbol: Str = None, params={}):
3235
+ """
3236
+ fetchs the position mode, hedged or one way, hedged for binance is set identically for all linear markets or all inverse markets
3237
+
3238
+ https://api-docs.poloniex.com/v3/futures/api/positions/position-mode-switch
3239
+
3240
+ :param str symbol: unified symbol of the market to fetch the order book for
3241
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3242
+ :returns dict: an object detailing whether the market is in hedged or one-way mode
3243
+ """
3244
+ response = await self.swapPrivateGetV3PositionMode(params)
3245
+ #
3246
+ # {
3247
+ # "code": "200",
3248
+ # "msg": "Success",
3249
+ # "data": {
3250
+ # "posMode": "ONE_WAY"
3251
+ # }
3252
+ # }
3253
+ #
3254
+ data = self.safe_dict(response, 'data', {})
3255
+ posMode = self.safe_string(data, 'posMode')
3256
+ hedged = posMode == 'HEDGE'
3257
+ return {
3258
+ 'info': response,
3259
+ 'hedged': hedged,
3260
+ }
3261
+
3262
+ async def set_position_mode(self, hedged: bool, symbol: Str = None, params={}):
3263
+ """
3264
+ set hedged to True or False for a market
3265
+
3266
+ https://api-docs.poloniex.com/v3/futures/api/positions/position-mode-switch
3267
+
3268
+ :param bool hedged: set to True to use dualSidePosition
3269
+ :param str symbol: not used by binance setPositionMode()
3270
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3271
+ :returns dict: response from the exchange
3272
+ """
3273
+ mode = 'HEDGE' if hedged else 'ONE_WAY'
3274
+ request: dict = {
3275
+ 'posMode': mode,
3276
+ }
3277
+ response = await self.swapPrivatePostV3PositionMode(self.extend(request, params))
3278
+ #
3279
+ # {
3280
+ # "code": "200",
3281
+ # "msg": "Success",
3282
+ # "data": {}
3283
+ # }
3284
+ #
3285
+ return response
3286
+
3287
+ async def fetch_positions(self, symbols: Strings = None, params={}):
3288
+ """
3289
+ fetch all open positions
3290
+
3291
+ https://api-docs.poloniex.com/v3/futures/api/positions/get-current-position
3292
+
3293
+ :param str[]|None symbols: list of unified market symbols
3294
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3295
+ :param boolean [params.standard]: whether to fetch standard contract positions
3296
+ :returns dict[]: a list of `position structures <https://docs.ccxt.com/#/?id=position-structure>`
3297
+ """
3298
+ await self.load_markets()
3299
+ symbols = self.market_symbols(symbols)
3300
+ response = await self.swapPrivateGetV3TradePositionOpens(params)
3301
+ #
3302
+ # {
3303
+ # "code": "200",
3304
+ # "msg": "",
3305
+ # "data": [
3306
+ # {
3307
+ # "symbol": "BTC_USDT_PERP",
3308
+ # "posSide": "LONG",
3309
+ # "side": "BUY",
3310
+ # "mgnMode": "CROSS",
3311
+ # "openAvgPx": "94193.42",
3312
+ # "qty": "1",
3313
+ # "availQty": "1",
3314
+ # "lever": "20",
3315
+ # "adl": "0.3007",
3316
+ # "liqPx": "84918.201844064386317906",
3317
+ # "im": "4.7047795",
3318
+ # "mm": "0.56457354",
3319
+ # "upl": "-0.09783",
3320
+ # "uplRatio": "-0.0207",
3321
+ # "pnl": "0",
3322
+ # "markPx": "94095.59",
3323
+ # "mgnRatio": "0.0582",
3324
+ # "state": "NORMAL",
3325
+ # "cTime": "1740950344401",
3326
+ # "uTime": "1740950344401",
3327
+ # "mgn": "4.7047795",
3328
+ # "actType": "TRADING",
3329
+ # "maxWAmt": "0",
3330
+ # "tpTrgPx": "",
3331
+ # "slTrgPx": ""
3332
+ # }
3333
+ # ]
3334
+ # }
3335
+ #
3336
+ positions = self.safe_list(response, 'data', [])
3337
+ return self.parse_positions(positions, symbols)
3338
+
3339
+ def parse_position(self, position: dict, market: Market = None):
3340
+ #
3341
+ # {
3342
+ # "symbol": "BTC_USDT_PERP",
3343
+ # "posSide": "LONG",
3344
+ # "side": "BUY",
3345
+ # "mgnMode": "CROSS",
3346
+ # "openAvgPx": "94193.42",
3347
+ # "qty": "1",
3348
+ # "availQty": "1",
3349
+ # "lever": "20",
3350
+ # "adl": "0.3007",
3351
+ # "liqPx": "84918.201844064386317906",
3352
+ # "im": "4.7047795",
3353
+ # "mm": "0.56457354",
3354
+ # "upl": "-0.09783",
3355
+ # "uplRatio": "-0.0207",
3356
+ # "pnl": "0",
3357
+ # "markPx": "94095.59",
3358
+ # "mgnRatio": "0.0582",
3359
+ # "state": "NORMAL",
3360
+ # "cTime": "1740950344401",
3361
+ # "uTime": "1740950344401",
3362
+ # "mgn": "4.7047795",
3363
+ # "actType": "TRADING",
3364
+ # "maxWAmt": "0",
3365
+ # "tpTrgPx": "",
3366
+ # "slTrgPx": ""
3367
+ # }
3368
+ #
3369
+ marketId = self.safe_string(position, 'symbol')
3370
+ market = self.safe_market(marketId, market)
3371
+ timestamp = self.safe_integer(position, 'cTime')
3372
+ marginMode = self.safe_string_lower(position, 'mgnMode')
3373
+ leverage = self.safe_string(position, 'lever')
3374
+ initialMargin = self.safe_string(position, 'im')
3375
+ notional = Precise.string_mul(leverage, initialMargin)
3376
+ qty = self.safe_string(position, 'qty')
3377
+ avgPrice = self.safe_string(position, 'openAvgPx')
3378
+ collateral = Precise.string_mul(qty, avgPrice)
3379
+ # todo: some more fields
3380
+ return self.safe_position({
3381
+ 'info': position,
3382
+ 'id': None,
3383
+ 'symbol': market['symbol'],
3384
+ 'notional': notional,
3385
+ 'marginMode': marginMode,
3386
+ 'liquidationPrice': self.safe_number(position, 'liqPx'),
3387
+ 'entryPrice': self.safe_number(position, 'openAvgPx'),
3388
+ 'unrealizedPnl': self.safe_number(position, 'upl'),
3389
+ 'percentage': None,
3390
+ 'contracts': self.safe_number(position, 'qty'),
3391
+ 'contractSize': None,
3392
+ 'markPrice': self.safe_number(position, 'markPx'),
3393
+ 'lastPrice': None,
3394
+ 'side': self.safe_string_lower(position, 'posSide'),
3395
+ 'hedged': None,
3396
+ 'timestamp': timestamp,
3397
+ 'datetime': self.iso8601(timestamp),
3398
+ 'lastUpdateTimestamp': None,
3399
+ 'maintenanceMargin': self.safe_number(position, 'mm'),
3400
+ 'maintenanceMarginPercentage': None,
3401
+ 'collateral': collateral,
3402
+ 'initialMargin': initialMargin,
3403
+ 'initialMarginPercentage': None,
3404
+ 'leverage': int(leverage),
3405
+ 'marginRatio': self.safe_number(position, 'mgnRatio'),
3406
+ 'stopLossPrice': self.safe_number(position, 'slTrgPx'),
3407
+ 'takeProfitPrice': self.safe_number(position, 'tpTrgPx'),
3408
+ })
3409
+
3410
+ async def modify_margin_helper(self, symbol: str, amount, type, params={}) -> MarginModification:
3411
+ await self.load_markets()
3412
+ market = self.market(symbol)
3413
+ amount = self.amount_to_precision(symbol, amount)
3414
+ request: dict = {
3415
+ 'symbol': market['id'],
3416
+ 'amt': Precise.string_abs(amount),
3417
+ 'type': type.upper(), # 'ADD' or 'REDUCE'
3418
+ }
3419
+ # todo: hedged handling, tricky
3420
+ if not ('posMode' in params):
3421
+ request['posMode'] = 'BOTH'
3422
+ response = await self.swapPrivatePostV3TradePositionMargin(self.extend(request, params))
3423
+ #
3424
+ # {
3425
+ # "code": 200,
3426
+ # "data": {
3427
+ # "amt": "50",
3428
+ # "lever": "20",
3429
+ # "symbol": "DOT_USDT_PERP",
3430
+ # "posSide": "BOTH",
3431
+ # "type": "ADD"
3432
+ # },
3433
+ # "msg": "Success"
3434
+ # }
3435
+ #
3436
+ if type == 'reduce':
3437
+ amount = Precise.string_abs(amount)
3438
+ data = self.safe_dict(response, 'data')
3439
+ return self.parse_margin_modification(data, market)
3440
+
3441
+ def parse_margin_modification(self, data: dict, market: Market = None) -> MarginModification:
3442
+ marketId = self.safe_string(data, 'symbol')
3443
+ market = self.safe_market(marketId, market)
3444
+ rawType = self.safe_string(data, 'type')
3445
+ type = 'add' if (rawType == 'ADD') else 'reduce'
3446
+ return {
3447
+ 'info': data,
3448
+ 'symbol': market['symbol'],
3449
+ 'type': type,
3450
+ 'marginMode': None,
3451
+ 'amount': self.safe_number(data, 'amt'),
3452
+ 'total': None,
3453
+ 'code': None,
3454
+ 'status': 'ok',
3455
+ 'timestamp': None,
3456
+ 'datetime': None,
3457
+ }
3458
+
3459
+ async def reduce_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
3460
+ """
3461
+ remove margin from a position
3462
+ :param str symbol: unified market symbol
3463
+ :param float amount: the amount of margin to remove
3464
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3465
+ :returns dict: a `margin structure <https://docs.ccxt.com/#/?id=reduce-margin-structure>`
3466
+ """
3467
+ return await self.modify_margin_helper(symbol, -amount, 'reduce', params)
3468
+
3469
+ async def add_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
3470
+ """
3471
+ add margin
3472
+ :param str symbol: unified market symbol
3473
+ :param float amount: amount of margin to add
3474
+ :param dict [params]: extra parameters specific to the exchange API endpoint
3475
+ :returns dict: a `margin structure <https://docs.ccxt.com/#/?id=add-margin-structure>`
3476
+ """
3477
+ return await self.modify_margin_helper(symbol, amount, 'add', params)
3478
+
2303
3479
  def nonce(self):
2304
3480
  return self.milliseconds()
2305
3481
 
2306
3482
  def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
2307
- url = self.urls['api']['rest']
3483
+ url = self.urls['api']['spot']
3484
+ if self.in_array(api, ['swapPublic', 'swapPrivate']):
3485
+ url = self.urls['api']['swap']
2308
3486
  query = self.omit(params, self.extract_params(path))
2309
3487
  implodedPath = self.implode_params(path, params)
2310
- if api == 'public':
3488
+ if api == 'public' or api == 'swapPublic':
2311
3489
  url += '/' + implodedPath
2312
3490
  if query:
2313
3491
  url += '?' + self.urlencode(query)