exchanges-wrapper 2.1.52__tar.gz → 2.2.2__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.52 → exchanges_wrapper-2.2.2}/PKG-INFO +2 -3
  2. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.2}/exchanges_wrapper/__init__.py +1 -1
  3. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.2}/exchanges_wrapper/exch_srv.py +60 -56
  4. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.2}/exchanges_wrapper/http_client.py +4 -5
  5. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.2}/exchanges_wrapper/martin/__init__.py +29 -29
  6. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.2}/exchanges_wrapper/proto/martin.proto +14 -12
  7. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.2}/exchanges_wrapper/web_sockets.py +16 -16
  8. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.2}/pyproject.toml +1 -2
  9. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.2}/LICENSE.md +0 -0
  10. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.2}/README.md +0 -0
  11. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.2}/exchanges_wrapper/client.py +0 -0
  12. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.2}/exchanges_wrapper/definitions.py +0 -0
  13. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.2}/exchanges_wrapper/errors.py +0 -0
  14. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.2}/exchanges_wrapper/events.py +0 -0
  15. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.2}/exchanges_wrapper/exch_srv_cfg.toml.template +0 -0
  16. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.2}/exchanges_wrapper/lib.py +0 -0
  17. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.2}/exchanges_wrapper/parsers/bitfinex.py +0 -0
  18. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.2}/exchanges_wrapper/parsers/bybit.py +0 -0
  19. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.2}/exchanges_wrapper/parsers/huobi.py +0 -0
  20. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.2}/exchanges_wrapper/parsers/okx.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: exchanges-wrapper
3
- Version: 2.1.52
3
+ Version: 2.2.2
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.12
@@ -12,9 +12,8 @@ Classifier: Operating System :: Unix
12
12
  Classifier: Operating System :: Microsoft :: Windows
13
13
  Classifier: Operating System :: MacOS
14
14
  License-File: LICENSE.md
15
- Requires-Dist: crypto-ws-api==2.1.12
15
+ Requires-Dist: crypto-ws-api==2.1.15
16
16
  Requires-Dist: pyotp==2.10.0
17
- Requires-Dist: simplejson==4.1.1
18
17
  Requires-Dist: aiohttp==3.14.3
19
18
  Requires-Dist: expiringdict==1.2.2
20
19
  Requires-Dist: betterproto==2.0.0b7
@@ -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.52"
15
+ __version__ = "2.2.2"
16
16
 
17
17
  from pathlib import Path
18
18
  import shutil
@@ -16,8 +16,7 @@ import gc
16
16
  import traceback
17
17
  import asyncio
18
18
  import functools
19
- # noinspection PyPackageRequirements
20
- import ujson as json
19
+ import orjson
21
20
  import logging
22
21
  from decimal import Decimal
23
22
  import ctypes, ctypes.util
@@ -162,7 +161,7 @@ class Martin(mr.MartinBase):
162
161
  real_market=open_client.real_market
163
162
  )
164
163
 
165
- async def reset_rate_limit(self, request: mr.OpenClientConnectionId) -> mr.SimpleResponse:
164
+ async def reset_rate_limit(self, request: mr.OpenClientConnectionId) -> SimpleResponse:
166
165
  Martin.rate_limiter = max(Martin.rate_limiter or 0, request.rate_limiter)
167
166
  _success = False
168
167
  open_client = OpenClient.get_client(request.client_id)
@@ -175,7 +174,7 @@ class Martin(mr.MartinBase):
175
174
  _success = True
176
175
  elif client.http.rate_limit_reached:
177
176
  Martin.rate_limit_reached_time = time.time()
178
- return mr.SimpleResponse(success=_success)
177
+ return SimpleResponse(success=_success)
179
178
 
180
179
  async def send_request(self, client_method_name, request, rate_limit=False, **kwargs):
181
180
  open_client_instance = OpenClient.get_client(request.client_id)
@@ -233,9 +232,9 @@ class Martin(mr.MartinBase):
233
232
  server_time = res.get('serverTime')
234
233
  return mr.FetchServerTimeResponse(server_time=server_time)
235
234
 
236
- async def one_click_arrival_deposit(self, request: mr.MarketRequest) -> mr.SimpleResponse:
235
+ async def one_click_arrival_deposit(self, request: mr.MarketRequest) -> SimpleResponse:
237
236
  res, _, _ = await self.send_request('one_click_arrival_deposit', request, tx_id=request.symbol)
238
- return mr.SimpleResponse(success=True, result=json.dumps(str(res)))
237
+ return SimpleResponse(success=True, result=orjson.dumps(res))
239
238
 
240
239
  async def fetch_open_orders(self, request: mr.MarketRequest) -> mr.FetchOpenOrdersResponse:
241
240
  response = mr.FetchOpenOrdersResponse()
@@ -248,7 +247,7 @@ class Martin(mr.MartinBase):
248
247
  )
249
248
  for order in res:
250
249
  order_id = order['orderId']
251
- response.orders.append(json.dumps(order))
250
+ response.orders.append(orjson.dumps(order))
252
251
  if client.exchange in ('bitfinex', 'huobi'):
253
252
  client.active_order(order_id, order['origQty'], order['executedQty'])
254
253
 
@@ -302,8 +301,8 @@ class Martin(mr.MartinBase):
302
301
  await _queue.put(weakref.ref(event)())
303
302
  logger.debug(f"{msg_header}: {trades}")
304
303
 
305
- async def cancel_all_orders(self, request: mr.MarketRequest) -> mr.SimpleResponse:
306
- response = mr.SimpleResponse()
304
+ async def cancel_all_orders(self, request: mr.MarketRequest) -> SimpleResponse:
305
+ response = SimpleResponse()
307
306
 
308
307
  res, _, _ = await self.send_request(
309
308
  'cancel_all_orders',
@@ -313,7 +312,7 @@ class Martin(mr.MartinBase):
313
312
  )
314
313
 
315
314
  response.success = True
316
- response.result = json.dumps(str(res))
315
+ response.result = orjson.dumps(res)
317
316
  return response
318
317
 
319
318
  async def fetch_exchange_info_symbol(self, request: mr.MarketRequest) -> mr.FetchExchangeInfoSymbolResponse:
@@ -353,8 +352,8 @@ class Martin(mr.MartinBase):
353
352
  setattr(filters, filter_type.lower(), filter_instance)
354
353
  return filters
355
354
 
356
- async def fetch_account_information(self, request: mr.OpenClientConnectionId) -> mr.JsonResponse:
357
- response = mr.JsonResponse()
355
+ async def fetch_account_information(self, request: mr.OpenClientConnectionId) -> mr.BytesResponse:
356
+ response = mr.BytesResponse()
358
357
  account_information, _, _ = await self.send_request(
359
358
  'fetch_account_information',
360
359
  request,
@@ -363,17 +362,21 @@ class Martin(mr.MartinBase):
363
362
  )
364
363
  # Send only balances
365
364
  res = account_information.get('balances', [])
366
- balances = [
367
- {'asset': i['asset'], 'free': i['free'], 'locked': i['locked']}
368
- for i in res if Decimal(i['free']) or Decimal(i['locked'])
369
- ]
370
- response.items = list(map(json.dumps, balances))
365
+ response.items.extend(
366
+ orjson.dumps({
367
+ 'asset': i['asset'],
368
+ 'free': i['free'],
369
+ 'locked': i['locked']
370
+ })
371
+ for i in res
372
+ if float(i['free']) or float(i['locked'])
373
+ )
371
374
  return response
372
375
 
373
- async def fetch_funding_wallet(self, request: mr.FetchFundingWalletRequest) -> mr.JsonResponse:
376
+ async def fetch_funding_wallet(self, request: mr.FetchFundingWalletRequest) -> mr.BytesResponse:
374
377
  open_client = OpenClient.get_client(request.client_id)
375
378
  client = open_client.client
376
- response = mr.JsonResponse()
379
+ response = mr.BytesResponse()
377
380
  res = []
378
381
  if client.exchange in ('bitfinex', 'okx', 'bybit') \
379
382
  or (open_client.real_market and client.exchange == 'binance'):
@@ -384,7 +387,7 @@ class Martin(mr.MartinBase):
384
387
  asset=request.asset,
385
388
  need_btc_valuation=request.need_btc_valuation
386
389
  )
387
- response.items = list(map(json.dumps, res))
390
+ response.items.extend(orjson.dumps(item) for item in res)
388
391
  return response
389
392
 
390
393
  async def fetch_order_book(self, request: mr.MarketRequest) -> mr.FetchOrderBookResponse:
@@ -396,9 +399,10 @@ class Martin(mr.MartinBase):
396
399
  symbol=request.symbol
397
400
  )
398
401
 
399
- res['bids'] = [json.dumps(v) for v in res.get('bids', [])]
400
- res['asks'] = [json.dumps(v) for v in res.get('asks', [])]
401
- return response.from_pydict(res)
402
+ response.last_update_id = res.get('lastUpdateId', 0)
403
+ response.bids.extend(orjson.dumps(v) for v in res.get('bids', []))
404
+ response.asks.extend(orjson.dumps(v) for v in res.get('asks', []))
405
+ return response
402
406
 
403
407
  async def fetch_symbol_price_ticker(self, request: mr.MarketRequest) -> mr.FetchSymbolPriceTickerResponse:
404
408
  response = mr.FetchSymbolPriceTickerResponse()
@@ -423,8 +427,8 @@ class Martin(mr.MartinBase):
423
427
  )
424
428
  return response.from_pydict(res)
425
429
 
426
- async def fetch_klines(self, request: mr.FetchKlinesRequest) -> mr.JsonResponse:
427
- response = mr.JsonResponse()
430
+ async def fetch_klines(self, request: mr.FetchKlinesRequest) -> mr.BytesResponse:
431
+ response = mr.BytesResponse()
428
432
 
429
433
  res, _, _ = await self.send_request(
430
434
  'fetch_klines',
@@ -437,16 +441,16 @@ class Martin(mr.MartinBase):
437
441
  limit=request.limit
438
442
  )
439
443
 
440
- response.items = list(map(json.dumps, res))
444
+ response.items.extend(orjson.dumps(item) for item in res)
441
445
  return response
442
446
 
443
447
  async def on_klines_update(self, request: mr.FetchKlinesRequest) -> AsyncGenerator[OnKlinesUpdateResponse, Any]:
444
- response = mr.OnKlinesUpdateResponse()
448
+ response = OnKlinesUpdateResponse()
445
449
  open_client = OpenClient.get_client(request.client_id)
446
450
  client = open_client.client
447
451
  _queue = asyncio.Queue(MAX_QUEUE_SIZE)
448
452
  client.stream_queue[request.trade_id] |= {_queue}
449
- _intervals = json.loads(request.interval)
453
+ _intervals = orjson.loads(request.intervals)
450
454
  event_types = []
451
455
  # Register streams for intervals
452
456
  if client.exchange == 'bitfinex':
@@ -477,7 +481,7 @@ class Martin(mr.MartinBase):
477
481
  # logger.info(f"OnKlinesUpdate.event: {exchange}:{_event.symbol}:{_event.kline_interval}")
478
482
  response.symbol = _event.symbol
479
483
  response.interval = _event.kline_interval
480
- response.candle = json.dumps(
484
+ response.candle = orjson.dumps(
481
485
  [_event.kline_start_time,
482
486
  _event.kline_open_price,
483
487
  _event.kline_high_price,
@@ -495,8 +499,8 @@ class Martin(mr.MartinBase):
495
499
  yield response
496
500
  _queue.task_done()
497
501
 
498
- async def fetch_account_trade_list(self, request: mr.AccountTradeListRequest) -> mr.JsonResponse:
499
- response = mr.JsonResponse()
502
+ async def fetch_account_trade_list(self, request: mr.AccountTradeListRequest) -> mr.BytesResponse:
503
+ response = mr.BytesResponse()
500
504
 
501
505
  res, _, _ = await self.send_request(
502
506
  'fetch_account_trade_list',
@@ -510,11 +514,11 @@ class Martin(mr.MartinBase):
510
514
  limit=request.limit
511
515
  )
512
516
 
513
- response.items = list(map(json.dumps, res))
517
+ response.items.extend(orjson.dumps(item) for item in res)
514
518
  return response
515
519
 
516
520
  async def on_ticker_update(self, request: mr.MarketRequest) -> AsyncGenerator[OnTickerUpdateResponse, Any]:
517
- response = mr.OnTickerUpdateResponse()
521
+ response = OnTickerUpdateResponse()
518
522
  open_client = OpenClient.get_client(request.client_id)
519
523
  client = open_client.client
520
524
  _queue = asyncio.Queue(MAX_QUEUE_SIZE)
@@ -551,7 +555,7 @@ class Martin(mr.MartinBase):
551
555
  _queue.task_done()
552
556
 
553
557
  async def on_order_book_update(self, request: mr.MarketRequest) -> AsyncGenerator[FetchOrderBookResponse, Any]:
554
- response = mr.FetchOrderBookResponse()
558
+ response = FetchOrderBookResponse()
555
559
  open_client = OpenClient.get_client(request.client_id)
556
560
  client = open_client.client
557
561
  _queue = asyncio.LifoQueue(MAX_QUEUE_SIZE * 5)
@@ -579,13 +583,13 @@ class Martin(mr.MartinBase):
579
583
  else:
580
584
  if _event.bids and _event.asks:
581
585
  response.last_update_id = _event.last_update_id
582
- response.bids = list(map(json.dumps, _event.bids))
583
- response.asks = list(map(json.dumps, _event.asks))
586
+ response.bids.extend(orjson.dumps(v) for v in _event.bids)
587
+ response.asks.extend(orjson.dumps(v) for v in _event.asks)
584
588
  yield response
585
589
  _queue.task_done()
586
590
 
587
591
  async def on_funds_update(self, request: mr.OnFundsUpdateRequest) -> AsyncGenerator[StreamResponse, Any]:
588
- response = mr.StreamResponse()
592
+ response = StreamResponse()
589
593
  open_client = OpenClient.get_client(request.client_id)
590
594
  client = open_client.client
591
595
  _queue = asyncio.Queue(MAX_QUEUE_SIZE)
@@ -600,12 +604,12 @@ class Martin(mr.MartinBase):
600
604
  logger.info(f"OnFundsUpdate: Stop user stream for {open_client.name}: {request.symbol}")
601
605
  return
602
606
  else:
603
- response.event = json.dumps(_event.balances)
607
+ response.event = orjson.dumps(_event.balances)
604
608
  yield response
605
609
  _queue.task_done()
606
610
 
607
611
  async def on_balance_update(self, request: mr.MarketRequest) -> AsyncGenerator[StreamResponse, Any]:
608
- response = mr.StreamResponse()
612
+ response = StreamResponse()
609
613
  open_client = OpenClient.get_client(request.client_id)
610
614
  client = open_client.client
611
615
  _queue = asyncio.Queue(MAX_QUEUE_SIZE)
@@ -644,14 +648,14 @@ class Martin(mr.MartinBase):
644
648
  "balance_delta": _event.balance_delta,
645
649
  "clear_time": _event.clear_time
646
650
  }
647
- response.event = json.dumps(balance)
651
+ response.event = orjson.dumps(balance)
648
652
  yield response
649
653
 
650
654
  if _get_event_from_queue:
651
655
  _queue.task_done()
652
656
 
653
657
  async def on_order_update(self, request: mr.MarketRequest) -> AsyncGenerator[SimpleResponse, Any]:
654
- response = mr.SimpleResponse()
658
+ response = SimpleResponse()
655
659
  open_client = OpenClient.get_client(request.client_id)
656
660
  client = open_client.client
657
661
  _queue = asyncio.Queue(MAX_QUEUE_SIZE)
@@ -670,7 +674,7 @@ class Martin(mr.MartinBase):
670
674
  event = vars(_event)
671
675
  event.pop('handlers', None)
672
676
  response.success = True
673
- response.result = json.dumps(event)
677
+ response.result = orjson.dumps(event)
674
678
  yield response
675
679
  _queue.task_done()
676
680
 
@@ -713,8 +717,8 @@ class Martin(mr.MartinBase):
713
717
  response.from_pydict(res)
714
718
  return response
715
719
 
716
- async def transfer_to_sub(self, request: mr.MarketRequest) -> mr.SimpleResponse:
717
- response = mr.SimpleResponse()
720
+ async def transfer_to_sub(self, request: mr.MarketRequest) -> SimpleResponse:
721
+ response = SimpleResponse()
718
722
  response.success = False
719
723
 
720
724
  res, _, _ = await self.send_request(
@@ -728,11 +732,11 @@ class Martin(mr.MartinBase):
728
732
 
729
733
  if res and res.get("txnId"):
730
734
  response.success = True
731
- response.result = json.dumps(res)
735
+ response.result = orjson.dumps(res)
732
736
  return response
733
737
 
734
- async def transfer_to_master(self, request: mr.MarketRequest) -> mr.SimpleResponse:
735
- response = mr.SimpleResponse()
738
+ async def transfer_to_master(self, request: mr.MarketRequest) -> SimpleResponse:
739
+ response = SimpleResponse()
736
740
  response.success = False
737
741
 
738
742
  res, _, _ = await self.send_request(
@@ -745,13 +749,13 @@ class Martin(mr.MartinBase):
745
749
 
746
750
  if res and res.get("txnId"):
747
751
  response.success = True
748
- response.result = json.dumps(res)
752
+ response.result = orjson.dumps(res)
749
753
  return response
750
754
 
751
- async def start_stream(self, request: mr.StartStreamRequest) -> mr.SimpleResponse:
755
+ async def start_stream(self, request: mr.StartStreamRequest) -> SimpleResponse:
752
756
  open_client = OpenClient.get_client(request.client_id)
753
757
  client = open_client.client
754
- response = mr.SimpleResponse()
758
+ response = SimpleResponse()
755
759
  _market_stream_count = 0
756
760
  while _market_stream_count < request.market_stream_count:
757
761
  await asyncio.sleep(HEARTBEAT)
@@ -764,8 +768,8 @@ class Martin(mr.MartinBase):
764
768
  response.success = True
765
769
  return response
766
770
 
767
- async def stop_stream(self, request: mr.MarketRequest) -> mr.SimpleResponse:
768
- response = mr.SimpleResponse()
771
+ async def stop_stream(self, request: mr.MarketRequest) -> SimpleResponse:
772
+ response = SimpleResponse()
769
773
  if open_client := OpenClient.get_client(request.client_id):
770
774
  client = open_client.client
771
775
  logger.info(f"StopStream request for {request.symbol} on {client.exchange}")
@@ -775,17 +779,17 @@ class Martin(mr.MartinBase):
775
779
  response.success = False
776
780
  return response
777
781
 
778
- async def check_stream(self, request: mr.MarketRequest) -> mr.SimpleResponse:
782
+ async def check_stream(self, request: mr.MarketRequest) -> SimpleResponse:
779
783
  last_update = Martin.ticker_update_time.get(request.trade_id, 0)
780
784
  check_time = time.time() - last_update
781
785
  success = check_time < WSS_TICKER_TIMEOUT
782
- response = mr.SimpleResponse(success=success)
786
+ response = SimpleResponse(success=success)
783
787
  if not success:
784
788
  Martin.ticker_update_time.pop(request.trade_id, None)
785
789
  logger.warning(f"CheckStream request failed for {request.trade_id}")
786
790
  return response
787
791
 
788
- async def client_restart(self, request: mr.MarketRequest) -> mr.SimpleResponse:
792
+ async def client_restart(self, request: mr.MarketRequest) -> SimpleResponse:
789
793
  await self.stop_stream(request)
790
794
  if client := OpenClient.get_client(request.client_id).client:
791
795
  if user_session := client.user_session:
@@ -793,7 +797,7 @@ class Martin(mr.MartinBase):
793
797
  if session := client.http:
794
798
  await session.close_session()
795
799
  OpenClient.remove_client(request.client_id)
796
- return mr.SimpleResponse(success=True)
800
+ return SimpleResponse(success=True)
797
801
 
798
802
 
799
803
  async def stop_stream_ex(client, trade_id):
@@ -1,7 +1,6 @@
1
1
  import asyncio
2
- import ujson as json
2
+ import orjson
3
3
  from urllib.parse import urlencode, urlparse
4
-
5
4
  import aiohttp
6
5
  import logging
7
6
  import time
@@ -189,7 +188,7 @@ class HttpClient:
189
188
  async def _bitfinex_request(self, path, method, signed, send_api_key, endpoint, timeout, **kwargs):
190
189
  _endpoint = endpoint or self.endpoint
191
190
  bfx_post = (method == 'POST' and kwargs) or "params" in kwargs
192
- _params = json.dumps(kwargs) if bfx_post else {}
191
+ _params = orjson.dumps(kwargs).decode() if bfx_post else {}
193
192
  url = f'{_endpoint}/{path}'
194
193
  query_kwargs = {"headers": {"Accept": AJ}}
195
194
  if kwargs and not bfx_post:
@@ -236,7 +235,7 @@ class HttpClient:
236
235
  signature_payload = f"{ts}{self.api_key}{query_string}"
237
236
  else:
238
237
  url += path
239
- data = json.dumps(kwargs)
238
+ data = orjson.dumps(kwargs).decode()
240
239
  signature_payload = f"{ts}{self.api_key}{data}"
241
240
 
242
241
  signature = generate_signature(self.exchange, self.api_secret, signature_payload)
@@ -288,7 +287,7 @@ class HttpClient:
288
287
  if signed:
289
288
  ts = f"{datetime.now(timezone.utc).replace(tzinfo=None).isoformat('T', 'milliseconds')}Z"
290
289
  if method == 'POST' and kwargs:
291
- data = json.dumps(kwargs.get('data') if 'data' in kwargs else kwargs)
290
+ data = orjson.dumps(kwargs.get('data') if 'data' in kwargs else kwargs).decode()
292
291
  signature_payload = f"{ts}{method}{path}{data}"
293
292
  else:
294
293
  signature_payload = f"{ts}{method}{path}"
@@ -16,7 +16,6 @@ import betterproto
16
16
  import grpclib
17
17
  from betterproto.grpc.grpclib_server import ServiceBase
18
18
 
19
-
20
19
  if TYPE_CHECKING:
21
20
  import grpclib.server
22
21
  from betterproto.grpc.grpclib_client import MetadataLike
@@ -24,13 +23,13 @@ if TYPE_CHECKING:
24
23
 
25
24
 
26
25
  @dataclass(eq=False, repr=False)
27
- class JsonResponse(betterproto.Message):
28
- items: List[str] = betterproto.string_field(1)
26
+ class BytesResponse(betterproto.Message):
27
+ items: List[bytes] = betterproto.bytes_field(1)
29
28
 
30
29
 
31
30
  @dataclass(eq=False, repr=False)
32
31
  class StreamResponse(betterproto.Message):
33
- event: str = betterproto.string_field(1)
32
+ event: bytes = betterproto.bytes_field(1)
34
33
 
35
34
 
36
35
  @dataclass(eq=False, repr=False)
@@ -146,7 +145,7 @@ class OnFundsUpdateRequest(betterproto.Message):
146
145
  @dataclass(eq=False, repr=False)
147
146
  class SimpleResponse(betterproto.Message):
148
147
  success: bool = betterproto.bool_field(1)
149
- result: str = betterproto.string_field(2)
148
+ result: bytes = betterproto.bytes_field(2)
150
149
 
151
150
 
152
151
  @dataclass(eq=False, repr=False)
@@ -169,7 +168,7 @@ class AccountTradeListRequest(betterproto.Message):
169
168
  class OnKlinesUpdateResponse(betterproto.Message):
170
169
  symbol: str = betterproto.string_field(1)
171
170
  interval: str = betterproto.string_field(2)
172
- candle: str = betterproto.string_field(3)
171
+ candle: bytes = betterproto.bytes_field(3)
173
172
 
174
173
 
175
174
  @dataclass(eq=False, repr=False)
@@ -179,6 +178,7 @@ class FetchKlinesRequest(betterproto.Message):
179
178
  symbol: str = betterproto.string_field(3)
180
179
  interval: str = betterproto.string_field(4)
181
180
  limit: int = betterproto.uint32_field(5)
181
+ intervals: bytes = betterproto.bytes_field(6)
182
182
 
183
183
 
184
184
  @dataclass(eq=False, repr=False)
@@ -215,8 +215,8 @@ class FetchSymbolPriceTickerResponse(betterproto.Message):
215
215
  @dataclass(eq=False, repr=False)
216
216
  class FetchOrderBookResponse(betterproto.Message):
217
217
  last_update_id: int = betterproto.uint64_field(1)
218
- bids: List[str] = betterproto.string_field(2)
219
- asks: List[str] = betterproto.string_field(3)
218
+ bids: List[bytes] = betterproto.bytes_field(2)
219
+ asks: List[bytes] = betterproto.bytes_field(3)
220
220
 
221
221
 
222
222
  @dataclass(eq=False, repr=False)
@@ -397,7 +397,7 @@ class FetchOrderResponse(betterproto.Message):
397
397
  @dataclass(eq=False, repr=False)
398
398
  class FetchOpenOrdersResponse(betterproto.Message):
399
399
  rate_limiter: int = betterproto.int32_field(1)
400
- orders: List[str] = betterproto.string_field(2)
400
+ orders: List[bytes] = betterproto.bytes_field(2)
401
401
 
402
402
 
403
403
  @dataclass(eq=False, repr=False)
@@ -526,11 +526,11 @@ class MartinStub(betterproto.ServiceStub):
526
526
  timeout: Optional[float] = None,
527
527
  deadline: Optional["Deadline"] = None,
528
528
  metadata: Optional["MetadataLike"] = None
529
- ) -> "JsonResponse":
529
+ ) -> "BytesResponse":
530
530
  return await self._unary_unary(
531
531
  "/martin.Martin/FetchAccountInformation",
532
532
  open_client_connection_id,
533
- JsonResponse,
533
+ BytesResponse,
534
534
  timeout=timeout,
535
535
  deadline=deadline,
536
536
  metadata=metadata,
@@ -543,11 +543,11 @@ class MartinStub(betterproto.ServiceStub):
543
543
  timeout: Optional[float] = None,
544
544
  deadline: Optional["Deadline"] = None,
545
545
  metadata: Optional["MetadataLike"] = None
546
- ) -> "JsonResponse":
546
+ ) -> "BytesResponse":
547
547
  return await self._unary_unary(
548
548
  "/martin.Martin/FetchAccountTradeList",
549
549
  account_trade_list_request,
550
- JsonResponse,
550
+ BytesResponse,
551
551
  timeout=timeout,
552
552
  deadline=deadline,
553
553
  metadata=metadata,
@@ -577,11 +577,11 @@ class MartinStub(betterproto.ServiceStub):
577
577
  timeout: Optional[float] = None,
578
578
  deadline: Optional["Deadline"] = None,
579
579
  metadata: Optional["MetadataLike"] = None
580
- ) -> "JsonResponse":
580
+ ) -> "BytesResponse":
581
581
  return await self._unary_unary(
582
582
  "/martin.Martin/FetchFundingWallet",
583
583
  fetch_funding_wallet_request,
584
- JsonResponse,
584
+ BytesResponse,
585
585
  timeout=timeout,
586
586
  deadline=deadline,
587
587
  metadata=metadata,
@@ -594,11 +594,11 @@ class MartinStub(betterproto.ServiceStub):
594
594
  timeout: Optional[float] = None,
595
595
  deadline: Optional["Deadline"] = None,
596
596
  metadata: Optional["MetadataLike"] = None
597
- ) -> "JsonResponse":
597
+ ) -> "BytesResponse":
598
598
  return await self._unary_unary(
599
599
  "/martin.Martin/FetchKlines",
600
600
  fetch_klines_request,
601
- JsonResponse,
601
+ BytesResponse,
602
602
  timeout=timeout,
603
603
  deadline=deadline,
604
604
  metadata=metadata,
@@ -973,12 +973,12 @@ class MartinBase(ServiceBase):
973
973
 
974
974
  async def fetch_account_information(
975
975
  self, open_client_connection_id: "OpenClientConnectionId"
976
- ) -> "JsonResponse":
976
+ ) -> "BytesResponse":
977
977
  raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
978
978
 
979
979
  async def fetch_account_trade_list(
980
980
  self, account_trade_list_request: "AccountTradeListRequest"
981
- ) -> "JsonResponse":
981
+ ) -> "BytesResponse":
982
982
  raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
983
983
 
984
984
  async def fetch_exchange_info_symbol(
@@ -988,12 +988,12 @@ class MartinBase(ServiceBase):
988
988
 
989
989
  async def fetch_funding_wallet(
990
990
  self, fetch_funding_wallet_request: "FetchFundingWalletRequest"
991
- ) -> "JsonResponse":
991
+ ) -> "BytesResponse":
992
992
  raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
993
993
 
994
994
  async def fetch_klines(
995
995
  self, fetch_klines_request: "FetchKlinesRequest"
996
- ) -> "JsonResponse":
996
+ ) -> "BytesResponse":
997
997
  raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
998
998
 
999
999
  async def fetch_open_orders(
@@ -1128,14 +1128,14 @@ class MartinBase(ServiceBase):
1128
1128
  await stream.send_message(response)
1129
1129
 
1130
1130
  async def __rpc_fetch_account_information(
1131
- self, stream: "grpclib.server.Stream[OpenClientConnectionId, JsonResponse]"
1131
+ self, stream: "grpclib.server.Stream[OpenClientConnectionId, BytesResponse]"
1132
1132
  ) -> None:
1133
1133
  request = await stream.recv_message()
1134
1134
  response = await self.fetch_account_information(request)
1135
1135
  await stream.send_message(response)
1136
1136
 
1137
1137
  async def __rpc_fetch_account_trade_list(
1138
- self, stream: "grpclib.server.Stream[AccountTradeListRequest, JsonResponse]"
1138
+ self, stream: "grpclib.server.Stream[AccountTradeListRequest, BytesResponse]"
1139
1139
  ) -> None:
1140
1140
  request = await stream.recv_message()
1141
1141
  response = await self.fetch_account_trade_list(request)
@@ -1150,14 +1150,14 @@ class MartinBase(ServiceBase):
1150
1150
  await stream.send_message(response)
1151
1151
 
1152
1152
  async def __rpc_fetch_funding_wallet(
1153
- self, stream: "grpclib.server.Stream[FetchFundingWalletRequest, JsonResponse]"
1153
+ self, stream: "grpclib.server.Stream[FetchFundingWalletRequest, BytesResponse]"
1154
1154
  ) -> None:
1155
1155
  request = await stream.recv_message()
1156
1156
  response = await self.fetch_funding_wallet(request)
1157
1157
  await stream.send_message(response)
1158
1158
 
1159
1159
  async def __rpc_fetch_klines(
1160
- self, stream: "grpclib.server.Stream[FetchKlinesRequest, JsonResponse]"
1160
+ self, stream: "grpclib.server.Stream[FetchKlinesRequest, BytesResponse]"
1161
1161
  ) -> None:
1162
1162
  request = await stream.recv_message()
1163
1163
  response = await self.fetch_klines(request)
@@ -1356,13 +1356,13 @@ class MartinBase(ServiceBase):
1356
1356
  self.__rpc_fetch_account_information,
1357
1357
  grpclib.const.Cardinality.UNARY_UNARY,
1358
1358
  OpenClientConnectionId,
1359
- JsonResponse,
1359
+ BytesResponse,
1360
1360
  ),
1361
1361
  "/martin.Martin/FetchAccountTradeList": grpclib.const.Handler(
1362
1362
  self.__rpc_fetch_account_trade_list,
1363
1363
  grpclib.const.Cardinality.UNARY_UNARY,
1364
1364
  AccountTradeListRequest,
1365
- JsonResponse,
1365
+ BytesResponse,
1366
1366
  ),
1367
1367
  "/martin.Martin/FetchExchangeInfoSymbol": grpclib.const.Handler(
1368
1368
  self.__rpc_fetch_exchange_info_symbol,
@@ -1374,13 +1374,13 @@ class MartinBase(ServiceBase):
1374
1374
  self.__rpc_fetch_funding_wallet,
1375
1375
  grpclib.const.Cardinality.UNARY_UNARY,
1376
1376
  FetchFundingWalletRequest,
1377
- JsonResponse,
1377
+ BytesResponse,
1378
1378
  ),
1379
1379
  "/martin.Martin/FetchKlines": grpclib.const.Handler(
1380
1380
  self.__rpc_fetch_klines,
1381
1381
  grpclib.const.Cardinality.UNARY_UNARY,
1382
1382
  FetchKlinesRequest,
1383
- JsonResponse,
1383
+ BytesResponse,
1384
1384
  ),
1385
1385
  "/martin.Martin/FetchOpenOrders": grpclib.const.Handler(
1386
1386
  self.__rpc_fetch_open_orders,
@@ -1,4 +1,5 @@
1
1
  /*
2
+ __version__ = "2.2.1"
2
3
  Protocol buffer definition for exchanges-wrapper exch_srv.py
3
4
  https://grpc.io/docs/languages/python/quickstart/
4
5
  For compile use next commands
@@ -16,11 +17,11 @@ service Martin {
16
17
  rpc CancelOrder (CancelOrderRequest) returns (CancelOrderResponse) {}
17
18
  rpc CheckStream (MarketRequest) returns (SimpleResponse) {}
18
19
  rpc CreateLimitOrder (CreateLimitOrderRequest) returns (CreateLimitOrderResponse) {}
19
- rpc FetchAccountInformation (OpenClientConnectionId) returns (JSONResponse) {}
20
- rpc FetchAccountTradeList (AccountTradeListRequest) returns (JSONResponse) {}
20
+ rpc FetchAccountInformation (OpenClientConnectionId) returns (BytesResponse) {}
21
+ rpc FetchAccountTradeList (AccountTradeListRequest) returns (BytesResponse) {}
21
22
  rpc FetchExchangeInfoSymbol (MarketRequest) returns (FetchExchangeInfoSymbolResponse) {}
22
- rpc FetchFundingWallet(FetchFundingWalletRequest) returns (JSONResponse) {}
23
- rpc FetchKlines (FetchKlinesRequest) returns (JSONResponse) {}
23
+ rpc FetchFundingWallet(FetchFundingWalletRequest) returns (BytesResponse) {}
24
+ rpc FetchKlines (FetchKlinesRequest) returns (BytesResponse) {}
24
25
  rpc FetchOpenOrders (MarketRequest) returns (FetchOpenOrdersResponse) {}
25
26
  rpc FetchOrder (FetchOrderRequest) returns (FetchOrderResponse) {}
26
27
  rpc FetchOrderBook (MarketRequest) returns (FetchOrderBookResponse) {}
@@ -43,12 +44,12 @@ service Martin {
43
44
  rpc ClientRestart (MarketRequest) returns (SimpleResponse) {}
44
45
  }
45
46
 
46
- message JSONResponse {
47
- repeated string items = 1;
47
+ message BytesResponse {
48
+ repeated bytes items = 1;
48
49
  }
49
50
 
50
51
  message StreamResponse {
51
- string event = 1;
52
+ bytes event = 1;
52
53
  }
53
54
 
54
55
  message FetchFundingWalletRequest{
@@ -157,7 +158,7 @@ message OnFundsUpdateRequest {
157
158
 
158
159
  message SimpleResponse {
159
160
  bool success = 1;
160
- string result = 2;
161
+ bytes result = 2;
161
162
  }
162
163
 
163
164
  message OnTickerUpdateResponse {
@@ -177,7 +178,7 @@ message AccountTradeListRequest {
177
178
  message OnKlinesUpdateResponse {
178
179
  string symbol = 1;
179
180
  string interval =2;
180
- string candle = 3;
181
+ bytes candle = 3;
181
182
  }
182
183
 
183
184
  message FetchKlinesRequest {
@@ -186,6 +187,7 @@ message FetchKlinesRequest {
186
187
  string symbol = 3;
187
188
  string interval = 4;
188
189
  uint32 limit = 5;
190
+ bytes intervals = 6;
189
191
  }
190
192
 
191
193
  message FetchTickerPriceChangeStatisticsResponse {
@@ -219,8 +221,8 @@ message FetchSymbolPriceTickerResponse {
219
221
 
220
222
  message FetchOrderBookResponse {
221
223
  uint64 lastUpdateId = 1;
222
- repeated string bids = 2;
223
- repeated string asks = 3;
224
+ repeated bytes bids = 2;
225
+ repeated bytes asks = 3;
224
226
  }
225
227
 
226
228
  message FetchExchangeInfoSymbolResponse {
@@ -362,7 +364,7 @@ message FetchOrderResponse {
362
364
 
363
365
  message FetchOpenOrdersResponse {
364
366
  int32 rate_limiter = 1;
365
- repeated string orders = 2;
367
+ repeated bytes orders = 2;
366
368
  }
367
369
 
368
370
  message MarketRequest {
@@ -2,9 +2,7 @@ import sys
2
2
  import asyncio
3
3
  import gc
4
4
  import logging
5
-
6
- # noinspection PyPackageRequirements
7
- import ujson as json
5
+ import orjson
8
6
  from pathlib import Path
9
7
  import time
10
8
  from decimal import Decimal
@@ -114,7 +112,7 @@ class EventsDataStream:
114
112
  pass # meant to be overridden in a subclass
115
113
 
116
114
  async def _handle_messages(self, msg, symbol=None, ch_type=str()):
117
- msg_data = json.loads(msg if isinstance(msg, str) else gzip.decompress(msg))
115
+ msg_data = orjson.loads(msg if isinstance(msg, str) else gzip.decompress(msg))
118
116
  if self.exchange == 'binance':
119
117
  if "stream" in msg_data:
120
118
  await self._handle_event(msg_data)
@@ -217,7 +215,7 @@ class EventsDataStream:
217
215
  elif self.exchange == 'huobi':
218
216
  if ping := msg_data.get('ping'):
219
217
  self.ping = 0
220
- await self.websocket.send(json.dumps({"pong": ping}))
218
+ await self.websocket.send(orjson.dumps({"pong": ping}), text = True)
221
219
  await asyncio.sleep(0)
222
220
  elif msg_data.get('action') == 'ping':
223
221
  self.ping = 0
@@ -227,7 +225,7 @@ class EventsDataStream:
227
225
  "ts": msg_data.get('data').get('ts')
228
226
  }
229
227
  }
230
- await self.websocket.send(json.dumps(pong))
228
+ await self.websocket.send(orjson.dumps(pong), text = True)
231
229
  await asyncio.sleep(0)
232
230
  elif msg_data.get('tick') or msg_data.get('data'):
233
231
  if ch_type == 'ticker':
@@ -252,7 +250,7 @@ class EventsDataStream:
252
250
 
253
251
  async def ws_listener(self, request=None, symbol=None, ch_type=str()):
254
252
  if request:
255
- await self.websocket.send(json.dumps(request))
253
+ await self.websocket.send(orjson.dumps(request), text = True)
256
254
  await asyncio.sleep(0)
257
255
  async for msg_data in self.websocket:
258
256
  await self._handle_messages(msg_data, symbol, ch_type)
@@ -261,7 +259,7 @@ class EventsDataStream:
261
259
  while True:
262
260
  await asyncio.sleep(interval)
263
261
  try:
264
- await self.websocket.send(json.dumps({"req_id": req_id, "op": "ping"}))
262
+ await self.websocket.send(orjson.dumps({"req_id": req_id, "op": "ping"}), text = True)
265
263
  except (ConnectionClosed, asyncio.exceptions.TimeoutError, AttributeError):
266
264
  break
267
265
 
@@ -418,9 +416,10 @@ class HbpPrivateEventsDataStream(EventsDataStream):
418
416
 
419
417
  async def start_wss(self):
420
418
  await self.websocket.send(
421
- json.dumps(
419
+ orjson.dumps(
422
420
  compose_htx_ws_auth(self.endpoint, self.exchange, self.client.api_key, self.client.api_secret)
423
- )
421
+ ),
422
+ text = True
424
423
  )
425
424
  await asyncio.sleep(0)
426
425
  await self._handle_messages(await self.websocket.recv(), symbol=self.symbol)
@@ -429,7 +428,7 @@ class HbpPrivateEventsDataStream(EventsDataStream):
429
428
  "action": "sub",
430
429
  "ch": "accounts.update#2"
431
430
  }
432
- await self.websocket.send(json.dumps(request))
431
+ await self.websocket.send(orjson.dumps(request), text = True)
433
432
  await asyncio.sleep(0)
434
433
  await self._handle_messages(await self.websocket.recv(), symbol=self.symbol)
435
434
  #
@@ -437,7 +436,7 @@ class HbpPrivateEventsDataStream(EventsDataStream):
437
436
  "action": "sub",
438
437
  "ch": f"orders#{self.symbol.lower()}"
439
438
  }
440
- await self.websocket.send(json.dumps(request))
439
+ await self.websocket.send(orjson.dumps(request), text = True)
441
440
  await asyncio.sleep(0)
442
441
  await self._handle_messages(await self.websocket.recv(), symbol=self.symbol)
443
442
  #
@@ -546,7 +545,7 @@ class OkxPrivateEventsDataStream(EventsDataStream):
546
545
  "sign": signature}
547
546
  ]
548
547
  }
549
- await self.websocket.send(json.dumps(request))
548
+ await self.websocket.send(orjson.dumps(request), text = True)
550
549
  await asyncio.sleep(0)
551
550
  await self._handle_messages(await self.websocket.recv())
552
551
  # Channel subscription
@@ -598,7 +597,7 @@ class BBTPrivateEventsDataStream(EventsDataStream):
598
597
  "op": 'auth',
599
598
  "args": [self.client.api_key, ts, signature]
600
599
  }
601
- await self.websocket.send(json.dumps(request))
600
+ await self.websocket.send(orjson.dumps(request), text = True)
602
601
  await asyncio.sleep(0)
603
602
  await self._handle_messages(await self.websocket.recv())
604
603
  # Channel subscription
@@ -633,9 +632,10 @@ class UserEventsDataStream(EventsDataStream):
633
632
 
634
633
  async def start_wss(self):
635
634
  await self.websocket.send(
636
- json.dumps(
635
+ orjson.dumps(
637
636
  compose_binance_ws_auth(self.trade_id, self.client.api_key, self.client.api_secret)
638
- )
637
+ ),
638
+ text = True
639
639
  )
640
640
  await asyncio.sleep(0)
641
641
  await self._handle_messages(await self.websocket.recv())
@@ -20,9 +20,8 @@ dynamic = ["version", "description"]
20
20
  requires-python = ">=3.12"
21
21
 
22
22
  dependencies = [
23
- "crypto-ws-api==2.1.12",
23
+ "crypto-ws-api==2.1.15",
24
24
  "pyotp==2.10.0",
25
- "simplejson==4.1.1",
26
25
  "aiohttp==3.14.3",
27
26
  "expiringdict==1.2.2",
28
27
  "betterproto==2.0.0b7",