hyperliquid-python-sdk-async 0.24.6__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.
- hyperliquid/__init__.py +0 -0
- hyperliquid/api.py +69 -0
- hyperliquid/exchange.py +888 -0
- hyperliquid/info.py +288 -0
- hyperliquid/utils/__init__.py +0 -0
- hyperliquid/utils/constants.py +3 -0
- hyperliquid/utils/error.py +17 -0
- hyperliquid/utils/signing.py +527 -0
- hyperliquid/utils/types.py +220 -0
- hyperliquid/websocket_manager.py +197 -0
- hyperliquid_python_sdk_async-0.24.6.dist-info/LICENSE.md +21 -0
- hyperliquid_python_sdk_async-0.24.6.dist-info/METADATA +162 -0
- hyperliquid_python_sdk_async-0.24.6.dist-info/RECORD +15 -0
- hyperliquid_python_sdk_async-0.24.6.dist-info/WHEEL +4 -0
- hyperliquid_python_sdk_async-0.24.6.dist-info/entry_points.txt +3 -0
hyperliquid/info.py
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
from hyperliquid.api import API
|
|
4
|
+
from hyperliquid.utils.types import (
|
|
5
|
+
Any,
|
|
6
|
+
Callable,
|
|
7
|
+
Cloid,
|
|
8
|
+
List,
|
|
9
|
+
Meta,
|
|
10
|
+
Optional,
|
|
11
|
+
SpotMeta,
|
|
12
|
+
SpotMetaAndAssetCtxs,
|
|
13
|
+
Subscription,
|
|
14
|
+
cast,
|
|
15
|
+
)
|
|
16
|
+
from hyperliquid.websocket_manager import WebsocketManager
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Info(API):
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
base_url: Optional[str] = None,
|
|
23
|
+
skip_ws: Optional[bool] = False,
|
|
24
|
+
meta: Optional[Meta] = None,
|
|
25
|
+
spot_meta: Optional[SpotMeta] = None,
|
|
26
|
+
perp_dexs: Optional[List[str]] = None,
|
|
27
|
+
timeout: Optional[float] = None,
|
|
28
|
+
session=None,
|
|
29
|
+
):
|
|
30
|
+
super().__init__(base_url, timeout, session=session)
|
|
31
|
+
self.skip_ws = bool(skip_ws)
|
|
32
|
+
self.ws_manager: Optional[WebsocketManager] = None
|
|
33
|
+
self._provided_meta = meta
|
|
34
|
+
self._provided_spot_meta = spot_meta
|
|
35
|
+
self._requested_perp_dexs = perp_dexs
|
|
36
|
+
self._initialized = False
|
|
37
|
+
self._init_lock = asyncio.Lock()
|
|
38
|
+
|
|
39
|
+
self.coin_to_asset = {}
|
|
40
|
+
self.name_to_coin = {}
|
|
41
|
+
self.asset_to_sz_decimals = {}
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
async def create(cls, *args, **kwargs) -> "Info":
|
|
45
|
+
client = cls(*args, **kwargs)
|
|
46
|
+
await client.initialize()
|
|
47
|
+
return client
|
|
48
|
+
|
|
49
|
+
async def initialize(self) -> "Info":
|
|
50
|
+
if self._initialized:
|
|
51
|
+
return self
|
|
52
|
+
|
|
53
|
+
async with self._init_lock:
|
|
54
|
+
if self._initialized:
|
|
55
|
+
return self
|
|
56
|
+
|
|
57
|
+
if not self.skip_ws:
|
|
58
|
+
self.ws_manager = WebsocketManager(self.base_url)
|
|
59
|
+
await self.ws_manager.start()
|
|
60
|
+
|
|
61
|
+
spot_meta = self._provided_spot_meta
|
|
62
|
+
if spot_meta is None:
|
|
63
|
+
spot_meta = cast(SpotMeta, await self.post("/info", {"type": "spotMeta"}))
|
|
64
|
+
|
|
65
|
+
self.coin_to_asset = {}
|
|
66
|
+
self.name_to_coin = {}
|
|
67
|
+
self.asset_to_sz_decimals = {}
|
|
68
|
+
|
|
69
|
+
token_by_index = {token["index"]: token for token in spot_meta["tokens"]}
|
|
70
|
+
|
|
71
|
+
for spot_info in spot_meta["universe"]:
|
|
72
|
+
asset = spot_info["index"] + 10000
|
|
73
|
+
self.coin_to_asset[spot_info["name"]] = asset
|
|
74
|
+
self.name_to_coin[spot_info["name"]] = spot_info["name"]
|
|
75
|
+
base, quote = spot_info["tokens"]
|
|
76
|
+
base_info = token_by_index[base]
|
|
77
|
+
quote_info = token_by_index[quote]
|
|
78
|
+
self.asset_to_sz_decimals[asset] = base_info["szDecimals"]
|
|
79
|
+
name = f'{base_info["name"]}/{quote_info["name"]}'
|
|
80
|
+
if name not in self.name_to_coin:
|
|
81
|
+
self.name_to_coin[name] = spot_info["name"]
|
|
82
|
+
|
|
83
|
+
perp_dex_to_offset = {"": 0}
|
|
84
|
+
perp_dexs = self._requested_perp_dexs
|
|
85
|
+
if perp_dexs is None:
|
|
86
|
+
perp_dexs = [""]
|
|
87
|
+
else:
|
|
88
|
+
perp_dex_entries = await self.perp_dexs()
|
|
89
|
+
for i, perp_dex in enumerate(perp_dex_entries[1:]):
|
|
90
|
+
perp_dex_to_offset[perp_dex["name"]] = 110000 + i * 10000
|
|
91
|
+
|
|
92
|
+
for perp_dex in perp_dexs:
|
|
93
|
+
offset = perp_dex_to_offset[perp_dex]
|
|
94
|
+
if perp_dex == "" and self._provided_meta is not None:
|
|
95
|
+
self.set_perp_meta(self._provided_meta, 0)
|
|
96
|
+
else:
|
|
97
|
+
fresh_meta = await self.meta(dex=perp_dex)
|
|
98
|
+
self.set_perp_meta(fresh_meta, offset)
|
|
99
|
+
|
|
100
|
+
self._initialized = True
|
|
101
|
+
|
|
102
|
+
return self
|
|
103
|
+
|
|
104
|
+
async def _ensure_initialized(self) -> None:
|
|
105
|
+
if not self._initialized:
|
|
106
|
+
await self.initialize()
|
|
107
|
+
|
|
108
|
+
async def aclose(self) -> None:
|
|
109
|
+
if self.ws_manager is not None:
|
|
110
|
+
await self.ws_manager.stop()
|
|
111
|
+
self.ws_manager = None
|
|
112
|
+
await super().aclose()
|
|
113
|
+
|
|
114
|
+
def set_perp_meta(self, meta: Meta, offset: int) -> Any:
|
|
115
|
+
for asset, asset_info in enumerate(meta["universe"]):
|
|
116
|
+
asset += offset
|
|
117
|
+
self.coin_to_asset[asset_info["name"]] = asset
|
|
118
|
+
self.name_to_coin[asset_info["name"]] = asset_info["name"]
|
|
119
|
+
self.asset_to_sz_decimals[asset] = asset_info["szDecimals"]
|
|
120
|
+
|
|
121
|
+
async def disconnect_websocket(self) -> None:
|
|
122
|
+
if self.ws_manager is None:
|
|
123
|
+
raise RuntimeError("Cannot call disconnect_websocket since skip_ws was used")
|
|
124
|
+
await self.ws_manager.stop()
|
|
125
|
+
self.ws_manager = None
|
|
126
|
+
|
|
127
|
+
async def user_state(self, address: str, dex: str = "") -> Any:
|
|
128
|
+
return await self.post("/info", {"type": "clearinghouseState", "user": address, "dex": dex})
|
|
129
|
+
|
|
130
|
+
async def spot_user_state(self, address: str) -> Any:
|
|
131
|
+
return await self.post("/info", {"type": "spotClearinghouseState", "user": address})
|
|
132
|
+
|
|
133
|
+
async def open_orders(self, address: str, dex: str = "") -> Any:
|
|
134
|
+
return await self.post("/info", {"type": "openOrders", "user": address, "dex": dex})
|
|
135
|
+
|
|
136
|
+
async def frontend_open_orders(self, address: str, dex: str = "") -> Any:
|
|
137
|
+
return await self.post("/info", {"type": "frontendOpenOrders", "user": address, "dex": dex})
|
|
138
|
+
|
|
139
|
+
async def all_mids(self, dex: str = "") -> Any:
|
|
140
|
+
return await self.post("/info", {"type": "allMids", "dex": dex})
|
|
141
|
+
|
|
142
|
+
async def user_fills(self, address: str) -> Any:
|
|
143
|
+
return await self.post("/info", {"type": "userFills", "user": address})
|
|
144
|
+
|
|
145
|
+
async def user_fills_by_time(
|
|
146
|
+
self, address: str, start_time: int, end_time: Optional[int] = None, aggregate_by_time: Optional[bool] = False
|
|
147
|
+
) -> Any:
|
|
148
|
+
return await self.post(
|
|
149
|
+
"/info",
|
|
150
|
+
{
|
|
151
|
+
"type": "userFillsByTime",
|
|
152
|
+
"user": address,
|
|
153
|
+
"startTime": start_time,
|
|
154
|
+
"endTime": end_time,
|
|
155
|
+
"aggregateByTime": aggregate_by_time,
|
|
156
|
+
},
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
async def meta(self, dex: str = "") -> Meta:
|
|
160
|
+
return cast(Meta, await self.post("/info", {"type": "meta", "dex": dex}))
|
|
161
|
+
|
|
162
|
+
async def meta_and_asset_ctxs(self) -> Any:
|
|
163
|
+
return await self.post("/info", {"type": "metaAndAssetCtxs"})
|
|
164
|
+
|
|
165
|
+
async def perp_dexs(self) -> Any:
|
|
166
|
+
return await self.post("/info", {"type": "perpDexs"})
|
|
167
|
+
|
|
168
|
+
async def spot_meta(self) -> SpotMeta:
|
|
169
|
+
return cast(SpotMeta, await self.post("/info", {"type": "spotMeta"}))
|
|
170
|
+
|
|
171
|
+
async def spot_meta_and_asset_ctxs(self) -> SpotMetaAndAssetCtxs:
|
|
172
|
+
return cast(SpotMetaAndAssetCtxs, await self.post("/info", {"type": "spotMetaAndAssetCtxs"}))
|
|
173
|
+
|
|
174
|
+
async def funding_history(self, name: str, startTime: int, endTime: Optional[int] = None) -> Any:
|
|
175
|
+
await self._ensure_initialized()
|
|
176
|
+
coin = self.name_to_coin[name]
|
|
177
|
+
if endTime is not None:
|
|
178
|
+
return await self.post(
|
|
179
|
+
"/info", {"type": "fundingHistory", "coin": coin, "startTime": startTime, "endTime": endTime}
|
|
180
|
+
)
|
|
181
|
+
return await self.post("/info", {"type": "fundingHistory", "coin": coin, "startTime": startTime})
|
|
182
|
+
|
|
183
|
+
async def user_funding_history(self, user: str, startTime: int, endTime: Optional[int] = None) -> Any:
|
|
184
|
+
if endTime is not None:
|
|
185
|
+
return await self.post(
|
|
186
|
+
"/info", {"type": "userFunding", "user": user, "startTime": startTime, "endTime": endTime}
|
|
187
|
+
)
|
|
188
|
+
return await self.post("/info", {"type": "userFunding", "user": user, "startTime": startTime})
|
|
189
|
+
|
|
190
|
+
async def l2_snapshot(self, name: str) -> Any:
|
|
191
|
+
await self._ensure_initialized()
|
|
192
|
+
return await self.post("/info", {"type": "l2Book", "coin": self.name_to_coin[name]})
|
|
193
|
+
|
|
194
|
+
async def candles_snapshot(self, name: str, interval: str, startTime: int, endTime: int) -> Any:
|
|
195
|
+
await self._ensure_initialized()
|
|
196
|
+
req = {"coin": self.name_to_coin[name], "interval": interval, "startTime": startTime, "endTime": endTime}
|
|
197
|
+
return await self.post("/info", {"type": "candleSnapshot", "req": req})
|
|
198
|
+
|
|
199
|
+
async def user_fees(self, address: str) -> Any:
|
|
200
|
+
return await self.post("/info", {"type": "userFees", "user": address})
|
|
201
|
+
|
|
202
|
+
async def user_staking_summary(self, address: str) -> Any:
|
|
203
|
+
return await self.post("/info", {"type": "delegatorSummary", "user": address})
|
|
204
|
+
|
|
205
|
+
async def user_staking_delegations(self, address: str) -> Any:
|
|
206
|
+
return await self.post("/info", {"type": "delegations", "user": address})
|
|
207
|
+
|
|
208
|
+
async def user_staking_rewards(self, address: str) -> Any:
|
|
209
|
+
return await self.post("/info", {"type": "delegatorRewards", "user": address})
|
|
210
|
+
|
|
211
|
+
async def delegator_history(self, user: str) -> Any:
|
|
212
|
+
return await self.post("/info", {"type": "delegatorHistory", "user": user})
|
|
213
|
+
|
|
214
|
+
async def query_order_by_oid(self, user: str, oid: int) -> Any:
|
|
215
|
+
return await self.post("/info", {"type": "orderStatus", "user": user, "oid": oid})
|
|
216
|
+
|
|
217
|
+
async def query_order_by_cloid(self, user: str, cloid: Cloid) -> Any:
|
|
218
|
+
return await self.post("/info", {"type": "orderStatus", "user": user, "oid": cloid.to_raw()})
|
|
219
|
+
|
|
220
|
+
async def query_referral_state(self, user: str) -> Any:
|
|
221
|
+
return await self.post("/info", {"type": "referral", "user": user})
|
|
222
|
+
|
|
223
|
+
async def query_sub_accounts(self, user: str) -> Any:
|
|
224
|
+
return await self.post("/info", {"type": "subAccounts", "user": user})
|
|
225
|
+
|
|
226
|
+
async def query_user_to_multi_sig_signers(self, multi_sig_user: str) -> Any:
|
|
227
|
+
return await self.post("/info", {"type": "userToMultiSigSigners", "user": multi_sig_user})
|
|
228
|
+
|
|
229
|
+
async def query_perp_deploy_auction_status(self) -> Any:
|
|
230
|
+
return await self.post("/info", {"type": "perpDeployAuctionStatus"})
|
|
231
|
+
|
|
232
|
+
async def query_user_dex_abstraction_state(self, user: str) -> Any:
|
|
233
|
+
return await self.post("/info", {"type": "userDexAbstraction", "user": user})
|
|
234
|
+
|
|
235
|
+
async def query_user_abstraction_state(self, user: str) -> Any:
|
|
236
|
+
return await self.post("/info", {"type": "userAbstraction", "user": user})
|
|
237
|
+
|
|
238
|
+
async def historical_orders(self, user: str) -> Any:
|
|
239
|
+
return await self.post("/info", {"type": "historicalOrders", "user": user})
|
|
240
|
+
|
|
241
|
+
async def user_non_funding_ledger_updates(self, user: str, startTime: int, endTime: Optional[int] = None) -> Any:
|
|
242
|
+
return await self.post(
|
|
243
|
+
"/info",
|
|
244
|
+
{"type": "userNonFundingLedgerUpdates", "user": user, "startTime": startTime, "endTime": endTime},
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
async def portfolio(self, user: str) -> Any:
|
|
248
|
+
return await self.post("/info", {"type": "portfolio", "user": user})
|
|
249
|
+
|
|
250
|
+
async def user_twap_slice_fills(self, user: str) -> Any:
|
|
251
|
+
return await self.post("/info", {"type": "userTwapSliceFills", "user": user})
|
|
252
|
+
|
|
253
|
+
async def user_vault_equities(self, user: str) -> Any:
|
|
254
|
+
return await self.post("/info", {"type": "userVaultEquities", "user": user})
|
|
255
|
+
|
|
256
|
+
async def user_role(self, user: str) -> Any:
|
|
257
|
+
return await self.post("/info", {"type": "userRole", "user": user})
|
|
258
|
+
|
|
259
|
+
async def user_rate_limit(self, user: str) -> Any:
|
|
260
|
+
return await self.post("/info", {"type": "userRateLimit", "user": user})
|
|
261
|
+
|
|
262
|
+
async def query_spot_deploy_auction_status(self, user: str) -> Any:
|
|
263
|
+
return await self.post("/info", {"type": "spotDeployState", "user": user})
|
|
264
|
+
|
|
265
|
+
async def extra_agents(self, user: str) -> Any:
|
|
266
|
+
return await self.post("/info", {"type": "extraAgents", "user": user})
|
|
267
|
+
|
|
268
|
+
def _remap_coin_subscription(self, subscription: Subscription) -> None:
|
|
269
|
+
if subscription["type"] in {"l2Book", "trades", "candle", "bbo", "activeAssetCtx"}:
|
|
270
|
+
subscription["coin"] = self.name_to_coin[subscription["coin"]]
|
|
271
|
+
|
|
272
|
+
async def subscribe(self, subscription: Subscription, callback: Callable[[Any], None]) -> int:
|
|
273
|
+
await self._ensure_initialized()
|
|
274
|
+
self._remap_coin_subscription(subscription)
|
|
275
|
+
if self.ws_manager is None:
|
|
276
|
+
raise RuntimeError("Cannot call subscribe since skip_ws was used")
|
|
277
|
+
return await self.ws_manager.subscribe(subscription, callback)
|
|
278
|
+
|
|
279
|
+
async def unsubscribe(self, subscription: Subscription, subscription_id: int) -> bool:
|
|
280
|
+
await self._ensure_initialized()
|
|
281
|
+
self._remap_coin_subscription(subscription)
|
|
282
|
+
if self.ws_manager is None:
|
|
283
|
+
raise RuntimeError("Cannot call unsubscribe since skip_ws was used")
|
|
284
|
+
return await self.ws_manager.unsubscribe(subscription, subscription_id)
|
|
285
|
+
|
|
286
|
+
async def name_to_asset(self, name: str) -> int:
|
|
287
|
+
await self._ensure_initialized()
|
|
288
|
+
return self.coin_to_asset[self.name_to_coin[name]]
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class Error(Exception):
|
|
2
|
+
pass
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ClientError(Error):
|
|
6
|
+
def __init__(self, status_code, error_code, error_message, header, error_data=None):
|
|
7
|
+
self.status_code = status_code
|
|
8
|
+
self.error_code = error_code
|
|
9
|
+
self.error_message = error_message
|
|
10
|
+
self.header = header
|
|
11
|
+
self.error_data = error_data
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ServerError(Error):
|
|
15
|
+
def __init__(self, status_code, message):
|
|
16
|
+
self.status_code = status_code
|
|
17
|
+
self.message = message
|