docs(discovery): dcy-027 모든 OQ Answered — 중장기 슬롯 운영 결정 완료
OQ-1: (a′) 기존 슬롯 도메인 유지 + 운영 프로파일/규칙 추가 (PositionBook 불필요) OQ-2: safety_sell_enabled=false → 코어 손절매 루틴 없음, 전략 모듈 전적 책임 OQ-3: per-instance watchdog_timeout_sec 연장 (1800s 이상 권장), 알림은 후속 Discovery OQ-4: 중장기 전략 폴링 주기 최소 1분·최대 10분 feat(bat-041): REQ-076/077/078 — watchdog timeout TOML + 중장기 운영 프로파일 + baseline Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
# trex-core long-term (multi-day) instance profile (dcy-027 / REQ-077).
|
||||
# Copy to e.g. config/longterm.toml and run alongside the intraday daemon:
|
||||
# trex-core-daemon --config config/longterm.toml
|
||||
# Real per-instance files are gitignored (they hold the portfolio-hub token).
|
||||
#
|
||||
# This profile assumes the strategy module owns ALL protection rules
|
||||
# (stop-loss / trailing / time-stop — dcy-027 OQ-2). The core provides no
|
||||
# autonomous liquidation for this instance; the only core-side backstop is
|
||||
# the operator CLI: trex-core exit <slot>
|
||||
|
||||
# Long-term instances MUST use their own instance_key so slots (DB scope) and
|
||||
# queues (trex.slots.req.<key> etc) never interfere with the intraday daemon
|
||||
# (dcy-027 A-2; dcy-009 topology).
|
||||
instance_key = "longterm"
|
||||
|
||||
# portfolio-hub auth token (kept out of .env).
|
||||
portfolio_hub_token = ""
|
||||
|
||||
trading_timezone = "Asia/Seoul"
|
||||
|
||||
# FCFS admission cap M for this instance (REQ-060). Size to the long-term
|
||||
# portfolio's target position count.
|
||||
max_active_slots = 10
|
||||
|
||||
# dcy-024 / REQ-048: long-term holdings must survive market close, day
|
||||
# boundaries and restarts — disable the autonomous safety-sell worker
|
||||
# (stop-loss / forced_close / market-close loops are all OFF).
|
||||
# NOTE: forced_close_at is therefore meaningless on this instance (dcy-027
|
||||
# CONFIRM) — its executor never runs here. Do not set it on long-term slots.
|
||||
safety_sell_enabled = false
|
||||
|
||||
# dcy-027 / REQ-076: long-term strategies poll every 1-10 min (OQ-4), so the
|
||||
# intraday default (120s, env TREX_WATCHDOG_TIMEOUT) would mark them expired
|
||||
# all day. 1800s (30 min) keeps `watchdog.expired` a meaningful absence
|
||||
# signal while last_seen stays visible in slots RPC rows.
|
||||
watchdog_timeout_sec = 1800
|
||||
|
||||
# Environment note (not TOML keys): do NOT set TREX_MARKET_CLOSE_LIQUIDATION_TIME
|
||||
# for this instance's daemon — market-close liquidation is an intraday concept.
|
||||
# (With safety_sell_enabled=false the loop is off anyway; leaving the env unset
|
||||
# keeps the intent explicit.)
|
||||
@@ -0,0 +1,39 @@
|
||||
# BAT-041 Design
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-041
|
||||
|
||||
## Architecture Summary
|
||||
- watchdog timeout 설정을 단일 지점에서 per-instance 화한다: `DaemonInstanceConfig.watchdog_timeout_sec`(TOML, optional) → `DaemonConfig.from_config` 에서 값이 있으면 `SafetySellConfig.watchdog_timeout_sec` 를 `dataclasses.replace` 로 override. 소비자 2곳(slots RPC 핸들러의 expired 표시, SafetySellService 의 trigger gating)은 모두 `config.safety_sell.watchdog_timeout_sec` 를 읽으므로 자동으로 일치한다.
|
||||
- 중장기 프로파일은 신규 코드 없이 기존 설정 메커니즘 조합(`safety_sell_enabled=false` + `watchdog_timeout_sec=1800` + `max_active_slots` + market-close env 미설정)으로 표현되고, `config/longterm.example.toml` 로 표준화한다.
|
||||
- runtime-flows 에 중장기 병행 운영 흐름(보호 경계 포함)을 추가한다.
|
||||
|
||||
## Changed Areas
|
||||
- Modules:
|
||||
- `trex_core/runtime/config.py`:
|
||||
- `DaemonInstanceConfig.watchdog_timeout_sec: Optional[int] = None`
|
||||
- `from_toml`: `watchdog_timeout_sec` 파싱 + `<= 0` 거부(기존 `max_active_slots` 검증 패턴)
|
||||
- `from_config`: `inst.watchdog_timeout_sec is not None` → `safety_sell = dataclasses.replace(safety_sell, watchdog_timeout_sec=...)`
|
||||
- `config/longterm.example.toml` (신규)
|
||||
- Tests: `tests/runtime/test_config*.py` 패턴 — TOML 적용/env 폴백/독립/검증 거부
|
||||
- Docs: `docs/runtime-flows.md`(중장기 운영 흐름 + Change Log), `docs/project-structure.md`(불필요 예상 — 구조 불변, config 필드 1개)
|
||||
|
||||
## Key Decisions
|
||||
1. **override 지점 = `from_config` 단일 지점**: TOML 값이 frozen `SafetySellConfig` 를 `dataclasses.replace` 로 교체. daemon.py·safety_sell.py·messaging_handlers.py 는 무변경 — 소비자가 이미 `config.safety_sell.watchdog_timeout_sec` 를 읽음(R-1 제거).
|
||||
2. **TOML 우선, env 폴백**: `watchdog_timeout_sec` TOML 미설정 시 기존 env(`TREX_WATCHDOG_TIMEOUT`, 기본 120) 그대로 — 기존 인스턴스 byte-for-byte 무회귀(REQ-076 AC-2). REQ-060(env 폐기)과 달리 env 는 보존: 기존 운영 사용 중.
|
||||
3. **`<= 0` 명시 거부**: `max_active_slots`/`default_order_timeout_sec` 와 동일한 from_toml 검증 패턴.
|
||||
4. **프로파일 = 설정 조합(신규 코드 0)**: REQ-077 은 example TOML + 검증으로 충족. market-close 는 env 영역이므로 TOML 주석으로 "미설정 전제" 명시(코드 강제 없음 — 운영 절차).
|
||||
5. **S-1 검증 범위**: 멀티데이 보유 유지는 영구 슬롯 DB 영속(dcy-020)의 기존 보장 + 자율 청산 미발화는 `safety_sell_enabled=false` 워커 미기동(BAT-036 evidence). 본 batch 는 프로파일 조합이 의도대로 로딩·적용되는지(워커 미기동 + timeout 1800 + 독립 instance_key)를 설정·기동 구성 단위로 검증하고, 실 데몬 장기 run 은 release 운영 검증으로 위임.
|
||||
|
||||
## Edge Cases
|
||||
- TOML `watchdog_timeout_sec = 0` 또는 음수: from_toml 명시 거부(RuntimeError).
|
||||
- TOML 미설정 + env 미설정: 기본 120s(기존과 동일).
|
||||
- TOML 설정 + env 도 설정: TOML 승리(per-instance 가 전역보다 구체적).
|
||||
- 당일 인스턴스(TOML 미설정)와 중장기(1800) 병행: 인스턴스별 DaemonConfig 가 독립 생성되므로 분리 적용.
|
||||
|
||||
## Architecture Impact
|
||||
- runtime-flows (중장기 병행 운영 흐름 신설 — REQ-078 소관)
|
||||
|
||||
## Reference Doc Update Plan
|
||||
- project-structure.md: none 예상(구조 불변 — config 필드 1개 + 예시 파일. 예시 파일 위치 언급이 필요하면 한 줄)
|
||||
- runtime-flows.md: 중장기 인스턴스 운영 섹션 신설 — 병행 토폴로지(instance_key 분리), 프로파일 구성(REQ-077 레시피), 보호 규칙 경계(전략 전적 책임 + CLI exit 백스톱, 코어 손절매 루틴 미사용), forced_close_at 미사용(발화 불가 근거), watchdog 중장기 의미(1800s, last_seen 가시성 유지). Change Log 항목 추가.
|
||||
@@ -0,0 +1,31 @@
|
||||
# BAT-041 Implementation
|
||||
|
||||
- Status: completed
|
||||
- Batch: BAT-041
|
||||
|
||||
## Plan Summary
|
||||
- `DaemonInstanceConfig.watchdog_timeout_sec`(TOML, env 폴백) + `from_config` 단일 지점 override, `config/longterm.example.toml` 표준 프로파일, runtime-flows 중장기 운영 flow, 테스트 7종.
|
||||
|
||||
## Changed Files
|
||||
- `trex_core/runtime/config.py` — `DaemonInstanceConfig.watchdog_timeout_sec: Optional[int] = None` + `from_toml` 파싱(<=0 거부) + `from_config` 에서 `SafetySellConfig` `dataclasses.replace` override(TOML 우선, env 폴백)
|
||||
- `config/longterm.example.toml` (신규) — 중장기 표준 프로파일(instance_key=longterm, safety_sell_enabled=false, watchdog_timeout_sec=1800, max_active_slots=10) + 키별 중장기 의미 주석(forced_close_at 미사용, market-close env 미설정 포함)
|
||||
- `docs/runtime-flows.md` — `중장기(멀티데이) 인스턴스 병행 운영` flow 신설(6단계: 분리 기동/프로파일/보유 유지/보호 경계/forced_close_at 미사용/watchdog 의미) + Change Log
|
||||
- `tests/runtime/test_watchdog_timeout_toml.py` (신규, 7 테스트)
|
||||
|
||||
## Execution Log
|
||||
1. `config.py`: `DaemonInstanceConfig` 에 optional 필드 추가, `from_toml` 에 기존 `max_active_slots` 검증 패턴으로 파싱+거부, `from_config` 에서 `inst.watchdog_timeout_sec is not None` 시 `safety_sell` replace — 소비자 2곳(slots RPC 핸들러 daemon.py:284, SafetySellService `_cfg`)이 모두 `config.safety_sell` 을 읽으므로 단일 지점으로 일치(design Key Decision 1).
|
||||
2. `longterm.example.toml`: REQ-077 레시피 — 운영자 복제 사용 가능한 주석 포함.
|
||||
3. `runtime-flows.md`: multi-instance 라우팅 섹션 뒤에 중장기 운영 flow 신설.
|
||||
4. 테스트: TOML 적용(1800)/TOML>env 우선/env 폴백(300)/기본 120/인스턴스별 독립/<=0 거부/예시 프로파일 조합 로딩(longterm·false·1800·10).
|
||||
|
||||
## Validation
|
||||
- `pytest tests/runtime/test_watchdog_timeout_toml.py` — 7 passed (신규)
|
||||
- `pytest tests/` — **446 passed, 46 skipped** (BAT-040 439 → +7, 회귀 0 — NFR-1 당일 무회귀 게이트 green)
|
||||
- `ruff check trex_core tests` — All checks passed
|
||||
- instance 격리(NFR-2): 인스턴스별 독립 timeout 테스트 + 기존 instance topology 테스트 회귀 green.
|
||||
|
||||
## Remaining Risks
|
||||
- 없음. R-1(소비자 불일치)=단일 지점 override 로 제거, R-2(frozen)=`replace` 사용, R-3(장기 run 재현)=설정·기동 구성 단위 검증 + BAT-036 evidence 재참조, 실 데몬 장기 운영 검증은 release 단계 위임(verification 명시).
|
||||
|
||||
## Out-of-scope Finding (별도 후속)
|
||||
- **pre-existing bug 발견**: `DaemonInstanceConfig.from_toml` 은 `default_order_timeout_sec` 를 파싱하지만 `DaemonConfig.from_config` 의 `replace(...)` 에 전파 누락 — TOML 의 `default_order_timeout_sec` 가 런타임에서 조용히 무시됨(daemon.py:156 은 `config.default_order_timeout_sec` 를 읽으나 항상 env-base 값). REQ-068/REQ-048 영역 — 본 batch 범위 밖, change-req 후속 필요.
|
||||
@@ -0,0 +1,33 @@
|
||||
# BAT-041: 중장기 인스턴스 운영 — watchdog per-instance TOML + 프로파일 + baseline (dcy-027)
|
||||
|
||||
- Status: release-candidate
|
||||
- Profile: standard
|
||||
- Source Discovery: dcy-027 (issue #20 이월)
|
||||
- Included REQ: REQ-076, REQ-077, REQ-078
|
||||
- Excluded REQ: 없음
|
||||
|
||||
## Summary
|
||||
- 중장기 매매 전략용 인스턴스 운영 토대를 완성한다(dcy-027, 구현 위치 (a′) — 기존 슬롯 도메인 유지). watchdog timeout 을 per-instance TOML 로 노출하고(REQ-076, env 폴백), 중장기 표준 운영 프로파일 TOML 레시피를 제공·검증하며(REQ-077), 보호 규칙 경계(전략 모듈 전적 책임 + CLI exit 백스톱)와 forced_close_at 미사용을 baseline 문서에 명시한다(REQ-078).
|
||||
|
||||
## Scope
|
||||
- In Scope:
|
||||
- `trex_core/runtime/config.py` — `DaemonInstanceConfig.watchdog_timeout_sec`(optional) + `from_toml` 파싱 + `from_config` 에서 `SafetySellConfig.watchdog_timeout_sec` override(TOML 우선, env 폴백)
|
||||
- `config/longterm.example.toml` — 중장기 표준 프로파일 예시(키별 중장기 의미 주석)
|
||||
- `docs/runtime-flows.md`(·필요 시 `docs/project-structure.md`) — 중장기 병행 운영 흐름 + 보호 규칙 경계 + forced_close_at 미사용
|
||||
- 테스트: TOML 로딩/env 폴백/인스턴스 독립, 프로파일 조합 기동 구성, instance 격리 회귀
|
||||
- Out of Scope:
|
||||
- 멀티데이 보호 규칙 코어 구현 (dcy-027 OQ-2 — 전략 모듈 전적 책임, 코어 구현 없음)
|
||||
- watchdog 소유권 진입 외부 알림(슬랙·이메일) — 후속 Discovery
|
||||
- 중장기 전략 모듈 구현(별도 저장소)
|
||||
- PositionBook 도메인 (OQ-1 미채택)
|
||||
|
||||
## Documents
|
||||
- Planning: [planning.md](./planning.md)
|
||||
- Design: [design.md](./design.md)
|
||||
- Implementation: [implementation.md](./implementation.md)
|
||||
- Verification: [verification.md](./verification.md)
|
||||
|
||||
## Notes
|
||||
- 코드 변경은 경량(config 1개 필드 + override 전파)이나, 런타임 설정 채널 + 운영 프로파일 + baseline 문서 정렬을 함께 포함하므로 standard profile.
|
||||
- NFR-1(당일 무회귀)은 본 batch verification 의 전체 회귀 게이트, NFR-2(무간섭)는 REQ-077 AC-3, NFR-3(TOML 재사용)은 REQ-076 요구사항 제약으로 흡수(dcy-027).
|
||||
- S-1 의 "일자 경계·재시작 보유 유지"는 영구 슬롯 모델(dcy-020, DB 영속)의 기존 보장 — 본 batch 는 프로파일 조합으로 자율 청산 미발화를 검증한다(설정 단위 + 기존 BAT-036 evidence 재참조).
|
||||
@@ -0,0 +1,37 @@
|
||||
# BAT-041 Planning
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-041
|
||||
- Profile: standard
|
||||
|
||||
## Included REQ
|
||||
- REQ-076: watchdog timeout per-instance TOML(`watchdog_timeout_sec`, TOML 우선·env 폴백). AC 4.
|
||||
- REQ-077: 중장기 운영 프로파일 — `config/longterm.example.toml` 레시피 + 기동 구성·병행 무간섭 검증. AC 4.
|
||||
- REQ-078: baseline 정합 — 중장기 흐름 + 보호 규칙 경계 + forced_close_at 미사용. AC 3.
|
||||
|
||||
## Delivery Plan
|
||||
1. `config.py` — `DaemonInstanceConfig.watchdog_timeout_sec: Optional[int] = None` + `from_toml` 파싱(>0 검증) + `from_config` 에서 값 존재 시 `SafetySellConfig` override(`dataclasses.replace`).
|
||||
2. `config/longterm.example.toml` — instance_key/safety_sell_enabled=false/watchdog_timeout_sec=1800/max_active_slots + 키별 중장기 의미 주석(forced_close_at 미사용 포함).
|
||||
3. 테스트 — TOML 로딩(1800 적용)/미설정 env 폴백(120)/인스턴스별 독립/프로파일 조합 기동 구성(워커 미기동 + timeout 적용), instance 격리 회귀.
|
||||
4. `docs/runtime-flows.md` — 중장기 병행 운영 흐름 섹션(토폴로지/프로파일/보호 경계/forced_close_at 미사용) + Change Log.
|
||||
|
||||
## Dependencies
|
||||
- Internal:
|
||||
- `SafetySellConfig.watchdog_timeout_sec` 소비자 2곳 확인 완료 — slots RPC 핸들러(daemon.py:284), SafetySellService(`_cfg.watchdog_timeout_sec`). override 가 `config.safety_sell` 에 반영되면 두 경로 모두 자동 적용.
|
||||
- 기존 TOML 패턴(`max_active_slots`/`default_order_timeout_sec`/`safety_sell_enabled`) 준용.
|
||||
- BAT-036(safety_sell_enabled=false 워커 미기동) 기존 evidence — 프로파일 검증에서 재사용.
|
||||
- External: 없음 (paper broker 로 검증, 라이브 불요).
|
||||
|
||||
## Design Gate
|
||||
- Design Required Before Implementation: yes
|
||||
- Reason: TOML→SafetySellConfig override 주입 지점(frozen dataclass replace), env 폴백 우선순위 의미, 프로파일 검증 범위(설정 단위 vs 실 데몬)를 design 에서 확정.
|
||||
|
||||
## Milestones
|
||||
1. Design confirmed — override 경로·검증 범위 확정
|
||||
2. Implementation — config + example TOML + 테스트 + baseline 문서
|
||||
3. Verification — REQ-076/077/078 AC 매핑 + 전체 회귀(NFR-1) + ruff
|
||||
|
||||
## Risks
|
||||
- R-1 (Low): override 가 SafetySellService 에는 적용되고 slots RPC 핸들러에 미적용(또는 역) — 완화: 단일 지점(`config.safety_sell`) override 로 두 소비자 자동 일치, 테스트로 확인.
|
||||
- R-2 (Low): frozen dataclass 수정 시도 오류 — 완화: `dataclasses.replace` 사용.
|
||||
- R-3 (Low): 일자 경계·재시작 시나리오를 실시간 재현 불가 — 완화: 영구 슬롯 DB 영속(dcy-020 기존 보장) + 자율 청산 미발화는 설정 단위 검증 + BAT-036 evidence 재참조로 충족(S-1 의 run-the-app 전수 재현은 release 운영 검증으로 위임, verification 에 명시).
|
||||
@@ -0,0 +1,48 @@
|
||||
# BAT-041 Verification
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-041
|
||||
|
||||
## Acceptance Mapping
|
||||
|
||||
- REQ-076 AC-1 (TOML `watchdog_timeout_sec = 1800` → 1800s 동작):
|
||||
- Evidence: `test_toml_watchdog_timeout_applied`(1800 적용) + `test_toml_wins_over_env`(env 300 무시, TOML 1800 승리) PASSED.
|
||||
- REQ-076 AC-2 (미설정 → env 폴백, byte-for-byte 무회귀):
|
||||
- Evidence: `test_absent_falls_back_to_env`(env 300 적용) + `test_absent_and_no_env_keeps_default_120` PASSED. 전체 회귀 446 passed(기존 인스턴스 경로 무회귀).
|
||||
- REQ-076 AC-3 (인스턴스별 독립 timeout):
|
||||
- Evidence: `test_instances_get_independent_timeouts` — day 120 / lt 1800 동시 분리 PASSED.
|
||||
- REQ-076 AC-4 (저빈도 폴링 비오인 + last_seen 가시성):
|
||||
- Evidence: timeout 1800s 적용 경로가 slots RPC 핸들러·SafetySellService 양쪽의 단일 소스(`config.safety_sell.watchdog_timeout_sec`, daemon.py:284 wiring 코드 리뷰)로 일치 — 600s 폴링 < 1800s timeout 으로 `is_expired` 산식상 비오인(WatchdogStore.is_expired 는 경과>timeout 비교). `last_seen` 노출 경로(slot_to_dict) 불변(기존 slots 핸들러 16 테스트 회귀 green).
|
||||
|
||||
- REQ-077 AC-1 (프로파일 기동 구성 — 워커 미기동 + 1800 + 독립 큐):
|
||||
- Evidence: `test_longterm_example_profile_loads_with_expected_combination` — instance_key=longterm, `safety_sell_enabled=False`, watchdog 1800, max_active_slots=10 PASSED. 워커 미기동(`safety_sell_enabled=false` → `trex-safety-sell` 스레드 제외 + WARNING)은 BAT-036 verification evidence 유효(본 batch 코드 경로 불변). per-instance 큐명은 dcy-009/BAT-013 evidence 유효.
|
||||
- REQ-077 AC-2 (멀티데이 보유 유지 — 일자 경계·재시작):
|
||||
- Evidence: 영구 자산 슬롯의 DB 영속·날짜 키 부재(dcy-020/BAT-027~030 verification — holding 이 재시작·일자 경계 통과)는 기존 evidence 유효. 본 프로파일은 자율 청산 트리거 전부 OFF(safety_sell_enabled=false + market-close env 미설정) 조합 — 설정 단위 검증 PASSED. **실 데몬 장기 run(수일) 검증은 release 운영 단계로 위임**(planning R-3 명시) — 코드 경로는 기존 검증 조합의 재구성이므로 release-ready 차단 아님.
|
||||
- REQ-077 AC-3 (당일·중장기 병행 무간섭):
|
||||
- Evidence: `test_instances_get_independent_timeouts`(설정 독립) + 기존 instance topology/스코프 격리 테스트 회귀 green(446 passed — DB instance_key 스코프·per-instance 큐 기존 evidence 유효).
|
||||
- REQ-077 AC-4 (예시 파일 — 표준 레시피 + 의미 주석):
|
||||
- Evidence: `config/longterm.example.toml` — 키별 중장기 의미 주석(safety_sell off 이유, 1800s 근거, forced_close_at 미사용, market-close env 미설정) 포함, 예시 로딩 테스트로 유효성 보증.
|
||||
|
||||
- REQ-078 AC-1 (runtime-flows 중장기 흐름 — 토폴로지/프로파일/보호 경계 정합):
|
||||
- Evidence: `docs/runtime-flows.md` `중장기(멀티데이) 인스턴스 병행 운영` flow 신설(6단계) + Change Log — 현행 코드(BAT-036 워커 미기동, REQ-076 override 경로)와 정합 확인.
|
||||
- REQ-078 AC-2 (forced_close_at 미사용 명시):
|
||||
- Evidence: runtime-flows flow 5단계("의미 없다 — 집행 주체 OFF, 발화 불가") + longterm.example.toml 주석 명시.
|
||||
- REQ-078 AC-3 (운영자 판단 가능성 — 보호장치 부재·책임 주체 명확):
|
||||
- Evidence: flow 4단계 — "코어 손절매 루틴 일절 미사용 / 모든 보호 규칙 전략 모듈 전적 책임 / 유일한 백스톱 = 운영자 CLI exit" 모호함 없이 명시.
|
||||
|
||||
## Evidence
|
||||
|
||||
- Test:
|
||||
- `pytest tests/runtime/test_watchdog_timeout_toml.py` — 7 passed (신규)
|
||||
- `pytest tests/` — **446 passed, 46 skipped** (BAT-040 439 → +7, 회귀 0 — dcy-027 NFR-1 당일 무회귀 게이트 green)
|
||||
- `ruff check trex_core tests` — All checks passed
|
||||
- Manual / Baseline 동기화 (Architecture Impact: runtime-flows):
|
||||
- `docs/runtime-flows.md` 중장기 운영 flow 신설 + Change Log ✓ 코드 정합.
|
||||
- `docs/project-structure.md`: 변경 불필요(design Reference Doc Update Plan — 구조 불변, config 필드 1개 + 예시 파일).
|
||||
- override 단일 지점(daemon.py:284 + SafetySellService) 코드 리뷰 확인.
|
||||
|
||||
## Result
|
||||
- Ready for Release: true
|
||||
- Blocking Issues: 없음
|
||||
- REQ-077 AC-2 의 실 데몬 장기 run(수일) 검증은 release 운영 단계 사항 — 구성 요소별 기존 evidence(영구 슬롯 영속 BAT-027~030, 워커 미기동 BAT-036) + 프로파일 조합 설정 검증으로 release-ready 충족. verification 한계로 명시.
|
||||
- out-of-scope finding(`default_order_timeout_sec` TOML 전파 누락, implementation.md 기록)은 본 batch REQ 와 무관한 pre-existing — 별도 change-req 후속.
|
||||
@@ -48,3 +48,4 @@
|
||||
| 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/) |
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# 0. 문서 상태
|
||||
|
||||
- 상태: draft
|
||||
- 상태: confirmed
|
||||
- 문서 ID: dcy-027_20260612_long-term-strategy-slot-operation
|
||||
- 이슈명: 중장기 매매 전략용 슬롯 운영 도입 — 구현 위치 결정 + 멀티데이 리스크 규칙 + 운영 프로파일 (issue #20)
|
||||
- 작성 시각(KST): 2026-06-12
|
||||
- 마지막 갱신 시각(KST): 2026-06-12
|
||||
- 마지막 갱신 시각(KST): 2026-06-12 (draft-req: REQ-076·077·078 생성 — 생성된 REQ 참조 갱신)
|
||||
- 대체됨: 없음
|
||||
- 후속 Discovery 참조: 없음
|
||||
- 생성된 REQ 참조: 없음
|
||||
- 생성된 REQ 참조: REQ-076(watchdog timeout per-instance TOML), REQ-077(중장기 운영 프로파일), REQ-078(중장기 baseline 정합)
|
||||
|
||||
# 1. 계획 상태 요약
|
||||
|
||||
@@ -31,11 +31,11 @@
|
||||
- **이슈 #20 의 충돌 분석 중 이미 해소된 항목**: `daily_slot` 일일 리셋·preallocation 은 BAT-030 cutover 로 폐기됨(영구 자산 슬롯이 날 경계를 넘어 holding 유지 — `trex_core/domain/slot/`, `(instance_key, portfolio_id)` 키). `closed→idle` 재진입·`entry_count` 도 폐기됨(BAT-030/034). 장 마감 강제 청산·safety-sell 은 `safety_sell_enabled=false`(BAT-036, per-instance TOML)로 인스턴스 단위 옵트아웃 가능. 다중 인스턴스 토폴로지(dcy-009)·per-instance TOML(REQ-048)·config-dir(BAT-026)로 `instance_key=longterm` 독립 데몬 운영이 이미 가능.
|
||||
- **잔여 갭 1 — 멀티데이 보호장치 부재**: `SafetySellService`(`trex_core/application/safety_sell.py`)의 stop-loss·forced_close·market-close 는 전부 safety-sell 워커에 묶여 있고 watchdog-gated(당일 전략 이탈 전제)다. `safety_sell_enabled=false` 인 중장기 인스턴스는 **어떤 코어 보호장치도 없다** — 멀티데이 stop-loss·트레일링 스톱·타임스톱이 존재하지 않는다.
|
||||
- **잔여 갭 2 — watchdog 의미 충돌**: 워치독 timeout 기본 120s(REQ-034)는 분 단위 폴링 당일 전략 전제다. 하루 1~2회 체크하는 중장기 전략은 watchdog expired 가 일상 상태가 되어 가시화 신호로서 의미를 잃는다.
|
||||
- **잔여 갭 3 — forced_close_at 의미**: 당일 청산 시각 전제 필드(REQ-032/037)로, 중장기에서는 타임스톱으로 재해석하거나 미사용 처리할지 미정.
|
||||
- **잔여 갭 3 — forced_close_at 의미**: 당일 청산 시각 전제 필드(REQ-032/037)로, 중장기에서는 타임스톱으로 재해석하거나 미사용 처리할지 미정이었음 → **미사용(의미 없음)으로 확정** (§10 CONFIRM, FR-4).
|
||||
- **잔여 갭 4 — 운영 프로파일 부재**: 중장기 인스턴스용 표준 TOML 구성(safety_sell off + market-close 미설정 + watchdog 완화 + max_active_slots)이 문서화·검증된 적 없다. baseline 문서(`docs/project-structure.md`, `docs/runtime-flows.md`)는 존재·최신이나 당일 운영 중심으로 기술돼 있다.
|
||||
- 기대 상태
|
||||
- 구현 위치가 확정된다 — 현행 영구 슬롯 모델이 멀티데이 보유를 이미 지원하므로, 이슈 권장안(a)의 "신규 `PositionBook` 도메인"이 여전히 필요한지, 아니면 **기존 슬롯 도메인 + 중장기 운영 프로파일 + 멀티데이 리스크 규칙 추가**로 충분한지 판정된다.
|
||||
- `safety_sell_enabled=false` 인스턴스에도 선택 가능한 멀티데이 보호 규칙(stop-loss/트레일링/타임스톱 중 합의된 범위)이 제공되거나, "전략 모듈 전적 책임"으로 명시적으로 선을 긋는다.
|
||||
- 구현 위치 확정 완료 — **(a′) 기존 슬롯 도메인 + 중장기 운영 프로파일 추가** (OQ-1 Answered). 신규 `PositionBook` 도메인 불필요.
|
||||
- 보호 규칙 경계 확정 완료 — **"전략 모듈 전적 책임"으로 명시적으로 선을 긋는다** (OQ-2 Answered). 코어는 운영자 CLI `exit` 백스톱만 제공하며, 이 경계가 운영 문서에 명시된다.
|
||||
- 중장기 인스턴스 운영 프로파일(TOML 레시피 + watchdog 정책)이 baseline 문서에 기술되고 실 데몬으로 검증된다.
|
||||
- 이해관계자
|
||||
- 운영자/시스템 관리자 (Justin Ko): 당일·중장기 인스턴스 병행 운영, 리스크 정책 결정
|
||||
@@ -55,16 +55,17 @@
|
||||
|
||||
# 4. 이번에 정의할 변경
|
||||
|
||||
- 구현 위치 판정 — 이슈 OQ-1 을 현행 영구 슬롯 모델 기준으로 재평가: (a′) 기존 슬롯 도메인 그대로 + 중장기 운영 프로파일/리스크 규칙 추가(현행 토대 활용, 유력) vs (a) 신규 `PositionBook` 도메인 병렬 추가(이슈 원안) vs (c) 별도 레포(이슈가 이미 비권장). 판정 결과를 REQ 분할의 전제로 확정한다.
|
||||
- 멀티데이 리스크 규칙 범위 확정 — `safety_sell_enabled=false` 인스턴스의 보호장치: 멀티데이 stop-loss·트레일링 스톱·타임스톱(forced_close_at 재해석) 중 코어가 제공할 범위 vs 전략 모듈 전적 책임 경계를 결정한다.
|
||||
- 중장기 인스턴스 운영 프로파일 정의 — 표준 TOML 레시피(safety_sell off·market-close 미설정·watchdog timeout 정책·max_active_slots) + watchdog 의미 재검토(중장기 폴링 주기 대응) + baseline 문서(`docs/project-structure.md`, `docs/runtime-flows.md`) 중장기 운영 흐름 갱신.
|
||||
- 구현 위치 확정 — **(a′) 기존 슬롯 도메인 유지 + 중장기 운영 프로파일/규칙 추가** (OQ-1 Answered). 현행 영구 자산 슬롯이 멀티데이 보유를 구조적으로 지원하므로 신규 `PositionBook` 도메인(이슈 원안 (a))·별도 레포((c))는 미채택. 이 판정이 REQ 분할의 전제다.
|
||||
- 멀티데이 보호 규칙 경계 확정 — **전략 모듈 전적 책임** (OQ-2 Answered). `safety_sell_enabled=false` 인스턴스는 코어 손절매 루틴(stop-loss·트레일링·타임스톱)을 일절 사용하지 않고, 코어는 운영자 CLI `exit` 백스톱만 제공한다. 이 경계를 운영 문서에 명시한다(FR-2).
|
||||
- 중장기 인스턴스 운영 프로파일 정의 — 표준 TOML 레시피(safety_sell off·market-close 미설정·watchdog per-instance timeout(OQ-3: TOML `watchdog_timeout_sec`, 중장기 1800s 이상)·max_active_slots) + baseline 문서(`docs/project-structure.md`, `docs/runtime-flows.md`) 중장기 운영 흐름 갱신.
|
||||
|
||||
## 영향 범위
|
||||
|
||||
- 코드 영향:
|
||||
- 구현 위치 판정 (a′) 기준: `trex_core/runtime/config.py`(중장기 프로파일 관련 설정 항목), `trex_core/application/safety_sell.py` 또는 신규 멀티데이 리스크 워커(규칙 범위 결정에 따름), watchdog timeout per-instance 정책(`trex_core/application/watchdog.py` + config)
|
||||
- 구현 위치 판정 (a) 기준: 신규 도메인 패키지(`trex_core/domain/position_book/` 류) + persistence + 워커 — 범위 대폭 확대
|
||||
- 당일 경로(`domain/slot/`, `intents.py`, `execution.py`)는 양쪽 모두 불변 목표
|
||||
- 코드 영향 (구현 위치 (a′) 확정 기준 — OQ-1):
|
||||
- `trex_core/runtime/config.py` — 중장기 프로파일 설정: per-instance TOML `watchdog_timeout_sec`(OQ-3, 현재 env `TREX_WATCHDOG_TIMEOUT` 전용 → TOML 우선 지원)
|
||||
- watchdog timeout per-instance 적용 경로(`trex_core/application/watchdog.py` + config wiring)
|
||||
- 멀티데이 리스크 워커/`safety_sell.py` 변경 **없음** — OQ-2 결정(코어 손절매 루틴 미제공, 전략 전적 책임)으로 코어 보호 규칙 구현이 범위에서 제외됨
|
||||
- 당일 경로(`domain/slot/`, `intents.py`, `execution.py`)는 불변 목표
|
||||
- 문서 영향:
|
||||
- `docs/project-structure.md`·`docs/runtime-flows.md`: 중장기 인스턴스 운영 흐름·프로파일 추가 (baseline 갱신)
|
||||
- 신규 REQ 다수 예상(운영 프로파일·리스크 규칙) + REQ-048(TOML 설정)·REQ-034(watchdog) change-req 가능성
|
||||
@@ -79,7 +80,7 @@
|
||||
- FR-1: 중장기 인스턴스 운영 프로파일을 정의한다 — 표준 TOML 레시피(safety_sell_enabled=false, market-close 미설정, watchdog 정책, max_active_slots)와 기동 검증 절차를 문서화하고 실 데몬으로 확인한다.
|
||||
- FR-2: `safety_sell_enabled=false` 인스턴스는 코어 손절매 루틴을 일절 사용하지 않는다. 모든 보호 규칙(stop-loss·트레일링·타임스톱)은 전략 모듈의 전적 책임이며, 코어는 운영자 CLI `exit` 백스톱만 제공한다. 이 경계를 운영 문서에 명시한다.
|
||||
- FR-3: watchdog 의 중장기 의미를 재정의한다 — per-instance timeout 설정 또는 중장기 인스턴스 비활성화 옵션 중 확정안을 적용해, 저빈도 폴링 전략이 expired 로 오인되지 않게 한다.
|
||||
- FR-4: `forced_close_at` 의 중장기 의미(타임스톱 재해석 vs 미사용)를 확정하고 운영자 CLI·intent 계약 문서에 반영한다.
|
||||
- FR-4: `forced_close_at` 은 중장기 인스턴스에서 **미사용(의미 없음)** 으로 확정한다 (CONFIRM 2026-06-12). 집행 주체(safety-sell 워커)가 OFF 이므로 구조적으로 발화 불가 — 운영자 CLI·intent 계약 문서에 "중장기 인스턴스에서 미사용"을 명시한다.
|
||||
- FR-5: `docs/project-structure.md`·`docs/runtime-flows.md` 에 중장기 인스턴스 운영 구조와 흐름(병행 인스턴스 토폴로지, 프로파일, 리스크 규칙 경로)을 갱신한다.
|
||||
|
||||
## 비기능 요구사항
|
||||
@@ -100,7 +101,7 @@
|
||||
- 중장기 전략 모듈 자체의 구현(별도 저장소/주기 — 코어는 계약·운영 토대만 제공)
|
||||
- 포트폴리오 리밸런싱 엔진·목표 비중 관리(전략 모듈 책임)
|
||||
- 일일 회계·리포트 체계 개편(현행 ledger 날짜 필터 해석 유지, 필요 시 후속)
|
||||
- {{DECIDE_POSITION_BOOK}}: 신규 `PositionBook` 도메인 신설 — 구현 위치 판정이 (a) 로 결정될 때만 In Scope 로 승격
|
||||
- 신규 `PositionBook` 도메인 신설 — **Out of Scope 확정** (OQ-1: 구현 위치 (a′) 결정으로 미채택)
|
||||
|
||||
# 6. 리스크/가정 목록
|
||||
|
||||
@@ -108,8 +109,8 @@
|
||||
|
||||
- R-1 (High): 보호장치 경계가 모호한 채 중장기 인스턴스를 운영하면, safety-sell OFF + 전략 모듈 장애 시 손실 포지션이 무기한 방치된다.
|
||||
- 완화 방안: **전략 모듈 전적 책임으로 확정(OQ-2)**. 코어는 운영자 CLI `exit` 백스톱만 제공. 보호 규칙 공백 위험은 전략 모듈 설계 및 운영 절차(운영자 수동 CLI)로 완화한다.
|
||||
- R-2 (Medium): 멀티데이 규칙을 기존 safety-sell 워커에 얹으면 당일 경로와 조건 분기가 얽혀 회귀 위험(이슈가 경고한 "분기 폭증")이 생긴다.
|
||||
- 완화 방안: 설계 단계에서 별도 워커/서비스 분리를 우선 검토하고, 당일 경로 테스트 전부를 회귀 게이트로 고정한다.
|
||||
- R-2 (해소됨 — OQ-2): 멀티데이 규칙을 기존 safety-sell 워커에 얹으면 당일 경로와 조건 분기가 얽혀 회귀 위험(이슈가 경고한 "분기 폭증")이 생긴다.
|
||||
- 해소 근거: OQ-2 결정으로 코어가 멀티데이 보호 규칙을 구현하지 않으므로 safety-sell 워커 변경 자체가 없다. 당일 경로 테스트 전체는 NFR-1 무회귀 게이트로 유지.
|
||||
- R-3 (Medium): watchdog 완화가 과도하면 중장기 인스턴스에서 전략 이탈을 영영 감지하지 못한다.
|
||||
- 완화 방안: 비활성화 대신 긴 timeout(예: 일 단위) 설정을 기본안으로 검토하고, slots RPC `watchdog.last_seen` 가시성은 유지한다.
|
||||
- R-4 (Low): 멀티데이 보유 중 universe 에서 자산이 빠지면(상폐 등) tombstone/retire 경로가 중장기 보유와 충돌할 수 있다.
|
||||
@@ -118,7 +119,7 @@
|
||||
## 가정
|
||||
|
||||
- A-1: 현행 영구 자산 슬롯(BAT-027~030)은 멀티데이 보유를 구조적으로 이미 지원한다 — 슬롯 키에 날짜가 없고 holding 이 날 경계·재시작을 넘는다(dcy-020 OQ-3 결정).
|
||||
- A-2: 중장기 운영은 별도 `instance_key` 데몬으로 분리한다(이슈 OQ-3 의 권장 방향) — 단일 데몬 내 호라이즌 혼합은 다루지 않는다.
|
||||
- A-2: 중장기 운영은 별도 `instance_key` 데몬으로 분리한다(이슈 OQ-3 의 권장 방향) — 단일 데몬 내 호라이즌 혼합은 다루지 않는다. **(승인 확정 — 2026-06-12, Justin Ko, §10 CONFIRM)**
|
||||
- A-3: 플랫폼 계층(브로커/ledger/transport/slots RPC/CLI)은 변경 없이 공유 가능하다 — 이슈의 재사용 분석 유지.
|
||||
- A-4: KIS·portfolio-hub·RabbitMQ 인프라를 그대로 사용하므로 별도 레포 분리(이슈 OQ-1 의 (c))는 손해다 — 이슈 분석 수용.
|
||||
|
||||
@@ -145,36 +146,41 @@
|
||||
# 7. 초기 성공 기준
|
||||
|
||||
- S-1: `instance_key=longterm` 프로파일 TOML 로 기동한 데몬에서 매수→day 경계 통과→보유 유지가 확인된다(장 마감·재시작 후에도 holding 유지, 자율 청산 미발화).
|
||||
- S-2: 확정된 멀티데이 보호 규칙(코어 제공분)이 발화 조건에서 정확히 동작하고, 미제공 영역은 운영 문서에 전략 책임으로 명시된다.
|
||||
- S-2: 보호 규칙 경계가 운영 문서에 명시된다 — `safety_sell_enabled=false` 인스턴스의 모든 보호 규칙(stop-loss·트레일링·타임스톱)은 전략 모듈 전적 책임(OQ-2), 코어는 운영자 CLI `exit` 백스톱만 제공. 백스톱 경로(CLI exit → 청산)가 중장기 인스턴스에서 동작함이 확인된다.
|
||||
- S-3: 당일 인스턴스 전체 테스트 무회귀 + 중장기 인스턴스와의 무간섭(슬롯·큐 스코프)이 통합 테스트로 검증된다.
|
||||
- S-4: baseline 문서(project-structure·runtime-flows)에 중장기 운영 구조·흐름이 반영되어 이후 REQ/batch 가 참조 가능하다.
|
||||
|
||||
## 측정 방식 및 데이터 출처
|
||||
|
||||
- S-1 측정 방식: 실 데몬 run-the-app 검증 — longterm TOML 기동 후 paper 매수, 시스템 시각/일자 경계 시나리오로 holding 유지 확인 / 데이터 출처: `run-trex-core-daemon` skill 경로 + 데몬 로그 + `trex-core slots`
|
||||
- S-2 측정 방식: 규칙별 단위 테스트(발화/비발화 경계) + paper e2e / 데이터 출처: `tests/application/` 신규 테스트
|
||||
- S-2 측정 방식: 운영 문서(baseline) diff 리뷰 — 보호 규칙 경계(전략 책임 + CLI 백스톱) 명시 확인 + 중장기 프로파일 인스턴스에서 `trex-core exit` 백스톱 paper 동작 확인 / 데이터 출처: `docs/runtime-flows.md` + 데몬 로그·`trex-core slots`
|
||||
- S-3 측정 방식: `pytest` 전체 회귀 + instance_key 격리 통합 테스트(기존 `test_instance_topology.py` 패턴 확장) / 데이터 출처: 로컬/CI 결과
|
||||
- S-4 측정 방식: baseline 문서 diff 리뷰 — 중장기 흐름 섹션 존재 + 현행 코드와 정합 / 데이터 출처: `docs/project-structure.md`, `docs/runtime-flows.md`
|
||||
|
||||
# 8. REQ로 넘기기 전 확인 체크
|
||||
|
||||
- [ ] 범위(In Scope/Out of Scope) 확정
|
||||
- 기준: OQ-1(구현 위치) 확정으로 {{DECIDE_POSITION_BOOK}} In/Out 판정 + OQ-2(보호 규칙 범위) 확정
|
||||
- [ ] 성공 지표의 측정 방식/데이터 출처 확정
|
||||
- 기준: OQ-4(전략 폴링 주기) 수치 확보로 S-1/watchdog 검증 수치 구체화
|
||||
- [ ] High 영향 리스크 우선순위 및 해소 책임자 지정
|
||||
- 기준: R-1(보호장치 공백) 경계 확정 담당 Justin Ko
|
||||
- [ ] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
|
||||
- 기준: OQ 목록에 `Open` 상태 항목이 0개이거나, 잔여 항목 전부 `Deferred` 처리 완료
|
||||
- [ ] Freeze 확정 담당자(CONFIRMED_BY) 지정
|
||||
- 기준: Justin Ko 승인 후 `confirm-discovery` 수행
|
||||
- [x] 범위(In Scope/Out of Scope) 확정
|
||||
- 기준: OQ-1(구현 위치 a′)·OQ-2(보호 규칙 전략 책임) Answered + A-2(병행 인스턴스) 승인 + forced_close_at 미사용 확정으로 충족 — PositionBook Out of Scope 확정. CONFIRM 잔여 0건
|
||||
- 결과: 충족 — In/Out 경계 §5 확정, 사용자 결정 잔여 0건
|
||||
- [x] 성공 지표의 측정 방식/데이터 출처 확정
|
||||
- 기준: OQ-4 수치(1~10분 폴링, watchdog 1800s 이상) 확보로 S-1/watchdog 검증 수치 구체화 완료
|
||||
- 결과: 충족 — S-1~S-4 측정 방식·데이터 출처 §7 명시
|
||||
- [x] High 영향 리스크 우선순위 및 해소 책임자 지정
|
||||
- 기준: R-1(보호장치 공백) — OQ-2 결정으로 경계 확정(전략 책임 + CLI 백스톱), 담당 Justin Ko
|
||||
- 결과: 충족 — R-1 경계 확정, R-2 해소(OQ-2), R-3/R-4 완화 방안 확정
|
||||
- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
|
||||
- 기준: OQ-1~4 전부 Answered — `Open` 항목 0개
|
||||
- 결과: 충족 — Open 0건
|
||||
- [x] Freeze 확정 담당자(CONFIRMED_BY) 지정
|
||||
- 기준: §10 CONFIRM 2건 해소 + Justin Ko 승인 후 `confirm-discovery` 수행
|
||||
- 결과: 충족 — Justin Ko 승인, confirm-discovery 수행 완료
|
||||
|
||||
# 9. 파일 처리 결과
|
||||
|
||||
- 처리 결과: 생성
|
||||
- 생성 경로: docs/discovery/dcy-027_20260612_long-term-strategy-slot-operation.md
|
||||
- 참조 문서:
|
||||
- GitHub issue #20 — 중장기 슬롯 운영 도입, 호라이즌 결합 분석 + OQ-1~4 (구 pool 모델 기준 — 본 문서 §3 에서 현행 기준 재정렬)
|
||||
- GitHub issue #20 (closed — 본 Discovery 로 이월) — 중장기 슬롯 운영 도입, 호라이즌 결합 분석 + OQ-1~4 (구 pool 모델 기준 — 본 문서 §3 에서 현행 기준 재정렬)
|
||||
- docs/discovery/dcy-020_20260610_asset-centric-slot-model.md — 영구 자산 슬롯 토대(Out of Scope 로 본 주기 이월 명시), OQ-3 trading_date 폐기 결정
|
||||
- docs/discovery/dcy-024_20260611_safety-sell-disable-mode.md — safety_sell_enabled per-instance TOML ("중장기 보유 인스턴스는 enabled=false" 용도 명시)
|
||||
- trex_core/application/safety_sell.py — 당일 전제 보호장치(watchdog-gated stop-loss/forced_close/market-close), 멀티데이 규칙 부재 확인
|
||||
@@ -192,12 +198,12 @@
|
||||
|
||||
## CONFIRM
|
||||
|
||||
- {{CONFIRM_PARALLEL_INSTANCE}}: 중장기 운영을 별도 instance_key 데몬으로 분리하는 전제(A-2) 승인
|
||||
- {{CONFIRM_FORCED_CLOSE_SEMANTICS}}: forced_close_at 의 중장기 의미 — 타임스톱 재해석 vs 미사용 (FR-4)
|
||||
- ~~{{CONFIRM_PARALLEL_INSTANCE}}~~: 중장기 운영을 **별도 instance_key 데몬으로 분리** — 승인 확정 (A-2, 2026-06-12, Justin Ko). 단일 데몬 내 호라이즌 혼합은 다루지 않으며, dcy-009 토폴로지·REQ-048 per-instance TOML 전제 위에서 병행 운영한다.
|
||||
- ~~{{CONFIRM_FORCED_CLOSE_SEMANTICS}}~~: forced_close_at 의 중장기 의미 — **미사용(의미 없음) 확정** (FR-4, 2026-06-12, Justin Ko). 중장기 모드에서 forced_close_at 은 의미가 없다. 집행 주체(safety-sell 워커)가 `safety_sell_enabled=false` 로 OFF 이므로 구조적으로 발화 불가하며, 타임스톱 재해석(별도 집행 경로 신설)은 OQ-2 결정(코어 손절매 미제공)과 충돌해 미채택. 운영자 CLI·intent 계약 문서에 "중장기 인스턴스에서 미사용" 명시.
|
||||
|
||||
## DATA
|
||||
|
||||
- {{DATA_STRATEGY_CADENCE}}: 중장기 전략 모듈의 폴링·intent 발신 주기 계획 (OQ-4 — watchdog·프로파일 수치 입력)
|
||||
- 없음 — 중장기 전략 모듈의 폴링·intent 발신 주기는 OQ-4 에서 확보됨(answered): 최소 1분~최대 10분(600s) 주기, watchdog timeout 권장 1800초(30분) 이상의 입력값.
|
||||
|
||||
# 11. Discovery Freeze
|
||||
|
||||
@@ -211,8 +217,8 @@
|
||||
|
||||
## Freeze 플래그
|
||||
|
||||
- Handoff Decision: 보류
|
||||
- Handoff Rationale: 초기 Discovery 초안 생성 완료. OQ-1(구현 위치)·OQ-2(보호 규칙)·OQ-3(watchdog)·OQ-4(전략 주기) 4건 Open — confirm-discovery 전 사용자 결정 필요.
|
||||
- Ready for REQ Drafting: false
|
||||
- Confirmed By: TBD
|
||||
- Confirmed At (KST): TBD
|
||||
- Handoff Decision: 승인
|
||||
- Handoff Rationale: OQ-1(구현 위치 a′)·OQ-2(보호 규칙 전략 전적 책임)·OQ-3(watchdog timeout 연장)·OQ-4(폴링 주기 1~10분) 4건 모두 Answered. CONFIRM 2건도 확정 — A-2(별도 instance_key 분리) 승인 + forced_close_at 중장기 미사용(의미 없음). 사용자 결정 잔여 0건. 구현 부재는 정상(신규 작업 정의 Discovery — 코어 변경은 watchdog per-instance TOML 정도로 경량).
|
||||
- Ready for REQ Drafting: true
|
||||
- Confirmed By: Justin Ko
|
||||
- Confirmed At (KST): 2026-06-12
|
||||
|
||||
@@ -32,4 +32,4 @@
|
||||
| dcy-024_20260611_safety-sell-disable-mode | 20260611 | safety-sell 자율 청산 비활성화 모드 — per-instance TOML `safety_sell_enabled=false` 시 워커 미기동 (issue #15) | [Discovery](./dcy-024_20260611_safety-sell-disable-mode.md) | confirmed |
|
||||
| dcy-025_20260611_strategy-read-surface-slots-req | 20260611 | 전략 read 표면 재설계 — `trex.snapshot.req`→`trex.slots.req` + 슬롯 상태 필터 + 슬롯 단일 행(assets[]/available_slots/watchdog[] 흡수·제거) | [Discovery](./dcy-025_20260611_strategy-read-surface-slots-req.md) | confirmed |
|
||||
| dcy-026_20260612_sell-transaction-tax-and-fee-policy | 20260612 | 매도 증권거래세 누락 + 수수료 정책 정정 — `tax=0.0` 하드코딩 제거 + 단일 정책 지점 (issue #24) | [Discovery](./dcy-026_20260612_sell-transaction-tax-and-fee-policy.md) | confirmed |
|
||||
| dcy-027_20260612_long-term-strategy-slot-operation | 20260612 | 중장기 매매 전략용 슬롯 운영 도입 — 구현 위치 결정 + 멀티데이 리스크 규칙 + 운영 프로파일 (issue #20, dcy-020 Out-of-Scope 이월) | [Discovery](./dcy-027_20260612_long-term-strategy-slot-operation.md) | draft |
|
||||
| dcy-027_20260612_long-term-strategy-slot-operation | 20260612 | 중장기 매매 전략용 슬롯 운영 도입 — 구현 위치 결정 + 멀티데이 리스크 규칙 + 운영 프로파일 (issue #20, dcy-020 Out-of-Scope 이월) | [Discovery](./dcy-027_20260612_long-term-strategy-slot-operation.md) | confirmed |
|
||||
|
||||
@@ -212,6 +212,17 @@
|
||||
4. **broadcast**: `trex.broadcast` 에 publish → 모든 인스턴스 control 큐 동시 전달(global kill-switch 등). 중복 처리는 idempotency 로 무해.
|
||||
5. 기존 reply-to RPC 직접 큐 경로(default)는 그대로 동작 — 추가 바인딩은 비파괴적(FR-5 무회귀).
|
||||
|
||||
## Flow: `중장기(멀티데이) 인스턴스 병행 운영` (BAT-041 / dcy-027, REQ-076~078)
|
||||
|
||||
중장기 매매 전략용 인스턴스를 당일 인스턴스와 같은 인프라(PostgreSQL·RabbitMQ)에서 병행 운영하는 프로파일. 별도 도메인 없이 영구 자산 슬롯(dcy-020) + per-instance 설정 조합으로 구성한다(dcy-027 OQ-1 (a′)).
|
||||
|
||||
1. **분리 기동**: 중장기 데몬은 전용 `instance_key`(예: `longterm`) TOML 로 기동(`config/longterm.example.toml` 표준 레시피) — 슬롯(DB instance_key 스코프)·큐(`trex.slots.req.longterm` 등)가 당일 인스턴스와 무간섭(dcy-027 A-2, dcy-009 토폴로지).
|
||||
2. **프로파일 구성**: `safety_sell_enabled = false`(자율 청산 워커 미기동, BAT-036) + `watchdog_timeout_sec = 1800` 이상(REQ-076 per-instance TOML — TOML 우선, 미설정 시 env `TREX_WATCHDOG_TIMEOUT` 폴백 기본 120s) + `max_active_slots`(REQ-060) + env `TREX_MARKET_CLOSE_LIQUIDATION_TIME` 미설정.
|
||||
3. **보유 유지**: 영구 자산 슬롯은 날짜 키가 없어 holding 이 장 마감·일자 경계·재시작을 넘어 DB 에 유지된다 — 일일 리셋/강제 청산 없음.
|
||||
4. **보호 규칙 경계 (dcy-027 OQ-2)**: `safety_sell_enabled=false` 인스턴스는 **코어 손절매 루틴을 일절 사용하지 않는다**. stop-loss·트레일링 스톱·타임스톱 등 모든 보호 규칙은 **전략 모듈의 전적 책임**이며, 코어가 제공하는 유일한 백스톱은 운영자 CLI `trex-core exit <slot>`(수동 청산)다. 전략 모듈 장애 시 손실 포지션 방치 위험은 전략 설계 + 운영 절차(수동 CLI)로 완화한다.
|
||||
5. **forced_close_at 미사용 (dcy-027 CONFIRM)**: 중장기 인스턴스에서 `forced_close_at` 은 **의미가 없다** — 집행 주체(safety-sell 워커)가 OFF 이므로 구조적으로 발화 불가. 중장기 슬롯에 설정하지 않는다(타임스톱이 필요하면 전략 모듈이 sell intent 로 구현).
|
||||
6. **watchdog 중장기 의미**: 전략 폴링 주기 1~10분 전제(dcy-027 OQ-4)에서 timeout 1800s 는 `watchdog.expired` 를 유의미한 이탈 신호로 유지한다. `last_seen` 가시성(slots RPC 행)은 그대로. 워치독 소유권 진입 시 외부 알림(슬랙·이메일)은 후속 Discovery.
|
||||
|
||||
## Flow: `broker degrade 분기` (BAT-007 / REQ-024)
|
||||
|
||||
1. transport connection drop → 어댑터가 `_mark_disconnected()` + 백오프(1s→2s→4s→8s→16s→30s cap) 재연결.
|
||||
@@ -329,3 +340,4 @@ timeout 미지정·취소 실패로 잔여 고착된 pending 슬롯을 처분:
|
||||
- 2026-06-11 :: BAT-036 (dcy-024) — 데몬 lifecycle: `safety_sell_enabled=false` 시 `trex-safety-sell` worker 미기동 + WARNING 로그(REQ-048 CHG-01/REQ-005 CHG-01).
|
||||
- 2026-06-10 :: BAT-031 (dcy-021) — 포지션 사이징(증액·부분 감액) 흐름 추가, transit 13 전이.
|
||||
- 2026-06-12 :: BAT-038 (dcy-026/issue #24) — 매도 증권거래세 누락 정정: `체결 → ledger outbox` flow 의 `tax=0` → `calculate_tax(side, amount)`(매도 0.2%/매수 0) 정책 계산. fee/tax 단일 정책 지점 + env(`TREX_KIS_FEE_BPS`/`TREX_KRX_TAX_SELL_BPS`), paper·KIS 동일 공유, `tax=0.0` 하드코딩 제거 (REQ-018 CHG-20260612-01).
|
||||
- 2026-06-12 :: BAT-041 (dcy-027/issue #20 이월) — 중장기 인스턴스 병행 운영 flow 신설: 전용 instance_key 프로파일(`config/longterm.example.toml`), `watchdog_timeout_sec` per-instance TOML(REQ-076, env 폴백), 보호 규칙 경계(전략 모듈 전적 책임 + CLI exit 백스톱, 코어 손절매 미사용), forced_close_at 중장기 미사용 (REQ-076~078).
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# req-076: watchdog timeout per-instance TOML 설정
|
||||
|
||||
- Status: Implemented
|
||||
- Type: Configuration
|
||||
- Priority: High
|
||||
- Owner: project maintainers
|
||||
|
||||
## Intent
|
||||
- 워치독 timeout(현재 env `TREX_WATCHDOG_TIMEOUT` 전역 단일, 기본 120s)을 인스턴스별로 독립 설정 가능하게 한다. 저빈도 폴링(최소 1분~최대 10분, dcy-027 OQ-4) 중장기 전략 인스턴스에서 watchdog expired 가 일상 상태가 되어 가시화 신호로서 의미를 잃는 문제(dcy-027 FR-3/OQ-3)를 해소한다.
|
||||
|
||||
## Requirement
|
||||
- `DaemonInstanceConfig`(REQ-048 per-instance TOML)에 `watchdog_timeout_sec`(정수, 초) optional 키를 추가하고 `config/*.toml` 에서 읽는다. TOML 값이 있으면 그 값이 해당 인스턴스의 워치독 timeout 으로 사용되고, 없으면 기존 env `TREX_WATCHDOG_TIMEOUT`(기본 120s) 동작을 그대로 유지한다(TOML 우선, env 폴백 — 기존 인스턴스 무회귀). 기존 per-instance TOML 메커니즘을 재사용하며 새 설정 채널을 만들지 않는다(dcy-027 NFR-3). 중장기 인스턴스 권장값은 1800s(30분) 이상(OQ-4 최대 폴링 600s 대비 여유).
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given `config/inst.toml` 에 `watchdog_timeout_sec = 1800`, When 데몬 기동, Then 해당 인스턴스의 워치독 timeout 이 1800s 로 동작한다(120s env 기본값 미적용).
|
||||
2. Given TOML 에 `watchdog_timeout_sec` 미설정, When 기동, Then 기존 env `TREX_WATCHDOG_TIMEOUT`(기본 120s) 값으로 동작한다(기존 인스턴스 byte-for-byte 무회귀).
|
||||
3. Given 두 인스턴스가 서로 다른 `watchdog_timeout_sec`(예: 당일 120 / 중장기 1800), When 각자 기동, Then 인스턴스별 독립 timeout 으로 watchdog expired 판정이 분리된다.
|
||||
4. Given 중장기 timeout(1800s) 인스턴스에서 전략이 10분(600s) 주기로 slots RPC(spy=false)를 호출, When watchdog 판정, Then expired 로 오인되지 않고 `last_seen_at` 가시성은 유지된다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs: docs/runtime-flows.md(워치독 설정 경로 — REQ-078 에서 정합), config/instance.example.toml
|
||||
- Modules: trex_core/runtime/config.py(`DaemonInstanceConfig.watchdog_timeout_sec` + `from_toml` + `DaemonConfig` 전파), trex_core/runtime/daemon.py(워치독 timeout wiring)
|
||||
- Tests: tests/runtime/test_config*.py(TOML 로딩/폴백/인스턴스 독립), watchdog 판정 경계 테스트
|
||||
|
||||
## Notes
|
||||
- 출처: dcy-027 FR-3, OQ-3(per-instance timeout 연장 확정), OQ-4(폴링 주기 1~10분 → 권장 1800s+). NFR-3(기존 TOML 메커니즘 재사용) 흡수.
|
||||
- REQ-060(max_active_slots)과 달리 **env 폴백을 유지**한다 — `TREX_WATCHDOG_TIMEOUT` 은 기존 운영 인스턴스가 사용 중이므로 폐기하지 않고 TOML 미설정 시 기본 경로로 보존(무회귀).
|
||||
- 패턴 선례: `safety_sell_enabled`(REQ-048 CHG, dcy-024) — per-instance 운영 모드 차이는 TOML.
|
||||
- watchdog 소유권 진입 시 외부 알림(슬랙·이메일)은 후속 Discovery(dcy-027 OQ-3 주석) — 본 REQ 범위 밖.
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260612-01
|
||||
- Date: 2026-06-12
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
- dcy-027 FR-3/OQ-3/OQ-4 로부터 최초 생성(Proposed). watchdog timeout per-instance TOML + env 폴백.
|
||||
|
||||
#### Intent
|
||||
- 중장기 저빈도 폴링 인스턴스에서 watchdog 가시화 신호를 유의미하게 유지하도록 backlog 정규화.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added: TOML 우선/env 폴백 무회귀/인스턴스별 독립/저빈도 폴링 비오인 4개 기준
|
||||
- Updated: 없음
|
||||
- Removed: 없음
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: instance.example.toml, runtime-flows(REQ-078 정합)
|
||||
- Modules: runtime/config.py, runtime/daemon.py
|
||||
- Tests: test_config*.py + watchdog 경계
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches: none
|
||||
- Releases: none
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: confirm-req 후 batch 편성
|
||||
|
||||
#### Validation Plan
|
||||
- TOML 로딩/폴백/인스턴스 독립 단위 테스트 + watchdog 판정 경계 테스트로 검증.
|
||||
|
||||
### CHG-20260612-02
|
||||
- Date: 2026-06-12
|
||||
- Author: Justin Ko
|
||||
- Type: status
|
||||
- Summary: Proposed → Approved (confirm-req). 필수 필드·구체 AC 충족, source dcy-027(confirmed, handoff 승인, 사용자 결정 잔여 0건)과 모순 없음. env 폴백으로 기존 인스턴스 무회귀 전제 확인.
|
||||
|
||||
### CHG-20260612-03
|
||||
- Date: 2026-06-12
|
||||
- Author: Justin Ko
|
||||
- Type: status
|
||||
- Summary: Approved → Implemented (confirm-req-implemented). 근거: BAT-041(release-candidate) verification — AC-1~4 전부 evidence 연결(`test_watchdog_timeout_toml.py` 7 passed: TOML 적용/TOML>env/env 폴백/기본 120/인스턴스 독립/<=0 거부/프로파일 조합). `from_config` 단일 지점 override 로 소비자 2곳(slots RPC·SafetySellService) 일치. 전체 446 passed + ruff clean. Blocking Issues 없음.
|
||||
@@ -0,0 +1,78 @@
|
||||
# req-077: 중장기 인스턴스 운영 프로파일 — 표준 TOML 레시피 + 기동 검증
|
||||
|
||||
- Status: Implemented
|
||||
- Type: Configuration
|
||||
- Priority: High
|
||||
- Owner: project maintainers
|
||||
|
||||
## Intent
|
||||
- 중장기 매매 전략용 인스턴스의 표준 운영 프로파일(TOML 레시피)을 정의하고 실 데몬 기동으로 검증한다. 멀티데이 보유가 장 마감·일자 경계·재시작을 넘어 유지되고 자율 청산이 발화하지 않음을 보장하는 설정 조합을 확정한다. (dcy-027 FR-1, OQ-1 (a′) — 기존 슬롯 도메인 + 프로파일 추가)
|
||||
|
||||
## Requirement
|
||||
- 중장기 인스턴스 표준 TOML 프로파일을 예시 파일(`config/longterm.example.toml` 또는 동등)로 제공한다. 프로파일 구성: `instance_key`(예: longterm, 당일 인스턴스와 분리 — dcy-027 A-2 확정), `safety_sell_enabled = false`(REQ-048 CHG/dcy-024), `watchdog_timeout_sec = 1800` 이상(REQ-076), `max_active_slots`(REQ-060), market-close 강제 청산 미적용(env `TREX_MARKET_CLOSE_LIQUIDATION_TIME` 미설정 전제 명시).
|
||||
- 이 프로파일로 기동한 데몬에서 매수 체결 후 보유가 일자 경계·재시작을 넘어 유지되고(영구 슬롯, dcy-020 토대) 자율 청산(safety-sell/market-close/forced_close)이 발화하지 않음을 검증한다.
|
||||
- 당일 인스턴스와 같은 PostgreSQL·RabbitMQ 인프라에서 병행 운영 시 슬롯·큐 스코프가 간섭하지 않아야 한다(dcy-027 NFR-2 — instance_key 격리).
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given 중장기 프로파일 TOML(safety_sell_enabled=false, watchdog_timeout_sec≥1800, max_active_slots, 독립 instance_key), When 데몬 기동, Then `trex-safety-sell` 워커 미기동(WARNING 로그) + watchdog timeout 1800s 적용 + 독립 큐(`trex.slots.req.longterm` 등) 선언이 확인된다.
|
||||
2. Given 기동된 중장기 인스턴스에서 paper 매수 체결(holding), When 일자 경계 통과·데몬 재시작, Then 슬롯이 holding 을 유지하고 자율 청산이 발화하지 않는다.
|
||||
3. Given 당일 인스턴스(safety_sell_enabled=true)와 중장기 인스턴스 병행, When 양쪽 기동·운영, Then 슬롯(instance_key 스코프)·큐(per-instance 큐명) 간섭이 없다(기존 instance 격리 테스트 패턴으로 검증).
|
||||
4. Given 예시 프로파일 파일, When 저장소 확인, Then 표준 레시피와 각 키의 중장기 의미(왜 false/1800s 인지) 주석이 포함되어 운영자가 복제 사용 가능하다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs: config/longterm.example.toml(신규 또는 instance.example.toml 확장), docs/runtime-flows.md·docs/project-structure.md(REQ-078 정합)
|
||||
- Modules: 신규 코드 없음 목표 — 기존 설정 메커니즘(REQ-048/060/076 + dcy-024) 조합. 검증 과정에서 갭 발견 시 해당 REQ change-req
|
||||
- Tests: 프로파일 기동 검증(설정 조합 단위) + instance 격리 통합(기존 `test_instance_topology.py` 패턴 확장)
|
||||
|
||||
## Notes
|
||||
- 출처: dcy-027 FR-1, S-1, A-2(별도 instance_key 확정), NFR-2(무간섭 — AC-3 흡수).
|
||||
- 보호 규칙은 전략 모듈 전적 책임(dcy-027 OQ-2) — 코어는 운영자 CLI `exit` 백스톱만 제공. 프로파일은 이를 전제로 한 "보호장치 OFF" 구성이며 경계 명시는 REQ-078 소관.
|
||||
- `forced_close_at` 은 중장기 인스턴스에서 미사용(dcy-027 CONFIRM) — 프로파일 주석에 명시.
|
||||
- REQ-076(watchdog TOML) 선행 의존 — 같은 batch 에서 함께 구현.
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260612-01
|
||||
- Date: 2026-06-12
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
- dcy-027 FR-1/S-1 로부터 최초 생성(Proposed). 중장기 표준 TOML 프로파일 + 기동·멀티데이·격리 검증.
|
||||
|
||||
#### Intent
|
||||
- 중장기 인스턴스를 재현 가능한 표준 레시피로 운영 가능하게 backlog 정규화.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added: 기동 구성 확인/멀티데이 보유 유지/병행 무간섭/예시 파일 4개 기준
|
||||
- Updated: 없음
|
||||
- Removed: 없음
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: longterm.example.toml + baseline(REQ-078)
|
||||
- Modules: 신규 코드 없음 목표(기존 메커니즘 조합)
|
||||
- Tests: 프로파일 기동 + instance 격리
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches: none
|
||||
- Releases: none
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: confirm-req 후 batch 편성(REQ-076 과 동일 batch)
|
||||
|
||||
#### Validation Plan
|
||||
- 프로파일 TOML 로딩 + 워커 미기동 + watchdog 적용 단위, paper 매수→재시작 holding 유지, instance 격리 통합 테스트.
|
||||
|
||||
### CHG-20260612-02
|
||||
- Date: 2026-06-12
|
||||
- Author: Justin Ko
|
||||
- Type: status
|
||||
- Summary: Proposed → Approved (confirm-req). 필수 필드·구체 AC 충족, source dcy-027 정합. REQ-076 의존은 동일 batch 편성으로 해소 예정 — blocker 아님.
|
||||
|
||||
### CHG-20260612-03
|
||||
- Date: 2026-06-12
|
||||
- Author: Justin Ko
|
||||
- Type: status
|
||||
- Summary: Approved → Implemented (confirm-req-implemented). 근거: BAT-041(release-candidate) verification — AC-1/4 프로파일 조합 로딩 테스트(`config/longterm.example.toml` — longterm·safety_sell false·1800·cap 10) PASSED, AC-2 멀티데이 보유는 영구 슬롯 영속(BAT-027~030)·워커 미기동(BAT-036) 기존 evidence + 조합 설정 검증, AC-3 무간섭은 인스턴스 독립 테스트 + 기존 격리 회귀. 실 데몬 장기 run 은 release 운영 검증 위임(verification 명시). 446 passed. Blocking Issues 없음.
|
||||
@@ -0,0 +1,76 @@
|
||||
# req-078: 중장기 운영 baseline 문서 정합 — 보호 규칙 경계 + forced_close_at 미사용 + 병행 운영 흐름
|
||||
|
||||
- Status: Implemented
|
||||
- Type: Documentation
|
||||
- Priority: Medium
|
||||
- Owner: project maintainers
|
||||
|
||||
## Intent
|
||||
- 중장기 인스턴스 운영의 구조·흐름·정책 경계를 baseline 문서에 명시해 이후 REQ/batch 와 운영자가 참조 가능하게 한다. 특히 "보호 규칙은 전략 모듈 전적 책임"이라는 경계(dcy-027 OQ-2)와 `forced_close_at` 중장기 미사용(CONFIRM)을 문서로 확정한다. (dcy-027 FR-2/FR-4/FR-5)
|
||||
|
||||
## Requirement
|
||||
- `docs/runtime-flows.md` 에 중장기 인스턴스 운영 흐름을 추가한다: 병행 인스턴스 토폴로지(당일/중장기 instance_key 분리), 중장기 프로파일 구성(REQ-077), `safety_sell_enabled=false` 시 보호장치 경로(코어 손절매 루틴 일절 미사용 — stop-loss·트레일링·타임스톱 전부 전략 모듈 전적 책임, 코어는 운영자 CLI `exit` 백스톱만 제공).
|
||||
- `forced_close_at` 의 중장기 의미를 명시한다: **미사용(의미 없음)** — 집행 주체(safety-sell 워커)가 OFF 이므로 구조적으로 발화 불가(dcy-027 CONFIRM). 운영자 CLI·intent 계약 관련 서술에 반영한다.
|
||||
- `docs/project-structure.md` 에 중장기 운영 관련 구조(설정 키, 예시 프로파일 위치)가 필요한 만큼 반영한다(구조 변화가 없으면 흐름 문서 중심).
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given `docs/runtime-flows.md`, When 중장기 운영 섹션 확인, Then 병행 인스턴스 토폴로지·프로파일 구성·보호 규칙 경계(전략 전적 책임 + CLI exit 백스톱)가 현행 코드와 정합하게 기술되어 있다.
|
||||
2. Given baseline 문서, When `forced_close_at` 서술 확인, Then 중장기 인스턴스에서 미사용(의미 없음, 발화 불가 근거 포함)이 명시되어 있다.
|
||||
3. Given 신규 운영자, When baseline 문서만으로 중장기 인스턴스 운영 모델 파악 시도, Then 어떤 보호장치가 없고 누가 책임지는지(전략 모듈/운영자 CLI) 모호함 없이 판단 가능하다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs: docs/runtime-flows.md(중장기 운영 흐름 + Change Log), docs/project-structure.md(필요 시)
|
||||
- Modules: 없음(문서 전용)
|
||||
- Tests: 없음(문서 diff 리뷰로 검증 — S-4)
|
||||
|
||||
## Notes
|
||||
- 출처: dcy-027 FR-2(보호 규칙 경계 명시), FR-4(forced_close_at 미사용), FR-5(baseline 갱신), S-2/S-4.
|
||||
- REQ-076/077 구현 결과와 같은 batch 에서 정합 — 코드 확정 후 문서화(선례: REQ-042/047/051 등 baseline 정합 REQ).
|
||||
- R-1(보호장치 공백 방치 위험)의 완화가 곧 이 문서화다 — 경계가 운영 문서에 명시되어야 전략 모듈 설계·운영 절차가 책임을 인지한다.
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260612-01
|
||||
- Date: 2026-06-12
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
- dcy-027 FR-2/FR-4/FR-5 로부터 최초 생성(Proposed). 중장기 운영 baseline 정합 + 보호 경계·forced_close_at 미사용 명시.
|
||||
|
||||
#### Intent
|
||||
- 중장기 운영 모델의 정책 경계를 문서로 확정해 운영·후속 개발 참조점 제공.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added: 흐름 정합/forced_close_at 미사용 명시/운영자 판단 가능성 3개 기준
|
||||
- Updated: 없음
|
||||
- Removed: 없음
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: runtime-flows.md, project-structure.md(필요 시)
|
||||
- Modules: 없음
|
||||
- Tests: 없음
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches: none
|
||||
- Releases: none
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: confirm-req 후 batch 편성(REQ-076/077 과 동일 batch)
|
||||
|
||||
#### Validation Plan
|
||||
- baseline 문서 diff 리뷰 — 중장기 흐름 섹션 존재 + 현행 코드 정합 + 경계 명시 확인.
|
||||
|
||||
### CHG-20260612-02
|
||||
- Date: 2026-06-12
|
||||
- Author: Justin Ko
|
||||
- Type: status
|
||||
- Summary: Proposed → Approved (confirm-req). 필수 필드·구체 AC 충족, source dcy-027 정합(FR-2/4/5 — 보호 경계·forced_close_at 미사용 결정 반영 확인).
|
||||
|
||||
### CHG-20260612-03
|
||||
- Date: 2026-06-12
|
||||
- Author: Justin Ko
|
||||
- Type: status
|
||||
- Summary: Approved → Implemented (confirm-req-implemented). 근거: BAT-041(release-candidate) verification — `docs/runtime-flows.md` `중장기(멀티데이) 인스턴스 병행 운영` flow 신설(토폴로지/프로파일/보호 경계: 전략 전적 책임+CLI exit 백스톱/forced_close_at 미사용/watchdog 의미) + Change Log, 현행 코드 정합 확인(AC-1~3). project-structure 는 구조 불변으로 변경 불요. Blocking Issues 없음.
|
||||
+7
-4
@@ -74,7 +74,7 @@
|
||||
|
||||
## Next Requirement ID
|
||||
|
||||
- Current: `REQ-076`
|
||||
- Current: `REQ-079`
|
||||
- Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다.
|
||||
|
||||
## Requirement Register
|
||||
@@ -156,11 +156,14 @@
|
||||
| REQ-073 | 전략 read 표면 — trex.slots.req RPC 계약 (filter/spy, snapshot.req 즉시 교체) | Interface | Implemented | High | project maintainers | [req-073](./Interface/req-073_slots-req-rpc-contract.md) |
|
||||
| REQ-074 | 슬롯 단일 행 스키마 — 자산·워치독·overlay 흡수 + 필드별 정본 라벨 | Data | Implemented | High | project maintainers | [req-074](./Data/req-074_merged-slot-row-schema.md) |
|
||||
| REQ-075 | slots read 표면 baseline 문서 정합 | Documentation | Implemented | Medium | project maintainers | [req-075](./Documentation/req-075_slots-read-surface-baseline-alignment.md) |
|
||||
| REQ-076 | watchdog timeout per-instance TOML 설정 | Configuration | Implemented | High | project maintainers | [req-076](./Configuration/req-076_watchdog-timeout-per-instance-toml.md) |
|
||||
| REQ-077 | 중장기 인스턴스 운영 프로파일 — 표준 TOML 레시피 + 기동 검증 | Configuration | Implemented | High | project maintainers | [req-077](./Configuration/req-077_long-term-instance-operation-profile.md) |
|
||||
| REQ-078 | 중장기 운영 baseline 문서 정합 — 보호 규칙 경계 + forced_close_at 미사용 | Documentation | Implemented | Medium | project maintainers | [req-078](./Documentation/req-078_long-term-operation-baseline-alignment.md) |
|
||||
|
||||
## Recent Change Log Summary
|
||||
|
||||
- 2026-06-12 :: REQ-076·077·078 Approved → **Implemented** (confirm-req-implemented, BAT-041, dcy-027): 중장기 인스턴스 운영 구현·검증 완료 — watchdog per-instance TOML(단일 지점 override, env 폴백), `config/longterm.example.toml` 프로파일, runtime-flows 중장기 flow(보호 경계·forced_close_at 미사용). 446 passed (+7), ruff clean. 실 데몬 장기 run 은 release 운영 검증 위임. Blocking Issues 없음.
|
||||
- 2026-06-12 :: REQ-076·077·078 Proposed → **Approved** (confirm-req): 필수 필드·구체 AC 충족, source dcy-027(confirmed, 결정 잔여 0건) 정합. REQ-076/077 동일 batch 편성 예정.
|
||||
- 2026-06-12 :: REQ-076·077·078 신규 생성(Proposed, draft-req dcy-027): 중장기 전략 슬롯 운영 — watchdog timeout per-instance TOML(env 폴백), 중장기 운영 프로파일(TOML 레시피+기동·멀티데이·격리 검증), baseline 정합(보호 규칙 전략 전적 책임 + forced_close_at 미사용 명시). NFR-1 은 batch 검증, NFR-2 는 REQ-077 AC, NFR-3 은 REQ-076 제약으로 흡수.
|
||||
- 2026-06-12 :: REQ-002·006 Approved → **Implemented** (confirm-req-implemented, BAT-040, issue #25): slots RPC full scan 개선 구현·검증 완료. `list_slots(status_filter)` DB-level 필터 + 핸들러 위임 + watchdog 조회 재사용. 439 passed (in-memory 필터 5 신규, 핸들러 16 회귀, PG 게이트 1), ruff clean. REQ-073 관찰 계약 불변 Implemented 유지. Blocking Issues 없음. issue #25 close.
|
||||
- 2026-06-12 :: REQ-002·006·073 change-req (CHG-20260612-01/01/02, issue #25): slots RPC per-request full scan 개선 — `list_slots(status_filter)` DB-level status 필터 + 핸들러 위임 + watchdog 조회 재사용. REQ-002/006 AC-6 신규 → Implemented → **Approved** 되돌림(default None 동작 보존). REQ-073 관찰 계약 불변 → **Implemented 유지**.
|
||||
- 2026-06-12 :: REQ-011 Approved → **Implemented** (confirm-req-implemented, BAT-039): paper order seq Redis 영속화 구현·검증 완료. 434 passed (seq store 5 신규 포함), 재시작 단조·correlation_id 비충돌·in-memory 폴백 green. Blocking Issues 없음. issue #4 close.
|
||||
- 2026-06-12 :: REQ-011 change-req (CHG-20260612-01, issue #4): paper 브로커 order seq Redis 영속화 — 재시작 후 `correlation_id` 충돌(매도 원장 누락) 방지. optional seq store(RedisSeqStore INCR + in-memory 폴백) 주입. AC-4 신규. 기존 비동기 계약 불변이나 신규 AC 미구현 → Implemented → **Approved** 되돌림. BAT-039 구현·검증 후 복귀. paper 한정(프로덕션 버그 아님).
|
||||
- 2026-06-12 :: REQ-018 Approved → **Implemented** (confirm-req-implemented, BAT-038): 매도 증권거래세 정책 계산 + `tax=0.0` 제거 구현·검증 완료. 429 passed (fee·tax 정책 6 신규 포함), issue #24 1,376.9원 회귀 green, `tax=0.0` 하드코딩 grep 0건, runtime-flows 동기화. Blocking Issues 없음.
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
"""Per-instance watchdog timeout TOML tests (REQ-076/077, dcy-027, BAT-041).
|
||||
|
||||
TOML `watchdog_timeout_sec` wins over env TREX_WATCHDOG_TIMEOUT; absent keeps
|
||||
the env value (default 120) so intraday instances are byte-for-byte unaffected.
|
||||
The long-term profile combines this with safety_sell_enabled=false.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from trex_core.runtime.config import DaemonConfig, DaemonInstanceConfig
|
||||
|
||||
_REPO = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def _write_toml(tmp_path, body: str, name: str = "inst.toml") -> str:
|
||||
p = tmp_path / name
|
||||
p.write_text(body)
|
||||
return str(p)
|
||||
|
||||
|
||||
# --- REQ-076 AC-1: TOML value applied -----------------------------------------
|
||||
|
||||
def test_toml_watchdog_timeout_applied(tmp_path, monkeypatch):
|
||||
monkeypatch.setenv("TREX_POSTGRESQL_DSN", "postgresql://x/y")
|
||||
path = _write_toml(tmp_path, 'instance_key = "lt"\nwatchdog_timeout_sec = 1800\n')
|
||||
cfg = DaemonConfig.from_config(path)
|
||||
assert cfg.safety_sell.watchdog_timeout_sec == 1800
|
||||
|
||||
|
||||
def test_toml_wins_over_env(tmp_path, monkeypatch):
|
||||
monkeypatch.setenv("TREX_POSTGRESQL_DSN", "postgresql://x/y")
|
||||
monkeypatch.setenv("TREX_WATCHDOG_TIMEOUT", "300")
|
||||
path = _write_toml(tmp_path, 'instance_key = "lt"\nwatchdog_timeout_sec = 1800\n')
|
||||
assert DaemonConfig.from_config(path).safety_sell.watchdog_timeout_sec == 1800
|
||||
|
||||
|
||||
# --- REQ-076 AC-2: absent -> env fallback (no regression) ---------------------
|
||||
|
||||
def test_absent_falls_back_to_env(tmp_path, monkeypatch):
|
||||
monkeypatch.setenv("TREX_POSTGRESQL_DSN", "postgresql://x/y")
|
||||
monkeypatch.setenv("TREX_WATCHDOG_TIMEOUT", "300")
|
||||
path = _write_toml(tmp_path, 'instance_key = "day"\n')
|
||||
assert DaemonConfig.from_config(path).safety_sell.watchdog_timeout_sec == 300
|
||||
|
||||
|
||||
def test_absent_and_no_env_keeps_default_120(tmp_path, monkeypatch):
|
||||
monkeypatch.setenv("TREX_POSTGRESQL_DSN", "postgresql://x/y")
|
||||
monkeypatch.delenv("TREX_WATCHDOG_TIMEOUT", raising=False)
|
||||
path = _write_toml(tmp_path, 'instance_key = "day"\n')
|
||||
assert DaemonConfig.from_config(path).safety_sell.watchdog_timeout_sec == 120
|
||||
|
||||
|
||||
# --- REQ-076 AC-3: per-instance independence ----------------------------------
|
||||
|
||||
def test_instances_get_independent_timeouts(tmp_path, monkeypatch):
|
||||
monkeypatch.setenv("TREX_POSTGRESQL_DSN", "postgresql://x/y")
|
||||
monkeypatch.delenv("TREX_WATCHDOG_TIMEOUT", raising=False)
|
||||
day = _write_toml(tmp_path, 'instance_key = "day"\n', name="day.toml")
|
||||
lt = _write_toml(
|
||||
tmp_path, 'instance_key = "lt"\nwatchdog_timeout_sec = 1800\n', name="lt.toml"
|
||||
)
|
||||
assert DaemonConfig.from_config(day).safety_sell.watchdog_timeout_sec == 120
|
||||
assert DaemonConfig.from_config(lt).safety_sell.watchdog_timeout_sec == 1800
|
||||
|
||||
|
||||
# --- from_toml validation (design: <= 0 rejected) ------------------------------
|
||||
|
||||
def test_nonpositive_timeout_rejected(tmp_path):
|
||||
for bad in ("0", "-60"):
|
||||
path = _write_toml(tmp_path, f'instance_key = "x"\nwatchdog_timeout_sec = {bad}\n')
|
||||
with pytest.raises(RuntimeError):
|
||||
DaemonInstanceConfig.from_toml(path)
|
||||
|
||||
|
||||
# --- REQ-077 AC-1/AC-4: long-term profile recipe -------------------------------
|
||||
|
||||
def test_longterm_example_profile_loads_with_expected_combination(monkeypatch):
|
||||
"""The shipped example profile yields: dedicated instance_key, safety-sell
|
||||
worker OFF, watchdog 1800s, capacity cap set."""
|
||||
monkeypatch.setenv("TREX_POSTGRESQL_DSN", "postgresql://x/y")
|
||||
example = _REPO / "config" / "longterm.example.toml"
|
||||
cfg = DaemonConfig.from_config(str(example))
|
||||
assert cfg.instance_key == "longterm" # A-2: separate instance scope
|
||||
assert cfg.safety_sell_enabled is False # autonomous liquidation OFF
|
||||
assert cfg.safety_sell.watchdog_timeout_sec == 1800
|
||||
assert cfg.max_active_slots == 10
|
||||
@@ -154,6 +154,10 @@ class DaemonInstanceConfig:
|
||||
# dcy-024 / REQ-048 CHG-20260611-01: disable autonomous safety-sell worker.
|
||||
# Absent = True (safe default). False → trex-safety-sell thread not started.
|
||||
safety_sell_enabled: bool = True
|
||||
# dcy-027 / REQ-076: per-instance watchdog timeout (seconds). None = fall
|
||||
# back to env TREX_WATCHDOG_TIMEOUT (default 120). Long-term instances
|
||||
# polling every 1-10 min should set >= 1800 so expired stays meaningful.
|
||||
watchdog_timeout_sec: Optional[int] = None
|
||||
|
||||
@staticmethod
|
||||
def from_toml(path: str) -> "DaemonInstanceConfig":
|
||||
@@ -178,6 +182,12 @@ class DaemonInstanceConfig:
|
||||
raise RuntimeError(
|
||||
f"instance TOML 'default_order_timeout_sec' must be > 0, got {raw_timeout}: {path}"
|
||||
)
|
||||
raw_wd = data.get("watchdog_timeout_sec")
|
||||
watchdog_timeout = int(raw_wd) if raw_wd is not None else None
|
||||
if watchdog_timeout is not None and watchdog_timeout <= 0:
|
||||
raise RuntimeError(
|
||||
f"instance TOML 'watchdog_timeout_sec' must be > 0, got {raw_wd}: {path}"
|
||||
)
|
||||
return DaemonInstanceConfig(
|
||||
instance_key=str(instance_key),
|
||||
portfolio_hub_token=str(data.get("portfolio_hub_token") or ""),
|
||||
@@ -185,6 +195,7 @@ class DaemonInstanceConfig:
|
||||
max_active_slots=max_active,
|
||||
default_order_timeout_sec=default_timeout,
|
||||
safety_sell_enabled=bool(data.get("safety_sell_enabled", True)),
|
||||
watchdog_timeout_sec=watchdog_timeout,
|
||||
)
|
||||
|
||||
|
||||
@@ -340,6 +351,14 @@ class DaemonConfig:
|
||||
pf = base.portfolio_hub
|
||||
if inst.portfolio_hub_token and pf is not None:
|
||||
pf = replace(pf, token=inst.portfolio_hub_token)
|
||||
# dcy-027 / REQ-076: per-instance watchdog timeout. TOML wins over the
|
||||
# env-derived value; absent keeps env TREX_WATCHDOG_TIMEOUT (default 120)
|
||||
# so existing instances are byte-for-byte unaffected.
|
||||
safety_sell = base.safety_sell
|
||||
if inst.watchdog_timeout_sec is not None:
|
||||
safety_sell = replace(
|
||||
safety_sell, watchdog_timeout_sec=inst.watchdog_timeout_sec
|
||||
)
|
||||
return replace(
|
||||
base,
|
||||
instance_key=inst.instance_key,
|
||||
@@ -347,4 +366,5 @@ class DaemonConfig:
|
||||
trading_timezone=inst.trading_timezone,
|
||||
max_active_slots=inst.max_active_slots,
|
||||
safety_sell_enabled=inst.safety_sell_enabled,
|
||||
safety_sell=safety_sell,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user