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:
@@ -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
|
||||||
@@ -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 |
|
||||||
Reference in New Issue
Block a user