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:
314
docs/TAIL_BACKTEST_WEB_VS_PARAM_SEARCH.md
Normal file
314
docs/TAIL_BACKTEST_WEB_VS_PARAM_SEARCH.md
Normal file
@@ -0,0 +1,314 @@
|
||||
# 꼬리잡기 백테스트 — 웹 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`):
|
||||
|
||||
```python
|
||||
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 신뢰) | ~123~161건 |
|
||||
| **False** | HTS A **재검사** (이중 필터) | **~5~6건** |
|
||||
|
||||
### 4.3 `min_drop_rate` / 회복률과의 차이
|
||||
|
||||
| | HTS A | `min_recovery_ratio` 등 |
|
||||
|--|-------|-------------------------|
|
||||
| 비교 | **직전 3분봉 종가 → 현재 종가** | **당일 저점~고가 범위 내 회복** |
|
||||
| 별도 축 | `TAIL_USE_INTRADAY_DROP=true` 시 **시가→저점 낙폭** (HTS A와 다름) | 항상 TRIGGER에서 검사 |
|
||||
|
||||
### 4.4 엔진 설계 의도
|
||||
|
||||
- **저장 이력 ON** → `skip_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조합, ~1~2분)
|
||||
|
||||
| 축 | 값 |
|
||||
|----|-----|
|
||||
| `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 표준 실행 명령
|
||||
|
||||
```bash
|
||||
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|all`) |
|
||||
| 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.py` — `PARAM_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` — **코드 변경·실수 목록·다른 전략 적용 체크리스트 (최종)**
|
||||
Reference in New Issue
Block a user