변경 사항 ---- - _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>
136 lines
4.6 KiB
Python
136 lines
4.6 KiB
Python
"""
|
|
config_schema.py — env 설정 키 → 테이블 분류 (전략별 config_* + 공통 env_config)
|
|
|
|
테이블 명명: config_{전략} (config_scalp, config_short, …)
|
|
공통(API·MM·인프라): env_config
|
|
|
|
기존 ENV_CONFIG_KEYS 전체는 get_merged_env_snapshot() 으로 flat dict 유지 (하위 호환).
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from typing import Dict, Iterable, List, Tuple
|
|
|
|
# (테이블명, 전략 ID — 로그/문서용)
|
|
CONFIG_TABLE_NAMES: Tuple[str, ...] = (
|
|
"env_config",
|
|
"config_scalp",
|
|
"config_short",
|
|
"config_momentum",
|
|
"config_breakout",
|
|
"config_range_break",
|
|
"config_updow",
|
|
"config_dbband",
|
|
)
|
|
|
|
STRATEGY_ID_TO_TABLE: Dict[str, str] = {
|
|
"SCALP": "config_scalp",
|
|
"SHORT": "config_short",
|
|
"MOMENTUM": "config_momentum",
|
|
"BREAKOUT": "config_breakout",
|
|
"RANGE_BREAK": "config_range_break",
|
|
"UPDOW": "config_updow",
|
|
"DBBAND": "config_dbband",
|
|
}
|
|
|
|
# 전략 ON/OFF · MM alias — 접두어 규칙 예외 없이 전략 테이블에만 저장
|
|
_EXPLICIT_KEY_TABLE: Dict[str, str] = {
|
|
"STRATEGY_SCALP_ENABLED": "config_scalp",
|
|
"STRATEGY_SHORT_ENABLED": "config_short",
|
|
"STRATEGY_MOMENTUM_ENABLED": "config_momentum",
|
|
"STRATEGY_BREAKOUT_ENABLED": "config_breakout",
|
|
"STRATEGY_RANGE_BREAK_ENABLED": "config_range_break",
|
|
"STRATEGY_UPDOW_ENABLED": "config_updow",
|
|
"STRATEGY_DBBAND_ENABLED": "config_dbband",
|
|
"KIS_SCALP_MM_CHANNEL": "config_scalp",
|
|
"KIS_SHORT_MM_CHANNEL": "config_short",
|
|
"KIS_MOMENTUM_MM_CHANNEL": "config_momentum",
|
|
"KIS_BREAKOUT_MM_CHANNEL": "config_breakout",
|
|
"KIS_RANGE_BREAK_MM_CHANNEL": "config_range_break",
|
|
"KIS_UPDOW_MM_CHANNEL": "config_updow",
|
|
"KIS_DBBAND_MM_CHANNEL": "config_dbband",
|
|
}
|
|
|
|
# 꼬리 오케스트레이터(SHORT strategy_id) — TAIL_* 와 분리
|
|
_SHORT_ORCHESTRATOR_KEYS = frozenset({
|
|
"SHORT_GAP_FILL_LIMIT",
|
|
"SHORT_LIVE_BACKTEST_ALIGN",
|
|
"SHORT_LIVE_SIGNAL_LOOKBACK_BARS",
|
|
"SHORT_UNIVERSE_SOURCE",
|
|
"RANK_SHORT_SORT",
|
|
"RANK_SHORT_LIMIT",
|
|
"CONDITION_SHORT_NAME",
|
|
"CONDITION_SHORT_SEQ",
|
|
"SHORT_CAND_LIMIT",
|
|
"KIS_SHORT_MM_CHANNEL",
|
|
})
|
|
|
|
# 익절 호가 — 실행 공통이지만 SHORT 튜닝과 함께 둠
|
|
_SHORT_EXEC_KEYS = frozenset({
|
|
"SELL_USE_ORDERBOOK_ON_PROFIT",
|
|
"SELL_ORDERBOOK_BID_LEVELS",
|
|
"SELL_ORDERBOOK_DEPTH_MULT",
|
|
})
|
|
|
|
|
|
def classify_config_key(key: str) -> str:
|
|
"""설정 키가 들어갈 테이블명 반환."""
|
|
k = (key or "").strip()
|
|
if not k:
|
|
return "env_config"
|
|
|
|
explicit = _EXPLICIT_KEY_TABLE.get(k)
|
|
if explicit:
|
|
return explicit
|
|
|
|
if k in _SHORT_ORCHESTRATOR_KEYS or k in _SHORT_EXEC_KEYS:
|
|
return "config_short"
|
|
if k.startswith("UPDOW_") or k == "MIN_CANDLE_LEN_UPDOW":
|
|
return "config_updow"
|
|
if k.startswith("DBBAND_"):
|
|
return "config_dbband"
|
|
if k.startswith("BREAKOUT_"):
|
|
return "config_breakout"
|
|
if k.startswith("RANGE_BREAK_"):
|
|
return "config_range_break"
|
|
if k.startswith("MOMENTUM_"):
|
|
return "config_momentum"
|
|
if k.startswith("SCALP_"):
|
|
return "config_scalp"
|
|
if k.startswith("SHORT_") or k.startswith("TAIL_") or k.startswith("SHOULDER"):
|
|
return "config_short"
|
|
if k.startswith("STOP_ATR") or k.startswith("TARGET_ATR"):
|
|
return "config_short"
|
|
|
|
if k.startswith("RANK_UPDOW_") or k.startswith("CONDITION_UPDOW"):
|
|
return "config_updow"
|
|
if k.startswith("RANK_DBBAND_") or k.startswith("CONDITION_DBBAND"):
|
|
return "config_dbband"
|
|
if k.startswith("RANK_BREAKOUT_") or k.startswith("CONDITION_BREAKOUT"):
|
|
return "config_breakout"
|
|
if k.startswith("RANK_RANGE_BREAK_") or k.startswith("CONDITION_RANGE_BREAK"):
|
|
return "config_range_break"
|
|
if k.startswith("RANK_MOMENTUM_") or k.startswith("CONDITION_MOMENTUM"):
|
|
return "config_momentum"
|
|
if k.startswith("RANK_SCALP_") or k.startswith("CONDITION_SCALP"):
|
|
return "config_scalp"
|
|
if k.startswith("RANK_SHORT_") or k.startswith("CONDITION_SHORT"):
|
|
return "config_short"
|
|
|
|
return "env_config"
|
|
|
|
|
|
def split_env_keys(all_keys: Iterable[str]) -> Dict[str, Tuple[str, ...]]:
|
|
"""키 목록을 테이블별 튜플로 분류."""
|
|
buckets: Dict[str, List[str]] = {t: [] for t in CONFIG_TABLE_NAMES}
|
|
for key in all_keys:
|
|
tbl = classify_config_key(key)
|
|
if tbl not in buckets:
|
|
tbl = "env_config"
|
|
buckets[tbl].append(key)
|
|
return {t: tuple(buckets[t]) for t in CONFIG_TABLE_NAMES}
|
|
|
|
|
|
def build_config_schema(all_keys: Tuple[str, ...]) -> Dict[str, Tuple[str, ...]]:
|
|
"""ENV_CONFIG_KEYS 로부터 테이블별 키 튜플 생성."""
|
|
return split_env_keys(all_keys)
|