feat(execution,obs): 호가단위 스냅(dcy-030/#28) + 운영 관측성(dcy-029/#29) + REL-007 환류
dcy-030 (issue #28, P0 체결 복구): - REQ-083 KRX 호가단위(tick) 순수 유틸 (domain/pricing.py) - REQ-070 CHG: 슬리피지 실효가 호가단위 스냅 — 상한 준수(매수 floor/매도 ceil) + band<1tick 하한/상한 보장 + 부동소수 epsilon 가드. 147700→148400/54500→54700. - BAT-044 release-candidate dcy-029 (issue #29, 운영 관측성): - REQ-084 구조화 로깅 기반 (runtime/logging_setup.py): ISO8601 KST·레벨·Loki JSON ·correlation id(slot_id) - REQ-085 주문 결과 로깅 (event=order_submit|fill|reject) - REQ-087 거부 ERROR 가시성 (조용한 롤백 제거) - REQ-086 청산 reason+actor 동반 노출 (안전망/전략 구분, 신규 필드 없음) - REQ-088 운영 조회 CLI report (application/operator_report.py, 보유·오늘 체결 ·실현 PnL net·대기, direct-DB) - REQ-089 운영자 런북 docs/operations.md - REQ-090 baseline 정합 (runtime-flows·project-structure·interface-contract) - BAT-045(P0)·BAT-046(P1) release-candidate REL-007: confirmed → feedback-captured (운영 인스턴스 라이브 검증 + #28/#29 환류 정규화). BAT-037~041 released. 검증: 537 passed, 47 skipped, 회귀 0, ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
# BAT-044 Design
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-044
|
||||
|
||||
## Architecture Summary
|
||||
- 가격→호가단위 변환을 순수 도메인 함수(`trex_core/domain/pricing.py`)로 분리하고, `execution.py`의 dispatch-time 슬리피지 산출이 이를 소비해 제출 직전 유효 호가로 스냅한다. 도메인은 무의존 유지(application이 domain을 단방향 참조).
|
||||
|
||||
## Changed Areas
|
||||
- Docs: docs/runtime-flows.md(주문 집행 루프 슬리피지 스냅 단계), docs/project-structure.md(domain/pricing.py)
|
||||
- Modules: trex_core/domain/pricing.py(신규), trex_core/application/execution.py(`_dispatch`)
|
||||
- Interfaces: 외부 메시지/계약 변경 없음 — 제출되는 `OrderRequest.limit_price` 값만 유효 호가로 정정(관측 가능한 동작: 거부→체결).
|
||||
|
||||
## Key Decisions
|
||||
1. **반올림 방향 = 상한 준수**(매수 `floor_to_tick`, 매도 `ceil_to_tick`). `slippage_pct`를 리스크 상한으로 취급 → 스냅이 상한을 넘기지 않음. REQ-070 원래 메모("체결 보장=매수 상위/매도 하위")에서 전환(dcy-030 결정).
|
||||
2. **band<1tick 보장**: 매수 = `max(floor_to_tick(raw), ceil_to_tick(base))`, 매도 = `min(ceil_to_tick(raw), floor_to_tick(base))`. slippage가 1틱 미만이어도 entry의 유효 호가에서 체결 가능(미체결 방지). on-tick entry(실거래 정상)에서는 guard가 no-op(floor 결과가 이미 ≥ entry).
|
||||
3. **부동소수 가드**: `floor_to_tick`/`ceil_to_tick`에서 `q=price/tick`에 ±1e-9 epsilon. `1000*1.005=1004.9999…` 같은 잡음이 off-by-one을 일으키지 않게 함. q가 유계라 안전.
|
||||
4. **유틸 위치 = domain**(순수 함수, 무의존). slot 도메인과 동급의 가격 도메인 모듈.
|
||||
5. **주식 전용**: 단일 구간표 상수. 종목유형 분기 없음(universe에 유형 필드 부재, dcy-030 OQ-1).
|
||||
|
||||
## Edge Cases
|
||||
- 시장가(`limit_price=None`) 또는 `slippage_pct` 없음/0(falsy) → 스냅 미적용, 기존 동작(회귀 없음).
|
||||
- 구간 경계(2,000/5,000/…/500,000) → 하한 포함 처리.
|
||||
- ceil이 상위 구간으로 넘어가는 경우(예: 199,950→200,000) → 결과가 상위 구간 tick의 배수임(KRX 표가 중첩 배수라 항상 유효).
|
||||
- 2,000원 미만 비주식(ETF 등) → 주식 tick(1원) 적용 시 무효 가능 → known limitation(미지원, dcy-030 OQ-1).
|
||||
|
||||
## Architecture Impact
|
||||
- both (project-structure: 신규 domain 모듈 / runtime-flows: 슬리피지 스냅 단계)
|
||||
|
||||
## Reference Doc Update Plan
|
||||
- project-structure.md: `domain/pricing.py` 모듈 1행 추가 — 완료.
|
||||
- runtime-flows.md: 주문 집행 루프 슬리피지 보호 지정가 항목에 호가단위 스냅·방향·band<tick 보장 반영 — 완료.
|
||||
@@ -0,0 +1,30 @@
|
||||
# BAT-044 Implementation
|
||||
|
||||
- Status: completed
|
||||
- Batch: BAT-044
|
||||
|
||||
## Plan Summary
|
||||
- KRX 호가단위 유틸 신설 + execution dispatch 슬리피지 스냅 적용(상한 준수 + band<tick 보장) + 테스트 + baseline 문서.
|
||||
|
||||
## Changed Files
|
||||
- `trex_core/domain/pricing.py` (신규) — `_KRX_STOCK_TICKS` 구간표, `krx_stock_tick`, `floor_to_tick`, `ceil_to_tick`(epsilon 1e-9 가드).
|
||||
- `trex_core/application/execution.py` — `_dispatch` 슬리피지 블록을 tick 스냅으로 교체(매수 `max(floor_to_tick(raw), ceil_to_tick(base))`, 매도 `min(ceil_to_tick(raw), floor_to_tick(base))`); `import math` 제거, `from trex_core.domain.pricing import ceil_to_tick, floor_to_tick` 추가; debug 로그에 raw/effective 표기.
|
||||
- `tests/domain/test_pricing.py` (신규) — tick 대표값/경계값/just-below + floor·ceil 불변식(`f ≤ price ≤ c`, 유효 배수, `c-f ≤ tick`).
|
||||
- `tests/application/test_execution_policy.py` — 슬리피지 테스트 방향 정정(`floor_to_tick`/`ceil_to_tick`) + issue #28 케이스(147700→148400, 54500→54700) + band<tick 하한 보장 + 상한 미초과 + 35000@0.7%→35200.
|
||||
- `docs/runtime-flows.md`, `docs/project-structure.md` — baseline 반영.
|
||||
|
||||
## Execution Log
|
||||
1. `domain/pricing.py` 작성 → `tests/domain/test_pricing.py` green.
|
||||
2. `execution.py` `_dispatch` 스냅 적용, `math` import 제거.
|
||||
3. 기존 슬리피지 테스트(ceil 가정) 정정 + 신규 케이스 추가.
|
||||
4. 부동소수 off-by-one(1000*1.005=1004.9999…→1004) 발견 → `floor/ceil_to_tick`에 epsilon 1e-9 가드 추가 → green.
|
||||
5. 전체 회귀 + ruff + baseline 문서.
|
||||
|
||||
## Validation
|
||||
- `pytest tests/domain/test_pricing.py tests/application/test_execution_policy.py` → 47 passed.
|
||||
- 전체 `pytest -q` → **516 passed, 47 skipped**(skip=실 DB/RabbitMQ 게이트), 회귀 0.
|
||||
- `ruff check` (pricing.py, execution.py, 테스트) → All checks passed.
|
||||
|
||||
## Remaining Risks
|
||||
- 2,000원 미만 비주식(ETF 등) 미지원(known limitation, dcy-030 OQ-1) — universe에 종목유형 필드 도입 시 후속.
|
||||
- 실계좌 KIS 거부 해소는 paper 환경 단위 검증까지 완료, 실 거래 검증은 release 운영 단계 위임.
|
||||
@@ -0,0 +1,30 @@
|
||||
# BAT-044: slippage 지정가 KRX 호가단위 스냅 (issue #28)
|
||||
|
||||
- Status: release-candidate
|
||||
- Profile: standard
|
||||
- Source Discovery: dcy-030
|
||||
- Included REQ: REQ-083 (신규 tick 유틸), REQ-070 (CHG-20260616-01, 슬리피지 스냅)
|
||||
- Excluded REQ: 없음
|
||||
|
||||
## Summary
|
||||
- slippage 보호 지정가(REQ-070)가 KRX 호가단위를 위반해 KIS `주식주문호가단위 오류`로 매수 전량 거부되던 버그(issue #28)를 수정한다. 가격→tick 순수 유틸(REQ-083)을 신설하고, `execution.py`의 슬리피지 실효가를 유효 호가단위로 스냅한다. 반올림 방향은 **상한 준수**(매수 내림/매도 올림), band<1tick은 entry 유효 호가를 하한/상한으로 보장(미체결 방지).
|
||||
|
||||
## Scope
|
||||
- In Scope:
|
||||
- `trex_core/domain/pricing.py` — `krx_stock_tick`/`floor_to_tick`/`ceil_to_tick` (REQ-083).
|
||||
- `trex_core/application/execution.py` — `_dispatch` 슬리피지 스냅 적용(상한 준수 + band<tick 보장) (REQ-070 CHG).
|
||||
- 단위 테스트(tick 경계값·floor/ceil 불변식·슬리피지 스냅 방향·issue #28 케이스·band<tick).
|
||||
- baseline: runtime-flows(슬리피지 스냅 단계), project-structure(pricing 모듈).
|
||||
- Out of Scope:
|
||||
- ETF/ETN 비주식 호가체계(dcy-030 OQ-1, 주식 전용), 2,000원 미만 비주식(known limitation).
|
||||
- slippage_pct 정책(밴드 폭/기본값) 변경.
|
||||
|
||||
## Documents
|
||||
- Planning: [planning.md](./planning.md)
|
||||
- Design: [design.md](./design.md)
|
||||
- Implementation: [implementation.md](./implementation.md)
|
||||
- Verification: [verification.md](./verification.md)
|
||||
|
||||
## Notes
|
||||
- P0(다음 거래일 전 체결 복구). 반올림 방향은 REQ-070 원래 메모("체결 보장")에서 "상한 준수"로 전환 — `slippage_pct`를 리스크 상한으로 취급(dcy-030 OQ 결정).
|
||||
- 부동소수 잡음(예: 1000*1.005=1004.9999…) 흡수용 epsilon(1e-9) 가드 포함.
|
||||
@@ -0,0 +1,32 @@
|
||||
# BAT-044 Planning
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-044
|
||||
- Profile: standard
|
||||
|
||||
## Included REQ
|
||||
- REQ-083 (KRX 호가단위 tick 유틸, Approved)
|
||||
- REQ-070 (슬리피지 스냅, CHG-20260616-01, Approved)
|
||||
|
||||
## Delivery Plan
|
||||
1. `trex_core/domain/pricing.py` 신설 — 구간표 + `krx_stock_tick` + `floor_to_tick`/`ceil_to_tick` (epsilon 가드).
|
||||
2. `execution.py` `_dispatch` 슬리피지 블록 교체 — 상한 준수 방향(매수 floor/매도 ceil) + band<tick 하한/상한 보장. `import math` 제거(미사용), `domain.pricing` import.
|
||||
3. 테스트: `tests/domain/test_pricing.py`(경계값·불변식), `tests/application/test_execution_policy.py`(방향 정정 + issue #28 케이스 + band<tick + 상한 미초과).
|
||||
4. baseline 문서 갱신(runtime-flows 슬리피지 스냅 단계, project-structure pricing 모듈).
|
||||
|
||||
## Dependencies
|
||||
- Internal: REQ-070 dispatch 경로가 REQ-083 유틸을 소비. domain(무의존) → application 단방향 유지.
|
||||
- External: 없음(순수 계산, 외부 의존 없음).
|
||||
|
||||
## Design Gate
|
||||
- Design Required Before Implementation: yes
|
||||
- Reason: 신규 도메인 모듈 배치 위치 + 반올림 방향/band<tick 보장 공식 + 부동소수 처리 결정이 필요.
|
||||
|
||||
## Milestones
|
||||
1. pricing 유틸 + 단위 테스트 green.
|
||||
2. execution 스냅 적용 + 정책 테스트 정정/추가 green.
|
||||
3. 전체 회귀 + baseline 문서 정합.
|
||||
|
||||
## Risks
|
||||
- 부동소수 잡음으로 경계 off-by-one(예: 1000*1.005) → epsilon 가드로 해소.
|
||||
- 기존 슬리피지 테스트(ceil 가정) 회귀 → 방향 정정에 맞춰 갱신.
|
||||
@@ -0,0 +1,39 @@
|
||||
# BAT-044 Verification
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-044
|
||||
|
||||
## Acceptance Mapping
|
||||
- REQ-083 (KRX tick 유틸):
|
||||
- AC-1 (1,500→1): `test_representative_prices[1500-1]` ✅
|
||||
- AC-2 (대표값 5/10/50/100/500/1000): `test_representative_prices` ✅
|
||||
- AC-3 (경계값 하한 포함): `test_boundaries_are_lower_inclusive` ✅
|
||||
- AC-4 (148,439→148,400 floor): `test_issue28_floor_cases` ✅
|
||||
- AC-5 (54,773→54,700 floor / 54,800 ceil): `test_issue28_floor_cases`·`test_ceil_case` ✅
|
||||
- AC-6 (floor≤p≤ceil, 유효 배수): `test_invariants_floor_le_price_le_ceil_and_valid_multiples` ✅
|
||||
- REQ-070 (슬리피지 스냅, CHG-20260616-01):
|
||||
- AC-1 (매수 floor_to_tick): `test_buy_slippage_floor_to_tick`(1000→1005)·`test_buy_slippage_fractional_floors_down`(1001@0.3%→1004) ✅
|
||||
- AC-2 (매도 ceil_to_tick): `test_sell_slippage_ceil_to_tick`(2000@1%→1980) ✅
|
||||
- AC-3 (시장가 무시): `test_no_slippage_when_market_order_no_entry_price` ✅
|
||||
- AC-4 (slippage_pct None 회귀): `test_no_slippage_when_slippage_pct_is_none` ✅
|
||||
- AC-5 (0% 기준가): `test_zero_slippage_uses_base_price` ✅
|
||||
- AC-6 (기존 회귀): 전체 스위트 516 passed ✅
|
||||
- AC-7 (147,700@0.5%→148,400): `test_issue28_buy_147700_snaps_to_valid_tick` ✅
|
||||
- AC-8 (54,500@0.5%→54,700): `test_issue28_buy_54500_snaps_to_valid_tick` ✅
|
||||
- AC-9 (band<1tick 하한 보장): `test_buy_sub_tick_band_floors_to_entry_tick` ✅
|
||||
- AC-10 (유효 호가 배수 + 상한/하한 미초과): `test_buy_snap_never_exceeds_slippage_ceiling`(35000@0.7%→35200) + 불변식 테스트 ✅
|
||||
|
||||
## Evidence
|
||||
- Test:
|
||||
- `pytest tests/domain/test_pricing.py tests/application/test_execution_policy.py -q` → **47 passed**.
|
||||
- 전체 `pytest -q` → **516 passed, 47 skipped**(실 DB/RabbitMQ 게이트 skip), 회귀 0.
|
||||
- `ruff check` → All checks passed.
|
||||
- Manual:
|
||||
- issue #28 두 케이스가 유효 호가(148,400 / 54,700)로 산출되고 0.5% 상한 미초과 확인.
|
||||
- Logs:
|
||||
- 해당 없음(단위 검증). 실 KIS 거부 해소는 release 운영 검증 위임.
|
||||
|
||||
## Result
|
||||
- Ready for Release: true
|
||||
- Blocking Issues:
|
||||
- 없음
|
||||
@@ -0,0 +1,31 @@
|
||||
# BAT-045 Design
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-045
|
||||
|
||||
## Architecture Summary
|
||||
- 로깅 설정을 단일 모듈(`trex_core/runtime/logging_setup.py`)로 모은다. 포맷터(text/json)·KST 타임스탬프·correlation id contextvar/filter를 제공하고, 엔트리포인트가 `configure_logging()`으로 root 핸들러를 구성한다. 호출부(`_log.info`)는 그대로 두어도 포맷·cid가 자동 적용. execution 루프는 주문 결과를 일관 event 라인으로 남기고 처리 슬롯마다 correlation id(slot_id)를 설정한다.
|
||||
|
||||
## Changed Areas
|
||||
- Docs: docs/runtime-flows.md·docs/project-structure.md(REQ-090/BAT-046에서 정합)
|
||||
- Modules: trex_core/runtime/logging_setup.py(신규), trex_core/runtime/daemon.py·reset.py(configure_logging), trex_core/application/execution.py(주문 결과 로깅+cid)
|
||||
- Interfaces: 외부 메시지 계약 변경 없음. 로그 라인 포맷/키만 추가(관측 표면).
|
||||
|
||||
## Key Decisions
|
||||
1. **correlation id = slot_id**. intent·order·fill의 공통 조인 키. intent_id를 slot에 저장하는 스키마 변경 대신 slot_id로 단일 키 구성(REQ-084 "파생 키" 허용). execution 루프가 dispatch/poll 각 슬롯 처리 시작 시 `set_correlation_id(slot_id)`.
|
||||
2. **포맷 선택 = env `TREX_LOG_FORMAT`**(text 기본 / json). JSON은 `ts/level/logger/msg/correlation_id` + extra 필드. KST 타임스탬프는 코드에서 명시적 +09:00(환경 TZ 비의존).
|
||||
3. **`configure_logging` idempotent**: 재호출 시 기존 핸들러 제거 후 단일 핸들러 부착(데몬·reset 양쪽 안전, 중복 출력 방지).
|
||||
4. **레이어 방향**: application(execution) → runtime(logging_setup) import는 기존 선례(safety_sell→runtime.config)와 동일. domain은 무의존 유지(import sweep 영향 없음).
|
||||
5. **거부 ERROR(REQ-087)는 REQ-085 reject 라인과 동일 지점**에서 ERROR 레벨로 구현(코드 중복 편집 방지). 정상 체결 경로는 INFO 유지.
|
||||
|
||||
## Edge Cases
|
||||
- correlation id 미설정 → 빈 문자열, 예외 없음(text는 cid 생략, json은 빈 값).
|
||||
- caplog 기반 기존 테스트: root 핸들러만 교체하고 메시지 텍스트 보존 → 회귀 없음(529 passed).
|
||||
- JSON extra 필드: 표준 LogRecord 속성 제외 후 직렬화(default=str로 비직렬 객체 방어).
|
||||
|
||||
## Architecture Impact
|
||||
- both (project-structure: 신규 logging 모듈 / runtime-flows: 로깅·관측성 흐름) — 실제 문서 정합은 REQ-090(BAT-046)에서 일괄.
|
||||
|
||||
## Reference Doc Update Plan
|
||||
- project-structure.md: `runtime/logging_setup.py` 추가 — REQ-090(BAT-046)에서.
|
||||
- runtime-flows.md: 구조화 로깅·correlation id·주문 결과 로깅·거부 ERROR 흐름 — REQ-090(BAT-046)에서.
|
||||
@@ -0,0 +1,30 @@
|
||||
# BAT-045 Implementation
|
||||
|
||||
- Status: completed
|
||||
- Batch: BAT-045
|
||||
|
||||
## Plan Summary
|
||||
- 구조화 로깅 기반(REQ-084) + 주문 결과 로깅(REQ-085) + 거부 ERROR(REQ-087) 구현.
|
||||
|
||||
## Changed Files
|
||||
- `trex_core/runtime/logging_setup.py` (신규) — `_correlation_id` contextvar, `set_correlation_id`/`get_correlation_id`/`correlation_id` cm, `_CorrelationFilter`, `TextFormatter`/`JsonFormatter`(KST ISO8601), `configure_logging`(idempotent root 핸들러).
|
||||
- `trex_core/runtime/daemon.py` — `main()`에서 `logging.basicConfig` → `configure_logging`.
|
||||
- `trex_core/runtime/reset.py` — 동일 교체.
|
||||
- `trex_core/application/execution.py` — import `set_correlation_id`; `_dispatch`에서 슬롯별 cid 설정 + `event=order_submit` INFO + `event=order_reject` ERROR; `_poll` 슬롯별 cid + terminal_reject ERROR; `_on_filled` `event=order_fill`(price/qty/fee/tax).
|
||||
- `tests/runtime/test_logging_setup.py` (신규) — text/json 포맷·KST·cid·idempotent.
|
||||
- `tests/application/test_execution_policy.py` — `TestOrderResultLogging`(submit/fill event 키 + cid=slot_id, 거부 ERROR, 정상 무ERROR).
|
||||
|
||||
## Execution Log
|
||||
1. `logging_setup.py` 작성 → 단위 테스트 green.
|
||||
2. 엔트리포인트 `configure_logging` 교체(daemon/reset).
|
||||
3. execution 주문 결과 로깅 + correlation id + 거부 ERROR.
|
||||
4. 테스트 추가/실행, 전체 회귀.
|
||||
|
||||
## Validation
|
||||
- `pytest tests/runtime/test_logging_setup.py tests/application/test_execution_policy.py` → 35 passed.
|
||||
- 전체 `pytest -q` → **529 passed, 47 skipped**(실 DB/RabbitMQ 게이트 skip), 회귀 0.
|
||||
- `ruff check`(logging_setup/execution/daemon/reset/테스트) → All checks passed.
|
||||
|
||||
## Remaining Risks
|
||||
- correlation id는 slot_id 기반(단일 키). intent_id 전체 전파는 후속(스키마 변경 필요 시).
|
||||
- Loki 실제 적재는 배포측 수집기 설정(코드는 JSON stdout). 실 환경 검증은 release 운영 위임.
|
||||
@@ -0,0 +1,30 @@
|
||||
# BAT-045: 관측성 로깅 기반 — 구조화 로깅 + 주문 결과 + 거부 ERROR (issue #29 P0)
|
||||
|
||||
- Status: release-candidate
|
||||
- Profile: standard
|
||||
- Source Discovery: dcy-029
|
||||
- Included REQ: REQ-084, REQ-085, REQ-087
|
||||
- Excluded REQ: REQ-086·088·089·090 (P1 → BAT-046)
|
||||
|
||||
## Summary
|
||||
- dcy-029 P0 — 운영 가시성의 토대. 모든 로그에 ISO8601(KST) 타임스탬프+레벨, Loki 적재용 JSON 출력 옵션, correlation id(slot_id, intent→order→fill join key)를 도입하고(REQ-084), 주문 submit/체결/거부를 일관 tailable 로그로 남기며(REQ-085), 거부를 ERROR 레벨로 가시화한다(REQ-087, 조용한 롤백 제거 — issue #28이 수 시간 묻혔던 원인).
|
||||
|
||||
## Scope
|
||||
- In Scope:
|
||||
- `trex_core/runtime/logging_setup.py`(신규) — TextFormatter/JsonFormatter + KST 타임스탬프 + correlation id contextvar/filter + `configure_logging`.
|
||||
- `daemon.py`·`reset.py` — `basicConfig` → `configure_logging`.
|
||||
- `execution.py` — submit/fill 로그(`event=order_submit|order_fill`) + 거부 ERROR(`event=order_reject`) + correlation id(slot_id) 설정.
|
||||
- 단위 테스트(포맷터 text/json/KST/cid, 설정 idempotent, 주문 결과 로깅, 거부 ERROR).
|
||||
- Out of Scope:
|
||||
- 청산 reason+actor 노출(REQ-086), PnL CLI(REQ-088), 런북(REQ-089), baseline 정합(REQ-090) → BAT-046.
|
||||
- 알림 전송 채널(dcy-029 OQ-3 Deferred).
|
||||
|
||||
## Documents
|
||||
- Planning: [planning.md](./planning.md)
|
||||
- Design: [design.md](./design.md)
|
||||
- Implementation: [implementation.md](./implementation.md)
|
||||
- Verification: [verification.md](./verification.md)
|
||||
|
||||
## Notes
|
||||
- correlation id 키 = **slot_id**(intent·order·fill 공통 조인 키). intent_id 전체 전파(slot에 저장)는 과한 스키마 변경이라 slot_id로 단일 키 구성(REQ-084 "intent_id 또는 그로부터 파생한 키" 허용).
|
||||
- REQ-087(FR-4)은 원래 P1이나 REQ-085의 reject 로깅 지점과 동일 코드라 P0에 동반 구현(코드 중복 편집 방지) — 사용자 위임 결정 범위 내 조정.
|
||||
@@ -0,0 +1,33 @@
|
||||
# BAT-045 Planning
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-045
|
||||
- Profile: standard
|
||||
|
||||
## Included REQ
|
||||
- REQ-084 (구조화 로깅 기반, Approved)
|
||||
- REQ-085 (주문 결과 로깅, Approved)
|
||||
- REQ-087 (주문 거부 ERROR 가시성, Approved)
|
||||
|
||||
## Delivery Plan
|
||||
1. `trex_core/runtime/logging_setup.py` 신설 — KST 포맷터(text/json) + correlation id contextvar/filter + `configure_logging`(basicConfig 대체, idempotent).
|
||||
2. `daemon.py`/`reset.py` 엔트리포인트에서 `configure_logging` 호출로 교체.
|
||||
3. `execution.py` `_dispatch`/`_poll`/`_on_filled`에 submit/fill/reject 일관 로그(event 키) + correlation id(slot_id) 설정. 거부 라인은 ERROR 레벨(REQ-087).
|
||||
4. 테스트: `tests/runtime/test_logging_setup.py`, `tests/application/test_execution_policy.py`(주문 결과 로깅 + 거부 ERROR).
|
||||
|
||||
## Dependencies
|
||||
- Internal: `execution.py`(application)가 `logging_setup`(runtime)의 `set_correlation_id` 사용 — 기존 선례(safety_sell→runtime.config) 동일 방향. domain 무의존 유지.
|
||||
- External: 없음. Loki 적재는 배포측 수집기(코드는 JSON stdout만, A-4 전제).
|
||||
|
||||
## Design Gate
|
||||
- Design Required Before Implementation: yes
|
||||
- Reason: correlation id 키 선택(slot_id), 포맷터 구조, 레이어 의존 방향, basicConfig 교체 idempotency 결정 필요.
|
||||
|
||||
## Milestones
|
||||
1. logging_setup + 단위 테스트 green.
|
||||
2. execution 주문 결과 로깅 + 거부 ERROR + 테스트 green.
|
||||
3. 전체 회귀 + ruff.
|
||||
|
||||
## Risks
|
||||
- 로그 포맷 변경이 기존 caplog 기반 테스트에 영향 → 메시지 텍스트 보존, 핸들러 교체는 root만.
|
||||
- correlation id 단일 스레드 execution 루프에서 set-per-iteration로 충분(리셋 불요).
|
||||
@@ -0,0 +1,41 @@
|
||||
# BAT-045 Verification
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-045
|
||||
|
||||
## Acceptance Mapping
|
||||
- REQ-084 (구조화 로깅 기반):
|
||||
- AC-1 (ISO8601 KST+레벨+메시지): `TestTextFormatter::test_has_iso8601_kst_timestamp_level_and_msg` ✅
|
||||
- AC-2 (json 유효+필수 키): `TestJsonFormatter::test_is_valid_json_with_required_keys` ✅
|
||||
- AC-3 (text 기본): `TestConfigureAndContext::test_configure_text_default` ✅
|
||||
- AC-4 (cid 포함): `test_includes_correlation_id_when_set`·`test_correlation_id_propagates` ✅
|
||||
- AC-5 (cid 미설정 안전): `test_omits_cid_when_unset`·json 빈 cid ✅
|
||||
- AC-6 (KST 고정): `+09:00` 검증(text/json) ✅
|
||||
- REQ-085 (주문 결과 로깅):
|
||||
- AC-1 (order_submit 키): `TestOrderResultLogging::test_submit_and_fill_are_logged_with_event_keys` ✅
|
||||
- AC-2 (order_fill 가격/수량/fee/tax): 동 테스트(`fee=1.5`) ✅
|
||||
- AC-3 (order_reject 사유): `test_reject_is_logged_at_error_level` ✅
|
||||
- AC-4 (json 구조화 필드): `JsonFormatter` extra 필드 테스트 + 라인 키 ✅
|
||||
- AC-5 (grep 전수 추출): `event=order_reject`/`event=order_submit` 안정 키 ✅
|
||||
- AC-6 (submit→fill 동일 cid): `get_correlation_id()==slot_id` 검증 ✅
|
||||
- REQ-087 (거부 ERROR 가시성):
|
||||
- AC-1·2 (매수/매도 거부 ERROR): `test_reject_is_logged_at_error_level`(dispatch) + poll terminal_reject ERROR 경로 ✅
|
||||
- AC-3 (정상 체결 비-ERROR): `test_normal_fill_emits_no_error` ✅
|
||||
- AC-4 (json level=ERROR): JsonFormatter level 키 ✅
|
||||
- AC-5 (반복 거부 가시화): reject 라인 event 키 grep ✅
|
||||
- AC-6 (알림 채널 미호출): 본 구현은 로그까지만(전송 채널 코드 없음, OQ-3 Deferred) ✅
|
||||
|
||||
## Evidence
|
||||
- Test:
|
||||
- `pytest tests/runtime/test_logging_setup.py tests/application/test_execution_policy.py -q` → **35 passed**.
|
||||
- 전체 `pytest -q` → **529 passed, 47 skipped**, 회귀 0.
|
||||
- `ruff check` → All checks passed.
|
||||
- Manual:
|
||||
- text/json 포맷·KST 타임스탬프·correlation id·거부 ERROR 출력 단위 검증.
|
||||
- Logs:
|
||||
- 실 데몬/Loki 적재 검증은 release 운영 단계 위임(코드는 JSON stdout 보장).
|
||||
|
||||
## Result
|
||||
- Ready for Release: true
|
||||
- Blocking Issues:
|
||||
- 없음
|
||||
@@ -0,0 +1,30 @@
|
||||
# BAT-046 Design
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-046
|
||||
|
||||
## Architecture Summary
|
||||
- P0 로깅 기반(BAT-045) 위에 분류·조회·문서를 얹는다. 청산 로그는 기존 reason·actor를 함께 노출(신규 필드 없음). 운영 조회는 순수 집계 모듈(`operator_report.py`) + CLI direct-DB 리더로 구성해 집계 로직을 테스트 가능하게 분리.
|
||||
|
||||
## Changed Areas
|
||||
- Docs: docs/operations.md(신규), docs/interface-contract.md(reason+actor 어휘 + report 매핑), docs/runtime-flows.md·docs/project-structure.md(정합)
|
||||
- Modules: trex_core/application/safety_sell.py(actor 로그), trex_core/application/operator_report.py(신규), trex_core/runtime/cli.py(report)
|
||||
- Interfaces: 운영자 CLI `report` 추가(direct-DB, RPC 없음). reason+actor는 로그/계약 노출(메시지 계약 불변).
|
||||
|
||||
## Key Decisions
|
||||
1. **REQ-086 = Option A**: 기존 `reason`(조건) + `actor`(주체)를 함께 노출. safety_sell fire 로그에 `actor=trex-safety-sell` 추가, interface-contract에 어휘 명문화. 신규 reason 값·분류 필드 없음. `slot_transition_log`는 이미 actor 기록 → 추가 스키마 변경 불요.
|
||||
2. **REQ-088 실현 PnL = net 매매 현금흐름**: `ledger_outbox`에 매도별 cost basis가 없어, 운영자가 수기 계산하던 당일 net(Σ매도 대금−수수료−세금 − Σ매수 대금−수수료)을 정의로 채택. 일중 진입·청산에서 net ≈ 실현손익. 미청산 평가손익 제외(런북·계약에 명시).
|
||||
3. **REQ-088 표면 = CLI direct-DB**(OQ-1): 신규 RPC 없음. 집계는 순수 함수(`operator_report.py`)로 분리해 단위 테스트, DB 와이어링은 `cli._cmd_report`(psycopg) 얇은 리더.
|
||||
4. **"오늘" 경계 = trading_date KST**: `kst_day_bounds_utc()`로 KST 자정 경계를 UTC 범위로 변환해 `ledger_outbox.executed_at` 필터.
|
||||
|
||||
## Edge Cases
|
||||
- ledger_outbox executed_at NULL → `COALESCE(executed_at, created_at)`로 폴백.
|
||||
- 체결 0건 → realized PnL 0, 빈 섹션.
|
||||
- report는 인스턴스 필터 없이 DB 전체 집계(pause/resume 선례) — 멀티인스턴스 분리는 후속(런북에 명시).
|
||||
|
||||
## Architecture Impact
|
||||
- both (project-structure: operator_report·logging_setup·operations.md / runtime-flows: 로깅·관측성·report 흐름) — REQ-090에서 정합 완료.
|
||||
|
||||
## Reference Doc Update Plan
|
||||
- project-structure.md: `application/operator_report.py`·`runtime/logging_setup.py`·`docs/operations.md`·cli report — 완료.
|
||||
- runtime-flows.md: 구조화 로깅/관측성 flow + 운영 조회 리포트 flow — 완료.
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
# BAT-046 Implementation
|
||||
|
||||
- Status: completed
|
||||
- Batch: BAT-046
|
||||
|
||||
## Plan Summary
|
||||
- 청산 reason+actor 노출(REQ-086) + 운영 조회 CLI(REQ-088) + 운영자 런북(REQ-089) + baseline 정합(REQ-090).
|
||||
|
||||
## Changed Files
|
||||
- `trex_core/application/safety_sell.py` — `_log_fire`에 `actor=trex-safety-sell` 동반 노출(REQ-086).
|
||||
- `docs/interface-contract.md` — 청산 reason+actor 어휘 절 + `report` CLI 매핑(REQ-086/088).
|
||||
- `trex_core/application/operator_report.py` (신규) — `Holding`/`PendingOrder`/`TradeRow`/`OperatorReport`, `realized_pnl`(net), `build_report`, `render_text`/`render_json`, `kst_day_bounds_utc`(REQ-088).
|
||||
- `trex_core/runtime/cli.py` — `report` 서브커맨드 + `_cmd_report`(direct-DB psycopg: daily_slot holdings/pending + ledger_outbox 오늘 체결 → build_report → text/json)(REQ-088).
|
||||
- `docs/operations.md` (신규) — 운영자 런북 4영역(REQ-089).
|
||||
- `docs/runtime-flows.md`·`docs/project-structure.md` — 로깅/관측성·report flow + 신규 모듈 정합(REQ-090).
|
||||
- `tests/application/test_operator_report.py` (신규) — 집계·net PnL·KST 경계·렌더.
|
||||
- `tests/application/test_safety_sell_visibility.py` — reason+actor 동반 노출 테스트(REQ-086).
|
||||
|
||||
## Execution Log
|
||||
1. operator_report 순수 집계 + 단위 테스트(7) green.
|
||||
2. cli `report` 서브커맨드(direct-DB) 추가.
|
||||
3. safety_sell actor 로그 + 테스트.
|
||||
4. interface-contract reason+actor 어휘 + report 매핑.
|
||||
5. docs/operations.md 런북 작성.
|
||||
6. runtime-flows·project-structure 정합, 전체 회귀 + ruff(F841 1건 수정).
|
||||
|
||||
## Validation
|
||||
- `pytest tests/application/test_operator_report.py tests/application/test_safety_sell_visibility.py` → 14 passed.
|
||||
- 전체 `pytest -q` → **537 passed, 47 skipped**, 회귀 0.
|
||||
- `ruff check trex_core/ tests/` → All checks passed.
|
||||
|
||||
## Remaining Risks
|
||||
- `report` direct-DB 와이어링은 순수 집계 단위 검증 + 실 DB 검증 release 위임(pause/resume 선례).
|
||||
- 실현 PnL은 net 현금흐름 정의(미청산 평가손익·cost-basis 매칭 제외) — 런북·계약 명시.
|
||||
- Loki 실 적재는 배포측 수집기.
|
||||
@@ -0,0 +1,30 @@
|
||||
# BAT-046: 관측성 P1 — reason+actor 노출 + PnL 조회 CLI + 운영자 런북 + baseline (issue #29)
|
||||
|
||||
- Status: release-candidate
|
||||
- Profile: standard
|
||||
- Source Discovery: dcy-029
|
||||
- Included REQ: REQ-086, REQ-088, REQ-089, REQ-090
|
||||
- Excluded REQ: REQ-084·085·087 (P0 → BAT-045)
|
||||
|
||||
## Summary
|
||||
- dcy-029 P1 — P0 로깅 기반(BAT-045) 위에 운영 가시성을 완성한다. 청산 reason+actor 동반 노출(REQ-086, 안전망/전략 구분), 운영 조회 CLI `report`(REQ-088, 보유·오늘 체결·실현 PnL·대기), 운영자 런북 `docs/operations.md`(REQ-089), baseline 문서 정합(REQ-090).
|
||||
|
||||
## Scope
|
||||
- In Scope:
|
||||
- `safety_sell.py` — 청산 fire 로그에 actor 동반(REQ-086) + `interface-contract.md` reason+actor 어휘.
|
||||
- `application/operator_report.py`(신규) + `cli.py` `report` 서브커맨드(REQ-088, direct-DB, RPC 없음).
|
||||
- `docs/operations.md`(신규 운영자 런북, REQ-089).
|
||||
- `docs/runtime-flows.md`·`docs/project-structure.md` 정합(REQ-090).
|
||||
- 단위 테스트(report 집계·KST 경계·렌더, safety_sell reason+actor).
|
||||
- Out of Scope:
|
||||
- 알림 전송 채널(OQ-3 Deferred). 실현 PnL의 cost-basis 매칭(net 현금흐름 정의 사용).
|
||||
|
||||
## Documents
|
||||
- Planning: [planning.md](./planning.md)
|
||||
- Design: [design.md](./design.md)
|
||||
- Implementation: [implementation.md](./implementation.md)
|
||||
- Verification: [verification.md](./verification.md)
|
||||
|
||||
## Notes
|
||||
- REQ-086 = Option A(기존 reason·actor 함께 노출, 신규 필드 없음). `slot_transition_log` 는 이미 actor 기록.
|
||||
- REQ-088 실현 PnL = `ledger_outbox` net 매매 현금흐름(수수료·세금 반영, BAT-038). 미청산 평가손익 제외.
|
||||
@@ -0,0 +1,34 @@
|
||||
# BAT-046 Planning
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-046
|
||||
- Profile: standard
|
||||
|
||||
## Included REQ
|
||||
- REQ-086 (청산 reason+actor 노출, Approved)
|
||||
- REQ-088 (운영 조회 CLI, Approved)
|
||||
- REQ-089 (운영자 런북, Approved)
|
||||
- REQ-090 (관측성 baseline 정합, Approved)
|
||||
|
||||
## Delivery Plan
|
||||
1. REQ-086: `safety_sell._log_fire` 에 actor 동반 + `interface-contract.md` reason+actor 어휘 절 추가.
|
||||
2. REQ-088: `application/operator_report.py`(순수 집계: holdings/pending/realized PnL net + KST 경계) + `cli.py` `report` 서브커맨드(direct-DB psycopg) + 단위 테스트.
|
||||
3. REQ-089: `docs/operations.md` 신설(배포/CLI/로깅·관측성/디버깅 4영역).
|
||||
4. REQ-090: `runtime-flows.md`(로깅/관측성 + report 흐름) + `project-structure.md`(logging_setup·operator_report·operations.md) 정합.
|
||||
|
||||
## Dependencies
|
||||
- Internal: BAT-045(P0 로깅 기반) 선행 — report/로그가 그 위에 동작. REQ-086 로그는 P0 로깅 포맷 활용.
|
||||
- External: psycopg(report direct-DB). 기존 의존, 신규 없음.
|
||||
|
||||
## Design Gate
|
||||
- Design Required Before Implementation: yes
|
||||
- Reason: 실현 PnL 정의(net 현금흐름 vs cost-basis), report 표면(CLI/direct-DB), reason+actor 노출 방식(신규 필드 없음) 결정 필요.
|
||||
|
||||
## Milestones
|
||||
1. operator_report 순수 집계 + 단위 테스트 green.
|
||||
2. report CLI + safety_sell actor + interface-contract.
|
||||
3. 런북 + baseline 정합 + 전체 회귀.
|
||||
|
||||
## Risks
|
||||
- 실현 PnL net 정의가 cost-basis 매칭 PnL과 다름 → 런북·계약 문서에 정의 명시(미청산 평가손익 제외).
|
||||
- report direct-DB 는 단위 미검증(psycopg) — 집계 순수 함수로 검증하고 DB 와이어링은 release 운영 검증(pause/resume 선례).
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
# BAT-046 Verification
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-046
|
||||
|
||||
## Acceptance Mapping
|
||||
- REQ-086 (청산 reason+actor 노출):
|
||||
- AC-1 (stop_loss+actor=safety_sell 함께): `test_fire_log_exposes_reason_and_actor`(forced_close_at+actor) + 로그 포맷(reason= actor=) ✅
|
||||
- AC-2 (전략 actor 구분): actor 어휘 interface-contract 문서화 + slot_transition_log actor 기록(기존) ✅
|
||||
- AC-3 (운영자 오해 제거): fire 로그 reason+actor 동반 ✅
|
||||
- AC-4 (safety_sell 4경로 reason+actor): `_log_fire` 모든 경로 공통 + 기존 forced/SL/TP/market 테스트 ✅
|
||||
- AC-5 (기존 reason 소비처 회귀 없음): reason 값 불변, 537 passed 회귀 0 ✅
|
||||
- AC-6 (interface-contract 문서화): "청산 사유 어휘 — reason + actor" 절 추가 ✅
|
||||
- REQ-088 (운영 조회 CLI):
|
||||
- AC-1 (보유): `test_render_text_has_sections`·`render_json` holdings ✅
|
||||
- AC-2 (오늘 체결 건수): `test_build_counts_and_pnl`(fills_count) ✅
|
||||
- AC-3 (실현 PnL fee/tax 반영, ledger_outbox 집계 일치): `test_buy_then_sell_net_cash_flow_with_fee_tax` ✅
|
||||
- AC-4 (대기 주문): pending 렌더 ✅
|
||||
- AC-5 (KST "오늘" 경계 전일 제외): `test_today_boundary_excludes_yesterday` ✅
|
||||
- AC-6 (신규 RPC 없이 동작): `report`=direct-DB, RPC 미추가(cli `_cmd_report`) ✅
|
||||
- REQ-089 (운영자 런북):
|
||||
- AC-1~5 (배포·CLI·로깅/관측성·디버깅 4영역 + 신규 운영자 수행 가능): `docs/operations.md` 4개 절(§1 배포/§2 CLI+report/§3 로깅·관측성·reason+actor/§4 디버깅+DB조회) ✅ (문서 리뷰)
|
||||
- REQ-090 (baseline 정합):
|
||||
- AC-1·2 (runtime-flows 로깅/관측성+주문결과+거부ERROR+reason+actor+report): "구조화 로깅/관측성" + "운영 조회 리포트" flow 추가 ✅
|
||||
- AC-3 (project-structure 신규 모듈): logging_setup·operator_report·operations.md·cli report 반영 ✅
|
||||
- AC-4 (구현과 무모순): 코드와 일치 ✅
|
||||
|
||||
## Evidence
|
||||
- Test:
|
||||
- `pytest tests/application/test_operator_report.py tests/application/test_safety_sell_visibility.py -q` → **14 passed**.
|
||||
- 전체 `pytest -q` → **537 passed, 47 skipped**, 회귀 0.
|
||||
- `ruff check trex_core/ tests/` → All checks passed.
|
||||
- Manual:
|
||||
- operations.md 4영역 포함 리뷰, interface-contract reason+actor 어휘 리뷰.
|
||||
- Logs:
|
||||
- report direct-DB·Loki 실 적재는 release 운영 검증 위임.
|
||||
|
||||
## Result
|
||||
- Ready for Release: true
|
||||
- Blocking Issues:
|
||||
- 없음
|
||||
@@ -44,10 +44,13 @@
|
||||
| BAT-034 | standard | released | REQ-002 (CHG-20260610-02), REQ-006 (CHG-20260610-02), REQ-007 (CHG-20260610-02), REQ-028 (CHG-20260610-01), REQ-032 (CHG-20260610-07) | dcy-023 (issue #22) | [bat-034](./bat-034_20260610_drop-allocated-capital/) |
|
||||
| BAT-035 | standard | released | REQ-073, REQ-074, REQ-075, REQ-021 (CHG-20260611-01), REQ-034 (CHG-20260611-01), REQ-063 (CHG-20260611-01) | dcy-025 | [bat-035](./bat-035_20260611_strategy-read-surface-slots-req/) |
|
||||
| BAT-036 | standard | released | REQ-048 (CHG-20260611-01), REQ-005 (CHG-20260611-01) | dcy-024 | [bat-036](./bat-036_20260611_safety-sell-disable-mode/) |
|
||||
| BAT-037 | standard | release-candidate | REQ-073 (CHG-20260612-01) | dcy-025 (REQ-073 change-req) | [bat-037](./bat-037_20260612_slots-rpc-available-slots/) |
|
||||
| BAT-038 | standard | release-candidate | REQ-018 (CHG-20260612-01) | dcy-026 (issue #24) | [bat-038](./bat-038_20260612_sell-transaction-tax-fee/) |
|
||||
| BAT-039 | standard | release-candidate | REQ-011 (CHG-20260612-01) | 없음 (issue #4) | [bat-039](./bat-039_20260612_paper-seq-redis-persistence/) |
|
||||
| BAT-040 | standard | release-candidate | REQ-002 (CHG-20260612-01), REQ-006 (CHG-20260612-01), REQ-073 (CHG-20260612-02) | 없음 (issue #25) | [bat-040](./bat-040_20260612_slots-rpc-db-filter/) |
|
||||
| BAT-041 | standard | release-candidate | REQ-076, REQ-077, REQ-078 | dcy-027 (issue #20 이월) | [bat-041](./bat-041_20260612_long-term-instance-profile/) |
|
||||
| BAT-037 | standard | released | REQ-073 (CHG-20260612-01) | dcy-025 (REQ-073 change-req) | [bat-037](./bat-037_20260612_slots-rpc-available-slots/) |
|
||||
| BAT-038 | standard | released | REQ-018 (CHG-20260612-01) | dcy-026 (issue #24) | [bat-038](./bat-038_20260612_sell-transaction-tax-fee/) |
|
||||
| BAT-039 | standard | released | REQ-011 (CHG-20260612-01) | 없음 (issue #4) | [bat-039](./bat-039_20260612_paper-seq-redis-persistence/) |
|
||||
| BAT-040 | standard | released | REQ-002 (CHG-20260612-01), REQ-006 (CHG-20260612-01), REQ-073 (CHG-20260612-02) | 없음 (issue #25) | [bat-040](./bat-040_20260612_slots-rpc-db-filter/) |
|
||||
| BAT-041 | standard | released | REQ-076, REQ-077, REQ-078 | dcy-027 (issue #20 이월) | [bat-041](./bat-041_20260612_long-term-instance-profile/) |
|
||||
| BAT-042 | standard | release-candidate | REQ-079, REQ-080, REQ-081, REQ-082, REQ-073 (CHG-20260615-01), REQ-074 (CHG-20260615-01) | dcy-028 (issue #26) | [bat-042](./bat-042_20260615_slot-strategy-state-store/) |
|
||||
| BAT-043 | batch-lite | release-candidate | REQ-007 (CHG-20260615-01) | 없음 (issue #27) | [bat-043](./bat-043_20260615_sell-intent-retryable-flag/) |
|
||||
| BAT-044 | standard | release-candidate | REQ-083, REQ-070 (CHG-20260616-01) | dcy-030 (issue #28) | [bat-044](./bat-044_20260616_slippage-tick-size-snapping/) |
|
||||
| BAT-045 | standard | release-candidate | REQ-084, REQ-085, REQ-087 | dcy-029 (issue #29, P0) | [bat-045](./bat-045_20260616_observability-logging-foundation/) |
|
||||
| BAT-046 | standard | release-candidate | REQ-086, REQ-088, REQ-089, REQ-090 | dcy-029 (issue #29, P1) | [bat-046](./bat-046_20260616_observability-classification-pnl-runbook/) |
|
||||
|
||||
Reference in New Issue
Block a user