Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ops/quant-monitor/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ VPS Codex 定时监控(`codex-quant.timer` 每 30 分钟)+ 收盘简报(`c

1. `sync_strategy_repos.sh` — `git pull` 四策略仓 + QPK
2. `health_cycle.py` — `build_dashboard` + `run_drift_detection`
3. `overall_score < 60` → Telegram 量化哨兵
3. lifecycle `overall_score < 60` → Telegram 量化哨兵(研究/回测健康,不代表平台执行故障)
4. drift ≥ 0.50(~2σ)→ `create_issues_for_domain` 开 Issue(不 @)
5. drift ≥ 0.75(~3σ)→ Telegram + Issue **@owner**

Expand Down
12 changes: 10 additions & 2 deletions ops/quant-monitor/scripts/health_cycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,19 @@ def _strategy_health_alert(row: dict[str, Any]) -> tuple[str, str] | None:
profile = str(row.get("strategy_profile") or "?")
domain = str(row.get("domain") or "?")
return (
f"[{domain}] {profile}: health_score={score:.1f}",
f"[{domain}] {profile}: lifecycle_health_score={score:.1f}",
f"strategy_health_below_{SCORE_ALERT:g}:{domain}:{profile}",
)


def _build_alert_body(lines: list[str]) -> str:
return (
"🚨 quant-monitor strategy_lifecycle\n"
"• scope: research/backtest lifecycle health; not platform runtime execution\n"
+ "\n".join(f"• {line}" for line in lines)
)


def _alert_state_path(root: Path) -> Path:
return root / _ALERT_STATE_RELATIVE_PATH

Expand Down Expand Up @@ -380,7 +388,7 @@ def main() -> int:
telegram_sent = False
duplicate_alert_suppressed = False
if notify_lines:
body = "🚨 quant-monitor health_cycle\n" + "\n".join(f"• {line}" for line in notify_lines)
body = _build_alert_body(notify_lines)
fingerprint = _alert_fingerprint(alert_identities)
duplicate_alert_suppressed = _is_duplicate_alert(root, fingerprint)
if not duplicate_alert_suppressed:
Expand Down
16 changes: 16 additions & 0 deletions ops/quant-monitor/tests/test_monitor_fail_closed.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,22 @@ def test_health_cycle_score_changes_keep_the_same_incident_fingerprint(self) ->
HEALTH_CYCLE._alert_fingerprint([second_identity]),
)

def test_health_cycle_labels_strategy_scores_as_lifecycle_not_runtime(self) -> None:
line, _identity = HEALTH_CYCLE._strategy_health_alert(
{
"domain": "us_equity",
"strategy_profile": "example",
"overall_score": 59.9,
}
)

body = HEALTH_CYCLE._build_alert_body([line])

self.assertIn("quant-monitor strategy_lifecycle", body)
self.assertIn("research/backtest lifecycle health", body)
self.assertIn("not platform runtime execution", body)
self.assertIn("lifecycle_health_score=59.9", body)

def test_health_cycle_non_object_alert_state_is_a_cache_miss(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
Expand Down