Files
auto-stock/backend/app/strategy/signal.py

80 lines
2.6 KiB
Python

"""
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})"
)