use alloy::primitives::I256;
const SCALE_1E6: f64 = 1_000_000.0;
#[inline]
fn i256_to_f64(x: I256) -> f64 {
if let Ok(narrow) = i64::try_from(x) {
return narrow as f64;
}
i256_to_f64_slow(x)
}
#[cold]
#[inline(never)]
fn i256_to_f64_slow(x: I256) -> f64 {
if let Ok(narrow) = i128::try_from(x) {
return narrow as f64;
}
let is_neg = x.is_negative();
let abs = x.unsigned_abs();
if let Ok(narrow) = u128::try_from(abs) {
let f = narrow as f64;
return if is_neg { -f } else { f };
}
if is_neg {
f64::NEG_INFINITY
} else {
f64::INFINITY
}
}
#[inline]
pub fn entry_price(entry_perp_delta: I256, entry_usd_delta: I256) -> f64 {
let perp_f = i256_to_f64(entry_perp_delta);
if perp_f == 0.0 {
return 0.0;
}
i256_to_f64(entry_usd_delta).abs() / perp_f.abs()
}
#[inline]
pub fn position_size(entry_perp_delta: I256) -> f64 {
i256_to_f64(entry_perp_delta) / SCALE_1E6
}
#[inline]
pub fn position_value(entry_perp_delta: I256, mark_price: f64) -> f64 {
let size = position_size(entry_perp_delta);
size.abs() * mark_price
}
#[inline]
pub fn leverage(position_value: f64, effective_margin: f64) -> f64 {
if effective_margin <= 0.0 {
return f64::INFINITY;
}
position_value / effective_margin
}
#[inline]
pub fn liquidation_price(
entry_perp_delta: I256,
entry_usd_delta: I256,
margin: f64,
liq_ratio_scaled: u32,
is_long: bool,
) -> Option<f64> {
let size = position_size(entry_perp_delta);
if size == 0.0 {
return None;
}
if margin <= 0.0 {
return None;
}
let ep = entry_price(entry_perp_delta, entry_usd_delta);
let abs_size = size.abs();
let notional = abs_size * ep;
let liq_ratio = liq_ratio_scaled as f64 / SCALE_1E6;
let margin_excess = margin - liq_ratio * notional;
if is_long {
let liq = ep - margin_excess / abs_size;
Some(liq.max(0.0))
} else {
let liq = ep + margin_excess / abs_size;
Some(liq)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn i(val: i64) -> I256 {
I256::try_from(val).unwrap()
}
#[test]
fn i256_to_f64_positive() {
assert!((i256_to_f64(i(1_000_000)) - 1_000_000.0).abs() < 0.5);
}
#[test]
fn i256_to_f64_negative() {
assert!((i256_to_f64(i(-1_000_000)) - (-1_000_000.0)).abs() < 0.5);
}
#[test]
fn i256_to_f64_zero() {
assert_eq!(i256_to_f64(I256::ZERO), 0.0);
}
#[test]
fn i256_to_f64_beyond_i128() {
let beyond_i128 = I256::try_from(i128::MAX).unwrap() + I256::try_from(1i64).unwrap();
let f = i256_to_f64(beyond_i128);
assert!(f.is_finite());
assert!(f > 0.0);
assert_eq!(i256_to_f64(I256::MAX), f64::INFINITY);
assert_eq!(i256_to_f64(I256::MIN), f64::NEG_INFINITY);
}
#[test]
fn entry_price_basic() {
let price = entry_price(i(1_000_000), i(-1_500_000_000));
assert!(
(price - 1500.0).abs() < 0.001,
"price={price}, expected 1500"
);
}
#[test]
fn entry_price_short() {
let price = entry_price(i(-1_000_000), i(1_500_000_000));
assert!(
(price - 1500.0).abs() < 0.001,
"price={price}, expected 1500"
);
}
#[test]
fn entry_price_fractional() {
let price = entry_price(i(500_000), i(-1_000_000_000));
assert!(
(price - 2000.0).abs() < 0.001,
"price={price}, expected 2000"
);
}
#[test]
fn entry_price_zero_perp_returns_zero() {
assert_eq!(entry_price(I256::ZERO, I256::ZERO), 0.0);
}
#[test]
fn position_size_basic() {
let size = position_size(i(2_500_000));
assert!((size - 2.5).abs() < 1e-6);
}
#[test]
fn position_size_negative() {
let size = position_size(i(-1_000_000));
assert!((size - (-1.0)).abs() < 1e-6);
}
#[test]
fn position_size_zero() {
assert_eq!(position_size(I256::ZERO), 0.0);
}
#[test]
fn position_size_fractional_eth() {
let size = position_size(i(1_000));
assert!((size - 0.001).abs() < 1e-9);
}
#[test]
fn position_value_basic() {
let val = position_value(i(1_000_000), 1600.0);
assert!((val - 1600.0).abs() < 0.001);
}
#[test]
fn position_value_short() {
let val = position_value(i(-1_000_000), 1600.0);
assert!((val - 1600.0).abs() < 0.001);
}
#[test]
fn position_value_half_eth() {
let val = position_value(i(500_000), 2000.0);
assert!((val - 1000.0).abs() < 0.001);
}
#[test]
fn leverage_basic() {
let lev = leverage(1000.0, 100.0);
assert!((lev - 10.0).abs() < 0.001);
}
#[test]
fn leverage_1x() {
let lev = leverage(1000.0, 1000.0);
assert!((lev - 1.0).abs() < 0.001);
}
#[test]
fn leverage_zero_margin() {
assert!(leverage(1000.0, 0.0).is_infinite());
}
#[test]
fn leverage_negative_margin() {
assert!(leverage(1000.0, -50.0).is_infinite());
}
#[test]
fn liquidation_price_long() {
let liq = liquidation_price(i(1_000_000), i(-1_500_000_000), 100.0, 25_000, true);
assert!(liq.is_some());
assert!(
(liq.unwrap() - 1437.5).abs() < 0.01,
"liq={}, expected 1437.5",
liq.unwrap()
);
}
#[test]
fn liquidation_price_short() {
let liq = liquidation_price(i(-1_000_000), i(1_500_000_000), 100.0, 25_000, false);
assert!(liq.is_some());
assert!(
(liq.unwrap() - 1562.5).abs() < 0.01,
"liq={}, expected 1562.5",
liq.unwrap()
);
}
#[test]
fn liquidation_price_long_clamped_to_zero() {
let liq = liquidation_price(i(1_000_000), i(-100_000_000), 200.0, 25_000, true);
assert!(liq.is_some());
assert_eq!(liq.unwrap(), 0.0);
}
#[test]
fn liquidation_price_zero_size() {
assert_eq!(
liquidation_price(I256::ZERO, I256::ZERO, 100.0, 25_000, true),
None
);
}
#[test]
fn liquidation_price_zero_margin() {
assert_eq!(
liquidation_price(i(1_000_000), i(-1_500_000_000), 0.0, 25_000, true),
None
);
}
#[test]
fn liquidation_price_negative_margin() {
assert_eq!(
liquidation_price(i(1_000_000), i(-1_500_000_000), -50.0, 25_000, true),
None
);
}
#[test]
fn liquidation_price_high_leverage_long() {
let liq = liquidation_price(i(1_000_000), i(-1_500_000_000), 15.0, 25_000, true);
assert!(liq.is_some());
let liq_val = liq.unwrap();
assert!(
(liq_val - 1522.5).abs() < 0.01,
"liq={liq_val}, expected 1522.5"
);
assert!(liq_val > 1500.0);
}
#[test]
fn liquidation_price_5_percent_ratio() {
let liq = liquidation_price(i(2_000_000), i(-2_000_000_000), 200.0, 50_000, true);
assert!(liq.is_some());
assert!(
(liq.unwrap() - 950.0).abs() < 0.01,
"liq={}, expected 950",
liq.unwrap()
);
}
}