Files
kis_trader/docs/TAIL_BACKTEST_WEB_VS_PARAM_SEARCH.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

11 KiB
Raw Permalink Blame History

꼬리잡기 백테스트 — 웹 vs 파라서치 정리 (2026-05-30)

목적: backtest_web 꼬리 탭, tail_param_search.py, 실매(TailCatchStrategy) 결과가 어긋나던 원인·수정·운영 가이드를 한곳에 모음.


1. 아키텍처: SCAN vs TRIGGER

꼬리 전략은 2단계로 동작한다.

[SCAN] HTS/KIS 조건검색 (널넬하게)
  A) 1봉 등락률 -10% ~ -1.5%  (직전봉 종가 대비 현재봉 하락)
  B) 체결강도 85% ~ 400%
  C) 3봉전 대비 0봉 거래량 1.8~20배
  → ConditionSearchManager / KIS 조건식 `tail`
  → target_candidates_history (strategy_id=SHORT) 저장

[TRIGGER] tail_engine (엄격하게 — 매수 직전)
  망치 꼬리, 당일 회복률, 3분봉 회복 위치, (선택) RSI·MA20,
  고점추격·피뢰침, 시간대/쿨다운, (옵션) HTS A 재검사
단계 역할 백테에서
SCAN 후보 종목·시각 좁히기 target_candidates_history (저장 이력 ON)
TRIGGER “지금 사도 되나?” 최종 검사 tail_engine._eval_tail_buy_at_index

원칙 (.cursorrules SCAN vs TRIGGER 분리):

  • 무거운 필터·API 호출은 5분 스캔이 아니라 매수 타점(TRIGGER) 에 둔다.
  • 저장 이력 ON이면 SCAN은 이미 HTS로 걸렀으므로, TRIGGER에서 중복 필터를 또 걸면 거래가 사라진다.

2. 공통 코드 경로 (2026-05-30 기준)

구성요소 파일 역할
공통 로더 kis_trader/backtest/tail_backtest_common.py 유니버스·캔들·손익·run_tail_backtest_web_aligned
엔진 kis_trader/engine/tail_engine.py 진입/청산, get_tail_defaults_from_db, run_tail_backtest
파라서치 kis_trader/backtest/tail_param_search.py fast/coarse 그리드, --apply
웹 API backtest_web.py/api/backtest/tail 꼬리 탭 백테

웹·파라서치 모두:

  1. tbc.resolve_tail_universe() — SHORT 저장 이력 (기본)
  2. tbc.load_tail_candles_by_code() — ws_candles
  3. te.run_tail_backtest() — 동일 엔진

3. 웹 123건 vs 파라서치 6건 — 원인과 수정

3.1 증상

조건 거래 수 손익 (대략)
파라서치 / DB base + 저장 이력 6건 +60~69만
웹 (버그 시) 저장 이력 5772슬롯 123건 +347만
저장 이력 OFF (전종목) 133건+

3.2 근본 원인

웹 API가 폼 숫자만 params에 넣고, DB의 TRIGGER 플래그를 빠뜨렸다.

플래그 DB (파라서치) 웹 누락 시 엔진 기본값 영향
skip_hts_scan_dupes False True (저장 이력 시 setdefault) 거래 수 결정적
use_rsi_filter False True 소폭
use_daily_range_filter False True 이 기간 거의 없음
use_high_chase_filter False True 이 기간 거의 없음

재현 (5/18~5/29, 저장 이력 ON):

설정 거래
웹 params만 (플래그 없음) 135건
DB 플래그 전부 반영 6건
skip_hts=True + 선택필터 ON (웹 버그와 유사) 123건
skip_hts=True only 161건

3.3 수정 (backtest_web.py)

tail_trigger_flags를 DB _def에서 병합:

  • skip_hts_scan_dupes, use_intraday_drop, use_ma20_filter
  • use_rsi_filter, use_daily_range_filter, use_high_chase_filter
  • bar_chg_min_pct, bar_chg_max_pct

응답 JSON params에도 동일 플래그 포함 (디버깅용).

확인: 재시작 후 응답에 skip_hts_scan_dupes: false 등이 보이고, 웹·파라서치 거래 수가 맞아야 함.


4. TAIL_SKIP_HTS_SCAN_DUPES / skip_hts_scan_dupes 상세

4.1 이름

이름 위치
TAIL_SKIP_HTS_SCAN_DUPES DB env_config
skip_hts_scan_dupes Python params dict

같은 스위치 — TRIGGER에서 HTS SCAN 조건을 또 검사할지 여부.

4.2 HTS A만 해당 (B·C 아님)

HTS 내용 TRIGGER 재검사
A 직전봉 종가 대비 -10% ~ -1.5% skip_hts가 제어
B 체결강도 (SCAN만)
C 3봉 전 거래량 배수 (SCAN만)

코드 (tail_engine.py):

if not skip_hts and i >= 1:
    bar_chg = (cl - prev_cl) / prev_cl * 100.0
    if bar_chg < bar_chg_min_pct or bar_chg > bar_chg_max_pct:
        return ("탈락-봉등락", ...)  # TAIL_BAR_CHG_MIN/MAX_PCT 기본 -10 ~ -1.5
의미 저장 이력 ON 시 거래 (5/18~5/29)
True HTS A 생략 (SCAN 신뢰) 123161건
False HTS A 재검사 (이중 필터) 56건

4.3 min_drop_rate / 회복률과의 차이

HTS A min_recovery_ratio
비교 직전 3분봉 종가 → 현재 종가 당일 저점~고가 범위 내 회복
별도 축 TAIL_USE_INTRADAY_DROP=true시가→저점 낙폭 (HTS A와 다름) 항상 TRIGGER에서 검사

4.4 엔진 설계 의도

  • 저장 이력 ONskip_hts=True 권장 (중복 필터 방지)
  • 전종목 백테 (이력 OFF) → skip_hts=False로 HTS A를 TRIGGER에서 대신 검사

현재 DB는 False → 매우 보수적 (~0.5건/일).


5. 기타 TRIGGER 플래그 (그리드 미포함, DB 고정)

env 키 params 키 True = False =
TAIL_USE_RSI_FILTER use_rsi_filter RSI≥임계값 탈락 (적음) RSI 검사 안 함
TAIL_USE_DAILY_RANGE_FILTER use_daily_range_filter 일중 변동폭 초과 탈락 검사 안 함
TAIL_USE_HIGH_CHASE_FILTER use_high_chase_filter 고점 추격 탈락 검사 안 함
TAIL_USE_MA20_FILTER use_ma20_filter MA20 필터 검사 안 함
TAIL_USE_INTRADAY_DROP use_intraday_drop 시가→저점 낙폭 검사 검사 안 함

use_*_filter: True = 필터 켜짐 → 기회
skip_hts: True = 검사 생략 → 기회 (이름만 반대로 느껴짐)


6. tail_param_search.py fast 그리드 (2026-05-30)

6.1 fast 모드 축 (768조합, 12분)

min_drop_rate 2%, 3%
min_recovery_ratio 40%, 45%, 50%
tail_ratio_min 1.0, 1.5
max_rec_3m 85%, 90%
shoulder_min_high 0.3%, 0.5%
shoulder_cut_pct 0.2%, 0.3%
stop_atr_mult 1.5, 2.0
target_atr_mult 1.5, 2.0
atr_tp_max_pct 0.8%, 1.0%

DB 고정 (그리드 밖): skip_hts_scan_dupes, use_rsi_filter, RSI 기간, 쿨다운, tail_pct_min 등.

6.2 --apply 동작

  • 그리드 축만 DB 반영 (전체 apply_cfg 덮어쓰기 방지)
  • 진입·청산 env 키 예: MIN_DROP_RATE, MIN_RECOVERY_RATIO_SHORT, TAIL_RATIO_MIN, MAX_RECOVERY_RATIO_3M, SHOULDER_*, STOP/TARGET_ATR_MULTIPLIER_TAIL, TAIL_ATR_TP_MAX_PCT

6.3 최근 실행 결과 (2026-05-18 ~ 2026-05-29, 830~1530, 저장 이력)

실행 조합 1위 거래 1위 손익 env_id
fast 192 (진입+청산) 192 6건 +609,166 206
fast 768 (+ tail_ratio, max_rec_3m) 768 5건 +690,252 207

768조합 대부분 동률skip_hts=False 병목 때문에 낙폭·회복·청산축을 바꿔도 거래 수가 거의 안 변함.
회복률 50%만 3건 등으로 더 줄어드는 예외 있음.

1위 (env 207) 적용값:

  • 낙폭 2%, 회복 40%, 꼬리/몸통 1.0, 3분 회복 상한 90%
  • 어깨 0.5%/0.2%, ATR 2.0/2.0, 익절 상한 1.0%

6.4 표준 실행 명령

python3 kis_trader/backtest/tail_param_search.py \
  --start 2026-05-18 --end 2026-05-29 \
  --time-start 830 --time-end 1530 \
  --min_trades 1 --apply

결과 JSON: kis_trader/backtest/results/search_tail_fast_YYYYMMDD_HHMMSS.json


7. RSI 14

  • 고정 아님env_config.RSI_PERIOD (DB 기본 14)
  • 꼬리 3분봉 + RSI(14) ≈ 42분 창 과열 필터
  • 스캘핑 RSI(3)와 별개

8. max_daily 함정

  • 종목당 하루 N회 (계좌 전체 합산이 아님)
  • 전종목 + 완화 진입 시 거래 수 폭증 가능

9. 가상 거래 내역 (최근 200건) — 정렬 수정

9.1 문제

  • all_trades[-200:] — 종목 코드 순 append → 최근 200건이 아님
  • 프론트 .reverse()만으로는 매도 시각 최신순 불가

9.2 수정 (backtest_web.py)

Python: _trades_recent_first(trades, 200)

  • exit_time / sell_time / sell_date 기준 내림차순 후 200건
  • 적용: 실거래, 스캘핑, 꼬리, 돌파 API

JavaScript: tradesNewestFirst(trades)

  • 모든 “가상 거래 내역 (최근 200건)” 테이블에 적용
  • 맨 위 = 가장 최근 매도

10. 웹 백테스트 체크리스트 (파라서치와 맞출 때)

항목 맞춰야 할 값
기간 --start / --end 동일
TF 3분 (ws_candles)
매수 시간 830~1530 (웹 tl_ts / tl_te)
저장 이력 ON/OFF 동일 (`universe=history
TRIGGER 플래그 응답 JSON skip_hts_scan_dupes 등 확인
서버 backtest_web.py 수정 후 재시작

경고 문구 (summary.universe_warning):

  • 저장 이력 ON인데 거래 > 25건 → 파라미터/플래그 불일치 의심
  • 수정 후에는 6건 전후가 정상 (skip_hts=False 기준)

11. 거래 빈도 목표 (주 5~10건) 시 권장

skip_hts만 토글하면 6건 ↔ 123건 cliff — 중간값 없음.

방향 조치
A (권장) 저장 이력 ON + TAIL_SKIP_HTS_SCAN_DUPES=true 고정, 진입 수치(min_drop, recovery, tail_ratio)로 조절
B skip_hts=False 유지 + 진입 수치 완화 (coarse/full)
C fast 그리드에 skip_hts 축 추가 → 768×2=1536조합 (모드 선택용)

12. 관련 env 키 (config_schema)

TAIL_SKIP_HTS_SCAN_DUPES
TAIL_USE_INTRADAY_DROP
TAIL_USE_MA20_FILTER
TAIL_USE_RSI_FILTER
TAIL_USE_DAILY_RANGE_FILTER
TAIL_USE_HIGH_CHASE_FILTER
TAIL_BAR_CHG_MIN_PCT / TAIL_BAR_CHG_MAX_PCT
MIN_DROP_RATE / MIN_RECOVERY_RATIO_SHORT
TAIL_RATIO_MIN / MAX_RECOVERY_RATIO_3M
SHOULDER_MIN_HIGH_PCT / SHOULDER_CUT_PCT
STOP_ATR_MULTIPLIER_TAIL / TARGET_ATR_MULTIPLIER_TAIL
TAIL_ATR_TP_MAX_PCT

13. 변경 이력 요약

날짜 변경
2026-05-30 tail_backtest_common.py — 웹·파라서치 공통 로더
2026-05-30 웹 API TRIGGER 플래그 DB 병합 (123 vs 6건 버그)
2026-05-30 fast 그리드: 진입(min_drop, recovery, tail_ratio, max_rec_3m) + 청산, 768조합
2026-05-30 가상 거래 내역 매도 시각 최신순 정렬

14. 참고 파일

  • kis_trader/engine/tail_engine.py — SCAN/TRIGGER 주석, _eval_tail_buy_at_index
  • kis_trader/backtest/tail_param_search.pyPARAM_GRIDS["fast"]
  • backtest_web.py/api/backtest/tail, _trades_recent_first, tradesNewestFirst()
  • docs/TAIL_BACKTEST_WEB_VS_PARAM_SEARCH.md — 꼬리 도메인·skip_hts·어깨컷 상세
  • docs/BACKTEST_ALIGNMENT_FINAL.md코드 변경·실수 목록·다른 전략 적용 체크리스트 (최종)