Files
kis_bot/kis_trader/README.md
Your Name 0ecac7cb95 이번에 들어간 내용
한투 호가 = 2번째 앱키 전용
키 없거나 start 실패 시 메인에 H0STASP0 안 붙임. 운영설정 WS_ORDERBOOK_SAVE_KIS 빨간 danger.

LS RAM 합집합
후보∪보유∪영구∪grace. sync_targets와 split reconcile 둘 다. 틱 DB 영구 게이트는 그대로.

분봉 쓰레기 → 다음 소스 봉 통째
그 분 틱 0건이거나 전부 봉끝 대비 LIVE_FEED_FALLBACK_MAX_AGE_SEC 초과면 구멍. 메인 WS → 2차 → LS → REST → rollup. CANDLE_GARBAGE_FALLBACK 기본 true.

파일: feed_fallback.py(신규), ws_manager.py, kis_ws.py, candle_series.py, bt_candle_source.py, live_config_schema.py, database.py, 스모크, MD 2개.

같은 ws_manager/database/kis_ws/live_config에는 직전 커밋 이후 쌓여 있던 시세 폴백·ENV 키 정리도 같이 들어갔습니다. 파일 단위로 나눌 수 없어서입니다.
2026-08-19 22:11:31 +09:00

148 lines
7.6 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_trader — 통합 트레이딩 봇
스캘핑(`SCALP`) + 꼬리잡기(`SHORT`, Tail Catch) 두 전략을 **단일 프로세스**에서
**각자 독립 쓰레드**로 돌리는 통합 봇. KIS REST/WebSocket 호출을 단일 허브로
묶고, 주문은 단일 `OrderManager` 를 경유해 ODNO 기반으로 추적한다.
## 디렉터리
```
kis_trader/
├── main.py # 오케스트레이터 (각 전략을 쓰레드로 기동)
├── utils/
│ ├── env.py # env_config (DB) + os.environ 통합 조회
│ ├── logger.py # 공용 로거 / 안전 JSON 저장 / 알림(MM/TG)
│ └── request_handler.py # SafeRequest: rate-limit + 재시도
├── network/
│ └── ws_manager.py # 단일 WS 허브 (kis_ws 재사용, 구독 ref-counting)
├── strategies/
│ ├── base.py # BaseStrategy (threading.Thread)
│ ├── scalping.py # scalping_engine 래퍼
│ └── tail_catch.py # tail_engine 래퍼
├── execution/
│ ├── kis_client.py # 통합 KIS REST 클라이언트 (ODNO 반환)
│ └── order_manager.py # Master Executor (종목 Lock + 실잔고검증)
├── database/
│ └── db_manager.py # TradeDBExt: 기존 TradeDB + orders 테이블
└── backtest/
└── backtest_web.py # 기존 backtest_web.py 재호출 래퍼
```
## 아키텍처 한 눈에
```
[SCALP 쓰레드] ──┐
│─▶ OrderRequest ─▶ OrderManager ─▶ KISClient ─▶ KIS API
[SHORT 쓰레드] ──┘ │ │ │
│ │ └── DB(orders) ← ODNO PK
│ └───── DB(active_trades, trade_history)
└──────── 실잔고 검증 (get_broker_holdings)
공유 인프라 (1개 인스턴스)
• KISClient : 토큰/Throttle/재시도
• WSManager : 단일 WS 세션, 구독 ref counting
• TradeDBExt : orders 테이블 + 기존 TradeDB
• OrderManager : 종목 Lock, 동일 종목 다전략 정책, ODNO 저장
```
## 왜 이 구조인가
기존 문제 | 새 구조에서의 해결
-- | --
봇이 잡고 있는 `holdings` 가 실제 계좌와 어긋남 | 매도/매수 직전 `OrderManager.get_broker_holdings(force=True)` 호출로 실잔고 검증
두 봇이 같은 종목에 동시 매수 → 이중 포지션 | `OrderManager._code_locks[code]` 로 직렬화 + `orders` 테이블 `UNIQUE(strategy,code,side,date)`
주문번호(ODNO) 미저장 → 사후 추적 불가 | `kis_client._order()` 가 ODNO 반환 → `TradeDBExt.insert_order()` 로 PK 저장
토큰 재발급 경합 / REST 호출 폭주 | `KISClient` 하나 + `kis_token_manager` 공유 + `SafeRequest` 쿨다운
두 봇이 WS 각자 → 구독 수/approval_key 경합 | `WSManager` 단일 세션 + 구독 레퍼런스 카운팅
설정 하드코딩 | 모든 값은 `env_config``os.environ` → 기본값 순으로 조회
## 실행
```bash
cd ~/kis_bot
# 기본(두 전략 모두 ON)
python -m kis_trader.main
# 스캘핑만
STRATEGY_SCALP_ENABLED=true STRATEGY_SHORT_ENABLED=false python -m kis_trader.main
# 꼬리잡기만
STRATEGY_SCALP_ENABLED=false STRATEGY_SHORT_ENABLED=true python -m kis_trader.main
```
## DB 설정 테이블 (2026-05 분리)
| 테이블 | 내용 |
|--------|------|
| `env_config` | 공통 — KIS/키움 API, MM, WS, 전략 ON/OFF, 수수료, `SLOT_MONEY_DEFAULT` 등 |
| `config_scalp` | 스캘핑 `SCALP_*` |
| `config_short` | 꼬리 `SHORT_*` / `TAIL_*` / `SHOULDER_*` / 익절 호가 `SELL_ORDERBOOK_*` |
| `config_momentum` | 모멘텀 `MOMENTUM_*` |
| `config_breakout` | 돌파 `BREAKOUT_*` |
| `config_updow` | 하락매수 `UPDOW_*` |
- 코드는 `get_latest_env()` / `get_env_from_db()`**병합 flat dict** 를 그대로 씁니다 (하위 호환).
- 최초 분리·재마이그레이션: `python3 -m kis_trader.scripts.migrate_split_env_config`
- 키 분류 규칙: 프로젝트 루트 `config_schema.py`
## 주요 환경변수
이름 | 기본 | 설명
-- | -- | --
`STRATEGY_SCALP_ENABLED` | `true` | 스캘핑 전략 활성화
`STRATEGY_SHORT_ENABLED` | `true` | 꼬리잡기 전략 활성화
`STRATEGY_SAME_CODE_POLICY` | `allow` | 같은 종목을 다른 전략이 `active_trades`에 있어도 `allow`(허용) / `block`(차단)
`REAL_BALANCE_VERIFY_BEFORE_BUY` | `false` | `false`=타 전략·수동 보유 있어도 매수 가능 / `true`+`MODE=strategy`면 동일 전략 DB만 차단
`REAL_BALANCE_VERIFY_BEFORE_BUY_MODE` | `strategy` | `strategy` \| `global`(실계좌 1주라도 있으면 매수 차단, 레거시)
`REAL_BALANCE_VERIFY_BEFORE_SELL` | `true` | 매도 전 실잔고. 보유(`hldg_qty`)와 매도가능(`ord_psbl_qty`) 분리. 매도가능 0만으로 유령정리 안 함
`ORDER_CASH_PCT` | `0.95` | 예수금 부족 시에만 가용금×N%÷MAX_STOCKS 로 qty 축소 (기존 산식 우선)
`ORDER_CASH_FEE_BUFFER` | `1.01` | 주문금액 대비 수수료 여유 (1.01=1%)
`ORDER_CASH_DIVIDE_BY_MAX_STOCKS` | `1` | 1=종목당 예수금÷MAX_STOCKS, 0=나누지 않음
`ACCOUNT_CASH_PERSIST_SEC` | `60` | 예수금 캐시 kv_store 저장 주기(체결 시 즉시 저장)
`SCALP_LIVE_BACKTEST_ALIGN` | `true` | 스캘핑: 신호봉(직전 확정) → 진입봉(현재 확정)=백테 다음봉 시가
`SHORT_LIVE_BACKTEST_ALIGN` | `true` | 꼬리(3분): 동일 패턴
`MOMENTUM_LIVE_BACKTEST_ALIGN` | `true` | 모멘텀(1분): 동일 패턴
`BREAKOUT_LIVE_BACKTEST_ALIGN` | `true` | 돌파(1분): 동일 + 진입가 진입봉 시가 우선
`*_LIVE_SIGNAL_LOOKBACK_BARS` | `1` | 직전 N개 신호봉까지 소급 검사
`MAX_STOCKS` | `3` | 전략당 동시 보유 최대 종목 수
`REENTRY_COOLDOWN_SEC` | `300` | 매도 후 같은 종목 재진입 쿨다운(초)
`WS_TIMEFRAMES` | `1,3,15,60` | WS 봉 집계 타임프레임(분)
`PERMANENT_WS_CODES` | `069500,229200` | WS 영구 구독 코드(쉼표)
`SCALP_STOP_LOSS_PCT` / `SCALP_TAKE_PROFIT_PCT` | `-0.015` / `0.015` | 스캘핑 손절/익절 비율
`STOP_LOSS_PCT` / `TAKE_PROFIT_PCT` | `-0.04` / `0.05` | 꼬리잡기 손절/익절 비율
`MAX_LOSS_PER_TRADE_KRW` | `200000` | 1회 거래 최대 손실 허용액(원)
`SLOT_MONEY_DEFAULT` | `3000000` | 1슬롯 기본 투자 금액(원)
## 전략 ON/OFF 동작 방식
`TradingOrchestrator` 는 시작 시 `STRATEGY_*_ENABLED` 를 읽어서 해당 전략 클래스를
쓰레드로 등록한다. 따라서:
1. 꼬리잡기만 끄고 싶을 때 → `env_config` 또는 OS env 에 `STRATEGY_SHORT_ENABLED=false`
2. 재시작 필요 (동작 중 on/off 전환은 지원 안 함, 대신 공격적 재시작 스크립트 사용)
## 백테스트
```bash
python -m kis_trader.backtest.backtest_web
```
엔진별로 백테·실매가 동일 함수를 호출한다 (체결·유니버스는 별도).
| 전략 | 엔진 | 청산 |
|------|------|------|
| SCALP reversal | `scalping_engine` | 어깨 → 익절 → 손절 |
| MOMENTUM | `momentum_engine` | 어깨·트레일 → 손절 → tp_max 상한 |
| SHORT(꼬리) | `tail_engine` | ATR·어깨·EOD |
| BREAKOUT | `breakout.py` | 익절 우선 (돌파 전용) |
## 주의사항
- 프로젝트 루트의 기존 파일 (`scalping_engine.py`, `tail_engine.py`, `kis_ws.py`,
`kis_token_manager.py`, `risk_manager.py`, `database.py` 등) 을 **그대로** 사용한다.
`kis_trader/` 는 이들을 **조립**하는 얇은 래퍼 층이다.
- DB 스키마에 `orders` 테이블이 자동 생성된다 (`TradeDBExt._ensure_orders_table`).
- 기존 `kis_scalping_ver2.py` / `kis_short_ver3.py` 는 남겨두되, 중복 실행 금지
(계좌/토큰/WS 경합).