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,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
|
||||
Reference in New Issue
Block a user