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:
@@ -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 로 배포.
|
||||
@@ -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/) |
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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"})
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user