From 78d02cf2ef1b1efd5526960e9444ec5060cf8bdd Mon Sep 17 00:00:00 2001 From: jackyu66git Date: Tue, 26 May 2026 12:52:36 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20BSP=E6=A3=80=E6=B5=8B=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E6=96=B0=E7=AC=94=E9=A9=B1=E5=8A=A8=EF=BC=8C=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E9=80=90tick=E5=AF=B9=E6=AF=94BSP=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 用 last_bi_id (start_klc.start_time) 跟踪最后一笔 - 新笔确认时检查上一笔终点是否为 BSP → 推送 - 中枢更新同样在新笔产生时触发 - 移除时间过滤、BSP列表diff、持久化去重等冗余逻辑 - 无新笔时快速跳过,tick从40s降到15s Co-Authored-By: Claude Opus 4.7 --- bsp_monitor/main.py | 77 +++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/bsp_monitor/main.py b/bsp_monitor/main.py index a3ae592..546ba34 100644 --- a/bsp_monitor/main.py +++ b/bsp_monitor/main.py @@ -5,8 +5,8 @@ main.py - 缠论买卖点监控主程序。 每整分钟: 1. 从 data_provider 拉取所有币对最新 1m K 线 2. 每个币对独立跑缠论管线 - 3. 检测新出现的买卖点(BSP)+ 中枢特征变化 - 4. 推送到 Telegram + 3. 检测新笔 → 检查上一笔终点是否 BSP → 推送 + 4. 检测中枢特征变化 → 推送 """ import asyncio import logging @@ -15,6 +15,7 @@ import os import time from dataclasses import dataclass, field from datetime import datetime, timezone, timedelta +from typing import Optional sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) @@ -39,12 +40,16 @@ def _short(symbol: str) -> str: return symbol.split(":")[0].replace("/", "") +def _bi_id(bi) -> tuple: + """笔的稳定标识,基于首K线时间戳。""" + return (bi.start_klc.start_time,) + + @dataclass class SymbolState: symbol: str pivot_monitor: ChanPivotMonitor = field(default_factory=ChanPivotMonitor) - known_bsp_keys: set = field(default_factory=set) - last_df_ts: object = None + last_bi_id: Optional[tuple] = None # 上次检查过的最后一笔 ID first_run: bool = True @@ -80,11 +85,6 @@ class BSPMonitor: logger.warning(f"[{name}] DataFrame 为空") return - latest_ts = df.iloc[-1]["timestamp"] - if st.last_df_ts and latest_ts <= st.last_df_ts: - return # 无新K线 - st.last_df_ts = latest_ts - # 2. 运行缠论管线 try: engine = ChanEngine(df) @@ -92,23 +92,46 @@ class BSPMonitor: logger.error(f"[{name}] 缠论计算失败: {e}", exc_info=True) return - # 3. 中枢特征监控 + 推送 - pivot_state = st.pivot_monitor.update(engine.bi_zs_list) + # 3. 获取已确认的笔 + confirmed = [b for b in engine.bi_list if b.is_sure] + if len(confirmed) < 2: + return - # 4. BSP 检测 + 推送 - current_bsps = engine.bsp_list - current_keys = {_bsp_stable_key(b, symbol) for b in current_bsps} + last_bi = confirmed[-1] + current_bi_id = _bi_id(last_bi) + # 4. 首轮:记录状态,不推送 if st.first_run: st.first_run = False - st.known_bsp_keys = current_keys - confirmed = [b for b in engine.bi_list if b.is_sure] + st.last_bi_id = current_bi_id + st.pivot_monitor.update(engine.bi_zs_list) # 初始化中枢状态 logger.info( f"[{name}] 首次完成 — {len(confirmed)} 笔, " - f"{len(current_bsps)} BSP (不推送)" + f"{len(engine.bsp_list)} BSP (不推送)" ) return + # 5. 检测新笔 + if current_bi_id == st.last_bi_id: + return # 无新笔,跳过 + + st.last_bi_id = current_bi_id + logger.info(f"[{name}] 新笔确认 — #{len(confirmed)} " + f"{'⬆️' if last_bi.dir.value == 1 else '⬇️'} " + f"高度: ${last_bi.height:.2f}") + + # 6. 检查上一笔终点是否为 BSP + prev_bi = confirmed[-2] + bsp = engine.get_bsp_for_bi(prev_bi) + if bsp: + key = f"{symbol}_{bsp.type}_{bsp.klc.end_time}" + msg = engine.format_bsp_detail(bsp, symbol) + msg = _escape_html(msg) + if send_bsp_alert(msg, bsp_key=key): + logger.info(f"[{name}] ✅ BSP: {key}") + + # 7. 中枢特征更新 + pivot_state = st.pivot_monitor.update(engine.bi_zs_list) if pivot_state: logger.info( f"[{name}] 中枢更新: bi_count={pivot_state['bi_count']} " @@ -117,22 +140,6 @@ class BSPMonitor: ) self._push_pivot(pivot_state, name) - new_keys = current_keys - st.known_bsp_keys - st.known_bsp_keys |= current_keys - - if not new_keys: - return - - logger.info(f"[{name}] {len(new_keys)} 个新 BSP") - for bsp in current_bsps: - key = _bsp_stable_key(bsp, symbol) - if key not in new_keys: - continue - msg = engine.format_bsp_detail(bsp, symbol) - msg = _escape_html(msg) - if send_bsp_alert(msg, bsp_key=key): - logger.info(f"[{name}] ✅ BSP: {key}") - def _push_pivot(self, state: dict, name: str): if state["is_sure"]: phase = "✅ 已确认" @@ -197,10 +204,6 @@ class BSPMonitor: await asyncio.sleep(5) -def _bsp_stable_key(bsp, symbol: str) -> str: - return f"{symbol}_{bsp.type}_{bsp.klc.end_time}" - - def _escape_html(msg: str) -> str: """HTML 转义,保留已有的 / 标签。""" msg = msg.replace("&", "&")