""" Signal Data Class 전략이 생성하는 매매 신호 데이터 구조 """ from enum import Enum from dataclasses import dataclass from typing import Optional, Dict, Any from datetime import datetime class SignalType(Enum): """신호 타입""" BUY = "BUY" # 매수 SELL = "SELL" # 매도 HOLD = "HOLD" # 보유 CLOSE = "CLOSE" # 청산 STOP_LOSS = "STOP_LOSS" # 손절 TAKE_PROFIT = "TAKE_PROFIT" # 익절 @dataclass class Signal: """ 매매 신호 데이터 클래스 전략이 생성한 매매 신호를 표준화된 형식으로 전달 """ # 필수 필드 stock_code: str # 종목코드 signal_type: SignalType # 신호 타입 strategy_name: str # 전략 이름 timestamp: datetime # 신호 생성 시각 # 주문 정보 quantity: Optional[int] = None # 수량 price: Optional[float] = None # 가격 (None = 시장가) confidence: float = 1.0 # 신뢰도 (0.0 ~ 1.0) # 손익 관리 stop_loss_price: Optional[float] = None # 손절가 take_profit_price: Optional[float] = None # 익절가 trailing_stop_pct: Optional[float] = None # 트레일링 스탑 % # 메타데이터 reason: Optional[str] = None # 신호 발생 이유 metadata: Optional[Dict[str, Any]] = None # 추가 데이터 def __post_init__(self): """유효성 검증""" if self.confidence < 0.0 or self.confidence > 1.0: raise ValueError(f"Confidence must be 0.0~1.0, got {self.confidence}") if isinstance(self.signal_type, str): self.signal_type = SignalType(self.signal_type) def to_dict(self) -> Dict[str, Any]: """딕셔너리 변환 (DB 저장용)""" return { "stock_code": self.stock_code, "signal_type": self.signal_type.value, "strategy_name": self.strategy_name, "timestamp": self.timestamp.isoformat(), "quantity": self.quantity, "price": self.price, "confidence": self.confidence, "stop_loss_price": self.stop_loss_price, "take_profit_price": self.take_profit_price, "trailing_stop_pct": self.trailing_stop_pct, "reason": self.reason, "metadata": self.metadata, } def __repr__(self) -> str: return ( f"Signal({self.signal_type.value} {self.stock_code} " f"@{self.price or 'MARKET'} x{self.quantity} " f"[{self.strategy_name}] conf={self.confidence:.2f})" )