use serde::{Deserialize, Serialize};
pub trait Curve {
fn name(&self) -> &'static str;
fn price_of_bin(&self, i: i64) -> f64;
fn delta_x_of_bin(&self, i: i64) -> f64;
fn cumulative_supply(&self, n: i64) -> f64 {
let mut s = 0.0;
for i in 0..n {
s += self.delta_x_of_bin(i);
}
s
}
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct Grid {
pub p0: f64,
pub bin_step_bps: f64,
}
impl Grid {
pub fn q(&self) -> f64 {
1.0 + self.bin_step_bps / 10_000.0
}
pub fn price_of_bin(&self, i: i64) -> f64 {
self.p0 * self.q().powi(i as i32)
}
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct Geometric {
pub grid: Grid,
pub theta: f64,
pub r0_quote: f64,
}
impl Geometric {
pub fn r(&self) -> f64 {
self.grid.q().powf(self.theta - 1.0)
}
pub fn g(&self) -> f64 {
self.grid.q().powf(self.theta)
}
pub fn delta_x0(&self) -> f64 {
self.r0_quote / self.grid.p0
}
pub fn s_n_closed(&self, n: i64) -> f64 {
let r = self.r();
if (r - 1.0).abs() < 1e-12 {
self.delta_x0() * n as f64
} else {
self.delta_x0() * (1.0 - r.powi(n as i32)) / (1.0 - r)
}
}
pub fn solve_r0_from_supply(&self, target_s: f64, n: i64) -> f64 {
let r = self.r();
if (r - 1.0).abs() < 1e-12 {
(target_s / (n as f64)) * self.grid.p0
} else {
let denom = 1.0 - r.powi(n as i32);
let a = target_s * (1.0 - r) / denom;
a * self.grid.p0
}
}
}
impl Curve for Geometric {
fn name(&self) -> &'static str {
"DLMM-Geometric(θ)"
}
fn price_of_bin(&self, i: i64) -> f64 {
self.grid.price_of_bin(i)
}
fn delta_x_of_bin(&self, i: i64) -> f64 {
self.delta_x0() * self.r().powi(i as i32)
}
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct LogisticS {
pub grid: Grid,
pub p_min: f64,
pub p_max: f64,
pub k: f64,
pub s_mid: f64,
pub bins: i64,
}
impl LogisticS {
fn s_of_p(&self, p: f64) -> f64 {
let eps = (self.p_max - self.p_min) * 1e-12;
let p = p.clamp(self.p_min + eps, self.p_max - eps);
let num = self.p_max - p;
let den = p - self.p_min;
self.s_mid - (num / den).ln() / self.k
}
fn s_i(&self, i: i64) -> f64 {
self.s_of_p(self.grid.price_of_bin(i))
}
}
impl Curve for LogisticS {
fn name(&self) -> &'static str {
"Logistic-S(on DLMM bins)"
}
fn price_of_bin(&self, i: i64) -> f64 {
self.grid.price_of_bin(i)
}
fn delta_x_of_bin(&self, i: i64) -> f64 {
if i + 1 >= self.bins {
return 0.0;
}
let s_i = self.s_i(i);
let s_ip1 = self.s_i(i + 1);
(s_ip1 - s_i).max(0.0)
}
}