Hyperliquid is an Elixir-based library for interacting with the Hyperliquid decentralized exchange platform. It provides a set of modules and functions to manage WebSocket connections, handle orders, and interact with the Hyperliquid API.
- Order management (market, limit, and close orders)
- Account operations (transfers, leverage adjustment, etc.)
- Price conversion and formatting
- Caching for efficient data access and improved performance
- Dynamic subscription management
- WebSocket streaming for real-time data
Add hyperliquid
to your list of dependencies in mix.exs
:
def deps do
[
{:hyperliquid, "~> 0.1.6"}
]
end
To use in livebook, add the following to the notebook dependencies and setup section:
Mix.install([
{:hyperliquid, "~> 0.1.6"}
],
config: [
hyperliquid: [private_key: "YOUR_KEY_HERE"]
]
)
# You can override the default ws and http urls to use testnet
Mix.install([
{:hyperliquid, "~> 0.1.6"}
],
config: [
hyperliquid: [
ws_url: "wss://api.hyperliquid-testnet.xyz/ws",
http_url: "https://api.hyperliquid-testnet.xyz",
private_key: "YOUR_KEY_HERE"
]
]
)
In config/config.exs
, add Hyperliquid protocol host params to your config file
config :hyperliquid,
private_key: "YOUR_KEY_HERE"
# Place a market sell order
Hyperliquid.Orders.market_sell("ETH", 1)
# Place a market buy order for a sub account (vault address)
Hyperliquid.Orders.market_buy("ETH", 1, "0x123...")
{:ok,
%{
"response" => %{
"data" => %{
"statuses" => [
%{"filled" => %{"avgPx" => "128.03", "oid" => 17114311614, "totalSz" => "1.0"}}
]
},
"type" => "order"
},
"status" => "ok"
}}
# Place a limit sell order
Hyperliquid.Orders.limit_order("BTC", 0.5, false, 50000, "gtc", false)
{:ok,
%{
"response" => %{
"data" => %{"statuses" => [%{"resting" => %{"oid" => 10030901240}}]},
"type" => "order"
},
"status" => "ok"
}}
# Close list of positions
{:ok, %{"assetPositions" => positions}} = Info.clearinghouse_state("0x123")
Hyperliquid.Orders.market_close(positions)
# Close single position
position = Enum.at(positions, 0)
Hyperliquid.Orders.market_close(position)
{:ok, %{
"response" => %{
"data" => %{
"statuses" => [
%{"filled" => %{"avgPx" => "148.07", "oid" => 10934427319, "totalSz" => "1.0"}}
]
},
"type" => "order"
},
"status" => "ok"
}}
# Close all positions for an address
Hyperliquid.Orders.market_close("0x123...")
[
ok: %{
"response" => %{
"data" => %{
"statuses" => [
%{"filled" => %{"avgPx" => "148.07", "oid" => 10934427319, "totalSz" => "1.0"}}
]
},
"type" => "order"
},
"status" => "ok"
}
]
To start a WebSocket stream:
# Pass in a single sub or list of subs to start a new ws connection.
{:ok, PID<0.408.0>} = Hyperliquid.Manager.maybe_start_stream(%{type: "allMids"})
{:ok, PID<0.408.0>} = Hyperliquid.Manager.maybe_start_stream([sub_list])
# The manager will check if the sub is currently already subscribed, and if not, open the connection.
# To subscribe to a user address, we call auto_start_user
{:ok, PID<0.408.0>} = Hyperliquid.Manager.auto_start_user(user_address)
# Because we are limited to 10 unique user subscriptions, it is crucial to keep track of which users
# are currently subbed to and that logic is handled internally by the manager but also available to be called externally.
Hyperliquid.Manager.get_subbed_users()
["0x123..."]
Hyperliquid.Manager.get_active_non_user_subs()
[%{type: "allMids"}]
Hyperliquid.Manager.get_active_user_subs()
[
%{type: "userFundings", user: "0x123..."},
%{type: "userHistoricalOrders", user: "0x123..."},
%{type: "userTwapHistory", user: "0x123..."},
%{type: "userTwapSliceFills", user: "0x123..."},
%{type: "userNonFundingLedgerUpdates", user: "0x123..."},
%{type: "userFills", user: "0x123...", aggregateByTime: false},
%{type: "notification", user: "0x123..."}
]
Manager.get_workers()
[PID<0.633.0>, PID<0.692.0>]
Once a Manager has started, it will automatically subscribe to allMids and update the cache. The Stream module is broadcasting each event it receives to the "ws_event" channel, to subscribe to these events in your own application, simply call subscribe like so:
Phoenix.PubSub.subscribe(Hyperliquid.PubSub, channel)
# also available is the shart hand method, via the Utils module.
Hyperliquid.Utils.subscribe(channel)
The application uses Cachex to handle in memory kv storage for fast and efficient lookup of values we frequently need to place valid orders, one of those key items is the current mid price of each asset.
When initialized, the Manager will make several requests to get this data, as well as subscribe to the "allMids" channel. This ensures the latest mid price is always up to date and can be immediately accessable.
Quick access utility functions.
def meta, do: Cache.get(:meta)
def spot_meta, do: Cache.get(:spot_meta)
def all_mids, do: Cache.get(:all_mids)
def asset_map, do: Cache.get(:asset_map)
def decimal_map, do: Cache.get(:decimal_map)
def perps, do: Cache.get(:perps)
def spot_pairs, do: Cache.get(:spot_pairs)
def tokens, do: Cache.get(:tokens)
def ctxs, do: Cache.get(:ctxs)
def spot_ctxs, do: Cache.get(:spot_ctxs)
You may also note some commonly used util methods in the Cache which can be used like this:
Hyperliquid.Cache.asset_from_coin("SOL")
5
Hyperliquid.Cache.decimals_from_coin("SOL")
2
Hyperliquid.Cache.get_token_by_name("HFUN")
%{
"evmContract" => nil,
"fullName" => nil,
"index" => 2,
"isCanonical" => false,
"name" => "HFUN",
"szDecimals" => 2,
"tokenId" => "0xbaf265ef389da684513d98d68edf4eae",
"weiDecimals" => 8
}
Hyperliquid.Cache.get_token_by_address("0xbaf265ef389da684513d98d68edf4eae")
%{
"evmContract" => nil,
"fullName" => nil,
"index" => 2,
"isCanonical" => false,
"name" => "HFUN",
"szDecimals" => 2,
"tokenId" => "0xbaf265ef389da684513d98d68edf4eae",
"weiDecimals" => 8
}
Hyperliquid.Cache.get_token_key("PURR")
"PURR:0xc1fb593aeffbeb02f85e0308e9956a90"
Hyperliquid.Cache.get_token_by_name("PURR") |> Cache.get_token_key()
"PURR:0xc1fb593aeffbeb02f85e0308e9956a90"
Hyperliquid.Cache.get(:tokens)
[
%{
"evmContract" => nil,
"fullName" => nil,
"index" => 0,
"isCanonical" => true,
"name" => "USDC",
"szDecimals" => 8,
"tokenId" => "0x6d1e7cde53ba9467b783cb7c530ce054",
"weiDecimals" => 8
},
...
]
One great place to look for more insight on how to utilize the cache, is the Orders module.
This project is licensed under the MIT License.