From 1a3448925c3283eda3608072e1b649104b57bba9 Mon Sep 17 00:00:00 2001 From: Andrei Nasui Date: Sun, 29 Jun 2025 16:04:33 +0300 Subject: [PATCH 1/4] Added activeAssetData websocket subscription --- examples/basic_ws.py | 1 + hyperliquid/utils/types.py | 24 ++++++++++++++++++++++++ hyperliquid/websocket_manager.py | 4 ++++ 3 files changed, 29 insertions(+) diff --git a/examples/basic_ws.py b/examples/basic_ws.py index e01eea57..549a9bb8 100644 --- a/examples/basic_ws.py +++ b/examples/basic_ws.py @@ -20,6 +20,7 @@ def main(): info.subscribe({"type": "bbo", "coin": "ETH"}, print) info.subscribe({"type": "activeAssetCtx", "coin": "BTC"}, print) # Perp info.subscribe({"type": "activeAssetCtx", "coin": "@1"}, print) # Spot + info.subscribe({"type": "activeAssetData", "user": address, "coin": "BTC"}, print) # Perp if __name__ == "__main__": diff --git a/hyperliquid/utils/types.py b/hyperliquid/utils/types.py index 00c58c7a..f7a971f8 100644 --- a/hyperliquid/utils/types.py +++ b/hyperliquid/utils/types.py @@ -50,6 +50,9 @@ ) WebData2Subscription = TypedDict("WebData2Subscription", {"type": Literal["webData2"], "user": str}) ActiveAssetCtxSubscription = TypedDict("ActiveAssetCtxSubscription", {"type": Literal["activeAssetCtx"], "coin": str}) +ActiveAssetDataSubscription = TypedDict( + "ActiveAssetDataSubscription", {"type": Literal["activeAssetData"], "user": str, "coin": str} +) # If adding new subscription types that contain coin's don't forget to handle automatically rewrite name to coin in info.subscribe Subscription = Union[ AllMidsSubscription, @@ -64,6 +67,7 @@ UserNonFundingLedgerUpdatesSubscription, WebData2Subscription, ActiveAssetCtxSubscription, + ActiveAssetDataSubscription, ] AllMidsData = TypedDict("AllMidsData", {"mids": Dict[str, str]}) @@ -75,6 +79,14 @@ BboMsg = TypedDict("BboMsg", {"channel": Literal["bbo"], "data": BboData}) PongMsg = TypedDict("PongMsg", {"channel": Literal["pong"]}) Trade = TypedDict("Trade", {"coin": str, "side": Side, "px": str, "sz": int, "hash": str, "time": int}) +Leverage = TypedDict( + "Leverage", + { + "type": Union[Literal["cross"], Literal["isolated"]], + "value": int, + "rawUsd": Optional[str], + }, +) TradesMsg = TypedDict("TradesMsg", {"channel": Literal["trades"], "data": List[Trade]}) PerpAssetCtx = TypedDict( "PerpAssetCtx", @@ -97,6 +109,18 @@ ActiveSpotAssetCtxMsg = TypedDict( "ActiveSpotAssetCtxMsg", {"channel": Literal["activeSpotAssetCtx"], "data": ActiveSpotAssetCtx} ) +ActiveAssetData = TypedDict( + "ActiveAssetData", + { + "user": str, + "coin": str, + "leverage": Leverage, + "maxTradeSzs": List[str], + "availableToTrade": List[str], + "markPx": str, + }, +) +ActiveAssetDataMsg = TypedDict("ActiveAssetDataMsg", {"channel": Literal["activeAssetData"], "data": ActiveAssetData}) Fill = TypedDict( "Fill", { diff --git a/hyperliquid/websocket_manager.py b/hyperliquid/websocket_manager.py index 7875dac5..a0b169f7 100644 --- a/hyperliquid/websocket_manager.py +++ b/hyperliquid/websocket_manager.py @@ -35,6 +35,8 @@ def subscription_to_identifier(subscription: Subscription) -> str: return f'bbo:{subscription["coin"].lower()}' elif subscription["type"] == "activeAssetCtx": return f'activeAssetCtx:{subscription["coin"].lower()}' + elif subscription["type"] == "activeAssetData": + return f'activeAssetData:{subscription["coin"].lower()}' def ws_msg_to_identifier(ws_msg: WsMsg) -> Optional[str]: @@ -68,6 +70,8 @@ def ws_msg_to_identifier(ws_msg: WsMsg) -> Optional[str]: return f'bbo:{ws_msg["data"]["coin"].lower()}' elif ws_msg["channel"] == "activeAssetCtx" or ws_msg["channel"] == "activeSpotAssetCtx": return f'activeAssetCtx:{ws_msg["data"]["coin"].lower()}' + elif ws_msg["channel"] == "activeAssetData": + return f'activeAssetData:{ws_msg["data"]["coin"].lower()}' class WebsocketManager(threading.Thread): From 685ed3250276d2c8a751f2db20892cde35296781 Mon Sep 17 00:00:00 2001 From: Andrei Nasui Date: Mon, 30 Jun 2025 23:26:28 +0300 Subject: [PATCH 2/4] updated based on review comments --- examples/basic_ws.py | 2 +- hyperliquid/utils/types.py | 7 ++++--- hyperliquid/websocket_manager.py | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/basic_ws.py b/examples/basic_ws.py index 549a9bb8..0557fa70 100644 --- a/examples/basic_ws.py +++ b/examples/basic_ws.py @@ -20,7 +20,7 @@ def main(): info.subscribe({"type": "bbo", "coin": "ETH"}, print) info.subscribe({"type": "activeAssetCtx", "coin": "BTC"}, print) # Perp info.subscribe({"type": "activeAssetCtx", "coin": "@1"}, print) # Spot - info.subscribe({"type": "activeAssetData", "user": address, "coin": "BTC"}, print) # Perp + info.subscribe({"type": "activeAssetData", "user": address, "coin": "BTC"}, print) # Perp only if __name__ == "__main__": diff --git a/hyperliquid/utils/types.py b/hyperliquid/utils/types.py index f7a971f8..6cde0fca 100644 --- a/hyperliquid/utils/types.py +++ b/hyperliquid/utils/types.py @@ -84,7 +84,7 @@ { "type": Union[Literal["cross"], Literal["isolated"]], "value": int, - "rawUsd": Optional[str], + "rawUsd": NotRequired[str], }, ) TradesMsg = TypedDict("TradesMsg", {"channel": Literal["trades"], "data": List[Trade]}) @@ -115,8 +115,8 @@ "user": str, "coin": str, "leverage": Leverage, - "maxTradeSzs": List[str], - "availableToTrade": List[str], + "maxTradeSzs": Tuple[str, str], + "availableToTrade": Tuple[str, str], "markPx": str, }, ) @@ -170,6 +170,7 @@ OtherWsMsg, ActiveAssetCtxMsg, ActiveSpotAssetCtxMsg, + ActiveAssetDataMsg, ] # b is the public address of the builder, f is the amount of the fee in tenths of basis points. e.g. 10 means 1 basis point diff --git a/hyperliquid/websocket_manager.py b/hyperliquid/websocket_manager.py index a0b169f7..19eac482 100644 --- a/hyperliquid/websocket_manager.py +++ b/hyperliquid/websocket_manager.py @@ -36,7 +36,7 @@ def subscription_to_identifier(subscription: Subscription) -> str: elif subscription["type"] == "activeAssetCtx": return f'activeAssetCtx:{subscription["coin"].lower()}' elif subscription["type"] == "activeAssetData": - return f'activeAssetData:{subscription["coin"].lower()}' + return f'activeAssetData:{subscription["coin"].lower()},{subscription["user"]}' def ws_msg_to_identifier(ws_msg: WsMsg) -> Optional[str]: @@ -71,7 +71,7 @@ def ws_msg_to_identifier(ws_msg: WsMsg) -> Optional[str]: elif ws_msg["channel"] == "activeAssetCtx" or ws_msg["channel"] == "activeSpotAssetCtx": return f'activeAssetCtx:{ws_msg["data"]["coin"].lower()}' elif ws_msg["channel"] == "activeAssetData": - return f'activeAssetData:{ws_msg["data"]["coin"].lower()}' + return f'activeAssetData:{ws_msg["data"]["coin"].lower()},{ws_msg["data"]["user"]}' class WebsocketManager(threading.Thread): From 40d25988687331243546bfa52ee9cad32f2d5f8e Mon Sep 17 00:00:00 2001 From: Andrei Nasui Date: Tue, 1 Jul 2025 11:43:33 +0300 Subject: [PATCH 3/4] split leverage in two different leverage types --- hyperliquid/utils/types.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/hyperliquid/utils/types.py b/hyperliquid/utils/types.py index 6cde0fca..051bcfa6 100644 --- a/hyperliquid/utils/types.py +++ b/hyperliquid/utils/types.py @@ -79,14 +79,22 @@ BboMsg = TypedDict("BboMsg", {"channel": Literal["bbo"], "data": BboData}) PongMsg = TypedDict("PongMsg", {"channel": Literal["pong"]}) Trade = TypedDict("Trade", {"coin": str, "side": Side, "px": str, "sz": int, "hash": str, "time": int}) -Leverage = TypedDict( - "Leverage", +CrossLeverage = TypedDict( + "CrossLeverage", { - "type": Union[Literal["cross"], Literal["isolated"]], + "type": Literal["cross"], "value": int, - "rawUsd": NotRequired[str], }, ) +IsolatedLeverage = TypedDict( + "IsolatedLeverage", + { + "type": Literal["isolated"], + "value": int, + "rawUsd": str, + }, +) +Leverage = Union[CrossLeverage, IsolatedLeverage] TradesMsg = TypedDict("TradesMsg", {"channel": Literal["trades"], "data": List[Trade]}) PerpAssetCtx = TypedDict( "PerpAssetCtx", From 7a5c911cc4be3c401ddca0ba79b53eac99a81ee3 Mon Sep 17 00:00:00 2001 From: Andrei Nasui Date: Wed, 2 Jul 2025 00:12:59 +0300 Subject: [PATCH 4/4] call lower on user address --- hyperliquid/websocket_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hyperliquid/websocket_manager.py b/hyperliquid/websocket_manager.py index 19eac482..4c73a688 100644 --- a/hyperliquid/websocket_manager.py +++ b/hyperliquid/websocket_manager.py @@ -36,7 +36,7 @@ def subscription_to_identifier(subscription: Subscription) -> str: elif subscription["type"] == "activeAssetCtx": return f'activeAssetCtx:{subscription["coin"].lower()}' elif subscription["type"] == "activeAssetData": - return f'activeAssetData:{subscription["coin"].lower()},{subscription["user"]}' + return f'activeAssetData:{subscription["coin"].lower()},{subscription["user"].lower()}' def ws_msg_to_identifier(ws_msg: WsMsg) -> Optional[str]: @@ -71,7 +71,7 @@ def ws_msg_to_identifier(ws_msg: WsMsg) -> Optional[str]: elif ws_msg["channel"] == "activeAssetCtx" or ws_msg["channel"] == "activeSpotAssetCtx": return f'activeAssetCtx:{ws_msg["data"]["coin"].lower()}' elif ws_msg["channel"] == "activeAssetData": - return f'activeAssetData:{ws_msg["data"]["coin"].lower()},{ws_msg["data"]["user"]}' + return f'activeAssetData:{ws_msg["data"]["coin"].lower()},{ws_msg["data"]["user"].lower()}' class WebsocketManager(threading.Thread):