ccxt 4.3.47__py2.py3-none-any.whl → 4.3.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.
ccxt/pro/gate.py CHANGED
@@ -6,12 +6,14 @@
6
6
  import ccxt.async_support
7
7
  from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById, ArrayCacheBySymbolBySide, ArrayCacheByTimestamp
8
8
  import hashlib
9
- from ccxt.base.types import Balances, Int, Liquidation, Order, OrderBook, Position, Str, Strings, Ticker, Tickers, Trade
9
+ from ccxt.base.types import Balances, Int, Liquidation, Market, MarketType, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, Trade
10
10
  from ccxt.async_support.base.ws.client import Client
11
11
  from typing import List
12
+ from ccxt.base.errors import ExchangeError
12
13
  from ccxt.base.errors import AuthenticationError
13
14
  from ccxt.base.errors import ArgumentsRequired
14
15
  from ccxt.base.errors import BadRequest
16
+ from ccxt.base.errors import NotSupported
15
17
  from ccxt.base.errors import InvalidNonce
16
18
  from ccxt.base.precise import Precise
17
19
 
@@ -22,6 +24,27 @@ class gate(ccxt.async_support.gate):
22
24
  return self.deep_extend(super(gate, self).describe(), {
23
25
  'has': {
24
26
  'ws': True,
27
+ 'cancelAllOrdersWs': True,
28
+ 'cancelOrderWs': True,
29
+ 'createMarketBuyOrderWithCostWs': True,
30
+ 'createMarketOrderWs': True,
31
+ 'createMarketOrderWithCostWs': False,
32
+ 'createMarketSellOrderWithCostWs': False,
33
+ 'createOrderWs': True,
34
+ 'createOrdersWs': True,
35
+ 'createPostOnlyOrderWs': True,
36
+ 'createReduceOnlyOrderWs': True,
37
+ 'createStopLimitOrderWs': True,
38
+ 'createStopLossOrderWs': True,
39
+ 'createStopMarketOrderWs': False,
40
+ 'createStopOrderWs': True,
41
+ 'createTakeProfitOrderWs': True,
42
+ 'createTriggerOrderWs': True,
43
+ 'editOrderWs': True,
44
+ 'fetchOrderWs': True,
45
+ 'fetchOrdersWs': False,
46
+ 'fetchOpenOrdersWs': True,
47
+ 'fetchClosedOrdersWs': True,
25
48
  'watchOrderBook': True,
26
49
  'watchTicker': True,
27
50
  'watchTickers': True,
@@ -95,15 +118,228 @@ class gate(ccxt.async_support.gate):
95
118
  'exceptions': {
96
119
  'ws': {
97
120
  'exact': {
121
+ '1': BadRequest,
98
122
  '2': BadRequest,
99
123
  '4': AuthenticationError,
100
124
  '6': AuthenticationError,
101
125
  '11': AuthenticationError,
102
126
  },
127
+ 'broad': {},
103
128
  },
104
129
  },
105
130
  })
106
131
 
132
+ async def create_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
133
+ """
134
+ :see: https://www.gate.io/docs/developers/apiv4/ws/en/#order-place
135
+ :see: https://www.gate.io/docs/developers/futures/ws/en/#order-place
136
+ Create an order on the exchange
137
+ :param str symbol: Unified CCXT market symbol
138
+ :param str type: 'limit' or 'market' *"market" is contract only*
139
+ :param str side: 'buy' or 'sell'
140
+ :param float amount: the amount of currency to trade
141
+ :param float [price]: *ignored in "market" orders* the price at which the order is to be fullfilled at in units of the quote currency
142
+ :param dict [params]: extra parameters specific to the exchange API endpoint
143
+ :param float [params.stopPrice]: The price at which a trigger order is triggered at
144
+ :param str [params.timeInForce]: "GTC", "IOC", or "PO"
145
+ :param float [params.stopLossPrice]: The price at which a stop loss order is triggered at
146
+ :param float [params.takeProfitPrice]: The price at which a take profit order is triggered at
147
+ :param str [params.marginMode]: 'cross' or 'isolated' - marginMode for margin trading if not provided self.options['defaultMarginMode'] is used
148
+ :param int [params.iceberg]: Amount to display for the iceberg order, Null or 0 for normal orders, Set to -1 to hide the order completely
149
+ :param str [params.text]: User defined information
150
+ :param str [params.account]: *spot and margin only* "spot", "margin" or "cross_margin"
151
+ :param bool [params.auto_borrow]: *margin only* Used in margin or cross margin trading to allow automatic loan of insufficient amount if balance is not enough
152
+ :param str [params.settle]: *contract only* Unified Currency Code for settle currency
153
+ :param bool [params.reduceOnly]: *contract only* Indicates if self order is to reduce the size of a position
154
+ :param bool [params.close]: *contract only* Set to close the position, with size set to 0
155
+ :param bool [params.auto_size]: *contract only* Set side to close dual-mode position, close_long closes the long side, while close_short the short one, size also needs to be set to 0
156
+ :param int [params.price_type]: *contract only* 0 latest deal price, 1 mark price, 2 index price
157
+ :param float [params.cost]: *spot market buy only* the quote quantity that can be used alternative for the amount
158
+ :returns dict|None: `An order structure <https://docs.ccxt.com/#/?id=order-structure>`
159
+ """
160
+ await self.load_markets()
161
+ market = self.market(symbol)
162
+ symbol = market['symbol']
163
+ messageType = self.get_type_by_market(market)
164
+ channel = messageType + '.order_place'
165
+ url = self.get_url_by_market(market)
166
+ params['textIsRequired'] = True
167
+ request = self.create_order_request(symbol, type, side, amount, price, params)
168
+ await self.authenticate(url, messageType)
169
+ rawOrder = await self.request_private(url, request, channel)
170
+ order = self.parse_order(rawOrder, market)
171
+ return order
172
+
173
+ async def create_orders_ws(self, orders: List[OrderRequest], params={}):
174
+ """
175
+ create a list of trade orders
176
+ :see: https://www.gate.io/docs/developers/futures/ws/en/#order-batch-place
177
+ :param Array orders: list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params
178
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
179
+ """
180
+ await self.load_markets()
181
+ request = self.createOrdersRequest(orders, params)
182
+ firstOrder = orders[0]
183
+ market = self.market(firstOrder['symbol'])
184
+ if market['swap'] is not True:
185
+ raise NotSupported(self.id + ' createOrdersWs is not supported for swap markets')
186
+ messageType = self.get_type_by_market(market)
187
+ channel = messageType + '.order_batch_place'
188
+ url = self.get_url_by_market(market)
189
+ await self.authenticate(url, messageType)
190
+ rawOrders = await self.request_private(url, request, channel)
191
+ return self.parse_orders(rawOrders, market)
192
+
193
+ async def cancel_all_orders_ws(self, symbol: Str = None, params={}):
194
+ """
195
+ cancel all open orders
196
+ :see: https://www.gate.io/docs/developers/futures/ws/en/#cancel-all-open-orders-matched
197
+ :see: https://www.gate.io/docs/developers/apiv4/ws/en/#order-cancel-all-with-specified-currency-pair
198
+ :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
199
+ :param dict [params]: extra parameters specific to the exchange API endpoint
200
+ :param str [params.channel]: the channel to use, defaults to spot.order_cancel_cp or futures.order_cancel_cp
201
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
202
+ """
203
+ await self.load_markets()
204
+ market = None if (symbol is None) else self.market(symbol)
205
+ stop = self.safe_bool_2(params, 'stop', 'trigger')
206
+ messageType = self.get_type_by_market(market)
207
+ channel = messageType + '.order_cancel_cp'
208
+ channel, params = self.handle_option_and_params(params, 'cancelAllOrdersWs', 'channel', channel)
209
+ url = self.get_url_by_market(market)
210
+ params = self.omit(params, ['stop', 'trigger'])
211
+ type, query = self.handle_market_type_and_params('cancelAllOrders', market, params)
212
+ request, requestParams = self.multiOrderSpotPrepareRequest(market, stop, query) if (type == 'spot') else self.prepareRequest(market, type, query)
213
+ await self.authenticate(url, messageType)
214
+ rawOrders = await self.request_private(url, self.extend(request, requestParams), channel)
215
+ return self.parse_orders(rawOrders, market)
216
+
217
+ async def cancel_order_ws(self, id: str, symbol: Str = None, params={}):
218
+ """
219
+ Cancels an open order
220
+ :see: https://www.gate.io/docs/developers/apiv4/ws/en/#order-cancel
221
+ :see: https://www.gate.io/docs/developers/futures/ws/en/#order-cancel
222
+ :param str id: Order id
223
+ :param str symbol: Unified market symbol
224
+ :param dict [params]: Parameters specified by the exchange api
225
+ :param bool [params.stop]: True if the order to be cancelled is a trigger order
226
+ :returns: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
227
+ """
228
+ await self.load_markets()
229
+ market = None if (symbol is None) else self.market(symbol)
230
+ stop = self.safe_value_2(params, 'is_stop_order', 'stop', False)
231
+ params = self.omit(params, ['is_stop_order', 'stop'])
232
+ type, query = self.handle_market_type_and_params('cancelOrder', market, params)
233
+ request, requestParams = self.spotOrderPrepareRequest(market, stop, query) if (type == 'spot' or type == 'margin') else self.prepareRequest(market, type, query)
234
+ messageType = self.get_type_by_market(market)
235
+ channel = messageType + '.order_cancel'
236
+ url = self.get_url_by_market(market)
237
+ await self.authenticate(url, messageType)
238
+ request['order_id'] = str(id)
239
+ res = await self.request_private(url, self.extend(request, requestParams), channel)
240
+ return self.parse_order(res, market)
241
+
242
+ async def edit_order_ws(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
243
+ """
244
+ edit a trade order, gate currently only supports the modification of the price or amount fields
245
+ :see: https://www.gate.io/docs/developers/apiv4/ws/en/#order-amend
246
+ :see: https://www.gate.io/docs/developers/futures/ws/en/#order-amend
247
+ :param str id: order id
248
+ :param str symbol: unified symbol of the market to create an order in
249
+ :param str type: 'market' or 'limit'
250
+ :param str side: 'buy' or 'sell'
251
+ :param float amount: how much of the currency you want to trade in units of the base currency
252
+ :param float [price]: the price at which the order is to be fullfilled, in units of the base currency, ignored in market orders
253
+ :param dict [params]: extra parameters specific to the exchange API endpoint
254
+ :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
255
+ """
256
+ await self.load_markets()
257
+ market = self.market(symbol)
258
+ extendedRequest = self.edit_order_request(id, symbol, type, side, amount, price, params)
259
+ messageType = self.get_type_by_market(market)
260
+ channel = messageType + '.order_amend'
261
+ url = self.get_url_by_market(market)
262
+ await self.authenticate(url, messageType)
263
+ rawOrder = await self.request_private(url, extendedRequest, channel)
264
+ return self.parse_order(rawOrder, market)
265
+
266
+ async def fetch_order_ws(self, id: str, symbol: Str = None, params={}):
267
+ """
268
+ Retrieves information on an order
269
+ :see: https://www.gate.io/docs/developers/apiv4/ws/en/#order-status
270
+ :see: https://www.gate.io/docs/developers/futures/ws/en/#order-status
271
+ :param str id: Order id
272
+ :param str symbol: Unified market symbol, *required for spot and margin*
273
+ :param dict [params]: Parameters specified by the exchange api
274
+ :param bool [params.stop]: True if the order being fetched is a trigger order
275
+ :param str [params.marginMode]: 'cross' or 'isolated' - marginMode for margin trading if not provided self.options['defaultMarginMode'] is used
276
+ :param str [params.type]: 'spot', 'swap', or 'future', if not provided self.options['defaultMarginMode'] is used
277
+ :param str [params.settle]: 'btc' or 'usdt' - settle currency for perpetual swap and future - market settle currency is used if symbol is not None, default="usdt" for swap and "btc" for future
278
+ :returns: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
279
+ """
280
+ await self.load_markets()
281
+ market = None if (symbol is None) else self.market(symbol)
282
+ request, requestParams = self.fetchOrderRequest(id, symbol, params)
283
+ messageType = self.get_type_by_market(market)
284
+ channel = messageType + '.order_status'
285
+ url = self.get_url_by_market(market)
286
+ await self.authenticate(url, messageType)
287
+ rawOrder = await self.request_private(url, self.extend(request, requestParams), channel)
288
+ return self.parse_order(rawOrder, market)
289
+
290
+ async def fetch_open_orders_ws(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
291
+ """
292
+ fetch all unfilled currently open orders
293
+ :see: https://www.gate.io/docs/developers/futures/ws/en/#order-list
294
+ :param str symbol: unified market symbol
295
+ :param int [since]: the earliest time in ms to fetch open orders for
296
+ :param int [limit]: the maximum number of open orders structures to retrieve
297
+ :param dict [params]: extra parameters specific to the exchange API endpoint
298
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
299
+ """
300
+ return await self.fetch_orders_by_status_ws('open', symbol, since, limit, params)
301
+
302
+ async def fetch_closed_orders_ws(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
303
+ """
304
+ fetches information on multiple closed orders made by the user
305
+ :see: https://www.gate.io/docs/developers/futures/ws/en/#order-list
306
+ :param str symbol: unified market symbol of the market orders were made in
307
+ :param int [since]: the earliest time in ms to fetch orders for
308
+ :param int [limit]: the maximum number of order structures to retrieve
309
+ :param dict [params]: extra parameters specific to the exchange API endpoint
310
+ :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
311
+ """
312
+ return await self.fetch_orders_by_status_ws('finished', symbol, since, limit, params)
313
+
314
+ async def fetch_orders_by_status_ws(self, status: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
315
+ """
316
+ :see: https://www.gate.io/docs/developers/futures/ws/en/#order-list
317
+ fetches information on multiple orders made by the user by status
318
+ :param str symbol: unified market symbol of the market orders were made in
319
+ :param int|None [since]: the earliest time in ms to fetch orders for
320
+ :param int|None [limit]: the maximum number of order structures to retrieve
321
+ :param dict [params]: extra parameters specific to the exchange API endpoint
322
+ :param int [params.orderId]: order id to begin at
323
+ :param int [params.limit]: the maximum number of order structures to retrieve
324
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
325
+ """
326
+ await self.load_markets()
327
+ market = None
328
+ if symbol is not None:
329
+ market = self.market(symbol)
330
+ symbol = market['symbol']
331
+ if market['swap'] is not True:
332
+ raise NotSupported(self.id + ' fetchOrdersByStatusWs is only supported by swap markets. Use rest API for other markets')
333
+ request, requestParams = self.fetchOrdersByStatusRequest(status, symbol, since, limit, params)
334
+ newRequest = self.omit(request, ['settle'])
335
+ messageType = self.get_type_by_market(market)
336
+ channel = messageType + '.order_list'
337
+ url = self.get_url_by_market(market)
338
+ await self.authenticate(url, messageType)
339
+ rawOrders = await self.request_private(url, self.extend(newRequest, requestParams), channel)
340
+ orders = self.parse_orders(rawOrders, market)
341
+ return self.filter_by_symbol_since_limit(orders, symbol, since, limit)
342
+
107
343
  async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
108
344
  """
109
345
  watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
@@ -1135,37 +1371,55 @@ class gate(ccxt.async_support.gate):
1135
1371
  })
1136
1372
 
1137
1373
  def handle_error_message(self, client: Client, message):
1138
- # {
1139
- # "time": 1647274664,
1140
- # "channel": "futures.orders",
1141
- # "event": "subscribe",
1142
- # "error": {code: 2, message: "unknown contract BTC_USDT_20220318"},
1143
- # }
1144
- # {
1145
- # "time": 1647276473,
1146
- # "channel": "futures.orders",
1147
- # "event": "subscribe",
1148
- # "error": {
1149
- # "code": 4,
1150
- # "message": "{"label":"INVALID_KEY","message":"Invalid key provided"}\n"
1151
- # },
1152
- # "result": null
1153
- # }
1154
- error = self.safe_value(message, 'error')
1155
- code = self.safe_integer(error, 'code')
1156
- id = self.safe_string(message, 'id')
1157
- if id is None:
1158
- return False
1159
- if code is not None:
1374
+ #
1375
+ # {
1376
+ # "time": 1647274664,
1377
+ # "channel": "futures.orders",
1378
+ # "event": "subscribe",
1379
+ # "error": {code: 2, message: "unknown contract BTC_USDT_20220318"},
1380
+ # }
1381
+ # {
1382
+ # "time": 1647276473,
1383
+ # "channel": "futures.orders",
1384
+ # "event": "subscribe",
1385
+ # "error": {
1386
+ # "code": 4,
1387
+ # "message": "{"label":"INVALID_KEY","message":"Invalid key provided"}\n"
1388
+ # },
1389
+ # "result": null
1390
+ # }
1391
+ # {
1392
+ # header: {
1393
+ # response_time: '1718551891329',
1394
+ # status: '400',
1395
+ # channel: 'spot.order_place',
1396
+ # event: 'api',
1397
+ # client_id: '81.34.68.6-0xc16375e2c0',
1398
+ # conn_id: '9539116e0e09678f'
1399
+ # },
1400
+ # data: {errs: {label: 'AUTHENTICATION_FAILED', message: 'Not login'}},
1401
+ # request_id: '10406147'
1402
+ # }
1403
+ #
1404
+ data = self.safe_dict(message, 'data')
1405
+ errs = self.safe_dict(data, 'errs')
1406
+ error = self.safe_dict(message, 'error', errs)
1407
+ code = self.safe_string_2(error, 'code', 'label')
1408
+ id = self.safe_string_2(message, 'id', 'requestId')
1409
+ if error is not None:
1160
1410
  messageHash = self.safe_string(client.subscriptions, id)
1161
- if messageHash is not None:
1162
- try:
1163
- self.throw_exactly_matched_exception(self.exceptions['ws']['exact'], code, self.json(message))
1164
- except Exception as e:
1165
- client.reject(e, messageHash)
1166
- if messageHash in client.subscriptions:
1167
- del client.subscriptions[messageHash]
1168
- del client.subscriptions[id]
1411
+ try:
1412
+ self.throw_exactly_matched_exception(self.exceptions['ws']['exact'], code, self.json(message))
1413
+ self.throw_exactly_matched_exception(self.exceptions['exact'], code, self.json(errs))
1414
+ errorMessage = self.safe_string(error, 'message', self.safe_string(errs, 'message'))
1415
+ self.throw_broadly_matched_exception(self.exceptions['ws']['broad'], errorMessage, self.json(message))
1416
+ raise ExchangeError(self.json(message))
1417
+ except Exception as e:
1418
+ client.reject(e, messageHash)
1419
+ if (messageHash is not None) and (messageHash in client.subscriptions):
1420
+ del client.subscriptions[messageHash]
1421
+ if id is not None:
1422
+ del client.subscriptions[id]
1169
1423
  return True
1170
1424
  return False
1171
1425
 
@@ -1302,6 +1556,17 @@ class gate(ccxt.async_support.gate):
1302
1556
  method = self.safe_value(v4Methods, channelType)
1303
1557
  if method is not None:
1304
1558
  method(client, message)
1559
+ requestId = self.safe_string(message, 'request_id')
1560
+ if requestId == 'authenticated':
1561
+ self.handle_authentication_message(client, message)
1562
+ return
1563
+ if requestId is not None:
1564
+ data = self.safe_dict(message, 'data')
1565
+ # use safeValue may be Array or an Object
1566
+ result = self.safe_value(data, 'result')
1567
+ ack = self.safe_bool(message, 'ack')
1568
+ if ack is not True:
1569
+ client.resolve(result, requestId)
1305
1570
 
1306
1571
  def get_url_by_market(self, market):
1307
1572
  baseUrl = self.urls['api'][market['type']]
@@ -1310,7 +1575,7 @@ class gate(ccxt.async_support.gate):
1310
1575
  else:
1311
1576
  return baseUrl
1312
1577
 
1313
- def get_type_by_market(self, market):
1578
+ def get_type_by_market(self, market: Market):
1314
1579
  if market['spot']:
1315
1580
  return 'spot'
1316
1581
  elif market['option']:
@@ -1318,7 +1583,7 @@ class gate(ccxt.async_support.gate):
1318
1583
  else:
1319
1584
  return 'futures'
1320
1585
 
1321
- def get_url_by_market_type(self, type, isInverse=False):
1586
+ def get_url_by_market_type(self, type: MarketType, isInverse=False):
1322
1587
  api = self.urls['api']
1323
1588
  url = api[type]
1324
1589
  if (type == 'swap') or (type == 'future'):
@@ -1377,6 +1642,49 @@ class gate(ccxt.async_support.gate):
1377
1642
  message = self.extend(request, params)
1378
1643
  return await self.watch_multiple(url, messageHashes, message, messageHashes)
1379
1644
 
1645
+ async def authenticate(self, url, messageType):
1646
+ channel = messageType + '.login'
1647
+ client = self.client(url)
1648
+ messageHash = 'authenticated'
1649
+ future = client.future(messageHash)
1650
+ authenticated = self.safe_value(client.subscriptions, messageHash)
1651
+ if authenticated is None:
1652
+ return await self.request_private(url, {}, channel, messageHash)
1653
+ return future
1654
+
1655
+ def handle_authentication_message(self, client: Client, message):
1656
+ messageHash = 'authenticated'
1657
+ future = self.safe_value(client.futures, messageHash)
1658
+ future.resolve(True)
1659
+
1660
+ async def request_private(self, url, reqParams, channel, requestId: Str = None):
1661
+ self.check_required_credentials()
1662
+ # uid is required for some subscriptions only so it's not a part of required credentials
1663
+ event = 'api'
1664
+ if requestId is None:
1665
+ reqId = self.request_id()
1666
+ requestId = str(reqId)
1667
+ messageHash = requestId
1668
+ time = self.seconds()
1669
+ # unfortunately, PHP demands double quotes for the escaped newline symbol
1670
+ signatureString = "\n".join([event, channel, self.json(reqParams), str(time)]) # eslint-disable-line quotes
1671
+ signature = self.hmac(self.encode(signatureString), self.encode(self.secret), hashlib.sha512, 'hex')
1672
+ payload: dict = {
1673
+ 'req_id': requestId,
1674
+ 'timestamp': str(time),
1675
+ 'api_key': self.apiKey,
1676
+ 'signature': signature,
1677
+ 'req_param': reqParams,
1678
+ }
1679
+ request: dict = {
1680
+ 'id': requestId,
1681
+ 'time': time,
1682
+ 'channel': channel,
1683
+ 'event': event,
1684
+ 'payload': payload,
1685
+ }
1686
+ return await self.watch(url, messageHash, request, messageHash)
1687
+
1380
1688
  async def subscribe_private(self, url, messageHash, payload, channel, params, requiresUid=False):
1381
1689
  self.check_required_credentials()
1382
1690
  # uid is required for some subscriptions only so it's not a part of required credentials
@@ -1402,7 +1710,7 @@ class gate(ccxt.async_support.gate):
1402
1710
  'id': requestId,
1403
1711
  'time': time,
1404
1712
  'channel': channel,
1405
- 'event': 'subscribe',
1713
+ 'event': event,
1406
1714
  'auth': auth,
1407
1715
  }
1408
1716
  if payload is not None:
@@ -66,4 +66,4 @@ def test_order_book(exchange, skipped_properties, method, orderbook, symbol):
66
66
  first_bid = exchange.safe_string(bids[0], 0)
67
67
  first_ask = exchange.safe_string(asks[0], 0)
68
68
  # check bid-ask spread
69
- assert Precise.string_lt(first_bid, first_ask), 'bids[0][0] (' + first_ask + ') should be < than asks[0][0] (' + first_ask + ')' + log_text
69
+ assert Precise.string_lt(first_bid, first_ask), 'bids[0][0] (' + first_bid + ') should be < than asks[0][0] (' + first_ask + ')' + log_text
ccxt/woo.py CHANGED
@@ -2361,6 +2361,10 @@ class woo(Exchange, ImplicitAPI):
2361
2361
  url += access + '/' + pathWithParams
2362
2362
  if params:
2363
2363
  url += '?' + self.urlencode(params)
2364
+ elif access == 'pub':
2365
+ url += pathWithParams
2366
+ if params:
2367
+ url += '?' + self.urlencode(params)
2364
2368
  else:
2365
2369
  self.check_required_credentials()
2366
2370
  if method == 'POST' and (path == 'algo/order' or path == 'order'):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ccxt
3
- Version: 4.3.47
3
+ Version: 4.3.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
@@ -268,13 +268,13 @@ console.log(version, Object.keys(exchanges));
268
268
 
269
269
  All-in-one browser bundle (dependencies included), served from a CDN of your choice:
270
270
 
271
- * jsDelivr: https://cdn.jsdelivr.net/npm/ccxt@4.3.47/dist/ccxt.browser.min.js
272
- * unpkg: https://unpkg.com/ccxt@4.3.47/dist/ccxt.browser.min.js
271
+ * jsDelivr: https://cdn.jsdelivr.net/npm/ccxt@4.3.49/dist/ccxt.browser.min.js
272
+ * unpkg: https://unpkg.com/ccxt@4.3.49/dist/ccxt.browser.min.js
273
273
 
274
274
  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.
275
275
 
276
276
  ```HTML
277
- <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/ccxt@4.3.47/dist/ccxt.browser.min.js"></script>
277
+ <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/ccxt@4.3.49/dist/ccxt.browser.min.js"></script>
278
278
  ```
279
279
 
280
280
  Creates a global `ccxt` object: