| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782 |
- from hyperliquid.api import API
- from hyperliquid.utils.types import (
- Any,
- Callable,
- Cloid,
- List,
- Meta,
- Optional,
- SpotMeta,
- SpotMetaAndAssetCtxs,
- Subscription,
- cast,
- )
- from hyperliquid.websocket_manager import WebsocketManager
- class Info(API):
- def __init__(
- self,
- base_url: Optional[str] = None,
- skip_ws: Optional[bool] = False,
- meta: Optional[Meta] = None,
- spot_meta: Optional[SpotMeta] = None,
- # Note that when perp_dexs is None, then "" is used as the perp dex. "" represents
- # the original dex.
- perp_dexs: Optional[List[str]] = None,
- timeout: Optional[float] = None,
- ): # pylint: disable=too-many-locals
- super().__init__(base_url, timeout)
- self.ws_manager: Optional[WebsocketManager] = None
- if not skip_ws:
- self.ws_manager = WebsocketManager(self.base_url)
- self.ws_manager.start()
- if spot_meta is None:
- spot_meta = self.spot_meta()
- self.coin_to_asset = {}
- self.name_to_coin = {}
- self.asset_to_sz_decimals = {}
- # spot assets start at 10000
- for spot_info in spot_meta["universe"]:
- asset = spot_info["index"] + 10000
- self.coin_to_asset[spot_info["name"]] = asset
- self.name_to_coin[spot_info["name"]] = spot_info["name"]
- base, quote = spot_info["tokens"]
- base_info = spot_meta["tokens"][base]
- quote_info = spot_meta["tokens"][quote]
- self.asset_to_sz_decimals[asset] = base_info["szDecimals"]
- name = f'{base_info["name"]}/{quote_info["name"]}'
- if name not in self.name_to_coin:
- self.name_to_coin[name] = spot_info["name"]
- perp_dex_to_offset = {"": 0}
- if perp_dexs is None:
- perp_dexs = [""]
- else:
- for i, perp_dex in enumerate(self.perp_dexs()[1:]):
- # builder-deployed perp dexs start at 110000
- perp_dex_to_offset[perp_dex["name"]] = 110000 + i * 10000
- for perp_dex in perp_dexs:
- offset = perp_dex_to_offset[perp_dex]
- if perp_dex == "" and meta is not None:
- self.set_perp_meta(meta, 0)
- else:
- fresh_meta = self.meta(dex=perp_dex)
- self.set_perp_meta(fresh_meta, offset)
- def set_perp_meta(self, meta: Meta, offset: int) -> Any:
- for asset, asset_info in enumerate(meta["universe"]):
- asset += offset
- self.coin_to_asset[asset_info["name"]] = asset
- self.name_to_coin[asset_info["name"]] = asset_info["name"]
- self.asset_to_sz_decimals[asset] = asset_info["szDecimals"]
- def disconnect_websocket(self):
- if self.ws_manager is None:
- raise RuntimeError("Cannot call disconnect_websocket since skip_ws was used")
- else:
- self.ws_manager.stop()
- def user_state(self, address: str, dex: str = "") -> Any:
- """Retrieve trading details about a user.
- POST /info
- Args:
- address (str): Onchain address in 42-character hexadecimal format;
- e.g. 0x0000000000000000000000000000000000000000.
- Returns:
- {
- assetPositions: [
- {
- position: {
- coin: str,
- entryPx: Optional[float string]
- leverage: {
- type: "cross" | "isolated",
- value: int,
- rawUsd: float string # only if type is "isolated"
- },
- liquidationPx: Optional[float string]
- marginUsed: float string,
- positionValue: float string,
- returnOnEquity: float string,
- szi: float string,
- unrealizedPnl: float string
- },
- type: "oneWay"
- }
- ],
- crossMarginSummary: MarginSummary,
- marginSummary: MarginSummary,
- withdrawable: float string,
- }
- where MarginSummary is {
- accountValue: float string,
- totalMarginUsed: float string,
- totalNtlPos: float string,
- totalRawUsd: float string,
- }
- """
- return self.post("/info", {"type": "clearinghouseState", "user": address, "dex": dex})
- def spot_user_state(self, address: str) -> Any:
- return self.post("/info", {"type": "spotClearinghouseState", "user": address})
- def open_orders(self, address: str, dex: str = "") -> Any:
- """Retrieve a user's open orders.
- POST /info
- Args:
- address (str): Onchain address in 42-character hexadecimal format;
- e.g. 0x0000000000000000000000000000000000000000.
- Returns: [
- {
- coin: str,
- limitPx: float string,
- oid: int,
- side: "A" | "B",
- sz: float string,
- timestamp: int
- }
- ]
- """
- return self.post("/info", {"type": "openOrders", "user": address, "dex": dex})
- def frontend_open_orders(self, address: str, dex: str = "") -> Any:
- """Retrieve a user's open orders with additional frontend info.
- POST /info
- Args:
- address (str): Onchain address in 42-character hexadecimal format;
- e.g. 0x0000000000000000000000000000000000000000.
- Returns: [
- {
- children:
- [
- dict of frontend orders
- ]
- coin: str,
- isPositionTpsl: bool,
- isTrigger: bool,
- limitPx: float string,
- oid: int,
- orderType: str,
- origSz: float string,
- reduceOnly: bool,
- side: "A" | "B",
- sz: float string,
- tif: str,
- timestamp: int,
- triggerCondition: str,
- triggerPx: float str
- }
- ]
- """
- return self.post("/info", {"type": "frontendOpenOrders", "user": address, "dex": dex})
- def all_mids(self, dex: str = "") -> Any:
- """Retrieve all mids for all actively traded coins.
- POST /info
- Returns:
- {
- ATOM: float string,
- BTC: float string,
- any other coins which are trading: float string
- }
- """
- return self.post("/info", {"type": "allMids", "dex": dex})
- def user_fills(self, address: str) -> Any:
- """Retrieve a given user's fills.
- POST /info
- Args:
- address (str): Onchain address in 42-character hexadecimal format;
- e.g. 0x0000000000000000000000000000000000000000.
- Returns:
- [
- {
- closedPnl: float string,
- coin: str,
- crossed: bool,
- dir: str,
- hash: str,
- oid: int,
- px: float string,
- side: str,
- startPosition: float string,
- sz: float string,
- time: int
- },
- ...
- ]
- """
- return self.post("/info", {"type": "userFills", "user": address})
- def user_fills_by_time(
- self, address: str, start_time: int, end_time: Optional[int] = None, aggregate_by_time: Optional[bool] = False
- ) -> Any:
- """Retrieve a given user's fills by time.
- POST /info
- Args:
- address (str): Onchain address in 42-character hexadecimal format;
- e.g. 0x0000000000000000000000000000000000000000.
- start_time (int): Unix timestamp in milliseconds
- end_time (Optional[int]): Unix timestamp in milliseconds
- aggregate_by_time (Optional[bool]): When true, partial fills are combined when a crossing order gets filled by multiple different resting orders. Resting orders filled by multiple crossing orders will not be aggregated.
- Returns:
- [
- {
- closedPnl: float string,
- coin: str,
- crossed: bool,
- dir: str,
- hash: str,
- oid: int,
- px: float string,
- side: str,
- startPosition: float string,
- sz: float string,
- time: int
- },
- ...
- ]
- """
- return self.post(
- "/info",
- {
- "type": "userFillsByTime",
- "user": address,
- "startTime": start_time,
- "endTime": end_time,
- "aggregateByTime": aggregate_by_time,
- },
- )
- def meta(self, dex: str = "") -> Meta:
- """Retrieve exchange perp metadata
- POST /info
- Returns:
- {
- universe: [
- {
- name: str,
- szDecimals: int
- },
- ...
- ]
- }
- """
- return cast(Meta, self.post("/info", {"type": "meta", "dex": dex}))
- def meta_and_asset_ctxs(self) -> Any:
- """Retrieve exchange MetaAndAssetCtxs
- POST /info
- Returns:
- [
- {
- universe: [
- {
- 'name': str,
- 'szDecimals': int
- 'maxLeverage': int,
- 'onlyIsolated': bool,
- },
- ...
- ]
- },
- [
- {
- "dayNtlVlm": float string,
- "funding": float string,
- "impactPxs": Optional([float string, float string]),
- "markPx": Optional(float string),
- "midPx": Optional(float string),
- "openInterest": float string,
- "oraclePx": float string,
- "premium": Optional(float string),
- "prevDayPx": float string
- },
- ...
- ]
- """
- return self.post("/info", {"type": "metaAndAssetCtxs"})
- def perp_dexs(self) -> Any:
- return self.post("/info", {"type": "perpDexs"})
- def spot_meta(self) -> SpotMeta:
- """Retrieve exchange spot metadata
- POST /info
- Returns:
- {
- universe: [
- {
- tokens: [int, int],
- name: str,
- index: int,
- isCanonical: bool
- },
- ...
- ],
- tokens: [
- {
- name: str,
- szDecimals: int,
- weiDecimals: int,
- index: int,
- tokenId: str,
- isCanonical: bool
- },
- ...
- ]
- }
- """
- return cast(SpotMeta, self.post("/info", {"type": "spotMeta"}))
- def spot_meta_and_asset_ctxs(self) -> SpotMetaAndAssetCtxs:
- """Retrieve exchange spot asset contexts
- POST /info
- Returns:
- [
- {
- universe: [
- {
- tokens: [int, int],
- name: str,
- index: int,
- isCanonical: bool
- },
- ...
- ],
- tokens: [
- {
- name: str,
- szDecimals: int,
- weiDecimals: int,
- index: int,
- tokenId: str,
- isCanonical: bool
- },
- ...
- ]
- },
- [
- {
- dayNtlVlm: float string,
- markPx: float string,
- midPx: Optional(float string),
- prevDayPx: float string,
- circulatingSupply: float string,
- coin: str
- }
- ...
- ]
- ]
- """
- return cast(SpotMetaAndAssetCtxs, self.post("/info", {"type": "spotMetaAndAssetCtxs"}))
- def funding_history(self, name: str, startTime: int, endTime: Optional[int] = None) -> Any:
- """Retrieve funding history for a given coin
- POST /info
- Args:
- name (str): Coin to retrieve funding history for.
- startTime (int): Unix timestamp in milliseconds.
- endTime (int): Unix timestamp in milliseconds.
- Returns:
- [
- {
- coin: str,
- fundingRate: float string,
- premium: float string,
- time: int
- },
- ...
- ]
- """
- coin = self.name_to_coin[name]
- if endTime is not None:
- return self.post(
- "/info", {"type": "fundingHistory", "coin": coin, "startTime": startTime, "endTime": endTime}
- )
- return self.post("/info", {"type": "fundingHistory", "coin": coin, "startTime": startTime})
- def user_funding_history(self, user: str, startTime: int, endTime: Optional[int] = None) -> Any:
- """Retrieve a user's funding history
- POST /info
- Args:
- user (str): Address of the user in 42-character hexadecimal format.
- startTime (int): Start time in milliseconds, inclusive.
- endTime (int, optional): End time in milliseconds, inclusive. Defaults to current time.
- Returns:
- List[Dict]: A list of funding history records, where each record contains:
- - user (str): User address.
- - type (str): Type of the record, e.g., "userFunding".
- - startTime (int): Unix timestamp of the start time in milliseconds.
- - endTime (int): Unix timestamp of the end time in milliseconds.
- """
- if endTime is not None:
- return self.post("/info", {"type": "userFunding", "user": user, "startTime": startTime, "endTime": endTime})
- return self.post("/info", {"type": "userFunding", "user": user, "startTime": startTime})
- def l2_snapshot(self, name: str) -> Any:
- """Retrieve L2 snapshot for a given coin
- POST /info
- Args:
- name (str): Coin to retrieve L2 snapshot for.
- Returns:
- {
- coin: str,
- levels: [
- [
- {
- n: int,
- px: float string,
- sz: float string
- },
- ...
- ],
- ...
- ],
- time: int
- }
- """
- return self.post("/info", {"type": "l2Book", "coin": self.name_to_coin[name]})
- def candles_snapshot(self, name: str, interval: str, startTime: int, endTime: int) -> Any:
- """Retrieve candles snapshot for a given coin
- POST /info
- Args:
- name (str): Coin to retrieve candles snapshot for.
- interval (str): Candlestick interval.
- startTime (int): Unix timestamp in milliseconds.
- endTime (int): Unix timestamp in milliseconds.
- Returns:
- [
- {
- T: int,
- c: float string,
- h: float string,
- i: str,
- l: float string,
- n: int,
- o: float string,
- s: string,
- t: int,
- v: float string
- },
- ...
- ]
- """
- req = {"coin": self.name_to_coin[name], "interval": interval, "startTime": startTime, "endTime": endTime}
- return self.post("/info", {"type": "candleSnapshot", "req": req})
- def user_fees(self, address: str) -> Any:
- """Retrieve the volume of trading activity associated with a user.
- POST /info
- Args:
- address (str): Onchain address in 42-character hexadecimal format;
- e.g. 0x0000000000000000000000000000000000000000.
- Returns:
- {
- activeReferralDiscount: float string,
- dailyUserVlm: [
- {
- date: str,
- exchange: str,
- userAdd: float string,
- userCross: float string
- },
- ],
- feeSchedule: {
- add: float string,
- cross: float string,
- referralDiscount: float string,
- tiers: {
- mm: [
- {
- add: float string,
- makerFractionCutoff: float string
- },
- ],
- vip: [
- {
- add: float string,
- cross: float string,
- ntlCutoff: float string
- },
- ]
- }
- },
- userAddRate: float string,
- userCrossRate: float string
- }
- """
- return self.post("/info", {"type": "userFees", "user": address})
- def user_staking_summary(self, address: str) -> Any:
- """Retrieve the staking summary associated with a user.
- POST /info
- Args:
- address (str): Onchain address in 42-character hexadecimal format;
- e.g. 0x0000000000000000000000000000000000000000.
- Returns:
- {
- delegated: float string,
- undelegated: float string,
- totalPendingWithdrawal: float string,
- nPendingWithdrawals: int
- }
- """
- return self.post("/info", {"type": "delegatorSummary", "user": address})
- def user_staking_delegations(self, address: str) -> Any:
- """Retrieve the user's staking delegations.
- POST /info
- Args:
- address (str): Onchain address in 42-character hexadecimal format;
- e.g. 0x0000000000000000000000000000000000000000.
- Returns:
- [
- {
- validator: string,
- amount: float string,
- lockedUntilTimestamp: int
- },
- ]
- """
- return self.post("/info", {"type": "delegations", "user": address})
- def user_staking_rewards(self, address: str) -> Any:
- """Retrieve the historic staking rewards associated with a user.
- POST /info
- Args:
- address (str): Onchain address in 42-character hexadecimal format;
- e.g. 0x0000000000000000000000000000000000000000.
- Returns:
- [
- {
- time: int,
- source: string,
- totalAmount: float string
- },
- ]
- """
- return self.post("/info", {"type": "delegatorRewards", "user": address})
- def delegator_history(self, user: str) -> Any:
- """Retrieve comprehensive staking history for a user.
- POST /info
- Args:
- user (str): Onchain address in 42-character hexadecimal format.
- Returns:
- Comprehensive staking history including delegation and undelegation
- events with timestamps, transaction hashes, and detailed delta information.
- """
- return self.post("/info", {"type": "delegatorHistory", "user": user})
- def query_order_by_oid(self, user: str, oid: int) -> Any:
- return self.post("/info", {"type": "orderStatus", "user": user, "oid": oid})
- def query_order_by_cloid(self, user: str, cloid: Cloid) -> Any:
- return self.post("/info", {"type": "orderStatus", "user": user, "oid": cloid.to_raw()})
- def query_referral_state(self, user: str) -> Any:
- return self.post("/info", {"type": "referral", "user": user})
- def query_sub_accounts(self, user: str) -> Any:
- return self.post("/info", {"type": "subAccounts", "user": user})
- def query_user_to_multi_sig_signers(self, multi_sig_user: str) -> Any:
- return self.post("/info", {"type": "userToMultiSigSigners", "user": multi_sig_user})
- def query_perp_deploy_auction_status(self) -> Any:
- return self.post("/info", {"type": "perpDeployAuctionStatus"})
- def historical_orders(self, user: str) -> Any:
- """Retrieve a user's historical orders.
- POST /info
- Args:
- user (str): Onchain address in 42-character hexadecimal format;
- e.g. 0x0000000000000000000000000000000000000000.
- Returns:
- Returns at most 2000 most recent historical orders with their current
- status and detailed order information.
- """
- return self.post("/info", {"type": "historicalOrders", "user": user})
- def user_non_funding_ledger_updates(self, user: str, startTime: int, endTime: Optional[int] = None) -> Any:
- """Retrieve non-funding ledger updates for a user.
- POST /info
- Args:
- user (str): Onchain address in 42-character hexadecimal format.
- startTime (int): Start time in milliseconds (epoch timestamp).
- endTime (Optional[int]): End time in milliseconds (epoch timestamp).
- Returns:
- Comprehensive ledger updates including deposits, withdrawals, transfers,
- liquidations, and other account activities excluding funding payments.
- """
- return self.post(
- "/info",
- {"type": "userNonFundingLedgerUpdates", "user": user, "startTime": startTime, "endTime": endTime},
- )
- def portfolio(self, user: str) -> Any:
- """Retrieve comprehensive portfolio performance data.
- POST /info
- Args:
- user (str): Onchain address in 42-character hexadecimal format.
- Returns:
- Comprehensive portfolio performance data across different time periods,
- including account value history, PnL history, and volume metrics.
- """
- return self.post("/info", {"type": "portfolio", "user": user})
- def user_twap_slice_fills(self, user: str) -> Any:
- """Retrieve a user's TWAP slice fills.
- POST /info
- Args:
- user (str): Onchain address in 42-character hexadecimal format.
- Returns:
- Returns at most 2000 most recent TWAP slice fills with detailed
- execution information.
- """
- return self.post("/info", {"type": "userTwapSliceFills", "user": user})
- def user_vault_equities(self, user: str) -> Any:
- """Retrieve user's equity positions across all vaults.
- POST /info
- Args:
- user (str): Onchain address in 42-character hexadecimal format.
- Returns:
- Detailed information about user's equity positions across all vaults
- including current values, profit/loss metrics, and withdrawal details.
- """
- return self.post("/info", {"type": "userVaultEquities", "user": user})
- def user_role(self, user: str) -> Any:
- """Retrieve the role and account type information for a user.
- POST /info
- Args:
- user (str): Onchain address in 42-character hexadecimal format.
- Returns:
- Role and account type information including account structure,
- permissions, and relationships within the Hyperliquid ecosystem.
- """
- return self.post("/info", {"type": "userRole", "user": user})
- def user_rate_limit(self, user: str) -> Any:
- """Retrieve user's API rate limit configuration and usage.
- POST /info
- Args:
- user (str): Onchain address in 42-character hexadecimal format.
- Returns:
- Detailed information about user's API rate limit configuration
- and current usage for managing API usage and avoiding rate limiting.
- """
- return self.post("/info", {"type": "userRateLimit", "user": user})
- def query_spot_deploy_auction_status(self, user: str) -> Any:
- return self.post("/info", {"type": "spotDeployState", "user": user})
- def extra_agents(self, user: str) -> Any:
- """Retrieve extra agents associated with a user.
- POST /info
- Args:
- user (str): Onchain address in 42-character hexadecimal format;
- e.g. 0x0000000000000000000000000000000000000000.
- Returns:
- [
- {
- "name": str,
- "address": str,
- "validUntil": int
- },
- ...
- ]
- """
- return self.post("/info", {"type": "extraAgents", "user": user})
- def _remap_coin_subscription(self, subscription: Subscription) -> None:
- if (
- subscription["type"] == "l2Book"
- or subscription["type"] == "trades"
- or subscription["type"] == "candle"
- or subscription["type"] == "bbo"
- or subscription["type"] == "activeAssetCtx"
- ):
- subscription["coin"] = self.name_to_coin[subscription["coin"]]
- def subscribe(self, subscription: Subscription, callback: Callable[[Any], None]) -> int:
- self._remap_coin_subscription(subscription)
- if self.ws_manager is None:
- raise RuntimeError("Cannot call subscribe since skip_ws was used")
- else:
- return self.ws_manager.subscribe(subscription, callback)
- def unsubscribe(self, subscription: Subscription, subscription_id: int) -> bool:
- self._remap_coin_subscription(subscription)
- if self.ws_manager is None:
- raise RuntimeError("Cannot call unsubscribe since skip_ws was used")
- else:
- return self.ws_manager.unsubscribe(subscription, subscription_id)
- def name_to_asset(self, name: str) -> int:
- return self.coin_to_asset[self.name_to_coin[name]]
|