Files
kis_bot/docs/호가.md

22 KiB
Raw Blame History

호가.md — 진입 필터 · 익절 지정가 · IOC(13) · 수익구간 호가매도 · 손실구간 호가매도

이 문서는 대화·코드·DB 기준으로 호가 관련 기능을 역할별로 구분해 정리한다.
이름이 비슷해도 서로 다른 스위치이므로 혼동하지 말 것.
아래에서는 L3 / SL-OB 같은 줄임말을 쓰지 않고 한글로 푼다.


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. 운영 메모 (스위치 OFF · 저장은 ON)

  1. 호가 저장 — 이미 ON. 장중 행 수만 가끔 확인하면 됨.
  2. 매수 호가필터 *_ORDERBOOK_FILTER_ENABLED = OFF 유지 (아직 실험 금지)
  3. 수익구간 호가매도 MOMENTUM_EXIT_OB_ENABLED / BREAKOUT_EXIT_OB_ENABLED = OFF 유지
  4. 손절호가 MOMENTUM_STOP_OB_ENABLED / BREAKOUT_STOP_OB_ENABLED = OFF 유지 (코드 배선됨 · 켜기 전 측정)
  5. 데이터가 모이면
    • 매수필터: 탈락 사유 비율 보고 쓸지 결정
    • 수익구간 호가매도: scripts/measure_ob_exit_early_fire.py 재측정
    • 손실구간 호가매도: 손실·손절 구간 측정 후 구현 여부
  6. Optuna TPE: 위 스위치들 당분간 그리드에 넣지 말 것 (§8)

실매·백테는 스위치 OFF면 예전과 동일. 저장만 계속된다.


0. 한눈에 구분

구분 언제 하는 일 대표 키 상태
호가 저장(쌓기) 장중 항상 DB에 호가 기록 LS_WS_ORDERBOOK_SAVE / WS_ORDERBOOK_SAVE_ENABLED 이미 동작 중 · 설계 아님
매수 호가필터 사기 직전 호가 나쁘면 안 삼 {전략}_ORDERBOOK_FILTER_ENABLED 코드 있음 · 운영 OFF
익절 호가 ON 이미 매도 신호 난 뒤 익절을 지정가로 냄 SELL_USE_ORDERBOOK_ON_PROFIT 운영 ON
매도 시장가 IOC(13) 시장가로 팔 때 실전만 13 USE_MARKET_IOC_SELL 운영·코드 분리
수익구간 호가매도 들고 있을 때 이득 난 뒤 사려는 사람 줄면 미리 이익 실현 MOMENTUM_EXIT_OB_* / BREAKOUT_EXIT_OB_* 코드 있음 · 기본 OFF
손실구간 호가매도 들고 있을 때 손해 난 뒤 사려는 사람 줄면 큰 손절 전에 먼저 나옴 MOMENTUM_STOP_OB_* / BREAKOUT_STOP_OB_* 배선 완료 · 기본 OFF

거래내역 「매수호가」 = 살 때 근처 · 「매도호가」 = 팔 때 근처. 서로 다른 칸.

수익구간 호가매도 vs 손실구간 호가매도 (초등)

둘 다 “호가판에 사려는 사람이 확 줄었다”를 본다.
언제 쓰느냐만 다르다.

수익구간 호가매도 손실구간 호가매도
내 장부 이미 조금 이득일 때만 이미 조금 손해일 때만
하는 일 “이득 났는데 분위기 깨지면 이익 챙기고 나감” “손해인데 분위기 더 깨지면, −몇% 손절선까지 안 가고 먼저 나감”
−몇% 손절선 안 건드림 공식은 그대로 · 그 앞에서 따로 한 번 더 봄
지금 코드 있음, 스위치 OFF 글만, 코드 없음
파람서치(TPE) 당분간 안 넣음 당분간 안 넣음

1. 매수 호가필터 (진입)

역할

  • TRIGGER 단계에서 스프레드·매수/매도 잔량비·얇은 호가·매도벽 등으로 매수 차단.
  • 청산 신호가 아님.

키 (전략별)

글로벌 ORDERBOOK_* 수치 폐기 → 전략 prefix만 사용.

  • SCALP_ORDERBOOK_FILTER_ENABLED
  • MOMENTUM_ORDERBOOK_FILTER_ENABLED
  • BREAKOUT_ORDERBOOK_FILTER_ENABLED
  • TAIL_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 / LS ls_ws_orderbook — 저장(필터 OFF여도 저장 가능)
  • 필터 ON = 그 스냅으로 매수 탈락 적용

켜면 매매가 확 줄어드는 이유 (조건식 탓 아님)

  • HTS/조건식 = “살 만한 이야기(타점)” 선별
  • 호가필터 = “지금 이 순간 대기표가 들어가기 좋은가” 추가 게이트
  • 모멘텀·돌파류는 급등·얇은 종이 많아, 통과 직후 호가가 스프레드 큼 / 살힘 약함 / 매도벽인 경우가 흔함
  • 거래내역 「매수호가」에 간격큼·살힘 0.1x대가 보여도 데이터가 이상한 게 아니라 그 타점 유동성이 그런 것
    → “조건식이 잘못 뽑았다”로 단정하지 말 것. 유동성 게이트가 한 겹 더 있어서 건수가 줄어든다.

RAM(실매) vs DB 3초(백테)

실매 필터 ON 백테 필터 ON
보는 것 RAM 최신 (get_orderbook_snapshot) DB 스냅 / filter_eval 재생
스냅 없음 안 막음(통과) 재생·본체 없으면 그 타점 못 탐·탈락 재현

DB ~3초 저장은 백테·표시용. 실매 차단은 RAM.
“3초라서 가짜로 막는다”보다 그 시각 근처 호가가 이미 나빴다에 가깝다.

임계값(0.45 · 0.85 …) — 업계 표준 아님

orderbook_env.py 프로젝트 시드/시작값 (Optuna·웹에서 변경 가능). “보편적으로 다들 0.45”가 아님.

기본 의미 체감
스프레드 0.45% (askbid)/mid 상한 중소형·급등에서 자주 걸림
잔량비 0.85 상위 N호가 매수÷매도 살 쪽이 거의 비슷해야 통과
깊이 1.2 필요수량 ×1.2 ≤ 매수호가 합
매도벽 3.0 매도잔량 과다 컷

AND라서 하나라도 깨지면 탈락 → 체감이 셈.

이 필터가 굳이 필요한가? → 필수 아님

목표 선택
거래 수·백테 건수 유지 OFF (현 운영 기본)
얇은 호가·큰 갭 진입을 줄이고 싶다 ON 또는 임계 완화
조건식이 이미 유동성 좋은 종만 필터 체감↓
  • 진입 필터 = 살까 말까
  • 익절 호가 ON = 이미 산 뒤 어떻게 팔까 (SELL_USE_ORDERBOOK_ON_PROFIT) — 다른 스위치
    호가가 안 좋게 찍힌다는 사실 자체가 “필터 필수” 근거가 아님.
    안 좋은 호가에 들어가느냐를 막을지 선택일 뿐. 과하면 끄거나 숫자만 푼다.

키움 조건 vs LS 조건 (진입·수익구간 호가매도 공통)

  • kiwoom_condition / condition → 호가 키움 0D RAM · DB ws_orderbook
  • ls_condition → 호가 LS UH1 RAM · DB ls_ws_orderbook (키움으로 메우지 않음)
    수익구간·손절호가도 같은 스냅샷 API를 탐. 전략 범위: 모멘텀(키움) + 돌파(LS) · 둘 다 기본 OFF. (진입 필터는 전 전략 키 있음).

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. 주식을 샀다.
  2. 가격이 조금 올랐다 (이득).
  3. 호가판을 보니 “사고 싶다”는 줄이 확 짧아졌다.
  4. 그때 이익을 챙기고 나간다 = 수익구간 호가매도.
  5. 아직 이득이 아니면 이 규칙으로 안 판다. (그건 손실구간 쪽)

왜 맨 위에 두면 안 되나

  • 장 초반·급등 초입은 원래 파는 사람이 많다 → “분위기 끝”으로 오해하기 쉬움
  • 사자마자 팔면 수수료만 나고 기회 날림
    맨 위(1순위) 금지. 래칫·어깨 다음, % 손절 .

측정(실매 매수 + ws_orderbook, 예: 2026-07-27 모멘텀):

  • 가드 없이 순간만 보면: 조기 신호 많음
  • 이득·최소보유 가드 후: 크게 줄어듦
    ※ 이 %는 수익률이 아님 = “산 뒤 N분 안에 이 신호가 뜬 건수 비율”

합의된 매도 순서

래칫 → 어깨 → 수익구간 호가매도 → 손실구간 호가매도 → % 손절 → 트레일 → …
  • 코드: 어깨 다음 · 손절 전 = 수익구간(호가컷) + 손절호가 · 기본 OFF
  • 모멘텀=키움 호가 · 돌파=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_*.
웹 모멘텀·돌파 탭에 컨트롤 있음. ENABLED 기본 false 유지.

발동 조건 (모두 만족)

조건
스위치 ON
비율 이동평균 < 기준 스냅 부족 → 안 함
최소 이득
최소 보유

배선 상태 (2026-08)

실매·백테 OR 히스토리 주입 · 웹 · Optuna 미포함(§8).
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 · 모멘텀+돌파)

수익구간과 같은 호가 눈, 반대 장부.

초등으로

  1. 주식을 샀다.
  2. 가격이 조금 떨어졌다 (손해).
  3. 호가판에 사려는 사람이 확 줄었다.
  4. 3% 같은 큰 손절선까지 기다리기 전에 먼저 나온다.
  5. 이득 중이면 이 규칙으로 안 판다. (그건 수익구간)

왜 만드나

% 손절만 있으면, 호가가 이미 무너진 뒤에 가격이 손절선에 닿아 더 나쁜 가격에 팔 수 있음.
수익구간 규칙만 있으면 손해 날 때는 안 도와줌.

원칙

  1. % 손절 숫자는 그대로. 이 규칙은 앞에서 한 번 더 보는 것뿐.
  2. 손해일 때만.
  3. 맨 위 금지 — 래칫·어깨 다음, % 손절 직전.
  4. 호가 스냅 없으면 안 함 (가격봉으로 호가 지어내기 금지).
  5. 나갈 때는 시장가/IOC(도망). 익절 지정가「호가 ON」과 다름.
  6. 기본 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 가정)

  1. 산 지 2분 미만 → 손절호가 안 함. (4% 하드만 가능)
  2. 2분 이상 + 현재가 0.3% 이하 + OR평균 < 0.4 → 사유 손절호가즉시 시장가 (하드 4%까지 안 기다림)
  3. 호가는 안 무너졌는데 가격만 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 (켜기 전 측정 권장)
TPE 안 넣음

측정 (호가 쌓인 뒤 · 모멘텀 매수가 있는 날)

  1. 손실→−%손절(또는 큰 손실) 건 중, 손절 직전에 이미 호가가 줄었던 비율
  2. 손해였다가 회복한 건에서 이 규칙이 너무 일찍 나갔을 비율
    → 측정 후 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. 매수필터 · 익절지정가 · 수익구간호가매도 · 손실구간호가매도를 한 스위치로 취급하지 말 것.
  2. “호가 쌓기”를 새 매매 설계로 착각하지 말 것 — 이미 있는 저장.
  3. 호가를 % 손절 공식에 섞지 말 것.
  4. 호가매도를 1순위에 두지 말 것.
  5. 스냅 없이 OHLC로 호가 지어내기 금지.
  6. 모의에 ORD_DVSN=13 강제 금지.
  7. 측정 없이 ENABLED=true 금지.
  8. 데이터 없이 TPE에 넣지 말 것 (§8).
  9. 거래대금·시총회전 으로 손절 만들지 말 것.

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 넣을까?

결론 (당분간) — 넣지 않음

매수필터 · 수익구간 호가매도 · 손실구간 호가매도 모두.

켜기/끄기를 TPE에 넣으면: 끈 구간=예전 점수, 켠 구간=호가 날씨에 흔들려 순위가 엉망이 된다.

나중에라도: 호가 충분 + 측정 숫자 + 사용자가 전용 mode를 말할 때만.
그때도 본체 tpe/fine이 아니라 좁은 mode에서 숫자만.

지금

쌓여 있는 호가로 측정 → (승인) 코드 → OFF 스모크 → ON 여부. TPE는 그 다음.


9. env 등록 주의

신규 키는 database.py ENV 등록 + get_env_from_db 재조회 검증.