51 KiB
0. 문서 상태
이 문서의 현재 상태와 식별 정보를 기록한다. 문서 추적과 승인 흐름에 필요한 최소 메타데이터를 적는다.
- 상태: confirmed
- 문서 ID: dcy-005_20260604_portfolio-hub-asset-snapshot-api
- 이슈명: portfolio-hub 종목 스냅샷 read API — 전략 모듈에 전략 실행 대상 자산 리스트 제공
- 작성 시각(KST): 2026-06-04 22:46
- 마지막 갱신 시각(KST): 2026-06-06 00:20
- 대체됨: 없음
- 후속 Discovery 참조: 없음
- 생성된 REQ 참조: REQ-027, REQ-028, REQ-029, REQ-030, REQ-031, REQ-032, REQ-033
1. 계획 상태 요약
현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다.
- 연계 문서 현황:
- 관련 점수 파일: 미생성
- 관련 TODO/메모: dcy-004(portfolio-hub ledger write 통합, BAT-006 진행 중)의 Out of Scope
asset master 동기화·get_assets/get_portfolios 연동. orbitapp/clients/portfolio_client.py(read 계약 get_assets/get_portfolios 패턴). dcy-006(confirmed) — trex-core ↔ 전략 모듈 통신 transport 를 RabbitMQ 로 확정(HTTP 표면 0). 본 Discovery 의 스냅샷 read 는 그 transport(RabbitMQ request-reply RPC) 위에서 전달된다.
- 해석:
- dcy-004 는 체결을 portfolio-hub 원장에 기록하는 write 방향이었고, 본 Discovery 는 그 반대인 read 방향 — portfolio-hub 종목 스냅샷을 trex-core 가 전략 모듈에 제공한다. dcy-004 가 명시적으로 후속으로 미룬 영역을 이어받는다.
- transport/계약 분리: 본 Discovery 는 스냅샷의 logical 계약(응답 단위·필드·마스킹·view 모드·strategy_key 필터·신선도)을 정의하며 transport-무관이다. 전달 방식(HTTP 아님 — dcy-006 의 RabbitMQ RPC)은 dcy-006 소관이고, 본 문서는 그 transport 위에 logical 계약을 얹는다.
- active-cycle 판정(BAT-006 draft 진행 중)이지만 명시적 신규 입력이고, BAT-006(write)과 read 방향이 분리되어 범위 충돌이 없으므로 병렬 독립 주기로 진행한다. core 경유의 핵심 가치는 단순 패스스루가 아니라 core 의 권위 슬롯 상태를 겹쳐(slot overlay) 전략 실행 대상 자산을 선별(pending·warning·pause 마스킹) 해 제공하는 것이다.
2. 요구사항 판정 결과
이번 요청이 기존 요구사항과 비교해 신규인지, 유사한지, 중복인지 판단하고 그 근거를 남긴다.
- 판정: 신규
- 근거
- dcy-004 는 write 방향(원장 기록)만 In Scope 였고 read 방향(get_assets/get_portfolios)은 명시적으로 Out of Scope(후속)였다. 본 Discovery 는 그 후속 read 통합이다.
- 기존 REQ 본문 수정이 아니라 신규 외부 read 통합 + 신규 read 계약(스냅샷 응답)이라
change-req가 아니라 새 Discovery 다.
3. 문제점의 요약
문제의 현재 상태, 기대 상태, 관련 이해관계자, 제약사항을 정리한다. Discovery의 핵심 맥락을 가장 먼저 이해할 수 있어야 한다.
- 현재 상태
- trex-core 의 read 경로는 슬롯/capacity 조회와 intent 조회뿐이고 portfolio-hub 종목을 노출하는 read 경로가 없다. (dcy-006 구현으로 통신 transport 가 RabbitMQ 로 전환됨 —
adapters/apiHTTP 어댑터 제거,adapters/messaging(rabbitmq/topology) 추가, daemon 이trex.snapshot.req/trex.intent.reqRPC consumer 운영. 따라서 본 스냅샷 read 는 그 snapshot RPC 위에 portfolio-hub 종목 logical 계약을 얹는다 — portfolio-hub read 포트/어댑터는 아직 없다.) - portfolio-hub read 어댑터(get_assets/get_portfolios)가 trex-core 에 없다. dcy-004/BAT-006 은 write 방향
LedgerPort+ portfolio-hub 어댑터만 추가하며, read 포트는 다루지 않는다. - 결과적으로 전략 모듈이 "무엇을 매매할지" 정할 portfolio-hub 종목 정보를 trex-core 를 통해 받을 경로가 없다.
- trex-core 의 read 경로는 슬롯/capacity 조회와 intent 조회뿐이고 portfolio-hub 종목을 노출하는 read 경로가 없다. (dcy-006 구현으로 통신 transport 가 RabbitMQ 로 전환됨 —
- 기대 상태
- trex-core daemon 이 설정 가능한 시각(기본 07:30 KST, .env)에 일일 스케줄로 portfolio-hub 에서 자신의
daemon_iduniverse(=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 가 이 스냅샷을 기반으로 portfolio-hub 보유 자산(holding 슬롯 자산 포함)을 담되 pending·warning·pause 상태 자산은 마스킹한 전략 실행 대상 자산 리스트를 반환한다(OQ-4).
- 응답은 요청
view파라미터(all기본 |buyable|held)로 base 집합을 고르고 envelope(assets[]+available_slots·degraded등 meta)로 반환한다(OQ-11/OQ-12/OQ-6). 가용 슬롯이 0 이어도 보유 자산은 계속 반환되며 신규 매수 가용성은available_slotsmeta 로 신호한다.
- trex-core daemon 이 설정 가능한 시각(기본 07:30 KST, .env)에 일일 스케줄로 portfolio-hub 에서 자신의
- 이해관계자
- 문서 작성자/구현자(AI agent)
- 리뷰어/승인자(운영자, genichin@gmail.com)
- 전략 모듈(매수/매도/position, trex-core 외부 소비자) 및 portfolio-hub(외부 자산/포지션 서비스)
- 제약사항
- 도메인 순수성 —
trex_core.domain은 portfolio-hub/네트워크 의존 0(read 어댑터 격리). - 단일 통합 경계 — portfolio-hub 자격증명/계약 접점은 trex-core 어댑터(dcy-004 재사용)로 한정하며, 매수 전략 등 모든 소비자는 core 스냅샷 계약을 경유한다(portfolio-hub 직접 호출 금지, OQ-3).
- core 는 슬롯/capacity 의 권위 단일 writer(불변식 A) — slot overlay 는 이 권위 상태를 사용한다.
- transport 종속성 — 스냅샷 read 의 전달은 dcy-006(confirmed)의 RabbitMQ request-reply RPC 를 사용한다(HTTP 아님). 본 Discovery 는 logical 계약만 정의하고 transport 세부(토폴로지·delivery)는 dcy-006 소관이다.
- 도메인 순수성 —
주요 사용자 시나리오
사용자 또는 운영자가 실제로 어떤 상황에서 이 변경을 필요로 하는지 대표 시나리오를 적는다.
- 전략 모듈이 자기 portfolio(전략 실행 대상 자산)를 trex-core 스냅샷 API 로 요청 → portfolio-hub 보유 자산(holding 슬롯 자산 포함)을 받되, pending·warning·pause 상태 자산은 마스킹되어 빠진 리스트를 받는다.
- 모든 슬롯이 차 있어 더 살 수 없는 상황 → 보유 자산은 계속 응답에 포함되고(
view=all/held), 신규 매수 가능 여부는available_slotsmeta 로 신호한다.view=buyable은available_slots=0이면[]를 반환한다(OQ-11/OQ-12). - 막 매수 주문되어 슬롯이 pending(미체결)인 종목 → 마스킹되어 전략 모듈 응답에서 빠져 중복 매수를 막고, 체결되어 holding 이 되면 보유 자산으로 응답에 포함된다.
- 스냅샷 페치 시 portfolio-hub 포지션과 실제 브로커 계좌 포지션이 어긋난 자산 →
warning으로 표시되어 그 자산은 응답 리스트에서 마스킹되고 매매 전략이 수행되지 않는다. - 전략 모듈이 자기
strategy_key를 파라미터로 넣어 포트폴리오를 요청 → 그strategy_key가 등록된 자산만 받는다. 파라미터를 생략하면 전체 자산을 받는다(여러 전략 모듈이 하나의 스냅샷 API 를 공유하되 자기 책임 자산만 조회). - 운영자가 특정 자산을 런타임에 pause → 전략 모듈이 포트폴리오를 요청해도 그 자산은 응답에서 마스킹되어 매매가 멈추고, resume 하면 다시 응답에 포함된다.
4. 이번에 정의할 변경
이번 Discovery에서 다루는 핵심 변경 항목을 목록으로 적는다. 기능, 구조, 규칙 변경을 함께 포함해도 된다.
- 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 에 더해, 체결 시 portfolio-hub ledger write 응답이 반환하는 변경 후 권위 cash/position 으로 갱신한다(옵션 D — 당초 옵션 C 의 core 로컬 체결 replay 보정을 대체).
- 포지션 정합 검증 + warning: 페치 시 portfolio-hub 포지션 vs 실제 브로커 계좌 포지션을 대조하고, 불일치 자산은
warning상태로 응답 리스트에서 마스킹한다. - 종목 스냅샷 read 계약: trex-core 가 보유 스냅샷을 read 계약(요청-응답, transport 는 dcy-006 RabbitMQ RPC)으로 제공하고, holding 포함·pending·warning·pause 마스킹한 전략 실행 대상 자산 리스트를 반환한다(slot overlay, OQ-4).
- strategy_key 필터: 스냅샷 read 요청이 선택적
strategy_key파라미터(요청 payload, dcy-006 OQ-2)를 받아, 주어지면 해당strategy_key가 등록된(=active_strategy_id일치) 자산만 반환하고, 없으면 (pending·warning·pause 마스킹 후) 모든 자산을 반환한다.strategy_key는 사람이 읽는 안정 유일 slug(예:target-value-v1), 단일active_strategy_id정확 일치(OQ-9). - 런타임 pause/resume: trex-core 가 보유한 스냅샷 universe 의 개별 항목을 사용자가 런타임에 일시 중단(pause)/재개(resume)할 수 있고, pause 된 항목은 전략 모듈의 포트폴리오 요청 결과에서 제외한다. pause 단위·제어 방식·영속성은 OQ-10.
- 응답 view 모드 + envelope: 응답 base 집합을 요청
view(all기본|buyable|held)로 선택하고 envelope(assets[]+view·available_slots·degraded·reasonmeta)로 반환한다(OQ-11/OQ-12/OQ-6). capacity 0 이어도 보유 자산은 반환,buyable만available_slots=0시[]. - baseline 갱신: interface-contract(read 포트/메시지 계약)·runtime-flows(스냅샷 조회 흐름, RabbitMQ RPC)·project-structure(read 어댑터) 갱신. (dcy-006 의 transport baseline 갱신과 정합.)
영향 범위
변경이 미칠 코드/문서/운영 범위를 명시해 REQ 정제와 batch planning에서 추정 누락을 줄인다.
- 코드 영향:
- 신규:
trex_core/applicationread 포트, 신규adapters/portfolioread 어댑터 + stub, 스냅샷 read 를 dcy-006 messaging transport(RabbitMQ RPC,adapters/messaging)로 노출 + 응답 스키마(transport-무관), slot overlay(점유 종목 제외) + 현금/포지션 write-응답 갱신 로직(cash_balance_after/asset_quantity_after, 옵션 D), 일일 스냅샷 스케줄러(런타임 신규), 포지션 정합 검증 +warning상태,config에 스냅샷 페치 시각 주입(daemon_id는 portfolio-hub 토큰에서 도출 — 별도 config 불필요, OQ-7),strategy_key필터(요청 payload →active_strategy_id매칭), 스냅샷 항목 pause/resume 상태 저장(Postgres, portfolio_id 키, refresh 무관) + 제어 표면(CLI + dcy-006 RPC 제어 메시지) + 응답 산출 시 pause 마스킹(전역).- (dcy-006 구현 완료:
adapters/api제거·adapters/messaging추가·trex.snapshot.reqRPC consumer 존재. 스냅샷 read 는 이 RPC consumer 위에 portfolio-hub 종목 logical 계약을 채워 구현한다.)
- (dcy-006 구현 완료:
- 기존: capacity/slot 조회 재사용(점유 종목·가용 슬롯 판정), 브로커 계좌 포지션 조회(BrokerPort 잔고조회 = KIS 주식잔고조회
TTTC8434R/VTTC8434R,pdno+hldg_qty, symbol 집계 — 정합 검증용, OQ-8) 재사용/확장.
- 신규:
- 문서 영향:
- interface-contract/runtime-flows/project-structure 갱신.
- 운영 영향:
- portfolio-hub read 의존 추가(dcy-004 자격증명 재사용). 캐시 도입 시 신선도/무효화 운영 고려.
5. 요구사항 목록
실제로 구현과 검증으로 이어질 요구사항을 기능/비기능으로 나누어 정리하고, 범위 경계를 명확히 적는다.
기능 요구사항
- FR-1: trex-core 가 종목 스냅샷을 read 계약(요청-응답)으로 제공한다. transport 는 dcy-006 의 RabbitMQ request-reply RPC 이며(HTTP 아님), 전략 모듈이 메시지로 소비한다. 스냅샷 단위는 daemon 이 관리하는 portfolio universe 이며, 항목당 symbol, asset_id(=portfolio_id), 현금(cash_balance), 포지션(asset_quantity), active_strategy_id, 전략파라미터(opaque JSON)를 담고 가격(price)은 제외한다. (OQ-1 Answered)
- 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(
all)의 구성 규칙이며, 응답 베이스 집합은 요청view파라미터(all/buyable/held, OQ-11/OQ-12)로 선택될 수 있고 마스킹은 선택된 베이스 위에 균일 적용된다. (변경 2026-06-05, OQ-4 결정: 종전 'pending·holding 점유 종목 제외, 매수 후보 리스트' → 'holding 포함, pending·warning·pause 만 마스킹'.) - FR-3: 응답은 envelope 로 제공한다(자산은 항상
assets: []배열, null 금지; meta:view·available_slots·degraded·reason) (OQ-6). 자산 base 집합은 요청view파라미터로 선택한다(OQ-11/OQ-12) —all(기본, universe 전체) |buyable(universe − 슬롯 점유) |held(holding 슬롯만). 마스킹(pending·warning·pause)은 전 모드 균일 적용(OQ-4; pending 은 모든 모드 마스킹이라held=holding 만). capacity 는available_slotsmeta 로 신호하며buyable은available_slots=0이면[]. (종전 '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 어댑터/자격증명을 재사용한다.daemon_id는 portfolio-hub 자격증명/토큰에서 도출하며(OQ-7, 단일 진실원) 별도 config 값이 아니다(필요 시 whoami 1회 도출·캐시). 전략파라미터는 opaque JSON 으로 통과시킨다(trex-core 가 스키마를 해석하지 않음). - FR-5: baseline 문서(interface-contract/runtime-flows/project-structure)를 read 포트·스냅샷 read 계약(RabbitMQ RPC)·read 어댑터·일일 스케줄러에 맞게 갱신한다(dcy-006 transport baseline 과 정합).
- FR-6: trex-core daemon 이 설정 가능한 시각(기본 07:30 KST, .env 로 지정)에 일일 스케줄로
daemon_iduniverse 를 페치해 스냅샷을 보유한다. 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 포지션과 실제 브로커 계좌 포지션이 수량 정확 일치하는지 검증한다(symbol 단위 집계, 허용 오차 없음 — OQ-8). 불일치 자산은
warning상태로 표시·응답 리스트에서 마스킹하고 해당 자산에 매매 전략을 수행하지 않으며, 다음 정합 성공 시 자동 해제한다(영속 hold 는 pause/FR-9). 브로커 포지션은 KIS 주식잔고조회(TTTC8434R/VTTC8434R,pdno+hldg_qty)를 BrokerPort 로 조회한다. - FR-8: 스냅샷 read 요청은 선택적
strategy_key파라미터(요청 payload, dcy-006 OQ-2 매핑)를 받는다.strategy_key가 주어지면 해당strategy_key가 등록된(= 자산의active_strategy_id와 일치) 자산만 응답 리스트에 포함하고,strategy_key가 없으면 (pending·warning·pause 마스킹 후) 모든 자산을 반환한다. 여러 전략 모듈이 같은 스냅샷 API 를 공유하되 각자 자기 책임 자산만 받는다.strategy_key는 사람이 읽을 수 있는 안정 유일 slug(예:target-value-v1)이며 자산의 단일active_strategy_id와 대소문자 구분 정확 일치로 매칭한다(OQ-9; 다중 등록은 범위 밖).strategy_key필터는view파라미터와 직교 합성된다(OQ-12). - FR-9: trex-core 가 보유한 스냅샷 universe 의 개별 항목을 사용자가 런타임에 일시 중단(pause)/재개(resume)할 수 있다. pause 된 항목은 전략 모듈의 포트폴리오 요청 결과(FR-2)에서 제외한다. 응답 리스트는 holding 포함을 기본으로 (pending 마스킹) ∧ (warning 마스킹) ∧ (pause 마스킹) ∧ (strategy_key 필터, 주어진 경우)의 합성으로 산출된다. pause 단위는
portfolio_id(=asset_id), 제어는 CLI + dcy-006 RPC 제어 메시지, Postgres 영속(재시작·일일 refresh 후 유지), 전역 적용, resume 시 다음 요청부터 즉시 복귀(OQ-10). pause(수동·영속) vs warning(자동·일시)은 독립 overlay·OR 합성.
비기능 요구사항
성능, 운영성, 일관성, 자동화 안정성 등 기능 외 요구사항을 적는다.
- NFR-1: 도메인 순수성 유지 — read 포트는 application 계약, 어댑터는 adapters 레이어 격리.
trex_core/domain은 portfolio-hub/네트워크 의존 0(import 스윕). - NFR-2: 일관성 — 점유 종목 제외/가용 슬롯 판정은 core 의 권위 슬롯 상태(강한 일관성)를 사용한다. 현금/포지션은 일일 baseline + 체결 시 portfolio-hub ledger write 응답의
cash_balance_after/asset_quantity_after권위 값 갱신으로 유지하고(옵션 D), 스냅샷 페치 시 브로커 계좌와 정합 검증한다(불일치 자산warning제외). - NFR-3: 가용성 degrade — portfolio-hub 가 불가해 권위 cash/position 유지가 불가하면 명시적 degraded 신호(reason code + sanitize 된 message 포함) + 신규 전략 매매 보수적 중단(OQ-5; 빈 리스트/silent stale 금지). core 자율 보호 매도는 유지(ledger 는 outbox 로 결국 기록). dcy-006 OQ-4 와 공통 '보수적 안전' 철학.
범위 경계
이번 반복에서 반드시 포함할 범위와 명시적으로 제외할 범위를 구분한다.
- In Scope
- portfolio-hub read 포트 + 어댑터 + stub, 종목 스냅샷 read API, holding 포함·pending·warning·pause 마스킹(slot overlay, OQ-4), capacity 0 응답 재정의(OQ-11), 설정 시각 일일 스냅샷 + 현금/포지션 write-응답 갱신(옵션 D), 포지션 정합 검증 +
warning제외, baseline 갱신.
- portfolio-hub read 포트 + 어댑터 + stub, 종목 스냅샷 read API, holding 포함·pending·warning·pause 마스킹(slot overlay, OQ-4), capacity 0 응답 재정의(OQ-11), 설정 시각 일일 스냅샷 + 현금/포지션 write-응답 갱신(옵션 D), 포지션 정합 검증 +
- Out of Scope (후속 Discovery)
- 매수 전략 의사결정 로직 자체(점수화·랭킹·진입 타이밍) — 전략 모듈 책임.
- portfolio-hub ↔ core 양방향 reconciliation, 포지션 재구성, asset master 영속 동기화.
- 매수/매도 전략 데몬, safety floor, 실계좌 go-live.
6. 리스크/가정 목록
불확실성과 전제 조건을 분리해서 적는다. 리스크는 영향과 완화 방안을, 가정은 현재 판단의 전제를 기록한다.
리스크
- R-1 (Medium): stale 스냅샷 — 일일 캐시한 현금/포지션이 장중에 어긋날 수 있다.
- 완화 방안(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 포지션에 아직 반영되기 전 중복 노출될 수 있다.
- 완화 방안(OQ-4 Answered): pending 슬롯 마스킹으로 미체결 in-flight 종목을 응답에서 제외해 중복 노출을 차단한다. 체결 완료(holding)된 자산은 보유 자산으로 포함되며, 전략 모듈이 보유 상태를 보고 재매수하지 않는다(holding 자산의 보유 수량 표기 정합은 core 권위 슬롯 상태 기준 — 설계 시 확정). (종전 'pending·holding 동시 제외'에서 'pending 만 마스킹, holding 포함'으로 변경.)
- R-3 (Medium): 가용성 결합 — portfolio-hub 장애가 전략 read 를 막는다.
- 완화 방안(OQ-5 Answered): 권위 cash/position 유지 불가 시 명시적 degraded 신호로 신규 매매 보수적 중단(stale 매매·누적 drift 차단). core 보호 매도는 유지(outbox 로 ledger 결국 기록). 어댑터 격리(NFR-1).
- R-4 (Low): 책임 경계 확장 — core 가 portfolio-hub read gateway 로 비대해질 위험.
- 완화 방안: 모든 소비자가 core 를 경유하되(OQ-3 Answered), core 의 부가가치(slot overlay·현금/포지션 보정·정합 검증)로 범위를 한정하고 단순 패스스루 프록시는 지양한다.
- R-5 (Medium): 포지션 drift — portfolio-hub 가 실제 브로커 계좌와 어긋나면(과거 누락·정합 오류) 스냅샷 baseline 자체가 틀린다. (옵션 D 로 전환해 core 로컬 replay 누적 drift 는 제거되었으나, portfolio-hub ↔ 브로커 계좌 간 drift 는 여전히 남는다.)
- 완화 방안(FR-7, OQ-8 Answered): 스냅샷 페치 시 portfolio-hub 포지션 vs 브로커 계좌 포지션 수량 정확 일치(symbol 집계) 검증, 불일치 자산은
warning으로 매매 제외(보수적 안전), 다음 정합 성공 시 자동 해제. 브로커 출처 = KIS 주식잔고조회.
- 완화 방안(FR-7, OQ-8 Answered): 스냅샷 페치 시 portfolio-hub 포지션 vs 브로커 계좌 포지션 수량 정확 일치(symbol 집계) 검증, 불일치 자산은
가정
아직 확정되지 않았지만 현재 문서 작성의 기반이 되는 전제를 적는다.
- A-1: portfolio-hub 는 daemon 인증(
auth_tokens.daemon_id)으로 자신이 관리하는 portfolio 집합을 조회하는 read 계약을 제공한다. (근거: portfolio-hubapp/api/v1_portfolios.py·v1_user_info.py의auth.daemon_id스코프) - A-2: trex-core 는 슬롯/capacity 의 권위 단일 소스이며, 점유 종목·가용 슬롯 판정에 자기 상태를 사용할 수 있다.
- A-3: 전략 모듈은 trex-core 외부 소비자이며 dcy-006 transport(RabbitMQ request-reply RPC)로 스냅샷을 받는다(HTTP 아님). logical 계약(필드/마스킹/view 모드/strategy_key)은 transport-무관.
- 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_paramsJSONB 로 보관한다. trex-core daemon 의 universe = 자신이 관리하는 portfolio 집합이다. (근거: portfolio-hubapp/domain/models.pyDaemon/Portfolio,strategy_paramsJSONB migration) - 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_after2개를 비파괴 추가하기로 회신. 더 나아가 이 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 초안 작성 이전에 확인할 질문으로 남긴다.
- OQ-1: 스냅샷의 정의 — 응답 단위와 필드.
- 상태: Answered (2026-06-04)
- 결정: 스냅샷 = daemon 이 관리하는 portfolio universe. 항목당 symbol, asset_id(=portfolio_id), 현금(cash_balance), 포지션(asset_quantity), active_strategy_id, 전략파라미터(opaque JSON). 가격(price)은 사용하지 않으므로 제외. 전략파라미터는 portfolio-hub 가
strategy_paramsJSONB 로 보관하므로 trex-core 는 해석 없이 opaque JSON 으로 통과시킨다. - 반영 위치:
# 3기대 상태,# 5FR-1·FR-4,# 6A-5.
- OQ-2: 신선도/캐시 정책 — universe 페치 주기와 현금/포지션 신선도.
- 상태: Answered (2026-06-04)
- 결정(개정 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)가 계속 담당. - 반영 위치:
# 4,# 5FR-6·NFR-2·In Scope,# 6R-1·R-5·A-6·A-7.
- OQ-3: core 경유 정책 — 모든 소비자가 trex-core 를 경유하는가, 일부는 portfolio-hub 직접 호출을 허용하는가?
- 상태: Answered (2026-06-04)
- 결정: 매수 전략 모듈 등 모든 소비자는 trex-core 스냅샷 API 를 경유한다. portfolio-hub 직접 호출은 허용하지 않는다. core 가 단일 통합 경계(자격증명·계약·slot overlay·현금/포지션 보정·정합 검증)를 일관 제공하고, 소비자는 portfolio-hub 계약/자격증명을 알 필요가 없다.
- 반영 위치:
# 3제약사항,# 6R-4 완화.
- OQ-4: in-flight 합성 규칙 — portfolio-hub 포지션과 core 슬롯 상태를 합쳐 응답을 만들 때의 포함/마스킹 규칙.
- 상태: Answered (2026-06-05)
- 결정: 응답(전략 실행 대상 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 마스킹'으로 변경.)
- 반영 위치:
# 3시나리오,# 5FR-2·FR-9,# 6R-2,# 7S-2. - 잔여: holding 자산의 보유 수량 표기를 core 권위 슬롯 vs 스냅샷 중 무엇 기준으로 할지(holding→ledger flush 갭의 수량 정합)는 설계 시 확정. 종전 FR-3(가용 슬롯 0 → 빈 리스트)은 'holding 포함' 변경과 충돌 → OQ-11 로 재정의.
- OQ-5: 가용성 degrade 정책 — portfolio-hub 불가 시 스냅샷 read 동작.
- 상태: Answered (2026-06-05)
- 결정: portfolio-hub 가 불가해 권위 cash/position 을 유지할 수 없는 상태(옵션 D 의 ledger write
*_after갱신 불가, 또는 일일 universe fetch 실패)면 신규 전략 매매를 보수적으로 중단한다. 차단 사유는 'ledger 기록 불가'가 아니라(dcy-004 outbox 가 유실 없이 결국 기록) stale cash/position 으로 매매 시 잘못된 사이징·누적 drift 위험이다.- read 응답: 빈 리스트(
[]— '후보 없음'과 모호)나 silent stale 캐시 서빙 대신 명시적 degraded 신호를 실어 전략이 신규 매매를 보류하게 한다. 신호는degraded=true+ degraded 사유를 포함한다 — machine-readable reason code(예:portfolio_hub_unavailable/snapshot_fetch_failed/cash_position_stale)로 전략이 원인별 분기 가능하게 하고, 사람용 message 로 운영 진단을 돕는다. 단 raw 예외/자격증명 등 민감정보는 노출하지 않고 sanitize 한다. - core 자율 보호 매도(손절·시간 만기, dcy-006 A-5)는 계속 동작 — 그 체결의 ledger 도 outbox 가 복구 후 기록하므로 outage 중에도 열린 포지션은 보호된다. 즉 '중단'은 신규 전략 매매에 한정한다.
- dcy-006 OQ-4(RabbitMQ degrade)와 공통 '보수적 안전' 철학: 임의의 핵심 의존성(portfolio-hub OR RabbitMQ) degrade 시 신규 매매 중단 + core 보호 매도 유지.
- read 응답: 빈 리스트(
- 반영 위치:
# 5NFR-3,# 6R-3. - 종료 조건(충족): degrade 동작이 NFR-3/R-3 에 반영됨.
- OQ-6: 빈 리스트 표현 시맨틱 — 응답이 비었을 때의 표현.
- 상태: Answered (2026-06-05)
- 결정: 응답은 envelope 로 감싸고 자산 목록은 항상 배열(
assets: [], null 금지)로 준다. 빈 배열은 '항목 없음'을 그대로 의미하고, '왜 비었는지'는 envelope 의 meta 로 구분한다 —view(에코된 view 모드, OQ-11/12),available_slots(capacity 신호),degraded+reason(OQ-5). 근거: 리스트 필드에 null 을 섞으면 소비자 분기·버그 유발 → 항상 배열. 빈 리스트만으로는 capacity 0 / 전부 마스킹 / 필터 0매칭 / degrade 를 구분 못 하므로 envelope 필요(OQ-5 가 이미degraded/reason을 요구해 envelope 는 사실상 강제). transport 가 메시지 payload(dcy-006 RPC)라 REST status-code 가 아니라 구조화 envelope 가 자연스럽다. - 반영 위치:
# 5FR-3 (자산 항목(FR-1)·마스킹(FR-2)은 envelope 의assets[]원소로 위치). - 종료 조건(충족): 표현 방식(envelope +
[])이 FR-3 에 반영됨. 정확한 envelope 필드 스키마는 OQ-12 view 파라미터와 함께 REQ/설계 단계 확정.
- OQ-7: trex-core daemon 의
daemon_id출처.- 상태: Answered (2026-06-05)
- 결정:
daemon_id는 portfolio-hub 자격증명/인증 토큰(auth_tokens.daemon_id)에서 도출한다(단일 진실원). 별도TREX_DAEMON_ID.env 설정값을 1차 출처로 두지 않는다 — 자격증명과 분리 설정 시 dual-source drift(잘못된 universe·auth 오류) 위험. portfolio-hub read 는 토큰으로 daemon 을 스코프하므로(A-1) trex-core 는 daemon_id 를 명시적으로 들 필요가 가급적 없고, 값이 필요하면 startup 때 whoami(portfolio-hubv1_user_info류) 1회로 도출·캐시한다. .env override 는 테스트/stub 한정이며 startup 에서 토큰 daemon_id 와 일치 검증(불일치 시 fail-closed)한다. dcy-004 의TREX_PORTFOLIO_HUB_*자격증명을 재사용하므로 신규TREX_DAEMON_ID주입은 불필요. - 반영 위치:
# 4영향 범위(config),# 5FR-4. - 종료 조건(충족): daemon_id 출처가 FR-4/영향 범위에 반영됨.
- OQ-8: 포지션 정합 검증 세부.
- 상태: Answered (2026-06-05)
- 결정:
- 일치 기준 = 수량 정확 일치(정수 shares, 허용 오차 없음). A-6(유일 매매 주체·장중 외부 변동 없음)상 portfolio-hub 포지션과 브로커 잔고는 동일 체결에서 파생되어 정확히 일치해야 하며, tolerance 는 실제 이상(누락 체결·외부 거래)을 가린다. 현금은 FR-7 범위 밖(수수료·세금·T+2 결제로 단순 비교 불가) — 수량만 대조.
- warning 회복 = 다음 정합 성공 시 자동 해제(매 정합 사이클 재계산되는 파생 플래그, sticky 아님). raise 는 매번 로깅/alert. 영속 hold 가 필요하면 운영자 pause(FR-9)로 분리한다(자동 warning=일시 / 수동 pause=영속).
- 브로커 포지션 출처 = KIS 주식잔고조회(실전
TTTC8434R/ 모의VTTC8434R,GET /uapi/domestic-stock/v1/trading/inquire-balance)를 BrokerPort 잔고조회로 재사용. 항목당pdno(종목코드) +hldg_qty(보유수량). paper broker 는 결정론 stub.- symbol 단위 집계 대조: 브로커는 단일 계좌(dcy-003 불변식 B)라 잔고가 symbol 단위 집계 → portfolio-hub per-portfolio 포지션을 symbol 로 합산해 broker per-symbol
hldg_qty와 대조(보통 1 portfolio:1 symbol 이나 안전하게 집계 기준). - 주의: 당일 전량매도분이
hldg_qty=0으로 D-2 까지 잔고에 남을 수 있음(015 문서) → 0 수량 잔고 처리 규칙은 설계 시 확정.
- symbol 단위 집계 대조: 브로커는 단일 계좌(dcy-003 불변식 B)라 잔고가 symbol 단위 집계 → portfolio-hub per-portfolio 포지션을 symbol 로 합산해 broker per-symbol
- 반영 위치:
# 4영향 범위,# 5FR-7,# 6R-5. - 참고: docs/broker/kis/rest/국내주식/주문/계좌/015_v1_국내주식-006_주식잔고조회.md
- 종료 조건(충족): 정합 기준·회복 규칙·포지션 출처가 FR-7 에 반영됨.
- OQ-9: 전략 ID 필터의 식별자/등록 시맨틱.
- 상태: Answered (2026-06-05)
- 결정:
- 식별자 형태: 전략 모듈이 넘기는
strategy_key는 사람이 읽을 수 있으면서 ID 역할을 하는 유일·안정 값(human-readable stable slug, 예:target-value-v1)이다. 'display name vs opaque ID' 이원화 없이 이 slug 자체가 유일 키다. 매칭은 대소문자 구분 정확 일치(exact).- 명명(2026-06-05): 파라미터명을
strategy_id→strategy_key로 변경한다. portfolio-hub 의strategy_id(그쪽 내부 전략 엔티티 ID)와 이름이 겹쳐 혼동되므로 분리한 것이다.strategy_key는 trex-core 요청 파라미터(전략 모듈 자기 식별자)이며, 그 값은 자산의 portfolio-hubactive_strategy_id와 매칭된다.
- 명명(2026-06-05): 파라미터명을
- 등록 판정: 자산의 단일
active_strategy_id와 동등 비교(strategy_key == active_strategy_id). 한 자산은 한 시점에 하나의 active 전략에 귀속한다(portfolio-hubactive_strategy_id단일, A-5). 자산당 다중 전략 등록(예: buy/sell 분리, trex-core#1)은 현재 범위 밖이며 필요해지면 '등록 목록' 필드로 확장(후속).
- 식별자 형태: 전략 모듈이 넘기는
- 반영 위치:
# 5FR-8,# 3시나리오. - 종료 조건(충족): 식별자 형태·등록 판정이 FR-8 에 반영됨.
- OQ-10: 런타임 pause/resume 메커니즘.
- 상태: Answered (2026-06-05)
- 결정:
- 단위:
portfolio_id(= asset_id) — universe 항목 키. (symbol 은 다중 portfolio 공유 시 모호하므로 portfolio_id 기준.) - 제어 방식: 운영자 admin 액션으로 CLI + dcy-006 RPC 제어 메시지 둘 다 제공(로컬 CLI + 원격 ops). config 리로드는 비채택.
- 영속성: Postgres 영속(기존 단일 store), portfolio_id 키. daemon 재시작 후 유지, 일일 스냅샷 refresh 와 무관(refresh 는 universe/포지션만 갱신, pause overlay 불변).
- 적용 범위: 전역 — 모든 전략 요청에서 마스킹(운영자 안전 액션, 어느 전략이 묻든 거래 금지).
- resume: pause 는 request-time mask 라 resume 시 다음 요청부터 즉시 응답 복귀(일일 갱신 대기 불요).
- pause(수동·영속) vs warning(자동·일시, OQ-8)은 독립 overlay 이며 OR 합성(둘 중 하나라도면 마스킹).
- 단위:
- 반영 위치:
# 4영향 범위,# 5FR-9. - 종료 조건(충족): pause 단위·제어 방식·영속성·적용 범위가 FR-9 에 반영됨.
- OQ-11: capacity 소진 시 응답 시맨틱.
- 상태: Answered (2026-06-05) — 방향 확정, 파라미터 설계는 OQ-12 로 분리.
- 결정: capacity(가용 슬롯)가 모두 소진됐을 때의 응답은 고정값이 아니라 전략 모듈이 요청 시 지정하는 view 모드(파라미터)에 따라 달라진다. 최소 3개 모드: (a) 빈 리스트
[](신규 매수 후보가 목적인 전략 — 살 게 없음), (b) 슬롯 점유 자산만 (보유 포지션 관리·매도/조절 전략 — 자기가 들고 있는 것만), (c) portfolio 스냅샷 − 슬롯 점유 자산 (점유 안 된 잔여 universe). 같은 view 모드 개념은 capacity 0 이 아닐 때도 응답 구성을 좌우한다(이 파라미터는 strategy_key 필터(FR-8)와 별개의 두 번째 요청 파라미터). (→ OQ-12 에서 enumall/buyable/held으로 확정.) - 반영 위치:
# 5FR-2·FR-3,# 6OQ-12. - 잔여(→ OQ-12): 파라미터 이름/enum 값, 기본 모드(생략 시), 각 모드와 마스킹(pending·warning·pause, OQ-4)·strategy_key 필터의 합성 규칙, capacity 신호 메타 필드(OQ-6) 동반 여부.
- OQ-12: 응답 view/scope 파라미터 설계.
- 상태: Answered (2026-06-05)
- 결정:
- 파라미터: 요청 payload
view(dcy-006 OQ-2 매핑), enum =all|buyable|held. base 집합 —all= universe 전체(portfolio-hub 보유 + holding 슬롯),buyable= universe − 슬롯 점유(미점유 신규 진입 후보),held= holding 슬롯 자산만. (1차안의remainder는buyable과 동일이라 drop.) - 기본 모드: 생략 시
all(OQ-4/FR-2 holding 포함·FR-8 전체와 정합). - 마스킹 직교·균일: 모드는 base 집합만 고르고, 마스킹(pending·warning·pause)은 전 모드 균일 적용. pending 은 모든 모드 마스킹 →
held= holding 만(미체결 제외). warning/pause 도 전 모드 마스킹. - strategy_key 직교:
view와strategy_key(FR-8)는 독립 합성 —assets = base(view) − mask(pending·warning·pause) − (strategy_key 필터, 주어지면). - capacity: envelope 에
available_slots항상 포함(OQ-6).buyable은 capacity-gated —available_slots=0이면[](OQ-11 정합).held엔 정보성. - 전략 타입 매핑(trex-core#1):
buyable=매수,held=position/매도,all=통합.
- 파라미터: 요청 payload
- 반영 위치:
# 5FR-2·FR-3·FR-8. - 종료 조건(충족): 파라미터 스펙·기본 모드·합성 규칙이 FR-2/FR-3/FR-8 에 반영됨.
오픈 질문 처리 기준
Open: 아직 답이 없고 다음 단계 진행에 영향이 있는 상태다. 문서에 유지한다.Answered: 답이 확정된 상태다. 관련 본문/요구사항에 반영한 뒤 오픈 질문 목록에서는 제거하거나 resolved 기록으로 이동한다.Deferred: 지금 단계에서 결정하지 않고 다음 단계 또는 후속 이슈로 넘기는 상태다. 넘길 대상과 이유를 반드시 적는다.Dropped: 더 이상 유효하지 않은 질문이다. 삭제 사유를 짧게 남기고 목록에서 제거한다.- 처리 원칙 1: 질문만 남기지 말고 반드시
상태,처리 방안,종료 조건을 함께 적는다. - 처리 원칙 2: 답이 나온 질문은 본문 반영 없이 단순 삭제하지 않는다. 반영 위치를 먼저 갱신한다.
- 처리 원칙 3: High 영향 질문은
Deferred처리 후 처리 방안에 REQ 인계 추적 방식을 명시한다.
7. 초기 성공 기준
이 변경이 성공했다고 판단할 기준을 측정 가능하게 정의한다. 가능하면 데이터 출처까지 연결한다.
- S-1: stub portfolio-hub read 로, 스냅샷 API 가 portfolio-hub 종목을 포함한 전략 실행 대상 자산 리스트를 반환한다.
- S-2: holding 슬롯 자산은 응답에 포함되고, pending 슬롯 자산은 마스킹되어 제외된다(slot overlay).
- S-3: 가용 슬롯 0 일 때
view=all/held는 보유 자산을 계속 반환하고view=buyable은[]를 반환하며,available_slots=0meta 가 신호된다(OQ-11/OQ-12). - S-4: 도메인 import 스윕에서 portfolio-hub/네트워크 의존이 0 으로 유지된다.
- S-5: interface-contract/runtime-flows/project-structure 가 read 레이어와 정합한다.
- S-6: 스냅샷 페치 시 portfolio-hub 포지션과 브로커 계좌 포지션이 불일치하는 자산이
warning으로 표시되고 응답 리스트에서 마스킹된다. - S-7:
strategy_key를 넣어 호출하면 해당strategy_key가 등록된 자산만, 생략하면 전체 자산이 반환된다. - S-8: 자산을 런타임에 pause 하면 응답 리스트에서 마스킹되고, resume 하면 다시 포함된다.
- S-9: portfolio-hub 가 불가해 권위 cash/position 유지가 불가하면 응답에
degraded=true+ reason code 가 실려 신규 매매 보류를 신호한다(OQ-5). - S-10: 요청
view(all/buyable/held)별로 base 집합이 다르고 마스킹(pending·warning·pause)은 전 모드 균일 적용된다(OQ-12). - S-11: 응답이
assets(항상 배열, 비면[]·null 아님) + meta(view·available_slots·degraded·reason) envelope 형태다(OQ-6). - S-12: 별도
TREX_DAEMON_ID없이 portfolio-hub 토큰에서 도출한 daemon_id 로 universe 를 조회한다(OQ-7).
측정 방식 및 데이터 출처
각 성공 기준을 어떻게 측정할지와 어떤 데이터나 문서를 근거로 쓸지 적는다.
- S-1 측정 방식: stub read 통합테스트(스냅샷 응답) / 데이터 출처: 테스트 결과, 응답 본문.
- S-2 측정 방식: 슬롯 holding/pending 진입 후 스냅샷 호출 테스트 / 데이터 출처: 응답 리스트(holding 포함·pending 마스킹) vs 슬롯 상태.
- S-3 측정 방식: 슬롯 소진(capacity 0) 후 스냅샷 호출 테스트 / 데이터 출처: 응답 리스트·capacity 신호 메타(OQ-11 확정안 기준).
- S-4 측정 방식: 정적 import 스윕 / 데이터 출처:
trex_core/domainimport 목록. - S-5 측정 방식: 문서-코드 정합 점검(리뷰) / 데이터 출처: baseline 문서 vs 실제 메시지 계약(RPC)/read 포트.
- S-6 측정 방식: 포지션 불일치 주입 후 스냅샷 페치 테스트 / 데이터 출처: 자산
warning상태·응답 리스트. - S-7 측정 방식:
strategy_key유/무 호출 비교 테스트 / 데이터 출처: 응답 리스트 vs 자산active_strategy_id. - S-8 측정 방식: pause→호출→resume→호출 시퀀스 테스트 / 데이터 출처: 응답 리스트 포함/제외 여부.
- S-9 측정 방식: portfolio-hub 불가(권위 유지 불가) 주입 후 호출 테스트 / 데이터 출처: 응답
degraded·reason필드. - S-10 측정 방식:
view값별 호출 비교 테스트 / 데이터 출처: 응답 base 집합 vs 슬롯 상태·마스킹. - S-11 측정 방식: 응답 스키마 검증(빈 응답 포함) / 데이터 출처: 응답 envelope 구조(
assets타입·meta 필드). - S-12 측정 방식:
TREX_DAEMON_ID미설정 기동 후 universe 조회 테스트 / 데이터 출처: 토큰 도출 daemon_id vs 조회된 portfolio 집합.
8. REQ로 넘기기 전 확인 체크
REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 확인한다. 미완료 항목은 후속 결정이 필요함을 뜻한다.
- 범위(In Scope/Out of Scope) 확정 — 특히 전략 의사결정 로직·reconciliation 제외
- 기준: 운영자가 read 슬라이스 경계를 확인했다.
- 성공 지표의 측정 방식/데이터 출처 확정
- 기준: S-1~S-12 측정 방식·출처 명시(본 문서 # 7).
- High 영향 리스크 우선순위 및 해소 책임자 지정
- 기준: 본 Discovery 의 리스크는 현재 전부 Medium/Low. 신선도(R-1)·race(R-2)·가용성(R-3) 처리 방침을 운영자가 확인.
- 모든 오픈 질문(OQ)
Open상태 없음 확인- 기준: OQ-1
OQ-12 이 전부 Answered/Deferred 처리(현재 OQ-1OQ-12 전부 Answered).
- 기준: OQ-1
- Freeze 확정 담당자(CONFIRMED_BY) 지정
- 기준: 운영자가 confirm-discovery 시점에 CONFIRMED_BY 를 지정한다.
9. 파일 처리 결과
이 Discovery 문서 생성/갱신 결과와 참조 문서를 기록한다. 이후 추적과 리뷰 시 근거로 사용한다.
- 처리 결과: 생성
- 생성 경로: docs/discovery/dcy-005_20260604_portfolio-hub-asset-snapshot-api.md
- 참조 문서:
- docs/discovery/dcy-004_20260604_portfolio-hub-ledger-integration.md (write 방향 — 본 Discovery 가 read 방향으로 이어받음)
- docs/discovery/dcy-006_20260605_strategy-module-rabbitmq-transport.md (confirmed — 스냅샷 read 의 전달 transport(RabbitMQ RPC, HTTP 0). 본 Discovery 는 logical 계약, dcy-006 은 transport)
- docs/interface-contract.md, docs/runtime-flows.md, docs/project-structure.md (baseline — 본 Discovery 가 갱신 요구)
- trex-orbit
app/clients/portfolio_client.py(get_assets/get_portfolios read 계약 참고)
10. 사용자 결정 필요 항목 요약
사용자나 승인자가 결정해야 하는 항목만 모아 정리한다. 결정, 확인, 데이터 요청을 구분해서 적는다.
DECIDE
스냅샷 응답 단위/필드→ 결정됨: daemon 관리 portfolio universe(symbol/asset_id/현금/포지션/active_strategy_id/전략파라미터 opaque JSON), 가격 제외 (OQ-1, 2026-06-04)신선도 정책→ 결정됨: universe·전략파라미터 설정 시각 일일 캐시 + 현금/포지션 baseline + 체결 시 portfolio-hub write 응답의 권위 cash/position 갱신(옵션 D) (OQ-2, 2026-06-04 / 옵션 C→D 개정 2026-06-05)daemon_id 주입→ 결정됨: portfolio-hub 자격증명/토큰에서 도출(단일 진실원), 별도TREX_DAEMON_ID불필요; .env override 는 테스트 한정 + 토큰 일치 fail-closed (OQ-7, 2026-06-05)빈 표현 시맨틱→ 결정됨: envelope +assets: [](null 금지), meta(view·available_slots·degraded·reason)로 의미 분리 (OQ-6, 2026-06-05)capacity 0 시 응답 재정의→ 결정됨: 응답 형태를 전략 모듈 요청 view 모드 파라미터로 선택(빈[]/ 슬롯 점유 자산만 / portfolio−점유) (OQ-11, 2026-06-05)응답 view/scope 파라미터→ 결정됨:view=all(기본)|buyable|held, strategy_key 직교 합성, 마스킹 전 모드 균일(held=holding만),available_slots메타(buyable은 0이면[]) (OQ-12, 2026-06-05)전략 ID 시맨틱→ 결정됨: 사람이 읽는 안정 유일 slug(예:target-value-v1), 자산의 단일active_strategy_id와 정확 일치; 다중 등록은 범위 밖 (OQ-9, 2026-06-05)pause/resume 메커니즘→ 결정됨: portfolio_id 단위, CLI + dcy-006 RPC 제어, Postgres 영속(refresh 무관), 전역, 즉시 복귀 (OQ-10, 2026-06-05)
CONFIRM
read 슬라이스 범위 경계→ 확정됨(2026-06-06, Justin): In Scope(portfolio-hub read 포트/어댑터·스냅샷 read 계약·마스킹·view 모드·옵션 D·정합 검증·baseline) / Out of Scope(전략 의사결정 로직·reconciliation·safety floor) 확인core 경유 정책→ 결정됨: 모든 소비자 trex-core 경유, portfolio-hub 직접 호출 금지 (OQ-3, 2026-06-04)in-flight 합성/제외 규칙→ 결정됨: holding 포함, pending·warning·pause 마스킹, core 권위 우선 (OQ-4, 2026-06-05)가용성 degrade 정책→ 결정됨: 권위 cash/position 유지 불가 시 명시적 degraded 신호 + 신규 매매 보수적 중단, core 보호 매도 유지 (OQ-5, 2026-06-05). dcy-006 OQ-4 와 공통 보수적 안전.
DATA
portfolio-hub read 계약 확인→ 검증됨(2026-06-06, confirm-discovery): portfolio-hubapp/api/v1_portfolios.pylist_portfolios_v1(GET)이managed_by_daemon_id=auth.daemon_id로 daemon universe 스코프 조회를 제공하고,PortfolioItem에cash_balance·asset_quantity·managed_by_daemon_id포함. 전략파라미터는strategy_paramsJSONB(A-5).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
REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다. 내용 재작성 없이 원본 섹션을 링크로 대신한다.
섹션 참조
- 범위: # 5. 요구사항 목록 > 범위 경계
- 리스크/오픈 질문: # 6. 리스크/가정 목록
- 성공 기준: # 7. 초기 성공 기준
- 인계 전 확인: # 8. REQ로 넘기기 전 확인 체크
- 사용자 결정 필요: # 10. 사용자 결정 필요 항목 요약
Freeze 플래그
- Handoff Decision: 승인
- Handoff Rationale: OQ-1(스냅샷 정의)·OQ-2(신선도, 옵션 D — portfolio-hub#8 write 응답
*_after확정)·OQ-3(core 경유 정책)·OQ-4(in-flight 합성 — holding 포함, pending·warning·pause 마스킹)·OQ-5(가용성 degrade — 권위 cash/position 유지 불가 시 degraded 신호+보수적 중단)·OQ-6(빈 표현 — envelope +assets: []/meta)·OQ-7(daemon_id — 토큰 도출 단일 진실원)·OQ-8(정합 — 수량 정확 일치·symbol 집계·자동 해제·KIS 잔고조회)·OQ-9(전략 ID — 사람이 읽는 안정 slug·단일 active 일치)·OQ-10(pause — portfolio_id·CLI+MQ·Postgres 영속·전역·즉시 복귀)·OQ-11(capacity 0 응답 — view 모드 파라미터)·OQ-12(view 파라미터 —all/buyable/held+ envelope) 전부 Answered. 남은 인계 전 항목은 범위 경계 확인(운영자)과 CONFIRMED_BY 지정이며, confirm-discovery 에서 확정 후 REQ Drafting 으로 인계한다. - Ready for REQ Drafting: true
- Confirmed By: Justin
- Confirmed At (KST): 2026-06-06 00:08