diff --git a/data/common/data_types.py b/data/common/data_types.py index 2607946..fbe7e39 100644 --- a/data/common/data_types.py +++ b/data/common/data_types.py @@ -201,6 +201,67 @@ class ProcessingStats: } +class SignalType(str, Enum): + """ + Types of trading signals that strategies can generate. + """ + BUY = "buy" + SELL = "sell" + HOLD = "hold" + ENTRY_LONG = "entry_long" + EXIT_LONG = "exit_long" + ENTRY_SHORT = "entry_short" + EXIT_SHORT = "exit_short" + STOP_LOSS = "stop_loss" + TAKE_PROFIT = "take_profit" + + +@dataclass +class StrategySignal: + """ + Container for individual strategy signals. + + Attributes: + timestamp: Signal timestamp (right-aligned with candle) + symbol: Trading symbol + timeframe: Candle timeframe + signal_type: Type of signal (buy/sell/hold etc.) + price: Price at which signal was generated + confidence: Signal confidence score (0.0 to 1.0) + metadata: Additional signal metadata (e.g., indicator values) + """ + timestamp: datetime + symbol: str + timeframe: str + signal_type: SignalType + price: float + confidence: float = 1.0 + metadata: Optional[Dict[str, Any]] = None + + +@dataclass +class StrategyResult: + """ + Container for strategy calculation results. + + Attributes: + timestamp: Candle timestamp (right-aligned) + symbol: Trading symbol + timeframe: Candle timeframe + strategy_name: Name of the strategy that generated this result + signals: List[StrategySignal] + indicators_used: Dictionary of indicator values used in calculation + metadata: Additional calculation metadata + """ + timestamp: datetime + symbol: str + timeframe: str + strategy_name: str + signals: List[StrategySignal] + indicators_used: Dict[str, float] + metadata: Optional[Dict[str, Any]] = None + + # Re-export from base_collector for convenience __all__ = [ 'DataType', diff --git a/database/repositories/strategy_repository.py b/database/repositories/strategy_repository.py index a91b716..8efca0b 100644 --- a/database/repositories/strategy_repository.py +++ b/database/repositories/strategy_repository.py @@ -8,7 +8,7 @@ from sqlalchemy import desc, and_, func from sqlalchemy.orm import joinedload from ..models import StrategySignal, StrategyRun -from strategies.data_types import StrategySignal as StrategySignalDataType, StrategyResult +from data.common.data_types import StrategySignal as StrategySignalDataType, StrategyResult from .base_repository import BaseRepository, DatabaseOperationError @@ -95,23 +95,25 @@ class StrategyRepository(BaseRepository): with self.get_session() as session: for result in strategy_results: for signal in result.signals: - strategy_signal = StrategySignal( - run_id=run_id, - strategy_name=result.strategy_name, - strategy_config=None, # Could be populated from StrategyRun.config - symbol=signal.symbol, - timeframe=signal.timeframe, - timestamp=signal.timestamp, - signal_type=signal.signal_type.value, - price=Decimal(str(signal.price)), - confidence=Decimal(str(signal.confidence)), - signal_metadata={ - 'indicators_used': result.indicators_used, - 'metadata': signal.metadata or {} - } - ) - session.add(strategy_signal) - signals_stored += 1 + # Only store BUY or SELL signals, not HOLD + if signal.signal_type.value in ["BUY", "SELL"]: + strategy_signal = StrategySignal( + run_id=run_id, + strategy_name=result.strategy_name, + strategy_config=None, # Could be populated from StrategyRun.config + symbol=signal.symbol, + timeframe=signal.timeframe, + timestamp=signal.timestamp, + signal_type=signal.signal_type.value, + price=Decimal(str(signal.price)), + confidence=Decimal(str(signal.confidence)), + signal_metadata={ + 'indicators_used': result.indicators_used, + 'metadata': signal.metadata or {} + } + ) + session.add(strategy_signal) + signals_stored += 1 session.commit() self.log_info(f"Stored {signals_stored} strategy signals for run {run_id}") diff --git a/strategies/__init__.py b/strategies/__init__.py index 90f083f..0653249 100644 --- a/strategies/__init__.py +++ b/strategies/__init__.py @@ -14,7 +14,7 @@ IMPORTANT: Mirrors Indicator Patterns from .base import BaseStrategy from .factory import StrategyFactory -from .data_types import StrategySignal, SignalType, StrategyResult +from data.common.data_types import StrategySignal, SignalType, StrategyResult from .manager import StrategyManager, StrategyConfig, StrategyType, StrategyCategory, get_strategy_manager from .data_integration import StrategyDataIntegrator, StrategyDataIntegrationConfig, get_strategy_data_integrator from .validation import StrategySignalValidator, ValidationConfig diff --git a/strategies/data_integration.py b/strategies/data_integration.py index a44fa28..ffb8571 100644 --- a/strategies/data_integration.py +++ b/strategies/data_integration.py @@ -20,7 +20,7 @@ from data.common.data_types import OHLCVCandle from data.common.indicators import TechnicalIndicators from components.charts.config.indicator_defs import convert_database_candles_to_ohlcv from .factory import StrategyFactory -from .data_types import StrategyResult +from data.common.data_types import StrategyResult from utils.logger import get_logger # Initialize logger diff --git a/strategies/data_types.py b/strategies/data_types.py deleted file mode 100644 index b4621dd..0000000 --- a/strategies/data_types.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Strategy Data Types and Signal Definitions - -This module provides data types for strategy calculations, signals, -and results in a standardized format. -""" - -from dataclasses import dataclass -from datetime import datetime -from typing import Dict, Optional, Any, List -from enum import Enum - - -class SignalType(str, Enum): - """ - Types of trading signals that strategies can generate. - """ - BUY = "buy" - SELL = "sell" - HOLD = "hold" - ENTRY_LONG = "entry_long" - EXIT_LONG = "exit_long" - ENTRY_SHORT = "entry_short" - EXIT_SHORT = "exit_short" - STOP_LOSS = "stop_loss" - TAKE_PROFIT = "take_profit" - - -@dataclass -class StrategySignal: - """ - Container for individual strategy signals. - - Attributes: - timestamp: Signal timestamp (right-aligned with candle) - symbol: Trading symbol - timeframe: Candle timeframe - signal_type: Type of signal (buy/sell/hold etc.) - price: Price at which signal was generated - confidence: Signal confidence score (0.0 to 1.0) - metadata: Additional signal metadata (e.g., indicator values) - """ - timestamp: datetime - symbol: str - timeframe: str - signal_type: SignalType - price: float - confidence: float = 1.0 - metadata: Optional[Dict[str, Any]] = None - - -@dataclass -class StrategyResult: - """ - Container for strategy calculation results. - - Attributes: - timestamp: Candle timestamp (right-aligned) - symbol: Trading symbol - timeframe: Candle timeframe - strategy_name: Name of the strategy that generated this result - signals: List of signals generated for this timestamp - indicators_used: Dictionary of indicator values used in calculation - metadata: Additional calculation metadata - """ - timestamp: datetime - symbol: str - timeframe: str - strategy_name: str - signals: List[StrategySignal] - indicators_used: Dict[str, float] - metadata: Optional[Dict[str, Any]] = None \ No newline at end of file diff --git a/strategies/realtime_execution.py b/strategies/realtime_execution.py index d9a0f3c..dd87088 100644 --- a/strategies/realtime_execution.py +++ b/strategies/realtime_execution.py @@ -207,25 +207,31 @@ class StrategySignalBroadcaster: result = signal.strategy_result context = signal.context - signal_data.append({ - 'strategy_name': context.strategy_name, - 'strategy_config': context.strategy_config, - 'symbol': context.symbol, - 'timeframe': context.timeframe, - 'exchange': context.exchange, - 'timestamp': result.timestamp, - 'signal_type': result.signal.signal_type.value if result.signal else 'HOLD', - 'price': float(result.price) if result.price else None, - 'confidence': result.confidence, - 'signal_metadata': result.metadata or {}, - 'generation_time': signal.generation_time - }) + # Only store BUY or SELL signals, not HOLD + if result.signal and result.signal.signal_type in [SignalType.BUY, SignalType.SELL]: + signal_data.append({ + 'strategy_name': context.strategy_name, + 'strategy_config': context.strategy_config, + 'symbol': context.symbol, + 'timeframe': context.timeframe, + 'exchange': context.exchange, + 'timestamp': result.timestamp, + 'signal_type': result.signal.signal_type.value, + 'price': float(result.price) if result.price else None, + 'confidence': result.confidence, + 'signal_metadata': result.metadata or {}, + 'generation_time': signal.generation_time + }) - # Batch insert into database - self.db_ops.strategy.store_signals_batch(signal_data) - - if self.logger: - self.logger.debug(f"Stored batch of {len(signals)} real-time signals") + if signal_data: # Only call store_signals_batch if there are signals to store + # Batch insert into database + self.db_ops.strategy.store_signals_batch(signal_data) + + if self.logger: + self.logger.debug(f"Stored batch of {len(signal_data)} real-time signals") + else: + if self.logger: + self.logger.debug("No BUY/SELL signals to store in this batch.") except Exception as e: if self.logger: diff --git a/tasks/4.0-strategy-engine-foundation.md b/tasks/4.0-strategy-engine-foundation.md index 048f13f..28ae7e6 100644 --- a/tasks/4.0-strategy-engine-foundation.md +++ b/tasks/4.0-strategy-engine-foundation.md @@ -102,6 +102,11 @@ - **Reasoning**: Real-time strategy execution requires different architecture than batch processing - event-driven triggers, background signal processing, throttled chart updates, and integration with existing dashboard refresh cycles. - **Impact**: Enables live strategy signal generation that integrates seamlessly with the existing chart system. Provides concurrent strategy execution, real-time signal storage, error handling with automatic strategy disabling, and performance monitoring for production use. +### 11. Exclusion of "HOLD" Signals from All Signal Storage +- **Decision**: Modified signal storage mechanisms across both real-time and batch processing to exclude "HOLD" signals, only persisting explicit "BUY" or "SELL" signals in the database. +- **Reasoning**: To significantly reduce data volume and improve storage/query performance across all execution types, as the absence of a BUY/SELL signal implicitly means "HOLD" and can be inferred during analysis or visualization. +- **Impact**: Leads to more efficient database usage and faster data retrieval for all stored signals. Requires careful handling in visualization and backtesting components to correctly interpret gaps as "HOLD" periods. + ## Tasks - [x] 1.0 Core Strategy Foundation Setup