Files
kis_bot/docs/like_mcp.md/MODIFICATION_GUIDE.md
Your Name 5e44b86f8b fix(시세): LS spill-only·토큰 파일캐시·호가 snap_time 3초컷
- _feed_fallback 미러 OFF, LS cap/grace/hold RAM을 KIS·키움 spill과 정합
- LS 접근토큰 .ls_token_cache_*.json (재시작 재사용, revoke 루프 없음)
- 호가 RAM을 틱과 동일 LIVE_FEED_FALLBACK(snap_time)로 컷, 필터 max_age=0은 유지
- 익절 지정가 로그에 실제 호가 벤더(kis/kiwoom/ls 1·2·3차) 표기

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-27 15:21:21 +09:00

386 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# kis_bot 코드 수정 가이드 — AI 에이전트 필수 체크리스트
> **이 문서의 목적**: 코드 수정 시 사이드 이펙트를 빠짐없이 잡기 위한 절차 정의.
> 두 참조 문서와 함께 사용하세요.
> - 📦 [code_architecture.md](./code_architecture.md) — 파일 간 import 의존성 맵
> - 🗄️ [db_erd.md](./db_erd.md) — DB 테이블 스키마 & ERD
---
## ⚡ 핵심 원칙
```
코드를 수정하기 전에,
반드시 아래 섹션에 해당하는 grep을 먼저 실행하고
결과를 확인한 뒤 수정 범위를 확정한다.
```
**grep을 생략해도 되는 유일한 경우**: 주석/docstring 텍스트만 바꾸는 경우.
### 실매 코어 스모크 (중요 코드 수정 후 필수 1회)
엔진/호가/WS(구독·해제·spill·갭보정·freeze)/env/스키마를 고친 뒤에는 완료 보고 전:
```bash
python3 -u scripts/test_live_execution_validation.py
# 로그: logs/test_live_execution_validation_YYYYMMDD_HHMMSS.log
```
통과: `최종: 통과` + `👑 [최종 판정] 완결!`. 실패 시 고치고 1회만 재실행. 룰: `.cursor/rules/live-execution-validation.mdc`
---
## 🔍 수정 유형별 필수 grep 목록
### 0. 조건검색 유니버스 (LS / 키움) — 시드·재시도
> LS 공백·job 이벤트 수정 시. 상세: [code_architecture.md 수동 노트](./code_architecture.md)
```bash
grep -rn "LS_T1859_EMPTY_RETRY\|t1859\|_refresh_empty\|STALE_RESYNC\|rows_prefer" /home/hoon/kis_bot/kis_trader/network --include="*.py"
grep -rn "condition_common\|condition_job_events\|CAND_LIMIT" /home/hoon/kis_bot --include="*.py" -l
```
**체크리스트**:
- [ ] LS: AFR≠풀덤프 → RAM0 재시드(`LS_T1859_EMPTY_RETRY_SEC`, 기본 **1**·TR_GAP≥1.1). **0=OFF** (무한 REST 아님)
- [ ] 키움 CNSRREQ / LS t1859 시드 경로 **합치지 말 것** (공통은 `condition_common` JOB 플래그만)
- [ ] 빈 t1859 → history INSERT 금지
- [ ] `database.py` ENV 키 목록 + `env_config_ext` 저장 검증
- [ ] 웹 조건 이벤트/정합 탭 API (해당 시)
---
### 1. ENV 파라미터 추가/변경/삭제
> 예: `MOMENTUM_E_MIN_CHG_PCT`, `SCALP_STOP_LOSS_PCT` 등
```bash
# ① env 키 이름 직접 참조
grep -r "MOMENTUM_E_MIN_CHG_PCT" /home/hoon/kis_bot --include="*.py" -l
# ② params dict snake_case 키
grep -r "e_min_chg_pct" /home/hoon/kis_bot --include="*.py" -l
# ③ database.py CREATE TABLE 컬럼 정의
grep -n "MOMENTUM_E_MIN_CHG_PCT" /home/hoon/kis_bot/database.py
# ④ env_keys.py 등록 여부
grep -r "E_MIN_CHG_PCT" /home/hoon/kis_bot/kis_trader/engine/momentum_env_keys.py
```
호가 필터 나이: `WS_ORDERBOOK_FILTER_MAX_AGE_SEC`(기본 0=마지막 RAM) ≠ 저장 TTL `WS_ORDERBOOK_TICK_MAX_AGE_SEC`.
후처리 TPE 진입 격자: `OPTUNA_OB_ENTRY_SPREAD_*` / `RATIO_*` / `ASK_MULT_*` (`optuna_common.ensure_optuna_gate_env_defaults`).
꼬리 TPE 진입모드는 탐색 축이 아님(웹 체크 → 스터디 고정/순차 2회).
돌파 TPE 손절모드(fixed/atr)도 탐색 축이 아님(웹 체크 → 스터디 고정/순차 2회).
align 스터디는 `limit_atr_mult` 제외. fixed 스터디는 `atr_sl_*` 제외.
`*_SKIP_HTS_SCAN_DUPES` true 금지.
유령정리: `INQUIRE_PSBL_RVSECNCL_BEFORE_GHOST` · 잔고는 `hldg_qty`(보유)와 `ord_psbl_qty`(매도가능) 분리. 매도가능 0 ≠ ghost.
시세·호가 읽기 폴백 (실매=옵투나 같은 읽기):
- `LIVE_TICK_PROVIDER` / `LIVE_OB_PROVIDER` 운영설정 **셀박** (`kiwoom`|`kis`). LS는 1차 아님(3차 spill).
- `LIVE_FEED_FALLBACK_MAX_AGE_SEC`(기본 3) = **그 틱의 체결시각(FID20/chetime) vs 이 서버 지금**. 1·2·3차 동일. 벤더끼리 시각 비교 금지.
- 호가 RAM도 동일 초: `snap_time`(KIS BSOP_HOUR / LS hotime / 키움 FID20) lag 초과 시 미반영·`get(max_age>0)` None. `FILTER_MAX_AGE=0`(마지막 RAM)은 snap 컷 안 함.
- `TRIGGER_FEED_DETAIL_LOG`(기본 true) = 매수체크 `🔍` 로그에 틱1차설정/실제(kis|kiwoom|ls)·px·틱타임·호가 bid/ask/or 꼬리.
- `BT_FEED_DETAIL_LOG`(기본 true) / `BT_FEED_DETAIL_LOG_MAX`(기본 40) = 옵투나·백테에도 동일 축: 틱/호가 로드 벤더 비율 INFO + TRIGGER 샘플. `entry_source=ws_ticks:kis` 등.
- 호가 후처리(`recommend_orderbook_parameters`): 코어 TPE 호가OFF와 무관. `[호가후처리]` DB벤더·`[호가후처리샘플]` bid/ask/src 가 **후처리 구간**에 나옴.
- 분봉 쓰레기=`CANDLE_GARBAGE_FALLBACK`(기본 true). 그 분 그 소스 틱 0건이거나 전부 **봉 끝시각** 대비 읽기나이 초과면 그 WS 봉을 구멍 → 다음 소스 **봉 통째**. 한 봉 안 키움+한투 혼합 금지. 실매 링이 그 소스·그 분을 커버 못하면 유지.
- 한투 호가=`WS_ORDERBOOK_SAVE_KIS`: **2번째 앱키(`KIS_APP_KEY_OB_REAL`) 전용 세션만**. 키 없거나 start 실패 시 메인에 H0STASP0 붙이지 않음(시세 41 합산 금지).
`WS_ORDERBOOK_SAVE_MODE=tick` 이면 메인 `H0STCNT0` 체결마다 2키 OB RAM을 `on_orderbook_tick_sync``ws_orderbook`(source=`kis_h0stasp0`). 키움 0B↔0D 틱동기와 동일.
- `LS_FEED_FALLBACK_SUBSCRIBE`(기본 false) → spill/permanent만. ON=후보보유grace LS RAM 미러(레거시).
- `LS_WS_TICK_SAVE``ls_ws_ticks` INSERT. 게이트=`_ls_is_subscribed`(구독 전체, 호가와 동일). 봉·VI만 `should_persist_ls`(영구).
- `BT_TICK_LS_THIRD_FALLBACK`(기본 true) → 백테/옵투나 `load_breakout_ticks_by_code` 가 같은 초 1·2차 없을 때 `ls_ws_ticks` 채움. 나이=`LIVE_FEED_FALLBACK_MAX_AGE_SEC`.
- 키움 RAM/리스너/봉 skip = 읽기 나이. `ws_ticks` 적재는 기본 전부(`KIWOOM_TICK_LIVE_MAX_LAG_SEC=0`). 같이 내리지 말 것. `ws_ticks.channel`(기본 ws)은 봉과 같은 경로 라벨. ka10007을 틱 INSERT 하지 말 것.
- 시세 REST 키 = `KIWOOM_WS_FORCE_REAL`(기본 true) 실키. `_get_kiwoom_creds``KIS_MOCK` 모의키로 가면 안 됨 (8001).
- `LS_GAP_FILL_CANDIDATES` 기본 OFF.
- `LS_WS_TICK_SAVE` 기본 false 가능(운영 ON 권장 검증기간). `_tick_recorder` 를 같이 끄면 호가 틱동기도 0건. `LS_WS_ALSO_HOGA` 기본 true(UH1 구독). `LS_WS_ORDERBOOK_SAVE` 기본 true(구독 종목). 호가필터 `FILTER_MAX_AGE=0` 을 저장 TTL 과 다시 합치지 말 것.
- 매수 루프 전 종목 REST 금지. 매도: 3초 체인 → last-RAM(`SELL_WS_LAST_RAM_MAX_AGE_SEC`) → 직전가 캐시 → 4차 키움 `ka10007`+쿨다운. 한투 60초 캐시 금지. 보유 KIS는 41슬롯 pin + `WS_TICK_GRACE_SEC`(후보 KIS grace 아님).
- MM 체결 알림: `시세: kiwoom(1차) | 호가: ls(3차)`.
**확인 후 수정해야 할 곳 체크리스트**:
- [ ] `database.py` — CREATE TABLE에 컬럼 추가
- [ ] `kis_trader/engine/momentum_env_keys.py``MOMENTUM_CONFIG_KEYS` 등록
- [ ] `kis_trader/engine/momentum_engine.py``get_momentum_defaults()` 로드 및 return dict
- [ ] `kis_trader/engine/momentum_hts_logic.py` — 실제 로직에서 사용
- [ ] `kis_trader/backtest/param_search_momentum.py` — 그리드/텀 탐색 범위
- [ ] `kis_trader/backtest/param_search_apply_snapshot.py` — 스냅샷 적용 매핑
- [ ] `kis_trader/backtest/optuna_search_space.py` — Optuna 탐색 공간 (해당 시)
---
### 2. DB 테이블 컬럼 추가/변경/삭제
```bash
# ① 해당 테이블 참조 파일 목록
grep -r "active_trades" /home/hoon/kis_bot --include="*.py" -l
# ② 컬럼명 직접 참조
grep -rn "avg_buy_price" /home/hoon/kis_bot --include="*.py"
# ③ INSERT/UPDATE 구문 (컬럼 순서에 민감)
grep -rn "INSERT INTO active_trades" /home/hoon/kis_bot --include="*.py"
grep -rn "UPDATE active_trades" /home/hoon/kis_bot --include="*.py"
```
**확인 후 수정해야 할 곳 체크리스트**:
- [ ] `database.py``CREATE TABLE`, `ALTER TABLE`
- [ ] `database.py` — INSERT/UPDATE 쿼리 전부
- [ ] 해당 테이블 참조 파일들 — SELECT 컬럼 파싱 로직
---
### 3. 함수/메서드 시그니처 변경
```bash
# ① 함수 정의 위치
grep -rn "def eval_momentum_hts_buy_at_index" /home/hoon/kis_bot --include="*.py"
# ② 함수 호출 위치 전부
grep -rn "eval_momentum_hts_buy_at_index" /home/hoon/kis_bot --include="*.py"
# ③ import 위치
grep -rn "from.*import.*eval_momentum_hts_buy_at_index" /home/hoon/kis_bot --include="*.py"
```
**확인 후 수정해야 할 곳 체크리스트**:
- [ ] 함수 정의 파일
- [ ] 함수를 import하는 모든 파일
- [ ] 함수를 호출하는 모든 파일
---
### 4. 전략 params dict 키 추가/변경
> 예: `mom_vol_mult`, `e_min_chg_pct` 같은 params 딕셔너리 키
```bash
# ① params에서 꺼내는 곳
grep -rn '"e_min_chg_pct"' /home/hoon/kis_bot --include="*.py"
# ② params에 넣는 곳 (파일 목록)
grep -rn "e_min_chg_pct" /home/hoon/kis_bot --include="*.py" -l
```
**확인 후 수정해야 할 곳 체크리스트**:
- [ ] 엔진 `get_*_defaults()` 함수 — params 생성
- [ ] 엔진 로직 파일 — `params.get("key", default)`
- [ ] 파라미터 서치 — 그리드 정의, 스냅샷 적용, `_to_env` 매핑
- [ ] Optuna 탐색 공간 — `_suggest_from_grid` 대상 그리드
---
### 5. 클래스/모듈 이름 변경 또는 파일 이동
```bash
# ① import 참조 전부
grep -rn "from kis_trader.engine.momentum_hts_logic import" /home/hoon/kis_bot --include="*.py"
# ② 문자열로 언급되는 곳 (동적 import, 로그 등)
grep -rn "momentum_hts_logic" /home/hoon/kis_bot --include="*.py"
```
---
### 6. 웜업(Warmup) 로직 변경
```bash
# ① 웜업 관련 함수 목록
grep -rn "candle_warmup" /home/hoon/kis_bot --include="*.py" -l
grep -rn "REST_WARMUP" /home/hoon/kis_bot --include="*.py" -l
# ② 웜업 bars env 키
grep -rn "BACKTEST_CANDLE_WARMUP_BARS" /home/hoon/kis_bot --include="*.py"
grep -rn "BACKTEST_REST_WARMUP_BARS" /home/hoon/kis_bot --include="*.py"
```
---
### 7. HTS 조건식 로직 변경
```bash
# ① skip_hts_scan_dupes 관련
grep -rn "skip_hts" /home/hoon/kis_bot --include="*.py" -l
# ② hts_trigger 설정
grep -rn "hts_trigger_defaults" /home/hoon/kis_bot --include="*.py"
grep -rn "trigger_e_confirm" /home/hoon/kis_bot --include="*.py"
```
---
## 📋 수정 전 표준 워크플로우
```
1. 수정 요청 파악
└─ 어떤 유형의 변경인가? (ENV / DB / 함수 / 파라미터 / 파일이동 ...)
2. code_architecture.md 에서 연관 파일 1차 파악
└─ 허브 파일 Top 20 체크
└─ 전략별 파일 분류에서 관련 전략 파일 확인
└─ DB 테이블 ↔ 코드 파일 연관 맵 확인
3. 위 섹션의 grep 명령어 실행 (반드시)
└─ 발견된 파일 목록 확정
4. db_erd.md 에서 DB 스키마 확인 (DB 관련 변경 시)
└─ 컬럼 타입, PK, 인덱스 확인
5. 수정 범위 확정 후 코드 수정
6. 문법 검사
└─ python3 -m py_compile <수정한 파일>
7. (필수) docs/ 문서 갱신 — 아래 조건 중 하나라도 해당하면
└─ 파일 추가/삭제/이동이 있었다면 → code_architecture.md 재생성
└─ DB 테이블/컬럼이 변경됐으면 → db_erd.md 재생성
└─ ENV 키/params 키가 추가됐으면 → MODIFICATION_GUIDE.md 체크리스트 갱신
```
---
## 🚨 이 프로젝트 특이 사항 (절대 무시 금지)
### 키움 0B catch-up / REG refresh
```
KIWOOM_TICK_LIVE_MAX_LAG_SEC (기본 5): FID20 vs 수신시각 lag 초과 → RAM·봉·호가동기·ws_ticks 미반영.
KIWOOM_TICK_TIME_MAX_LAG_SEC (기본 120): 같은 FID20 가 이 초 이상일 때만 아침 동결 예외.
KIWOOM_WS_REG_REFRESH (기본 1): 0은 LOGIN 일괄 REG 첫 청크만. 장중 추가 REG는 항상 1.
```
### SKIP_HTS_SCAN_DUPES 규칙
```
`*_SKIP_HTS_SCAN_DUPES` 는 사용자가 언급하기 전까지 false 유지.
임의로 true로 바꾸지 말 것.
```
### HTS 조건식과 코드 분리
```
HTS는 후보 유니버스 참고용.
그리드를 HTS 밴드에 맞추라고 강제하지 말 것.
```
### 백테스트 웜업 전략별 분리
```
모멘텀 웜업 변경이 스캘핑/꼬리잡기 웜업에 영향을 주지 않는다.
각 전략 웜업 함수는 독립적이다:
- momentum_backtest_candle_warmup_bars() ← momentum_backtest_common.py
- scalp_backtest_candle_warmup_bars() ← scalping_backtest_common.py
- tail_backtest_candle_warmup_bars() ← tail_backtest_common.py
기간 봉 로드는 fetch_ws_candles_by_code_bulk (종목 for 금지).
웜업 prepend 만 fetch_ws_candles_warmup_before (종목당).
CANDLE_GARBAGE_FALLBACK 끄지 말 것. history_source=ls 는 별도.
```
### env 키 저장 경로
```
database.py의 CREATE TABLE 컬럼에 있는 키 → 해당 config_* 테이블 컬럼
database.py의 CREATE TABLE에 없는 키 → env_config_ext (key-value 테이블)
새 env 키 추가 시 두 경로 중 어디에 저장될지 확인 필요.
Optuna 후처리(실매 엔진 비영향): OPTUNA_POST_TOP_N / INCLUDE_MODE / INCLUDE_LIVE /
OB·WHIPSAW trial 수 · OPTUNA_OB_COMBO_TRIALS_* · OPTUNA_OB_AXIS_TRIALS · 진입/익절/STOP 탐색 범위 —
ensure_optuna_gate_env_defaults → env_config_ext.
모듈: kis_trader/backtest/optuna_postprocess_topn.py · optuna_orderbook_recommend.py
· optuna_rerun_postprocess.py (구 JSON 「이 잡 후처리 재실행」. 호가스냅<3 `not_enough_trades` 는 구JSON이 아님 — 재실행해도 스냅 없으면 8방 동일)
후처리 앵커: gated TopN + stable TopN(표/적용) + mode + live(참고). 합의·과적합 가점은 gated+mode만.
웹 Optuna: 학습/gated/stable Top5 행에 안정점수(↑·만점없음·원) + 과적합%(↓·0~100). 1위 비교표·mode_combo 실측행은 표시 안 함.
**본 TPE 호가·휩쏘 (2026-08-23 A):** `OPTUNA_TPE_INCLUDE_ORDERBOOK`/`WHIPSAW` 기본 true →
trial suggest 에 `_orderbook_filter_enabled`·spread/ratio/ask · `whipsaw_*`(돌파 제외).
CLI `--orderbook-filter off` 여도 TPE 시 호가 스냅 로드. 사후 8방「필터후」는
`OPTUNA_POST_FORCE_OB_WHIPSAW` 없으면 OFF (`_run_ob_whipsaw_full`). 축 변경 시 **새 study-name**.
**웹 스캘 진입 스냅 (2026-08-23):** `run_scalping_backtest_web_aligned` 도 모멘텀/돌파/꼬리처럼
`resolve_trigger_snapshots_for_backtest` → `_bt_orderbook_by_code` (표시용 enrich와 별개).
미로드+호가ON+REJECT_IF_EMPTY 가 웹 0건 구멍이었음.
**호가 UI=DB (2026-08-23):** 모멘텀 `mom_ob_filter` HTML `checked`+「ON」고정 금지.
꼬리/돌파 `tl_/bo_ob_filter` 도 기본 unchecked → `/api/env/params` 실효값으로만 체크.
스캘은 `bt_ob_ro_on` 읽기전용(원래 DB 반영).
진입 격자(2026-08-15): OPTUNA_OB_ENTRY_SPREAD 0.1~8 / RATIO 0.05~1.5 / ASK_MULT 1~80 / LOOKBACK 30분.
8방 스택(2026-08-16/17): OPTUNA_OB_COMBO_TRIALS_SINGLE=150 / DOUBLE=200 / TRIPLE=250.
적용=방 단위(build_combo_env_patch). 합의=8방 중 median PnL 최고 방(_consensus_from_anchors).
OPTUNA_OB_AXIS_TRIALS=500 은 축독립 잔여키(8방 미사용).
실매 필터 나이: WS_ORDERBOOK_FILTER_MAX_AGE_SEC(0) ≠ WS_ORDERBOOK_TICK_MAX_AGE_SEC(저장).
사진 한 장도 없으면: WS_ORDERBOOK_FILTER_REJECT_IF_EMPTY(기본 true) → 필터 ON일 때 안 삼.
```
### e_min_chg_pct 단위
```
e_min_chg_pct 는 퍼센트 단위 (0.5 = 0.5%).
코드 내부에서 / 100.0 변환 후 사용.
```
---
## 🛠️ 자주 쓰는 grep 원라이너 모음
```bash
# ENV 키 전체 참조 추적
grep -rn "ENV_KEY_NAME" /home/hoon/kis_bot --include="*.py"
# DB 테이블 참조 파일 목록
grep -rl "table_name" /home/hoon/kis_bot --include="*.py"
# params dict 특정 키 추적
grep -rn '"param_key"' /home/hoon/kis_bot --include="*.py"
# 함수 호출 추적
grep -rn "function_name(" /home/hoon/kis_bot --include="*.py"
# import 경로 추적
grep -rn "from module.path import" /home/hoon/kis_bot --include="*.py"
# 특정 전략 파일만 검색
grep -rn "keyword" /home/hoon/kis_bot/kis_trader/engine --include="*momentum*"
# SQL 쿼리 내 테이블 참조
grep -rn "FROM table_name\|INTO table_name\|UPDATE table_name" /home/hoon/kis_bot --include="*.py"
```
---
## ✅ 이 가이드 + 두 MD 파일이면 충분한가?
| 상황 | 충분 여부 |
|------|----------|
| ENV 파라미터 추가/변경 | ✅ 충분 (위 체크리스트 따르면) |
| DB 컬럼 추가/변경 | ✅ 충분 (grep 필수 실행 시) |
| 함수 시그니처 변경 | ✅ 충분 |
| 전략 간 공유 유틸 변경 | ✅ `code_architecture.md` 허브 파일 + grep |
| 동적 import 패턴 변경 | ⚠️ grep으로만 확인 가능 (MD로 탐지 불가) |
| 런타임에만 나타나는 의존 | ❌ 실제 실행해봐야 함 |
> **결론**: grep을 반드시 실행한다는 전제 하에,
> 이 세 파일(MODIFICATION_GUIDE.md + code_architecture.md + db_erd.md)이면
> 런타임 의존성을 제외한 대부분의 케이스에서 누락 없이 수정 가능합니다.
---
## 🔄 docs/ 문서 갱신 규칙 (필수)
### 갱신 트리거 조건
| 변경 유형 | 갱신할 문서 |
|-----------|------------|
| .py 파일 추가/삭제/이동 | `code_architecture.md` |
| import 구조 변경 | `code_architecture.md` |
| DB 테이블/컬럼 변경 | `db_erd.md` |
| ENV 키/params dict 키 추가 | `MODIFICATION_GUIDE.md` 체크리스트 |
| 전략 추가/삭제 | `code_architecture.md` + `MODIFICATION_GUIDE.md` |
### 갱신 방법
```bash
# code_architecture.md 재생성 (AST 분석 스크립트)
cd /home/hoon/kis_bot && python3 /tmp/gen_arch_md.py
# db_erd.md 재생성 (DB DDL 추출 → MD 변환)
cd /home/hoon/kis_bot && python3 /tmp/gen_erd_md.py
```
### 동적 import 안전성 (2026-08-01 감사 결과)
이 프로젝트의 동적 import는 **총 7건**이며, 전부 무해한 패턴입니다:
- 표준 라이브러리 인라인 호출: `__import__("json")`, `__import__("time")` — 3건
- 테스트 모듈 로딩: `importlib.import_module("_test_...")` — 2건
- 레거시 (remove/ 폴더, 비활성) — 2건
**핵심 매매/엔진/백테 코드에서 동적 import로 전략을 분기하는 패턴은 없습니다.**
코드 수정 불필요.