fix(execution): dcy-012 매도 가격 버그 정정 — slot entry_price→limit_price rename (BAT-016)
매도(sell) 주문이 현재가가 아닌 매수가(entry_price) 지정가로 나가던 버그(issue #10) 정정. 슬롯의 매수 전용 entry_price 를 side 무관 "현재 pending 주문 지정가" limit_price 로 내부 rename 하고, pending 이탈(→holding/→closed) 시 clear 해 매수 지정가가 매도로 누수되는 경로를 구조적으로 제거. 거버넌스: 전부 change-req (신규 REQ 없음, dcy-012 OQ-3). - REQ-002: Slot entry_price→limit_price rename + clear-on-exit 생애주기 - REQ-006: daily_slot 컬럼 rename 마이그레이션 006 (idempotent) + persistence 매핑 - REQ-013: _dispatch 가 slot.limit_price 단일 필드로 가격/주문유형 결정 - REQ-007: SellIntent/sell payload optional limit_price + _admit_sell 운반 - REQ-036: SlotService.sell optional 가격 → 자율 매도(safety-sell/kill-switch) 시장가 - REQ-003: 컬럼 계약/드리프트 가드 limit_price 반영 - REQ-015: runtime-flows/interface-contract/data-model 정합 매도 정책(OQ-1 하이브리드): 가격 지정→지정가("00"), 미지정→시장가("01"). 자율 매도는 가격 미전달 → 항상 시장가 (손절 미체결 pending_sell 갇힘 방지, issue #9 연계). 검증: 단위 286 passed/32 skipped, 인프라 게이트(PG+RabbitMQ) 318 passed, ruff clean. 신규 test_sell_pricing(recording broker)로 지정가 매수 슬롯의 매도가 매수가 재사용 안 함을 직접 검증. BAT-016 release-candidate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
# BAT-016 Design
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-016
|
||||
- Profile: standard
|
||||
|
||||
## 설계 목표
|
||||
- 매도 주문이 매수 지정가(`entry_price`)로 나가는 버그(issue #10)를 도메인 모델 차원에서 구조적으로 제거한다.
|
||||
- released 집행 경로 변경·컬럼 rename 마이그레이션을 회귀 없이 cutover 한다.
|
||||
|
||||
## 1. 슬롯 가격 필드 의미 통일 (REQ-002)
|
||||
|
||||
### rename
|
||||
- `Slot.entry_price` → `Slot.limit_price` (side 무관 "현재 pending 주문 지정가"). `EXPECTED_SLOT_FIELDS` 동시 반영(컬럼 계약 가드가 곧 검출).
|
||||
- `transit_slot`/`_apply_transition_fields` 의 `entry_price` 파라미터 → `limit_price`.
|
||||
|
||||
### limit_price 생애주기 (FR-1, 핵심)
|
||||
- 결정: limit_price 는 **pending 상태에서만 유효**. `_apply_transition_fields` 의 generic 경로에서:
|
||||
- `to_state ∈ {PENDING_BUY, PENDING_SELL}` → `limit_price = 입력값 if not None else slot.limit_price`(set on entry).
|
||||
- `to_state ∈ {HOLDING, CLOSED}` → `limit_price = None`(clear on exit).
|
||||
- `to_state == IDLE`(롤백/복구) → 기존 reset 분기에서 이미 None clear.
|
||||
- 효과: PENDING_BUY→HOLDING 에서 매수 지정가가 clear 되므로, 이후 HOLDING→PENDING_SELL 에서 매도 가격 미전달이면 slot.limit_price 는 None → 시장가. 매수 지정가가 매도로 누수될 경로가 구조적으로 없음.
|
||||
- 전이 매트릭스:
|
||||
- IDLE→PENDING_BUY: set(매수 지정가) / PENDING_BUY→HOLDING: clear / HOLDING→PENDING_SELL: set(매도 지정가 또는 None) / PENDING_SELL→CLOSED: clear / *→IDLE: clear.
|
||||
|
||||
### SlotService.sell (REQ-036)
|
||||
- `sell(slot_id, actor, reason, *, limit_price=None)`. default None → 자율 매도(safety-sell/kill-switch)는 가격 없이 호출 → 시장가. transit_slot 에 limit_price 전달.
|
||||
|
||||
## 2. dispatch 가격 결정 (REQ-013)
|
||||
- `_dispatch`: side 분기 제거. `limit = int(slot.limit_price) if slot.limit_price else None`; `order_type = "00" if limit is not None else "01"`. 매수가 재사용 경로 삭제.
|
||||
|
||||
## 3. sell intent 계약 (REQ-007)
|
||||
- `SellIntent.limit_price: Optional[float] = None`. `_admit_sell` → `transit_slot(..., limit_price=intent.limit_price)`.
|
||||
- `_admit_buy`: 기존 `entry_price=intent.entry_price`(BuyIntent 외부 필드 유지) → 슬롯 `limit_price=intent.entry_price` 로 매핑(매수 동작 동일).
|
||||
- 단일 메커니즘(가격 유무 분기, OQ-2): reason 별 코드 차등 없음.
|
||||
|
||||
## 4. 메시지 경계 (REQ-007, NFR-3)
|
||||
- sell 핸들러: `limit_price=_opt_float(body.get("limit_price"))` 추가.
|
||||
- buy 핸들러: 외부 payload 필드명 `entry_price` 유지(BuyIntent.entry_price).
|
||||
- slot read(`_slot_to_dict`): 외부 read 키 `"entry_price"` 유지하되 내부 `s.limit_price` 로 매핑(계약 호환 — 외부 필드명 rename 은 본 주기 밖).
|
||||
|
||||
## 5. persistence + 마이그레이션 (REQ-006)
|
||||
- `006_rename_entry_price_to_limit_price.sql`: `ALTER TABLE daily_slot RENAME COLUMN entry_price TO limit_price` 를 idempotent DO-block(컬럼 존재 && 신컬럼 부재 시에만)으로. 글롭 재적용 모델에서 안전. 순수 rename → 기존 행 데이터 보존(S-3).
|
||||
- `postgresql.py`: `_SLOT_COLUMNS`/update_slot/_row_to_slot 의 entry_price → limit_price.
|
||||
|
||||
## 6. 컬럼 계약 가드 (REQ-003)
|
||||
- `test_contract.py`(도메인 필드셋)·`test_migration_and_drift.py`(DB 컬럼 = EXPECTED_SLOT_FIELDS)는 상수 참조라 rename 자동 반영; PG 게이트에서 실 컬럼명 limit_price 검증.
|
||||
|
||||
## 7. baseline 문서 (REQ-015)
|
||||
- runtime-flows: dispatch 가격 결정 = slot.limit_price 단일 필드 + limit_price 생애주기.
|
||||
- interface-contract: sell payload optional limit_price + ORD_DVSN 매핑.
|
||||
- data-model: 슬롯 limit_price 컬럼 의미/생애주기.
|
||||
|
||||
## 하위호환 / 회귀 통제
|
||||
- 외부 buy/sell payload·slot read 필드명 유지(NFR-3) → 전략 모듈 계약 무파괴.
|
||||
- sell limit_price optional default None → 기존 재량 매도 호출 시장가로 안전 동작.
|
||||
- 전체 회귀 green 이 rename 호출부 누락 부재의 1차 게이트.
|
||||
|
||||
## 테스트 설계
|
||||
- 신규 `tests/application/test_sell_pricing.py`(recording paper broker 로 OrderRequest 직접 검증):
|
||||
- (a) 지정가 매수 슬롯 + 가격 미전달 매도 → sell order_type "01"/limit None(entry_price 미사용 — 핵심 버그 검출).
|
||||
- (b) 지정가 매수 슬롯 + 가격 전달 매도 → "00"/그 가격.
|
||||
- (c) SlotService.sell(가격 없음) → 시장가 + closed(S-2).
|
||||
- (d) 시장가 매수 슬롯 매도 → 시장가(회귀 안전).
|
||||
- `test_service.py`: PENDING_BUY→HOLDING 시 limit_price clear 단언 추가.
|
||||
@@ -0,0 +1,48 @@
|
||||
# BAT-016 Implementation
|
||||
|
||||
- Status: completed
|
||||
- Batch: BAT-016
|
||||
|
||||
## 구현 요약
|
||||
- dcy-012 매도 가격 버그(issue #10)를 슬롯 `entry_price`→`limit_price` rename + clear-on-exit 생애주기 + dispatch 단일 필드 결정으로 정정. 전부 change-req(신규 REQ 없음).
|
||||
|
||||
## 변경 내역
|
||||
|
||||
### REQ-002 — 슬롯 도메인 rename + 생애주기
|
||||
- `trex_core/domain/slot/models.py`: `Slot.entry_price` → `limit_price`(주석: side 무관 pending 주문 지정가, clear-on-exit); `EXPECTED_SLOT_FIELDS` 의 entry_price → limit_price.
|
||||
- `trex_core/domain/slot/service.py`:
|
||||
- `transit_slot`/`_apply_transition_fields` 파라미터 `entry_price` → `limit_price`.
|
||||
- IDLE reset 분기: `entry_price=None` → `limit_price=None`.
|
||||
- generic 경로: `to_state ∈ {PENDING_BUY, PENDING_SELL}` 면 set(입력 or 유지), 아니면(HOLDING/CLOSED) `new_limit_price=None` clear.
|
||||
- `SlotService.sell(..., *, limit_price=None)` 추가 → transit_slot 에 전달.
|
||||
|
||||
### REQ-013 — dispatch 단일 필드
|
||||
- `trex_core/application/execution.py::_dispatch`: `limit = int(slot.limit_price) if slot.limit_price else None`(side 분기/entry_price 재사용 제거). order_type 은 limit 유무로 "00"/"01".
|
||||
|
||||
### REQ-007 — sell intent 가격
|
||||
- `trex_core/application/intents.py`: `SellIntent.limit_price: Optional[float] = None`; `_admit_sell` → `transit_slot(..., limit_price=intent.limit_price)`; `_admit_buy` → `limit_price=intent.entry_price`(BuyIntent.entry_price 외부 필드 유지).
|
||||
- `trex_core/application/messaging_handlers.py`: sell 핸들러 `limit_price=_opt_float(body.get("limit_price"))`; buy payload `entry_price` 유지; `_slot_to_dict` 외부 키 `"entry_price"` 유지하되 `s.limit_price` 매핑(NFR-3).
|
||||
|
||||
### REQ-036 — 자동 매도 시장가
|
||||
- `SlotService.sell` optional `limit_price`(default None) → safety-sell/kill-switch 자율 매도는 가격 미전달 → 시장가.
|
||||
|
||||
### REQ-006 — 마이그레이션 + persistence
|
||||
- `trex_core/adapters/persistence/sql/006_rename_entry_price_to_limit_price.sql`(신규): idempotent DO-block rename(컬럼 존재 && 신컬럼 부재 시에만). 순수 rename → 데이터 보존.
|
||||
- `trex_core/adapters/persistence/postgresql.py`: `_SLOT_COLUMNS`/update_slot SQL+바인딩/_row_to_slot 의 entry_price → limit_price.
|
||||
|
||||
### REQ-003 — 컬럼 계약 가드
|
||||
- `EXPECTED_SLOT_FIELDS` rename 으로 `test_contract.py`/`test_migration_and_drift.py` 자동 반영(상수 참조). PG 게이트에서 실 컬럼 limit_price 검증.
|
||||
|
||||
### REQ-015 — baseline 문서
|
||||
- `docs/runtime-flows.md`: dispatch 가격 결정(slot.limit_price 단일 필드) + limit_price 생애주기 set/clear. Last Updated → BAT-016.
|
||||
- `docs/interface-contract.md`: sell payload optional limit_price + ORD_DVSN 매핑. Last Updated → BAT-016.
|
||||
- `docs/data-model.md`: 슬롯 limit_price 컬럼 의미/생애주기. Last Updated → BAT-016.
|
||||
|
||||
## 테스트
|
||||
- 신규 `tests/application/test_sell_pricing.py`(recording paper broker): 4건 — (a) 지정가 매수+시장가 매도(entry_price 미사용), (b) 지정가 매수+지정가 매도, (c) SlotService.sell 시장가+closed, (d) 시장가 매수 매도 회귀.
|
||||
- `tests/domain/slot/test_service.py`: PENDING_BUY→HOLDING 시 `limit_price is None` 단언 추가; 두 →HOLDING 전이의 obsolete `entry_price=` 인자 제거.
|
||||
|
||||
## 검증 실행
|
||||
- 단위: `TREX_BROKER=paper .venv/bin/python -m pytest -q` → `286 passed / 32 skipped`(`::test_sandbox_token_and_submit` deselect — LIVE `.env` 환경 의존, 본 batch 무관).
|
||||
- 인프라 게이트: `TREX_POSTGRESQL_TEST_DSN=...(trex_core_test) TREX_RABBITMQ_TEST_URL=... pytest -q` → `318 passed`(006 rename 마이그레이션 적용 + 컬럼 계약 limit_price + 마이그레이션 idempotency 포함).
|
||||
- `ruff check trex_core tests` → All checks passed.
|
||||
@@ -0,0 +1,36 @@
|
||||
# BAT-016: 매도 가격 정책 정정 + 슬롯 entry_price→limit_price rename (sell dispatch 버그)
|
||||
|
||||
- Status: release-candidate
|
||||
- Profile: standard
|
||||
- Source Discovery: dcy-012
|
||||
- Included REQ: REQ-013 (CHG-20260608-01), REQ-007 (CHG-20260608-02), REQ-036 (CHG-20260608-01), REQ-002 (CHG-20260608-01), REQ-006 (CHG-20260608-01), REQ-003 (CHG-20260608-01), REQ-015 (CHG-20260608-01)
|
||||
|
||||
## Summary
|
||||
- dcy-012(매도 주문이 현재가가 아닌 entry_price 지정가로 나가는 버그, issue #10)를 단일 standard batch 로 delivery 한다. 거버넌스는 dcy-012 OQ-3 결정대로 **전부 change-req, 신규 REQ 없음**.
|
||||
- 핵심: 슬롯의 매수 전용 `entry_price` 를 side 무관 "현재 pending 주문 지정가" 단일 필드 `limit_price` 로 내부 rename(Option A) 하고, pending 이탈(→HOLDING/→CLOSED) 시 clear 한다. `_dispatch` 는 `slot.limit_price` 한 필드만 읽어 가격/주문유형을 결정(값=지정가 `00`, 없음=시장가 `01`). `SellIntent`/sell payload 에 optional `limit_price` 추가, 자율 매도(safety-sell/kill-switch)는 가격 미전달 → 시장가.
|
||||
|
||||
## Scope
|
||||
- In Scope:
|
||||
- 슬롯 `entry_price` → `limit_price` 내부 rename(도메인 모델/EXPECTED_SLOT_FIELDS, REQ-002) + pending 이탈 clear 생애주기
|
||||
- DB 컬럼 rename 마이그레이션 `006_*.sql`(idempotent) + persistence 매핑 (REQ-006)
|
||||
- `_dispatch` 가 `slot.limit_price` 단일 필드로 가격/주문유형 결정 (REQ-013)
|
||||
- `SellIntent`/sell payload optional `limit_price` + `_admit_sell` 운반, `SlotService.sell` optional 가격 (REQ-007/REQ-036)
|
||||
- 컬럼 계약/드리프트 가드 갱신 (REQ-003)
|
||||
- baseline 문서(runtime-flows/interface-contract/data-model) 정합 (REQ-015)
|
||||
- Out of Scope:
|
||||
- 외부 buy/sell payload·slot read 의 필드명(`entry_price`) rename — REQ-007/dcy-011 과 조율할 별도 계약 변경(NFR-3 호환 유지)
|
||||
- issue #9(KIS 외부 취소 시 pending_sell 갇힘) 자체의 복구 로직 — 본 버그는 #9 의 한 원인 제거, 복구는 후속 분리
|
||||
- dispatch 시 현재가 자동 조회(`get_current_price`) 기반 지정가 — OQ-1 미채택
|
||||
- buy 주문 가격 의미 변경(매수 지정가는 동일 limit_price 로 set, 동작 동일)
|
||||
|
||||
## Documents
|
||||
- Planning: [planning.md](./planning.md)
|
||||
- Design: [design.md](./design.md)
|
||||
- Implementation: [implementation.md](./implementation.md)
|
||||
- Verification: [verification.md](./verification.md)
|
||||
|
||||
## Notes
|
||||
- Source discovery 단일(dcy-012). OQ-1(하이브리드)·OQ-2(가격 유무 분기)·OQ-3(전부 change-req) Answered.
|
||||
- 버그 재현 조건: `entry_price` 가 채워진 **지정가 매수 슬롯**의 매도. 시장가 매수 슬롯은 entry_price=NULL 로 우연히 정상 → 검증은 지정가 매수 슬롯 케이스 필수(S-1).
|
||||
- `OrderRequest.limit_price`(broker_port) 는 기존 존재 — 슬롯 필드 rename 으로 이름까지 정합.
|
||||
- 마이그레이션은 글롭 재적용 모델이므로 006 은 idempotent DO-block(컬럼 존재 시에만 rename).
|
||||
@@ -0,0 +1,55 @@
|
||||
# BAT-016 Planning
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-016
|
||||
- Profile: standard
|
||||
|
||||
## Included REQ
|
||||
- REQ-002 (Interface, change-req): Slot `entry_price`→`limit_price` rename + clear-on-exit(→HOLDING/→CLOSED) 생애주기.
|
||||
- REQ-006 (Data, change-req): daily_slot 컬럼 rename 마이그레이션 006(idempotent) + persistence 매핑.
|
||||
- REQ-013 (Integration, change-req): `_dispatch` 가 `slot.limit_price` 단일 필드로 가격/주문유형 결정(매수가 재사용 제거).
|
||||
- REQ-007 (Interface, change-req): `SellIntent`/sell payload optional `limit_price` + `_admit_sell` 운반; buy entry_price→limit_price 매핑.
|
||||
- REQ-036 (Interface, change-req): `SlotService.sell` optional `limit_price`(default None → 자율 매도 시장가).
|
||||
- REQ-003 (Testing, change-req): 컬럼 계약/드리프트 가드 limit_price 반영.
|
||||
- REQ-015 (Documentation, change-req): runtime-flows/interface-contract/data-model 정합.
|
||||
|
||||
## Delivery Plan
|
||||
1. REQ-002: `models.py` Slot 필드 + EXPECTED_SLOT_FIELDS rename; `service.py` transit_slot/_apply_transition_fields 파라미터 rename + clear-on-exit(→HOLDING/→CLOSED), `SlotService.sell` optional limit_price.
|
||||
2. REQ-013: `execution.py::_dispatch` 가 `slot.limit_price` 단일 필드 사용.
|
||||
3. REQ-007: `intents.py` SellIntent.limit_price + _admit_sell 운반 + _admit_buy limit_price 매핑; `messaging_handlers.py` sell payload limit_price, slot read 외부 entry_price→limit_price 매핑.
|
||||
4. REQ-006: `006_*.sql` rename 마이그레이션(idempotent) + `postgresql.py` 매핑.
|
||||
5. REQ-003: 컬럼 계약/드리프트 가드 확인(상수 참조 자동 + DB 게이트).
|
||||
6. REQ-015: baseline 3문서 정합.
|
||||
7. 신규 테스트(test_sell_pricing) + 전체 회귀 + ruff + 인프라 게이트(PG 마이그레이션/컬럼 계약).
|
||||
|
||||
## Dependencies
|
||||
- Internal:
|
||||
- dcy-003/BAT-004 broker execution(`_dispatch`) — 버그 지점.
|
||||
- dcy-001/BAT-001 slot 도메인(REQ-002/003) — rename 대상.
|
||||
- dcy-002/BAT-002 persistence(REQ-006) — 컬럼/마이그레이션.
|
||||
- dcy-007/BAT-010 safety-sell(REQ-036 SlotService.sell) — 자율 매도 시장가 경로.
|
||||
- dcy-011/BAT-015 — slot.symbol 서버 resolve(가정 A-1) 충족.
|
||||
- External:
|
||||
- PostgreSQL(rename 마이그레이션). KIS ORD_DVSN 00/01 매핑(OrderRequest 경유).
|
||||
|
||||
## Design Gate
|
||||
- Design Required Before Implementation: yes
|
||||
- Reason: (a) `entry_price→limit_price` rename 은 도메인 모델·서비스 전이·persistence·마이그레이션·컬럼 계약 가드까지 **교차 절단(cross-cutting)** — 전이별 set/clear 매트릭스를 설계에서 못박지 않으면 매수 지정가 누수/복원 회귀(R-4) 위험. (b) 매도 가격 하이브리드(가격 유무 분기)의 단일 메커니즘 경계(자율=None=시장가, 재량=optional 지정가)를 설계에서 고정해야 reason 별 분기 혼입을 막는다(OQ-2). (c) released 집행 경로(R-1)·컬럼 rename 마이그레이션 idempotency 를 설계에서 명시. → design.md 동일 턴 scaffold.
|
||||
|
||||
## Milestones
|
||||
1. M1 — REQ-002 도메인 rename + clear-on-exit, 단위 green
|
||||
2. M2 — REQ-013 _dispatch limit_price 단일 필드 + REQ-007/036 sell 가격 경로, test_sell_pricing green
|
||||
3. M3 — REQ-006 마이그레이션 006 + persistence 매핑, PG 게이트 컬럼 계약 green
|
||||
4. M4 — REQ-003 가드 + REQ-015 문서 정합
|
||||
5. M5 — 전체 회귀 + ruff + 인프라 게이트
|
||||
|
||||
## Risks
|
||||
- R-1 (High): released 집행 경로 매도 동작 변경. 대응: paper recording broker 로 손절/지정가/현재가>매수가 시나리오 검증 + change-req 이력. 실거래 배포는 release 단계 검증.
|
||||
- R-2 (Medium): 재량 지정가 미체결 시 pending_sell 잔류. 대응: 본 주기는 가격 결정 분기에 한정, 미체결 처리는 issue #9/기존 롤백 범위.
|
||||
- R-3 (Low, OQ-2): reason 별 차등 없이 가격 유무로 분기 → 자율 매도는 default None 으로 항상 시장가(체결성 보장).
|
||||
- R-4 (Medium): rename/clear-on-exit 가 도메인/persistence/마이그레이션 교차. 대응: 전이별 set/clear 단위 + 컬럼 계약 가드 + idempotent 마이그레이션 + 외부 필드명 유지(경계 매핑).
|
||||
|
||||
## Notes
|
||||
- 1차 게이트: 전체 회귀 green(rename 호출부 누락 부재 확인).
|
||||
- 버그 검증은 반드시 지정가 매수 슬롯(entry_price 채워짐) 매도 케이스 포함(S-1 측정 조건).
|
||||
- 마이그레이션은 PG 게이트(실 PostgreSQL) 컬럼 계약/드리프트 테스트로 검증.
|
||||
@@ -0,0 +1,74 @@
|
||||
# BAT-016 Verification
|
||||
|
||||
- Status: verified
|
||||
- Batch: BAT-016
|
||||
|
||||
## Acceptance Mapping
|
||||
|
||||
### REQ-013 (dispatch 단일 필드, change-req)
|
||||
| AC | Evidence |
|
||||
|----|----------|
|
||||
| dispatch 가 slot.limit_price 단일 필드로 결정(매수가 재사용 금지) | tests/application/test_sell_pricing.py::test_limit_buy_then_market_sell_does_not_reuse_buy_limit |
|
||||
| 기존 buy/sell lifecycle 회귀 | tests/application/test_execution.py (전체) |
|
||||
|
||||
### REQ-007 (sell intent 가격, change-req)
|
||||
| AC | Evidence |
|
||||
|----|----------|
|
||||
| sell optional limit_price → 지정가 | test_sell_pricing.py::test_limit_buy_then_limit_sell_uses_intent_price |
|
||||
| 미지정 → 시장가(호환) | test_sell_pricing.py::test_limit_buy_then_market_sell_does_not_reuse_buy_limit |
|
||||
| buy entry_price → 슬롯 limit_price 매핑(동작 동일) | test_sell_pricing.py::test_limit_buy_then_market_sell... (buy_req order_type "00"/1000) |
|
||||
| 멱등/동시성 회귀 | tests/application/test_intent_concurrency.py |
|
||||
|
||||
### REQ-036 (자동 매도 시장가, change-req)
|
||||
| AC | Evidence |
|
||||
|----|----------|
|
||||
| SlotService.sell(가격 없음) → 시장가 + closed | test_sell_pricing.py::test_safety_sell_dispatches_at_market |
|
||||
| idempotency 회귀 | tests/application/test_auto_sell* / 전체 회귀 |
|
||||
|
||||
### REQ-002 (도메인 rename + clear-on-exit, change-req)
|
||||
| AC | Evidence |
|
||||
|----|----------|
|
||||
| limit_price clear on →HOLDING | tests/domain/slot/test_service.py::test_pending_buy_to_holding (limit_price is None) |
|
||||
| clear on →CLOSED / →IDLE | test_service.py::test_closed_to_idle_resets_fields_and_bumps_counter + 전체 회귀 |
|
||||
| 필드셋 계약 | tests/domain/slot/test_contract.py |
|
||||
|
||||
### REQ-006 (마이그레이션 + persistence, change-req)
|
||||
| AC | Evidence |
|
||||
|----|----------|
|
||||
| 컬럼 rename(데이터 보존) | tests/adapters/persistence/test_migration_and_drift.py::test_daily_slot_columns_match_expected_contract (PG 게이트) |
|
||||
| 마이그레이션 idempotent | ::test_apply_migrations_is_idempotent |
|
||||
| persistence round-trip | PG 게이트 repository/전이 테스트 전반 |
|
||||
|
||||
### REQ-003 (컬럼 계약 가드, change-req)
|
||||
| AC | Evidence |
|
||||
|----|----------|
|
||||
| 도메인 필드셋 = limit_price | tests/domain/slot/test_contract.py |
|
||||
| DB 컬럼 드리프트 가드 = limit_price | test_migration_and_drift.py::test_daily_slot_columns_match_expected_contract / ::test_drift_detected_when_contract_extended |
|
||||
|
||||
### REQ-015 (baseline 문서, change-req)
|
||||
| AC | Evidence |
|
||||
|----|----------|
|
||||
| runtime-flows/interface-contract/data-model 정합 | 문서 diff(BAT-016) |
|
||||
|
||||
## Evidence
|
||||
- Test:
|
||||
- 단위 전체 회귀: `286 passed / 32 skipped`(신규 sell-pricing 4 + holding clear 단언 포함; `::test_sandbox_token_and_submit` deselect).
|
||||
- 인프라 게이트(PG trex_core_test + RabbitMQ): `318 passed`(006 rename 마이그레이션 + 컬럼 계약 limit_price + idempotency + 전이 round-trip 포함).
|
||||
- `ruff check trex_core tests` → All checks passed.
|
||||
- Logs: `286 passed, 32 skipped, 1 deselected` / `318 passed, 1 deselected ~15.8s`.
|
||||
|
||||
## 성공 기준(dcy-012) 대응
|
||||
- S-1(매도가 매수 지정가 재사용 안 함): test_limit_buy_then_market_sell_does_not_reuse_buy_limit(a) + test_limit_buy_then_limit_sell_uses_intent_price(b) + test_market_buy_slot_sells_at_market_regression(c). **지정가 매수 슬롯** 케이스 포함(측정 필수 조건 충족).
|
||||
- S-2(손절 시장가 체결, pending_sell 갇힘 없음): test_safety_sell_dispatches_at_market.
|
||||
- S-3(rename 마이그레이션 데이터 보존 + 생애주기): test_migration_and_drift(컬럼 계약+idempotency) + test_service(holding clear).
|
||||
- S-4(코드-문서 정합): runtime-flows/interface-contract/data-model 갱신.
|
||||
|
||||
## Carried-Over / 비차단
|
||||
- KIS 시장가 매도(`ORD_DVSN="01"`)의 paper/실계좌 수용·체결 특성(A-4): paper 검증 완료, 실계좌는 release 단계 운영 검증.
|
||||
- 재량 지정가 미체결 시 pending_sell 잔류(R-2)·issue #9 복구는 본 주기 밖.
|
||||
- 과거 누락/잘못 체결 데이터 보정은 범위 밖.
|
||||
|
||||
## Result
|
||||
- Ready for Release: true
|
||||
- Blocking Issues:
|
||||
- 없음. rename 호출부 전환 누락은 전체 회귀(318 passed) green 으로 부재 확인. 외부 payload/read 필드명 유지로 전략 모듈 계약 무파괴. LIVE sandbox 토큰 테스트 deselect 는 `.env` 환경 의존(본 batch 무관).
|
||||
@@ -23,3 +23,4 @@
|
||||
| BAT-013 | standard | release-candidate | REQ-048, REQ-049, REQ-050, REQ-051 | dcy-009 | [bat-013](./bat-013_20260607_multi-instance-topology/) |
|
||||
| BAT-014 | batch-lite | release-candidate | REQ-052, REQ-053, REQ-054 | dcy-010 | [bat-014](./bat-014_20260608_safety-sell-cooldown/) |
|
||||
| BAT-015 | standard | release-candidate | REQ-007 (CHG-20260608-01), REQ-018 (CHG-20260608-01), REQ-021 (CHG-20260608-01), REQ-055 | dcy-011 | [bat-015](./bat-015_20260608_buy-intent-portfolio-id-contract/) |
|
||||
| BAT-016 | standard | release-candidate | REQ-002 (CHG-20260608-01), REQ-003 (CHG-20260608-01), REQ-006 (CHG-20260608-01), REQ-007 (CHG-20260608-02), REQ-013 (CHG-20260608-01), REQ-015 (CHG-20260608-01), REQ-036 (CHG-20260608-01) | dcy-012 | [bat-016](./bat-016_20260608_sell-pricing-limit-price-rename/) |
|
||||
|
||||
Reference in New Issue
Block a user