REQ-112: BrokerCredentialPayload/TradingContext 데이터클래스 + PortfolioHubReadPort.fetch_broker_credential() 포트 메서드 + hub_read 어댑터(_fetch_user_info 헬퍼 추출) + stub_read 결정론 stub REQ-113: DaemonInstanceConfig/DaemonConfig.credential_fetch_enabled opt-in(기본 False) + TOML 파싱 + break-glass(TREX_KIS_BROKER_BREAK_GLASS) 감지 REQ-114: SnapshotScheduler._maybe_fetch_credential() — fail-closed 3조건(unavailable/broker-null/trading_disabled) + CRITICAL 알람 DAY_START_LOAD_FAILED + IntentService entry_allowed_fn 게이트(BuyIntent 차단, SellIntent 통과) REQ-115: 그날 고정 캐시(_cached_credential/_credential_date) — 동일 날짜 재fetch 방지 + app_secret 메모리 한정(Redis 미기록) 스텁 기반 구현: 실 portfolio-hub/네트워크 없이 AC 전부 단위 테스트. 회귀: credential_fetch_enabled=False(기본) 경로 byte-for-byte 보존. 745 passed, 47 skipped (0 failed). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
5.5 KiB
5.5 KiB
BAT-053: Implementation
변경 파일 목록
신규/확장 모듈
| 파일 | 변경 유형 | 관련 REQ |
|---|---|---|
trex_core/application/portfolio.py |
확장 | REQ-112 |
trex_core/adapters/portfolio/hub_read.py |
확장 | REQ-112 |
trex_core/adapters/portfolio/stub_read.py |
확장 | REQ-112 |
trex_core/runtime/config.py |
확장 | REQ-113 |
trex_core/application/snapshot_scheduler.py |
확장 | REQ-113/114/115 |
trex_core/application/intents.py |
확장 | REQ-114 |
trex_core/runtime/daemon.py |
확장 (조립) | REQ-113/114 |
신규 테스트
| 파일 | 관련 REQ |
|---|---|
tests/adapters/portfolio/test_hub_read.py (확장) |
REQ-112 AC-1~7 |
tests/adapters/portfolio/test_stub_read.py (확장) |
REQ-112 AC-6 |
tests/runtime/test_instance_config.py (확장) |
REQ-113 AC-1,7 |
tests/application/test_snapshot_scheduler.py (확장) |
REQ-114 AC-1~9, REQ-115 AC-3/6 |
REQ별 구현 요약
REQ-112: BrokerCredentialPayload + TradingContext + 포트 메서드 + 어댑터
application/portfolio.py:BrokerCredentialPayload(account_ref, app_key, app_secret, credential_version)frozen dataclass 추가TradingContext(broker, trading_enabled)frozen dataclass 추가PortfolioHubReadPort.fetch_broker_credential() -> TradingContext포트 메서드 추가
adapters/portfolio/hub_read.py:whoami()내부 로직을_fetch_user_info() -> dict헬퍼로 추출 — 외부 동작 불변(AC-5 회귀 보존)fetch_broker_credential()구현: broker null → TradingContext(broker=None), credential_payload null → TradingContext(broker=None), 성공 → BrokerCredentialPayload 반환- NFR-2: app_secret 평문 로그 없음 — 어댑터 내 로깅 코드에 app_secret 미포함
adapters/portfolio/stub_read.py:trading_context: Optional[TradingContext],fetch_broker_raises: bool인자 추가fetch_broker_credential()stub 구현 — 네트워크 없이 결정론적 반환/예외
REQ-113: credential_fetch_enabled opt-in + break-glass
runtime/config.py:DaemonInstanceConfig.credential_fetch_enabled: bool = False추가 (TOML opt-in, 기본 False)DaemonInstanceConfig.from_toml():credential_fetch_enabled파싱 (부재 시 False)DaemonConfig.credential_fetch_enabled: bool = False추가DaemonConfig.from_config():inst.credential_fetch_enabled→DaemonConfig전파DaemonConfig.from_env(): 변경 없음 → 항상 False (AC-1 회귀 0)
application/snapshot_scheduler.py:- 생성자
credential_fetch_enabled: bool = False인자 추가 - 초기화 시
TREX_KIS_BROKER_BREAK_GLASSenv 감지(break-glass 플래그) - break-glass: INFO 로그 후 env-only 경로(fetch 없이 entry_allowed=True)
- 생성자
REQ-114: 그날-시작 로드 게이트 + fail-closed + 알람
application/snapshot_scheduler.py:_entry_allowed: bool = True상태 추가_maybe_fetch_credential(): fail-closed 3조건 처리 + CRITICAL 알람DAY_START_LOAD_FAILED발신run_once()선두:credential_fetch_enabled=True이면_maybe_fetch_credential()호출get_entry_allowed() -> bool노출
application/intents.py:IntentService생성자entry_allowed_fn: Optional[Callable[[], bool]] = None추가_admit_buy()선두:entry_allowed_fn is not None and not entry_allowed_fn()→ rejectday_start_load_failed- SellIntent(
_admit_sell) 변경 없음 — 청산 경로 영향 없음 (AC-5)
runtime/daemon.py:SnapshotScheduler생성 후entry_allowed_fn = scheduler.get_entry_allowed를 IntentService에 주입credential_fetch_enabled=False(기본) →entry_allowed_fn=None→ 게이트 비활성
REQ-115: 그날 고정 캐시 + 장중 무중단
application/snapshot_scheduler.py:_cached_credential: Optional[BrokerCredentialPayload],_credential_date: Optional[date]추가_maybe_fetch_credential():_credential_date == today시 재fetch 생략 (그날 고정 캐시, AC-3)- 성공 시 캐시 저장 + INFO 로그(account_ref/trading_enabled/credential_version; app_secret 미포함)
- Redis 등 외부 저장소 기록 없음 — 메모리 한정 (AC-5)
설계 결정 반영 확인
- D1(entry_allowed 위치): SnapshotScheduler 내 + IntentService callable 주입 — 구현됨
- D2(credential_fetch_enabled 전파): DaemonInstanceConfig → DaemonConfig → SnapshotScheduler — 구현됨
- D3(우선순위 로직 위치): SnapshotScheduler._maybe_fetch_credential() — 구현됨
- D4(그날 고정 캐시):
_credential_date비교로 동일 날짜 재fetch 방지 — 구현됨 - D5(중장기 후크): BAT-053에서는 단기 07:30 경로(run_once())만 구현. 중장기는 동일 run_once() 경유로 처리됨 — 확인
- D6(AlarmEmitter): publish_alarm("DAY_START_LOAD_FAILED", ...) 직접 호출 패턴 — 구현됨
주요 설계 메모
credential_fetch_enabled=False(기본)일 때:_maybe_fetch_credential()미호출 →_entry_allowed=True고정 → IntentServiceentry_allowed_fn=None→ 게이트 완전 비활성. 기존 동작 byte-for-byte 보존(회귀 0).- KISBroker 재조립(fetch 결과를 실제 KIS API에 적용)은 BAT-053 범위 외 — M3 별도 batch. 현재
_cached_credential이 메모리에 저장되어 있으며 다음 batch에서 KISBroker 재초기화에 활용. - REQ-115 AC-2(ledger_outbox 무손실): 기존 REQ-019 구현 속성 재확인. 별도 구현 불필요, 기존 테스트 그린(745 passed).