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:
@@ -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))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user