97 lines
3.1 KiB
Python
97 lines
3.1 KiB
Python
"""Tests for 2-CLP concentrated liquidity pool."""
|
||
|
||
import numpy as np
|
||
import pytest
|
||
from src.primitives.concentrated_2clp import (
|
||
compute_invariant,
|
||
virtual_offset_x,
|
||
virtual_offset_y,
|
||
calc_out_given_in,
|
||
calc_in_given_out,
|
||
spot_price,
|
||
price_bounds,
|
||
)
|
||
|
||
|
||
class TestInvariant:
|
||
def test_balanced_pool(self):
|
||
"""Balanced pool should have positive invariant."""
|
||
L = compute_invariant(1000.0, 1000.0, 0.9, 1.1)
|
||
assert L > 0
|
||
|
||
def test_invariant_increases_with_reserves(self):
|
||
"""More reserves → larger invariant."""
|
||
L1 = compute_invariant(100.0, 100.0, 0.9, 1.1)
|
||
L2 = compute_invariant(200.0, 200.0, 0.9, 1.1)
|
||
assert L2 > L1
|
||
|
||
def test_narrow_range_higher_invariant(self):
|
||
"""Narrower price range → higher L for same balances (more concentrated)."""
|
||
L_wide = compute_invariant(1000.0, 1000.0, 0.5, 2.0)
|
||
L_narrow = compute_invariant(1000.0, 1000.0, 0.95, 1.05)
|
||
assert L_narrow > L_wide
|
||
|
||
def test_virtual_reserves_satisfy_cpmm(self):
|
||
"""(x + a)(y + b) should equal L²."""
|
||
x, y = 500.0, 800.0
|
||
sa, sb = 0.8, 1.2
|
||
L = compute_invariant(x, y, sa, sb)
|
||
a = virtual_offset_x(L, sb)
|
||
b = virtual_offset_y(L, sa)
|
||
product = (x + a) * (y + b)
|
||
assert abs(product - L**2) < 1e-6
|
||
|
||
|
||
class TestSwaps:
|
||
def test_invariant_preserved(self):
|
||
"""Swap should preserve L."""
|
||
x, y = 1000.0, 1000.0
|
||
sa, sb = 0.9, 1.1
|
||
|
||
L_before = compute_invariant(x, y, sa, sb)
|
||
dy = calc_out_given_in(x, y, sa, sb, 100.0, token_in=0)
|
||
L_after = compute_invariant(x + 100.0, y - dy, sa, sb)
|
||
|
||
assert abs(L_after - L_before) / L_before < 1e-10
|
||
|
||
def test_round_trip(self):
|
||
"""calc_in_given_out inverts calc_out_given_in."""
|
||
x, y = 1000.0, 1000.0
|
||
sa, sb = 0.9, 1.1
|
||
|
||
amount_in = 50.0
|
||
amount_out = calc_out_given_in(x, y, sa, sb, amount_in, token_in=0)
|
||
recovered = calc_in_given_out(x, y, sa, sb, amount_out, token_out=1)
|
||
assert abs(recovered - amount_in) < 1e-8
|
||
|
||
def test_spot_price_in_range(self):
|
||
"""Spot price should be within [α, β]."""
|
||
x, y = 1000.0, 1000.0
|
||
sa, sb = 0.9, 1.1
|
||
sp = spot_price(x, y, sa, sb)
|
||
alpha, beta = price_bounds(sa, sb)
|
||
assert alpha <= sp <= beta
|
||
|
||
def test_concentrated_less_slippage(self):
|
||
"""Narrower range = less slippage for same-size swap."""
|
||
x, y = 1000.0, 1000.0
|
||
# Narrow range
|
||
out_narrow = calc_out_given_in(x, y, 0.95, 1.05, 50.0)
|
||
# Wide range
|
||
out_wide = calc_out_given_in(x, y, 0.5, 2.0, 50.0)
|
||
# Narrow range should give better price (more output)
|
||
assert out_narrow > out_wide
|
||
|
||
def test_both_directions(self):
|
||
"""Swap in both directions should work."""
|
||
x, y = 1000.0, 1000.0
|
||
sa, sb = 0.9, 1.1
|
||
|
||
# x → y
|
||
dy = calc_out_given_in(x, y, sa, sb, 100.0, token_in=0)
|
||
assert dy > 0
|
||
|
||
# y → x
|
||
dx = calc_out_given_in(x, y, sa, sb, 100.0, token_in=1)
|
||
assert dx > 0
|