docs(discovery): add dcy-006 RabbitMQ transport (confirmed) + advance dcy-005

dcy-006 (new, confirmed — Confirmed By: Justin): trex-core ↔ 전략 모듈
통신 transport를 RabbitMQ로 전환. OQ-1~7 전부 Answered —
- HTTP /v1 API 완전 대체(표면 0, healthz 제거; liveness=프로세스 감독)
- 토폴로지: 스냅샷·intent admission = RPC(direct reply-to), 이벤트 = topic
- delivery: at-least-once + idempotency = effectively-once, 스냅샷 reconcile
- degrade: 버퍼링 금지·보수적 중단, core 자율 안전 매도 독립(미구현, 추후)
- 클라이언트: pika 동기 + 스레드 런타임(uvicorn 제거)
- 자격증명: TREX_RABBITMQ_URL

dcy-005 (advance): strategy_id 필터 + 런타임 pause/resume(FR-8/9);
옵션 D(체결 시 portfolio-hub write 응답 cash_balance_after/asset_quantity_after
갱신, portfolio-hub#8); OQ-4 holding 포함·pending/warning/pause 마스킹;
OQ-11 view-mode 파라미터 + OQ-12; 응답 용어 sweep.

index: dcy-006 행 추가(confirmed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-05 22:33:42 +09:00
co-authored by Claude Opus 4.8
parent aed54911d4
commit 0d6854aaa5
3 changed files with 366 additions and 51 deletions
@@ -3,9 +3,9 @@
- 상태: draft - 상태: draft
- 문서 ID: dcy-005_20260604_portfolio-hub-asset-snapshot-api - 문서 ID: dcy-005_20260604_portfolio-hub-asset-snapshot-api
- 이슈명: portfolio-hub 종목 스냅샷 read API — 매수 전략 모듈에 슬롯-제외 후보 제공 - 이슈명: portfolio-hub 종목 스냅샷 read API — 전략 모듈에 전략 실행 대상 자산 리스트 제공
- 작성 시각(KST): 2026-06-04 22:46 - 작성 시각(KST): 2026-06-04 22:46
- 마지막 갱신 시각(KST): 2026-06-04 23:55 - 마지막 갱신 시각(KST): 2026-06-05 21:02
- 대체됨: 없음 - 대체됨: 없음
- 후속 Discovery 참조: 없음 - 후속 Discovery 참조: 없음
- 생성된 REQ 참조: 없음 - 생성된 REQ 참조: 없음
@@ -18,7 +18,7 @@
- 관련 TODO/메모: dcy-004(portfolio-hub ledger write 통합, BAT-006 진행 중)의 Out of Scope `asset master 동기화·get_assets/get_portfolios 연동`. orbit `app/clients/portfolio_client.py`(read 계약 get_assets/get_portfolios 패턴) - 관련 TODO/메모: dcy-004(portfolio-hub ledger write 통합, BAT-006 진행 중)의 Out of Scope `asset master 동기화·get_assets/get_portfolios 연동`. orbit `app/clients/portfolio_client.py`(read 계약 get_assets/get_portfolios 패턴)
- 해석: - 해석:
- dcy-004 는 체결을 portfolio-hub 원장에 기록하는 **write 방향**이었고, 본 Discovery 는 그 반대인 **read 방향** — portfolio-hub 종목 스냅샷을 trex-core API 로 매수 전략 모듈에 제공한다. dcy-004 가 명시적으로 후속으로 미룬 영역을 이어받는다. - dcy-004 는 체결을 portfolio-hub 원장에 기록하는 **write 방향**이었고, 본 Discovery 는 그 반대인 **read 방향** — portfolio-hub 종목 스냅샷을 trex-core API 로 매수 전략 모듈에 제공한다. dcy-004 가 명시적으로 후속으로 미룬 영역을 이어받는다.
- active-cycle 판정(BAT-006 draft 진행 중)이지만 명시적 신규 입력이고, BAT-006(write)과 read 방향이 분리되어 범위 충돌이 없으므로 병렬 독립 주기로 진행한다. core 경유의 핵심 가치는 단순 패스스루가 아니라 **core 의 권위 슬롯 상태를 겹쳐(slot overlay) 이미 점유된 종목을 제외한 매수 후보**를 제공하는 것이다. - active-cycle 판정(BAT-006 draft 진행 중)이지만 명시적 신규 입력이고, BAT-006(write)과 read 방향이 분리되어 범위 충돌이 없으므로 병렬 독립 주기로 진행한다. core 경유의 핵심 가치는 단순 패스스루가 아니라 **core 의 권위 슬롯 상태를 겹쳐(slot overlay) 전략 실행 대상 자산을 선별(pending·warning·pause 마스킹)** 해 제공하는 것이다.
# 2. 요구사항 판정 결과 # 2. 요구사항 판정 결과
이번 요청이 기존 요구사항과 비교해 신규인지, 유사한지, 중복인지 판단하고 그 근거를 남긴다. 이번 요청이 기존 요구사항과 비교해 신규인지, 유사한지, 중복인지 판단하고 그 근거를 남긴다.
@@ -37,40 +37,44 @@
- 결과적으로 매수 전략 모듈이 "무엇을 살지" 정할 portfolio-hub 종목 정보를 trex-core 를 통해 받을 경로가 없다. - 결과적으로 매수 전략 모듈이 "무엇을 살지" 정할 portfolio-hub 종목 정보를 trex-core 를 통해 받을 경로가 없다.
- 기대 상태 - 기대 상태
- trex-core daemon 이 설정 가능한 시각(기본 07:30 KST, .env)에 일일 스케줄로 portfolio-hub 에서 자신의 `daemon_id` universe(= `managed_by_daemon_id` 가 해당 daemon 인 portfolio 집합)를 가져와 스냅샷으로 보유한다. universe 항목은 portfolio 단위로 symbol, asset_id(=portfolio_id), 현금(cash_balance), 포지션(asset_quantity), active_strategy_id, 전략파라미터(opaque JSON)를 담고 **가격(price)은 제외**한다. - trex-core daemon 이 설정 가능한 시각(기본 07:30 KST, .env)에 일일 스케줄로 portfolio-hub 에서 자신의 `daemon_id` universe(= `managed_by_daemon_id` 가 해당 daemon 인 portfolio 집합)를 가져와 스냅샷으로 보유한다. universe 항목은 portfolio 단위로 symbol, asset_id(=portfolio_id), 현금(cash_balance), 포지션(asset_quantity), active_strategy_id, 전략파라미터(opaque JSON)를 담고 **가격(price)은 제외**한다.
- 매수 전략 모듈 요청 시 trex-core 가 이 스냅샷을 기반으로 **현재 슬롯을 점유(pending/holding)한 종목을 제외한 매수 후보 리스트**를 반환한다. - 전략 모듈 요청 시 trex-core 가 이 스냅샷을 기반으로 **portfolio-hub 보유 자산(holding 슬롯 자산 포함)을 담되 pending·warning·pause 상태 자산은 마스킹한 전략 실행 대상 자산 리스트**를 반환한다(OQ-4).
- 가용 슬롯이 없으면(capacity 소진) 매수 후보가 없음을 명확한 시맨틱(빈 리스트)으로 반환한다. - 가용 슬롯이 없을 때(capacity 소진)의 응답 시맨틱은 holding 포함 변경에 맞춰 재정의 대상이다(OQ-11).
- 이해관계자 - 이해관계자
- 문서 작성자/구현자(AI agent) - 문서 작성자/구현자(AI agent)
- 리뷰어/승인자(운영자, genichin@gmail.com) - 리뷰어/승인자(운영자, genichin@gmail.com)
- 매수 전략 모듈(trex-core 외부 소비자) 및 portfolio-hub(외부 자산/포지션 서비스) - 매수 전략 모듈(trex-core 외부 소비자) 및 portfolio-hub(외부 자산/포지션 서비스)
- 제약사항 - 제약사항
- 도메인 순수성 — `trex_core.domain` 은 portfolio-hub/네트워크 의존 0(read 어댑터 격리). - 도메인 순수성 — `trex_core.domain` 은 portfolio-hub/네트워크 의존 0(read 어댑터 격리).
- 단일 통합 경계 — portfolio-hub 자격증명/계약 접점은 trex-core 어댑터(dcy-004 재사용)로 한정. - 단일 통합 경계 — portfolio-hub 자격증명/계약 접점은 trex-core 어댑터(dcy-004 재사용)로 한정하며, 매수 전략 등 모든 소비자는 core 스냅샷 API 를 경유한다(portfolio-hub 직접 호출 금지, OQ-3).
- core 는 슬롯/capacity 의 권위 단일 writer(불변식 A) — slot overlay 는 이 권위 상태를 사용한다. - core 는 슬롯/capacity 의 권위 단일 writer(불변식 A) — slot overlay 는 이 권위 상태를 사용한다.
## 주요 사용자 시나리오 ## 주요 사용자 시나리오
사용자 또는 운영자가 실제로 어떤 상황에서 이 변경을 필요로 하는지 대표 시나리오를 적는다. 사용자 또는 운영자가 실제로 어떤 상황에서 이 변경을 필요로 하는지 대표 시나리오를 적는다.
- 매수 전략 모듈이 매수 후보를 정하려고 trex-core 스냅샷 API 를 호출 → portfolio-hub 종목 중 이미 슬롯을 점유한 종목을 뺀 후보 리스트를 받는다. - 전략 모듈이 자기 portfolio(전략 실행 대상 자산)를 trex-core 스냅샷 API 로 요청 → portfolio-hub 보유 자산(holding 슬롯 자산 포함)을 받되, pending·warning·pause 상태 자산은 마스킹되어 빠진 리스트를 받는다.
- 모든 슬롯이 차 있어 더 살 수 없는 상황 → 스냅샷 API 가 빈 후보 리스트를 반환해 전략이 매수를 시도하지 않는다. - 모든 슬롯이 차 있어 더 살 수 없는 상황 → (holding 포함 변경으로 이 시맨틱 재정의 필요 — OQ-11) 신규 매수 가능 여부 신호 방식 확정 전까지 보유 자산은 계속 응답에 포함될 수 있다.
- 막 체결되어 슬롯이 pending/holding 인 종목 → portfolio-hub 포지션에 아직 반영되기 전이라도 core slot overlay 로 후보에서 즉시 제외되어 중복 매수를 막는다. - 막 매수 주문되어 슬롯이 pending(미체결)인 종목 → 마스킹되어 전략 모듈 응답에서 빠져 중복 매수를 막고, 체결되어 holding 이 되면 보유 자산으로 응답에 포함된다.
- 스냅샷 페치 시 portfolio-hub 포지션과 실제 브로커 계좌 포지션이 어긋난 자산 → `warning` 으로 표시되어 그 자산은 매수 후보에서 빠지고 매매 전략이 수행되지 않는다. - 스냅샷 페치 시 portfolio-hub 포지션과 실제 브로커 계좌 포지션이 어긋난 자산 → `warning` 으로 표시되어 그 자산은 응답 리스트에서 마스킹되고 매매 전략이 수행되지 않는다.
- 전략 모듈이 자기 `strategy_id` 를 파라미터로 넣어 포트폴리오를 요청 → 그 전략 ID 가 등록된 자산만 받는다. 파라미터를 생략하면 전체 자산을 받는다(여러 전략 모듈이 하나의 스냅샷 API 를 공유하되 자기 책임 자산만 조회).
- 운영자가 특정 자산을 런타임에 pause → 전략 모듈이 포트폴리오를 요청해도 그 자산은 응답에서 마스킹되어 매매가 멈추고, resume 하면 다시 응답에 포함된다.
# 4. 이번에 정의할 변경 # 4. 이번에 정의할 변경
이번 Discovery에서 다루는 핵심 변경 항목을 목록으로 적는다. 기능, 구조, 규칙 변경을 함께 포함해도 된다. 이번 Discovery에서 다루는 핵심 변경 항목을 목록으로 적는다. 기능, 구조, 규칙 변경을 함께 포함해도 된다.
- portfolio-hub read 포트 + 어댑터: `PortfolioHubReadPort.get_portfolios(daemon_id)` 추상화 + portfolio-hub 어댑터(`managed_by_daemon_id` 로 daemon universe 조회, dcy-004 어댑터/자격증명 재사용) + 결정론 stub. 전략파라미터는 trex-core 가 해석하지 않는 opaque JSON 으로 운반한다. - portfolio-hub read 포트 + 어댑터: `PortfolioHubReadPort.get_portfolios(daemon_id)` 추상화 + portfolio-hub 어댑터(`managed_by_daemon_id` 로 daemon universe 조회, dcy-004 어댑터/자격증명 재사용) + 결정론 stub. 전략파라미터는 trex-core 가 해석하지 않는 opaque JSON 으로 운반한다.
- 일일 스냅샷 갱신: 설정 시각(기본 07:30 KST, .env)에 스케줄러(신규 daemon 컴포넌트)가 universe 를 페치해 스냅샷을 갱신한다. 현금/포지션은 baseline + core in-flight 체결 보정(옵션 C). - 일일 스냅샷 갱신: 설정 시각(기본 07:30 KST, .env)에 스케줄러(신규 daemon 컴포넌트)가 universe 를 페치해 스냅샷을 갱신한다. 현금/포지션은 baseline 에 더해, 체결 시 portfolio-hub ledger write 응답이 반환하는 변경 후 권위 cash/position 으로 갱신한다(옵션 D — 당초 옵션 C 의 core 로컬 체결 replay 보정을 대체).
- 포지션 정합 검증 + warning: 페치 시 portfolio-hub 포지션 vs 실제 브로커 계좌 포지션을 대조하고, 불일치 자산은 `warning` 상태로 매수 후보에서 제외한다. - 포지션 정합 검증 + warning: 페치 시 portfolio-hub 포지션 vs 실제 브로커 계좌 포지션을 대조하고, 불일치 자산은 `warning` 상태로 응답 리스트에서 마스킹한다.
- 종목 스냅샷 read API: trex-core 가 보유 스냅샷을 read 엔드포인트로 제공하고, **현재 슬롯 점유(pending/holding) 종목을 제외**한 매수 후보 리스트를 반환한다(slot overlay). - 종목 스냅샷 read API: trex-core 가 보유 스냅샷을 read 엔드포인트로 제공하고, **holding 포함·pending·warning·pause 마스킹**한 전략 실행 대상 자산 리스트를 반환한다(slot overlay, OQ-4).
- 빈 후보 시맨틱: 가용 슬롯이 0 이면 매수 후보를 빈 리스트로 반환한다(표현 방식 null vs empty 는 OQ-6). - 전략 ID 필터: read 엔드포인트가 선택적 `strategy_id` 파라미터를 받아, 주어지면 해당 전략 ID 가 등록된(= `active_strategy_id` 일치) 자산만 반환하고, 없으면 (pending·warning·pause 마스킹 후) 모든 자산을 반환한다. 전략 ID 의 식별자 형태("이름?")와 "등록" 판정 기준은 OQ-9.
- 런타임 pause/resume: trex-core 가 보유한 스냅샷 universe 의 개별 항목을 사용자가 런타임에 일시 중단(pause)/재개(resume)할 수 있고, pause 된 항목은 전략 모듈의 포트폴리오 요청 결과에서 제외한다. pause 단위·제어 방식·영속성은 OQ-10.
- capacity 0 응답 시맨틱: 가용 슬롯이 0 일 때의 응답은 holding 포함 변경에 맞춰 재정의한다(OQ-11; 빈/표현 형식은 OQ-6).
- baseline 갱신: interface-contract(read 포트/엔드포인트)·runtime-flows(스냅샷 조회 흐름)·project-structure(read 어댑터) 갱신. - baseline 갱신: interface-contract(read 포트/엔드포인트)·runtime-flows(스냅샷 조회 흐름)·project-structure(read 어댑터) 갱신.
## 영향 범위 ## 영향 범위
변경이 미칠 코드/문서/운영 범위를 명시해 REQ 정제와 batch planning에서 추정 누락을 줄인다. 변경이 미칠 코드/문서/운영 범위를 명시해 REQ 정제와 batch planning에서 추정 누락을 줄인다.
- 코드 영향: - 코드 영향:
- 신규: `trex_core/application` read 포트, 신규 `adapters/portfolio` read 어댑터 + stub, `trex_core/adapters/api` 스냅샷 read 엔드포인트 + 응답 스키마, slot overlay(점유 종목 제외) + 현금/포지션 in-flight 보정 로직, 일일 스냅샷 스케줄러(런타임 신규), 포지션 정합 검증 + `warning` 상태, `config` 에 `daemon_id` + 스냅샷 페치 시각 주입. - 신규: `trex_core/application` read 포트, 신규 `adapters/portfolio` read 어댑터 + stub, `trex_core/adapters/api` 스냅샷 read 엔드포인트 + 응답 스키마, slot overlay(점유 종목 제외) + 현금/포지션 write-응답 갱신 로직(`cash_balance_after`/`asset_quantity_after`, 옵션 D), 일일 스냅샷 스케줄러(런타임 신규), 포지션 정합 검증 + `warning` 상태, `config` 에 `daemon_id` + 스냅샷 페치 시각 주입, `strategy_id` 필터(쿼리 파라미터 → `active_strategy_id` 매칭), 스냅샷 항목 pause/resume 상태 저장 + 제어 표면(제어 API/CLI) + 응답 산출 시 pause 마스킹.
- 기존: capacity/slot 조회 재사용(점유 종목·가용 슬롯 판정), 브로커 계좌 포지션 조회(BrokerPort 잔고조회 — 정합 검증용) 재사용/확장. - 기존: capacity/slot 조회 재사용(점유 종목·가용 슬롯 판정), 브로커 계좌 포지션 조회(BrokerPort 잔고조회 — 정합 검증용) 재사용/확장.
- 문서 영향: - 문서 영향:
- interface-contract/runtime-flows/project-structure 갱신. - interface-contract/runtime-flows/project-structure 갱신.
@@ -82,25 +86,27 @@
## 기능 요구사항 ## 기능 요구사항
- FR-1: trex-core 가 종목 스냅샷을 read API 엔드포인트로 제공한다(GET, 매수 전략 모듈이 HTTP 로 소비). 스냅샷 단위는 daemon 이 관리하는 portfolio universe 이며, 항목당 symbol, asset_id(=portfolio_id), 현금(cash_balance), 포지션(asset_quantity), active_strategy_id, 전략파라미터(opaque JSON)를 담고 가격(price)은 제외한다. (OQ-1 Answered) - FR-1: trex-core 가 종목 스냅샷을 read API 엔드포인트로 제공한다(GET, 매수 전략 모듈이 HTTP 로 소비). 스냅샷 단위는 daemon 이 관리하는 portfolio universe 이며, 항목당 symbol, asset_id(=portfolio_id), 현금(cash_balance), 포지션(asset_quantity), active_strategy_id, 전략파라미터(opaque JSON)를 담고 가격(price)은 제외한다. (OQ-1 Answered)
- FR-2: 스냅샷 응답에서 **현재 슬롯을 점유(pending/holding)한 종목을 제외**한 매수 후보 리스트를 반환한다. 제외 판정은 core 의 권위 슬롯 상태를 사용한다(slot overlay). - FR-2: 스냅샷 응답(전략 실행 대상 portfolio 자산 리스트)은 **portfolio-hub 에 보유된 자산을 기본 포함하며, holding 슬롯의 자산도 포함**한다(전략 모듈이 보유 포지션까지 받아 관리). 단 다음 상태의 자산은 **마스킹**하여 응답에서 제외한다: (a) 슬롯 상태 pending(미체결 in-flight), (b) 자산 상태 warning(브로커 실제 position 불일치, FR-7), (c) 사용자 pause(FR-9). 포함/마스킹 판정은 core 권위 슬롯 상태 + 정합 검증 + pause 상태를 사용하며 portfolio-hub 보유 여부보다 우선한다(slot overlay). 본 조항은 기본(default) view 의 구성 규칙이며, 응답 베이스 집합은 요청 view 모드 파라미터(OQ-11/OQ-12)로 선택될 수 있고 마스킹은 선택된 베이스 위에 적용된다. (변경 2026-06-05, OQ-4 결정: 종전 'pending·holding 점유 종목 제외, 매수 후보 리스트' → 'holding 포함, pending·warning·pause 만 마스킹'.)
- FR-3: 가용 슬롯이 0(capacity 소진)이면 매수 후보를 빈 리스트로 반환한다. 빈 리스트의 표현(빈 배열 vs null vs 메타 필드)은 OQ-6 결정에 따른다. - FR-3: 가용 슬롯이 0(capacity 소진)일 때의 응답은 **고정값이 아니라 전략 모듈이 요청 시 지정하는 view 모드 파라미터**에 따른다(OQ-11 결정). 최소 3개 모드: (a) 빈 리스트 `[]`, (b) 슬롯 점유 자산만, (c) portfolio 스냅샷 − 슬롯 점유 자산. 파라미터 이름/enum·기본 모드·마스킹(OQ-4)·strategy_id 필터(FR-8)와의 합성은 OQ-12, 빈/표현 형식은 OQ-6 에서 확정한다. (종전 'capacity 0 → 매수 후보 빈 리스트'는 OQ-4 holding 포함 + 본 결정으로 대체.)
- FR-4: portfolio-hub read 접근을 `PortfolioHubReadPort.get_portfolios(daemon_id)` 추상화 + portfolio-hub 어댑터(`managed_by_daemon_id` 기준) + 결정론 stub 로 제공하고, dcy-004 의 portfolio-hub 어댑터/자격증명을 재사용한다. 전략파라미터는 opaque JSON 으로 통과시킨다(trex-core 가 스키마를 해석하지 않음). - FR-4: portfolio-hub read 접근을 `PortfolioHubReadPort.get_portfolios(daemon_id)` 추상화 + portfolio-hub 어댑터(`managed_by_daemon_id` 기준) + 결정론 stub 로 제공하고, dcy-004 의 portfolio-hub 어댑터/자격증명을 재사용한다. 전략파라미터는 opaque JSON 으로 통과시킨다(trex-core 가 스키마를 해석하지 않음).
- FR-5: baseline 문서(interface-contract/runtime-flows/project-structure)를 read 포트·스냅샷 엔드포인트·read 어댑터·일일 스케줄러에 맞게 갱신한다. - FR-5: baseline 문서(interface-contract/runtime-flows/project-structure)를 read 포트·스냅샷 엔드포인트·read 어댑터·일일 스케줄러에 맞게 갱신한다.
- FR-6: trex-core daemon 이 설정 가능한 시각(기본 07:30 KST, .env 로 지정)에 일일 스케줄로 `daemon_id` universe 를 페치해 스냅샷을 보유한다. universe 멤버십·전략파라미터는 일일 스냅샷으로 고정하고, 현금/포지션은 스냅샷 baseline 에 그날 core 의 in-flight 체결분을 반영해 보정한다(옵션 C). - FR-6: trex-core daemon 이 설정 가능한 시각(기본 07:30 KST, .env 로 지정)에 일일 스케줄로 `daemon_id` universe 를 페치해 스냅샷을 보유한다. universe 멤버십·전략파라미터는 일일 스냅샷으로 고정하고, 현금/포지션은 스냅샷 baseline 에 더해 **체결 시 portfolio-hub ledger write 응답의 `cash_balance_after` / `asset_quantity_after` 권위 값으로 갱신한다(옵션 D)**. (포지션/현금의 ground truth 는 portfolio-hub 이며 응답값으로 덮어써 로컬 누적 보정 drift 를 제거. portfolio-hub 가 ledger write 를 회계 단일 권위로 단일 트랜잭션 처리하고 `*_after` 를 반환하기로 확정 — A-7, portfolio-hub#8. portfolio-hub 배포(REL-003 등) 시점까지 구현 대기.)
- FR-7: 스냅샷 페치 시 각 portfolio 의 portfolio-hub 포지션과 실제 브로커 계좌 포지션이 일치하는지 검증한다. 불일치하는 자산은 `warning` 상태로 표시하고 매수 후보에서 제외해 해당 자산에 매매 전략을 수행하지 않는다. - FR-7: 스냅샷 페치 시 각 portfolio 의 portfolio-hub 포지션과 실제 브로커 계좌 포지션이 일치하는지 검증한다. 불일치하는 자산은 `warning` 상태로 표시하고 응답 리스트에서 마스킹해 해당 자산에 매매 전략을 수행하지 않는다.
- FR-8: 스냅샷 read API 는 선택적 `strategy_id` 파라미터를 받는다. `strategy_id` 가 주어지면 해당 전략 ID 가 등록된(= 자산의 `active_strategy_id` 와 일치) 자산만 응답 리스트에 포함하고, `strategy_id` 가 없으면 (pending·warning·pause 마스킹 후) 모든 자산을 반환한다. 여러 전략 모듈이 같은 스냅샷 API 를 공유하되 각자 자기 책임 자산만 받는다. 전략 ID 의 식별자 형태(ID vs 이름)와 "등록" 판정 기준(단일 `active_strategy_id` 일치 vs 다중 등록)은 OQ-9.
- FR-9: trex-core 가 보유한 스냅샷 universe 의 개별 항목을 사용자가 런타임에 일시 중단(pause)/재개(resume)할 수 있다. pause 된 항목은 전략 모듈의 포트폴리오 요청 결과(FR-2)에서 제외한다. 응답 리스트는 holding 포함을 기본으로 (pending 마스킹) ∧ (warning 마스킹) ∧ (pause 마스킹) ∧ (strategy_id 필터, 주어진 경우)의 합성으로 산출된다. pause 단위(portfolio/asset/symbol), 제어 방식(제어 API vs CLI vs config), 영속성(스냅샷 갱신·daemon 재시작 후 유지 여부), 전략별 vs 전역 적용은 OQ-10.
## 비기능 요구사항 ## 비기능 요구사항
성능, 운영성, 일관성, 자동화 안정성 등 기능 외 요구사항을 적는다. 성능, 운영성, 일관성, 자동화 안정성 등 기능 외 요구사항을 적는다.
- NFR-1: 도메인 순수성 유지 — read 포트는 application 계약, 어댑터는 adapters 레이어 격리. `trex_core/domain` 은 portfolio-hub/네트워크 의존 0(import 스윕). - NFR-1: 도메인 순수성 유지 — read 포트는 application 계약, 어댑터는 adapters 레이어 격리. `trex_core/domain` 은 portfolio-hub/네트워크 의존 0(import 스윕).
- NFR-2: 일관성 — 점유 종목 제외/가용 슬롯 판정은 core 의 권위 슬롯 상태(강한 일관성)를 사용한다. 현금/포지션은 일일 baseline + core in-flight 체결 보정으로 유지하고(옵션 C), 스냅샷 페치 시 브로커 계좌와 정합 검증한다(불일치 자산 `warning` 제외). - NFR-2: 일관성 — 점유 종목 제외/가용 슬롯 판정은 core 의 권위 슬롯 상태(강한 일관성)를 사용한다. 현금/포지션은 일일 baseline + 체결 시 portfolio-hub ledger write 응답의 `cash_balance_after` / `asset_quantity_after` 권위 값 갱신으로 유지하고(옵션 D), 스냅샷 페치 시 브로커 계좌와 정합 검증한다(불일치 자산 `warning` 제외).
- NFR-3: 가용성 degrade — portfolio-hub 불가 시 스냅샷 read 의 동작(에러 vs 캐시 fallback vs 빈 응답)은 OQ-5 정책을 따른다. - NFR-3: 가용성 degrade — portfolio-hub 불가 시 스냅샷 read 의 동작(에러 vs 캐시 fallback vs 빈 응답)은 OQ-5 정책을 따른다.
## 범위 경계 ## 범위 경계
이번 반복에서 반드시 포함할 범위와 명시적으로 제외할 범위를 구분한다. 이번 반복에서 반드시 포함할 범위와 명시적으로 제외할 범위를 구분한다.
- In Scope - In Scope
- portfolio-hub read 포트 + 어댑터 + stub, 종목 스냅샷 read API, 슬롯 점유 종목 제외(slot overlay), 가용 슬롯 0 → 빈 후보, 설정 시각 일일 스냅샷 + 현금/포지션 in-flight 보정(옵션 C), 포지션 정합 검증 + `warning` 제외, baseline 갱신. - portfolio-hub read 포트 + 어댑터 + stub, 종목 스냅샷 read API, holding 포함·pending·warning·pause 마스킹(slot overlay, OQ-4), capacity 0 응답 재정의(OQ-11), 설정 시각 일일 스냅샷 + 현금/포지션 write-응답 갱신(옵션 D), 포지션 정합 검증 + `warning` 제외, baseline 갱신.
- Out of Scope (후속 Discovery) - Out of Scope (후속 Discovery)
- 매수 전략 의사결정 로직 자체(점수화·랭킹·진입 타이밍) — 전략 모듈 책임. - 매수 전략 의사결정 로직 자체(점수화·랭킹·진입 타이밍) — 전략 모듈 책임.
- portfolio-hub ↔ core 양방향 reconciliation, 포지션 재구성, asset master 영속 동기화. - portfolio-hub ↔ core 양방향 reconciliation, 포지션 재구성, asset master 영속 동기화.
@@ -111,14 +117,14 @@
## 리스크 ## 리스크
- R-1 (Medium): stale 스냅샷 — 일일 캐시한 현금/포지션이 장중에 어긋날 수 있다. - R-1 (Medium): stale 스냅샷 — 일일 캐시한 현금/포지션이 장중에 어긋날 수 있다.
- 완화 방안(OQ-2 Answered, 옵션 C): 현금/포지션을 baseline + core in-flight 체결 보정으로 유지. daemon 이 유일 매매 주체이고 장중 입출금이 없다는 전제(A-6)로 정확. 추가로 페치 시 브로커 계좌 정합 검증(FR-7). - 완화 방안(OQ-2 Answered, 옵션 D): 현금/포지션을 baseline + 체결 시 portfolio-hub ledger write 응답의 권위 cash/position 으로 갱신해 유지. daemon 이 유일 매매 주체이고 장중 입출금이 없다는 전제(A-6)로 정확. 추가로 페치 시 브로커 계좌 정합 검증(FR-7). 잔여 갭: 체결→write flush 사이에는 baseline 값이라 현금이 잠시 stale 할 수 있으나, 중복 매수 배제는 slot overlay(FR-2)가 즉시 담당.
- R-2 (Medium): in-flight race — dcy-004 ledger outbox 가 eventually-consistent 라, 막 체결한 종목이 portfolio-hub 포지션에 아직 반영되기 전 후보에 중복 노출될 수 있다. - R-2 (Medium): in-flight race — dcy-004 ledger outbox 가 eventually-consistent 라, 막 체결한 종목이 portfolio-hub 포지션에 아직 반영되기 전 중복 노출될 수 있다.
- 완화 방안(OQ-4): core slot overlay(pending/holding 제외)로 이 gap 을 메운다. portfolio-hub 포지션과 core 슬롯 상태의 합성 규칙을 명확화한다. - 완화 방안(OQ-4 Answered): pending 슬롯 마스킹으로 미체결 in-flight 종목을 응답에서 제외해 중복 노출을 차단한다. 체결 완료(holding)된 자산은 보유 자산으로 포함되며, 전략 모듈이 보유 상태를 보고 재매수하지 않는다(holding 자산의 보유 수량 표기 정합은 core 권위 슬롯 상태 기준 — 설계 시 확정). (종전 'pending·holding 동시 제외'에서 'pending 만 마스킹, holding 포함'으로 변경.)
- R-3 (Medium): 가용성 결합 — portfolio-hub 장애가 매수 전략 read 를 막는다. - R-3 (Medium): 가용성 결합 — portfolio-hub 장애가 매수 전략 read 를 막는다.
- 완화 방안(OQ-5): 캐시 fallback 또는 명시적 degrade 응답 정책. 어댑터 격리(NFR-1). - 완화 방안(OQ-5): 캐시 fallback 또는 명시적 degrade 응답 정책. 어댑터 격리(NFR-1).
- R-4 (Low): 책임 경계 확장 — core 가 portfolio-hub read gateway 로 비대해질 위험. - R-4 (Low): 책임 경계 확장 — core 가 portfolio-hub read gateway 로 비대해질 위험.
- 완화 방안: slot overlay 부가가치(점유 제외·가용 판정)로 범위를 한정하고 단순 패스스루 프록시는 지양(OQ-3). - 완화 방안: 모든 소비자가 core 를 경유하되(OQ-3 Answered), core 의 부가가치(slot overlay·현금/포지션 보정·정합 검증)로 범위를 한정하고 단순 패스스루 프록시는 지양한다.
- R-5 (Medium): 포지션 drift — portfolio-hub baseline 포지션이 실제 브로커 계좌와 어긋나면(과거 누락·정합 오류) 옵션 C 의 보정 baseline 자체가 틀린다. - R-5 (Medium): 포지션 drift — portfolio-hub 가 실제 브로커 계좌와 어긋나면(과거 누락·정합 오류) 스냅샷 baseline 자체가 틀린다. (옵션 D 로 전환해 core 로컬 replay 누적 drift 는 제거되었으나, portfolio-hub ↔ 브로커 계좌 간 drift 는 여전히 남는다.)
- 완화 방안(FR-7): 스냅샷 페치 시 portfolio-hub 포지션 vs 브로커 계좌 포지션 정합 검증, 불일치 자산은 `warning` 으로 매매 제외(보수적 안전). 일치 기준·회복 규칙은 OQ-8. - 완화 방안(FR-7): 스냅샷 페치 시 portfolio-hub 포지션 vs 브로커 계좌 포지션 정합 검증, 불일치 자산은 `warning` 으로 매매 제외(보수적 안전). 일치 기준·회복 규칙은 OQ-8.
## 가정 ## 가정
@@ -129,7 +135,8 @@
- A-3: 매수 전략 모듈은 trex-core 외부 소비자이며 HTTP 로 스냅샷을 받는다. - A-3: 매수 전략 모듈은 trex-core 외부 소비자이며 HTTP 로 스냅샷을 받는다.
- A-4: dcy-004 의 portfolio-hub 어댑터/자격증명(.env `TREX_PORTFOLIO_HUB_*`)을 read 방향에서도 재사용한다. - A-4: dcy-004 의 portfolio-hub 어댑터/자격증명(.env `TREX_PORTFOLIO_HUB_*`)을 read 방향에서도 재사용한다.
- A-5: portfolio-hub 의 `daemon` 은 1급 엔티티이며 portfolio 가 `managed_by_daemon_id` 로 daemon 에 귀속된다(1 daemon : N portfolio, 각 portfolio ≈ 1 asset, asset_id == portfolio_id). 전략파라미터는 portfolio-hub 가 `strategy_params` JSONB 로 보관한다. trex-core daemon 의 universe = 자신이 관리하는 portfolio 집합이다. (근거: portfolio-hub `app/domain/models.py` Daemon/Portfolio, `strategy_params` JSONB migration) - A-5: portfolio-hub 의 `daemon` 은 1급 엔티티이며 portfolio 가 `managed_by_daemon_id` 로 daemon 에 귀속된다(1 daemon : N portfolio, 각 portfolio ≈ 1 asset, asset_id == portfolio_id). 전략파라미터는 portfolio-hub 가 `strategy_params` JSONB 로 보관한다. trex-core daemon 의 universe = 자신이 관리하는 portfolio 집합이다. (근거: portfolio-hub `app/domain/models.py` Daemon/Portfolio, `strategy_params` JSONB migration)
- A-6: 이 trex-core daemon 은 자신이 관리하는 portfolio 의 유일한 매매 주체이며, 장중 거래 외 현금 변동(입출금)은 없다. 따라서 일일 baseline + core in-flight 체결 보정만으로 현금/포지션을 정확히 유지할 수 있다(옵션 C 전제). (사용자 확인 2026-06-04) - A-6: 이 trex-core daemon 은 자신이 관리하는 portfolio 의 유일한 매매 주체이며, 장중 거래 외 현금 변동(입출금)은 없다. 따라서 일일 baseline + 체결 시 portfolio-hub write 응답 갱신으로 현금/포지션을 정확히 유지할 수 있다(옵션 D 전제). (사용자 확인 2026-06-04)
- A-7 (확정 2026-06-05, portfolio-hub#8): portfolio-hub 가 `POST /v1/portfolios/{portfolio_id}/ledger/trades` 응답 root 에 flat 필드 **`cash_balance_after`, `asset_quantity_after`** 2개를 비파괴 추가하기로 회신. 더 나아가 이 endpoint 를 회계 단일 권위로 만들어 **ledger row INSERT + portfolio cash/quantity UPDATE 를 단일 DB 트랜잭션(row lock)** 으로 처리한다 → trex-core 는 write 1회로 회계 완결, drift 원천 차단, read round-trip 불요. status 별: `new`(201) = 본 거래 반영 후 권위 값, `duplicate`(200) = 현재 권위 값(idempotent 정합), `409`/잔고부족 `422` = `*_after` 미정의(본문 미정). trex-core 는 이 응답값으로 스냅샷 현금/포지션을 갱신한다(옵션 D). 의존: portfolio-hub 측은 별도 Discovery(dcy-006)·release(REL-003 등)로 배포 예정 — **배포 시점까지 trex-core 구현은 대기**. 추적: https://github.com/fourblocks2020/portfolio-hub/issues/8 . (사용자 결정 2026-06-05 / portfolio-hub 회신 반영)
## 오픈 질문 ## 오픈 질문
답을 아직 모르는 항목을 적는다. 추정으로 덮지 말고 REQ 초안 작성 이전에 확인할 질문으로 남긴다. 답을 아직 모르는 항목을 적는다. 추정으로 덮지 말고 REQ 초안 작성 이전에 확인할 질문으로 남긴다.
@@ -140,21 +147,22 @@
- 반영 위치: `# 3` 기대 상태, `# 5` FR-1·FR-4, `# 6` A-5. - 반영 위치: `# 3` 기대 상태, `# 5` FR-1·FR-4, `# 6` A-5.
- OQ-2: 신선도/캐시 정책 — universe 페치 주기와 현금/포지션 신선도. - OQ-2: 신선도/캐시 정책 — universe 페치 주기와 현금/포지션 신선도.
- 상태: Answered (2026-06-04) - 상태: Answered (2026-06-04)
- 결정: universe 멤버십·전략파라미터는 설정 시각(기본 07:30 KST, .env) 일일 스냅샷으로 고정. 현금/포지션은 스냅샷 baseline 에 **core 의 그날 in-flight 체결분을 반영해 보정**한다(옵션 C). 근거: 이 daemon 이 자기 portfolio 의 유일 매매 주체이고(A-6) 장중 입출금 등 거래 외 변동이 없으므로 baseline + 자기 체결 보정으로 정확한 현금/포지션을 portfolio-hub 추가 호출(매 요청 live) 없이 유지 가능. 매수 전략의 현금 조회 주기가 빠를 수 있어(분 단위) 매 요청 live 호출(옵션 B)의 가용성 결합·지연을 회피한다. - 결정(개정 2026-06-05, 옵션 C → 옵션 D): universe 멤버십·전략파라미터는 설정 시각(기본 07:30 KST, .env) 일일 스냅샷으로 고정. 현금/포지션은 스냅샷 baseline 에 더해 **체결 시 portfolio-hub ledger write 요청의 응답이 반환하는 변경 후 권위 cash/position 으로 갱신**한다(옵션 D). 근거: portfolio-hub 가 cash/position 의 ground truth 이므로 write 응답값으로 덮어쓰면 옵션 C 의 core 로컬 체결 replay 누적 보정에서 생길 수 있던 drift(R-5)가 발생하지 않는다. 갱신이 체결(write) 경로에서만 일어나므로 read 경로의 portfolio-hub 가용성 결합(옵션 B 의 매 요청 live 호출)도 피한다. A-6(유일 매매 주체·장중 외부 변동 없음) 전제는 유지. 의존(확정 2026-06-05, portfolio-hub#8): portfolio-hub 가 ledger write 응답에 `cash_balance_after` / `asset_quantity_after` 를 반환하고 ledger INSERT + 잔고 UPDATE 를 단일 트랜잭션으로 처리하기로 회신 — trex-core 는 write 응답값으로 스냅샷을 갱신한다. portfolio-hub 배포(dcy-006/REL-003) 시점까지 구현 대기. 체결→write flush 사이 즉시 갭의 중복 매수 배제는 slot overlay(FR-2)가 계속 담당.
- 반영 위치: `# 5` FR-6·NFR-2, `# 6` R-1·A-6. - 반영 위치: `# 4`, `# 5` FR-6·NFR-2·In Scope, `# 6` R-1·R-5·A-6·A-7.
- OQ-3: core 경유 정책 — 모든 소비자가 trex-core 를 경유하는가, 일부는 portfolio-hub 직접 호출을 허용하는가? core 경유의 부가가치(slot overlay)를 어디까지 의무화하는가? - OQ-3: core 경유 정책 — 모든 소비자가 trex-core 를 경유하는가, 일부는 portfolio-hub 직접 호출을 허용하는가?
- 상태: Open - 상태: Answered (2026-06-04)
- 처리 방안: 운영자 결정. 직접 호출 대비 indirection 비용과 slot 결합 가치 비교. - 결정: 매수 전략 모듈 등 **모든 소비자는 trex-core 스냅샷 API 를 경유**한다. portfolio-hub 직접 호출은 허용하지 않는다. core 가 단일 통합 경계(자격증명·계약·slot overlay·현금/포지션 보정·정합 검증)를 일관 제공하고, 소비자는 portfolio-hub 계약/자격증명을 알 필요가 없다.
- 종료 조건: 경유 정책이 범위 경계에 반영되면 Answered. - 반영 위치: `# 3` 제약사항, `# 6` R-4 완화.
- OQ-4: in-flight 합성 규칙 — portfolio-hub 포지션과 core 슬롯 상태(pending/holding)를 합쳐 후보를 만들 때, ledger outbox eventual consistency 로 인한 race 를 어떤 규칙으로 제거하는가? - OQ-4: in-flight 합성 규칙 — portfolio-hub 포지션과 core 슬롯 상태를 합쳐 응답을 만들 때의 포함/마스킹 규칙.
- 상태: Open - 상태: Answered (2026-06-05)
- 처리 방안: 설계 시 slot overlay 우선순위(core 슬롯이 portfolio-hub 보다 우선) 규칙 확정. - 결정: 응답(전략 실행 대상 portfolio 자산 리스트)은 **portfolio-hub 보유 자산을 기본 포함하고 holding 슬롯 자산도 포함**한다. 다음 상태만 **마스킹**(제외): (a) 슬롯 pending(미체결 in-flight), (b) warning(브로커 실제 position 불일치, FR-7), (c) 사용자 pause(FR-9). 우선순위: core 권위 슬롯 상태 + 정합 검증 + pause 가 portfolio-hub 보유 여부보다 우선(마스킹 조건 충족 시 보유 여부와 무관하게 제외). in-flight race(R-2)는 pending 마스킹으로 차단; 체결 완료분은 holding 으로 포함. (종전 'pending·holding 동시 제외' → 'holding 포함, pending·warning·pause 마스킹'으로 변경.)
- 종료 조건: 합성/제외 규칙이 FR-2 와 R-2 완화에 반영되면 Answered. - 반영 위치: `# 3` 시나리오, `# 5` FR-2·FR-9, `# 6` R-2, `# 7` S-2.
- 잔여: holding 자산의 보유 수량 표기를 core 권위 슬롯 vs 스냅샷 중 무엇 기준으로 할지(holding→ledger flush 갭의 수량 정합)는 설계 시 확정. 종전 FR-3(가용 슬롯 0 → 빈 리스트)은 'holding 포함' 변경과 충돌 → OQ-11 로 재정의.
- OQ-5: 가용성 degrade 정책 — portfolio-hub 불가 시 read 응답을 에러로 반환하는가, 캐시 fallback 하는가, 빈 응답을 반환하는가? - OQ-5: 가용성 degrade 정책 — portfolio-hub 불가 시 read 응답을 에러로 반환하는가, 캐시 fallback 하는가, 빈 응답을 반환하는가?
- 상태: Open - 상태: Open
- 처리 방안: 운영자 결정. 매수 전략의 안전 기본값(보수적 매수 중단) 우선 검토. - 처리 방안: 운영자 결정. 매수 전략의 안전 기본값(보수적 매수 중단) 우선 검토.
- 종료 조건: degrade 동작이 NFR-3/R-3 에 반영되면 Answered. - 종료 조건: degrade 동작이 NFR-3/R-3 에 반영되면 Answered.
- OQ-6: 빈 후보 표현 시맨틱 — 가용 슬롯이 0 일 때 빈 배열(`[]`)을 반환하는가, null 을 반환하는가, 별도 메타 필드(예: `available_slots: 0`)로 신호하는가? - OQ-6: 빈 리스트 표현 시맨틱 — 응답 리스트가 비었을 때 빈 배열(`[]`)을 반환하는가, null 을 반환하는가, 별도 메타 필드(예: `available_slots: 0`)로 신호하는가?
- 상태: Open - 상태: Open
- 처리 방안: 매수 전략 모듈 소비 계약에 맞춰 결정(REST 관례상 빈 배열 + 메타 필드 권장안 검토). - 처리 방안: 매수 전략 모듈 소비 계약에 맞춰 결정(REST 관례상 빈 배열 + 메타 필드 권장안 검토).
- 종료 조건: 표현 방식이 FR-3 에 반영되면 Answered. - 종료 조건: 표현 방식이 FR-3 에 반영되면 Answered.
@@ -166,6 +174,23 @@
- 상태: Open - 상태: Open
- 처리 방안: 설계 시 확정. BrokerPort 잔고조회 계약 + `warning` 상태 저장/회복 규칙 정의. - 처리 방안: 설계 시 확정. BrokerPort 잔고조회 계약 + `warning` 상태 저장/회복 규칙 정의.
- 종료 조건: 정합 기준·회복 규칙·포지션 출처가 FR-7 에 반영되면 Answered. - 종료 조건: 정합 기준·회복 규칙·포지션 출처가 FR-7 에 반영되면 Answered.
- OQ-9: 전략 ID 필터의 식별자/등록 시맨틱 — 전략 모듈이 넘기는 파라미터가 안정적 식별자(ID)인가 사람이 읽는 이름인가(사용자 표기 "이름?"), 그리고 "등록" 판정이 자산의 단일 `active_strategy_id` 일치인가 자산당 다중 전략 등록(여러 전략이 한 자산을 공유) 개념인가? portfolio-hub `strategy_params`/`active_strategy_id` 와의 매핑도 함께 확정한다.
- 상태: Open
- 처리 방안: 운영자/전략 모듈 계약 확인. 1차안은 안정적 `strategy_id` 식별자 + 단일 `active_strategy_id` 동등 비교. 다중 등록이 필요하면 등록 목록 필드 도입 검토.
- 종료 조건: 식별자 형태와 등록 판정 기준이 FR-8 에 반영되면 Answered.
- OQ-10: 런타임 pause/resume 메커니즘 — pause 단위(portfolio/asset/symbol), 제어 방식(전용 제어 API 엔드포인트 vs CLI vs config 리로드), 영속성(스냅샷 일일 갱신·daemon 재시작 후 pause 유지 여부와 저장소), 적용 범위(전역 vs 특정 전략 요청에만), resume 시 즉시 응답 복귀 규칙.
- 상태: Open
- 처리 방안: 운영자 결정. 1차안은 자산(=portfolio_id) 단위 pause + 제어 API + Postgres 영속(재시작·일일 갱신 후 유지) + 전역 적용. 사용 시나리오(운영 중 특정 종목만 일시 정지)에 맞춰 확정.
- 종료 조건: pause 단위·제어 방식·영속성·적용 범위가 FR-9 에 반영되면 Answered.
- OQ-11: capacity 소진 시 응답 시맨틱.
- 상태: Answered (2026-06-05) — 방향 확정, 파라미터 설계는 OQ-12 로 분리.
- 결정: capacity(가용 슬롯)가 모두 소진됐을 때의 응답은 **고정값이 아니라 전략 모듈이 요청 시 지정하는 view 모드(파라미터)에 따라 달라진다.** 최소 3개 모드: (a) **빈 리스트 `[]`** (신규 매수 후보가 목적인 전략 — 살 게 없음), (b) **슬롯 점유 자산만** (보유 포지션 관리·매도/조절 전략 — 자기가 들고 있는 것만), (c) **portfolio 스냅샷 − 슬롯 점유 자산** (점유 안 된 잔여 universe). 같은 view 모드 개념은 capacity 0 이 아닐 때도 응답 구성을 좌우한다(이 파라미터는 strategy_id 필터(FR-8)와 별개의 두 번째 요청 파라미터).
- 반영 위치: `# 5` FR-2·FR-3, `# 6` OQ-12.
- 잔여(→ OQ-12): 파라미터 이름/enum 값, 기본 모드(생략 시), 각 모드와 마스킹(pending·warning·pause, OQ-4)·strategy_id 필터의 합성 규칙, capacity 신호 메타 필드(OQ-6) 동반 여부.
- OQ-12: 응답 view/scope 파라미터 설계 — OQ-11 의 모드 선택을 어떤 요청 파라미터로 노출하는가.
- 상태: Open
- 처리 방안: 전략 모듈 소비 계약에 맞춰 결정. 정의 대상: (1) 파라미터 이름·enum(예: `view=buyable|held|remainder|all`), (2) 생략 시 기본 모드, (3) 각 모드 베이스 집합에 OQ-4 마스킹(pending·warning·pause)과 FR-8 strategy_id 필터를 어떻게 합성하는지(특히 'held' 모드에서 pending 포함 여부), (4) capacity 가용성 메타 필드(OQ-6) 동반 여부. 매수/매도/position 전략 구분(trex-core#1)과도 연계 — view 모드가 전략 타입별 뷰를 제공.
- 종료 조건: 파라미터 스펙·기본 모드·합성 규칙이 FR-2/FR-3/FR-8 에 반영되면 Answered.
### 오픈 질문 처리 기준 ### 오픈 질문 처리 기준
- `Open`: 아직 답이 없고 다음 단계 진행에 영향이 있는 상태다. 문서에 유지한다. - `Open`: 아직 답이 없고 다음 단계 진행에 영향이 있는 상태다. 문서에 유지한다.
@@ -179,22 +204,26 @@
# 7. 초기 성공 기준 # 7. 초기 성공 기준
이 변경이 성공했다고 판단할 기준을 측정 가능하게 정의한다. 가능하면 데이터 출처까지 연결한다. 이 변경이 성공했다고 판단할 기준을 측정 가능하게 정의한다. 가능하면 데이터 출처까지 연결한다.
- S-1: stub portfolio-hub read 로, 스냅샷 API 가 portfolio-hub 종목을 포함한 매수 후보 리스트를 반환한다. - S-1: stub portfolio-hub read 로, 스냅샷 API 가 portfolio-hub 종목을 포함한 전략 실행 대상 자산 리스트를 반환한다.
- S-2: pending/holding 슬롯이 점유한 종목이 후보 리스트에서 제외된다(slot overlay). - S-2: holding 슬롯 자산은 응답에 포함되고, pending 슬롯 자산은 마스킹되어 제외된다(slot overlay).
- S-3: 가용 슬롯이 0 일 때 스냅샷 API 가 빈 후보 리스트를 반환한다. - S-3: (재정의 대기 — OQ-11) 가용 슬롯 0 일 때의 응답 시맨틱(holding 포함 여부·신규 매수 불가 신호)이 확정된 대로 동작한다.
- S-4: 도메인 import 스윕에서 portfolio-hub/네트워크 의존이 0 으로 유지된다. - S-4: 도메인 import 스윕에서 portfolio-hub/네트워크 의존이 0 으로 유지된다.
- S-5: interface-contract/runtime-flows/project-structure 가 read 레이어와 정합한다. - S-5: interface-contract/runtime-flows/project-structure 가 read 레이어와 정합한다.
- S-6: 스냅샷 페치 시 portfolio-hub 포지션과 브로커 계좌 포지션이 불일치하는 자산이 `warning` 으로 표시되고 매수 후보에서 제외된다. - S-6: 스냅샷 페치 시 portfolio-hub 포지션과 브로커 계좌 포지션이 불일치하는 자산이 `warning` 으로 표시되고 응답 리스트에서 마스킹된다.
- S-7: `strategy_id` 를 넣어 호출하면 해당 전략 ID 가 등록된 자산만, 생략하면 전체 자산이 반환된다.
- S-8: 자산을 런타임에 pause 하면 응답 리스트에서 마스킹되고, resume 하면 다시 포함된다.
## 측정 방식 및 데이터 출처 ## 측정 방식 및 데이터 출처
각 성공 기준을 어떻게 측정할지와 어떤 데이터나 문서를 근거로 쓸지 적는다. 각 성공 기준을 어떻게 측정할지와 어떤 데이터나 문서를 근거로 쓸지 적는다.
- S-1 측정 방식: stub read 통합테스트(스냅샷 응답) / 데이터 출처: 테스트 결과, 응답 본문. - S-1 측정 방식: stub read 통합테스트(스냅샷 응답) / 데이터 출처: 테스트 결과, 응답 본문.
- S-2 측정 방식: 슬롯 점유 후 스냅샷 호출 테스트 / 데이터 출처: 후보 리스트 vs 슬롯 점유 종목. - S-2 측정 방식: 슬롯 holding/pending 진입 후 스냅샷 호출 테스트 / 데이터 출처: 응답 리스트(holding 포함·pending 마스킹) vs 슬롯 상태.
- S-3 측정 방식: 슬롯 소진(capacity 0) 후 스냅샷 호출 테스트 / 데이터 출처: 후보 리스트 길이. - S-3 측정 방식: 슬롯 소진(capacity 0) 후 스냅샷 호출 테스트 / 데이터 출처: 응답 리스트·capacity 신호 메타(OQ-11 확정안 기준).
- S-4 측정 방식: 정적 import 스윕 / 데이터 출처: `trex_core/domain` import 목록. - S-4 측정 방식: 정적 import 스윕 / 데이터 출처: `trex_core/domain` import 목록.
- S-5 측정 방식: 문서-코드 정합 점검(리뷰) / 데이터 출처: baseline 문서 vs 실제 엔드포인트/포트. - S-5 측정 방식: 문서-코드 정합 점검(리뷰) / 데이터 출처: baseline 문서 vs 실제 엔드포인트/포트.
- S-6 측정 방식: 포지션 불일치 주입 후 스냅샷 페치 테스트 / 데이터 출처: 자산 `warning` 상태·후보 리스트. - S-6 측정 방식: 포지션 불일치 주입 후 스냅샷 페치 테스트 / 데이터 출처: 자산 `warning` 상태·응답 리스트.
- S-7 측정 방식: `strategy_id` 유/무 호출 비교 테스트 / 데이터 출처: 응답 리스트 vs 자산 `active_strategy_id`.
- S-8 측정 방식: pause→호출→resume→호출 시퀀스 테스트 / 데이터 출처: 응답 리스트 포함/제외 여부.
# 8. REQ로 넘기기 전 확인 체크 # 8. REQ로 넘기기 전 확인 체크
REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 확인한다. 미완료 항목은 후속 결정이 필요함을 뜻한다. REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 확인한다. 미완료 항목은 후속 결정이 필요함을 뜻한다.
@@ -206,7 +235,7 @@ REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지
- [ ] High 영향 리스크 우선순위 및 해소 책임자 지정 - [ ] High 영향 리스크 우선순위 및 해소 책임자 지정
- 기준: 본 Discovery 의 리스크는 현재 전부 Medium/Low. 신선도(R-1)·race(R-2)·가용성(R-3) 처리 방침을 운영자가 확인. - 기준: 본 Discovery 의 리스크는 현재 전부 Medium/Low. 신선도(R-1)·race(R-2)·가용성(R-3) 처리 방침을 운영자가 확인.
- [ ] 모든 오픈 질문(OQ) `Open` 상태 없음 확인 - [ ] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
- 기준: OQ-1~OQ-8 이 전부 Answered/Deferred 처리(현재 OQ-1·OQ-2 Answered, OQ-3~OQ-8 Open). - 기준: OQ-1~OQ-12 이 전부 Answered/Deferred 처리(현재 OQ-1·OQ-2·OQ-3·OQ-4·OQ-11 Answered, OQ-5~OQ-10·OQ-12 Open).
- [ ] Freeze 확정 담당자(CONFIRMED_BY) 지정 - [ ] Freeze 확정 담당자(CONFIRMED_BY) 지정
- 기준: 운영자가 confirm-discovery 시점에 CONFIRMED_BY 를 지정한다. - 기준: 운영자가 confirm-discovery 시점에 CONFIRMED_BY 를 지정한다.
@@ -225,17 +254,23 @@ REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지
## DECIDE ## DECIDE
- ~~스냅샷 응답 단위/필드~~ → 결정됨: daemon 관리 portfolio universe(symbol/asset_id/현금/포지션/active_strategy_id/전략파라미터 opaque JSON), 가격 제외 (OQ-1, 2026-06-04) - ~~스냅샷 응답 단위/필드~~ → 결정됨: daemon 관리 portfolio universe(symbol/asset_id/현금/포지션/active_strategy_id/전략파라미터 opaque JSON), 가격 제외 (OQ-1, 2026-06-04)
- ~~신선도 정책~~ → 결정됨: universe·전략파라미터 설정 시각 일일 캐시 + 현금/포지션 baseline+core in-flight 보정(옵션 C) (OQ-2, 2026-06-04) - ~~신선도 정책~~ → 결정됨: universe·전략파라미터 설정 시각 일일 캐시 + 현금/포지션 baseline + 체결 시 portfolio-hub write 응답의 권위 cash/position 갱신(옵션 D) (OQ-2, 2026-06-04 / 옵션 C→D 개정 2026-06-05)
- {{DECIDE_DAEMON_ID_INJECTION}} — trex-core daemon 의 daemon_id 주입(.env vs 인증 토큰) (OQ-7) - {{DECIDE_DAEMON_ID_INJECTION}} — trex-core daemon 의 daemon_id 주입(.env vs 인증 토큰) (OQ-7)
- {{DECIDE_EMPTY_SEMANTICS}} — 가용 슬롯 0 시 빈 배열 vs null vs 메타 필드 표현 (OQ-6) - {{DECIDE_EMPTY_SEMANTICS}} — 가용 슬롯 0 시 빈 배열 vs null vs 메타 필드 표현 (OQ-6)
- ~~capacity 0 시 응답 재정의~~ → 결정됨: 응답 형태를 전략 모듈 요청 view 모드 파라미터로 선택(빈 `[]` / 슬롯 점유 자산만 / portfolio−점유) (OQ-11, 2026-06-05)
- {{DECIDE_VIEW_PARAM}} — 응답 view/scope 파라미터 설계: 이름·enum·기본 모드·마스킹/strategy_id 합성·capacity 메타 (OQ-12)
- {{DECIDE_STRATEGY_ID_SEMANTICS}} — 전략 ID 파라미터의 식별자 형태(ID vs 이름)와 등록 판정(단일 active vs 다중 등록) (OQ-9)
- {{DECIDE_PAUSE_MECHANISM}} — pause/resume 단위·제어 방식(API/CLI/config)·영속성·적용 범위 (OQ-10)
## CONFIRM ## CONFIRM
- {{CONFIRM_READ_SCOPE}} — read 슬라이스 범위 경계(전략 로직·reconciliation 제외) 확정 - {{CONFIRM_READ_SCOPE}} — read 슬라이스 범위 경계(전략 로직·reconciliation 제외) 확정
- {{CONFIRM_CORE_PROXY_POLICY}} — 모든 소비자 core 경유 의무화 vs 일부 직접 호출 허용 (OQ-3) - ~~core 경유 정책~~ → 결정됨: 모든 소비자 trex-core 경유, portfolio-hub 직접 호출 금지 (OQ-3, 2026-06-04)
- {{CONFIRM_INFLIGHT_OVERLAY}} — portfolio-hub 포지션 + core 슬롯 합성/제외 규칙, 가용성 degrade 정책 (OQ-4/OQ-5) - ~~in-flight 합성/제외 규칙~~ → 결정됨: holding 포함, pending·warning·pause 마스킹, core 권위 우선 (OQ-4, 2026-06-05)
- {{CONFIRM_AVAILABILITY_DEGRADE}} — portfolio-hub 불가 시 가용성 degrade 정책 (OQ-5)
## DATA ## DATA
- {{DATA_PORTFOLIO_HUB_READ_CONTRACT}} — portfolio-hub read API 계약(`managed_by_daemon_id` 로 daemon universe 조회) 및 응답 필드(cash_balance/asset_quantity/strategy_params JSONB) 확인 - {{DATA_PORTFOLIO_HUB_READ_CONTRACT}} — portfolio-hub read API 계약(`managed_by_daemon_id` 로 daemon universe 조회) 및 응답 필드(cash_balance/asset_quantity/strategy_params JSONB) 확인
- ~~portfolio-hub write 응답 계약 확장~~ → 확정됨(2026-06-05, portfolio-hub#8): `POST /v1/portfolios/{portfolio_id}/ledger/trades` 응답 root 에 flat 필드 `cash_balance_after`, `asset_quantity_after` 추가 + ledger INSERT/잔고 UPDATE 단일 트랜잭션(회계 단일 권위). status 별 `*_after` 의미 정의(new=반영 후, duplicate=현재 권위, 409/422=미정). portfolio-hub 측은 dcy-006/REL-003 으로 배포 예정 — 배포 시점까지 trex-core 구현 대기. 추적: https://github.com/fourblocks2020/portfolio-hub/issues/8 (옵션 D 의존, A-7)
# 11. Discovery Freeze # 11. Discovery Freeze
REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다. 내용 재작성 없이 원본 섹션을 링크로 대신한다. REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다. 내용 재작성 없이 원본 섹션을 링크로 대신한다.
@@ -249,7 +284,7 @@ REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록
## Freeze 플래그 ## Freeze 플래그
- Handoff Decision: 보류 - Handoff Decision: 보류
- Handoff Rationale: OQ-1(스냅샷 정의)·OQ-2(신선도, 옵션 C) Answered. OQ-3~OQ-8(core 경유 정책·in-flight 합성·가용성 degrade·빈 후보 시맨틱·daemon_id 주입·포지션 정합 세부)이 미확정이며, confirm-discovery 에서 운영자 결정 후 REQ Drafting 으로 인계한다. - Handoff Rationale: OQ-1(스냅샷 정의)·OQ-2(신선도, 옵션 D — portfolio-hub#8 write 응답 `*_after` 확정)·OQ-3(core 경유 정책)·OQ-4(in-flight 합성 — holding 포함, pending·warning·pause 마스킹)·OQ-11(capacity 0 응답 — view 모드 파라미터) Answered. OQ-5~OQ-10·OQ-12(가용성 degrade·빈 표현 시맨틱·daemon_id 주입·포지션 정합 세부·전략 ID 필터 시맨틱·런타임 pause/resume·응답 view 파라미터 설계)이 미확정이며, confirm-discovery 에서 운영자 결정 후 REQ Drafting 으로 인계한다.
- Ready for REQ Drafting: false - Ready for REQ Drafting: false
- Confirmed By: TBD - Confirmed By: TBD
- Confirmed At (KST): TBD - Confirmed At (KST): TBD
@@ -0,0 +1,279 @@
# 0. 문서 상태
이 문서의 현재 상태와 식별 정보를 기록한다. 문서 추적과 승인 흐름에 필요한 최소 메타데이터를 적는다.
- 상태: confirmed
- 문서 ID: dcy-006_20260605_strategy-module-rabbitmq-transport
- 이슈명: trex-core ↔ 전략 모듈 통신 transport — RabbitMQ 도입 (스냅샷 read + intent 제출 + 이벤트)
- 작성 시각(KST): 2026-06-05 21:23
- 마지막 갱신 시각(KST): 2026-06-05 22:30
- 대체됨: 없음
- 후속 Discovery 참조: 없음
- 생성된 REQ 참조: 없음
# 1. 계획 상태 요약
현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다.
- 연계 문서 현황:
- 관련 점수 파일: 미생성
- 관련 TODO/메모: dcy-005(portfolio-hub 종목 스냅샷 read API — FR-1/A-3 가 HTTP pull 가정), dcy-002(slot-core 데몬 + HTTP intent API), dcy-003(주문 집행 + durable outbox/idempotency), trex-core#1(매수/매도/position 전략 taxonomy)
- 해석:
- dcy-002(intent API)와 dcy-005(스냅샷 read)는 전략 모듈과의 통신을 **HTTP API**로 가정했는데, 사용자가 전략 모듈 통신을 API endpoint 대신 **RabbitMQ(AMQP)** 로 하고자 한다. transport 선택은 스냅샷 read·intent 제출·이벤트 전반에 걸친 cross-cutting 통합 아키텍처 결정이라, read 슬라이스(dcy-005) 안에 두지 않고 **독립 Discovery로 분리**한다.
- 핵심 분리 원칙: logical 계약("무엇을 주고받나" — 스냅샷 필드/마스킹/view 모드/strategy_id 필터, intent 스키마)은 dcy-005/002/003 소관으로 **transport-무관**하게 유지하고, 본 Discovery 는 "어떻게 전달하나"(전달 방식·메시지 매핑·delivery 보증·운영)만 다룬다.
# 2. 요구사항 판정 결과
이번 요청이 기존 요구사항과 비교해 신규인지, 유사한지, 중복인지 판단하고 그 근거를 남긴다.
- 판정: 신규
- 근거
- transport/messaging/RabbitMQ 를 다루는 기존 Discovery 가 없다(dcy-001~005 탐색 결과, repo 내 amqp/rabbitmq/pika 구현 0건).
- 가장 가까운 dcy-002 는 HTTP API "수립"이 주제이지 transport "선택"이 아니며, MQ 전환은 dcy-002/005 의 미세 조정이 아니라 통신 모델 자체를 바꾸는 새 통합 아키텍처 문제다.
# 3. 문제점의 요약
문제의 현재 상태, 기대 상태, 관련 이해관계자, 제약사항을 정리한다. Discovery의 핵심 맥락을 가장 먼저 이해할 수 있어야 한다.
- 현재 상태
- trex-core daemon 은 FastAPI/uvicorn 기반 **HTTP `/v1` API**(`trex_core/adapters/api/app.py`)로 전략/소비자와 통신한다(`/v1/intents/buy|sell`, `/v1/slots`, `/v1/capacity`; dcy-005 스냅샷 read 는 draft). messaging/AMQP 인프라는 없다(repo grep: rabbitmq/amqp/pika 0건).
- baseline 문서가 HTTP 를 명시적 transport 로 기술한다 — `interface-contract.md`: "HTTP `/v1` API :: 슬롯 코어 데몬이 노출, 매수·매도 전략 데몬이 호출". `runtime-flows.md` 도 HTTP request/response 흐름 기준.
- 전략 모듈은 외부 소비자로서 HTTP pull 로 스냅샷을 받고 HTTP POST 로 intent 를 제출한다고 가정한다(dcy-005 A-3, dcy-002).
- adapters 레이어는 `api / broker / ledger / persistence` 로 구성되며 messaging 어댑터는 없다.
- 기대 상태
- trex-core ↔ 전략 모듈 통신을 RabbitMQ(AMQP) 메시지 기반으로 제공한다. 스냅샷 요청과 intent admission 은 request-reply(reply-to/correlation-id), 체결/슬롯 상태 변화는 이벤트(pub/sub)로 전달한다(intent 체결 결과는 이벤트로, OQ-2).
- view 모드(dcy-005 OQ-11)·strategy_id(dcy-005 FR-8) 같은 요청 속성이 메시지 속성/routing key 로 매핑된다.
- logical 계약은 불변이고 transport 만 교체된다. HTTP `/v1` API 는 RabbitMQ 로 완전 대체되어 **HTTP 표면이 0 이 된다**(병행 없음, OQ-1; healthz 포함 제거, OQ-7). liveness 는 프로세스 감독이 담당.
- 이해관계자
- 문서 작성자/구현자(AI agent)
- 리뷰어/승인자(운영자, genichin@gmail.com)
- 전략 모듈(trex-core 외부 소비자) 및 trex-core daemon
- 운영자(RabbitMQ 인프라 도입·운영)
- 제약사항
- 도메인 순수성 — `trex_core.domain` 은 transport/AMQP 의존 0(어댑터 격리).
- logical 계약 보존 — 스냅샷/intent 의 의미·필드·마스킹·view 모드·스키마는 transport 와 독립이며 본 Discovery 가 바꾸지 않는다.
- core 권위 불변식 유지 — 슬롯/capacity 단일 writer(불변식 A) 는 transport 교체와 무관하게 보존된다.
## 주요 사용자 시나리오
사용자 또는 운영자가 실제로 어떤 상황에서 이 변경을 필요로 하는지 대표 시나리오를 적는다.
- 전략 모듈이 portfolio 스냅샷을 요청 → trex-core 가 RabbitMQ reply 큐로 전략 실행 대상 자산 리스트를 응답한다(HTTP GET 대체).
- 전략 모듈이 매수/매도(향후 target) intent 를 메시지로 발행 → trex-core 가 소비해 슬롯 전이를 수행한다.
- 체결/슬롯 상태가 바뀌면 → trex-core 가 이벤트를 발행하고 전략 모듈이 구독해 즉시 반영한다(HTTP polling 불요).
- RabbitMQ 가 일시 불가 → degrade 정책(버퍼링 금지·신규 매매 보수적 중단, OQ-4)에 따라 신규 전략 매매는 멈추고, 열린 포지션은 core 자율 안전 매도가 보호한다(미구현, A-5).
# 4. 이번에 정의할 변경
이번 Discovery에서 다루는 핵심 변경 항목을 목록으로 적는다. 기능, 구조, 규칙 변경을 함께 포함해도 된다.
- transport 추상화 + RabbitMQ 어댑터(`pika` 동기): trex-core ↔ 전략 통신을 메시지 기반으로 제공. 스냅샷·intent admission = request-reply, 이벤트 = pub/sub 패턴 정의. 어댑터는 `adapters/messaging`(신규) 레이어로 격리하고 결정론 stub 제공. 런타임은 uvicorn 제거 후 sync+thread daemon 재구성(OQ-5).
- 메시지 계약 매핑: 기존 logical 계약(스냅샷 필드/마스킹/view 모드/strategy_id, intent 스키마)을 메시지 payload/속성/routing key 로 1:1 의미 보존 매핑.
- delivery 신뢰성: ack/재처리, idempotency(dcy-003 의 intent correlation_id 재사용), dead-letter, 내구성 큐. 순서는 intent 단일 consumer FIFO 로 보존하고 이벤트는 순서 보증 불요(상태 운반·last-wins, OQ-3).
- HTTP 완전 제거: HTTP `/v1` API 를 RabbitMQ 로 대체(병행 없음, OQ-1)하고 **`healthz` 포함 모든 HTTP 라우트와 `adapters/api` HTTP 어댑터를 제거**(HTTP 표면 0, OQ-7). dcy-005 FR-1/A-3·dcy-002 의 HTTP 가정을 갱신. liveness 는 프로세스 감독, 앱 헬스/관측성은 로그·메트릭.
- baseline 갱신: `interface-contract.md`(메시지 계약)·`runtime-flows.md`(MQ 흐름)·`project-structure.md`(messaging 어댑터) 갱신.
## 영향 범위
변경이 미칠 코드/문서/운영 범위를 명시해 REQ 정제와 batch planning에서 추정 누락을 줄인다.
- 코드 영향:
- 신규: `trex_core/adapters/messaging` RabbitMQ(`pika`) 어댑터 + stub, 스레드별 connection/channel 관리, 직렬화, request-reply correlation(reply-to/correlation-id), intent RPC consumer 스레드, snapshot RPC consumer 스레드, 이벤트 publisher, 재연결/백오프, `config` 에 `TREX_RABBITMQ_URL`(.env) 주입(기존 `TREX_*` 패턴, OQ-6). uvicorn 제거로 `TREX_API_HOST`/`TREX_API_PORT` 폐기.
- 기존: **`adapters/api` HTTP 어댑터·uvicorn 제거**, daemon entrypoint 를 sync+thread 런타임으로 교체(OQ-5), application 레이어의 intent/스냅샷 진입점을 transport-무관 포트로 재배선(직접 변경 최소화).
- 문서 영향:
- interface-contract/runtime-flows/project-structure 갱신, dcy-005 FR-1/A-3·dcy-002 HTTP 가정 완화 cross-reference.
- 운영 영향:
- RabbitMQ 브로커 인프라 추가(현 스택의 redis/timescale 처럼 docker 추가 가능). exchange/queue 토폴로지, 모니터링, 자격증명/접속 운영.
# 5. 요구사항 목록
실제로 구현과 검증으로 이어질 요구사항을 기능/비기능으로 나누어 정리하고, 범위 경계를 명확히 적는다.
## 기능 요구사항
- FR-1: trex-core ↔ 전략 모듈 통신을 RabbitMQ(AMQP) 메시지 기반으로 제공한다(토폴로지 OQ-2). 스냅샷 요청과 **intent admission 은 request-reply**(`trex.snapshot.req`/`trex.intent.req` + RabbitMQ direct reply-to + `correlation_id`), 체결/슬롯 상태 변화는 **이벤트(topic exchange `trex.events`, pub/sub)** 로 전달한다. intent 의 체결 결과는 admission 응답이 아니라 이벤트로 전달해 현 2단계 시맨틱(admission 동기 + fill 비동기)을 보존한다.
- FR-2: 기존 logical 계약(스냅샷 필드/마스킹/view 모드(dcy-005 OQ-11)/strategy_id 필터(dcy-005 FR-8), intent 스키마(dcy-002/003))을 transport-무관으로 보존하고, 메시지 payload/속성/routing key 로 의미 손실 없이 매핑한다. 매핑 규칙(OQ-2): **요청(스냅샷·intent)의 view 모드·strategy_id 는 payload, 이벤트의 strategy_id·symbol 은 routing key**(`{event_type}.{strategy_id}.{symbol}`)로 싣는다.
- FR-3: messaging 접근을 transport 포트 추상화 + RabbitMQ 어댑터(`pika` 동기 클라이언트) + 결정론 stub 로 제공한다. 런타임은 uvicorn 제거 후 순수 sync+thread daemon(consumer/execution/ledger 스레드)으로 재구성한다(OQ-5). `trex_core/domain` 은 transport/AMQP 의존 0 을 유지한다.
- FR-4: delivery 신뢰성 — at-least-once + idempotency 로 effectively-once 를 제공한다(OQ-3). intent 는 슬롯 durable commit 후 ack + `correlation_id`(dcy-003 멱등키) dedup, durable/persistent + 단일 consumer FIFO. 이벤트는 durable exchange + per-strategy durable queue(catch-up) + 현재 상태·단조 시퀀스 운반(last-wins, 순서 보증 불요) + TTL/max-length. 발행은 publisher confirms, 실패는 dead-letter. 중복 전달에도 슬롯 단일 writer 불변식을 유지하고, 미수신/중복은 스냅샷 RPC 로 reconcile 한다.
- FR-5: HTTP `/v1` API 를 RabbitMQ 로 **완전 대체**한다(병행 없음, OQ-1; HTTP 표면 0, OQ-7). 전략-facing 통신(`intents/buy|sell`·`intents/{id}`·`slots`·`capacity`·dcy-005 스냅샷 read)을 메시지 기반으로 이전하고, **`healthz` 포함 모든 HTTP 라우트와 HTTP 어댑터를 제거**한다. liveness 는 프로세스 감독이 담당(앱 헬스/관측성은 로그·메트릭). dcy-005 FR-1/A-3·dcy-002 의 HTTP 가정을 갱신한다.
- FR-6: baseline 문서(`interface-contract.md`/`runtime-flows.md`/`project-structure.md`)를 메시지 계약·MQ 흐름·messaging 어댑터에 맞게 갱신한다.
## 비기능 요구사항
성능, 운영성, 일관성, 자동화 안정성 등 기능 외 요구사항을 적는다.
- NFR-1: 도메인 순수성 — transport/AMQP 의존은 adapters 레이어에 격리하고 `trex_core/domain` import 0(import 스윕).
- NFR-2: 신뢰성/일관성 — at-least-once 전달 + idempotency 로 effectively-once 효과를 보장한다(OQ-3, 진짜 exactly-once 는 미추구). 슬롯/capacity 권위 불변식을 보존하고, 이벤트는 advisory(push 최적화)이며 스냅샷 RPC 가 권위 reconcile 경로다.
- NFR-3: 가용성 degrade — RabbitMQ 불가 시 **버퍼링 금지 + 신규 매매 보수적 중단**(OQ-4). 단 **core 자율 안전 매도(손절·시간 만기)는 MQ·전략과 독립으로 계속**해 열린 포지션을 보호한다(이 capability 는 별도 core 기능, 현재 미구현 — A-5). dcy-005 OQ-5 와 공통 '보수적 안전' 철학을 따른다.
## 범위 경계
이번 반복에서 반드시 포함할 범위와 명시적으로 제외할 범위를 구분한다.
- In Scope
- transport 포트 + RabbitMQ 어댑터 + stub, 스냅샷·intent admission request-reply, 이벤트 pub/sub, 메시지 계약 매핑, delivery 신뢰성, HTTP 완전 제거, baseline 갱신.
- Out of Scope (다른 Discovery 소관)
- 스냅샷/intent 의 logical 계약 자체 변경(dcy-005/002/003), 전략 의사결정 로직(전략 모듈 책임, trex-core#1), RabbitMQ 클러스터/HA 운영 세부, portfolio-hub 등 다른 외부 연동의 transport.
# 6. 리스크/가정 목록
불확실성과 전제 조건을 분리해서 적는다. 리스크는 영향과 완화 방안을, 가정은 현재 판단의 전제를 기록한다.
## 리스크
- R-1 (Low, 완화됨): 이중 transport 복잡도 — OQ-1 에서 HTTP 병행 없이 **대체**로 결정되어 계약 표류·중복 유지 비용은 대부분 해소. 단 전환기 동안 HTTP 어댑터 제거 시점과 전략 모듈 MQ 전환의 동기화가 필요(big-bang 마이그레이션 리스크는 R-5 로 분리).
- 완화 방안: 단일 logical 계약을 transport-무관으로 정의(FR-2/FR-3), HTTP 어댑터 제거를 전략 모듈 전환과 함께 단계적 계획.
- R-2 (Medium): delivery 의미 불일치 — at-least-once 재전달이 intent 를 중복 실행할 수 있다.
- 완화 방안(OQ-3 Answered): dcy-003 correlation_id idempotency dedup + 슬롯 durable commit 후 ack + dead-letter(FR-4), 슬롯 단일 writer 불변식(NFR-2). 미수신/중복 이벤트는 스냅샷 RPC reconcile.
- R-3 (Medium→High, 대체 결정으로 상향): 가용성 결합 — RabbitMQ 장애가 전략 통신을 전면 차단한다. HTTP 병행을 두지 않기로(OQ-1) 했으므로 **HTTP fallback 이 없어 단일 장애점 성격이 강해진다.**
- 완화 방안(OQ-4 Answered): 버퍼링 금지 + 신규 매매 보수적 중단. **열린 포지션은 core 자율 안전 매도(손절·시간 만기)가 MQ·전략과 독립으로 보호** → SPOF 영향이 '신규 매매 중단'으로 한정되고 포지션 방치 위험은 제거된다. 단 이 보호는 아직 미구현 safety 기능에 의존(A-5, 추후 구현). RabbitMQ durable/HA 운영도 고려.
- R-4 (Low): request-reply over MQ 복잡도 — 동기 HTTP pull 보다 reply 상관·타임아웃 처리가 필요하다.
- 완화 방안: correlation-id + reply-to 규약(OQ-2). RPC per-request 타임아웃·재요청 규약은 설계 시 표준화(전략 클라이언트 측).
- R-5 (Medium): big-bang transport 마이그레이션 — HTTP 제거와 전략 모듈 MQ 전환이 어긋나면 통신 단절.
- 완화 방안: 전환 순서/롤백 계획 수립, stub 기반 사전 검증(FR-3), 전환 중 관측성은 로그·메트릭으로 확보(OQ-7 에서 HTTP healthz 제거 결정 — probe 대체 안 함).
## 가정
아직 확정되지 않았지만 현재 문서 작성의 기반이 되는 전제를 적는다.
- A-1: 전략 모듈은 RabbitMQ 클라이언트로 메시지를 발행/구독할 수 있는 외부 소비자다.
- A-2: trex-core 는 슬롯/capacity 권위 단일 소스이며 transport 교체와 무관하게 그 불변식을 유지한다.
- A-3: 스냅샷/intent 의 logical 계약(필드/마스킹/view 모드/strategy_id/스키마)은 dcy-005/002/003 에서 정의되고, 본 Discovery 는 전달 방식만 다룬다.
- A-4: RabbitMQ 브로커는 운영 환경에 도입 가능하다(현 스택에 redis/timescale 처럼 docker 로 추가 가능).
- A-5: trex-core 는 transport·전략과 독립으로 동작하는 **기본 안전 매도(손절 stop-loss, 시간 만기 forced_close) capability 를 갖는다(또는 추후 구현)**. degrade 시(RabbitMQ 불가·전략 슬롯 방치) 열린 포지션 보호의 전제다(OQ-4). 현재 미구현(`forced_close_at` 필드만 존재, stop-loss 없음)이며 구현은 별도 작업으로 추적(transport 범위 밖 core 매매 안전 기능).
## 오픈 질문
답을 아직 모르는 항목을 적는다. 추정으로 덮지 말고 REQ 초안 작성 이전에 확인할 질문으로 남긴다.
- OQ-1: HTTP 대체 vs 병행 — RabbitMQ 가 HTTP `/v1` API 를 완전 대체하는가, 둘을 병행(이중 transport)하는가?
- 상태: Answered (2026-06-05)
- 결정: **RabbitMQ 가 HTTP `/v1` API 를 대체한다.** 이중 transport 병행은 채택하지 않는다. 전략-facing 통신(`intents/buy|sell`, `intents/{id}`, `slots`, `capacity`, dcy-005 스냅샷 read)은 전부 메시지 기반(command / request-reply / event)으로 옮긴다.
- 반영 위치: `# 3` 기대 상태, `# 4` 변경 항목, `# 5` FR-5, `# 6` R-1·R-3.
- 잔여(해소): 운영 probe/admin 표면의 '대체' 범위는 OQ-7 에서 Answered(앱 레벨 헬스 표면 제거, HTTP 표면 0).
- OQ-2: 통신 패턴별 토폴로지 — exchange/queue/routing key + reply 상관 + view 모드/strategy_id 매핑.
- 상태: Answered (2026-06-05)
- 결정: 3 패턴 토폴로지 확정.
- ① 스냅샷 RPC(request-reply): 요청 queue `trex.snapshot.req`(direct/default exchange) → trex-core 소비, 응답은 RabbitMQ direct reply-to(`amq.rabbitmq.reply-to`) + `correlation_id`. 요청 payload 에 `strategy_id`·`view` 모드.
- ② intent admission RPC(request-reply): 요청 queue `trex.intent.req` → trex-core 소비, 응답 = admission(`status`, `slot_id`, `reason`). `correlation_id` = intent 멱등키(dcy-003 `{asset_id}:{order_id}:{execution_id}`)로 RPC 상관 + dedup 일원화. **체결/종료(fill/close) 결과는 ② 응답이 아니라 ③ 이벤트로** 전달 — 현 2단계 시맨틱(admission 동기 + fill 비동기) 무손실 보존.
- ③ 이벤트 pub/sub: trex-core 가 체결·슬롯 상태 변화를 topic exchange `trex.events` 에 발행, routing key = `{event_type}.{strategy_id}.{symbol}`. 전략은 자기 queue 를 `*.{strategy_id}.#` 패턴으로 바인딩해 자기 자산 이벤트만 수신(broker단 필터).
- 매핑 원칙: view 모드·strategy_id 는 **요청(①②)에서는 payload, 이벤트(③)에서는 routing key**(요청은 라우팅 대상이 아니고 이벤트는 대상).
- 반영 위치: `# 5` FR-1·FR-2·FR-4.
- 잔여(→ 타 OQ): 이벤트 queue durability(전략 down 중 유실)·at-least-once/exactly-once → OQ-3; RPC reply 대기 클라이언트(pika/aio-pika) → OQ-5; RPC 타임아웃/degrade → OQ-4.
- OQ-3: delivery 보증 수준 — at-least-once + idempotency 로 충분한가, 추가 ordering/exactly-once 가 필요한가?
- 상태: Answered (2026-06-05, 위임 결정)
- 결정: **at-least-once + idempotency = effectively-once** 를 기준으로 한다. 분산 메시징에서 진짜 exactly-once 는 불가하므로 추구하지 않고 멱등 소비로 '사실상 한 번' 효과를 낸다. 패턴별:
- ① 스냅샷 RPC: read-only → 멱등. at-least-once, 별도 dedup 불요. 요청/응답은 transient(클라이언트 timeout 후 재요청). manual ack.
- ② intent admission RPC: at-least-once + `correlation_id`(= dcy-003 멱등키) dedup. 슬롯 전이를 Postgres 에 durable commit 한 뒤 ack → crash 로 재전달돼도 idempotency 가 중복 실행을 차단. intent 요청 queue·메시지는 durable/persistent, **단일 consumer FIFO** 로 순서 보존(trex-core 단일 writer 불변식 A 와 정합). 전역 total order 는 불요.
- ③ 이벤트 pub/sub: at-least-once + 멱등 소비. durable exchange + **per-strategy durable queue 로 전략 down 중 이벤트 catch-up**. 이벤트는 delta 가 아니라 **현재 상태 + 단조 증가 시퀀스(슬롯 version/timestamp)** 를 실어 last-wins 적용 → **순서 보증 불요**. 미수신/중복은 **스냅샷 RPC(권위 reconcile 경로)** 로 항상 복구(이벤트는 push 최적화이지 단일 진실원이 아님).
- 공통: trex-core 발행은 publisher confirms 로 broker 수락 확인. per-strategy 이벤트 queue 는 무한 적체 방지를 위해 TTL/max-length(초과분 drop-oldest, 스냅샷 reconcile 로 회복) 적용.
- 반영 위치: `# 5` FR-4·NFR-2, `# 6` R-2.
- 근거: 멱등키(dcy-003)와 스냅샷 권위 reconcile 이 이미 있어 exactly-once 인프라 비용 없이 안전. 순서는 단일 consumer FIFO + 상태 운반 이벤트로 충분.
- OQ-4: 가용성 degrade 정책 — RabbitMQ 불가 시 행동 규약.
- 상태: Answered (2026-06-05)
- 결정:
1. **버퍼링 금지** — broker 불가 시 intent 를 로컬에 쌓아 복구 후 재생하지 않는다(지나간 결정의 지연 실행 위험 차단). HTTP fallback 은 OQ-1(HTTP 0)으로 선택지에서 제외.
2. **보수적 중단** — broker 불가 시 신규 전략 매매 유입은 중단하고, 전략은 복구 후 신선 데이터로 재판단한다. 스냅샷 RPC 타임아웃 시 전략은 매매를 보류한다.
3. **core 자율 안전 매도는 transport·전략과 독립으로 계속** — trex-core 의 기본 안전 매도(손절 stop-loss, 시간 만기 forced_close)는 RabbitMQ 가용성과 무관하게, 그리고 **전략이 특정 슬롯을 더 이상 패치/관리하지 않아도(dead-man's-switch)** 동작해야 한다. broker 장애나 전략 방치 시에도 열린 포지션은 core 가 보호한다.
- 참고(현행 코드, 정직 기록): 이 안전 매도 기능은 **아직 미구현**이다 — `forced_close_at` 은 필드만 존재(BAT-014/REQ-066 lineage)하고 자동 트리거 로직이 execution 루프에 없으며, stop-loss(손절)는 코드에 전혀 없다. **나중에 구현 예정**이며 본 문서에는 degrade 정책의 전제로 참조만 기입한다(별도 구현 Discovery 는 추후, A-5).
- 반영 위치: `# 5` NFR-3, `# 6` R-3·A-5(신규).
- 종료 조건(충족): degrade 동작·core 안전 매도 독립성 전제가 NFR-3/R-3/A-5 에 반영됨.
- OQ-5: 클라이언트/런타임 통합.
- 상태: Answered (2026-06-05)
- 결정: **`pika`(동기) + 스레드 기반 런타임**. HTTP 제거(OQ-1)로 uvicorn/FastAPI 를 들어내고, 현 sync stack(psycopg 동기·동기 서비스·`threading.Thread` execution/ledger 루프)에 정합하는 순수 sync+thread daemon 으로 재구성한다.
- consumer: intent RPC consumer 스레드(단일, prefetch=1, Postgres commit 후 ack → OQ-3 FIFO/멱등 충족) + snapshot RPC consumer 스레드. pika connection 은 thread-safe 가 아니므로 스레드별 connection/channel 또는 `add_callback_threadsafe` 규약을 따른다.
- 이벤트 publisher: execution 루프 스레드가 체결/종료 시 자기 channel 로 `trex.events` 에 발행(OQ-2/OQ-3).
- entrypoint: `uvicorn.run(app)` → connection 셋업 + consumer/execution/ledger 스레드 기동 + graceful shutdown(기존 `stop` 이벤트 패턴 확장 + connection close).
- 재연결/백오프: pika 는 자동 재연결이 없으므로 직접 구현하며 OQ-4 degrade(보수적 중단) 정책과 묶는다.
- 반영 위치: `# 4` 변경 항목·영향 범위, `# 5` FR-3.
- 종료 조건(충족): 클라이언트·런타임 통합이 FR-3/영향 범위에 반영됨.
- OQ-6: 자격증명/설정 주입 — `.env` AMQP URL 등 접속 정보 주입 패턴.
- 상태: Answered (2026-06-05)
- 결정: 기존 `TREX_*` env 주입 패턴을 재사용한다. AMQP 접속은 단일 URL env **`TREX_RABBITMQ_URL`**(예: `amqp://user:pass@host:5672/vhost`)로 받아 `DaemonConfig.from_env()` 에서 로드한다 — 기존 `TREX_KIS_REDIS_URL`(URL+자격증명을 한 env 로) 패턴과 동형. 자격증명은 URL 에 포함(redis 와 동일 방식).
- 반영 위치: `# 4` 영향 범위(config), `# 5` FR-3, `# 10` DATA.
- 참고: uvicorn 제거(OQ-5)로 기존 `TREX_API_HOST`/`TREX_API_PORT` env 는 폐기 대상이 된다.
- 종료 조건(충족): 주입 방식이 config/영향 범위에 반영됨.
- OQ-7: 운영 probe/admin 표면의 '대체' 범위 — OQ-1 의 HTTP 대체 결정이 전략-facing 통신에 한정인가, `GET /v1/healthz` 같은 앱 레벨 헬스 표면까지 제거하는가? (현 배포 환경: 오케스트레이터 없음 — daemon 은 맨 프로세스(uvicorn)로 실행, repo 에 k8s/compose/Dockerfile 없음. 따라서 '프로세스 살아있음' liveness 는 프로세스 감독(systemd/supervisor/docker restart)이 담당하며 transport 와 무관. HTTP 대체로 잃는 것은 앱 레벨 HTTP 헬스 표면뿐.)
- 상태: Answered (2026-06-05)
- 결정: **(a) 앱 레벨 헬스 표면 제거 — HTTP 표면 0.** `GET /v1/healthz` 포함 모든 HTTP 엔드포인트를 제거한다. liveness("프로세스 살아있음")는 프로세스 감독(systemd/supervisor/docker restart)이 담당하며 transport·broker 와 무관하다(broker 장애가 재시작 루프를 유발하지 않음). 앱 레벨 헬스/관측성은 로그·메트릭으로 대체한다.
- 반영 위치: `# 3` 기대 상태, `# 4` 변경 항목, `# 5` FR-5, `# 6` R-5.
- 영향: 현재 `trex_core/adapters/api/app.py` 의 `healthz` 라우트도 제거 대상에 포함된다.
### 오픈 질문 처리 기준
- `Open`: 아직 답이 없고 다음 단계 진행에 영향이 있는 상태다. 문서에 유지한다.
- `Answered`: 답이 확정된 상태다. 관련 본문/요구사항에 반영한 뒤 오픈 질문 목록에서는 제거하거나 resolved 기록으로 이동한다.
- `Deferred`: 지금 단계에서 결정하지 않고 다음 단계 또는 후속 이슈로 넘기는 상태다. 넘길 대상과 이유를 반드시 적는다.
- `Dropped`: 더 이상 유효하지 않은 질문이다. 삭제 사유를 짧게 남기고 목록에서 제거한다.
- 처리 원칙 1: 질문만 남기지 말고 반드시 `상태`, `처리 방안`, `종료 조건`을 함께 적는다.
- 처리 원칙 2: 답이 나온 질문은 본문 반영 없이 단순 삭제하지 않는다. 반영 위치를 먼저 갱신한다.
- 처리 원칙 3: High 영향 질문은 `Deferred` 처리 후 처리 방안에 REQ 인계 추적 방식을 명시한다.
# 7. 초기 성공 기준
이 변경이 성공했다고 판단할 기준을 측정 가능하게 정의한다. 가능하면 데이터 출처까지 연결한다.
- S-1: stub messaging 어댑터로 스냅샷 request-reply 가 HTTP 와 동일한 logical 응답(필드/마스킹/view 모드)을 반환한다.
- S-2: intent admission RPC 메시지가 슬롯 전이를 일으키고, 중복 전달에도 correlation_id idempotency 로 단일 실행된다.
- S-3: 체결/슬롯 상태 이벤트가 발행되고 구독자가 수신한다.
- S-4: 도메인 import 스윕에서 transport/AMQP 의존이 0 으로 유지된다.
- S-5: interface-contract/runtime-flows/project-structure 가 메시지 계약·MQ 흐름과 정합한다.
- S-6: daemon 기동 후 HTTP 표면이 0 이다(`healthz` 포함 HTTP 포트/엔드포인트 없음, OQ-1/OQ-7).
- S-7: RabbitMQ 불가 시 신규 intent 가 버퍼링되지 않고 보수적으로 중단되며, broker 복구 후에도 옛 intent 가 자동 재생되지 않는다(OQ-4).
- S-8: strategy_id 를 routing key 로 한 이벤트가 해당 전략 queue 에만 전달되고 다른 전략 queue 에는 전달되지 않는다(FR-2/OQ-2).
## 측정 방식 및 데이터 출처
각 성공 기준을 어떻게 측정할지와 어떤 데이터나 문서를 근거로 쓸지 적는다.
- S-1 측정 방식: stub request-reply 통합테스트 / 데이터 출처: 응답 메시지 본문 vs HTTP 응답.
- S-2 측정 방식: 중복 intent 메시지 주입 테스트 / 데이터 출처: 슬롯 상태·실행 횟수.
- S-3 측정 방식: 이벤트 발행-구독 테스트 / 데이터 출처: 구독자 수신 메시지.
- S-4 측정 방식: 정적 import 스윕 / 데이터 출처: `trex_core/domain` import 목록.
- S-5 측정 방식: 문서-코드 정합 리뷰 / 데이터 출처: baseline 문서 vs 실제 토폴로지/어댑터.
- S-6 측정 방식: 기동 후 포트/라우트 스캔 / 데이터 출처: daemon 런타임(HTTP 리스너 부재).
- S-7 측정 방식: broker 중단 주입 후 intent 발행 테스트 / 데이터 출처: 신규 슬롯 전이 없음·복구 후 재생 없음.
- S-8 측정 방식: 2개 전략 queue 바인딩 후 이벤트 발행 테스트 / 데이터 출처: 각 queue 수신 메시지.
# 8. REQ로 넘기기 전 확인 체크
REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 확인한다. 미완료 항목은 후속 결정이 필요함을 뜻한다.
- [x] 범위(In Scope/Out of Scope) 확정 — 특히 logical 계약 불변·전략 로직 제외
- 기준: 운영자가 transport 범위 경계(logical 계약은 dcy-005/002/003 소관)를 확인했다.
- [x] 성공 지표의 측정 방식/데이터 출처 확정
- 기준: S-1~S-8 측정 방식·출처 명시(본 문서 # 7).
- [x] High 영향 리스크 우선순위 및 해소 책임자 지정
- 기준: HTTP 대체 결정(OQ-1)으로 가용성 결합(R-3)이 High 로 상향(HTTP fallback 부재). delivery(R-2)·가용성(R-3)·big-bang 마이그레이션(R-5) 처리 방침을 운영자가 확인.
- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
- 기준: OQ-1~OQ-7 이 전부 Answered/Deferred 처리(현재 OQ-1~OQ-7 전부 Answered).
- [x] Freeze 확정 담당자(CONFIRMED_BY) 지정
- 기준: 운영자가 confirm-discovery 시점에 CONFIRMED_BY 를 지정한다.
# 9. 파일 처리 결과
이 Discovery 문서 생성/갱신 결과와 참조 문서를 기록한다. 이후 추적과 리뷰 시 근거로 사용한다.
- 처리 결과: 생성
- 생성 경로: docs/discovery/dcy-006_20260605_strategy-module-rabbitmq-transport.md
- 참조 문서:
- docs/discovery/dcy-005_20260604_portfolio-hub-asset-snapshot-api.md (스냅샷 read — HTTP 가정 FR-1/A-3, view 모드/strategy_id logical 계약)
- docs/discovery/dcy-002_20260603_slot-core-daemon-http-api.md (HTTP intent API — transport 가정 출처)
- docs/discovery/dcy-003_20260604_broker-execution-and-outbox.md (correlation_id idempotency/outbox — delivery 신뢰성 재사용)
- docs/interface-contract.md, docs/runtime-flows.md, docs/project-structure.md (baseline — 본 Discovery 가 갱신 요구)
- stage-pilot/templates/project-structure.md, stage-pilot/templates/runtime-flows.md (baseline 갱신 논리 템플릿)
- https://github.com/fourblocks2020/trex-core/issues/1 (전략 taxonomy — intent 종류와 연계)
# 10. 사용자 결정 필요 항목 요약
사용자나 승인자가 결정해야 하는 항목만 모아 정리한다. 결정, 확인, 데이터 요청을 구분해서 적는다.
## DECIDE
- ~~HTTP 대체 vs 병행~~ → 결정됨: RabbitMQ 가 HTTP `/v1` API 를 **대체**(병행 없음) (OQ-1, 2026-06-05). 운영 probe 잔존 범위는 OQ-7.
- ~~운영 probe/admin 범위~~ → 결정됨: 앱 레벨 헬스 표면 제거, HTTP 표면 0(`healthz` 포함). liveness=프로세스 감독 (OQ-7, 2026-06-05)
- ~~delivery 보증 수준~~ → 결정됨: at-least-once + idempotency = effectively-once. intent=durable commit 후 ack+dedup·단일 consumer FIFO, 이벤트=durable per-strategy queue+상태/시퀀스 운반(순서 불요)+스냅샷 reconcile, exactly-once 미추구 (OQ-3, 2026-06-05)
- ~~클라이언트/런타임 통합~~ → 결정됨: `pika`(동기) + 스레드 런타임, uvicorn 제거, consumer 스레드(intent prefetch=1 FIFO·snapshot), 재연결 직접 구현 (OQ-5, 2026-06-05)
## CONFIRM
- ~~transport 범위 경계~~ → 확정됨(2026-06-05, Justin): In Scope(RabbitMQ 전환·HTTP 완전 제거·delivery 신뢰성·baseline 갱신) / Out of Scope(logical 계약 변경·전략 로직·core 안전 매도 구현·RabbitMQ HA 운영) 확인
- ~~degrade 정책~~ → 결정됨: 버퍼링 금지 + 신규 매매 보수적 중단 + core 자율 안전 매도(손절·시간 만기)는 MQ·전략 독립 동작(미구현, 추후, A-5). dcy-005 OQ-5 와 공통 보수적 안전 (OQ-4, 2026-06-05)
- ~~통신 패턴별 토폴로지~~ → 결정됨: 스냅샷·intent admission = request-reply(direct reply-to), 이벤트 = topic exchange `trex.events`(rk `{event_type}.{strategy_id}.{symbol}`), 요청은 payload·이벤트는 routing key 매핑, intent fill 은 이벤트로 (OQ-2, 2026-06-05)
## DATA
- DATA(비차단, REQ/delivery 단계 계측·provision): RabbitMQ 브로커 인프라 provision + 접속 자격증명 — 주입 방식은 `TREX_RABBITMQ_URL` env 로 확정(OQ-6). 실제 broker 인스턴스/계정 제공은 delivery 단계 운영 작업이며 FR/NFR·범위·성공 기준·검증 시나리오가 충분해 REQ 초안 작성을 막지 않는다.
# 11. Discovery Freeze
REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다. 내용 재작성 없이 원본 섹션을 링크로 대신한다.
## 섹션 참조
- 범위: [# 5. 요구사항 목록 > 범위 경계](#5-요구사항-목록)
- 리스크/오픈 질문: [# 6. 리스크/가정 목록](#6-리스크가정-목록)
- 성공 기준: [# 7. 초기 성공 기준](#7-초기-성공-기준)
- 인계 전 확인: [# 8. REQ로 넘기기 전 확인 체크](#8-req로-넘기기-전-확인-체크)
- 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약)
## Freeze 플래그
- Handoff Decision: 승인
- Handoff Rationale: **OQ-1~OQ-7 전부 Answered** — HTTP 대체(표면 0)·통신 토폴로지(RPC 스냅샷/intent + topic 이벤트)·delivery(effectively-once)·degrade(버퍼링 금지·보수적 중단·core 자율 안전 매도 독립)·클라이언트(pika 동기 + 스레드, uvicorn 제거)·자격증명(`TREX_RABBITMQ_URL`)까지 transport 골격 확정. 남은 인계 전 항목은 transport 범위 경계 확인(운영자)과 CONFIRMED_BY 지정뿐이다. DATA(RabbitMQ 인프라 provision)는 delivery 단계 운영 작업으로 비차단. confirm-discovery 에서 확정 후 REQ Drafting 으로 인계한다.
- Ready for REQ Drafting: true
- Confirmed By: Justin
- Confirmed At (KST): 2026-06-05 22:30
+1
View File
@@ -11,3 +11,4 @@
| dcy-003_20260604_broker-execution-and-outbox | 20260604 | 브로커 단일 세션 + 주문 집행 + durable outbox (불변식 B) | [Discovery](./dcy-003_20260604_broker-execution-and-outbox.md) | confirmed | | dcy-003_20260604_broker-execution-and-outbox | 20260604 | 브로커 단일 세션 + 주문 집행 + durable outbox (불변식 B) | [Discovery](./dcy-003_20260604_broker-execution-and-outbox.md) | confirmed |
| dcy-004_20260604_portfolio-hub-ledger-integration | 20260604 | portfolio-hub ledger 연동 — 체결을 외부 원장에 기록 | [Discovery](./dcy-004_20260604_portfolio-hub-ledger-integration.md) | confirmed | | dcy-004_20260604_portfolio-hub-ledger-integration | 20260604 | portfolio-hub ledger 연동 — 체결을 외부 원장에 기록 | [Discovery](./dcy-004_20260604_portfolio-hub-ledger-integration.md) | confirmed |
| dcy-005_20260604_portfolio-hub-asset-snapshot-api | 20260604 | portfolio-hub 종목 스냅샷 read API — 매수 전략 모듈에 슬롯-제외 후보 제공 | [Discovery](./dcy-005_20260604_portfolio-hub-asset-snapshot-api.md) | draft | | dcy-005_20260604_portfolio-hub-asset-snapshot-api | 20260604 | portfolio-hub 종목 스냅샷 read API — 매수 전략 모듈에 슬롯-제외 후보 제공 | [Discovery](./dcy-005_20260604_portfolio-hub-asset-snapshot-api.md) | draft |
| dcy-006_20260605_strategy-module-rabbitmq-transport | 20260605 | trex-core ↔ 전략 모듈 통신 transport — RabbitMQ 도입 (스냅샷 read + intent 제출 + 이벤트) | [Discovery](./dcy-006_20260605_strategy-module-rabbitmq-transport.md) | confirmed |