TCPDashboard/tests/test_example_strategies.py

537 lines
22 KiB
Python
Raw Normal View History

"""
Tests for Example Strategy Configurations
Tests the example trading strategies including EMA crossover, momentum,
mean reversion, scalping, and swing trading strategies.
"""
import pytest
import json
from typing import Dict, List
from components.charts.config.example_strategies import (
StrategyExample,
create_ema_crossover_strategy,
create_momentum_breakout_strategy,
create_mean_reversion_strategy,
create_scalping_strategy,
create_swing_trading_strategy,
get_all_example_strategies,
get_example_strategy,
get_strategies_by_difficulty,
get_strategies_by_risk_level,
get_strategies_by_market_condition,
get_strategy_summary,
export_example_strategies_to_json
)
from components.charts.config.strategy_charts import StrategyChartConfig
from components.charts.config.defaults import TradingStrategy
class TestStrategyExample:
"""Test StrategyExample dataclass."""
def test_strategy_example_creation(self):
"""Test StrategyExample creation with defaults."""
# Create a minimal config for testing
config = StrategyChartConfig(
strategy_name="Test Strategy",
strategy_type=TradingStrategy.DAY_TRADING,
description="Test strategy",
timeframes=["1h"]
)
example = StrategyExample(
config=config,
description="Test description"
)
assert example.config == config
assert example.description == "Test description"
assert example.author == "TCPDashboard"
assert example.difficulty == "Beginner"
assert example.risk_level == "Medium"
assert example.market_conditions == ["Trending"] # Default
assert example.notes == [] # Default
assert example.references == [] # Default
def test_strategy_example_with_custom_values(self):
"""Test StrategyExample with custom values."""
config = StrategyChartConfig(
strategy_name="Custom Strategy",
strategy_type=TradingStrategy.SCALPING,
description="Custom strategy",
timeframes=["1m"]
)
example = StrategyExample(
config=config,
description="Custom description",
author="Custom Author",
difficulty="Advanced",
expected_return="10% monthly",
risk_level="High",
market_conditions=["Volatile", "High Volume"],
notes=["Note 1", "Note 2"],
references=["Reference 1"]
)
assert example.author == "Custom Author"
assert example.difficulty == "Advanced"
assert example.expected_return == "10% monthly"
assert example.risk_level == "High"
assert example.market_conditions == ["Volatile", "High Volume"]
assert example.notes == ["Note 1", "Note 2"]
assert example.references == ["Reference 1"]
class TestEMACrossoverStrategy:
"""Test EMA Crossover strategy."""
def test_ema_crossover_creation(self):
"""Test EMA crossover strategy creation."""
strategy = create_ema_crossover_strategy()
assert isinstance(strategy, StrategyExample)
assert isinstance(strategy.config, StrategyChartConfig)
# Check strategy specifics
assert strategy.config.strategy_name == "EMA Crossover Strategy"
assert strategy.config.strategy_type == TradingStrategy.DAY_TRADING
assert "15m" in strategy.config.timeframes
assert "1h" in strategy.config.timeframes
assert "4h" in strategy.config.timeframes
# Check indicators
assert "ema_12" in strategy.config.overlay_indicators
assert "ema_26" in strategy.config.overlay_indicators
assert "ema_50" in strategy.config.overlay_indicators
assert "bb_20_20" in strategy.config.overlay_indicators
# Check subplots
assert len(strategy.config.subplot_configs) == 2
assert any(subplot.subplot_type.value == "rsi" for subplot in strategy.config.subplot_configs)
assert any(subplot.subplot_type.value == "macd" for subplot in strategy.config.subplot_configs)
# Check metadata
assert strategy.difficulty == "Intermediate"
assert strategy.risk_level == "Medium"
assert "Trending" in strategy.market_conditions
assert len(strategy.notes) > 0
assert len(strategy.references) > 0
def test_ema_crossover_validation(self):
"""Test EMA crossover strategy validation."""
strategy = create_ema_crossover_strategy()
is_valid, errors = strategy.config.validate()
# Strategy should be valid or have minimal issues
assert isinstance(is_valid, bool)
assert isinstance(errors, list)
class TestMomentumBreakoutStrategy:
"""Test Momentum Breakout strategy."""
def test_momentum_breakout_creation(self):
"""Test momentum breakout strategy creation."""
strategy = create_momentum_breakout_strategy()
assert isinstance(strategy, StrategyExample)
assert strategy.config.strategy_name == "Momentum Breakout Strategy"
assert strategy.config.strategy_type == TradingStrategy.MOMENTUM
# Check for momentum-specific indicators
assert "ema_8" in strategy.config.overlay_indicators
assert "ema_21" in strategy.config.overlay_indicators
assert "bb_20_25" in strategy.config.overlay_indicators
# Check for fast indicators
rsi_subplot = next((s for s in strategy.config.subplot_configs if s.subplot_type.value == "rsi"), None)
assert rsi_subplot is not None
assert "rsi_7" in rsi_subplot.indicators
assert "rsi_14" in rsi_subplot.indicators
# Check volume subplot
volume_subplot = next((s for s in strategy.config.subplot_configs if s.subplot_type.value == "volume"), None)
assert volume_subplot is not None
# Check metadata
assert strategy.difficulty == "Advanced"
assert strategy.risk_level == "High"
assert "Volatile" in strategy.market_conditions
class TestMeanReversionStrategy:
"""Test Mean Reversion strategy."""
def test_mean_reversion_creation(self):
"""Test mean reversion strategy creation."""
strategy = create_mean_reversion_strategy()
assert isinstance(strategy, StrategyExample)
assert strategy.config.strategy_name == "Mean Reversion Strategy"
assert strategy.config.strategy_type == TradingStrategy.MEAN_REVERSION
# Check for mean reversion indicators
assert "sma_20" in strategy.config.overlay_indicators
assert "sma_50" in strategy.config.overlay_indicators
assert "bb_20_20" in strategy.config.overlay_indicators
assert "bb_20_15" in strategy.config.overlay_indicators
# Check RSI configurations
rsi_subplot = next((s for s in strategy.config.subplot_configs if s.subplot_type.value == "rsi"), None)
assert rsi_subplot is not None
assert "rsi_14" in rsi_subplot.indicators
assert "rsi_21" in rsi_subplot.indicators
# Check metadata
assert strategy.difficulty == "Intermediate"
assert strategy.risk_level == "Medium"
assert "Sideways" in strategy.market_conditions
class TestScalpingStrategy:
"""Test Scalping strategy."""
def test_scalping_creation(self):
"""Test scalping strategy creation."""
strategy = create_scalping_strategy()
assert isinstance(strategy, StrategyExample)
assert strategy.config.strategy_name == "Scalping Strategy"
assert strategy.config.strategy_type == TradingStrategy.SCALPING
# Check fast timeframes
assert "1m" in strategy.config.timeframes
assert "5m" in strategy.config.timeframes
# Check very fast indicators
assert "ema_5" in strategy.config.overlay_indicators
assert "ema_12" in strategy.config.overlay_indicators
assert "ema_21" in strategy.config.overlay_indicators
# Check fast RSI
rsi_subplot = next((s for s in strategy.config.subplot_configs if s.subplot_type.value == "rsi"), None)
assert rsi_subplot is not None
assert "rsi_7" in rsi_subplot.indicators
# Check metadata
assert strategy.difficulty == "Advanced"
assert strategy.risk_level == "High"
assert "High Liquidity" in strategy.market_conditions
class TestSwingTradingStrategy:
"""Test Swing Trading strategy."""
def test_swing_trading_creation(self):
"""Test swing trading strategy creation."""
strategy = create_swing_trading_strategy()
assert isinstance(strategy, StrategyExample)
assert strategy.config.strategy_name == "Swing Trading Strategy"
assert strategy.config.strategy_type == TradingStrategy.SWING_TRADING
# Check longer timeframes
assert "4h" in strategy.config.timeframes
assert "1d" in strategy.config.timeframes
# Check swing trading indicators
assert "sma_20" in strategy.config.overlay_indicators
assert "sma_50" in strategy.config.overlay_indicators
assert "ema_21" in strategy.config.overlay_indicators
assert "bb_20_20" in strategy.config.overlay_indicators
# Check metadata
assert strategy.difficulty == "Beginner"
assert strategy.risk_level == "Medium"
assert "Trending" in strategy.market_conditions
class TestStrategyAccessors:
"""Test strategy accessor functions."""
def test_get_all_example_strategies(self):
"""Test getting all example strategies."""
strategies = get_all_example_strategies()
assert isinstance(strategies, dict)
assert len(strategies) == 5 # Should have 5 strategies
expected_strategies = [
"ema_crossover", "momentum_breakout", "mean_reversion",
"scalping", "swing_trading"
]
for strategy_name in expected_strategies:
assert strategy_name in strategies
assert isinstance(strategies[strategy_name], StrategyExample)
def test_get_example_strategy(self):
"""Test getting a specific example strategy."""
# Test existing strategy
ema_strategy = get_example_strategy("ema_crossover")
assert ema_strategy is not None
assert isinstance(ema_strategy, StrategyExample)
assert ema_strategy.config.strategy_name == "EMA Crossover Strategy"
# Test non-existing strategy
non_existent = get_example_strategy("non_existent_strategy")
assert non_existent is None
def test_get_strategies_by_difficulty(self):
"""Test filtering strategies by difficulty."""
# Test beginner strategies
beginner_strategies = get_strategies_by_difficulty("Beginner")
assert isinstance(beginner_strategies, list)
assert len(beginner_strategies) > 0
for strategy in beginner_strategies:
assert strategy.difficulty == "Beginner"
# Test intermediate strategies
intermediate_strategies = get_strategies_by_difficulty("Intermediate")
assert isinstance(intermediate_strategies, list)
assert len(intermediate_strategies) > 0
for strategy in intermediate_strategies:
assert strategy.difficulty == "Intermediate"
# Test advanced strategies
advanced_strategies = get_strategies_by_difficulty("Advanced")
assert isinstance(advanced_strategies, list)
assert len(advanced_strategies) > 0
for strategy in advanced_strategies:
assert strategy.difficulty == "Advanced"
# Test non-existent difficulty
empty_strategies = get_strategies_by_difficulty("Expert")
assert isinstance(empty_strategies, list)
assert len(empty_strategies) == 0
def test_get_strategies_by_risk_level(self):
"""Test filtering strategies by risk level."""
# Test medium risk strategies
medium_risk = get_strategies_by_risk_level("Medium")
assert isinstance(medium_risk, list)
assert len(medium_risk) > 0
for strategy in medium_risk:
assert strategy.risk_level == "Medium"
# Test high risk strategies
high_risk = get_strategies_by_risk_level("High")
assert isinstance(high_risk, list)
assert len(high_risk) > 0
for strategy in high_risk:
assert strategy.risk_level == "High"
# Test non-existent risk level
empty_strategies = get_strategies_by_risk_level("Ultra High")
assert isinstance(empty_strategies, list)
assert len(empty_strategies) == 0
def test_get_strategies_by_market_condition(self):
"""Test filtering strategies by market condition."""
# Test trending market strategies
trending_strategies = get_strategies_by_market_condition("Trending")
assert isinstance(trending_strategies, list)
assert len(trending_strategies) > 0
for strategy in trending_strategies:
assert "Trending" in strategy.market_conditions
# Test volatile market strategies
volatile_strategies = get_strategies_by_market_condition("Volatile")
assert isinstance(volatile_strategies, list)
assert len(volatile_strategies) > 0
for strategy in volatile_strategies:
assert "Volatile" in strategy.market_conditions
# Test sideways market strategies
sideways_strategies = get_strategies_by_market_condition("Sideways")
assert isinstance(sideways_strategies, list)
assert len(sideways_strategies) > 0
for strategy in sideways_strategies:
assert "Sideways" in strategy.market_conditions
class TestStrategyUtilities:
"""Test strategy utility functions."""
def test_get_strategy_summary(self):
"""Test getting strategy summary."""
summary = get_strategy_summary()
assert isinstance(summary, dict)
assert len(summary) == 5 # Should have 5 strategies
# Check summary structure
for strategy_name, strategy_info in summary.items():
assert isinstance(strategy_info, dict)
required_fields = [
"name", "type", "difficulty", "risk_level",
"timeframes", "market_conditions", "expected_return"
]
for field in required_fields:
assert field in strategy_info
assert isinstance(strategy_info[field], str)
# Check specific strategy
assert "ema_crossover" in summary
ema_summary = summary["ema_crossover"]
assert ema_summary["name"] == "EMA Crossover Strategy"
assert ema_summary["type"] == "day_trading"
assert ema_summary["difficulty"] == "Intermediate"
def test_export_example_strategies_to_json(self):
"""Test exporting strategies to JSON."""
json_str = export_example_strategies_to_json()
# Should be valid JSON
data = json.loads(json_str)
assert isinstance(data, dict)
assert len(data) == 5 # Should have 5 strategies
# Check structure
for strategy_name, strategy_data in data.items():
assert "config" in strategy_data
assert "metadata" in strategy_data
# Check config structure
config = strategy_data["config"]
assert "strategy_name" in config
assert "strategy_type" in config
assert "timeframes" in config
# Check metadata structure
metadata = strategy_data["metadata"]
assert "description" in metadata
assert "author" in metadata
assert "difficulty" in metadata
assert "risk_level" in metadata
# Check specific strategy
assert "ema_crossover" in data
ema_data = data["ema_crossover"]
assert ema_data["config"]["strategy_name"] == "EMA Crossover Strategy"
assert ema_data["metadata"]["difficulty"] == "Intermediate"
class TestStrategyValidation:
"""Test validation of example strategies."""
def test_all_strategies_have_required_fields(self):
"""Test that all strategies have required fields."""
strategies = get_all_example_strategies()
for strategy_name, strategy in strategies.items():
# Check StrategyExample fields
assert strategy.config is not None
assert strategy.description is not None
assert strategy.author is not None
assert strategy.difficulty in ["Beginner", "Intermediate", "Advanced"]
assert strategy.risk_level in ["Low", "Medium", "High"]
assert isinstance(strategy.market_conditions, list)
assert isinstance(strategy.notes, list)
assert isinstance(strategy.references, list)
# Check StrategyChartConfig fields
config = strategy.config
assert config.strategy_name is not None
assert config.strategy_type is not None
assert isinstance(config.timeframes, list)
assert len(config.timeframes) > 0
assert isinstance(config.overlay_indicators, list)
assert isinstance(config.subplot_configs, list)
def test_strategy_configurations_are_valid(self):
"""Test that all strategy configurations are valid."""
strategies = get_all_example_strategies()
for strategy_name, strategy in strategies.items():
# Test basic validation
is_valid, errors = strategy.config.validate()
# Should be valid or have minimal issues (like missing indicators in test environment)
assert isinstance(is_valid, bool)
assert isinstance(errors, list)
# If there are errors, they should be reasonable (like missing indicators)
if not is_valid:
for error in errors:
# Common acceptable errors in test environment
acceptable_errors = [
"not found in defaults", # Missing indicators
"not found", # Missing indicators
]
assert any(acceptable in error for acceptable in acceptable_errors), \
f"Unexpected error in {strategy_name}: {error}"
def test_strategy_timeframes_match_types(self):
"""Test that strategy timeframes match their types."""
strategies = get_all_example_strategies()
# Expected timeframes for different strategy types
expected_timeframes = {
TradingStrategy.SCALPING: ["1m", "5m"],
TradingStrategy.DAY_TRADING: ["5m", "15m", "1h", "4h"],
TradingStrategy.SWING_TRADING: ["1h", "4h", "1d"],
TradingStrategy.MOMENTUM: ["5m", "15m", "1h"],
TradingStrategy.MEAN_REVERSION: ["15m", "1h", "4h"]
}
for strategy_name, strategy in strategies.items():
strategy_type = strategy.config.strategy_type
timeframes = strategy.config.timeframes
if strategy_type in expected_timeframes:
expected = expected_timeframes[strategy_type]
# Should have some overlap with expected timeframes
overlap = set(timeframes) & set(expected)
assert len(overlap) > 0, \
f"Strategy {strategy_name} timeframes {timeframes} don't match type {strategy_type}"
class TestStrategyIntegration:
"""Test integration with other systems."""
def test_strategy_configs_work_with_validation(self):
"""Test that strategy configs work with validation system."""
from components.charts.config.validation import validate_configuration
strategies = get_all_example_strategies()
for strategy_name, strategy in strategies.items():
try:
report = validate_configuration(strategy.config)
assert hasattr(report, 'is_valid')
assert hasattr(report, 'errors')
assert hasattr(report, 'warnings')
except Exception as e:
pytest.fail(f"Validation failed for {strategy_name}: {e}")
def test_strategy_json_roundtrip(self):
"""Test JSON export and import roundtrip."""
from components.charts.config.strategy_charts import (
export_strategy_config_to_json,
load_strategy_config_from_json
)
# Test one strategy for roundtrip
original_strategy = create_ema_crossover_strategy()
# Export to JSON
json_str = export_strategy_config_to_json(original_strategy.config)
# Import from JSON
loaded_config, errors = load_strategy_config_from_json(json_str)
if loaded_config:
# Compare key fields
assert loaded_config.strategy_name == original_strategy.config.strategy_name
assert loaded_config.strategy_type == original_strategy.config.strategy_type
assert loaded_config.timeframes == original_strategy.config.timeframes
assert loaded_config.overlay_indicators == original_strategy.config.overlay_indicators
if __name__ == "__main__":
pytest.main([__file__])