trd-utils 0.0.36__tar.gz → 0.0.37__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.
Potentially problematic release.
This version of trd-utils might be problematic. Click here for more details.
- {trd_utils-0.0.36 → trd_utils-0.0.37}/PKG-INFO +1 -1
- {trd_utils-0.0.36 → trd_utils-0.0.37}/pyproject.toml +1 -1
- trd_utils-0.0.37/trd_utils/__init__.py +3 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/exchanges/bx_ultra/bx_types.py +69 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/exchanges/bx_ultra/bx_ultra_client.py +156 -30
- trd_utils-0.0.36/trd_utils/__init__.py +0 -3
- {trd_utils-0.0.36 → trd_utils-0.0.37}/LICENSE +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/README.md +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/cipher/__init__.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/common_utils/float_utils.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/common_utils/wallet_utils.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/date_utils/__init__.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/date_utils/datetime_helpers.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/exchanges/README.md +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/exchanges/__init__.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/exchanges/base_types.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/exchanges/blofin/__init__.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/exchanges/blofin/blofin_client.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/exchanges/blofin/blofin_types.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/exchanges/bx_ultra/__init__.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/exchanges/bx_ultra/bx_utils.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/exchanges/errors.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/exchanges/exchange_base.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/exchanges/hyperliquid/README.md +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/exchanges/hyperliquid/__init__.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/exchanges/hyperliquid/hyperliquid_client.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/exchanges/hyperliquid/hyperliquid_types.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/exchanges/okx/__init__.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/exchanges/okx/okx_client.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/exchanges/okx/okx_types.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/exchanges/price_fetcher.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/html_utils/__init__.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/html_utils/html_formats.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/tradingview/__init__.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/tradingview/tradingview_client.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/tradingview/tradingview_types.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/types_helper/__init__.py +0 -0
- {trd_utils-0.0.36 → trd_utils-0.0.37}/trd_utils/types_helper/base_model.py +0 -0
|
@@ -1217,6 +1217,75 @@ class ContractConfigResponse(BxApiResponse):
|
|
|
1217
1217
|
data: ContractConfigData = None
|
|
1218
1218
|
|
|
1219
1219
|
|
|
1220
|
+
# endregion
|
|
1221
|
+
|
|
1222
|
+
###########################################################
|
|
1223
|
+
|
|
1224
|
+
# region std futures types
|
|
1225
|
+
|
|
1226
|
+
|
|
1227
|
+
class CopyTraderStdFuturesPositionInfo(BaseModel):
|
|
1228
|
+
order_no: str = None
|
|
1229
|
+
quotation_coin_vo: QuotationCoinVOInfo = None
|
|
1230
|
+
margin: Decimal = None
|
|
1231
|
+
margin_coin_name: str = None
|
|
1232
|
+
lever_times: Decimal = None
|
|
1233
|
+
display_lever_times: Decimal = None
|
|
1234
|
+
amount: Decimal = None
|
|
1235
|
+
display_price: Decimal = None
|
|
1236
|
+
display_close_price: Decimal = None
|
|
1237
|
+
order_type: int = None
|
|
1238
|
+
close_type: int = None
|
|
1239
|
+
status: Any = None
|
|
1240
|
+
open_date: datetime = None
|
|
1241
|
+
fees: Decimal = None
|
|
1242
|
+
lever_fee: Decimal = None
|
|
1243
|
+
name: str = None
|
|
1244
|
+
order_create_type: int = None
|
|
1245
|
+
hide_price: bool = None
|
|
1246
|
+
fee_rate: Decimal = None
|
|
1247
|
+
hide: bool = None
|
|
1248
|
+
liquidation_desc: str = None
|
|
1249
|
+
contract_account_mode: Any = None
|
|
1250
|
+
current_price: Decimal = None
|
|
1251
|
+
sys_force_price: Decimal = None
|
|
1252
|
+
fund_type: int = None
|
|
1253
|
+
interest: Any = None
|
|
1254
|
+
order_open_trade: OrderOpenTradeInfo = None
|
|
1255
|
+
order_debit: OrderDebitInfo = None
|
|
1256
|
+
open_rate: Decimal = None
|
|
1257
|
+
close_rate: Decimal = None
|
|
1258
|
+
market_status: int = None
|
|
1259
|
+
create_time: datetime = None
|
|
1260
|
+
coupon_amount: Decimal = None
|
|
1261
|
+
stop_profit_rate: Decimal = None
|
|
1262
|
+
stop_loss_rate: Decimal = None
|
|
1263
|
+
stop_profit_modify_time: datetime = None
|
|
1264
|
+
stop_loss_modify_time: datetime = None
|
|
1265
|
+
show_adjust_margin: int = None
|
|
1266
|
+
trailing_stop: Decimal = None
|
|
1267
|
+
trailing_close_price: Decimal = None
|
|
1268
|
+
stop_rate: Decimal = None
|
|
1269
|
+
profit_loss_info: ProfitLossInfoContainer = None
|
|
1270
|
+
stop_offset_rate: None
|
|
1271
|
+
|
|
1272
|
+
def __str__(self):
|
|
1273
|
+
return super().__str__()
|
|
1274
|
+
|
|
1275
|
+
def __repr__(self):
|
|
1276
|
+
return self.__str__()
|
|
1277
|
+
|
|
1278
|
+
|
|
1279
|
+
class CopyTraderStdFuturesPositionsResult(BaseModel):
|
|
1280
|
+
page_id: int = None
|
|
1281
|
+
total_str: str = None
|
|
1282
|
+
positions: list[CopyTraderStdFuturesPositionInfo] = None
|
|
1283
|
+
|
|
1284
|
+
|
|
1285
|
+
class CopyTraderStdFuturesPositionsResponse(BxApiResponse):
|
|
1286
|
+
data: CopyTraderStdFuturesPositionsResult = None
|
|
1287
|
+
|
|
1288
|
+
|
|
1220
1289
|
# endregion
|
|
1221
1290
|
|
|
1222
1291
|
###########################################################
|
|
@@ -16,10 +16,8 @@ import httpx
|
|
|
16
16
|
import time
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
|
|
19
|
+
import pytz
|
|
19
20
|
import websockets
|
|
20
|
-
import websockets.asyncio
|
|
21
|
-
import websockets.asyncio.client
|
|
22
|
-
import websockets.asyncio.connection
|
|
23
21
|
|
|
24
22
|
from trd_utils.exchanges.base_types import (
|
|
25
23
|
UnifiedPositionInfo,
|
|
@@ -34,6 +32,7 @@ from trd_utils.exchanges.bx_ultra.bx_types import (
|
|
|
34
32
|
ContractsListResponse,
|
|
35
33
|
CopyTraderFuturesStatsResponse,
|
|
36
34
|
CopyTraderResumeResponse,
|
|
35
|
+
CopyTraderStdFuturesPositionsResponse,
|
|
37
36
|
CopyTraderTradePositionsResponse,
|
|
38
37
|
CreateOrderDelegationResponse,
|
|
39
38
|
HintListResponse,
|
|
@@ -67,7 +66,7 @@ WEB_APP_VERSION = "4.78.12"
|
|
|
67
66
|
TG_APP_VERSION = "5.0.15"
|
|
68
67
|
|
|
69
68
|
ACCEPT_ENCODING_HEADER = "gzip, deflate, br, zstd"
|
|
70
|
-
BASE_PROFILE_URL = "https
|
|
69
|
+
BASE_PROFILE_URL = "https://\u0062ing\u0078.co\u006d/en/CopyTr\u0061ding/"
|
|
71
70
|
|
|
72
71
|
logger = logging.getLogger(__name__)
|
|
73
72
|
|
|
@@ -102,6 +101,7 @@ class BXUltraClient(ExchangeBase, IPriceFetcher):
|
|
|
102
101
|
# a dict that maps "BTC/USDT" to it single candle info.
|
|
103
102
|
__last_candle_storage: dict = None
|
|
104
103
|
__last_candle_lock: asyncio.Lock = None
|
|
104
|
+
|
|
105
105
|
# endregion
|
|
106
106
|
###########################################################
|
|
107
107
|
# region client constructor
|
|
@@ -323,11 +323,15 @@ class BXUltraClient(ExchangeBase, IPriceFetcher):
|
|
|
323
323
|
self.price_ws_connection = ws
|
|
324
324
|
self._internal_lock.release()
|
|
325
325
|
|
|
326
|
-
await ws.send(
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
326
|
+
await ws.send(
|
|
327
|
+
json.dumps(
|
|
328
|
+
{
|
|
329
|
+
"dataType": "swap.market.v2.contracts",
|
|
330
|
+
"id": uuid.uuid4().hex,
|
|
331
|
+
"reqType": "sub",
|
|
332
|
+
}
|
|
333
|
+
)
|
|
334
|
+
)
|
|
331
335
|
async for msg in ws:
|
|
332
336
|
try:
|
|
333
337
|
decompressed_message = gzip.decompress(msg)
|
|
@@ -336,7 +340,9 @@ class BXUltraClient(ExchangeBase, IPriceFetcher):
|
|
|
336
340
|
str_msg=str_msg,
|
|
337
341
|
)
|
|
338
342
|
except Exception as ex:
|
|
339
|
-
logger.info(
|
|
343
|
+
logger.info(
|
|
344
|
+
f"failed to handle ws message from exchange: {msg}; {ex}"
|
|
345
|
+
)
|
|
340
346
|
|
|
341
347
|
async def _handle_price_ws_msg(self, str_msg: str):
|
|
342
348
|
if str_msg.lower() == "ping":
|
|
@@ -356,14 +362,18 @@ class BXUltraClient(ExchangeBase, IPriceFetcher):
|
|
|
356
362
|
target_id = data["ping"]
|
|
357
363
|
target_time = data.get(
|
|
358
364
|
"time",
|
|
359
|
-
datetime.now(
|
|
360
|
-
|
|
361
|
-
)
|
|
365
|
+
datetime.now(timezone(timedelta(hours=8))).isoformat(
|
|
366
|
+
timespec="seconds"
|
|
367
|
+
),
|
|
368
|
+
)
|
|
369
|
+
await self.price_ws_connection.send(
|
|
370
|
+
json.dumps(
|
|
371
|
+
{
|
|
372
|
+
"pong": target_id,
|
|
373
|
+
"time": target_time,
|
|
374
|
+
}
|
|
375
|
+
)
|
|
362
376
|
)
|
|
363
|
-
await self.price_ws_connection.send(json.dumps({
|
|
364
|
-
"pong": target_id,
|
|
365
|
-
"time": target_time,
|
|
366
|
-
}))
|
|
367
377
|
return
|
|
368
378
|
|
|
369
379
|
inner_data = data.get("data", None)
|
|
@@ -379,7 +389,7 @@ class BXUltraClient(ExchangeBase, IPriceFetcher):
|
|
|
379
389
|
return
|
|
380
390
|
|
|
381
391
|
logger.info(f"we got some unknown data: {data}")
|
|
382
|
-
|
|
392
|
+
|
|
383
393
|
async def get_last_candle(self, pair: str) -> SingleCandleInfo:
|
|
384
394
|
"""
|
|
385
395
|
Returns the last candle's info in this exchange.
|
|
@@ -390,16 +400,16 @@ class BXUltraClient(ExchangeBase, IPriceFetcher):
|
|
|
390
400
|
info = self.__last_candle_storage.get(pair.lower())
|
|
391
401
|
self.__last_candle_lock.release()
|
|
392
402
|
return info
|
|
393
|
-
|
|
403
|
+
|
|
394
404
|
# endregion
|
|
395
405
|
###########################################################
|
|
396
406
|
# region contract
|
|
397
407
|
async def get_contract_config(
|
|
398
408
|
self,
|
|
399
|
-
fund_type: int,
|
|
400
|
-
coin_name: str,
|
|
401
|
-
valuation_name: str,
|
|
402
|
-
margin_coin_name: str,
|
|
409
|
+
fund_type: int, # e.g. 1
|
|
410
|
+
coin_name: str, # e.g. "SOL"
|
|
411
|
+
valuation_name: str, # e.g. "USDT"
|
|
412
|
+
margin_coin_name: str, # e.g. "USDT"
|
|
403
413
|
) -> ContractConfigResponse:
|
|
404
414
|
params = {
|
|
405
415
|
"fundType": f"{fund_type}",
|
|
@@ -417,7 +427,7 @@ class BXUltraClient(ExchangeBase, IPriceFetcher):
|
|
|
417
427
|
params=params,
|
|
418
428
|
model_type=CopyTraderTradePositionsResponse,
|
|
419
429
|
)
|
|
420
|
-
|
|
430
|
+
|
|
421
431
|
async def get_contract_list(
|
|
422
432
|
self,
|
|
423
433
|
quotation_coin_id: int = -1,
|
|
@@ -605,7 +615,7 @@ class BXUltraClient(ExchangeBase, IPriceFetcher):
|
|
|
605
615
|
"marginCoinName": margin_coin_name or "USDT",
|
|
606
616
|
"marketFactor": market_factor or 1,
|
|
607
617
|
"orderType": f"{order_type or 0}",
|
|
608
|
-
"price": float(price),
|
|
618
|
+
"price": float(price), # e.g. 107161.27
|
|
609
619
|
"profitLossRateDto": {
|
|
610
620
|
"stopProfitRate": stop_profit_rate or -1,
|
|
611
621
|
"stopLossRate": stop_loss_rate or -1,
|
|
@@ -626,6 +636,7 @@ class BXUltraClient(ExchangeBase, IPriceFetcher):
|
|
|
626
636
|
content=payload,
|
|
627
637
|
model_type=CreateOrderDelegationResponse,
|
|
628
638
|
)
|
|
639
|
+
|
|
629
640
|
# endregion
|
|
630
641
|
###########################################################
|
|
631
642
|
# region copy-trade-facade
|
|
@@ -652,6 +663,27 @@ class BXUltraClient(ExchangeBase, IPriceFetcher):
|
|
|
652
663
|
model_type=CopyTraderTradePositionsResponse,
|
|
653
664
|
)
|
|
654
665
|
|
|
666
|
+
async def get_copy_trader_std_futures_positions(
|
|
667
|
+
self,
|
|
668
|
+
uid: int | str,
|
|
669
|
+
page_size: int = 20,
|
|
670
|
+
page_id: int = 0,
|
|
671
|
+
) -> CopyTraderStdFuturesPositionsResponse:
|
|
672
|
+
params = {
|
|
673
|
+
"trader": f"{uid}",
|
|
674
|
+
# it seems like this method doesn't really need api identity param...
|
|
675
|
+
# "apiIdentity": f"{api_identity}",
|
|
676
|
+
"pageSize": f"{page_size}",
|
|
677
|
+
"pageId": f"{page_id}",
|
|
678
|
+
}
|
|
679
|
+
headers = self.get_headers(params)
|
|
680
|
+
return await self.invoke_get(
|
|
681
|
+
f"{self.we_api_base_url}/v1/copy-trade/traderContractHold",
|
|
682
|
+
headers=headers,
|
|
683
|
+
params=params,
|
|
684
|
+
model_type=CopyTraderStdFuturesPositionsResponse,
|
|
685
|
+
)
|
|
686
|
+
|
|
655
687
|
async def search_copy_traders(
|
|
656
688
|
self,
|
|
657
689
|
exchange_id: int = 2,
|
|
@@ -740,7 +772,9 @@ class BXUltraClient(ExchangeBase, IPriceFetcher):
|
|
|
740
772
|
api_identity = resume.data.api_identity
|
|
741
773
|
if not api_identity:
|
|
742
774
|
# second try: try to use one of the sub-accounts' identity
|
|
743
|
-
api_identity = resume.data.get_account_identity_by_filter(
|
|
775
|
+
api_identity = resume.data.get_account_identity_by_filter(
|
|
776
|
+
filter_text=sub_account_filter,
|
|
777
|
+
)
|
|
744
778
|
|
|
745
779
|
# maybe also try to fetch it in other ways later?
|
|
746
780
|
# ...
|
|
@@ -881,6 +915,37 @@ class BXUltraClient(ExchangeBase, IPriceFetcher):
|
|
|
881
915
|
# region unified methods
|
|
882
916
|
|
|
883
917
|
async def get_unified_trader_positions(
|
|
918
|
+
self,
|
|
919
|
+
uid: int | str,
|
|
920
|
+
api_identity: int | str | None = None,
|
|
921
|
+
no_warn: bool = False,
|
|
922
|
+
) -> UnifiedTraderPositions:
|
|
923
|
+
perp_positions = []
|
|
924
|
+
std_positions = []
|
|
925
|
+
try:
|
|
926
|
+
result = await self.get_unified_trader_positions_perp(
|
|
927
|
+
uid=uid,
|
|
928
|
+
api_identity=api_identity,
|
|
929
|
+
)
|
|
930
|
+
perp_positions = result.positions
|
|
931
|
+
except Exception as ex:
|
|
932
|
+
if not no_warn:
|
|
933
|
+
logger.warning(f"Failed to fetch perp positions of {uid}: {ex}")
|
|
934
|
+
|
|
935
|
+
try:
|
|
936
|
+
result = await self.get_unified_trader_positions_std(
|
|
937
|
+
uid=uid,
|
|
938
|
+
)
|
|
939
|
+
std_positions = result.positions
|
|
940
|
+
except Exception as ex:
|
|
941
|
+
if not no_warn:
|
|
942
|
+
logger.warning(f"Failed to fetch std positions of {uid}: {ex}")
|
|
943
|
+
|
|
944
|
+
unified_result = UnifiedTraderPositions()
|
|
945
|
+
unified_result.positions = perp_positions + std_positions
|
|
946
|
+
return unified_result
|
|
947
|
+
|
|
948
|
+
async def get_unified_trader_positions_perp(
|
|
884
949
|
self,
|
|
885
950
|
uid: int | str,
|
|
886
951
|
api_identity: int | str | None = None,
|
|
@@ -891,7 +956,7 @@ class BXUltraClient(ExchangeBase, IPriceFetcher):
|
|
|
891
956
|
uid=uid,
|
|
892
957
|
sub_account_filter=sub_account_filter,
|
|
893
958
|
)
|
|
894
|
-
|
|
959
|
+
|
|
895
960
|
if not api_identity:
|
|
896
961
|
raise ValueError(f"Failed to fetch api_identity for user {uid}")
|
|
897
962
|
|
|
@@ -920,7 +985,9 @@ class BXUltraClient(ExchangeBase, IPriceFetcher):
|
|
|
920
985
|
unified_pos.margin_mode = "isolated" # TODO: fix this
|
|
921
986
|
unified_pos.position_leverage = position.leverage
|
|
922
987
|
unified_pos.position_pair = position.symbol.replace("-", "/")
|
|
923
|
-
unified_pos.open_time =
|
|
988
|
+
unified_pos.open_time = datetime.now(
|
|
989
|
+
pytz.UTC
|
|
990
|
+
) # TODO: do something for this?
|
|
924
991
|
unified_pos.open_price = position.avg_price
|
|
925
992
|
unified_pos.initial_margin = position.margin
|
|
926
993
|
unified_pos.open_price_unit = (
|
|
@@ -936,6 +1003,65 @@ class BXUltraClient(ExchangeBase, IPriceFetcher):
|
|
|
936
1003
|
|
|
937
1004
|
return unified_result
|
|
938
1005
|
|
|
1006
|
+
async def get_unified_trader_positions_std(
|
|
1007
|
+
self,
|
|
1008
|
+
uid: int | str,
|
|
1009
|
+
page_offset: int = 0,
|
|
1010
|
+
page_size: int = 50,
|
|
1011
|
+
) -> UnifiedTraderPositions:
|
|
1012
|
+
unified_result = UnifiedTraderPositions()
|
|
1013
|
+
unified_result.positions = []
|
|
1014
|
+
current_page_id = page_offset - 1
|
|
1015
|
+
|
|
1016
|
+
while True:
|
|
1017
|
+
current_page_id += 1
|
|
1018
|
+
try:
|
|
1019
|
+
result = await self.get_copy_trader_std_futures_positions(
|
|
1020
|
+
uid=uid,
|
|
1021
|
+
page_size=page_size,
|
|
1022
|
+
page_id=current_page_id,
|
|
1023
|
+
)
|
|
1024
|
+
|
|
1025
|
+
if result.code != 0 and not result.data:
|
|
1026
|
+
if result.msg:
|
|
1027
|
+
raise ExchangeError(f"got error from API: {result.msg}")
|
|
1028
|
+
raise ExchangeError(
|
|
1029
|
+
f"got unknown error from bx API while fetching std positions for {uid}"
|
|
1030
|
+
)
|
|
1031
|
+
|
|
1032
|
+
for position in result.data.positions:
|
|
1033
|
+
unified_pos = UnifiedPositionInfo()
|
|
1034
|
+
unified_pos.position_id = position.order_no
|
|
1035
|
+
unified_pos.position_pnl = (
|
|
1036
|
+
position.current_price - position.display_price
|
|
1037
|
+
) * position.amount
|
|
1038
|
+
unified_pos.position_side = (
|
|
1039
|
+
"LONG" if position.amount > 0 else "SHORT"
|
|
1040
|
+
)
|
|
1041
|
+
unified_pos.margin_mode = "isolated" # TODO: fix this
|
|
1042
|
+
unified_pos.position_leverage = position.lever_times
|
|
1043
|
+
unified_pos.position_pair = f"{position.quotation_coin_vo.coin.name}/{position.margin_coin_name}"
|
|
1044
|
+
unified_pos.open_time = position.create_time
|
|
1045
|
+
unified_pos.open_price = position.display_price
|
|
1046
|
+
unified_pos.initial_margin = position.margin
|
|
1047
|
+
unified_pos.open_price_unit = position.margin_coin_name
|
|
1048
|
+
|
|
1049
|
+
last_candle = await self.get_last_candle(unified_pos.position_pair)
|
|
1050
|
+
if last_candle:
|
|
1051
|
+
unified_pos.last_price = last_candle.close_price
|
|
1052
|
+
unified_pos.last_volume = last_candle.quote_volume
|
|
1053
|
+
|
|
1054
|
+
unified_result.positions.append(unified_pos)
|
|
1055
|
+
|
|
1056
|
+
if int(result.data.total_str) <= len(unified_result.positions):
|
|
1057
|
+
# all is done
|
|
1058
|
+
return unified_result
|
|
1059
|
+
except Exception as ex:
|
|
1060
|
+
logger.warning(
|
|
1061
|
+
f"Failed to fetch std positions from exchange for {uid}: {ex}"
|
|
1062
|
+
)
|
|
1063
|
+
return unified_result
|
|
1064
|
+
|
|
939
1065
|
async def get_unified_trader_info(
|
|
940
1066
|
self,
|
|
941
1067
|
uid: int | str,
|
|
@@ -945,8 +1071,8 @@ class BXUltraClient(ExchangeBase, IPriceFetcher):
|
|
|
945
1071
|
)
|
|
946
1072
|
if resume_resp.code != 0 and not resume_resp.data:
|
|
947
1073
|
if resume_resp.msg:
|
|
948
|
-
raise
|
|
949
|
-
raise
|
|
1074
|
+
raise ExchangeError(f"got error from API: {resume_resp.msg}")
|
|
1075
|
+
raise ExchangeError(
|
|
950
1076
|
f"got unknown error from bx API while fetching resume for {uid}"
|
|
951
1077
|
)
|
|
952
1078
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|