use rust_decimal::Decimal;
pub fn sqrt_price_x64_to_price(sqrt_price_x64: u128, decimals_a: u8, decimals_b: u8) -> Decimal {
let sqrt_ratio = sqrt_price_x64 as f64 / (1u128 << 64) as f64;
let raw_price = sqrt_ratio * sqrt_ratio;
let decimal_diff = decimals_a as i32 - decimals_b as i32;
let adjustment = 10f64.powi(decimal_diff);
let adjusted_price = raw_price * adjustment;
Decimal::try_from(adjusted_price).unwrap_or(Decimal::ZERO)
}
pub fn sqrt_price_to_price(sqrt_price_x64: u128, decimals_a: u8, decimals_b: u8) -> f64 {
let sqrt_ratio = sqrt_price_x64 as f64 / (1u128 << 64) as f64;
let raw_price = sqrt_ratio * sqrt_ratio;
let decimal_diff = decimals_a as i32 - decimals_b as i32;
let adjustment = 10f64.powi(decimal_diff);
raw_price * adjustment
}
pub fn invert_price(price: f64) -> f64 {
if price == 0.0 {
return 0.0;
}
1.0 / price
}
#[cfg(test)]
mod tests {
use super::*;
const Q64: u128 = 1u128 << 64;
#[test]
fn test_tick_zero_price_same_decimals() {
let price = sqrt_price_x64_to_price(Q64, 6, 6);
let diff = (price - Decimal::ONE).abs();
assert!(
diff < Decimal::new(1, 6), "tick 0 same decimals: expected ~1.0, got {}",
price
);
}
#[test]
fn test_tick_zero_price_different_decimals() {
let price = sqrt_price_x64_to_price(Q64, 9, 6);
let expected = Decimal::from(1000);
let diff = (price - expected).abs();
assert!(diff < Decimal::ONE, "tick 0 (9,6): expected ~1000, got {}", price);
}
#[test]
fn test_tick_positive_100() {
use crate::tick_math::tick_to_sqrt_price_x64;
let sqrt = tick_to_sqrt_price_x64(100).unwrap();
let price = sqrt_price_x64_to_price(sqrt, 6, 6);
let expected = Decimal::new(101005, 5); let diff = (price - expected).abs();
assert!(
diff < Decimal::new(1, 3), "tick +100: expected ~1.01005, got {}",
price
);
}
#[test]
fn test_tick_negative_100() {
use crate::tick_math::tick_to_sqrt_price_x64;
let sqrt = tick_to_sqrt_price_x64(-100).unwrap();
let price = sqrt_price_x64_to_price(sqrt, 6, 6);
let expected = Decimal::new(99005, 5); let diff = (price - expected).abs();
assert!(diff < Decimal::new(1, 3), "tick -100: expected ~0.99005, got {}", price);
}
#[test]
fn test_f64_variant_matches_decimal() {
use crate::tick_math::tick_to_sqrt_price_x64;
let sqrt = tick_to_sqrt_price_x64(10000).unwrap();
let price_f64 = sqrt_price_to_price(sqrt, 9, 6);
let price_dec = sqrt_price_x64_to_price(sqrt, 9, 6);
let diff = (price_dec - Decimal::try_from(price_f64).unwrap()).abs();
assert!(
diff < Decimal::ONE,
"f64 and Decimal variants diverge: f64={}, dec={}",
price_f64,
price_dec
);
}
#[test]
fn test_sol_usdc_realistic_price() {
let sqrt_price_x64: u128 = 5_832_617_287_695_007_744;
let price = sqrt_price_x64_to_price(sqrt_price_x64, 9, 6);
let expected = Decimal::from(100);
let tolerance = Decimal::from(5);
assert!((price - expected).abs() < tolerance, "SOL/USDC: expected ~100, got {}", price);
}
#[test]
fn test_price_symmetry() {
use crate::tick_math::tick_to_sqrt_price_x64;
for tick in [100, 1000, 10000] {
let sqrt_pos = tick_to_sqrt_price_x64(tick).unwrap();
let sqrt_neg = tick_to_sqrt_price_x64(-tick).unwrap();
let price_pos = sqrt_price_to_price(sqrt_pos, 6, 6);
let price_neg = sqrt_price_to_price(sqrt_neg, 6, 6);
let product = price_pos * price_neg;
assert!(
(product - 1.0).abs() < 0.001,
"symmetry broken for tick {}: product={}",
tick,
product
);
}
}
#[test]
fn test_invert_price_zero() {
assert_eq!(invert_price(0.0), 0.0);
}
#[test]
fn test_invert_price_one() {
assert!((invert_price(1.0) - 1.0).abs() < 1e-15);
}
#[test]
fn test_invert_price_typical() {
let price = 150.0; let inverted = invert_price(price);
assert!((inverted - 1.0 / 150.0).abs() < 1e-15);
assert!((invert_price(inverted) - price).abs() < 1e-10);
}
#[test]
fn test_invert_price_small() {
let price = 0.00001;
let inverted = invert_price(price);
assert!((inverted - 100_000.0).abs() < 1e-5);
}
}