myco-bonding-curve/tests/test_reserve_tranching.py

104 lines
3.5 KiB
Python

"""Tests for reserve tranching."""
import numpy as np
import pytest
from src.primitives.reserve_tranching import (
VaultMetadata, Vault, ReserveState,
current_weights, target_weights, weight_deviations,
is_safe_to_mint, is_safe_to_redeem,
optimal_deposit_split, update_flow, check_flow_limit,
)
def make_balanced_state():
"""Three vaults with equal balances and weights."""
vaults = [
Vault(
metadata=VaultMetadata(
name=f"vault_{i}", target_weight=1/3,
price_at_calibration=1.0, weight_at_calibration=1/3,
weight_previous=1/3, calibration_time=0,
transition_duration=100,
),
balance=1000.0, current_price=1.0,
)
for i in range(3)
]
return ReserveState(vaults=vaults, myco_supply=3000.0)
class TestWeights:
def test_balanced_weights(self):
state = make_balanced_state()
cw = current_weights(state)
np.testing.assert_allclose(cw, [1/3, 1/3, 1/3], atol=1e-10)
def test_target_weights_at_calibration(self):
state = make_balanced_state()
tw = target_weights(state, current_time=0)
np.testing.assert_allclose(tw, [1/3, 1/3, 1/3], atol=1e-10)
def test_weight_deviations_balanced(self):
state = make_balanced_state()
dev = weight_deviations(state, current_time=0)
np.testing.assert_allclose(dev, [0, 0, 0], atol=1e-10)
class TestSafety:
def test_proportional_deposit_safe(self):
state = make_balanced_state()
amounts = np.array([100.0, 100.0, 100.0])
safe, msg = is_safe_to_mint(state, amounts, current_time=0)
assert safe
def test_extreme_imbalance_blocked(self):
state = make_balanced_state()
# Deposit everything into vault 0 — should be blocked if already overweight
state.vaults[0].balance = 2000.0 # Make overweight
state.total_value = 4000.0
amounts = np.array([1000.0, 0.0, 0.0])
safe, msg = is_safe_to_mint(state, amounts, current_time=0)
# With max_deviation=0.1 and target=0.33, this should fail
assert not safe
def test_rebalancing_deposit_allowed(self):
state = make_balanced_state()
state.vaults[0].balance = 500.0 # Make underweight
state.total_value = 2500.0
amounts = np.array([500.0, 0.0, 0.0]) # Rebalance
safe, msg = is_safe_to_mint(state, amounts, current_time=0)
assert safe
class TestOptimal:
def test_balanced_gets_proportional(self):
state = make_balanced_state()
split = optimal_deposit_split(state, 300.0, current_time=0)
np.testing.assert_allclose(split, [100, 100, 100], atol=1e-6)
def test_underweight_gets_more(self):
state = make_balanced_state()
state.vaults[0].balance = 500.0 # Underweight
state.total_value = 2500.0
split = optimal_deposit_split(state, 500.0, current_time=0)
# Vault 0 should get the most
assert split[0] > split[1]
assert split[0] > split[2]
class TestFlow:
def test_flow_decays(self):
v = make_balanced_state().vaults[0]
v = update_flow(v, 100.0, current_time=0)
assert v.recent_flow == 100.0
v = update_flow(v, 0.0, current_time=100)
assert v.recent_flow < 100.0 # Should have decayed
def test_flow_limit_check(self):
v = make_balanced_state().vaults[0]
v = update_flow(v, 200.0, current_time=0)
ok, msg = check_flow_limit(v)
# 200 > 1000 * 0.05 = 50 → should exceed
assert not ok