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:
2026-06-12 22:19:33 +09:00
co-authored by Claude Sonnet 4.6
parent d737467d70
commit 6786ffd05d
16 changed files with 634 additions and 44 deletions
@@ -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