ccxt 4.2.47__py2.py3-none-any.whl → 4.2.49__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 (50) hide show
  1. ccxt/__init__.py +1 -1
  2. ccxt/abstract/bitstamp.py +8 -0
  3. ccxt/abstract/indodax.py +9 -8
  4. ccxt/async_support/__init__.py +1 -1
  5. ccxt/async_support/base/exchange.py +1 -1
  6. ccxt/async_support/binance.py +9 -4
  7. ccxt/async_support/bitmart.py +24 -2
  8. ccxt/async_support/bitstamp.py +8 -0
  9. ccxt/async_support/btcalpha.py +4 -0
  10. ccxt/async_support/btcmarkets.py +4 -0
  11. ccxt/async_support/btcturk.py +4 -0
  12. ccxt/async_support/bybit.py +132 -6
  13. ccxt/async_support/idex.py +48 -1
  14. ccxt/async_support/independentreserve.py +47 -1
  15. ccxt/async_support/indodax.py +115 -19
  16. ccxt/async_support/latoken.py +16 -0
  17. ccxt/async_support/luno.py +18 -0
  18. ccxt/async_support/lykke.py +19 -0
  19. ccxt/async_support/ndax.py +18 -0
  20. ccxt/async_support/okx.py +32 -5
  21. ccxt/async_support/upbit.py +92 -17
  22. ccxt/base/exchange.py +2 -2
  23. ccxt/binance.py +9 -4
  24. ccxt/bitmart.py +24 -2
  25. ccxt/bitstamp.py +8 -0
  26. ccxt/btcalpha.py +4 -0
  27. ccxt/btcmarkets.py +4 -0
  28. ccxt/btcturk.py +4 -0
  29. ccxt/bybit.py +132 -6
  30. ccxt/idex.py +48 -1
  31. ccxt/independentreserve.py +47 -1
  32. ccxt/indodax.py +115 -19
  33. ccxt/latoken.py +16 -0
  34. ccxt/luno.py +18 -0
  35. ccxt/lykke.py +19 -0
  36. ccxt/ndax.py +18 -0
  37. ccxt/okx.py +32 -5
  38. ccxt/pro/__init__.py +1 -1
  39. ccxt/pro/ascendex.py +19 -7
  40. ccxt/pro/bitget.py +24 -7
  41. ccxt/pro/bitstamp.py +1 -1
  42. ccxt/pro/hitbtc.py +6 -11
  43. ccxt/pro/mexc.py +2 -1
  44. ccxt/test/test_async.py +10 -10
  45. ccxt/test/test_sync.py +10 -10
  46. ccxt/upbit.py +92 -17
  47. {ccxt-4.2.47.dist-info → ccxt-4.2.49.dist-info}/METADATA +4 -4
  48. {ccxt-4.2.47.dist-info → ccxt-4.2.49.dist-info}/RECORD +50 -50
  49. {ccxt-4.2.47.dist-info → ccxt-4.2.49.dist-info}/WHEEL +0 -0
  50. {ccxt-4.2.47.dist-info → ccxt-4.2.49.dist-info}/top_level.txt +0 -0
ccxt/pro/ascendex.py CHANGED
@@ -202,7 +202,7 @@ class ascendex(ccxt.async_support.ascendex):
202
202
  """
203
203
  await self.load_markets()
204
204
  market = self.market(symbol)
205
- channel = 'depth-realtime' + ':' + market['id']
205
+ channel = 'depth' + ':' + market['id']
206
206
  params = self.extend(params, {
207
207
  'ch': channel,
208
208
  })
@@ -212,7 +212,7 @@ class ascendex(ccxt.async_support.ascendex):
212
212
  async def watch_order_book_snapshot(self, symbol: str, limit: Int = None, params={}):
213
213
  await self.load_markets()
214
214
  market = self.market(symbol)
215
- action = 'depth-snapshot-realtime'
215
+ action = 'depth-snapshot'
216
216
  channel = action + ':' + market['id']
217
217
  params = self.extend(params, {
218
218
  'action': action,
@@ -224,6 +224,14 @@ class ascendex(ccxt.async_support.ascendex):
224
224
  orderbook = await self.watch_public(channel, params)
225
225
  return orderbook.limit()
226
226
 
227
+ async def fetch_order_book_snapshot(self, symbol: str, limit: Int = None, params={}):
228
+ restOrderBook = await self.fetch_rest_order_book_safe(symbol, limit, params)
229
+ if not (symbol in self.orderbooks):
230
+ self.orderbooks[symbol] = self.order_book()
231
+ orderbook = self.orderbooks[symbol]
232
+ orderbook.reset(restOrderBook)
233
+ return orderbook
234
+
227
235
  def handle_order_book_snapshot(self, client: Client, message):
228
236
  #
229
237
  # {
@@ -826,8 +834,8 @@ class ascendex(ccxt.async_support.ascendex):
826
834
  'ping': self.handle_ping,
827
835
  'auth': self.handle_authenticate,
828
836
  'sub': self.handle_subscription_status,
829
- 'depth-realtime': self.handle_order_book,
830
- 'depth-snapshot-realtime': self.handle_order_book_snapshot,
837
+ 'depth': self.handle_order_book,
838
+ 'depth-snapshot': self.handle_order_book_snapshot,
831
839
  'trades': self.handle_trades,
832
840
  'bar': self.handle_ohlcv,
833
841
  'balance': self.handle_balance,
@@ -851,7 +859,7 @@ class ascendex(ccxt.async_support.ascendex):
851
859
  # {m: 'sub', id: "1647515701", ch: "depth:BTC/USDT", code: 0}
852
860
  #
853
861
  channel = self.safe_string(message, 'ch', '')
854
- if channel.find('depth-realtime') > -1:
862
+ if channel.find('depth') > -1 and not (channel.find('depth-snapshot') > -1):
855
863
  self.handle_order_book_subscription(client, message)
856
864
  return message
857
865
 
@@ -859,11 +867,15 @@ class ascendex(ccxt.async_support.ascendex):
859
867
  channel = self.safe_string(message, 'ch')
860
868
  parts = channel.split(':')
861
869
  marketId = parts[1]
862
- symbol = self.safe_symbol(marketId)
870
+ market = self.safe_market(marketId)
871
+ symbol = market['symbol']
863
872
  if symbol in self.orderbooks:
864
873
  del self.orderbooks[symbol]
865
874
  self.orderbooks[symbol] = self.order_book({})
866
- self.spawn(self.watch_order_book_snapshot, symbol)
875
+ if self.options['defaultType'] == 'swap' or market['contract']:
876
+ self.spawn(self.fetch_order_book_snapshot, symbol)
877
+ else:
878
+ self.spawn(self.watch_order_book_snapshot, symbol)
867
879
 
868
880
  async def pong(self, client, message):
869
881
  #
ccxt/pro/bitget.py CHANGED
@@ -908,6 +908,7 @@ class bitget(ccxt.async_support.bitget):
908
908
  # "clientOid": "798d1425-d31d-4ada-a51b-ec701e00a1d9",
909
909
  # "price": "35000.00",
910
910
  # "size": "7.0000",
911
+ # "newSize": "500.0000",
911
912
  # "notional": "7.000000",
912
913
  # "orderType": "limit",
913
914
  # "force": "gtc",
@@ -1069,6 +1070,7 @@ class bitget(ccxt.async_support.bitget):
1069
1070
  # "clientOid": "798d1425-d31d-4ada-a51b-ec701e00a1d9",
1070
1071
  # "price": "35000.00",
1071
1072
  # "size": "7.0000",
1073
+ # "newSize": "500.0000",
1072
1074
  # "notional": "7.000000",
1073
1075
  # "orderType": "limit",
1074
1076
  # "force": "gtc",
@@ -1175,6 +1177,21 @@ class bitget(ccxt.async_support.bitget):
1175
1177
  'currency': self.safe_currency_code(feeCurrency),
1176
1178
  }
1177
1179
  triggerPrice = self.safe_number(order, 'triggerPrice')
1180
+ price = self.safe_string(order, 'price')
1181
+ avgPrice = self.omit_zero(self.safe_string_2(order, 'priceAvg', 'fillPrice'))
1182
+ cost = self.safe_string_n(order, ['notional', 'notionalUsd', 'quoteSize'])
1183
+ side = self.safe_string(order, 'side')
1184
+ type = self.safe_string(order, 'orderType')
1185
+ if side == 'buy' and market['spot'] and (type == 'market'):
1186
+ cost = self.safe_string(order, 'newSize', cost)
1187
+ filled = self.safe_string_2(order, 'accBaseVolume', 'baseVolume')
1188
+ if market['spot'] and (rawStatus != 'live'):
1189
+ filled = Precise.string_div(cost, avgPrice)
1190
+ amount = self.safe_string(order, 'baseVolume')
1191
+ if not market['spot'] or not (side == 'buy' and type == 'market'):
1192
+ amount = self.safe_string(order, 'newSize', amount)
1193
+ if market['swap'] and (amount is None):
1194
+ amount = self.safe_string(order, 'size')
1178
1195
  return self.safe_order({
1179
1196
  'info': order,
1180
1197
  'symbol': symbol,
@@ -1183,17 +1200,17 @@ class bitget(ccxt.async_support.bitget):
1183
1200
  'timestamp': timestamp,
1184
1201
  'datetime': self.iso8601(timestamp),
1185
1202
  'lastTradeTimestamp': self.safe_integer(order, 'uTime'),
1186
- 'type': self.safe_string(order, 'orderType'),
1203
+ 'type': type,
1187
1204
  'timeInForce': self.safe_string_upper(order, 'force'),
1188
1205
  'postOnly': None,
1189
- 'side': self.safe_string(order, 'side'),
1190
- 'price': self.safe_string(order, 'price'),
1206
+ 'side': side,
1207
+ 'price': price,
1191
1208
  'stopPrice': triggerPrice,
1192
1209
  'triggerPrice': triggerPrice,
1193
- 'amount': self.safe_string(order, 'baseVolume'),
1194
- 'cost': self.safe_string_n(order, ['notional', 'notionalUsd', 'quoteSize']),
1195
- 'average': self.omit_zero(self.safe_string_2(order, 'priceAvg', 'fillPrice')),
1196
- 'filled': self.safe_string_2(order, 'accBaseVolume', 'baseVolume'),
1210
+ 'amount': amount,
1211
+ 'cost': cost,
1212
+ 'average': avgPrice,
1213
+ 'filled': filled,
1197
1214
  'remaining': None,
1198
1215
  'status': self.parse_ws_order_status(rawStatus),
1199
1216
  'fee': feeObject,
ccxt/pro/bitstamp.py CHANGED
@@ -111,7 +111,7 @@ class bitstamp(ccxt.async_support.bitstamp):
111
111
  # usually it takes at least 4-5 deltas to resolve
112
112
  snapshotDelay = self.handle_option('watchOrderBook', 'snapshotDelay', 6)
113
113
  if cacheLength == snapshotDelay:
114
- self.spawn(self.load_order_book, client, messageHash, symbol)
114
+ self.spawn(self.load_order_book, client, messageHash, symbol, None, {})
115
115
  storedOrderBook.cache.append(delta)
116
116
  return
117
117
  elif nonce >= deltaNonce:
ccxt/pro/hitbtc.py CHANGED
@@ -215,7 +215,7 @@ class hitbtc(ccxt.async_support.hitbtc):
215
215
  'symbols': [market['id']],
216
216
  },
217
217
  }
218
- orderbook = await self.subscribe_public(name, name, [symbol], self.deep_extend(request, params))
218
+ orderbook = await self.subscribe_public(name, 'orderbooks', [symbol], self.deep_extend(request, params))
219
219
  return orderbook.limit()
220
220
 
221
221
  def handle_order_book(self, client: Client, message):
@@ -244,13 +244,12 @@ class hitbtc(ccxt.async_support.hitbtc):
244
244
  #
245
245
  data = self.safe_value_2(message, 'snapshot', 'update', {})
246
246
  marketIds = list(data.keys())
247
- channel = self.safe_string(message, 'ch')
248
247
  for i in range(0, len(marketIds)):
249
248
  marketId = marketIds[i]
250
249
  market = self.safe_market(marketId)
251
250
  symbol = market['symbol']
252
251
  item = data[marketId]
253
- messageHash = channel + '::' + symbol
252
+ messageHash = 'orderbooks::' + symbol
254
253
  if not (symbol in self.orderbooks):
255
254
  subscription = self.safe_value(client.subscriptions, messageHash, {})
256
255
  limit = self.safe_integer(subscription, 'limit')
@@ -303,7 +302,7 @@ class hitbtc(ccxt.async_support.hitbtc):
303
302
  'symbols': [market['id']],
304
303
  },
305
304
  }
306
- result = await self.subscribe_public(name, 'ticker', [symbol], self.deep_extend(request, params))
305
+ result = await self.subscribe_public(name, 'tickers', [symbol], self.deep_extend(request, params))
307
306
  return self.safe_value(result, symbol)
308
307
 
309
308
  async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
@@ -380,7 +379,6 @@ class hitbtc(ccxt.async_support.hitbtc):
380
379
  #
381
380
  data = self.safe_value(message, 'data', {})
382
381
  marketIds = list(data.keys())
383
- channel = self.safe_string(message, 'ch')
384
382
  newTickers = {}
385
383
  for i in range(0, len(marketIds)):
386
384
  marketId = marketIds[i]
@@ -389,8 +387,6 @@ class hitbtc(ccxt.async_support.hitbtc):
389
387
  ticker = self.parse_ws_ticker(data[marketId], market)
390
388
  self.tickers[symbol] = ticker
391
389
  newTickers[symbol] = ticker
392
- messageHash = channel + '::' + symbol
393
- client.resolve(newTickers, messageHash)
394
390
  client.resolve(newTickers, 'tickers')
395
391
  messageHashes = self.find_message_hashes(client, 'tickers::')
396
392
  for i in range(0, len(messageHashes)):
@@ -403,7 +399,6 @@ class hitbtc(ccxt.async_support.hitbtc):
403
399
  numTickers = len(tickersSymbols)
404
400
  if numTickers > 0:
405
401
  client.resolve(tickers, messageHash)
406
- client.resolve(self.tickers, channel)
407
402
  return message
408
403
 
409
404
  def parse_ws_ticker(self, ticker, market=None):
@@ -481,7 +476,7 @@ class hitbtc(ccxt.async_support.hitbtc):
481
476
  if limit is not None:
482
477
  request['limit'] = limit
483
478
  name = 'trades'
484
- trades = await self.subscribe_public(name, name, [symbol], self.deep_extend(request, params))
479
+ trades = await self.subscribe_public(name, 'trades', [symbol], self.deep_extend(request, params))
485
480
  if self.newUpdates:
486
481
  limit = trades.getLimit(symbol, limit)
487
482
  return self.filter_by_since_limit(trades, since, limit, 'timestamp')
@@ -602,7 +597,7 @@ class hitbtc(ccxt.async_support.hitbtc):
602
597
  }
603
598
  if limit is not None:
604
599
  request['params']['limit'] = limit
605
- ohlcv = await self.subscribe_public(name, name, [symbol], self.deep_extend(request, params))
600
+ ohlcv = await self.subscribe_public(name, 'candles', [symbol], self.deep_extend(request, params))
606
601
  if self.newUpdates:
607
602
  limit = ohlcv.getLimit(symbol, limit)
608
603
  return self.filter_by_since_limit(ohlcv, since, limit, 0)
@@ -660,7 +655,7 @@ class hitbtc(ccxt.async_support.hitbtc):
660
655
  ohlcvs = self.parse_ws_ohlcvs(data[marketId], market)
661
656
  for j in range(0, len(ohlcvs)):
662
657
  stored.append(ohlcvs[j])
663
- messageHash = channel + '::' + symbol
658
+ messageHash = 'candles::' + symbol
664
659
  client.resolve(stored, messageHash)
665
660
  return message
666
661
 
ccxt/pro/mexc.py CHANGED
@@ -439,6 +439,7 @@ class mexc(ccxt.async_support.mexc):
439
439
  symbol = self.safe_symbol(marketId)
440
440
  messageHash = 'orderbook:' + symbol
441
441
  subscription = self.safe_value(client.subscriptions, messageHash)
442
+ limit = self.safe_integer(subscription, 'limit')
442
443
  if subscription is True:
443
444
  # we set client.subscriptions[messageHash] to 1
444
445
  # once we have received the first delta and initialized the orderbook
@@ -450,7 +451,7 @@ class mexc(ccxt.async_support.mexc):
450
451
  cacheLength = len(storedOrderBook.cache)
451
452
  snapshotDelay = self.handle_option('watchOrderBook', 'snapshotDelay', 25)
452
453
  if cacheLength == snapshotDelay:
453
- self.spawn(self.load_order_book, client, messageHash, symbol)
454
+ self.spawn(self.load_order_book, client, messageHash, symbol, limit, {})
454
455
  storedOrderBook.cache.append(data)
455
456
  return
456
457
  try:
ccxt/test/test_async.py CHANGED
@@ -158,8 +158,9 @@ def json_stringify(elem):
158
158
  return json.dumps(elem)
159
159
 
160
160
 
161
- def convert_to_snake_case(conent):
162
- return re.sub(r'(?<!^)(?=[A-Z])', '_', conent).lower()
161
+ def convert_to_snake_case(content):
162
+ res = re.sub(r'(?<!^)(?=[A-Z])', '_', content).lower()
163
+ return res.replace('o_h_l_c_v', 'ohlcv')
163
164
 
164
165
 
165
166
  def get_test_name(methodName):
@@ -185,8 +186,7 @@ def io_dir_read(path):
185
186
 
186
187
 
187
188
  async def call_method(test_files, methodName, exchange, skippedProperties, args):
188
- methodNameToCall = convert_to_snake_case(methodName)
189
- methodNameToCall = 'test_' + methodNameToCall.replace('o_h_l_c_v', 'ohlcv')
189
+ methodNameToCall = 'test_' + convert_to_snake_case(methodName)
190
190
  return await getattr(test_files[methodName], methodNameToCall)(exchange, skippedProperties, *args)
191
191
 
192
192
 
@@ -583,7 +583,7 @@ class testMainClass(baseMainTestClass):
583
583
  result = await self.test_safe('loadMarkets', exchange, [], True)
584
584
  if not result:
585
585
  return False
586
- symbols = ['BTC/CNY', 'BTC/USD', 'BTC/USDT', 'BTC/EUR', 'BTC/ETH', 'ETH/BTC', 'BTC/JPY', 'ETH/EUR', 'ETH/JPY', 'ETH/CNY', 'ETH/USD', 'LTC/CNY', 'DASH/BTC', 'DOGE/BTC', 'BTC/AUD', 'BTC/PLN', 'USD/SLL', 'BTC/RUB', 'BTC/UAH', 'LTC/BTC', 'EUR/USD']
586
+ symbols = ['BTC/USDT', 'BTC/USDC', 'BTC/CNY', 'BTC/USD', 'BTC/EUR', 'BTC/ETH', 'ETH/BTC', 'BTC/JPY', 'ETH/EUR', 'ETH/JPY', 'ETH/CNY', 'ETH/USD', 'LTC/CNY', 'DASH/BTC', 'DOGE/BTC', 'BTC/AUD', 'BTC/PLN', 'USD/SLL', 'BTC/RUB', 'BTC/UAH', 'LTC/BTC', 'EUR/USD']
587
587
  result_symbols = []
588
588
  exchange_specific_symbols = exchange.symbols
589
589
  for i in range(0, len(exchange_specific_symbols)):
@@ -638,8 +638,8 @@ class testMainClass(baseMainTestClass):
638
638
  def get_valid_symbol(self, exchange, spot=True):
639
639
  current_type_markets = self.get_markets_from_exchange(exchange, spot)
640
640
  codes = ['BTC', 'ETH', 'XRP', 'LTC', 'BCH', 'EOS', 'BNB', 'BSV', 'USDT', 'ATOM', 'BAT', 'BTG', 'DASH', 'DOGE', 'ETC', 'IOTA', 'LSK', 'MKR', 'NEO', 'PAX', 'QTUM', 'TRX', 'TUSD', 'USD', 'USDC', 'WAVES', 'XEM', 'XMR', 'ZEC', 'ZRX']
641
- spot_symbols = ['BTC/USD', 'BTC/USDT', 'BTC/CNY', 'BTC/EUR', 'BTC/ETH', 'ETH/BTC', 'ETH/USD', 'ETH/USDT', 'BTC/JPY', 'LTC/BTC', 'ZRX/WETH', 'EUR/USD']
642
- swap_symbols = ['BTC/USDT:USDT', 'BTC/USD:USD', 'ETH/USDT:USDT', 'ETH/USD:USD', 'LTC/USDT:USDT', 'DOGE/USDT:USDT', 'ADA/USDT:USDT', 'BTC/USD:BTC', 'ETH/USD:ETH']
641
+ spot_symbols = ['BTC/USDT', 'BTC/USDC', 'BTC/USD', 'BTC/CNY', 'BTC/EUR', 'BTC/ETH', 'ETH/BTC', 'ETH/USD', 'ETH/USDT', 'BTC/JPY', 'LTC/BTC', 'ZRX/WETH', 'EUR/USD']
642
+ swap_symbols = ['BTC/USDT:USDT', 'BTC/USDC:USDC', 'BTC/USD:USD', 'ETH/USDT:USDT', 'ETH/USD:USD', 'LTC/USDT:USDT', 'DOGE/USDT:USDT', 'ADA/USDT:USDT', 'BTC/USD:BTC', 'ETH/USD:ETH']
643
643
  target_symbols = spot_symbols if spot else swap_symbols
644
644
  symbol = self.get_test_symbol(exchange, spot, target_symbols)
645
645
  # if symbols wasn't found from above hardcoded list, then try to locate any symbol which has our target hardcoded 'base' code
@@ -688,16 +688,16 @@ class testMainClass(baseMainTestClass):
688
688
  if swap_symbol is not None:
689
689
  dump('[INFO:MAIN] Selected SWAP SYMBOL:', swap_symbol)
690
690
  if not self.private_test_only:
691
- # note, spot & swap tests should run sequentially, because of conflicting `exchange.options['type']` setting
691
+ # note, spot & swap tests should run sequentially, because of conflicting `exchange.options['defaultType']` setting
692
692
  if exchange.has['spot'] and spot_symbol is not None:
693
693
  if self.info:
694
694
  dump('[INFO] ### SPOT TESTS ###')
695
- exchange.options['type'] = 'spot'
695
+ exchange.options['defaultType'] = 'spot'
696
696
  await self.run_public_tests(exchange, spot_symbol)
697
697
  if exchange.has['swap'] and swap_symbol is not None:
698
698
  if self.info:
699
699
  dump('[INFO] ### SWAP TESTS ###')
700
- exchange.options['type'] = 'swap'
700
+ exchange.options['defaultType'] = 'swap'
701
701
  await self.run_public_tests(exchange, swap_symbol)
702
702
  if self.private_test or self.private_test_only:
703
703
  if exchange.has['spot'] and spot_symbol is not None:
ccxt/test/test_sync.py CHANGED
@@ -157,8 +157,9 @@ def json_stringify(elem):
157
157
  return json.dumps(elem)
158
158
 
159
159
 
160
- def convert_to_snake_case(conent):
161
- return re.sub(r'(?<!^)(?=[A-Z])', '_', conent).lower()
160
+ def convert_to_snake_case(content):
161
+ res = re.sub(r'(?<!^)(?=[A-Z])', '_', content).lower()
162
+ return res.replace('o_h_l_c_v', 'ohlcv')
162
163
 
163
164
 
164
165
  def get_test_name(methodName):
@@ -184,8 +185,7 @@ def io_dir_read(path):
184
185
 
185
186
 
186
187
  def call_method(test_files, methodName, exchange, skippedProperties, args):
187
- methodNameToCall = convert_to_snake_case(methodName)
188
- methodNameToCall = 'test_' + methodNameToCall.replace('o_h_l_c_v', 'ohlcv')
188
+ methodNameToCall = 'test_' + convert_to_snake_case(methodName)
189
189
  return getattr(test_files[methodName], methodNameToCall)(exchange, skippedProperties, *args)
190
190
 
191
191
 
@@ -582,7 +582,7 @@ class testMainClass(baseMainTestClass):
582
582
  result = self.test_safe('loadMarkets', exchange, [], True)
583
583
  if not result:
584
584
  return False
585
- symbols = ['BTC/CNY', 'BTC/USD', 'BTC/USDT', 'BTC/EUR', 'BTC/ETH', 'ETH/BTC', 'BTC/JPY', 'ETH/EUR', 'ETH/JPY', 'ETH/CNY', 'ETH/USD', 'LTC/CNY', 'DASH/BTC', 'DOGE/BTC', 'BTC/AUD', 'BTC/PLN', 'USD/SLL', 'BTC/RUB', 'BTC/UAH', 'LTC/BTC', 'EUR/USD']
585
+ symbols = ['BTC/USDT', 'BTC/USDC', 'BTC/CNY', 'BTC/USD', 'BTC/EUR', 'BTC/ETH', 'ETH/BTC', 'BTC/JPY', 'ETH/EUR', 'ETH/JPY', 'ETH/CNY', 'ETH/USD', 'LTC/CNY', 'DASH/BTC', 'DOGE/BTC', 'BTC/AUD', 'BTC/PLN', 'USD/SLL', 'BTC/RUB', 'BTC/UAH', 'LTC/BTC', 'EUR/USD']
586
586
  result_symbols = []
587
587
  exchange_specific_symbols = exchange.symbols
588
588
  for i in range(0, len(exchange_specific_symbols)):
@@ -637,8 +637,8 @@ class testMainClass(baseMainTestClass):
637
637
  def get_valid_symbol(self, exchange, spot=True):
638
638
  current_type_markets = self.get_markets_from_exchange(exchange, spot)
639
639
  codes = ['BTC', 'ETH', 'XRP', 'LTC', 'BCH', 'EOS', 'BNB', 'BSV', 'USDT', 'ATOM', 'BAT', 'BTG', 'DASH', 'DOGE', 'ETC', 'IOTA', 'LSK', 'MKR', 'NEO', 'PAX', 'QTUM', 'TRX', 'TUSD', 'USD', 'USDC', 'WAVES', 'XEM', 'XMR', 'ZEC', 'ZRX']
640
- spot_symbols = ['BTC/USD', 'BTC/USDT', 'BTC/CNY', 'BTC/EUR', 'BTC/ETH', 'ETH/BTC', 'ETH/USD', 'ETH/USDT', 'BTC/JPY', 'LTC/BTC', 'ZRX/WETH', 'EUR/USD']
641
- swap_symbols = ['BTC/USDT:USDT', 'BTC/USD:USD', 'ETH/USDT:USDT', 'ETH/USD:USD', 'LTC/USDT:USDT', 'DOGE/USDT:USDT', 'ADA/USDT:USDT', 'BTC/USD:BTC', 'ETH/USD:ETH']
640
+ spot_symbols = ['BTC/USDT', 'BTC/USDC', 'BTC/USD', 'BTC/CNY', 'BTC/EUR', 'BTC/ETH', 'ETH/BTC', 'ETH/USD', 'ETH/USDT', 'BTC/JPY', 'LTC/BTC', 'ZRX/WETH', 'EUR/USD']
641
+ swap_symbols = ['BTC/USDT:USDT', 'BTC/USDC:USDC', 'BTC/USD:USD', 'ETH/USDT:USDT', 'ETH/USD:USD', 'LTC/USDT:USDT', 'DOGE/USDT:USDT', 'ADA/USDT:USDT', 'BTC/USD:BTC', 'ETH/USD:ETH']
642
642
  target_symbols = spot_symbols if spot else swap_symbols
643
643
  symbol = self.get_test_symbol(exchange, spot, target_symbols)
644
644
  # if symbols wasn't found from above hardcoded list, then try to locate any symbol which has our target hardcoded 'base' code
@@ -687,16 +687,16 @@ class testMainClass(baseMainTestClass):
687
687
  if swap_symbol is not None:
688
688
  dump('[INFO:MAIN] Selected SWAP SYMBOL:', swap_symbol)
689
689
  if not self.private_test_only:
690
- # note, spot & swap tests should run sequentially, because of conflicting `exchange.options['type']` setting
690
+ # note, spot & swap tests should run sequentially, because of conflicting `exchange.options['defaultType']` setting
691
691
  if exchange.has['spot'] and spot_symbol is not None:
692
692
  if self.info:
693
693
  dump('[INFO] ### SPOT TESTS ###')
694
- exchange.options['type'] = 'spot'
694
+ exchange.options['defaultType'] = 'spot'
695
695
  self.run_public_tests(exchange, spot_symbol)
696
696
  if exchange.has['swap'] and swap_symbol is not None:
697
697
  if self.info:
698
698
  dump('[INFO] ### SWAP TESTS ###')
699
- exchange.options['type'] = 'swap'
699
+ exchange.options['defaultType'] = 'swap'
700
700
  self.run_public_tests(exchange, swap_symbol)
701
701
  if self.private_test or self.private_test_only:
702
702
  if exchange.has['spot'] and spot_symbol is not None:
ccxt/upbit.py CHANGED
@@ -48,6 +48,7 @@ class upbit(Exchange, ImplicitAPI):
48
48
  'fetchBalance': True,
49
49
  'fetchCanceledOrders': True,
50
50
  'fetchClosedOrders': True,
51
+ 'fetchDeposit': True,
51
52
  'fetchDepositAddress': True,
52
53
  'fetchDepositAddresses': True,
53
54
  'fetchDeposits': True,
@@ -75,6 +76,7 @@ class upbit(Exchange, ImplicitAPI):
75
76
  'fetchTradingFee': True,
76
77
  'fetchTradingFees': False,
77
78
  'fetchTransactions': False,
79
+ 'fetchWithdrawal': True,
78
80
  'fetchWithdrawals': True,
79
81
  'transfer': False,
80
82
  'withdraw': True,
@@ -988,6 +990,7 @@ class upbit(Exchange, ImplicitAPI):
988
990
  """
989
991
  create a trade order
990
992
  :see: https://docs.upbit.com/reference/%EC%A3%BC%EB%AC%B8%ED%95%98%EA%B8%B0
993
+ :see: https://global-docs.upbit.com/reference/order
991
994
  :param str symbol: unified symbol of the market to create an order in
992
995
  :param str type: 'market' or 'limit'
993
996
  :param str side: 'buy' or 'sell'
@@ -995,6 +998,7 @@ class upbit(Exchange, ImplicitAPI):
995
998
  :param float [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
996
999
  :param dict [params]: extra parameters specific to the exchange API endpoint
997
1000
  :param float [params.cost]: for market buy orders, the quote quantity that can be used alternative for the amount
1001
+ :param str [params.timeInForce]: 'IOC' or 'FOK'
998
1002
  :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
999
1003
  """
1000
1004
  self.load_markets()
@@ -1039,6 +1043,11 @@ class upbit(Exchange, ImplicitAPI):
1039
1043
  clientOrderId = self.safe_string_2(params, 'clientOrderId', 'identifier')
1040
1044
  if clientOrderId is not None:
1041
1045
  request['identifier'] = clientOrderId
1046
+ if type != 'market':
1047
+ timeInForce = self.safe_string_lower_2(params, 'timeInForce', 'time_in_force')
1048
+ params = self.omit(params, 'timeInForce')
1049
+ if timeInForce is not None:
1050
+ request['time_in_force'] = timeInForce
1042
1051
  params = self.omit(params, ['clientOrderId', 'identifier'])
1043
1052
  response = self.privatePostOrders(self.extend(request, params))
1044
1053
  #
@@ -1138,6 +1147,42 @@ class upbit(Exchange, ImplicitAPI):
1138
1147
  #
1139
1148
  return self.parse_transactions(response, currency, since, limit)
1140
1149
 
1150
+ def fetch_deposit(self, id: str, code: Str = None, params={}):
1151
+ """
1152
+ fetch information on a deposit
1153
+ :see: https://global-docs.upbit.com/reference/individual-deposit-inquiry
1154
+ :param str id: the unique id for the deposit
1155
+ :param str [code]: unified currency code of the currency deposited
1156
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1157
+ :param str [params.txid]: withdrawal transaction id, the id argument is reserved for uuid
1158
+ :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
1159
+ """
1160
+ self.load_markets()
1161
+ request = {
1162
+ 'uuid': id,
1163
+ }
1164
+ currency = None
1165
+ if code is not None:
1166
+ currency = self.currency(code)
1167
+ request['currency'] = currency['id']
1168
+ response = self.privateGetDeposit(self.extend(request, params))
1169
+ #
1170
+ # {
1171
+ # "type": "deposit",
1172
+ # "uuid": "7f54527e-2eee-4268-860e-fd8b9d7fe3c7",
1173
+ # "currency": "ADA",
1174
+ # "net_type": "ADA",
1175
+ # "txid": "99795bbfeca91eaa071068bb659b33eeb65d8aaff2551fdf7c78f345d188952b",
1176
+ # "state": "ACCEPTED",
1177
+ # "created_at": "2023-12-12T04:58:41Z",
1178
+ # "done_at": "2023-12-12T05:31:50Z",
1179
+ # "amount": "35.72344",
1180
+ # "fee": "0.0",
1181
+ # "transaction_type": "default"
1182
+ # }
1183
+ #
1184
+ return self.parse_transaction(response, currency)
1185
+
1141
1186
  def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
1142
1187
  """
1143
1188
  :see: https://docs.upbit.com/reference/%EC%A0%84%EC%B2%B4-%EC%B6%9C%EA%B8%88-%EC%A1%B0%ED%9A%8C
@@ -1178,13 +1223,49 @@ class upbit(Exchange, ImplicitAPI):
1178
1223
  #
1179
1224
  return self.parse_transactions(response, currency, since, limit)
1180
1225
 
1226
+ def fetch_withdrawal(self, id: str, code: Str = None, params={}):
1227
+ """
1228
+ fetch data on a currency withdrawal via the withdrawal id
1229
+ :see: https://global-docs.upbit.com/reference/individual-withdrawal-inquiry
1230
+ :param str id: the unique id for the withdrawal
1231
+ :param str [code]: unified currency code of the currency withdrawn
1232
+ :param dict [params]: extra parameters specific to the exchange API endpoint
1233
+ :param str [params.txid]: withdrawal transaction id, the id argument is reserved for uuid
1234
+ :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
1235
+ """
1236
+ self.load_markets()
1237
+ request = {
1238
+ 'uuid': id,
1239
+ }
1240
+ currency = None
1241
+ if code is not None:
1242
+ currency = self.currency(code)
1243
+ request['currency'] = currency['id']
1244
+ response = self.privateGetWithdraw(self.extend(request, params))
1245
+ #
1246
+ # {
1247
+ # "type": "withdraw",
1248
+ # "uuid": "95ef274b-23a6-4de4-95b0-5cbef4ca658f",
1249
+ # "currency": "ADA",
1250
+ # "net_type": "ADA",
1251
+ # "txid": "b1528f149297a71671b86636f731f8fdb0ff53da0f1d8c19093d59df96f34583",
1252
+ # "state": "DONE",
1253
+ # "created_at": "2023-12-14T02:46:52Z",
1254
+ # "done_at": "2023-12-14T03:10:11Z",
1255
+ # "amount": "35.22344",
1256
+ # "fee": "0.5",
1257
+ # "transaction_type": "default"
1258
+ # }
1259
+ #
1260
+ return self.parse_transaction(response, currency)
1261
+
1181
1262
  def parse_transaction_status(self, status):
1182
1263
  statuses = {
1183
1264
  'submitting': 'pending', # 처리 중
1184
1265
  'submitted': 'pending', # 처리 완료
1185
1266
  'almost_accepted': 'pending', # 출금대기중
1186
1267
  'rejected': 'failed', # 거부
1187
- 'accepted': 'pending', # 승인됨
1268
+ 'accepted': 'ok', # 승인됨
1188
1269
  'processing': 'pending', # 처리 중
1189
1270
  'done': 'ok', # 완료
1190
1271
  'canceled': 'canceled', # 취소됨
@@ -1193,7 +1274,7 @@ class upbit(Exchange, ImplicitAPI):
1193
1274
 
1194
1275
  def parse_transaction(self, transaction, currency: Currency = None) -> Transaction:
1195
1276
  #
1196
- # fetchDeposits
1277
+ # fetchDeposits, fetchDeposit
1197
1278
  #
1198
1279
  # {
1199
1280
  # "type": "deposit",
@@ -1207,7 +1288,7 @@ class upbit(Exchange, ImplicitAPI):
1207
1288
  # "fee": "0.0"
1208
1289
  # }
1209
1290
  #
1210
- # fetchWithdrawals
1291
+ # fetchWithdrawals, fetchWithdrawal
1211
1292
  #
1212
1293
  # {
1213
1294
  # "type": "withdraw",
@@ -1222,26 +1303,20 @@ class upbit(Exchange, ImplicitAPI):
1222
1303
  # "krw_amount": "80420.0"
1223
1304
  # }
1224
1305
  #
1225
- id = self.safe_string(transaction, 'uuid')
1226
- amount = self.safe_number(transaction, 'amount')
1227
1306
  address = None # not present in the data structure received from the exchange
1228
1307
  tag = None # not present in the data structure received from the exchange
1229
- txid = self.safe_string(transaction, 'txid')
1230
1308
  updatedRaw = self.safe_string(transaction, 'done_at')
1231
- updated = self.parse8601(updatedRaw)
1232
1309
  timestamp = self.parse8601(self.safe_string(transaction, 'created_at', updatedRaw))
1233
1310
  type = self.safe_string(transaction, 'type')
1234
1311
  if type == 'withdraw':
1235
1312
  type = 'withdrawal'
1236
1313
  currencyId = self.safe_string(transaction, 'currency')
1237
- code = self.safe_currency_code(currencyId)
1238
- status = self.parse_transaction_status(self.safe_string_lower(transaction, 'state'))
1239
- feeCost = self.safe_number(transaction, 'fee')
1314
+ code = self.safe_currency_code(currencyId, currency)
1240
1315
  return {
1241
1316
  'info': transaction,
1242
- 'id': id,
1317
+ 'id': self.safe_string(transaction, 'uuid'),
1243
1318
  'currency': code,
1244
- 'amount': amount,
1319
+ 'amount': self.safe_number(transaction, 'amount'),
1245
1320
  'network': None,
1246
1321
  'address': address,
1247
1322
  'addressTo': None,
@@ -1249,17 +1324,17 @@ class upbit(Exchange, ImplicitAPI):
1249
1324
  'tag': tag,
1250
1325
  'tagTo': None,
1251
1326
  'tagFrom': None,
1252
- 'status': status,
1327
+ 'status': self.parse_transaction_status(self.safe_string_lower(transaction, 'state')),
1253
1328
  'type': type,
1254
- 'updated': updated,
1255
- 'txid': txid,
1329
+ 'updated': self.parse8601(updatedRaw),
1330
+ 'txid': self.safe_string(transaction, 'txid'),
1256
1331
  'timestamp': timestamp,
1257
1332
  'datetime': self.iso8601(timestamp),
1258
1333
  'internal': None,
1259
1334
  'comment': None,
1260
1335
  'fee': {
1261
1336
  'currency': code,
1262
- 'cost': feeCost,
1337
+ 'cost': self.safe_number(transaction, 'fee'),
1263
1338
  },
1264
1339
  }
1265
1340
 
@@ -1705,7 +1780,7 @@ class upbit(Exchange, ImplicitAPI):
1705
1780
  url += '?' + self.urlencode(query)
1706
1781
  if api == 'private':
1707
1782
  self.check_required_credentials()
1708
- nonce = self.nonce()
1783
+ nonce = self.uuid()
1709
1784
  request = {
1710
1785
  'access_key': self.apiKey,
1711
1786
  'nonce': nonce,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ccxt
3
- Version: 4.2.47
3
+ Version: 4.2.49
4
4
  Summary: A JavaScript / TypeScript / Python / C# / PHP cryptocurrency trading library with support for 100+ exchanges
5
5
  Home-page: https://ccxt.com
6
6
  Author: Igor Kroitor
@@ -259,13 +259,13 @@ console.log(version, Object.keys(exchanges));
259
259
 
260
260
  All-in-one browser bundle (dependencies included), served from a CDN of your choice:
261
261
 
262
- * jsDelivr: https://cdn.jsdelivr.net/npm/ccxt@4.2.47/dist/ccxt.browser.js
263
- * unpkg: https://unpkg.com/ccxt@4.2.47/dist/ccxt.browser.js
262
+ * jsDelivr: https://cdn.jsdelivr.net/npm/ccxt@4.2.49/dist/ccxt.browser.js
263
+ * unpkg: https://unpkg.com/ccxt@4.2.49/dist/ccxt.browser.js
264
264
 
265
265
  CDNs are not updated in real-time and may have delays. Defaulting to the most recent version without specifying the version number is not recommended. Please, keep in mind that we are not responsible for the correct operation of those CDN servers.
266
266
 
267
267
  ```HTML
268
- <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/ccxt@4.2.47/dist/ccxt.browser.js"></script>
268
+ <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/ccxt@4.2.49/dist/ccxt.browser.js"></script>
269
269
  ```
270
270
 
271
271
  Creates a global `ccxt` object: