dcy-007(core 자율 안전 매도) 전체 delivery. BAT-010 기반 인프라가 미커밋 상태였고 BAT-011 트리거 레이어와 공유 파일에서 섞여 있어 한 커밋으로 묶음. BAT-010 (REQ-034/035/036/041, release-candidate): - WatchdogStore(application/watchdog.py) + snapshot RPC 워치독 갱신 - Slot stop_loss_price/take_profit_price 필드 + 004_add_price_thresholds.sql - SlotService.sell() 자율 매도 내부 경로 + idempotency - SafetySellConfig env 5종 파싱 BAT-011 (REQ-037/038/039/040/042, release-candidate): - SafetySellService(application/safety_sell.py): forced_close 만기 / stop-loss· take-profit KIS polling / 장 마감 강제 청산 + trex-safety-sell 7번째 스레드 - BrokerPort.get_current_price + KIS inquire-price(TTTC8397R/VTTC8397R, stck_prpr) + paper stub - SlotService.update_thresholds() + intent RPC update_thresholds 액션 (부분 갱신/null 해제, 워치독 last_seen_at 리셋) - baseline 3문서 정합(interface-contract/runtime-flows/project-structure) 거버넌스: REQ-034~042 Implemented 동기화, BAT-010/011 release-candidate, dcy-007 discovery 포함. 검증: 신규 38 테스트 green, 단위 236 passed/23 skipped, 인프라 게이트(PG+RabbitMQ) 259 passed(마이그레이션 001~004 round-trip 포함), ruff clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5.2 KiB
5.2 KiB
BAT-011 Verification
- Status: verified
- Batch: BAT-011
Acceptance Mapping
REQ-037 (forced_close_at 만기 자동 매도 루프)
| AC | Evidence |
|---|---|
| AC-1: 만기+워치독 만료 → sell() | test_safety_sell_worker.py::TestForcedClose::test_expired_past_due_sells |
| AC-2: 미만기(now+1h) → 미호출 | test_future_not_due_no_sell |
| AC-3: 워치독 활성 → 미호출(보호) | test_watchdog_active_protected |
| AC-4: forced_close_at None → 제외 | test_no_forced_close_excluded |
| AC-5: pending_sell 중복 미호출 | test_idempotent_on_pending_sell (transition_log PENDING_SELL 1건) |
| AC-6: interval 주기 | TestRunOnce::test_base_tick_is_finest_interval + safety_sell.base_tick_sec/run_once _due 캐던스(코드) |
| grace 보호 | test_grace_period_defers |
REQ-038 (stop-loss·take-profit KIS polling + 트리거)
| AC | Evidence |
|---|---|
| AC-1: stck_prpr ≤ stop_loss → 손절 | TestPriceTriggers::test_stop_loss_triggers |
| AC-2: stck_prpr ≥ take_profit → 익절 | test_take_profit_triggers |
| AC-3: 두 조건 미충족 → 미호출 | test_neither_condition_no_sell |
| AC-4: 워치독 활성 → KIS 호출 0 | test_watchdog_active_no_kis_call (broker.calls == []) |
| AC-5: 임계값 None → 제외 | test_no_thresholds_excluded |
| AC-6: pending_sell 중복 미호출 | test_idempotent_on_pending_sell |
| AC-7: 실전 TTTC8397R / 모의 VTTC8397R | test_kis_price.py::test_tr_price_live_vs_mock + test_get_current_price_parses_stck_prpr |
| AC-8: 시간당 호출 ≤ N×(3600/interval) | test_quota_estimate(N=3,interval=30→360) + test_only_eligible_slots_polled(워치독 만료+임계 슬롯만 poll) |
| 가격 미수신 → skip | test_price_unavailable_skips |
REQ-039 (장 마감 강제 청산)
| AC | Evidence |
|---|---|
| AC-1: 시각 도래 → 전체 holding sell() | TestMarketClose::test_time_reached_liquidates_all |
| AC-2: 워치독 활성 포함 청산 | test_watchdog_active_included |
| AC-3: 미설정 → 미동작 | test_not_configured_no_sell |
| AC-4: 거래일 1회 발동 가드 | test_once_per_day_guard |
| AC-5: pending_sell 중복 미호출 | REQ-036 idempotency(SlotService.sell) — test_service_sell.py::test_sell_idempotent_on_pending_sell 승계, 청산 경로 동일 sell() 사용 |
| AC-6: grace period 무관 청산 | test_grace_period_does_not_block |
| 발동 시각 이전 → 미동작 | test_before_time_no_sell |
REQ-040 (update_thresholds intent RPC)
| AC | Evidence |
|---|---|
| AC-1: stop_loss_price 갱신 | test_update_thresholds_handler.py::test_updates_stop_loss |
| AC-2: take_profit + forced_close 갱신 | test_updates_take_profit_and_forced_close |
| AC-3: last_seen_at 리셋 | test_resets_watchdog_last_seen |
| AC-4: 부분 갱신(미포함 유지) | test_partial_update_keeps_other_fields |
| AC-5: null → 해제 | test_explicit_null_clears |
| AC-6: non-holding → ignored/not_holding | test_non_holding_ignored |
| AC-7: reply_to → updated 응답 | test_reply_updated_status |
| 미존재 슬롯 → ignored/slot_not_found | test_unknown_slot_ignored |
REQ-042 (baseline 문서 정합)
| AC | Evidence |
|---|---|
| AC-1: interface-contract update_thresholds 계약 | docs/interface-contract.md trex.intent.req update_thresholds 액션 블록 + Patterns 행 |
| AC-2: interface-contract price 선택 필드 | buy payload 에 stop_loss_price/take_profit_price 추가 |
| AC-3: runtime-flows 4종 흐름 | docs/runtime-flows.md 워치독 갱신/forced_close/가격 체크/장 마감 흐름 4종 추가 |
| AC-4: runtime-flows A-5 주석 갱신 | broker degrade 흐름의 "A-5 미구현" → dcy-007 REQ-037~041 해소로 갱신 |
| AC-5: project-structure trex-safety-sell 스레드 | docs/project-structure.md 7 스레드 목록 + safety_sell.py/watchdog.py 모듈 + 변경 로그 |
Evidence
- Test:
- 신규 테스트 38종 green (test_kis_price 7 + test_safety_sell_worker 23 + test_update_thresholds_handler 8).
- 단위 전체 회귀:
236 passed / 23 skipped(::test_sandbox_token_and_submitdeselect —.env=LIVE산물). - 인프라 게이트(PG trex_core_test + RabbitMQ):
259 passed(마이그레이션 001~004 적용 + PG repo round-trip 포함). ruff check trex_core tests→ All checks passed.
- Manual:
- 없음(트리거 로직은 결정적 단위 테스트로 커버; 실 KIS 라이브 트리거는 운영 실증 이월).
- Logs:
236 passed, 23 skipped, 1 deselected in ~0.8s/259 passed ... in ~11s.
Carried-Over Items
- BAT-010 R-2 (실 PostgreSQL
004_add_price_thresholds.sqlround-trip): 해소 — 인프라 게이트 259 passed(마이그레이션 적용 + PG repo 테스트 green)로 확인. - 운영 실증 이월(차기 거래일): stop-loss·take-profit·forced_close·장 마감의 실 KIS 라이브 트리거 1건 관찰. 휴장/운영 제약으로 본 batch 는 mock·정적 검증까지. release feedback 으로 추적.
Result
- Ready for Release: true
- Blocking Issues:
- 없음. 잔여(실 KIS 라이브 트리거 운영 실증)는 blocker 아님 — paper/mock 결정적 검증 + KIS 어댑터 tr_id/파싱 단위 검증으로 release gate 충족. 차기 거래일 운영 관찰로 보강(REL feedback).