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.

@@ -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
- 'Accept-Encoding': 'gzip, deflate',
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,3 @@
1
+ # Hyperdash
2
+
3
+ NOTE: [Hyperdash](https://hyperdash.info) is not an exchange, but a tracker for HyperLiquid.
@@ -0,0 +1,7 @@
1
+
2
+ from .hyperliquid_client import HyperLiquidClient
3
+
4
+
5
+ __all__ = [
6
+ HyperLiquidClient,
7
+ ]
@@ -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)):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: trd_utils
3
- Version: 0.0.13
3
+ Version: 0.0.15
4
4
  Summary: Common Basic Utils for Python3. By ALiwoto.
5
5
  Keywords: utils,trd_utils,basic-utils,common-utils
6
6
  Author: ALiwoto
@@ -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,,