use crate::math::concentrated_liquidity;
use rust_decimal::Decimal;
use rust_decimal::prelude::*;
pub fn calculate_il_constant_product(
entry_price: Decimal,
current_price: Decimal,
) -> Result<Decimal, &'static str> {
if entry_price.is_zero() {
return Err("Entry price cannot be zero");
}
let price_ratio = current_price / entry_price;
let ratio_f64 = price_ratio.to_f64().ok_or("Overflow converting to f64")?;
let sqrt_ratio = ratio_f64.sqrt();
let numerator = 2.0 * sqrt_ratio;
let denominator = 1.0 + ratio_f64;
let result_f64 = (numerator / denominator) - 1.0;
Decimal::from_f64(result_f64).ok_or("Overflow converting result")
}
pub fn calculate_il_concentrated(
entry_price: Decimal,
current_price: Decimal,
price_lower: Decimal,
price_upper: Decimal,
) -> Result<Decimal, &'static str> {
if entry_price.is_zero() || price_lower.is_zero() || price_upper.is_zero() {
return Err("Prices must be non-zero");
}
if price_lower >= price_upper {
return Err("Invalid range");
}
let liquidity = 1_000_000_000_000_000_000u128;
let sqrt = |p: Decimal| -> Result<Decimal, &'static str> {
let f = p.to_f64().ok_or("Overflow")?;
Decimal::from_f64(f.sqrt()).ok_or("Overflow")
};
let sqrt_entry = sqrt(entry_price)?;
let sqrt_curr = sqrt(current_price)?;
let sqrt_lower = sqrt(price_lower)?;
let sqrt_upper = sqrt(price_upper)?;
let get_amounts = |p_sqrt: Decimal| -> Result<(Decimal, Decimal), &'static str> {
let mut amt0 = Decimal::ZERO;
let mut amt1 = Decimal::ZERO;
if p_sqrt < sqrt_lower {
let a0 = concentrated_liquidity::get_amount0_delta(liquidity, sqrt_lower, sqrt_upper)?;
amt0 = Decimal::from_str(&a0.0.to_string()).unwrap();
} else if p_sqrt >= sqrt_upper {
let a1 = concentrated_liquidity::get_amount1_delta(liquidity, sqrt_lower, sqrt_upper)?;
amt1 = Decimal::from_str(&a1.0.to_string()).unwrap();
} else {
let a0 = concentrated_liquidity::get_amount0_delta(liquidity, p_sqrt, sqrt_upper)?;
amt0 = Decimal::from_str(&a0.0.to_string()).unwrap();
let a1 = concentrated_liquidity::get_amount1_delta(liquidity, sqrt_lower, p_sqrt)?;
amt1 = Decimal::from_str(&a1.0.to_string()).unwrap();
}
Ok((amt0, amt1))
};
let (x0, y0) = get_amounts(sqrt_entry)?;
let (x1, y1) = get_amounts(sqrt_curr)?;
let value_held = x0 * current_price + y0;
let value_lp = x1 * current_price + y1;
if value_held.is_zero() {
return Ok(Decimal::ZERO);
}
let il = (value_lp - value_held) / value_held;
Ok(il)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_calculate_il_constant_product() {
let entry = Decimal::from(100);
let curr = Decimal::from(200);
let il = calculate_il_constant_product(entry, curr).unwrap();
let expected = Decimal::from_f64(-0.05719).unwrap();
let diff = (il - expected).abs();
assert!(diff < Decimal::from_f64(0.0001).unwrap());
}
#[test]
fn test_calculate_il_concentrated() {
let entry = Decimal::from(100);
let curr = Decimal::from(100);
let lower = Decimal::from(90);
let upper = Decimal::from(110);
let il = calculate_il_concentrated(entry, curr, lower, upper).unwrap();
assert!(il.abs() < Decimal::from_f64(0.000001).unwrap());
let curr_up = Decimal::from(105);
let il_up = calculate_il_concentrated(entry, curr_up, lower, upper).unwrap();
assert!(il_up < Decimal::ZERO);
}
}