Files
kis_trader/docs/CANDLE_FLOW.md
Hwang 61c72a8a4c 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>
2026-07-06 01:27:00 +09:00

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. 트랙 1 (매매 두뇌)
    WebSocket 틱 → on_tick()RAM에서만 OHLCV 갱신 →
    매수/매도 판단은 get_candles() / get_latest_confirmed()메모리만 사용 (DB 대기 없음).
  2. 트랙 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는 비동기로 쌓인다.