137 lines
4.3 KiB
Python
137 lines
4.3 KiB
Python
"""Tests for E-CLP elliptical concentrated liquidity pool."""
|
|
|
|
import numpy as np
|
|
import pytest
|
|
from src.primitives.elliptical_clp import (
|
|
ECLPParams,
|
|
compute_derived_params,
|
|
compute_invariant,
|
|
calc_out_given_in,
|
|
calc_in_given_out,
|
|
spot_price,
|
|
virtual_offsets,
|
|
)
|
|
|
|
|
|
def make_default_params():
|
|
"""Standard E-CLP params for testing (near-stable pair)."""
|
|
return ECLPParams(
|
|
alpha=0.97,
|
|
beta=1.03,
|
|
c=1.0, # No rotation
|
|
s=0.0,
|
|
lam=10.0, # Moderate concentration
|
|
)
|
|
|
|
|
|
def make_rotated_params():
|
|
"""E-CLP with rotation (non-unit price target)."""
|
|
phi = np.pi / 6 # 30 degrees
|
|
return ECLPParams(
|
|
alpha=0.8,
|
|
beta=1.2,
|
|
c=np.cos(phi),
|
|
s=np.sin(phi),
|
|
lam=5.0,
|
|
)
|
|
|
|
|
|
class TestInvariant:
|
|
def test_positive_invariant(self):
|
|
p = make_default_params()
|
|
dp = compute_derived_params(p)
|
|
r = compute_invariant(1000.0, 1000.0, p, dp)
|
|
assert r > 0
|
|
|
|
def test_invariant_scales(self):
|
|
"""Invariant should be approximately homogeneous degree 1."""
|
|
p = make_default_params()
|
|
dp = compute_derived_params(p)
|
|
r1 = compute_invariant(1000.0, 1000.0, p, dp)
|
|
r2 = compute_invariant(2000.0, 2000.0, p, dp)
|
|
# Should be approximately 2x
|
|
assert abs(r2 / r1 - 2.0) < 0.1 # Allow some tolerance
|
|
|
|
def test_rotated_params(self):
|
|
"""Rotated E-CLP should work."""
|
|
p = make_rotated_params()
|
|
dp = compute_derived_params(p)
|
|
r = compute_invariant(1000.0, 1000.0, p, dp)
|
|
assert r > 0
|
|
|
|
|
|
class TestSwaps:
|
|
def test_invariant_preserved(self):
|
|
"""Swap preserves invariant."""
|
|
p = make_default_params()
|
|
dp = compute_derived_params(p)
|
|
x, y = 1000.0, 1000.0
|
|
|
|
r_before = compute_invariant(x, y, p, dp)
|
|
dy = calc_out_given_in(x, y, p, dp, 50.0, token_in=0)
|
|
r_after = compute_invariant(x + 50.0, y - dy, p, dp)
|
|
|
|
assert abs(r_after - r_before) / r_before < 1e-6
|
|
|
|
def test_round_trip(self):
|
|
"""calc_in_given_out inverts calc_out_given_in."""
|
|
p = make_default_params()
|
|
dp = compute_derived_params(p)
|
|
|
|
amount_in = 30.0
|
|
amount_out = calc_out_given_in(1000.0, 1000.0, p, dp, amount_in, token_in=0)
|
|
recovered = calc_in_given_out(1000.0, 1000.0, p, dp, amount_out, token_out=1)
|
|
assert abs(recovered - amount_in) < 1e-4
|
|
|
|
def test_lambda_changes_curve_shape(self):
|
|
"""Higher λ should produce different swap outputs (curve shape change)."""
|
|
p_lo = ECLPParams(0.5, 2.0, 1.0, 0.0, 1.0)
|
|
p_hi = ECLPParams(0.5, 2.0, 1.0, 0.0, 20.0)
|
|
dp_lo = compute_derived_params(p_lo)
|
|
dp_hi = compute_derived_params(p_hi)
|
|
|
|
out_lo = calc_out_given_in(1000.0, 1000.0, p_lo, dp_lo, 50.0)
|
|
out_hi = calc_out_given_in(1000.0, 1000.0, p_hi, dp_hi, 50.0)
|
|
|
|
# λ should meaningfully change the output (different curve geometry)
|
|
assert out_lo != out_hi
|
|
assert abs(out_lo - out_hi) / out_lo > 0.001 # At least 0.1% difference
|
|
|
|
def test_rotated_swap(self):
|
|
"""Swap works with rotation."""
|
|
p = make_rotated_params()
|
|
dp = compute_derived_params(p)
|
|
dy = calc_out_given_in(1000.0, 1000.0, p, dp, 50.0)
|
|
assert dy > 0
|
|
|
|
def test_spot_price_positive(self):
|
|
"""Spot price should be positive."""
|
|
p = make_default_params()
|
|
dp = compute_derived_params(p)
|
|
sp = spot_price(1000.0, 1000.0, p, dp)
|
|
assert sp > 0
|
|
|
|
|
|
class TestDerivedParams:
|
|
def test_tau_normalized(self):
|
|
"""tau vectors should be approximately unit length."""
|
|
p = make_default_params()
|
|
dp = compute_derived_params(p)
|
|
assert abs(np.linalg.norm(dp.tau_alpha) - 1.0) < 1e-10
|
|
assert abs(np.linalg.norm(dp.tau_beta) - 1.0) < 1e-10
|
|
|
|
def test_dSq_near_one(self):
|
|
"""dSq = c² + s² should be ≈ 1."""
|
|
p = make_default_params()
|
|
dp = compute_derived_params(p)
|
|
assert abs(dp.dSq - 1.0) < 1e-10
|
|
|
|
def test_virtual_offsets_positive(self):
|
|
"""Virtual offsets should be positive for typical params."""
|
|
p = make_default_params()
|
|
dp = compute_derived_params(p)
|
|
r = compute_invariant(1000.0, 1000.0, p, dp)
|
|
a, b = virtual_offsets(r, p, dp)
|
|
assert a > 0
|
|
assert b > 0
|