BAT-035 (dcy-025): trex.snapshot.req → trex.slots.req 즉시 교체
- topology: SNAPSHOT_REQ_QUEUE 제거, SLOTS_REQ_QUEUE = "trex.slots.req" 추가
- portfolio.py: View/build_snapshot_envelope/SnapshotEnvelope/_asset_to_dict 제거;
slot_to_dict 시그니처 확장(snapshot_store/watchdog_store/pause_store/watchdog_timeout_sec)
— asset_quantity(hub)/cash_balance/cash_estimated/warned/watchdog{}/paused 병합 (REQ-074)
- messaging_handlers.py: make_snapshot_rpc_handler 제거, make_slots_rpc_handler 추가
— filter(all/inactive/pending/holding/error) + spy(bool) 처리; strategy 필드 제외 (REQ-073/074)
- daemon.py: _snapshot_rpc_loop → _slots_rpc_loop, make_slots_rpc_handler 연결
- cli.py: universe/slots → slots_queue + spy=True + server-side filter; buyable→inactive 매핑 (REQ-063)
- rabbitmq.py: snapshot_queue → slots_queue
- docs: interface-contract.md + runtime-flows.md slots.req 계약으로 업데이트 (REQ-075)
- REQ-028: Implemented → Deprecated (코드 삭제 완료)
- 테스트: test_snapshot_handler/selection/rpc_watchdog/messaging_snapshot_consumer 삭제;
test_slots_handler.py(신규, 13 cases), test_killswitch_masking_resume 재작성,
test_ledger_option_d._apply_cash_estimate → slots handler 경유, test_topology/cli_operator 갱신
BAT-036 (dcy-024): per-instance safety_sell_enabled 플래그
- config.py: DaemonInstanceConfig + DaemonConfig 에 safety_sell_enabled: bool = True 추가;
from_toml 파싱, from_config 전파
- daemon.py: start() workers 조건부 — safety_sell_enabled=false 시 trex-safety-sell 미기동 + WARNING
- tests: test_instance_config.py 에 safety_sell_enabled 파싱 3개 케이스 추가
REQ: 073/074/075/021/034/063/048/005 Approved → Implemented
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
15 KiB
15 KiB
0. 문서 상태
- 상태: confirmed
- 문서 ID: dcy-024_20260611_safety-sell-disable-mode
- 이슈명: safety-sell 자율 청산 비활성화 모드 (issue #15)
- 작성 시각(KST): 2026-06-11
- 마지막 갱신 시각(KST): 2026-06-11 13:50 KST (change-req 진행: REQ-048 CHG-20260611-01(safety_sell_enabled TOML) + REQ-005 CHG-20260611-01(조건부 워커·기동 로그·runtime-flows), 둘 다 Implemented→Approved. 신규 REQ 없음(OQ-1).)
- 대체됨: 없음
- 후속 Discovery 참조: 없음
- 생성된 REQ 참조: 없음 (change-req only, dcy-024 OQ-1). Delivery: REQ-048(CHG-20260611-01)·REQ-005(CHG-20260611-01) change-req → 차기 batch.
1. 계획 상태 요약
- 연계 문서 현황:
- 관련 점수 파일: 없음
- 관련 TODO/메모: GitHub issue #15 (자율 청산 비활성화 모드 설계 전문 포함)
- 해석:
- REL-006 피드백 환류 Discovery 후보로 식별됨. 자율 청산(forced_close / SL·TP / market_close) 워커를 완전히 끄고 전략·운영자 지시 매도만 허용하는 운영 모드 요구.
- REQ-048(per-instance TOML) + REQ-005(daemon bootstrap) change-req 2건으로 닫는다 — OQ-1·OQ-2 확정. SafetySellConfig(env) 변경 없음.
2. 요구사항 판정 결과
- 판정: 신규
- 근거
- dcy-007(core-autonomous-safety-sell)은 자율 매도 기능을 구현한 Discovery로 REL-003에서 released됨. 비활성화 모드는 그 기능의 확장이 아닌 운영 정책 플래그 추가 — 독립 후속 주기.
- 현재 SafetySellConfig에
enabled플래그가 없고,daemon.start()의 workers 리스트에trex-safety-sell스레드가 조건 없이 포함됨. 코드 갭이 실존하는 신규 요구.
3. 문제점의 요약
- 현재 상태
trex_core/runtime/daemon.py:335:("trex-safety-sell", self._safety_sell_loop)이 workers 리스트에 항상 포함되어 데몬 기동 시 매번 자율 청산 워커가 시작됨.SafetySellConfig(config.py:113)에는enabled플래그가 없고, per-trigger 비활성화 수단도 없음. 관찰 전용 인스턴스·신규 전략 검증 환경에서 자율 청산을 막을 방법이 없음.
- 기대 상태
DaemonInstanceConfig(per-instance TOML)에safety_sell_enabled: bool = True추가(OQ-2 — env 제외,max_active_slots/default_order_timeout_sec선례)로,false시trex-safety-sell스레드를 데몬 기동부에서 아예 시작하지 않음.- 기동 로그에
safety_sell=disabled표시로 운영자가 안전망 없는 인스턴스임을 즉시 인지 가능(스냅샷 응답 미포함 — OQ-3).
- 이해관계자
- 운영자/시스템 관리자 (Justin Ko): 관찰 전용 인스턴스·신규 전략 검증 시 자율 청산 OFF 필요
- 전략 모듈(trex-orb-v1): 전략 지시 매도는 항상 유지되어야 함
- 리뷰어/승인자: 안전망 없음을 명시한 운영 가이드 확인
- 제약사항
ExecutionService(집행 루프), 운영자 CLI, kill-switch는 이 플래그에 영향받지 않아야 함 — 자율 청산(safety-sell 워커)만 OFF.- Redis·RabbitMQ·PostgreSQL 의존성 변경 없음. config.py + daemon.py 한정 수정.
주요 사용자 시나리오
- 관찰 전용 인스턴스: per-instance TOML
safety_sell_enabled = false설정 후 데몬 기동 → 기동 로그에safety_sell=disabled확인 → 포지션 유지 중 자동 청산 미발생, 전략 sell intent는 정상 집행. - 신규 전략 검증(paper 브로커): safety-sell 없이 전략 모듈 매도 타이밍만 테스트 — 의도치 않은 장마감 강제청산 없이 전략 e2e 검증.
- 기본 운영(enabled=true): 기존 동작과 완전 동일. 하위 호환 변경 없음.
4. 이번에 정의할 변경
DaemonInstanceConfig(per-instance TOML)에safety_sell_enabled: bool = True필드 추가(OQ-2 — env 제외, SafetySellConfig 변경 없음).daemon.start()workers 리스트 구성부에서safety_sell_enabled=false시trex-safety-sell스레드 제외 — run_once 내부 if-가드보다 스레드 미기동이 물리적으로 자율 청산을 차단.- 기동 로그(
RUNNING)에safety_sell_enabled상태 노출(스냅샷 응답 미포함 — OQ-3) — 운영자가 인지 없이 안전망 없는 인스턴스를 운영하는 사고 방지.
영향 범위
- 코드 영향:
trex_core/runtime/config.py:DaemonInstanceConfig에safety_sell_enabled: bool = True필드 추가 +from_toml()파싱 (SafetySellConfig 변경 없음)trex_core/runtime/daemon.py:start()workers 조건부 구성 —config.instance.safety_sell_enabled참조 + 기동 로그safety_sell=disabled/enabled
- 문서 영향:
- REQ-048(per-instance TOML 설정) change-req:
safety_sell_enabled필드 추가 - REQ-005(daemon bootstrap) change-req: 워커 조건부 기동 명시
docs/runtime-flows.md: safety-sell 비활성 모드 플로우 추가
- REQ-048(per-instance TOML 설정) change-req:
- 운영 영향:
- 기본값
true— 기존 운영 인스턴스 동작 무변. 기동 전.env또는 TOML에TREX_SAFETY_SELL_ENABLED=false추가 시에만 비활성 모드로 전환. - 비활성 모드 인스턴스는 손절/강제만기/장마감 안전망 없음 — 운영 가이드·기동 로그 명시 필수.
- 기본값
5. 요구사항 목록
기능 요구사항
- FR-1:
DaemonInstanceConfig(per-instance TOML)에safety_sell_enabled: bool = True필드를 추가하고, TOMLsafety_sell_enabled = false로 인스턴스별 비활성화를 제어한다. 기본값true(미지정 시 기존 동작 유지). env var 경로 미노출 — 인스턴스별 운영 모드 차이(max_active_slots선례). - FR-2:
daemon.start()시DaemonInstanceConfig.safety_sell_enabled == False이면trex-safety-sell워커 스레드를 workers 리스트(daemon.py:335)에 포함하지 않는다.True(기본)이면 기존과 동일하게 기동한다. - FR-3: 기동 시 RUNNING 로그에
safety_sell_enabled=<true|false>필드를 포함한다. 비활성 모드(false)이면 추가로no autonomous liquidation경고를 로그에 남긴다. - FR-4:
safety_sell_enabled모드는 기동 로그(FR-3)로만 노출하고 스냅샷/slots 응답에는 포함하지 않는다(OQ-3). 근거: 인스턴스 config 이지 per-slot 데이터가 아니며, dcy-025 가 read 표면을 "응답=상태(slots[])+health"로 최소화하므로 config 를 응답에 싣지 않는 패턴과 정합한다. 런타임 노출이 필요해지면 per-slot 이 아니라 dcy-025 envelope-level(또는 별도 instance-status 표면)에서 조율한다.
비기능 요구사항
- NFR-1:
enabled=false시ExecutionService(집행 루프)·운영자 CLI(enter/exit/cancel)·kill-switch·messaging handler에는 영향 없음. 자율 청산(safety-sell 워커)만 미기동. - NFR-2: 기본값
true— 기존 운영 인스턴스 재배포 시 config 변경 없이 기존 동작 보존(완전 하위 호환). - NFR-3: 비활성 모드는 테스트에서
config.safety_sell.enabled=False를 주입하는 것만으로 재현 가능. 추가 mocking 불필요.
범위 경계
- In Scope
- SafetySellConfig
enabled필드 + env + TOML 파싱 - daemon.start() workers 조건부 구성
- 기동 로그에 모드 노출(스냅샷 응답 미포함 — OQ-3)
- REQ-048(per-instance TOML) change-req + REQ-005(daemon bootstrap) change-req + runtime-flows 갱신
- SafetySellConfig
- Out of Scope
- per-trigger 개별 플래그(forced_close / SL·TP / market_close 각각 ON/OFF) — 마스터 플래그 이후 별도 후속 이슈로 분리
- kill-switch 포함 여부 변경 — 운영자 지시 경로이므로 유지(혼용 금지)
- 비활성 모드용 별도 watchdog 가시화 — 워커 미기동 시 로깅 없어도 무방(과설계 회피)
- trex-orb-v1 전략 모듈 변경 — 전략 sell intent는 messaging handler 경로이며 영향 없음
6. 리스크/가정 목록
리스크
- R-1 (Medium): 비활성 모드 인스턴스에서 전략 모듈이 죽어 폴링이 끊겨도 안전망 없이 포지션이 계속 유지됨 — 의도적 모드이지만 운영자 인지 없이 설정하면 무제한 손실 리스크.
- 완화 방안: 기동 로그
no autonomous liquidation경고로 운영자가 즉시 인지(스냅샷 응답 미포함 — OQ-3). 운영 가이드에 위험 명시.
- 완화 방안: 기동 로그
- R-2 (Low): TOML
safety_sell_enabled오타/잘못된 타입이 의도와 다르게 해석될 경우 기대와 반대로 자율 청산이 활성화됨.- 완화 방안: TOML bool 파싱 규약을 따르되(미지정/비-false →
true), 기동 로그에 파싱된 값을 항상 출력해 운영자가 확인 가능하게 함.
- 완화 방안: TOML bool 파싱 규약을 따르되(미지정/비-false →
- R-3 (Low): 기존 SafetySellConfig 단위 테스트가
enabled필드 추가 후 drift할 수 있음.- 완화 방안:
EXPECTED_SLOT_FIELDSdrift test 선례처럼 config 계약 가드 테스트 추가 또는 기존 테스트 보완.
- 완화 방안:
가정
- A-1: 단일 마스터 플래그(
enabled) 구현으로 시작하고, per-trigger 세분화는 후속 이슈로 분리한다. - A-2:
enabled=false시 kill-switch, 운영자 CLI exit, 전략 sell intent는 그대로 동작한다. - A-3: 신규 REQ 없이 REQ-048(per-instance TOML) + REQ-005(daemon bootstrap) change-req 2건으로 닫는다 — OQ-1·OQ-2 Justin Ko 확정. SafetySellConfig(env) 변경 없음.
오픈 질문
- OQ-1: 신규 REQ 없이 change-req 경로(REQ-041·005)만으로 진행할지, 별도 Configuration REQ를 신규 발행할지?
- 상태: Answered
- 처리 방안: Justin Ko 확정 — change-req 우선(신규 REQ 미발행). REQ-048(DaemonInstanceConfig TOML) + REQ-005(daemon bootstrap) change-req 2건으로 닫는다.
- 종료 조건: 완료
- OQ-2: per-instance TOML
[safety_sell] enabled = false지원 포함 여부(env만 지원할지 TOML도 포함할지)?- 상태: Answered
- 처리 방안: Justin Ko 확정 — TOML 포함(env 제외). 당일 매매 인스턴스는 enabled=true, 중장기 보유 인스턴스는 enabled=false — 인스턴스마다 다른 운영 모드이므로
DaemonInstanceConfig(TOML)에 배치.max_active_slots·default_order_timeout_sec선례 동일. REQ-048 change-req 추가. - 종료 조건: 완료
- OQ-3: 스냅샷 응답(
slots[]envelope)에safety_sell_enabled필드 추가 포함 여부(FR-4)?- 상태: Answered (2026-06-11, 사용자 결정)
- 결정: 기동 로그만, 스냅샷/slots 응답 미포함. 근거:
safety_sell_enabled는 인스턴스 config(per-slot 데이터 아님)이고, dcy-025 가 read 표면을 "응답=상태(slots[])+health"로 최소화하므로 config 를 응답에 싣지 않는 패턴과 정합. per-slot 행에 넣지 않는다. 런타임 노출이 필요해지면 per-slot 이 아니라 dcy-025 envelope-level(또는 별도 instance-status 표면)에서 조율(후속). 반영: FR-4.
7. 초기 성공 기준
- S-1: per-instance TOML
safety_sell_enabled = false로 데몬 기동 시trex-safety-sell스레드가 시작되지 않는다. 기동 로그에safety_sell_enabled=false와no autonomous liquidation경고가 포함된다. - S-2: 비활성 모드에서 forced_close_at 만기·SL·TP·장마감 시각이 지나도 슬롯이 자동 청산되지 않는다. 전략 모듈 sell intent + 운영자 CLI exit는 정상 집행된다.
- S-3:
enabled=true(기본값) 시 기존 동작과 동일하며 기존 단위·통합 테스트 회귀가 없다.
측정 방식 및 데이터 출처
- S-1 측정 방식: 단위 테스트 —
enabled=Falseconfig 주입 +daemon.start()후 thread 이름 목록에trex-safety-sell부재 확인 / 데이터 출처:tests/runtime/test_daemon.py또는 신규 테스트 - S-2 측정 방식: run-the-app 스모크 — 비활성 모드 데몬 기동 후 holding 슬롯 유지 중 장마감 시각 경과 → 슬롯 상태
holding유지 + 전략 sell intent 처리 확인 / 데이터 출처: 데몬 기동 로그(safety_sell_enabled=false) + 슬롯 상태(trex-core slots로 holding 유지 확인 — slots 출력 자체엔 safety_sell_enabled 미포함, OQ-3) - S-3 측정 방식:
pytest전체 실행 회귀 없음,ruff checkclean / 데이터 출처: CI 또는 로컬 테스트 결과
8. REQ로 넘기기 전 확인 체크
- 범위(In Scope/Out of Scope) 확정
- 기준: per-trigger 플래그 Out of Scope + TOML 지원(OQ-2 TOML·env 제외) 확정
- 성공 지표의 측정 방식/데이터 출처 확정
- 기준: S-1 단위(스레드 부재)·S-2 run-the-app 스모크·S-3 회귀로 정의됨
- High 영향 리스크 우선순위 및 해소 책임자 지정
- 기준: R-1(비활성 모드 손실)은 기동 로그 경고 + 운영 가이드로 해소, 가이드 작성 담당 = 구현 batch/Justin Ko(아래)
- 모든 오픈 질문(OQ)
Open상태 없음 확인- 기준: OQ-1/2/3 전부 Answered
- Freeze 확정 담당자(CONFIRMED_BY) 지정
- 기준: Justin Ko 승인 후
confirm-discovery수행
- 기준: Justin Ko 승인 후
- (delivery 인계) 비활성 모드 운영 가이드(손실 리스크 경고) 작성 — 구현 batch 단계 산출물, 담당 project maintainers/Justin Ko (Discovery confirm 비차단)
9. 파일 처리 결과
- 처리 결과: 생성
- 생성 경로: docs/discovery/dcy-024_20260611_safety-sell-disable-mode.md
- 참조 문서:
- GitHub issue #15 — 자율 청산 비활성화 모드 설계 전문 (영향 범위·구현 방안·OQ 상세)
- docs/discovery/dcy-007_20260606_core-autonomous-safety-sell.md — 자율 매도 구현 Discovery (선행 참조)
- trex_core/runtime/config.py:113 — SafetySellConfig (변경 대상)
- trex_core/runtime/daemon.py:335 — workers 리스트 (
trex-safety-sell항목, 변경 대상)
10. 사용자 결정 필요 항목 요약
DECIDE
- 없음 (OQ-1·OQ-2 확정 완료)
CONFIRM
- 없음. (1) 스냅샷 응답 노출 = OQ-3 Answered(기동 로그만). (2) kill-switch·운영자 CLI exit가
safety_sell_enabled=false와 무관하게 유지됨은 설계로 확정(A-2/NFR-1) — 저장소상KillSwitchService(daemon.py:166)·운영자 CLI exit 는trex-safety-sell워커와 독립 경로라 워커 미기동이 그 둘에 영향 없음(확인 완료).
DATA
- 없음. (운영 가이드(손실 리스크 경고) 작성은 Discovery 정책 결정이 아니라 delivery 단계 산출물이므로 §8 인계 체크로 이동 — 담당은 구현 batch/project maintainers.)
11. Discovery Freeze
섹션 참조
- 범위: # 5. 요구사항 목록 > 범위 경계
- 리스크/오픈 질문: # 6. 리스크/가정 목록
- 성공 기준: # 7. 초기 성공 기준
- 인계 전 확인: # 8. REQ로 넘기기 전 확인 체크
- 사용자 결정 필요: # 10. 사용자 결정 필요 항목 요약
Freeze 플래그
- Handoff Decision: 승인
- Handoff Rationale: OQ-1/2/3 전부 Answered(change-req 경로·TOML·기동 로그만). C형 플레이스홀더 0(CONFIRM/DATA 해소). 저장소 사실 재확인 일치. 운영 가이드 작성만 delivery 단계 비차단 인계. REQ Drafting 인계 가능.
- Ready for REQ Drafting: true
- Confirmed By: Justin Ko
- Confirmed At (KST): 2026-06-11 13:29 KST