diff --git a/.env.example b/.env.example index 42cd2e8..e626583 100644 --- a/.env.example +++ b/.env.example @@ -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) diff --git a/docs/batches/bat-009_20260606_paper-broker-env-tuning/implementation.md b/docs/batches/bat-009_20260606_paper-broker-env-tuning/implementation.md new file mode 100644 index 0000000..f0704bd --- /dev/null +++ b/docs/batches/bat-009_20260606_paper-broker-env-tuning/implementation.md @@ -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 는 단위테스트로 고정. diff --git a/docs/batches/bat-009_20260606_paper-broker-env-tuning/index.md b/docs/batches/bat-009_20260606_paper-broker-env-tuning/index.md new file mode 100644 index 0000000..98587ca --- /dev/null +++ b/docs/batches/bat-009_20260606_paper-broker-env-tuning/index.md @@ -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 에 정리한다. diff --git a/docs/batches/bat-009_20260606_paper-broker-env-tuning/planning.md b/docs/batches/bat-009_20260606_paper-broker-env-tuning/planning.md new file mode 100644 index 0000000..01e37d0 --- /dev/null +++ b/docs/batches/bat-009_20260606_paper-broker-env-tuning/planning.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` 빈/공백 토큰 제거 — 단위테스트로 고정. diff --git a/docs/batches/bat-009_20260606_paper-broker-env-tuning/verification.md b/docs/batches/bat-009_20260606_paper-broker-env-tuning/verification.md new file mode 100644 index 0000000..cab5bce --- /dev/null +++ b/docs/batches/bat-009_20260606_paper-broker-env-tuning/verification.md @@ -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 로 배포. diff --git a/docs/batches/index.md b/docs/batches/index.md index 58ec4fc..0ce4ff9 100644 --- a/docs/batches/index.md +++ b/docs/batches/index.md @@ -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/) | diff --git a/docs/releases/index.md b/docs/releases/index.md index a437591..1277228 100644 --- a/docs/releases/index.md +++ b/docs/releases/index.md @@ -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) | diff --git a/docs/releases/rel-001_20260606_trex-core-slot-core-baseline.md b/docs/releases/rel-001_20260606_trex-core-slot-core-baseline.md index 05beccf..4c6fbdb 100644 --- a/docs/releases/rel-001_20260606_trex-core-slot-core-baseline.md +++ b/docs/releases/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) diff --git a/docs/srs/Configuration/req-010_daemon-dotenv-loading.md b/docs/srs/Configuration/req-010_daemon-dotenv-loading.md index e35bb6a..79b0926 100644 --- a/docs/srs/Configuration/req-010_daemon-dotenv-loading.md +++ b/docs/srs/Configuration/req-010_daemon-dotenv-loading.md @@ -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 재참조로 충분. diff --git a/docs/srs/index.md b/docs/srs/index.md index 1a2b2e0..785bfe4 100644 --- a/docs/srs/index.md +++ b/docs/srs/index.md @@ -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) diff --git a/tests/runtime/test_config_dotenv.py b/tests/runtime/test_config_dotenv.py index e79046e..58e9e56 100644 --- a/tests/runtime/test_config_dotenv.py +++ b/tests/runtime/test_config_dotenv.py @@ -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"}) diff --git a/trex_core/runtime/config.py b/trex_core/runtime/config.py index e6bdf8d..423bab6 100644 --- a/trex_core/runtime/config.py +++ b/trex_core/runtime/config.py @@ -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, diff --git a/trex_core/runtime/daemon.py b/trex_core/runtime/daemon.py index e940928..d8fb38a 100644 --- a/trex_core/runtime/daemon.py +++ b/trex_core/runtime/daemon.py @@ -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: