exchanges-wrapper 2.1.32__tar.gz → 2.1.34__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.32 → exchanges_wrapper-2.1.34}/PKG-INFO +13 -10
  2. {exchanges_wrapper-2.1.32 → exchanges_wrapper-2.1.34}/README.md +9 -6
  3. {exchanges_wrapper-2.1.32 → exchanges_wrapper-2.1.34}/exchanges_wrapper/__init__.py +1 -1
  4. {exchanges_wrapper-2.1.32 → exchanges_wrapper-2.1.34}/exchanges_wrapper/client.py +4 -32
  5. {exchanges_wrapper-2.1.32 → exchanges_wrapper-2.1.34}/exchanges_wrapper/exch_srv.py +1 -0
  6. {exchanges_wrapper-2.1.32 → exchanges_wrapper-2.1.34}/exchanges_wrapper/exch_srv_cfg.toml.template +22 -26
  7. {exchanges_wrapper-2.1.32 → exchanges_wrapper-2.1.34}/exchanges_wrapper/http_client.py +0 -1
  8. {exchanges_wrapper-2.1.32 → exchanges_wrapper-2.1.34}/exchanges_wrapper/lib.py +2 -5
  9. {exchanges_wrapper-2.1.32 → exchanges_wrapper-2.1.34}/exchanges_wrapper/parsers/bitfinex.py +18 -32
  10. {exchanges_wrapper-2.1.32 → exchanges_wrapper-2.1.34}/exchanges_wrapper/parsers/bybit.py +2 -3
  11. {exchanges_wrapper-2.1.32 → exchanges_wrapper-2.1.34}/exchanges_wrapper/parsers/huobi.py +4 -4
  12. {exchanges_wrapper-2.1.32 → exchanges_wrapper-2.1.34}/exchanges_wrapper/parsers/okx.py +11 -24
  13. {exchanges_wrapper-2.1.32 → exchanges_wrapper-2.1.34}/exchanges_wrapper/web_sockets.py +27 -29
  14. {exchanges_wrapper-2.1.32 → exchanges_wrapper-2.1.34}/pyproject.toml +3 -3
  15. {exchanges_wrapper-2.1.32 → exchanges_wrapper-2.1.34}/LICENSE.md +0 -0
  16. {exchanges_wrapper-2.1.32 → exchanges_wrapper-2.1.34}/exchanges_wrapper/definitions.py +0 -0
  17. {exchanges_wrapper-2.1.32 → exchanges_wrapper-2.1.34}/exchanges_wrapper/errors.py +0 -0
  18. {exchanges_wrapper-2.1.32 → exchanges_wrapper-2.1.34}/exchanges_wrapper/events.py +0 -0
  19. {exchanges_wrapper-2.1.32 → exchanges_wrapper-2.1.34}/exchanges_wrapper/martin/__init__.py +0 -0
  20. {exchanges_wrapper-2.1.32 → exchanges_wrapper-2.1.34}/exchanges_wrapper/proto/martin.proto +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: exchanges-wrapper
3
- Version: 2.1.32
3
+ Version: 2.1.34
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
- Requires-Python: >=3.9
6
+ Requires-Python: >=3.10
7
7
  Description-Content-Type: text/markdown
8
8
  Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Development Status :: 5 - Production/Stable
@@ -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.0.18
15
+ Requires-Dist: crypto-ws-api==2.0.20
16
16
  Requires-Dist: pyotp==2.9.0
17
17
  Requires-Dist: simplejson==3.20.1
18
- Requires-Dist: aiohttp~=3.11.12
18
+ Requires-Dist: aiohttp~=3.11.18
19
19
  Requires-Dist: expiringdict~=1.2.2
20
20
  Requires-Dist: betterproto==2.0.0b7
21
21
  Requires-Dist: grpclib~=0.4.7
@@ -35,7 +35,8 @@ Project-URL: Source, https://github.com/DogsTailFarmer/exchanges-wrapper
35
35
  <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>
36
36
  <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>
37
37
  ***
38
- From `2.1.21` must be updated `exch_srv_cfg.toml` from `exchanges_wrapper/exch_srv_cfg.toml.template`
38
+ On `Binance`: now API key type [Ed25519](https://www.binance.com/en/support/faq/detail/6b9a63f1e3384cf48a2eedb82767a69a) is used instead of `HMAC`
39
+ From `2.1.34` must be updated `exch_srv_cfg.toml` from `exchanges_wrapper/exch_srv_cfg.toml.template`
39
40
  ***
40
41
 
41
42
  ## exchanges-wrapper vs [binance.py](https://github.com/Th0rgal/binance.py)
@@ -75,11 +76,13 @@ at real bidding. First, run applications on the [Binance Spot Test Network](http
75
76
 
76
77
  ## Get started
77
78
  ### Prepare exchange account
78
- * Create account on [Binance](https://accounts.binance.com/en/register?ref=FXQ6HY5O) and get 10% discount on all trading fee
79
- * Create account on [Bitfinex](https://www.bitfinex.com/sign-up?refcode=v_4az2nCP) and get 6% rebate fee
80
- * Create account on [HUOBI](https://www.huobi.com/en-us/topic/double-reward/?invite_code=9uaw3223) and get 10% cashback on all trading fee
81
- * Create account on [OKX](https://www.okx.com/join/2607649) and get Mystery Boxes worth up to $10,000
82
- * Create account on [Bybit](https://www.bybit.com/invite?ref=9KEW1K) and get exclusive referral rewards
79
+ Create account on [Binance](https://accounts.binance.com/en/register?ref=FXQ6HY5O) and get 10% discount on all trading fee
80
+ Create account on [HTX](https://www.htx.com/invite/en-us/1f?invite_code=9uaw3223) From every invitee, win a Mystery Box (up to 1,500 USDT) and 30% of their trading fees.
81
+ Create account on [Bitfinex](https://www.bitfinex.com/sign-up?refcode=v_4az2nCP) and get 6% rebate fee
82
+ Create account on [OKX](https://okx.com/join/2607649) and will be in for the chance to earn up to 100 USDT
83
+ Create account on [Bybit](https://www.bybit.com/invite?ref=9KEW1K) and get exclusive referral rewards
84
+
85
+ Also, you can start strategy on [Hetzner](https://hetzner.cloud/?ref=uFdrF8nsdGMc) cloud VPS only for 4.75 € per month
83
86
 
84
87
  * For test purpose log in at [Binance Spot Test Network](https://testnet.binance.vision/)
85
88
  * Create API Key
@@ -12,7 +12,8 @@
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 `2.1.21` must be updated `exch_srv_cfg.toml` from `exchanges_wrapper/exch_srv_cfg.toml.template`
15
+ On `Binance`: now API key type [Ed25519](https://www.binance.com/en/support/faq/detail/6b9a63f1e3384cf48a2eedb82767a69a) is used instead of `HMAC`
16
+ From `2.1.34` must be updated `exch_srv_cfg.toml` from `exchanges_wrapper/exch_srv_cfg.toml.template`
16
17
  ***
17
18
 
18
19
  ## exchanges-wrapper vs [binance.py](https://github.com/Th0rgal/binance.py)
@@ -52,11 +53,13 @@ at real bidding. First, run applications on the [Binance Spot Test Network](http
52
53
 
53
54
  ## Get started
54
55
  ### Prepare exchange account
55
- * Create account on [Binance](https://accounts.binance.com/en/register?ref=FXQ6HY5O) and get 10% discount on all trading fee
56
- * Create account on [Bitfinex](https://www.bitfinex.com/sign-up?refcode=v_4az2nCP) and get 6% rebate fee
57
- * Create account on [HUOBI](https://www.huobi.com/en-us/topic/double-reward/?invite_code=9uaw3223) and get 10% cashback on all trading fee
58
- * Create account on [OKX](https://www.okx.com/join/2607649) and get Mystery Boxes worth up to $10,000
59
- * Create account on [Bybit](https://www.bybit.com/invite?ref=9KEW1K) and get exclusive referral rewards
56
+ Create account on [Binance](https://accounts.binance.com/en/register?ref=FXQ6HY5O) and get 10% discount on all trading fee
57
+ Create account on [HTX](https://www.htx.com/invite/en-us/1f?invite_code=9uaw3223) From every invitee, win a Mystery Box (up to 1,500 USDT) and 30% of their trading fees.
58
+ Create account on [Bitfinex](https://www.bitfinex.com/sign-up?refcode=v_4az2nCP) and get 6% rebate fee
59
+ Create account on [OKX](https://okx.com/join/2607649) and will be in for the chance to earn up to 100 USDT
60
+ Create account on [Bybit](https://www.bybit.com/invite?ref=9KEW1K) and get exclusive referral rewards
61
+
62
+ Also, you can start strategy on [Hetzner](https://hetzner.cloud/?ref=uFdrF8nsdGMc) cloud VPS only for 4.75 € per month
60
63
 
61
64
  * For test purpose log in at [Binance Spot Test Network](https://testnet.binance.vision/)
62
65
  * Create API Key
@@ -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.32"
15
+ __version__ = "2.1.34"
16
16
 
17
17
  from pathlib import Path
18
18
  import shutil
@@ -30,7 +30,6 @@ from crypto_ws_api.ws_session import UserWSSession
30
30
  logger = logging.getLogger(__name__)
31
31
 
32
32
  STATUS_TIMEOUT = 5 # sec, also use for lifetime limit for inactive order (Bitfinex) as 60 * STATUS_TIMEOUT
33
- USER_DATA_STREAM = "/api/v3/userDataStream"
34
33
  ORDER_ENDPOINT = "/api/v3/order"
35
34
 
36
35
  def fallback_warning(exchange, symbol=None):
@@ -161,7 +160,7 @@ class Client:
161
160
  logger.info(f"Start '{self.exchange}' user events listener for {_trade_id}")
162
161
  user_data_stream = None
163
162
  if self.exchange == 'binance':
164
- user_data_stream = await UserEventsDataStream(self, self.endpoint_ws_auth, self.exchange, _trade_id)
163
+ user_data_stream = UserEventsDataStream(self, self.endpoint_ws_api, self.exchange, _trade_id)
165
164
  elif self.exchange == 'bitfinex':
166
165
  user_data_stream = BfxPrivateEventsDataStream(self, self.endpoint_ws_auth, self.exchange, _trade_id)
167
166
  elif self.exchange == 'huobi':
@@ -185,7 +184,7 @@ class Client:
185
184
  if not timeout:
186
185
  logger.warning(f"{self.exchange} user WSS start timeout reached for {_trade_id}")
187
186
  break
188
- await asyncio.sleep(0.05)
187
+ await asyncio.sleep(0.1)
189
188
 
190
189
  async def start_market_events_listener(self, _trade_id):
191
190
  _events = self.events.registered_streams.get(self.exchange, {}).get(_trade_id, set())
@@ -525,6 +524,7 @@ class Client:
525
524
  self.ledgers_id.pop(0)
526
525
  balances.append(i[_id])
527
526
  return balances
527
+ return None
528
528
 
529
529
  # https://github.com/binance/binance-spot-api-docs/blob/master/rest-api.md#recent-trades-list
530
530
  async def fetch_recent_trades_list(self, symbol, limit=500):
@@ -773,6 +773,7 @@ class Client:
773
773
  params=params,
774
774
  signed=True,
775
775
  )
776
+ return None
776
777
 
777
778
  async def fetch_api_info(self):
778
779
  res, _ = await self.http.send_api_call("/v5/user/query-api", signed=True)
@@ -816,7 +817,6 @@ class Client:
816
817
  trade_id,
817
818
  "order.place",
818
819
  _params=params,
819
- send_api_key=True,
820
820
  _signed=True
821
821
  )
822
822
  if binance_res is None:
@@ -947,7 +947,6 @@ class Client:
947
947
  trade_id,
948
948
  "order.status",
949
949
  _params=params,
950
- send_api_key=True,
951
950
  _signed=True,
952
951
  )
953
952
  if b_res is None:
@@ -1050,7 +1049,6 @@ class Client:
1050
1049
  trade_id,
1051
1050
  "order.cancel",
1052
1051
  _params=params,
1053
- send_api_key=True,
1054
1052
  _signed=True
1055
1053
  )
1056
1054
  if binance_res is None:
@@ -1152,7 +1150,6 @@ class Client:
1152
1150
  trade_id,
1153
1151
  "openOrders.cancelAll",
1154
1152
  _params=params,
1155
- send_api_key=True,
1156
1153
  _signed=True
1157
1154
  )
1158
1155
  if binance_res is None:
@@ -1275,7 +1272,6 @@ class Client:
1275
1272
  trade_id,
1276
1273
  "openOrders.status",
1277
1274
  _params=params,
1278
- send_api_key=True,
1279
1275
  _signed=True
1280
1276
  )
1281
1277
  if binance_res is None:
@@ -1522,7 +1518,6 @@ class Client:
1522
1518
  trade_id,
1523
1519
  "account.status",
1524
1520
  _params=params,
1525
- send_api_key=True,
1526
1521
  _signed=True
1527
1522
  )
1528
1523
  if binance_res is None:
@@ -1744,7 +1739,6 @@ class Client:
1744
1739
  trade_id,
1745
1740
  "myTrades",
1746
1741
  _params=params,
1747
- send_api_key=True,
1748
1742
  _signed=True
1749
1743
  )
1750
1744
  if binance_res is None:
@@ -1839,25 +1833,3 @@ class Client:
1839
1833
  return b_res
1840
1834
 
1841
1835
  # endregion
1842
-
1843
- # USER DATA STREAM ENDPOINTS
1844
-
1845
- # https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md#create-a-listenkey
1846
- async def create_listen_key(self):
1847
- return await self.http.send_api_call(USER_DATA_STREAM, "POST")
1848
-
1849
- # https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md#close-a-listenkey
1850
- async def keep_alive_listen_key(self, listen_key):
1851
- if not listen_key:
1852
- raise ValueError("This query requires a listen_key.")
1853
- return await self.http.send_api_call(
1854
- USER_DATA_STREAM, "PUT", params={"listenKey": listen_key}
1855
- )
1856
-
1857
- # https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md#close-a-listenkey
1858
- async def close_listen_key(self, listen_key):
1859
- if not listen_key:
1860
- raise ValueError("This query requires a listen_key.")
1861
- return await self.http.send_api_call(
1862
- USER_DATA_STREAM, "DELETE", params={"listenKey": listen_key}
1863
- )
@@ -12,6 +12,7 @@ import gc
12
12
  import traceback
13
13
  import asyncio
14
14
  import functools
15
+ # noinspection PyPackageRequirements
15
16
  import ujson as json
16
17
  import logging.handlers
17
18
  from decimal import Decimal
@@ -1,28 +1,17 @@
1
1
  # Parameters for exchanges-wrapper REST API Server exch_srv.py
2
2
  # Copyright © 2021-2025 Jerry Fedorenko aka VM
3
- # __version__ = "2.1.29"
3
+ # __version__ = "2.1.34"
4
+
4
5
  # region endpoint
5
6
  [endpoint]
6
7
  [endpoint.binance]
7
8
  api_public = 'https://api.binance.com'
8
9
  api_auth = 'https://api.binance.com'
9
10
  ws_public = 'wss://stream.binance.com:9443'
10
- ws_auth = 'wss://stream.binance.com:9443'
11
11
  api_test = 'https://testnet.binance.vision'
12
- ws_test = 'wss://testnet.binance.vision/ws'
13
12
  ws_api = 'wss://ws-api.binance.com:443/ws-api/v3'
14
13
  ws_api_test = 'wss://testnet.binance.vision/ws-api/v3'
15
14
 
16
- [endpoint.binance_us]
17
- api_public = 'https://api.binance.us'
18
- api_auth = 'https://api.binance.us'
19
- ws_public = 'wss://stream.binance.us:9443'
20
- ws_auth = 'wss://stream.binance.us:9443'
21
- api_test = 'https://testnet.binance.vision'
22
- ws_test = 'wss://testnet.binance.vision/ws'
23
- ws_api = 'wss://ws-api.binance.us:443/ws-api/v3'
24
- ws_api_test = 'wss://testnet.binance.vision/ws-api/v3'
25
-
26
15
  [endpoint.bitfinex]
27
16
  api_public = 'https://api-pub.bitfinex.com'
28
17
  api_auth = 'https://api.bitfinex.com'
@@ -58,35 +47,42 @@
58
47
  # endregion
59
48
 
60
49
  # region Binance.com accounts
50
+ # How to Generate an Ed25519 Key Pair and Register then on Binance:
51
+ # https://www.binance.com/en/support/faq/detail/6b9a63f1e3384cf48a2eedb82767a69a
61
52
  [[accounts]]
62
53
  exchange = 'binance'
63
54
  name = 'Demo - Binance'
64
- api_key = '*********** Place API key there ************'
65
- api_secret = '*********** Place secret API key there ************'
55
+ api_key = '*********** Place Ed25519 API key there ************'
56
+ api_secret = """
57
+ -----BEGIN PRIVATE KEY-----
58
+ *********** Place PRIVATE API key there ************
59
+ -----END PRIVATE KEY-----
60
+ """
66
61
  test_net = true
67
62
 
68
63
  [[accounts]]
69
64
  exchange = 'binance'
70
65
  name = 'BinanceSub1'
71
- api_key = '*********** Place API key there ************'
72
- api_secret = '*********** Place secret API key there ************'
66
+ api_key = '*********** Place Ed25519 API key there ************'
67
+ api_secret = """
68
+ -----BEGIN PRIVATE KEY-----
69
+ *********** Place PRIVATE API key there ************
70
+ -----END PRIVATE KEY-----
71
+ """
73
72
  test_net = false
74
73
 
75
74
  [[accounts]]
76
75
  exchange = 'binance'
77
76
  name = 'BinanceSub2'
78
- api_key = '*********** Place API key there ************'
79
- api_secret = '*********** Place secret API key there ************'
77
+ api_key = '*********** Place Ed25519 API key there ************'
78
+ api_secret = """
79
+ -----BEGIN PRIVATE KEY-----
80
+ *********** Place PRIVATE API key there ************
81
+ -----END PRIVATE KEY-----
82
+ """
80
83
  master_email = 'sub1@mail.com' # If set, 'BinanceSub1' use for collecting assets instead of Main
81
84
  test_net = false
82
85
 
83
- # Binance.us accounts
84
- [[accounts]]
85
- exchange = 'binance_us'
86
- name = 'Binance US'
87
- api_key = '*********** Place API key there ************'
88
- api_secret = '*********** Place secret API key there ************'
89
- test_net = false
90
86
  # endregion
91
87
 
92
88
  # region Bitfinex accounts
@@ -45,7 +45,6 @@ class HttpClient:
45
45
  self.rate_limit_handler = RateLimitHandler() if self.exchange == 'bybit' else None
46
46
 
47
47
  def declare_exchanges_implementation(self):
48
- # noinspection PyTypeChecker
49
48
  self.ex_imps = {
50
49
  'binance': self._binance_request,
51
50
  'bitfinex': self._bitfinex_request,
@@ -77,9 +77,6 @@ def get_account(account_name: str) -> dict:
77
77
  ws_public = get_ws_public(endpoint, exchange, test_net)
78
78
  ws_api, ws_auth = get_ws_api_auth(endpoint, exchange, test_net)
79
79
 
80
- if exchange == 'binance_us':
81
- exchange = 'binance'
82
-
83
80
  return {
84
81
  'exchange': exchange,
85
82
  'sub_account': sub_account,
@@ -96,7 +93,7 @@ def get_account(account_name: str) -> dict:
96
93
  'master_name': master_name,
97
94
  'two_fa': account.get('two_fa'),
98
95
  'ws_api': ws_api,
99
- }
96
+ }
100
97
  return {}
101
98
 
102
99
  def get_ws_add_on(endpoint, exchange):
@@ -118,7 +115,7 @@ def get_ws_api_auth(endpoint, exchange, test_net):
118
115
  if exchange == 'bitfinex':
119
116
  ws_api = ws_auth = endpoint['ws_auth']
120
117
  else:
121
- ws_auth = endpoint['ws_test'] if test_net else endpoint['ws_auth']
118
+ ws_auth = endpoint.get('ws_test') if test_net else endpoint.get('ws_auth')
122
119
  ws_api = endpoint.get('ws_api_test') if test_net else endpoint.get('ws_api')
123
120
  if exchange == 'okx':
124
121
  ws_api = ws_auth
@@ -50,7 +50,7 @@ class OrderBook:
50
50
  return self
51
51
 
52
52
 
53
- def get_symbols(symbols_details: [], symbol) -> str:
53
+ def get_symbols(symbols_details: list, symbol) -> str:
54
54
  symbols = []
55
55
  res = ",t"
56
56
  for symbol_details in symbols_details:
@@ -86,7 +86,7 @@ def symbol_name(_pair: str) -> ():
86
86
  return pair, base_asset, quote_asset
87
87
 
88
88
 
89
- def exchange_info(symbols_details: [], tickers: [], symbol_t) -> {}:
89
+ def exchange_info(symbols_details: list, tickers: list, symbol_t) -> dict:
90
90
  symbols = []
91
91
  symbols_price = {
92
92
  pair[0].replace(':', '').upper()[1:]: pair[7] for pair in tickers
@@ -159,7 +159,7 @@ def exchange_info(symbols_details: [], tickers: [], symbol_t) -> {}:
159
159
  }
160
160
 
161
161
 
162
- def account_information(res: []) -> {}:
162
+ def account_balances(res: list) -> dict:
163
163
  balances = []
164
164
  for balance in res:
165
165
  if balance[0] == 'exchange':
@@ -172,23 +172,10 @@ def account_information(res: []) -> {}:
172
172
  "locked": locked,
173
173
  }
174
174
  balances.append(_binance_res)
175
+ return {"balances": balances}
175
176
 
176
- return {
177
- "makerCommission": 0,
178
- "takerCommission": 0,
179
- "buyerCommission": 0,
180
- "sellerCommission": 0,
181
- "canTrade": True,
182
- "canWithdraw": False,
183
- "canDeposit": False,
184
- "updateTime": int(time.time() * 1000),
185
- "accountType": "SPOT",
186
- "balances": balances,
187
- "permissions": ["SPOT"],
188
- }
189
177
 
190
-
191
- def order(res: [], response_type=None, cancelled=False) -> {}:
178
+ def order(res: list, response_type=None, cancelled=False) -> dict:
192
179
  # print(f"order.order: {res}")
193
180
  symbol = res[3][1:].replace(':', '')
194
181
  order_id = res[0]
@@ -275,7 +262,7 @@ def order(res: [], response_type=None, cancelled=False) -> {}:
275
262
  }
276
263
 
277
264
 
278
- def orders(res: [], response_type=None, cancelled=False) -> []:
265
+ def orders(res: list, response_type=None, cancelled=False) -> list:
279
266
  binance_orders = []
280
267
  for _order in res:
281
268
  i_order = order(_order, response_type=response_type, cancelled=cancelled)
@@ -283,7 +270,7 @@ def orders(res: [], response_type=None, cancelled=False) -> []:
283
270
  return binance_orders
284
271
 
285
272
 
286
- def order_book(res: []) -> {}:
273
+ def order_book(res: list) -> dict:
287
274
  binance_order_book = {"lastUpdateId": int(time.time() * 1000)}
288
275
  bids = []
289
276
  asks = []
@@ -297,7 +284,7 @@ def order_book(res: []) -> {}:
297
284
  return binance_order_book
298
285
 
299
286
 
300
- def ticker_price_change_statistics(res: [], symbol):
287
+ def ticker_price_change_statistics(res: list, symbol):
301
288
  return {
302
289
  "symbol": symbol,
303
290
  "priceChange": str(res[4]),
@@ -323,7 +310,7 @@ def ticker_price_change_statistics(res: [], symbol):
323
310
  }
324
311
 
325
312
 
326
- def fetch_symbol_price_ticker(res: [], symbol) -> {}:
313
+ def fetch_symbol_price_ticker(res: list, symbol) -> dict:
327
314
  return {
328
315
  "symbol": symbol,
329
316
  "price": str(res[6]),
@@ -348,7 +335,7 @@ def interval(_interval: str) -> int:
348
335
  return resolution.get(_interval, 0)
349
336
 
350
337
 
351
- def klines(res: [], _interval: str) -> []:
338
+ def klines(res: list, _interval: str) -> list:
352
339
  binance_klines = []
353
340
  for i in res:
354
341
  start_time = i[0]
@@ -370,7 +357,7 @@ def klines(res: [], _interval: str) -> []:
370
357
  return binance_klines
371
358
 
372
359
 
373
- def candle(res: [], symbol: str = None, ch_type: str = None) -> {}:
360
+ def candle(res: list, symbol: str = None, ch_type: str = None) -> dict:
374
361
  symbol = symbol[1:].replace(':', '')
375
362
  start_time = res[0]
376
363
  _interval = ch_type.split('_')[1]
@@ -403,7 +390,7 @@ def candle(res: [], symbol: str = None, ch_type: str = None) -> {}:
403
390
  }
404
391
 
405
392
 
406
- def account_trade_list(res: [], order_id=None) -> []:
393
+ def account_trade_list(res: list, order_id=None) -> list:
407
394
  binance_trade_list = []
408
395
  for trade in res:
409
396
  if order_id is None or order_id == trade[3]:
@@ -429,7 +416,7 @@ def account_trade_list(res: [], order_id=None) -> []:
429
416
  return binance_trade_list
430
417
 
431
418
 
432
- def ticker(res: [], symbol: str = None) -> {}:
419
+ def ticker(res: list, symbol: str = None) -> dict:
433
420
  _symbol = symbol[1:].replace(':', '').lower()
434
421
  return {
435
422
  'stream': f"{_symbol}@miniTicker",
@@ -447,7 +434,7 @@ def ticker(res: [], symbol: str = None) -> {}:
447
434
  }
448
435
 
449
436
 
450
- def on_funds_update(res: []) -> {}:
437
+ def on_funds_update(res: list) -> dict:
451
438
  binance_funds = {
452
439
  'e': 'outboundAccountPosition',
453
440
  'E': int(time.time() * 1000),
@@ -471,7 +458,7 @@ def on_funds_update(res: []) -> {}:
471
458
  return binance_funds
472
459
 
473
460
 
474
- def on_balance_update(res: []) -> {}:
461
+ def on_balance_update(res: list) -> dict:
475
462
  return {
476
463
  'e': 'balanceUpdate',
477
464
  'E': res[3],
@@ -481,7 +468,7 @@ def on_balance_update(res: []) -> {}:
481
468
  }
482
469
 
483
470
 
484
- def on_order_update(res: [], _order: {}) -> {}:
471
+ def on_order_update(res: list, _order: {}) -> dict:
485
472
  # logger.info(f"on_order_update.res: {res}, order: {_order}")
486
473
  side = 'BUY' if res[7] > 0 else 'SELL'
487
474
  #
@@ -548,7 +535,7 @@ def on_order_update(res: [], _order: {}) -> {}:
548
535
  }
549
536
 
550
537
 
551
- def on_order_trade(_order: {}) -> {}:
538
+ def on_order_trade(_order: dict) -> dict:
552
539
  # logger.info(f"on_order_trade._order: {_order}")
553
540
  event = _order['lastEvent']
554
541
  orig_qty = _order['origQty']
@@ -604,7 +591,7 @@ def on_order_trade(_order: {}) -> {}:
604
591
  }
605
592
 
606
593
 
607
- def funding_wallet(res: []) -> []:
594
+ def funding_wallet(res: list) -> list:
608
595
  balances = []
609
596
  for balance in res:
610
597
  if balance[0] in ('exchange', 'funding'):
@@ -621,7 +608,6 @@ def funding_wallet(res: []) -> []:
621
608
  "btcValuation": "0.0",
622
609
  }
623
610
  balances.append(_binance_res)
624
-
625
611
  return balances
626
612
 
627
613
 
@@ -17,7 +17,6 @@ class OrderBook:
17
17
  self.asks = {i[0]: i[1] for i in snapshot["a"]}
18
18
  self.bids = {i[0]: i[1] for i in snapshot["b"]}
19
19
 
20
- # noinspection PyTypeChecker
21
20
  def get_book(self) -> dict:
22
21
  return {
23
22
  'stream': f"{self.symbol}@depth5",
@@ -541,7 +540,7 @@ def on_balance_update(data_in: list, ts: str, symbol: str, mode: str, uid=None)
541
540
  return data_out
542
541
 
543
542
 
544
- def funding_wallet(res: []) -> []:
543
+ def funding_wallet(res: list) -> list:
545
544
  balances = []
546
545
  for balance in res:
547
546
  _free = Decimal(balance["transferBalance"])
@@ -559,7 +558,7 @@ def funding_wallet(res: []) -> []:
559
558
  return balances
560
559
 
561
560
 
562
- def order_trade_list(res: []) -> []:
561
+ def order_trade_list(res: list) -> list:
563
562
  trades = []
564
563
  for trade in reversed(res):
565
564
  trades.append(
@@ -79,7 +79,7 @@ def exchange_info(server_time: int, _symbol_params) -> {}:
79
79
  }
80
80
 
81
81
 
82
- def orders(res: [], response_type=None) -> []:
82
+ def orders(res: list, response_type=None) -> list:
83
83
  binance_orders = []
84
84
  for _order in res:
85
85
  i_order = order(_order, response_type=response_type)
@@ -341,7 +341,7 @@ def interval2value(_interval) -> int:
341
341
  return resolution.get(_interval, 0)
342
342
 
343
343
 
344
- def klines(res: [], _interval: str) -> []:
344
+ def klines(res: list, _interval: str) -> list:
345
345
  binance_klines = []
346
346
  for i in res:
347
347
  start_time = i.get('id') * 1000
@@ -363,7 +363,7 @@ def klines(res: [], _interval: str) -> []:
363
363
  return binance_klines
364
364
 
365
365
 
366
- def candle(res: [], symbol: str = None, ch_type: str = None) -> {}:
366
+ def candle(res: dict, symbol: str = None, ch_type: str = None) -> {}:
367
367
  tick = res.get('tick')
368
368
  start_time = tick.get('id')
369
369
  _interval = ch_type.split('_')[1]
@@ -473,7 +473,7 @@ def on_order_update(_order: {}) -> {}:
473
473
  }
474
474
 
475
475
 
476
- def account_trade_list(res: []) -> []:
476
+ def account_trade_list(res: list) -> list:
477
477
  binance_trade_list = []
478
478
  for trade in res:
479
479
  price = trade['price']
@@ -8,12 +8,13 @@ import logging
8
8
  logger = logging.getLogger(__name__)
9
9
 
10
10
 
11
- def fetch_server_time(res: []) -> {}:
11
+ def fetch_server_time(res: list) -> dict | None:
12
12
  if res:
13
13
  return {'serverTime': int(res[0].get('ts'))}
14
+ return None
14
15
 
15
16
 
16
- def exchange_info(server_time: int, trading_symbol: [], tickers: [], symbol_t) -> {}:
17
+ def exchange_info(server_time: int, trading_symbol: list, tickers: list, symbol_t) -> {}:
17
18
  symbols = []
18
19
  symbols_price = {}
19
20
  for pair in tickers:
@@ -88,7 +89,7 @@ def exchange_info(server_time: int, trading_symbol: [], tickers: [], symbol_t) -
88
89
  }
89
90
 
90
91
 
91
- def orders(res: [], response_type=None) -> []:
92
+ def orders(res: list, response_type=None) -> list:
92
93
  binance_orders = []
93
94
  for _order in res:
94
95
  i_order = order(_order, response_type=response_type)
@@ -201,7 +202,7 @@ def place_order_response(res: {}, req: {}) -> {}:
201
202
  }
202
203
 
203
204
 
204
- def account_information(res: [], u_time: str) -> {}:
205
+ def account_balances(res: list) -> dict:
205
206
  balances = []
206
207
  for asset in res:
207
208
  _binance_res = {
@@ -210,24 +211,10 @@ def account_information(res: [], u_time: str) -> {}:
210
211
  "locked": asset.get('frozenBal'),
211
212
  }
212
213
  balances.append(_binance_res)
213
-
214
- return {
215
- "makerCommission": 0,
216
- "takerCommission": 0,
217
- "buyerCommission": 0,
218
- "sellerCommission": 0,
219
- "canTrade": True,
220
- "canWithdraw": False,
221
- "canDeposit": False,
222
- "brokered": False,
223
- "updateTime": int(u_time),
224
- "accountType": "SPOT",
225
- "balances": balances,
226
- "permissions": ["SPOT"],
227
- }
214
+ return {"balances": balances}
228
215
 
229
216
 
230
- def order_book(res: {}) -> {}:
217
+ def order_book(res: dict) -> dict:
231
218
  asks = []
232
219
  bids = []
233
220
  binance_order_book = {"lastUpdateId": int(res.get('ts'))}
@@ -313,7 +300,7 @@ def interval(_interval: str) -> str:
313
300
  return resolution.get(_interval, 0)
314
301
 
315
302
 
316
- def klines(res: [], _interval: str) -> []:
303
+ def klines(res: list, _interval: str) -> list:
317
304
  binance_klines = []
318
305
  for i in res:
319
306
  start_time = int(i[0])
@@ -352,7 +339,7 @@ def interval2value(_interval: str) -> int:
352
339
  return resolution.get(_interval, 0)
353
340
 
354
341
 
355
- def candle(res: [], symbol: str = None, ch_type: str = None) -> {}:
342
+ def candle(res: list, symbol: str = None, ch_type: str = None) -> {}:
356
343
  symbol = symbol.replace('-', '').lower()
357
344
  start_time = int(res[0])
358
345
  _interval = ch_type.replace('kline_', '')
@@ -491,7 +478,7 @@ def on_balance_update(res: list, buffer: dict, transfer: bool) -> ():
491
478
  return res_diff, buffer
492
479
 
493
480
 
494
- def funding_wallet(res: []) -> []:
481
+ def funding_wallet(res: list) -> list:
495
482
  balances = []
496
483
  for balance in res:
497
484
  _binance_res = {
@@ -506,7 +493,7 @@ def funding_wallet(res: []) -> []:
506
493
  return balances
507
494
 
508
495
 
509
- def order_trade_list(res: []) -> []:
496
+ def order_trade_list(res: list) -> list:
510
497
  binance_trade_list = []
511
498
  for trade in res:
512
499
  price = trade['fillPx']
@@ -1,5 +1,6 @@
1
1
  import sys
2
2
  import asyncio
3
+ # noinspection PyPackageRequirements
3
4
  import ujson as json
4
5
  import logging.handlers
5
6
  from pathlib import Path
@@ -8,14 +9,16 @@ from decimal import Decimal
8
9
  import gzip
9
10
  from datetime import datetime, timezone
10
11
 
12
+ # noinspection PyPackageRequirements
11
13
  from websockets.asyncio.client import connect
14
+ # noinspection PyPackageRequirements
12
15
  from websockets import ConnectionClosed
13
16
 
14
17
  import exchanges_wrapper.parsers.bitfinex as bfx
15
18
  import exchanges_wrapper.parsers.huobi as hbp
16
19
  import exchanges_wrapper.parsers.okx as okx
17
20
  import exchanges_wrapper.parsers.bybit as bbt
18
- from crypto_ws_api.ws_session import generate_signature, compose_htx_ws_auth
21
+ from crypto_ws_api.ws_session import generate_signature, compose_htx_ws_auth, compose_binance_ws_auth
19
22
  from exchanges_wrapper import LOG_PATH
20
23
 
21
24
  logger = logging.getLogger(__name__)
@@ -55,7 +58,7 @@ class EventsDataStream:
55
58
  async for self.websocket in connect(
56
59
  self.endpoint,
57
60
  logger=logger,
58
- ping_interval=None if self.exchange == 'huobi' else 20
61
+ ping_interval=None if self.exchange in ('binance', 'huobi') else 20
59
62
  ):
60
63
  start_time = datetime.now(timezone.utc).replace(tzinfo=None)
61
64
  try:
@@ -100,7 +103,14 @@ class EventsDataStream:
100
103
  async def _handle_messages(self, msg, symbol=None, ch_type=str()):
101
104
  msg_data = json.loads(msg if isinstance(msg, str) else gzip.decompress(msg))
102
105
  if self.exchange == 'binance':
103
- await self._handle_event(msg_data)
106
+ if "stream" in msg_data:
107
+ await self._handle_event(msg_data)
108
+ elif event := msg_data.get("event"):
109
+ await self._handle_event(event)
110
+ elif msg_data.get("status") == 200:
111
+ result = msg_data.get("result")
112
+ if isinstance(result, dict) and not result:
113
+ self.wss_started = True
104
114
  elif self.exchange == 'bybit':
105
115
  if _data := msg_data.get('data'):
106
116
  if ch_type == 'depth5':
@@ -368,7 +378,7 @@ class MarketEventsDataStream(EventsDataStream):
368
378
  content = self._order_book.get_book()
369
379
  #
370
380
  stream_name = None
371
- if isinstance(content, dict) and "stream" in content:
381
+ if isinstance(content, dict):
372
382
  stream_name = content["stream"]
373
383
  content = content["data"]
374
384
  content["stream"] = stream_name
@@ -593,33 +603,21 @@ class BBTPrivateEventsDataStream(EventsDataStream):
593
603
 
594
604
 
595
605
  class UserEventsDataStream(EventsDataStream):
596
- def __init__(self, client, endpoint, exchange, trade_id):
597
- super().__init__(client, endpoint, exchange, trade_id)
598
- self.listen_key = None
599
-
600
- async def async_init(self):
601
- self.listen_key = (await self.client.create_listen_key())["listenKey"]
602
- self.endpoint = f"{self.endpoint}/ws/{self.listen_key}"
603
- self.wss_started = True
604
- return self
605
-
606
- def __await__(self):
607
- return self.async_init().__await__()
608
-
609
- async def _heartbeat(self, listen_key, interval=60 * 30):
610
- # 30 minutes is recommended according to
611
- # https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md#pingkeep-alive-a-listenkey
612
- while True:
613
- await asyncio.sleep(interval)
614
- await self.client.keep_alive_listen_key(listen_key)
615
606
 
616
607
  async def start_wss(self):
617
- logger.info(f"Start User WSS for {self.exchange}")
618
- self.tasks_manage(self._heartbeat(self.listen_key))
619
- try:
620
- await self.ws_listener()
621
- finally:
622
- self.tasks_cancel()
608
+ await self.websocket.send(
609
+ json.dumps(
610
+ compose_binance_ws_auth(self.trade_id, self.client.api_key, self.client.api_secret)
611
+ )
612
+ )
613
+ await asyncio.sleep(0)
614
+ await self._handle_messages(await self.websocket.recv())
615
+ #
616
+ request = {
617
+ "id": self.trade_id,
618
+ "method": "userDataStream.subscribe"
619
+ }
620
+ await self.ws_listener(request)
623
621
 
624
622
  async def _handle_event(self, content):
625
623
  # logger.debug(f"UserEventsDataStream._handle_event.content: {content}")
@@ -14,13 +14,13 @@ classifiers=["Programming Language :: Python :: 3",
14
14
  "Operating System :: Microsoft :: Windows",
15
15
  "Operating System :: MacOS"]
16
16
  dynamic = ["version", "description"]
17
- requires-python = ">=3.9"
17
+ requires-python = ">=3.10"
18
18
 
19
19
  dependencies = [
20
- "crypto-ws-api==2.0.18",
20
+ "crypto-ws-api==2.0.20",
21
21
  "pyotp==2.9.0",
22
22
  "simplejson==3.20.1",
23
- "aiohttp~=3.11.12",
23
+ "aiohttp~=3.11.18",
24
24
  "expiringdict~=1.2.2",
25
25
  "betterproto==2.0.0b7",
26
26
  "grpclib~=0.4.7"