exchanges-wrapper 2.1.42__tar.gz → 2.1.45__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.
- {exchanges_wrapper-2.1.42 → exchanges_wrapper-2.1.45}/PKG-INFO +4 -4
- {exchanges_wrapper-2.1.42 → exchanges_wrapper-2.1.45}/README.md +1 -1
- {exchanges_wrapper-2.1.42 → exchanges_wrapper-2.1.45}/exchanges_wrapper/__init__.py +1 -1
- {exchanges_wrapper-2.1.42 → exchanges_wrapper-2.1.45}/exchanges_wrapper/client.py +43 -36
- {exchanges_wrapper-2.1.42 → exchanges_wrapper-2.1.45}/exchanges_wrapper/exch_srv.py +38 -39
- {exchanges_wrapper-2.1.42 → exchanges_wrapper-2.1.45}/exchanges_wrapper/parsers/okx.py +1 -0
- {exchanges_wrapper-2.1.42 → exchanges_wrapper-2.1.45}/exchanges_wrapper/web_sockets.py +4 -3
- {exchanges_wrapper-2.1.42 → exchanges_wrapper-2.1.45}/pyproject.toml +2 -2
- {exchanges_wrapper-2.1.42 → exchanges_wrapper-2.1.45}/LICENSE.md +0 -0
- {exchanges_wrapper-2.1.42 → exchanges_wrapper-2.1.45}/exchanges_wrapper/definitions.py +0 -0
- {exchanges_wrapper-2.1.42 → exchanges_wrapper-2.1.45}/exchanges_wrapper/errors.py +0 -0
- {exchanges_wrapper-2.1.42 → exchanges_wrapper-2.1.45}/exchanges_wrapper/events.py +0 -0
- {exchanges_wrapper-2.1.42 → exchanges_wrapper-2.1.45}/exchanges_wrapper/exch_srv_cfg.toml.template +0 -0
- {exchanges_wrapper-2.1.42 → exchanges_wrapper-2.1.45}/exchanges_wrapper/http_client.py +0 -0
- {exchanges_wrapper-2.1.42 → exchanges_wrapper-2.1.45}/exchanges_wrapper/lib.py +0 -0
- {exchanges_wrapper-2.1.42 → exchanges_wrapper-2.1.45}/exchanges_wrapper/martin/__init__.py +0 -0
- {exchanges_wrapper-2.1.42 → exchanges_wrapper-2.1.45}/exchanges_wrapper/parsers/bitfinex.py +0 -0
- {exchanges_wrapper-2.1.42 → exchanges_wrapper-2.1.45}/exchanges_wrapper/parsers/bybit.py +0 -0
- {exchanges_wrapper-2.1.42 → exchanges_wrapper-2.1.45}/exchanges_wrapper/parsers/huobi.py +0 -0
- {exchanges_wrapper-2.1.42 → exchanges_wrapper-2.1.45}/exchanges_wrapper/proto/martin.proto +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: exchanges-wrapper
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.45
|
|
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.10
|
|
@@ -12,10 +12,10 @@ 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.
|
|
15
|
+
Requires-Dist: crypto-ws-api==2.1.5
|
|
16
16
|
Requires-Dist: pyotp==2.9.0
|
|
17
17
|
Requires-Dist: simplejson==3.20.2
|
|
18
|
-
Requires-Dist: aiohttp~=3.13.
|
|
18
|
+
Requires-Dist: aiohttp~=3.13.2
|
|
19
19
|
Requires-Dist: expiringdict~=1.2.2
|
|
20
20
|
Requires-Dist: betterproto==2.0.0b7
|
|
21
21
|
Requires-Dist: grpclib~=0.4.8
|
|
@@ -157,7 +157,7 @@ docker run -itP \
|
|
|
157
157
|
*USDT* (TRC20) TN8F3Dz8BU8VwECRh3LTKi7FrsU8eWfsZz
|
|
158
158
|
|
|
159
159
|
## Powered by exchanges-wrapper
|
|
160
|
-
<a><img align="middle" src="https://
|
|
160
|
+
<a><img align="middle" src="https://raw.githubusercontent.com/DogsTailFarmer/martin-binance/55758a8d32b08c4deb1b34add01c5e259b88f738/doc/Modified%20martingale.svg" width="50"></a>
|
|
161
161
|
[martin-binance](https://github.com/DogsTailFarmer/martin-binance)
|
|
162
162
|
|
|
163
163
|
Free trading system for crypto exchanges SPOT markets. Adaptive customizable reverse grid strategy based on martingale.
|
|
@@ -134,7 +134,7 @@ docker run -itP \
|
|
|
134
134
|
*USDT* (TRC20) TN8F3Dz8BU8VwECRh3LTKi7FrsU8eWfsZz
|
|
135
135
|
|
|
136
136
|
## Powered by exchanges-wrapper
|
|
137
|
-
<a><img align="middle" src="https://
|
|
137
|
+
<a><img align="middle" src="https://raw.githubusercontent.com/DogsTailFarmer/martin-binance/55758a8d32b08c4deb1b34add01c5e259b88f738/doc/Modified%20martingale.svg" width="50"></a>
|
|
138
138
|
[martin-binance](https://github.com/DogsTailFarmer/martin-binance)
|
|
139
139
|
|
|
140
140
|
Free trading system for crypto exchanges SPOT markets. Adaptive customizable reverse grid strategy based on martingale.
|
|
@@ -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.
|
|
15
|
+
__version__ = "2.1.45"
|
|
16
16
|
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
import shutil
|
|
@@ -51,7 +51,7 @@ class Client:
|
|
|
51
51
|
'passphrase', 'endpoint_api_public', 'endpoint_ws_public',
|
|
52
52
|
'endpoint_api_auth', 'endpoint_ws_auth', 'endpoint_ws_api',
|
|
53
53
|
'ws_add_on', 'master_email', 'master_name', 'two_fa', 'http',
|
|
54
|
-
'user_session', '
|
|
54
|
+
'user_session', 'symbols', 'highest_precision',
|
|
55
55
|
'rate_limits', 'data_streams', 'active_orders', 'wss_buffer',
|
|
56
56
|
'stream_queue', 'on_order_update_queues', 'account_id',
|
|
57
57
|
'account_uid', 'main_account_id', 'main_account_uid',
|
|
@@ -98,7 +98,6 @@ class Client:
|
|
|
98
98
|
else:
|
|
99
99
|
self.user_session = None
|
|
100
100
|
#
|
|
101
|
-
self.loaded = False
|
|
102
101
|
self.symbols = {}
|
|
103
102
|
self.highest_precision = None
|
|
104
103
|
self.rate_limits = None
|
|
@@ -150,7 +149,6 @@ class Client:
|
|
|
150
149
|
logger.info(f"Main ByBit account UID: {self.main_account_uid}, sub-UID: {self.account_uid}")
|
|
151
150
|
# load rate limits
|
|
152
151
|
self.rate_limits = infos["rateLimits"]
|
|
153
|
-
self.loaded = True
|
|
154
152
|
logger.info(f"Info for {self.exchange}:{symbol} loaded successfully")
|
|
155
153
|
|
|
156
154
|
async def close(self):
|
|
@@ -229,7 +227,7 @@ class Client:
|
|
|
229
227
|
await self.user_session.stop(_trade_id)
|
|
230
228
|
|
|
231
229
|
def assert_symbol_exists(self, symbol):
|
|
232
|
-
if
|
|
230
|
+
if symbol not in self.symbols:
|
|
233
231
|
raise ExchangePyError(f"Symbol {symbol} is not valid according to the loaded exchange infos")
|
|
234
232
|
|
|
235
233
|
def symbol_to_bfx(self, symbol) -> str:
|
|
@@ -246,6 +244,9 @@ class Client:
|
|
|
246
244
|
symbol_info = self.symbols.get(symbol)
|
|
247
245
|
return f"{symbol_info.get('baseAsset')}-{symbol_info.get('quoteAsset')}"
|
|
248
246
|
|
|
247
|
+
def symbol_to_id(self, symbol) -> int:
|
|
248
|
+
return self.symbols.get(symbol).get('instIdCode')
|
|
249
|
+
|
|
249
250
|
def active_order(self, order_id: int, quantity="0", executed_qty="0", last_event=None):
|
|
250
251
|
quantity_decimal = Decimal(quantity)
|
|
251
252
|
executed_qty_decimal = Decimal(executed_qty)
|
|
@@ -279,32 +280,31 @@ class Client:
|
|
|
279
280
|
def refine_amount(self, symbol, amount: Union[str, Decimal], _quote=False):
|
|
280
281
|
if type(amount) is str: # to save time for developers
|
|
281
282
|
amount = Decimal(amount)
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
283
|
+
|
|
284
|
+
precision = self.symbols[symbol]["baseAssetPrecision"]
|
|
285
|
+
lot_size_filter = self.symbols[symbol]["filters"]["LOT_SIZE"]
|
|
286
|
+
step_size = Decimal(lot_size_filter["stepSize"])
|
|
287
|
+
# noinspection PyStringFormat
|
|
288
|
+
amount = (
|
|
289
|
+
(f"%.{precision}f" % truncate(amount if _quote else (amount - amount % step_size), precision))
|
|
290
|
+
.rstrip("0")
|
|
291
|
+
.rstrip(".")
|
|
292
|
+
)
|
|
292
293
|
return amount
|
|
293
294
|
|
|
294
295
|
def refine_price(self, symbol, price: Union[str, Decimal]):
|
|
295
296
|
if isinstance(price, str): # to save time for developers
|
|
296
297
|
price = Decimal(price)
|
|
297
298
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
)
|
|
299
|
+
precision = self.symbols[symbol]["baseAssetPrecision"]
|
|
300
|
+
price_filter = self.symbols[symbol]["filters"]["PRICE_FILTER"]
|
|
301
|
+
price = price - (price % Decimal(price_filter["tickSize"]))
|
|
302
|
+
# noinspection PyStringFormat
|
|
303
|
+
price = (
|
|
304
|
+
(f"%.{precision}f" % truncate(price, precision))
|
|
305
|
+
.rstrip("0")
|
|
306
|
+
.rstrip(".")
|
|
307
|
+
)
|
|
308
308
|
return price
|
|
309
309
|
|
|
310
310
|
def assert_symbol(self, symbol):
|
|
@@ -861,7 +861,7 @@ class Client:
|
|
|
861
861
|
self.active_order(int(res), quantity, binance_res['executedQty'])
|
|
862
862
|
elif self.exchange == 'okx':
|
|
863
863
|
params = {
|
|
864
|
-
"
|
|
864
|
+
"instIdCode": self.symbol_to_id(symbol),
|
|
865
865
|
"tdMode": "cash",
|
|
866
866
|
"clOrdId": new_client_order_id,
|
|
867
867
|
"side": side.lower(),
|
|
@@ -870,6 +870,7 @@ class Client:
|
|
|
870
870
|
"px": price,
|
|
871
871
|
}
|
|
872
872
|
res = await self.user_session.handle_request(trade_id, "order", _params=params)
|
|
873
|
+
params["instId"] = self.symbol_to_okx(symbol)
|
|
873
874
|
if res is None:
|
|
874
875
|
fallback_warning(self.exchange, symbol)
|
|
875
876
|
res = await self.http.send_api_call(
|
|
@@ -1074,19 +1075,19 @@ class Client:
|
|
|
1074
1075
|
_queue = asyncio.Queue()
|
|
1075
1076
|
self.on_order_update_queues.update({f"{_symbol}{order_id}": _queue})
|
|
1076
1077
|
params = {
|
|
1077
|
-
"
|
|
1078
|
+
"instIdCode": self.symbol_to_id(symbol),
|
|
1078
1079
|
"ordId": str(order_id),
|
|
1079
1080
|
"clOrdId": str(origin_client_order_id),
|
|
1080
1081
|
}
|
|
1081
|
-
_res = (
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1082
|
+
_res = await self.user_session.handle_request(trade_id, "cancel-order", _params=params)
|
|
1083
|
+
if _res is None:
|
|
1084
|
+
params["instId"] = self.symbol_to_okx(symbol)
|
|
1085
|
+
_res = await self.http.send_api_call(
|
|
1086
|
+
"/api/v5/trade/cancel-order",
|
|
1087
|
+
method="POST",
|
|
1088
|
+
signed=True,
|
|
1089
|
+
**params,
|
|
1090
|
+
)
|
|
1090
1091
|
if _res[0].get('sCode') != '0':
|
|
1091
1092
|
raise UserWarning(_res[0].get('sMsg'))
|
|
1092
1093
|
try:
|
|
@@ -1183,7 +1184,13 @@ class Client:
|
|
|
1183
1184
|
for order in orders:
|
|
1184
1185
|
order['status'] = 'CANCELED'
|
|
1185
1186
|
orders_canceled.append(order)
|
|
1186
|
-
params.append(
|
|
1187
|
+
params.append(
|
|
1188
|
+
{
|
|
1189
|
+
"instIdCode": self.symbol_to_id(symbol),
|
|
1190
|
+
'instId': _symbol,
|
|
1191
|
+
'ordId': order.get('orderId')
|
|
1192
|
+
}
|
|
1193
|
+
)
|
|
1187
1194
|
if i >= 19:
|
|
1188
1195
|
break
|
|
1189
1196
|
i += 1
|
|
@@ -8,6 +8,8 @@ import grpclib.exceptions
|
|
|
8
8
|
from exchanges_wrapper import __version__ as VER_EW
|
|
9
9
|
# noinspection PyPep8Naming
|
|
10
10
|
from crypto_ws_api import __version__ as VER_CW
|
|
11
|
+
from crypto_ws_api.ws_session import set_logger
|
|
12
|
+
|
|
11
13
|
import time
|
|
12
14
|
import weakref
|
|
13
15
|
import gc
|
|
@@ -16,8 +18,9 @@ import asyncio
|
|
|
16
18
|
import functools
|
|
17
19
|
# noinspection PyPackageRequirements
|
|
18
20
|
import ujson as json
|
|
19
|
-
import logging
|
|
21
|
+
import logging
|
|
20
22
|
from decimal import Decimal
|
|
23
|
+
import ctypes, ctypes.util
|
|
21
24
|
|
|
22
25
|
import exchanges_wrapper.martin as mr
|
|
23
26
|
from exchanges_wrapper import WORK_PATH, LOG_FILE, errors, Server, Status, GRPCError, graceful_exit
|
|
@@ -41,24 +44,12 @@ HEARTBEAT = 1 # sec
|
|
|
41
44
|
MAX_QUEUE_SIZE = 100
|
|
42
45
|
WSS_TICKER_TIMEOUT = 600 # sec
|
|
43
46
|
#
|
|
44
|
-
logger = logging.
|
|
45
|
-
formatter = logging.Formatter(fmt="[%(asctime)s: %(levelname)s] %(message)s")
|
|
46
|
-
#
|
|
47
|
-
fh = logging.handlers.RotatingFileHandler(LOG_FILE, maxBytes=1000000, backupCount=10)
|
|
48
|
-
fh.setFormatter(formatter)
|
|
49
|
-
fh.setLevel(logging.DEBUG)
|
|
50
|
-
#
|
|
51
|
-
sh = logging.StreamHandler()
|
|
52
|
-
sh.setFormatter(formatter)
|
|
53
|
-
sh.setLevel(logging.INFO)
|
|
54
|
-
#
|
|
55
|
-
root_logger = logging.getLogger()
|
|
56
|
-
root_logger.setLevel(min([fh.level, sh.level]))
|
|
57
|
-
root_logger.addHandler(fh)
|
|
58
|
-
root_logger.addHandler(sh)
|
|
59
|
-
|
|
60
|
-
logging.basicConfig()
|
|
47
|
+
logger = set_logger(__name__, LOG_FILE, file_level=logging.DEBUG, set_root_logger=True)
|
|
61
48
|
logging.getLogger('hpack').setLevel(logging.INFO)
|
|
49
|
+
logging.getLogger('grpclib.protocol').setLevel(logging.INFO)
|
|
50
|
+
|
|
51
|
+
def malloc_trim(trim_type: int = 0):
|
|
52
|
+
ctypes.CDLL(ctypes.util.find_library('c')).malloc_trim(trim_type)
|
|
62
53
|
|
|
63
54
|
|
|
64
55
|
class OpenClient:
|
|
@@ -94,9 +85,9 @@ class OpenClient:
|
|
|
94
85
|
return res
|
|
95
86
|
|
|
96
87
|
@classmethod
|
|
97
|
-
def remove_client(cls,
|
|
98
|
-
|
|
99
|
-
|
|
88
|
+
def remove_client(cls, _id):
|
|
89
|
+
# noinspection PyTypeHints
|
|
90
|
+
cls.open_clients[:] = [i for i in cls.open_clients if id(i) != _id]
|
|
100
91
|
|
|
101
92
|
# noinspection PyPep8Naming,PyMethodMayBeStatic
|
|
102
93
|
class Martin(mr.MartinBase):
|
|
@@ -144,7 +135,7 @@ class Martin(mr.MartinBase):
|
|
|
144
135
|
await asyncio.wait_for(open_client.client.load(request.symbol), timeout=HEARTBEAT * 60)
|
|
145
136
|
except asyncio.exceptions.TimeoutError:
|
|
146
137
|
await OpenClient.get_client(client_id).client.http.close_session()
|
|
147
|
-
OpenClient.remove_client(
|
|
138
|
+
OpenClient.remove_client(client_id)
|
|
148
139
|
raise GRPCError(status=Status.UNAVAILABLE, message=f"'{open_client.name}' timeout error")
|
|
149
140
|
except Exception as ex:
|
|
150
141
|
logger.warning(f"OpenClientConnection for '{open_client.name}' exception: {ex}")
|
|
@@ -258,7 +249,7 @@ class Martin(mr.MartinBase):
|
|
|
258
249
|
|
|
259
250
|
async def fetch_order(self, request: mr.FetchOrderRequest) -> mr.FetchOrderResponse:
|
|
260
251
|
response = mr.FetchOrderResponse()
|
|
261
|
-
res,
|
|
252
|
+
res, client, msg_header = await self.send_request(
|
|
262
253
|
'fetch_order',
|
|
263
254
|
request,
|
|
264
255
|
rate_limit=True,
|
|
@@ -269,14 +260,15 @@ class Martin(mr.MartinBase):
|
|
|
269
260
|
)
|
|
270
261
|
logger.debug(f"{msg_header}: {res}")
|
|
271
262
|
|
|
272
|
-
if
|
|
273
|
-
request.
|
|
274
|
-
|
|
263
|
+
if _queue := client.on_order_update_queues.get(request.trade_id):
|
|
264
|
+
if res and request.filled_update_call and Decimal(res['executedQty']):
|
|
265
|
+
request.order_id = res['orderId']
|
|
266
|
+
await self.create_trade_stream_event(request, res, _queue)
|
|
275
267
|
response.from_pydict(res)
|
|
276
268
|
return response
|
|
277
269
|
|
|
278
|
-
async def create_trade_stream_event(self, request, order):
|
|
279
|
-
trades,
|
|
270
|
+
async def create_trade_stream_event(self, request, order, _queue):
|
|
271
|
+
trades, _, msg_header = await self.send_request(
|
|
280
272
|
'fetch_order_trade_list',
|
|
281
273
|
request,
|
|
282
274
|
trade_id=request.trade_id,
|
|
@@ -284,8 +276,6 @@ class Martin(mr.MartinBase):
|
|
|
284
276
|
order_id=request.order_id
|
|
285
277
|
)
|
|
286
278
|
|
|
287
|
-
_queue = client.on_order_update_queues.get(request.trade_id)
|
|
288
|
-
|
|
289
279
|
for trade in trades:
|
|
290
280
|
trade['updateTime'] = trade.pop('time')
|
|
291
281
|
trade |= {
|
|
@@ -529,10 +519,12 @@ class Martin(mr.MartinBase):
|
|
|
529
519
|
_event_type = f"{_symbol}@miniTicker"
|
|
530
520
|
client.events.register_event(functools.partial(event_handler, _queue, client, request.trade_id, _event_type),
|
|
531
521
|
_event_type, client.exchange, request.trade_id)
|
|
522
|
+
Martin.ticker_update_time[request.trade_id] = time.time()
|
|
532
523
|
while True:
|
|
533
524
|
_event = await _queue.get()
|
|
534
525
|
if isinstance(_event, str) and _event == request.trade_id:
|
|
535
526
|
client.stream_queue.get(request.trade_id, set()).discard(_queue)
|
|
527
|
+
Martin.ticker_update_time.pop(request.trade_id, None)
|
|
536
528
|
logger.info(f"OnTickerUpdate: Stop loop for {open_client.name}: {request.symbol}")
|
|
537
529
|
return
|
|
538
530
|
else:
|
|
@@ -774,19 +766,23 @@ class Martin(mr.MartinBase):
|
|
|
774
766
|
return response
|
|
775
767
|
|
|
776
768
|
async def check_stream(self, request: mr.MarketRequest) -> mr.SimpleResponse:
|
|
777
|
-
|
|
778
|
-
check_time = time.time() -
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
769
|
+
last_update = Martin.ticker_update_time.get(request.trade_id, 0)
|
|
770
|
+
check_time = time.time() - last_update
|
|
771
|
+
success = check_time < WSS_TICKER_TIMEOUT
|
|
772
|
+
response = mr.SimpleResponse(success=success)
|
|
773
|
+
if not success:
|
|
774
|
+
Martin.ticker_update_time.pop(request.trade_id, None)
|
|
783
775
|
logger.warning(f"CheckStream request failed for {request.trade_id}")
|
|
784
776
|
return response
|
|
785
777
|
|
|
786
778
|
async def client_restart(self, request: mr.MarketRequest) -> mr.SimpleResponse:
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
779
|
+
await self.stop_stream(request)
|
|
780
|
+
if client := OpenClient.get_client(request.client_id).client:
|
|
781
|
+
if user_session := client.user_session:
|
|
782
|
+
await user_session.stop(request.trade_id)
|
|
783
|
+
if session := client.http:
|
|
784
|
+
await session.close_session()
|
|
785
|
+
OpenClient.remove_client(request.client_id)
|
|
790
786
|
return mr.SimpleResponse(success=True)
|
|
791
787
|
|
|
792
788
|
|
|
@@ -797,7 +793,9 @@ async def stop_stream_ex(client, trade_id):
|
|
|
797
793
|
await asyncio.sleep(0)
|
|
798
794
|
client.on_order_update_queues.pop(trade_id, None)
|
|
799
795
|
client.stream_queue.pop(trade_id, None)
|
|
796
|
+
Martin.ticker_update_time.pop(trade_id, None)
|
|
800
797
|
gc.collect(generation=2)
|
|
798
|
+
malloc_trim()
|
|
801
799
|
|
|
802
800
|
|
|
803
801
|
async def event_handler(_queue, client, trade_id, _event_type, event):
|
|
@@ -841,5 +839,6 @@ def main():
|
|
|
841
839
|
print(f"Exception: {expt}")
|
|
842
840
|
print(traceback.format_exc())
|
|
843
841
|
|
|
842
|
+
|
|
844
843
|
if __name__ == '__main__':
|
|
845
844
|
main()
|
|
@@ -78,6 +78,7 @@ def exchange_info(server_time: int, trading_symbol: list, tickers: list, symbol_
|
|
|
78
78
|
"isMarginTradingAllowed": False,
|
|
79
79
|
"filters": [_price_filter, _lot_size, _min_notional, _percent_price],
|
|
80
80
|
"permissions": ["SPOT"],
|
|
81
|
+
"instIdCode": market.get("instIdCode")
|
|
81
82
|
}
|
|
82
83
|
symbols.append(symbol)
|
|
83
84
|
|
|
@@ -84,7 +84,7 @@ class EventsDataStream:
|
|
|
84
84
|
except ConnectionClosed as ex:
|
|
85
85
|
self.websocket = None
|
|
86
86
|
ct = str(datetime.now(timezone.utc).replace(tzinfo=None) - start_time).rsplit('.')[0]
|
|
87
|
-
self.logger.info(f"WSS life time for {self.exchange} is {ct}")
|
|
87
|
+
self.logger.info(f"WSS life time for {self.exchange}:{self.trade_id} is {ct}")
|
|
88
88
|
if ex.rcvd and ex.rcvd.code == 4000:
|
|
89
89
|
self.logger.info(f"WSS closed for {self.exchange}:{self.trade_id}")
|
|
90
90
|
break
|
|
@@ -262,7 +262,7 @@ class EventsDataStream:
|
|
|
262
262
|
await asyncio.sleep(interval)
|
|
263
263
|
try:
|
|
264
264
|
await self.websocket.send(json.dumps({"req_id": req_id, "op": "ping"}))
|
|
265
|
-
except (ConnectionClosed, asyncio.exceptions.TimeoutError):
|
|
265
|
+
except (ConnectionClosed, asyncio.exceptions.TimeoutError, AttributeError):
|
|
266
266
|
break
|
|
267
267
|
|
|
268
268
|
async def htx_keepalive(self, interval=60):
|
|
@@ -274,7 +274,8 @@ class EventsDataStream:
|
|
|
274
274
|
else:
|
|
275
275
|
self.ping = 1
|
|
276
276
|
self.logger.warning("From HTX server PING timeout exceeded")
|
|
277
|
-
|
|
277
|
+
if self.websocket:
|
|
278
|
+
await self.websocket.close()
|
|
278
279
|
|
|
279
280
|
|
|
280
281
|
class MarketEventsDataStream(EventsDataStream):
|
|
@@ -20,10 +20,10 @@ dynamic = ["version", "description"]
|
|
|
20
20
|
requires-python = ">=3.10"
|
|
21
21
|
|
|
22
22
|
dependencies = [
|
|
23
|
-
"crypto-ws-api==2.1.
|
|
23
|
+
"crypto-ws-api==2.1.5",
|
|
24
24
|
"pyotp==2.9.0",
|
|
25
25
|
"simplejson==3.20.2",
|
|
26
|
-
"aiohttp~=3.13.
|
|
26
|
+
"aiohttp~=3.13.2",
|
|
27
27
|
"expiringdict~=1.2.2",
|
|
28
28
|
"betterproto==2.0.0b7",
|
|
29
29
|
"grpclib~=0.4.8"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{exchanges_wrapper-2.1.42 → exchanges_wrapper-2.1.45}/exchanges_wrapper/exch_srv_cfg.toml.template
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|