exchanges-wrapper 2.1.40__tar.gz → 2.1.41__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.40 → exchanges_wrapper-2.1.41}/PKG-INFO +6 -3
- {exchanges_wrapper-2.1.40 → exchanges_wrapper-2.1.41}/README.md +4 -1
- {exchanges_wrapper-2.1.40 → exchanges_wrapper-2.1.41}/exchanges_wrapper/__init__.py +12 -2
- {exchanges_wrapper-2.1.40 → exchanges_wrapper-2.1.41}/exchanges_wrapper/client.py +2 -2
- {exchanges_wrapper-2.1.40 → exchanges_wrapper-2.1.41}/exchanges_wrapper/exch_srv.py +4 -4
- {exchanges_wrapper-2.1.40 → exchanges_wrapper-2.1.41}/exchanges_wrapper/http_client.py +2 -2
- {exchanges_wrapper-2.1.40 → exchanges_wrapper-2.1.41}/exchanges_wrapper/parsers/bitfinex.py +29 -11
- {exchanges_wrapper-2.1.40 → exchanges_wrapper-2.1.41}/exchanges_wrapper/parsers/bybit.py +2 -2
- {exchanges_wrapper-2.1.40 → exchanges_wrapper-2.1.41}/exchanges_wrapper/parsers/okx.py +9 -4
- {exchanges_wrapper-2.1.40 → exchanges_wrapper-2.1.41}/pyproject.toml +1 -1
- {exchanges_wrapper-2.1.40 → exchanges_wrapper-2.1.41}/LICENSE.md +0 -0
- {exchanges_wrapper-2.1.40 → exchanges_wrapper-2.1.41}/exchanges_wrapper/definitions.py +0 -0
- {exchanges_wrapper-2.1.40 → exchanges_wrapper-2.1.41}/exchanges_wrapper/errors.py +0 -0
- {exchanges_wrapper-2.1.40 → exchanges_wrapper-2.1.41}/exchanges_wrapper/events.py +0 -0
- {exchanges_wrapper-2.1.40 → exchanges_wrapper-2.1.41}/exchanges_wrapper/exch_srv_cfg.toml.template +0 -0
- {exchanges_wrapper-2.1.40 → exchanges_wrapper-2.1.41}/exchanges_wrapper/lib.py +0 -0
- {exchanges_wrapper-2.1.40 → exchanges_wrapper-2.1.41}/exchanges_wrapper/martin/__init__.py +0 -0
- {exchanges_wrapper-2.1.40 → exchanges_wrapper-2.1.41}/exchanges_wrapper/parsers/huobi.py +0 -0
- {exchanges_wrapper-2.1.40 → exchanges_wrapper-2.1.41}/exchanges_wrapper/proto/martin.proto +0 -0
- {exchanges_wrapper-2.1.40 → exchanges_wrapper-2.1.41}/exchanges_wrapper/web_sockets.py +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.41
|
|
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,7 +12,7 @@ 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.0
|
|
15
|
+
Requires-Dist: crypto-ws-api==2.1.0
|
|
16
16
|
Requires-Dist: pyotp==2.9.0
|
|
17
17
|
Requires-Dist: simplejson==3.20.1
|
|
18
18
|
Requires-Dist: aiohttp~=3.12.13
|
|
@@ -28,12 +28,15 @@ Project-URL: Source, https://github.com/DogsTailFarmer/exchanges-wrapper
|
|
|
28
28
|
<h3 align="center">For SPOT markets</h3>
|
|
29
29
|
|
|
30
30
|
***
|
|
31
|
-
<a href="https://pypi.org/project/exchanges-wrapper/"><img src="https://img.shields.io/pypi/v/exchanges-wrapper" alt="PyPI version"></a>
|
|
31
|
+
<h1 align="center"><a href="https://pypi.org/project/exchanges-wrapper/"><img src="https://img.shields.io/pypi/v/exchanges-wrapper" alt="PyPI version"></a>
|
|
32
32
|
<a href="https://deepsource.io/gh/DogsTailFarmer/exchanges-wrapper/?ref=repository-badge}" target="_blank"><img alt="DeepSource" title="DeepSource" src="https://deepsource.io/gh/DogsTailFarmer/exchanges-wrapper.svg/?label=resolved+issues&token=XuG5PmzMiKlDL921-qREIuX_"/></a>
|
|
33
33
|
<a href="https://deepsource.io/gh/DogsTailFarmer/exchanges-wrapper/?ref=repository-badge}" target="_blank"><img alt="DeepSource" title="DeepSource" src="https://deepsource.io/gh/DogsTailFarmer/exchanges-wrapper.svg/?label=active+issues&token=XuG5PmzMiKlDL921-qREIuX_"/></a>
|
|
34
34
|
<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>
|
|
35
35
|
<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>
|
|
36
|
+
</h1>
|
|
37
|
+
|
|
36
38
|
***
|
|
39
|
+
|
|
37
40
|
On `Binance`: now API key type [Ed25519](https://www.binance.com/en/support/faq/detail/6b9a63f1e3384cf48a2eedb82767a69a) is used instead of `HMAC`
|
|
38
41
|
|
|
39
42
|
From `2.1.34` must be updated `exch_srv_cfg.toml` from `exchanges_wrapper/exch_srv_cfg.toml.template`
|
|
@@ -5,12 +5,15 @@
|
|
|
5
5
|
<h3 align="center">For SPOT markets</h3>
|
|
6
6
|
|
|
7
7
|
***
|
|
8
|
-
<a href="https://pypi.org/project/exchanges-wrapper/"><img src="https://img.shields.io/pypi/v/exchanges-wrapper" alt="PyPI version"></a>
|
|
8
|
+
<h1 align="center"><a href="https://pypi.org/project/exchanges-wrapper/"><img src="https://img.shields.io/pypi/v/exchanges-wrapper" alt="PyPI version"></a>
|
|
9
9
|
<a href="https://deepsource.io/gh/DogsTailFarmer/exchanges-wrapper/?ref=repository-badge}" target="_blank"><img alt="DeepSource" title="DeepSource" src="https://deepsource.io/gh/DogsTailFarmer/exchanges-wrapper.svg/?label=resolved+issues&token=XuG5PmzMiKlDL921-qREIuX_"/></a>
|
|
10
10
|
<a href="https://deepsource.io/gh/DogsTailFarmer/exchanges-wrapper/?ref=repository-badge}" target="_blank"><img alt="DeepSource" title="DeepSource" src="https://deepsource.io/gh/DogsTailFarmer/exchanges-wrapper.svg/?label=active+issues&token=XuG5PmzMiKlDL921-qREIuX_"/></a>
|
|
11
11
|
<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>
|
|
12
12
|
<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>
|
|
13
|
+
</h1>
|
|
14
|
+
|
|
13
15
|
***
|
|
16
|
+
|
|
14
17
|
On `Binance`: now API key type [Ed25519](https://www.binance.com/en/support/faq/detail/6b9a63f1e3384cf48a2eedb82767a69a) is used instead of `HMAC`
|
|
15
18
|
|
|
16
19
|
From `2.1.34` must be updated `exch_srv_cfg.toml` from `exchanges_wrapper/exch_srv_cfg.toml.template`
|
|
@@ -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.41"
|
|
16
16
|
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
import shutil
|
|
@@ -23,7 +23,17 @@ from grpclib.utils import graceful_exit
|
|
|
23
23
|
from grpclib import exceptions
|
|
24
24
|
|
|
25
25
|
__all__ = [
|
|
26
|
-
'
|
|
26
|
+
'__version__',
|
|
27
|
+
'Server',
|
|
28
|
+
'GRPCError',
|
|
29
|
+
'Status',
|
|
30
|
+
'Channel',
|
|
31
|
+
'graceful_exit',
|
|
32
|
+
'exceptions',
|
|
33
|
+
'LOG_PATH',
|
|
34
|
+
'WORK_PATH',
|
|
35
|
+
'LOG_FILE',
|
|
36
|
+
'CONFIG_FILE'
|
|
27
37
|
]
|
|
28
38
|
|
|
29
39
|
WORK_PATH = Path(Path.home(), ".MartinBinance")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
|
-
from typing import Union
|
|
2
|
+
from typing import Union, Dict
|
|
3
3
|
import decimal
|
|
4
4
|
import math
|
|
5
5
|
import asyncio
|
|
@@ -587,7 +587,7 @@ class Client:
|
|
|
587
587
|
"/api/v3/klines", params=params, signed=False
|
|
588
588
|
)
|
|
589
589
|
elif self.exchange == 'bitfinex':
|
|
590
|
-
params = {'limit': limit, 'sort': -1}
|
|
590
|
+
params: Dict[str, Union[str, int]] = {'limit': limit, 'sort': -1}
|
|
591
591
|
if start_time:
|
|
592
592
|
params["start"] = str(start_time)
|
|
593
593
|
if end_time:
|
|
@@ -4,7 +4,9 @@ from typing import Any, AsyncGenerator
|
|
|
4
4
|
|
|
5
5
|
import grpclib.exceptions
|
|
6
6
|
|
|
7
|
+
# noinspection PyPep8Naming
|
|
7
8
|
from exchanges_wrapper import __version__ as VER_EW
|
|
9
|
+
# noinspection PyPep8Naming
|
|
8
10
|
from crypto_ws_api import __version__ as VER_CW
|
|
9
11
|
import time
|
|
10
12
|
import weakref
|
|
@@ -136,8 +138,6 @@ class Martin(mr.MartinBase):
|
|
|
136
138
|
|
|
137
139
|
try:
|
|
138
140
|
await asyncio.wait_for(open_client.client.load(request.symbol), timeout=HEARTBEAT * 60)
|
|
139
|
-
except asyncio.CancelledError:
|
|
140
|
-
pass # Task cancellation should not be logged as an error
|
|
141
141
|
except asyncio.exceptions.TimeoutError:
|
|
142
142
|
await OpenClient.get_client(client_id).client.http.session.close()
|
|
143
143
|
OpenClient.remove_client(request.account_name)
|
|
@@ -186,7 +186,7 @@ class Martin(mr.MartinBase):
|
|
|
186
186
|
|
|
187
187
|
try:
|
|
188
188
|
res = await asyncio.wait_for(getattr(client, client_method_name)(**kwargs), timeout=90)
|
|
189
|
-
except
|
|
189
|
+
except KeyboardInterrupt:
|
|
190
190
|
raise GRPCError(status=Status.UNAVAILABLE, message=f"{msg_header} Server Shutdown")
|
|
191
191
|
except asyncio.exceptions.TimeoutError:
|
|
192
192
|
self.log_and_raise_grpc_error(msg_header, Status.DEADLINE_EXCEEDED, "timeout error")
|
|
@@ -834,7 +834,7 @@ async def amain(host: str = '127.0.0.1', port: int = 50051):
|
|
|
834
834
|
def main():
|
|
835
835
|
try:
|
|
836
836
|
asyncio.run(amain())
|
|
837
|
-
except
|
|
837
|
+
except grpclib.exceptions.StreamTerminatedError:
|
|
838
838
|
pass # Task cancellation should not be logged as an error
|
|
839
839
|
except Exception as ex:
|
|
840
840
|
print(f"Exception: {ex}")
|
|
@@ -56,7 +56,7 @@ class HttpClient:
|
|
|
56
56
|
async def _create_session_if_required(self):
|
|
57
57
|
if self.session is None or self.session.closed:
|
|
58
58
|
async with self._session_mutex:
|
|
59
|
-
self.session = aiohttp.ClientSession(timeout=TIMEOUT)
|
|
59
|
+
self.session = aiohttp.ClientSession(trust_env=True, timeout=TIMEOUT)
|
|
60
60
|
|
|
61
61
|
async def handle_errors(self, response, path=None):
|
|
62
62
|
if response.status >= 500:
|
|
@@ -170,7 +170,7 @@ class HttpClient:
|
|
|
170
170
|
async def _bitfinex_request(self, path, method, signed, send_api_key, endpoint, timeout, **kwargs):
|
|
171
171
|
_endpoint = endpoint or self.endpoint
|
|
172
172
|
bfx_post = (method == 'POST' and kwargs) or "params" in kwargs
|
|
173
|
-
_params = json.dumps(kwargs) if bfx_post else
|
|
173
|
+
_params = json.dumps(kwargs) if bfx_post else {}
|
|
174
174
|
url = f'{_endpoint}/{path}'
|
|
175
175
|
query_kwargs = {"headers": {"Accept": AJ}}
|
|
176
176
|
if kwargs and not bfx_post:
|
|
@@ -4,6 +4,7 @@ Parser for convert Bitfinex REST API/WSS response to Binance like result
|
|
|
4
4
|
import time
|
|
5
5
|
from decimal import Decimal
|
|
6
6
|
import logging
|
|
7
|
+
from typing import Dict, List, Union
|
|
7
8
|
|
|
8
9
|
logger = logging.getLogger(__name__)
|
|
9
10
|
|
|
@@ -21,9 +22,9 @@ class OrderBook:
|
|
|
21
22
|
self.asks[str(i[0])] = str(abs(i[2]))
|
|
22
23
|
|
|
23
24
|
def get_book(self) -> dict:
|
|
24
|
-
bids = list(
|
|
25
|
+
bids = [list(item) for item in self.bids.items()]
|
|
25
26
|
bids.sort(key=lambda x: float(x[0]), reverse=True)
|
|
26
|
-
asks = list(
|
|
27
|
+
asks = [list(item) for item in self.asks.items()]
|
|
27
28
|
asks.sort(key=lambda x: float(x[0]), reverse=False)
|
|
28
29
|
return {
|
|
29
30
|
'stream': f"{self.symbol}@depth5",
|
|
@@ -270,15 +271,32 @@ def orders(res: list, response_type=None, cancelled=False) -> list:
|
|
|
270
271
|
return binance_orders
|
|
271
272
|
|
|
272
273
|
|
|
273
|
-
def order_book(res:
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
274
|
+
def order_book(res: List[List[float]]) -> Dict[str, Union[int, List[List[str]]]]:
|
|
275
|
+
"""
|
|
276
|
+
Processes a list of raw order book entries into a structured dictionary.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
res: A list of order entries, where each entry is a list like
|
|
280
|
+
[price (float), quantity (float), side_indicator (float)].
|
|
281
|
+
side_indicator > 0 for bids, < 0 for asks.
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
A dictionary representing the order book with 'lastUpdateId', 'bids', and 'asks'.
|
|
285
|
+
"""
|
|
286
|
+
binance_order_book: Dict[str, Union[int, List[List[str]]]] = {
|
|
287
|
+
"lastUpdateId": int(time.time() * 1000)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
bids: List[List[str]] = []
|
|
291
|
+
asks: List[List[str]] = []
|
|
292
|
+
|
|
277
293
|
for i in res:
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
294
|
+
# Assuming i[2] determines bid/ask side and its absolute value is the quantity
|
|
295
|
+
if i[2] > 0: # This means it's a bid (positive quantity indicator)
|
|
296
|
+
bids.append([str(i[0]), str(i[2])]) # price, quantity
|
|
297
|
+
else: # This means it's an ask (negative quantity indicator or zero)
|
|
298
|
+
asks.append([str(i[0]), str(abs(i[2]))]) # price, absolute quantity
|
|
299
|
+
|
|
282
300
|
binance_order_book['bids'] = bids
|
|
283
301
|
binance_order_book['asks'] = asks
|
|
284
302
|
return binance_order_book
|
|
@@ -435,7 +453,7 @@ def ticker(res: list, symbol: str = None) -> dict:
|
|
|
435
453
|
|
|
436
454
|
|
|
437
455
|
def on_funds_update(res: list) -> dict:
|
|
438
|
-
binance_funds = {
|
|
456
|
+
binance_funds: Dict[str, Union[str, int, List]] = {
|
|
439
457
|
'e': 'outboundAccountPosition',
|
|
440
458
|
'E': int(time.time() * 1000),
|
|
441
459
|
'u': int(time.time() * 1000),
|
|
@@ -23,8 +23,8 @@ class OrderBook:
|
|
|
23
23
|
'stream': f"{self.symbol}@depth5",
|
|
24
24
|
'data': {
|
|
25
25
|
'lastUpdateId': self.last_update_id,
|
|
26
|
-
'bids': list(
|
|
27
|
-
'asks': list(
|
|
26
|
+
'bids': [list(item) for item in self.bids.items()][:5],
|
|
27
|
+
'asks': [list(item) for item in self.asks.items()][:5],
|
|
28
28
|
},
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -3,6 +3,7 @@ Parser for convert OKX REST API/WSS V5 response to Binance like result
|
|
|
3
3
|
"""
|
|
4
4
|
import time
|
|
5
5
|
from decimal import Decimal
|
|
6
|
+
from typing import Dict, List, Union
|
|
6
7
|
import logging
|
|
7
8
|
|
|
8
9
|
logger = logging.getLogger(__name__)
|
|
@@ -214,10 +215,14 @@ def account_balances(res: list) -> dict:
|
|
|
214
215
|
return {"balances": balances}
|
|
215
216
|
|
|
216
217
|
|
|
217
|
-
def order_book(res: dict) ->
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
218
|
+
def order_book(res: dict) -> Dict[str, Union[int, List[List[str]]]]:
|
|
219
|
+
binance_order_book: Dict[str, Union[int, List[List[str]]]] = {
|
|
220
|
+
"lastUpdateId": int(time.time() * 1000)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
bids: List[List[str]] = []
|
|
224
|
+
asks: List[List[str]] = []
|
|
225
|
+
|
|
221
226
|
[asks.append(ask[:2]) for ask in res.get('asks')]
|
|
222
227
|
binance_order_book['asks'] = asks
|
|
223
228
|
[bids.append(bid[:2]) for bid in res.get('bids')]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{exchanges_wrapper-2.1.40 → exchanges_wrapper-2.1.41}/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
|