feat(config): expose paper broker tuning via env (REQ-010 AC-5, BAT-009)

Wire the existing PaperBroker scenario knobs (fill_after_polls /
qty_per_poll / reject_symbols) to daemon env so REQ-011 AC-2 e2e
scenarios (delayed / partial / reject) can be reproduced by a daemon
restart. Unset == prior PaperBroker() behaviour, byte-for-byte (no
regression).

- config.py: PaperBrokerConfig + from_env parsing (TREX_PAPER_*)
- daemon.py: _build_broker injects config.paper into PaperBroker
- .env.example: document the 3 paper tuning keys (paper-only, e2e)
- tests: paper broker defaults + env tuning parsing

SDLC: REQ-010 CHG-20260606-01 (AC-5 added) / -02 (Implemented);
BAT-009 batch-lite release-candidate; REL-001 feedback handoff records
pre-deployment e2e observations incl. event-publisher idle-drop and
paper correlation_id collision (GitHub #4, #5).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-06 16:49:51 +09:00
co-authored by Claude Opus 4.8
parent f8c5661147
commit 2f57aad7b2
13 changed files with 280 additions and 8 deletions
+6
View File
@@ -14,6 +14,12 @@ TREX_EXECUTION_INTERVAL_SEC=1.0
# 'paper' (deterministic test double, default) | 'kis' (real KIS Open API)
TREX_BROKER=paper
# --- Paper broker tuning (only when TREX_BROKER=paper; for e2e scenarios) ---
# Unset == immediate full fills, no rejects (default production-like behaviour).
# TREX_PAPER_FILL_AFTER_POLLS=0 # polls before fills begin (delayed fill)
# TREX_PAPER_QTY_PER_POLL= # filled qty per poll (partial fill); empty => fill all at once
# TREX_PAPER_REJECT_SYMBOLS= # comma-separated symbols to reject (rollback path)
# --- KIS broker (only when TREX_BROKER=kis) ---
# 모의투자(sandbox): https://openapivts.koreainvestment.com:29443
# 실계좌(live, 실자금): https://openapi.koreainvestment.com:9443 (requires TREX_KIS_ALLOW_LIVE=true)
@@ -0,0 +1,28 @@
# BAT-009 Implementation
- Status: completed
- Batch: BAT-009
## Plan Summary
- REQ-010 CHG-20260606-01 / AC-5: paper broker 튜닝 knob(REQ-011 AC-2 지연/부분/거부)을 데몬 env 로 노출. 미설정=기존 `PaperBroker()` 동작 byte-for-byte(회귀 없음).
## Changed Files
- trex_core/runtime/config.py: `PaperBrokerConfig` frozen dataclass(`fill_after_polls:int=0`, `qty_per_poll:Optional[int]=None`, `reject_symbols:frozenset[str]=frozenset()`). `DaemonConfig.paper` 필드(broker=='paper' 일 때만 참조). `from_env()` 에서 `TREX_PAPER_FILL_AFTER_POLLS`(int, 기본 0), `TREX_PAPER_QTY_PER_POLL`(빈 문자열→None), `TREX_PAPER_REJECT_SYMBOLS`(콤마 분리, 공백/빈 토큰 제거→frozenset) 파싱.
- trex_core/runtime/daemon.py: `_build_broker` paper 분기를 `PaperBroker(fill_after_polls=config.paper.fill_after_polls, qty_per_poll=config.paper.qty_per_poll, reject_symbols=set(config.paper.reject_symbols))` 로 교체.
- .env.example: `TREX_BROKER=paper` 아래 paper 튜닝 키 3개 주석 문서화(미설정=즉시 전량 체결·거부 없음).
- tests/runtime/test_config_dotenv.py: `_KEYS` 에 3개 키 추가, `test_paper_broker_defaults`(미설정=0/None/frozenset()), `test_paper_broker_env_tuning`(FILL_AFTER_POLLS=2, QTY_PER_POLL=4, REJECT_SYMBOLS="005930, 000660 ," → 2/4/{005930,000660}).
## Execution Log
1. `PaperBrokerConfig` dataclass + `DaemonConfig.paper` 필드 추가(기본값 = 기존 `PaperBroker()` 동작).
2. `from_env()` env 파싱(빈 문자열/공백 토큰 정규화).
3. `_build_broker` paper 분기 주입 + `.env.example` 문서화.
4. 단위테스트 2개 추가, 좁은 테스트 → 전체 회귀 → ruff 순서로 검증.
## Validation
- `pytest tests/runtime/test_config_dotenv.py`: **6 passed**(기존 4 + 신규 2).
- 전체 `pytest --deselect ...::test_sandbox_token_and_submit`(네트워크 의존 KIS sandbox 제외): **167 passed, 23 skipped**.
- `ruff check trex_core tests`: All checks passed.
- e2e(별도, verification.md 에 상세): 데몬 env 재기동으로 거부 롤백 `idle→pending_buy→idle`, 지연 체결 `+3.7s`, 부분 체결 qty 6 누적 `+2.5s` 재현.
## Remaining Risks
- 없음(저위험). broker=='paper' 일 때만 참조(kis 경로 무시), 미설정=기존 동작. 빈/공백 토큰 edge 는 단위테스트로 고정.
@@ -0,0 +1,32 @@
# BAT-009: paper broker 튜닝 env 노출 (minor-change)
- Status: release-candidate
- Profile: batch-lite
- Source Discovery: dcy-002 (REQ-010 데몬 설정 편의) — 동기는 dcy-003/REQ-011 e2e 시나리오 재현(Notes 참조)
- Included REQ: REQ-010 (CHG-20260606-01, AC-5)
- Excluded REQ: 없음
## Summary
- REQ-010 CHG-20260606-01 로 추가된 AC-5(paper broker 튜닝 env 노출)를 delivery 하는 minor-change batch-lite. REQ-011 AC-2 가 이미 요구하는 paper 더블의 결정적 시나리오(지연/부분/거부)를 데몬 재기동만으로 재현할 수 있도록, 기존 `PaperBroker` 생성자 knob 을 `.env`/OS env(`TREX_PAPER_FILL_AFTER_POLLS`/`TREX_PAPER_QTY_PER_POLL`/`TREX_PAPER_REJECT_SYMBOLS`)로 노출한다. 미설정이면 기존 `PaperBroker()` 동작과 byte-for-byte 동일(회귀 없음).
## Scope
- In Scope:
- `runtime/config.py`: `PaperBrokerConfig` frozen dataclass + `DaemonConfig.from_env()` 파싱(빈 문자열 → None/frozenset 정규화).
- `runtime/daemon.py`: `_build_broker` paper 분기에서 `config.paper` 주입.
- `.env.example`: paper 튜닝 키 3개 문서화(미설정=production-like 기본).
- `tests/runtime/test_config_dotenv.py`: 기본값(test_paper_broker_defaults) + env 파싱(test_paper_broker_env_tuning).
- Out of Scope:
- PaperBroker 내부 체결 로직 변경(REQ-011 에서 이미 구현·검증).
- kis broker 경로(`broker=kis` 시 paper config 무시).
- 부분 매도(수량 일부) 등 신규 능력(별도 Discovery/REQ).
## Documents
- Planning: [planning.md](./planning.md)
- Design: none (batch-lite with no architecture impact)
- Implementation: [implementation.md](./implementation.md)
- Verification: [verification.md](./verification.md)
## Notes
- minor-change: 기존 PaperBroker 생성자 인자를 config 표면으로 노출하는 additive 변경. BrokerPort 계약·슬롯 런타임 흐름 불변, design 불요.
- 동기 출처: REQ-011(dcy-003) e2e 시나리오 재현 필요. REQ-010(dcy-002) 의 설정 로딩 메커니즘 위에 키를 얹는 형태라 REQ-010 change-req 로 귀속.
- 구현 코드는 본 batch 생성 시점에 이미 working tree 에 존재(미커밋). verification stage 에서 단위테스트(166 pass) + e2e evidence(거부 롤백/지연/부분)를 verification.md 에 정리한다.
@@ -0,0 +1,32 @@
# BAT-009 Planning
- Status: confirmed
- Batch: BAT-009
- Profile: batch-lite
## Included REQ
- REQ-010 (Configuration): 데몬 설정 .env 로딩 — CHG-20260606-01 / AC-5 (paper 튜닝 env 노출)
## Delivery Plan
1. `runtime/config.py`: `PaperBrokerConfig(fill_after_polls:int=0, qty_per_poll:Optional[int]=None, reject_symbols:frozenset[str]=frozenset())` 추가. `DaemonConfig` 에 `paper: PaperBrokerConfig` 필드(broker=='paper' 일 때만 참조). `from_env()` 에서 `TREX_PAPER_FILL_AFTER_POLLS`(int, 기본 0), `TREX_PAPER_QTY_PER_POLL`(빈 문자열→None), `TREX_PAPER_REJECT_SYMBOLS`(콤마 분리, 공백/빈 토큰 제거→frozenset) 파싱.
2. `runtime/daemon.py`: `_build_broker` paper 분기를 `PaperBroker(fill_after_polls=..., qty_per_poll=..., reject_symbols=set(...))` 로 교체.
3. `.env.example`: `TREX_BROKER=paper` 아래 paper 튜닝 키 3개 주석 문서화(미설정=즉시 전량 체결·거부 없음).
4. `tests/runtime/test_config_dotenv.py`: `_KEYS` 에 3개 키 추가, `test_paper_broker_defaults`(미설정=0/None/frozenset()), `test_paper_broker_env_tuning`(FILL_AFTER_POLLS=2, QTY_PER_POLL=4, REJECT_SYMBOLS="005930, 000660 ," → 2/4/{005930,000660}).
## Dependencies
- Internal: REQ-011 PaperBroker(생성자 knob 이미 존재), REQ-010 dotenv 로딩 메커니즘.
- External: 없음.
## Design Gate
- Design Required Before Implementation: no
- Reason: 기존 PaperBroker 생성자 인자를 config/env 표면으로 노출하는 additive wiring. 외부 인터페이스(BrokerPort)·슬롯 런타임 흐름 변경 없음. 미설정 시 기존 동작과 byte-for-byte 동일. batch-lite + design 생략(minor-change).
## Milestones
1. M1 — PaperBrokerConfig + from_env 파싱.
2. M2 — _build_broker 주입 + .env.example 문서화.
3. M3 — 단위테스트 green(기본/파싱) + ruff clean + 전체 회귀(166 pass).
4. M4 — e2e 시나리오 evidence(거부 롤백 `idle→pending_buy→idle`, 지연 `+3.7s`, 부분 qty 누적 `+2.5s`).
## Risks
- R-1 (Low): env 오설정이 production 데몬 동작을 바꿈. 대응: broker=='paper' 일 때만 참조(kis 경로 무시), 미설정=기본 동작, `.env.example` 에 "e2e 전용" 명시.
- R-2 (Low): 빈 문자열/공백 토큰 파싱 edge. 대응: `qty_per_poll` 빈 문자열→None, `reject_symbols` 빈/공백 토큰 제거 — 단위테스트로 고정.
@@ -0,0 +1,27 @@
# BAT-009 Verification
- Status: confirmed
- Batch: BAT-009
## Acceptance Mapping
- REQ-010 (CHG-20260606-01):
- AC1 `.env` 로드 → `test_loads_values_from_dotenv`. **PASS** (BAT-003 기존, 회귀 재확인)
- AC2 OS env 우선 → `test_os_env_takes_precedence_over_dotenv`. **PASS** (BAT-003 기존)
- AC3 `.env` 부재 회귀 → `test_no_dotenv_uses_os_env_only`. **PASS** (BAT-003 기존)
- AC4 `.gitignore` → `.env` 무시 / `.env.example` 추적. **PASS** (BAT-003 기존)
- **AC5 (본 batch)** paper 튜닝 env 파싱 + `_build_broker` 주입, 미설정=기본 동작 byte-for-byte:
- 단위: `test_paper_broker_defaults`(미설정 → fill_after_polls=0, qty_per_poll=None, reject_symbols=frozenset()). **PASS**
- 단위: `test_paper_broker_env_tuning`(FILL_AFTER_POLLS=2 / QTY_PER_POLL=4 / REJECT_SYMBOLS="005930, 000660 ," → 2 / 4 / {005930,000660}). **PASS**
- e2e(데몬 env 재기동): 거부(`TREX_PAPER_REJECT_SYMBOLS=999999`) → `idle→pending_buy→idle` 롤백; 지연(`TREX_PAPER_FILL_AFTER_POLLS=3`) → `pending_buy→holding +3.7s`; 부분(`TREX_PAPER_QTY_PER_POLL=2`, qty 6) → holding qty 6 누적 `+2.5s`. **PASS**
## Evidence
- Test: `pytest tests/runtime/test_config_dotenv.py` 6 passed(기존 4 + 신규 2). 전체 `pytest --deselect ...::test_sandbox_token_and_submit` **167 passed, 23 skipped**. `ruff check trex_core tests` All checks passed.
- e2e: 실 PostgreSQL + RabbitMQ + portfolio-hub(dev) 대상 데몬 e2e. 슬롯 전이 trail(`slot_transition_log`)·timestamp gap 으로 시나리오별 결정성 확인. (REL-001 Feedback Handoff Observation Summary 와 동일 evidence)
## no architecture impact confirmed
- batch-lite, planning Design Gate=no. 기존 `PaperBroker` 생성자 인자를 config/env 표면으로 노출하는 additive wiring 뿐 — BrokerPort 계약·슬롯 런타임 흐름 변경 없음. 미설정 시 기존 동작 byte-for-byte. baseline 문서(`docs/project-structure.md`/`docs/runtime-flows.md`) 영향 없음.
## Result
- Ready for Release: true
- Blocking Issues:
- 없음. e2e 는 paper broker 한정(운영 KIS 경로 무영향). REL-001 배포 scope 와 분리된 후속 release 로 배포.
+1
View File
@@ -16,3 +16,4 @@
| BAT-006 | standard | release-candidate | REQ-017, REQ-018, REQ-019, REQ-020 | dcy-004 | [bat-006](./bat-006_20260604_portfolio-hub-ledger-integration/) |
| BAT-007 | standard | release-candidate | REQ-021, REQ-022, REQ-023, REQ-024, REQ-025, REQ-026 | dcy-006 | [bat-007](./bat-007_20260605_strategy-module-rabbitmq-transport/) |
| BAT-008 | standard | release-candidate | REQ-027, REQ-028, REQ-029, REQ-030, REQ-031, REQ-032, REQ-033 | dcy-005 | [bat-008](./bat-008_20260606_portfolio-hub-snapshot-read-layer/) |
| BAT-009 | batch-lite | release-candidate | REQ-010 (CHG-20260606-01 / AC-5) | dcy-002 | [bat-009](./bat-009_20260606_paper-broker-env-tuning/) |
+1 -1
View File
@@ -8,4 +8,4 @@
| REL ID | Profile | Status | Included Batch | Discovery/REQ Input | Link |
| --- | --- | --- | --- | --- | --- |
| REL-001 | app-service | confirmed | BAT-001~008 | Discovery: orb-v1 전략 모듈 / Change-req: REQ-030 옵션 D, 전략 필드 실데이터 | [rel-001](rel-001_20260606_trex-core-slot-core-baseline.md) |
| REL-001 | app-service | confirmed | BAT-001~008 | Discovery: orb-v1 전략 모듈, 부분 매도(#5) / Change-req: REQ-030 옵션 D, 전략 필드 실데이터, 이벤트 publisher idle 견고성, paper execution_id Redis(#4) | [rel-001](rel-001_20260606_trex-core-slot-core-baseline.md) |
@@ -91,12 +91,18 @@
- <기록 시각>
## Feedback Handoff
> 주의: 아래는 **배포 전(pre-deployment) paper-broker e2e/smoke 관찰**이다. REL-001 은 아직 `released` 가 아니므로(Release Execution Evidence 미기재) release status 는 `confirmed` 유지하며, capture-release-feedback 의 status 전환(`feedback-captured` / batch `released`)은 실제 배포 후 수행한다.
- Observation Summary:
- <운영 관찰 사실 요약 또는 없음>
- paper-broker e2e (2026-06-06): admission/read 그룹(멱등성·매수 거부 no_idle_slot·매도 거부 slot_not_found·pause/resume RPC·pause 는 intent 비차단·snapshot buyable 마스킹·happy-path 이벤트 캡처)과 broker-behavior 그룹(거부 롤백 `idle→pending_buy→idle`·지연 체결 `+3.7s`·부분 체결 qty 누적 `+2.5s`) 전부 통과. snapshot/pause RPC envelope·ledger outbox·슬롯 전이 정상.
- 견고성 관찰: 데몬 부팅 후 오래 idle 였던 이벤트 publisher pika 연결이 **첫 발행 시 `StreamLostError(EOF)` 로 1회 끊겼다가 자동 복구**(best-effort 경로, 롤백 없음, 슬롯/ledger 정합 영향 없음). 첫 이벤트 1건 유실 가능.
- Discovery Input:
- 전략 모듈(orb-v1) Discovery — REQ-029 CHG-04 소비 참고 사항 입력.
- 부분 매도(한 슬롯 수량 일부) 능력 — 현재 미구현(전량 매도만). 신규 Discovery 후보. (GitHub #5)
- REQ Input:
- <후속 req 후보>
- Change Request Input:
- REQ-030 옵션 D 활성화(portfolio-hub#8 배포 후) — cash/position `*_after` 갱신.
- 전략 필드 실데이터 채움(2번째 전략 도입 시) — REQ-027/REQ-029 연계.
- 이벤트 publisher idle 연결 견고성(BAT-007 / REQ-022·024) — 발행 전 heartbeat/재연결 또는 idle 후 첫 발행 재시도로 첫 이벤트 유실 방지. (배포 전 관찰)
- paper 모드 마지막 execution_id Redis 영속 — 재기동 시 PAPER seq 리셋에 의한 correlation_id 충돌(ON CONFLICT dedup) 회피. paper 전용 테스트 견고성(운영 KIS odno 는 고유라 무해). (GitHub #4)
@@ -16,11 +16,12 @@
2. Given OS env 와 `.env` 에 같은 키가 다른 값으로 존재, When `from_env()`, Then OS env 값이 우선한다(.env 가 덮어쓰지 않음).
3. Given `.env` 가 없음, When `from_env()`, Then 기존처럼 `os.environ` 만으로 동작한다(회귀 없음).
4. Given 저장소, When `.gitignore` 확인, Then `.env` 가 무시 대상이며 `.env.example` 은 추적된다.
5. Given `broker=paper` 이고 `.env`/OS env 에 `TREX_PAPER_FILL_AFTER_POLLS`/`TREX_PAPER_QTY_PER_POLL`/`TREX_PAPER_REJECT_SYMBOLS` 가 설정, When `DaemonConfig.from_env()` 후 `_build_broker`, Then paper 더블이 해당 시나리오(지연/부분/거부, REQ-011 AC-2)로 구성된다. 미설정이면 `PaperBroker()` 기본(즉시 전량 체결·거부 없음)과 byte-for-byte 동일하다(회귀 없음).
## Impacted Area
- Docs: 없음(baseline 구조/인터페이스/런타임 흐름 변경 없음 — minor-change)
- Modules: pyproject.toml(python-dotenv), trex_core/runtime/config.py(load_dotenv), .gitignore, .env.example(신규)
- Tests: tests/runtime/test_config_dotenv.py (로드/우선순위/부재 회귀)
- Modules: pyproject.toml(python-dotenv), trex_core/runtime/config.py(load_dotenv, PaperBrokerConfig), trex_core/runtime/daemon.py(_build_broker paper 주입), .gitignore, .env.example(신규 + paper 튜닝 키)
- Tests: tests/runtime/test_config_dotenv.py (로드/우선순위/부재 회귀, paper 튜닝 기본/파싱)
## Notes
- minor-change fast path: 단일 저위험 Configuration REQ, 구조/인터페이스/런타임 흐름 영향 없음(기동 시 env 소스 추가뿐). batch-lite 로 처리, design 불요.
@@ -151,3 +152,86 @@
#### Validation Plan
- BAT-003 verification evidence 재참조로 충분.
### CHG-20260606-01
- Date: 2026-06-06
- Author: Justin Ko
#### Change Summary
- paper broker 튜닝 knob(REQ-011 AC-2 의 지연/부분/거부 시나리오)을 데몬 env 로 노출하는 신규 scope 추가. `DaemonConfig.from_env()` 가 `TREX_PAPER_FILL_AFTER_POLLS`/`TREX_PAPER_QTY_PER_POLL`/`TREX_PAPER_REJECT_SYMBOLS` 를 `PaperBrokerConfig` 로 파싱하고 `_build_broker` 가 paper 더블에 주입한다. 미설정이면 기존 `PaperBroker()`(즉시 전량 체결·거부 없음)와 byte-for-byte 동일(회귀 없음).
#### Intent
- e2e 시나리오(주문 거부 롤백/지연 체결/부분 체결)를 데몬 재기동만으로 재현할 수 있도록 REQ-011 의 결정적 더블 knob 을 운영 설정 표면(REQ-010 .env 로딩)에 연결. REQ-011 본문은 이미 시나리오를 요구(AC-2)하므로 broker 요구사항 변경이 아니라 Configuration 노출.
#### Acceptance Criteria Delta
- Added:
- AC-5: paper 튜닝 env 파싱 + `_build_broker` 주입, 미설정 시 기본 동작 byte-for-byte 동일(회귀 없음)
- Updated:
- 없음
- Removed:
- 없음
#### Impacted Area Delta
- Docs:
- docs/srs/index.md (Register 상태·Recent Change Log Summary)
- Modules:
- trex_core/runtime/config.py (PaperBrokerConfig dataclass + from_env 파싱), trex_core/runtime/daemon.py (_build_broker 주입), .env.example (paper 튜닝 키 3개 문서화)
- Tests:
- tests/runtime/test_config_dotenv.py (test_paper_broker_defaults, test_paper_broker_env_tuning)
#### Delivery Trace Delta
- Batches:
- BAT-009 (batch-lite, tooling — 신설 예정; AC-5 delivery/verification 담당)
- Releases:
- REL-001 영향 없음: REL-001 이 포함한 REQ-010 coverage 는 AC-1~4(.env 로딩 메커니즘, BAT-003)로 그대로 유효. AC-5 는 REL-001 이후 신규 scope 이며 별도 release 로 배포.
#### Revalidation Impact
- Reverification Needed: yes (신규 AC-5 한정; 기존 AC-1~4 evidence(BAT-003)는 무효화되지 않음)
- Existing Implementation Invalidated: no
- Status Recommendation: Implemented → Approved (신규 AC-5 는 BAT-009 verification 전까지 미확정)
- Follow-up Action: draft-batch 로 BAT-009(batch-lite, tooling) 생성 → verification → confirm-req-implemented 로 Approved → Implemented 재전환
#### Validation Plan
- 단위: test_paper_broker_defaults(미설정=기본), test_paper_broker_env_tuning(3 env 파싱) — 이미 작성·통과(166 pass).
- e2e: 데몬 env 재기동으로 거부 롤백(`idle→pending_buy→idle`), 지연 체결(`+3.7s`), 부분 체결(qty 누적 `+2.5s`) 재현 — BAT-009 verification 에 evidence 기록.
### CHG-20260606-02
- Date: 2026-06-06
- Author: Justin Ko
#### Change Summary
- Status Approved → Implemented (confirm-req-implemented, 근거 batch BAT-009 release-candidate).
#### Intent
- BAT-009 verification.md 의 AC↔evidence(AC-1~4 BAT-003 회귀 재확인 + AC-5 단위 2개·e2e 거부/지연/부분)로 전체 AC 충족 → 구현 완료 동기화.
#### Acceptance Criteria Delta
- Added:
- 없음
- Updated:
- 없음
- Removed:
- 없음
#### Impacted Area Delta
- Docs:
- docs/srs/index.md (Register Implemented)
- Modules:
- 없음
- Tests:
- 없음
#### Delivery Trace Delta
- Batches:
- BAT-009 (release-candidate)
- Releases:
- none (REL-001 과 분리된 후속 release 로 배포 예정)
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current (Implemented)
- Follow-up Action: draft-release 로 BAT-009 배포 편성(원하는 시점에).
#### Validation Plan
- BAT-009 verification evidence 재참조로 충분.
+2 -2
View File
@@ -117,8 +117,8 @@
## Recent Change Log Summary
- 2026-06-06 :: REQ-010 CHG-20260606-02 — Status Approved → Implemented (confirm-req-implemented, 근거 BAT-009 release-candidate). AC-1~4 BAT-003 회귀 + AC-5 단위 2개·e2e(거부/지연/부분) evidence 충족. (confirm-req-implemented)
- 2026-06-06 :: REQ-010 CHG-20260606-01 — paper broker 튜닝 knob(REQ-011 AC-2 지연/부분/거부)을 데몬 env(`TREX_PAPER_FILL_AFTER_POLLS`/`QTY_PER_POLL`/`REJECT_SYMBOLS`)로 노출(AC-5 신설). 미설정=기존 동작 byte-for-byte. 신규 AC 한정 Implemented → Approved(BAT-009 batch-lite verification 대기). REL-001 의 AC-1~4 coverage 는 유효. (change-req)
- 2026-06-06 :: REQ-017 CHG-20260606-01 — 실 portfolio-hub ledger write 계약 정정(`POST /v1/portfolios/{portfolio_id}/ledger/trades`, body `quantity`/`fee_amount`/`tax_amount`, 성공 201). 구 orbit 가정(`/v1/assets/{asset_id}`, `qty`/`fee`/`tax`) 폐기. 정정 구현 재검증 완료(unit 16 + 실 e2e 201)·Implemented 유지. REL-001 ledger blocker 해소. (change-req)
- 2026-06-06 :: REQ-027 CHG-04 — 실 portfolio-hub 계약 정정(whoami `/v1/user_info`, `{portfolios}` envelope, 클라이언트측 daemon 필터, 전략 필드 None/`{}` 이월). 정정 구현 재검증 완료·Implemented 유지. (change-req)
- 2026-06-06 :: REQ-029 CHG-04 — orb-v1 소비 참고 사항 추가(strategy_key 없이 view=buyable, 전략 필드 부재 무해). 문서 전용·Implemented 유지. (change-req)
- 2026-06-06 :: REQ-027~029, REQ-031~033 Approved → Implemented — BAT-008 release-candidate verification evidence 기반. REQ-030 은 AC-1 evidence 확보·AC-2/AC-3 portfolio-hub#8 배포 대기로 Approved 유지. (confirm-req-implemented)
- 2026-06-06 :: REQ-027~033 Proposed → Approved — dcy-005 confirmed, 전체 승인 게이트 통과. REQ-030 옵션 D 구현은 portfolio-hub#8 배포 후 착수. (confirm-req)
+28 -1
View File
@@ -5,7 +5,11 @@ import pytest
from trex_core.runtime.config import DaemonConfig
_KEYS = ("TREX_POSTGRESQL_DSN", "TREX_TOTAL_SLOTS", "TREX_API_HOST", "TREX_API_PORT")
_KEYS = (
"TREX_POSTGRESQL_DSN", "TREX_TOTAL_SLOTS", "TREX_API_HOST", "TREX_API_PORT",
"TREX_PAPER_FILL_AFTER_POLLS", "TREX_PAPER_QTY_PER_POLL",
"TREX_PAPER_REJECT_SYMBOLS",
)
@pytest.fixture(autouse=True)
@@ -48,3 +52,26 @@ def test_missing_dsn_raises(tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
with pytest.raises(RuntimeError):
DaemonConfig.from_env()
def test_paper_broker_defaults(tmp_path, monkeypatch):
"""Unset paper env == immediate full fills, no rejects (REQ-011)."""
monkeypatch.chdir(tmp_path)
monkeypatch.setenv("TREX_POSTGRESQL_DSN", "dbname=x")
cfg = DaemonConfig.from_env()
assert cfg.paper.fill_after_polls == 0
assert cfg.paper.qty_per_poll is None
assert cfg.paper.reject_symbols == frozenset()
def test_paper_broker_env_tuning(tmp_path, monkeypatch):
"""Paper broker scenario knobs are parsed from env (REQ-011)."""
monkeypatch.chdir(tmp_path)
monkeypatch.setenv("TREX_POSTGRESQL_DSN", "dbname=x")
monkeypatch.setenv("TREX_PAPER_FILL_AFTER_POLLS", "2")
monkeypatch.setenv("TREX_PAPER_QTY_PER_POLL", "4")
monkeypatch.setenv("TREX_PAPER_REJECT_SYMBOLS", "005930, 000660 ,")
cfg = DaemonConfig.from_env()
assert cfg.paper.fill_after_polls == 2
assert cfg.paper.qty_per_poll == 4
assert cfg.paper.reject_symbols == frozenset({"005930", "000660"})
+25
View File
@@ -40,6 +40,19 @@ class PortfolioHubConfig:
timeout: float = 5.0
@dataclass(frozen=True)
class PaperBrokerConfig:
"""Paper broker tuning for e2e test scenarios (REQ-011).
Defaults reproduce immediate, full fills with no rejects, so an unset
environment is byte-for-byte the prior `PaperBroker()` behaviour.
"""
fill_after_polls: int = 0
qty_per_poll: Optional[int] = None
reject_symbols: frozenset[str] = frozenset()
@dataclass(frozen=True)
class RabbitMQConfig:
"""RabbitMQ transport adapter config (REQ-022, dcy-006 OQ-6)."""
@@ -63,6 +76,8 @@ class DaemonConfig:
broker: str # 'paper' | 'kis'
execution_interval_sec: float
kis: Optional[KISConfig] = None
# REQ-011: paper broker tuning (only consulted when broker == 'paper').
paper: PaperBrokerConfig = PaperBrokerConfig()
# REQ-017/019: ledger writer config (None => in-memory stub ledger,
# appropriate for slice-1 paper-broker integration tests).
portfolio_hub: Optional[PortfolioHubConfig] = None
@@ -93,6 +108,15 @@ class DaemonConfig:
allow_live=os.environ.get("TREX_KIS_ALLOW_LIVE", "").lower() in ("1", "true", "yes"),
redis_url=os.environ.get("TREX_KIS_REDIS_URL", ""),
)
qpp_raw = os.environ.get("TREX_PAPER_QTY_PER_POLL", "").strip()
reject_raw = os.environ.get("TREX_PAPER_REJECT_SYMBOLS", "")
paper = PaperBrokerConfig(
fill_after_polls=int(os.environ.get("TREX_PAPER_FILL_AFTER_POLLS", "0")),
qty_per_poll=int(qpp_raw) if qpp_raw else None,
reject_symbols=frozenset(
s.strip() for s in reject_raw.split(",") if s.strip()
),
)
portfolio_hub: Optional[PortfolioHubConfig] = None
pf_base = os.environ.get("TREX_PORTFOLIO_HUB_BASE_URL", "").strip()
if pf_base:
@@ -118,6 +142,7 @@ class DaemonConfig:
broker=broker,
execution_interval_sec=float(os.environ.get("TREX_EXECUTION_INTERVAL_SEC", "1.0")),
kis=kis,
paper=paper,
portfolio_hub=portfolio_hub,
ledger_interval_sec=float(os.environ.get("TREX_LEDGER_INTERVAL_SEC", "1.0")),
rabbitmq=rabbitmq,
+5 -1
View File
@@ -78,7 +78,11 @@ def _build_broker(config: DaemonConfig) -> BrokerPort:
base_url=k.base_url, timeout=k.timeout, allow_live=k.allow_live,
token_cache=token_cache,
)
return PaperBroker()
return PaperBroker(
fill_after_polls=config.paper.fill_after_polls,
qty_per_poll=config.paper.qty_per_poll,
reject_symbols=set(config.paper.reject_symbols),
)
def _build_ledger(config: DaemonConfig) -> LedgerPort: