ccxt 4.4.77__py2.py3-none-any.whl → 4.4.80__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ccxt/__init__.py +3 -3
- ccxt/abstract/apex.py +31 -0
- ccxt/abstract/bitmart.py +1 -0
- ccxt/apex.py +1884 -0
- ccxt/ascendex.py +23 -6
- ccxt/async_support/__init__.py +3 -3
- ccxt/async_support/apex.py +1884 -0
- ccxt/async_support/ascendex.py +23 -6
- ccxt/async_support/base/exchange.py +5 -1
- ccxt/async_support/binance.py +9 -3
- ccxt/async_support/bingx.py +4 -4
- ccxt/async_support/bitfinex.py +61 -36
- ccxt/async_support/bitflyer.py +2 -2
- ccxt/async_support/bitget.py +186 -128
- ccxt/async_support/bitmart.py +9 -4
- ccxt/async_support/bitmex.py +14 -7
- ccxt/async_support/bitopro.py +5 -1
- ccxt/async_support/bitrue.py +2 -1
- ccxt/async_support/bitso.py +1 -1
- ccxt/async_support/bitteam.py +2 -0
- ccxt/async_support/bitvavo.py +25 -10
- ccxt/async_support/btcalpha.py +1 -1
- ccxt/async_support/btcmarkets.py +1 -1
- ccxt/async_support/btcturk.py +1 -1
- ccxt/async_support/bybit.py +27 -15
- ccxt/async_support/cex.py +1 -1
- ccxt/async_support/coinbase.py +17 -4
- ccxt/async_support/coincatch.py +66 -0
- ccxt/async_support/coinex.py +2 -1
- ccxt/async_support/coinlist.py +1 -0
- ccxt/async_support/coinone.py +1 -0
- ccxt/async_support/cryptocom.py +2 -2
- ccxt/async_support/defx.py +1 -1
- ccxt/async_support/delta.py +4 -1
- ccxt/async_support/deribit.py +3 -2
- ccxt/async_support/derive.py +2 -2
- ccxt/async_support/digifinex.py +2 -2
- ccxt/async_support/gate.py +1 -1
- ccxt/async_support/hitbtc.py +5 -2
- ccxt/async_support/hollaex.py +1 -0
- ccxt/async_support/htx.py +9 -5
- ccxt/async_support/huobijp.py +1 -0
- ccxt/async_support/hyperliquid.py +14 -6
- ccxt/async_support/kraken.py +4 -2
- ccxt/async_support/krakenfutures.py +2 -2
- ccxt/async_support/kucoinfutures.py +2 -2
- ccxt/async_support/mexc.py +50 -52
- ccxt/async_support/okx.py +2 -2
- ccxt/async_support/oxfun.py +2 -2
- ccxt/async_support/paradex.py +2 -2
- ccxt/async_support/phemex.py +4 -3
- ccxt/async_support/poloniex.py +4 -3
- ccxt/async_support/probit.py +1 -0
- ccxt/async_support/timex.py +2 -2
- ccxt/async_support/tradeogre.py +2 -1
- ccxt/async_support/upbit.py +243 -63
- ccxt/async_support/vertex.py +2 -2
- ccxt/async_support/whitebit.py +66 -12
- ccxt/async_support/woo.py +5 -3
- ccxt/async_support/woofipro.py +2 -2
- ccxt/async_support/xt.py +9 -2
- ccxt/base/exchange.py +69 -2
- ccxt/binance.py +9 -3
- ccxt/bingx.py +4 -4
- ccxt/bitfinex.py +61 -36
- ccxt/bitflyer.py +2 -2
- ccxt/bitget.py +186 -128
- ccxt/bitmart.py +9 -4
- ccxt/bitmex.py +14 -7
- ccxt/bitopro.py +5 -1
- ccxt/bitrue.py +2 -1
- ccxt/bitso.py +1 -1
- ccxt/bitteam.py +2 -0
- ccxt/bitvavo.py +25 -10
- ccxt/btcalpha.py +1 -1
- ccxt/btcmarkets.py +1 -1
- ccxt/btcturk.py +1 -1
- ccxt/bybit.py +27 -15
- ccxt/cex.py +1 -1
- ccxt/coinbase.py +17 -4
- ccxt/coincatch.py +66 -0
- ccxt/coinex.py +2 -1
- ccxt/coinlist.py +1 -0
- ccxt/coinone.py +1 -0
- ccxt/cryptocom.py +2 -2
- ccxt/defx.py +1 -1
- ccxt/delta.py +4 -1
- ccxt/deribit.py +3 -2
- ccxt/derive.py +2 -2
- ccxt/digifinex.py +2 -2
- ccxt/gate.py +1 -1
- ccxt/hitbtc.py +5 -2
- ccxt/hollaex.py +1 -0
- ccxt/htx.py +9 -5
- ccxt/huobijp.py +1 -0
- ccxt/hyperliquid.py +14 -6
- ccxt/kraken.py +4 -2
- ccxt/krakenfutures.py +2 -2
- ccxt/kucoinfutures.py +2 -2
- ccxt/mexc.py +50 -52
- ccxt/okx.py +2 -2
- ccxt/oxfun.py +2 -2
- ccxt/paradex.py +2 -2
- ccxt/phemex.py +4 -3
- ccxt/poloniex.py +4 -3
- ccxt/pro/__init__.py +5 -1
- ccxt/pro/apex.py +984 -0
- ccxt/pro/binance.py +3 -3
- ccxt/pro/coinbase.py +43 -57
- ccxt/pro/gate.py +22 -2
- ccxt/pro/hollaex.py +2 -2
- ccxt/pro/p2b.py +2 -2
- ccxt/pro/tradeogre.py +272 -0
- ccxt/pro/upbit.py +42 -0
- ccxt/probit.py +1 -0
- ccxt/test/tests_async.py +4 -1
- ccxt/test/tests_sync.py +4 -1
- ccxt/timex.py +2 -2
- ccxt/tradeogre.py +2 -1
- ccxt/upbit.py +243 -63
- ccxt/vertex.py +2 -2
- ccxt/whitebit.py +66 -12
- ccxt/woo.py +5 -3
- ccxt/woofipro.py +2 -2
- ccxt/xt.py +9 -2
- {ccxt-4.4.77.dist-info → ccxt-4.4.80.dist-info}/METADATA +9 -11
- {ccxt-4.4.77.dist-info → ccxt-4.4.80.dist-info}/RECORD +130 -128
- ccxt/abstract/ace.py +0 -15
- ccxt/ace.py +0 -1152
- ccxt/async_support/ace.py +0 -1152
- {ccxt-4.4.77.dist-info → ccxt-4.4.80.dist-info}/LICENSE.txt +0 -0
- {ccxt-4.4.77.dist-info → ccxt-4.4.80.dist-info}/WHEEL +0 -0
- {ccxt-4.4.77.dist-info → ccxt-4.4.80.dist-info}/top_level.txt +0 -0
ccxt/pro/binance.py
CHANGED
@@ -71,12 +71,12 @@ class binance(ccxt.async_support.binance):
|
|
71
71
|
'urls': {
|
72
72
|
'test': {
|
73
73
|
'ws': {
|
74
|
-
'spot': 'wss://testnet.binance.vision/ws',
|
75
|
-
'margin': 'wss://testnet.binance.vision/ws',
|
74
|
+
'spot': 'wss://stream.testnet.binance.vision/ws',
|
75
|
+
'margin': 'wss://stream.testnet.binance.vision/ws',
|
76
76
|
'future': 'wss://fstream.binancefuture.com/ws',
|
77
77
|
'delivery': 'wss://dstream.binancefuture.com/ws',
|
78
78
|
'ws-api': {
|
79
|
-
'spot': 'wss://testnet.binance.vision/ws-api/v3',
|
79
|
+
'spot': 'wss://ws-api.testnet.binance.vision/ws-api/v3',
|
80
80
|
'future': 'wss://testnet.binancefuture.com/ws-fapi/v1',
|
81
81
|
'delivery': 'wss://testnet.binancefuture.com/ws-dapi/v1',
|
82
82
|
},
|
ccxt/pro/coinbase.py
CHANGED
@@ -78,7 +78,7 @@ class coinbase(ccxt.async_support.coinbase):
|
|
78
78
|
messageHash = messageHash + '::' + ','.join(symbol)
|
79
79
|
elif symbol is not None:
|
80
80
|
market = self.market(symbol)
|
81
|
-
messageHash = name + '::' +
|
81
|
+
messageHash = name + '::' + symbol
|
82
82
|
productIds = [market['id']]
|
83
83
|
url = self.urls['api']['ws']
|
84
84
|
subscribe = {
|
@@ -115,7 +115,7 @@ class coinbase(ccxt.async_support.coinbase):
|
|
115
115
|
market = self.market(symbol)
|
116
116
|
marketId = market['id']
|
117
117
|
productIds.append(marketId)
|
118
|
-
messageHashes.append(name + '::' +
|
118
|
+
messageHashes.append(name + '::' + symbol)
|
119
119
|
url = self.urls['api']['ws']
|
120
120
|
subscribe = {
|
121
121
|
'type': 'subscribe',
|
@@ -176,8 +176,11 @@ class coinbase(ccxt.async_support.coinbase):
|
|
176
176
|
if symbols is None:
|
177
177
|
symbols = self.symbols
|
178
178
|
name = 'ticker_batch'
|
179
|
-
|
179
|
+
ticker = await self.subscribe_multiple(name, False, symbols, params)
|
180
180
|
if self.newUpdates:
|
181
|
+
tickers = {}
|
182
|
+
symbol = ticker['symbol']
|
183
|
+
tickers[symbol] = ticker
|
181
184
|
return tickers
|
182
185
|
return self.tickers
|
183
186
|
|
@@ -272,7 +275,7 @@ class coinbase(ccxt.async_support.coinbase):
|
|
272
275
|
#
|
273
276
|
#
|
274
277
|
channel = self.safe_string(message, 'channel')
|
275
|
-
events = self.
|
278
|
+
events = self.safe_list(message, 'events', [])
|
276
279
|
datetime = self.safe_string(message, 'timestamp')
|
277
280
|
timestamp = self.parse8601(datetime)
|
278
281
|
newTickers = []
|
@@ -281,31 +284,18 @@ class coinbase(ccxt.async_support.coinbase):
|
|
281
284
|
tickers = self.safe_list(tickersObj, 'tickers', [])
|
282
285
|
for j in range(0, len(tickers)):
|
283
286
|
ticker = tickers[j]
|
287
|
+
wsMarketId = self.safe_string(ticker, 'product_id')
|
288
|
+
if wsMarketId is None:
|
289
|
+
continue
|
284
290
|
result = self.parse_ws_ticker(ticker)
|
285
291
|
result['timestamp'] = timestamp
|
286
292
|
result['datetime'] = datetime
|
287
293
|
symbol = result['symbol']
|
288
294
|
self.tickers[symbol] = result
|
289
|
-
wsMarketId = self.safe_string(ticker, 'product_id')
|
290
|
-
if wsMarketId is None:
|
291
|
-
continue
|
292
|
-
messageHash = channel + '::' + wsMarketId
|
293
295
|
newTickers.append(result)
|
296
|
+
messageHash = channel + '::' + symbol
|
294
297
|
client.resolve(result, messageHash)
|
295
|
-
|
296
|
-
client.resolve(result, messageHash + 'C') # sometimes we subscribe to BTC/USDC and coinbase returns BTC/USD
|
297
|
-
messageHashes = self.find_message_hashes(client, 'ticker_batch::')
|
298
|
-
for i in range(0, len(messageHashes)):
|
299
|
-
messageHash = messageHashes[i]
|
300
|
-
parts = messageHash.split('::')
|
301
|
-
symbolsString = parts[1]
|
302
|
-
symbols = symbolsString.split(',')
|
303
|
-
tickers = self.filter_by_array(newTickers, 'symbol', symbols)
|
304
|
-
if not self.is_empty(tickers):
|
305
|
-
client.resolve(tickers, messageHash)
|
306
|
-
if messageHash.endswith('USD'):
|
307
|
-
client.resolve(tickers, messageHash + 'C') # sometimes we subscribe to BTC/USDC and coinbase returns BTC/USD
|
308
|
-
return message
|
298
|
+
self.try_resolve_usdc(client, messageHash, result)
|
309
299
|
|
310
300
|
def parse_ws_ticker(self, ticker, market=None):
|
311
301
|
#
|
@@ -470,13 +460,13 @@ class coinbase(ccxt.async_support.coinbase):
|
|
470
460
|
# ]
|
471
461
|
# }
|
472
462
|
#
|
473
|
-
events = self.
|
463
|
+
events = self.safe_list(message, 'events')
|
474
464
|
event = self.safe_value(events, 0)
|
475
|
-
trades = self.
|
476
|
-
trade = self.
|
465
|
+
trades = self.safe_list(event, 'trades')
|
466
|
+
trade = self.safe_dict(trades, 0)
|
477
467
|
marketId = self.safe_string(trade, 'product_id')
|
478
|
-
messageHash = 'market_trades::' + marketId
|
479
468
|
symbol = self.safe_symbol(marketId)
|
469
|
+
messageHash = 'market_trades::' + symbol
|
480
470
|
tradesArray = self.safe_value(self.trades, symbol)
|
481
471
|
if tradesArray is None:
|
482
472
|
tradesLimit = self.safe_integer(self.options, 'tradesLimit', 1000)
|
@@ -484,14 +474,12 @@ class coinbase(ccxt.async_support.coinbase):
|
|
484
474
|
self.trades[symbol] = tradesArray
|
485
475
|
for i in range(0, len(events)):
|
486
476
|
currentEvent = events[i]
|
487
|
-
currentTrades = self.
|
477
|
+
currentTrades = self.safe_list(currentEvent, 'trades')
|
488
478
|
for j in range(0, len(currentTrades)):
|
489
479
|
item = currentTrades[i]
|
490
480
|
tradesArray.append(self.parse_trade(item))
|
491
481
|
client.resolve(tradesArray, messageHash)
|
492
|
-
|
493
|
-
client.resolve(tradesArray, messageHash + 'C') # sometimes we subscribe to BTC/USDC and coinbase returns BTC/USD
|
494
|
-
return message
|
482
|
+
self.try_resolve_usdc(client, messageHash, tradesArray)
|
495
483
|
|
496
484
|
def handle_order(self, client, message):
|
497
485
|
#
|
@@ -522,14 +510,14 @@ class coinbase(ccxt.async_support.coinbase):
|
|
522
510
|
# ]
|
523
511
|
# }
|
524
512
|
#
|
525
|
-
events = self.
|
513
|
+
events = self.safe_list(message, 'events')
|
526
514
|
marketIds = []
|
527
515
|
if self.orders is None:
|
528
516
|
limit = self.safe_integer(self.options, 'ordersLimit', 1000)
|
529
517
|
self.orders = ArrayCacheBySymbolById(limit)
|
530
518
|
for i in range(0, len(events)):
|
531
519
|
event = events[i]
|
532
|
-
responseOrders = self.
|
520
|
+
responseOrders = self.safe_list(event, 'orders')
|
533
521
|
for j in range(0, len(responseOrders)):
|
534
522
|
responseOrder = responseOrders[j]
|
535
523
|
parsed = self.parse_ws_order(responseOrder)
|
@@ -540,12 +528,11 @@ class coinbase(ccxt.async_support.coinbase):
|
|
540
528
|
cachedOrders.append(parsed)
|
541
529
|
for i in range(0, len(marketIds)):
|
542
530
|
marketId = marketIds[i]
|
543
|
-
|
531
|
+
symbol = self.safe_symbol(marketId)
|
532
|
+
messageHash = 'user::' + symbol
|
544
533
|
client.resolve(self.orders, messageHash)
|
545
|
-
|
546
|
-
client.resolve(self.orders, messageHash + 'C') # sometimes we subscribe to BTC/USDC and coinbase returns BTC/USD
|
534
|
+
self.try_resolve_usdc(client, messageHash, self.orders)
|
547
535
|
client.resolve(self.orders, 'user')
|
548
|
-
return message
|
549
536
|
|
550
537
|
def parse_ws_order(self, order, market=None):
|
551
538
|
#
|
@@ -635,36 +622,35 @@ class coinbase(ccxt.async_support.coinbase):
|
|
635
622
|
# ]
|
636
623
|
# }
|
637
624
|
#
|
638
|
-
events = self.
|
625
|
+
events = self.safe_list(message, 'events')
|
639
626
|
datetime = self.safe_string(message, 'timestamp')
|
640
627
|
for i in range(0, len(events)):
|
641
628
|
event = events[i]
|
642
|
-
updates = self.
|
629
|
+
updates = self.safe_list(event, 'updates', [])
|
643
630
|
marketId = self.safe_string(event, 'product_id')
|
644
|
-
|
631
|
+
# sometimes we subscribe to BTC/USDC and coinbase returns BTC/USD, are aliases
|
632
|
+
market = self.safe_market(marketId)
|
633
|
+
symbol = market['symbol']
|
634
|
+
messageHash = 'level2::' + symbol
|
645
635
|
subscription = self.safe_value(client.subscriptions, messageHash, {})
|
646
636
|
limit = self.safe_integer(subscription, 'limit')
|
647
|
-
symbol = self.safe_symbol(marketId)
|
648
637
|
type = self.safe_string(event, 'type')
|
649
638
|
if type == 'snapshot':
|
650
639
|
self.orderbooks[symbol] = self.order_book({}, limit)
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
client.resolve(orderbook, messageHash)
|
666
|
-
if messageHash.endswith('USD'):
|
667
|
-
client.resolve(orderbook, messageHash + 'C') # sometimes we subscribe to BTC/USDC and coinbase returns BTC/USD
|
640
|
+
# unknown bug, can't reproduce, but sometimes orderbook is None
|
641
|
+
if not (symbol in self.orderbooks) and self.orderbooks[symbol] is None:
|
642
|
+
continue
|
643
|
+
orderbook = self.orderbooks[symbol]
|
644
|
+
self.handle_order_book_helper(orderbook, updates)
|
645
|
+
orderbook['timestamp'] = self.parse8601(datetime)
|
646
|
+
orderbook['datetime'] = datetime
|
647
|
+
orderbook['symbol'] = symbol
|
648
|
+
client.resolve(orderbook, messageHash)
|
649
|
+
self.try_resolve_usdc(client, messageHash, orderbook)
|
650
|
+
|
651
|
+
def try_resolve_usdc(self, client, messageHash, result):
|
652
|
+
if messageHash.endswith('/USD') or messageHash.endswith('-USD'):
|
653
|
+
client.resolve(result, messageHash + 'C') # when subscribing to BTC/USDC and coinbase returns BTC/USD, so resolve USDC too
|
668
654
|
|
669
655
|
def handle_subscription_status(self, client, message):
|
670
656
|
#
|
ccxt/pro/gate.py
CHANGED
@@ -1175,8 +1175,28 @@ class gate(ccxt.async_support.gate):
|
|
1175
1175
|
for i in range(0, len(data)):
|
1176
1176
|
rawPosition = data[i]
|
1177
1177
|
position = self.parse_position(rawPosition)
|
1178
|
-
|
1179
|
-
|
1178
|
+
symbol = self.safe_string(position, 'symbol')
|
1179
|
+
side = self.safe_string(position, 'side')
|
1180
|
+
# Control when position is closed no side is returned
|
1181
|
+
if side is None:
|
1182
|
+
prevLongPosition = self.safe_dict(cache, symbol + 'long')
|
1183
|
+
if prevLongPosition is not None:
|
1184
|
+
position['side'] = prevLongPosition['side']
|
1185
|
+
newPositions.append(position)
|
1186
|
+
cache.append(position)
|
1187
|
+
prevShortPosition = self.safe_dict(cache, symbol + 'short')
|
1188
|
+
if prevShortPosition is not None:
|
1189
|
+
position['side'] = prevShortPosition['side']
|
1190
|
+
newPositions.append(position)
|
1191
|
+
cache.append(position)
|
1192
|
+
# if no prev position is found, default to long
|
1193
|
+
if prevLongPosition is None and prevShortPosition is None:
|
1194
|
+
position['side'] = 'long'
|
1195
|
+
newPositions.append(position)
|
1196
|
+
cache.append(position)
|
1197
|
+
else:
|
1198
|
+
newPositions.append(position)
|
1199
|
+
cache.append(position)
|
1180
1200
|
messageHashes = self.find_message_hashes(client, type + ':positions::')
|
1181
1201
|
for i in range(0, len(messageHashes)):
|
1182
1202
|
messageHash = messageHashes[i]
|
ccxt/pro/hollaex.py
CHANGED
@@ -572,8 +572,8 @@ class hollaex(ccxt.async_support.hollaex):
|
|
572
572
|
|
573
573
|
def on_error(self, client: Client, error):
|
574
574
|
self.options['ws-expires'] = None
|
575
|
-
self.on_error(client, error)
|
575
|
+
super(hollaex, self).on_error(client, error)
|
576
576
|
|
577
577
|
def on_close(self, client: Client, error):
|
578
578
|
self.options['ws-expires'] = None
|
579
|
-
self.on_close(client, error)
|
579
|
+
super(hollaex, self).on_close(client, error)
|
ccxt/pro/p2b.py
CHANGED
@@ -478,8 +478,8 @@ class p2b(ccxt.async_support.p2b):
|
|
478
478
|
|
479
479
|
def on_error(self, client: Client, error):
|
480
480
|
self.options['tickerSubs'] = self.create_safe_dictionary()
|
481
|
-
self.on_error(client, error)
|
481
|
+
super(p2b, self).on_error(client, error)
|
482
482
|
|
483
483
|
def on_close(self, client: Client, error):
|
484
484
|
self.options['tickerSubs'] = self.create_safe_dictionary()
|
485
|
-
self.on_close(client, error)
|
485
|
+
super(p2b, self).on_close(client, error)
|
ccxt/pro/tradeogre.py
ADDED
@@ -0,0 +1,272 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
4
|
+
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
5
|
+
|
6
|
+
import ccxt.async_support
|
7
|
+
from ccxt.async_support.base.ws.cache import ArrayCache
|
8
|
+
from ccxt.base.types import Any, Int, OrderBook, Trade
|
9
|
+
from ccxt.async_support.base.ws.client import Client
|
10
|
+
from typing import List
|
11
|
+
|
12
|
+
|
13
|
+
class tradeogre(ccxt.async_support.tradeogre):
|
14
|
+
|
15
|
+
def describe(self) -> Any:
|
16
|
+
return self.deep_extend(super(tradeogre, self).describe(), {
|
17
|
+
'has': {
|
18
|
+
'ws': True,
|
19
|
+
'watchTrades': True,
|
20
|
+
'watchTradesForSymbols': True,
|
21
|
+
'watchOrderBook': True,
|
22
|
+
'watchOrderBookForSymbols': False,
|
23
|
+
'watchOHLCV': False,
|
24
|
+
'watchOHLCVForSymbols': False,
|
25
|
+
'watchOrders': False,
|
26
|
+
'watchMyTrades': False,
|
27
|
+
'watchTicker': False,
|
28
|
+
'watchTickers': False,
|
29
|
+
'watchBidsAsks': False,
|
30
|
+
'watchBalance': False,
|
31
|
+
'createOrderWs': False,
|
32
|
+
'editOrderWs': False,
|
33
|
+
'cancelOrderWs': False,
|
34
|
+
'cancelOrdersWs': False,
|
35
|
+
},
|
36
|
+
'urls': {
|
37
|
+
'api': {
|
38
|
+
'ws': 'wss://tradeogre.com:8443',
|
39
|
+
},
|
40
|
+
},
|
41
|
+
'options': {
|
42
|
+
},
|
43
|
+
'streaming': {
|
44
|
+
},
|
45
|
+
})
|
46
|
+
|
47
|
+
async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
|
48
|
+
"""
|
49
|
+
watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
|
50
|
+
|
51
|
+
https://tradeogre.com/help/api
|
52
|
+
|
53
|
+
:param str symbol: unified symbol of the market to fetch the order book for
|
54
|
+
:param int [limit]: the maximum amount of order book entries to return(not used by the exchange)
|
55
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
56
|
+
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
|
57
|
+
"""
|
58
|
+
await self.load_markets()
|
59
|
+
market = self.market(symbol)
|
60
|
+
url = self.urls['api']['ws']
|
61
|
+
messageHash = 'orderbook' + ':' + market['symbol']
|
62
|
+
request: dict = {
|
63
|
+
'a': 'subscribe',
|
64
|
+
'e': 'book',
|
65
|
+
't': market['id'],
|
66
|
+
}
|
67
|
+
orderbook = await self.watch(url, messageHash, self.extend(request, params), messageHash)
|
68
|
+
return orderbook.limit()
|
69
|
+
|
70
|
+
def handle_order_book(self, client: Client, message):
|
71
|
+
#
|
72
|
+
# initial snapshot is fetched with ccxt's fetchOrderBook
|
73
|
+
# the feed does not include a snapshot, just the deltas
|
74
|
+
#
|
75
|
+
# {
|
76
|
+
# "e": "book",
|
77
|
+
# "t": "ETH-USDT",
|
78
|
+
# "s": "10752324",
|
79
|
+
# "d": {
|
80
|
+
# "bids": {"1787.02497915": "0"},
|
81
|
+
# "asks": {}
|
82
|
+
# }
|
83
|
+
# }
|
84
|
+
#
|
85
|
+
marketId = self.safe_string(message, 't')
|
86
|
+
symbol = self.safe_symbol(marketId)
|
87
|
+
if not (symbol in self.orderbooks):
|
88
|
+
self.orderbooks[symbol] = self.order_book({})
|
89
|
+
storedOrderBook = self.orderbooks[symbol]
|
90
|
+
nonce = self.safe_integer(storedOrderBook, 'nonce')
|
91
|
+
deltaNonce = self.safe_integer(message, 's')
|
92
|
+
messageHash = 'orderbook:' + symbol
|
93
|
+
if nonce is None:
|
94
|
+
cacheLength = len(storedOrderBook.cache)
|
95
|
+
snapshotDelay = self.handle_option('watchOrderBook', 'snapshotDelay', 6)
|
96
|
+
if cacheLength == snapshotDelay:
|
97
|
+
self.spawn(self.load_order_book, client, messageHash, symbol, None, {})
|
98
|
+
storedOrderBook.cache.append(message)
|
99
|
+
return
|
100
|
+
elif nonce >= deltaNonce:
|
101
|
+
return
|
102
|
+
self.handle_delta(storedOrderBook, message)
|
103
|
+
client.resolve(storedOrderBook, messageHash)
|
104
|
+
|
105
|
+
def handle_delta(self, orderbook, delta):
|
106
|
+
# timestamp = self.milliseconds() # todo check if self is correct
|
107
|
+
# orderbook['timestamp'] = timestamp
|
108
|
+
# orderbook['datetime'] = self.iso8601(timestamp)
|
109
|
+
orderbook['nonce'] = self.safe_integer(delta, 's')
|
110
|
+
data = self.safe_dict(delta, 'd', {})
|
111
|
+
bids = self.safe_dict(data, 'bids', {})
|
112
|
+
asks = self.safe_dict(data, 'asks', {})
|
113
|
+
storedBids = orderbook['bids']
|
114
|
+
storedAsks = orderbook['asks']
|
115
|
+
self.handle_bid_asks(storedBids, bids)
|
116
|
+
self.handle_bid_asks(storedAsks, asks)
|
117
|
+
|
118
|
+
def handle_bid_asks(self, bookSide, bidAsks):
|
119
|
+
keys = list(bidAsks.keys())
|
120
|
+
for i in range(0, len(keys)):
|
121
|
+
price = self.safe_string(keys, i)
|
122
|
+
amount = self.safe_number(bidAsks, price)
|
123
|
+
bidAsk = [self.parse_number(price), amount]
|
124
|
+
bookSide.storeArray(bidAsk)
|
125
|
+
# for i in range(0, len(bidAsks)):
|
126
|
+
# bidAsk = self.parse_bid_ask(bidAsks[i])
|
127
|
+
# bookSide.storeArray(bidAsk)
|
128
|
+
# }
|
129
|
+
|
130
|
+
def get_cache_index(self, orderbook, deltas):
|
131
|
+
firstElement = deltas[0]
|
132
|
+
firstElementNonce = self.safe_integer(firstElement, 's')
|
133
|
+
nonce = self.safe_integer(orderbook, 'nonce')
|
134
|
+
if nonce < firstElementNonce:
|
135
|
+
return -1
|
136
|
+
for i in range(0, len(deltas)):
|
137
|
+
delta = deltas[i]
|
138
|
+
deltaNonce = self.safe_integer(delta, 's')
|
139
|
+
if deltaNonce == nonce:
|
140
|
+
return i + 1
|
141
|
+
return len(deltas)
|
142
|
+
|
143
|
+
async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
144
|
+
"""
|
145
|
+
watches information on multiple trades made in a market
|
146
|
+
|
147
|
+
https://tradeogre.com/help/api
|
148
|
+
|
149
|
+
:param str symbol: unified market symbol of the market trades were made in
|
150
|
+
:param int [since]: the earliest time in ms to fetch trades for
|
151
|
+
:param int [limit]: the maximum number of trade structures to retrieve
|
152
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
153
|
+
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
|
154
|
+
"""
|
155
|
+
await self.load_markets()
|
156
|
+
market = self.market(symbol)
|
157
|
+
symbol = market['symbol']
|
158
|
+
return await self.watch_trades_for_symbols([symbol], since, limit, params)
|
159
|
+
|
160
|
+
async def watch_trades_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
161
|
+
"""
|
162
|
+
|
163
|
+
https://tradeogre.com/help/api
|
164
|
+
|
165
|
+
get the list of most recent trades for a list of symbols
|
166
|
+
:param str[] symbols: unified symbol of the market to fetch trades for(empty array means all markets)
|
167
|
+
:param int [since]: timestamp in ms of the earliest trade to fetch
|
168
|
+
:param int [limit]: the maximum amount of trades to fetch
|
169
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
170
|
+
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
|
171
|
+
"""
|
172
|
+
await self.load_markets()
|
173
|
+
symbols = self.market_symbols(symbols, None, True)
|
174
|
+
messageHashes = []
|
175
|
+
symbolsLength = 0
|
176
|
+
if symbols is not None:
|
177
|
+
symbolsLength = len(symbols)
|
178
|
+
if symbolsLength > 0:
|
179
|
+
for i in range(0, len(symbols)):
|
180
|
+
symbol = symbols[i]
|
181
|
+
messageHash = 'trades:' + symbol
|
182
|
+
messageHashes.append(messageHash)
|
183
|
+
else:
|
184
|
+
messageHash = 'trades'
|
185
|
+
messageHashes.append(messageHash)
|
186
|
+
request: dict = {
|
187
|
+
'a': 'subscribe',
|
188
|
+
'e': 'trade',
|
189
|
+
't': '*',
|
190
|
+
}
|
191
|
+
url = self.urls['api']['ws']
|
192
|
+
trades = await self.watch_multiple(url, messageHashes, self.extend(request, params), ['trades'])
|
193
|
+
if self.newUpdates:
|
194
|
+
first = self.safe_dict(trades, 0)
|
195
|
+
tradeSymbol = self.safe_string(first, 'symbol')
|
196
|
+
limit = trades.getLimit(tradeSymbol, limit)
|
197
|
+
return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
|
198
|
+
|
199
|
+
def handle_trade(self, client: Client, message):
|
200
|
+
#
|
201
|
+
# {
|
202
|
+
# "e": "trade",
|
203
|
+
# "t": "LTC-USDT",
|
204
|
+
# "d": {
|
205
|
+
# "t": 0,
|
206
|
+
# "p": "84.50000000",
|
207
|
+
# "q": "1.28471270",
|
208
|
+
# "d": "1745392002"
|
209
|
+
# }
|
210
|
+
# }
|
211
|
+
#
|
212
|
+
marketId = self.safe_string(message, 't')
|
213
|
+
market = self.safe_market(marketId)
|
214
|
+
data = self.safe_dict(message, 'd', {})
|
215
|
+
symbol = market['symbol']
|
216
|
+
if not (symbol in self.trades):
|
217
|
+
limit = self.safe_integer(self.options, 'tradesLimit', 1000)
|
218
|
+
stored = ArrayCache(limit)
|
219
|
+
self.trades[symbol] = stored
|
220
|
+
cache = self.trades[symbol]
|
221
|
+
trade = self.parse_ws_trade(data, market)
|
222
|
+
cache.append(trade)
|
223
|
+
messageHash = 'trades:' + symbol
|
224
|
+
client.resolve(cache, messageHash)
|
225
|
+
client.resolve(cache, 'trades')
|
226
|
+
|
227
|
+
def parse_ws_trade(self, trade, market=None):
|
228
|
+
#
|
229
|
+
# {
|
230
|
+
# "t": 0,
|
231
|
+
# "p": "84.50000000",
|
232
|
+
# "q": "1.28471270",
|
233
|
+
# "d": "1745392002"
|
234
|
+
# }
|
235
|
+
#
|
236
|
+
timestamp = self.safe_integer_product(trade, 'd', 1000)
|
237
|
+
sideEnum = self.safe_string(trade, 't')
|
238
|
+
return self.safe_trade({
|
239
|
+
'info': trade,
|
240
|
+
'id': None,
|
241
|
+
'timestamp': timestamp,
|
242
|
+
'datetime': self.iso8601(timestamp),
|
243
|
+
'symbol': self.safe_string(market, 'symbol'),
|
244
|
+
'order': None,
|
245
|
+
'type': None,
|
246
|
+
'side': self.parse_ws_trade_side(sideEnum),
|
247
|
+
'takerOrMaker': None,
|
248
|
+
'price': self.safe_string(trade, 'p'),
|
249
|
+
'amount': self.safe_string(trade, 'q'),
|
250
|
+
'cost': None,
|
251
|
+
'fee': {
|
252
|
+
'currency': None,
|
253
|
+
'cost': None,
|
254
|
+
},
|
255
|
+
}, market)
|
256
|
+
|
257
|
+
def parse_ws_trade_side(self, side):
|
258
|
+
sides = {
|
259
|
+
'0': 'buy',
|
260
|
+
'1': 'sell',
|
261
|
+
}
|
262
|
+
return self.safe_string(sides, side, side)
|
263
|
+
|
264
|
+
def handle_message(self, client: Client, message):
|
265
|
+
methods: dict = {
|
266
|
+
'book': self.handle_order_book,
|
267
|
+
'trade': self.handle_trade,
|
268
|
+
}
|
269
|
+
event = self.safe_string(message, 'e')
|
270
|
+
method = self.safe_value(methods, event)
|
271
|
+
if method is not None:
|
272
|
+
method(client, message)
|
ccxt/pro/upbit.py
CHANGED
@@ -8,6 +8,7 @@ from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById
|
|
8
8
|
from ccxt.base.types import Any, Balances, Int, Order, OrderBook, Str, Strings, Ticker, Tickers, Trade
|
9
9
|
from ccxt.async_support.base.ws.client import Client
|
10
10
|
from typing import List
|
11
|
+
from ccxt.base.errors import NotSupported
|
11
12
|
|
12
13
|
|
13
14
|
class upbit(ccxt.async_support.upbit):
|
@@ -21,6 +22,7 @@ class upbit(ccxt.async_support.upbit):
|
|
21
22
|
'watchTickers': True,
|
22
23
|
'watchTrades': True,
|
23
24
|
'watchTradesForSymbols': True,
|
25
|
+
'watchOHLCV': True,
|
24
26
|
'watchOrders': True,
|
25
27
|
'watchMyTrades': True,
|
26
28
|
'watchBalance': True,
|
@@ -190,6 +192,25 @@ class upbit(ccxt.async_support.upbit):
|
|
190
192
|
orderbook = await self.watch_public(symbol, 'orderbook')
|
191
193
|
return orderbook.limit()
|
192
194
|
|
195
|
+
async def watch_ohlcv(self, symbol: str, timeframe='1s', since: Int = None, limit: Int = None, params={}) -> List[list]:
|
196
|
+
"""
|
197
|
+
watches information an OHLCV with timestamp, openingPrice, highPrice, lowPrice, tradePrice, baseVolume in 1s.
|
198
|
+
|
199
|
+
https://docs.upbit.com/kr/reference/websocket-candle for Upbit KR
|
200
|
+
https://global-docs.upbit.com/reference/websocket-candle for Upbit Global
|
201
|
+
|
202
|
+
:param str symbol: unified market symbol of the market orders were made in
|
203
|
+
:param str timeframe: specifies the OHLCV candle interval to watch. As of now, Upbit only supports 1s candles.
|
204
|
+
:param int [since]: the earliest time in ms to fetch orders for
|
205
|
+
:param int [limit]: the maximum number of order structures to retrieve
|
206
|
+
:param dict [params]: extra parameters specific to the exchange API endpoint
|
207
|
+
:returns OHLCV[]: a list of `OHLCV structures <https://docs.ccxt.com/#/?id=ohlcv-structure>`
|
208
|
+
"""
|
209
|
+
if timeframe != '1s':
|
210
|
+
raise NotSupported(self.id + ' watchOHLCV does not support' + timeframe + ' candle.')
|
211
|
+
timeFrameOHLCV = 'candle.' + timeframe
|
212
|
+
return await self.watch_public(symbol, timeFrameOHLCV)
|
213
|
+
|
193
214
|
def handle_ticker(self, client: Client, message):
|
194
215
|
# 2020-03-17T23:07:36.511Z "onMessage" <Buffer 7b 22 74 79 70 65 22 3a 22 74 69 63 6b 65 72 22 2c 22 63 6f 64 65 22 3a 22 42 54 43 2d 45 54 48 22 2c 22 6f 70 65 6e 69 6e 67 5f 70 72 69 63 65 22 3a ... >
|
195
216
|
# {type: "ticker",
|
@@ -313,6 +334,26 @@ class upbit(ccxt.async_support.upbit):
|
|
313
334
|
messageHash = 'trade:' + marketId
|
314
335
|
client.resolve(stored, messageHash)
|
315
336
|
|
337
|
+
def handle_ohlcv(self, client: Client, message):
|
338
|
+
# {
|
339
|
+
# type: 'candle.1s',
|
340
|
+
# code: 'KRW-USDT',
|
341
|
+
# candle_date_time_utc: '2025-04-22T09:50:34',
|
342
|
+
# candle_date_time_kst: '2025-04-22T18:50:34',
|
343
|
+
# opening_price: 1438,
|
344
|
+
# high_price: 1438,
|
345
|
+
# low_price: 1438,
|
346
|
+
# trade_price: 1438,
|
347
|
+
# candle_acc_trade_volume: 1145.8935,
|
348
|
+
# candle_acc_trade_price: 1647794.853,
|
349
|
+
# timestamp: 1745315434125,
|
350
|
+
# stream_type: 'REALTIME'
|
351
|
+
# }
|
352
|
+
marketId = self.safe_string(message, 'code')
|
353
|
+
messageHash = 'candle.1s:' + marketId
|
354
|
+
ohlcv = self.parse_ohlcv(message)
|
355
|
+
client.resolve(ohlcv, messageHash)
|
356
|
+
|
316
357
|
async def authenticate(self, params={}):
|
317
358
|
self.check_required_credentials()
|
318
359
|
wsOptions: dict = self.safe_dict(self.options, 'ws', {})
|
@@ -611,6 +652,7 @@ class upbit(ccxt.async_support.upbit):
|
|
611
652
|
'trade': self.handle_trades,
|
612
653
|
'myOrder': self.handle_my_order,
|
613
654
|
'myAsset': self.handle_balance,
|
655
|
+
'candle.1s': self.handle_ohlcv,
|
614
656
|
}
|
615
657
|
methodName = self.safe_string(message, 'type')
|
616
658
|
method = self.safe_value(methods, methodName)
|
ccxt/probit.py
CHANGED
@@ -563,6 +563,7 @@ class probit(Exchange, ImplicitAPI):
|
|
563
563
|
'active': active,
|
564
564
|
'deposit': deposit,
|
565
565
|
'withdraw': withdraw,
|
566
|
+
'type': 'crypto',
|
566
567
|
'fee': fee,
|
567
568
|
'precision': self.parse_number(self.parse_precision(self.safe_string(platform, 'precision'))),
|
568
569
|
'limits': {
|
ccxt/test/tests_async.py
CHANGED
@@ -348,7 +348,6 @@ class testMainClass:
|
|
348
348
|
'fetchOHLCV': [symbol],
|
349
349
|
'fetchTrades': [symbol],
|
350
350
|
'fetchOrderBook': [symbol],
|
351
|
-
'fetchL2OrderBook': [symbol],
|
352
351
|
'fetchOrderBooks': [],
|
353
352
|
'fetchBidsAsks': [],
|
354
353
|
'fetchStatus': [],
|
@@ -896,6 +895,8 @@ class testMainClass:
|
|
896
895
|
async def test_request_statically(self, exchange, method, data, type, skip_keys):
|
897
896
|
output = None
|
898
897
|
request_url = None
|
898
|
+
if self.info:
|
899
|
+
dump('[INFO] STATIC REQUEST TEST:', method, ':', data['description'])
|
899
900
|
try:
|
900
901
|
if not is_sync():
|
901
902
|
await call_exchange_method_dynamically(exchange, method, self.sanitize_data_input(data['input']))
|
@@ -918,6 +919,8 @@ class testMainClass:
|
|
918
919
|
async def test_response_statically(self, exchange, method, skip_keys, data):
|
919
920
|
expected_result = exchange.safe_value(data, 'parsedResponse')
|
920
921
|
mocked_exchange = set_fetch_response(exchange, data['httpResponse'])
|
922
|
+
if self.info:
|
923
|
+
dump('[INFO] STATIC RESPONSE TEST:', method, ':', data['description'])
|
921
924
|
try:
|
922
925
|
if not is_sync():
|
923
926
|
unified_result = await call_exchange_method_dynamically(exchange, method, self.sanitize_data_input(data['input']))
|