exchanges-wrapper 2.1.10__tar.gz → 2.1.12__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.
Files changed (20) hide show
  1. {exchanges_wrapper-2.1.10 → exchanges_wrapper-2.1.12}/PKG-INFO +7 -2
  2. {exchanges_wrapper-2.1.10 → exchanges_wrapper-2.1.12}/README.md +5 -0
  3. {exchanges_wrapper-2.1.10 → exchanges_wrapper-2.1.12}/exchanges_wrapper/__init__.py +1 -1
  4. {exchanges_wrapper-2.1.10 → exchanges_wrapper-2.1.12}/exchanges_wrapper/client.py +4 -16
  5. {exchanges_wrapper-2.1.10 → exchanges_wrapper-2.1.12}/exchanges_wrapper/exch_srv.py +47 -36
  6. {exchanges_wrapper-2.1.10 → exchanges_wrapper-2.1.12}/pyproject.toml +1 -1
  7. {exchanges_wrapper-2.1.10 → exchanges_wrapper-2.1.12}/LICENSE.md +0 -0
  8. {exchanges_wrapper-2.1.10 → exchanges_wrapper-2.1.12}/exchanges_wrapper/definitions.py +0 -0
  9. {exchanges_wrapper-2.1.10 → exchanges_wrapper-2.1.12}/exchanges_wrapper/errors.py +0 -0
  10. {exchanges_wrapper-2.1.10 → exchanges_wrapper-2.1.12}/exchanges_wrapper/events.py +0 -0
  11. {exchanges_wrapper-2.1.10 → exchanges_wrapper-2.1.12}/exchanges_wrapper/exch_srv_cfg.toml.template +0 -0
  12. {exchanges_wrapper-2.1.10 → exchanges_wrapper-2.1.12}/exchanges_wrapper/http_client.py +0 -0
  13. {exchanges_wrapper-2.1.10 → exchanges_wrapper-2.1.12}/exchanges_wrapper/lib.py +0 -0
  14. {exchanges_wrapper-2.1.10 → exchanges_wrapper-2.1.12}/exchanges_wrapper/martin/__init__.py +0 -0
  15. {exchanges_wrapper-2.1.10 → exchanges_wrapper-2.1.12}/exchanges_wrapper/parsers/bitfinex_parser.py +0 -0
  16. {exchanges_wrapper-2.1.10 → exchanges_wrapper-2.1.12}/exchanges_wrapper/parsers/bybit_parser.py +0 -0
  17. {exchanges_wrapper-2.1.10 → exchanges_wrapper-2.1.12}/exchanges_wrapper/parsers/huobi_parser.py +0 -0
  18. {exchanges_wrapper-2.1.10 → exchanges_wrapper-2.1.12}/exchanges_wrapper/parsers/okx_parser.py +0 -0
  19. {exchanges_wrapper-2.1.10 → exchanges_wrapper-2.1.12}/exchanges_wrapper/proto/martin.proto +0 -0
  20. {exchanges_wrapper-2.1.10 → exchanges_wrapper-2.1.12}/exchanges_wrapper/web_sockets.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: exchanges-wrapper
3
- Version: 2.1.10
3
+ Version: 2.1.12
4
4
  Summary: REST API and WebSocket asyncio wrapper with grpc powered multiplexer server
5
5
  Author-email: Thomas Marchand <thomas.marchand@tuta.io>, Jerry Fedorenko <jerry.fedorenko@yahoo.com>
6
6
  Requires-Python: >=3.9
@@ -11,7 +11,7 @@ Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Operating System :: Unix
12
12
  Classifier: Operating System :: Microsoft :: Windows
13
13
  Classifier: Operating System :: MacOS
14
- Requires-Dist: crypto-ws-api==2.0.9
14
+ Requires-Dist: crypto-ws-api==2.0.11
15
15
  Requires-Dist: grpcio==1.62.0
16
16
  Requires-Dist: pyotp~=2.9.0
17
17
  Requires-Dist: simplejson==3.19.2
@@ -108,6 +108,11 @@ pip install -U exchanges-wrapper
108
108
 
109
109
  #### Start server
110
110
  * Run in terminal window
111
+ ```
112
+ exchanges-wrapper-init
113
+ ```
114
+ and
115
+
111
116
  ```
112
117
  exchanges-wrapper-srv
113
118
  ```
@@ -82,6 +82,11 @@ pip install -U exchanges-wrapper
82
82
 
83
83
  #### Start server
84
84
  * Run in terminal window
85
+ ```
86
+ exchanges-wrapper-init
87
+ ```
88
+ and
89
+
85
90
  ```
86
91
  exchanges-wrapper-srv
87
92
  ```
@@ -12,7 +12,7 @@ __maintainer__ = "Jerry Fedorenko"
12
12
  __contact__ = "https://github.com/DogsTailFarmer"
13
13
  __email__ = "jerry.fedorenko@yahoo.com"
14
14
  __credits__ = ["https://github.com/DanyaSWorlD"]
15
- __version__ = "2.1.10"
15
+ __version__ = "2.1.12"
16
16
 
17
17
  from pathlib import Path
18
18
  import shutil
@@ -109,6 +109,8 @@ class Client:
109
109
  self.ledgers_id = []
110
110
  self.ts_start = {}
111
111
  self.tasks = set()
112
+ self.request_event = asyncio.Event()
113
+ self.request_event.set()
112
114
 
113
115
  def tasks_manage(self, coro):
114
116
  _t = asyncio.create_task(coro)
@@ -377,24 +379,10 @@ class Client:
377
379
  # MARKET DATA ENDPOINTS
378
380
 
379
381
  # https://github.com/binance/binance-spot-api-docs/blob/master/rest-api.md#order-book
380
- async def fetch_order_book(self, symbol, precision='P0', limit=100):
382
+ async def fetch_order_book(self, symbol, precision='P0'):
381
383
  self.assert_symbol(symbol)
382
- valid_limits = []
383
- if self.exchange == 'binance':
384
- valid_limits = [5, 10, 20, 50, 100, 500, 1000, 5000]
385
- elif self.exchange == 'bitfinex':
386
- valid_limits = [1, 25, 100]
387
- elif self.exchange == 'huobi':
388
- valid_limits = [5, 10, 20]
389
- elif self.exchange == 'okx':
390
- valid_limits = [1, 5, 10, 20, 50, 100, 400]
391
- elif self.exchange == 'bybit':
392
- valid_limits = range(1, 51)
384
+ limit = 1 if self.exchange in ('bitfinex', 'okx', 'bybit') else 5
393
385
  binance_res = {}
394
- if limit not in valid_limits:
395
- raise ValueError(
396
- f"{limit} is not a valid limit. Valid limits: {valid_limits}"
397
- )
398
386
  if self.exchange == 'binance':
399
387
  binance_res = await self.http.send_api_call(
400
388
  "/api/v3/depth",
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env python3
2
2
  # -*- coding: utf-8 -*-
3
+
3
4
  import grpclib.exceptions
4
5
 
5
- from exchanges_wrapper import __version__ as ver_ew
6
- from crypto_ws_api import __version__ as ver_cw
6
+ from exchanges_wrapper import __version__ as VER_EW
7
+ from crypto_ws_api import __version__ as VER_CW
7
8
  import time
8
9
  import weakref
9
10
  import gc
@@ -148,10 +149,10 @@ class Martin(mr.MartinBase):
148
149
  rate_limit_reached_time = None
149
150
  rate_limiter = None
150
151
 
151
- async def rate_limit_control(self, client, _call_from='default'):
152
- if client.client.exchange == 'bitfinex':
153
- rate_limit_interval = REST_RATE_LIMIT_INTERVAL.get(client.client.exchange, {}).get(_call_from, 0)
154
- ts_diff = time.time() - client.ts_rlc
152
+ async def rate_limit_control(self, exchange, ts_rlc, _call_from='default'):
153
+ if exchange == 'bitfinex':
154
+ rate_limit_interval = REST_RATE_LIMIT_INTERVAL.get(exchange, {}).get(_call_from, 0)
155
+ ts_diff = time.time() - ts_rlc
155
156
  if ts_diff < rate_limit_interval:
156
157
  sleep_duration = rate_limit_interval - ts_diff
157
158
  await asyncio.sleep(sleep_duration)
@@ -201,7 +202,7 @@ class Martin(mr.MartinBase):
201
202
  Martin.rate_limiter = max(Martin.rate_limiter or 0, request.rate_limiter)
202
203
  return mr.OpenClientConnectionId(
203
204
  client_id=client_id,
204
- srv_version=f"{ver_cw}:{ver_ew}",
205
+ srv_version=f"{VER_CW}:{VER_EW}",
205
206
  exchange=open_client.client.exchange,
206
207
  real_market=open_client.real_market
207
208
  )
@@ -231,12 +232,19 @@ class Martin(mr.MartinBase):
231
232
  if hasattr(request, 'client_order_id'):
232
233
  msg_header += f"({request.client_order_id}):"
233
234
  if rate_limit:
234
- await self.rate_limit_control(open_client)
235
+ await self.rate_limit_control(client.exchange, open_client.ts_rlc)
236
+ if client.exchange == 'bitfinex':
237
+ await client.request_event.wait()
238
+ client.request_event.clear()
235
239
  try:
236
- res = await getattr(client, client_method_name)(**kwargs)
237
- except (asyncio.CancelledError, asyncio.exceptions.CancelledError):
240
+ res = await asyncio.wait_for(getattr(client, client_method_name)(**kwargs), timeout=HEARTBEAT * 60)
241
+ except asyncio.exceptions.CancelledError:
238
242
  msg = f"{msg_header} Server Shutdown"
239
243
  raise GRPCError(status=Status.UNAVAILABLE, message=msg)
244
+ except asyncio.exceptions.TimeoutError:
245
+ msg = f"{msg_header} timeout error"
246
+ logger.warning(msg)
247
+ raise GRPCError(status=Status.RESOURCE_EXHAUSTED, message=msg)
240
248
  except (errors.RateLimitReached, errors.QueryCanceled) as ex:
241
249
  Martin.rate_limit_reached_time = time.time()
242
250
  msg = f"{msg_header} RateLimitReached: {ex}"
@@ -259,6 +267,8 @@ class Martin(mr.MartinBase):
259
267
  if rate_limit:
260
268
  open_client.ts_rlc = time.time()
261
269
  return res, client, msg_header
270
+ finally:
271
+ client.request_event.set()
262
272
 
263
273
  async def fetch_server_time(self, request: mr.OpenClientConnectionId) -> mr.FetchServerTimeResponse:
264
274
  res, _, _ = await self.send_request('fetch_server_time', request, rate_limit=True)
@@ -428,36 +438,39 @@ class Martin(mr.MartinBase):
428
438
  return response
429
439
 
430
440
  async def fetch_order_book(self, request: mr.MarketRequest) -> mr.FetchOrderBookResponse:
431
- open_client = OpenClient.get_client(request.client_id)
432
- client = open_client.client
433
441
  response = mr.FetchOrderBookResponse()
434
- await self.rate_limit_control(open_client)
435
- limit = 1 if client.exchange in ('bitfinex', 'okx') else 5
436
- res = await client.fetch_order_book(symbol=request.symbol, limit=limit)
437
- open_client.ts_rlc = time.time()
442
+ res, _, _ = await self.send_request(
443
+ 'fetch_order_book',
444
+ request,
445
+ rate_limit=True,
446
+ symbol=request.symbol
447
+ )
448
+
438
449
  res['bids'] = [json.dumps(v) for v in res.get('bids', [])]
439
450
  res['asks'] = [json.dumps(v) for v in res.get('asks', [])]
440
451
  return response.from_pydict(res)
441
452
 
442
453
  async def fetch_symbol_price_ticker(self, request: mr.MarketRequest) -> mr.FetchSymbolPriceTickerResponse:
443
- open_client = OpenClient.get_client(request.client_id)
444
- client = open_client.client
445
454
  response = mr.FetchSymbolPriceTickerResponse()
446
- await self.rate_limit_control(open_client)
447
- res = await client.fetch_symbol_price_ticker(symbol=request.symbol)
448
- open_client.ts_rlc = time.time()
455
+ res, _, _ = await self.send_request(
456
+ 'fetch_symbol_price_ticker',
457
+ request,
458
+ rate_limit=True,
459
+ symbol=request.symbol
460
+ )
449
461
  return response.from_pydict(res)
450
462
 
451
463
  async def fetch_ticker_price_change_statistics(
452
464
  self,
453
465
  request: mr.MarketRequest
454
466
  ) -> mr.FetchTickerPriceChangeStatisticsResponse:
455
- open_client = OpenClient.get_client(request.client_id)
456
- client = open_client.client
457
467
  response = mr.FetchTickerPriceChangeStatisticsResponse()
458
- await self.rate_limit_control(open_client)
459
- res = await client.fetch_ticker_price_change_statistics(symbol=request.symbol)
460
- open_client.ts_rlc = time.time()
468
+ res, _, _ = await self.send_request(
469
+ 'fetch_ticker_price_change_statistics',
470
+ request,
471
+ rate_limit=True,
472
+ symbol=request.symbol
473
+ )
461
474
  return response.from_pydict(res)
462
475
 
463
476
  async def fetch_klines(self, request: mr.FetchKlinesRequest) -> mr.JsonResponse:
@@ -663,15 +676,13 @@ class Martin(mr.MartinBase):
663
676
  _get_event_from_queue = False
664
677
 
665
678
  if client.exchange in ('bitfinex', 'huobi', 'bybit'):
666
- await self.rate_limit_control(open_client)
667
- try:
668
- balances = await client.fetch_ledgers(request.symbol)
669
- except Exception as _ex:
670
- logger.warning(f"OnBalanceUpdate: for {open_client.name}:{request.symbol}: {_ex}")
671
- logger.debug(f"OnBalanceUpdate: {traceback.format_exc()}")
672
- else:
673
- open_client.ts_rlc = time.time()
674
- [_events.append(client.events.wrap_event(balance)) for balance in balances]
679
+ balances, _, _ = await self.send_request(
680
+ 'fetch_ledgers',
681
+ request,
682
+ rate_limit=True,
683
+ symbol=request.symbol
684
+ )
685
+ [_events.append(client.events.wrap_event(balance)) for balance in balances]
675
686
 
676
687
  for _event in _events:
677
688
  if _event.asset in request.symbol:
@@ -847,7 +858,7 @@ async def amain(host: str = '127.0.0.1', port: int = 50051):
847
858
  server = Server([Martin()])
848
859
  with graceful_exit([server]):
849
860
  await server.start(host, port)
850
- logger.info(f"Starting server v:{ver_cw}:{ver_ew} on {host}:{port}")
861
+ logger.info(f"Starting server v:{VER_CW}:{VER_EW} on {host}:{port}")
851
862
  await server.wait_closed()
852
863
 
853
864
  for oc in OpenClient.open_clients:
@@ -17,7 +17,7 @@ dynamic = ["version", "description"]
17
17
  requires-python = ">=3.9"
18
18
 
19
19
  dependencies = [
20
- "crypto-ws-api==2.0.9",
20
+ "crypto-ws-api==2.0.11",
21
21
  "grpcio==1.62.0",
22
22
  "pyotp~=2.9.0",
23
23
  "simplejson==3.19.2",