Initial commit: Crypto trader application

This commit is contained in:
2025-12-25 20:20:40 -05:00
commit 07a04c1bb8
47895 changed files with 2042266 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
"""Unit tests for strategy framework."""

View File

@@ -0,0 +1,89 @@
"""Tests for base strategy class."""
import pytest
import pandas as pd
from src.strategies.base import BaseStrategy, StrategyRegistry
class ConcreteStrategy(BaseStrategy):
"""Concrete strategy for testing."""
async def on_data(self, new_data: pd.DataFrame):
"""Handle new data."""
self.current_data = pd.concat([self.current_data, new_data]).tail(100)
async def generate_signal(self):
"""Generate signal."""
if len(self.current_data) > 0:
return {"signal": "hold", "price": self.current_data['close'].iloc[-1]}
return {"signal": "hold", "price": None}
async def calculate_position_size(self, capital: float, risk_percentage: float) -> float:
"""Calculate position size."""
return capital * risk_percentage
class TestBaseStrategy:
"""Tests for BaseStrategy."""
@pytest.fixture
def strategy(self):
"""Create strategy instance."""
return ConcreteStrategy(
strategy_id=1,
name="test_strategy",
symbol="BTC/USD",
timeframe="1h",
parameters={}
)
def test_strategy_initialization(self, strategy):
"""Test strategy initialization."""
assert strategy.strategy_id == 1
assert strategy.name == "test_strategy"
assert strategy.symbol == "BTC/USD"
assert strategy.timeframe == "1h"
assert not strategy.is_active
@pytest.mark.asyncio
async def test_strategy_start_stop(self, strategy):
"""Test strategy start and stop."""
await strategy.start()
assert strategy.is_active
await strategy.stop()
assert not strategy.is_active
@pytest.mark.asyncio
async def test_generate_signal(self, strategy):
"""Test signal generation."""
signal = await strategy.generate_signal()
assert "signal" in signal
assert signal["signal"] in ["buy", "sell", "hold"]
@pytest.mark.asyncio
async def test_calculate_position_size(self, strategy):
"""Test position size calculation."""
size = await strategy.calculate_position_size(1000.0, 0.01)
assert size == 10.0
class TestStrategyRegistry:
"""Tests for StrategyRegistry."""
def test_register_strategy(self):
"""Test strategy registration."""
StrategyRegistry.register_strategy("test_strategy", ConcreteStrategy)
assert "test_strategy" in StrategyRegistry.list_available()
def test_get_strategy_class(self):
"""Test getting strategy class."""
StrategyRegistry.register_strategy("test_strategy", ConcreteStrategy)
strategy_class = StrategyRegistry.get_strategy_class("test_strategy")
assert strategy_class == ConcreteStrategy
def test_get_nonexistent_strategy(self):
"""Test getting non-existent strategy."""
with pytest.raises(ValueError, match="not registered"):
StrategyRegistry.get_strategy_class("nonexistent")

View File

@@ -0,0 +1,55 @@
"""Tests for Bollinger Bands mean reversion strategy."""
import pytest
from decimal import Decimal
from src.strategies.technical.bollinger_mean_reversion import BollingerMeanReversionStrategy
from src.strategies.base import SignalType
class TestBollingerMeanReversionStrategy:
"""Tests for BollingerMeanReversionStrategy."""
@pytest.fixture
def strategy(self):
"""Create Bollinger mean reversion strategy instance."""
return BollingerMeanReversionStrategy(
name="test_bollinger_mr",
parameters={
'period': 20,
'std_dev': 2.0,
'trend_filter': True,
'trend_ma_period': 50,
'entry_threshold': 0.95,
'exit_threshold': 0.5
}
)
def test_initialization(self, strategy):
"""Test strategy initialization."""
assert strategy.period == 20
assert strategy.std_dev == 2.0
assert strategy.trend_filter is True
assert strategy.trend_ma_period == 50
assert strategy.entry_threshold == 0.95
assert strategy.exit_threshold == 0.5
def test_on_tick_insufficient_data(self, strategy):
"""Test that strategy returns None with insufficient data."""
signal = strategy.on_tick(
symbol="BTC/USD",
price=Decimal("50000"),
timeframe="1h",
data={'volume': 1000}
)
assert signal is None
def test_position_tracking(self, strategy):
"""Test position tracking."""
assert strategy._in_position is False
assert strategy._entry_price is None
def test_strategy_metadata(self, strategy):
"""Test strategy metadata."""
assert strategy.name == "test_bollinger_mr"
assert strategy.enabled is False

View File

@@ -0,0 +1,61 @@
"""Tests for Confirmed strategy."""
import pytest
from decimal import Decimal
from src.strategies.technical.confirmed_strategy import ConfirmedStrategy
from src.strategies.base import SignalType
class TestConfirmedStrategy:
"""Tests for ConfirmedStrategy."""
@pytest.fixture
def strategy(self):
"""Create Confirmed strategy instance."""
return ConfirmedStrategy(
name="test_confirmed",
parameters={
'rsi_period': 14,
'macd_fast': 12,
'macd_slow': 26,
'macd_signal': 9,
'ma_fast': 10,
'ma_slow': 30,
'min_confirmations': 2,
'require_rsi': True,
'require_macd': True,
'require_ma': True
}
)
def test_initialization(self, strategy):
"""Test strategy initialization."""
assert strategy.rsi_period == 14
assert strategy.macd_fast == 12
assert strategy.ma_fast == 10
assert strategy.min_confirmations == 2
def test_on_tick_insufficient_data(self, strategy):
"""Test that strategy returns None with insufficient data."""
signal = strategy.on_tick(
symbol="BTC/USD",
price=Decimal("50000"),
timeframe="1h",
data={'volume': 1000}
)
assert signal is None
def test_min_confirmations_requirement(self, strategy):
"""Test that signal requires minimum confirmations."""
# This would require actual price history to generate real signals
# For now, we test the structure
assert strategy.min_confirmations == 2
assert strategy.require_rsi is True
assert strategy.require_macd is True
assert strategy.require_ma is True
def test_strategy_metadata(self, strategy):
"""Test strategy metadata."""
assert strategy.name == "test_confirmed"
assert strategy.enabled is False

View File

@@ -0,0 +1,53 @@
"""Tests for Consensus (ensemble) strategy."""
import pytest
from decimal import Decimal
from src.strategies.ensemble.consensus_strategy import ConsensusStrategy
from src.strategies.base import SignalType
class TestConsensusStrategy:
"""Tests for ConsensusStrategy."""
@pytest.fixture
def strategy(self):
"""Create Consensus strategy instance."""
return ConsensusStrategy(
name="test_consensus",
parameters={
'strategy_names': ['rsi', 'macd'],
'min_consensus': 2,
'use_weights': True,
'min_weight': 0.3,
'exclude_strategies': []
}
)
def test_initialization(self, strategy):
"""Test strategy initialization."""
assert strategy.min_consensus == 2
assert strategy.use_weights is True
assert strategy.min_weight == 0.3
def test_on_tick_no_strategies(self, strategy):
"""Test that strategy handles empty strategy list."""
# Strategy should handle cases where no strategies are available
signal = strategy.on_tick(
symbol="BTC/USD",
price=Decimal("50000"),
timeframe="1h",
data={'volume': 1000}
)
# May return None if no strategies available or no consensus
assert signal is None or isinstance(signal, (type(None), object))
def test_strategy_metadata(self, strategy):
"""Test strategy metadata."""
assert strategy.name == "test_consensus"
assert strategy.enabled is False
def test_consensus_calculation(self, strategy):
"""Test consensus calculation parameters."""
assert strategy.min_consensus == 2
assert strategy.use_weights is True

View File

@@ -0,0 +1,52 @@
"""Tests for DCA strategy."""
import pytest
from decimal import Decimal
from datetime import datetime, timedelta
from src.strategies.dca.dca_strategy import DCAStrategy
def test_dca_strategy_initialization():
"""Test DCA strategy initializes correctly."""
strategy = DCAStrategy("Test DCA", {"amount": 10, "interval": "daily"})
assert strategy.name == "Test DCA"
assert strategy.amount == Decimal("10")
assert strategy.interval == "daily"
def test_dca_daily_interval():
"""Test DCA with daily interval."""
strategy = DCAStrategy("Daily DCA", {"amount": 10, "interval": "daily"})
assert strategy.interval_delta == timedelta(days=1)
def test_dca_weekly_interval():
"""Test DCA with weekly interval."""
strategy = DCAStrategy("Weekly DCA", {"amount": 10, "interval": "weekly"})
assert strategy.interval_delta == timedelta(weeks=1)
def test_dca_monthly_interval():
"""Test DCA with monthly interval."""
strategy = DCAStrategy("Monthly DCA", {"amount": 10, "interval": "monthly"})
assert strategy.interval_delta == timedelta(days=30)
def test_dca_signal_generation():
"""Test DCA generates buy signals."""
strategy = DCAStrategy("Test DCA", {"amount": 10, "interval": "daily"})
strategy.last_purchase_time = None
signal = strategy.on_tick("BTC/USD", Decimal("100"), "1h", {})
assert signal is not None
assert signal.signal_type.value == "buy"
assert signal.quantity == Decimal("0.1") # 10 / 100
def test_dca_interval_respect():
"""Test DCA respects interval timing."""
strategy = DCAStrategy("Test DCA", {"amount": 10, "interval": "daily"})
strategy.last_purchase_time = datetime.utcnow() - timedelta(hours=12)
signal = strategy.on_tick("BTC/USD", Decimal("100"), "1h", {})
assert signal is None # Should not generate signal yet

View File

@@ -0,0 +1,59 @@
"""Tests for Divergence strategy."""
import pytest
from decimal import Decimal
from src.strategies.technical.divergence_strategy import DivergenceStrategy
from src.strategies.base import SignalType
class TestDivergenceStrategy:
"""Tests for DivergenceStrategy."""
@pytest.fixture
def strategy(self):
"""Create Divergence strategy instance."""
return DivergenceStrategy(
name="test_divergence",
parameters={
'indicator_type': 'rsi',
'rsi_period': 14,
'lookback': 20,
'min_swings': 2,
'min_confidence': 0.5
}
)
def test_initialization(self, strategy):
"""Test strategy initialization."""
assert strategy.indicator_type == 'rsi'
assert strategy.rsi_period == 14
assert strategy.lookback == 20
assert strategy.min_swings == 2
assert strategy.min_confidence == 0.5
def test_on_tick_insufficient_data(self, strategy):
"""Test that strategy returns None with insufficient data."""
signal = strategy.on_tick(
symbol="BTC/USD",
price=Decimal("50000"),
timeframe="1h",
data={'volume': 1000}
)
assert signal is None
def test_indicator_type_selection(self, strategy):
"""Test indicator type selection."""
assert strategy.indicator_type == 'rsi'
# Test MACD indicator type
macd_strategy = DivergenceStrategy(
name="test_divergence_macd",
parameters={'indicator_type': 'macd'}
)
assert macd_strategy.indicator_type == 'macd'
def test_strategy_metadata(self, strategy):
"""Test strategy metadata."""
assert strategy.name == "test_divergence"
assert strategy.enabled is False

View File

@@ -0,0 +1,69 @@
"""Tests for Grid strategy."""
import pytest
from decimal import Decimal
from src.strategies.grid.grid_strategy import GridStrategy
def test_grid_strategy_initialization():
"""Test Grid strategy initializes correctly."""
strategy = GridStrategy("Test Grid", {
"grid_spacing": 1,
"num_levels": 10,
"profit_target": 2
})
assert strategy.name == "Test Grid"
assert strategy.grid_spacing == Decimal("0.01")
assert strategy.num_levels == 10
def test_grid_levels_calculation():
"""Test grid levels are calculated correctly."""
strategy = GridStrategy("Test Grid", {
"grid_spacing": 1,
"num_levels": 5,
"center_price": 100
})
strategy._update_grid_levels(Decimal("100"))
assert len(strategy.buy_levels) == 5
assert len(strategy.sell_levels) == 5
# Buy levels should be below center
assert all(level < Decimal("100") for level in strategy.buy_levels)
# Sell levels should be above center
assert all(level > Decimal("100") for level in strategy.sell_levels)
def test_grid_buy_signal():
"""Test grid generates buy signal at lower level."""
strategy = GridStrategy("Test Grid", {
"grid_spacing": 1,
"num_levels": 5,
"center_price": 100,
"position_size": Decimal("0.1")
})
# Price at buy level
signal = strategy.on_tick("BTC/USD", Decimal("99"), "1h", {})
assert signal is not None
assert signal.signal_type.value == "buy"
def test_grid_profit_taking():
"""Test grid takes profit at target."""
strategy = GridStrategy("Test Grid", {
"grid_spacing": 1,
"num_levels": 5,
"profit_target": 2
})
# Simulate position
entry_price = Decimal("100")
strategy.positions[entry_price] = Decimal("0.1")
# Price with profit
signal = strategy.on_tick("BTC/USD", Decimal("102"), "1h", {})
assert signal is not None
assert signal.signal_type.value == "sell"
assert entry_price not in strategy.positions # Position removed

View File

@@ -0,0 +1,45 @@
"""Tests for MACD strategy."""
import pytest
import pandas as pd
from src.strategies.technical.macd_strategy import MACDStrategy
class TestMACDStrategy:
"""Tests for MACDStrategy."""
@pytest.fixture
def strategy(self):
"""Create MACD strategy instance."""
return MACDStrategy(
strategy_id=1,
name="test_macd",
symbol="BTC/USD",
timeframe="1h",
parameters={
"fast_period": 12,
"slow_period": 26,
"signal_period": 9
}
)
@pytest.mark.asyncio
async def test_macd_strategy_initialization(self, strategy):
"""Test MACD strategy initialization."""
assert strategy.fast_period == 12
assert strategy.slow_period == 26
assert strategy.signal_period == 9
@pytest.mark.asyncio
async def test_generate_signal(self, strategy):
"""Test signal generation."""
# Create minimal data
data = pd.DataFrame({
'close': [100 + i * 0.1 for i in range(50)]
})
strategy.current_data = data
signal = await strategy.generate_signal()
assert "signal" in signal
assert signal["signal"] in ["buy", "sell", "hold"]

View File

@@ -0,0 +1,72 @@
"""Tests for Momentum strategy."""
import pytest
from decimal import Decimal
import pandas as pd
from src.strategies.momentum.momentum_strategy import MomentumStrategy
def test_momentum_strategy_initialization():
"""Test Momentum strategy initializes correctly."""
strategy = MomentumStrategy("Test Momentum", {
"lookback_period": 20,
"momentum_threshold": 0.05
})
assert strategy.name == "Test Momentum"
assert strategy.lookback_period == 20
assert strategy.momentum_threshold == Decimal("0.05")
def test_momentum_calculation():
"""Test momentum calculation."""
strategy = MomentumStrategy("Test", {"lookback_period": 5})
# Create price history
prices = pd.Series([100, 101, 102, 103, 104, 105])
momentum = strategy._calculate_momentum(prices)
# Should be positive (price increased)
assert momentum > 0
assert momentum == 0.05 # (105 - 100) / 100
def test_momentum_entry_signal():
"""Test momentum generates entry signal."""
strategy = MomentumStrategy("Test", {
"lookback_period": 5,
"momentum_threshold": 0.05,
"volume_threshold": 1.0
})
# Build price history with momentum
for i in range(10):
price = 100 + i * 2 # Strong upward momentum
volume = 1000 * (1.5 if i >= 5 else 1.0) # Volume increase
strategy.on_tick("BTC/USD", Decimal(str(price)), "1h", {"volume": volume})
# Should generate buy signal
signal = strategy.on_tick("BTC/USD", Decimal("120"), "1h", {"volume": 2000})
assert signal is not None
assert signal.signal_type.value == "buy"
assert strategy._in_position == True
def test_momentum_exit_signal():
"""Test momentum generates exit signal on reversal."""
strategy = MomentumStrategy("Test", {
"lookback_period": 5,
"exit_threshold": -0.02
})
strategy._in_position = True
strategy._entry_price = Decimal("100")
# Build history with reversal
for i in range(10):
price = 100 - i # Downward momentum
strategy.on_tick("BTC/USD", Decimal(str(price)), "1h", {"volume": 1000})
signal = strategy.on_tick("BTC/USD", Decimal("90"), "1h", {"volume": 1000})
assert signal is not None
assert signal.signal_type.value == "sell"
assert strategy._in_position == False

View File

@@ -0,0 +1,45 @@
"""Tests for Moving Average strategy."""
import pytest
import pandas as pd
from src.strategies.technical.moving_avg_strategy import MovingAverageStrategy
class TestMovingAverageStrategy:
"""Tests for MovingAverageStrategy."""
@pytest.fixture
def strategy(self):
"""Create Moving Average strategy instance."""
return MovingAverageStrategy(
strategy_id=1,
name="test_ma",
symbol="BTC/USD",
timeframe="1h",
parameters={
"short_period": 10,
"long_period": 30,
"ma_type": "SMA"
}
)
@pytest.mark.asyncio
async def test_ma_strategy_initialization(self, strategy):
"""Test Moving Average strategy initialization."""
assert strategy.short_period == 10
assert strategy.long_period == 30
assert strategy.ma_type == "SMA"
@pytest.mark.asyncio
async def test_generate_signal(self, strategy):
"""Test signal generation."""
# Create data with trend
data = pd.DataFrame({
'close': [100 + i * 0.5 for i in range(50)]
})
strategy.current_data = data
signal = await strategy.generate_signal()
assert "signal" in signal
assert signal["signal"] in ["buy", "sell", "hold"]

View File

@@ -0,0 +1,89 @@
import pytest
import pandas as pd
import numpy as np
from unittest.mock import MagicMock, AsyncMock, patch
from decimal import Decimal
from src.strategies.technical.pairs_trading import PairsTradingStrategy
from src.strategies.base import SignalType
@pytest.fixture
def mock_pricing_service():
service = MagicMock()
service.get_ohlcv = MagicMock()
return service
@pytest.fixture
def strategy(mock_pricing_service):
with patch('src.strategies.technical.pairs_trading.get_pricing_service', return_value=mock_pricing_service):
params = {
'second_symbol': 'AVAX/USD',
'lookback_period': 5,
'z_score_threshold': 1.5,
'symbol': 'SOL/USD'
}
strat = PairsTradingStrategy("test_pairs", params)
strat.enabled = True
return strat
@pytest.mark.asyncio
async def test_pairs_trading_short_spread_signal(strategy, mock_pricing_service):
# Setup Data
# Scenario: SOL (A) pumps relative to AVAX (B) -> Spread widens -> Z-Score High -> Sell A / Buy B
# Prices for A (SOL): 100, 100, 100, 100, 120 (Pump)
ohlcv_a = [
[0, 100, 100, 100, 100, 1000],
[0, 100, 100, 100, 100, 1000],
[0, 100, 100, 100, 100, 1000],
[0, 100, 100, 100, 100, 1000],
[0, 120, 120, 120, 120, 1000],
]
# Prices for B (AVAX): 25, 25, 25, 25, 25 (Flat)
ohlcv_b = [
[0, 25, 25, 25, 25, 1000],
[0, 25, 25, 25, 25, 1000],
[0, 25, 25, 25, 25, 1000],
[0, 25, 25, 25, 25, 1000],
[0, 25, 25, 25, 25, 1000],
]
# Spread: 4, 4, 4, 4, 4.8
# Mean: 4.16, StdDev: approx small but let's see.
# Actually StdDev will be non-zero because of the last value.
mock_pricing_service.get_ohlcv.side_effect = [ohlcv_a, ohlcv_b]
# Execute
signal = await strategy.on_tick("SOL/USD", Decimal(120), "1h", {})
# Verify
assert signal is not None
assert signal.signal_type == SignalType.SELL # Sell Primary (SOL)
assert signal.metadata['secondary_action'] == 'buy' # Buy Secondary (AVAX)
assert signal.metadata['z_score'] > 1.5
@pytest.mark.asyncio
async def test_pairs_trading_long_spread_signal(strategy, mock_pricing_service):
# Scenario: SOL (A) dumps -> Spread drops -> Z-Score Low -> Buy A / Sell B
ohlcv_a = [
[0, 100, 100, 100, 100, 1000],
[0, 100, 100, 100, 100, 1000],
[0, 100, 100, 100, 100, 1000],
[0, 100, 100, 100, 100, 1000],
[0, 80, 80, 80, 80, 1000], # Dump
]
ohlcv_b = [
[0, 25, 25, 25, 25, 1000] for _ in range(5)
]
mock_pricing_service.get_ohlcv.side_effect = [ohlcv_a, ohlcv_b]
signal = await strategy.on_tick("SOL/USD", Decimal(80), "1h", {})
assert signal is not None
assert signal.signal_type == SignalType.BUY # Buy Primary
assert signal.metadata['secondary_action'] == 'sell' # Sell Secondary
assert signal.metadata['z_score'] < -1.5

View File

@@ -0,0 +1,67 @@
"""Tests for RSI strategy."""
import pytest
import pandas as pd
from src.strategies.technical.rsi_strategy import RSIStrategy
class TestRSIStrategy:
"""Tests for RSIStrategy."""
@pytest.fixture
def strategy(self):
"""Create RSI strategy instance."""
return RSIStrategy(
strategy_id=1,
name="test_rsi",
symbol="BTC/USD",
timeframe="1h",
parameters={
"rsi_period": 14,
"overbought": 70,
"oversold": 30
}
)
@pytest.fixture
def sample_data(self):
"""Create sample price data."""
dates = pd.date_range(start='2025-01-01', periods=50, freq='1H')
# Create data with clear trend for RSI calculation
prices = [100 - i * 0.5 for i in range(50)] # Downward trend
return pd.DataFrame({
'timestamp': dates,
'open': prices,
'high': [p + 1 for p in prices],
'low': [p - 1 for p in prices],
'close': prices,
'volume': [1000.0] * 50
})
@pytest.mark.asyncio
async def test_rsi_strategy_initialization(self, strategy):
"""Test RSI strategy initialization."""
assert strategy.rsi_period == 14
assert strategy.overbought == 70
assert strategy.oversold == 30
@pytest.mark.asyncio
async def test_on_data(self, strategy, sample_data):
"""Test on_data method."""
await strategy.on_data(sample_data)
assert len(strategy.current_data) > 0
@pytest.mark.asyncio
async def test_generate_signal_oversold(self, strategy, sample_data):
"""Test signal generation for oversold condition."""
await strategy.on_data(sample_data)
# Calculate RSI - should be low for downward trend
from src.data.indicators import get_indicators
indicators = get_indicators()
rsi = indicators.rsi(strategy.current_data['close'], period=14)
# If RSI is low, should generate buy signal
signal = await strategy.generate_signal()
assert "signal" in signal
assert signal["signal"] in ["buy", "sell", "hold"]

View File

@@ -0,0 +1,88 @@
"""Tests for trend filter functionality."""
import pytest
import pandas as pd
from decimal import Decimal
from src.strategies.base import BaseStrategy, StrategySignal, SignalType
from src.strategies.technical.rsi_strategy import RSIStrategy
class TestTrendFilter:
"""Tests for trend filter in BaseStrategy."""
@pytest.fixture
def strategy(self):
"""Create strategy instance with trend filter enabled."""
strategy = RSIStrategy(
name="test_rsi_with_filter",
parameters={'use_trend_filter': True}
)
return strategy
@pytest.fixture
def ohlcv_data(self):
"""Create sample OHLCV data."""
dates = pd.date_range(start='2025-01-01', periods=50, freq='1H')
base_price = 50000
return pd.DataFrame({
'high': [base_price + 100 + i * 10 for i in range(50)],
'low': [base_price - 100 + i * 10 for i in range(50)],
'close': [base_price + i * 10 for i in range(50)],
'open': [base_price - 50 + i * 10 for i in range(50)],
'volume': [1000.0] * 50
}, index=dates)
def test_trend_filter_method_exists(self, strategy):
"""Test that apply_trend_filter method exists."""
assert hasattr(strategy, 'apply_trend_filter')
assert callable(getattr(strategy, 'apply_trend_filter'))
def test_trend_filter_insufficient_data(self, strategy):
"""Test trend filter with insufficient data."""
signal = StrategySignal(
signal_type=SignalType.BUY,
symbol="BTC/USD",
strength=0.8,
price=Decimal("50000")
)
insufficient_data = pd.DataFrame({
'high': [51000],
'low': [49000],
'close': [50000]
})
# Should allow signal when insufficient data
result = strategy.apply_trend_filter(signal, insufficient_data)
assert result is not None
def test_trend_filter_none_data(self, strategy):
"""Test trend filter with None data."""
signal = StrategySignal(
signal_type=SignalType.BUY,
symbol="BTC/USD",
strength=0.8,
price=Decimal("50000")
)
# Should allow signal when no data provided
result = strategy.apply_trend_filter(signal, None)
assert result is not None
def test_trend_filter_when_disabled(self, strategy):
"""Test that trend filter doesn't filter when disabled."""
strategy_no_filter = RSIStrategy(
name="test_rsi_no_filter",
parameters={'use_trend_filter': False}
)
signal = StrategySignal(
signal_type=SignalType.BUY,
symbol="BTC/USD",
strength=0.8,
price=Decimal("50000")
)
result = strategy_no_filter.apply_trend_filter(signal, None)
assert result == signal