변경 사항 ---- - _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>
4.2 KiB
4.2 KiB
캔들 수집·저장·매매 흐름 (스캘핑 vs 꼬리잡기)
1. 어떤 봉이 쌓이나? (전략별)
| 전략 | CandleAggregator timeframes | 주전략 봉 | 비고 |
|---|---|---|---|
| 스캘핑 (kis_scalping_ver1/ver2) | [1, 3, 15, 60] | 1분봉 (SCALP_CANDLE_TIMEFRAME=1) | 4분봉 없음 |
| 꼬리잡기 (kis_short_ver2/ver3) | [3, 15, 60] | 3분봉 | 1분봉·4분봉 없음 |
- 4분봉은 어디에도 사용하지 않음. 1분 / 3분 / 15분 / 60분만 집계·저장.
- 스캘핑은 1분봉 중심, 꼬리잡기는 3분봉만 매수 신호에 사용.
2. 메모리 vs DB — 전략별 PK 여부
- ws_candles 테이블은 전략별 PK가 없음.
UNIQUE KEY (code, timeframe, candle_time)하나로
스캘핑이 넣은 1·3·15·60분봉과 꼬리잡기가 넣은 3·15·60분봉이 같은 테이블에 쌓임. - 꼬리잡기도 3분봉을 메모리(CandleAggregator) 에 먼저 쌓고,
봉이 확정될 때마다 Queue → 백그라운드 스레드가 ws_candles에 배치 INSERT.
흐름 요약:
- 트랙 1 (매매 두뇌)
WebSocket 틱 →on_tick()→ RAM에서만 OHLCV 갱신 →
매수/매도 판단은get_candles()/get_latest_confirmed()등 메모리만 사용 (DB 대기 없음). - 트랙 2 (기록)
봉 확정 시점에만 dict를 Queue에put_nowait()→
_db_writer스레드가 BATCH_SIZE(50)개 또는 FLUSH_INTERVAL(2초)마다 ws_candles에 배치 INSERT.
→ 봉 모을 때까지 기다리지 않고, 확정되는 대로 메모리에 반영되고, DB는 그 뒤에 비동기로 저장.
3. 체결 틱 (ws_ticks, C안 — TickRecorder)
| 구분 | 내용 |
|---|---|
| 모듈 | kis_trader/ws/tick_recorder.py |
| 트랙 1 | 종목별 deque 링버퍼 (WS_TICK_BUFFER_MAX_PER_CODE) — WSManager.get_recent_ticks() |
| 트랙 2 | 제한 큐 → TickDBWriter 스레드 → ws_ticks 배치 INSERT |
| 대상 종목 | WS_TICK_RECORD_SCOPE=candidates(기본): 후보+영구 / subscribed: 후보+보유+영구 |
| 해외 확장 | market, exchange, currency, session 컬럼 (국내 기본 KR/KRX/KRW) |
| env | WS_TICK_SAVE_ENABLED, WS_TICK_DB_BATCH_SIZE, WS_TICK_DB_FLUSH_SEC, WS_TICK_KEEP_DAYS 등 |
journalctl 파싱이 아니라 WS H0STCNT0 / 키움 0B 와 동일 틱 스트림을 사용한다.
4. 매수 시 캔들 조회 — 세 가지 분기 (DB / 메모리 / 키움·KIS REST)
꼬리잡기 매수 신호(check_buy_signal_tail_catch)에서 3분봉을 가져오는 순서:
| 순서 | 경로 | 설명 |
|---|---|---|
| 1 | 메모리 | candle_agg.get_candles(code, 3, 50) — 확정봉만, DB/네트워크 없음 |
| 2 | DB | 메모리에 부족하면 db.get_ws_candles(code, 3, limit=50, confirmed_only=True) |
| 3 | REST | 그래도 없으면 _get_candles_df() → 실패 시 client.get_minute_chart(code, period="3", limit=20) (KIS). 갭보정은 키움 우선, 없으면 KIS. |
- 매수가 확정봉일 때만 가능하도록 되어 있음:
- 1·2번은 애초에 확정봉만 반환.
- 3번(REST)에서 가져온 DataFrame은 마지막 행(진행봉) 제거
df.iloc[:-1]후 신호 판단에 사용.
위 분기는 kis_short_ver3.py check_buy_signal_tail_catch() 안에 그대로 구현되어 있음 (메모리 → DB → REST 순).
5. “캔들 넣고 바로 매매” — 현재 동작
- 봉이 확정되는 순간 이미 RAM(_confirmed) 에 들어가므로,
매수 루프는 DB 쓰기 완료를 기다리지 않고 바로 그 데이터로 신호 판단. - DB는 그 뒤에 배치로 저장되므로, “캔들 DB에 넣고 나서 매매”를 기다릴 필요 없음.
다만 봇 기동 직후에는 메모리에 봉이 없을 수 있음 →
_fill_all_gaps()에서 키움(우선) 또는 KIS REST로 과거 봉을 가져와fill_gap_from_rest()로
RAM + Queue(→ DB) 에 채움. 이 갭보정이 끝나면 해당 종목은 바로 매수 체크 가능.
정리: 봉 모을 때까지 기다리지 않고, 확정봉이 메모리에 들어오는 즉시 매매 로직에 쓰이고, DB는 비동기로 쌓인다.