138 lines
3.9 KiB
Python
138 lines
3.9 KiB
Python
|
|
"""Tests for trade safety limits and validations."""
|
||
|
|
|
||
|
|
from decimal import Decimal
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
from data.common.transformation.safety import (
|
||
|
|
TradeLimits,
|
||
|
|
DEFAULT_LIMITS,
|
||
|
|
STABLECOIN_LIMITS,
|
||
|
|
VOLATILE_LIMITS,
|
||
|
|
is_stablecoin_pair,
|
||
|
|
get_trade_limits,
|
||
|
|
validate_trade_size,
|
||
|
|
validate_trade_price,
|
||
|
|
validate_symbol_format
|
||
|
|
)
|
||
|
|
|
||
|
|
def test_stablecoin_detection():
|
||
|
|
"""Test stablecoin pair detection."""
|
||
|
|
# Test stablecoin pairs
|
||
|
|
assert is_stablecoin_pair('BTC-USDT')
|
||
|
|
assert is_stablecoin_pair('ETH/USDC')
|
||
|
|
assert is_stablecoin_pair('USDT-BTC')
|
||
|
|
assert is_stablecoin_pair('DAI/ETH')
|
||
|
|
|
||
|
|
# Test non-stablecoin pairs
|
||
|
|
assert not is_stablecoin_pair('BTC-ETH')
|
||
|
|
assert not is_stablecoin_pair('LTC/XRP')
|
||
|
|
assert not is_stablecoin_pair('DOT-SOL')
|
||
|
|
|
||
|
|
def test_get_trade_limits():
|
||
|
|
"""Test trade limits selection."""
|
||
|
|
# Stablecoin pairs should get higher limits
|
||
|
|
assert get_trade_limits('BTC-USDT') == STABLECOIN_LIMITS
|
||
|
|
assert get_trade_limits('ETH/USDC') == STABLECOIN_LIMITS
|
||
|
|
|
||
|
|
# Other pairs should get volatile limits
|
||
|
|
assert get_trade_limits('BTC-ETH') == VOLATILE_LIMITS
|
||
|
|
assert get_trade_limits('LTC/XRP') == VOLATILE_LIMITS
|
||
|
|
|
||
|
|
def test_validate_trade_size():
|
||
|
|
"""Test trade size validation."""
|
||
|
|
# Valid sizes should pass
|
||
|
|
validate_trade_size(
|
||
|
|
Decimal('1.0'),
|
||
|
|
Decimal('50000'),
|
||
|
|
'BTC-USDT'
|
||
|
|
)
|
||
|
|
|
||
|
|
# Test minimum size
|
||
|
|
with pytest.raises(ValueError, match='below minimum'):
|
||
|
|
validate_trade_size(
|
||
|
|
Decimal('0.000000001'),
|
||
|
|
Decimal('50000'),
|
||
|
|
'BTC-USDT'
|
||
|
|
)
|
||
|
|
|
||
|
|
# Test maximum size
|
||
|
|
with pytest.raises(ValueError, match='exceeds maximum'):
|
||
|
|
validate_trade_size(
|
||
|
|
Decimal('2000000'),
|
||
|
|
Decimal('50000'),
|
||
|
|
'BTC-USDT'
|
||
|
|
)
|
||
|
|
|
||
|
|
# Test minimum notional
|
||
|
|
with pytest.raises(ValueError, match='below minimum'):
|
||
|
|
validate_trade_size(
|
||
|
|
Decimal('0.00001'),
|
||
|
|
Decimal('10'),
|
||
|
|
'BTC-USDT'
|
||
|
|
)
|
||
|
|
|
||
|
|
# Test maximum notional
|
||
|
|
with pytest.raises(ValueError, match='exceeds maximum'):
|
||
|
|
validate_trade_size(
|
||
|
|
Decimal('1000'),
|
||
|
|
Decimal('1000000'),
|
||
|
|
'BTC-USDT'
|
||
|
|
)
|
||
|
|
|
||
|
|
def test_validate_trade_price():
|
||
|
|
"""Test trade price validation."""
|
||
|
|
# Valid prices should pass
|
||
|
|
validate_trade_price(
|
||
|
|
Decimal('50000'),
|
||
|
|
Decimal('49000'),
|
||
|
|
'BTC-USDT'
|
||
|
|
)
|
||
|
|
|
||
|
|
# Test maximum deviation for stablecoins
|
||
|
|
with pytest.raises(ValueError, match='deviation'):
|
||
|
|
validate_trade_price(
|
||
|
|
Decimal('1.10'),
|
||
|
|
Decimal('1.00'),
|
||
|
|
'USDT-USDC' # 10% deviation exceeds 5% limit
|
||
|
|
)
|
||
|
|
|
||
|
|
# Test maximum deviation for volatile pairs
|
||
|
|
with pytest.raises(ValueError, match='deviation'):
|
||
|
|
validate_trade_price(
|
||
|
|
Decimal('60000'),
|
||
|
|
Decimal('30000'),
|
||
|
|
'BTC-ETH' # 100% deviation exceeds 50% limit
|
||
|
|
)
|
||
|
|
|
||
|
|
# None market price should be handled
|
||
|
|
validate_trade_price(
|
||
|
|
Decimal('50000'),
|
||
|
|
None,
|
||
|
|
'BTC-USDT'
|
||
|
|
)
|
||
|
|
|
||
|
|
def test_validate_symbol_format():
|
||
|
|
"""Test symbol format validation."""
|
||
|
|
# Valid formats should pass
|
||
|
|
validate_symbol_format('BTC-USDT')
|
||
|
|
validate_symbol_format('ETH/USDC')
|
||
|
|
validate_symbol_format('LTC-BTC')
|
||
|
|
|
||
|
|
# Test invalid formats
|
||
|
|
with pytest.raises(ValueError):
|
||
|
|
validate_symbol_format('') # Empty
|
||
|
|
|
||
|
|
with pytest.raises(ValueError):
|
||
|
|
validate_symbol_format('BTCUSDT') # No separator
|
||
|
|
|
||
|
|
with pytest.raises(ValueError):
|
||
|
|
validate_symbol_format('BTC_USDT') # Wrong separator
|
||
|
|
|
||
|
|
with pytest.raises(ValueError):
|
||
|
|
validate_symbol_format('BTC-USD-T') # Too many parts
|
||
|
|
|
||
|
|
with pytest.raises(ValueError):
|
||
|
|
validate_symbol_format('a-b') # Too short
|
||
|
|
|
||
|
|
with pytest.raises(ValueError):
|
||
|
|
validate_symbol_format('VERYLONGTOKEN-BTC') # Too long
|