use crate::arithmetic::Fx;
#[derive(Clone, Copy, Debug)]
pub struct Reserves {
pub s_yes: Fx,
pub s_no: Fx,
}
fn hypot(a: Fx, b: Fx) -> Fx {
(a * a + b * b).sqrt()
}
pub fn cost(r: Reserves, lambda: Fx) -> Fx {
lambda * hypot(r.s_yes, r.s_no)
}
pub fn price_yes(r: Reserves, lambda: Fx) -> Fx {
lambda * r.s_yes.div(hypot(r.s_yes, r.s_no))
}
pub fn price_no(r: Reserves, lambda: Fx) -> Fx {
lambda * r.s_no.div(hypot(r.s_yes, r.s_no))
}
#[cfg(test)]
mod tests {
use super::*;
fn r(yes: i64, no: i64) -> Reserves {
Reserves {
s_yes: Fx::from_int(yes),
s_no: Fx::from_int(no),
}
}
#[test]
fn zero_reserves_price_at_zero() {
let lambda = Fx::ONE;
assert_eq!(cost(r(0, 0), lambda).to_f64(), 0.0);
assert_eq!(price_yes(r(0, 0), lambda).to_f64(), 0.0);
assert_eq!(price_no(r(0, 0), lambda).to_f64(), 0.0);
}
#[test]
fn cost_grows_with_traded_volume() {
let lambda = Fx::ONE;
let c_small = cost(r(1, 1), lambda).to_f64();
let c_large = cost(r(10, 10), lambda).to_f64();
assert!(
c_large > c_small,
"TVL should grow with reserves: {c_small} vs {c_large}"
);
}
#[test]
fn prices_stay_on_the_lambda_circle() {
let lambda = Fx::from_int(2);
for (yes, no) in [(1, 1), (5, 1), (1, 5), (7, 3), (100, 1)] {
let py = price_yes(r(yes, no), lambda).to_f64();
let pn = price_no(r(yes, no), lambda).to_f64();
let sum_sq = py * py + pn * pn;
let target = lambda.to_f64() * lambda.to_f64();
assert!(
(sum_sq - target).abs() < 1e-3,
"({yes},{no}): p_YES^2+p_NO^2 = {sum_sq}, want {target}"
);
}
}
#[test]
fn prices_bounded_by_lambda() {
let lambda = Fx::from_int(3);
for (yes, no) in [(1, 0), (0, 1), (1, 1), (1000, 1), (1, 1000)] {
let py = price_yes(r(yes, no), lambda).to_f64();
let pn = price_no(r(yes, no), lambda).to_f64();
assert!((0.0..=lambda.to_f64() + 1e-6).contains(&py), "p_YES={py}");
assert!((0.0..=lambda.to_f64() + 1e-6).contains(&pn), "p_NO={pn}");
}
}
#[test]
fn symmetric_reserves_split_price_evenly() {
let lambda = Fx::ONE;
let py = price_yes(r(9, 9), lambda).to_f64();
let pn = price_no(r(9, 9), lambda).to_f64();
assert!((py - pn).abs() < 1e-3, "p_YES={py}, p_NO={pn}");
let expect = lambda.to_f64() / std::f64::consts::SQRT_2;
assert!((py - expect).abs() < 1e-3, "p_YES={py}, want {expect}");
}
#[test]
fn buying_yes_raises_its_price_and_suppresses_no() {
let lambda = Fx::ONE;
let before = r(5, 5);
let after = r(9, 5);
let py_before = price_yes(before, lambda).to_f64();
let py_after = price_yes(after, lambda).to_f64();
let pn_before = price_no(before, lambda).to_f64();
let pn_after = price_no(after, lambda).to_f64();
assert!(
py_after > py_before,
"buying YES should raise its own price: {py_before} -> {py_after}"
);
assert!(
pn_after < pn_before,
"buying YES should suppress NO's price: {pn_before} -> {pn_after}"
);
}
}