Created
April 8, 2025 08:18
-
-
Save abrkn/9c803060393e872fdb2cf0284ff97839 to your computer and use it in GitHub Desktop.
Hyperliquid rounding
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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