From 963dafb39d1056219053f6bc7cfe41eb4a24022c Mon Sep 17 00:00:00 2001 From: basicker Date: Sun, 25 Aug 2024 23:24:15 +0200 Subject: [PATCH] Add Cache.asset_name_to_coin/1 and Cache.asset_name_to_index/1 --- .gitignore | 2 + README.md | 2 +- lib/hyperliquid/cache/cache.ex | 114 +++++++++++++++++++++------ lib/hyperliquid/orders/order_wire.ex | 6 +- lib/hyperliquid/orders/orders.ex | 38 +++++---- 5 files changed, 119 insertions(+), 43 deletions(-) diff --git a/.gitignore b/.gitignore index ef34699..e70d8eb 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ hyperliquid-*.tar # Temporary files, for example, from tests. /tmp/ + +.idea \ No newline at end of file diff --git a/README.md b/README.md index 3bee99b..db68b94 100644 --- a/README.md +++ b/README.md @@ -197,7 +197,7 @@ 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: ```elixir -Hyperliquid.Cache.asset_from_coin("SOL") +Hyperliquid.Cache.asset_name_to_index("SOL") 5 Hyperliquid.Cache.decimals_from_coin("SOL") diff --git a/lib/hyperliquid/cache/cache.ex b/lib/hyperliquid/cache/cache.ex index ea4d4d5..e62ded0 100644 --- a/lib/hyperliquid/cache/cache.ex +++ b/lib/hyperliquid/cache/cache.ex @@ -33,10 +33,15 @@ defmodule Hyperliquid.Cache do spot_pairs = Map.get(spot_meta, "universe") tokens = Map.get(spot_meta, "tokens") - asset_map = Map.merge( - create_asset_map(meta), - create_asset_map(spot_meta, 10_000) - ) + assets_name_to_coin_map = Map.merge( + create_perp_assets_name_to_coin_map(meta), + create_spot_assets_name_to_coin_map(spot_meta) + ) + + assets_name_to_index_map = Map.merge( + create_perp_assets_name_to_index_map(meta), + create_spot_assets_name_to_index_map(spot_meta) + ) decimal_map = Map.merge( create_decimal_map(meta), @@ -46,34 +51,71 @@ defmodule Hyperliquid.Cache do Cachex.put!(@cache, :meta, meta) Cachex.put!(@cache, :spot_meta, spot_meta) Cachex.put!(@cache, :all_mids, all_mids) - Cachex.put!(@cache, :asset_map, asset_map) Cachex.put!(@cache, :decimal_map, decimal_map) Cachex.put!(@cache, :perps, perps) Cachex.put!(@cache, :spot_pairs, spot_pairs) Cachex.put!(@cache, :tokens, tokens) Cachex.put!(@cache, :ctxs, ctxs) Cachex.put!(@cache, :spot_ctxs, spot_ctxs) + Cachex.put!(@cache, :assets_name_to_index_map, assets_name_to_index_map) + Cachex.put!(@cache, :assets_name_to_coin_map, assets_name_to_coin_map) end - 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) + def meta, do: Cache.get(:meta) + def spot_meta, do: Cache.get(:spot_meta) + def all_mids, do: Cache.get(:all_mids) + 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) + def assets_name_to_index_map, do: Cache.get(:assets_name_to_index_map) + def assets_name_to_coin_map, do: Cache.get(:assets_name_to_coin_map) ###### Setters ###### - defp create_asset_map(data, buffer \\ 0) do + defp create_perp_assets_name_to_index_map(data) do + perp_buffer = 0 + data |> Map.get("universe") - |> Enum.with_index(&{&1["name"], &2 + buffer}) + |> Enum.with_index(&{&1["name"], &2 + perp_buffer}) |> Enum.into(%{}) end + defp create_spot_assets_name_to_index_map(data) do + spot_buffer = 10_000 + tokens = Map.get(data, "tokens") + + data + |> Map.get("universe") + |> Enum.reduce(%{}, fn spot_asset, spot_assets_index -> + [base, quote] = spot_asset["tokens"] + spot_asset_name = "#{Enum.at(tokens, base)["name"]}/#{Enum.at(tokens, quote)["name"]}" + Map.put(spot_assets_index, spot_asset_name, spot_asset["index"] + spot_buffer) + end) + end + + defp create_perp_assets_name_to_coin_map(data) do + data + |> Map.get("universe") + |> Enum.reduce(%{}, fn perp_assets, perp_assets_name_to_coin_map -> + Map.put(perp_assets_name_to_coin_map, perp_assets["name"], perp_assets["name"]) + end) + end + + defp create_spot_assets_name_to_coin_map(data) do + tokens = Map.get(data, "tokens") + + data + |> Map.get("universe") + |> Enum.reduce(%{}, fn spot_asset, spot_assets_name_to_coin_map -> + [base, quote] = spot_asset["tokens"] + spot_asset_name = "#{Enum.at(tokens, base)["name"]}/#{Enum.at(tokens, quote)["name"]}" + Map.put(spot_assets_name_to_coin_map, spot_asset_name, spot_asset["name"]) + end) + end + defp create_decimal_map(data) do data |> Map.get("universe") @@ -91,24 +133,52 @@ defmodule Hyperliquid.Cache do ###### Helpers ###### @doc """ - Retrieves the asset index for a given coin symbol. + Retrieves the asset index for a given asset name. ## Parameters - - `coin`: The coin symbol (e.g., "BTC", "ETH") + - `name`: The asset name (e.g., "BTC", "ETH" for perp assets or "PURR/USDC", "HFUN/USDC" for spot assets) ## Returns - The asset index corresponding to the given coin symbol, or nil if not found. + The asset index corresponding to the given asset name, or nil if not found. ## Example - iex> Hyperliquid.Cache.asset_from_coin("SOL") + iex> Hyperliquid.Cache.asset_name_to_index("SOL") 5 + + iex> Hyperliquid.Cache.asset_name_to_index("PURR/USDC") + 10000 """ - def asset_from_coin(coin), do: Cache.get(:asset_map)[coin] + def asset_name_to_index(name), do: Cache.get(:assets_name_to_index_map)[name] def decimals_from_coin(coin), do: Cache.get(:decimal_map)[coin] + + @doc """ + Retrieves the coin symbol for a given asset name. + + ## Parameters + + - `name`: The asset name (e.g., "BTC", "ETH" for perp assets or "PURR/USDC", "HFUN/USDC" for spot assets) + + ## Returns + + The coin symbol corresponding to the given asset name, or nil if not found. + + ## Example + + iex> Hyperliquid.Cache.asset_name_to_coin("SOL") + SOL + + iex> Hyperliquid.Cache.asset_name_to_coin("PURR/USDC") + PURR/USDC + + iex> Hyperliquid.Cache.asset_name_to_coin("HFUN/USDC") + @1 + """ + def asset_name_to_coin(name), do: Cache.get(:assets_name_to_coin_map)[name] + def get_token_by_index(index), do: Cache.get(:tokens) |> Enum.find(& &1["index"] == index) diff --git a/lib/hyperliquid/orders/order_wire.ex b/lib/hyperliquid/orders/order_wire.ex index d6a5ca9..54051c0 100644 --- a/lib/hyperliquid/orders/order_wire.ex +++ b/lib/hyperliquid/orders/order_wire.ex @@ -32,11 +32,11 @@ defmodule Hyperliquid.Orders.OrderWire do The OrderWire struct is typically used within order placement functions in the Orders module. Here's an example of how it is used for a `limit_order` function: - def limit_order(coin, sz, is_buy?, px, tif \\ "gtc", reduce? \\ false, vault_address \\ nil) do + def limit_order(asset_name, sz, is_buy?, px, tif \\ "gtc", reduce? \\ false, vault_address \\ nil) do trigger = trigger_from_order_type(tif) - asset = Cache.asset_from_coin(coin) + asset_index = Cache.asset_name_to_index(asset_name) - OrderWire.new(asset, is_buy?, px, sz, reduce?, trigger) + OrderWire.new(asset_index, is_buy?, px, sz, reduce?, trigger) |> OrderWire.purify() |> Exchange.place_order("na", vault_address) end diff --git a/lib/hyperliquid/orders/orders.ex b/lib/hyperliquid/orders/orders.ex index b56a249..eab6787 100644 --- a/lib/hyperliquid/orders/orders.ex +++ b/lib/hyperliquid/orders/orders.ex @@ -22,9 +22,12 @@ defmodule Hyperliquid.Orders do Usage examples: - # Retrieve mid-price for a coin - mid_price = Hyperliquid.Orders.get_midprice("SOL") - 135.545 + # Retrieve mid-price for an asset + mid_price = Hyperliquid.Orders.get_midprice("SOL") + 135.545 + + mid_price = Hyperliquid.Orders.get_midprice("PURR/USDC") + 0.18546 # Place a market buy order Hyperliquid.Orders.market_buy("ETH", 1.0, "0x123...") @@ -84,7 +87,8 @@ defmodule Hyperliquid.Orders do @default_slippage 0.05 - def get_midprice(coin) do + def get_midprice(asset_name) do + coin = Cache.asset_name_to_coin(asset_name) mids = Cache.all_mids() || fetch_mids() coin = String.to_existing_atom(coin) @@ -101,8 +105,8 @@ defmodule Hyperliquid.Orders do end end - def slippage_price(coin, buy?, slippage \\ @default_slippage, px \\ nil) do - px = px || get_midprice(coin) + def slippage_price(asset_name, buy?, slippage \\ @default_slippage, px \\ nil) do + px = px || get_midprice(asset_name) px = if buy?, do: px * (1 + slippage), else: px * (1 - slippage) case PriceConverter.convert_price(px, :perp) do @@ -124,27 +128,27 @@ defmodule Hyperliquid.Orders do def trigger_from_order_type(type) when is_map(type), do: %{trigger: type} - def market_buy(coin, sz, vault_address \\ nil), do: - market_order(coin, sz, true, false, vault_address, nil, @default_slippage) + def market_buy(asset_name, sz, vault_address \\ nil), do: + market_order(asset_name, sz, true, false, vault_address, nil, @default_slippage) - def market_sell(coin, sz, vault_address \\ nil), do: - market_order(coin, sz, false, false, vault_address, nil, @default_slippage) + def market_sell(asset_name, sz, vault_address \\ nil), do: + market_order(asset_name, sz, false, false, vault_address, nil, @default_slippage) - def market_order(coin, sz, buy?, reduce?, vault_address \\ nil, px \\ nil, slippage \\ @default_slippage) do - px = slippage_price(coin, buy?, slippage, px) + def market_order(asset_name, sz, buy?, reduce?, vault_address \\ nil, px \\ nil, slippage \\ @default_slippage) do + px = slippage_price(asset_name, buy?, slippage, px) trigger = trigger_from_order_type("ioc") - asset = Cache.asset_from_coin(coin) + asset_index = Cache.asset_name_to_index(asset_name) - OrderWire.new(asset, buy?, px, sz, reduce?, trigger) + OrderWire.new(asset_index, buy?, px, sz, reduce?, trigger) |> OrderWire.purify() |> Exchange.place_order("na", vault_address) end - def limit_order(coin, sz, buy?, px, tif \\ "gtc", reduce? \\ false, vault_address \\ nil) do + def limit_order(asset_name, sz, buy?, px, tif \\ "gtc", reduce? \\ false, vault_address \\ nil) do trigger = trigger_from_order_type(tif) - asset = Cache.asset_from_coin(coin) + asset_index = Cache.asset_name_to_index(asset_name) - OrderWire.new(asset, buy?, px, sz, reduce?, trigger) + OrderWire.new(asset_index, buy?, px, sz, reduce?, trigger) |> OrderWire.purify() |> Exchange.place_order("na", vault_address) end