From 55109e82d3852a54b0f41eaba78774f1a348702f Mon Sep 17 00:00:00 2001 From: dekun Date: Wed, 29 Jul 2026 18:15:09 +0800 Subject: [PATCH] Hide SIM label in WeCom; count completed rounds from closed groups. Co-authored-by: Cursor --- backend/app/notify/wecom.py | 18 +++++++++++++----- backend/app/strategy/engine.py | 13 +++++++++---- backend/tests/test_rest_after_close.py | 9 ++++++++- docs/更新说明.md | 9 +++++++++ 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/backend/app/notify/wecom.py b/backend/app/notify/wecom.py index e9f0fc9..daf6c8c 100644 --- a/backend/app/notify/wecom.py +++ b/backend/app/notify/wecom.py @@ -55,11 +55,11 @@ def wecom_webhook_url() -> str: return "" -def venue_label() -> str: - """模拟盘 / 实盘·OKX / 实盘·BINANCE。""" +def venue_label() -> str | None: + """实盘时返回「实盘·交易所」;SIM 不展示盘口标签。""" s = get_settings() if s.is_sim: - return "模拟盘" + return None try: ex = load_runtime_settings().exchange except Exception: @@ -67,13 +67,17 @@ def venue_label() -> str: ex_u = str(ex).strip().lower() if ex_u in ("binance", "bn"): return "实盘·币安" + if ex_u in ("gate", "gateio"): + return "实盘·Gate" return "实盘·OKX" def build_markdown(*, tag: str, title: str, lines: list[str] | None = None) -> str: body = "\n".join(f"> {ln}" if not ln.startswith(">") else ln for ln in (lines or [])) + venue = venue_label() + head = f"## 【{venue}】{title}" if venue else f"## {title}" parts = [ - f"## 【{venue_label()}】{title}", + head, f"> **标识**: `{tag}`", f"> **时间**: {time.strftime('%Y-%m-%d %H:%M:%S')}", ] @@ -211,9 +215,13 @@ def notify_fault(*, title: str, detail: str, dedupe_key: str | None = None) -> N def notify_test() -> tuple[bool, str]: + venue = venue_label() + lines = ["企业微信通知已连通。"] + if venue: + lines.append(f"当前盘口标签: **{venue}**") content = build_markdown( tag=TAG_TEST, title="测试推送", - lines=["企业微信通知已连通。", f"当前盘口标签: **{venue_label()}**"], + lines=lines, ) return _post_markdown_sync(content) diff --git a/backend/app/strategy/engine.py b/backend/app/strategy/engine.py index f0010ee..556da97 100644 --- a/backend/app/strategy/engine.py +++ b/backend/app/strategy/engine.py @@ -119,7 +119,7 @@ class StrategyEngine: return { "running": bool(row["running"]), "phase": row["phase"], - "rounds_done": int(row["rounds_done"] or 0), + "rounds_done": self._closed_rounds(), "window_key": row["window_key"], "rest_until_ms": rest_until, "rest_left_sec": rest_left, @@ -266,11 +266,16 @@ class StrategyEngine: """全平成功后进入组间休息(自动 / 手动 / 紧急共用)。""" self._after_close() + def _closed_rounds(self) -> int: + """已完成组数 = 已平仓组数量(与顶栏总交易口径一致,避免计数漂移)。""" + row = self.db.fetchone( + "SELECT COUNT(*) AS n FROM groups WHERE status='closed'" + ) + return int(row["n"] or 0) if row else 0 + def _after_close(self) -> None: s = get_settings() - row = self.db.fetchone("SELECT * FROM strategy_state WHERE id=1") - assert row is not None - rounds = int(row["rounds_done"] or 0) + 1 + rounds = self._closed_rounds() rest_sec = self.ledger.get_setting_int("rest_seconds", s.rest_seconds) rest_until = int(time.time() * 1000) + rest_sec * 1000 self._set_state( diff --git a/backend/tests/test_rest_after_close.py b/backend/tests/test_rest_after_close.py index 856b04a..af923f1 100644 --- a/backend/tests/test_rest_after_close.py +++ b/backend/tests/test_rest_after_close.py @@ -10,13 +10,20 @@ def test_enter_rest_after_close_sets_resting(tmp_path) -> None: db = Database(tmp_path / "rest.db") set_db(db) db.set_setting("rest_seconds", "120") + now = int(time.time() * 1000) + db.execute( + """INSERT INTO groups( + group_id, status, open_at_ms, close_at_ms, realized_pnl + ) VALUES (?,?,?,?,?)""", + ("G-20260729-01", "closed", now - 1000, now, 1.0), + ) eng = StrategyEngine() before = int(time.time() * 1000) eng.enter_rest_after_close() row = db.fetchone("SELECT * FROM strategy_state WHERE id=1") assert row is not None assert row["phase"] == "resting" - assert int(row["rounds_done"] or 0) >= 1 + assert int(row["rounds_done"] or 0) == 1 until = int(row["rest_until_ms"] or 0) assert until >= before + 100_000 db.close() diff --git a/docs/更新说明.md b/docs/更新说明.md index 82a9886..c86801b 100644 --- a/docs/更新说明.md +++ b/docs/更新说明.md @@ -5,6 +5,15 @@ --- +## 2026-07-29 — 企微不标模拟盘;已完成组数按已平仓统计 + +### 变更 + +1. 企业微信:SIM 不再显示「模拟盘」;LIVE 显示「实盘·OKX/币安」。 +2. 「已完成 N 组」改为按 `groups.status=closed` 计数,与实际平仓组一致(修正漂移显示 6 实为 5)。 + +--- + ## 2026-07-29 — 规则说明展示策略数值;导航加大;不可开才通知 ### 变更