feat(rel-006): dcy-022/023 + BAT-032~034 — 실행 정책·qty 검증·allocated_capital 제거 통합

BAT-032 (dcy-022): timeout_sec/slippage_pct 실행 정책, 미체결 자동 취소,
보호 지정가, `trex-core cancel` 백스톱. 마이그레이션 012.
BAT-033: `trex-core enter --qty` 필수 검증 — 미지정 시 RPC 전 즉시 오류.
BAT-034 (dcy-023): allocated_capital 죽은 필드 전 경로 제거 (domain·intent·
persistence·snapshot·CLI). 마이그레이션 013(비가역 DROP). 외부 발신자
unknown key 무시 수신 의미론 적용.
REL-006 confirmed: BAT-026~034 통합 릴리즈, pre-release smoke 증거 포함.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 07:42:03 +09:00
co-authored by Claude Sonnet 4.6
parent 5ee5f7f4e2
commit fb05a73130
56 changed files with 3151 additions and 103 deletions
+2 -1
View File
@@ -12,4 +12,5 @@
| REL-002 | tooling | feedback-captured | BAT-009 | Change-req: paper execution_id Redis(#4) | [rel-002](rel-002_20260606_paper-broker-env-tuning.md) |
| REL-003 | app-service | feedback-captured | BAT-010, BAT-011, BAT-012, BAT-013 | dcy-007 (자율 안전 매도) + dcy-008 (kill-switch) + dcy-009 (다중 인스턴스 토폴로지) — 통합 릴리즈 | [rel-003](rel-003_20260607_safety-killswitch-multi-instance.md) |
| REL-004 | app-service | feedback-captured | BAT-014, BAT-015, BAT-016, BAT-017, BAT-018, BAT-019, BAT-020, BAT-021, BAT-022, BAT-023 | dcy-010 (safety-sell 쿨다운) + dcy-011~017 (buy/sell 계약·KIS 취소·trading_date TZ·ledger 백오프 버그픽스 + 운영자 CLI + 워치독 가시화) + dcy-016 후속(슬롯 상태 가시화) + dcy-018 (ledger 현금/포지션 write-back, option D) — 통합 릴리즈 | [rel-004](rel-004_20260608_safety-cooldown-bugfix-hardening-cli.md) |
| REL-005 | app-service | confirmed | BAT-024, BAT-025 | dcy-016 후속/issue #17 (operator enter --forced-close-at) + dcy-019/issue #18 (slot entry_count + closed 슬롯 재할당) — 통합 릴리즈 | [rel-005](rel-005_20260609_operator-forced-close-and-slot-reallocation.md) |
| REL-005 | app-service | feedback-captured | BAT-024, BAT-025 | dcy-016 후속/issue #17 (operator enter --forced-close-at) + dcy-019/issue #18 (slot entry_count + closed 슬롯 재할당) — 통합 릴리즈 | [rel-005](rel-005_20260609_operator-forced-close-and-slot-reallocation.md) |
| REL-006 | app-service | confirmed | BAT-026, BAT-027, BAT-028, BAT-029, BAT-030, BAT-031, BAT-032, BAT-033, BAT-034 | dcy-020 (자산 중심 영구 슬롯 α~δ cutover) + dcy-021 (포지션 사이징/issue #1·#5) + dcy-022 (실행 정책/issue #21) + dcy-023 (allocated_capital 제거/issue #22) + REQ-048·010 CHG (config-dir) + REQ-032 CHG (enter --qty/issue #23) — 통합 릴리즈 | [rel-006](rel-006_20260611_asset-centric-sizing-execution-policy.md) |
@@ -1,6 +1,6 @@
# REL-005: operator enter --forced-close-at + slot entry_count·closed 재할당 통합 릴리즈
- Status: confirmed
- Status: feedback-captured
- Profile: app-service
- Included Batch: BAT-024, BAT-025
@@ -65,53 +65,55 @@ confirm-release 전 "운영자가 쓰는 프로그램이 실제로 동작한다"
### Deployment Facts
- Executed At:
- <배포 시작 시각>
- 2026-06-10
- Completed At:
- <배포 완료 시각>
- 2026-06-10
- Environment:
- <배포 대상 환경>
- paper 브로커 + 테스트 PostgreSQL(`trex_core_test`) + 테스트 RabbitMQ + portfolio-hub stub(`pf-005930`/`pf-000660`) + `TREX_TOTAL_SLOTS=1` + instance_key `rel005smoke`(운영/기존 데몬과 큐 격리). 실계좌(LIVE KIS) 미사용 — paper 한정.
- Operator:
- <배포 수행자>
- Justin Ko
- Revision or Artifact:
- <커밋 SHA 또는 배포 artifact 식별자>
- 작업 트리(`941d324` + dcy-019 미커밋 변경 포함)
- Commands or Procedure:
- <실행 명령 또는 배포 절차 요약>
- `trex-core-daemon --config /tmp/rel005smoke.toml` 기동 후 CLI 스모크(단계 3~5의 Rollout Plan 수행)
### Profile-Specific Checks
- app-service:
- Health Endpoint:
- <snapshot RPC degraded=false 확인 결과와 시각>
- RabbitMQ 토폴로지 선언(`trex.intent.req.rel005smoke` / `trex.snapshot.req.rel005smoke`) 성공, idle 슬롯 1개 preallocation 완료, 에러 로그 없음, `Normal shutdown` 정상 종료 확인
- Startup Log Fields:
- <app, event, command, run_id 확인 결과>
- 마이그레이션 001~008 적용 후 `daily_slot.entry_count` 컬럼 존재 확인(실 PostgreSQL round-trip, additive·멱등 검증)
- Request Log Fields:
- <buy intent / forced_close / reallocation 전이 로그 확인>
- 전이 로그(slot 4fc104cf): `idle→pending_buy → pending_buy→holding → holding→pending_sell → pending_sell→closed → closed→pending_buy → pending_buy→holding` — dcy-019 신규 전이 `closed→pending_buy` 실 데몬 발화 확인. `ledger_outbox` 3행(buy/sell/buy) 모두 `status=completed`.
- Smoke or Manual Verification:
- <enter --forced-close-at + closed 재할당 e2e 결과>
- **BAT-024 PASS**: `trex-core enter pf-005930 --capital 1000000 --strategy-id STRAT-A --forced-close-at "2026-06-09T15:00:00+09:00" --yes --json` → `{"status":"admitted","slot_id":"4fc104cf-…"}`. DB: `status=holding symbol=005930 entry_count=1 forced_close_at=2026-06-09 06:00:00+00`(=KST 15:00 정확 변환). 잘못된 형식 `--forced-close-at "not-a-time"` → 제출 전 거부 `{"error":"invalid --forced-close-at (expected ISO8601): 'not-a-time'"}`, exit=1.
- **BAT-025 PASS**: 진입 시 `entry_count` 0→1 증가. `trex-core exit` → `pending_sell → closed`(entry_count=1 보존). idle 0개 상태에서 `trex-core enter pf-000660 --capital 500000 --strategy-id STRAT-B --yes` → 동일 물리 슬롯 admitted(closed→pending_buy 직접 재할당). 재할당 후 DB: `status=holding symbol=000660 entry_count=2 forced_close_at=NULL strategy_id=STRAT-B`(clean-slate·entry_count 누적 확인).
### Rollback Outcome
- Rollback Required:
- <yes 또는 no>
- no
- Rollback Trigger:
- <발생 시 원인, 없으면 none>
- none
- Rollback Action:
- <수행한 조치, 없으면 none>
- none
### Release Decision Evidence
- Released Decision:
- <released 전환 여부>
- released (feedback-captured로 전환)
- Rationale:
- <released 전환 판단 근거>
- 배포 실패·회귀 없음. `--forced-close-at` 동작 정상(유효 → admitted·KST 변환, 잘못된 형식 → 사전 거부). `entry_count` 기반 closed 슬롯 재할당 실 데몬 발화 확인. 슬롯 상태 전이 완전. ledger 3행 completed.
- Recorded By:
- <기록자>
- Justin Ko
- Recorded At:
- <기록 시각>
- 2026-06-10
## Feedback Handoff
- Observation Summary:
- <운영 관찰 사실 요약 또는 없음>
- REL-005 동작 모두 confirmed. `--forced-close-at` 사전 검증·UTC 변환 정상. closed 슬롯 재할당(entry_count 최저 우선)이 실 데몬에서 발화됨. entry_count 누적·clean-slate 교체 정상. 전이 로그 완전. ledger 3행 completed. 배포 실패·롤백 없음.
- 한계 이월: 실계좌(LIVE KIS) 주문 미검증(paper 한정), portfolio-hub stub 사용(실 #8 연동 미검증 — 슬롯 도메인/CLI 범위 밖).
- Discovery Input:
- <후속 discovery 후보>
- none (이번 릴리즈 관찰 범위에서 신규 후속 탐색 트리거 없음)
- REQ Input:
- <후속 req 후보>
- none
- Change Request Input:
- `daily_symbol_entries`(심볼 재진입 카운터) 제거 — production 소비자 없음 + 심볼 재진입 제한 미사용 확정(dcy-019). REQ-002 change-req 트랙(별도 batch-lite) — REL-005 범위 밖.
@@ -0,0 +1,127 @@
# REL-006: 자산 중심 영구 슬롯 cutover + 포지션 사이징 + 실행 정책 + 운영 정리 통합 릴리즈
- Status: confirmed
- Profile: app-service
- Included Batch: BAT-026, BAT-027, BAT-028, BAT-029, BAT-030, BAT-031, BAT-032, BAT-033, BAT-034
## Scope
- Included:
- BAT-026 (batch-lite / REQ-048·010 CHG): config 디렉터리 기반 설정 관리 — `--config <name>` + `--config-path <dir>` + config-dir `.env` 결정적 해석(`--env-file` 우선), legacy 루트 `.env` 폴백 유지(완전 하위호환). 데몬·운영자 CLI 양쪽 적용.
- BAT-027 (dcy-020 BAT-α / REQ-056·057): **자산 1:1 영구 슬롯 기반** — `(instance_key, portfolio_id)` 키 영구 슬롯 + `inactive↔pending_buy↔holding↔pending_sell` 상태머신, 인스턴스 스코프 advisory lock. 마이그레이션 009(instance_key·retired_at)·010(literal inactive).
- BAT-028 (dcy-020 BAT-β / REQ-058·059·060): universe reconcile(슬롯 생성/retire, 대량 소멸 가드) + 자산 중심 buy admission(`max_active_slots` 동시 활성 상한).
- BAT-029 (dcy-020 BAT-γ / REQ-061·062·063): intent 계약 자산 키 통일(slot_id 정본 + portfolio_id 수용) + 수량 정본=hub·core 미러 divergence reconcile + slots 단일 뷰(universe_detached 표기).
- BAT-030 (dcy-020 BAT-δ / REQ-064·065 + 056·057·062 잔여): **pool 모델 폐기 cutover** — daily preallocation 제거(영구 슬롯은 universe reconcile 이 소유), 마이그레이션 011(`trading_date`·`entry_count` DROP, 비가역).
- BAT-031 (dcy-021 / REQ-066·067 + 057·061·062 CHG): 포지션 사이징 — 보유 슬롯 증액(holding→pending_buy, avg_price 가중평균), 부분 매도(집행 수량=min(intent,브로커잔고), 잔량 holding/전량 inactive, cost basis 유지), 부분 체결 실제만 반영.
- BAT-032 (dcy-022 / REQ-068~072 + 013·061 CHG): 주문 실행 정책 — intent `timeout_sec`(미체결 자동 취소·cancel-fill race 처리)·`slippage_pct`(보호 지정가 buy ceil/sell floor), per-instance TOML `default_order_timeout_sec`, 운영자 `trex-core cancel` 백스톱. 마이그레이션 012(execution policy 필드).
- BAT-033 (batch-lite / REQ-032 CHG-06, issue #23): `trex-core enter` `--qty` 필수 검증 — 미지정 시 RPC 전 즉시 오류(0주 주문 silent 실패 제거).
- BAT-034 (dcy-023 / REQ-002·006·007·028·032 CHG, issue #22): 죽은 필드 `allocated_capital` 전 경로 제거 — CLI `--capital` argparse 거부, BuyIntent·payload 수신 무시, Slot/`EXPECTED_SLOT_FIELDS`, snapshot `slots[]`. 마이그레이션 013(컬럼 DROP, 비가역).
- Excluded:
- BAT-001~025 (REL-001~005 에서 released)
- trex-orb-v1 등 외부 전략 모듈 저장소 변경(발신 계약 전환·allocated_capital 송신 정리 — 별도 합의)
- `daily_symbol_entries` 잔재 제거(REL-005 환류 — 별도 change-req 트랙)
## Rollout Plan
1. 배포 전: 거래 세션 외 시간 확보, 현재 운영 리비전 SHA 기록, 본 릴리즈 작업 트리를 main 에 커밋해 배포 SHA 확정.
2. **DB 백업(필수)**: 마이그레이션 011·013 은 비가역 컬럼 DROP — 적용 전 운영 `daily_slot`(권장: DB 전체) 백업. BAT-030 design §4 절차 준용.
3. 신 리비전 배포 + 데몬 재기동. `apply_migrations` 가 009~013 자동 적용(전부 멱등). 기동 로그에서 마이그레이션 적용·RUNNING·비-degraded 확인.
4. **cutover 확인**: daily preallocation 부재가 정상 — 영구 자산 슬롯은 snapshot scheduler 의 universe reconcile 이 생성/retire 한다. 기동 직후 universe fetch → 슬롯 생성 로그 확인, `trex-core slots` 로 자산 수 = 슬롯 수 확인.
5. 운영자 CLI 스모크(paper 또는 비거래 시간):
- `trex-core slots` — inactive/holding 상태와 universe_detached 표기 (BAT-027~030)
- `trex-core enter <pf> --yes` → `--qty` 누락 즉시 오류 (BAT-033); `--qty N --yes` → admitted (BAT-029 자산 키)
- `trex-core enter ... --capital ...` → argparse 거부 (BAT-034)
- 증액/부분 매도 intent e2e (BAT-031), timeout_sec/slippage_pct intent + `trex-core cancel` (BAT-032)
6. 전략 모듈(trex-orb-v1) 재연결 후 buy intent 정상 admit 확인 — `allocated_capital` 송신 잔존은 unknown key 로 무시되어 무해.
7. 거래 세션 재개.
## Rollback Plan
- 조건: 기동 실패, 마이그레이션 오류, universe reconcile 슬롯 생성 실패(전 자산 미생성), buy admission 회귀, 집행 루프(timeout/slippage) 오작동, CLI 회귀.
- 절차: 직전 released 리비전(REL-005 시점)으로 롤백 + 데몬 재기동 + **백업 복원**.
- 마이그레이션: 011(`trading_date`·`entry_count` DROP)·013(`allocated_capital` DROP)은 구 코드의 SELECT/UPDATE 가 해당 컬럼을 참조하므로 **구 리비전과 공존 불가** — 롤백은 반드시 2번 백업 복원과 함께 수행한다. 009·010·012 는 additive/호환이라 단독으로는 무해.
- 데이터: cutover 후 생성된 영구 슬롯 행은 구 pool 모델 코드와 호환되지 않음 — 복원 시점 이후 거래가 없도록 비거래 시간 배포를 전제로 한다.
## Verification Checklist
- 배포 전 (각 batch verification evidence):
- BAT-026: 단위 23 + 전체 375 passed, ruff clean, 라이브 스모크(name·path 동일 결과)
- BAT-027~030 (dcy-020): 단위 367 / PG 29 / MQ 21 passed + run-the-app 자산 중심 e2e(영구 슬롯 생성·reconcile·slot_id 매수)
- BAT-031 (dcy-021): 단위 378 / 통합 422 passed + 사이징 e2e(증액 가중평균·부분 매도 잔량·전량 매도)
- BAT-032 (dcy-022): 425 passed(신규 71 tests — policy lifecycle·timeout·slippage·cancel), 회귀 없음
- BAT-033: 426 passed (AC-7 `--qty` guard + 기존 enter 회귀 보완)
- BAT-034 (dcy-023): 428 passed + PG 게이트 21 passed + ruff clean, 마이그레이션 013 테스트 DSN 멱등 round-trip + 컬럼 부재, `grep allocated_capital` 전수 0건
- SRS: 포함 REQ 전부 Implemented(Approved/Proposed 0건), batch register 9건 release-candidate
- 배포 후 (app-service profile):
- 데몬 RUNNING + 비-degraded, 마이그레이션 009~013 적용 로그, 백그라운드 스레드(snapshot scheduler·execution loop) 기동
- universe reconcile 슬롯 생성: 자산 수 = 슬롯 수, retire/tombstone 동작
- `trex-core enter`(--qty 필수·--capital 거부)·`slots`·`cancel` 스모크
- buy→fill→증액→부분 매도 e2e + timeout/slippage 정책 발화 확인
- `ledger_outbox` completed, `daily_slot` 에 `trading_date`/`entry_count`/`allocated_capital` 컬럼 부재 확인
## Pre-Release Run Evidence (operator-facing smoke)
confirm-release 전 run-the-app 증거 (2026-06-11, `run-trex-core-daemon` 프로젝트 skill 경로). 실 데몬(RabbitMQ + 실 PostgreSQL) 기동 + 메시징/CLI 구동으로 본 릴리즈의 신규 동작을 검증했다.
- 환경: paper 브로커 + **테스트 PostgreSQL `trex_core_test`**(운영 DB 미사용) + 테스트 RabbitMQ, portfolio-hub **stub**(pf-005930/pf-000660), instance_key `rel006smoke`(큐 격리), `--config /tmp/rel006smoke/rel006smoke.toml --env-file /tmp/rel006smoke/.env`(BAT-026 config-dir 경로 실사용), `max_active_slots=5`.
- **기동 + cutover (BAT-027~030) — PASS**: 토폴로지 선언(`trex.intent.req.rel006smoke` 등) 후 데몬 ready. daily preallocation 없이 snapshot scheduler 의 universe reconcile 이 영구 자산 슬롯 생성: `slot reconcile: {'created': 2, 'retired': 0, 'tombstoned': 0}` — 자산 수(2) = 슬롯 수(2), status `inactive`.
- **e2e 메시징 smoke (`smoke.py`) — PASS**: snapshot RPC(slots=2, available_slots=5) → slot_id 키 buy admitted → 동일 intent_id 중복 재전송 identical(멱등) → paper fill 로 `holding qty=10 avg_price=1000` → sell admitted → `inactive`(점유 해제·자산 바인딩 유지).
- **BAT-033 (`--qty` 필수) — PASS**: `enter pf-005930 --yes`(qty 없음) → `error: --qty is required...`, rc=1, RPC 미발생.
- **BAT-034 (`--capital` 제거) — PASS**: `enter ... --capital 100000` → argparse `unrecognized arguments: --capital`, rc=2. `slots` detail 에 allocated_capital 행 없음.
- **BAT-031 (포지션 사이징) — PASS**: holding(10@1000) 슬롯에 증액 매수 `--qty 5 --entry-price 2000` → admitted(holding→pending_buy) → fill 후 `qty=15.0 avg_price=1333.3333`(정확한 가중평균). 전량 매도 → `inactive qty=None portfolio_id 유지`.
- **BAT-032 (운영자 cancel 백스톱) — PASS**: holding 슬롯에 `trex-core cancel` → `ignored: not_pending`, rc=0 (control RPC 경로 end-to-end).
- **DB 계약 (마이그레이션 009~013) — PASS**: `daily_slot` 에 `trading_date`/`entry_count`(011)·`allocated_capital`(013) 컬럼 **부재** 확인. `ledger_outbox` 5행 전부 `completed`.
- 종료: SIGTERM → `Normal shutdown`, 에러/traceback 0건(로그의 error-매칭 라인은 전부 pika INFO).
- **한계(운영 배포 검증으로 이월)**: LIVE KIS 주문 미검증(paper 한정), portfolio-hub stub(실 universe/ledger 연동은 배포 후 확인), timeout_sec/slippage_pct 정책 발화는 전략 intent 필드라 CLI 미노출 — BAT-032 단위(71 tests) evidence 로 갈음하고 배포 후 전략 모듈 경유로 확인.
## Release Execution Evidence
- Release Status Transition Target: released
### Deployment Facts
- Executed At:
- <배포 시작 시각>
- Completed At:
- <배포 완료 시각>
- Environment:
- <배포 대상 환경>
- Operator:
- <배포 수행자>
- Revision or Artifact:
- <커밋 SHA — 배포 전 main 커밋으로 확정>
- Commands or Procedure:
- <실행 명령 또는 배포 절차 요약>
### Profile-Specific Checks
- app-service:
- Health Endpoint:
- <snapshot RPC degraded=false 확인 결과와 시각>
- Startup Log Fields:
- <마이그레이션 009~013 적용 + universe reconcile 슬롯 생성 로그>
- Request Log Fields:
- <buy/sizing intent admission·집행·timeout/slippage·cancel 전이 로그>
- Smoke or Manual Verification:
- <Rollout 5~6 단계 스모크 결과>
### Rollback Outcome
- Rollback Required:
- <yes 또는 no>
- Rollback Trigger:
- <발생 시 원인, 없으면 none>
- Rollback Action:
- <수행한 조치, 없으면 none>
### Release Decision Evidence
- Released Decision:
- <released 전환 여부>
- Rationale:
- <released 전환 판단 근거>
- Recorded By:
- <기록자>
- Recorded At:
- <기록 시각>
## Feedback Handoff
- Observation Summary:
- <운영 관찰 사실 요약 또는 없음>
- Discovery Input:
- <후속 discovery 후보 — 후보 백로그: 자율 청산 OFF 모드(issue #15), 중장기 전략 슬롯(issue #20)>
- REQ Input:
- <후속 req 후보>
- Change Request Input:
- `daily_symbol_entries` 잔재 제거(REL-005 환류 승계 — REQ-002/006 change-req 트랙), paper 브로커 order seq Redis 영속화(issue #4 — REQ-011 change-req 트랙)