## CRITICAL 수정 (20건) - day_trading: global 선언 누락, loss_tracker 연동, ban 체크 추가 - trade_executor: BUY 체결가 order_price로 수정, remaining_pct 누적 계산 - risk_validator: get_current_price dict 파싱, MAX_POSITION_SIZE 동기화, DB 세션 누수 - risk_management/recommendations: date.today() → KST 변환 - mdd_manager: Lock 밖 executed_key 참조 수정 - correlation_filter: output → output2 키 수정 - portfolio: 해외 종목 USD/KRW 환율 적용 분리 - Sharpe Ratio 거래빈도 기반 연간화 - telegram_notifier: send_message() 공개 래퍼 추가 - position_manager: 앱 재시작 시 DB 포지션 복원 - swing exit_service: remaining_pct 조건 범위 수정 - run_backtest: original_qty 기반 remaining_pct 계산 - us_market_schedule: 2027년 미국 공휴일 추가 - Login.tsx: res.ok 검증 추가 - mean_reversion_strategy: RSI 로그 하드코딩 수정 ## HIGH 수정 (10건) - db_worker: DLQ 파일 백업 메커니즘 추가 - korea_investment_api: 만료 토큰 임의 연장 제거 - market_data: chart_cache 최대 100개 + TTL 만료 - main.py: rate_limit_store 1시간 미접근 IP 정리 - recommendations: fire-and-forget try/except 추가 - swing/scheduler: DB 세션 3개 분리 - Dashboard: wsMessages 전체 순회 - Layout: /health 엔드포인트 + 30초 폴링 - ml_ensemble: POST /train require_api_key 추가 - day_trading: record_trade_result_safe() 전환 ## 코드 정리 - 프론트엔드: isUsStock, formatTime 공통 유틸 추출 - 백엔드: dead code 제거, 중복 import 통합 - MCP: JSON 파싱 헬퍼 추출 (~60줄 감소) - 전체 ~100개 파일 한국어 주석 추가 - 테스트 13건 코드 기준 수정 (468 passed) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
174 lines
5.1 KiB
Python
174 lines
5.1 KiB
Python
"""
|
|
공유 유틸리티 함수 및 상수
|
|
|
|
이 모듈은 여러 파일에서 중복되는 함수와 상수를 한 곳에 정의하여
|
|
코드 중복을 제거합니다.
|
|
|
|
포함 항목:
|
|
- _is_domestic(): 국내/해외 종목 판별
|
|
- CircuitBreakerState Enum: 회로차단기 상태
|
|
- Chart data extraction helpers: 차트 데이터 추출 유틸
|
|
- DomesticChartItem / OverseasChartItem: 차트 데이터 TypedDict
|
|
"""
|
|
|
|
from enum import Enum
|
|
from typing import List, Dict, Any, Optional, TypedDict
|
|
|
|
|
|
# ── 해외 거래소 코드 목록 ────────────────────────────────────────────────────────
|
|
OVERSEAS_MARKETS = {"NASD", "NYSE", "AMEX", "TSEX", "SEHK", "SHAA", "SZAA", "HASE", "VNSE"}
|
|
|
|
|
|
class CircuitBreakerState(Enum):
|
|
"""회로차단기(Circuit Breaker) 상태.
|
|
|
|
States:
|
|
CLOSED : 정상 운영 (요청 허용)
|
|
OPEN : 차단 (recovery_timeout 경과 전까지 요청 거부)
|
|
HALF_OPEN: 복구 시도 (1개 요청 허용 → 성공 시 CLOSED, 실패 시 OPEN)
|
|
"""
|
|
CLOSED = "CLOSED"
|
|
OPEN = "OPEN"
|
|
HALF_OPEN = "HALF_OPEN"
|
|
|
|
|
|
class DomesticChartItem(TypedDict, total=False):
|
|
"""한국 국내 주식 차트 데이터 아이템 (KIS API).
|
|
|
|
Field descriptions:
|
|
stck_clpr: 종가 (closing price)
|
|
stck_hgpr: 고가 (high price)
|
|
stck_lwpr: 저가 (low price)
|
|
stck_oprc: 시가 (opening price)
|
|
acml_vol: 누적거래량 (accumulated volume)
|
|
stck_bsop_date: 영업일자 (business date)
|
|
"""
|
|
stck_clpr: str
|
|
stck_hgpr: str
|
|
stck_lwpr: str
|
|
stck_oprc: str
|
|
acml_vol: str
|
|
stck_bsop_date: str
|
|
|
|
|
|
class OverseasChartItem(TypedDict, total=False):
|
|
"""해외 주식 차트 데이터 아이템 (KIS API).
|
|
|
|
Field descriptions:
|
|
clos: 종가 (closing price)
|
|
high: 고가 (high price)
|
|
low: 저가 (low price)
|
|
open: 시가 (opening price)
|
|
v: 거래량 (volume)
|
|
"""
|
|
clos: str
|
|
high: str
|
|
low: str
|
|
open: str
|
|
v: str
|
|
|
|
|
|
def _is_domestic(stock_code: str, market: str = None) -> bool:
|
|
"""국내 종목 여부 판별.
|
|
|
|
market 컬럼이 있으면 우선 사용, 없으면 종목코드 패턴으로 폴백.
|
|
|
|
Args:
|
|
stock_code: 종목 코드 (예: "005930")
|
|
market: 시장 코드 (예: "NASD", "KRX"). Optional.
|
|
|
|
Returns:
|
|
True if domestic (KRX), False if overseas.
|
|
"""
|
|
if market:
|
|
return market not in OVERSEAS_MARKETS
|
|
# 폴백: 6자리 숫자이면 국내(KRX)
|
|
return stock_code.isdigit() and len(stock_code) == 6
|
|
|
|
|
|
def extract_closes(chart_data: List[Dict[str, Any]], is_domestic: bool = True) -> List[float]:
|
|
"""차트 데이터에서 종가(close price) 추출.
|
|
|
|
KIS API는 최신순으로 반환하므로 필요시 역순 처리.
|
|
|
|
Args:
|
|
chart_data: KIS API 차트 데이터 (최신순 또는 시간순)
|
|
is_domestic: True면 국내(stck_clpr), False면 해외(clos) 필드 사용
|
|
|
|
Returns:
|
|
종가 리스트 (float)
|
|
"""
|
|
if not chart_data:
|
|
return []
|
|
|
|
key = "stck_clpr" if is_domestic else "clos"
|
|
return [float(item.get(key, 0)) for item in chart_data]
|
|
|
|
|
|
def extract_highs(chart_data: List[Dict[str, Any]], is_domestic: bool = True) -> List[float]:
|
|
"""차트 데이터에서 고가(high price) 추출.
|
|
|
|
Args:
|
|
chart_data: KIS API 차트 데이터
|
|
is_domestic: True면 국내(stck_hgpr), False면 해외(high) 필드 사용
|
|
|
|
Returns:
|
|
고가 리스트 (float)
|
|
"""
|
|
if not chart_data:
|
|
return []
|
|
|
|
key = "stck_hgpr" if is_domestic else "high"
|
|
return [float(item.get(key, 0)) for item in chart_data]
|
|
|
|
|
|
def extract_lows(chart_data: List[Dict[str, Any]], is_domestic: bool = True) -> List[float]:
|
|
"""차트 데이터에서 저가(low price) 추출.
|
|
|
|
Args:
|
|
chart_data: KIS API 차트 데이터
|
|
is_domestic: True면 국내(stck_lwpr), False면 해외(low) 필드 사용
|
|
|
|
Returns:
|
|
저가 리스트 (float)
|
|
"""
|
|
if not chart_data:
|
|
return []
|
|
|
|
key = "stck_lwpr" if is_domestic else "low"
|
|
return [float(item.get(key, 0)) for item in chart_data]
|
|
|
|
|
|
def extract_opens(chart_data: List[Dict[str, Any]], is_domestic: bool = True) -> List[float]:
|
|
"""차트 데이터에서 시가(open price) 추출.
|
|
|
|
Args:
|
|
chart_data: KIS API 차트 데이터
|
|
is_domestic: True면 국내(stck_oprc), False면 해외(open) 필드 사용
|
|
|
|
Returns:
|
|
시가 리스트 (float)
|
|
"""
|
|
if not chart_data:
|
|
return []
|
|
|
|
key = "stck_oprc" if is_domestic else "open"
|
|
return [float(item.get(key, 0)) for item in chart_data]
|
|
|
|
|
|
def extract_volumes(chart_data: List[Dict[str, Any]], is_domestic: bool = True) -> List[float]:
|
|
"""차트 데이터에서 거래량(volume) 추출.
|
|
|
|
Args:
|
|
chart_data: KIS API 차트 데이터
|
|
is_domestic: True면 국내(acml_vol), False면 해외(v) 필드 사용
|
|
|
|
Returns:
|
|
거래량 리스트 (float)
|
|
"""
|
|
if not chart_data:
|
|
return []
|
|
|
|
key = "acml_vol" if is_domestic else "v"
|
|
return [float(item.get(key, 0)) for item in chart_data]
|