162 lines
6.4 KiB
Python
162 lines
6.4 KiB
Python
|
|
"""Tests for strategy grouping module."""
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
from src.autopilot.strategy_groups import (
|
||
|
|
StrategyGroup,
|
||
|
|
STRATEGY_TO_GROUP,
|
||
|
|
GROUP_TO_STRATEGIES,
|
||
|
|
get_strategy_group,
|
||
|
|
get_strategies_in_group,
|
||
|
|
get_all_groups,
|
||
|
|
get_best_strategy_in_group,
|
||
|
|
convert_strategy_to_group_label,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
class TestStrategyGroupMappings:
|
||
|
|
"""Tests for strategy group mappings."""
|
||
|
|
|
||
|
|
def test_all_strategies_have_group(self):
|
||
|
|
"""Verify all registered strategies are mapped to a group."""
|
||
|
|
# These are the strategies registered in src/strategies/__init__.py
|
||
|
|
expected_strategies = [
|
||
|
|
"rsi", "macd", "moving_average", "confirmed", "divergence",
|
||
|
|
"bollinger_mean_reversion", "dca", "grid", "momentum",
|
||
|
|
"consensus", "pairs_trading", "volatility_breakout",
|
||
|
|
"sentiment", "market_making"
|
||
|
|
]
|
||
|
|
|
||
|
|
for strategy in expected_strategies:
|
||
|
|
group = get_strategy_group(strategy)
|
||
|
|
assert group is not None, f"Strategy '{strategy}' is not mapped to any group"
|
||
|
|
|
||
|
|
def test_get_strategy_group_case_insensitive(self):
|
||
|
|
"""Test that strategy lookup is case-insensitive."""
|
||
|
|
assert get_strategy_group("RSI") == get_strategy_group("rsi")
|
||
|
|
assert get_strategy_group("MACD") == get_strategy_group("macd")
|
||
|
|
assert get_strategy_group("Moving_Average") == get_strategy_group("moving_average")
|
||
|
|
|
||
|
|
def test_get_strategy_group_unknown(self):
|
||
|
|
"""Test that unknown strategies return None."""
|
||
|
|
assert get_strategy_group("nonexistent_strategy") is None
|
||
|
|
assert get_strategy_group("") is None
|
||
|
|
|
||
|
|
def test_group_to_strategies_reverse_mapping(self):
|
||
|
|
"""Verify GROUP_TO_STRATEGIES is the reverse of STRATEGY_TO_GROUP."""
|
||
|
|
for strategy, group in STRATEGY_TO_GROUP.items():
|
||
|
|
assert strategy in GROUP_TO_STRATEGIES[group]
|
||
|
|
|
||
|
|
def test_all_groups_have_strategies(self):
|
||
|
|
"""Verify all groups have at least one strategy."""
|
||
|
|
for group in StrategyGroup:
|
||
|
|
strategies = get_strategies_in_group(group)
|
||
|
|
assert len(strategies) > 0, f"Group '{group}' has no strategies"
|
||
|
|
|
||
|
|
|
||
|
|
class TestGetAllGroups:
|
||
|
|
"""Tests for get_all_groups function."""
|
||
|
|
|
||
|
|
def test_returns_all_groups(self):
|
||
|
|
"""Verify all groups are returned."""
|
||
|
|
groups = get_all_groups()
|
||
|
|
assert len(groups) == 5
|
||
|
|
assert StrategyGroup.TREND_FOLLOWING in groups
|
||
|
|
assert StrategyGroup.MEAN_REVERSION in groups
|
||
|
|
assert StrategyGroup.MOMENTUM in groups
|
||
|
|
assert StrategyGroup.MARKET_MAKING in groups
|
||
|
|
assert StrategyGroup.SENTIMENT_BASED in groups
|
||
|
|
|
||
|
|
|
||
|
|
class TestGetStrategiesInGroup:
|
||
|
|
"""Tests for get_strategies_in_group function."""
|
||
|
|
|
||
|
|
def test_trend_following_strategies(self):
|
||
|
|
"""Test trend following group contains expected strategies."""
|
||
|
|
strategies = get_strategies_in_group(StrategyGroup.TREND_FOLLOWING)
|
||
|
|
assert "moving_average" in strategies
|
||
|
|
assert "macd" in strategies
|
||
|
|
assert "confirmed" in strategies
|
||
|
|
|
||
|
|
def test_mean_reversion_strategies(self):
|
||
|
|
"""Test mean reversion group contains expected strategies."""
|
||
|
|
strategies = get_strategies_in_group(StrategyGroup.MEAN_REVERSION)
|
||
|
|
assert "rsi" in strategies
|
||
|
|
assert "bollinger_mean_reversion" in strategies
|
||
|
|
assert "grid" in strategies
|
||
|
|
|
||
|
|
def test_momentum_strategies(self):
|
||
|
|
"""Test momentum group contains expected strategies."""
|
||
|
|
strategies = get_strategies_in_group(StrategyGroup.MOMENTUM)
|
||
|
|
assert "momentum" in strategies
|
||
|
|
assert "volatility_breakout" in strategies
|
||
|
|
|
||
|
|
|
||
|
|
class TestGetBestStrategyInGroup:
|
||
|
|
"""Tests for get_best_strategy_in_group function."""
|
||
|
|
|
||
|
|
def test_trend_following_high_adx(self):
|
||
|
|
"""Test trend following selection with high ADX."""
|
||
|
|
features = {"adx": 35.0, "rsi": 50.0, "atr_percent": 2.0, "volume_ratio": 1.0}
|
||
|
|
strategy, confidence = get_best_strategy_in_group(
|
||
|
|
StrategyGroup.TREND_FOLLOWING, features
|
||
|
|
)
|
||
|
|
assert strategy == "confirmed"
|
||
|
|
assert confidence > 0.5
|
||
|
|
|
||
|
|
def test_mean_reversion_extreme_rsi(self):
|
||
|
|
"""Test mean reversion selection with extreme RSI."""
|
||
|
|
features = {"adx": 15.0, "rsi": 25.0, "atr_percent": 1.5, "volume_ratio": 1.0}
|
||
|
|
strategy, confidence = get_best_strategy_in_group(
|
||
|
|
StrategyGroup.MEAN_REVERSION, features
|
||
|
|
)
|
||
|
|
assert strategy == "rsi"
|
||
|
|
assert confidence > 0.5
|
||
|
|
|
||
|
|
def test_momentum_high_volume(self):
|
||
|
|
"""Test momentum selection with high volume."""
|
||
|
|
features = {"adx": 25.0, "rsi": 55.0, "atr_percent": 3.0, "volume_ratio": 2.0}
|
||
|
|
strategy, confidence = get_best_strategy_in_group(
|
||
|
|
StrategyGroup.MOMENTUM, features
|
||
|
|
)
|
||
|
|
assert strategy == "volatility_breakout"
|
||
|
|
assert confidence > 0.5
|
||
|
|
|
||
|
|
def test_respects_available_strategies(self):
|
||
|
|
"""Test that only available strategies are selected."""
|
||
|
|
features = {"adx": 35.0, "rsi": 50.0}
|
||
|
|
# Only MACD available from trend following
|
||
|
|
strategy, confidence = get_best_strategy_in_group(
|
||
|
|
StrategyGroup.TREND_FOLLOWING,
|
||
|
|
features,
|
||
|
|
available_strategies=["macd"]
|
||
|
|
)
|
||
|
|
assert strategy == "macd"
|
||
|
|
|
||
|
|
def test_fallback_when_no_strategies_available(self):
|
||
|
|
"""Test fallback when no strategies in group are available."""
|
||
|
|
features = {"adx": 25.0, "rsi": 50.0}
|
||
|
|
strategy, confidence = get_best_strategy_in_group(
|
||
|
|
StrategyGroup.TREND_FOLLOWING,
|
||
|
|
features,
|
||
|
|
available_strategies=["some_other_strategy"]
|
||
|
|
)
|
||
|
|
# Should return safe default
|
||
|
|
assert strategy == "rsi"
|
||
|
|
assert confidence == 0.5
|
||
|
|
|
||
|
|
|
||
|
|
class TestConvertStrategyToGroupLabel:
|
||
|
|
"""Tests for convert_strategy_to_group_label function."""
|
||
|
|
|
||
|
|
def test_converts_known_strategies(self):
|
||
|
|
"""Test conversion of known strategies."""
|
||
|
|
assert convert_strategy_to_group_label("rsi") == "mean_reversion"
|
||
|
|
assert convert_strategy_to_group_label("macd") == "trend_following"
|
||
|
|
assert convert_strategy_to_group_label("momentum") == "momentum"
|
||
|
|
assert convert_strategy_to_group_label("dca") == "market_making"
|
||
|
|
assert convert_strategy_to_group_label("sentiment") == "sentiment_based"
|
||
|
|
|
||
|
|
def test_unknown_strategy_returns_original(self):
|
||
|
|
"""Test that unknown strategies return original name."""
|
||
|
|
assert convert_strategy_to_group_label("unknown") == "unknown"
|