feat(rel-006): dcy-022/023 + BAT-032~034 — 실행 정책·qty 검증·allocated_capital 제거 통합

BAT-032 (dcy-022): timeout_sec/slippage_pct 실행 정책, 미체결 자동 취소,
보호 지정가, `trex-core cancel` 백스톱. 마이그레이션 012.
BAT-033: `trex-core enter --qty` 필수 검증 — 미지정 시 RPC 전 즉시 오류.
BAT-034 (dcy-023): allocated_capital 죽은 필드 전 경로 제거 (domain·intent·
persistence·snapshot·CLI). 마이그레이션 013(비가역 DROP). 외부 발신자
unknown key 무시 수신 의미론 적용.
REL-006 confirmed: BAT-026~034 통합 릴리즈, pre-release smoke 증거 포함.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 07:42:03 +09:00
co-authored by Claude Sonnet 4.6
parent 5ee5f7f4e2
commit fb05a73130
56 changed files with 3151 additions and 103 deletions
@@ -0,0 +1,220 @@
# 0. 문서 상태
이 문서의 현재 상태와 식별 정보를 기록한다. 문서 추적과 승인 흐름에 필요한 최소 메타데이터를 적는다.
- 상태: confirmed
- 문서 ID: dcy-022_20260610_stuck-pending-order-recovery
- 이슈명: 전략 발신 intent 실행 정책 — 주문 timeout(TIF)/슬리피지 허용 + 미체결 고착 백스톱 (issue #21, dcy-020/021 모델 전제)
- 작성 시각(KST): 2026-06-10 17:43 KST
- 마지막 갱신 시각(KST): 2026-06-10 19:30 KST (confirm-discovery 승인 — OQ-1~4 Answered, 저장소 사실 재확인(cancel_order 미배선·payload 무 timeout/slippage), C형 0 → 상태 confirmed)
- 대체됨: 없음
- 후속 Discovery 참조: 없음
- 생성된 REQ 참조: 없음
# 1. 계획 상태 요약
현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다.
- 연계 문서 현황:
- 관련 점수 파일: 미생성
- 관련 TODO/메모: GitHub issue #21(pending 고착). dcy-020(자산 중심 영구 슬롯·slot_id 정본·상태머신 — 토대, BAT-027~030 delivery), dcy-021(포지션 사이징·부분 매도 — BAT-031), dcy-013(stale 워치독 Alert-only + 외부취소 cncl_yn 롤백 선례).
- 해석:
- 본래 issue #21 은 "미체결 고착을 운영자/코어가 사후 정리"하는 반응적 문제였다. 그러나 더 나은 근원 해법은 **전략이 intent 제출 시 실행 정책(주문 timeout, 슬리피지 허용)을 함께 실어** core 가 이를 집행하는 것이다 — 미체결 주문은 전략이 지정한 timeout 에 자동 취소되어 애초에 고착되지 않는다.
- 따라서 본 Discovery 의 **주축**은 intent 실행 정책(timeout=TIF + slippage)이고, 운영자 강제 취소·코어 fallback 은 **백스톱**으로 둔다. 가격(`entry_price`)·청산시간(`forced_close_at`)·수량(`qty`)은 이미 계약에 있으므로 **신규는 timeout 과 slippage 두 가지**다. 브로커 `cancel_order` 는 어댑터에 이미 존재(미배선)라 timeout 집행 배선만 필요하다.
# 2. 요구사항 판정 결과
이번 요청이 기존 요구사항과 비교해 신규인지, 유사한지, 중복인지 판단하고 그 근거를 남긴다.
- 판정: 신규
- 근거
- dcy-013(REQ-014)은 stale pending 을 **Alert-only**(상태 변경 없음)로 의도 고정했다. 본 주기는 전략이 명시한 실행 정책(timeout)을 core 가 집행하므로 "코어 임의 취소" 우려가 해소되며, 능동적 취소·실행 정책을 정의하는 신규 주기다.
- intent 에 실행 정책(timeout/slippage)을 추가하는 것은 매수/매도 계약(REQ-007/021/061)의 확장이며, 기존 dcy 어디에도 정의되지 않은 신규 능력이다.
# 3. 문제점의 요약
문제의 현재 상태, 기대 상태, 관련 이해관계자, 제약사항을 정리한다. Discovery의 핵심 맥락을 가장 먼저 이해할 수 있어야 한다.
- 현재 상태
- **주문에 실행 정책이 없다**: buy/sell intent payload(`interface-contract.md`)는 `entry_price`(지정가)·`qty`·`forced_close_at`(청산시간)·`stop_loss_price`/`take_profit_price` 는 있으나, **주문 유효시간(TIF/timeout)·슬리피지 허용**을 지정할 수단이 없다. 지정가 미체결 시 무기한 대기.
- **미체결 시 무기한 pending**: 집행 루프(`execution.py`)는 poll 결과가 "접수/부분(취소 아님)"이면 상태를 두고 재polling 한다 — 체결·외부취소·거부가 없으면 자산 슬롯이 pending_buy/pending_sell 에 영구 고착(issue #21).
- **stale 워치독은 Alert-only**: `_check_stale_pending`(dcy-013/REQ-014)은 임계 초과 시 WARN+카운터만 — 자동 조치 없음. 의도는 "코어가 전략 의도를 몰라 임의 취소 불가"였다.
- **브로커 취소 송신 미배선**: 어댑터에 `cancel_order(...)`가 이미 존재(`paper.py`/`kis.py`)하나 어떤 자동/운영자 흐름에도 호출되지 않는다.
- **운영자 처분 경로 부재**: `trex-core exit` 는 holding 대상이라 pending 에 적용 불가.
- **(dcy-021 후) 부분 매도 고착**: 포지션 사이징 부분 매도 주문도 pending_sell 로 나가 미체결 시 같은 고착 + 보유 슬롯 조정 잠금.
- baseline(`docs/interface-contract.md` payload, `docs/runtime-flows.md` 집행) 갱신 대상.
- 기대 상태
- 전략이 intent 에 `timeout_sec`(예 60)·`slippage_pct`(예 0.1%)를 실으면, core 가 이를 집행한다 — timeout 초과 미체결 주문은 자동 취소·롤백(고착 예방), slippage 로 보호 지정가를 산출해 과도한 불리 체결을 막는다.
- timeout 이 없는(또는 timeout 취소 자체가 실패한) 잔여 고착은 운영자 강제 취소 + (선택) 코어 fallback 으로 처리한다(백스톱).
- 가격·청산시간·수량과 함께 실행 정책이 하나의 주문 명세로 통합되어, 전략이 "어떻게 체결할지"를 선언한다.
- 이해관계자
- 문서 작성자(AI agent) — Discovery/REQ/Batch 산출물
- 리뷰어/승인자 — 실행 정책 계약·취소 집행·안전성 승인
- 전략 모듈(orb-v1 등) — intent 에 timeout/slippage 발신(외부 repo 발신 측 변경)
- 운영 담당자 — 잔여 고착 백스톱 처분·관측
- 제약사항
- 취소·롤백은 단일 writer·멱등·불변식(advisory lock, instance 스코프 dcy-020)을 보존하고, 이미 체결된 부분 수량을 잃지 않아야 한다(dcy-013 부분 취소 회계 계승).
- 취소 요청 송신과 실제 취소 확인(cncl_yn/poll)을 분리 — 취소-체결 race 처리.
- 슬리피지·timeout 미지정 시 기존 동작(무기한 지정가/시장가)과 호환(하위호환·옵션 필드).
## 주요 사용자 시나리오
사용자 또는 운영자가 실제로 어떤 상황에서 이 변경을 필요로 하는지 대표 시나리오를 적는다.
- orb-v1 이 매수 신호를 줄 때 `entry_price`·`qty`·`forced_close_at` 과 함께 `timeout_sec=60`·`slippage_pct=0.1` 을 실어 보낸다. 60초 내 미체결이면 core 가 주문을 취소하고 슬롯을 inactive 로 롤백한다(고착 없음).
- 시장가 매수에 `slippage_pct=0.1` 을 주면 core 가 현재가×(1+0.1%) 보호 지정가로 제출해 과도한 불리 체결을 막는다. 밴드 밖이면 미체결 → timeout 취소로 수렴.
- 부분 매도(dcy-021)에 timeout 을 주면 미체결 시 자동 취소되어 보유로 되돌아가고 부분 체결분은 보존된다.
- (백스톱) timeout 없는 옛 주문이 고착되면 운영자가 `trex-core cancel <slot_id>` 로 취소·롤백한다.
# 4. 이번에 정의할 변경
이번 Discovery에서 다루는 핵심 변경 항목을 목록으로 적는다. 기능, 구조, 규칙 변경을 함께 포함해도 된다.
- **intent 실행 정책 필드 추가(주축)**: buy/sell payload 에 optional `timeout_sec`(주문 유효시간, TIF)·`slippage_pct`(슬리피지 허용) 추가. 계약(REQ-007/021/061) 확장. 운영자 config 기본 timeout(per-instance TOML `default_order_timeout_sec`, opt-in) + per-intent override(OQ-4). deprecate 없음.
- **timeout 집행**: 집행 루프가 dispatched-pending 주문의 경과시간을 추적해 `timeout_sec` 초과 시 브로커 `cancel_order` 송신 → 취소 확인 후 롤백(pending_buy→inactive / pending_sell→holding, 부분 체결 보존). 기존 어댑터 `cancel_order` 배선.
- **슬리피지 집행(OQ-2/3)**: `slippage_pct` 는 `entry_price` 기준 밴드 — 실효 지정가 = 매수 `entry_price*(1+pct)`/매도 `entry_price*(1-pct)`(호가단위 반올림). `entry_price` 없으면 무시(순수 시장가). 즉 `entry_price`=기준/목표, `slippage_pct`=허용폭, 실효 limit=둘의 곱(`entry_price` 단독이면 하드 지정가=현행).
- **고착 백스톱**: timeout 미지정/취소 실패 잔여 고착에 운영자 강제 취소(`cancel`) + (선택) 코어 fallback. dcy-013 Alert-only 는 기본 유지.
- baseline 문서(`docs/interface-contract.md` payload, `docs/runtime-flows.md` 집행/취소 흐름) 반영.
## 영향 범위
변경이 미칠 코드/문서/운영 범위를 명시해 REQ 정제와 batch planning에서 추정 누락을 줄인다.
- 코드 영향:
- `trex_core/application/intents.py` / `messaging_handlers.py` — payload `timeout_sec`/`slippage_pct` 파싱·운반, 슬롯/주문에 전달
- `trex_core/application/execution.py` — dispatched-pending 경과시간 추적 + timeout 취소 송신, slippage→limit_price 산출, 취소 확인 후 롤백
- `trex_core/adapters/broker/*` — 기존 `cancel_order` 배선(신규 메서드 아님), 보호 지정가 주문
- `trex_core/domain/slot/service.py` / `models.py` — (필요 시) 주문 정책/제출시각 보유 + 취소 롤백 전이(부분 보존)
- `trex_core/runtime/cli.py` / control RPC — 운영자 `cancel` 백스톱
- `trex_core/runtime/config.py`(`DaemonInstanceConfig`) + `config/*.toml` — `default_order_timeout_sec` opt-in 기본값(REQ-048 per-instance TOML 계열, OQ-4)
- 문서 영향:
- `docs/interface-contract.md`(buy/sell payload 에 timeout/slippage), `docs/runtime-flows.md`(집행·timeout 취소·slippage·백스톱)
- 운영 영향:
- 미체결 고착 예방으로 활성 capacity 회복. 전략 모듈(외부) 발신 계약 변경 조율 필요.
# 5. 요구사항 목록
실제로 구현과 검증으로 이어질 요구사항을 기능/비기능으로 나누어 정리하고, 범위 경계를 명확히 적는다.
## 기능 요구사항
- FR-1: buy/sell intent payload 에 optional `timeout_sec`(주문 유효시간)·`slippage_pct`(슬리피지 허용)를 추가하고 core 가 파싱·운반한다(REQ-007/021/061 확장). core 는 수신 측 먼저 배포 가능(필드 누락 시 무시 — orb-v1 발신과 lockstep 불필요, 하위호환). `timeout_sec` 미지정 시 운영자 config 기본값(per-instance TOML `default_order_timeout_sec`, 미설정=OFF=무기한)을 적용하고 per-intent 값이 override 한다(OQ-4). `slippage_pct` 는 기본값 없음(미지정=보호밴드 없음).
- FR-2: 집행 루프는 dispatched-pending 주문이 `timeout_sec` 를 초과하면 브로커 `cancel_order` 를 송신하고, 취소 확인 후 슬롯을 롤백한다(pending_buy→inactive / pending_sell→holding). 이미 체결된 부분 수량은 보존한다.
- FR-3: `slippage_pct` 는 **기준가(`entry_price`)가 있을 때만** 적용한다(OQ-2) — 실효 지정가 = 매수 `entry_price*(1+pct)` / 매도 `entry_price*(1-pct)`(KRX 호가단위로 반올림: 체결 보장 측 — 매수 올림/매도 내림). `entry_price` 가 없으면(순수 시장가) `slippage_pct` 는 **무시**(무제한 시장가). 밴드 밖 미체결은 timeout 취소(FR-2)로 수렴한다.
- FR-4: 취소 요청 송신과 실제 취소 확인(cncl_yn/poll)을 분리한다 — 취소가 거부(이미 체결)되면 정상 체결 경로로 수렴하고 슬롯을 임의 변경하지 않는다(취소-체결 race).
- FR-5: (백스톱) 운영자가 자산 슬롯의 고착 pending 을 강제 취소·롤백할 수 있다(timeout 미지정/취소 실패 대비). dcy-013 Alert-only 기본 동작은 회귀 없이 유지한다.
- FR-6: `docs/interface-contract.md`·`docs/runtime-flows.md` 에 실행 정책·timeout 취소·slippage·백스톱을 반영한다.
## 비기능 요구사항
성능, 운영성, 일관성, 자동화 안정성 등 기능 외 요구사항을 적는다.
- NFR-1: 안전·멱등 — 취소·롤백은 단일 writer·advisory lock(instance 스코프)·멱등을 보존하고, 중복 취소/경합에서 부분 체결분을 잃지 않는다.
- NFR-2: 비차단 — timeout 취소 송신 실패·지연이 집행 루프·다른 슬롯 처리를 막지 않는다(best-effort, 요청/확인 분리).
- NFR-3: 하위호환 — `timeout_sec`/`slippage_pct` 미지정 시 기존 매수/매도 경로와 byte 동등(회귀 없음).
- NFR-4: 관측성 — timeout 취소·slippage 적용·백스톱 처분이 로그+카운터로 가시화된다(stale 카운터 계승).
## 범위 경계
이번 반복에서 반드시 포함할 범위와 명시적으로 제외할 범위를 구분한다.
- In Scope
- intent `timeout_sec`/`slippage_pct` 계약 + core 집행(timeout 취소·보호 지정가)
- 브로커 `cancel_order` 배선 + 취소-체결 race 처리(부분 보존)
- 운영자 강제 취소 백스톱 + runtime-flows/interface-contract 정합
- Out of Scope
- 브로커 `cancel_order` 신규 구현(이미 존재 — 배선만)
- 전략 모듈(orb-v1) 내부 신호/사이징 로직(외부 repo, 발신 계약만 합의)
- IOC/FOK 등 네이티브 TIF 분류 확장 — **후속 주기**(본 주기는 코어-타이밍 `timeout_sec` 만, OQ-1 Answered)
# 6. 리스크/가정 목록
불확실성과 전제 조건을 분리해서 적는다. 리스크는 영향과 완화 방안을, 가정은 현재 판단의 전제를 기록한다.
## 리스크
- R-1 (High): 취소 요청과 실제 체결의 경쟁(race) — timeout 취소 송신 직전/직후 체결되면 슬롯과 브로커 실제가 어긋날 수 있다.
- 완화 방안: 취소 송신 후 즉시 롤백하지 말고 cncl_yn/poll 확인 후 전이(FR-4). 이미 체결 시 정상 경로로 수렴.
- R-2 (Medium): 슬리피지 보호 지정가 산출 오류로 의도와 다른 체결/미체결.
- 완화 방안: 방향별 부호·산출식을 명확히 정의(OQ-2), 단위 테스트. 밴드 밖은 timeout 취소로 안전 수렴.
- R-3 (Medium): 부분 체결분 처리 오류로 회계/포지션 불일치.
- 완화 방안: dcy-013 부분 취소 회계 선례 재사용 + 브로커 정본 대조(REQ-062) + 테스트.
## 가정
아직 확정되지 않았지만 현재 문서 작성의 기반이 되는 전제를 적는다.
- A-1: dcy-020(자산 중심 영구 슬롯·slot_id 정본)·dcy-021(부분 매도)이 구현 완료된 상태다(BAT-027~031).
- A-2: 브로커 어댑터 `cancel_order` 가 동작하며 cncl_yn 으로 취소 결과를 관측 가능하다(dcy-013).
- A-3: 전략 모듈이 intent 에 실행 정책 필드를 발신하도록 합의·변경 가능하다(외부 repo).
## 오픈 질문
답을 아직 모르는 항목을 적는다. 추정으로 덮지 말고 REQ 초안 작성 이전에 확인할 질문으로 남긴다.
- OQ-1: timeout 시맨틱 범위 — 단순 `timeout_sec`(미체결 시 취소)만 우선 도입하고 IOC/FOK 등 TIF 분류는 후속으로 둘지, 본 주기에 함께 정의할지.
- 상태: Answered (2026-06-10, 사용자 결정)
- 결정: **timeout 만 도입** — `timeout_sec`(N초 미체결 시 코어가 `cancel_order` 송신·롤백, GTD/timeout 성격, 브로커 무관). **IOC/FOK 등 네이티브 TIF 는 후속 주기**(브로커 네이티브 플래그·부분 체결 규칙 별도). 반영: §4, FR-1/2, §5 범위 경계.
- OQ-2: 슬리피지 산출 규칙.
- 상태: Answered (2026-06-10, 사용자 결정)
- 결정: `slippage_pct` 는 **기준가 `entry_price` 가 있을 때만** 적용 — 실효 지정가 = 매수 `entry_price*(1+pct)` / 매도 `entry_price*(1-pct)`(KRX 호가단위 반올림, 체결 보장 측). `entry_price` 없으면(순수 시장가) **무시**(무제한). 시장가엔 곱할 기준이 없다는 점에서 "기준가 없으면 슬리피지 없음". 반영: FR-3, §4.
- OQ-3: 필드 상호작용/우선순위.
- 상태: Answered (2026-06-10, OQ-2 동반 결정)
- 결정: `entry_price`=기준/목표, `slippage_pct`=허용폭 → 실효 limit = `entry_price*(1±pct)`. `entry_price` 단독(slippage 없음)=하드 지정가(현행). `timeout_sec`=주문 유효시간(미체결 취소), `forced_close_at`=포지션 만기 청산(별개 축, REQ-066/037). 네 필드 역할 분리 명확. 반영: FR-3, §4, payload 계약(REQ 단계 interface-contract).
- OQ-4: 계약/발신 위치 — `timeout_sec`/`slippage_pct` 를 buy/sell payload optional 로 추가(core 수신)하고, 전략 모듈(orb-v1) 발신 변경을 어떻게 조율할지(기본값·하위호환·deprecate 정책).
- 상태: Answered (2026-06-10, 사용자 결정)
- 결정: (1) buy/sell payload 에 **optional** 필드 추가 — core 수신 측 먼저 배포(누락 시 무시), orb-v1 발신은 별도 일정(lockstep 불필요·하위호환). (2) `timeout_sec` 기본값 = **운영자 config opt-in**(per-instance TOML `default_order_timeout_sec`, 미설정=OFF=현행), per-intent override. `slippage_pct` 기본값 없음. (3) **deprecate 없음**(전부 신규 optional 필드, `entry_price` 등 기존 필드 유지). 반영: FR-1, §4, 영향 범위(config).
# 7. 초기 성공 기준
이 변경이 성공했다고 판단할 기준을 측정 가능하게 정의한다. 가능하면 데이터 출처까지 연결한다.
- S-1: `timeout_sec=N` 을 실은 intent 가 N초 내 미체결이면 core 가 주문을 취소하고 슬롯을 롤백하며 부분 체결분을 보존한다(고착 없음).
- S-2: `slippage_pct` 가 주어진 주문이 방향별 보호 지정가로 제출되고, 밴드 밖이면 미체결 → timeout 취소로 수렴한다. 미지정 주문은 기존 동작과 동일(회귀 없음).
- S-3: 취소-체결 race 에서 이미 체결된 경우 정상 체결 경로로 수렴(임의 롤백 없음). 백스톱 운영자 `cancel` 로 timeout 없는 고착도 해소된다.
## 측정 방식 및 데이터 출처
각 성공 기준을 어떻게 측정할지와 어떤 데이터나 문서를 근거로 쓸지 적는다.
- S-1 측정 방식: timeout 경과 픽스처 → 취소·롤백·부분 보존 단위/통합 / 데이터 출처: 슬롯 전이, 브로커 cancel_order/poll
- S-2 측정 방식: 시장가/지정가 + slippage 산출 단언 + 미지정 회귀 / 데이터 출처: OrderRequest limit_price, 슬롯
- S-3 측정 방식: 취소-체결 동시 시나리오 수렴 + 운영자 cancel 백스톱 / 데이터 출처: poll_fill/cncl_yn, control RPC
# 8. REQ로 넘기기 전 확인 체크
REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 확인한다. 미완료 항목은 후속 결정이 필요함을 뜻한다.
- [x] 범위(In Scope/Out of Scope) 확정
- 기준: TIF 범위(OQ-1 timeout만)·슬리피지 규칙(OQ-2 entry_price 밴드) 합의 → In/Out 경계 명확
- [x] 성공 지표의 측정 방식/데이터 출처 확정
- 기준: S-1~3 측정 시나리오·데이터 출처가 테스트 가능한 형태로 정의됨
- [x] High 영향 리스크 우선순위 및 해소 책임자 지정
- 기준: R-1(취소-체결 race)은 FR-4(취소 송신·확인 분리)로 해소 방향 확정. 해소 책임은 구현 batch.
- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
- 기준: OQ-1~4 전부 Answered (2026-06-10 사용자 결정)
- [x] Freeze 확정 담당자(CONFIRMED_BY) 지정
- 기준: 승인자 Justin Ko confirm-discovery 수행
# 9. 파일 처리 결과
이 Discovery 문서 생성/갱신 결과와 참조 문서를 기록한다. 이후 추적과 리뷰 시 근거로 사용한다.
- 처리 결과: 갱신 (주축 재작성)
- 생성 경로: docs/discovery/dcy-022_20260610_stuck-pending-order-recovery.md
- 참조 문서:
- docs/discovery/dcy-020_20260610_asset-centric-slot-model.md (자산 중심 영구 슬롯·slot_id 정본·상태머신 — 토대)
- docs/discovery/dcy-021_20260610_position-sizing-target-intent.md (부분 매도 — 실행 정책 적용 대상)
- docs/discovery/dcy-013_20260608_kis-external-cancel-pending-sell-stuck.md (stale Alert-only + cncl_yn 롤백·부분 취소 회계 선례)
- docs/interface-contract.md(buy/sell payload), docs/runtime-flows.md (baseline — 갱신 대상)
- GitHub issue #21(pending 고착)
# 10. 사용자 결정 필요 항목 요약
사용자나 승인자가 결정해야 하는 항목만 모아 정리한다. 결정, 확인, 데이터 요청을 구분해서 적는다.
## DECIDE
- 없음 (OQ-1~4 전부 Answered — §6 참조)
## CONFIRM
- 없음 (전제 dcy-020/021 구현은 BAT-027~031 로 충족 — A-1)
## DATA
- 없음 (외부 데이터 의존 없음 — 내부 도메인/브로커 계약 + 전략 발신 합의)
# 11. Discovery Freeze
REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다. 내용 재작성 없이 원본 섹션을 링크로 대신한다.
## 섹션 참조
- 범위: [# 5. 요구사항 목록 > 범위 경계](#5-요구사항-목록)
- 리스크/오픈 질문: [# 6. 리스크/가정 목록](#6-리스크가정-목록)
- 성공 기준: [# 7. 초기 성공 기준](#7-초기-성공-기준)
- 인계 전 확인: [# 8. REQ로 넘기기 전 확인 체크](#8-req로-넘기기-전-확인-체크)
- 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약)
## Freeze 플래그
- Handoff Decision: 승인
- Handoff Rationale: OQ-1~4 전부 Answered, C형 플레이스홀더 0, 저장소 사실 재확인 일치(cancel_order 어댑터 존재·미배선, payload 무 timeout/slippage, stale Alert-only). REQ Drafting 인계 가능.
- Ready for REQ Drafting: true
- Confirmed By: Justin Ko
- Confirmed At (KST): 2026-06-10 19:30 KST
@@ -0,0 +1,208 @@
# 0. 문서 상태
이 문서의 현재 상태와 식별 정보를 기록한다. 문서 추적과 승인 흐름에 필요한 최소 메타데이터를 적는다.
- 상태: confirmed
- 문서 ID: dcy-023_20260610_allocated-capital-dead-field-removal
- 이슈명: allocated_capital 죽은 필드 제거 — `enter --capital` 옵션 + slot/intent/snapshot 계약 정리 (issue #22)
- 작성 시각(KST): 2026-06-10 21:27
- 마지막 갱신 시각(KST): 2026-06-10 21:42
- 대체됨: 없음
- 후속 Discovery 참조: 없음
- 생성된 REQ 참조: 신규 REQ 없음 — change-req 5건: REQ-002(CHG-20260610-02)·REQ-006(CHG-20260610-02)·REQ-007(CHG-20260610-02)·REQ-028(CHG-20260610-01)·REQ-032(CHG-20260610-07)
# 1. 계획 상태 요약
현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다.
- 연계 문서 현황:
- 관련 점수 파일: 미생성
- 관련 TODO/메모: GitHub issue #22 (`trex-core enter --capital 옵션 및 slot allocated_capital 필드 제거`)
- 해석:
- issue #22 가 지적한 대로 `allocated_capital` 은 주문 사이징·회계·전략 결정 어디에도 쓰이지 않는 죽은 메타데이터다 — 설정(CLI)·운반(intent)·영속(daily_slot)·표시(snapshot) 경로만 존재한다.
- dcy-021(포지션 사이징)에서 "전략이 cash+quantity 로 qty 를 계산해 명령형 intent 로 제출, core 는 사이징하지 않음" 계약이 확정되어 capital 기반 자동 사이징의 미래 용도도 사라졌으므로, 필드 제거의 전제(OQ-3)가 사실상 해소된 상태에서 제거 범위·마이그레이션 정책만 결정하면 된다.
# 2. 요구사항 판정 결과
이번 요청이 기존 요구사항과 비교해 신규인지, 유사한지, 중복인지 판단하고 그 근거를 남긴다.
- 판정: 신규
- 근거
- 기존 Discovery 22건 중 `allocated_capital` 제거를 다루는 문서 없음. 가장 가까운 dcy-021(포지션 사이징)은 qty 사이징 계약 확립이 주제이고, 본 건은 그 결정의 귀결인 죽은 필드 정리로 문제 정의가 다르다.
- 슬롯 데이터 계약·영속(컬럼 drop)·intent payload·CLI·snapshot 노출에 걸치는 다중 REQ change-req 묶음이라 독립 주기로 추적하는 편이 명확하다.
# 3. 문제점의 요약
문제의 현재 상태, 기대 상태, 관련 이해관계자, 제약사항을 정리한다. Discovery의 핵심 맥락을 가장 먼저 이해할 수 있어야 한다.
- 현재 상태
- `allocated_capital` 소비처는 전수 추적 결과 설정·운반·영속·표시뿐이다: `trex_core/runtime/cli.py`(`--capital`→payload, slot detail 표시), `trex_core/application/intents.py`(BuyIntent 필드 + admission 전달), `trex_core/application/messaging_handlers.py`(payload 파싱), `trex_core/domain/slot/models.py`(Slot 필드 + `EXPECTED_SLOT_FIELDS`), `trex_core/domain/slot/service.py`(buy/전이 시 설정·클리어), `trex_core/adapters/persistence/postgresql.py`(daily_slot 컬럼 SELECT/UPDATE/복원), `trex_core/application/portfolio.py`(snapshot `slots[]` 노출). 의사결정/회계/사이징 소비자는 없다.
- 집행 수량은 `qty = int(slot.qty or 0)` 로 `slot.qty` 에서만 온다. issue #22 가 지적한 footgun(`--capital` 만 주고 `--qty` 누락 → 0주 주문)의 CLI 측면은 BAT-033(REQ-032 AC-7, `--qty` 필수 검증)으로 이미 차단됐으나, 필드 자체는 슬롯 계약·영속·스냅샷에 잔존해 유지 비용과 "capital 이 사이징한다"는 오해 여지가 남아 있다.
- 마이그레이션은 012 까지 존재하며, BAT-030 의 011(`drop_pool_model_columns`)이 비가역 컬럼 drop + 사전 백업 운영 절차의 선례를 남겼다.
- baseline 문서 `docs/project-structure.md`, `docs/runtime-flows.md` 존재 — 확인 결과 두 문서 모두 `allocated_capital` 을 직접 기술하지 않으므로 FR-5(baseline 정합)는 제거 후 재확인 수준의 경량 항목이다.
- 기대 상태
- `trex-core enter` 에서 `--capital` 옵션이 제거되고, buy intent payload·Slot 모델·daily_slot 컬럼·snapshot `slots[]` 에서 `allocated_capital` 이 사라진다.
- 슬롯 데이터 계약이 실제 소비되는 필드만 남아 계약·영속·스냅샷·테스트의 유지 비용이 줄고, capital 기반 사이징 오해가 원천 제거된다.
- 이해관계자
- 문서 작성자(AI agent) — Discovery/REQ/batch 산출물 작성
- 운영자(Justin Ko) — CLI 인터페이스 변경(`--capital` 제거)과 마이그레이션(컬럼 drop) 승인
- 전략 모듈 발신자 — buy intent payload 계약 변경의 영향 대상(현재 `allocated_capital` 송신 여부 확인 필요)
- 제약사항
- daily_slot 컬럼 drop 은 비가역 — BAT-030 선례처럼 사전 백업 + 롤백 절차 명시 필수.
- 운영 라이브 DB 에 대한 파괴적 작업 금지 원칙 — 마이그레이션 검증은 테스트 DSN(trex_core_test)에서 수행.
## 주요 사용자 시나리오
사용자 또는 운영자가 실제로 어떤 상황에서 이 변경을 필요로 하는지 대표 시나리오를 적는다.
- 운영자가 `trex-core enter <pf> --qty 10 --yes` 로 진입한다 — `--capital` 은 더 이상 받지 않으며, 잘못 입력하면 argparse 가 즉시 거부해 "capital 로 사이징된다"는 오해가 생길 여지가 없다.
- 전략 모듈이 buy intent 를 제출할 때 payload 에 `allocated_capital` 을 넣지 않는다(또는 deprecated 기간 동안 넣어도 무시된다) — 계약 문서와 실제 동작이 일치한다.
- 개발자가 Slot 모델/persistence/snapshot 코드를 읽을 때 죽은 필드가 없어 실제 소비되는 계약만 추적하면 된다.
# 4. 이번에 정의할 변경
이번 Discovery에서 다루는 핵심 변경 항목을 목록으로 적는다. 기능, 구조, 규칙 변경을 함께 포함해도 된다.
- `trex-core enter` 의 `--capital` 옵션 제거 + buy intent payload 의 `allocated_capital` 필드 폐기(즉시 제거 vs deprecated 수신-무시 중 OQ-1 결정).
- Slot 도메인 모델·`EXPECTED_SLOT_FIELDS`·persistence flat 매핑·daily_slot 컬럼(마이그레이션 013, drop)·snapshot `slots[]` 노출에서 `allocated_capital` 제거.
- 데이터 계약 변경에 따른 baseline 문서(`docs/project-structure.md`, `docs/runtime-flows.md`) 슬롯 필드 기술 정합 갱신(해당 기술이 있는 경우).
## 영향 범위
변경이 미칠 코드/문서/운영 범위를 명시해 REQ 정제와 batch planning에서 추정 누락을 줄인다.
- 코드 영향:
- `trex_core/runtime/cli.py`(`--capital` 인자·summary·payload·slot detail 표시), `trex_core/application/intents.py`(BuyIntent 필드·admission), `trex_core/application/messaging_handlers.py`(payload 파싱), `trex_core/domain/slot/models.py`(+`EXPECTED_SLOT_FIELDS`), `trex_core/domain/slot/service.py`(설정/클리어 경로), `trex_core/adapters/persistence/postgresql.py`(컬럼 매핑), `trex_core/application/portfolio.py`(snapshot 노출)
- 신규 마이그레이션 `013_drop_allocated_capital.sql`(ALTER TABLE daily_slot DROP COLUMN IF EXISTS, 멱등) + 관련 단위 테스트 정리
- 문서 영향:
- REQ-002(슬롯 도메인)·REQ-006(persistence/마이그레이션)·REQ-007(buy intent payload)·REQ-028(snapshot slots[])·REQ-032(운영자 CLI) change-req
- baseline `docs/project-structure.md`/`docs/runtime-flows.md` 슬롯 필드 기술 정합(존재 시)
- 운영 영향:
- 마이그레이션 013 적용(비가역 drop — 사전 백업 절차), `enter --capital` 사용 스크립트/습관 변경 공지. 데몬 재기동 1회.
# 5. 요구사항 목록
실제로 구현과 검증으로 이어질 요구사항을 기능/비기능으로 나누어 정리하고, 범위 경계를 명확히 적는다.
## 기능 요구사항
- FR-1: `trex-core enter` 는 `--capital` 옵션을 받지 않는다. 지정 시 argparse 수준에서 unknown argument 로 거부된다.
- FR-2: buy intent payload 계약에서 `allocated_capital` 이 즉시 제거된다 — messaging handler 의 파싱 제거로 외부 발신자가 키를 계속 보내도 unknown key 로 무시된다(OQ-1 결정: 즉시 제거).
- FR-3: Slot 모델·`EXPECTED_SLOT_FIELDS`·persistence 매핑·daily_slot 컬럼에서 `allocated_capital` 이 제거된다(마이그레이션 013, `DROP COLUMN IF EXISTS` 멱등).
- FR-4: snapshot `slots[]` 응답과 CLI `slots` detail 표시에서 `allocated_capital` 필드가 사라진다.
- FR-5: baseline 문서(`docs/project-structure.md`, `docs/runtime-flows.md`)에 슬롯 필드/intent 계약 기술이 있으면 제거 결과와 정합하도록 갱신한다.
## 비기능 요구사항
성능, 운영성, 일관성, 자동화 안정성 등 기능 외 요구사항을 적는다.
- NFR-1: 마이그레이션 013 은 멱등(`IF EXISTS`)이며, 구 코드와의 공존이 불가능한 비가역 drop 이므로 적용 전 백업·롤백 절차를 release 문서에 명시한다(BAT-030 011 선례 준용).
- NFR-2: 전체 단위 테스트 suite 회귀 없음(제거 대상 필드를 참조하던 테스트는 함께 정리).
- NFR-3: 변경 후에도 buy intent admission·집행 경로의 런타임 동작(qty 기반 사이징)은 불변이다 — 죽은 필드 제거가 거래 동작을 바꾸지 않는다.
## 범위 경계
이번 반복에서 반드시 포함할 범위와 명시적으로 제외할 범위를 구분한다.
- In Scope
- CLI `--capital` 제거, buy intent `allocated_capital` 폐기, Slot/persistence/snapshot 필드 제거, 마이그레이션 013, 관련 REQ change-req 5건, baseline 정합
- Out of Scope
- capital 기반 자동 사이징(capital→qty) 도입 — dcy-021 에서 "전략이 사이징, core 미관여" 로 확정되어 채택하지 않음
- `entry_price`/`limit_price` 등 다른 슬롯 필드 정리 — 본 건과 무관
- trex-orb-v1 등 외부 전략 모듈 저장소의 발신 코드 수정 — 사용자 확인 결과 무시(범위 제외). RabbitMQ payload 는 관용적 계약이라 송신 잔존이 무해함.
# 6. 리스크/가정 목록
불확실성과 전제 조건을 분리해서 적는다. 리스크는 영향과 완화 방안을, 가정은 현재 판단의 전제를 기록한다.
## 리스크
- R-1 (Low): 외부 발신자(trex-orb-v1, `intent_builder.py:56`)가 `allocated_capital` 을 계속 송신함 — 파싱 제거 후에는 unknown key 로 무시되어 동작 영향 없음(사용자 확인: 외부 발신자 무시).
- 완화 방안: messaging handler 파싱 제거만으로 충분. 외부 저장소 수정은 범위 제외.
- R-2 (Medium): daily_slot 컬럼 drop 은 비가역 — 롤백 시 구 코드는 해당 컬럼을 UPDATE 절에 포함하므로 컬럼 부재 시 구 리비전과 공존 불가.
- 완화 방안: BAT-030 011 선례 준용 — 적용 전 daily_slot 백업, release 문서에 롤백 절차(컬럼 재추가 + 백업 복원) 명시, 테스트 DSN 에서 round-trip 검증 후 적용.
- R-3 (Low): 제거 대상 필드를 참조하는 테스트·스냅샷 fixture 누락으로 suite 가 깨질 수 있음.
- 완화 방안: `allocated_capital` 전수 grep(소스+테스트) 후 일괄 정리, 전체 suite green 을 DoD 로 설정.
## 가정
아직 확정되지 않았지만 현재 문서 작성의 기반이 되는 전제를 적는다.
- A-1: capital 기반 자동 사이징은 향후에도 core 책임이 아니다 — dcy-021 확정 계약(전략이 cash+quantity 로 qty 계산, 명령형 intent)이 유지된다.
- A-2: 운영 daily_slot 의 기존 `allocated_capital` 값은 보존 가치가 없다(의사결정·회계 소비자가 없었으므로 이력 데이터로도 무의미). 필요 시 drop 전 백업으로 충분하다.
- A-3: BAT-033 의 `--qty` 필수 검증이 release 되면 `--capital` 단독 입력 footgun 은 이미 차단된 상태에서 본 건은 순수 계약 정리다.
## 오픈 질문
답을 아직 모르는 항목을 적는다. 추정으로 덮지 말고 REQ 초안 작성 이전에 확인할 질문으로 남긴다.
- OQ-1: buy intent payload 의 `allocated_capital` 을 즉시 제거(수신 시 무시)할지, deprecated 명시 후 유예 기간을 둘지.
- 상태: Answered — **즉시 제거** (2026-06-10, 운영자 결정)
- 근거: RabbitMQ payload 는 관용적 계약이라 파싱 제거 = 수신-무시이며 유예가 주는 추가 안전이 없음. trex-orb-v1 이 실제 송신 중이나(intent_builder.py:56) 사용자 확인으로 무시(범위 제외). FR-2 에 반영 완료.
- 종료 조건: 충족 (FR-2 본문 반영).
- OQ-2: 마이그레이션 013 의 적용 시점 — 코드 배포와 동시 적용 vs 코드 먼저(컬럼 미사용화) 배포 후 별도 시점 drop.
- 상태: Answered — **동시 적용** (2026-06-10, 운영자 결정)
- 근거: 단일 인스턴스 운영 + BAT-030 마이그레이션 011(pool-model 컬럼 drop) 동시 적용 선례. 롤백은 "백업 복원 + 구 리비전"으로 동일하게 커버. `apply_migrations` 가 데몬 기동 시 자동 적용하므로 별도 절차 불필요.
- 종료 조건: 충족 (REQ-006 change-req 에 동시 적용 + 사전 백업 절차로 반영 예정).
### 오픈 질문 처리 기준
- `Open`: 아직 답이 없고 다음 단계 진행에 영향이 있는 상태다. 문서에 유지한다.
- `Answered`: 답이 확정된 상태다. 관련 본문/요구사항에 반영한 뒤 오픈 질문 목록에서는 제거하거나 resolved 기록으로 이동한다.
- `Deferred`: 지금 단계에서 결정하지 않고 다음 단계 또는 후속 이슈로 넘기는 상태다. 넘길 대상과 이유를 반드시 적는다.
- `Dropped`: 더 이상 유효하지 않은 질문이다. 삭제 사유를 짧게 남기고 목록에서 제거한다.
- 처리 원칙 1: 질문만 남기지 말고 반드시 `상태`, `처리 방안`, `종료 조건`을 함께 적는다.
- 처리 원칙 2: 답이 나온 질문은 본문 반영 없이 단순 삭제하지 않는다. 반영 위치를 먼저 갱신한다.
- 처리 원칙 3: High 영향 질문은 `Deferred` 처리 후 처리 방안에 REQ 인계 추적 방식을 명시한다.
# 7. 초기 성공 기준
이 변경이 성공했다고 판단할 기준을 측정 가능하게 정의한다. 가능하면 데이터 출처까지 연결한다.
- S-1: 소스 트리(`trex_core/`)에서 `allocated_capital` 참조가 0건이 된다(deprecated 유예를 택한 경우 messaging handler 의 무시 주석 1건만 예외).
- S-2: 마이그레이션 013 적용 후 daily_slot 에 `allocated_capital` 컬럼이 없고, `EXPECTED_SLOT_FIELDS` ↔ information_schema 계약 가드가 green 이다.
- S-3: 전체 단위 테스트 suite green(회귀 0) + `trex-core enter --capital ...` 이 argparse 오류로 거부된다.
## 측정 방식 및 데이터 출처
각 성공 기준을 어떻게 측정할지와 어떤 데이터나 문서를 근거로 쓸지 적는다.
- S-1 측정 방식: `grep -rn allocated_capital trex_core/ tests/` 결과 0건(또는 예외 1건) / 데이터 출처: 저장소 소스 트리
- S-2 측정 방식: 테스트 DSN 에서 013 적용 후 information_schema 조회 + 컬럼 계약 가드 테스트 / 데이터 출처: trex_core_test PostgreSQL
- S-3 측정 방식: `pytest -q` 전체 green + CLI 수동 스모크(`enter --capital` 거부 확인) / 데이터 출처: 테스트 실행 로그
# 8. REQ로 넘기기 전 확인 체크
REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 확인한다. 미완료 항목은 후속 결정이 필요함을 뜻한다.
- [x] 범위(In Scope/Out of Scope) 확정
- 기준: 외부 전략 모듈 저장소 수정 포함 여부 확인 완료 — 운영자 확인으로 범위 제외 확정 (2026-06-10)
- [x] 성공 지표의 측정 방식/데이터 출처 확정
- 기준: S-1~S-3 측정 방식에 대한 사용자 이견 없음 — grep 전수·계약 가드 테스트·suite green 모두 저장소 근거로 측정 가능
- [x] High 영향 리스크 우선순위 및 해소 책임자 지정
- 기준: R-2(비가역 drop) 완화 절차(백업·롤백, BAT-030 011 선례 준용)를 운영자가 OQ-2 동시 적용 결정과 함께 승인 (2026-06-10)
- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
- 기준: OQ-1(즉시 제거)·OQ-2(동시 적용) 모두 Answered (2026-06-10)
- [x] Freeze 확정 담당자(CONFIRMED_BY) 지정
- 기준: Justin Ko 지정 (2026-06-10, 사용자 명시)
# 9. 파일 처리 결과
이 Discovery 문서 생성/갱신 결과와 참조 문서를 기록한다. 이후 추적과 리뷰 시 근거로 사용한다.
- 처리 결과: 생성
- 생성 경로: docs/discovery/dcy-023_20260610_allocated-capital-dead-field-removal.md
- 참조 문서:
- GitHub issue #22 (trex-core enter --capital 옵션 및 slot allocated_capital 필드 제거)
- docs/discovery/dcy-021_20260610_position-sizing-target-intent.md (사이징 계약 확정 — capital 사이징 불채택 근거)
- trex_core/adapters/persistence/sql/011_drop_pool_model_columns.sql (비가역 컬럼 drop 선례)
# 10. 사용자 결정 필요 항목 요약
사용자나 승인자가 결정해야 하는 항목만 모아 정리한다. 결정, 확인, 데이터 요청을 구분해서 적는다.
## DECIDE
- ~~OQ-1 — buy intent `allocated_capital` 즉시 제거 vs deprecated 유예~~ → **즉시 제거 확정** (2026-06-10, 운영자)
- ~~OQ-2 — 마이그레이션 013 동시 적용 vs 2단계 분리~~ → **동시 적용 확정** (2026-06-10, 운영자)
## CONFIRM
- ~~외부 전략 모듈(trex-orb-v1 등)의 송신 코드 정리 범위 포함 여부~~ → **무시(범위 제외) 확정** (2026-06-10, 운영자)
## DATA
- ~~trex-orb-v1 의 `allocated_capital` 송신 여부~~ → **송신함** (`trex_orb_v1/runtime/intent_builder.py:56`). 단 사이징 입력은 자체 계산(`daemon.py:83`)이며 trex-core snapshot 의존 아님. 운영자 결정으로 무시.
# 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(동시 적용) 모두 운영자 결정으로 Answered, 외부 발신자 확인 완료(무시·범위 제외). C 유형 플레이스홀더 0건, baseline 문서 모순 없음(allocated_capital 직접 기술 없음 확인). REQ change-req 5건(REQ-002·006·007·028·032) 인계 준비 완료.
- Ready for REQ Drafting: true
- Confirmed By: Justin Ko
- Confirmed At (KST): 2026-06-10 21:42
+2
View File
@@ -27,3 +27,5 @@
| dcy-019_20260609_slot-entry-count-and-closed-reallocation | 20260609 | slot entry_count 추가 및 closed 슬롯 재할당 정책 (issue #18) | [Discovery](./dcy-019_20260609_slot-entry-count-and-closed-reallocation.md) | confirmed |
| dcy-020_20260610_asset-centric-slot-model | 20260610 | 자산 중심 슬롯 모델 재설계 — portfolio-hub universe 와 1:1 슬롯 + 활성 슬롯 개수 제어 (일반 슬롯 풀 모델 대체) | [Discovery](./dcy-020_20260610_asset-centric-slot-model.md) | confirmed |
| dcy-021_20260610_position-sizing-target-intent | 20260610 | 포지션 사이징 — 활성 자산 슬롯 보유 수량 변경(HOLDING→HOLDING)·부분 매도·선언형 target intent (issue #1, dcy-020 OQ-4 후속) | [Discovery](./dcy-021_20260610_position-sizing-target-intent.md) | confirmed |
| dcy-022_20260610_stuck-pending-order-recovery | 20260610 | 전략 발신 intent 실행 정책 — 주문 timeout(TIF)/슬리피지 허용 + 미체결 고착 백스톱 (issue #21, dcy-020/021 모델 전제) | [Discovery](./dcy-022_20260610_stuck-pending-order-recovery.md) | confirmed |
| dcy-023_20260610_allocated-capital-dead-field-removal | 20260610 | allocated_capital 죽은 필드 제거 — `enter --capital` 옵션 + slot/intent/snapshot 계약 정리 (issue #22) | [Discovery](./dcy-023_20260610_allocated-capital-dead-field-removal.md) | confirmed |