- _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>
28 KiB
호가.md — 진입 필터 · 익절 지정가 · IOC(13) · 수익구간 호가매도 · 손실구간 호가매도
이 문서는 대화·코드·DB 기준으로 호가 관련 기능을 역할별로 구분해 정리한다.
이름이 비슷해도 서로 다른 스위치이므로 혼동하지 말 것.
아래에서는 L3 / SL-OB 같은 줄임말을 쓰지 않고 한글로 푼다.
0-0b. 벤더 공통: 실시간 호가(RAM) vs 틱동기 호가(DB) — 2026-08-25
UH3 없음. LS 체결=US3, 호가=UH1. (한투=H0STCNT0/H0STASP0, 키움=0B/0D)
| RAM (실매 필터가 봄) | DB 호가 (*_ORDERBOOK_SAVE_MODE=tick 기본) |
|
|---|---|---|
| 한투 | H0STASP0 올 때마다 | H0STCNT0(체결) 1건 → 그때 RAM 1장 → ws_orderbook |
| 키움 | 0D 올 때마다 | 0B(체결) 1건 → 그때 RAM 1장 → ws_orderbook |
| LS | UH1 올 때마다 | US3(체결) 1건 → 그때 RAM 1장 → ls_ws_orderbook |
MODE=interval= 호가 메시지(UH1/0D/ASP0) 쪽에서 간격 저장. 세 증권사 공통 스위치이지 LS 전용 신기능이 아님.LS_WS_TICK_SAVE=ls_ws_ticksINSERT. 구독 전체(영구∪후보∪보유) — 호가와 동일. 봉·VI만 영구.BT_TICK_LS_THIRD_FALLBACK(기본 true) = 백테/옵투나 틱: 같은 초 1·2차 없으면ls_ws_ticks(나이=폴백 3초).- 실매는 DB를 안 보고 RAM. DB는 백테/옵투나 재현용 샘플.
2026-08-25 LS ls_ws_orderbook 0건 — 원인 (코드)
의도(US3 틱동기)는 한투·키움과 같음. 문제는:
- 장중 Bye ≈ 매분 → OPEN 직후 구독 복구가
sends≈1(JIF만)로 미완료. - 서버에 US3/UH1 REG가 안 붙음 → 체결·호가 수신 없음 → tick 모드라 DB 0.
- 미완료인데도
recovering=False로 풀리던 버그 → 워치독이 틱없음으로 또 끊음 (악순환).
수정(동작) 2026-08-27: ls_ws.py + ls_token.py
- OPEN마다
_ws_reconnect_step=0리셋 제거 — Bye 플랩 시 1초 연타 원인. - 조기 CLOSE/Bye 서킷 (
LS_WS_STABLE_OPEN_SEC/EARLY_BYE_STREAK/BYE_CIRCUIT_SLEEP_SEC) — streak 도달 시 로컬 토큰 무효화+1회 재발급 후 대기. - REG 미완료면
recovering유지 —reg_ok일 때만 해제·step/streak 리셋. - adhoc에서
fetch_ls_access_token강제 발급 금지 (봇 프로세스 토큰 무효화).
LS_WS_ENABLED / TICK·ORDERBOOK_SAVE / 3차 spill 끄지 않음 (옵션1).
Bye ≈ 매분 — 로그·git으로 좁힌 것 (2026-08-25 journal)
| 비교 | Aug24 (정상 수집) | Aug25 (DB 0) |
|---|---|---|
시간대당 LSWebSocket.*Bye |
0 (09~15시) | |
같은 시간 LS WS watchdog |
드묾(장초 1회 등) | 장중 시간대 0 |
| OPEN 복구 | sends≈15~339 완료 |
거의 항상 sends≈1 (JIF만) |
- Bye 페이로드:
opcode=8+\x03\xe8Bye= WebSocket 정상종료(1000) + 서버가 보낸 Bye. 우리 워치독 문구(틱 없음 → 강제 재연결)와 무관. - 패턴:
OPEN(sends≈1)→ 약 60초 유지 →Bye+CLOSE→ 같은 초~1초 안에 다시OPEN(sends≈1). 침묵 워치독(grace30+silence45≈75초)보다 먼저 서버가 끊음. - 05:07 봇 재시작 이후 07:00부터 이미 동일 루프(장 시작 전). Aug24 18:37은
KR=169 sends≈339로 복구 성공 이력 있음. sends≈1+ KR 수백: REG 루프가 JIF 직후_opened해제되어 abort된 형태(가설).
검증 키: CLOSEws_same=False가 OPEN/REG 중 끼는지, 복구 로그abort_why=opened_cleared. 미확정.
아직 단정 금지: 이중세션(조건식 AFR vs 시세)·계정 한도·서버 idle 정책만의 단독 원인. 위는 “워치독 매분 킥”은 아님까지 확정.
0-0. 「호가 쌓기」는 설계가 아니다 (헷갈림 방지)
DB에 호가 한 장씩 넣는 것(저장)은 이미 돌아가는 수집 기능이다.
새 매매 로직·새 설계 항목이 아니다.
| 말 | 진짜 뜻 |
|---|---|
| “호가 INSERT / 쌓기” | 봇이 장중 호가를 ws_orderbook / ls_ws_orderbook 에 기록하는 중 (지금 하고 있음) |
| “필터·호가매도 끄고 쌓기만” | 끄는 건 매매 스위치다. 저장은 켠 채, 매수 막을지 / 호가로 팔지 스위치만 OFF |
| 설계에 적어 둔 이유 | “저장이 새 기능”이 아니라 스위치 켜서 실험하지 말자는 운영 메모 |
초등 비유:
일기장에 날씨를 매일 적는 것 = 쌓기(이미 함).
일기 보고 “비 오면 우산 사기” 규칙을 켜는 것 = 필터·호가매도 ON.
규칙은 아직 끄고, 일기만 모으자는 뜻이었다.
2026-08-03 장일 확인 (틱 동기)
| 테이블 | 오늘 | 방식 | 판정 |
|---|---|---|---|
ls_ws_orderbook (ls_uh1) |
약 274만 행 / 88종목 | 체결 1건 ≈ 호가 1장 (비율 ≈ 0.999) | 틱 동기화로 쌓임 ✅ |
ws_orderbook (kiwoom_0d) |
약 12.5만 행 / 38종목 | 간격 중앙값 약 3초 | 연속 저장 ✅ · 틱 1:1은 아님 |
ws_orderbook (filter_eval) |
29행 | 진입 판정 순간 1장 | 필터 판정 로그용 |
모멘텀 실매 매수 오늘 0건 (유니버스 이슈) → 매도쪽 측정 스크립트는 오늘 모수 없음.
최근 모수: 2026-07-31 모멘텀 7건 + 키움 호가 (§4 측정).
0-1. 운영 메모 (2026-08-15 기준 · DB가 진실)
아래는 옛 “OFF 유지” 메모를 폐기한 현재 운영이다. 스위치는 웹「운영 설정」·전략탭 DB를 본다.
- 호가 저장 — ON. 시계열 TTL =
WS_ORDERBOOK_TICK_MAX_AGE_SEC(코드 기본 30초. UI에 예전에 “3초”라고 적혀 있던 것은 저장용 힌트 오류였음). - 매수 호가필터
*_ORDERBOOK_FILTER_ENABLED= ON (모멘텀·돌파·스캘프·꼬리). 숫자는 전략별(스프·잔량·매도벽). - 수익구간 호가매도
MOMENTUM_EXIT_OB_ENABLED/BREAKOUT_EXIT_OB_ENABLED= 운영 ON인 적이 있음 → DB 확인. 본 문서 옛 “OFF 유지”는 폐기. - 손절호가
*_STOP_OB_ENABLED= 마찬가지로 DB 확인 (코드 배선됨). - 필터 RAM 나이 (B안, 2026-08-15)
WS_ORDERBOOK_FILTER_MAX_AGE_SEC기본 0 = 마지막 RAM 호가로 반드시 검사. REST 없음.- 저장 TTL과 공유 금지. 예전에 둘 다
TICK_MAX_AGE라서 30초 넘으면None→ 필터 ON인데 통과(구멍). - 한 번도 호가가 없고 필터 ON이면 안 삼 (
WS_ORDERBOOK_FILTER_REJECT_IF_EMPTY기본 true). REST 없음. 다음 매수 검사에서 사진 있으면 다시 봄.
한산해서 사진이 안 바뀌는 것과 다름 — 마지막 사진이 있으면 그걸로 검사.
- Optuna Stage2(후처리) 는 캔들 TPE와 별도. 진입 격자·매도벽 축은
docs/옵투나.md· §8.--apply-best없이 후처리만 재실행 가능 (optuna_rerun_postprocess.py).
0. 한눈에 구분
| 구분 | 언제 | 하는 일 | 대표 키 | 상태 |
|---|---|---|---|---|
| 호가 저장(쌓기) | 장중 항상 | DB에 호가 기록 | LS_WS_ORDERBOOK_SAVE / WS_ORDERBOOK_SAVE_ENABLED 등 |
이미 동작 중 · 설계 아님 |
| 매수 호가필터 | 사기 직전 | 호가 나쁘면 안 삼 | {전략}_ORDERBOOK_FILTER_ENABLED |
코드 있음 · 2026-08 운영 ON |
| 익절 호가 ON | 이미 매도 신호 난 뒤 | 익절을 지정가로 냄 | SELL_USE_ORDERBOOK_ON_PROFIT |
운영 ON |
| 매도 시장가 IOC(13) | 시장가로 팔 때 | 실전만 13 | USE_MARKET_IOC_SELL |
운영·코드 분리 |
| 수익구간 호가매도 | 들고 있을 때 이득 난 뒤 | 사려는 사람 줄면 미리 이익 실현 | MOMENTUM_EXIT_OB_* / BREAKOUT_EXIT_OB_* |
코드 있음 · 시드 OFF / 운영은 DB |
| 손실구간 호가매도 | 들고 있을 때 손해 난 뒤 | 사려는 사람 줄면 큰 손절 전에 먼저 나옴 | MOMENTUM_STOP_OB_* / BREAKOUT_STOP_OB_* |
배선 완료 · 시드 OFF / 운영은 DB |
거래내역 「매수호가」 = 살 때 근처 · 「매도호가」 = 팔 때 근처. 서로 다른 칸.
수익구간 호가매도 vs 손실구간 호가매도 (초등)
둘 다 “호가판에 사려는 사람이 확 줄었다”를 본다.
언제 쓰느냐만 다르다.
| 수익구간 호가매도 | 손실구간 호가매도 | |
|---|---|---|
| 내 장부 | 이미 조금 이득일 때만 | 이미 조금 손해일 때만 |
| 하는 일 | “이득 났는데 분위기 깨지면 이익 챙기고 나감” | “손해인데 분위기 더 깨지면, −몇% 손절선까지 안 가고 먼저 나감” |
| −몇% 손절선 | 안 건드림 | 공식은 그대로 · 그 앞에서 따로 한 번 더 봄 |
| 지금 | 코드 있음 · ENABLED는 DB | 코드 있음(STOP_OB) · ENABLED는 DB |
| 파람서치 | 캔들 TPE 본체에는 안 넣음. 후처리 Stage2 (docs/옵투나.md) |
후처리 STOP 축 |
1. 매수 호가필터 (진입)
역할
- TRIGGER 단계에서 스프레드·매수/매도 잔량비·얇은 호가·매도벽 등으로 매수 차단.
- 청산 신호가 아님.
키 (전략별)
글로벌 ORDERBOOK_* 수치 폐기 → 전략 prefix만 사용.
SCALP_ORDERBOOK_FILTER_ENABLEDMOMENTUM_ORDERBOOK_FILTER_ENABLEDBREAKOUT_ORDERBOOK_FILTER_ENABLEDTAIL_ORDERBOOK_FILTER_ENABLED(SHORT → TAIL)
임계 예: *_ORDERBOOK_MAX_SPREAD_PCT, *_MIN_BID_ASK_RATIO, *_ENTRY_BID_LEVELS 등.
코드
- 판정 공통:
kis_trader/engine/orderbook_filter.py - env:
kis_trader/engine/orderbook_env.py - 웹: 예) 모멘텀 「호가필터 ON」(
mom_ob_filter) — 이 백테 회차 진입 필터
왜 전략별로 갈라졌나
스캘프·모멘텀·돌파·꼬리는 호가 의미가 달라 ON/OFF·임계를 전략마다 둔다.
판정 함수는 공유, 설정 키만 분리.
수집 vs 필터
KIWOOM_WS_ORDERBOOK_ENABLED— 호가 WS 수신- 시계열
kiwoom_0d/ LSls_ws_orderbook— 저장(필터 OFF여도 저장 가능) - 필터 ON = 그 스냅으로 매수 탈락 적용
켜면 매매가 확 줄어드는 이유 (조건식 탓 아님)
- HTS/조건식 = “살 만한 이야기(타점)” 선별
- 호가필터 = “지금 이 순간 대기표가 들어가기 좋은가” 추가 게이트
- 모멘텀·돌파류는 급등·얇은 종이 많아, 통과 직후 호가가 스프레드 큼 / 살힘 약함 / 매도벽인 경우가 흔함
- 거래내역 「매수호가」에 간격큼·살힘 0.1x대가 보여도 데이터가 이상한 게 아니라 그 타점 유동성이 그런 것
→ “조건식이 잘못 뽑았다”로 단정하지 말 것. 유동성 게이트가 한 겹 더 있어서 건수가 줄어든다.
RAM(실매) vs DB 저장(백테)
| 실매 필터 ON | 백테 필터 ON | |
|---|---|---|
| 보는 것 | RAM get_orderbook_snapshot(max_age=FILTER_MAX_AGE) |
DB 스냅 / filter_eval 재생 |
| 나이 | FILTER_MAX_AGE=0 → 마지막 장으로 검사 (저장 30초와 무관) |
체결 시각 근처 행 |
| 스냅 한 번도 없음 | 필터 ON이면 안 삼(기본). REST 없음 | 재생·본체 없으면 그 타점 못 탐 |
저장 TTL(TICK_MAX_AGE, 기본 30)은 0D 덤프·가짜 채움 방지용.
필터가 이걸 같이 쓰면 만료=None=통과 구멍이 난다 (2026-08-14 실매에서 확인).
거래내역 「매수호가」칸의 +N초 탈락-매도벽 은 근처 스냅 스탬프이지, 그 초가 필터를 통과했다는 뜻이 아님.
임계값(0.45 · 0.85 …) — 업계 표준 아님
orderbook_env.py 프로젝트 시드/시작값 (Optuna·웹에서 변경 가능). “보편적으로 다들 0.45”가 아님.
| 기본 | 의미 | 체감 |
|---|---|---|
| 스프레드 0.45% (시드) | (ask−bid)/mid 상한 |
운영 모멘텀은 3.0 근처도 있음. orderbook_env 시드 ≠ 실매 DB |
| 잔량비 0.85 | 상위 N호가 매수÷매도 | 살 쪽이 거의 비슷해야 통과 |
| 깊이 1.2 | 필요수량 ×1.2 ≤ 매수호가 합 | |
| 매도벽 3.0 | 매도잔량 과다 컷 |
AND라서 하나라도 깨지면 탈락 → 체감이 셈.
이 필터가 굳이 필요한가? → 필수 아님
| 목표 | 선택 |
|---|---|
| 거래 수·백테 건수 유지 | OFF 또는 임계 완화 |
| 얇은 호가·큰 갭 진입을 줄이고 싶다 | ON (2026-08 실매는 이 쪽) |
| 조건식이 이미 유동성 좋은 종만 | 필터 체감↓ |
- 진입 필터 = 살까 말까
- 익절 호가 ON = 이미 산 뒤 어떻게 팔까 (
SELL_USE_ORDERBOOK_ON_PROFIT) — 다른 스위치
호가가 안 좋게 찍힌다는 사실 자체가 “필터 필수” 근거가 아님.
안 좋은 호가에 들어가느냐를 막을지 선택일 뿐. 과하면 끄거나 숫자만 푼다.
키움 조건 vs LS 조건 (진입·수익구간 호가매도 공통)
kiwoom_condition/condition→ 호가 키움 0D RAM · DBws_orderbookls_condition→ 호가 LS UH1 RAM · DBls_ws_orderbook(키움으로 메우지 않음)
수익구간·손절호가도 같은 스냅샷 API. 2026-08 운영: 4전략 유니버스는 키움 조건이 본체. 호가 구독/읽기 체인 kis(2키 OB 41)→kiwoom→ls(3차). LS는 spill 3차지 “LS 전용 종목”이 아님 (LIVE_OB_PROVIDER=kis,WS_OB_SUBSCRIBE_CHAIN=kis,kiwoom,ls).
2. 익절 「호가 ON」— 지정가 매도 (실행)
역할
청산 판정(래칫·어깨·트레일)이 이미 난 뒤, 주문을 어떻게 내는지.
| 스위치 | ON | OFF / 호가 없음 |
|---|---|---|
SELL_USE_ORDERBOOK_ON_PROFIT |
익절·어깨·트레일 → 매수1호가 지정가(ORD_DVSN=00) · 잔량 부족 시 대기 |
같은 익절 신호 → 시장가 |
손절 · 긴급 · 장마감(EOD) → 이 스위치와 무관, 항상 시장가.
운영 값 (확인 시점 기준)
저장: config_short
| 키 | 값 | 의미 |
|---|---|---|
SELL_USE_ORDERBOOK_ON_PROFIT |
true | 익절 지정가 경로 ON |
SELL_ORDERBOOK_BID_LEVELS |
2 | 매수 1~2호가 잔량 합 검사 |
SELL_ORDERBOOK_DEPTH_MULT |
1.5 | 필요 잔량 = 수량 × 1.5 |
코드
kis_trader/execution/order_manager.py— 매도 전송 분기kis_trader/execution/orderbook_sell.py— 잔량·지정가 가격 산출
전 전략 공통(OrderManager 한 경로). 진입 필터처럼 전략별 키가 아님.
UI
라이브 스키마에 진입 「호가필터 ON」은 잘 보이지만,
SELL_USE_ORDERBOOK_ON_PROFIT는 스키마 전용 체크가 약하고 DB(config_short)·get_env로 동작.
3. 매도 주문코드 13 — 시장가 IOC (실전만)
역할
시장가 매도를 낼 때의 ORD_DVSN 선택.
익절 지정가(00)와는 별개.
| 환경 | 시장가 매도 코드 |
|---|---|
| 모의 | 항상 01 (IOC 13 미지원 → 코드에서 고정) |
실전 + USE_MARKET_IOC_SELL=true |
13 (시장가 IOC) |
| 실전 + false | 01 (일반 시장가) |
매수용은 별도: USE_MARKET_IOC (실전 매수 13 / 모의 01).
코드
kis_trader/execution/kis_client.py
sell_market_order/uses_market_sell_ioc()sell_limit_order→ 항상00
운영 UI
live_config_schema: 「매도 시장가 IOC」=USE_MARKET_IOC_SELL
흐름 요약
익절 신호
├─ SELL_USE_ORDERBOOK_ON_PROFIT ON + 잔량 OK → 지정가 00
└─ OFF / 호가없음 / 잔량실패 후 시장가
├─ 실전 + IOC ON → 13
└─ 모의 → 01
손절·긴급·EOD → 시장가 (위와 동일하게 실전 13 / 모의 01)
4. 수익구간 호가매도 (코드 있음 · 기본 OFF)
진입 필터·익절 지정가와 다른 축:
이미 산 뒤, 이득이 난 상태에서 “사려는 사람이 줄었다”면 팔지 말지를 본다.
초등으로
- 주식을 샀다.
- 가격이 조금 올랐다 (이득).
- 호가판을 보니 “사고 싶다”는 줄이 확 짧아졌다.
- 그때 이익을 챙기고 나간다 = 수익구간 호가매도.
- 아직 이득이 아니면 이 규칙으로 안 판다. (그건 손실구간 쪽)
왜 맨 위에 두면 안 되나
- 장 초반·급등 초입은 원래 파는 사람이 많다 → “분위기 끝”으로 오해하기 쉬움
- 사자마자 팔면 수수료만 나고 기회 날림
→ 맨 위(1순위) 금지. 래칫·어깨 다음, −% 손절 앞.
측정(실매 매수 + ws_orderbook, 예: 2026-07-27 모멘텀):
- 가드 없이 순간만 보면: 조기 신호 많음
- 이득·최소보유 가드 후: 크게 줄어듦
※ 이 %는 수익률이 아님 = “산 뒤 N분 안에 이 신호가 뜬 건수 비율”
합의된 매도 순서
래칫 → 어깨 → 수익구간 호가매도 → 손실구간 호가매도 → −% 손절 → 트레일 → …
- 코드: 어깨 다음 · 손절 전 = 수익구간(
호가컷) + 손절호가 · 코드 시드 OFF(운영 DB) - 모멘텀=키움 호가 · 돌파=LS 호가 (교차 메우기 금지)
- −% 손절은 가격 하나만. 호가를 손절 % 숫자에 섞지 않음.
env 키 (모멘텀=키움 · 돌파=LS · 수익구간)
| 키 | 기본 | 역할 |
|---|---|---|
MOMENTUM_EXIT_OB_ENABLED / BREAKOUT_EXIT_OB_ENABLED |
false | 켜기/끄기 |
*_EXIT_OB_RATIO_MIN |
0.4 | 사려는/팔려는 비율 이동평균이 이하면 “줄었다” |
*_EXIT_OB_MA_WINDOW |
5 | 몇 장 평균 볼지 |
*_EXIT_OB_MIN_PROFIT_PCT |
0.005 (+0.5%) | 이만큼 이득일 때만 |
*_EXIT_OB_MIN_HOLD_BARS |
3 | 산 지 최소 N분 |
등록: database.py (config_momentum / config_breakout) · defaults → exit_ob_*.
웹 모멘텀·돌파 탭에 컨트롤 있음. 코드 기본 false · 실제 ON/OFF는 DB.
발동 조건 (모두 만족)
| 조건 | |
|---|---|
| 스위치 ON | |
| 비율 이동평균 < 기준 | 스냅 부족 → 안 함 |
| 최소 이득 | |
| 최소 보유 |
배선 상태 (2026-08)
실매·백테 OR 히스토리 주입 ✅ · 웹 ✅ · Optuna 캔들 본체 TPE에는 미포함.
숫자 탐색은 후처리 Stage2 (optuna_orderbook_recommend.py EXIT/STOP 축). OFF면 예전과 동일.
측정 스크립트 (매도쪽 · 필터 켜기 전)
scripts/measure_ob_exit_early_fire.py
- 실매 매수 + 실매 호가 테이블
- A) 맨 위+가드없음 / B) 맨 위+가드 / C) 3순위+가드(현 설계)
- 예:
--date 2026-07-31 --ob-table ws_orderbook - 2026-08-03: 모멘텀 매수 0건 → 측정 불가 (호가는 쌓여 있음)
2026-07-31 매도쪽 한 줄 (키움 ws_orderbook)
모멘텀 7건 전부 호가 있음.
A 28.6% → B/C 14.3% (가드가 핵심).
같은 날 ls_ws_orderbook은 그 종목들 스냅 0 → LS로 못 재현 (모멘텀=키움 조건 쪽).
전략 범위
모멘텀+돌파 배선. 진입 필터 키와 혼용 금지.
4-1. 손실구간 호가매도 (배선 완료 · 기본 OFF · 모멘텀+돌파)
수익구간과 같은 호가 눈, 반대 장부.
초등으로
- 주식을 샀다.
- 가격이 조금 떨어졌다 (손해).
- 호가판에 사려는 사람이 확 줄었다.
- −3% 같은 큰 손절선까지 기다리기 전에 먼저 나온다.
- 이득 중이면 이 규칙으로 안 판다. (그건 수익구간)
왜 만드나
−% 손절만 있으면, 호가가 이미 무너진 뒤에 가격이 손절선에 닿아 더 나쁜 가격에 팔 수 있음.
수익구간 규칙만 있으면 손해 날 때는 안 도와줌.
원칙
- −% 손절 숫자는 그대로. 이 규칙은 앞에서 한 번 더 보는 것뿐.
- 손해일 때만.
- 맨 위 금지 — 래칫·어깨 다음, −% 손절 직전.
- 호가 스냅 없으면 안 함 (가격봉으로 호가 지어내기 금지).
- 나갈 때는 시장가/IOC(도망). 익절 지정가「호가 ON」과 다름.
- 기본 OFF. 측정 → 승인 → 코드 → ON.
목표 순서
1 래칫
2 어깨
3 수익구간 호가매도 ← 이득 + 호가 줄음
4 손실구간 호가매도 ← 손해 + 호가 줄음 ★배선·기본 OFF
5 −% 손절 ← 가격만
6 트레일 · 시간 · …
env (모멘텀 · 배선됨) — 한글 뜻
| 키 (영문) | 한글 이름 | 기본 | 한줄 |
|---|---|---|---|
MOMENTUM_STOP_OB_ENABLED / BREAKOUT_STOP_OB_ENABLED |
손절호가 켜기 | false | 끄면 이 규칙 전부 무시 |
MOMENTUM_STOP_OB_RATIO_MIN |
호가 붕괴 기준 | 0.4 | 총사÷총팔 평균이 이보다 작으면 “사람 줄었다” |
MOMENTUM_STOP_OB_MA_WINDOW |
평균 몇 장 | 5 | 위 비율을 최근 5장 평균내서 봄 (순간 깜빡임 방지) |
MOMENTUM_STOP_OB_MIN_LOSS_PCT |
최소 손해 폭 | 0.003 (−0.3%) | 이만큼은 이미 손해여야 발동 (수익 중엔 안 씀) |
MOMENTUM_STOP_OB_MIN_HOLD_BARS |
최소 보유 분 | 2 | 산 지 2분 전엔 안 씀 |
DDL·컬럼 COMMENT: scripts/sql/config_momentum_ob_column_comments.sql · scripts/sql/config_breakout_ob_column_comments.sql
하드 손절 4%일 때 언제 나감? (손절호가 ON 가정)
- 산 지 2분 미만 → 손절호가 안 함. (−4% 하드만 가능)
- 2분 이상 + 현재가 −0.3% 이하 + OR평균 < 0.4 → 사유
손절호가로 즉시 시장가 (하드 4%까지 안 기다림) - 호가는 안 무너졌는데 가격만 −4% → 예전처럼 사유
손절 - 지금 DB 기본은 ENABLED=false → 2번은 아예 없고 4% 하드만.
“얼마나 빨리” = “−0.3%에 OR만 깨지면 그때”. 4%까지 버티는 규칙이 아님.
OR 5장 채우는 시간: LS 틱동기면 체결 몇 번(초 단위) · 키움 ~3초 저장이면 대략 십수 초면 충분. 최소보유 2분이 보통 더 긴 병목.
삼성전자급 두꺼운 호가: 평소 OR이 0.4 아래로 잘 안 떨어짐 → 손절호가가 거의 안 울릴 수 있음. 진짜 수급 붕괴·급락 구간에서야 −0.3%~−4% 사이에서 선제. 얇은 중소형에서 체감이 큼.
사유 문자열: 손절호가 (수익구간의 호가컷과 구분 · 시장가 긴급).
상태
| 항목 | |
|---|---|
| 설계 | ✅ |
| 코드·DB·웹 | ✅ 2026-08-03 배선 |
| 기본값 | 코드 시드 OFF · 실제는 DB |
| TPE | 캔들 본체 축 아님. 후처리 STOP 축 (docs/옵투나.md) |
측정 (호가 쌓인 뒤 · 모멘텀 매수가 있는 날)
- 손실→−%손절(또는 큰 손실) 건 중, 손절 직전에 이미 호가가 줄었던 비율
- 손해였다가 회복한 건에서 이 규칙이 너무 일찍 나갔을 비율
→ 측정 후 ON 여부 결정 (지금 기본 OFF).
5. 데이터 · UI 참고
| 항목 | 내용 |
|---|---|
| 호가 테이블 | ws_orderbook (키움), ls_ws_orderbook (LS) |
| 테이블에 있는 것 | best_bid/best_ask, total_bid_qty/total_ask_qty, bid_qty_l3/ask_qty_l3 등 원본 |
| 테이블에 없는 것 | 0.45%·살힘·OR 같은 계산된 %/비율 — DB 컬럼이 아님 |
| 거래내역 「매수호가」「매도호가」 | trade_orderbook_enrich.py가 매수/매도 시각 근처 스냅을 붙인 뒤, 웹 JS가 원본으로 계산·색칠 |
| 기준 숫자(시드) | 매수: 간격≤0.45% · 살힘≥0.85 / 매도 OR: ≈0.40 (수익구간 호가매도 기본) — 업계 표준 아님 |
| LS 오늘 | 틱 동기 (체결≈호가 1:1) |
| 키움 오늘 | 연속 저장 · 간격 약 3초 (틱 1:1 아님) |
UI 칸 — 이상(높을수록 / 낮을수록)
| 칸 | 지표 | 이상 | 초등 |
|---|---|---|---|
| 매수호가 | 가격간격 % | ↓ 낮을수록 | 사·팔 가격이 가까울수록 들어가기 쉬움 |
| 매수호가 | 살 힘 (위3칸 사÷팔) | ↑ 높을수록 | 사겠다는 대기가 많을수록 버팀 |
| 매도호가 | OR (총사÷총팔) | ↑ 높을수록 수급OK · ↓ 낮으면 약함 | 팔 때 사려는 사람이 많으면 버팀, 적으면 무너짐 |
| 매도호가 | 가격간격 % | ↓ 낮을수록 | 팔 때도 간격 작을수록 체결 덜 미끄러짐 |
열 헤더: ↓간격 ↑살힘 / ↑OR ↓간격 (마우스 올리면 상세).
6. 하지 말 것 (요약)
- 매수필터 · 익절지정가 · 수익구간호가매도 · 손실구간호가매도를 한 스위치로 취급하지 말 것.
- “호가 쌓기”를 새 매매 설계로 착각하지 말 것 — 이미 있는 저장.
- 호가를 −% 손절 공식에 섞지 말 것.
- 호가매도를 1순위에 두지 말 것.
- 스냅 없이 OHLC로 호가 지어내기 금지.
- 모의에 ORD_DVSN=13 강제 금지.
- 필터 TTL과 저장 TTL을 한 키로 묶지 말 것 (
FILTER_MAX_AGEvsTICK_MAX_AGE). - 캔들 TPE 본체에 호가 ON/OFF를 넣지 말 것. 숫자 탐색은 후처리만 (§8).
- 거래대금·시총회전 만으로 손절 만들지 말 것.
- 필터 ON인데 호가 사진이 없다고 통과시키지 말 것. REST로 메우지 말 것 (
REJECT_IF_EMPTY).
7. 관련 파일
| 파일 | 내용 |
|---|---|
kis_trader/engine/orderbook_filter.py |
매수 호가필터 |
kis_trader/engine/orderbook_env.py |
전략별 진입 env |
kis_trader/execution/orderbook_sell.py |
익절 지정가·잔량 |
kis_trader/execution/order_manager.py |
매도 실행 분기 |
kis_trader/execution/kis_client.py |
00/01/13 주문 |
kis_trader/backtest/trade_orderbook_enrich.py |
거래내역 호가 표시 |
kis_trader/engine/momentum_hts_logic.py |
수익구간·손절호가 판정 (모멘텀·돌파 공용) |
kis_trader/engine/momentum_engine.py |
exit_ob_* defaults |
kis_trader/engine/momentum_env_keys.py · BREAKOUT_*_OB_*(database/config_breakout) |
전략별 env |
kis_trader/strategies/momentum.py |
실매 OR append |
scripts/measure_ob_exit_early_fire.py |
매도쪽 조기신호 측정 |
docs/layered_exit_design.md |
옛 설계서 (현행은 본 MD §4·§4-1) |
docs/호가.md |
본 문서 |
8. Optuna / TPE (2026-08-15)
캔들 본체 TPE (Stage 1)
매수필터 ON/OFF · EXIT_OB · STOP_OB 를 차트 trial 축에 넣지 않음.
끈 구간과 켠 구간이 한 랭킹에 섞이면 순위가 호가 날씨에 흔들린다.
후처리 (Stage 2) — 이미 동작
optuna_postprocess_topn.py → optuna_orderbook_recommend.py.
구 JSON만 다시: python3 -u kis_trader/backtest/optuna_rerun_postprocess.py --result-json <path>
(--apply-best 아님. 실매 숫자는 안 바뀜.)
진입 탐색 격자 (ensure_optuna_gate_env_defaults / OPTUNA_OB_ENTRY_*, env_config_ext):
| 축 | 범위 (널널 검사, 2026-08-15) | 실매 참고 |
|---|---|---|
| 스프레드 % | 0.1 ~ 8.0 step 0.1 | 모멘텀 운영 3.0 포함 |
| 잔량비 | 0.05 ~ 1.5 step 0.05 | 0.58~1.0 포함 |
| 매도벽 배수 | 1 ~ 80 step 1 | 운영 3 / 스캘프 8. 예전 후처리는 이 축이 없어 벽 탈락을 못 봄 |
| lookback | OPTUNA_OB_LOOKBACK_MIN 기본 30분 |
건수 하한: 잔여가 원본의 30% 미만이면 trial 무효 → 너무 센 컷은 점수에서 죽음.
후처리는 이미 난 체결 + 근처 호가 재생. 실매 “스냅 없음 통과” 구멍은 모델링하지 않음 (그건 B안 필터 TTL).
DB 반영은 사용자 명시 / scripts/apply_optuna_ob_consensus.py 만. 후처리 재실행 ≠ 적용.
9. env 등록 주의
신규 키는 database.py ENV 등록 + get_env_from_db 재조회 검증.