trd-utils 0.0.13__py3-none-any.whl → 0.0.15__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/exchanges/README.md +201 -0
- trd_utils/exchanges/__init__.py +10 -0
- trd_utils/exchanges/base_types.py +53 -0
- trd_utils/exchanges/blofin/blofin_client.py +31 -10
- trd_utils/exchanges/blofin/blofin_types.py +35 -35
- trd_utils/exchanges/bx_ultra/bx_types.py +289 -42
- trd_utils/exchanges/bx_ultra/bx_ultra_client.py +46 -3
- trd_utils/exchanges/exchange_base.py +34 -1
- trd_utils/exchanges/hyperliquid/README.md +3 -0
- trd_utils/exchanges/hyperliquid/__init__.py +7 -0
- trd_utils/exchanges/hyperliquid/hyperliquid_client.py +188 -0
- trd_utils/exchanges/hyperliquid/hyperliquid_types.py +109 -0
- trd_utils/types_helper/base_model.py +11 -0
- {trd_utils-0.0.13.dist-info → trd_utils-0.0.15.dist-info}/METADATA +1 -1
- trd_utils-0.0.15.dist-info/RECORD +29 -0
- trd_utils-0.0.13.dist-info/RECORD +0 -23
- {trd_utils-0.0.13.dist-info → trd_utils-0.0.15.dist-info}/LICENSE +0 -0
- {trd_utils-0.0.13.dist-info → trd_utils-0.0.15.dist-info}/WHEEL +0 -0
|
@@ -17,6 +17,8 @@ from trd_utils.exchanges.bx_ultra.bx_types import (
|
|
|
17
17
|
AssetsInfoResponse,
|
|
18
18
|
ContractOrdersHistoryResponse,
|
|
19
19
|
ContractsListResponse,
|
|
20
|
+
CopyTraderFuturesStatsResponse,
|
|
21
|
+
CopyTraderResumeResponse,
|
|
20
22
|
CopyTraderTradePositionsResponse,
|
|
21
23
|
HintListResponse,
|
|
22
24
|
HomePageResponse,
|
|
@@ -26,6 +28,7 @@ from trd_utils.exchanges.bx_ultra.bx_types import (
|
|
|
26
28
|
SearchCopyTradersResponse,
|
|
27
29
|
UserFavoriteQuotationResponse,
|
|
28
30
|
ZenDeskABStatusResponse,
|
|
31
|
+
ZenDeskAuthResponse,
|
|
29
32
|
ZoneModuleListResponse,
|
|
30
33
|
BxApiResponse,
|
|
31
34
|
)
|
|
@@ -45,6 +48,8 @@ ANDROID_APP_VERSION = "4.28.3"
|
|
|
45
48
|
WEB_APP_VERSION = "4.78.12"
|
|
46
49
|
TG_APP_VERSION = "5.0.15"
|
|
47
50
|
|
|
51
|
+
ACCEPT_ENCODING_HEADER = "gzip, deflate, br, zstd"
|
|
52
|
+
|
|
48
53
|
logger = logging.getLogger(__name__)
|
|
49
54
|
|
|
50
55
|
|
|
@@ -195,7 +200,13 @@ class BXUltraClient(ExchangeBase):
|
|
|
195
200
|
headers=headers,
|
|
196
201
|
model=ZenDeskABStatusResponse,
|
|
197
202
|
)
|
|
198
|
-
|
|
203
|
+
async def do_zendesk_auth(self) -> ZenDeskAuthResponse:
|
|
204
|
+
headers = self.get_headers(needs_auth=True)
|
|
205
|
+
return await self.invoke_get(
|
|
206
|
+
f"{self.we_api_base_url}/customer/v1/zendesk/auth/jwt",
|
|
207
|
+
headers=headers,
|
|
208
|
+
model=ZenDeskAuthResponse,
|
|
209
|
+
)
|
|
199
210
|
# endregion
|
|
200
211
|
###########################################################
|
|
201
212
|
# region platform-tool
|
|
@@ -380,7 +391,7 @@ class BXUltraClient(ExchangeBase):
|
|
|
380
391
|
# region copy-trade-facade
|
|
381
392
|
async def get_copy_trade_trader_positions(
|
|
382
393
|
self,
|
|
383
|
-
uid: str,
|
|
394
|
+
uid: int | str,
|
|
384
395
|
api_identity: str,
|
|
385
396
|
page_size: int = 20,
|
|
386
397
|
page_id: int = 0,
|
|
@@ -438,6 +449,38 @@ class BXUltraClient(ExchangeBase):
|
|
|
438
449
|
model=SearchCopyTradersResponse,
|
|
439
450
|
)
|
|
440
451
|
|
|
452
|
+
async def get_copy_trader_futures_stats(
|
|
453
|
+
self,
|
|
454
|
+
uid: int | str,
|
|
455
|
+
api_identity: str,
|
|
456
|
+
) -> CopyTraderFuturesStatsResponse:
|
|
457
|
+
params = {
|
|
458
|
+
"uid": f"{uid}",
|
|
459
|
+
"apiIdentity": f"{api_identity}",
|
|
460
|
+
}
|
|
461
|
+
headers = self.get_headers(params)
|
|
462
|
+
return await self.invoke_get(
|
|
463
|
+
f"{self.we_api_base_url}/copy-trade-facade/v4/trader/account/futures/stat",
|
|
464
|
+
headers=headers,
|
|
465
|
+
params=params,
|
|
466
|
+
model=CopyTraderFuturesStatsResponse,
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
async def get_copy_trader_resume(
|
|
470
|
+
self,
|
|
471
|
+
uid: int | str,
|
|
472
|
+
) -> CopyTraderResumeResponse:
|
|
473
|
+
params = {
|
|
474
|
+
"uid": f"{uid}",
|
|
475
|
+
}
|
|
476
|
+
headers = self.get_headers(params)
|
|
477
|
+
return await self.invoke_get(
|
|
478
|
+
f"{self.we_api_base_url}/copy-trade-facade/v1/trader/resume",
|
|
479
|
+
headers=headers,
|
|
480
|
+
params=params,
|
|
481
|
+
model=CopyTraderResumeResponse,
|
|
482
|
+
)
|
|
483
|
+
|
|
441
484
|
# endregion
|
|
442
485
|
###########################################################
|
|
443
486
|
# region welfare
|
|
@@ -484,7 +527,7 @@ class BXUltraClient(ExchangeBase):
|
|
|
484
527
|
payload_data=payload,
|
|
485
528
|
),
|
|
486
529
|
"Timestamp": f"{the_timestamp}",
|
|
487
|
-
|
|
530
|
+
"Accept-Encoding": ACCEPT_ENCODING_HEADER,
|
|
488
531
|
"User-Agent": self.user_agent,
|
|
489
532
|
"Connection": "close",
|
|
490
533
|
"appsiteid": "0",
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
|
|
2
1
|
from decimal import Decimal
|
|
3
2
|
from typing import Any
|
|
4
3
|
from abc import ABC
|
|
5
4
|
|
|
6
5
|
import httpx
|
|
7
6
|
|
|
7
|
+
from trd_utils.exchanges.base_types import UnifiedTraderInfo, UnifiedTraderPositions
|
|
8
|
+
|
|
8
9
|
|
|
9
10
|
class ExchangeBase(ABC):
|
|
10
11
|
###########################################################
|
|
@@ -25,6 +26,38 @@ class ExchangeBase(ABC):
|
|
|
25
26
|
|
|
26
27
|
_fav_letter: str = "^"
|
|
27
28
|
# endregion
|
|
29
|
+
###########################################################
|
|
30
|
+
|
|
31
|
+
# region abstract trading methods
|
|
32
|
+
|
|
33
|
+
async def get_unified_trader_positions(
|
|
34
|
+
self,
|
|
35
|
+
uid: int | str,
|
|
36
|
+
) -> UnifiedTraderPositions:
|
|
37
|
+
"""
|
|
38
|
+
Returns the unified version of all currently open positions of the specific
|
|
39
|
+
trader. Note that different exchanges might fill different fields, according to the
|
|
40
|
+
data they provide in their public APIs.
|
|
41
|
+
If you want to fetch past positions history, you have to use another method.
|
|
42
|
+
"""
|
|
43
|
+
raise NotImplementedError(
|
|
44
|
+
"This method is not implemented in ExchangeBase class. "
|
|
45
|
+
"Please use a real exchange class inheriting and implementing this method."
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
async def get_unified_trader_info(self, uid: int | str) -> UnifiedTraderInfo:
|
|
49
|
+
"""
|
|
50
|
+
Returns information about a specific trader.
|
|
51
|
+
Different exchanges might return and fill different information according to the
|
|
52
|
+
data returned from their public APIs.
|
|
53
|
+
"""
|
|
54
|
+
raise NotImplementedError(
|
|
55
|
+
"This method is not implemented in ExchangeBase class. "
|
|
56
|
+
"Please use a real exchange class inheriting and implementing this method."
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# endregion
|
|
60
|
+
|
|
28
61
|
###########################################################
|
|
29
62
|
# region client helper methods
|
|
30
63
|
def get_headers(self, payload=None, needs_auth: bool = False) -> dict:
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
from typing import Type
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from trd_utils.cipher import AESCipher
|
|
11
|
+
from trd_utils.exchanges.exchange_base import ExchangeBase
|
|
12
|
+
from trd_utils.exchanges.hyperliquid.hyperliquid_types import HyperLiquidApiResponse, TraderPositionsInfoResponse
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class HyperLiquidClient(ExchangeBase):
|
|
18
|
+
###########################################################
|
|
19
|
+
# region client parameters
|
|
20
|
+
hyperliquid_api_base_host: str = "https://api.hyperliquid.xyz"
|
|
21
|
+
hyperliquid_api_base_url: str = "https://api.hyperliquid.xyz"
|
|
22
|
+
origin_header: str = "app.hyperliquid.xy"
|
|
23
|
+
|
|
24
|
+
# endregion
|
|
25
|
+
###########################################################
|
|
26
|
+
# region client constructor
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
account_name: str = "default",
|
|
30
|
+
http_verify: bool = True,
|
|
31
|
+
fav_letter: str = "^",
|
|
32
|
+
read_session_file: bool = False,
|
|
33
|
+
sessions_dir: str = "sessions",
|
|
34
|
+
use_http1: bool = True,
|
|
35
|
+
use_http2: bool = False,
|
|
36
|
+
):
|
|
37
|
+
# it looks like hyperliquid's api endpoints don't support http2 :(
|
|
38
|
+
self.httpx_client = httpx.AsyncClient(
|
|
39
|
+
verify=http_verify,
|
|
40
|
+
http1=use_http1,
|
|
41
|
+
http2=use_http2,
|
|
42
|
+
)
|
|
43
|
+
self.account_name = account_name
|
|
44
|
+
self._fav_letter = fav_letter
|
|
45
|
+
self.sessions_dir = sessions_dir
|
|
46
|
+
|
|
47
|
+
if read_session_file:
|
|
48
|
+
self.read_from_session_file(f"{sessions_dir}/{self.account_name}.hl")
|
|
49
|
+
|
|
50
|
+
# endregion
|
|
51
|
+
###########################################################
|
|
52
|
+
# region info endpoints
|
|
53
|
+
async def get_trader_positions_info(
|
|
54
|
+
self,
|
|
55
|
+
uid: int | str,
|
|
56
|
+
) -> TraderPositionsInfoResponse:
|
|
57
|
+
payload = {
|
|
58
|
+
"type": "clearinghouseState",
|
|
59
|
+
"user": f"{uid}",
|
|
60
|
+
}
|
|
61
|
+
headers = self.get_headers()
|
|
62
|
+
return await self.invoke_post(
|
|
63
|
+
f"{self.hyperliquid_api_base_host}/info",
|
|
64
|
+
headers=headers,
|
|
65
|
+
content=payload,
|
|
66
|
+
model=TraderPositionsInfoResponse,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
#endregion
|
|
70
|
+
###########################################################
|
|
71
|
+
# region another-thing
|
|
72
|
+
# async def get_another_thing_info(self, uid: int) -> AnotherThingInfoResponse:
|
|
73
|
+
# payload = {
|
|
74
|
+
# "uid": uid,
|
|
75
|
+
# }
|
|
76
|
+
# headers = self.get_headers()
|
|
77
|
+
# return await self.invoke_post(
|
|
78
|
+
# f"{self.hyperliquid_api_base_url}/another-thing/info",
|
|
79
|
+
# headers=headers,
|
|
80
|
+
# content=payload,
|
|
81
|
+
# model=CopyTraderInfoResponse,
|
|
82
|
+
# )
|
|
83
|
+
|
|
84
|
+
# endregion
|
|
85
|
+
###########################################################
|
|
86
|
+
# region client helper methods
|
|
87
|
+
def get_headers(self, payload=None, needs_auth: bool = False) -> dict:
|
|
88
|
+
the_headers = {
|
|
89
|
+
# "Host": self.hyperliquid_api_base_host,
|
|
90
|
+
"Content-Type": "application/json",
|
|
91
|
+
"Accept": "application/json",
|
|
92
|
+
"Accept-Encoding": "gzip, deflate, br, zstd",
|
|
93
|
+
"User-Agent": self.user_agent,
|
|
94
|
+
"Connection": "close",
|
|
95
|
+
"appsiteid": "0",
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if self.x_requested_with:
|
|
99
|
+
the_headers["X-Requested-With"] = self.x_requested_with
|
|
100
|
+
|
|
101
|
+
if needs_auth:
|
|
102
|
+
the_headers["Authorization"] = f"Bearer {self.authorization_token}"
|
|
103
|
+
return the_headers
|
|
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
|
+
def read_from_session_file(self, file_path: str) -> None:
|
|
156
|
+
"""
|
|
157
|
+
Reads from session file; if it doesn't exist, creates it.
|
|
158
|
+
"""
|
|
159
|
+
# check if path exists
|
|
160
|
+
target_path = Path(file_path)
|
|
161
|
+
if not target_path.exists():
|
|
162
|
+
return self._save_session_file(file_path=file_path)
|
|
163
|
+
|
|
164
|
+
aes = AESCipher(key=f"bf_{self.account_name}_bf", fav_letter=self._fav_letter)
|
|
165
|
+
content = aes.decrypt(target_path.read_text()).decode("utf-8")
|
|
166
|
+
json_data: dict = json.loads(content)
|
|
167
|
+
|
|
168
|
+
self.authorization_token = json_data.get(
|
|
169
|
+
"authorization_token",
|
|
170
|
+
self.authorization_token,
|
|
171
|
+
)
|
|
172
|
+
self.user_agent = json_data.get("user_agent", self.user_agent)
|
|
173
|
+
|
|
174
|
+
def _save_session_file(self, file_path: str) -> None:
|
|
175
|
+
"""
|
|
176
|
+
Saves current information to the session file.
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
json_data = {
|
|
180
|
+
"authorization_token": self.authorization_token,
|
|
181
|
+
"user_agent": self.user_agent,
|
|
182
|
+
}
|
|
183
|
+
aes = AESCipher(key=f"bf_{self.account_name}_bf", fav_letter=self._fav_letter)
|
|
184
|
+
target_path = Path(file_path)
|
|
185
|
+
target_path.write_text(aes.encrypt(json.dumps(json_data)))
|
|
186
|
+
|
|
187
|
+
# endregion
|
|
188
|
+
###########################################################
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
from trd_utils.types_helper import BaseModel
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
###########################################################
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# region Common types
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class HyperLiquidApiResponse(BaseModel):
|
|
12
|
+
time: int = None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# endregion
|
|
16
|
+
|
|
17
|
+
###########################################################
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# region info types
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class CumFundingInfo(BaseModel):
|
|
24
|
+
all_time: str = None
|
|
25
|
+
since_open: str = None
|
|
26
|
+
since_change: str = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class LeverageInfo(BaseModel):
|
|
30
|
+
type: str = None
|
|
31
|
+
value: int = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class PositionInfo(BaseModel):
|
|
35
|
+
coin: str = None
|
|
36
|
+
szi: Decimal = None
|
|
37
|
+
leverage: LeverageInfo = None
|
|
38
|
+
entry_px: Decimal = None
|
|
39
|
+
position_value: Decimal = None
|
|
40
|
+
unrealized_pnl: Decimal = None
|
|
41
|
+
return_on_equity: Decimal = None
|
|
42
|
+
liquidation_px: Decimal = None
|
|
43
|
+
margin_used: Decimal = None
|
|
44
|
+
max_leverage: int = None
|
|
45
|
+
cum_funding: CumFundingInfo = None
|
|
46
|
+
|
|
47
|
+
def get_side(self) -> str:
|
|
48
|
+
if self.szi > 0:
|
|
49
|
+
return "LONG"
|
|
50
|
+
elif self.szi < 0:
|
|
51
|
+
return "SHORT"
|
|
52
|
+
return "UNKNOWN_SIDE"
|
|
53
|
+
|
|
54
|
+
def get_position_id(self) -> str:
|
|
55
|
+
"""
|
|
56
|
+
As far as I know, the API endpoint does not return the position id,
|
|
57
|
+
maybe it only returns it to the account owner?
|
|
58
|
+
In any case, we will have to somehow fake it in order to be able to compare
|
|
59
|
+
it with other positions...
|
|
60
|
+
"""
|
|
61
|
+
return (
|
|
62
|
+
f"{self.coin}-{self.leverage.value}{self.entry_px}"
|
|
63
|
+
).encode("utf-8").hex()
|
|
64
|
+
|
|
65
|
+
def get_leverage(self) -> str:
|
|
66
|
+
return f"{self.leverage.value}x ({self.leverage.type})"
|
|
67
|
+
|
|
68
|
+
def __repr__(self):
|
|
69
|
+
return (
|
|
70
|
+
f"{self.get_side()} {self.get_leverage()} {self.coin} "
|
|
71
|
+
f"Margin: {self.margin_used}, PNL: {self.unrealized_pnl}"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def __str__(self):
|
|
75
|
+
return self.__repr__()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class AssetPosition(BaseModel):
|
|
79
|
+
type: str = None
|
|
80
|
+
position: PositionInfo = None
|
|
81
|
+
|
|
82
|
+
def __repr__(self):
|
|
83
|
+
return f"{self.position}; {self.type}"
|
|
84
|
+
|
|
85
|
+
def __str__(self):
|
|
86
|
+
return self.__str__()
|
|
87
|
+
|
|
88
|
+
def get_position_id(self) -> str:
|
|
89
|
+
return self.position.get_position_id()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class MarginSummaryInfo(BaseModel):
|
|
93
|
+
account_value: Decimal = None
|
|
94
|
+
total_ntl_pos: Decimal = None
|
|
95
|
+
total_raw_usd: Decimal = None
|
|
96
|
+
total_margin_used: Decimal = None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class TraderPositionsInfoResponse(BaseModel):
|
|
100
|
+
margin_summary: MarginSummaryInfo = None
|
|
101
|
+
cross_margin_summary: MarginSummaryInfo = None
|
|
102
|
+
cross_maintenance_margin_used: Decimal = None
|
|
103
|
+
withdrawable: Decimal = None
|
|
104
|
+
asset_positions: list[AssetPosition] = None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# endregion
|
|
108
|
+
|
|
109
|
+
###########################################################
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import datetime
|
|
1
2
|
from decimal import Decimal
|
|
2
3
|
import json
|
|
3
4
|
from typing import (
|
|
@@ -7,6 +8,8 @@ from typing import (
|
|
|
7
8
|
get_args as get_type_args,
|
|
8
9
|
)
|
|
9
10
|
|
|
11
|
+
import dateutil.parser
|
|
12
|
+
|
|
10
13
|
from trd_utils.html_utils.html_formats import camel_to_snake
|
|
11
14
|
|
|
12
15
|
# Whether to use ultra-list instead of normal python list or not.
|
|
@@ -241,6 +244,14 @@ class BaseModel:
|
|
|
241
244
|
|
|
242
245
|
if ULTRA_LIST_ENABLED and isinstance(value, list):
|
|
243
246
|
value = convert_to_ultra_list(value)
|
|
247
|
+
elif expected_type is datetime.datetime and isinstance(value, str):
|
|
248
|
+
try:
|
|
249
|
+
value = dateutil.parser.parse(value)
|
|
250
|
+
except Exception as ex:
|
|
251
|
+
raise ValueError(
|
|
252
|
+
f"Failed to parse the string as datetime: {value}"
|
|
253
|
+
f" Are you sure it's in correct format? inner error: {ex}"
|
|
254
|
+
)
|
|
244
255
|
|
|
245
256
|
# Type checking
|
|
246
257
|
elif not (is_any_type(expected_type) or isinstance(value, expected_type)):
|
|
@@ -0,0 +1,29 @@
|
|
|
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,,
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
trd_utils/__init__.py,sha256=ksSTwj4RO6vrwFfVs_ChoS8hAYDemQDbl8pB-dXIwV0,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/__init__.py,sha256=SQJt5cIXh305miWuDumkOLZHzqDUyOqSmlhTT9Xc9RY,180
|
|
5
|
-
trd_utils/exchanges/blofin/__init__.py,sha256=dQkY9aSbI5fZJDOSbkrbrbpHSbWbJjLEmjpkXxDMDD4,74
|
|
6
|
-
trd_utils/exchanges/blofin/blofin_client.py,sha256=SoA272BtqvG8FpxZYpwWx-flJxHEm1-qVMzklmDYZkQ,11611
|
|
7
|
-
trd_utils/exchanges/blofin/blofin_types.py,sha256=8uTJEMYL3Tq3Q_-yAZ4naGKQdQCgyjVn7bE1GRIr8kA,3845
|
|
8
|
-
trd_utils/exchanges/bx_ultra/__init__.py,sha256=8Ssy-eOemQR32Nv1-FoPHm87nRqRO4Fm2PU5GHEFKfQ,80
|
|
9
|
-
trd_utils/exchanges/bx_ultra/bx_types.py,sha256=FiJish58XpF6_vDRWxvpBVlPwXjC7uzK5K7CaA6Y8AI,25217
|
|
10
|
-
trd_utils/exchanges/bx_ultra/bx_ultra_client.py,sha256=4Zsybe7lzB7qSvOByp8KdoVvFcOAnFuStJJQuGWT6po,21164
|
|
11
|
-
trd_utils/exchanges/bx_ultra/bx_utils.py,sha256=PwapomwDW33arVmKIDj6cL-aP0ptu4BYy_lOCqSAPOo,1392
|
|
12
|
-
trd_utils/exchanges/exchange_base.py,sha256=SQERi-z9nvDAzuM4oZ3_v-wYWiw_eZV75jmAL2E4MG4,1976
|
|
13
|
-
trd_utils/html_utils/__init__.py,sha256=1WWs8C7JszRjTkmzIRLHpxWECHur_DrulTPGIeX88oM,426
|
|
14
|
-
trd_utils/html_utils/html_formats.py,sha256=unKsvOiiDmYTTaM0DYZEUNLEUzWQKKrqASJXvY54kvU,2299
|
|
15
|
-
trd_utils/tradingview/__init__.py,sha256=H0QYb-O5qvy7qC3yswtlcSWLmeBnaS6oJ3JtjvmaV_Y,154
|
|
16
|
-
trd_utils/tradingview/tradingview_client.py,sha256=g_eWYaCRQAL8Kvd-r6AnAdbH7Jha6C_GAyCuxh-RQUU,3917
|
|
17
|
-
trd_utils/tradingview/tradingview_types.py,sha256=z21MXPVdWHAduEl3gSeMIRhxtBN9yK-jPYHfZSMIbSA,6144
|
|
18
|
-
trd_utils/types_helper/__init__.py,sha256=VlEXDzOyn6fYH-dE86EGJ6u_el08QvdyOtJkj-0EAVA,65
|
|
19
|
-
trd_utils/types_helper/base_model.py,sha256=YFaZFVgLe49YdDVEcF1ezvGeofei0rSWPLLkc-lHT0M,10144
|
|
20
|
-
trd_utils-0.0.13.dist-info/LICENSE,sha256=J1EP2xt87RjjmsTV1jTjHDQMLIM9FjdwEftTpw8hyv4,1067
|
|
21
|
-
trd_utils-0.0.13.dist-info/METADATA,sha256=tgfIzjF3WSJ7Yd1cnL5SEjT3Lhra5xlJiLP271Cmp3g,1095
|
|
22
|
-
trd_utils-0.0.13.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
23
|
-
trd_utils-0.0.13.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|