feat(tests): 신규 키움 웹소켓 조건검색 및 실시간 조건검색 테스트 추가

변경 사항
----
- _test_kiwoom_condition_list.py: 키움 웹소켓 조건검색 '목록조회' 기능을 단독으로 테스트하는 스크립트 추가
- _test_kiwoom_condition_realtime.py: 'momentum' 조건식을 실시간으로 등록하고 초기 매칭 종목 리스트 및 실시간 편입/이탈을 수신하는 테스트 스크립트 추가
- _verify_columnar_bitid.py, _verify_shared_e2e_breakout.py, _verify_shared_e2e.py: 공유 메모리 및 dict 간의 데이터 일관성을 검증하는 테스트 추가

영향
----
- 신규 테스트 스크립트 추가로 키움 웹소켓 API의 기능 검증 및 안정성을 높임
- 기존 기능에 대한 영향 없음

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-06 01:27:00 +09:00
parent d8ba01afa4
commit 61c72a8a4c
171 changed files with 176914 additions and 7329 deletions

View File

@@ -1,20 +1,25 @@
"""
kis_trader/utils/env.py — DB(env_config) 우선 + os.environ 폴백 환경변수 헬퍼
kis_trader/utils/env.py — DB(config_* + env_config 병합) 우선 + os.environ 폴백 환경변수 헬퍼
===========================================================================
기존 kis_scalping_ver2 / kis_short_ver3 에 흩어져 있던 `get_env_*` 를 통합.
- DB(env_config 최신 row) → os.environ → default 순으로 조회.
- DB get_merged_env_snapshot / get_strategy_env_dict → os.environ → default 순으로 조회.
- 하드코딩 금지 원칙에 맞춰 전 모듈에서 이 함수들만 사용하도록 한다.
"""
from __future__ import annotations
import logging
import os
from typing import Any
from typing import Any, Dict, Optional
logger = logging.getLogger("kis_trader.env")
# 순환 import 방지용 레이지 TradeDB 참조
_db_instance = None
# get_merged_env_snapshot() — 프로세스당 1회 (백테·파라서치 env 키 반복 조회 가속)
_merged_env_cache: Optional[Dict[str, str]] = None
# env 캐시 세대 카운터 — invalidate 시 +1. 파생 캐시(예: whipsaw 파라미터)가
# 이 값으로 무효화를 감지해 안전하게 재계산한다(값은 그대로, 재계산 시점만 동일).
_env_generation: int = 0
def _get_db():
@@ -36,6 +41,22 @@ def set_db(db_obj) -> None:
_db_instance = db_obj
def invalidate_merged_env_cache() -> None:
"""env/config 저장 후 스냅샷 캐시 무효화 (insert_env_snapshot 등)."""
global _merged_env_cache, _env_generation
_merged_env_cache = None
_env_generation += 1
def env_cache_generation() -> int:
"""현재 env 캐시 세대. invalidate 될 때마다 증가.
파생 캐시가 이 값을 키에 포함하면, 설정 저장(무효화) 시 자동으로 재계산되고
그 전까지는 동일 값을 재사용한다(get_env_from_db 의 병합캐시와 동일 수명).
"""
return _env_generation
def _strip_comment(val: Any) -> Any:
"""DB 컬럼에 `#` 이후 주석이 들어 있을 때 제거. 기존 규칙 유지."""
if isinstance(val, str) and "#" in val:
@@ -43,19 +64,57 @@ def _strip_comment(val: Any) -> Any:
return val
def get_env_from_db(key: str, default: str = "") -> str:
"""env_config 최신 row → 키 값을 문자열로 반환. 없으면 os.environ → default."""
def get_merged_env_dict() -> dict:
"""env_config + config_scalp/short/momentum/… 최신 행 병합 flat dict."""
global _merged_env_cache
if _merged_env_cache is not None:
return _merged_env_cache
db = _get_db()
if db is not None:
try:
if db is None:
return {}
try:
if hasattr(db, "get_merged_env_snapshot"):
merged = db.get_merged_env_snapshot()
else:
row = db.get_latest_env()
if row and row.get("snapshot"):
v = row["snapshot"].get(key)
v = _strip_comment(v)
if v not in (None, ""):
return str(v)
except Exception as e:
logger.debug("env_config 조회 실패 (%s): %s", key, e)
merged = dict((row or {}).get("snapshot") or {})
_merged_env_cache = dict(merged or {})
return _merged_env_cache
except Exception as e:
logger.debug("merged env 조회 실패: %s", e)
return {}
def get_strategy_env_dict(strategy_id: str) -> dict:
"""전략 config_* 테이블 + 공통 env 병합 (전략 키 우선).
SCALP → config_scalp, MOMENTUM → config_momentum, SHORT → config_short …
실매(get_env_from_db) · 웹 · 파라서치가 동일 소스를 쓰도록 한다.
"""
merged = get_merged_env_dict()
db = _get_db()
if db is None:
return merged
try:
if hasattr(db, "get_strategy_config_snapshot"):
strat = db.get_strategy_config_snapshot(strategy_id)
if strat:
out = dict(merged)
out.update(strat)
return out
except Exception as e:
logger.debug("strategy env 조회 실패 (%s): %s", strategy_id, e)
return merged
def get_env_from_db(key: str, default: str = "") -> str:
"""config_* + env_config 병합 스냅샷 → 키 값. 없으면 os.environ → default."""
merged = get_merged_env_dict()
if merged:
v = _strip_comment(merged.get(key))
if v not in (None, ""):
return str(v)
# os.environ 폴백 (기존 호환)
return os.environ.get(key, str(default))