""" kis_trader 전략 ID 단일 정의 — active_trades / trade_history / 웹 실거래·보유탭 공통. - ``main.py`` 전략 클래스의 ``strategy_id`` (SCALP, SHORT, MOMENTUM, UPDOW, BREAKOUT, HOLDING) - ``trade_history`` 신규 기록은 ``canonical_strategy_id()`` 로 위 ID 만 저장 → 실거래 분석 탭과 일치 - 구식명(SCALP_RSI_REVERSAL, SHORT_ANT_SHAKING)은 조회 시 접두어(LIKE)로 묶고, 신규 저장은 canonical """ from __future__ import annotations from typing import List, Optional # kis_trader/main.py 에 등록되는 전략 ID (실거래 분석·보유탭 공통) KIS_TRADER_STRATEGY_IDS: List[str] = [ "SCALP", "SHORT", "MOMENTUM", "UPDOW", "BREAKOUT", "RANGE_BREAK", "DBBAND", "HOLDING", ] # 사용자 지정 — 웹·계산에서 완전히 숨길 전략 (비활성·중복·저성능). # DB 에 기존 거래기록이 남아 있어도 실거래 분석·대시보드·보유탭·운영설정에서 표시·집계하지 않는다. # SCALP : MOMENTUM 과 1분봉 슬롯 중복 (반등 vs 추세) → MOMENTUM 만 사용 # RANGE_BREAK : BREAKOUT 과 돌파 컨셉 중복 + 미검증 (파라서치 거래 0건) # DBBAND : 표본 부족·미성숙 (백테 2~4건) HIDDEN_STRATEGY_IDS: List[str] = [ "SCALP", "RANGE_BREAK", "DBBAND", ] # 웹·실거래 집계에서 제외 (DB에 남아 있어도 표시·집계 안 함) EXCLUDED_STRATEGY_IDS: List[str] = [ "MANUAL", "SCALP_RSI_REVERSAL", "SHORT_ANT_SHAKING", "SCALP_TEST", ] + HIDDEN_STRATEGY_IDS # 보유·매도 탭 전용 — 홀딩봇(HOLDING)은 조회만, 여기서 매도 대상 아님 PORTFOLIO_EXCLUDED_STRATEGY_IDS: List[str] = EXCLUDED_STRATEGY_IDS + [ "HOLDING", ] def canonical_strategy_id(strategy: Optional[str]) -> str: """ DB 저장·trade_history 기록용 canonical ID. MANUAL 은 그대로, 구식명은 SCALP/SHORT 등으로 접힘. """ s = (strategy or "").strip().upper() if not s: return "MANUAL" if s == "MANUAL": return "MANUAL" if s.startswith("SCALP"): return "SCALP" if s.startswith("SHORT") or s.startswith("TAIL"): return "SHORT" if s.startswith("MOMENTUM"): return "MOMENTUM" if s.startswith("UPDOW"): return "UPDOW" if s.startswith("BREAKOUT"): return "BREAKOUT" if s.startswith("RANGE_BREAK"): return "RANGE_BREAK" if s.startswith("DBBAND") or s.startswith("BBBAND"): return "DBBAND" if s.startswith("HOLDING"): return "HOLDING" return s def strategy_prefix_for_filter(strategy: Optional[str]) -> str: """ trade_history / active_trades LIKE 필터용 접두어. 실거래 분석 탭·보유탭이 동일 규칙 사용. """ s_upper = (strategy or "").upper().strip() if not s_upper or s_upper == "ALL": return "" if s_upper == "MANUAL": return "MANUAL" if s_upper.startswith("SCALP"): return "SCALP" if s_upper.startswith("SHORT") or s_upper.startswith("TAIL"): return "SHORT" if s_upper.startswith("MOMENTUM"): return "MOMENTUM" if s_upper.startswith("UPDOW"): return "UPDOW" if s_upper.startswith("BREAKOUT"): return "BREAKOUT" if s_upper.startswith("RANGE_BREAK"): return "RANGE_BREAK" if s_upper.startswith("DBBAND") or s_upper.startswith("BBBAND"): return "DBBAND" if s_upper.startswith("HOLDING"): return "HOLDING" return s_upper def strategy_like_pattern(strategy: Optional[str]) -> Optional[str]: """None 이면 전체, 아니면 ``PREFIX%``.""" prefix = strategy_prefix_for_filter(strategy) if not prefix: return None return prefix + "%" def is_bot_strategy(strategy: Optional[str]) -> bool: """kis_trader 봇 전략 row 인지 (MANUAL 제외).""" c = canonical_strategy_id(strategy) return c in KIS_TRADER_STRATEGY_IDS