exchanges-wrapper 2.1.52__tar.gz → 2.2.4__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.52 → exchanges_wrapper-2.2.4}/PKG-INFO +2 -3
- {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/__init__.py +1 -1
- {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/exch_srv.py +113 -88
- {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/http_client.py +4 -5
- {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/martin/__init__.py +29 -29
- {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/parsers/huobi.py +18 -18
- {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/proto/martin.proto +14 -12
- {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/web_sockets.py +16 -16
- {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/pyproject.toml +1 -2
- {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/LICENSE.md +0 -0
- {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/README.md +0 -0
- {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/client.py +0 -0
- {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/definitions.py +0 -0
- {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/errors.py +0 -0
- {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/events.py +0 -0
- {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/exch_srv_cfg.toml.template +0 -0
- {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/lib.py +0 -0
- {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/parsers/bitfinex.py +0 -0
- {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/parsers/bybit.py +0 -0
- {exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/parsers/okx.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: exchanges-wrapper
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.4
|
|
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.12
|
|
@@ -12,9 +12,8 @@ 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.15
|
|
16
16
|
Requires-Dist: pyotp==2.10.0
|
|
17
|
-
Requires-Dist: simplejson==4.1.1
|
|
18
17
|
Requires-Dist: aiohttp==3.14.3
|
|
19
18
|
Requires-Dist: expiringdict==1.2.2
|
|
20
19
|
Requires-Dist: betterproto==2.0.0b7
|
|
@@ -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.
|
|
15
|
+
__version__ = "2.2.4"
|
|
16
16
|
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
import shutil
|
|
@@ -16,8 +16,7 @@ import gc
|
|
|
16
16
|
import traceback
|
|
17
17
|
import asyncio
|
|
18
18
|
import functools
|
|
19
|
-
|
|
20
|
-
import ujson as json
|
|
19
|
+
import orjson
|
|
21
20
|
import logging
|
|
22
21
|
from decimal import Decimal
|
|
23
22
|
import ctypes, ctypes.util
|
|
@@ -162,7 +161,7 @@ class Martin(mr.MartinBase):
|
|
|
162
161
|
real_market=open_client.real_market
|
|
163
162
|
)
|
|
164
163
|
|
|
165
|
-
async def reset_rate_limit(self, request: mr.OpenClientConnectionId) ->
|
|
164
|
+
async def reset_rate_limit(self, request: mr.OpenClientConnectionId) -> SimpleResponse:
|
|
166
165
|
Martin.rate_limiter = max(Martin.rate_limiter or 0, request.rate_limiter)
|
|
167
166
|
_success = False
|
|
168
167
|
open_client = OpenClient.get_client(request.client_id)
|
|
@@ -175,7 +174,7 @@ class Martin(mr.MartinBase):
|
|
|
175
174
|
_success = True
|
|
176
175
|
elif client.http.rate_limit_reached:
|
|
177
176
|
Martin.rate_limit_reached_time = time.time()
|
|
178
|
-
return
|
|
177
|
+
return SimpleResponse(success=_success)
|
|
179
178
|
|
|
180
179
|
async def send_request(self, client_method_name, request, rate_limit=False, **kwargs):
|
|
181
180
|
open_client_instance = OpenClient.get_client(request.client_id)
|
|
@@ -233,9 +232,9 @@ class Martin(mr.MartinBase):
|
|
|
233
232
|
server_time = res.get('serverTime')
|
|
234
233
|
return mr.FetchServerTimeResponse(server_time=server_time)
|
|
235
234
|
|
|
236
|
-
async def one_click_arrival_deposit(self, request: mr.MarketRequest) ->
|
|
235
|
+
async def one_click_arrival_deposit(self, request: mr.MarketRequest) -> SimpleResponse:
|
|
237
236
|
res, _, _ = await self.send_request('one_click_arrival_deposit', request, tx_id=request.symbol)
|
|
238
|
-
return
|
|
237
|
+
return SimpleResponse(success=True, result=orjson.dumps(res))
|
|
239
238
|
|
|
240
239
|
async def fetch_open_orders(self, request: mr.MarketRequest) -> mr.FetchOpenOrdersResponse:
|
|
241
240
|
response = mr.FetchOpenOrdersResponse()
|
|
@@ -248,7 +247,7 @@ class Martin(mr.MartinBase):
|
|
|
248
247
|
)
|
|
249
248
|
for order in res:
|
|
250
249
|
order_id = order['orderId']
|
|
251
|
-
response.orders.append(
|
|
250
|
+
response.orders.append(orjson.dumps(order))
|
|
252
251
|
if client.exchange in ('bitfinex', 'huobi'):
|
|
253
252
|
client.active_order(order_id, order['origQty'], order['executedQty'])
|
|
254
253
|
|
|
@@ -302,8 +301,8 @@ class Martin(mr.MartinBase):
|
|
|
302
301
|
await _queue.put(weakref.ref(event)())
|
|
303
302
|
logger.debug(f"{msg_header}: {trades}")
|
|
304
303
|
|
|
305
|
-
async def cancel_all_orders(self, request: mr.MarketRequest) ->
|
|
306
|
-
response =
|
|
304
|
+
async def cancel_all_orders(self, request: mr.MarketRequest) -> SimpleResponse:
|
|
305
|
+
response = SimpleResponse()
|
|
307
306
|
|
|
308
307
|
res, _, _ = await self.send_request(
|
|
309
308
|
'cancel_all_orders',
|
|
@@ -313,7 +312,7 @@ class Martin(mr.MartinBase):
|
|
|
313
312
|
)
|
|
314
313
|
|
|
315
314
|
response.success = True
|
|
316
|
-
response.result =
|
|
315
|
+
response.result = orjson.dumps(res)
|
|
317
316
|
return response
|
|
318
317
|
|
|
319
318
|
async def fetch_exchange_info_symbol(self, request: mr.MarketRequest) -> mr.FetchExchangeInfoSymbolResponse:
|
|
@@ -353,8 +352,8 @@ class Martin(mr.MartinBase):
|
|
|
353
352
|
setattr(filters, filter_type.lower(), filter_instance)
|
|
354
353
|
return filters
|
|
355
354
|
|
|
356
|
-
async def fetch_account_information(self, request: mr.OpenClientConnectionId) -> mr.
|
|
357
|
-
response = mr.
|
|
355
|
+
async def fetch_account_information(self, request: mr.OpenClientConnectionId) -> mr.BytesResponse:
|
|
356
|
+
response = mr.BytesResponse()
|
|
358
357
|
account_information, _, _ = await self.send_request(
|
|
359
358
|
'fetch_account_information',
|
|
360
359
|
request,
|
|
@@ -363,17 +362,21 @@ class Martin(mr.MartinBase):
|
|
|
363
362
|
)
|
|
364
363
|
# Send only balances
|
|
365
364
|
res = account_information.get('balances', [])
|
|
366
|
-
|
|
367
|
-
{
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
365
|
+
response.items.extend(
|
|
366
|
+
orjson.dumps({
|
|
367
|
+
'asset': i['asset'],
|
|
368
|
+
'free': i['free'],
|
|
369
|
+
'locked': i['locked']
|
|
370
|
+
})
|
|
371
|
+
for i in res
|
|
372
|
+
if float(i['free']) or float(i['locked'])
|
|
373
|
+
)
|
|
371
374
|
return response
|
|
372
375
|
|
|
373
|
-
async def fetch_funding_wallet(self, request: mr.FetchFundingWalletRequest) -> mr.
|
|
376
|
+
async def fetch_funding_wallet(self, request: mr.FetchFundingWalletRequest) -> mr.BytesResponse:
|
|
374
377
|
open_client = OpenClient.get_client(request.client_id)
|
|
375
378
|
client = open_client.client
|
|
376
|
-
response = mr.
|
|
379
|
+
response = mr.BytesResponse()
|
|
377
380
|
res = []
|
|
378
381
|
if client.exchange in ('bitfinex', 'okx', 'bybit') \
|
|
379
382
|
or (open_client.real_market and client.exchange == 'binance'):
|
|
@@ -384,7 +387,7 @@ class Martin(mr.MartinBase):
|
|
|
384
387
|
asset=request.asset,
|
|
385
388
|
need_btc_valuation=request.need_btc_valuation
|
|
386
389
|
)
|
|
387
|
-
response.items
|
|
390
|
+
response.items.extend(orjson.dumps(item) for item in res)
|
|
388
391
|
return response
|
|
389
392
|
|
|
390
393
|
async def fetch_order_book(self, request: mr.MarketRequest) -> mr.FetchOrderBookResponse:
|
|
@@ -396,9 +399,10 @@ class Martin(mr.MartinBase):
|
|
|
396
399
|
symbol=request.symbol
|
|
397
400
|
)
|
|
398
401
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
+
response.last_update_id = res.get('lastUpdateId', 0)
|
|
403
|
+
response.bids.extend(orjson.dumps(v) for v in res.get('bids', []))
|
|
404
|
+
response.asks.extend(orjson.dumps(v) for v in res.get('asks', []))
|
|
405
|
+
return response
|
|
402
406
|
|
|
403
407
|
async def fetch_symbol_price_ticker(self, request: mr.MarketRequest) -> mr.FetchSymbolPriceTickerResponse:
|
|
404
408
|
response = mr.FetchSymbolPriceTickerResponse()
|
|
@@ -423,8 +427,8 @@ class Martin(mr.MartinBase):
|
|
|
423
427
|
)
|
|
424
428
|
return response.from_pydict(res)
|
|
425
429
|
|
|
426
|
-
async def fetch_klines(self, request: mr.FetchKlinesRequest) -> mr.
|
|
427
|
-
response = mr.
|
|
430
|
+
async def fetch_klines(self, request: mr.FetchKlinesRequest) -> mr.BytesResponse:
|
|
431
|
+
response = mr.BytesResponse()
|
|
428
432
|
|
|
429
433
|
res, _, _ = await self.send_request(
|
|
430
434
|
'fetch_klines',
|
|
@@ -437,16 +441,16 @@ class Martin(mr.MartinBase):
|
|
|
437
441
|
limit=request.limit
|
|
438
442
|
)
|
|
439
443
|
|
|
440
|
-
response.items
|
|
444
|
+
response.items.extend(orjson.dumps(item) for item in res)
|
|
441
445
|
return response
|
|
442
446
|
|
|
443
447
|
async def on_klines_update(self, request: mr.FetchKlinesRequest) -> AsyncGenerator[OnKlinesUpdateResponse, Any]:
|
|
444
|
-
response =
|
|
448
|
+
response = OnKlinesUpdateResponse()
|
|
445
449
|
open_client = OpenClient.get_client(request.client_id)
|
|
446
450
|
client = open_client.client
|
|
447
451
|
_queue = asyncio.Queue(MAX_QUEUE_SIZE)
|
|
448
452
|
client.stream_queue[request.trade_id] |= {_queue}
|
|
449
|
-
_intervals =
|
|
453
|
+
_intervals = orjson.loads(request.intervals)
|
|
450
454
|
event_types = []
|
|
451
455
|
# Register streams for intervals
|
|
452
456
|
if client.exchange == 'bitfinex':
|
|
@@ -474,29 +478,31 @@ class Martin(mr.MartinBase):
|
|
|
474
478
|
logger.info(f"OnKlinesUpdate: Stop loop for {open_client.name}:{request.symbol}:{_intervals}")
|
|
475
479
|
return
|
|
476
480
|
else:
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
response.
|
|
480
|
-
response.
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
481
|
+
ev = _event
|
|
482
|
+
|
|
483
|
+
response.symbol = ev.symbol
|
|
484
|
+
response.interval = ev.kline_interval
|
|
485
|
+
response.candle = b""
|
|
486
|
+
response.candle = orjson.dumps([
|
|
487
|
+
ev.kline_start_time,
|
|
488
|
+
ev.kline_open_price,
|
|
489
|
+
ev.kline_high_price,
|
|
490
|
+
ev.kline_low_price,
|
|
491
|
+
ev.kline_close_price,
|
|
492
|
+
ev.kline_base_asset_volume,
|
|
493
|
+
ev.kline_close_time,
|
|
494
|
+
ev.kline_quote_asset_volume,
|
|
495
|
+
ev.kline_trades_number,
|
|
496
|
+
ev.kline_taker_buy_base_asset_volume,
|
|
497
|
+
ev.kline_taker_buy_quote_asset_volume,
|
|
498
|
+
ev.kline_ignore
|
|
499
|
+
])
|
|
500
|
+
|
|
495
501
|
yield response
|
|
496
502
|
_queue.task_done()
|
|
497
503
|
|
|
498
|
-
async def fetch_account_trade_list(self, request: mr.AccountTradeListRequest) -> mr.
|
|
499
|
-
response = mr.
|
|
504
|
+
async def fetch_account_trade_list(self, request: mr.AccountTradeListRequest) -> mr.BytesResponse:
|
|
505
|
+
response = mr.BytesResponse()
|
|
500
506
|
|
|
501
507
|
res, _, _ = await self.send_request(
|
|
502
508
|
'fetch_account_trade_list',
|
|
@@ -510,11 +516,11 @@ class Martin(mr.MartinBase):
|
|
|
510
516
|
limit=request.limit
|
|
511
517
|
)
|
|
512
518
|
|
|
513
|
-
response.items
|
|
519
|
+
response.items.extend(orjson.dumps(item) for item in res)
|
|
514
520
|
return response
|
|
515
521
|
|
|
516
522
|
async def on_ticker_update(self, request: mr.MarketRequest) -> AsyncGenerator[OnTickerUpdateResponse, Any]:
|
|
517
|
-
response =
|
|
523
|
+
response = OnTickerUpdateResponse()
|
|
518
524
|
open_client = OpenClient.get_client(request.client_id)
|
|
519
525
|
client = open_client.client
|
|
520
526
|
_queue = asyncio.Queue(MAX_QUEUE_SIZE)
|
|
@@ -540,18 +546,18 @@ class Martin(mr.MartinBase):
|
|
|
540
546
|
return
|
|
541
547
|
else:
|
|
542
548
|
Martin.ticker_update_time[request.trade_id] = time.time()
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
549
|
+
ev = _event
|
|
550
|
+
response.open_price = ""
|
|
551
|
+
response.last_price = ""
|
|
552
|
+
response.open_price = str(ev.open_price)
|
|
553
|
+
response.last_price = str(ev.close_price)
|
|
554
|
+
response.close_time = int(ev.event_time)
|
|
555
|
+
|
|
550
556
|
yield response
|
|
551
557
|
_queue.task_done()
|
|
552
558
|
|
|
553
559
|
async def on_order_book_update(self, request: mr.MarketRequest) -> AsyncGenerator[FetchOrderBookResponse, Any]:
|
|
554
|
-
response =
|
|
560
|
+
response = FetchOrderBookResponse()
|
|
555
561
|
open_client = OpenClient.get_client(request.client_id)
|
|
556
562
|
client = open_client.client
|
|
557
563
|
_queue = asyncio.LifoQueue(MAX_QUEUE_SIZE * 5)
|
|
@@ -577,15 +583,26 @@ class Martin(mr.MartinBase):
|
|
|
577
583
|
logger.info(f"OnOrderBookUpdate: Stop loop for {open_client.name}: {request.symbol}")
|
|
578
584
|
return
|
|
579
585
|
else:
|
|
580
|
-
|
|
586
|
+
event_bids = _event.bids
|
|
587
|
+
event_asks = _event.asks
|
|
588
|
+
|
|
589
|
+
if event_bids and event_asks:
|
|
581
590
|
response.last_update_id = _event.last_update_id
|
|
582
|
-
|
|
583
|
-
|
|
591
|
+
|
|
592
|
+
res_bids = response.bids
|
|
593
|
+
res_asks = response.asks
|
|
594
|
+
|
|
595
|
+
res_bids.clear()
|
|
596
|
+
res_asks.clear()
|
|
597
|
+
|
|
598
|
+
res_bids.extend(orjson.dumps(v) for v in event_bids)
|
|
599
|
+
res_asks.extend(orjson.dumps(v) for v in event_asks)
|
|
600
|
+
|
|
584
601
|
yield response
|
|
585
602
|
_queue.task_done()
|
|
586
603
|
|
|
587
604
|
async def on_funds_update(self, request: mr.OnFundsUpdateRequest) -> AsyncGenerator[StreamResponse, Any]:
|
|
588
|
-
response =
|
|
605
|
+
response = StreamResponse()
|
|
589
606
|
open_client = OpenClient.get_client(request.client_id)
|
|
590
607
|
client = open_client.client
|
|
591
608
|
_queue = asyncio.Queue(MAX_QUEUE_SIZE)
|
|
@@ -600,12 +617,13 @@ class Martin(mr.MartinBase):
|
|
|
600
617
|
logger.info(f"OnFundsUpdate: Stop user stream for {open_client.name}: {request.symbol}")
|
|
601
618
|
return
|
|
602
619
|
else:
|
|
603
|
-
response.event =
|
|
620
|
+
response.event = b""
|
|
621
|
+
response.event = orjson.dumps(_event.balances)
|
|
604
622
|
yield response
|
|
605
623
|
_queue.task_done()
|
|
606
624
|
|
|
607
625
|
async def on_balance_update(self, request: mr.MarketRequest) -> AsyncGenerator[StreamResponse, Any]:
|
|
608
|
-
response =
|
|
626
|
+
response = StreamResponse()
|
|
609
627
|
open_client = OpenClient.get_client(request.client_id)
|
|
610
628
|
client = open_client.client
|
|
611
629
|
_queue = asyncio.Queue(MAX_QUEUE_SIZE)
|
|
@@ -637,21 +655,26 @@ class Martin(mr.MartinBase):
|
|
|
637
655
|
[_events.append(client.events.wrap_event(balance)) for balance in balances]
|
|
638
656
|
|
|
639
657
|
for _event in _events:
|
|
640
|
-
|
|
658
|
+
ev = _event
|
|
659
|
+
ev_asset = ev.asset
|
|
660
|
+
|
|
661
|
+
if ev_asset in request.symbol:
|
|
641
662
|
balance = {
|
|
642
|
-
"event_time":
|
|
643
|
-
"asset":
|
|
644
|
-
"balance_delta":
|
|
645
|
-
"clear_time":
|
|
663
|
+
"event_time": ev.event_time,
|
|
664
|
+
"asset": ev_asset,
|
|
665
|
+
"balance_delta": ev.balance_delta,
|
|
666
|
+
"clear_time": ev.clear_time
|
|
646
667
|
}
|
|
647
|
-
|
|
668
|
+
|
|
669
|
+
response.event = b""
|
|
670
|
+
response.event = orjson.dumps(balance)
|
|
648
671
|
yield response
|
|
649
672
|
|
|
650
673
|
if _get_event_from_queue:
|
|
651
674
|
_queue.task_done()
|
|
652
675
|
|
|
653
676
|
async def on_order_update(self, request: mr.MarketRequest) -> AsyncGenerator[SimpleResponse, Any]:
|
|
654
|
-
response =
|
|
677
|
+
response = SimpleResponse()
|
|
655
678
|
open_client = OpenClient.get_client(request.client_id)
|
|
656
679
|
client = open_client.client
|
|
657
680
|
_queue = asyncio.Queue(MAX_QUEUE_SIZE)
|
|
@@ -667,10 +690,12 @@ class Martin(mr.MartinBase):
|
|
|
667
690
|
logger.info(f"OnOrderUpdate: Stop user stream for {open_client.name}: {request.symbol}")
|
|
668
691
|
return
|
|
669
692
|
else:
|
|
670
|
-
|
|
671
|
-
|
|
693
|
+
event_dict = _event.__dict__.copy()
|
|
694
|
+
event_dict.pop('handlers', None)
|
|
695
|
+
response.result = b""
|
|
672
696
|
response.success = True
|
|
673
|
-
response.result =
|
|
697
|
+
response.result = orjson.dumps(event_dict)
|
|
698
|
+
|
|
674
699
|
yield response
|
|
675
700
|
_queue.task_done()
|
|
676
701
|
|
|
@@ -713,8 +738,8 @@ class Martin(mr.MartinBase):
|
|
|
713
738
|
response.from_pydict(res)
|
|
714
739
|
return response
|
|
715
740
|
|
|
716
|
-
async def transfer_to_sub(self, request: mr.MarketRequest) ->
|
|
717
|
-
response =
|
|
741
|
+
async def transfer_to_sub(self, request: mr.MarketRequest) -> SimpleResponse:
|
|
742
|
+
response = SimpleResponse()
|
|
718
743
|
response.success = False
|
|
719
744
|
|
|
720
745
|
res, _, _ = await self.send_request(
|
|
@@ -728,11 +753,11 @@ class Martin(mr.MartinBase):
|
|
|
728
753
|
|
|
729
754
|
if res and res.get("txnId"):
|
|
730
755
|
response.success = True
|
|
731
|
-
response.result =
|
|
756
|
+
response.result = orjson.dumps(res)
|
|
732
757
|
return response
|
|
733
758
|
|
|
734
|
-
async def transfer_to_master(self, request: mr.MarketRequest) ->
|
|
735
|
-
response =
|
|
759
|
+
async def transfer_to_master(self, request: mr.MarketRequest) -> SimpleResponse:
|
|
760
|
+
response = SimpleResponse()
|
|
736
761
|
response.success = False
|
|
737
762
|
|
|
738
763
|
res, _, _ = await self.send_request(
|
|
@@ -745,13 +770,13 @@ class Martin(mr.MartinBase):
|
|
|
745
770
|
|
|
746
771
|
if res and res.get("txnId"):
|
|
747
772
|
response.success = True
|
|
748
|
-
response.result =
|
|
773
|
+
response.result = orjson.dumps(res)
|
|
749
774
|
return response
|
|
750
775
|
|
|
751
|
-
async def start_stream(self, request: mr.StartStreamRequest) ->
|
|
776
|
+
async def start_stream(self, request: mr.StartStreamRequest) -> SimpleResponse:
|
|
752
777
|
open_client = OpenClient.get_client(request.client_id)
|
|
753
778
|
client = open_client.client
|
|
754
|
-
response =
|
|
779
|
+
response = SimpleResponse()
|
|
755
780
|
_market_stream_count = 0
|
|
756
781
|
while _market_stream_count < request.market_stream_count:
|
|
757
782
|
await asyncio.sleep(HEARTBEAT)
|
|
@@ -764,8 +789,8 @@ class Martin(mr.MartinBase):
|
|
|
764
789
|
response.success = True
|
|
765
790
|
return response
|
|
766
791
|
|
|
767
|
-
async def stop_stream(self, request: mr.MarketRequest) ->
|
|
768
|
-
response =
|
|
792
|
+
async def stop_stream(self, request: mr.MarketRequest) -> SimpleResponse:
|
|
793
|
+
response = SimpleResponse()
|
|
769
794
|
if open_client := OpenClient.get_client(request.client_id):
|
|
770
795
|
client = open_client.client
|
|
771
796
|
logger.info(f"StopStream request for {request.symbol} on {client.exchange}")
|
|
@@ -775,17 +800,17 @@ class Martin(mr.MartinBase):
|
|
|
775
800
|
response.success = False
|
|
776
801
|
return response
|
|
777
802
|
|
|
778
|
-
async def check_stream(self, request: mr.MarketRequest) ->
|
|
803
|
+
async def check_stream(self, request: mr.MarketRequest) -> SimpleResponse:
|
|
779
804
|
last_update = Martin.ticker_update_time.get(request.trade_id, 0)
|
|
780
805
|
check_time = time.time() - last_update
|
|
781
806
|
success = check_time < WSS_TICKER_TIMEOUT
|
|
782
|
-
response =
|
|
807
|
+
response = SimpleResponse(success=success)
|
|
783
808
|
if not success:
|
|
784
809
|
Martin.ticker_update_time.pop(request.trade_id, None)
|
|
785
810
|
logger.warning(f"CheckStream request failed for {request.trade_id}")
|
|
786
811
|
return response
|
|
787
812
|
|
|
788
|
-
async def client_restart(self, request: mr.MarketRequest) ->
|
|
813
|
+
async def client_restart(self, request: mr.MarketRequest) -> SimpleResponse:
|
|
789
814
|
await self.stop_stream(request)
|
|
790
815
|
if client := OpenClient.get_client(request.client_id).client:
|
|
791
816
|
if user_session := client.user_session:
|
|
@@ -793,7 +818,7 @@ class Martin(mr.MartinBase):
|
|
|
793
818
|
if session := client.http:
|
|
794
819
|
await session.close_session()
|
|
795
820
|
OpenClient.remove_client(request.client_id)
|
|
796
|
-
return
|
|
821
|
+
return SimpleResponse(success=True)
|
|
797
822
|
|
|
798
823
|
|
|
799
824
|
async def stop_stream_ex(client, trade_id):
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import
|
|
2
|
+
import orjson
|
|
3
3
|
from urllib.parse import urlencode, urlparse
|
|
4
|
-
|
|
5
4
|
import aiohttp
|
|
6
5
|
import logging
|
|
7
6
|
import time
|
|
@@ -189,7 +188,7 @@ class HttpClient:
|
|
|
189
188
|
async def _bitfinex_request(self, path, method, signed, send_api_key, endpoint, timeout, **kwargs):
|
|
190
189
|
_endpoint = endpoint or self.endpoint
|
|
191
190
|
bfx_post = (method == 'POST' and kwargs) or "params" in kwargs
|
|
192
|
-
_params =
|
|
191
|
+
_params = orjson.dumps(kwargs).decode() if bfx_post else {}
|
|
193
192
|
url = f'{_endpoint}/{path}'
|
|
194
193
|
query_kwargs = {"headers": {"Accept": AJ}}
|
|
195
194
|
if kwargs and not bfx_post:
|
|
@@ -236,7 +235,7 @@ class HttpClient:
|
|
|
236
235
|
signature_payload = f"{ts}{self.api_key}{query_string}"
|
|
237
236
|
else:
|
|
238
237
|
url += path
|
|
239
|
-
data =
|
|
238
|
+
data = orjson.dumps(kwargs).decode()
|
|
240
239
|
signature_payload = f"{ts}{self.api_key}{data}"
|
|
241
240
|
|
|
242
241
|
signature = generate_signature(self.exchange, self.api_secret, signature_payload)
|
|
@@ -288,7 +287,7 @@ class HttpClient:
|
|
|
288
287
|
if signed:
|
|
289
288
|
ts = f"{datetime.now(timezone.utc).replace(tzinfo=None).isoformat('T', 'milliseconds')}Z"
|
|
290
289
|
if method == 'POST' and kwargs:
|
|
291
|
-
data =
|
|
290
|
+
data = orjson.dumps(kwargs.get('data') if 'data' in kwargs else kwargs).decode()
|
|
292
291
|
signature_payload = f"{ts}{method}{path}{data}"
|
|
293
292
|
else:
|
|
294
293
|
signature_payload = f"{ts}{method}{path}"
|
|
@@ -16,7 +16,6 @@ import betterproto
|
|
|
16
16
|
import grpclib
|
|
17
17
|
from betterproto.grpc.grpclib_server import ServiceBase
|
|
18
18
|
|
|
19
|
-
|
|
20
19
|
if TYPE_CHECKING:
|
|
21
20
|
import grpclib.server
|
|
22
21
|
from betterproto.grpc.grpclib_client import MetadataLike
|
|
@@ -24,13 +23,13 @@ if TYPE_CHECKING:
|
|
|
24
23
|
|
|
25
24
|
|
|
26
25
|
@dataclass(eq=False, repr=False)
|
|
27
|
-
class
|
|
28
|
-
items: List[
|
|
26
|
+
class BytesResponse(betterproto.Message):
|
|
27
|
+
items: List[bytes] = betterproto.bytes_field(1)
|
|
29
28
|
|
|
30
29
|
|
|
31
30
|
@dataclass(eq=False, repr=False)
|
|
32
31
|
class StreamResponse(betterproto.Message):
|
|
33
|
-
event:
|
|
32
|
+
event: bytes = betterproto.bytes_field(1)
|
|
34
33
|
|
|
35
34
|
|
|
36
35
|
@dataclass(eq=False, repr=False)
|
|
@@ -146,7 +145,7 @@ class OnFundsUpdateRequest(betterproto.Message):
|
|
|
146
145
|
@dataclass(eq=False, repr=False)
|
|
147
146
|
class SimpleResponse(betterproto.Message):
|
|
148
147
|
success: bool = betterproto.bool_field(1)
|
|
149
|
-
result:
|
|
148
|
+
result: bytes = betterproto.bytes_field(2)
|
|
150
149
|
|
|
151
150
|
|
|
152
151
|
@dataclass(eq=False, repr=False)
|
|
@@ -169,7 +168,7 @@ class AccountTradeListRequest(betterproto.Message):
|
|
|
169
168
|
class OnKlinesUpdateResponse(betterproto.Message):
|
|
170
169
|
symbol: str = betterproto.string_field(1)
|
|
171
170
|
interval: str = betterproto.string_field(2)
|
|
172
|
-
candle:
|
|
171
|
+
candle: bytes = betterproto.bytes_field(3)
|
|
173
172
|
|
|
174
173
|
|
|
175
174
|
@dataclass(eq=False, repr=False)
|
|
@@ -179,6 +178,7 @@ class FetchKlinesRequest(betterproto.Message):
|
|
|
179
178
|
symbol: str = betterproto.string_field(3)
|
|
180
179
|
interval: str = betterproto.string_field(4)
|
|
181
180
|
limit: int = betterproto.uint32_field(5)
|
|
181
|
+
intervals: bytes = betterproto.bytes_field(6)
|
|
182
182
|
|
|
183
183
|
|
|
184
184
|
@dataclass(eq=False, repr=False)
|
|
@@ -215,8 +215,8 @@ class FetchSymbolPriceTickerResponse(betterproto.Message):
|
|
|
215
215
|
@dataclass(eq=False, repr=False)
|
|
216
216
|
class FetchOrderBookResponse(betterproto.Message):
|
|
217
217
|
last_update_id: int = betterproto.uint64_field(1)
|
|
218
|
-
bids: List[
|
|
219
|
-
asks: List[
|
|
218
|
+
bids: List[bytes] = betterproto.bytes_field(2)
|
|
219
|
+
asks: List[bytes] = betterproto.bytes_field(3)
|
|
220
220
|
|
|
221
221
|
|
|
222
222
|
@dataclass(eq=False, repr=False)
|
|
@@ -397,7 +397,7 @@ class FetchOrderResponse(betterproto.Message):
|
|
|
397
397
|
@dataclass(eq=False, repr=False)
|
|
398
398
|
class FetchOpenOrdersResponse(betterproto.Message):
|
|
399
399
|
rate_limiter: int = betterproto.int32_field(1)
|
|
400
|
-
orders: List[
|
|
400
|
+
orders: List[bytes] = betterproto.bytes_field(2)
|
|
401
401
|
|
|
402
402
|
|
|
403
403
|
@dataclass(eq=False, repr=False)
|
|
@@ -526,11 +526,11 @@ class MartinStub(betterproto.ServiceStub):
|
|
|
526
526
|
timeout: Optional[float] = None,
|
|
527
527
|
deadline: Optional["Deadline"] = None,
|
|
528
528
|
metadata: Optional["MetadataLike"] = None
|
|
529
|
-
) -> "
|
|
529
|
+
) -> "BytesResponse":
|
|
530
530
|
return await self._unary_unary(
|
|
531
531
|
"/martin.Martin/FetchAccountInformation",
|
|
532
532
|
open_client_connection_id,
|
|
533
|
-
|
|
533
|
+
BytesResponse,
|
|
534
534
|
timeout=timeout,
|
|
535
535
|
deadline=deadline,
|
|
536
536
|
metadata=metadata,
|
|
@@ -543,11 +543,11 @@ class MartinStub(betterproto.ServiceStub):
|
|
|
543
543
|
timeout: Optional[float] = None,
|
|
544
544
|
deadline: Optional["Deadline"] = None,
|
|
545
545
|
metadata: Optional["MetadataLike"] = None
|
|
546
|
-
) -> "
|
|
546
|
+
) -> "BytesResponse":
|
|
547
547
|
return await self._unary_unary(
|
|
548
548
|
"/martin.Martin/FetchAccountTradeList",
|
|
549
549
|
account_trade_list_request,
|
|
550
|
-
|
|
550
|
+
BytesResponse,
|
|
551
551
|
timeout=timeout,
|
|
552
552
|
deadline=deadline,
|
|
553
553
|
metadata=metadata,
|
|
@@ -577,11 +577,11 @@ class MartinStub(betterproto.ServiceStub):
|
|
|
577
577
|
timeout: Optional[float] = None,
|
|
578
578
|
deadline: Optional["Deadline"] = None,
|
|
579
579
|
metadata: Optional["MetadataLike"] = None
|
|
580
|
-
) -> "
|
|
580
|
+
) -> "BytesResponse":
|
|
581
581
|
return await self._unary_unary(
|
|
582
582
|
"/martin.Martin/FetchFundingWallet",
|
|
583
583
|
fetch_funding_wallet_request,
|
|
584
|
-
|
|
584
|
+
BytesResponse,
|
|
585
585
|
timeout=timeout,
|
|
586
586
|
deadline=deadline,
|
|
587
587
|
metadata=metadata,
|
|
@@ -594,11 +594,11 @@ class MartinStub(betterproto.ServiceStub):
|
|
|
594
594
|
timeout: Optional[float] = None,
|
|
595
595
|
deadline: Optional["Deadline"] = None,
|
|
596
596
|
metadata: Optional["MetadataLike"] = None
|
|
597
|
-
) -> "
|
|
597
|
+
) -> "BytesResponse":
|
|
598
598
|
return await self._unary_unary(
|
|
599
599
|
"/martin.Martin/FetchKlines",
|
|
600
600
|
fetch_klines_request,
|
|
601
|
-
|
|
601
|
+
BytesResponse,
|
|
602
602
|
timeout=timeout,
|
|
603
603
|
deadline=deadline,
|
|
604
604
|
metadata=metadata,
|
|
@@ -973,12 +973,12 @@ class MartinBase(ServiceBase):
|
|
|
973
973
|
|
|
974
974
|
async def fetch_account_information(
|
|
975
975
|
self, open_client_connection_id: "OpenClientConnectionId"
|
|
976
|
-
) -> "
|
|
976
|
+
) -> "BytesResponse":
|
|
977
977
|
raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
|
|
978
978
|
|
|
979
979
|
async def fetch_account_trade_list(
|
|
980
980
|
self, account_trade_list_request: "AccountTradeListRequest"
|
|
981
|
-
) -> "
|
|
981
|
+
) -> "BytesResponse":
|
|
982
982
|
raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
|
|
983
983
|
|
|
984
984
|
async def fetch_exchange_info_symbol(
|
|
@@ -988,12 +988,12 @@ class MartinBase(ServiceBase):
|
|
|
988
988
|
|
|
989
989
|
async def fetch_funding_wallet(
|
|
990
990
|
self, fetch_funding_wallet_request: "FetchFundingWalletRequest"
|
|
991
|
-
) -> "
|
|
991
|
+
) -> "BytesResponse":
|
|
992
992
|
raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
|
|
993
993
|
|
|
994
994
|
async def fetch_klines(
|
|
995
995
|
self, fetch_klines_request: "FetchKlinesRequest"
|
|
996
|
-
) -> "
|
|
996
|
+
) -> "BytesResponse":
|
|
997
997
|
raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
|
|
998
998
|
|
|
999
999
|
async def fetch_open_orders(
|
|
@@ -1128,14 +1128,14 @@ class MartinBase(ServiceBase):
|
|
|
1128
1128
|
await stream.send_message(response)
|
|
1129
1129
|
|
|
1130
1130
|
async def __rpc_fetch_account_information(
|
|
1131
|
-
self, stream: "grpclib.server.Stream[OpenClientConnectionId,
|
|
1131
|
+
self, stream: "grpclib.server.Stream[OpenClientConnectionId, BytesResponse]"
|
|
1132
1132
|
) -> None:
|
|
1133
1133
|
request = await stream.recv_message()
|
|
1134
1134
|
response = await self.fetch_account_information(request)
|
|
1135
1135
|
await stream.send_message(response)
|
|
1136
1136
|
|
|
1137
1137
|
async def __rpc_fetch_account_trade_list(
|
|
1138
|
-
self, stream: "grpclib.server.Stream[AccountTradeListRequest,
|
|
1138
|
+
self, stream: "grpclib.server.Stream[AccountTradeListRequest, BytesResponse]"
|
|
1139
1139
|
) -> None:
|
|
1140
1140
|
request = await stream.recv_message()
|
|
1141
1141
|
response = await self.fetch_account_trade_list(request)
|
|
@@ -1150,14 +1150,14 @@ class MartinBase(ServiceBase):
|
|
|
1150
1150
|
await stream.send_message(response)
|
|
1151
1151
|
|
|
1152
1152
|
async def __rpc_fetch_funding_wallet(
|
|
1153
|
-
self, stream: "grpclib.server.Stream[FetchFundingWalletRequest,
|
|
1153
|
+
self, stream: "grpclib.server.Stream[FetchFundingWalletRequest, BytesResponse]"
|
|
1154
1154
|
) -> None:
|
|
1155
1155
|
request = await stream.recv_message()
|
|
1156
1156
|
response = await self.fetch_funding_wallet(request)
|
|
1157
1157
|
await stream.send_message(response)
|
|
1158
1158
|
|
|
1159
1159
|
async def __rpc_fetch_klines(
|
|
1160
|
-
self, stream: "grpclib.server.Stream[FetchKlinesRequest,
|
|
1160
|
+
self, stream: "grpclib.server.Stream[FetchKlinesRequest, BytesResponse]"
|
|
1161
1161
|
) -> None:
|
|
1162
1162
|
request = await stream.recv_message()
|
|
1163
1163
|
response = await self.fetch_klines(request)
|
|
@@ -1356,13 +1356,13 @@ class MartinBase(ServiceBase):
|
|
|
1356
1356
|
self.__rpc_fetch_account_information,
|
|
1357
1357
|
grpclib.const.Cardinality.UNARY_UNARY,
|
|
1358
1358
|
OpenClientConnectionId,
|
|
1359
|
-
|
|
1359
|
+
BytesResponse,
|
|
1360
1360
|
),
|
|
1361
1361
|
"/martin.Martin/FetchAccountTradeList": grpclib.const.Handler(
|
|
1362
1362
|
self.__rpc_fetch_account_trade_list,
|
|
1363
1363
|
grpclib.const.Cardinality.UNARY_UNARY,
|
|
1364
1364
|
AccountTradeListRequest,
|
|
1365
|
-
|
|
1365
|
+
BytesResponse,
|
|
1366
1366
|
),
|
|
1367
1367
|
"/martin.Martin/FetchExchangeInfoSymbol": grpclib.const.Handler(
|
|
1368
1368
|
self.__rpc_fetch_exchange_info_symbol,
|
|
@@ -1374,13 +1374,13 @@ class MartinBase(ServiceBase):
|
|
|
1374
1374
|
self.__rpc_fetch_funding_wallet,
|
|
1375
1375
|
grpclib.const.Cardinality.UNARY_UNARY,
|
|
1376
1376
|
FetchFundingWalletRequest,
|
|
1377
|
-
|
|
1377
|
+
BytesResponse,
|
|
1378
1378
|
),
|
|
1379
1379
|
"/martin.Martin/FetchKlines": grpclib.const.Handler(
|
|
1380
1380
|
self.__rpc_fetch_klines,
|
|
1381
1381
|
grpclib.const.Cardinality.UNARY_UNARY,
|
|
1382
1382
|
FetchKlinesRequest,
|
|
1383
|
-
|
|
1383
|
+
BytesResponse,
|
|
1384
1384
|
),
|
|
1385
1385
|
"/martin.Martin/FetchOpenOrders": grpclib.const.Handler(
|
|
1386
1386
|
self.__rpc_fetch_open_orders,
|
|
@@ -8,7 +8,7 @@ import logging
|
|
|
8
8
|
logger = logging.getLogger(__name__)
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def on_balance_update(res:
|
|
11
|
+
def on_balance_update(res: dict) -> dict:
|
|
12
12
|
return {
|
|
13
13
|
'e': 'balanceUpdate',
|
|
14
14
|
'E': res.get('transactTime'),
|
|
@@ -18,11 +18,11 @@ def on_balance_update(res: {}) -> {}:
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
def fetch_server_time(res:
|
|
21
|
+
def fetch_server_time(res: dict) -> dict:
|
|
22
22
|
return {'serverTime': res}
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
def exchange_info(server_time: int, _symbol_params) ->
|
|
25
|
+
def exchange_info(server_time: int, _symbol_params) -> dict:
|
|
26
26
|
_tick_size = str(10**(-_symbol_params.get('pp')))
|
|
27
27
|
_price_filter = {
|
|
28
28
|
"filterType": "PRICE_FILTER",
|
|
@@ -87,7 +87,7 @@ def orders(res: list, response_type=None) -> list:
|
|
|
87
87
|
return binance_orders
|
|
88
88
|
|
|
89
89
|
|
|
90
|
-
def order(res:
|
|
90
|
+
def order(res: dict, response_type=None) -> dict:
|
|
91
91
|
symbol = res.get('symbol').upper()
|
|
92
92
|
order_id = res.get('id')
|
|
93
93
|
order_list_id = -1
|
|
@@ -171,7 +171,7 @@ def order(res: {}, response_type=None) -> {}:
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
|
|
174
|
-
def order_cancelled(symbol, order_id=None, origin_client_order_id=None,) ->
|
|
174
|
+
def order_cancelled(symbol, order_id=None, origin_client_order_id=None,) -> dict:
|
|
175
175
|
return {
|
|
176
176
|
"symbol": symbol,
|
|
177
177
|
"origClientOrderId": origin_client_order_id,
|
|
@@ -190,7 +190,7 @@ def order_cancelled(symbol, order_id=None, origin_client_order_id=None,) -> {}:
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
|
|
193
|
-
def account_balances(res:
|
|
193
|
+
def account_balances(res: dict) -> dict:
|
|
194
194
|
"""
|
|
195
195
|
This function parses the Huobi API response for account information and
|
|
196
196
|
returns a dictionary with relevant details.
|
|
@@ -205,7 +205,7 @@ def account_balances(res: {}) -> {}:
|
|
|
205
205
|
# Filter out balances that have zero value
|
|
206
206
|
res[:] = [i for i in res if i.get('balance') != '0']
|
|
207
207
|
|
|
208
|
-
assets =
|
|
208
|
+
assets = dict
|
|
209
209
|
for balance in res:
|
|
210
210
|
asset = balance['currency']
|
|
211
211
|
assets.setdefault(asset, {
|
|
@@ -230,12 +230,12 @@ def account_balances(res: {}) -> {}:
|
|
|
230
230
|
return {"balances": balances}
|
|
231
231
|
|
|
232
232
|
|
|
233
|
-
def order_book(res:
|
|
233
|
+
def order_book(res: dict) -> dict:
|
|
234
234
|
res["lastUpdateId"] = res.pop("ts")
|
|
235
235
|
return res
|
|
236
236
|
|
|
237
237
|
|
|
238
|
-
def order_book_ws(res:
|
|
238
|
+
def order_book_ws(res: dict, symbol: str) -> dict:
|
|
239
239
|
return {
|
|
240
240
|
'stream': f"{symbol}@depth5",
|
|
241
241
|
'data': {'lastUpdateId': res['ts'],
|
|
@@ -245,14 +245,14 @@ def order_book_ws(res: {}, symbol: str) -> {}:
|
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
|
|
248
|
-
def fetch_symbol_price_ticker(res:
|
|
248
|
+
def fetch_symbol_price_ticker(res: dict, symbol) -> dict:
|
|
249
249
|
return {
|
|
250
250
|
"symbol": symbol,
|
|
251
251
|
"price": str(res.get('data')[0].get('price'))
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
|
|
255
|
-
def ticker_price_change_statistics(res:
|
|
255
|
+
def ticker_price_change_statistics(res: dict, symbol) -> dict:
|
|
256
256
|
return {
|
|
257
257
|
"symbol": symbol,
|
|
258
258
|
"priceChange": str(res.get('close') - res.get('open')),
|
|
@@ -280,8 +280,8 @@ def ticker_price_change_statistics(res: {}, symbol) -> {}:
|
|
|
280
280
|
}
|
|
281
281
|
|
|
282
282
|
|
|
283
|
-
def ticker(res:
|
|
284
|
-
tick = res.get('tick')
|
|
283
|
+
def ticker(res: dict, symbol: str = None) -> dict:
|
|
284
|
+
tick: dict = res.get('tick')
|
|
285
285
|
return {
|
|
286
286
|
'stream': f"{symbol}@miniTicker",
|
|
287
287
|
'data': {
|
|
@@ -350,9 +350,9 @@ def klines(res: list, _interval: str) -> list:
|
|
|
350
350
|
return binance_klines
|
|
351
351
|
|
|
352
352
|
|
|
353
|
-
def candle(res: dict, symbol
|
|
354
|
-
tick = res.get('tick')
|
|
355
|
-
start_time = tick.get('id')
|
|
353
|
+
def candle(res: dict, symbol = '', ch_type = '') -> dict:
|
|
354
|
+
tick: dict = res.get('tick')
|
|
355
|
+
start_time = tick.get('id') * 1000
|
|
356
356
|
_interval = ch_type.split('_')[1]
|
|
357
357
|
end_time = start_time + interval2value(interval(_interval)) * 1000 - 1
|
|
358
358
|
return {
|
|
@@ -384,7 +384,7 @@ def candle(res: dict, symbol: str = None, ch_type: str = None) -> {}:
|
|
|
384
384
|
}
|
|
385
385
|
|
|
386
386
|
|
|
387
|
-
def on_funds_update(data:
|
|
387
|
+
def on_funds_update(data: dict) -> dict:
|
|
388
388
|
event_time = int(time.time() * 1000)
|
|
389
389
|
binance_funds = {
|
|
390
390
|
'e': 'outboundAccountPosition',
|
|
@@ -404,7 +404,7 @@ def on_funds_update(data: {}) -> {}:
|
|
|
404
404
|
return binance_funds
|
|
405
405
|
|
|
406
406
|
|
|
407
|
-
def on_order_update(_order:
|
|
407
|
+
def on_order_update(_order: dict) -> dict:
|
|
408
408
|
event = _order['lastEvent']
|
|
409
409
|
order_quantity = event.get('orderSize', event.get('orderValue'))
|
|
410
410
|
order_price = event.get('orderPrice', event.get('tradePrice'))
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
+
__version__ = "2.2.1"
|
|
2
3
|
Protocol buffer definition for exchanges-wrapper exch_srv.py
|
|
3
4
|
https://grpc.io/docs/languages/python/quickstart/
|
|
4
5
|
For compile use next commands
|
|
@@ -16,11 +17,11 @@ service Martin {
|
|
|
16
17
|
rpc CancelOrder (CancelOrderRequest) returns (CancelOrderResponse) {}
|
|
17
18
|
rpc CheckStream (MarketRequest) returns (SimpleResponse) {}
|
|
18
19
|
rpc CreateLimitOrder (CreateLimitOrderRequest) returns (CreateLimitOrderResponse) {}
|
|
19
|
-
rpc FetchAccountInformation (OpenClientConnectionId) returns (
|
|
20
|
-
rpc FetchAccountTradeList (AccountTradeListRequest) returns (
|
|
20
|
+
rpc FetchAccountInformation (OpenClientConnectionId) returns (BytesResponse) {}
|
|
21
|
+
rpc FetchAccountTradeList (AccountTradeListRequest) returns (BytesResponse) {}
|
|
21
22
|
rpc FetchExchangeInfoSymbol (MarketRequest) returns (FetchExchangeInfoSymbolResponse) {}
|
|
22
|
-
rpc FetchFundingWallet(FetchFundingWalletRequest) returns (
|
|
23
|
-
rpc FetchKlines (FetchKlinesRequest) returns (
|
|
23
|
+
rpc FetchFundingWallet(FetchFundingWalletRequest) returns (BytesResponse) {}
|
|
24
|
+
rpc FetchKlines (FetchKlinesRequest) returns (BytesResponse) {}
|
|
24
25
|
rpc FetchOpenOrders (MarketRequest) returns (FetchOpenOrdersResponse) {}
|
|
25
26
|
rpc FetchOrder (FetchOrderRequest) returns (FetchOrderResponse) {}
|
|
26
27
|
rpc FetchOrderBook (MarketRequest) returns (FetchOrderBookResponse) {}
|
|
@@ -43,12 +44,12 @@ service Martin {
|
|
|
43
44
|
rpc ClientRestart (MarketRequest) returns (SimpleResponse) {}
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
message
|
|
47
|
-
repeated
|
|
47
|
+
message BytesResponse {
|
|
48
|
+
repeated bytes items = 1;
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
message StreamResponse {
|
|
51
|
-
|
|
52
|
+
bytes event = 1;
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
message FetchFundingWalletRequest{
|
|
@@ -157,7 +158,7 @@ message OnFundsUpdateRequest {
|
|
|
157
158
|
|
|
158
159
|
message SimpleResponse {
|
|
159
160
|
bool success = 1;
|
|
160
|
-
|
|
161
|
+
bytes result = 2;
|
|
161
162
|
}
|
|
162
163
|
|
|
163
164
|
message OnTickerUpdateResponse {
|
|
@@ -177,7 +178,7 @@ message AccountTradeListRequest {
|
|
|
177
178
|
message OnKlinesUpdateResponse {
|
|
178
179
|
string symbol = 1;
|
|
179
180
|
string interval =2;
|
|
180
|
-
|
|
181
|
+
bytes candle = 3;
|
|
181
182
|
}
|
|
182
183
|
|
|
183
184
|
message FetchKlinesRequest {
|
|
@@ -186,6 +187,7 @@ message FetchKlinesRequest {
|
|
|
186
187
|
string symbol = 3;
|
|
187
188
|
string interval = 4;
|
|
188
189
|
uint32 limit = 5;
|
|
190
|
+
bytes intervals = 6;
|
|
189
191
|
}
|
|
190
192
|
|
|
191
193
|
message FetchTickerPriceChangeStatisticsResponse {
|
|
@@ -219,8 +221,8 @@ message FetchSymbolPriceTickerResponse {
|
|
|
219
221
|
|
|
220
222
|
message FetchOrderBookResponse {
|
|
221
223
|
uint64 lastUpdateId = 1;
|
|
222
|
-
repeated
|
|
223
|
-
repeated
|
|
224
|
+
repeated bytes bids = 2;
|
|
225
|
+
repeated bytes asks = 3;
|
|
224
226
|
}
|
|
225
227
|
|
|
226
228
|
message FetchExchangeInfoSymbolResponse {
|
|
@@ -362,7 +364,7 @@ message FetchOrderResponse {
|
|
|
362
364
|
|
|
363
365
|
message FetchOpenOrdersResponse {
|
|
364
366
|
int32 rate_limiter = 1;
|
|
365
|
-
repeated
|
|
367
|
+
repeated bytes orders = 2;
|
|
366
368
|
}
|
|
367
369
|
|
|
368
370
|
message MarketRequest {
|
|
@@ -2,9 +2,7 @@ import sys
|
|
|
2
2
|
import asyncio
|
|
3
3
|
import gc
|
|
4
4
|
import logging
|
|
5
|
-
|
|
6
|
-
# noinspection PyPackageRequirements
|
|
7
|
-
import ujson as json
|
|
5
|
+
import orjson
|
|
8
6
|
from pathlib import Path
|
|
9
7
|
import time
|
|
10
8
|
from decimal import Decimal
|
|
@@ -114,7 +112,7 @@ class EventsDataStream:
|
|
|
114
112
|
pass # meant to be overridden in a subclass
|
|
115
113
|
|
|
116
114
|
async def _handle_messages(self, msg, symbol=None, ch_type=str()):
|
|
117
|
-
msg_data =
|
|
115
|
+
msg_data = orjson.loads(msg if isinstance(msg, str) else gzip.decompress(msg))
|
|
118
116
|
if self.exchange == 'binance':
|
|
119
117
|
if "stream" in msg_data:
|
|
120
118
|
await self._handle_event(msg_data)
|
|
@@ -217,7 +215,7 @@ class EventsDataStream:
|
|
|
217
215
|
elif self.exchange == 'huobi':
|
|
218
216
|
if ping := msg_data.get('ping'):
|
|
219
217
|
self.ping = 0
|
|
220
|
-
await self.websocket.send(
|
|
218
|
+
await self.websocket.send(orjson.dumps({"pong": ping}), text = True)
|
|
221
219
|
await asyncio.sleep(0)
|
|
222
220
|
elif msg_data.get('action') == 'ping':
|
|
223
221
|
self.ping = 0
|
|
@@ -227,7 +225,7 @@ class EventsDataStream:
|
|
|
227
225
|
"ts": msg_data.get('data').get('ts')
|
|
228
226
|
}
|
|
229
227
|
}
|
|
230
|
-
await self.websocket.send(
|
|
228
|
+
await self.websocket.send(orjson.dumps(pong), text = True)
|
|
231
229
|
await asyncio.sleep(0)
|
|
232
230
|
elif msg_data.get('tick') or msg_data.get('data'):
|
|
233
231
|
if ch_type == 'ticker':
|
|
@@ -252,7 +250,7 @@ class EventsDataStream:
|
|
|
252
250
|
|
|
253
251
|
async def ws_listener(self, request=None, symbol=None, ch_type=str()):
|
|
254
252
|
if request:
|
|
255
|
-
await self.websocket.send(
|
|
253
|
+
await self.websocket.send(orjson.dumps(request), text = True)
|
|
256
254
|
await asyncio.sleep(0)
|
|
257
255
|
async for msg_data in self.websocket:
|
|
258
256
|
await self._handle_messages(msg_data, symbol, ch_type)
|
|
@@ -261,7 +259,7 @@ class EventsDataStream:
|
|
|
261
259
|
while True:
|
|
262
260
|
await asyncio.sleep(interval)
|
|
263
261
|
try:
|
|
264
|
-
await self.websocket.send(
|
|
262
|
+
await self.websocket.send(orjson.dumps({"req_id": req_id, "op": "ping"}), text = True)
|
|
265
263
|
except (ConnectionClosed, asyncio.exceptions.TimeoutError, AttributeError):
|
|
266
264
|
break
|
|
267
265
|
|
|
@@ -418,9 +416,10 @@ class HbpPrivateEventsDataStream(EventsDataStream):
|
|
|
418
416
|
|
|
419
417
|
async def start_wss(self):
|
|
420
418
|
await self.websocket.send(
|
|
421
|
-
|
|
419
|
+
orjson.dumps(
|
|
422
420
|
compose_htx_ws_auth(self.endpoint, self.exchange, self.client.api_key, self.client.api_secret)
|
|
423
|
-
)
|
|
421
|
+
),
|
|
422
|
+
text = True
|
|
424
423
|
)
|
|
425
424
|
await asyncio.sleep(0)
|
|
426
425
|
await self._handle_messages(await self.websocket.recv(), symbol=self.symbol)
|
|
@@ -429,7 +428,7 @@ class HbpPrivateEventsDataStream(EventsDataStream):
|
|
|
429
428
|
"action": "sub",
|
|
430
429
|
"ch": "accounts.update#2"
|
|
431
430
|
}
|
|
432
|
-
await self.websocket.send(
|
|
431
|
+
await self.websocket.send(orjson.dumps(request), text = True)
|
|
433
432
|
await asyncio.sleep(0)
|
|
434
433
|
await self._handle_messages(await self.websocket.recv(), symbol=self.symbol)
|
|
435
434
|
#
|
|
@@ -437,7 +436,7 @@ class HbpPrivateEventsDataStream(EventsDataStream):
|
|
|
437
436
|
"action": "sub",
|
|
438
437
|
"ch": f"orders#{self.symbol.lower()}"
|
|
439
438
|
}
|
|
440
|
-
await self.websocket.send(
|
|
439
|
+
await self.websocket.send(orjson.dumps(request), text = True)
|
|
441
440
|
await asyncio.sleep(0)
|
|
442
441
|
await self._handle_messages(await self.websocket.recv(), symbol=self.symbol)
|
|
443
442
|
#
|
|
@@ -546,7 +545,7 @@ class OkxPrivateEventsDataStream(EventsDataStream):
|
|
|
546
545
|
"sign": signature}
|
|
547
546
|
]
|
|
548
547
|
}
|
|
549
|
-
await self.websocket.send(
|
|
548
|
+
await self.websocket.send(orjson.dumps(request), text = True)
|
|
550
549
|
await asyncio.sleep(0)
|
|
551
550
|
await self._handle_messages(await self.websocket.recv())
|
|
552
551
|
# Channel subscription
|
|
@@ -598,7 +597,7 @@ class BBTPrivateEventsDataStream(EventsDataStream):
|
|
|
598
597
|
"op": 'auth',
|
|
599
598
|
"args": [self.client.api_key, ts, signature]
|
|
600
599
|
}
|
|
601
|
-
await self.websocket.send(
|
|
600
|
+
await self.websocket.send(orjson.dumps(request), text = True)
|
|
602
601
|
await asyncio.sleep(0)
|
|
603
602
|
await self._handle_messages(await self.websocket.recv())
|
|
604
603
|
# Channel subscription
|
|
@@ -633,9 +632,10 @@ class UserEventsDataStream(EventsDataStream):
|
|
|
633
632
|
|
|
634
633
|
async def start_wss(self):
|
|
635
634
|
await self.websocket.send(
|
|
636
|
-
|
|
635
|
+
orjson.dumps(
|
|
637
636
|
compose_binance_ws_auth(self.trade_id, self.client.api_key, self.client.api_secret)
|
|
638
|
-
)
|
|
637
|
+
),
|
|
638
|
+
text = True
|
|
639
639
|
)
|
|
640
640
|
await asyncio.sleep(0)
|
|
641
641
|
await self._handle_messages(await self.websocket.recv())
|
|
@@ -20,9 +20,8 @@ dynamic = ["version", "description"]
|
|
|
20
20
|
requires-python = ">=3.12"
|
|
21
21
|
|
|
22
22
|
dependencies = [
|
|
23
|
-
"crypto-ws-api==2.1.
|
|
23
|
+
"crypto-ws-api==2.1.15",
|
|
24
24
|
"pyotp==2.10.0",
|
|
25
|
-
"simplejson==4.1.1",
|
|
26
25
|
"aiohttp==3.14.3",
|
|
27
26
|
"expiringdict==1.2.2",
|
|
28
27
|
"betterproto==2.0.0b7",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{exchanges_wrapper-2.1.52 → exchanges_wrapper-2.2.4}/exchanges_wrapper/exch_srv_cfg.toml.template
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|