feat: Enhance trading system with new permanent subscription features and order book management

Changes:
- Added a new API endpoint for managing permanent subscriptions, allowing users to enable or disable subscriptions dynamically.
- Implemented a function to fill candle data from Kiwoom, ensuring that only relevant data is inserted into the database.
- Introduced a mechanism to handle master subscription states, improving the management of subscription statuses.
- Updated the database schema to include new fields for managing subscription states and order book filtering.

Impact:
- These enhancements improve the flexibility and reliability of the trading system, allowing for better management of subscriptions and order book data, while reducing the risk of data inconsistencies.

히스토리 align 제거 븅신같은 초기설계 아예 제거
진입모드에 구멍메움
호가진입을 켜도 호가가 안들어올때 호가 안보고 그냥 사버림
This commit is contained in:
Your Name
2026-08-15 23:01:14 +09:00
parent 4a18ce2697
commit 36a3e2b4a1
94 changed files with 6368 additions and 1639 deletions

View File

@@ -35,19 +35,20 @@
---
## 0-1. 운영 메모 (스위치 OFF · 저장은 ON)
## 0-1. 운영 메모 (2026-08-15 기준 · DB가 진실)
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 유지” 메모를 폐기**한 현재 운영이다. 스위치는 웹「운영 설정」·전략탭 DB를 본다.
실매·백테는 스위치 OFF면 **예전과 동일**. 저장만 계속된다.
1. **호가 저장** — ON. 시계열 TTL = `WS_ORDERBOOK_TICK_MAX_AGE_SEC`(코드 기본 **30초**. UI에 예전에 “3초”라고 적혀 있던 것은 **저장용 힌트 오류**였음).
2. **매수 호가필터** `*_ORDERBOOK_FILTER_ENABLED` = **ON** (모멘텀·돌파·스캘프·꼬리). 숫자는 전략별(스프·잔량·매도벽).
3. **수익구간 호가매도** `MOMENTUM_EXIT_OB_ENABLED` / `BREAKOUT_EXIT_OB_ENABLED` = 운영 **ON**인 적이 있음 → **DB 확인**. 본 문서 옛 “OFF 유지”는 폐기.
4. **손절호가** `*_STOP_OB_ENABLED` = 마찬가지로 **DB 확인** (코드 배선됨).
5. **필터 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 없음. 다음 매수 검사에서 사진 있으면 다시 봄.
한산해서 사진이 **안 바뀌는 것**과 다름 — 마지막 사진이 있으면 그걸로 검사.
6. **Optuna Stage2(후처리)** 는 캔들 TPE와 **별도**. 진입 격자·매도벽 축은 `docs/옵투나.md` · §8. `--apply-best` 없이 후처리만 재실행 가능 (`optuna_rerun_postprocess.py`).
---
@@ -56,11 +57,11 @@
| 구분 | 언제 | 하는 일 | 대표 키 | 상태 |
|------|------|---------|---------|------|
| **호가 저장(쌓기)** | 장중 항상 | DB에 호가 기록 | `LS_WS_ORDERBOOK_SAVE` / `WS_ORDERBOOK_SAVE_ENABLED` 등 | **이미 동작 중** · 설계 아님 |
| **매수 호가필터** | 사기 직전 | 호가 나쁘면 **안 삼** | `{전략}_ORDERBOOK_FILTER_ENABLED` | 코드 있음 · 운영 OFF |
| **매수 호가필터** | 사기 직전 | 호가 나쁘면 **안 삼** | `{전략}_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** |
| **손실구간 호가매도** | 들고 있을 때 **손해 난 뒤** | 사려는 사람 줄면 **큰 손절 전에 먼저 나옴** | `MOMENTUM_STOP_OB_*` / `BREAKOUT_STOP_OB_*` | **배선 완료 · 기본 OFF** |
| **수익구간 호가매도** | 들고 있을 때 **이득 난 뒤** | 사려는 사람 줄면 **미리 이익 실현** | `MOMENTUM_EXIT_OB_*` / `BREAKOUT_EXIT_OB_*` | 코드 있음 · **시드 OFF / 운영은 DB** |
| **손실구간 호가매도** | 들고 있을 때 **손해 난 뒤** | 사려는 사람 줄면 **큰 손절 전에 먼저 나옴** | `MOMENTUM_STOP_OB_*` / `BREAKOUT_STOP_OB_*` | 배선 완료 · **시드 OFF / 운영은 DB** |
거래내역 **「매수호가」** = 살 때 근처 · **「매도호가」** = 팔 때 근처. 서로 다른 칸.
@@ -74,8 +75,8 @@
| 내 장부 | **이미 조금 이득**일 때만 | **이미 조금 손해**일 때만 |
| 하는 일 | “이득 났는데 분위기 깨지면 이익 챙기고 나감” | “손해인데 분위기 더 깨지면, −몇% 손절선까지 안 가고 먼저 나감” |
| −몇% 손절선 | **안 건드림** | **공식은 그대로** · 그 **앞에서** 따로 한 번 더 봄 |
| 지금 | 코드 있음, 스위치 OFF | 글만, 코드 없음 |
| 파람서치(TPE) | **당분간 안 넣음** | **당분간 안 넣음** |
| 지금 | 코드 있음 · ENABLED는 **DB** | 코드 있음(`STOP_OB`) · ENABLED는 **DB** |
| 파람서치 | 캔들 TPE 본체에는 안 넣음. **후처리** Stage2 (`docs/옵투나.md`) | 후처리 STOP 축 |
---
@@ -116,21 +117,25 @@
- 거래내역 「매수호가」에 **간격큼·살힘 0.1x대**가 보여도 **데이터가 이상한 게 아니라** 그 타점 유동성이 그런 것
→ “조건식이 잘못 뽑았다”로 단정하지 말 것. **유동성 게이트가 한 겹 더 있어서** 건수가 줄어든다.
### RAM(실매) vs DB 3초(백테)
### RAM(실매) vs DB 저장(백테)
| | 실매 필터 ON | 백테 필터 ON |
|--|--------------|--------------|
| 보는 것 | **RAM 최신** (`get_orderbook_snapshot`) | DB 스냅 / `filter_eval` 재생 |
| 스냅 없음 | **안 막음**(통과) | 재생·본체 없으면 그 타점 못 탐·탈락 재현 |
| 보는 것 | **RAM** `get_orderbook_snapshot(max_age=FILTER_MAX_AGE)` | DB 스냅 / `filter_eval` 재생 |
| 나이 | `FILTER_MAX_AGE=0` → **마지막 장**으로 검사 (저장 30초와 무관) | 체결 시각 근처 행 |
| 스냅 한 번도 없음 | 필터 ON이면 **안 삼**(기본). REST 없음 | 재생·본체 없으면 그 타점 못 탐 |
DB ~3초 저장은 **백테·표시용**. 실매 차단은 RAM.
“3초라서 가짜로 막는다”보다 **그 시각 근처 호가가 이미 나빴다**에 가깝다.
저장 TTL(`TICK_MAX_AGE`, 기본 30)은 **0D 덤프·가짜 채움 방지**용.
필터가 이걸 같이 쓰면 만료=`None`=통과 구멍이 난다 (2026-08-14 실매에서 확인).
거래내역 「매수호가」칸의 `+N초 탈락-매도벽` 은 **근처 스냅 스탬프**이지, 그 초가 필터를 통과했다는 뜻이 아님.
### 임계값(0.45 · 0.85 …) — 업계 표준 아님
`orderbook_env.py` **프로젝트 시드/시작값** (Optuna·웹에서 변경 가능). “보편적으로 다들 0.45”가 **아님**.
| 기본 | 의미 | 체감 |
|------|------|------|
| 스프레드 **0.45%** | `(askbid)/mid` 상한 | 중소형·급등에서 **자주 걸림** |
| 스프레드 **0.45%** (시드) | `(askbid)/mid` 상한 | 운영 모멘텀은 **3.0** 근처도 있음. `orderbook_env` 시드 ≠ 실매 DB |
| 잔량비 **0.85** | 상위 N호가 매수÷매도 | 살 쪽이 거의 비슷해야 통과 |
| 깊이 **1.2** | 필요수량 ×1.2 ≤ 매수호가 합 | |
| 매도벽 **3.0** | 매도잔량 과다 컷 | |
@@ -140,8 +145,8 @@ AND라서 하나라도 깨지면 탈락 → 체감이 셈.
### 이 필터가 굳이 필요한가? → **필수 아님**
| 목표 | 선택 |
|------|------|
| 거래 수·백테 건수 유지 | **OFF** (현 운영 기본) |
| 얇은 호가·큰 갭 진입을 줄이고 싶다 | ON 또는 **임계 완화** |
| 거래 수·백테 건수 유지 | **OFF** 또는 임계 완화 |
| 얇은 호가·큰 갭 진입을 줄이고 싶다 | **ON** (2026-08 실매는 이 쪽) |
| 조건식이 이미 유동성 좋은 종만 | 필터 체감↓ |
- **진입 필터** = 살까 말까
@@ -152,7 +157,7 @@ AND라서 하나라도 깨지면 탈락 → 체감이 셈.
### 키움 조건 vs LS 조건 (진입·수익구간 호가매도 공통)
- `kiwoom_condition` / `condition` → 호가 **키움 0D RAM** · DB `ws_orderbook`
- `ls_condition` → 호가 **LS UH1 RAM** · DB `ls_ws_orderbook` (키움으로 메우지 않음)
수익구간·손절호가도 같은 스냅샷 API를 탐. **전략 범위: 모멘텀(키움) + 돌파(LS)** · 둘 다 기본 OFF. (진입 필터는 전 전략 키 있음).
수익구간·손절호가도 같은 스냅샷 API. **2026-08 운영:** 4전략 유니버스는 키움 조건이 본체. LS는 틱/호가 **구독 spill 3차**이지 “LS 전용 종목”이 아님 (`LIVE_OB_PROVIDER=kiwoom`).
---
@@ -249,7 +254,7 @@ AND라서 하나라도 깨지면 탈락 → 체감이 셈.
```
래칫 → 어깨 → 수익구간 호가매도 → 손실구간 호가매도 → % 손절 → 트레일 → …
```
- 코드: 어깨 다음 · 손절 전 = 수익구간(`호가컷`) + 손절호가 · **기본 OFF**
- 코드: 어깨 다음 · 손절 전 = 수익구간(`호가컷`) + 손절호가 · 코드 시드 OFF(운영 DB)
- 모멘텀=키움 호가 · 돌파=LS 호가 (교차 메우기 금지)
- % 손절은 **가격 하나만**. 호가를 손절 % 숫자에 섞지 않음.
@@ -263,7 +268,7 @@ AND라서 하나라도 깨지면 탈락 → 체감이 셈.
| `*_EXIT_OB_MIN_HOLD_BARS` | 3 | 산 지 최소 N분 |
등록: `database.py` (`config_momentum` / `config_breakout`) · defaults → `exit_ob_*`.
웹 모멘텀·돌파 탭에 컨트롤 있음. **ENABLED 기본 false 유지.**
웹 모멘텀·돌파 탭에 컨트롤 있음. **코드 기본 false** · 실제 ON/OFF는 **DB**.
### 발동 조건 (모두 만족)
| 조건 | |
@@ -274,8 +279,8 @@ AND라서 하나라도 깨지면 탈락 → 체감이 셈.
| 최소 보유 | |
### 배선 상태 (2026-08)
실매·백테 OR 히스토리 주입 ✅ · 웹 ✅ · Optuna **미포함**(§8).
OFF면 예전과 동일.
실매·백테 OR 히스토리 주입 ✅ · 웹 ✅ · Optuna **캔들 본체 TPE에는 미포함**.
숫자 탐색은 **후처리** Stage2 (`optuna_orderbook_recommend.py` EXIT/STOP 축). OFF면 예전과 동일.
### 측정 스크립트 (매도쪽 · 필터 켜기 전)
`scripts/measure_ob_exit_early_fire.py`
@@ -290,7 +295,7 @@ A 28.6% → B/C 14.3% (가드가 핵심).
같은 날 `ls_ws_orderbook`은 그 종목들 스냅 0 → LS로 못 재현 (모멘텀=키움 조건 쪽).
### 전략 범위
파일럿 **모멘텀만**. 진입 필터 키와 **혼용 금지**.
모멘텀+돌파 배선. 진입 필터 키와 **혼용 금지**.
---
@@ -358,8 +363,8 @@ OR 5장 채우는 시간: LS 틱동기면 체결 몇 번(초 단위) · 키움 ~
|------|--|
| 설계 | ✅ |
| 코드·DB·웹 | ✅ 2026-08-03 배선 |
| 기본값 | **OFF** (켜기 전 측정 권장) |
| TPE | **안 넣음** |
| 기본값 | 코드 시드 **OFF** · 실제는 **DB** |
| TPE | 캔들 본체 축 **아님**. 후처리 STOP 축 (`docs/옵투나.md`) |
### 측정 (호가 쌓인 뒤 · 모멘텀 매수가 있는 날)
1. 손실→−%손절(또는 큰 손실) 건 중, 손절 **직전**에 이미 호가가 줄었던 비율
@@ -402,9 +407,10 @@ OR 5장 채우는 시간: LS 틱동기면 체결 몇 번(초 단위) · 키움 ~
4. 호가매도를 **1순위**에 두지 말 것.
5. 스냅 없이 OHLC로 호가 **지어내기** 금지.
6. 모의에 ORD_DVSN=13 강제 금지.
7. 측정 없이 ENABLED=true 금지.
8. 데이터 없이 TPE에 넣지 말 것 (§8).
9. 거래대금·시총회전 **만**으로 손절 만들지 말 것.
7. 필터 TTL과 저장 TTL을 **한 키로 묶지 말 것** (`FILTER_MAX_AGE` vs `TICK_MAX_AGE`).
8. 캔들 TPE 본체에 호가 ON/OFF를 넣지 말 것. 숫자 탐색은 **후처리**만 (§8).
9. 거래대금·시총회전 **만**으로 손절 만들지 말 것.
10. 필터 ON인데 호가 사진이 없다고 **통과**시키지 말 것. REST로 메우지 말 것 (`REJECT_IF_EMPTY`).
---
@@ -428,18 +434,30 @@ OR 5장 채우는 시간: LS 틱동기면 체결 몇 번(초 단위) · 키움 ~
---
## 8. Optuna / TPE 넣을까?
## 8. Optuna / TPE (2026-08-15)
### 결론 (당분간) — **넣지 않음**
매수필터 · 수익구간 호가매도 · 손실구간 호가매도 모두.
### 캔들 본체 TPE (Stage 1)
매수필터 ON/OFF · EXIT_OB · STOP_OB 를 **차트 trial 축에 넣지 않음**.
끈 구간과 켠 구간이 한 랭킹에 섞이면 순위가 호가 날씨에 흔들린다.
켜기/끄기를 TPE에 넣으면: 끈 구간=예전 점수, 켠 구간=호가 날씨에 흔들려 **순위가 엉망**이 된다.
### 후처리 (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` 아님. 실매 숫자는 안 바뀜.)
나중에라도: 호가 충분 + 측정 숫자 + 사용자가 **전용 mode**를 말할 때만.
그때도 본체 `tpe`/`fine`이 아니라 **좁은 mode**에서 숫자만.
진입 탐색 격자 (`ensure_optuna_gate_env_defaults` / `OPTUNA_OB_ENTRY_*`, env_config_ext):
### 지금
쌓여 있는 호가로 **측정** → (승인) 코드 → OFF 스모크 → ON 여부. **TPE는 그 다음.**
| 축 | 범위 (널널 검사, 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` 만. 후처리 재실행 ≠ 적용.
---