exchanges-wrapper 2.1.13__tar.gz → 2.1.16__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.13 → exchanges_wrapper-2.1.16}/PKG-INFO +1 -2
- {exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/exchanges_wrapper/__init__.py +1 -1
- {exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/exchanges_wrapper/client.py +55 -42
- {exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/exchanges_wrapper/exch_srv.py +19 -1
- {exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/exchanges_wrapper/martin/__init__.py +36 -0
- {exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/exchanges_wrapper/parsers/bybit_parser.py +0 -3
- {exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/exchanges_wrapper/parsers/okx_parser.py +1 -1
- {exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/exchanges_wrapper/proto/martin.proto +2 -0
- {exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/exchanges_wrapper/web_sockets.py +23 -8
- {exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/pyproject.toml +0 -1
- {exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/LICENSE.md +0 -0
- {exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/README.md +0 -0
- {exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/exchanges_wrapper/definitions.py +0 -0
- {exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/exchanges_wrapper/errors.py +0 -0
- {exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/exchanges_wrapper/events.py +0 -0
- {exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/exchanges_wrapper/exch_srv_cfg.toml.template +0 -0
- {exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/exchanges_wrapper/http_client.py +0 -0
- {exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/exchanges_wrapper/lib.py +0 -0
- {exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/exchanges_wrapper/parsers/bitfinex_parser.py +0 -0
- {exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/exchanges_wrapper/parsers/huobi_parser.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: exchanges-wrapper
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.16
|
|
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
|
|
@@ -16,7 +16,6 @@ Requires-Dist: grpcio==1.62.0
|
|
|
16
16
|
Requires-Dist: pyotp~=2.9.0
|
|
17
17
|
Requires-Dist: simplejson==3.19.2
|
|
18
18
|
Requires-Dist: aiohttp==3.9.5
|
|
19
|
-
Requires-Dist: Pympler~=1.0.1
|
|
20
19
|
Requires-Dist: websockets~=12.0
|
|
21
20
|
Requires-Dist: expiringdict~=1.2.2
|
|
22
21
|
Requires-Dist: ujson~=5.10.0
|
|
@@ -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.16"
|
|
16
16
|
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
import shutil
|
|
@@ -354,13 +354,7 @@ class Client:
|
|
|
354
354
|
server_time = await self.fetch_server_time()
|
|
355
355
|
params = {'symbols': symbol.lower()}
|
|
356
356
|
trading_symbol = await self.http.send_api_call("v1/settings/common/market-symbols", **params)
|
|
357
|
-
|
|
358
|
-
accounts = await self.http.send_api_call("v1/account/accounts", signed=True)
|
|
359
|
-
for account in accounts:
|
|
360
|
-
if account.get('type') == 'spot':
|
|
361
|
-
self.account_id = account.get('id')
|
|
362
|
-
break
|
|
363
|
-
self.account_uid = await self.http.send_api_call("v2/user/uid", signed=True)
|
|
357
|
+
await self.set_htx_ids()
|
|
364
358
|
binance_res = hbp.exchange_info(server_time.get('serverTime'), trading_symbol[0])
|
|
365
359
|
elif self.exchange == 'okx':
|
|
366
360
|
params = {'instType': 'SPOT'}
|
|
@@ -376,6 +370,15 @@ class Client:
|
|
|
376
370
|
# logger.info(f"fetch_exchange_info: binance_res: {binance_res}")
|
|
377
371
|
return binance_res
|
|
378
372
|
|
|
373
|
+
async def set_htx_ids(self):
|
|
374
|
+
if self.account_id is None:
|
|
375
|
+
accounts = await self.http.send_api_call("v1/account/accounts", signed=True)
|
|
376
|
+
for account in accounts:
|
|
377
|
+
if account.get('type') == 'spot':
|
|
378
|
+
self.account_id = account.get('id')
|
|
379
|
+
break
|
|
380
|
+
self.account_uid = await self.http.send_api_call("v2/user/uid", signed=True)
|
|
381
|
+
|
|
379
382
|
# MARKET DATA ENDPOINTS
|
|
380
383
|
|
|
381
384
|
# https://github.com/binance/binance-spot-api-docs/blob/master/rest-api.md#order-book
|
|
@@ -493,24 +496,24 @@ class Client:
|
|
|
493
496
|
uid=self.account_uid
|
|
494
497
|
)
|
|
495
498
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
499
|
+
if not _res:
|
|
500
|
+
# Get Transaction Log
|
|
501
|
+
params.pop('status')
|
|
502
|
+
params['accountType'] = 'UNIFIED'
|
|
503
|
+
params['category'] = 'spot'
|
|
504
|
+
params['type'] = 'TRANSFER_IN'
|
|
500
505
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
'log'
|
|
513
|
-
)
|
|
506
|
+
res, ts = await self.http.send_api_call(
|
|
507
|
+
"/v5/account/transaction-log",
|
|
508
|
+
signed=True,
|
|
509
|
+
**params
|
|
510
|
+
)
|
|
511
|
+
_res += bbt.on_balance_update(
|
|
512
|
+
res['list'],
|
|
513
|
+
ts,
|
|
514
|
+
symbol,
|
|
515
|
+
'log'
|
|
516
|
+
)
|
|
514
517
|
|
|
515
518
|
for i in _res:
|
|
516
519
|
_id = next(iter(i))
|
|
@@ -703,7 +706,8 @@ class Client:
|
|
|
703
706
|
# https://github.com/binance/binance-spot-api-docs/blob/master/rest-api.md#symbol-price-ticker
|
|
704
707
|
async def fetch_symbol_price_ticker(self, symbol=None):
|
|
705
708
|
if symbol:
|
|
706
|
-
self.
|
|
709
|
+
if not (self.exchange == 'binance' and symbol == 'BNBUSDT'):
|
|
710
|
+
self.assert_symbol_exists(symbol)
|
|
707
711
|
binance_res = {}
|
|
708
712
|
elif self.exchange in ('bitfinex', 'huobi'):
|
|
709
713
|
raise ValueError('For fetch_symbol_price_ticker() symbol parameter required')
|
|
@@ -1603,25 +1607,34 @@ class Client:
|
|
|
1603
1607
|
binance_res = bbt.funding_wallet(res["balance"])
|
|
1604
1608
|
return binance_res
|
|
1605
1609
|
|
|
1610
|
+
# https://developers.binance.com/docs/sub_account/asset-management/Transfer-to-Sub-account-of-Same-Master
|
|
1611
|
+
async def transfer_to_sub(self, email, symbol, quantity, receive_window=None):
|
|
1612
|
+
if self.exchange == 'binance':
|
|
1613
|
+
quantity = any2str(Decimal(quantity).quantize(Decimal('0.01234567'), rounding=ROUND_HALF_DOWN))
|
|
1614
|
+
params = {"toEmail": email, "asset": symbol, "amount": quantity}
|
|
1615
|
+
if receive_window:
|
|
1616
|
+
params["recvWindow"] = receive_window
|
|
1617
|
+
return await self.http.send_api_call(
|
|
1618
|
+
"/sapi/v1/sub-account/transfer/subToSub",
|
|
1619
|
+
"POST",
|
|
1620
|
+
signed=True,
|
|
1621
|
+
params=params
|
|
1622
|
+
)
|
|
1623
|
+
else:
|
|
1624
|
+
raise ValueError(f"Can't implemented for {self.exchange}")
|
|
1625
|
+
|
|
1606
1626
|
# https://binance-docs.github.io/apidocs/spot/en/#transfer-to-master-for-sub-account
|
|
1607
1627
|
async def transfer_to_master(self, symbol, quantity, receive_window=None):
|
|
1608
|
-
|
|
1609
|
-
|
|
1628
|
+
_quantity = any2str(Decimal(quantity).quantize(Decimal('0.01234567'), rounding=ROUND_HALF_DOWN))
|
|
1610
1629
|
binance_res = {}
|
|
1611
1630
|
if self.exchange == 'binance':
|
|
1612
|
-
params = {"asset": symbol, "amount": quantity}
|
|
1613
|
-
if receive_window:
|
|
1614
|
-
params["recvWindow"] = receive_window
|
|
1615
1631
|
if self.master_email:
|
|
1616
|
-
logger.info(f"Collect {
|
|
1617
|
-
|
|
1618
|
-
binance_res = await self.http.send_api_call(
|
|
1619
|
-
"/sapi/v1/sub-account/transfer/subToSub",
|
|
1620
|
-
"POST",
|
|
1621
|
-
signed=True,
|
|
1622
|
-
params=params
|
|
1623
|
-
)
|
|
1632
|
+
logger.info(f"Collect {_quantity}{symbol} to {self.master_email} sub-account")
|
|
1633
|
+
binance_res = await self.transfer_to_sub(self.master_email, symbol, quantity, receive_window)
|
|
1624
1634
|
else:
|
|
1635
|
+
params = {"asset": symbol, "amount": _quantity}
|
|
1636
|
+
if receive_window:
|
|
1637
|
+
params["recvWindow"] = receive_window
|
|
1625
1638
|
binance_res = await self.http.send_api_call(
|
|
1626
1639
|
"/sapi/v1/sub-account/transfer/subToMaster",
|
|
1627
1640
|
"POST",
|
|
@@ -1636,7 +1649,7 @@ class Client:
|
|
|
1636
1649
|
"from": "exchange",
|
|
1637
1650
|
"to": "exchange",
|
|
1638
1651
|
"currency": symbol,
|
|
1639
|
-
"amount":
|
|
1652
|
+
"amount": _quantity,
|
|
1640
1653
|
"email_dst": self.master_email,
|
|
1641
1654
|
"tfaToken": {"method": "otp", "token": totp.now()}
|
|
1642
1655
|
}
|
|
@@ -1658,7 +1671,7 @@ class Client:
|
|
|
1658
1671
|
'to-account-type': "spot",
|
|
1659
1672
|
'to-account': self.main_account_id,
|
|
1660
1673
|
'currency': symbol.lower(),
|
|
1661
|
-
'amount':
|
|
1674
|
+
'amount': _quantity
|
|
1662
1675
|
}
|
|
1663
1676
|
res = await self.http.send_api_call(
|
|
1664
1677
|
"v1/account/transfer",
|
|
@@ -1670,7 +1683,7 @@ class Client:
|
|
|
1670
1683
|
elif self.exchange == 'okx':
|
|
1671
1684
|
params = {
|
|
1672
1685
|
"ccy": symbol,
|
|
1673
|
-
"amt":
|
|
1686
|
+
"amt": _quantity,
|
|
1674
1687
|
"from": '18',
|
|
1675
1688
|
"to": '18',
|
|
1676
1689
|
"type": '3'
|
|
@@ -1692,7 +1705,7 @@ class Client:
|
|
|
1692
1705
|
params = {
|
|
1693
1706
|
'transferId': str(uuid.uuid4()),
|
|
1694
1707
|
'coin': symbol,
|
|
1695
|
-
'amount': str(math.floor(float(
|
|
1708
|
+
'amount': str(math.floor(float(_quantity) * 10 ** n) / 10 ** n),
|
|
1696
1709
|
'fromMemberId': self.account_uid,
|
|
1697
1710
|
'toMemberId': self.main_account_uid,
|
|
1698
1711
|
'fromAccountType': 'UNIFIED',
|
|
@@ -173,7 +173,7 @@ class Martin(mr.MartinBase):
|
|
|
173
173
|
# For HuobiPro get master account uid and account_id
|
|
174
174
|
main_account = get_account(open_client.client.master_name)
|
|
175
175
|
main_client = Client(*main_account)
|
|
176
|
-
await main_client.
|
|
176
|
+
await asyncio.wait_for(main_client.set_htx_ids(), timeout=HEARTBEAT * 60)
|
|
177
177
|
if main_client.account_uid and main_client.account_id:
|
|
178
178
|
open_client.client.main_account_uid = main_client.account_uid
|
|
179
179
|
open_client.client.main_account_id = main_client.account_id
|
|
@@ -767,6 +767,24 @@ class Martin(mr.MartinBase):
|
|
|
767
767
|
response.from_pydict(res)
|
|
768
768
|
return response
|
|
769
769
|
|
|
770
|
+
async def transfer_to_sub(self, request: mr.MarketRequest) -> mr.SimpleResponse:
|
|
771
|
+
response = mr.SimpleResponse()
|
|
772
|
+
response.success = False
|
|
773
|
+
|
|
774
|
+
res, _, _ = await self.send_request(
|
|
775
|
+
'transfer_to_sub',
|
|
776
|
+
request,
|
|
777
|
+
rate_limit=True,
|
|
778
|
+
email=request.data,
|
|
779
|
+
symbol=request.symbol,
|
|
780
|
+
quantity=request.amount
|
|
781
|
+
)
|
|
782
|
+
|
|
783
|
+
if res and res.get("txnId"):
|
|
784
|
+
response.success = True
|
|
785
|
+
response.result = json.dumps(res)
|
|
786
|
+
return response
|
|
787
|
+
|
|
770
788
|
async def transfer_to_master(self, request: mr.MarketRequest) -> mr.SimpleResponse:
|
|
771
789
|
response = mr.SimpleResponse()
|
|
772
790
|
response.success = False
|
|
@@ -407,6 +407,7 @@ class MarketRequest(betterproto.Message):
|
|
|
407
407
|
trade_id: str = betterproto.string_field(2)
|
|
408
408
|
symbol: str = betterproto.string_field(3)
|
|
409
409
|
amount: str = betterproto.string_field(4)
|
|
410
|
+
data: str = betterproto.string_field(5)
|
|
410
411
|
|
|
411
412
|
|
|
412
413
|
@dataclass(eq=False, repr=False)
|
|
@@ -916,6 +917,23 @@ class MartinStub(betterproto.ServiceStub):
|
|
|
916
917
|
metadata=metadata,
|
|
917
918
|
)
|
|
918
919
|
|
|
920
|
+
async def transfer_to_sub(
|
|
921
|
+
self,
|
|
922
|
+
market_request: "MarketRequest",
|
|
923
|
+
*,
|
|
924
|
+
timeout: Optional[float] = None,
|
|
925
|
+
deadline: Optional["Deadline"] = None,
|
|
926
|
+
metadata: Optional["MetadataLike"] = None
|
|
927
|
+
) -> "SimpleResponse":
|
|
928
|
+
return await self._unary_unary(
|
|
929
|
+
"/martin.Martin/TransferToSub",
|
|
930
|
+
market_request,
|
|
931
|
+
SimpleResponse,
|
|
932
|
+
timeout=timeout,
|
|
933
|
+
deadline=deadline,
|
|
934
|
+
metadata=metadata,
|
|
935
|
+
)
|
|
936
|
+
|
|
919
937
|
|
|
920
938
|
class MartinBase(ServiceBase):
|
|
921
939
|
|
|
@@ -1056,6 +1074,11 @@ class MartinBase(ServiceBase):
|
|
|
1056
1074
|
) -> "SimpleResponse":
|
|
1057
1075
|
raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
|
|
1058
1076
|
|
|
1077
|
+
async def transfer_to_sub(
|
|
1078
|
+
self, market_request: "MarketRequest"
|
|
1079
|
+
) -> "SimpleResponse":
|
|
1080
|
+
raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
|
|
1081
|
+
|
|
1059
1082
|
async def __rpc_cancel_all_orders(
|
|
1060
1083
|
self, stream: "grpclib.server.Stream[MarketRequest, SimpleResponse]"
|
|
1061
1084
|
) -> None:
|
|
@@ -1270,6 +1293,13 @@ class MartinBase(ServiceBase):
|
|
|
1270
1293
|
response = await self.transfer_to_master(request)
|
|
1271
1294
|
await stream.send_message(response)
|
|
1272
1295
|
|
|
1296
|
+
async def __rpc_transfer_to_sub(
|
|
1297
|
+
self, stream: "grpclib.server.Stream[MarketRequest, SimpleResponse]"
|
|
1298
|
+
) -> None:
|
|
1299
|
+
request = await stream.recv_message()
|
|
1300
|
+
response = await self.transfer_to_sub(request)
|
|
1301
|
+
await stream.send_message(response)
|
|
1302
|
+
|
|
1273
1303
|
def __mapping__(self) -> Dict[str, grpclib.const.Handler]:
|
|
1274
1304
|
return {
|
|
1275
1305
|
"/martin.Martin/CancelAllOrders": grpclib.const.Handler(
|
|
@@ -1434,4 +1464,10 @@ class MartinBase(ServiceBase):
|
|
|
1434
1464
|
MarketRequest,
|
|
1435
1465
|
SimpleResponse,
|
|
1436
1466
|
),
|
|
1467
|
+
"/martin.Martin/TransferToSub": grpclib.const.Handler(
|
|
1468
|
+
self.__rpc_transfer_to_sub,
|
|
1469
|
+
grpclib.const.Cardinality.UNARY_UNARY,
|
|
1470
|
+
MarketRequest,
|
|
1471
|
+
SimpleResponse,
|
|
1472
|
+
),
|
|
1437
1473
|
}
|
{exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/exchanges_wrapper/parsers/bybit_parser.py
RENAMED
|
@@ -452,7 +452,6 @@ def on_balance_update(data_in: list, ts: str, symbol: str, mode: str, uid=None)
|
|
|
452
452
|
}
|
|
453
453
|
}
|
|
454
454
|
)
|
|
455
|
-
|
|
456
455
|
elif mode == 'universal':
|
|
457
456
|
for i in data_in:
|
|
458
457
|
if i['coin'] in symbol and \
|
|
@@ -469,7 +468,6 @@ def on_balance_update(data_in: list, ts: str, symbol: str, mode: str, uid=None)
|
|
|
469
468
|
}
|
|
470
469
|
}
|
|
471
470
|
)
|
|
472
|
-
|
|
473
471
|
elif mode == 'log':
|
|
474
472
|
for i in data_in:
|
|
475
473
|
if i['currency'] in symbol:
|
|
@@ -484,7 +482,6 @@ def on_balance_update(data_in: list, ts: str, symbol: str, mode: str, uid=None)
|
|
|
484
482
|
}
|
|
485
483
|
}
|
|
486
484
|
)
|
|
487
|
-
|
|
488
485
|
return data_out
|
|
489
486
|
|
|
490
487
|
|
{exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/exchanges_wrapper/parsers/okx_parser.py
RENAMED
|
@@ -16,7 +16,7 @@ def fetch_server_time(res: []) -> {}:
|
|
|
16
16
|
def exchange_info(server_time: int, trading_symbol: [], tickers: [], symbol_t) -> {}:
|
|
17
17
|
symbols = []
|
|
18
18
|
symbols_price = {
|
|
19
|
-
pair.get('instId').replace('-', ''): Decimal(pair.get('last'))
|
|
19
|
+
pair.get('instId').replace('-', ''): Decimal(pair.get('last', '0'))
|
|
20
20
|
for pair in tickers
|
|
21
21
|
}
|
|
22
22
|
for market in trading_symbol:
|
|
@@ -38,6 +38,7 @@ service Martin {
|
|
|
38
38
|
rpc StartStream (StartStreamRequest) returns (SimpleResponse) {}
|
|
39
39
|
rpc StopStream (MarketRequest) returns (SimpleResponse) {}
|
|
40
40
|
rpc TransferToMaster(MarketRequest) returns (SimpleResponse) {}
|
|
41
|
+
rpc TransferToSub(MarketRequest) returns (SimpleResponse) {}
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
message JSONResponse {
|
|
@@ -368,6 +369,7 @@ message MarketRequest {
|
|
|
368
369
|
string trade_id = 2;
|
|
369
370
|
string symbol = 3;
|
|
370
371
|
string amount = 4;
|
|
372
|
+
string data = 5;
|
|
371
373
|
}
|
|
372
374
|
|
|
373
375
|
message StartStreamRequest {
|
|
@@ -17,10 +17,7 @@ import exchanges_wrapper.parsers.bybit_parser as bbt
|
|
|
17
17
|
from crypto_ws_api.ws_session import generate_signature
|
|
18
18
|
from exchanges_wrapper import LOG_PATH
|
|
19
19
|
|
|
20
|
-
logger = logging.getLogger(
|
|
21
|
-
|
|
22
|
-
logger_ws = logging.getLogger(__name__)
|
|
23
|
-
logger_ws.level = logging.INFO
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
24
21
|
formatter = logging.Formatter(fmt="[%(asctime)s: %(levelname)s] %(message)s")
|
|
25
22
|
#
|
|
26
23
|
fh = logging.handlers.RotatingFileHandler(Path(LOG_PATH, 'websockets.log'), maxBytes=1000000, backupCount=10)
|
|
@@ -31,8 +28,9 @@ sh = logging.StreamHandler()
|
|
|
31
28
|
sh.setFormatter(formatter)
|
|
32
29
|
sh.setLevel(logging.INFO)
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
logger.addHandler(fh)
|
|
32
|
+
logger.addHandler(sh)
|
|
33
|
+
logger.propagate = False
|
|
36
34
|
|
|
37
35
|
sys.tracebacklimit = 0
|
|
38
36
|
|
|
@@ -50,12 +48,13 @@ class EventsDataStream:
|
|
|
50
48
|
self._price = None
|
|
51
49
|
self.tasks = set()
|
|
52
50
|
self.wss_started = False
|
|
51
|
+
self.ping = 0
|
|
53
52
|
|
|
54
53
|
async def start(self):
|
|
55
54
|
ping_interval = None if self.exchange == 'huobi' else 20
|
|
56
55
|
async for self.websocket in websockets.connect(
|
|
57
56
|
self.endpoint,
|
|
58
|
-
logger=
|
|
57
|
+
logger=logger,
|
|
59
58
|
ping_interval=ping_interval
|
|
60
59
|
):
|
|
61
60
|
try:
|
|
@@ -66,7 +65,7 @@ class EventsDataStream:
|
|
|
66
65
|
logger.info(f"WSS closed for {self.exchange}:{self.trade_id}")
|
|
67
66
|
break
|
|
68
67
|
else:
|
|
69
|
-
logger.warning(f"Restart WSS for {self.exchange}")
|
|
68
|
+
logger.warning(f"Restart WSS for {self.exchange}: {ex}")
|
|
70
69
|
continue
|
|
71
70
|
except Exception as ex:
|
|
72
71
|
self.tasks_cancel()
|
|
@@ -187,9 +186,11 @@ class EventsDataStream:
|
|
|
187
186
|
logger.debug(f"Bitfinex undefined WSS: symbol: {symbol}, ch_type: {ch_type}, msg_data: {msg_data}")
|
|
188
187
|
elif self.exchange == 'huobi':
|
|
189
188
|
if ping := msg_data.get('ping'):
|
|
189
|
+
self.ping = 0
|
|
190
190
|
await self.websocket.send(json.dumps({"pong": ping}))
|
|
191
191
|
await asyncio.sleep(0)
|
|
192
192
|
elif msg_data.get('action') == 'ping':
|
|
193
|
+
self.ping = 0
|
|
193
194
|
pong = {
|
|
194
195
|
"action": "pong",
|
|
195
196
|
"data": {
|
|
@@ -229,6 +230,17 @@ class EventsDataStream:
|
|
|
229
230
|
await asyncio.sleep(interval)
|
|
230
231
|
await self.websocket.send(json.dumps({"req_id": req_id, "op": "ping"}))
|
|
231
232
|
|
|
233
|
+
async def htx_keepalive(self, interval=60):
|
|
234
|
+
await asyncio.sleep(interval * 10)
|
|
235
|
+
while True:
|
|
236
|
+
await asyncio.sleep(interval)
|
|
237
|
+
if self.ping:
|
|
238
|
+
break
|
|
239
|
+
else:
|
|
240
|
+
self.ping = 1
|
|
241
|
+
logger.warning("From HTX server PING timeout exceeded")
|
|
242
|
+
await self.websocket.close()
|
|
243
|
+
|
|
232
244
|
|
|
233
245
|
class MarketEventsDataStream(EventsDataStream):
|
|
234
246
|
def __init__(self, client, endpoint, exchange, trade_id, channel=None):
|
|
@@ -300,6 +312,8 @@ class MarketEventsDataStream(EventsDataStream):
|
|
|
300
312
|
elif ch_type == 'depth5':
|
|
301
313
|
request = {'sub': f"market.{symbol}.depth.step0"}
|
|
302
314
|
|
|
315
|
+
self.tasks_manage(self.htx_keepalive(interval=30))
|
|
316
|
+
|
|
303
317
|
await self.ws_listener(request, symbol, ch_type)
|
|
304
318
|
|
|
305
319
|
async def _handle_event(self, content, symbol=None, ch_type=str()):
|
|
@@ -404,6 +418,7 @@ class HbpPrivateEventsDataStream(EventsDataStream):
|
|
|
404
418
|
"action": "sub",
|
|
405
419
|
"ch": f"trade.clearing#{self.symbol.lower()}#0"
|
|
406
420
|
}
|
|
421
|
+
self.tasks_manage(self.htx_keepalive())
|
|
407
422
|
await self.ws_listener(request, symbol=self.symbol)
|
|
408
423
|
|
|
409
424
|
async def _handle_event(self, msg_data, *args):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/exchanges_wrapper/exch_srv_cfg.toml.template
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/exchanges_wrapper/parsers/bitfinex_parser.py
RENAMED
|
File without changes
|
{exchanges_wrapper-2.1.13 → exchanges_wrapper-2.1.16}/exchanges_wrapper/parsers/huobi_parser.py
RENAMED
|
File without changes
|