exchanges-wrapper 2.1.21rc2__tar.gz → 2.1.22__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 (33) hide show
  1. {exchanges-wrapper-2.1.21rc2 → exchanges_wrapper-2.1.22}/LICENSE.md +0 -0
  2. {exchanges-wrapper-2.1.21rc2 → exchanges_wrapper-2.1.22}/PKG-INFO +9 -12
  3. {exchanges-wrapper-2.1.21rc2 → exchanges_wrapper-2.1.22}/README.md +4 -7
  4. {exchanges-wrapper-2.1.21rc2 → exchanges_wrapper-2.1.22}/exchanges_wrapper/__init__.py +4 -3
  5. {exchanges-wrapper-2.1.21rc2 → exchanges_wrapper-2.1.22}/exchanges_wrapper/client.py +33 -43
  6. {exchanges-wrapper-2.1.21rc2 → exchanges_wrapper-2.1.22}/exchanges_wrapper/definitions.py +0 -0
  7. {exchanges-wrapper-2.1.21rc2 → exchanges_wrapper-2.1.22}/exchanges_wrapper/errors.py +0 -0
  8. {exchanges-wrapper-2.1.21rc2 → exchanges_wrapper-2.1.22}/exchanges_wrapper/events.py +0 -0
  9. {exchanges-wrapper-2.1.21rc2 → exchanges_wrapper-2.1.22}/exchanges_wrapper/exch_srv.py +20 -12
  10. {exchanges-wrapper-2.1.21rc2 → exchanges_wrapper-2.1.22}/exchanges_wrapper/exch_srv_cfg.toml.template +4 -10
  11. {exchanges-wrapper-2.1.21rc2 → exchanges_wrapper-2.1.22}/exchanges_wrapper/http_client.py +80 -121
  12. {exchanges-wrapper-2.1.21rc2 → exchanges_wrapper-2.1.22}/exchanges_wrapper/martin/__init__.py +56 -23
  13. {exchanges-wrapper-2.1.21rc2 → exchanges_wrapper-2.1.22}/exchanges_wrapper/parsers/bitfinex.py +0 -0
  14. {exchanges-wrapper-2.1.21rc2 → exchanges_wrapper-2.1.22}/exchanges_wrapper/parsers/bybit.py +4 -2
  15. {exchanges-wrapper-2.1.21rc2 → exchanges_wrapper-2.1.22}/exchanges_wrapper/parsers/huobi.py +0 -0
  16. {exchanges-wrapper-2.1.21rc2 → exchanges_wrapper-2.1.22}/exchanges_wrapper/parsers/okx.py +0 -0
  17. {exchanges-wrapper-2.1.21rc2 → exchanges_wrapper-2.1.22}/exchanges_wrapper/proto/martin.proto +3 -1
  18. {exchanges-wrapper-2.1.21rc2 → exchanges_wrapper-2.1.22}/exchanges_wrapper/web_sockets.py +1 -16
  19. {exchanges-wrapper-2.1.21rc2 → exchanges_wrapper-2.1.22}/pyproject.toml +3 -3
  20. exchanges-wrapper-2.1.21rc2/.deepsource.toml +0 -13
  21. exchanges-wrapper-2.1.21rc2/.dockerignore +0 -3
  22. exchanges-wrapper-2.1.21rc2/.github/FUNDING.yml +0 -1
  23. exchanges-wrapper-2.1.21rc2/.github/dependabot.yml +0 -11
  24. exchanges-wrapper-2.1.21rc2/.github/workflows/docker-image.yml +0 -32
  25. exchanges-wrapper-2.1.21rc2/.github/workflows/python-publish.yml +0 -40
  26. exchanges-wrapper-2.1.21rc2/.run/exchanges-wrapper-image.run.xml +0 -15
  27. exchanges-wrapper-2.1.21rc2/.run/exchanges-wrapper.run.xml +0 -32
  28. exchanges-wrapper-2.1.21rc2/CHANGELOG.md +0 -605
  29. exchanges-wrapper-2.1.21rc2/Dockerfile +0 -23
  30. exchanges-wrapper-2.1.21rc2/example/exch_client.py +0 -436
  31. exchanges-wrapper-2.1.21rc2/example/ms_cfg.toml +0 -5
  32. exchanges-wrapper-2.1.21rc2/requirements.txt +0 -10
  33. {exchanges-wrapper-2.1.21rc2 → exchanges_wrapper-2.1.22}/exchanges_wrapper/lib.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: exchanges-wrapper
3
- Version: 2.1.21rc2
3
+ Version: 2.1.22
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,15 +11,15 @@ 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.15rc1
14
+ Requires-Dist: crypto-ws-api==2.0.15
15
15
  Requires-Dist: pyotp==2.9.0
16
16
  Requires-Dist: simplejson==3.19.3
17
17
  Requires-Dist: toml~=0.10.2
18
- Requires-Dist: aiohttp~=3.11.2
18
+ Requires-Dist: aiohttp~=3.11.11
19
19
  Requires-Dist: websockets==14.1
20
20
  Requires-Dist: expiringdict~=1.2.2
21
21
  Requires-Dist: ujson~=5.10.0
22
- Requires-Dist: betterproto==2.0.0b6
22
+ Requires-Dist: betterproto==2.0.0b7
23
23
  Requires-Dist: grpclib~=0.4.7
24
24
  Project-URL: Source, https://github.com/DogsTailFarmer/exchanges-wrapper
25
25
 
@@ -37,10 +37,10 @@ Project-URL: Source, https://github.com/DogsTailFarmer/exchanges-wrapper
37
37
  <a href="https://sonarcloud.io/summary/new_code?id=DogsTailFarmer_exchanges-wrapper" target="_blank"><img alt="sonarcloud" title="sonarcloud" src="https://sonarcloud.io/api/project_badges/measure?project=DogsTailFarmer_exchanges-wrapper&metric=alert_status"/></a>
38
38
  <a href="https://pepy.tech/project/exchanges-wrapper" target="_blank"><img alt="Downloads" title="Downloads" src="https://static.pepy.tech/badge/exchanges-wrapper/month"/></a>
39
39
  ***
40
- From `v1.4.0` must be updated `exch_srv_cfg.toml`
40
+ From `2.1.21` must be updated `exch_srv_cfg.toml` from `exchanges_wrapper/exch_srv_cfg.toml.template`
41
41
  ***
42
42
 
43
- ## exchanges-wrapper vs binance.py
43
+ ## exchanges-wrapper vs [binance.py](https://github.com/Th0rgal/binance.py)
44
44
  The main difference is the development of the project for trading with many exchanges.
45
45
 
46
46
  Next is adding a new module ```exchanges_wrapper/exch_srv.py``` as a multiplexer layer, providing simultaneous async interaction for many accounts
@@ -65,11 +65,8 @@ Served methods describes into ```example/exch_client.py```
65
65
  - Adaptive algorithm to ensure maximum performance and avoid exceeding the rates limits
66
66
  - Passthrough logging
67
67
  - WSS keepalive
68
- - Reuse session for new client sessions
69
- - Utilizing Websocket API (bidirectional) for the most commonly used methods:
70
- + Binance ws-api/v3
71
- + Bitfinex Websocket Authenticated Inputs v2
72
- + OKX WS v5
68
+ - Reuse session for new client connections
69
+ - [Utilizing Websocket API (bidirectional) for the most commonly used methods:](https://github.com/DogsTailFarmer/crypto-ws-api)
73
70
 
74
71
  ## Extra exchanges implementation features
75
72
  - Binance REST API and WSS are accepted as basic, connection of other exchanges wrapped their API to Binance compatible
@@ -12,10 +12,10 @@
12
12
  <a href="https://sonarcloud.io/summary/new_code?id=DogsTailFarmer_exchanges-wrapper" target="_blank"><img alt="sonarcloud" title="sonarcloud" src="https://sonarcloud.io/api/project_badges/measure?project=DogsTailFarmer_exchanges-wrapper&metric=alert_status"/></a>
13
13
  <a href="https://pepy.tech/project/exchanges-wrapper" target="_blank"><img alt="Downloads" title="Downloads" src="https://static.pepy.tech/badge/exchanges-wrapper/month"/></a>
14
14
  ***
15
- From `v1.4.0` must be updated `exch_srv_cfg.toml`
15
+ From `2.1.21` must be updated `exch_srv_cfg.toml` from `exchanges_wrapper/exch_srv_cfg.toml.template`
16
16
  ***
17
17
 
18
- ## exchanges-wrapper vs binance.py
18
+ ## exchanges-wrapper vs [binance.py](https://github.com/Th0rgal/binance.py)
19
19
  The main difference is the development of the project for trading with many exchanges.
20
20
 
21
21
  Next is adding a new module ```exchanges_wrapper/exch_srv.py``` as a multiplexer layer, providing simultaneous async interaction for many accounts
@@ -40,11 +40,8 @@ Served methods describes into ```example/exch_client.py```
40
40
  - Adaptive algorithm to ensure maximum performance and avoid exceeding the rates limits
41
41
  - Passthrough logging
42
42
  - WSS keepalive
43
- - Reuse session for new client sessions
44
- - Utilizing Websocket API (bidirectional) for the most commonly used methods:
45
- + Binance ws-api/v3
46
- + Bitfinex Websocket Authenticated Inputs v2
47
- + OKX WS v5
43
+ - Reuse session for new client connections
44
+ - [Utilizing Websocket API (bidirectional) for the most commonly used methods:](https://github.com/DogsTailFarmer/crypto-ws-api)
48
45
 
49
46
  ## Extra exchanges implementation features
50
47
  - Binance REST API and WSS are accepted as basic, connection of other exchanges wrapped their API to Binance compatible
@@ -2,7 +2,7 @@
2
2
  # -*- coding: utf-8 -*-
3
3
  """
4
4
  REST API and WebSocket asyncio wrapper with grpc powered multiplexer server
5
- for crypto exchanges (Binance, Bitfinex, OKX, Huobi)
5
+ for crypto exchanges (Binance, Bitfinex, OKX, Huobi, ByBit)
6
6
  Utilizes one connection for many accounts and trading pairs.
7
7
  For SPOT market only
8
8
  """
@@ -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.21rc2"
15
+ __version__ = "2.1.22"
16
16
 
17
17
  from pathlib import Path
18
18
  import shutil
@@ -20,9 +20,10 @@ import shutil
20
20
  from grpclib.server import Server, GRPCError, Status
21
21
  from grpclib.client import Channel
22
22
  from grpclib.utils import graceful_exit
23
+ from grpclib import exceptions
23
24
 
24
25
  __all__ = [
25
- Server, GRPCError, Status, Channel, graceful_exit
26
+ Server, GRPCError, Status, Channel, graceful_exit, exceptions
26
27
  ]
27
28
 
28
29
  WORK_PATH = Path(Path.home(), ".MartinBinance")
@@ -1,4 +1,3 @@
1
- import aiohttp
2
1
  from enum import Enum
3
2
  from typing import Union
4
3
  import decimal
@@ -12,7 +11,7 @@ from expiringdict import ExpiringDict
12
11
  import uuid
13
12
  from decimal import Decimal, ROUND_HALF_DOWN
14
13
 
15
- from exchanges_wrapper.http_client import ClientBinance, ClientBFX, ClientHBP, ClientOKX, ClientBybit
14
+ from exchanges_wrapper.http_client import HttpClient
16
15
  from exchanges_wrapper.errors import ExchangePyError
17
16
  from exchanges_wrapper.web_sockets import UserEventsDataStream, \
18
17
  MarketEventsDataStream, \
@@ -31,7 +30,10 @@ logger = logging.getLogger(__name__)
31
30
 
32
31
  STATUS_TIMEOUT = 5 # sec, also use for lifetime limit for inactive order (Bitfinex) as 60 * STATUS_TIMEOUT
33
32
  USER_DATA_STREAM = "/api/v3/userDataStream"
34
- FALLBACK_WARNING = 'Fallback to HTTP API call due to None response from user WSS session'
33
+ ORDER_ENDPOINT = "/api/v3/order"
34
+
35
+ def fallback_warning(exchange, symbol=None):
36
+ logger.warning(f"{exchange}:{symbol}: Fallback to HTTP API call")
35
37
 
36
38
  def truncate(f, n):
37
39
  return math.floor(f * 10 ** n) / 10 ** n
@@ -59,29 +61,16 @@ class Client:
59
61
  self.master_name = acc['master_name']
60
62
  self.two_fa = acc['two_fa']
61
63
  #
62
- self.session = aiohttp.ClientSession()
63
- client_init_params = {
64
- 'api_key': self.api_key,
65
- 'api_secret': self.api_secret,
66
- 'passphrase': self.passphrase,
67
- 'endpoint': self.endpoint_api_auth,
68
- 'session': self.session,
69
- 'exchange': self.exchange,
70
- 'sub_account': self.sub_account,
71
- 'test_net': self.test_net
72
- }
73
- if self.exchange == 'binance':
74
- self.http = ClientBinance(client_init_params)
75
- elif self.exchange == 'bitfinex':
76
- self.http = ClientBFX(client_init_params)
77
- elif self.exchange == 'huobi':
78
- self.http = ClientHBP(client_init_params)
79
- elif self.exchange == 'okx':
80
- self.http = ClientOKX(client_init_params)
81
- elif self.exchange == 'bybit':
82
- self.http = ClientBybit(client_init_params)
83
- else:
84
- raise UserWarning(f"Exchange {self.exchange} not yet connected")
64
+ self.http = HttpClient(
65
+ {
66
+ 'api_key': self.api_key,
67
+ 'api_secret': self.api_secret,
68
+ 'passphrase': self.passphrase,
69
+ 'endpoint': self.endpoint_api_auth,
70
+ 'exchange': self.exchange,
71
+ 'test_net': self.test_net
72
+ }
73
+ )
85
74
 
86
75
  if self.exchange in ('binance', 'okx', 'bitfinex', 'huobi'):
87
76
  self.user_session = UserWSSession(
@@ -157,7 +146,8 @@ class Client:
157
146
  logger.info(f"Info for {self.exchange}:{symbol} loaded successfully")
158
147
 
159
148
  async def close(self):
160
- await self.session.close()
149
+ if self.http.session:
150
+ await self.http.session.close()
161
151
 
162
152
  @property
163
153
  def events(self):
@@ -825,8 +815,8 @@ class Client:
825
815
  _signed=True
826
816
  )
827
817
  if binance_res is None:
828
- logger.warning(FALLBACK_WARNING)
829
- route = "/api/v3/order/test" if test else "/api/v3/order"
818
+ fallback_warning(self.exchange, symbol)
819
+ route = "/api/v3/order/test" if test else ORDER_ENDPOINT
830
820
  binance_res = await self.http.send_api_call(route, "POST", data=params, signed=True)
831
821
  elif self.exchange == 'bitfinex':
832
822
  params = {
@@ -843,7 +833,7 @@ class Client:
843
833
  res = await self.user_session.handle_request(trade_id, "on", _params=params)
844
834
 
845
835
  if not res or (res and isinstance(res, list) and res[6] != 'SUCCESS'):
846
- logger.warning(FALLBACK_WARNING)
836
+ fallback_warning(self.exchange, symbol)
847
837
  logger.debug(f"create_order.bitfinex {new_client_order_id}: {res}")
848
838
  res = await self.http.send_api_call(
849
839
  "v2/auth/w/order/submit",
@@ -871,7 +861,7 @@ class Client:
871
861
  res = await self.user_session.handle_request(trade_id, "create-order", _params=params)
872
862
 
873
863
  if res is None:
874
- logger.warning(FALLBACK_WARNING)
864
+ fallback_warning(self.exchange, symbol)
875
865
  res = await self.http.send_api_call(
876
866
  "v1/order/orders/place",
877
867
  method="POST",
@@ -898,7 +888,7 @@ class Client:
898
888
  }
899
889
  res = await self.user_session.handle_request(trade_id, "order", _params=params)
900
890
  if res is None:
901
- logger.warning(FALLBACK_WARNING)
891
+ fallback_warning(self.exchange, symbol)
902
892
  res = await self.http.send_api_call(
903
893
  "/api/v5/trade/order",
904
894
  method="POST",
@@ -956,9 +946,9 @@ class Client:
956
946
  _signed=True,
957
947
  )
958
948
  if b_res is None:
959
- logger.warning(FALLBACK_WARNING)
949
+ fallback_warning(self.exchange, symbol)
960
950
  b_res = await self.http.send_api_call(
961
- "/api/v3/order",
951
+ ORDER_ENDPOINT,
962
952
  params=params,
963
953
  signed=True,
964
954
  )
@@ -1048,9 +1038,9 @@ class Client:
1048
1038
  _signed=True
1049
1039
  )
1050
1040
  if binance_res is None:
1051
- logger.warning(FALLBACK_WARNING)
1041
+ fallback_warning(self.exchange, symbol)
1052
1042
  binance_res = await self.http.send_api_call(
1053
- "/api/v3/order",
1043
+ ORDER_ENDPOINT,
1054
1044
  "DELETE",
1055
1045
  params=params,
1056
1046
  signed=True,
@@ -1063,7 +1053,7 @@ class Client:
1063
1053
  params = {'id': order_id}
1064
1054
  res = await self.user_session.handle_request(trade_id, "oc", _params=params)
1065
1055
  if res is None or (res and isinstance(res, list) and res[6] == 'ERROR'):
1066
- logger.warning(FALLBACK_WARNING)
1056
+ fallback_warning(self.exchange, symbol)
1067
1057
  logger.debug(f"cancel_order.bitfinex {order_id}: res1: {res}")
1068
1058
  res = await self.http.send_api_call(
1069
1059
  "v2/auth/w/order/cancel",
@@ -1150,7 +1140,7 @@ class Client:
1150
1140
  _signed=True
1151
1141
  )
1152
1142
  if binance_res is None:
1153
- logger.warning(FALLBACK_WARNING)
1143
+ fallback_warning(self.exchange, symbol)
1154
1144
  binance_res = await self.http.send_api_call(
1155
1145
  "/api/v3/openOrders",
1156
1146
  "DELETE",
@@ -1163,7 +1153,7 @@ class Client:
1163
1153
  params = {'id': orders_id}
1164
1154
  res = await self.user_session.handle_request(trade_id, "oc_multi", _params=params)
1165
1155
  if not res or (res and isinstance(res, list) and res[6] != 'SUCCESS'):
1166
- logger.warning(FALLBACK_WARNING)
1156
+ fallback_warning(self.exchange, symbol)
1167
1157
  logger.debug(f"cancel_all_orders.bitfinex {orders_id}: res1: {res}")
1168
1158
  res = await self.http.send_api_call(
1169
1159
  "v2/auth/w/order/cancel/multi",
@@ -1183,7 +1173,7 @@ class Client:
1183
1173
  res = await self.user_session.handle_request(trade_id, "cancel", _params=params)
1184
1174
 
1185
1175
  if res is None:
1186
- logger.warning(FALLBACK_WARNING)
1176
+ fallback_warning(self.exchange, symbol)
1187
1177
  res = await self.http.send_api_call(
1188
1178
  "v1/order/orders/batchcancel",
1189
1179
  method="POST",
@@ -1273,7 +1263,7 @@ class Client:
1273
1263
  _signed=True
1274
1264
  )
1275
1265
  if binance_res is None:
1276
- logger.warning(FALLBACK_WARNING)
1266
+ fallback_warning(self.exchange, symbol)
1277
1267
  binance_res = await self.http.send_api_call(
1278
1268
  "/api/v3/openOrders",
1279
1269
  params=params,
@@ -1518,7 +1508,7 @@ class Client:
1518
1508
  _signed=True
1519
1509
  )
1520
1510
  if binance_res is None:
1521
- logger.warning(FALLBACK_WARNING)
1511
+ fallback_warning(self.exchange)
1522
1512
  binance_res = await self.http.send_api_call(
1523
1513
  "/api/v3/account",
1524
1514
  params=params,
@@ -1740,7 +1730,7 @@ class Client:
1740
1730
  _signed=True
1741
1731
  )
1742
1732
  if binance_res is None:
1743
- logger.warning(FALLBACK_WARNING)
1733
+ fallback_warning(self.exchange, symbol)
1744
1734
  binance_res = await self.http.send_api_call(
1745
1735
  "/api/v3/myTrades",
1746
1736
  params=params,
@@ -25,17 +25,17 @@ from exchanges_wrapper.lib import (
25
25
  REST_RATE_LIMIT_INTERVAL,
26
26
  FILTER_TYPE_MAP,
27
27
  )
28
- from exchanges_wrapper.errors import ExchangeError
29
28
  #
30
- HEARTBEAT = 1 # Sec
29
+ HEARTBEAT = 1 # sec
31
30
  MAX_QUEUE_SIZE = 100
31
+ WSS_TICKER_TIMEOUT = 300 # sec
32
32
  #
33
33
  logger = logging.getLogger(__name__)
34
34
  formatter = logging.Formatter(fmt="[%(asctime)s: %(levelname)s] %(message)s")
35
35
  #
36
36
  fh = logging.handlers.RotatingFileHandler(LOG_FILE, maxBytes=1000000, backupCount=10)
37
37
  fh.setFormatter(formatter)
38
- fh.setLevel(logging.INFO)
38
+ fh.setLevel(logging.DEBUG)
39
39
  #
40
40
  sh = logging.StreamHandler()
41
41
  sh.setFormatter(formatter)
@@ -91,6 +91,7 @@ class OpenClient:
91
91
  class Martin(mr.MartinBase):
92
92
  rate_limit_reached_time = None
93
93
  rate_limiter = None
94
+ ticker_update_time = {}
94
95
 
95
96
  async def rate_limit_control(self, exchange, ts_rlc, _call_from='default'):
96
97
  if exchange == 'bitfinex':
@@ -133,7 +134,7 @@ class Martin(mr.MartinBase):
133
134
  except asyncio.CancelledError:
134
135
  pass # Task cancellation should not be logged as an error
135
136
  except asyncio.exceptions.TimeoutError:
136
- await OpenClient.get_client(client_id).client.session.close()
137
+ await OpenClient.get_client(client_id).client.http.session.close()
137
138
  OpenClient.remove_client(request.account_name)
138
139
  raise GRPCError(status=Status.UNAVAILABLE, message=f"'{open_client.name}' timeout error")
139
140
  except Exception as ex:
@@ -188,7 +189,7 @@ class Martin(mr.MartinBase):
188
189
  self.log_and_raise_grpc_error(msg_header, Status.RESOURCE_EXHAUSTED, f"RateLimitReached: {ex}")
189
190
  except errors.HTTPError as ex:
190
191
  self.log_and_raise_grpc_error(msg_header, Status.FAILED_PRECONDITION, f"HTTPError: {ex}")
191
- except ExchangeError as ex:
192
+ except errors.ExchangeError as ex:
192
193
  self.log_and_raise_grpc_error(msg_header, Status.OUT_OF_RANGE, str(ex))
193
194
  except Exception as ex:
194
195
  logger.error(f"{msg_header} exception: {ex}")
@@ -531,6 +532,7 @@ class Martin(mr.MartinBase):
531
532
  logger.info(f"OnTickerUpdate: Stop loop for {open_client.name}: {request.symbol}")
532
533
  return
533
534
  else:
535
+ Martin.ticker_update_time[request.trade_id] = time.time()
534
536
  response.from_pydict(
535
537
  {
536
538
  'openPrice': _event.open_price,
@@ -775,14 +777,19 @@ class Martin(mr.MartinBase):
775
777
 
776
778
  async def check_stream(self, request: mr.MarketRequest) -> mr.SimpleResponse:
777
779
  response = mr.SimpleResponse()
778
- response.success = False
779
- if open_client := OpenClient.get_client(request.client_id):
780
- client = open_client.client
781
- response.success = bool(client.data_streams.get(request.trade_id))
780
+ check_time = time.time() - Martin.ticker_update_time.get(request.trade_id, time.time() - WSS_TICKER_TIMEOUT - 1)
781
+ if check_time < WSS_TICKER_TIMEOUT:
782
+ response.success = True
782
783
  else:
783
- logger.warning(f"CheckStream request failed for {request.symbol}")
784
+ response.success = False
785
+ logger.warning(f"CheckStream request failed for {request.trade_id}")
784
786
  return response
785
787
 
788
+ async def client_restart(self, request: mr.MarketRequest) -> mr.SimpleResponse:
789
+ if session:=OpenClient.get_client(request.client_id).client.http.session:
790
+ await session.close()
791
+ OpenClient.remove_client(request.account_name)
792
+ return mr.SimpleResponse(success=True)
786
793
 
787
794
  async def stop_stream(client, trade_id):
788
795
  await client.stop_events_listener(trade_id)
@@ -820,7 +827,8 @@ async def amain(host: str = '127.0.0.1', port: int = 50051):
820
827
  await server.wait_closed()
821
828
 
822
829
  for oc in OpenClient.open_clients:
823
- await oc.client.session.close()
830
+ if oc.client.http.session:
831
+ await oc.client.http.session.close()
824
832
 
825
833
  [task.cancel() for task in asyncio.all_tasks() if not task.done() and task is not asyncio.current_task()]
826
834
 
@@ -829,7 +837,7 @@ def main():
829
837
  try:
830
838
  asyncio.run(amain())
831
839
  except (asyncio.exceptions.CancelledError, grpclib.exceptions.StreamTerminatedError):
832
- pass # # Task cancellation should not be logged as an error
840
+ pass # Task cancellation should not be logged as an error
833
841
  except Exception as ex:
834
842
  print(f"Exception: {ex}")
835
843
  print(traceback.format_exc())
@@ -1,7 +1,6 @@
1
1
  # Parameters for exchanges-wrapper REST API Server exch_srv.py
2
- # Copyright © 2021 Jerry Fedorenko aka VM
2
+ # Copyright © 2021-2025 Jerry Fedorenko aka VM
3
3
  # __version__ = "2.1.21"
4
- # TODO update from vertex
5
4
  # region endpoint
6
5
  [endpoint]
7
6
  [endpoint.binance]
@@ -24,12 +23,6 @@
24
23
  ws_api = 'wss://ws-api.binance.us:443/ws-api/v3'
25
24
  ws_api_test = 'wss://testnet.binance.vision/ws-api/v3'
26
25
 
27
- [endpoint.ftx]
28
- api_public = 'https://ftx.com/api'
29
- ws_public = 'wss://ftx.com/ws/'
30
- api_auth = 'https://ftx.com/api'
31
- ws_auth = 'wss://ftx.com/ws/'
32
-
33
26
  [endpoint.bitfinex]
34
27
  api_public = 'https://api-pub.bitfinex.com'
35
28
  api_auth = 'https://api.bitfinex.com'
@@ -97,8 +90,9 @@
97
90
  # endregion
98
91
 
99
92
  # region Bitfinex accounts
100
- # Set up a sub-account two-fa (2FA) key and master_email (Main account EMail)
101
- # for transfer funds from sub-account to master account
93
+ # Set up a subaccount two-fa (2FA) key and master_email (Main account EMail)
94
+ # for transfer funds from subaccount to master account
95
+
102
96
  [[accounts]]
103
97
  exchange = 'bitfinex'
104
98
  name = 'Demo - Bitfinex'