feat(execution,obs): 호가단위 스냅(dcy-030/#28) + 운영 관측성(dcy-029/#29) + REL-007 환류
dcy-030 (issue #28, P0 체결 복구): - REQ-083 KRX 호가단위(tick) 순수 유틸 (domain/pricing.py) - REQ-070 CHG: 슬리피지 실효가 호가단위 스냅 — 상한 준수(매수 floor/매도 ceil) + band<1tick 하한/상한 보장 + 부동소수 epsilon 가드. 147700→148400/54500→54700. - BAT-044 release-candidate dcy-029 (issue #29, 운영 관측성): - REQ-084 구조화 로깅 기반 (runtime/logging_setup.py): ISO8601 KST·레벨·Loki JSON ·correlation id(slot_id) - REQ-085 주문 결과 로깅 (event=order_submit|fill|reject) - REQ-087 거부 ERROR 가시성 (조용한 롤백 제거) - REQ-086 청산 reason+actor 동반 노출 (안전망/전략 구분, 신규 필드 없음) - REQ-088 운영 조회 CLI report (application/operator_report.py, 보유·오늘 체결 ·실현 PnL net·대기, direct-DB) - REQ-089 운영자 런북 docs/operations.md - REQ-090 baseline 정합 (runtime-flows·project-structure·interface-contract) - BAT-045(P0)·BAT-046(P1) release-candidate REL-007: confirmed → feedback-captured (운영 인스턴스 라이브 검증 + #28/#29 환류 정규화). BAT-037~041 released. 검증: 537 passed, 47 skipped, 회귀 0, ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
# 0. 문서 상태
|
||||
이 문서의 현재 상태와 식별 정보를 기록한다. 문서 추적과 승인 흐름에 필요한 최소 메타데이터를 적는다.
|
||||
|
||||
- 상태: confirmed
|
||||
- 문서 ID: dcy-029_20260616_operational-observability-logging-and-pnl-view
|
||||
- 이슈명: 운영 관측성 — 주문 결과 로깅 + 로그 타임스탬프 + 청산사유 분류 + 슬롯/PnL 조회 + 운영자 런북 (issue #29)
|
||||
- 작성 시각(KST): 2026-06-16 10:52
|
||||
- 마지막 갱신 시각(KST): 2026-06-16 12:55
|
||||
- 대체됨: 없음
|
||||
- 후속 Discovery 참조: 없음
|
||||
- 생성된 REQ 참조: REQ-084, REQ-085, REQ-086, REQ-087, REQ-088, REQ-089, REQ-090
|
||||
|
||||
# 1. 계획 상태 요약
|
||||
현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다.
|
||||
|
||||
- 연계 문서 현황:
|
||||
- 관련 점수 파일: 미생성
|
||||
- 관련 TODO/메모: GitHub issue #29 (P0: 1·2 / P1: 3·4·5), 관련 issue #28(호가단위 거부 — 별도 정확성 버그 트랙)
|
||||
- 해석:
|
||||
- 2026-06-15/16 라이브 테스트에서 trex-core의 운영 가시성 부족이 진단을 막았다. 매수 거부(#28)가 로그에 없고 `slot_transition_log` DB에만 남아 수 시간 동안 인지하지 못했고, 로그에 타임스탬프가 없어 타임라인 재구성이 불가능했다.
|
||||
- 이번 Discovery는 "거래 실패를 즉시·정확히 관측"하기 위한 로깅/분류/조회 표면을 정의한다. 청산사유 분류·조회 뷰가 런타임 흐름과 read 표면을 바꾸므로 minor-change가 아니라 정식 Discovery로 다룬다.
|
||||
|
||||
# 2. 요구사항 판정 결과
|
||||
이번 요청이 기존 요구사항과 비교해 신규인지, 유사한지, 중복인지 판단하고 그 근거를 남긴다.
|
||||
|
||||
- 판정: 신규
|
||||
- 근거
|
||||
- 기존 Discovery(dcy-001~028)는 슬롯 도메인·전송·브로커·안전매도·자산중심 슬롯·전략 read 표면을 다뤘고, "운영 관측성(로깅/타임스탬프/청산사유 분류/PnL 조회)"을 정면으로 다룬 주기는 없다. dcy-016/dcy-018(슬롯 상태 가시화·운영자 CLI)은 슬롯 상태 조회에 한정돼 주문 결과 로깅·PnL 집계와 범위가 다르다.
|
||||
- issue #29는 신규 운영 환류(라이브 테스트 인사이트)에서 출발한 독립 주제로, 후속 반복을 독립 주기로 추적하는 편이 책임 경계가 명확하다.
|
||||
|
||||
# 3. 문제점의 요약
|
||||
문제의 현재 상태, 기대 상태, 관련 이해관계자, 제약사항을 정리한다. Discovery의 핵심 맥락을 가장 먼저 이해할 수 있어야 한다.
|
||||
|
||||
- 현재 상태
|
||||
- 로그 포맷: `trex_core/runtime/daemon.py:408`·`trex_core/runtime/reset.py:129`가 `logging.basicConfig(level=logging.INFO)`만 호출해 **format 문자열이 없다** → 모든 로그 라인에 타임스탬프·레벨 prefix가 없다.
|
||||
- 주문 결과 가시성: `trex_core/application/execution.py`의 submit/poll 경로는 산발적 `_log.debug`/`_log.warning`만 남기고, 주문 submit + 결과(체결가/수량/수수료/세금 또는 거부+사유)를 일관된 tailable 로그로 남기지 않는다. `_rollback()`은 거부 시 슬롯을 **조용히** 전이(`slot_transition_log`에만 기록)하고 ERROR 로그를 남기지 않아 호가단위 거부(#28)가 로그에 보이지 않았다.
|
||||
- 청산사유 분류: `trex_core/application/safety_sell.py`·`trex_core/application/watchdog.py` 양쪽이 매도를 일으키는데, watchdog 청산이 `stop_loss`로 뭉뚱그려져 "진짜 손절"로 오해를 유발했다(2026-06-15 064400 사례).
|
||||
- 조회 표면: 운영자 CLI(`trex_core/runtime/cli.py`)는 `slots/universe/pause/enter/exit/cancel`만 제공하고, 보유/오늘 체결/실현 PnL(수수료·세금 반영)/대기 주문을 한눈에 보는 뷰가 없다. 일일 실현 PnL을 `ledger_outbox`에서 수기 계산해야 했다. 데몬은 RabbitMQ 전용(BAT-007)이라 HTTP 표면이 없다.
|
||||
- baseline 문서(`docs/project-structure.md`, `docs/runtime-flows.md`)는 존재하며 최신(2026-06-15 갱신). 단, 로깅/관측성·PnL 조회 흐름은 아직 기술돼 있지 않다.
|
||||
- 기대 상태
|
||||
- 모든 로그 라인에 ISO8601(KST) 타임스탬프 + 레벨이 붙고, 모든 주문 submit/결과/거부가 타임스탬프 달린 일관 로그(Loki 적재용 구조화 JSON + correlation id)로 즉시 관측된다.
|
||||
- 청산이 기존 reason 어휘(`stop_loss`/`take_profit`/`forced_close_at`/`market_close_liquidation`)와 actor(`safety_sell`/`strategy`/`execution_loop`)를 함께 노출해, "안전망 자율청산"과 "전략 손절"이 구분 기록된다.
|
||||
- 운영자가 보유·오늘 체결·실현 PnL(수수료·세금 반영)·대기 주문을 단일 CLI 명령으로 조회할 수 있어 일일 리포트 자동화의 기반이 된다.
|
||||
- 주문 거부가 조용히 묻히지 않고 ERROR 로그로 즉시 인지된다(알림 전송 채널은 후속 반복으로 Deferred).
|
||||
- 이해관계자
|
||||
- 운영 담당자(trex 운영팀): 실패를 즉시 인지하고 일일 리포트를 자동화해야 하는 1차 수요자.
|
||||
- 문서 작성자(AI agent): Discovery/REQ/Batch 산출물 작성·구현·검증.
|
||||
- 리뷰어/승인자(Justin Ko): 범위·계약·우선순위 확정.
|
||||
- 제약사항
|
||||
- 데몬은 HTTP 표면이 없다(BAT-007, RabbitMQ-only). "조회 endpoint"는 운영자 CLI 확장 또는 RabbitMQ RPC로 구현해야 하며 HTTP 신설은 아키텍처 역행이다.
|
||||
- 청산사유 reason 값은 전략/운영자가 읽는 계약 표면(`slots.req`, 이벤트, `slot_transition_log`)에 노출되므로 어휘 변경 시 하위호환을 고려해야 한다.
|
||||
- 코어는 알림 전송 채널의 1차 소유자가 아니다(ERROR 로그는 코어, 전송은 운영 인프라/외부).
|
||||
|
||||
## 주요 사용자 시나리오
|
||||
사용자 또는 운영자가 실제로 어떤 상황에서 이 변경을 필요로 하는지 대표 시나리오를 적는다.
|
||||
|
||||
- 운영자가 거래일 중 `tail -f`로 trex-core 로그를 보며 매수 주문이 호가단위 오류로 거부되는 것을 **즉시** 발견하고(타임스탬프+ERROR), #28 같은 문제를 몇 시간이 아니라 즉시 인지한다.
|
||||
- 운영자가 장 마감 후 단일 명령/RPC로 "오늘 체결 N건, 실현 PnL(수수료·세금 반영) X원, 현재 보유 M종목, 대기 주문 K건"을 조회해 일일 리포트를 자동 생성한다.
|
||||
- 운영자가 슬롯 청산 기록을 보고 그것이 watchdog 강제청산인지 진짜 손절(stop_loss)인지 사유로 즉시 구분한다.
|
||||
|
||||
# 4. 이번에 정의할 변경
|
||||
이번 Discovery에서 다루는 핵심 변경 항목을 목록으로 적는다. 기능, 구조, 규칙 변경을 함께 포함해도 된다.
|
||||
|
||||
- 로깅 기반(P0): 전 로그 라인에 ISO8601(KST) 타임스탬프 + 레벨 prefix 부여, 모든 주문 submit/결과(체결가·수량·수수료·세금 또는 거부+사유)를 타임스탬프 달린 일관 로그로 출력, Loki 적재용 구조화(JSON) 출력 + correlation id(intent→order→fill) 전파.
|
||||
- 의미론·가시성(P1): 청산을 기존 reason 어휘 + actor를 함께 노출해 안전망/전략을 구분 기록(신규 필드 없음), 주문 거부 시 조용한 롤백 대신 ERROR 로그(알림 전송 채널은 Deferred).
|
||||
- 조회 표면(P1): 보유·오늘 체결·실현 PnL(수수료·세금 반영)·대기 주문 조회 뷰를 운영자 CLI 서브커맨드로 추가(DB-backed, RPC 없음).
|
||||
- baseline 문서 갱신: `docs/runtime-flows.md`에 로깅/관측성·청산사유 분류·PnL 조회 흐름을 추가하고, 신규 모듈(로깅 포맷/주문결과 로깅/PnL 집계)이 생기면 `docs/project-structure.md`에 책임을 반영.
|
||||
- 운영자 런북 문서(P1): `docs/operations.md` 신설 — 배포/실행·운영자 CLI 사용법·로깅/관측성 사용법·디버깅/트러블슈팅. `run-trex-core-daemon` SKILL의 사실 자료 추출 + 본 반복 신설 관측성 기능 사용법 통합.
|
||||
|
||||
## 영향 범위
|
||||
변경이 미칠 코드/문서/운영 범위를 명시해 REQ 정제와 batch planning에서 추정 누락을 줄인다.
|
||||
|
||||
- 코드 영향:
|
||||
- `trex_core/runtime/daemon.py`·`trex_core/runtime/reset.py`: 로깅 포맷(타임스탬프+레벨, KST) 설정.
|
||||
- `trex_core/application/execution.py`: 주문 submit/결과/거부 일관 로깅, `_rollback()` 거부 시 ERROR 로그.
|
||||
- `trex_core/application/safety_sell.py`·`trex_core/application/watchdog.py`(+ `trex_core/domain/slot/service.py` reason 전파): 청산사유 분류.
|
||||
- 신규/확장: 주문결과 로깅 헬퍼, PnL 집계 read(`ledger_outbox`/체결 집계), 조회 CLI 서브커맨드(`trex_core/runtime/cli.py`).
|
||||
- 구조화 출력(Loki JSON) + correlation id(intent→order→fill) 전파 (FR-7, In Scope).
|
||||
- 문서 영향:
|
||||
- `docs/runtime-flows.md`(로깅/관측성·PnL 조회 흐름 추가), `docs/project-structure.md`(신규 모듈 책임), `docs/interface-contract.md`(조회 CLI·청산사유 reason+actor 어휘·거부 로그 계약).
|
||||
- 운영 영향:
|
||||
- 로그 포맷 변경으로 기존 로그 파싱(있다면) 조정 필요. 조회 뷰/PnL 집계로 일일 리포트 자동화 가능. (알림 채널은 이번 범위 밖.)
|
||||
|
||||
# 5. 요구사항 목록
|
||||
실제로 구현과 검증으로 이어질 요구사항을 기능/비기능으로 나누어 정리하고, 범위 경계를 명확히 적는다.
|
||||
|
||||
## 기능 요구사항
|
||||
- FR-1 (P0): 모든 로그 라인에 ISO8601 타임스탬프(KST) + 로그 레벨 prefix를 부여한다(현재 prefix 없음).
|
||||
- FR-2 (P0): 모든 주문 submit과 그 결과(체결가·체결수량·수수료·세금, 또는 거부+사유)를 타임스탬프 달린 일관(tailable) 로그 라인으로 출력한다. 호가단위 같은 거부가 즉시 보여야 한다.
|
||||
- FR-3 (P1): 청산 사유를 **두 축**으로 정확히 구분 노출한다(OQ-4 결정 = Option A: 신규 필드 없음). (1) 조건축 = 기존 `reason` 어휘 `{stop_loss, take_profit, forced_close_at, market_close_liquidation}`를 계약으로 고정, (2) 주체축 = 기존 `actor`(`safety_sell`=안전망 watchdog 소유 자율청산 vs `strategy`=전략 자율매도 vs `execution_loop`)를 reason과 **함께** 로그/`slot_transition_log`/조회 뷰에 노출. 현재 stop_loss/take_profit/forced_close_at는 모두 watchdog expired(전략 침묵→안전망 소유)에서만 발화하므로, actor를 같이 보여야 "안전망이 SL선에서 청산"과 "전략이 손절"을 구분할 수 있다(2026-06-15 064400 오해 원인). 신규 reason 값·신규 분류 필드는 추가하지 않는다.
|
||||
- FR-4 (P1): 주문 거부 시 조용한 inactive/holding 롤백 대신 ERROR 레벨 로그를 남긴다(알림 전송 채널은 Deferred, OQ-3). 반복 거부를 즉시 인지할 수 있어야 한다.
|
||||
- FR-5 (P1): 현재 보유·오늘 체결·오늘 실현 PnL(수수료·세금 반영)·대기 주문을 조회하는 운영 뷰를 **운영자 CLI 서브커맨드**로 제공한다(OQ-1 결정). 조회는 `ledger_outbox`/slots를 권위 출처로 한 DB-backed 시점 집계이며, RPC는 추가하지 않는다. 일일 리포트 자동화의 기반.
|
||||
- FR-6: 이 변경이 런타임 흐름/구조를 바꾸므로 `docs/runtime-flows.md`(로깅/관측성·PnL 조회 흐름)와 필요 시 `docs/project-structure.md`(신규 모듈 책임)를 갱신한다.
|
||||
- FR-7 (P0): 로그를 **Loki 적재용 구조화(JSON) 출력**으로 내보내고, correlation id(intent→order→fill)를 전파해 한 거래의 인텐트·주문·체결 로그를 단일 키로 묶는다(OQ-2 결정, 이번 범위 In Scope). FR-1·2·3·4의 로그가 이 구조화 스트림으로 흐른다.
|
||||
- FR-8 (P1, Documentation): 운영자용 런북 문서(`docs/operations.md`)를 신설한다. 배포/실행(env 변수·`--config`·`--env-file`), 운영자 CLI 사용법(`pause/resume/slots/universe/enter/exit/cancel` + 본 반복 신설 PnL/조회 서브커맨드), 로깅·관측성 사용법(타임스탬프·주문결과 로그 grep·Loki/correlation id·청산 reason+actor 해석), 디버깅·트러블슈팅(거부 ERROR 확인, 미체결 진단, DB 상태 조회)을 포함한다. 기존 `.claude/skills/run-trex-core-daemon/SKILL.md`의 사실 자료를 사람용 문서로 추출·정리하고, FR-1~7로 추가되는 관측성 기능의 운영 사용법을 함께 기술한다.
|
||||
|
||||
## 비기능 요구사항
|
||||
성능, 운영성, 일관성, 자동화 안정성 등 기능 외 요구사항을 적는다.
|
||||
|
||||
- NFR-1 (운영성): 로그 타임스탬프는 KST 기준 ISO8601로 일관되며, 주문 결과 로그는 `grep`/`tail`로 거부·체결을 즉시 필터링할 수 있는 안정적 키(예: 종목·사유·intent_id)를 포함한다.
|
||||
- NFR-2 (하위호환): 청산사유 노출 방식 개선(기존 reason 어휘 고정 + actor 동반 노출, 신규 필드 없음)과 조회 표면 추가는 기존 `slots.req`/이벤트/CLI 동작을 깨지 않는 additive 변경을 원칙으로 한다.
|
||||
- NFR-3 (정확성): PnL 집계는 BAT-038(매도 증권거래세+수수료) 정책과 일치하는 수수료·세금 반영 값을 사용하며 `ledger_outbox`/체결 기록을 단일 출처로 한다.
|
||||
|
||||
## 범위 경계
|
||||
이번 반복에서 반드시 포함할 범위와 명시적으로 제외할 범위를 구분한다.
|
||||
|
||||
- In Scope
|
||||
- FR-1·FR-2(P0 로깅 기반), FR-3·FR-4(P1 분류·거부 가시성), FR-5(P1 조회 뷰), FR-6(baseline 문서 갱신), FR-7(P0 Loki 구조화 출력 + correlation id), FR-8(P1 운영자 런북 `docs/operations.md`).
|
||||
- FR-5 조회 표면 = 운영자 CLI 서브커맨드(DB-backed). RPC는 추가하지 않음(OQ-1 결정).
|
||||
- FR-7 Loki 구조화(JSON) 출력 + correlation id(intent→order→fill) 전파를 이번 범위에 포함(OQ-2 결정).
|
||||
- Out of Scope
|
||||
- issue #28(호가단위 tick-size 스냅) — 별도 정확성 버그 트랙. 본 Discovery는 그 거부를 "관측"하는 범위만 다룬다.
|
||||
- FR-5 조회용 RabbitMQ RPC 표면 — 이번 반복 제외(CLI로 충분, OQ-1 결정).
|
||||
- 거부 시 알림 "전송 채널" 구현(claude-bus/외부 webhook 등) — Deferred. 이번 범위는 FR-4의 ERROR 로그까지만(OQ-3 결정). 후속 반복/이슈로 인계.
|
||||
|
||||
# 6. 리스크/가정 목록
|
||||
불확실성과 전제 조건을 분리해서 적는다. 리스크는 영향과 완화 방안을, 가정은 현재 판단의 전제를 기록한다.
|
||||
|
||||
## 리스크
|
||||
- R-1 (Low): 청산사유 노출 방식 변경(FR-3)이 reason 계약을 바꿔 다운스트림 소비자(trex-tsl/trex-orb, 운영 리포트)에 영향을 줄 수 있다. (OQ-4=Option A로 신규 reason 값·필드 없음 → 리스크 Low로 하향)
|
||||
- 완화 방안: 기존 reason 값 유지(어휘 고정만), actor는 이미 존재하는 필드를 함께 노출하는 additive 변경. `docs/interface-contract.md`에 reason+actor 어휘를 명문화, 표시 방식 변경은 issue 코멘트로 전략팀 통지.
|
||||
- R-2 (Medium): 로그 포맷 변경(FR-1)이 기존 로그 수집/파싱 파이프라인(있다면)을 깨거나, KST 타임스탬프 도입이 컨테이너 TZ 설정에 의존해 불일치할 수 있다.
|
||||
- 완화 방안: TZ는 코드에서 명시적으로 KST(Asia/Seoul) 변환, 환경 TZ 비의존. 포맷 변경 영향은 운영팀과 사전 합의.
|
||||
- R-3 (Low): PnL 집계(FR-5)가 `ledger_outbox`/체결 기록의 부분 상태(미정산·재시도 중)와 수수료·세금 정책 차이로 수기 계산과 어긋날 수 있다.
|
||||
- 완화 방안: BAT-038 정책과 동일 계산 지점 재사용, "오늘" 경계는 trading_date(KST) 기준, 집계 시점의 outbox 상태를 명시.
|
||||
|
||||
## 가정
|
||||
아직 확정되지 않았지만 현재 문서 작성의 기반이 되는 전제를 적는다.
|
||||
|
||||
- A-1: 조회 표면은 HTTP가 아니라 운영자 CLI 서브커맨드로 구현한다(OQ-1 결정, 데몬 RabbitMQ-only 제약).
|
||||
- A-2: P0(FR-1·2·7)와 P1(FR-3·4·5·8)은 별도 batch로 분리해 P0를 먼저 배포할 수 있다(CONFIRM 확인됨). FR-8 운영자 런북은 관측성 기능 사용법을 함께 문서화하므로 P1에 둔다(정확한 batch 분할은 suggest-batch-reqs/draft-batch에서 확정).
|
||||
- A-3: PnL·수수료·세금 계산은 BAT-038(매도세+수수료) 정책을 단일 출처로 재사용한다.
|
||||
- A-4: 배포 환경이 Loki 로그 수집을 사용한다(OQ-2 결정의 전제). 구조화(JSON) 출력은 Loki 미사용 환경에서도 stdout tail로 동작하므로 비파괴적이다.
|
||||
|
||||
## 오픈 질문
|
||||
답을 아직 모르는 항목을 적는다. 추정으로 덮지 말고 REQ 초안 작성 이전에 확인할 질문으로 남긴다.
|
||||
|
||||
- OQ-1: FR-5 조회 표면을 운영자 CLI 서브커맨드 확장으로 할지, 신규 RabbitMQ RPC로 할지, 둘 다 제공할지. (HTTP는 제외)
|
||||
- 상태: Answered (2026-06-16, Justin Ko)
|
||||
- 결정: **운영자 CLI 서브커맨드(DB-backed)** 로 제공. RPC는 추가하지 않음. → FR-5 및 §5 범위에 반영.
|
||||
- 종료 조건: 조회 표면 형태가 In Scope에 단일 값으로 고정되면 Answered. (충족)
|
||||
- OQ-2: 기능 6(Loki 구조화 JSON 출력 + correlation id intent→order→fill 전파)을 이번 범위에 포함할지, 후속으로 Deferred할지.
|
||||
- 상태: Answered (2026-06-16, Justin Ko)
|
||||
- 결정: **이번 범위 In Scope.** 구조화(JSON) 출력 + correlation id 전파를 FR-7로 신설. FR-1·2·3·4 로그가 이 스트림으로 흐름. 배포 환경이 Loki를 사용한다는 전제(A-4)로 진행.
|
||||
- 종료 조건: 범위 포함/Deferred가 확정되면 Answered. (충족)
|
||||
- OQ-3: FR-4 거부 알림에서 ERROR 로그 외 "알림 전송 채널"(claude-bus/외부 webhook 등)을 이번 범위에 포함할지.
|
||||
- 상태: Answered (2026-06-16, Justin Ko)
|
||||
- 결정: **Deferred.** 이번 범위는 FR-4의 ERROR 로그까지만. 알림 전송 채널은 후속 반복/이슈로 인계(처리 방안: 차기 Discovery 입력으로 등록). → §5 Out of Scope에 반영.
|
||||
- 종료 조건: 알림 전송 범위가 확정되면 Answered. (Deferred로 종료)
|
||||
- OQ-4: FR-3 청산사유 분류를 신규 reason 값 추가(`watchdog_liquidation` 등)로 할지, 기존 reason 필드 + 보조 분류 필드로 할지. (계약 하위호환 방식)
|
||||
- 상태: Answered (2026-06-16, Justin Ko)
|
||||
- 결정: **Option A — 기존 reason 어휘를 계약으로 고정 + 기존 actor와 함께 노출. 신규 reason 값·신규 분류 필드 없음.** 근거: 코드 확인 결과 reason(조건축)·actor(주체축)가 이미 존재하며, 오해 원인은 둘이 합쳐 노출되지 않은 것. 우산 라벨(`watchdog_liquidation`)로 뭉치면 SL/TP/forced 구체 정보가 손실되어 비채택. → FR-3에 반영.
|
||||
- 종료 조건: 어휘/매핑 방식이 확정돼 FR-3에 반영되면 Answered. (충족)
|
||||
|
||||
### 오픈 질문 처리 기준
|
||||
- `Open`: 아직 답이 없고 다음 단계 진행에 영향이 있는 상태다. 문서에 유지한다.
|
||||
- `Answered`: 답이 확정된 상태다. 관련 본문/요구사항에 반영한 뒤 오픈 질문 목록에서는 제거하거나 resolved 기록으로 이동한다.
|
||||
- `Deferred`: 지금 단계에서 결정하지 않고 다음 단계 또는 후속 이슈로 넘기는 상태다. 넘길 대상과 이유를 반드시 적는다.
|
||||
- `Dropped`: 더 이상 유효하지 않은 질문이다. 삭제 사유를 짧게 남기고 목록에서 제거한다.
|
||||
- 처리 원칙 1: 질문만 남기지 말고 반드시 `상태`, `처리 방안`, `종료 조건`을 함께 적는다.
|
||||
- 처리 원칙 2: 답이 나온 질문은 본문 반영 없이 단순 삭제하지 않는다. 반영 위치를 먼저 갱신한다.
|
||||
- 처리 원칙 3: High 영향 질문은 `Deferred` 처리 후 처리 방안에 REQ 인계 추적 방식을 명시한다.
|
||||
|
||||
# 7. 초기 성공 기준
|
||||
이 변경이 성공했다고 판단할 기준을 측정 가능하게 정의한다. 가능하면 데이터 출처까지 연결한다.
|
||||
|
||||
- S-1: 모든 로그 라인이 ISO8601(KST) 타임스탬프 + 레벨 prefix를 갖고, 주문 거부가 발생하면 ERROR 로그로 즉시 보인다(#28 유형 재현 시 로그만으로 인지 가능).
|
||||
- S-2: 모든 주문 submit/결과(체결가·수량·수수료·세금 또는 거부+사유)가 단일 tailable 로그로 출력되어 `grep`으로 당일 체결/거부를 전수 추출할 수 있다.
|
||||
- S-3: 청산 기록이 기존 reason 어휘 + actor를 함께 노출해, 안전망 자율청산(actor=safety_sell)과 전략 손절(actor=strategy)을 운영자가 구분할 수 있다(reason=stop_loss만으로 오해되지 않음).
|
||||
- S-4: 운영자가 단일 CLI 호출로 보유·오늘 체결·실현 PnL(수수료·세금 반영)·대기 주문을 조회할 수 있고, 값이 `ledger_outbox` 수기 계산과 일치한다.
|
||||
- S-5: `docs/operations.md`가 존재하고, 신규 운영자가 그 문서만으로 데몬 배포·실행, CLI로 상태/PnL 조회, 거부·미체결 디버깅을 수행할 수 있다(배포·CLI·로깅/관측성·트러블슈팅 4영역 모두 포함).
|
||||
|
||||
## 측정 방식 및 데이터 출처
|
||||
각 성공 기준을 어떻게 측정할지와 어떤 데이터나 문서를 근거로 쓸지 적는다.
|
||||
|
||||
- S-1 측정 방식: 단위 테스트(로그 포맷터)+라이브 데몬 로그 라인 검사 / 데이터 출처: 데몬 stdout 로그, `execution.py` 거부 경로.
|
||||
- S-2 측정 방식: 거부/체결 시나리오 구동 후 로그 grep 검증(단위+end-to-end) / 데이터 출처: 데몬 로그, paper 브로커 체결.
|
||||
- S-3 측정 방식: safety_sell 각 경로(stop_loss/take_profit/forced_close_at/market_close_liquidation) + 전략 매도 단위 테스트로 기록 reason·actor 확인 / 데이터 출처: `slot_transition_log`, 이벤트 payload.
|
||||
- S-4 측정 방식: 체결 후 조회 CLI 결과와 `ledger_outbox` 직접 집계 비교 / 데이터 출처: `ledger_outbox`, 체결 기록, BAT-038 수수료·세금 정책.
|
||||
- S-5 측정 방식: `docs/operations.md` 4영역(배포·CLI·로깅/관측성·트러블슈팅) 포함 여부 리뷰 체크 / 데이터 출처: `docs/operations.md`, `run-trex-core-daemon` SKILL, 신설 CLI.
|
||||
|
||||
# 8. REQ로 넘기기 전 확인 체크
|
||||
REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 확인한다. 미완료 항목은 후속 결정이 필요함을 뜻한다.
|
||||
|
||||
- [x] 범위(In Scope/Out of Scope) 확정
|
||||
- 기준: OQ-1(조회 표면=CLI)·OQ-2(Loki/correlation In Scope)·OQ-3(알림 Deferred)·OQ-4(reason+actor) 결정으로 In/Out 경계의 플레이스홀더가 모두 해소됨 (충족)
|
||||
- [x] 성공 지표의 측정 방식/데이터 출처 확정
|
||||
- 기준: S-1~S-4의 측정 방식과 데이터 출처가 테스트/로그/DB로 매핑됨 (충족)
|
||||
- [x] High 영향 리스크 우선순위 및 해소 책임자 지정
|
||||
- 기준: 현재 High 리스크 없음. Medium(R-2 로그포맷)·Low(R-1 reason 계약, R-3 PnL 집계)의 처리 방안이 명시됨 (충족)
|
||||
- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
|
||||
- 기준: OQ 목록에 `Open` 상태 항목이 0개. OQ-1·2·4 Answered, OQ-3 Deferred 처리 완료 (충족)
|
||||
- [ ] Freeze 확정 담당자(CONFIRMED_BY) 지정
|
||||
- 기준: 승인자(Justin Ko)가 confirm-discovery로 Freeze 확정 (confirm-discovery 단계에서 처리)
|
||||
|
||||
# 9. 파일 처리 결과
|
||||
이 Discovery 문서 생성/갱신 결과와 참조 문서를 기록한다. 이후 추적과 리뷰 시 근거로 사용한다.
|
||||
|
||||
- 처리 결과: 생성
|
||||
- 생성 경로: docs/discovery/dcy-029_20260616_operational-observability-logging-and-pnl-view.md
|
||||
- 참조 문서:
|
||||
- GitHub issue #29 (운영 관측성), issue #28 (호가단위 거부 — 관련 별도 트랙)
|
||||
- docs/runtime-flows.md, docs/project-structure.md (baseline — 본 Discovery에서 갱신 대상)
|
||||
- docs/interface-contract.md (조회 RPC/청산사유 어휘/거부 로그 계약 반영 대상)
|
||||
- docs/operations.md (FR-8 신설 대상 운영자 런북), .claude/skills/run-trex-core-daemon/SKILL.md (런북 사실 자료 출처)
|
||||
|
||||
# 10. 사용자 결정 필요 항목 요약
|
||||
사용자나 승인자가 결정해야 하는 항목만 모아 정리한다. 결정, 확인, 데이터 요청을 구분해서 적는다.
|
||||
|
||||
## DECIDE
|
||||
- (해소됨) FR-5 조회 표면 = CLI(DB-backed), RPC 없음 — OQ-1 Answered
|
||||
- (해소됨) Loki 구조화 출력 + correlation id 이번 범위 포함 — OQ-2 Answered (FR-7)
|
||||
- (해소됨) 알림 전송 채널 = Deferred, ERROR 로그만 — OQ-3 Answered
|
||||
- (해소됨) FR-3 청산사유 = 기존 reason 어휘 고정 + actor 함께 노출, 신규 필드 없음 — OQ-4 Answered (Option A)
|
||||
|
||||
## CONFIRM
|
||||
- (확인됨, 2026-06-16, Justin Ko) P0(FR-1·2·7) batch 선행 → P1(FR-3·4·5·8) 후속 batch 분할 방향 동의. FR-8 운영자 런북은 P1(관측성 기능 사용법을 함께 문서화). 정확한 batch 경계는 suggest-batch-reqs에서 확정.
|
||||
|
||||
## DATA
|
||||
- (해소됨) 배포 환경 Loki 사용 — A-4 전제로 진행 (OQ-2 결정에 반영)
|
||||
|
||||
# 11. Discovery Freeze
|
||||
REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다. 내용 재작성 없이 원본 섹션을 링크로 대신한다.
|
||||
|
||||
## 섹션 참조
|
||||
- 범위: [# 5. 요구사항 목록 > 범위 경계](#5-요구사항-목록)
|
||||
- 리스크/오픈 질문: [# 6. 리스크/가정 목록](#6-리스크가정-목록)
|
||||
- 성공 기준: [# 7. 초기 성공 기준](#7-초기-성공-기준)
|
||||
- 인계 전 확인: [# 8. REQ로 넘기기 전 확인 체크](#8-req로-넘기기-전-확인-체크)
|
||||
- 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약)
|
||||
|
||||
## Freeze 플래그
|
||||
- Handoff Decision: 승인
|
||||
- Handoff Rationale: OQ-1~4 결정 완료 + FR-8(운영자 런북 docs/operations.md) 추가 반영. 신규 FR은 추가 OQ 없는 P1 문서 산출물. 본문·범위·성공기준(S-5) 정합. REQ 초안 작성 진행 가능.
|
||||
- Ready for REQ Drafting: true
|
||||
- Confirmed By: Justin Ko
|
||||
- Confirmed At (KST): 2026-06-16 12:20
|
||||
@@ -0,0 +1,229 @@
|
||||
# 0. 문서 상태
|
||||
이 문서의 현재 상태와 식별 정보를 기록한다. 문서 추적과 승인 흐름에 필요한 최소 메타데이터를 적는다.
|
||||
|
||||
- 상태: confirmed
|
||||
- 문서 ID: dcy-030_20260616_slippage-limit-price-tick-size-snapping
|
||||
- 이슈명: slippage 지정가 KRX 호가단위 미반영 → KIS 거부 (tick-size 스냅, issue #28)
|
||||
- 작성 시각(KST): 2026-06-16 11:18
|
||||
- 마지막 갱신 시각(KST): 2026-06-16 12:35
|
||||
- 대체됨: 없음
|
||||
- 후속 Discovery 참조: 없음
|
||||
- 생성된 REQ 참조: REQ-083 (신규, tick 유틸), REQ-070 (change-req CHG-20260616-01, 슬리피지 스냅)
|
||||
|
||||
# 1. 계획 상태 요약
|
||||
현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다.
|
||||
|
||||
- 연계 문서 현황:
|
||||
- 관련 점수 파일: 미생성
|
||||
- 관련 TODO/메모: GitHub issue #28 (P0, 다음 거래일 전 수정 필수), REQ-070(slippage 지정가 — 호가단위 반올림을 "후속 주기"로 연기해 둔 당사자), 관련 issue #29/dcy-029(이 거부를 "관측"하는 별도 트랙)
|
||||
- 해석:
|
||||
- ORB v1 라이브 테스트(2026-06-16)에서 slippage 밴드가 적용된 매수 한계가가 KRX 호가단위를 위반해 KIS `주식주문호가단위 오류`로 전량 거부됐다. slippage_pct>0인 매수는 산출 한계가가 거의 항상 호가단위를 벗어나 0체결이 된다.
|
||||
- REQ-070은 호가단위 정밀 반올림을 의도적으로 후속 주기로 미뤘고(int ceil/floor만 구현), 그 미뤄둔 항목이 이제 P0 정확성 버그로 표면화했다. 이번 Discovery는 KRX 호가단위 스냅을 구현하되, **반올림 방향을 REQ-070의 원래 메모(체결 보장 = 매수 상위/매도 하위)와 달리 "상한 준수(매수 내림/매도 올림)"로 전환**한다(승인자 결정).
|
||||
|
||||
# 2. 요구사항 판정 결과
|
||||
이번 요청이 기존 요구사항과 비교해 신규인지, 유사한지, 중복인지 판단하고 그 근거를 남긴다.
|
||||
|
||||
- 판정: 유사
|
||||
- 근거
|
||||
- 기존 REQ-070(slippage 지정가 집행)의 동작 결함/미완 항목이다. REQ-070 Requirement/Notes가 "KRX 정밀 호가단위 반올림은 후속 주기"로 명시 연기했으므로, 새 기능이라기보다 REQ-070을 완성·정정하는 change-req 성격이다.
|
||||
- 다만 반올림 방향 정책 전환과 가격대별 호가단위 테이블 도입이라는 결정 요소가 있어, change-req 근거를 남기기 위해 Discovery로 먼저 문제·정책을 정의한다(기존 REQ 본문 수정은 change-req로 수행).
|
||||
|
||||
# 3. 문제점의 요약
|
||||
문제의 현재 상태, 기대 상태, 관련 이해관계자, 제약사항을 정리한다. Discovery의 핵심 맥락을 가장 먼저 이해할 수 있어야 한다.
|
||||
|
||||
- 현재 상태
|
||||
- `trex_core/application/execution.py:147-158`(`_dispatch`)이 slippage 밴드를 `매수=int(ceil(limit*(1+pct/100)))`, `매도=int(floor(limit*(1-pct/100)))`로 산출하고 **KRX 호가단위로 스냅하지 않는다**. 예: `ceil(147700*1.005)=148439` → 호가단위(100원) 위반 → KIS 거부.
|
||||
- REQ-070(Status: Implemented) Requirement는 "KRX 호가단위 반올림은 체결 보장 방향(매수=상위, 매도=하위)으로 적용"을 적어뒀으나 Notes에서 "정밀 호가단위 테이블은 후속 주기, 현재는 int(ceil/floor)로 충분"으로 **미구현 상태로 남겨 둔 것**이 확인된다. 즉 현재 코드는 호가단위 스냅이 전혀 없다.
|
||||
- 영향: `slippage_pct>0`인 매수는 산출 한계가가 거의 항상 호가단위를 벗어나 **전량 거부**(연결·시세 정상이어도 0체결). 매도(floor)도 limit_price+slippage를 쓰는 경로면 동일 위험. ORB v1처럼 slippage_pct를 쓰는 모든 인스턴스 해당.
|
||||
- 매도 자율청산 경로: `trex_core/application/safety_sell.py`는 SL/TP/forced/market-close 모두 limit_price 없이 **시장가**(grep상 limit 미사용)라 이 버그 영향 밖. 영향받는 매도는 **전략 SellIntent(지정가+slippage)** 경로로 한정된다.
|
||||
- baseline 문서(`docs/runtime-flows.md`, `docs/project-structure.md`)는 존재하며 최신. runtime-flows에 slippage dispatch 흐름은 기술돼 있으나 호가단위 스냅 단계는 없다.
|
||||
- 기대 상태
|
||||
- slippage 산출 한계가가 항상 **유효한 KRX 호가단위 배수**로 제출되어 `주식주문호가단위 오류` 거부가 사라진다.
|
||||
- 반올림 방향은 **상한 준수**: 매수는 호가단위로 **내림**(설정 slippage 상한을 넘지 않음), 매도는 호가단위로 **올림**(매도 하한을 넘지 않음). 즉 `slippage_pct`를 하드 리스크 상한으로 취급한다.
|
||||
- 가격대별 호가단위 산출이 순수 함수(가격→tick)로 분리되어 단위테스트로 경계값까지 검증된다.
|
||||
- 이해관계자
|
||||
- 운영 담당자(trex 운영팀): 매수 체결 복구가 1차 수요. 다음 거래일 전 수정 필요.
|
||||
- 전략팀(trex-orb-v1 등 slippage_pct 사용 인스턴스): 거부 없는 체결의 직접 수혜자.
|
||||
- 문서 작성자(AI agent) / 승인자(Justin Ko): 반올림 방향·범위 확정.
|
||||
- 제약사항
|
||||
- KRX 주식 호가단위(2023.1.25~)는 가격 구간별로 다르다(아래 표). ETF/ETN 등은 호가체계가 다를 수 있다.
|
||||
- `slippage_pct`는 리스크 상한이므로 스냅으로 상한을 넘기면 안 된다(반올림 방향 결정의 근거).
|
||||
- 변경은 dispatch-time 변환만 — 슬롯의 `limit_price`(=entry_price) 원본은 덮어쓰지 않는다(REQ-070 설계 보존).
|
||||
|
||||
KRX 주식 호가단위 (2023.1.25~):
|
||||
|
||||
| 가격 구간(원) | 호가단위(원) |
|
||||
| --- | --- |
|
||||
| ~ 2,000 | 1 |
|
||||
| 2,000 ~ 5,000 | 5 |
|
||||
| 5,000 ~ 20,000 | 10 |
|
||||
| 20,000 ~ 50,000 | 50 |
|
||||
| 50,000 ~ 200,000 | 100 |
|
||||
| 200,000 ~ 500,000 | 500 |
|
||||
| 500,000 ~ | 1,000 |
|
||||
|
||||
## 주요 사용자 시나리오
|
||||
사용자 또는 운영자가 실제로 어떤 상황에서 이 변경을 필요로 하는지 대표 시나리오를 적는다.
|
||||
|
||||
- 전략이 entry_price=147,700, slippage_pct=0.5%로 매수 인텐트를 내면, dispatch가 산출 한계가 148,439를 **148,400(호가단위 내림)** 으로 스냅해 유효 지정가로 제출하고 정상 체결된다(거부 없음).
|
||||
- 전략이 entry_price=54,500, slippage_pct=0.5%로 매수하면 54,772.5 → **54,700(내림)** 으로 제출되어 0.5% 상한 안에서 체결된다.
|
||||
- 전략이 지정가+slippage로 매도하면 산출 한계가가 **호가단위 올림**으로 스냅되어 매도 하한을 넘지 않는 유효 지정가로 제출된다.
|
||||
|
||||
# 4. 이번에 정의할 변경
|
||||
이번 Discovery에서 다루는 핵심 변경 항목을 목록으로 적는다. 기능, 구조, 규칙 변경을 함께 포함해도 된다.
|
||||
|
||||
- KRX 호가단위 스냅 도입: slippage 산출 한계가를 가격대별 호가단위 유효 배수로 정규화. 방향 = **상한 준수(매수 내림 / 매도 올림)**.
|
||||
- 호가단위 산출 유틸리티 신설: 가격 → tick 순수 함수(KRX 2023.1.25~ 구간표). 재사용·테스트 가능.
|
||||
- REQ-070 정정: 호가단위 반올림 방향을 원래 메모의 "체결 보장(매수 상위/매도 하위)"에서 "**상한 준수(매수 하위/매도 상위)**"로 전환하고, "후속 주기"로 미뤄둔 정밀 호가단위 테이블을 이번에 구현으로 확정(change-req).
|
||||
- baseline 문서 갱신: `docs/runtime-flows.md`의 slippage dispatch 흐름에 호가단위 스냅 단계 추가, 신규 tick 유틸 모듈이 생기면 `docs/project-structure.md`에 반영.
|
||||
|
||||
## 영향 범위
|
||||
변경이 미칠 코드/문서/운영 범위를 명시해 REQ 정제와 batch planning에서 추정 누락을 줄인다.
|
||||
|
||||
- 코드 영향:
|
||||
- `trex_core/application/execution.py`(`_dispatch`): slippage 산출 직후 호가단위 스냅 적용(매수 내림/매도 올림).
|
||||
- 신규: 호가단위 유틸(가격→tick, tick 배수 내림/올림). 위치는 design에서 확정(예: `trex_core/domain/` 또는 `trex_core/application/` 순수 함수 모듈).
|
||||
- 문서 영향:
|
||||
- `docs/runtime-flows.md`(slippage dispatch + 호가단위 스냅 흐름), `docs/project-structure.md`(신규 tick 유틸 모듈 책임), REQ-070 본문/AC(change-req).
|
||||
- 운영 영향:
|
||||
- slippage_pct 사용 인스턴스의 매수 거부가 해소돼 체결 복구. 반올림 방향 변경으로 체결가가 entry에 더 가깝게(매수) 형성됨(상한 미초과). 배포 절차 변경 없음.
|
||||
|
||||
# 5. 요구사항 목록
|
||||
실제로 구현과 검증으로 이어질 요구사항을 기능/비기능으로 나누어 정리하고, 범위 경계를 명확히 적는다.
|
||||
|
||||
## 기능 요구사항
|
||||
- FR-1: 가격 → KRX 호가단위(tick)를 산출하는 순수 함수를 제공한다(2023.1.25~ 구간표). 구간 경계값에서 정확한 tick을 반환한다.
|
||||
- FR-2: slippage 산출 한계가를 호가단위 유효 배수로 스냅한다. 방향 = **상한 준수**: 매수 = 해당 tick의 **내림 배수**(≤ 산출 한계가, slippage 상한 미초과), 매도 = 해당 tick의 **올림 배수**(≥ 산출 한계가, 매도 하한 미초과).
|
||||
- FR-2b: band < 1 tick 엣지에서 미체결을 막기 위해 **하한(매수)/상한(매도)을 보장**한다(OQ-2 결정). 매수 실효가 = `max(floor_to_tick(entry×(1+pct)), ceil_to_tick(entry))`, 매도 실효가 = `min(ceil_to_tick(entry×(1-pct)), floor_to_tick(entry))`. 즉 slippage가 1틱 이상이면 정상 반영, 1틱 미만이어도 최소 entry의 유효 호가는 보장한다. 이 보장값도 slippage 상한/하한을 넘지 않는다.
|
||||
- FR-3: `limit_price=None`(시장가) 또는 `slippage_pct` 없음/0인 경로는 기존 동작을 보존한다(호가단위 스냅·회귀 없음). slippage가 적용된 경우에만 스냅한다.
|
||||
- FR-4: REQ-070을 change-req로 정정한다 — 반올림 방향을 "상한 준수(매수 하위/매도 상위)"로 변경하고, 정밀 호가단위 테이블 구현을 확정한다. 변경 근거(상한 준수 > 체결 보장)를 Change Log에 남긴다.
|
||||
- FR-5: 이 변경이 런타임 흐름/구조를 바꾸므로 `docs/runtime-flows.md`(slippage dispatch 호가단위 스냅)와 필요 시 `docs/project-structure.md`(신규 tick 유틸 모듈)를 갱신한다.
|
||||
|
||||
## 비기능 요구사항
|
||||
성능, 운영성, 일관성, 자동화 안정성 등 기능 외 요구사항을 적는다.
|
||||
|
||||
- NFR-1 (정확성): 호가단위 스냅 후 제출 지정가는 항상 유효한 KRX 호가단위 배수이며, `slippage_pct`가 정의한 가격 상한(매수)/하한(매도)을 넘지 않는다.
|
||||
- NFR-2 (하위호환): slippage 미사용·시장가 경로의 기존 동작과 REQ-070 AC-3~6의 회귀 없음을 보장하는 additive 변경이다.
|
||||
- NFR-3 (테스트 가능성): 가격→tick 함수와 스냅 방향을 각 가격 구간 경계값(2,000 / 5,000 / 20,000 / 50,000 / 200,000 / 500,000)에서 단위테스트로 검증한다.
|
||||
|
||||
## 범위 경계
|
||||
이번 반복에서 반드시 포함할 범위와 명시적으로 제외할 범위를 구분한다.
|
||||
|
||||
- In Scope
|
||||
- FR-1·FR-2·FR-2b·FR-3·FR-4·FR-5. KRX **주식** 호가단위 스냅(상한 준수 방향 + band<tick 하한/상한 보장), tick 유틸, REQ-070 change-req, baseline 문서 갱신.
|
||||
- Out of Scope
|
||||
- issue #29/dcy-029(거부 관측성 — 별도 트랙).
|
||||
- ETF/ETN 등 비주식 호가체계 지원 — 이번 반복 제외, **주식 전용**(OQ-1 결정). 근거: universe 항목(`PortfolioItem`)에 종목유형 필드가 없어 분기 불가하며, ETF 지원은 portfolio-hub 계약 변경을 수반. 주식 tick은 2,000원 이상 구간에서 모두 5의 배수라 ETF에도 유효 주문이 나옴. **알려진 한계: 2,000원 미만 ETF는 주식 tick(1원) 스냅 시 무효 가능 → 미지원**. ETF/ETN은 실제 편입 + 종목유형 필드 도입과 함께 후속 Discovery로 인계.
|
||||
- slippage_pct 정책 자체(밴드 폭·기본값) 변경 — 본 Discovery는 호가단위 스냅과 반올림 방향만 다룬다.
|
||||
|
||||
# 6. 리스크/가정 목록
|
||||
불확실성과 전제 조건을 분리해서 적는다. 리스크는 영향과 완화 방안을, 가정은 현재 판단의 전제를 기록한다.
|
||||
|
||||
## 리스크
|
||||
- R-1 (Medium): 반올림 방향 전환(매수 내림)이 slippage 헤드룸을 한 틱 줄여 체결확률이 미세하게 낮아질 수 있다(특히 변동성 구간).
|
||||
- 완화 방안: 매수 내림은 entry 기준 여전히 +slippage 범위 안의 상위 가격이라 헤드룸 대부분 유지. 거부(0체결) 해소 효과가 훨씬 크다. 필요 시 운영 데이터로 체결률 모니터링.
|
||||
- R-2 (Low): slippage_pct가 한 틱보다 작은 경우(band < 1 tick) 매수 내림 스냅이 entry 자체 호가 또는 그 아래로 떨어져 미체결될 수 있다. (OQ-2=하한 보장 결정으로 완화 → Low)
|
||||
- 완화 방안: FR-2b 하한/상한 보장 — 매수는 `ceil_to_tick(entry)`를 하한으로, 매도는 `floor_to_tick(entry)`를 상한으로 보장해 최소 entry 유효 호가에서 체결 가능. slippage 상한도 미초과.
|
||||
- R-3 (Low): 가격대별 호가단위 표가 KRX 제도 변경 시 낡을 수 있다(2023.1.25 기준).
|
||||
- 완화 방안: tick 표를 단일 유틸에 상수로 모아 변경 지점을 1곳으로, 출처·기준일을 주석/문서에 명시.
|
||||
|
||||
## 가정
|
||||
아직 확정되지 않았지만 현재 문서 작성의 기반이 되는 전제를 적는다.
|
||||
|
||||
- A-1: 현재 universe는 KRX 주식 중심이고 종목유형 필드가 없어, 이번 반복은 주식 호가단위 표로 한정한다(OQ-1 결정).
|
||||
- A-2: slippage가 적용되는 매도는 전략 SellIntent(지정가) 경로뿐이고, safety-sell 자율청산은 시장가라 영향 밖이다(코드 확인).
|
||||
- A-3: 변경은 dispatch-time 변환만 — 슬롯 `limit_price` 원본은 보존한다(REQ-070 설계).
|
||||
|
||||
## 오픈 질문
|
||||
답을 아직 모르는 항목을 적는다. 추정으로 덮지 말고 REQ 초안 작성 이전에 확인할 질문으로 남긴다.
|
||||
|
||||
- OQ-1: ETF/ETN 등 비주식 호가체계 종목을 이번 범위에 포함할지(별도 tick 표 필요), 주식 전용으로 한정할지.
|
||||
- 상태: Answered (2026-06-16, Justin Ko)
|
||||
- 결정: **주식 전용.** 근거: universe `PortfolioItem`에 종목유형 필드 부재(코드 확인, `hub_read.py:114-120`) → 분기 불가. ETF 지원은 portfolio-hub 계약 변경 수반. 주식 tick은 2,000원 이상에서 5의 배수라 ETF에도 유효. 2,000원 미만 ETF는 미지원(known limitation). ETF/ETN은 후속 Discovery로 인계. → §5 Out of Scope에 반영.
|
||||
- 종료 조건: 범위가 In/Out으로 고정되면 Answered. (충족)
|
||||
- OQ-2: band < 1 tick 엣지(매수 내림 스냅이 entry 호가 이하로 떨어지는 경우)의 처리 정책.
|
||||
- 상태: Answered (2026-06-16, Justin Ko)
|
||||
- 결정: **하한/상한 보장.** 매수 = `max(floor_to_tick(entry×(1+pct)), ceil_to_tick(entry))`, 매도 = `min(ceil_to_tick(entry×(1-pct)), floor_to_tick(entry))`. slippage<1tick이어도 최소 entry 유효 호가 보장(미체결 방지), slippage 상한 미초과. → FR-2b로 반영.
|
||||
- 종료 조건: 엣지 동작이 확정돼 FR-2(b)에 반영되면 Answered. (충족)
|
||||
- OQ-3: issue #28 미해결 의문 — 2026-06-15 064400이 100,300에 정상 체결된 건이 slippage 미적용(시장가)이었는지.
|
||||
- 상태: Deferred
|
||||
- 처리 방안: delivery/verification 단계에서 해당 intent payload(slippage_pct 유무) 확인으로 검증. 본 Discovery confirm을 막지 않는 운영 데이터 검증 항목으로 인계.
|
||||
- 종료 조건: intent payload로 시장가/지정가 여부가 확인되면 Answered.
|
||||
|
||||
### 오픈 질문 처리 기준
|
||||
- `Open`: 아직 답이 없고 다음 단계 진행에 영향이 있는 상태다. 문서에 유지한다.
|
||||
- `Answered`: 답이 확정된 상태다. 관련 본문/요구사항에 반영한 뒤 오픈 질문 목록에서는 제거하거나 resolved 기록으로 이동한다.
|
||||
- `Deferred`: 지금 단계에서 결정하지 않고 다음 단계 또는 후속 이슈로 넘기는 상태다. 넘길 대상과 이유를 반드시 적는다.
|
||||
- `Dropped`: 더 이상 유효하지 않은 질문이다. 삭제 사유를 짧게 남기고 목록에서 제거한다.
|
||||
- 처리 원칙 1: 질문만 남기지 말고 반드시 `상태`, `처리 방안`, `종료 조건`을 함께 적는다.
|
||||
- 처리 원칙 2: 답이 나온 질문은 본문 반영 없이 단순 삭제하지 않는다. 반영 위치를 먼저 갱신한다.
|
||||
- 처리 원칙 3: High 영향 질문은 `Deferred` 처리 후 처리 방안에 REQ 인계 추적 방식을 명시한다.
|
||||
|
||||
# 7. 초기 성공 기준
|
||||
이 변경이 성공했다고 판단할 기준을 측정 가능하게 정의한다. 가능하면 데이터 출처까지 연결한다.
|
||||
|
||||
- S-1: issue #28의 두 케이스(147,700 / 54,500 @ 0.5% 매수)가 각각 **148,400 / 54,700**(호가단위 내림, 유효 호가, 상한 미초과)으로 산출된다.
|
||||
- S-2: slippage 적용 매수/매도 산출 한계가가 모든 가격 구간에서 항상 유효한 KRX 호가단위 배수이고, 매수 ≤ 산출값 / 매도 ≥ 산출값(상한·하한 준수)이다.
|
||||
- S-3: slippage 미사용·시장가·slippage_pct=0 경로가 기존 동작과 동일하다(REQ-070 AC-3~6 회귀 없음).
|
||||
|
||||
## 측정 방식 및 데이터 출처
|
||||
각 성공 기준을 어떻게 측정할지와 어떤 데이터나 문서를 근거로 쓸지 적는다.
|
||||
|
||||
- S-1 측정 방식: 단위 테스트(두 케이스 명시 검증) / 데이터 출처: issue #28 케이스, `execution.py` `_dispatch`.
|
||||
- S-2 측정 방식: 각 가격 구간 경계값 파라미터라이즈드 단위 테스트(tick 함수 + 스냅 방향) / 데이터 출처: KRX 호가단위 표, tick 유틸.
|
||||
- S-3 측정 방식: REQ-070 기존 AC 회귀 테스트 통과 / 데이터 출처: `tests/application/test_execution.py`, REQ-070 AC-3~6.
|
||||
|
||||
# 8. REQ로 넘기기 전 확인 체크
|
||||
REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 확인한다. 미완료 항목은 후속 결정이 필요함을 뜻한다.
|
||||
|
||||
- [x] 범위(In Scope/Out of Scope) 확정
|
||||
- 기준: OQ-1(주식 전용)·OQ-2(하한 보장) 결정으로 In/Out 경계 플레이스홀더가 해소됨 (충족)
|
||||
- [x] 성공 지표의 측정 방식/데이터 출처 확정
|
||||
- 기준: S-1~S-3의 측정 방식이 테스트/AC로 매핑됨 (충족)
|
||||
- [x] High 영향 리스크 우선순위 및 해소 책임자 지정
|
||||
- 기준: 현재 High 리스크 없음. Medium(R-1 체결확률)·Low(R-2 band<tick, R-3 tick표 노후)의 처리 방안 명시 (충족)
|
||||
- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
|
||||
- 기준: OQ-1·2 Answered, OQ-3 Deferred. `Open` 0개 (충족)
|
||||
- [ ] Freeze 확정 담당자(CONFIRMED_BY) 지정
|
||||
- 기준: 승인자(Justin Ko)가 confirm-discovery로 Freeze 확정 (confirm-discovery 단계에서 처리)
|
||||
|
||||
# 9. 파일 처리 결과
|
||||
이 Discovery 문서 생성/갱신 결과와 참조 문서를 기록한다. 이후 추적과 리뷰 시 근거로 사용한다.
|
||||
|
||||
- 처리 결과: 생성
|
||||
- 생성 경로: docs/discovery/dcy-030_20260616_slippage-limit-price-tick-size-snapping.md
|
||||
- 참조 문서:
|
||||
- GitHub issue #28 (호가단위 거부), issue #29/dcy-029 (관측성 — 관련 별도 트랙)
|
||||
- docs/srs/Integration/req-070_slippage-limit-price-execution.md (정정 대상 REQ)
|
||||
- docs/runtime-flows.md, docs/project-structure.md (baseline — 본 Discovery에서 갱신 대상)
|
||||
|
||||
# 10. 사용자 결정 필요 항목 요약
|
||||
사용자나 승인자가 결정해야 하는 항목만 모아 정리한다. 결정, 확인, 데이터 요청을 구분해서 적는다.
|
||||
|
||||
## DECIDE
|
||||
- (해소됨) 반올림 방향 = 상한 준수(매수 내림/매도 올림) — 승인자 결정, FR-2/§4에 반영
|
||||
- (해소됨) 호가단위 범위 = 주식 전용, ETF/ETN Deferred — OQ-1 Answered
|
||||
- (해소됨) band<1tick 엣지 = 하한/상한 보장(FR-2b) — OQ-2 Answered
|
||||
|
||||
## CONFIRM
|
||||
- (확인됨, 2026-06-16, Justin Ko) REQ-070 반올림 방향을 원래 메모(체결 보장=매수 상위/매도 하위)에서 상한 준수(매수 하위/매도 상위)로 전환하는 change-req 방향 동의. 반올림 방향 (A) 결정에 내포됨.
|
||||
|
||||
## DATA
|
||||
- (해소됨) universe 종목유형 필드 부재 확인(`hub_read.py`) → 주식 전제 — OQ-1 결정에 반영
|
||||
|
||||
# 11. Discovery Freeze
|
||||
REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다. 내용 재작성 없이 원본 섹션을 링크로 대신한다.
|
||||
|
||||
## 섹션 참조
|
||||
- 범위: [# 5. 요구사항 목록 > 범위 경계](#5-요구사항-목록)
|
||||
- 리스크/오픈 질문: [# 6. 리스크/가정 목록](#6-리스크가정-목록)
|
||||
- 성공 기준: [# 7. 초기 성공 기준](#7-초기-성공-기준)
|
||||
- 인계 전 확인: [# 8. REQ로 넘기기 전 확인 체크](#8-req로-넘기기-전-확인-체크)
|
||||
- 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약)
|
||||
|
||||
## Freeze 플래그
|
||||
- Handoff Decision: 승인
|
||||
- Handoff Rationale: 반올림 방향(상한 준수), OQ-1(주식 전용), OQ-2(하한 보장) 모두 결정 완료. REQ-070 change-req 방향 동의. 저장소 사실관계(execution.py 미스냅, REQ-070 연기, hub_read 유형필드 부재) 확인. REQ 초안 작성 진행 가능.
|
||||
- Ready for REQ Drafting: true
|
||||
- Confirmed By: Justin Ko
|
||||
- Confirmed At (KST): 2026-06-16 11:30
|
||||
@@ -34,3 +34,5 @@
|
||||
| dcy-026_20260612_sell-transaction-tax-and-fee-policy | 20260612 | 매도 증권거래세 누락 + 수수료 정책 정정 — `tax=0.0` 하드코딩 제거 + 단일 정책 지점 (issue #24) | [Discovery](./dcy-026_20260612_sell-transaction-tax-and-fee-policy.md) | confirmed |
|
||||
| dcy-027_20260612_long-term-strategy-slot-operation | 20260612 | 중장기 매매 전략용 슬롯 운영 도입 — 구현 위치 결정 + 멀티데이 리스크 규칙 + 운영 프로파일 (issue #20, dcy-020 Out-of-Scope 이월) | [Discovery](./dcy-027_20260612_long-term-strategy-slot-operation.md) | confirmed |
|
||||
| dcy-028_20260615_slot-strategy-state-store | 20260615 | Slot Strategy-State 저장소 — per-strategy opaque state store (read/write CAS/close 삭제, issue #26 C-1/C-2/C-3) | [Discovery](./dcy-028_20260615_slot-strategy-state-store.md) | confirmed |
|
||||
| dcy-029_20260616_operational-observability-logging-and-pnl-view | 20260616 | 운영 관측성 — 주문 결과 로깅 + 로그 타임스탬프(KST) + 청산사유 분류 + 슬롯/PnL 조회 뷰 + 운영자 런북 (issue #29) | [Discovery](./dcy-029_20260616_operational-observability-logging-and-pnl-view.md) | confirmed |
|
||||
| dcy-030_20260616_slippage-limit-price-tick-size-snapping | 20260616 | slippage 지정가 KRX 호가단위 미반영 → KIS 거부 (tick-size 스냅, 상한 준수 방향, issue #28) | [Discovery](./dcy-030_20260616_slippage-limit-price-tick-size-snapping.md) | confirmed |
|
||||
|
||||
Reference in New Issue
Block a user