"""Signal types for trading strategies.""" from dataclasses import dataclass, field from datetime import datetime from decimal import Decimal from enum import Enum from typing import Any import structlog logger = structlog.get_logger(__name__) class SignalType(str, Enum): """Type of trading signal.""" ENTRY_LONG = "entry_long" ENTRY_SHORT = "entry_short" EXIT_LONG = "exit_long" EXIT_SHORT = "exit_short" @dataclass class Signal: """Trading signal emitted by a strategy. Attributes: signal_type: Type of signal (entry/exit, long/short) symbol: Trading symbol (e.g., "BTCUSDT") price: Suggested entry/exit price stop_loss: Stop loss price for risk calculation take_profit: Optional take profit price confidence: Signal confidence from 0.0 to 1.0 timestamp: When the signal was generated strategy_name: Name of the strategy that generated the signal metadata: Additional signal-specific data """ signal_type: SignalType symbol: str price: Decimal stop_loss: Decimal take_profit: Decimal | None confidence: float timestamp: datetime strategy_name: str metadata: dict[str, Any] = field(default_factory=dict) def __post_init__(self) -> None: """Validate signal parameters.""" if not 0.0 <= self.confidence <= 1.0: raise ValueError(f"Confidence must be between 0.0 and 1.0, got {self.confidence}") if self.price <= Decimal("0"): raise ValueError(f"Price must be positive, got {self.price}") if self.stop_loss <= Decimal("0"): raise ValueError(f"Stop loss must be positive, got {self.stop_loss}") @property def is_entry(self) -> bool: """Check if this is an entry signal.""" return self.signal_type in (SignalType.ENTRY_LONG, SignalType.ENTRY_SHORT) @property def is_exit(self) -> bool: """Check if this is an exit signal.""" return self.signal_type in (SignalType.EXIT_LONG, SignalType.EXIT_SHORT) @property def is_long(self) -> bool: """Check if this is a long-side signal.""" return self.signal_type in (SignalType.ENTRY_LONG, SignalType.EXIT_LONG) @property def is_short(self) -> bool: """Check if this is a short-side signal.""" return self.signal_type in (SignalType.ENTRY_SHORT, SignalType.EXIT_SHORT) @property def risk_reward_ratio(self) -> Decimal | None: """Calculate risk/reward ratio if take profit is set.""" if self.take_profit is None: return None risk = abs(self.price - self.stop_loss) reward = abs(self.take_profit - self.price) if risk == Decimal("0"): return None return reward / risk