exchanges-wrapper 2.1.11__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.11 → exchanges_wrapper-2.1.12}/PKG-INFO +7 -2
  2. {exchanges_wrapper-2.1.11 → exchanges_wrapper-2.1.12}/README.md +5 -0
  3. {exchanges_wrapper-2.1.11 → exchanges_wrapper-2.1.12}/exchanges_wrapper/__init__.py +1 -1
  4. {exchanges_wrapper-2.1.11 → exchanges_wrapper-2.1.12}/exchanges_wrapper/client.py +4 -16
  5. {exchanges_wrapper-2.1.11 → exchanges_wrapper-2.1.12}/exchanges_wrapper/exch_srv.py +42 -35
  6. {exchanges_wrapper-2.1.11 → exchanges_wrapper-2.1.12}/pyproject.toml +1 -1
  7. {exchanges_wrapper-2.1.11 → exchanges_wrapper-2.1.12}/LICENSE.md +0 -0
  8. {exchanges_wrapper-2.1.11 → exchanges_wrapper-2.1.12}/exchanges_wrapper/definitions.py +0 -0
  9. {exchanges_wrapper-2.1.11 → exchanges_wrapper-2.1.12}/exchanges_wrapper/errors.py +0 -0
  10. {exchanges_wrapper-2.1.11 → exchanges_wrapper-2.1.12}/exchanges_wrapper/events.py +0 -0
  11. {exchanges_wrapper-2.1.11 → exchanges_wrapper-2.1.12}/exchanges_wrapper/exch_srv_cfg.toml.template +0 -0
  12. {exchanges_wrapper-2.1.11 → exchanges_wrapper-2.1.12}/exchanges_wrapper/http_client.py +0 -0
  13. {exchanges_wrapper-2.1.11 → exchanges_wrapper-2.1.12}/exchanges_wrapper/lib.py +0 -0
  14. {exchanges_wrapper-2.1.11 → exchanges_wrapper-2.1.12}/exchanges_wrapper/martin/__init__.py +0 -0
  15. {exchanges_wrapper-2.1.11 → exchanges_wrapper-2.1.12}/exchanges_wrapper/parsers/bitfinex_parser.py +0 -0
  16. {exchanges_wrapper-2.1.11 → exchanges_wrapper-2.1.12}/exchanges_wrapper/parsers/bybit_parser.py +0 -0
  17. {exchanges_wrapper-2.1.11 → exchanges_wrapper-2.1.12}/exchanges_wrapper/parsers/huobi_parser.py +0 -0
  18. {exchanges_wrapper-2.1.11 → exchanges_wrapper-2.1.12}/exchanges_wrapper/parsers/okx_parser.py +0 -0
  19. {exchanges_wrapper-2.1.11 → exchanges_wrapper-2.1.12}/exchanges_wrapper/proto/martin.proto +0 -0
  20. {exchanges_wrapper-2.1.11 → 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.11
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.10
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.11"
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,7 +232,10 @@ 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
240
  res = await asyncio.wait_for(getattr(client, client_method_name)(**kwargs), timeout=HEARTBEAT * 60)
237
241
  except asyncio.exceptions.CancelledError:
@@ -240,7 +244,7 @@ class Martin(mr.MartinBase):
240
244
  except asyncio.exceptions.TimeoutError:
241
245
  msg = f"{msg_header} timeout error"
242
246
  logger.warning(msg)
243
- raise GRPCError(status=Status.OUT_OF_RANGE, message=msg)
247
+ raise GRPCError(status=Status.RESOURCE_EXHAUSTED, message=msg)
244
248
  except (errors.RateLimitReached, errors.QueryCanceled) as ex:
245
249
  Martin.rate_limit_reached_time = time.time()
246
250
  msg = f"{msg_header} RateLimitReached: {ex}"
@@ -263,6 +267,8 @@ class Martin(mr.MartinBase):
263
267
  if rate_limit:
264
268
  open_client.ts_rlc = time.time()
265
269
  return res, client, msg_header
270
+ finally:
271
+ client.request_event.set()
266
272
 
267
273
  async def fetch_server_time(self, request: mr.OpenClientConnectionId) -> mr.FetchServerTimeResponse:
268
274
  res, _, _ = await self.send_request('fetch_server_time', request, rate_limit=True)
@@ -432,36 +438,39 @@ class Martin(mr.MartinBase):
432
438
  return response
433
439
 
434
440
  async def fetch_order_book(self, request: mr.MarketRequest) -> mr.FetchOrderBookResponse:
435
- open_client = OpenClient.get_client(request.client_id)
436
- client = open_client.client
437
441
  response = mr.FetchOrderBookResponse()
438
- await self.rate_limit_control(open_client)
439
- limit = 1 if client.exchange in ('bitfinex', 'okx') else 5
440
- res = await client.fetch_order_book(symbol=request.symbol, limit=limit)
441
- 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
+
442
449
  res['bids'] = [json.dumps(v) for v in res.get('bids', [])]
443
450
  res['asks'] = [json.dumps(v) for v in res.get('asks', [])]
444
451
  return response.from_pydict(res)
445
452
 
446
453
  async def fetch_symbol_price_ticker(self, request: mr.MarketRequest) -> mr.FetchSymbolPriceTickerResponse:
447
- open_client = OpenClient.get_client(request.client_id)
448
- client = open_client.client
449
454
  response = mr.FetchSymbolPriceTickerResponse()
450
- await self.rate_limit_control(open_client)
451
- res = await client.fetch_symbol_price_ticker(symbol=request.symbol)
452
- 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
+ )
453
461
  return response.from_pydict(res)
454
462
 
455
463
  async def fetch_ticker_price_change_statistics(
456
464
  self,
457
465
  request: mr.MarketRequest
458
466
  ) -> mr.FetchTickerPriceChangeStatisticsResponse:
459
- open_client = OpenClient.get_client(request.client_id)
460
- client = open_client.client
461
467
  response = mr.FetchTickerPriceChangeStatisticsResponse()
462
- await self.rate_limit_control(open_client)
463
- res = await client.fetch_ticker_price_change_statistics(symbol=request.symbol)
464
- 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
+ )
465
474
  return response.from_pydict(res)
466
475
 
467
476
  async def fetch_klines(self, request: mr.FetchKlinesRequest) -> mr.JsonResponse:
@@ -667,15 +676,13 @@ class Martin(mr.MartinBase):
667
676
  _get_event_from_queue = False
668
677
 
669
678
  if client.exchange in ('bitfinex', 'huobi', 'bybit'):
670
- await self.rate_limit_control(open_client)
671
- try:
672
- balances = await client.fetch_ledgers(request.symbol)
673
- except Exception as _ex:
674
- logger.warning(f"OnBalanceUpdate: for {open_client.name}:{request.symbol}: {_ex}")
675
- logger.debug(f"OnBalanceUpdate: {traceback.format_exc()}")
676
- else:
677
- open_client.ts_rlc = time.time()
678
- [_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]
679
686
 
680
687
  for _event in _events:
681
688
  if _event.asset in request.symbol:
@@ -851,7 +858,7 @@ async def amain(host: str = '127.0.0.1', port: int = 50051):
851
858
  server = Server([Martin()])
852
859
  with graceful_exit([server]):
853
860
  await server.start(host, port)
854
- 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}")
855
862
  await server.wait_closed()
856
863
 
857
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.10",
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",