Skip to content

Instantly share code, notes, and snippets.

@abrkn
Created April 8, 2025 08:18
Show Gist options
  • Save abrkn/9c803060393e872fdb2cf0284ff97839 to your computer and use it in GitHub Desktop.
Save abrkn/9c803060393e872fdb2cf0284ff97839 to your computer and use it in GitHub Desktop.
Hyperliquid rounding
const PRICE_MAX_SD: u32 = 5;
const PRICE_MAX_DP_PERPS: u32 = 6;
const PRICE_MAX_DP_SPOT: u32 = 8;
/// Rounds the price to the max significant digits or max decimal places
/// See https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/tick-and-lot-size
///
/// The `sz_decimals` is fetched from the meta info endpoint.
///
/// Use `RoundingStrategy::ToZero` when buying and `RoundingStrategy::AwayFromZero` when selling
pub fn round_price(price: Decimal, max_dp: u32, sz_decimals: u32, round_up: bool) -> Decimal {
let strategy = if round_up {
RoundingStrategy::AwayFromZero
} else {
RoundingStrategy::ToZero
};
// Round to PRICE_MAX_SD
let price = price
.round_sf_with_strategy(PRICE_MAX_SD, strategy)
.expect("Failed to round price");
// Round to max_dp - sz_decimals
price.round_dp_with_strategy(max_dp - sz_decimals, strategy)
}
/// Rounds the quantity to the max significant digits or max decimal places
/// See https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/tick-and-lot-size
///
/// The `sz_decimals` is fetched from the meta info endpoint.
///
/// Use `RoundingStrategy::ToZero` when opening a position and `RoundingStrategy::AwayFromZero` when closing
pub fn round_qty(qty: Decimal, sz_decimals: u32, round_up: bool) -> Decimal {
// Round to PRICE_MAX_DP - sz_decimals
qty.round_dp_with_strategy(
sz_decimals,
if round_up {
RoundingStrategy::AwayFromZero
} else {
RoundingStrategy::ToZero
},
)
}
pub fn perp_round_price(&self, asset: &str, price: Decimal, round_up: bool) -> Decimal {
let entry = self.perp_meta.universe.iter().find(|a| a.name == asset);
if let Some(entry) = entry {
let sz_decimals = entry.sz_decimals;
round_price(price, PRICE_MAX_DP_PERPS, sz_decimals, round_up)
} else {
panic!("Unknown asset: {}", asset);
}
}
pub fn spot_round_price(&self, asset: &str, price: Decimal, round_up: bool) -> Decimal {
let sz_decimals = self
.cache
.borrow()
.spot_meta
.tokens
.iter()
.find_map(|t| {
if t.name == asset {
Some(t.sz_decimals)
} else {
None
}
})
.unwrap_or_else(|| panic!("Asset {} not found", asset));
round_price(price, PRICE_MAX_DP_SPOT, sz_decimals as u32, round_up)
}
pub fn perp_round_qty(&self, asset: &str, qty: Decimal, round_up: bool) -> Decimal {
let entry = self.perp_meta.universe.iter().find(|a| a.name == asset);
if let Some(entry) = entry {
let sz_decimals = entry.sz_decimals;
round_qty(qty, sz_decimals, round_up)
} else {
panic!("Unknown asset: {}", asset);
}
}
pub fn spot_round_qty(&self, asset: &str, qty: Decimal, round_up: bool) -> Decimal {
let sz_decimals = self
.cache
.borrow()
.spot_meta
.tokens
.iter()
.find_map(|t| {
if t.name == asset {
Some(t.sz_decimals)
} else {
None
}
})
.unwrap_or_else(|| panic!("Asset {} not found", asset));
round_qty(qty, sz_decimals as u32, round_up)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment