Skip to content

Commit 6765c61

Browse files
author
Louis Tao
committed
Version 0.3.0
1 parent ef147ea commit 6765c61

File tree

7 files changed

+133
-6
lines changed

7 files changed

+133
-6
lines changed

assets/images/coverage.svg

Lines changed: 2 additions & 2 deletions
Loading

examples/basic_spot_order.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import json
2+
3+
from hyperliquid.utils import constants
4+
import example_utils
5+
6+
7+
COIN = "PURR/USDC"
8+
9+
10+
def main():
11+
address, info, exchange = example_utils.setup(base_url=constants.TESTNET_API_URL, skip_ws=True)
12+
13+
# Get the user state and print out position information
14+
spot_user_state = info.spot_user_state(address)
15+
if len(spot_user_state["balances"]) > 0:
16+
print("spot balances:")
17+
for balance in spot_user_state["balances"]:
18+
print(json.dumps(balance, indent=2))
19+
else:
20+
print("no available token balances")
21+
22+
# Place an order that should rest by setting the price very low
23+
order_result = exchange.order(COIN, True, 1200, 0.01, {"limit": {"tif": "Gtc"}})
24+
print(order_result)
25+
26+
# Query the order status by oid
27+
if order_result["status"] == "ok":
28+
status = order_result["response"]["data"]["statuses"][0]
29+
if "resting" in status:
30+
order_status = info.query_order_by_oid(address, status["resting"]["oid"])
31+
print("Order status by oid:", order_status)
32+
33+
# Cancel the order
34+
if order_result["status"] == "ok":
35+
status = order_result["response"]["data"]["statuses"][0]
36+
if "resting" in status:
37+
cancel_result = exchange.cancel(COIN, status["resting"]["oid"])
38+
print(cancel_result)
39+
40+
41+
if __name__ == "__main__":
42+
main()

examples/basic_spot_to_perp.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from hyperliquid.utils import constants
2+
import example_utils
3+
4+
5+
def main():
6+
address, info, exchange = example_utils.setup(constants.TESTNET_API_URL, skip_ws=True)
7+
8+
# Transfer 1.23 USDC from perp wallet to spot wallet
9+
transfer_result = exchange.user_spot_transfer(1.23, False)
10+
print(transfer_result)
11+
12+
# Transfer 1.23 USDC from spot wallet to perp wallet
13+
transfer_result = exchange.user_spot_transfer(1.23, True)
14+
print(transfer_result)
15+
16+
17+
if __name__ == "__main__":
18+
main()

hyperliquid/exchange.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
sign_withdraw_from_bridge_action,
2727
sign_agent,
2828
)
29-
from hyperliquid.utils.types import Any, List, Meta, Optional, Tuple, Cloid
29+
from hyperliquid.utils.types import Any, List, Meta, SpotMeta, Optional, Tuple, Cloid
3030

3131

3232
class Exchange(API):
@@ -41,6 +41,7 @@ def __init__(
4141
meta: Optional[Meta] = None,
4242
vault_address: Optional[str] = None,
4343
account_address: Optional[str] = None,
44+
spot_meta: Optional[SpotMeta] = None,
4445
):
4546
super().__init__(base_url)
4647
self.wallet = wallet
@@ -51,8 +52,18 @@ def __init__(
5152
self.meta = self.info.meta()
5253
else:
5354
self.meta = meta
55+
56+
if spot_meta is None:
57+
self.spot_meta = self.info.spot_meta()
58+
else:
59+
self.spot_meta = spot_meta
60+
5461
self.coin_to_asset = {asset_info["name"]: asset for (asset, asset_info) in enumerate(self.meta["universe"])}
5562

63+
# spot assets start at 10000
64+
for i, spot_pair in enumerate(self.spot_meta["universe"]):
65+
self.coin_to_asset[spot_pair["name"]] = i + 10000
66+
5667
def _post_action(self, action, signature, nonce):
5768
payload = {
5869
"action": action,
@@ -389,6 +400,29 @@ def create_sub_account(self, name: str) -> Any:
389400
timestamp,
390401
)
391402

403+
def user_spot_transfer(self, usdc: float, to_perp: bool) -> Any:
404+
usdc = int(round(usdc, 2) * 1e6)
405+
timestamp = get_timestamp_ms()
406+
spot_user_action = {
407+
"type": "spotUser",
408+
"classTransfer": {
409+
"usdc": usdc,
410+
"toPerp": to_perp,
411+
},
412+
}
413+
signature = sign_l1_action(
414+
self.wallet,
415+
spot_user_action,
416+
self.vault_address,
417+
timestamp,
418+
self.base_url == MAINNET_API_URL,
419+
)
420+
return self._post_action(
421+
spot_user_action,
422+
signature,
423+
timestamp,
424+
)
425+
392426
def sub_account_transfer(self, sub_account_user: str, is_deposit: bool, usd: int) -> Any:
393427
timestamp = get_timestamp_ms()
394428
sub_account_transfer_action = {

hyperliquid/info.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from hyperliquid.api import API
2-
from hyperliquid.utils.types import Any, Callable, Meta, Optional, Subscription, cast, Cloid
2+
from hyperliquid.utils.types import Any, Callable, Meta, SpotMeta, Optional, Subscription, cast, Cloid
33
from hyperliquid.websocket_manager import WebsocketManager
44

55

@@ -54,6 +54,9 @@ def user_state(self, address: str) -> Any:
5454
"""
5555
return self.post("/info", {"type": "clearinghouseState", "user": address})
5656

57+
def spot_user_state(self, address: str) -> Any:
58+
return self.post("/info", {"type": "spotClearinghouseState", "user": address})
59+
5760
def open_orders(self, address: str) -> Any:
5861
"""Retrieve a user's open orders.
5962
@@ -152,7 +155,7 @@ def user_fills(self, address: str) -> Any:
152155
return self.post("/info", {"type": "userFills", "user": address})
153156

154157
def meta(self) -> Meta:
155-
"""Retrieve exchange metadata
158+
"""Retrieve exchange perp metadata
156159
157160
POST /info
158161
@@ -169,6 +172,32 @@ def meta(self) -> Meta:
169172
"""
170173
return cast(Meta, self.post("/info", {"type": "meta"}))
171174

175+
def spot_meta(self) -> SpotMeta:
176+
"""Retrieve exchange spot metadata
177+
178+
POST /info
179+
180+
Returns:
181+
{
182+
tokens: [
183+
{
184+
name: str,
185+
szDecimals: int,
186+
weiDecimals: int
187+
},
188+
...
189+
],
190+
universe: [
191+
{
192+
name: str,
193+
tokens: [int, int]
194+
},
195+
...
196+
]
197+
}
198+
"""
199+
return cast(SpotMeta, self.post("/info", {"type": "spotMeta"}))
200+
172201
def funding_history(self, coin: str, startTime: int, endTime: Optional[int] = None) -> Any:
173202
"""Retrieve funding history for a given coin
174203

hyperliquid/utils/types.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
Side = Union[Literal["A"], Literal["B"]]
2323
SIDES: List[Side] = ["A", "B"]
2424

25+
SpotAssetInfo = TypedDict("SpotAssetInfo", {"name": str, "tokens": List[int]})
26+
SpotTokenInfo = TypedDict("SpotTokenInfo", {"name": str, "szDecimals": int, "weiDecimals": int})
27+
SpotMeta = TypedDict("SpotMeta", {"universe": List[SpotAssetInfo], "tokens": List[SpotTokenInfo]})
28+
2529
AllMidsSubscription = TypedDict("AllMidsSubscription", {"type": Literal["allMids"]})
2630
L2BookSubscription = TypedDict("L2BookSubscription", {"type": Literal["l2Book"], "coin": str})
2731
TradesSubscription = TypedDict("TradesSubscription", {"type": Literal["trades"], "coin": str})

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api"
55

66
[tool.poetry]
77
name = "hyperliquid-python-sdk"
8-
version = "0.2.3"
8+
version = "0.3.0"
99
description = "SDK for Hyperliquid API trading with Python."
1010
readme = "README.md"
1111
authors = ["Hyperliquid <[email protected]>"]

0 commit comments

Comments
 (0)