trd-utils 0.0.19__tar.gz → 0.0.21__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.19 → trd_utils-0.0.21}/PKG-INFO +1 -1
- {trd_utils-0.0.19 → trd_utils-0.0.21}/pyproject.toml +1 -1
- trd_utils-0.0.21/trd_utils/__init__.py +3 -0
- trd_utils-0.0.21/trd_utils/common_utils/wallet_utils.py +26 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/exchanges/base_types.py +20 -15
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/exchanges/hyperliquid/hyperliquid_client.py +51 -2
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/exchanges/hyperliquid/hyperliquid_types.py +11 -0
- trd_utils-0.0.19/trd_utils/__init__.py +0 -3
- {trd_utils-0.0.19 → trd_utils-0.0.21}/LICENSE +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/README.md +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/cipher/__init__.py +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/common_utils/float_utils.py +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/date_utils/__init__.py +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/date_utils/datetime_helpers.py +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/exchanges/README.md +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/exchanges/__init__.py +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/exchanges/blofin/__init__.py +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/exchanges/blofin/blofin_client.py +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/exchanges/blofin/blofin_types.py +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/exchanges/bx_ultra/__init__.py +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/exchanges/bx_ultra/bx_types.py +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/exchanges/bx_ultra/bx_ultra_client.py +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/exchanges/bx_ultra/bx_utils.py +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/exchanges/exchange_base.py +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/exchanges/hyperliquid/README.md +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/exchanges/hyperliquid/__init__.py +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/html_utils/__init__.py +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/html_utils/html_formats.py +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/tradingview/__init__.py +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/tradingview/tradingview_client.py +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/tradingview/tradingview_types.py +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/types_helper/__init__.py +0 -0
- {trd_utils-0.0.19 → trd_utils-0.0.21}/trd_utils/types_helper/base_model.py +0 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""
|
|
2
|
+
General usage wallet-related utils code.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def shorten_wallet_address(
|
|
7
|
+
address: str,
|
|
8
|
+
start_chars: int = 7,
|
|
9
|
+
end_chars: int = 6,
|
|
10
|
+
):
|
|
11
|
+
"""
|
|
12
|
+
Shortens an Ethereum address by keeping a specific number of characters
|
|
13
|
+
from the beginning and end, separated by '...'.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
address (str): The Ethereum address to shorten
|
|
17
|
+
start_chars (int): Number of characters to keep from the beginning (default: 7)
|
|
18
|
+
end_chars (int): Number of characters to keep from the end (default: 6)
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
str: The shortened address
|
|
22
|
+
"""
|
|
23
|
+
if not address or len(address) <= start_chars + end_chars:
|
|
24
|
+
return address
|
|
25
|
+
|
|
26
|
+
return f"{address[:start_chars]}...{address[-end_chars:]}"
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
1
|
from datetime import datetime
|
|
4
2
|
from decimal import Decimal
|
|
5
3
|
|
|
6
4
|
from trd_utils.types_helper.base_model import BaseModel
|
|
7
5
|
|
|
6
|
+
|
|
8
7
|
class UnifiedPositionInfo(BaseModel):
|
|
9
8
|
# The id of the position.
|
|
10
9
|
position_id: str = None
|
|
@@ -40,20 +39,22 @@ class UnifiedPositionInfo(BaseModel):
|
|
|
40
39
|
|
|
41
40
|
def __str__(self):
|
|
42
41
|
parts = []
|
|
43
|
-
|
|
42
|
+
|
|
44
43
|
# Add position pair and ID
|
|
45
|
-
parts.append(
|
|
46
|
-
|
|
44
|
+
parts.append(
|
|
45
|
+
f"Position: {self.position_pair or 'Unknown'} (ID: {self.position_id or 'N/A'})"
|
|
46
|
+
)
|
|
47
|
+
|
|
47
48
|
# Add side and leverage
|
|
48
49
|
side_str = f"Side: {self.position_side or 'Unknown'}"
|
|
49
50
|
if self.position_leverage is not None:
|
|
50
51
|
side_str += f", {self.position_leverage}x"
|
|
51
52
|
parts.append(side_str)
|
|
52
|
-
|
|
53
|
+
|
|
53
54
|
# Add margin mode if available
|
|
54
55
|
if self.margin_mode:
|
|
55
56
|
parts.append(f"Margin: {self.margin_mode}")
|
|
56
|
-
|
|
57
|
+
|
|
57
58
|
# Add open price if available
|
|
58
59
|
price_str = "Open price: "
|
|
59
60
|
if self.open_price is not None:
|
|
@@ -63,23 +64,25 @@ class UnifiedPositionInfo(BaseModel):
|
|
|
63
64
|
else:
|
|
64
65
|
price_str += "N/A"
|
|
65
66
|
parts.append(price_str)
|
|
66
|
-
|
|
67
|
+
|
|
67
68
|
# Add open time if available
|
|
68
69
|
if self.open_time:
|
|
69
70
|
parts.append(f"Opened: {self.open_time.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
70
|
-
|
|
71
|
+
|
|
71
72
|
# Add PNL if available
|
|
72
73
|
if self.position_pnl is not None:
|
|
73
74
|
parts.append(f"PNL: {self.position_pnl}")
|
|
74
|
-
|
|
75
|
+
|
|
75
76
|
return " | ".join(parts)
|
|
76
77
|
|
|
77
78
|
def __repr__(self):
|
|
78
79
|
return self.__str__()
|
|
79
80
|
|
|
81
|
+
|
|
80
82
|
class UnifiedTraderPositions(BaseModel):
|
|
81
83
|
positions: list[UnifiedPositionInfo] = None
|
|
82
84
|
|
|
85
|
+
|
|
83
86
|
class UnifiedTraderInfo(BaseModel):
|
|
84
87
|
# Trader's id. Either int or str. In DEXes (such as HyperLiquid),
|
|
85
88
|
# this might be wallet address of the trader.
|
|
@@ -94,13 +97,15 @@ class UnifiedTraderInfo(BaseModel):
|
|
|
94
97
|
# Trader's win-rate. Not all exchanges might support this field.
|
|
95
98
|
win_rate: Decimal = None
|
|
96
99
|
|
|
100
|
+
def get_win_rate_str(self) -> str:
|
|
101
|
+
return str(round(self.win_rate, 2)) if self.win_rate is not None else "N/A"
|
|
102
|
+
|
|
97
103
|
def __str__(self):
|
|
98
104
|
return (
|
|
99
|
-
f"
|
|
100
|
-
f"
|
|
101
|
-
f"
|
|
105
|
+
f"{self.trader_name} ({self.trader_id})"
|
|
106
|
+
f" | Win Rate: {self.get_win_rate_str()}"
|
|
107
|
+
f" | Profile: {self.trader_url}"
|
|
102
108
|
)
|
|
103
|
-
|
|
109
|
+
|
|
104
110
|
def __repr__(self):
|
|
105
111
|
return self.__str__()
|
|
106
|
-
|
|
@@ -8,11 +8,15 @@ import httpx
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
|
|
10
10
|
from trd_utils.cipher import AESCipher
|
|
11
|
+
from trd_utils.common_utils.wallet_utils import shorten_wallet_address
|
|
12
|
+
from trd_utils.exchanges.base_types import UnifiedPositionInfo, UnifiedTraderInfo, UnifiedTraderPositions
|
|
11
13
|
from trd_utils.exchanges.exchange_base import ExchangeBase
|
|
12
14
|
from trd_utils.exchanges.hyperliquid.hyperliquid_types import HyperLiquidApiResponse, TraderPositionsInfoResponse
|
|
13
15
|
|
|
14
16
|
logger = logging.getLogger(__name__)
|
|
15
17
|
|
|
18
|
+
BASE_PROFILE_URL = "https://hypurrscan.io/address/"
|
|
19
|
+
|
|
16
20
|
|
|
17
21
|
class HyperLiquidClient(ExchangeBase):
|
|
18
22
|
###########################################################
|
|
@@ -63,7 +67,7 @@ class HyperLiquidClient(ExchangeBase):
|
|
|
63
67
|
f"{self.hyperliquid_api_base_host}/info",
|
|
64
68
|
headers=headers,
|
|
65
69
|
content=payload,
|
|
66
|
-
|
|
70
|
+
model_type=TraderPositionsInfoResponse,
|
|
67
71
|
)
|
|
68
72
|
|
|
69
73
|
#endregion
|
|
@@ -78,7 +82,7 @@ class HyperLiquidClient(ExchangeBase):
|
|
|
78
82
|
# f"{self.hyperliquid_api_base_url}/another-thing/info",
|
|
79
83
|
# headers=headers,
|
|
80
84
|
# content=payload,
|
|
81
|
-
#
|
|
85
|
+
# model_type=CopyTraderInfoResponse,
|
|
82
86
|
# )
|
|
83
87
|
|
|
84
88
|
# endregion
|
|
@@ -138,3 +142,48 @@ class HyperLiquidClient(ExchangeBase):
|
|
|
138
142
|
|
|
139
143
|
# endregion
|
|
140
144
|
###########################################################
|
|
145
|
+
# region unified methods
|
|
146
|
+
async def get_unified_trader_positions(
|
|
147
|
+
self,
|
|
148
|
+
uid: int | str,
|
|
149
|
+
) -> UnifiedTraderPositions:
|
|
150
|
+
result = await self.get_trader_positions_info(
|
|
151
|
+
uid=uid,
|
|
152
|
+
)
|
|
153
|
+
unified_result = UnifiedTraderPositions()
|
|
154
|
+
unified_result.positions = []
|
|
155
|
+
for position_container in result.asset_positions:
|
|
156
|
+
position = position_container.position
|
|
157
|
+
unified_pos = UnifiedPositionInfo()
|
|
158
|
+
unified_pos.position_id = position.get_position_id()
|
|
159
|
+
unified_pos.position_pnl = round(position.unrealized_pnl, 3)
|
|
160
|
+
unified_pos.position_side = position.get_side()
|
|
161
|
+
unified_pos.margin_mode = position.leverage.type
|
|
162
|
+
unified_pos.position_leverage = Decimal(position.leverage.value)
|
|
163
|
+
unified_pos.position_pair = f"{position.coin}/USDT"
|
|
164
|
+
unified_pos.open_time = None # hyperliquid doesn't provide this...
|
|
165
|
+
unified_pos.open_price = position.entry_px
|
|
166
|
+
unified_pos.open_price_unit = "USDT"
|
|
167
|
+
unified_result.positions.append(unified_pos)
|
|
168
|
+
|
|
169
|
+
return unified_result
|
|
170
|
+
|
|
171
|
+
async def get_unified_trader_info(
|
|
172
|
+
self,
|
|
173
|
+
uid: int | str,
|
|
174
|
+
) -> UnifiedTraderInfo:
|
|
175
|
+
if not isinstance(uid, str):
|
|
176
|
+
uid = str(uid)
|
|
177
|
+
# sadly hyperliquid doesn't really have an endpoint to fetch information
|
|
178
|
+
# so we have to somehow *fake* these...
|
|
179
|
+
# maybe in future try to find a better way?
|
|
180
|
+
unified_info = UnifiedTraderInfo()
|
|
181
|
+
unified_info.trader_id = uid
|
|
182
|
+
unified_info.trader_name = shorten_wallet_address(uid)
|
|
183
|
+
unified_info.trader_url = f"{BASE_PROFILE_URL}{uid}"
|
|
184
|
+
unified_info.win_rate = None
|
|
185
|
+
|
|
186
|
+
return unified_info
|
|
187
|
+
|
|
188
|
+
# endregion
|
|
189
|
+
###########################################################
|
|
@@ -58,6 +58,17 @@ class PositionInfo(BaseModel):
|
|
|
58
58
|
In any case, we will have to somehow fake it in order to be able to compare
|
|
59
59
|
it with other positions...
|
|
60
60
|
"""
|
|
61
|
+
entry = self.entry_px
|
|
62
|
+
if entry > 100:
|
|
63
|
+
entry = round(entry, 1)
|
|
64
|
+
elif entry > 10:
|
|
65
|
+
entry = round(entry, 2)
|
|
66
|
+
elif entry > 1:
|
|
67
|
+
entry = round(entry, 3)
|
|
68
|
+
elif entry > 0.1:
|
|
69
|
+
entry = round(entry, 4)
|
|
70
|
+
elif entry > 0.01:
|
|
71
|
+
entry = round(entry, 5)
|
|
61
72
|
return (
|
|
62
73
|
f"{self.coin}-{self.leverage.value}{self.entry_px}"
|
|
63
74
|
).encode("utf-8").hex()
|
|
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
|