ccxt 4.2.35__py2.py3-none-any.whl → 4.2.37__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/bitmex.py CHANGED
@@ -292,6 +292,7 @@ class bitmex(Exchange, ImplicitAPI):
292
292
  def fetch_currencies(self, params={}):
293
293
  """
294
294
  fetches all available currencies on an exchange
295
+ :see: https://www.bitmex.com/api/explorer/#not /Wallet/Wallet_getAssetsConfig
295
296
  :param dict [params]: extra parameters specific to the exchange API endpoint
296
297
  :returns dict: an associative dictionary of currencies
297
298
  """
@@ -752,6 +753,7 @@ class bitmex(Exchange, ImplicitAPI):
752
753
  def fetch_balance(self, params={}) -> Balances:
753
754
  """
754
755
  query for balance and get the amount of funds available for trading or funds locked in orders
756
+ :see: https://www.bitmex.com/api/explorer/#not /User/User_getMargin
755
757
  :param dict [params]: extra parameters specific to the exchange API endpoint
756
758
  :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
757
759
  """
@@ -812,6 +814,7 @@ class bitmex(Exchange, ImplicitAPI):
812
814
  def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
813
815
  """
814
816
  fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
817
+ :see: https://www.bitmex.com/api/explorer/#not /OrderBook/OrderBook_getL2
815
818
  :param str symbol: unified symbol of the market to fetch the order book for
816
819
  :param int [limit]: the maximum amount of order book entries to return
817
820
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -850,6 +853,7 @@ class bitmex(Exchange, ImplicitAPI):
850
853
  def fetch_order(self, id: str, symbol: Str = None, params={}):
851
854
  """
852
855
  fetches information on an order made by the user
856
+ :see: https://www.bitmex.com/api/explorer/#not /Order/Order_getOrders
853
857
  :param str symbol: unified symbol of the market the order was made in
854
858
  :param dict [params]: extra parameters specific to the exchange API endpoint
855
859
  :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
@@ -907,6 +911,7 @@ class bitmex(Exchange, ImplicitAPI):
907
911
  def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
908
912
  """
909
913
  fetch all unfilled currently open orders
914
+ :see: https://www.bitmex.com/api/explorer/#not /Order/Order_getOrders
910
915
  :param str symbol: unified market symbol
911
916
  :param int [since]: the earliest time in ms to fetch open orders for
912
917
  :param int [limit]: the maximum number of open orders structures to retrieve
@@ -923,6 +928,7 @@ class bitmex(Exchange, ImplicitAPI):
923
928
  def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
924
929
  """
925
930
  fetches information on multiple closed orders made by the user
931
+ :see: https://www.bitmex.com/api/explorer/#not /Order/Order_getOrders
926
932
  :param str symbol: unified market symbol of the market orders were made in
927
933
  :param int [since]: the earliest time in ms to fetch orders for
928
934
  :param int [limit]: the maximum number of order structures to retrieve
@@ -935,8 +941,8 @@ class bitmex(Exchange, ImplicitAPI):
935
941
 
936
942
  def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
937
943
  """
938
- :see: https://www.bitmex.com/api/explorer/#not /Execution/Execution_getTradeHistory
939
944
  fetch all trades made by the user
945
+ :see: https://www.bitmex.com/api/explorer/#not /Execution/Execution_getTradeHistory
940
946
  :param str symbol: unified market symbol
941
947
  :param int [since]: the earliest time in ms to fetch trades for
942
948
  :param int [limit]: the maximum number of trades structures to retrieve
@@ -1132,6 +1138,7 @@ class bitmex(Exchange, ImplicitAPI):
1132
1138
  def fetch_ledger(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
1133
1139
  """
1134
1140
  fetch the history of changes, actions done by the user or operations that altered balance of the user
1141
+ :see: https://www.bitmex.com/api/explorer/#not /User/User_getWalletHistory
1135
1142
  :param str code: unified currency code, default is None
1136
1143
  :param int [since]: timestamp in ms of the earliest ledger entry, default is None
1137
1144
  :param int [limit]: max number of ledger entrys to return, default is None
@@ -1179,6 +1186,7 @@ class bitmex(Exchange, ImplicitAPI):
1179
1186
  def fetch_deposits_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
1180
1187
  """
1181
1188
  fetch history of deposits and withdrawals
1189
+ :see: https://www.bitmex.com/api/explorer/#not /User/User_getWalletHistory
1182
1190
  :param str [code]: unified currency code for the currency of the deposit/withdrawals, default is None
1183
1191
  :param int [since]: timestamp in ms of the earliest deposit/withdrawal, default is None
1184
1192
  :param int [limit]: max number of deposit/withdrawals to return, default is None
@@ -1290,6 +1298,7 @@ class bitmex(Exchange, ImplicitAPI):
1290
1298
  def fetch_ticker(self, symbol: str, params={}) -> Ticker:
1291
1299
  """
1292
1300
  fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
1301
+ :see: https://www.bitmex.com/api/explorer/#not /Instrument/Instrument_get
1293
1302
  :param str symbol: unified symbol of the market to fetch the ticker for
1294
1303
  :param dict [params]: extra parameters specific to the exchange API endpoint
1295
1304
  :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
@@ -1308,6 +1317,7 @@ class bitmex(Exchange, ImplicitAPI):
1308
1317
  def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
1309
1318
  """
1310
1319
  fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
1320
+ :see: https://www.bitmex.com/api/explorer/#not /Instrument/Instrument_getActiveAndIndices
1311
1321
  :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
1312
1322
  :param dict [params]: extra parameters specific to the exchange API endpoint
1313
1323
  :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
@@ -1386,8 +1396,8 @@ class bitmex(Exchange, ImplicitAPI):
1386
1396
 
1387
1397
  def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
1388
1398
  """
1389
- :see: https://www.bitmex.com/api/explorer/#not /Trade/Trade_getBucketed
1390
1399
  fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
1400
+ :see: https://www.bitmex.com/api/explorer/#not /Trade/Trade_getBucketed
1391
1401
  :param str symbol: unified symbol of the market to fetch OHLCV data for
1392
1402
  :param str timeframe: the length of time each candle represents
1393
1403
  :param int [since]: timestamp in ms of the earliest candle to fetch
@@ -1683,8 +1693,8 @@ class bitmex(Exchange, ImplicitAPI):
1683
1693
 
1684
1694
  def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
1685
1695
  """
1686
- :see: https://www.bitmex.com/api/explorer/#not /Trade/Trade_get
1687
1696
  get the list of most recent trades for a particular symbol
1697
+ :see: https://www.bitmex.com/api/explorer/#not /Trade/Trade_get
1688
1698
  :param str symbol: unified symbol of the market to fetch trades for
1689
1699
  :param int [since]: timestamp in ms of the earliest trade to fetch
1690
1700
  :param int [limit]: the maximum amount of trades to fetch
@@ -1866,6 +1876,7 @@ class bitmex(Exchange, ImplicitAPI):
1866
1876
  def cancel_order(self, id: str, symbol: Str = None, params={}):
1867
1877
  """
1868
1878
  cancels an open order
1879
+ :see: https://www.bitmex.com/api/explorer/#not /Order/Order_cancel
1869
1880
  :param str id: order id
1870
1881
  :param str symbol: not used by bitmex cancelOrder()
1871
1882
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -1891,6 +1902,7 @@ class bitmex(Exchange, ImplicitAPI):
1891
1902
  def cancel_orders(self, ids, symbol: Str = None, params={}):
1892
1903
  """
1893
1904
  cancel multiple orders
1905
+ :see: https://www.bitmex.com/api/explorer/#not /Order/Order_cancel
1894
1906
  :param str[] ids: order ids
1895
1907
  :param str symbol: not used by bitmex cancelOrders()
1896
1908
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -1912,6 +1924,7 @@ class bitmex(Exchange, ImplicitAPI):
1912
1924
  def cancel_all_orders(self, symbol: Str = None, params={}):
1913
1925
  """
1914
1926
  cancel all open orders
1927
+ :see: https://www.bitmex.com/api/explorer/#not /Order/Order_cancelAll
1915
1928
  :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
1916
1929
  :param dict [params]: extra parameters specific to the exchange API endpoint
1917
1930
  :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
@@ -1967,6 +1980,7 @@ class bitmex(Exchange, ImplicitAPI):
1967
1980
  def fetch_positions(self, symbols: Strings = None, params={}):
1968
1981
  """
1969
1982
  fetch all open positions
1983
+ :see: https://www.bitmex.com/api/explorer/#not /Position/Position_get
1970
1984
  :param str[]|None symbols: list of unified market symbols
1971
1985
  :param dict [params]: extra parameters specific to the exchange API endpoint
1972
1986
  :returns dict[]: a list of `position structure <https://docs.ccxt.com/#/?id=position-structure>`
@@ -2220,6 +2234,7 @@ class bitmex(Exchange, ImplicitAPI):
2220
2234
  def withdraw(self, code: str, amount: float, address, tag=None, params={}):
2221
2235
  """
2222
2236
  make a withdrawal
2237
+ :see: https://www.bitmex.com/api/explorer/#not /User/User_requestWithdrawal
2223
2238
  :param str code: unified currency code
2224
2239
  :param float amount: the amount to withdraw
2225
2240
  :param str address: the address to withdraw to
@@ -2265,6 +2280,7 @@ class bitmex(Exchange, ImplicitAPI):
2265
2280
  def fetch_funding_rates(self, symbols: Strings = None, params={}):
2266
2281
  """
2267
2282
  fetch the funding rate for multiple markets
2283
+ :see: https://www.bitmex.com/api/explorer/#not /Instrument/Instrument_getActiveAndIndices
2268
2284
  :param str[]|None symbols: list of unified market symbols
2269
2285
  :param dict [params]: extra parameters specific to the exchange API endpoint
2270
2286
  :returns dict: a dictionary of `funding rates structures <https://docs.ccxt.com/#/?id=funding-rates-structure>`, indexe by market symbols
@@ -2312,6 +2328,7 @@ class bitmex(Exchange, ImplicitAPI):
2312
2328
  def fetch_funding_rate_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
2313
2329
  """
2314
2330
  Fetches the history of funding rates
2331
+ :see: https://www.bitmex.com/api/explorer/#not /Funding/Funding_get
2315
2332
  :param str symbol: unified symbol of the market to fetch the funding rate history for
2316
2333
  :param int [since]: timestamp in ms of the earliest funding rate to fetch
2317
2334
  :param int [limit]: the maximum amount of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-history-structure>` to fetch
@@ -2386,6 +2403,7 @@ class bitmex(Exchange, ImplicitAPI):
2386
2403
  def set_leverage(self, leverage: Int, symbol: Str = None, params={}):
2387
2404
  """
2388
2405
  set the level of leverage for a market
2406
+ :see: https://www.bitmex.com/api/explorer/#not /Position/Position_updateLeverage
2389
2407
  :param float leverage: the rate of leverage
2390
2408
  :param str symbol: unified market symbol
2391
2409
  :param dict [params]: extra parameters specific to the exchange API endpoint
@@ -2408,6 +2426,7 @@ class bitmex(Exchange, ImplicitAPI):
2408
2426
  def set_margin_mode(self, marginMode, symbol: Str = None, params={}):
2409
2427
  """
2410
2428
  set margin mode to 'cross' or 'isolated'
2429
+ :see: https://www.bitmex.com/api/explorer/#not /Position/Position_isolateMargin
2411
2430
  :param str marginMode: 'cross' or 'isolated'
2412
2431
  :param str symbol: unified market symbol
2413
2432
  :param dict [params]: extra parameters specific to the exchange API endpoint
ccxt/bybit.py CHANGED
@@ -3525,9 +3525,19 @@ class bybit(Exchange, ImplicitAPI):
3525
3525
  if isStopLoss:
3526
3526
  slTriggerPrice = self.safe_value_2(stopLoss, 'triggerPrice', 'stopPrice', stopLoss)
3527
3527
  request['stopLoss'] = self.price_to_precision(symbol, slTriggerPrice)
3528
+ slLimitPrice = self.safe_value(stopLoss, 'price')
3529
+ if slLimitPrice is not None:
3530
+ request['tpslMode'] = 'Partial'
3531
+ request['slOrderType'] = 'Limit'
3532
+ request['slLimitPrice'] = self.price_to_precision(symbol, slLimitPrice)
3528
3533
  if isTakeProfit:
3529
3534
  tpTriggerPrice = self.safe_value_2(takeProfit, 'triggerPrice', 'stopPrice', takeProfit)
3530
3535
  request['takeProfit'] = self.price_to_precision(symbol, tpTriggerPrice)
3536
+ tpLimitPrice = self.safe_value(takeProfit, 'price')
3537
+ if tpLimitPrice is not None:
3538
+ request['tpslMode'] = 'Partial'
3539
+ request['tpOrderType'] = 'Limit'
3540
+ request['tpLimitPrice'] = self.price_to_precision(symbol, tpLimitPrice)
3531
3541
  if market['spot']:
3532
3542
  # only works for spot market
3533
3543
  if triggerPrice is not None:
@@ -5621,9 +5631,6 @@ class bybit(Exchange, ImplicitAPI):
5621
5631
  timestamp = self.parse8601(self.safe_string(position, 'updated_at'))
5622
5632
  if timestamp is None:
5623
5633
  timestamp = self.safe_integer_n(position, ['updatedTime', 'updatedAt'])
5624
- # default to cross of USDC margined positions
5625
- tradeMode = self.safe_integer(position, 'tradeMode', 0)
5626
- marginMode = 'isolated' if tradeMode else 'cross'
5627
5634
  collateralString = self.safe_string(position, 'positionBalance')
5628
5635
  entryPrice = self.omit_zero(self.safe_string_2(position, 'entryPrice', 'avgPrice'))
5629
5636
  liquidationPrice = self.omit_zero(self.safe_string(position, 'liqPrice'))
@@ -5680,7 +5687,7 @@ class bybit(Exchange, ImplicitAPI):
5680
5687
  'markPrice': self.safe_number(position, 'markPrice'),
5681
5688
  'lastPrice': None,
5682
5689
  'collateral': self.parse_number(collateralString),
5683
- 'marginMode': marginMode,
5690
+ 'marginMode': None,
5684
5691
  'side': side,
5685
5692
  'percentage': None,
5686
5693
  'stopLossPrice': self.safe_number_2(position, 'stop_loss', 'stopLoss'),
ccxt/pro/__init__.py CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # ----------------------------------------------------------------------------
6
6
 
7
- __version__ = '4.2.35'
7
+ __version__ = '4.2.37'
8
8
 
9
9
  # ----------------------------------------------------------------------------
10
10
 
ccxt/pro/binance.py CHANGED
@@ -56,8 +56,8 @@ class binance(ccxt.async_support.binance):
56
56
  },
57
57
  'api': {
58
58
  'ws': {
59
- 'spot': 'wss://stream.binance.com/ws',
60
- 'margin': 'wss://stream.binance.com/ws',
59
+ 'spot': 'wss://stream.binance.com:9443/ws',
60
+ 'margin': 'wss://stream.binance.com:9443/ws',
61
61
  'future': 'wss://fstream.binance.com/ws',
62
62
  'delivery': 'wss://dstream.binance.com/ws',
63
63
  'ws': 'wss://ws-api.binance.com:443/ws-api/v3',
ccxt/pro/gemini.py CHANGED
@@ -10,6 +10,7 @@ from ccxt.base.types import Int, Order, OrderBook, Str, Trade
10
10
  from ccxt.async_support.base.ws.client import Client
11
11
  from typing import List
12
12
  from ccxt.base.errors import ExchangeError
13
+ from ccxt.base.errors import NotSupported
13
14
 
14
15
 
15
16
  class gemini(ccxt.async_support.gemini):
@@ -22,9 +23,11 @@ class gemini(ccxt.async_support.gemini):
22
23
  'watchTicker': False,
23
24
  'watchTickers': False,
24
25
  'watchTrades': True,
26
+ 'watchTradesForSymbols': True,
25
27
  'watchMyTrades': False,
26
28
  'watchOrders': True,
27
29
  'watchOrderBook': True,
30
+ 'watchOrderBookForSymbols': True,
28
31
  'watchOHLCV': True,
29
32
  },
30
33
  'hostname': 'api.gemini.com',
@@ -70,7 +73,26 @@ class gemini(ccxt.async_support.gemini):
70
73
  limit = trades.getLimit(market['symbol'], limit)
71
74
  return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
72
75
 
73
- def parse_ws_trade(self, trade, market=None):
76
+ async def watch_trades_for_symbols(self, symbols: List[str], since: Int = None, limit: Int = None, params={}) -> List[Trade]:
77
+ """
78
+ :see: https://docs.gemini.com/websocket-api/#multi-market-data
79
+ get the list of most recent trades for a list of symbols
80
+ :param str[] symbols: unified symbol of the market to fetch trades for
81
+ :param int [since]: timestamp in ms of the earliest trade to fetch
82
+ :param int [limit]: the maximum amount of trades to fetch
83
+ :param dict [params]: extra parameters specific to the exchange API endpoint
84
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
85
+ """
86
+ trades = await self.helper_for_watch_multiple_construct('trades', symbols, params)
87
+ if self.newUpdates:
88
+ first = self.safe_list(trades, 0)
89
+ tradeSymbol = self.safe_string(first, 'symbol')
90
+ limit = trades.getLimit(tradeSymbol, limit)
91
+ return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
92
+
93
+ def parse_ws_trade(self, trade, market=None) -> Trade:
94
+ #
95
+ # regular v2 trade
74
96
  #
75
97
  # {
76
98
  # "type": "trade",
@@ -82,11 +104,28 @@ class gemini(ccxt.async_support.gemini):
82
104
  # "side": "buy"
83
105
  # }
84
106
  #
107
+ # multi data trade
108
+ #
109
+ # {
110
+ # "type": "trade",
111
+ # "symbol": "ETHUSD",
112
+ # "tid": "1683002242170204", # self is not TS, but somewhat ID
113
+ # "price": "2299.24",
114
+ # "amount": "0.002662",
115
+ # "makerSide": "bid"
116
+ # }
117
+ #
85
118
  timestamp = self.safe_integer(trade, 'timestamp')
86
- id = self.safe_string(trade, 'event_id')
119
+ id = self.safe_string_2(trade, 'event_id', 'tid')
87
120
  priceString = self.safe_string(trade, 'price')
88
- amountString = self.safe_string(trade, 'quantity')
121
+ amountString = self.safe_string_2(trade, 'quantity', 'amount')
89
122
  side = self.safe_string_lower(trade, 'side')
123
+ if side is None:
124
+ marketSide = self.safe_string_lower(trade, 'makerSide')
125
+ if marketSide == 'bid':
126
+ side = 'sell'
127
+ elif marketSide == 'ask':
128
+ side = 'buy'
90
129
  marketId = self.safe_string_lower(trade, 'symbol')
91
130
  symbol = self.safe_symbol(marketId, market)
92
131
  return self.safe_trade({
@@ -182,6 +221,30 @@ class gemini(ccxt.async_support.gemini):
182
221
  messageHash = 'trades:' + symbol
183
222
  client.resolve(stored, messageHash)
184
223
 
224
+ def handle_trades_for_multidata(self, client: Client, trades, timestamp: Int):
225
+ if trades is not None:
226
+ tradesLimit = self.safe_integer(self.options, 'tradesLimit', 1000)
227
+ storesForSymbols = {}
228
+ for i in range(0, len(trades)):
229
+ marketId = trades[i]['symbol']
230
+ market = self.safe_market(marketId.lower())
231
+ symbol = market['symbol']
232
+ trade = self.parse_ws_trade(trades[i], market)
233
+ trade['timestamp'] = timestamp
234
+ trade['datetime'] = self.iso8601(timestamp)
235
+ stored = self.safe_value(self.trades, symbol)
236
+ if stored is None:
237
+ stored = ArrayCache(tradesLimit)
238
+ self.trades[symbol] = stored
239
+ stored.append(trade)
240
+ storesForSymbols[symbol] = stored
241
+ symbols = list(storesForSymbols.keys())
242
+ for i in range(0, len(symbols)):
243
+ symbol = symbols[i]
244
+ stored = storesForSymbols[symbol]
245
+ messageHash = 'trades:' + symbol
246
+ client.resolve(stored, messageHash)
247
+
185
248
  async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
186
249
  """
187
250
  watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
@@ -317,6 +380,83 @@ class gemini(ccxt.async_support.gemini):
317
380
  self.orderbooks[symbol] = orderbook
318
381
  client.resolve(orderbook, messageHash)
319
382
 
383
+ async def watch_order_book_for_symbols(self, symbols: List[str], limit: Int = None, params={}) -> OrderBook:
384
+ """
385
+ watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
386
+ :see: https://docs.gemini.com/websocket-api/#multi-market-data
387
+ :param str[] symbols: unified array of symbols
388
+ :param int [limit]: the maximum amount of order book entries to return
389
+ :param dict [params]: extra parameters specific to the exchange API endpoint
390
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
391
+ """
392
+ orderbook = await self.helper_for_watch_multiple_construct('orderbook', symbols, params)
393
+ return orderbook.limit()
394
+
395
+ async def helper_for_watch_multiple_construct(self, itemHashName: str, symbols: List[str], params={}):
396
+ await self.load_markets()
397
+ symbols = self.market_symbols(symbols, None, False, True, True)
398
+ firstMarket = self.market(symbols[0])
399
+ if not firstMarket['spot'] and not firstMarket['linear']:
400
+ raise NotSupported(self.id + ' watchMultiple supports only spot or linear-swap symbols')
401
+ messageHashes = []
402
+ marketIds = []
403
+ for i in range(0, len(symbols)):
404
+ symbol = symbols[i]
405
+ messageHash = itemHashName + ':' + symbol
406
+ messageHashes.append(messageHash)
407
+ market = self.market(symbol)
408
+ marketIds.append(market['id'])
409
+ queryStr = ','.join(marketIds)
410
+ url = self.urls['api']['ws'] + '/v1/multimarketdata?symbols=' + queryStr + '&heartbeat=true&'
411
+ if itemHashName == 'orderbook':
412
+ url += 'trades=false&bids=true&offers=true'
413
+ elif itemHashName == 'trades':
414
+ url += 'trades=true&bids=false&offers=false'
415
+ return await self.watch_multiple(url, messageHashes, None)
416
+
417
+ def handle_order_book_for_multidata(self, client: Client, rawOrderBookChanges, timestamp: Int, nonce: Int):
418
+ #
419
+ # rawOrderBookChanges
420
+ #
421
+ # [
422
+ # {
423
+ # delta: "4105123935484.817624",
424
+ # price: "0.000000001",
425
+ # reason: "initial", # initial|cancel|place
426
+ # remaining: "4105123935484.817624",
427
+ # side: "bid", # bid|ask
428
+ # symbol: "SHIBUSD",
429
+ # type: "change", # seems always change
430
+ # },
431
+ # ...
432
+ #
433
+ marketId = rawOrderBookChanges[0]['symbol']
434
+ market = self.safe_market(marketId.lower())
435
+ symbol = market['symbol']
436
+ messageHash = 'orderbook:' + symbol
437
+ orderbook = self.safe_dict(self.orderbooks, symbol)
438
+ if orderbook is None:
439
+ orderbook = self.order_book()
440
+ bids = orderbook['bids']
441
+ asks = orderbook['asks']
442
+ for i in range(0, len(rawOrderBookChanges)):
443
+ entry = rawOrderBookChanges[i]
444
+ price = self.safe_number(entry, 'price')
445
+ size = self.safe_number(entry, 'remaining')
446
+ rawSide = self.safe_string(entry, 'side')
447
+ if rawSide == 'bid':
448
+ bids.store(price, size)
449
+ else:
450
+ asks.store(price, size)
451
+ orderbook['bids'] = bids
452
+ orderbook['asks'] = asks
453
+ orderbook['symbol'] = symbol
454
+ orderbook['nonce'] = nonce
455
+ orderbook['timestamp'] = timestamp
456
+ orderbook['datetime'] = self.iso8601(timestamp)
457
+ self.orderbooks[symbol] = orderbook
458
+ client.resolve(orderbook, messageHash)
459
+
320
460
  def handle_l2_updates(self, client: Client, message):
321
461
  #
322
462
  # {
@@ -393,6 +533,7 @@ class gemini(ccxt.async_support.gemini):
393
533
  # "socket_sequence": 7
394
534
  # }
395
535
  #
536
+ client.lastPong = self.milliseconds()
396
537
  return message
397
538
 
398
539
  def handle_subscription(self, client: Client, message):
@@ -586,6 +727,27 @@ class gemini(ccxt.async_support.gemini):
586
727
  method = self.safe_value(methods, type)
587
728
  if method is not None:
588
729
  method(client, message)
730
+ # handle multimarketdata
731
+ if type == 'update':
732
+ ts = self.safe_integer(message, 'timestampms', self.milliseconds())
733
+ eventId = self.safe_integer(message, 'eventId')
734
+ events = self.safe_list(message, 'events')
735
+ orderBookItems = []
736
+ collectedEventsOfTrades = []
737
+ for i in range(0, len(events)):
738
+ event = events[i]
739
+ eventType = self.safe_string(event, 'type')
740
+ isOrderBook = (eventType == 'change') and ('side' in event) and self.in_array(event['side'], ['ask', 'bid'])
741
+ if isOrderBook:
742
+ orderBookItems.append(event)
743
+ elif eventType == 'trade':
744
+ collectedEventsOfTrades.append(events[i])
745
+ lengthOb = len(orderBookItems)
746
+ if lengthOb > 0:
747
+ self.handle_order_book_for_multidata(client, orderBookItems, ts, eventId)
748
+ lengthTrades = len(collectedEventsOfTrades)
749
+ if lengthTrades > 0:
750
+ self.handle_trades_for_multidata(client, collectedEventsOfTrades, ts)
589
751
 
590
752
  async def authenticate(self, params={}):
591
753
  url = self.safe_string(params, 'url')
ccxt/test/test_async.py CHANGED
@@ -816,10 +816,10 @@ class testMainClass(baseMainTestClass):
816
816
  # --- Init of static tests functions------------------------------------------
817
817
  # -----------------------------------------------------------------------------
818
818
  calculated_string = json_stringify(calculated_output)
819
- output_string = json_stringify(stored_output)
820
- error_message = message + ' expected ' + output_string + ' received: ' + calculated_string
819
+ stored_string = json_stringify(stored_output)
820
+ error_message = message + ' computed ' + stored_string + ' stored: ' + calculated_string
821
821
  if key is not None:
822
- error_message = ' | ' + key + ' | ' + 'computed value: ' + output_string + ' stored value: ' + calculated_string
822
+ error_message = ' | ' + key + ' | ' + 'computed value: ' + stored_string + ' stored value: ' + calculated_string
823
823
  assert cond, error_message
824
824
 
825
825
  def load_markets_from_file(self, id):
@@ -1141,7 +1141,9 @@ class testMainClass(baseMainTestClass):
1141
1141
  await close(exchange)
1142
1142
  return True # in c# methods that will be used with promiseAll need to return something
1143
1143
 
1144
- def get_number_of_tests_from_exchange(self, exchange, exchange_data):
1144
+ def get_number_of_tests_from_exchange(self, exchange, exchange_data, test_name=None):
1145
+ if test_name is not None:
1146
+ return 1
1145
1147
  sum = 0
1146
1148
  methods = exchange_data['methods']
1147
1149
  methods_names = list(methods.keys())
@@ -1171,7 +1173,7 @@ class testMainClass(baseMainTestClass):
1171
1173
  for i in range(0, len(exchanges)):
1172
1174
  exchange_name = exchanges[i]
1173
1175
  exchange_data = static_data[exchange_name]
1174
- number_of_tests = self.get_number_of_tests_from_exchange(exchange, exchange_data)
1176
+ number_of_tests = self.get_number_of_tests_from_exchange(exchange, exchange_data, test_name)
1175
1177
  sum = exchange.sum(sum, number_of_tests)
1176
1178
  if type == 'request':
1177
1179
  promises.append(self.test_exchange_request_statically(exchange_name, exchange_data, test_name))
@@ -1444,5 +1446,5 @@ class testMainClass(baseMainTestClass):
1444
1446
 
1445
1447
 
1446
1448
  if __name__ == '__main__':
1447
- symbol = argv.symbol if argv.symbol and '/' in argv.symbol else None
1449
+ symbol = argv.symbol if argv.symbol else None
1448
1450
  asyncio.run(testMainClass().init(argv.exchange, symbol))
ccxt/test/test_sync.py CHANGED
@@ -815,10 +815,10 @@ class testMainClass(baseMainTestClass):
815
815
  # --- Init of static tests functions------------------------------------------
816
816
  # -----------------------------------------------------------------------------
817
817
  calculated_string = json_stringify(calculated_output)
818
- output_string = json_stringify(stored_output)
819
- error_message = message + ' expected ' + output_string + ' received: ' + calculated_string
818
+ stored_string = json_stringify(stored_output)
819
+ error_message = message + ' computed ' + stored_string + ' stored: ' + calculated_string
820
820
  if key is not None:
821
- error_message = ' | ' + key + ' | ' + 'computed value: ' + output_string + ' stored value: ' + calculated_string
821
+ error_message = ' | ' + key + ' | ' + 'computed value: ' + stored_string + ' stored value: ' + calculated_string
822
822
  assert cond, error_message
823
823
 
824
824
  def load_markets_from_file(self, id):
@@ -1140,7 +1140,9 @@ class testMainClass(baseMainTestClass):
1140
1140
  close(exchange)
1141
1141
  return True # in c# methods that will be used with promiseAll need to return something
1142
1142
 
1143
- def get_number_of_tests_from_exchange(self, exchange, exchange_data):
1143
+ def get_number_of_tests_from_exchange(self, exchange, exchange_data, test_name=None):
1144
+ if test_name is not None:
1145
+ return 1
1144
1146
  sum = 0
1145
1147
  methods = exchange_data['methods']
1146
1148
  methods_names = list(methods.keys())
@@ -1170,7 +1172,7 @@ class testMainClass(baseMainTestClass):
1170
1172
  for i in range(0, len(exchanges)):
1171
1173
  exchange_name = exchanges[i]
1172
1174
  exchange_data = static_data[exchange_name]
1173
- number_of_tests = self.get_number_of_tests_from_exchange(exchange, exchange_data)
1175
+ number_of_tests = self.get_number_of_tests_from_exchange(exchange, exchange_data, test_name)
1174
1176
  sum = exchange.sum(sum, number_of_tests)
1175
1177
  if type == 'request':
1176
1178
  promises.append(self.test_exchange_request_statically(exchange_name, exchange_data, test_name))
@@ -1443,5 +1445,5 @@ class testMainClass(baseMainTestClass):
1443
1445
 
1444
1446
 
1445
1447
  if __name__ == '__main__':
1446
- symbol = argv.symbol if argv.symbol and '/' in argv.symbol else None
1448
+ symbol = argv.symbol if argv.symbol else None
1447
1449
  (testMainClass().init(argv.exchange, symbol))