myco-bonding-curve/tests/test_redemption_curve.py

103 lines
4.0 KiB
Python

"""Tests for P-AMM redemption curve."""
import numpy as np
import pytest
from src.primitives.redemption_curve import (
PAMMParams, PAMMState,
compute_redemption_rate, redeem,
backing_ratio_trajectory,
)
class TestRedemptionRate:
def test_fully_backed_redeems_at_par(self):
"""When ba >= 1, rate should be 1.0."""
state = PAMMState(reserve_value=10000, myco_supply=10000)
params = PAMMParams()
rate = compute_redemption_rate(state, params, 100.0)
assert abs(rate - 1.0) < 1e-10
def test_overbacked_redeems_at_par(self):
"""When ba > 1, rate should be 1.0 (not more)."""
state = PAMMState(reserve_value=15000, myco_supply=10000)
params = PAMMParams()
rate = compute_redemption_rate(state, params, 100.0)
assert abs(rate - 1.0) < 1e-10
def test_underbacked_gives_discount(self):
"""When ba < 1, rate should be less than 1."""
state = PAMMState(reserve_value=8000, myco_supply=10000)
params = PAMMParams()
rate = compute_redemption_rate(state, params, 100.0)
assert rate < 1.0
assert rate > 0
def test_deeply_underbacked_hits_floor(self):
"""Very low backing should approach θ̄."""
state = PAMMState(reserve_value=100, myco_supply=10000)
params = PAMMParams(theta_bar=0.5)
rate = compute_redemption_rate(state, params, 100.0)
assert rate >= params.theta_bar
def test_rate_bounded(self):
"""Rate should always be in [θ̄, 1.0]."""
params = PAMMParams(theta_bar=0.3)
for ba in [0.01, 0.1, 0.3, 0.5, 0.7, 0.9, 1.0, 1.5]:
state = PAMMState(
reserve_value=ba * 10000,
myco_supply=10000,
)
rate = compute_redemption_rate(state, params, 100.0)
assert params.theta_bar <= rate <= 1.0
class TestRedeem:
def test_redeem_reduces_reserve(self):
state = PAMMState(reserve_value=10000, myco_supply=10000)
params = PAMMParams()
new_state, usd_out = redeem(state, params, 100.0, current_time=0)
assert new_state.reserve_value < state.reserve_value
assert new_state.myco_supply < state.myco_supply
assert usd_out > 0
def test_redeem_never_exceeds_reserve(self):
"""Should never return more USD than available."""
state = PAMMState(reserve_value=50, myco_supply=10000)
params = PAMMParams()
new_state, usd_out = redeem(state, params, 5000.0, current_time=0)
assert usd_out <= 50
assert new_state.reserve_value >= 0
def test_sequential_redemptions_degrade(self):
"""Sequential redemptions should get progressively worse rates."""
state = PAMMState(reserve_value=8000, myco_supply=10000)
params = PAMMParams()
rates = []
for t in range(5):
rate = compute_redemption_rate(state, params, 500.0)
state, _ = redeem(state, params, 500.0, current_time=float(t))
rates.append(rate)
# Later redemptions should get same or worse rate
# (backing ratio decreases)
assert all(rates[i] >= rates[i+1] - 0.01 for i in range(len(rates)-1))
class TestTrajectory:
def test_trajectory_length(self):
state = PAMMState(reserve_value=10000, myco_supply=10000)
params = PAMMParams()
schedule = [(float(t), 100.0) for t in range(10)]
traj = backing_ratio_trajectory(state, params, schedule)
assert len(traj) == 10
def test_trajectory_backing_decreases(self):
"""Backing ratio should decrease with continuous redemptions."""
state = PAMMState(reserve_value=10000, myco_supply=10000)
params = PAMMParams()
schedule = [(float(t), 200.0) for t in range(10)]
traj = backing_ratio_trajectory(state, params, schedule)
backing_ratios = [t[1] for t in traj]
# Each step should have lower or equal backing
for i in range(len(backing_ratios) - 1):
assert backing_ratios[i+1] <= backing_ratios[i] + 1e-10