exchanges-wrapper 2.1.52__tar.gz → 2.2.4__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.4}/PKG-INFO +2 -3
  2. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/__init__.py +1 -1
  3. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/exch_srv.py +113 -88
  4. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/http_client.py +4 -5
  5. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/martin/__init__.py +29 -29
  6. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/parsers/huobi.py +18 -18
  7. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/proto/martin.proto +14 -12
  8. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/web_sockets.py +16 -16
  9. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/pyproject.toml +1 -2
  10. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/LICENSE.md +0 -0
  11. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/README.md +0 -0
  12. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/client.py +0 -0
  13. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/definitions.py +0 -0
  14. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/errors.py +0 -0
  15. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/events.py +0 -0
  16. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/exch_srv_cfg.toml.template +0 -0
  17. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/lib.py +0 -0
  18. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/parsers/bitfinex.py +0 -0
  19. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/parsers/bybit.py +0 -0
  20. {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/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.4
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.4"
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':
@@ -474,29 +478,31 @@ class Martin(mr.MartinBase):
474
478
  logger.info(f"OnKlinesUpdate: Stop loop for {open_client.name}:{request.symbol}:{_intervals}")
475
479
  return
476
480
  else:
477
- # logger.info(f"OnKlinesUpdate.event: {exchange}:{_event.symbol}:{_event.kline_interval}")
478
- response.symbol = _event.symbol
479
- response.interval = _event.kline_interval
480
- response.candle = json.dumps(
481
- [_event.kline_start_time,
482
- _event.kline_open_price,
483
- _event.kline_high_price,
484
- _event.kline_low_price,
485
- _event.kline_close_price,
486
- _event.kline_base_asset_volume,
487
- _event.kline_close_time,
488
- _event.kline_quote_asset_volume,
489
- _event.kline_trades_number,
490
- _event.kline_taker_buy_base_asset_volume,
491
- _event.kline_taker_buy_quote_asset_volume,
492
- _event.kline_ignore
493
- ]
494
- )
481
+ ev = _event
482
+
483
+ response.symbol = ev.symbol
484
+ response.interval = ev.kline_interval
485
+ response.candle = b""
486
+ response.candle = orjson.dumps([
487
+ ev.kline_start_time,
488
+ ev.kline_open_price,
489
+ ev.kline_high_price,
490
+ ev.kline_low_price,
491
+ ev.kline_close_price,
492
+ ev.kline_base_asset_volume,
493
+ ev.kline_close_time,
494
+ ev.kline_quote_asset_volume,
495
+ ev.kline_trades_number,
496
+ ev.kline_taker_buy_base_asset_volume,
497
+ ev.kline_taker_buy_quote_asset_volume,
498
+ ev.kline_ignore
499
+ ])
500
+
495
501
  yield response
496
502
  _queue.task_done()
497
503
 
498
- async def fetch_account_trade_list(self, request: mr.AccountTradeListRequest) -> mr.JsonResponse:
499
- response = mr.JsonResponse()
504
+ async def fetch_account_trade_list(self, request: mr.AccountTradeListRequest) -> mr.BytesResponse:
505
+ response = mr.BytesResponse()
500
506
 
501
507
  res, _, _ = await self.send_request(
502
508
  'fetch_account_trade_list',
@@ -510,11 +516,11 @@ class Martin(mr.MartinBase):
510
516
  limit=request.limit
511
517
  )
512
518
 
513
- response.items = list(map(json.dumps, res))
519
+ response.items.extend(orjson.dumps(item) for item in res)
514
520
  return response
515
521
 
516
522
  async def on_ticker_update(self, request: mr.MarketRequest) -> AsyncGenerator[OnTickerUpdateResponse, Any]:
517
- response = mr.OnTickerUpdateResponse()
523
+ response = OnTickerUpdateResponse()
518
524
  open_client = OpenClient.get_client(request.client_id)
519
525
  client = open_client.client
520
526
  _queue = asyncio.Queue(MAX_QUEUE_SIZE)
@@ -540,18 +546,18 @@ class Martin(mr.MartinBase):
540
546
  return
541
547
  else:
542
548
  Martin.ticker_update_time[request.trade_id] = time.time()
543
- response.from_pydict(
544
- {
545
- 'openPrice': _event.open_price,
546
- 'lastPrice': _event.close_price,
547
- 'closeTime': _event.event_time
548
- }
549
- )
549
+ ev = _event
550
+ response.open_price = ""
551
+ response.last_price = ""
552
+ response.open_price = str(ev.open_price)
553
+ response.last_price = str(ev.close_price)
554
+ response.close_time = int(ev.event_time)
555
+
550
556
  yield response
551
557
  _queue.task_done()
552
558
 
553
559
  async def on_order_book_update(self, request: mr.MarketRequest) -> AsyncGenerator[FetchOrderBookResponse, Any]:
554
- response = mr.FetchOrderBookResponse()
560
+ response = FetchOrderBookResponse()
555
561
  open_client = OpenClient.get_client(request.client_id)
556
562
  client = open_client.client
557
563
  _queue = asyncio.LifoQueue(MAX_QUEUE_SIZE * 5)
@@ -577,15 +583,26 @@ class Martin(mr.MartinBase):
577
583
  logger.info(f"OnOrderBookUpdate: Stop loop for {open_client.name}: {request.symbol}")
578
584
  return
579
585
  else:
580
- if _event.bids and _event.asks:
586
+ event_bids = _event.bids
587
+ event_asks = _event.asks
588
+
589
+ if event_bids and event_asks:
581
590
  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))
591
+
592
+ res_bids = response.bids
593
+ res_asks = response.asks
594
+
595
+ res_bids.clear()
596
+ res_asks.clear()
597
+
598
+ res_bids.extend(orjson.dumps(v) for v in event_bids)
599
+ res_asks.extend(orjson.dumps(v) for v in event_asks)
600
+
584
601
  yield response
585
602
  _queue.task_done()
586
603
 
587
604
  async def on_funds_update(self, request: mr.OnFundsUpdateRequest) -> AsyncGenerator[StreamResponse, Any]:
588
- response = mr.StreamResponse()
605
+ response = StreamResponse()
589
606
  open_client = OpenClient.get_client(request.client_id)
590
607
  client = open_client.client
591
608
  _queue = asyncio.Queue(MAX_QUEUE_SIZE)
@@ -600,12 +617,13 @@ class Martin(mr.MartinBase):
600
617
  logger.info(f"OnFundsUpdate: Stop user stream for {open_client.name}: {request.symbol}")
601
618
  return
602
619
  else:
603
- response.event = json.dumps(_event.balances)
620
+ response.event = b""
621
+ response.event = orjson.dumps(_event.balances)
604
622
  yield response
605
623
  _queue.task_done()
606
624
 
607
625
  async def on_balance_update(self, request: mr.MarketRequest) -> AsyncGenerator[StreamResponse, Any]:
608
- response = mr.StreamResponse()
626
+ response = StreamResponse()
609
627
  open_client = OpenClient.get_client(request.client_id)
610
628
  client = open_client.client
611
629
  _queue = asyncio.Queue(MAX_QUEUE_SIZE)
@@ -637,21 +655,26 @@ class Martin(mr.MartinBase):
637
655
  [_events.append(client.events.wrap_event(balance)) for balance in balances]
638
656
 
639
657
  for _event in _events:
640
- if _event.asset in request.symbol:
658
+ ev = _event
659
+ ev_asset = ev.asset
660
+
661
+ if ev_asset in request.symbol:
641
662
  balance = {
642
- "event_time": _event.event_time,
643
- "asset": _event.asset,
644
- "balance_delta": _event.balance_delta,
645
- "clear_time": _event.clear_time
663
+ "event_time": ev.event_time,
664
+ "asset": ev_asset,
665
+ "balance_delta": ev.balance_delta,
666
+ "clear_time": ev.clear_time
646
667
  }
647
- response.event = json.dumps(balance)
668
+
669
+ response.event = b""
670
+ response.event = orjson.dumps(balance)
648
671
  yield response
649
672
 
650
673
  if _get_event_from_queue:
651
674
  _queue.task_done()
652
675
 
653
676
  async def on_order_update(self, request: mr.MarketRequest) -> AsyncGenerator[SimpleResponse, Any]:
654
- response = mr.SimpleResponse()
677
+ response = SimpleResponse()
655
678
  open_client = OpenClient.get_client(request.client_id)
656
679
  client = open_client.client
657
680
  _queue = asyncio.Queue(MAX_QUEUE_SIZE)
@@ -667,10 +690,12 @@ class Martin(mr.MartinBase):
667
690
  logger.info(f"OnOrderUpdate: Stop user stream for {open_client.name}: {request.symbol}")
668
691
  return
669
692
  else:
670
- event = vars(_event)
671
- event.pop('handlers', None)
693
+ event_dict = _event.__dict__.copy()
694
+ event_dict.pop('handlers', None)
695
+ response.result = b""
672
696
  response.success = True
673
- response.result = json.dumps(event)
697
+ response.result = orjson.dumps(event_dict)
698
+
674
699
  yield response
675
700
  _queue.task_done()
676
701
 
@@ -713,8 +738,8 @@ class Martin(mr.MartinBase):
713
738
  response.from_pydict(res)
714
739
  return response
715
740
 
716
- async def transfer_to_sub(self, request: mr.MarketRequest) -> mr.SimpleResponse:
717
- response = mr.SimpleResponse()
741
+ async def transfer_to_sub(self, request: mr.MarketRequest) -> SimpleResponse:
742
+ response = SimpleResponse()
718
743
  response.success = False
719
744
 
720
745
  res, _, _ = await self.send_request(
@@ -728,11 +753,11 @@ class Martin(mr.MartinBase):
728
753
 
729
754
  if res and res.get("txnId"):
730
755
  response.success = True
731
- response.result = json.dumps(res)
756
+ response.result = orjson.dumps(res)
732
757
  return response
733
758
 
734
- async def transfer_to_master(self, request: mr.MarketRequest) -> mr.SimpleResponse:
735
- response = mr.SimpleResponse()
759
+ async def transfer_to_master(self, request: mr.MarketRequest) -> SimpleResponse:
760
+ response = SimpleResponse()
736
761
  response.success = False
737
762
 
738
763
  res, _, _ = await self.send_request(
@@ -745,13 +770,13 @@ class Martin(mr.MartinBase):
745
770
 
746
771
  if res and res.get("txnId"):
747
772
  response.success = True
748
- response.result = json.dumps(res)
773
+ response.result = orjson.dumps(res)
749
774
  return response
750
775
 
751
- async def start_stream(self, request: mr.StartStreamRequest) -> mr.SimpleResponse:
776
+ async def start_stream(self, request: mr.StartStreamRequest) -> SimpleResponse:
752
777
  open_client = OpenClient.get_client(request.client_id)
753
778
  client = open_client.client
754
- response = mr.SimpleResponse()
779
+ response = SimpleResponse()
755
780
  _market_stream_count = 0
756
781
  while _market_stream_count < request.market_stream_count:
757
782
  await asyncio.sleep(HEARTBEAT)
@@ -764,8 +789,8 @@ class Martin(mr.MartinBase):
764
789
  response.success = True
765
790
  return response
766
791
 
767
- async def stop_stream(self, request: mr.MarketRequest) -> mr.SimpleResponse:
768
- response = mr.SimpleResponse()
792
+ async def stop_stream(self, request: mr.MarketRequest) -> SimpleResponse:
793
+ response = SimpleResponse()
769
794
  if open_client := OpenClient.get_client(request.client_id):
770
795
  client = open_client.client
771
796
  logger.info(f"StopStream request for {request.symbol} on {client.exchange}")
@@ -775,17 +800,17 @@ class Martin(mr.MartinBase):
775
800
  response.success = False
776
801
  return response
777
802
 
778
- async def check_stream(self, request: mr.MarketRequest) -> mr.SimpleResponse:
803
+ async def check_stream(self, request: mr.MarketRequest) -> SimpleResponse:
779
804
  last_update = Martin.ticker_update_time.get(request.trade_id, 0)
780
805
  check_time = time.time() - last_update
781
806
  success = check_time < WSS_TICKER_TIMEOUT
782
- response = mr.SimpleResponse(success=success)
807
+ response = SimpleResponse(success=success)
783
808
  if not success:
784
809
  Martin.ticker_update_time.pop(request.trade_id, None)
785
810
  logger.warning(f"CheckStream request failed for {request.trade_id}")
786
811
  return response
787
812
 
788
- async def client_restart(self, request: mr.MarketRequest) -> mr.SimpleResponse:
813
+ async def client_restart(self, request: mr.MarketRequest) -> SimpleResponse:
789
814
  await self.stop_stream(request)
790
815
  if client := OpenClient.get_client(request.client_id).client:
791
816
  if user_session := client.user_session:
@@ -793,7 +818,7 @@ class Martin(mr.MartinBase):
793
818
  if session := client.http:
794
819
  await session.close_session()
795
820
  OpenClient.remove_client(request.client_id)
796
- return mr.SimpleResponse(success=True)
821
+ return SimpleResponse(success=True)
797
822
 
798
823
 
799
824
  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,
@@ -8,7 +8,7 @@ import logging
8
8
  logger = logging.getLogger(__name__)
9
9
 
10
10
 
11
- def on_balance_update(res: {}) -> {}:
11
+ def on_balance_update(res: dict) -> dict:
12
12
  return {
13
13
  'e': 'balanceUpdate',
14
14
  'E': res.get('transactTime'),
@@ -18,11 +18,11 @@ def on_balance_update(res: {}) -> {}:
18
18
  }
19
19
 
20
20
 
21
- def fetch_server_time(res: {}) -> {}:
21
+ def fetch_server_time(res: dict) -> dict:
22
22
  return {'serverTime': res}
23
23
 
24
24
 
25
- def exchange_info(server_time: int, _symbol_params) -> {}:
25
+ def exchange_info(server_time: int, _symbol_params) -> dict:
26
26
  _tick_size = str(10**(-_symbol_params.get('pp')))
27
27
  _price_filter = {
28
28
  "filterType": "PRICE_FILTER",
@@ -87,7 +87,7 @@ def orders(res: list, response_type=None) -> list:
87
87
  return binance_orders
88
88
 
89
89
 
90
- def order(res: {}, response_type=None) -> {}:
90
+ def order(res: dict, response_type=None) -> dict:
91
91
  symbol = res.get('symbol').upper()
92
92
  order_id = res.get('id')
93
93
  order_list_id = -1
@@ -171,7 +171,7 @@ def order(res: {}, response_type=None) -> {}:
171
171
  }
172
172
 
173
173
 
174
- def order_cancelled(symbol, order_id=None, origin_client_order_id=None,) -> {}:
174
+ def order_cancelled(symbol, order_id=None, origin_client_order_id=None,) -> dict:
175
175
  return {
176
176
  "symbol": symbol,
177
177
  "origClientOrderId": origin_client_order_id,
@@ -190,7 +190,7 @@ def order_cancelled(symbol, order_id=None, origin_client_order_id=None,) -> {}:
190
190
  }
191
191
 
192
192
 
193
- def account_balances(res: {}) -> {}:
193
+ def account_balances(res: dict) -> dict:
194
194
  """
195
195
  This function parses the Huobi API response for account information and
196
196
  returns a dictionary with relevant details.
@@ -205,7 +205,7 @@ def account_balances(res: {}) -> {}:
205
205
  # Filter out balances that have zero value
206
206
  res[:] = [i for i in res if i.get('balance') != '0']
207
207
 
208
- assets = {}
208
+ assets = dict
209
209
  for balance in res:
210
210
  asset = balance['currency']
211
211
  assets.setdefault(asset, {
@@ -230,12 +230,12 @@ def account_balances(res: {}) -> {}:
230
230
  return {"balances": balances}
231
231
 
232
232
 
233
- def order_book(res: {}) -> {}:
233
+ def order_book(res: dict) -> dict:
234
234
  res["lastUpdateId"] = res.pop("ts")
235
235
  return res
236
236
 
237
237
 
238
- def order_book_ws(res: {}, symbol: str) -> {}:
238
+ def order_book_ws(res: dict, symbol: str) -> dict:
239
239
  return {
240
240
  'stream': f"{symbol}@depth5",
241
241
  'data': {'lastUpdateId': res['ts'],
@@ -245,14 +245,14 @@ def order_book_ws(res: {}, symbol: str) -> {}:
245
245
  }
246
246
 
247
247
 
248
- def fetch_symbol_price_ticker(res: {}, symbol) -> {}:
248
+ def fetch_symbol_price_ticker(res: dict, symbol) -> dict:
249
249
  return {
250
250
  "symbol": symbol,
251
251
  "price": str(res.get('data')[0].get('price'))
252
252
  }
253
253
 
254
254
 
255
- def ticker_price_change_statistics(res: {}, symbol) -> {}:
255
+ def ticker_price_change_statistics(res: dict, symbol) -> dict:
256
256
  return {
257
257
  "symbol": symbol,
258
258
  "priceChange": str(res.get('close') - res.get('open')),
@@ -280,8 +280,8 @@ def ticker_price_change_statistics(res: {}, symbol) -> {}:
280
280
  }
281
281
 
282
282
 
283
- def ticker(res: {}, symbol: str = None) -> {}:
284
- tick = res.get('tick')
283
+ def ticker(res: dict, symbol: str = None) -> dict:
284
+ tick: dict = res.get('tick')
285
285
  return {
286
286
  'stream': f"{symbol}@miniTicker",
287
287
  'data': {
@@ -350,9 +350,9 @@ def klines(res: list, _interval: str) -> list:
350
350
  return binance_klines
351
351
 
352
352
 
353
- def candle(res: dict, symbol: str = None, ch_type: str = None) -> {}:
354
- tick = res.get('tick')
355
- start_time = tick.get('id')
353
+ def candle(res: dict, symbol = '', ch_type = '') -> dict:
354
+ tick: dict = res.get('tick')
355
+ start_time = tick.get('id') * 1000
356
356
  _interval = ch_type.split('_')[1]
357
357
  end_time = start_time + interval2value(interval(_interval)) * 1000 - 1
358
358
  return {
@@ -384,7 +384,7 @@ def candle(res: dict, symbol: str = None, ch_type: str = None) -> {}:
384
384
  }
385
385
 
386
386
 
387
- def on_funds_update(data: {}) -> {}:
387
+ def on_funds_update(data: dict) -> dict:
388
388
  event_time = int(time.time() * 1000)
389
389
  binance_funds = {
390
390
  'e': 'outboundAccountPosition',
@@ -404,7 +404,7 @@ def on_funds_update(data: {}) -> {}:
404
404
  return binance_funds
405
405
 
406
406
 
407
- def on_order_update(_order: {}) -> {}:
407
+ def on_order_update(_order: dict) -> dict:
408
408
  event = _order['lastEvent']
409
409
  order_quantity = event.get('orderSize', event.get('orderValue'))
410
410
  order_price = event.get('orderPrice', event.get('tradePrice'))
@@ -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",