trd-utils 0.0.15__py3-none-any.whl → 0.0.16__py3-none-any.whl
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/__init__.py +1 -1
- trd_utils/date_utils/__init__.py +8 -0
- trd_utils/date_utils/datetime_helpers.py +16 -0
- trd_utils/exchanges/__init__.py +7 -7
- trd_utils/exchanges/base_types.py +71 -18
- trd_utils/exchanges/blofin/__init__.py +1 -1
- trd_utils/exchanges/blofin/blofin_client.py +55 -67
- trd_utils/exchanges/blofin/blofin_types.py +1 -1
- trd_utils/exchanges/bx_ultra/bx_ultra_client.py +124 -70
- trd_utils/exchanges/exchange_base.py +36 -11
- trd_utils/exchanges/hyperliquid/__init__.py +1 -1
- trd_utils/exchanges/hyperliquid/hyperliquid_client.py +0 -50
- trd_utils/types_helper/__init__.py +1 -1
- trd_utils/types_helper/base_model.py +1 -1
- {trd_utils-0.0.15.dist-info → trd_utils-0.0.16.dist-info}/METADATA +1 -1
- trd_utils-0.0.16.dist-info/RECORD +31 -0
- trd_utils-0.0.15.dist-info/RECORD +0 -29
- {trd_utils-0.0.15.dist-info → trd_utils-0.0.16.dist-info}/LICENSE +0 -0
- {trd_utils-0.0.15.dist-info → trd_utils-0.0.16.dist-info}/WHEEL +0 -0
trd_utils/__init__.py
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
from datetime import datetime, timezone
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def dt_from_ts(timestamp: float) -> datetime:
|
|
9
|
+
"""
|
|
10
|
+
Return a datetime from a timestamp.
|
|
11
|
+
:param timestamp: timestamp in seconds or milliseconds
|
|
12
|
+
"""
|
|
13
|
+
if timestamp > 1e10:
|
|
14
|
+
# Timezone in ms - convert to seconds
|
|
15
|
+
timestamp /= 1000
|
|
16
|
+
return datetime.fromtimestamp(timestamp, tz=timezone.utc)
|
trd_utils/exchanges/__init__.py
CHANGED
|
@@ -11,11 +11,11 @@ from .hyperliquid import HyperLiquidClient
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
__all__ = [
|
|
14
|
-
ExchangeBase,
|
|
15
|
-
UnifiedTraderInfo,
|
|
16
|
-
UnifiedTraderPositions,
|
|
17
|
-
UnifiedPositionInfo,
|
|
18
|
-
BXUltraClient,
|
|
19
|
-
BlofinClient,
|
|
20
|
-
HyperLiquidClient,
|
|
14
|
+
"ExchangeBase",
|
|
15
|
+
"UnifiedTraderInfo",
|
|
16
|
+
"UnifiedTraderPositions",
|
|
17
|
+
"UnifiedPositionInfo",
|
|
18
|
+
"BXUltraClient",
|
|
19
|
+
"BlofinClient",
|
|
20
|
+
"HyperLiquidClient",
|
|
21
21
|
]
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
from decimal import Decimal
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
from trd_utils.types_helper.base_model import BaseModel
|
|
7
|
+
|
|
8
|
+
class UnifiedPositionInfo(BaseModel):
|
|
7
9
|
# The id of the position.
|
|
8
10
|
position_id: str = None
|
|
9
11
|
|
|
@@ -13,41 +15,92 @@ class UnifiedPositionInfo:
|
|
|
13
15
|
# The position side, either "LONG" or "SHORT".
|
|
14
16
|
position_side: str = None
|
|
15
17
|
|
|
18
|
+
# The position's leverage.
|
|
19
|
+
position_leverage: Decimal = None
|
|
20
|
+
|
|
21
|
+
# The margin mode; e.g. cross or isolated. Please note that
|
|
22
|
+
# different exchanges might provide different kinds of margin modes,
|
|
23
|
+
# depending on what they support, that's why we can't support a unified
|
|
24
|
+
# enum type for this as of yet.
|
|
25
|
+
margin_mode: str = None
|
|
26
|
+
|
|
16
27
|
# The formatted pair string of this position.
|
|
17
28
|
# e.g. BTC/USDT.
|
|
18
29
|
position_pair: str = None
|
|
19
30
|
|
|
20
|
-
# Side but with a proper emoji alongside of it.
|
|
21
|
-
side_with_emoji: str = None
|
|
22
|
-
|
|
23
31
|
# The open time of this position.
|
|
24
32
|
# Note that not all public APIs might provide this field.
|
|
25
33
|
open_time: datetime = None
|
|
26
34
|
|
|
27
|
-
# The relative open time of this position.
|
|
28
|
-
relative_open_time: str = None
|
|
29
|
-
|
|
30
35
|
# Open price of the position.
|
|
31
36
|
open_price: Decimal = None
|
|
32
37
|
|
|
33
|
-
# The
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
# The base unit that the open-price is based on (e.g. USD, USDT, USDC)
|
|
39
|
+
open_price_unit: str | None = None
|
|
40
|
+
|
|
41
|
+
def __str__(self):
|
|
42
|
+
parts = []
|
|
43
|
+
|
|
44
|
+
# Add position pair and ID
|
|
45
|
+
parts.append(f"Position: {self.position_pair or 'Unknown'} (ID: {self.position_id or 'N/A'})")
|
|
46
|
+
|
|
47
|
+
# Add side and leverage
|
|
48
|
+
side_str = f"Side: {self.position_side or 'Unknown'}"
|
|
49
|
+
if self.position_leverage is not None:
|
|
50
|
+
side_str += f", {self.position_leverage}x"
|
|
51
|
+
parts.append(side_str)
|
|
52
|
+
|
|
53
|
+
# Add margin mode if available
|
|
54
|
+
if self.margin_mode:
|
|
55
|
+
parts.append(f"Margin: {self.margin_mode}")
|
|
56
|
+
|
|
57
|
+
# Add open price if available
|
|
58
|
+
price_str = "Open price: "
|
|
59
|
+
if self.open_price is not None:
|
|
60
|
+
price_str += f"{self.open_price}"
|
|
61
|
+
if self.open_price_unit:
|
|
62
|
+
price_str += f" {self.open_price_unit}"
|
|
63
|
+
else:
|
|
64
|
+
price_str += "N/A"
|
|
65
|
+
parts.append(price_str)
|
|
66
|
+
|
|
67
|
+
# Add open time if available
|
|
68
|
+
if self.open_time:
|
|
69
|
+
parts.append(f"Opened: {self.open_time.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
70
|
+
|
|
71
|
+
# Add PNL if available
|
|
72
|
+
if self.position_pnl is not None:
|
|
73
|
+
parts.append(f"PNL: {self.position_pnl}")
|
|
74
|
+
|
|
75
|
+
return " | ".join(parts)
|
|
76
|
+
|
|
77
|
+
def __repr__(self):
|
|
78
|
+
return self.__str__()
|
|
79
|
+
|
|
80
|
+
class UnifiedTraderPositions(BaseModel):
|
|
39
81
|
positions: list[UnifiedPositionInfo] = None
|
|
40
82
|
|
|
41
|
-
class UnifiedTraderInfo:
|
|
83
|
+
class UnifiedTraderInfo(BaseModel):
|
|
84
|
+
# Trader's id. Either int or str. In DEXes (such as HyperLiquid),
|
|
85
|
+
# this might be wallet address of the trader.
|
|
86
|
+
trader_id: int | str = None
|
|
87
|
+
|
|
42
88
|
# Name of the trader
|
|
43
89
|
trader_name: str = None
|
|
44
90
|
|
|
45
91
|
# The URL in which we can see the trader's profile
|
|
46
92
|
trader_url: str = None
|
|
47
93
|
|
|
48
|
-
# Trader's id. Either int or str. In DEXes (such as HyperLiquid),
|
|
49
|
-
# this might be wallet address of the trader.
|
|
50
|
-
trader_id: int | str = None
|
|
51
|
-
|
|
52
94
|
# Trader's win-rate. Not all exchanges might support this field.
|
|
53
95
|
win_rate: Decimal = None
|
|
96
|
+
|
|
97
|
+
def __str__(self):
|
|
98
|
+
return (
|
|
99
|
+
f"Trader: {self.trader_name} (ID: {self.trader_id})"
|
|
100
|
+
f"{' | Win Rate: ' + str(round(self.win_rate, 2))}"
|
|
101
|
+
f"{' | Profile: ' + self.trader_url}"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
def __repr__(self):
|
|
105
|
+
return self.__str__()
|
|
106
|
+
|
|
@@ -2,15 +2,18 @@ import asyncio
|
|
|
2
2
|
from decimal import Decimal
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
5
|
-
from typing import Type
|
|
6
5
|
import httpx
|
|
7
6
|
|
|
8
7
|
import time
|
|
9
8
|
from pathlib import Path
|
|
10
9
|
|
|
11
|
-
from trd_utils.
|
|
10
|
+
from trd_utils.date_utils.datetime_helpers import dt_from_ts
|
|
11
|
+
from trd_utils.exchanges.base_types import (
|
|
12
|
+
UnifiedPositionInfo,
|
|
13
|
+
UnifiedTraderInfo,
|
|
14
|
+
UnifiedTraderPositions,
|
|
15
|
+
)
|
|
12
16
|
from trd_utils.exchanges.blofin.blofin_types import (
|
|
13
|
-
BlofinApiResponse,
|
|
14
17
|
CmsColorResponse,
|
|
15
18
|
CopyTraderAllOrderHistory,
|
|
16
19
|
CopyTraderAllOrderList,
|
|
@@ -22,6 +25,9 @@ from trd_utils.exchanges.blofin.blofin_types import (
|
|
|
22
25
|
from trd_utils.cipher import AESCipher
|
|
23
26
|
from trd_utils.exchanges.exchange_base import ExchangeBase
|
|
24
27
|
|
|
28
|
+
|
|
29
|
+
BASE_PROFILE_URL = "https://blofin.com/copy-trade/details/"
|
|
30
|
+
|
|
25
31
|
logger = logging.getLogger(__name__)
|
|
26
32
|
|
|
27
33
|
|
|
@@ -65,7 +71,7 @@ class BlofinClient(ExchangeBase):
|
|
|
65
71
|
return await self.invoke_get(
|
|
66
72
|
f"{self.blofin_api_base_url}/cms/share_config",
|
|
67
73
|
headers=headers,
|
|
68
|
-
|
|
74
|
+
model_type=ShareConfigResponse,
|
|
69
75
|
)
|
|
70
76
|
|
|
71
77
|
async def get_cms_color(self) -> CmsColorResponse:
|
|
@@ -73,7 +79,7 @@ class BlofinClient(ExchangeBase):
|
|
|
73
79
|
return await self.invoke_get(
|
|
74
80
|
f"{self.blofin_api_base_url}/cms/color",
|
|
75
81
|
headers=headers,
|
|
76
|
-
|
|
82
|
+
model_type=CmsColorResponse,
|
|
77
83
|
)
|
|
78
84
|
|
|
79
85
|
# endregion
|
|
@@ -88,26 +94,26 @@ class BlofinClient(ExchangeBase):
|
|
|
88
94
|
f"{self.blofin_api_base_url}/copy/trader/info",
|
|
89
95
|
headers=headers,
|
|
90
96
|
content=payload,
|
|
91
|
-
|
|
97
|
+
model_type=CopyTraderInfoResponse,
|
|
92
98
|
)
|
|
93
99
|
|
|
94
100
|
async def get_copy_trader_order_list(
|
|
95
101
|
self,
|
|
96
|
-
uid: int,
|
|
102
|
+
uid: int | str,
|
|
97
103
|
from_param: int = 0,
|
|
98
104
|
limit_param: int = 20,
|
|
99
105
|
) -> CopyTraderOrderListResponse:
|
|
100
106
|
payload = {
|
|
101
107
|
"from": from_param,
|
|
102
108
|
"limit": limit_param,
|
|
103
|
-
"uid": uid,
|
|
109
|
+
"uid": int(uid),
|
|
104
110
|
}
|
|
105
111
|
headers = self.get_headers()
|
|
106
112
|
return await self.invoke_post(
|
|
107
113
|
f"{self.blofin_api_base_url}/copy/trader/order/list",
|
|
108
114
|
headers=headers,
|
|
109
115
|
content=payload,
|
|
110
|
-
|
|
116
|
+
model_type=CopyTraderOrderListResponse,
|
|
111
117
|
)
|
|
112
118
|
|
|
113
119
|
async def get_copy_trader_all_order_list(
|
|
@@ -133,11 +139,13 @@ class BlofinClient(ExchangeBase):
|
|
|
133
139
|
from_param=current_id_from,
|
|
134
140
|
limit_param=chunk_limit,
|
|
135
141
|
)
|
|
136
|
-
if (
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
142
|
+
if not isinstance(current_result, CopyTraderOrderListResponse):
|
|
143
|
+
raise ValueError(
|
|
144
|
+
"get_copy_trader_order_list returned invalid value of "
|
|
145
|
+
f"{type(current_result)}",
|
|
146
|
+
)
|
|
147
|
+
if not current_result.data:
|
|
148
|
+
# we no longer have anything else here
|
|
141
149
|
return result
|
|
142
150
|
|
|
143
151
|
if current_result.data[0].id == current_id_from:
|
|
@@ -177,7 +185,7 @@ class BlofinClient(ExchangeBase):
|
|
|
177
185
|
f"{self.blofin_api_base_url}/copy/trader/order/history",
|
|
178
186
|
headers=headers,
|
|
179
187
|
content=payload,
|
|
180
|
-
|
|
188
|
+
model_type=CopyTraderOrderHistoryResponse,
|
|
181
189
|
)
|
|
182
190
|
|
|
183
191
|
async def get_copy_trader_all_order_history(
|
|
@@ -256,56 +264,6 @@ class BlofinClient(ExchangeBase):
|
|
|
256
264
|
the_headers["Authorization"] = f"Bearer {self.authorization_token}"
|
|
257
265
|
return the_headers
|
|
258
266
|
|
|
259
|
-
async def invoke_get(
|
|
260
|
-
self,
|
|
261
|
-
url: str,
|
|
262
|
-
headers: dict | None = None,
|
|
263
|
-
params: dict | None = None,
|
|
264
|
-
model: Type[BlofinApiResponse] | None = None,
|
|
265
|
-
parse_float=Decimal,
|
|
266
|
-
) -> "BlofinApiResponse":
|
|
267
|
-
"""
|
|
268
|
-
Invokes the specific request to the specific url with the specific params and headers.
|
|
269
|
-
"""
|
|
270
|
-
response = await self.httpx_client.get(
|
|
271
|
-
url=url,
|
|
272
|
-
headers=headers,
|
|
273
|
-
params=params,
|
|
274
|
-
)
|
|
275
|
-
return model.deserialize(response.json(parse_float=parse_float))
|
|
276
|
-
|
|
277
|
-
async def invoke_post(
|
|
278
|
-
self,
|
|
279
|
-
url: str,
|
|
280
|
-
headers: dict | None = None,
|
|
281
|
-
params: dict | None = None,
|
|
282
|
-
content: dict | str | bytes = "",
|
|
283
|
-
model: Type[BlofinApiResponse] | None = None,
|
|
284
|
-
parse_float=Decimal,
|
|
285
|
-
) -> "BlofinApiResponse":
|
|
286
|
-
"""
|
|
287
|
-
Invokes the specific request to the specific url with the specific params and headers.
|
|
288
|
-
"""
|
|
289
|
-
|
|
290
|
-
if isinstance(content, dict):
|
|
291
|
-
content = json.dumps(content, separators=(",", ":"), sort_keys=True)
|
|
292
|
-
|
|
293
|
-
response = await self.httpx_client.post(
|
|
294
|
-
url=url,
|
|
295
|
-
headers=headers,
|
|
296
|
-
params=params,
|
|
297
|
-
content=content,
|
|
298
|
-
)
|
|
299
|
-
if not model:
|
|
300
|
-
return response.json()
|
|
301
|
-
|
|
302
|
-
return model.deserialize(response.json(parse_float=parse_float))
|
|
303
|
-
|
|
304
|
-
async def aclose(self) -> None:
|
|
305
|
-
await self.httpx_client.aclose()
|
|
306
|
-
logger.info("BlofinClient closed")
|
|
307
|
-
return True
|
|
308
|
-
|
|
309
267
|
def read_from_session_file(self, file_path: str) -> None:
|
|
310
268
|
"""
|
|
311
269
|
Reads from session file; if it doesn't exist, creates it.
|
|
@@ -347,13 +305,43 @@ class BlofinClient(ExchangeBase):
|
|
|
347
305
|
self,
|
|
348
306
|
uid: int | str,
|
|
349
307
|
) -> UnifiedTraderPositions:
|
|
350
|
-
|
|
308
|
+
result = await self.get_copy_trader_all_order_list(
|
|
309
|
+
uid=uid,
|
|
310
|
+
)
|
|
311
|
+
unified_result = UnifiedTraderPositions()
|
|
312
|
+
unified_result.positions = []
|
|
313
|
+
for position in result.data:
|
|
314
|
+
unified_pos = UnifiedPositionInfo()
|
|
315
|
+
unified_pos.position_id = position.id or position.order_id
|
|
316
|
+
unified_pos.position_pnl = position.real_pnl or position.pnl
|
|
317
|
+
unified_pos.position_side = (
|
|
318
|
+
"LONG" if position.order_side in ("LONG", "BUY") else "SHORT"
|
|
319
|
+
)
|
|
320
|
+
unified_pos.margin_mode = position.margin_mode
|
|
321
|
+
unified_pos.position_leverage = Decimal(position.leverage)
|
|
322
|
+
unified_pos.position_pair = position.symbol.replace("-", "/")
|
|
323
|
+
unified_pos.open_time = dt_from_ts(position.open_time)
|
|
324
|
+
unified_pos.open_price = position.avg_open_price
|
|
325
|
+
unified_pos.open_price_unit = position.symbol.split("-")[-1]
|
|
326
|
+
unified_result.positions.append(unified_pos)
|
|
327
|
+
|
|
328
|
+
return unified_result
|
|
351
329
|
|
|
352
330
|
async def get_unified_trader_info(
|
|
353
331
|
self,
|
|
354
332
|
uid: int | str,
|
|
355
333
|
) -> UnifiedTraderInfo:
|
|
356
|
-
|
|
334
|
+
info_resp = await self.get_copy_trader_info(
|
|
335
|
+
uid=uid,
|
|
336
|
+
)
|
|
337
|
+
info = info_resp.data
|
|
338
|
+
unified_info = UnifiedTraderInfo()
|
|
339
|
+
unified_info.trader_id = info.uid
|
|
340
|
+
unified_info.trader_name = info.nick_name
|
|
341
|
+
unified_info.trader_url = f"{BASE_PROFILE_URL}{info.uid}"
|
|
342
|
+
unified_info.win_rate = info.win_rate
|
|
343
|
+
|
|
344
|
+
return unified_info
|
|
357
345
|
|
|
358
346
|
# endregion
|
|
359
347
|
###########################################################
|
|
@@ -114,7 +114,7 @@ class CopyTraderSingleOrderInfo(BaseModel):
|
|
|
114
114
|
price: Any = None
|
|
115
115
|
fill_quantity: Any = None
|
|
116
116
|
fill_quantity_cont: Any = None
|
|
117
|
-
pnl:
|
|
117
|
+
pnl: Decimal = None
|
|
118
118
|
cancel_source: Any = None
|
|
119
119
|
order_type: Any = None
|
|
120
120
|
order_open_state: Any = None
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
BxUltra exchange subclass
|
|
3
|
+
"""
|
|
2
4
|
|
|
3
5
|
import asyncio
|
|
4
6
|
from decimal import Decimal
|
|
5
7
|
import json
|
|
6
8
|
import logging
|
|
7
|
-
from typing import Type
|
|
8
9
|
import uuid
|
|
9
10
|
|
|
10
11
|
import httpx
|
|
@@ -12,6 +13,11 @@ import httpx
|
|
|
12
13
|
import time
|
|
13
14
|
from pathlib import Path
|
|
14
15
|
|
|
16
|
+
from trd_utils.exchanges.base_types import (
|
|
17
|
+
UnifiedPositionInfo,
|
|
18
|
+
UnifiedTraderInfo,
|
|
19
|
+
UnifiedTraderPositions,
|
|
20
|
+
)
|
|
15
21
|
from trd_utils.exchanges.bx_ultra.bx_utils import do_ultra_ss
|
|
16
22
|
from trd_utils.exchanges.bx_ultra.bx_types import (
|
|
17
23
|
AssetsInfoResponse,
|
|
@@ -30,7 +36,6 @@ from trd_utils.exchanges.bx_ultra.bx_types import (
|
|
|
30
36
|
ZenDeskABStatusResponse,
|
|
31
37
|
ZenDeskAuthResponse,
|
|
32
38
|
ZoneModuleListResponse,
|
|
33
|
-
BxApiResponse,
|
|
34
39
|
)
|
|
35
40
|
from trd_utils.cipher import AESCipher
|
|
36
41
|
|
|
@@ -49,9 +54,17 @@ WEB_APP_VERSION = "4.78.12"
|
|
|
49
54
|
TG_APP_VERSION = "5.0.15"
|
|
50
55
|
|
|
51
56
|
ACCEPT_ENCODING_HEADER = "gzip, deflate, br, zstd"
|
|
57
|
+
BASE_PROFILE_URL = "https://bingx.com/en/CopyTrading/"
|
|
52
58
|
|
|
53
59
|
logger = logging.getLogger(__name__)
|
|
54
60
|
|
|
61
|
+
# The cache in which we will be storing the api identities.
|
|
62
|
+
# The key of this dict is uid (long user identifier), to api-identity.
|
|
63
|
+
# Why is this a global variable, and not a class attribute? because as far as
|
|
64
|
+
# I've observed, api-identities in bx (unlike Telegram's access-hashes) are not
|
|
65
|
+
# specific to the current session that is fetching them,
|
|
66
|
+
user_api_identity_cache: dict[int, int] = {}
|
|
67
|
+
|
|
55
68
|
|
|
56
69
|
class BXUltraClient(ExchangeBase):
|
|
57
70
|
###########################################################
|
|
@@ -119,7 +132,7 @@ class BXUltraClient(ExchangeBase):
|
|
|
119
132
|
f"{self.we_api_base_url}/coin/v1/zone/module-info",
|
|
120
133
|
headers=headers,
|
|
121
134
|
params=params,
|
|
122
|
-
|
|
135
|
+
model_type=ZoneModuleListResponse,
|
|
123
136
|
)
|
|
124
137
|
|
|
125
138
|
async def get_user_favorite_quotation(
|
|
@@ -136,7 +149,7 @@ class BXUltraClient(ExchangeBase):
|
|
|
136
149
|
f"{self.we_api_base_url}/coin/v1/user/favorite/quotation",
|
|
137
150
|
headers=headers,
|
|
138
151
|
params=params,
|
|
139
|
-
|
|
152
|
+
model_type=UserFavoriteQuotationResponse,
|
|
140
153
|
)
|
|
141
154
|
|
|
142
155
|
async def get_quotation_rank(
|
|
@@ -153,7 +166,7 @@ class BXUltraClient(ExchangeBase):
|
|
|
153
166
|
f"{self.we_api_base_url}/coin/v1/rank/quotation-rank",
|
|
154
167
|
headers=headers,
|
|
155
168
|
params=params,
|
|
156
|
-
|
|
169
|
+
model_type=QuotationRankResponse,
|
|
157
170
|
)
|
|
158
171
|
|
|
159
172
|
async def get_hot_search(
|
|
@@ -170,7 +183,7 @@ class BXUltraClient(ExchangeBase):
|
|
|
170
183
|
f"{self.we_api_base_url}/coin/v1/quotation/hot-search",
|
|
171
184
|
headers=headers,
|
|
172
185
|
params=params,
|
|
173
|
-
|
|
186
|
+
model_type=HotSearchResponse,
|
|
174
187
|
)
|
|
175
188
|
|
|
176
189
|
async def get_homepage(
|
|
@@ -187,7 +200,7 @@ class BXUltraClient(ExchangeBase):
|
|
|
187
200
|
f"{self.we_api_base_url}/coin/v1/discovery/homepage",
|
|
188
201
|
headers=headers,
|
|
189
202
|
params=params,
|
|
190
|
-
|
|
203
|
+
model_type=HomePageResponse,
|
|
191
204
|
)
|
|
192
205
|
|
|
193
206
|
# endregion
|
|
@@ -198,15 +211,17 @@ class BXUltraClient(ExchangeBase):
|
|
|
198
211
|
return await self.invoke_get(
|
|
199
212
|
f"{self.we_api_base_url}/customer/v1/zendesk/ab-status",
|
|
200
213
|
headers=headers,
|
|
201
|
-
|
|
214
|
+
model_type=ZenDeskABStatusResponse,
|
|
202
215
|
)
|
|
216
|
+
|
|
203
217
|
async def do_zendesk_auth(self) -> ZenDeskAuthResponse:
|
|
204
218
|
headers = self.get_headers(needs_auth=True)
|
|
205
219
|
return await self.invoke_get(
|
|
206
220
|
f"{self.we_api_base_url}/customer/v1/zendesk/auth/jwt",
|
|
207
221
|
headers=headers,
|
|
208
|
-
|
|
222
|
+
model_type=ZenDeskAuthResponse,
|
|
209
223
|
)
|
|
224
|
+
|
|
210
225
|
# endregion
|
|
211
226
|
###########################################################
|
|
212
227
|
# region platform-tool
|
|
@@ -215,7 +230,7 @@ class BXUltraClient(ExchangeBase):
|
|
|
215
230
|
return await self.invoke_get(
|
|
216
231
|
f"{self.we_api_base_url}/platform-tool/v1/hint/list",
|
|
217
232
|
headers=headers,
|
|
218
|
-
|
|
233
|
+
model_type=HintListResponse,
|
|
219
234
|
)
|
|
220
235
|
|
|
221
236
|
# endregion
|
|
@@ -226,7 +241,7 @@ class BXUltraClient(ExchangeBase):
|
|
|
226
241
|
return await self.invoke_get(
|
|
227
242
|
f"{self.we_api_base_url}/asset-manager/v1/assets/account-total-overview",
|
|
228
243
|
headers=headers,
|
|
229
|
-
|
|
244
|
+
model_type=AssetsInfoResponse,
|
|
230
245
|
)
|
|
231
246
|
|
|
232
247
|
# endregion
|
|
@@ -255,7 +270,7 @@ class BXUltraClient(ExchangeBase):
|
|
|
255
270
|
f"{self.we_api_base_url}/v4/contract/order/hold",
|
|
256
271
|
headers=headers,
|
|
257
272
|
params=params,
|
|
258
|
-
|
|
273
|
+
model_type=ContractsListResponse,
|
|
259
274
|
)
|
|
260
275
|
|
|
261
276
|
async def get_contract_order_history(
|
|
@@ -282,7 +297,7 @@ class BXUltraClient(ExchangeBase):
|
|
|
282
297
|
f"{self.we_api_base_url}/v2/contract/order/history",
|
|
283
298
|
headers=headers,
|
|
284
299
|
params=params,
|
|
285
|
-
|
|
300
|
+
model_type=ContractOrdersHistoryResponse,
|
|
286
301
|
)
|
|
287
302
|
|
|
288
303
|
async def get_today_contract_earnings(
|
|
@@ -389,7 +404,7 @@ class BXUltraClient(ExchangeBase):
|
|
|
389
404
|
# endregion
|
|
390
405
|
###########################################################
|
|
391
406
|
# region copy-trade-facade
|
|
392
|
-
async def
|
|
407
|
+
async def get_copy_trader_positions(
|
|
393
408
|
self,
|
|
394
409
|
uid: int | str,
|
|
395
410
|
api_identity: str,
|
|
@@ -409,7 +424,7 @@ class BXUltraClient(ExchangeBase):
|
|
|
409
424
|
f"{self.we_api_base_url}/copy-trade-facade/v2/real/trader/positions",
|
|
410
425
|
headers=headers,
|
|
411
426
|
params=params,
|
|
412
|
-
|
|
427
|
+
model_type=CopyTraderTradePositionsResponse,
|
|
413
428
|
)
|
|
414
429
|
|
|
415
430
|
async def search_copy_traders(
|
|
@@ -446,7 +461,7 @@ class BXUltraClient(ExchangeBase):
|
|
|
446
461
|
headers=headers,
|
|
447
462
|
params=params,
|
|
448
463
|
content=payload,
|
|
449
|
-
|
|
464
|
+
model_type=SearchCopyTradersResponse,
|
|
450
465
|
)
|
|
451
466
|
|
|
452
467
|
async def get_copy_trader_futures_stats(
|
|
@@ -454,6 +469,11 @@ class BXUltraClient(ExchangeBase):
|
|
|
454
469
|
uid: int | str,
|
|
455
470
|
api_identity: str,
|
|
456
471
|
) -> CopyTraderFuturesStatsResponse:
|
|
472
|
+
"""
|
|
473
|
+
Returns futures statistics of a certain trader.
|
|
474
|
+
If you do not have the api_identity parameter, please first invoke
|
|
475
|
+
get_copy_trader_resume method and get it from there.
|
|
476
|
+
"""
|
|
457
477
|
params = {
|
|
458
478
|
"uid": f"{uid}",
|
|
459
479
|
"apiIdentity": f"{api_identity}",
|
|
@@ -463,7 +483,7 @@ class BXUltraClient(ExchangeBase):
|
|
|
463
483
|
f"{self.we_api_base_url}/copy-trade-facade/v4/trader/account/futures/stat",
|
|
464
484
|
headers=headers,
|
|
465
485
|
params=params,
|
|
466
|
-
|
|
486
|
+
model_type=CopyTraderFuturesStatsResponse,
|
|
467
487
|
)
|
|
468
488
|
|
|
469
489
|
async def get_copy_trader_resume(
|
|
@@ -478,9 +498,22 @@ class BXUltraClient(ExchangeBase):
|
|
|
478
498
|
f"{self.we_api_base_url}/copy-trade-facade/v1/trader/resume",
|
|
479
499
|
headers=headers,
|
|
480
500
|
params=params,
|
|
481
|
-
|
|
501
|
+
model_type=CopyTraderResumeResponse,
|
|
482
502
|
)
|
|
483
503
|
|
|
504
|
+
async def get_trader_api_identity(
|
|
505
|
+
self,
|
|
506
|
+
uid: int | str,
|
|
507
|
+
) -> int | str:
|
|
508
|
+
api_identity = user_api_identity_cache.get(uid, None)
|
|
509
|
+
if not api_identity:
|
|
510
|
+
resume = await self.get_copy_trader_resume(
|
|
511
|
+
uid=uid,
|
|
512
|
+
)
|
|
513
|
+
api_identity = resume.data.api_identity
|
|
514
|
+
user_api_identity_cache[uid] = api_identity
|
|
515
|
+
return api_identity
|
|
516
|
+
|
|
484
517
|
# endregion
|
|
485
518
|
###########################################################
|
|
486
519
|
# region welfare
|
|
@@ -490,7 +523,7 @@ class BXUltraClient(ExchangeBase):
|
|
|
490
523
|
f"{self.original_base_host}/api/act-operation/v1/welfare/sign-in/do",
|
|
491
524
|
headers=headers,
|
|
492
525
|
content="",
|
|
493
|
-
|
|
526
|
+
model_type=None,
|
|
494
527
|
)
|
|
495
528
|
|
|
496
529
|
# endregion
|
|
@@ -540,56 +573,6 @@ class BXUltraClient(ExchangeBase):
|
|
|
540
573
|
the_headers["Authorization"] = f"Bearer {self.authorization_token}"
|
|
541
574
|
return the_headers
|
|
542
575
|
|
|
543
|
-
async def invoke_get(
|
|
544
|
-
self,
|
|
545
|
-
url: str,
|
|
546
|
-
headers: dict | None = None,
|
|
547
|
-
params: dict | None = None,
|
|
548
|
-
model: Type[BxApiResponse] | None = None,
|
|
549
|
-
parse_float=Decimal,
|
|
550
|
-
) -> "BxApiResponse":
|
|
551
|
-
"""
|
|
552
|
-
Invokes the specific request to the specific url with the specific params and headers.
|
|
553
|
-
"""
|
|
554
|
-
response = await self.httpx_client.get(
|
|
555
|
-
url=url,
|
|
556
|
-
headers=headers,
|
|
557
|
-
params=params,
|
|
558
|
-
)
|
|
559
|
-
return model.deserialize(response.json(parse_float=parse_float))
|
|
560
|
-
|
|
561
|
-
async def invoke_post(
|
|
562
|
-
self,
|
|
563
|
-
url: str,
|
|
564
|
-
headers: dict | None = None,
|
|
565
|
-
params: dict | None = None,
|
|
566
|
-
content: dict | str | bytes = "",
|
|
567
|
-
model: Type[BxApiResponse] | None = None,
|
|
568
|
-
parse_float=Decimal,
|
|
569
|
-
) -> "BxApiResponse":
|
|
570
|
-
"""
|
|
571
|
-
Invokes the specific request to the specific url with the specific params and headers.
|
|
572
|
-
"""
|
|
573
|
-
|
|
574
|
-
if isinstance(content, dict):
|
|
575
|
-
content = json.dumps(content, separators=(",", ":"), sort_keys=True)
|
|
576
|
-
|
|
577
|
-
response = await self.httpx_client.post(
|
|
578
|
-
url=url,
|
|
579
|
-
headers=headers,
|
|
580
|
-
params=params,
|
|
581
|
-
content=content,
|
|
582
|
-
)
|
|
583
|
-
if not model:
|
|
584
|
-
return response.json()
|
|
585
|
-
|
|
586
|
-
return model.deserialize(response.json(parse_float=parse_float))
|
|
587
|
-
|
|
588
|
-
async def aclose(self) -> None:
|
|
589
|
-
await self.httpx_client.aclose()
|
|
590
|
-
logger.info("BXUltraClient closed")
|
|
591
|
-
return True
|
|
592
|
-
|
|
593
576
|
def read_from_session_file(self, file_path: str) -> None:
|
|
594
577
|
"""
|
|
595
578
|
Reads from session file; if it doesn't exist, creates it.
|
|
@@ -654,3 +637,74 @@ class BXUltraClient(ExchangeBase):
|
|
|
654
637
|
|
|
655
638
|
# endregion
|
|
656
639
|
###########################################################
|
|
640
|
+
# region unified methods
|
|
641
|
+
|
|
642
|
+
async def get_unified_trader_positions(
|
|
643
|
+
self,
|
|
644
|
+
uid: int | str,
|
|
645
|
+
) -> UnifiedTraderPositions:
|
|
646
|
+
global user_api_identity_cache
|
|
647
|
+
|
|
648
|
+
api_identity = await self.get_trader_api_identity(
|
|
649
|
+
uid=uid,
|
|
650
|
+
)
|
|
651
|
+
|
|
652
|
+
result = await self.get_copy_trader_positions(
|
|
653
|
+
uid=uid,
|
|
654
|
+
api_identity=api_identity,
|
|
655
|
+
page_size=50, # TODO: make this dynamic I guess...
|
|
656
|
+
)
|
|
657
|
+
if result.data.hide:
|
|
658
|
+
# TODO: do proper exceptions here...
|
|
659
|
+
raise ValueError("The trader has made their positions hidden")
|
|
660
|
+
unified_result = UnifiedTraderPositions()
|
|
661
|
+
unified_result.positions = []
|
|
662
|
+
for position in result.data.positions:
|
|
663
|
+
unified_pos = UnifiedPositionInfo()
|
|
664
|
+
unified_pos.position_id = position.position_no
|
|
665
|
+
unified_pos.position_pnl = position.unrealized_pnl
|
|
666
|
+
unified_pos.position_side = position.position_side
|
|
667
|
+
unified_pos.margin_mode = "isolated" # TODO: fix this
|
|
668
|
+
unified_pos.position_leverage = position.leverage
|
|
669
|
+
unified_pos.position_pair = (
|
|
670
|
+
position.symbol
|
|
671
|
+
) # TODO: make sure correct format
|
|
672
|
+
unified_pos.open_time = None # TODO: do something for this?
|
|
673
|
+
unified_pos.open_price = position.avg_price
|
|
674
|
+
unified_pos.open_price_unit = position.symbol.split("-")[-1] # TODO
|
|
675
|
+
unified_result.positions.append(unified_pos)
|
|
676
|
+
|
|
677
|
+
return unified_result
|
|
678
|
+
|
|
679
|
+
async def get_unified_trader_info(
|
|
680
|
+
self,
|
|
681
|
+
uid: int | str,
|
|
682
|
+
) -> UnifiedTraderInfo:
|
|
683
|
+
resume_resp = await self.get_copy_trader_resume(
|
|
684
|
+
uid=uid,
|
|
685
|
+
)
|
|
686
|
+
if resume_resp.code != 0 and not resume_resp.data:
|
|
687
|
+
if resume_resp.msg:
|
|
688
|
+
raise ValueError(f"got error from API: {resume_resp.msg}")
|
|
689
|
+
raise ValueError(
|
|
690
|
+
f"got unknown error from bx API while fetching resume for {uid}"
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
resume = resume_resp.data
|
|
694
|
+
api_identity = resume.api_identity
|
|
695
|
+
|
|
696
|
+
info_resp = await self.get_copy_trader_futures_stats(
|
|
697
|
+
uid=uid,
|
|
698
|
+
api_identity=api_identity,
|
|
699
|
+
)
|
|
700
|
+
info = info_resp.data
|
|
701
|
+
unified_info = UnifiedTraderInfo()
|
|
702
|
+
unified_info.trader_id = resume.trader_info.uid
|
|
703
|
+
unified_info.trader_name = resume.trader_info.nick_name
|
|
704
|
+
unified_info.trader_url = f"{BASE_PROFILE_URL}{uid}"
|
|
705
|
+
unified_info.win_rate = Decimal(info.win_rate.rstrip("%")) / 100
|
|
706
|
+
|
|
707
|
+
return unified_info
|
|
708
|
+
|
|
709
|
+
# endregion
|
|
710
|
+
###########################################################
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
from decimal import Decimal
|
|
2
|
-
|
|
2
|
+
import json
|
|
3
|
+
from typing import Any, Type
|
|
3
4
|
from abc import ABC
|
|
4
5
|
|
|
5
6
|
import httpx
|
|
6
7
|
|
|
7
8
|
from trd_utils.exchanges.base_types import UnifiedTraderInfo, UnifiedTraderPositions
|
|
9
|
+
from trd_utils.types_helper.base_model import BaseModel
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
class ExchangeBase(ABC):
|
|
@@ -66,32 +68,55 @@ class ExchangeBase(ABC):
|
|
|
66
68
|
async def invoke_get(
|
|
67
69
|
self,
|
|
68
70
|
url: str,
|
|
69
|
-
headers: dict | None,
|
|
70
|
-
params: dict | None,
|
|
71
|
-
|
|
71
|
+
headers: dict | None = None,
|
|
72
|
+
params: dict | None = None,
|
|
73
|
+
model_type: Type[BaseModel] | None = None,
|
|
72
74
|
parse_float=Decimal,
|
|
73
|
-
) ->
|
|
75
|
+
) -> "BaseModel":
|
|
74
76
|
"""
|
|
75
77
|
Invokes the specific request to the specific url with the specific params and headers.
|
|
76
78
|
"""
|
|
77
|
-
|
|
79
|
+
response = await self.httpx_client.get(
|
|
80
|
+
url=url,
|
|
81
|
+
headers=headers,
|
|
82
|
+
params=params,
|
|
83
|
+
)
|
|
84
|
+
return model_type.deserialize(response.json(parse_float=parse_float))
|
|
78
85
|
|
|
79
86
|
async def invoke_post(
|
|
80
87
|
self,
|
|
81
88
|
url: str,
|
|
82
89
|
headers: dict | None = None,
|
|
83
90
|
params: dict | None = None,
|
|
84
|
-
content: str | bytes = "",
|
|
85
|
-
|
|
91
|
+
content: dict | str | bytes = "",
|
|
92
|
+
model_type: Type[BaseModel] | None = None,
|
|
86
93
|
parse_float=Decimal,
|
|
87
|
-
):
|
|
94
|
+
) -> "BaseModel":
|
|
88
95
|
"""
|
|
89
96
|
Invokes the specific request to the specific url with the specific params and headers.
|
|
90
97
|
"""
|
|
91
|
-
|
|
98
|
+
|
|
99
|
+
if isinstance(content, dict):
|
|
100
|
+
content = json.dumps(content, separators=(",", ":"), sort_keys=True)
|
|
101
|
+
|
|
102
|
+
response = await self.httpx_client.post(
|
|
103
|
+
url=url,
|
|
104
|
+
headers=headers,
|
|
105
|
+
params=params,
|
|
106
|
+
content=content,
|
|
107
|
+
)
|
|
108
|
+
if not model_type:
|
|
109
|
+
return response.json()
|
|
110
|
+
|
|
111
|
+
return model_type.deserialize(response.json(parse_float=parse_float))
|
|
112
|
+
|
|
92
113
|
|
|
93
114
|
async def aclose(self) -> None:
|
|
94
|
-
|
|
115
|
+
await self.httpx_client.aclose()
|
|
116
|
+
|
|
117
|
+
# endregion
|
|
118
|
+
###########################################################
|
|
119
|
+
# region data-files related methods
|
|
95
120
|
|
|
96
121
|
def read_from_session_file(self, file_path: str) -> None:
|
|
97
122
|
"""
|
|
@@ -102,56 +102,6 @@ class HyperLiquidClient(ExchangeBase):
|
|
|
102
102
|
the_headers["Authorization"] = f"Bearer {self.authorization_token}"
|
|
103
103
|
return the_headers
|
|
104
104
|
|
|
105
|
-
async def invoke_get(
|
|
106
|
-
self,
|
|
107
|
-
url: str,
|
|
108
|
-
headers: dict | None = None,
|
|
109
|
-
params: dict | None = None,
|
|
110
|
-
model: Type[HyperLiquidApiResponse] | None = None,
|
|
111
|
-
parse_float=Decimal,
|
|
112
|
-
) -> "HyperLiquidApiResponse":
|
|
113
|
-
"""
|
|
114
|
-
Invokes the specific request to the specific url with the specific params and headers.
|
|
115
|
-
"""
|
|
116
|
-
response = await self.httpx_client.get(
|
|
117
|
-
url=url,
|
|
118
|
-
headers=headers,
|
|
119
|
-
params=params,
|
|
120
|
-
)
|
|
121
|
-
return model.deserialize(response.json(parse_float=parse_float))
|
|
122
|
-
|
|
123
|
-
async def invoke_post(
|
|
124
|
-
self,
|
|
125
|
-
url: str,
|
|
126
|
-
headers: dict | None = None,
|
|
127
|
-
params: dict | None = None,
|
|
128
|
-
content: dict | str | bytes = "",
|
|
129
|
-
model: Type[HyperLiquidApiResponse] | None = None,
|
|
130
|
-
parse_float=Decimal,
|
|
131
|
-
) -> "HyperLiquidApiResponse":
|
|
132
|
-
"""
|
|
133
|
-
Invokes the specific request to the specific url with the specific params and headers.
|
|
134
|
-
"""
|
|
135
|
-
|
|
136
|
-
if isinstance(content, dict):
|
|
137
|
-
content = json.dumps(content, separators=(",", ":"), sort_keys=True)
|
|
138
|
-
|
|
139
|
-
response = await self.httpx_client.post(
|
|
140
|
-
url=url,
|
|
141
|
-
headers=headers,
|
|
142
|
-
params=params,
|
|
143
|
-
content=content,
|
|
144
|
-
)
|
|
145
|
-
if not model:
|
|
146
|
-
return response.json()
|
|
147
|
-
|
|
148
|
-
return model.deserialize(response.json(parse_float=parse_float))
|
|
149
|
-
|
|
150
|
-
async def aclose(self) -> None:
|
|
151
|
-
await self.httpx_client.aclose()
|
|
152
|
-
logger.info("HyperLiquidClient closed")
|
|
153
|
-
return True
|
|
154
|
-
|
|
155
105
|
def read_from_session_file(self, file_path: str) -> None:
|
|
156
106
|
"""
|
|
157
107
|
Reads from session file; if it doesn't exist, creates it.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
trd_utils/__init__.py,sha256=TpbNgwUz9GdaWu47KkuekhUKxSBLfBCjqfgHJ3CAzUc,25
|
|
2
|
+
trd_utils/cipher/__init__.py,sha256=V05KNuzQwCic-ihMVHlC8sENaJGc3I8MCb4pg4849X8,1765
|
|
3
|
+
trd_utils/common_utils/float_utils.py,sha256=W-jv7nzjl88xwGB6gsEXmDDhF6DseOrrVT2qx7OvyCo,266
|
|
4
|
+
trd_utils/date_utils/__init__.py,sha256=Erg_E1TfKWNpiuZFm_NXRjCwoRMfxpPS2-mJK6V4lFM,77
|
|
5
|
+
trd_utils/date_utils/datetime_helpers.py,sha256=Ai9rDCLoMg2dRH8NIBTJQgUSfNQp15Vtp6a1IQwHEbw,366
|
|
6
|
+
trd_utils/exchanges/README.md,sha256=UwkpsfcoLCJaMvJe4yBsFkDpf8P6DOLYhtybb6xWMLc,6738
|
|
7
|
+
trd_utils/exchanges/__init__.py,sha256=x74_8-P7ktZyL-1AuAU_SDQD8eKZMPh1gfkLzPuS1xY,440
|
|
8
|
+
trd_utils/exchanges/base_types.py,sha256=bPsRUetQIHdW7ESMAQcjvX-Z2uixtCC93Vb-XnX6FFs,3229
|
|
9
|
+
trd_utils/exchanges/blofin/__init__.py,sha256=X4r9o4Nyjla4UeOBG8lrgtnGYO2aErFMKaJ7yQrFasE,76
|
|
10
|
+
trd_utils/exchanges/blofin/blofin_client.py,sha256=q9NrAalg8KtyBkDrFKA0Vkq0iw2aIK7SQugzVpPJHds,12261
|
|
11
|
+
trd_utils/exchanges/blofin/blofin_types.py,sha256=LnK3LEVlqU3Vg0Cg1jNmb5GGHQImwI6LCKaDw-aQa6A,4059
|
|
12
|
+
trd_utils/exchanges/bx_ultra/__init__.py,sha256=8Ssy-eOemQR32Nv1-FoPHm87nRqRO4Fm2PU5GHEFKfQ,80
|
|
13
|
+
trd_utils/exchanges/bx_ultra/bx_types.py,sha256=muHLa9lEjIOIDLQCb6xdyxSoQiIMSHErVe0NCoAS-RI,31017
|
|
14
|
+
trd_utils/exchanges/bx_ultra/bx_ultra_client.py,sha256=VcHBhyckn99T8bI_se9u2vyfOHl3w8VPuLrjE16-sQU,24978
|
|
15
|
+
trd_utils/exchanges/bx_ultra/bx_utils.py,sha256=PwapomwDW33arVmKIDj6cL-aP0ptu4BYy_lOCqSAPOo,1392
|
|
16
|
+
trd_utils/exchanges/exchange_base.py,sha256=ZY_9vL9LXtAbAmlWygdVyy0wC-QDla-JGWZ8HMRlkk8,4266
|
|
17
|
+
trd_utils/exchanges/hyperliquid/README.md,sha256=-qaxmDt_9NTus2xRuzyFGkKgYDWgWk7ufHVTSkyn3t4,105
|
|
18
|
+
trd_utils/exchanges/hyperliquid/__init__.py,sha256=QhwGRcneGFHREM-MMdYpbcx-aWdsWsu2WznHzx7LaUM,92
|
|
19
|
+
trd_utils/exchanges/hyperliquid/hyperliquid_client.py,sha256=tEYys_w0sGzxzGdLAUIfl14YTh-zapYwxH0K3B5jBxM,4683
|
|
20
|
+
trd_utils/exchanges/hyperliquid/hyperliquid_types.py,sha256=ueL7Q4yOAK4orlUqeLVNRk6u1AG83pDeGJasTeT3774,2666
|
|
21
|
+
trd_utils/html_utils/__init__.py,sha256=1WWs8C7JszRjTkmzIRLHpxWECHur_DrulTPGIeX88oM,426
|
|
22
|
+
trd_utils/html_utils/html_formats.py,sha256=unKsvOiiDmYTTaM0DYZEUNLEUzWQKKrqASJXvY54kvU,2299
|
|
23
|
+
trd_utils/tradingview/__init__.py,sha256=H0QYb-O5qvy7qC3yswtlcSWLmeBnaS6oJ3JtjvmaV_Y,154
|
|
24
|
+
trd_utils/tradingview/tradingview_client.py,sha256=g_eWYaCRQAL8Kvd-r6AnAdbH7Jha6C_GAyCuxh-RQUU,3917
|
|
25
|
+
trd_utils/tradingview/tradingview_types.py,sha256=z21MXPVdWHAduEl3gSeMIRhxtBN9yK-jPYHfZSMIbSA,6144
|
|
26
|
+
trd_utils/types_helper/__init__.py,sha256=lLbUiW1jUV1gjzTMFLthwkvF0hwauH-F_J2JZq--1U0,67
|
|
27
|
+
trd_utils/types_helper/base_model.py,sha256=cdkvzkjZ1RE__GY98YvhtALHKMux9M4uzcdd8ofQNYU,10617
|
|
28
|
+
trd_utils-0.0.16.dist-info/LICENSE,sha256=J1EP2xt87RjjmsTV1jTjHDQMLIM9FjdwEftTpw8hyv4,1067
|
|
29
|
+
trd_utils-0.0.16.dist-info/METADATA,sha256=vUC58vdFOVmQuZeOra-BglRiwTjzItdZBtEYTCzvOvE,1095
|
|
30
|
+
trd_utils-0.0.16.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
31
|
+
trd_utils-0.0.16.dist-info/RECORD,,
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
trd_utils/__init__.py,sha256=tth9x7eIK8zwa9EZR2wIczpeBj0qNJRjG8svFe9CfmE,25
|
|
2
|
-
trd_utils/cipher/__init__.py,sha256=V05KNuzQwCic-ihMVHlC8sENaJGc3I8MCb4pg4849X8,1765
|
|
3
|
-
trd_utils/common_utils/float_utils.py,sha256=W-jv7nzjl88xwGB6gsEXmDDhF6DseOrrVT2qx7OvyCo,266
|
|
4
|
-
trd_utils/exchanges/README.md,sha256=UwkpsfcoLCJaMvJe4yBsFkDpf8P6DOLYhtybb6xWMLc,6738
|
|
5
|
-
trd_utils/exchanges/__init__.py,sha256=ghL9RKX76Fr6xhtZ6QEBwq5GCg0A3HRZMvsgD-BNnus,426
|
|
6
|
-
trd_utils/exchanges/base_types.py,sha256=NkgTw81oAUG0BMWHOS_kMOBoR17N9N1FQaXVvwuWhvQ,1417
|
|
7
|
-
trd_utils/exchanges/blofin/__init__.py,sha256=dQkY9aSbI5fZJDOSbkrbrbpHSbWbJjLEmjpkXxDMDD4,74
|
|
8
|
-
trd_utils/exchanges/blofin/blofin_client.py,sha256=TWeo90mubIzfxIfRRX8gGT7EE_Ye6EShYxXrxKLK1oU,12092
|
|
9
|
-
trd_utils/exchanges/blofin/blofin_types.py,sha256=ejBgJeGrbqVrLIJSZ8UZQIuIxhlDOVm1UOJFBI_qvJg,4055
|
|
10
|
-
trd_utils/exchanges/bx_ultra/__init__.py,sha256=8Ssy-eOemQR32Nv1-FoPHm87nRqRO4Fm2PU5GHEFKfQ,80
|
|
11
|
-
trd_utils/exchanges/bx_ultra/bx_types.py,sha256=muHLa9lEjIOIDLQCb6xdyxSoQiIMSHErVe0NCoAS-RI,31017
|
|
12
|
-
trd_utils/exchanges/bx_ultra/bx_ultra_client.py,sha256=UtWbS__FYyYbYgGvxIrpIkuXLaD4nppP_AyEZWOe5ec,22591
|
|
13
|
-
trd_utils/exchanges/bx_ultra/bx_utils.py,sha256=PwapomwDW33arVmKIDj6cL-aP0ptu4BYy_lOCqSAPOo,1392
|
|
14
|
-
trd_utils/exchanges/exchange_base.py,sha256=-cphxYRbj_DTls2a_HsYMSsutAVCIxI_2lcOdwHGBZE,3330
|
|
15
|
-
trd_utils/exchanges/hyperliquid/README.md,sha256=-qaxmDt_9NTus2xRuzyFGkKgYDWgWk7ufHVTSkyn3t4,105
|
|
16
|
-
trd_utils/exchanges/hyperliquid/__init__.py,sha256=mgrug9TELB1K4T0QVynYzz4QDGR850_CKJLvjgAUY1k,90
|
|
17
|
-
trd_utils/exchanges/hyperliquid/hyperliquid_client.py,sha256=BZe7GZ_Fcxsl3doapvTnfXibwidF5TesZY6dQU16QUs,6245
|
|
18
|
-
trd_utils/exchanges/hyperliquid/hyperliquid_types.py,sha256=ueL7Q4yOAK4orlUqeLVNRk6u1AG83pDeGJasTeT3774,2666
|
|
19
|
-
trd_utils/html_utils/__init__.py,sha256=1WWs8C7JszRjTkmzIRLHpxWECHur_DrulTPGIeX88oM,426
|
|
20
|
-
trd_utils/html_utils/html_formats.py,sha256=unKsvOiiDmYTTaM0DYZEUNLEUzWQKKrqASJXvY54kvU,2299
|
|
21
|
-
trd_utils/tradingview/__init__.py,sha256=H0QYb-O5qvy7qC3yswtlcSWLmeBnaS6oJ3JtjvmaV_Y,154
|
|
22
|
-
trd_utils/tradingview/tradingview_client.py,sha256=g_eWYaCRQAL8Kvd-r6AnAdbH7Jha6C_GAyCuxh-RQUU,3917
|
|
23
|
-
trd_utils/tradingview/tradingview_types.py,sha256=z21MXPVdWHAduEl3gSeMIRhxtBN9yK-jPYHfZSMIbSA,6144
|
|
24
|
-
trd_utils/types_helper/__init__.py,sha256=VlEXDzOyn6fYH-dE86EGJ6u_el08QvdyOtJkj-0EAVA,65
|
|
25
|
-
trd_utils/types_helper/base_model.py,sha256=uy3s_AzMBxCVQEIMQYZZw9a5go327oOEGIl1iIZP-gs,10600
|
|
26
|
-
trd_utils-0.0.15.dist-info/LICENSE,sha256=J1EP2xt87RjjmsTV1jTjHDQMLIM9FjdwEftTpw8hyv4,1067
|
|
27
|
-
trd_utils-0.0.15.dist-info/METADATA,sha256=eI7bHdiO4nCbVmy-5CU81hyt_Tej_mtSfV89aiccL74,1095
|
|
28
|
-
trd_utils-0.0.15.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
29
|
-
trd_utils-0.0.15.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|