crypto-ws-api 2.0.6__tar.gz → 2.0.8__tar.gz

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.
@@ -1,3 +1,11 @@
1
+ ## 2.0.8 - 2024-03-31
2
+ ### Fix
3
+ * tons log records: `websockets socket.send() raised exception.`
4
+
5
+ ## 2.0.7 - 2024-03-25
6
+ ### Update
7
+ * Refactoring and some minor fixes
8
+
1
9
  ## 2.0.6 - 2024-01-05
2
10
  ### Update
3
11
  * replacing json with ujson to improve performance
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: crypto-ws-api
3
- Version: 2.0.6
3
+ Version: 2.0.8
4
4
  Summary: Crypto WS API connector for ASYNC requests
5
5
  Author-email: Jerry Fedorenko <jerry.fedorenko@yahoo.com>
6
6
  Requires-Python: >=3.8
@@ -37,7 +37,7 @@ For :heavy_check_mark:Binance, :heavy_check_mark:OKX, :heavy_check_mark:Bitfinex
37
37
 
38
38
  ## Features
39
39
  Lightweight and efficient solution to utilize of all available methods** provided through the:
40
- * [Binance Websocket API v3](https://developers.binance.com/docs/binance-trading-api/websocket_api)
40
+ * [Binance Websocket API v3](https://binance-docs.github.io/apidocs/websocket_api/en)
41
41
  * [OKX Websocket API v5](https://www.okx.com/docs-v5/en/#overview-websocket)
42
42
  * [Bitfinex Websocket Inputs](https://docs.bitfinex.com/reference/ws-auth-input)
43
43
 
@@ -222,7 +222,5 @@ If you send a request in this state, the answer will be `None`
222
222
  *In any case, you are protected from exceeding limits and blocking for this reason*
223
223
 
224
224
  ## Donate
225
- *BNB*, *BUSD*, *USDT* (BEP20) 0x5b52c6ba862b11318616ee6cef64388618318b92
226
-
227
- *USDT* (TRC20) TP1Y43dpY7rrRyTSLaSKDZmFirqvRcpopC
225
+ *USDT* (TRC20) TU3kagV9kxbjuUmEi6bUym5MTXjeM7Tm8K
228
226
 
@@ -17,7 +17,7 @@ For :heavy_check_mark:Binance, :heavy_check_mark:OKX, :heavy_check_mark:Bitfinex
17
17
 
18
18
  ## Features
19
19
  Lightweight and efficient solution to utilize of all available methods** provided through the:
20
- * [Binance Websocket API v3](https://developers.binance.com/docs/binance-trading-api/websocket_api)
20
+ * [Binance Websocket API v3](https://binance-docs.github.io/apidocs/websocket_api/en)
21
21
  * [OKX Websocket API v5](https://www.okx.com/docs-v5/en/#overview-websocket)
22
22
  * [Bitfinex Websocket Inputs](https://docs.bitfinex.com/reference/ws-auth-input)
23
23
 
@@ -202,6 +202,4 @@ If you send a request in this state, the answer will be `None`
202
202
  *In any case, you are protected from exceeding limits and blocking for this reason*
203
203
 
204
204
  ## Donate
205
- *BNB*, *BUSD*, *USDT* (BEP20) 0x5b52c6ba862b11318616ee6cef64388618318b92
206
-
207
- *USDT* (TRC20) TP1Y43dpY7rrRyTSLaSKDZmFirqvRcpopC
205
+ *USDT* (TRC20) TU3kagV9kxbjuUmEi6bUym5MTXjeM7Tm8K
@@ -13,7 +13,7 @@ __maintainer__ = "Jerry Fedorenko"
13
13
  __contact__ = "https://github.com/DogsTailFarmer"
14
14
  __email__ = "jerry.fedorenko@yahoo.com"
15
15
  __credits__ = ["https://github.com/DanyaSWorlD"]
16
- __version__ = "2.0.6"
16
+ __version__ = "2.0.8"
17
17
 
18
18
  from pathlib import Path
19
19
  import shutil
@@ -21,7 +21,7 @@ from platformdirs import user_config_path
21
21
 
22
22
 
23
23
  TIMEOUT = 5 # sec timeout for WSS initialization and get response
24
- DELAY = 0.1 # sec delay in while loop
24
+ DELAY = 0.1 # sec delay in keepalive loop
25
25
  # Maximum str size for unique query ID
26
26
  ID_LEN_LIMIT = {
27
27
  "binance": 36,
@@ -104,7 +104,7 @@ class UserWSS:
104
104
  elif self.exchange in ['okx', 'bitfinex']:
105
105
  self._response_pool[res.get('id') or self.ws_id] = res.get('data') or res
106
106
  self.in_event.set()
107
- await asyncio.sleep(0)
107
+ await asyncio.sleep(0)
108
108
  else:
109
109
  logger.warning(f"UserWSS: {self.ws_id}: {msg}")
110
110
  await self.stop()
@@ -171,12 +171,12 @@ class UserWSS:
171
171
  await self._ws.send(
172
172
  json.dumps(self.compose_request(_id, _api_key, method, params, _signed))
173
173
  )
174
+ await asyncio.sleep(0)
174
175
  try:
175
176
  res = await asyncio.wait_for(self._response_distributor(_id), timeout=TIMEOUT)
176
- except asyncio.TimeoutError:
177
+ except asyncio.exceptions.TimeoutError:
177
178
  logger.warning(f"UserWSS: get response timeout error: {self.ws_id}")
178
179
  await self.stop()
179
- return None
180
180
  except asyncio.CancelledError:
181
181
  pass # Task cancellation should not be logged as an error
182
182
  else:
@@ -193,53 +193,59 @@ class UserWSS:
193
193
  return self._response_pool.pop(f"NoneResponse{self.ws_id}", None)
194
194
 
195
195
  def compose_request(self, _id, api_key, method, params, signed):
196
- req = None
197
196
  if self.exchange == "binance":
198
- req = {"id": _id, "method": method}
199
- if (api_key or signed) and not params:
200
- params = {}
201
- if api_key:
202
- params["apiKey"] = self._api_key
203
- if signed:
204
- params["timestamp"] = int(time.time() * 1000)
205
- payload = '&'.join(f"{key}={value}" for key, value in dict(sorted(params.items())).items())
206
- params["signature"] = generate_signature('binance_ws', self._api_secret, payload)
207
- if params:
208
- req["params"] = params
197
+ return self._compose_binance_request(_id, api_key, method, params, signed)
209
198
  elif self.exchange == "okx":
210
- if method == CONST_3:
211
- # https://www.okx.com/docs-v5/en/?python#overview-websocket-connect
212
- ts = int(time.time())
213
- signature_payload = f"{ts}GET/users/self/verify"
214
- signature = generate_signature(self.exchange, self._api_secret, signature_payload)
215
- # Login on account
216
- req = {"op": 'login',
217
- "args": [{"apiKey": self._api_key,
218
- "passphrase": self._passphrase,
219
- "timestamp": ts,
220
- "sign": signature}
221
- ]
222
- }
223
- else:
224
- req = {"id": _id, "op": method, "args": params if isinstance(params, list) else [params]}
199
+ return self._compose_okx_request(_id, method, params)
225
200
  elif self.exchange == 'bitfinex':
226
- if method == CONST_3:
227
- ts = int(time.time() * 1000)
228
- data = f"AUTH{ts}"
229
- req = {
230
- 'event': "auth",
231
- 'apiKey': self._api_key,
232
- 'authSig': generate_signature(self.exchange, self._api_secret, data),
233
- 'authPayload': data,
234
- 'authNonce': ts,
235
- 'filter': ['trading']
236
- }
237
- else:
238
- if method == 'on':
239
- params.update({"meta": {"aff_code": "v_4az2nCP"}})
240
- req = [0, method, _id, params]
201
+ return self._compose_bitfinex_request(_id, method, params)
202
+ else:
203
+ raise ValueError(f"Unsupported exchange: {self.exchange}")
204
+
205
+ def _compose_binance_request(self, _id, api_key, method, params, signed):
206
+ req = {"id": _id, "method": method}
207
+ params = params or {}
208
+ if api_key:
209
+ params["apiKey"] = self._api_key
210
+ if signed:
211
+ params["timestamp"] = int(time.time() * 1000)
212
+ payload = '&'.join(f"{key}={value}" for key, value in sorted(params.items()))
213
+ params["signature"] = generate_signature('binance_ws', self._api_secret, payload)
214
+ if params:
215
+ req["params"] = params
241
216
  return req
242
217
 
218
+ def _compose_okx_request(self, _id, method, params):
219
+ if method == CONST_3:
220
+ ts = int(time.time())
221
+ signature_payload = f"{ts}GET/users/self/verify"
222
+ signature = generate_signature(self.exchange, self._api_secret, signature_payload)
223
+ return {
224
+ "op": 'login',
225
+ "args": [
226
+ {"apiKey": self._api_key, "passphrase": self._passphrase, "timestamp": ts, "sign": signature}
227
+ ]
228
+ }
229
+ else:
230
+ return {"id": _id, "op": method, "args": params if isinstance(params, list) else [params]}
231
+
232
+ def _compose_bitfinex_request(self, _id, method, params):
233
+ if method == CONST_3:
234
+ ts = int(time.time() * 1000)
235
+ data = f"AUTH{ts}"
236
+ return {
237
+ 'event': "auth",
238
+ 'apiKey': self._api_key,
239
+ 'authSig': generate_signature(self.exchange, self._api_secret, data),
240
+ 'authPayload': data,
241
+ 'authNonce': ts,
242
+ 'filter': ['trading']
243
+ }
244
+ else:
245
+ if method == 'on':
246
+ params.update({"meta": {"aff_code": "v_4az2nCP"}})
247
+ return [0, method, _id, params]
248
+
243
249
  async def _keepalive(self, interval=10):
244
250
  while self.operational_status is not None:
245
251
  if self.request_limit_reached and (int(time.time() * 1000) - self._retry_after >= 0):
@@ -291,40 +297,60 @@ class UserWSS:
291
297
  elif self.exchange == 'bitfinex':
292
298
  return await self.bitfinex_error_handle(msg)
293
299
 
300
+ #region BitfinexErrorHandle
294
301
  async def bitfinex_error_handle(self, msg):
295
302
  if isinstance(msg, dict):
296
- if msg.get('event') == 'info' and not msg.get('platform', {}).get('status'):
297
- logger.warning(f"UserWSS Bitfinex platform in maintenance mode: {msg}")
298
- await self.stop()
299
- if msg.get('event') == 'auth' and msg.get('status') == "OK":
300
- return msg
301
- if msg.get('event') == 'info' and msg.get('version') != 2:
302
- logger.critical('Bitfinex WSS platform: version change detected')
303
- # sourcery skip: merge-nested-ifs
304
- if msg.get('code'):
305
- if msg.get('code') == 10305:
306
- logger.warning('UserWSS Bitfinex: Reached limit of open channels')
307
- self._retry_after = int((time.time() + TIMEOUT) * 1000)
308
- self.request_limit_reached = True
309
- logger.warning(f"Malformed request for {self.ws_id}: {msg}")
310
- return None
311
- return 'pass'
303
+ return await self._handle_dict_message(msg)
312
304
  elif isinstance(msg, list) and msg[1] == 'n' and msg[2][1] in ('on-req', 'oc-req', 'oc_multi-req'):
313
- msg = {"id": msg[2][2],
314
- "data": [msg[2][0],
315
- msg[2][1],
316
- None,
317
- None,
318
- [msg[2][4]] if msg[2][1] == 'on-req' else msg[2][4],
319
- None,
320
- msg[2][6],
321
- msg[2][7]
322
- ]
323
- }
324
- return msg
305
+ return self._transform_list_message(msg)
325
306
  else:
326
307
  return 'pass'
327
308
 
309
+ async def _handle_dict_message(self, msg):
310
+ event = msg.get('event')
311
+ if event == 'info':
312
+ return await self._handle_info_event(msg)
313
+ elif event == 'auth':
314
+ return msg if msg.get('status') == "OK" else None
315
+ elif msg.get('code'):
316
+ return await self._handle_error_code(msg)
317
+ return 'pass'
318
+
319
+ async def _handle_info_event(self, msg):
320
+ if not msg.get('platform', {}).get('status'):
321
+ logger.warning(f"UserWSS Bitfinex platform in maintenance mode: {msg}")
322
+ await self.stop()
323
+ elif msg.get('version') != 2:
324
+ logger.critical('Bitfinex WSS platform: version change detected')
325
+ return 'pass'
326
+
327
+ async def _handle_error_code(self, msg):
328
+ code = msg.get('code')
329
+ if code == 10305:
330
+ logger.warning('UserWSS Bitfinex: Reached limit of open channels')
331
+ self._retry_after = int((time.time() + TIMEOUT) * 1000)
332
+ self.request_limit_reached = True
333
+ else:
334
+ logger.warning(f"Malformed request for {self.ws_id}: {msg}")
335
+ return None
336
+
337
+ @staticmethod
338
+ def _transform_list_message(msg):
339
+ return {
340
+ "id": msg[2][2],
341
+ "data": [
342
+ msg[2][0],
343
+ msg[2][1],
344
+ None,
345
+ None,
346
+ [msg[2][4]] if msg[2][1] == 'on-req' else msg[2][4],
347
+ None,
348
+ msg[2][6],
349
+ msg[2][7]
350
+ ]
351
+ }
352
+ #endregion
353
+
328
354
  async def okx_error_handle(self, msg):
329
355
  if msg.get('code') == '1':
330
356
  logger.warning(f"Operation failed: {msg}")
File without changes