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:
@@ -117,6 +117,7 @@ class VolumeRankManager:
|
||||
self._running = False
|
||||
|
||||
self._current: Dict[str, Set[str]] = {}
|
||||
self._current_order: Dict[str, List[str]] = {}
|
||||
self._name_map: Dict[str, str] = {}
|
||||
self._initialized: Set[str] = set()
|
||||
self._lock = threading.Lock()
|
||||
@@ -161,7 +162,9 @@ class VolumeRankManager:
|
||||
"""BaseStrategy._load_candidates 와 호환되는 dict 리스트."""
|
||||
sid = (strategy_id or "").upper()
|
||||
with self._lock:
|
||||
codes = list(self._current.get(sid, set()))
|
||||
codes = list(self._current_order.get(sid) or [])
|
||||
if not codes:
|
||||
codes = list(self._current.get(sid, set()))
|
||||
nm = dict(self._name_map)
|
||||
out = []
|
||||
for c in codes:
|
||||
@@ -170,6 +173,7 @@ class VolumeRankManager:
|
||||
"name": nm.get(c, c),
|
||||
"scalp_on": True,
|
||||
"tail_on": True,
|
||||
"updow_on": True,
|
||||
"score": 0.0,
|
||||
"price": 0.0,
|
||||
})
|
||||
@@ -206,9 +210,14 @@ class VolumeRankManager:
|
||||
time.sleep(0.5)
|
||||
|
||||
def _tick_all(self) -> None:
|
||||
from ..utils.universe_source import universe_source_active
|
||||
|
||||
# (blng, market, limit) 조합이 같으면 REST 1회만 호출
|
||||
cache: Dict[Tuple[str, str, int, bool], List[Dict]] = {}
|
||||
for cfg in self._configs:
|
||||
sid = cfg["strategy_id"]
|
||||
if not universe_source_active(sid, "ranking"):
|
||||
continue
|
||||
key = (cfg["blng"], cfg["market"], cfg["limit"], cfg["exclude_non_stock"])
|
||||
if key in cache:
|
||||
rows = cache[key]
|
||||
@@ -252,6 +261,20 @@ class VolumeRankManager:
|
||||
enters = new_set - prev
|
||||
exits = prev - new_set
|
||||
self._current[strategy_id] = new_set
|
||||
ordered: List[str] = []
|
||||
seen: Set[str] = set()
|
||||
for r in rows:
|
||||
code = (
|
||||
r.get("mksc_shrn_iscd") or r.get("stk_cd")
|
||||
or r.get("code") or ""
|
||||
).strip()
|
||||
if code and code in new_set and code not in seen:
|
||||
seen.add(code)
|
||||
ordered.append(code)
|
||||
for c in new_set:
|
||||
if c not in seen:
|
||||
ordered.append(c)
|
||||
self._current_order[strategy_id] = ordered
|
||||
self._initialized.add(strategy_id)
|
||||
for c, n in new_names.items():
|
||||
self._name_map[c] = n
|
||||
@@ -282,22 +305,36 @@ class VolumeRankManager:
|
||||
|
||||
# 변동 또는 첫 tick → 풀 스냅샷 저장 (condition 과 동일 테이블/포맷)
|
||||
if changed or first_tick:
|
||||
self._save_snapshot(strategy_id, new_set, new_names)
|
||||
if not self._history_save_allowed(strategy_id):
|
||||
return
|
||||
with self._lock:
|
||||
ordered = list(self._current_order.get(strategy_id) or [])
|
||||
self._save_snapshot(strategy_id, ordered, new_names)
|
||||
|
||||
@staticmethod
|
||||
def _history_save_allowed(strategy_id: str) -> bool:
|
||||
"""active UNIVERSE_SOURCE 가 ``ranking`` 일 때만 ranking history 기록."""
|
||||
from ..utils.universe_source import universe_source_active
|
||||
sid = (strategy_id or "").upper()
|
||||
if not sid:
|
||||
return True
|
||||
try:
|
||||
return universe_source_active(sid, "ranking")
|
||||
except Exception:
|
||||
return True
|
||||
|
||||
def _save_snapshot(
|
||||
self, strategy_id: str, codes: Set[str], names: Dict[str, str],
|
||||
self, strategy_id: str, codes_ordered: List[str], names: Dict[str, str],
|
||||
) -> None:
|
||||
"""변동 감지 tick 마다 즉시 DB 에 풀 스냅샷 INSERT.
|
||||
|
||||
* ``event_time`` 은 REST 응답 직후 찍은 초단위 시각 (YYYY-MM-DD HH:MM:SS).
|
||||
* 이 값이 백테스트 매칭 기준 시각이며, DB INSERT 가 1ms 지연되든 100ms
|
||||
지연되든 행에 박히는 event_time 은 불변이므로 정합성은 보존.
|
||||
* 폴링은 10초 주기라 DB 부하 매우 적음. 버퍼링으로 얻을 이득 없음.
|
||||
"""
|
||||
"""변동 감지 tick 마다 즉시 DB 에 풀 스냅샷 INSERT (랭킹 응답 순서 유지)."""
|
||||
if not (self.history_enabled and self.db is not None):
|
||||
return
|
||||
event_time = dt.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
items = [{"code": c, "name": names.get(c, c)} for c in sorted(codes)]
|
||||
items = [
|
||||
{"code": c, "name": names.get(c, c)}
|
||||
for c in codes_ordered
|
||||
if c
|
||||
]
|
||||
try:
|
||||
n = self.db.insert_condition_universe_snapshot(
|
||||
strategy_id=strategy_id,
|
||||
|
||||
Reference in New Issue
Block a user