Skip to main content

HyperliquidClient

Struct HyperliquidClient 

Source
pub struct HyperliquidClient { /* private fields */ }
Expand description

Hyperliquid perpetual futures execution client.

Wraps the official hyperliquid_rust_sdk to implement the ExecutionClient trait. Supports perpetual futures trading on Hyperliquid DEX.

Implementations§

Source§

impl HyperliquidClient

Source

pub async fn connect( config: HyperliquidConfig, ) -> Result<Self, HyperliquidConnectError>

Create a new client asynchronously.

Use this when calling from an async context (e.g., tokio tests). For sync contexts, use ExecutionClient::new().

Reads the markets of each DEX in HyperliquidConfig::dexes; see HIP-3 perpetuals.

§Errors
§Cost

With any DEX configured: perpDexs, spotMeta and each DEX’s meta, weight 20 each, so 40 + 20 × dexes, besides what the SDK reads to build its own clients.

Source

pub fn wallet_address(&self) -> String

Returns the wallet address as a hex string (for logging/debugging).

Source

pub fn order_precision( &self, instrument: &InstrumentNameExchange, ) -> Option<OrderPrecision>

The precision rules for orders on the perpetual instrument, from its szDecimals.

open_order refuses an order that breaks them with OrderError::InvalidPrecision and sends nothing. Round with these rules first. See OrderPrecision.

None when instrument names no perpetual this client trades, or one listed after the client connected: the client reads each DEX’s perpetuals once, at connect, so a perpetual listed since needs a new client.

Trait Implementations§

Source§

impl Clone for HyperliquidClient

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for HyperliquidClient

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl ExecutionClient for HyperliquidClient

Source§

fn new(config: Self::Config) -> Self

Creates a new Hyperliquid client synchronously.

§Panics
  • If no Tokio runtime is available on the current thread
  • If called from within an async context (e.g., inside async fn, spawn, or block_on)
  • Whenever HyperliquidClient::connect would fail: SDK initialization fails (network error, invalid credentials), or a configured DEX is unknown or unreadable

Use HyperliquidClient::connect instead — it’s async-safe and returns Result. This method exists only for trait compliance; prefer connect() in all new code.

Source§

async fn account_snapshot( &self, _assets: &[AssetNameExchange], instruments: &[InstrumentNameExchange], ) -> Result<UnindexedAccountSnapshot, UnindexedClientError>

The collateral balances, and each perpetual’s open orders and position, across the default DEX and every configured HIP-3 DEX.

§Balances

Read where the account’s abstraction mode holds them (see HIP-3 perpetuals):

  • Unified account or portfolio margin: one balance per collateral token of the DEXs traded (USDC, USDH), from the spot clearinghouse, free of what is on hold. A token not held is reported at zero. Other tokens, which portfolio margin may also count as collateral, are left to spot::HyperliquidSpotClient.
  • Standard (the default for an account that never chose a mode): one balance per DEX, from its margin summary, as each margins separately: the default DEX’s as USDC, each HIP-3 DEX’s as {dex}:{collateral} (xyz:USDC, flx:USDH). The free balance is what margin does not use, never below zero.

A mode this version does not know fails the snapshot rather than report balances read from the wrong place.

Positions on a HIP-3 DEX the client is not configured with are not read, and so not reported: name the DEX in HyperliquidConfig::dexes to see them.

§Positions

Every requested perpetual is listed. Its position is PositionReport::Open when Hyperliquid reports a non-zero size for it, and PositionReport::Flat otherwise, since each DEX’s state holds every open position on it. A size that does not parse is PositionReport::Unreported, with a warning.

§Errors

ApiError::InstrumentInvalid for a requested instrument that names no perpetual this client trades, such as one on a HIP-3 DEX it is not configured with: it was not read, so nothing can be said about it.

§Cost

At once, userAbstraction (weight 20) and, for each DEX traded, the default one included, a clearinghouseState (weight 2) and an openOrders (weight 20); then, under a unified account or portfolio margin, spotClearinghouseState (weight 2). With n HIP-3 DEXs that is 42 + 22n, or 44 + 22n, against Hyperliquid’s limit of 1,200 a minute per IP address.

Source§

async fn account_stream( &self, _assets: &[AssetNameExchange], _instruments: &[InstrumentNameExchange], ) -> Result<Self::AccountStream, UnindexedClientError>

Returns a live stream of account events (fills, order updates, funding payments).

Funding payments arrive as AccountEventKind::CashFlow; see the module docs.

§Instrument filtering

The instruments parameter is ignored — Hyperliquid’s WebSocket API does not support per-instrument subscriptions for user events. All fills, order updates and funding payments across all instruments are delivered. Consumers requiring instrument filtering must filter client-side.

§Task lifecycle

Spawns four background tasks (fills, orders, funding payments, and the reconnect’s order check below) that are automatically cancelled when the returned stream is dropped. The ws_client is held by the orders task; when cancelled, the tasks exit and the WebSocket connection closes.

§Orders that ended while disconnected

The SDK reconnects the socket underneath this stream and resubscribes, but orderUpdates opens with no snapshot, so an order that ended while the socket was down is never reported on it. After each reconnect the stream therefore reports how each order the client holds as live ended, where it did, as an AccountEventKind::OrderSnapshot of its inactive state, read as OrderStatusClient::fetch_ended_orders reads it: filled (without an average price), cancelled (with what filled before) or rejected. Each carries StrategyId::unknown, since Hyperliquid records no strategy; the engine matches it to the order it tracks by client order id.

  • When. The SDK tells each subscription that the socket dropped, and the venue opens the resubscribed userFills with a snapshot of recent fills. The check starts once that snapshot’s fills have been sent on, so the fills of an order that it carries arrive before how the order ended. The snapshot is all the fill recovery there is (see the module docs), and Hyperliquid does not document how far back it reaches, so after a long outage an order can be reported filled before, or without, fills older than that. The SDK resubscribes in no fixed order and only logs a resubscription that fails: an order that ends after the check lists the open orders but before orderUpdates is subscribed again is missed, and if userFills is not subscribed again no check runs until a later reconnect.
  • Which orders. The client holds an order as live from the response to placing it, from a listing of open orders (account_snapshot, fetch_open_orders), and from its live reports on any of its account streams, until it sees the order end, which a successful cancel does. It holds up to 4,096 orders and forgets the oldest past that, logged at warn. An order placed outside this client and never listed or reported to it is not covered. Every instrument is checked, as the stream ignores instruments.
  • Cost. One openOrders request (weight 20) per DEX traded, together listing every open order, then one orderStatus request (weight 2) for each held order the listing no longer shows, 8 at a time. Both count against Hyperliquid’s REST limit of 1,200 a minute per IP address. The cost is per stream: a perpetuals and a spot stream on one wallet each make their own.
  • Failures. Each order’s lookup is settled as it ends. An instrument whose listing or lookup fails, or whose check is still running after 30 s, is retried while the stream is open 1, 2, 4, 8 and 16 minutes later, asking only about the orders still held, then given up, logged at error; its orders are asked about again at the next reconnect. A listing that fails charges every instrument in it. An order Hyperliquid does not know stops being held, logged at warn; one still live, or in a state this version cannot read, stays held.
§Errors

ConnectivityError::Socket if the WebSocket cannot be opened, or if any of the userFills, orderUpdates and userFundings subscriptions fails: the stream is not opened with only some of them.

Source§

async fn fetch_balances( &self, _assets: &[AssetNameExchange], ) -> Result<Vec<AssetBalance<AssetNameExchange>>, UnindexedClientError>

The collateral balances, read as account_snapshot reads them: userAbstraction (weight 20), then either spotClearinghouseState or each DEX’s clearinghouseState (weight 2 each).

Source§

async fn fetch_open_orders( &self, instruments: &[InstrumentNameExchange], ) -> Result<Vec<Order<ExchangeId, InstrumentNameExchange, Open>>, UnindexedClientError>

Every open order on each DEX traded, with one openOrders (weight 20) per DEX.

§Errors

ApiError::InstrumentInvalid for an instrument in instruments that names no perpetual this client trades.

Source§

async fn fetch_trades( &self, start: DateTime<Utc>, end: DateTime<Utc>, instruments: &[InstrumentNameExchange], ) -> Result<TradesRead<AssetNameExchange, InstrumentNameExchange>, UnindexedClientError>

Reads the span with userFillsByTime, to its end within the call, so the read is always complete (resume: None). Hyperliquid keeps only each wallet’s 10,000 most recent fills, so a span reaching further back is read only as far as those go, and held_since says where that is. One read covers every DEX; fills on a HIP-3 DEX this client is not configured with are left out.

§Errors

ApiError::InstrumentInvalid for an instrument in instruments that names no perpetual this client trades.

Source§

const EXCHANGE: ExchangeId = ExchangeId::HyperliquidPerp

Source§

const SUPPORTED_KINDS: &'static [InstrumentKindDiscriminant]

The InstrumentKindDiscriminants this client can trade. Read more
Source§

type Config = HyperliquidConfig

Source§

type AccountStream = Pin<Box<dyn Stream<Item = AccountEvent<ExchangeId, AssetNameExchange, InstrumentNameExchange>> + Send>>

Source§

async fn cancel_order( &self, request: OrderRequestCancel<ExchangeId, &InstrumentNameExchange>, ) -> UnindexedOrderResponseCancel

Cancel an order at the venue. Read more
Source§

async fn open_order( &self, request: OrderRequestOpen<ExchangeId, &InstrumentNameExchange>, ) -> Order<ExchangeId, InstrumentNameExchange, UnindexedOrderState>

Place an order on the exchange. Read more
Source§

fn validate_config( config: &Self::Config, instruments: &[ClientInstrument<'_>], ) -> Result<(), String>

Checks config against the instruments this client is about to be built to trade, before Self::new is called. Err names what is wrong. Read more
Source§

fn cancel_orders<'a>( &self, requests: impl IntoIterator<Item = OrderRequestCancel<ExchangeId, &'a InstrumentNameExchange>>, ) -> impl Stream<Item = UnindexedOrderResponseCancel> + Send

Source§

fn open_orders<'a>( &self, requests: impl IntoIterator<Item = OrderRequestOpen<ExchangeId, &'a InstrumentNameExchange>>, ) -> impl Stream<Item = Order<ExchangeId, InstrumentNameExchange, UnindexedOrderState>> + Send

Source§

impl OrderStatusClient for HyperliquidClient

Looks each order up with orderStatus (weight 2), by the cloid it was placed with, or by its oid for an order placed without one, which this client reports under its oid. Up to 8 run at once.

filled is reported as fully filled, without an average price, which the record does not carry; every status ending in Canceled, and scheduledCancel, as cancelled with what filled before it; and every status ending in Rejected as OpenFailed. Hyperliquid has no expiry. An order the venue does not know, one on another instrument than the key’s, and an id that is neither a canonical UUID nor an oid are unknown. An oid whose order carries a cloid is unknown too, since this client reports that order under its cloid.

The account stream runs the same lookup itself after a reconnect; see account_stream.

Source§

async fn fetch_order_status( &self, key: &UnindexedOrderKey, ) -> Result<VenueOrderStatus, UnindexedClientError>

One orderStatus (weight 2), which reports a live order as well as an ended one, read as fetch_ended_orders reads it.

§Errors

When the lookup fails, or Hyperliquid’s record of the order does not convert.

Source§

async fn fetch_ended_orders( &self, orders: &[UnindexedOrderKey], ) -> Result<Vec<UnindexedInactiveOrder>, UnindexedClientError>

How each of orders ended, for those that have. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> Conv for T

Source§

fn conv<T>(self) -> T
where Self: Into<T>,

Converts self into T using Into<T>. Read more
Source§

impl<T> FmtForward for T

Source§

fn fmt_binary(self) -> FmtBinary<Self>
where Self: Binary,

Causes self to use its Binary implementation when Debug-formatted.
Source§

fn fmt_display(self) -> FmtDisplay<Self>
where Self: Display,

Causes self to use its Display implementation when Debug-formatted.
Source§

fn fmt_lower_exp(self) -> FmtLowerExp<Self>
where Self: LowerExp,

Causes self to use its LowerExp implementation when Debug-formatted.
Source§

fn fmt_lower_hex(self) -> FmtLowerHex<Self>
where Self: LowerHex,

Causes self to use its LowerHex implementation when Debug-formatted.
Source§

fn fmt_octal(self) -> FmtOctal<Self>
where Self: Octal,

Causes self to use its Octal implementation when Debug-formatted.
Source§

fn fmt_pointer(self) -> FmtPointer<Self>
where Self: Pointer,

Causes self to use its Pointer implementation when Debug-formatted.
Source§

fn fmt_upper_exp(self) -> FmtUpperExp<Self>
where Self: UpperExp,

Causes self to use its UpperExp implementation when Debug-formatted.
Source§

fn fmt_upper_hex(self) -> FmtUpperHex<Self>
where Self: UpperHex,

Causes self to use its UpperHex implementation when Debug-formatted.
Source§

fn fmt_list(self) -> FmtList<Self>
where &'a Self: for<'a> IntoIterator,

Formats each item in a sequence. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> JsonSchemaMaybe for T

Source§

impl<T> Pipe for T
where T: ?Sized,

Source§

fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> R
where Self: Sized,

Pipes by value. This is generally the method you want to use. Read more
Source§

fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> R
where R: 'a,

Borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> R
where R: 'a,

Mutably borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
where Self: Borrow<B>, B: 'a + ?Sized, R: 'a,

Borrows self, then passes self.borrow() into the pipe function. Read more
Source§

fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
where Self: BorrowMut<B>, B: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.borrow_mut() into the pipe function. Read more
Source§

fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
where Self: AsRef<U>, U: 'a + ?Sized, R: 'a,

Borrows self, then passes self.as_ref() into the pipe function.
Source§

fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
where Self: AsMut<U>, U: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.as_mut() into the pipe function.
Source§

fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
where Self: Deref<Target = T>, T: 'a + ?Sized, R: 'a,

Borrows self, then passes self.deref() into the pipe function.
Source§

fn pipe_deref_mut<'a, T, R>( &'a mut self, func: impl FnOnce(&'a mut T) -> R, ) -> R
where Self: DerefMut<Target = T> + Deref, T: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.deref_mut() into the pipe function.
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> Tap for T

Source§

fn tap(self, func: impl FnOnce(&Self)) -> Self

Immutable access to a value. Read more
Source§

fn tap_mut(self, func: impl FnOnce(&mut Self)) -> Self

Mutable access to a value. Read more
Source§

fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Immutable access to the Borrow<B> of a value. Read more
Source§

fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Mutable access to the BorrowMut<B> of a value. Read more
Source§

fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Immutable access to the AsRef<R> view of a value. Read more
Source§

fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Mutable access to the AsMut<R> view of a value. Read more
Source§

fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Immutable access to the Deref::Target of a value. Read more
Source§

fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Mutable access to the Deref::Target of a value. Read more
Source§

fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self

Calls .tap() only in debug builds, and is erased in release builds.
Source§

fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self

Calls .tap_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Calls .tap_borrow() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Calls .tap_borrow_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Calls .tap_ref() only in debug builds, and is erased in release builds.
Source§

fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Calls .tap_ref_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Calls .tap_deref() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_mut_dbg<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Calls .tap_deref_mut() only in debug builds, and is erased in release builds.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> TryConv for T

Source§

fn try_conv<T>(self) -> Result<T, Self::Error>
where Self: TryInto<T>,

Attempts to convert self into T using TryInto<T>. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more