trd-utils 0.0.14__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.

@@ -0,0 +1,138 @@
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
+ def read_from_session_file(self, file_path: str) -> None:
106
+ """
107
+ Reads from session file; if it doesn't exist, creates it.
108
+ """
109
+ # check if path exists
110
+ target_path = Path(file_path)
111
+ if not target_path.exists():
112
+ return self._save_session_file(file_path=file_path)
113
+
114
+ aes = AESCipher(key=f"bf_{self.account_name}_bf", fav_letter=self._fav_letter)
115
+ content = aes.decrypt(target_path.read_text()).decode("utf-8")
116
+ json_data: dict = json.loads(content)
117
+
118
+ self.authorization_token = json_data.get(
119
+ "authorization_token",
120
+ self.authorization_token,
121
+ )
122
+ self.user_agent = json_data.get("user_agent", self.user_agent)
123
+
124
+ def _save_session_file(self, file_path: str) -> None:
125
+ """
126
+ Saves current information to the session file.
127
+ """
128
+
129
+ json_data = {
130
+ "authorization_token": self.authorization_token,
131
+ "user_agent": self.user_agent,
132
+ }
133
+ aes = AESCipher(key=f"bf_{self.account_name}_bf", fav_letter=self._fav_letter)
134
+ target_path = Path(file_path)
135
+ target_path.write_text(aes.encrypt(json.dumps(json_data)))
136
+
137
+ # endregion
138
+ ###########################################################
@@ -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
+ ###########################################################
@@ -2,5 +2,5 @@ from .base_model import BaseModel
2
2
 
3
3
 
4
4
  __all__ = [
5
- BaseModel,
5
+ "BaseModel",
6
6
  ]
@@ -138,7 +138,7 @@ def generic_obj_to_value(
138
138
  return expected_type(**value)
139
139
 
140
140
  if not expected_type_args:
141
- if isinstance(value, expected_type):
141
+ if value is None or isinstance(value, expected_type):
142
142
  return value
143
143
  return expected_type(value)
144
144
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: trd_utils
3
- Version: 0.0.14
3
+ Version: 0.0.16
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,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,23 +0,0 @@
1
- trd_utils/__init__.py,sha256=Q91tBCajUFOuF7TB9Y-VDxL4Kg7YxglI35Ypj_Aww5A,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=muHLa9lEjIOIDLQCb6xdyxSoQiIMSHErVe0NCoAS-RI,31017
10
- trd_utils/exchanges/bx_ultra/bx_ultra_client.py,sha256=UtWbS__FYyYbYgGvxIrpIkuXLaD4nppP_AyEZWOe5ec,22591
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=uy3s_AzMBxCVQEIMQYZZw9a5go327oOEGIl1iIZP-gs,10600
20
- trd_utils-0.0.14.dist-info/LICENSE,sha256=J1EP2xt87RjjmsTV1jTjHDQMLIM9FjdwEftTpw8hyv4,1067
21
- trd_utils-0.0.14.dist-info/METADATA,sha256=h6G9UoCSP4YXQfiguu3LwASCqAPx69JOJMncTkYxG44,1095
22
- trd_utils-0.0.14.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
23
- trd_utils-0.0.14.dist-info/RECORD,,