From 5bdd72040e9f556009ab4f8af54f6f36207473a8 Mon Sep 17 00:00:00 2001 From: Justin Ko Date: Mon, 22 Jun 2026 10:36:50 +0900 Subject: [PATCH] =?UTF-8?q?docs(operations):=20=EB=AC=B4=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0/=EC=A3=BD=EC=9D=80=20=EC=A2=85=EB=AA=A9=20=EC=9C=84?= =?UTF-8?q?=EC=83=9D=20=EC=9A=B4=EC=98=81=EC=9E=90=20=EB=9F=B0=EB=B6=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(dcy-017=20=ED=9B=84=EC=86=8D)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dcy-017 지속 무데이터 알람의 후속 운영 절차를 operations.md §7로 문서화. - pause 시맨틱 코드 검증 결과 기술(신규 매수만 차단, 보유분 exit는 정상) - Step 1 알람검증 / Step 2 포지션확인 / Step 3a pause(가역) / Step 3b 영구제거 - 보유 포지션 안전성 + 영구삭제 시 고아 방지 순서 명시 - 코드 변경 없음(기존 trex-core pause/resume 기능 문서화) Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5 --- docs/operations.md | 144 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/docs/operations.md b/docs/operations.md index 4b2923d..f8fbb5b 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -223,6 +223,150 @@ psql "$TREX_POSTGRESQL_DSN" -c "SELECT slot_id, from_state, to_state, reason, ac --- +## 7. 무데이터/죽은 종목 위생 (dcy-017 후속) + +dcy-017이 "지속 무데이터" WARN 다이제스트를 운영자에게 표시한다. 그 후속 조치로 해당 종목을 매매 유니버스에서 pause 또는 영구 제거하는 절차. + +> **용어**: `portfolio_id` = trex-core에서 종목을 식별하는 키 (portfolio-hub asset_id와 동일). pause/resume 명령의 대상이다. + +### 트리거 + +- dcy-017 지속 무데이터 WARN 알람(시세 N회 연속 무응답). +- 운영자가 직접 관찰: KRX 거래정지/상장폐지 공시, 종목 이상 감지. + +--- + +### pause 시맨틱 (코드 검증 결과) + +> 아래는 코드 직접 확인 결과다. 추측 없이 file:line을 근거로 기술한다. + +**pause가 하는 것 — 신규 진입(매수) 차단** + +`pause_store.pause(portfolio_id)` = `INSERT INTO snapshot_pause (portfolio_id, ...) ON CONFLICT DO UPDATE` (`adapters/portfolio/pause_store.py:23`). 이후 slots RPC 응답에서 해당 slot의 `paused=True` 필드가 반환된다 (`application/portfolio.py:309-313`, `application/messaging_handlers.py:264-273`). + +trex-core 코어 자체(`application/intents.py` `_admit_buy`, line 179~257)에는 pause 체크가 없다. **pause는 전략 데몬(orb 등)이 slots 응답의 `paused=True`를 읽고 그 slot에 buy intent를 보내지 않는 방식으로 신규 진입을 차단한다.** 전략 데몬이 pause를 무시하고 buy intent를 보내면 코어는 수락한다(전략 데몬 쪽 정책). + +**pause가 하지 않는 것 — 기존 보유분 매도는 막지 않는다** + +`application/safety_sell.py`의 `check_forced_close` / `check_prices` / `check_market_close` 모두 `self._holding(now)` (= `slot_repo.list_holding_slots()`)를 직접 조회하고 pause_store를 참조하지 않는다. tsl 전략 SellIntent, `trex-core exit` 명령도 마찬가지다. + +**결론**: HOLDING 슬롯이 있는 종목을 pause해도 그 보유분은 safety-sell, tsl 전략, 운영자 exit 등 모든 매도 경로로 정상 청산된다. **pause는 신규 매수만 막고, 기존 포지션의 exit은 방해하지 않는다.** 포지션이 있어도 pause 명령은 안전하다. + +**resume 원복** + +`pause_store.resume(portfolio_id)` = `DELETE FROM snapshot_pause WHERE portfolio_id = %s` (`adapters/portfolio/pause_store.py:35`). 삭제 즉시 다음 slots RPC 응답부터 `paused=False`로 반환되어 정상 진입이 재개된다. + +--- + +### Step 1 — 무데이터 알람 검증 + +알람 수신 후 즉시 종목 상태를 확인한다. + +```bash +# 해당 종목의 슬롯 상태 확인 (symbol 또는 portfolio_id로 필터) +.venv/bin/trex-core slots --symbol +.venv/bin/trex-core slots --status all # 전체 목록에서 확인 +``` + +KRX 공시/HTS에서 상태 분류: + +| 상태 | 의미 | 권장 조치 | +|---|---|---| +| 일시적 시세 장애 | 단기 데이터 공백, 종목 정상 | 관망 → 자동 복구 확인 | +| 임시 거래정지 (공시/사유 있음) | 거래 재개 예정 가능 | pause (Step 3a) | +| 장기 거래정지 / 상폐 예정 | 재개 불명확 | pause 또는 영구 제거 검토 (Step 3b) | +| 상장폐지 완료 | 거래 불가 | 포지션 0 확인 후 영구 제거 (Step 3b) | + +--- + +### Step 2 — 포지션 확인 + +pause 또는 제거 전에 **해당 종목에 보유 포지션이 있는지** 반드시 확인한다. + +```bash +# 1) 운영자 리포트 — 보유·대기 주문 한눈에 확인 +.venv/bin/trex-core report + +# 2) 해당 종목 슬롯 상세 조회 +.venv/bin/trex-core slots --symbol +# 또는 portfolio_id를 알고 있을 때 +.venv/bin/trex-core slots --status holding + +# 3) DB 직접 확인 (정밀 조회) +psql "$TREX_POSTGRESQL_DSN" -c \ + "SELECT slot_id, status, symbol, qty, avg_price, portfolio_id FROM daily_slot WHERE symbol = ''" +``` + +출력의 `status` 확인: +- `inactive` — 포지션 없음. pause 또는 제거가 자유롭다. +- `holding` — **보유 포지션 있음.** pause는 안전하다(아래 설명). 영구 삭제는 포지션 청산 후에만 한다. +- `pending_buy` / `pending_sell` — 주문 대기 중. 완료 대기 또는 `trex-core cancel `로 취소 후 진행. + +--- + +### Step 3a — pause (가역, 권장 기본) + +임시 거래정지, 무데이터 장기화, 불확실한 상황에서는 **pause가 기본 선택**이다. 언제든 resume으로 원복 가능하다. + +```bash +# pause 적용 (direct-DB write, 데몬 RPC 불필요) +.venv/bin/trex-core pause +# 예: trex-core pause pf-005930 + +# pause 상태 확인 +.venv/bin/trex-core paused + +# 슬롯 조회 시 paused=True 확인 +.venv/bin/trex-core slots --symbol +``` + +pause 후 동작: +- **다음 slots RPC 응답부터** 해당 slot의 `paused=True` 반환 → orb 등 전략 데몬이 그 slot에 buy intent를 보내지 않음 → **신규 매수 차단**. +- **기존 HOLDING 포지션은 영향 없음** — safety-sell(watchdog, stop-loss, 장마감 청산), tsl 전략 SellIntent, `trex-core exit` 모두 pause와 무관하게 정상 작동. +- pause는 Postgres `snapshot_pause` 테이블에 영속 저장되므로 **데몬 재시작 후에도 유지**된다. + +복구 (종목이 정상화되면): +```bash +.venv/bin/trex-core resume +# 예: trex-core resume pf-005930 + +# 확인 +.venv/bin/trex-core paused # 목록에서 사라졌는지 +.venv/bin/trex-core slots --symbol # paused=False 확인 +``` + +--- + +### Step 3b — 영구 제거 (상장폐지 등, 드문 케이스) + +> **안전 경고**: 영구 제거는 비가역이다. 반드시 포지션이 0인 것을 확인한 후 진행한다. portfolio-hub 삭제는 고아 슬롯 위험이 있으므로 아래 순서를 엄수한다. + +**전제 조건**: +1. 해당 종목 slot이 `inactive` 상태 (보유·대기 포지션 없음). +2. 포지션이 남아 있으면 먼저 청산(tsl/safety-sell 대기 또는 `trex-core exit`으로 수동 매도) 후 진행. + +**절차**: +1. `trex-core pause ` — 추가 진입 차단(옵션이나 권장). +2. `trex-core slots --symbol ` — `status=inactive`, `qty=None` 확인. +3. portfolio-hub에서 해당 portfolio(asset) 삭제 → **portfolio-hub 소관 작업. 직접 invocation은 portfolio-hub 운영자에게 요청하거나 사장님과 협의.** (portfolio-hub가 삭제하면 다음 universe 스냅샷 갱신 시 해당 slot이 자동 retire된다.) +4. 다음 날 또는 스냅샷 갱신 후 `trex-core slots`에서 해당 종목이 사라졌는지 확인. + +> portfolio-hub 삭제 없이 trex-core DB에서 slot을 직접 제거하면 고아 원장 레코드가 생길 수 있다. DB 직접 삭제는 금지한다. + +--- + +### 안전 경고 요약 + +``` +[PAUSE] 가역·원장보존. 언제든 resume 가능. 포지션 있어도 안전(매도 경로 유지). +[DELETE] 영구·비가역. 반드시 포지션 0 확인 후. portfolio-hub와 협의. + +포지션(HOLDING)이 있는 종목은 pause만 적용하라. +영구 삭제는 포지션을 완전히 청산한 뒤에만 수행한다. +``` + +--- + ## 관련 문서 - `docs/interface-contract.md` — RabbitMQ 토폴로지 + 메시지 payload + reason/actor 어휘 - `docs/runtime-flows.md` — 흐름 다이어그램(스냅샷/intent RPC/이벤트/집행 루프/로깅)