diff --git a/crypto_monitor_binance/app.py b/crypto_monitor_binance/app.py index b588b1c..6b9015a 100644 --- a/crypto_monitor_binance/app.py +++ b/crypto_monitor_binance/app.py @@ -168,6 +168,8 @@ from key_monitor_lib import ( KEY_MONITOR_RS_TYPES, auto_amp_ok, auto_confirm_ok, + box_breakout_invalidate_by_mark, + box_breakout_invalidate_edge_label, claim_rs_level_notify, detect_rs_box_break, format_auto_amp_line, @@ -6036,6 +6038,21 @@ def check_key_monitors(): direction = (r["direction"] or "long").lower() if direction == KEY_DIRECTION_WATCH: continue + if typ in KEY_MONITOR_AUTO_TYPES: + mark = get_symbol_mark_price(sym) + if mark is not None and box_breakout_invalidate_by_mark(direction, mark, up, low): + edge = float(low) if direction == "long" else float(up) + edge_label = box_breakout_invalidate_edge_label(direction) + msg = ( + f"# ⚠️ {sym} 关键位监控失效\n" + f"**账户:{_wechat_account_label()}**\n" + f"- 类型:{typ}|{_wechat_direction_text(direction)}\n" + f"- 标记价 {format_price_for_symbol(sym, mark)} 已突破反向{edge_label} " + f"{format_price_for_symbol(sym, edge)}(设置失效)\n" + ) + send_wechat_msg(msg) + _finalize_key_monitor_one_shot(conn, r, msg, "box_opposite_break") + continue try: checks = _key_hard_checks(sym, direction, up, low, typ) except Exception: @@ -7197,6 +7214,7 @@ def api_price_snapshot(): fib_gate_ok = True fb_gate_ok = True te_gate_ok = True + box_gate_ok = True if is_fib: direction = (r["direction"] or "long").lower() inval = fib_invalidate_by_mark(direction, price, r["upper"], r["lower"]) @@ -7253,16 +7271,22 @@ def api_price_snapshot(): except Exception: gate_summary = "-" elif (r["monitor_type"] or "").strip() in KEY_MONITOR_AUTO_TYPES: - try: - gate = _key_hard_checks( - r["symbol"], - (r["direction"] or "long").lower(), - r["upper"], - r["lower"], - r["monitor_type"], - ) - except Exception: - gate = None + direction = (r["direction"] or "long").lower() + if box_breakout_invalidate_by_mark(direction, price, r["upper"], r["lower"]): + edge_label = box_breakout_invalidate_edge_label(direction) + gate_summary = f"反向突破{edge_label}·将撤销" + box_gate_ok = False + else: + try: + gate = _key_hard_checks( + r["symbol"], + direction, + r["upper"], + r["lower"], + r["monitor_type"], + ) + except Exception: + gate = None if gate: rank_seg = "ERR" if int(gate.get("rank_total") or 0) <= 0 else f"{gate.get('rank')}/{gate.get('rank_total')}" gate_summary = ( @@ -7301,7 +7325,7 @@ def api_price_snapshot(): fib_gate_ok if is_fib else fb_gate_ok if is_fb else te_gate_ok if is_te - else bool(gate and gate.get("ok")) + else box_gate_ok and bool(gate and gate.get("ok")) ), "gate_metrics": gate_metrics, }) diff --git a/crypto_monitor_binance/关键位自动下单说明.md b/crypto_monitor_binance/关键位自动下单说明.md index 07743c1..5bddb8c 100644 --- a/crypto_monitor_binance/关键位自动下单说明.md +++ b/crypto_monitor_binance/关键位自动下单说明.md @@ -112,6 +112,7 @@ Binance / OKX 见各自目录下同名文档;共享逻辑在仓库根目录 `k | `close_reason` | 含义 | |----------------|------| +| `box_opposite_break` | 标记价先突破反向边界(多:≤下沿;空:≥上沿) | | `rr_insufficient` | 门控通过但 RR 不达标或 SL/TP 几何无效 | | `exchange_failed` | RR 达标但实盘/交易所等原因未开仓 | | `auto_opened` | RR 达标且市价开仓成功 | diff --git a/crypto_monitor_gate/app.py b/crypto_monitor_gate/app.py index 5c384b2..2bf772e 100644 --- a/crypto_monitor_gate/app.py +++ b/crypto_monitor_gate/app.py @@ -167,6 +167,8 @@ from key_monitor_lib import ( KEY_MONITOR_RS_TYPES, auto_amp_ok, auto_confirm_ok, + box_breakout_invalidate_by_mark, + box_breakout_invalidate_edge_label, claim_rs_level_notify, detect_rs_box_break, format_auto_amp_line, @@ -5780,6 +5782,21 @@ def check_key_monitors(): direction = (r["direction"] or "long").lower() if direction == KEY_DIRECTION_WATCH: continue + if typ in KEY_MONITOR_AUTO_TYPES: + mark = get_symbol_mark_price(sym) + if mark is not None and box_breakout_invalidate_by_mark(direction, mark, up, low): + edge = float(low) if direction == "long" else float(up) + edge_label = box_breakout_invalidate_edge_label(direction) + msg = ( + f"# ⚠️ {sym} 关键位监控失效\n" + f"**账户:{_wechat_account_label()}**\n" + f"- 类型:{typ}|{_wechat_direction_text(direction)}\n" + f"- 标记价 {format_price_for_symbol(sym, mark)} 已突破反向{edge_label} " + f"{format_price_for_symbol(sym, edge)}(设置失效)\n" + ) + send_wechat_msg(msg) + _finalize_key_monitor_one_shot(conn, r, msg, "box_opposite_break") + continue try: checks = _key_hard_checks(sym, direction, up, low, typ) except Exception: @@ -7106,6 +7123,7 @@ def api_price_snapshot(): fib_gate_ok = True fb_gate_ok = True te_gate_ok = True + box_gate_ok = True if is_fib: direction = (r["direction"] or "long").lower() inval = fib_invalidate_by_mark(direction, price, r["upper"], r["lower"]) @@ -7162,16 +7180,22 @@ def api_price_snapshot(): except Exception: gate_summary = "-" elif (r["monitor_type"] or "").strip() in KEY_MONITOR_AUTO_TYPES: - try: - gate = _key_hard_checks( - r["symbol"], - (r["direction"] or "long").lower(), - r["upper"], - r["lower"], - r["monitor_type"], - ) - except Exception: - gate = None + direction = (r["direction"] or "long").lower() + if box_breakout_invalidate_by_mark(direction, price, r["upper"], r["lower"]): + edge_label = box_breakout_invalidate_edge_label(direction) + gate_summary = f"反向突破{edge_label}·将撤销" + box_gate_ok = False + else: + try: + gate = _key_hard_checks( + r["symbol"], + direction, + r["upper"], + r["lower"], + r["monitor_type"], + ) + except Exception: + gate = None if gate: rank_seg = "ERR" if int(gate.get("rank_total") or 0) <= 0 else f"{gate.get('rank')}/{gate.get('rank_total')}" gate_summary = ( @@ -7214,7 +7238,7 @@ def api_price_snapshot(): fib_gate_ok if is_fib else fb_gate_ok if is_fb else te_gate_ok if is_te - else bool(gate and gate.get("ok")) + else box_gate_ok and bool(gate and gate.get("ok")) ), "gate_metrics": gate_metrics, }) diff --git a/crypto_monitor_gate/关键位自动下单说明.md b/crypto_monitor_gate/关键位自动下单说明.md index 07743c1..5bddb8c 100644 --- a/crypto_monitor_gate/关键位自动下单说明.md +++ b/crypto_monitor_gate/关键位自动下单说明.md @@ -112,6 +112,7 @@ Binance / OKX 见各自目录下同名文档;共享逻辑在仓库根目录 `k | `close_reason` | 含义 | |----------------|------| +| `box_opposite_break` | 标记价先突破反向边界(多:≤下沿;空:≥上沿) | | `rr_insufficient` | 门控通过但 RR 不达标或 SL/TP 几何无效 | | `exchange_failed` | RR 达标但实盘/交易所等原因未开仓 | | `auto_opened` | RR 达标且市价开仓成功 | diff --git a/crypto_monitor_gate_bot/app.py b/crypto_monitor_gate_bot/app.py index 9da1018..daf3039 100644 --- a/crypto_monitor_gate_bot/app.py +++ b/crypto_monitor_gate_bot/app.py @@ -167,6 +167,8 @@ from key_monitor_lib import ( KEY_MONITOR_RS_TYPES, auto_amp_ok, auto_confirm_ok, + box_breakout_invalidate_by_mark, + box_breakout_invalidate_edge_label, claim_rs_level_notify, detect_rs_box_break, format_auto_amp_line, @@ -5780,6 +5782,21 @@ def check_key_monitors(): direction = (r["direction"] or "long").lower() if direction == KEY_DIRECTION_WATCH: continue + if typ in KEY_MONITOR_AUTO_TYPES: + mark = get_symbol_mark_price(sym) + if mark is not None and box_breakout_invalidate_by_mark(direction, mark, up, low): + edge = float(low) if direction == "long" else float(up) + edge_label = box_breakout_invalidate_edge_label(direction) + msg = ( + f"# ⚠️ {sym} 关键位监控失效\n" + f"**账户:{_wechat_account_label()}**\n" + f"- 类型:{typ}|{_wechat_direction_text(direction)}\n" + f"- 标记价 {format_price_for_symbol(sym, mark)} 已突破反向{edge_label} " + f"{format_price_for_symbol(sym, edge)}(设置失效)\n" + ) + send_wechat_msg(msg) + _finalize_key_monitor_one_shot(conn, r, msg, "box_opposite_break") + continue try: checks = _key_hard_checks(sym, direction, up, low, typ) except Exception: @@ -7102,6 +7119,7 @@ def api_price_snapshot(): fib_gate_ok = True fb_gate_ok = True te_gate_ok = True + box_gate_ok = True if is_fib: direction = (r["direction"] or "long").lower() inval = fib_invalidate_by_mark(direction, price, r["upper"], r["lower"]) @@ -7158,16 +7176,22 @@ def api_price_snapshot(): except Exception: gate_summary = "-" elif (r["monitor_type"] or "").strip() in KEY_MONITOR_AUTO_TYPES: - try: - gate = _key_hard_checks( - r["symbol"], - (r["direction"] or "long").lower(), - r["upper"], - r["lower"], - r["monitor_type"], - ) - except Exception: - gate = None + direction = (r["direction"] or "long").lower() + if box_breakout_invalidate_by_mark(direction, price, r["upper"], r["lower"]): + edge_label = box_breakout_invalidate_edge_label(direction) + gate_summary = f"反向突破{edge_label}·将撤销" + box_gate_ok = False + else: + try: + gate = _key_hard_checks( + r["symbol"], + direction, + r["upper"], + r["lower"], + r["monitor_type"], + ) + except Exception: + gate = None if gate: rank_seg = "ERR" if int(gate.get("rank_total") or 0) <= 0 else f"{gate.get('rank')}/{gate.get('rank_total')}" gate_summary = ( @@ -7210,7 +7234,7 @@ def api_price_snapshot(): fib_gate_ok if is_fib else fb_gate_ok if is_fb else te_gate_ok if is_te - else bool(gate and gate.get("ok")) + else box_gate_ok and bool(gate and gate.get("ok")) ), "gate_metrics": gate_metrics, }) diff --git a/crypto_monitor_gate_bot/关键位自动下单说明.md b/crypto_monitor_gate_bot/关键位自动下单说明.md index 50dfb3d..af6a433 100644 --- a/crypto_monitor_gate_bot/关键位自动下单说明.md +++ b/crypto_monitor_gate_bot/关键位自动下单说明.md @@ -110,6 +110,7 @@ Binance / OKX 见各自目录下同名文档;共享逻辑在仓库根目录 `k | `close_reason` | 含义 | |----------------|------| +| `box_opposite_break` | 标记价先突破反向边界(多:≤下沿;空:≥上沿) | | `rr_insufficient` | 门控通过但 RR 不达标或 SL/TP 几何无效 | | `exchange_failed` | RR 达标但实盘/交易所等原因未开仓 | | `auto_opened` | RR 达标且市价开仓成功 | diff --git a/crypto_monitor_okx/app.py b/crypto_monitor_okx/app.py index 83b3f14..07c3cbb 100644 --- a/crypto_monitor_okx/app.py +++ b/crypto_monitor_okx/app.py @@ -166,6 +166,8 @@ from key_monitor_lib import ( KEY_MONITOR_RS_TYPES, auto_amp_ok, auto_confirm_ok, + box_breakout_invalidate_by_mark, + box_breakout_invalidate_edge_label, claim_rs_level_notify, detect_rs_box_break, format_auto_amp_line, @@ -5539,6 +5541,21 @@ def check_key_monitors(): direction = (r["direction"] or "long").lower() if direction == KEY_DIRECTION_WATCH: continue + if typ in KEY_MONITOR_AUTO_TYPES: + mark = get_symbol_mark_price(sym) + if mark is not None and box_breakout_invalidate_by_mark(direction, mark, up, low): + edge = float(low) if direction == "long" else float(up) + edge_label = box_breakout_invalidate_edge_label(direction) + msg = ( + f"# ⚠️ {sym} 关键位监控失效\n" + f"**账户:{_wechat_account_label()}**\n" + f"- 类型:{typ}|{_wechat_direction_text(direction)}\n" + f"- 标记价 {format_price_for_symbol(sym, mark)} 已突破反向{edge_label} " + f"{format_price_for_symbol(sym, edge)}(设置失效)\n" + ) + send_wechat_msg(msg) + _finalize_key_monitor_one_shot(conn, r, msg, "box_opposite_break") + continue try: checks = _key_hard_checks(sym, direction, up, low, typ) except Exception: @@ -6651,6 +6668,7 @@ def api_price_snapshot(): fib_gate_ok = True fb_gate_ok = True te_gate_ok = True + box_gate_ok = True if is_fib: direction = (r["direction"] or "long").lower() inval = fib_invalidate_by_mark(direction, price, r["upper"], r["lower"]) @@ -6707,16 +6725,22 @@ def api_price_snapshot(): except Exception: gate_summary = "-" elif (r["monitor_type"] or "").strip() in KEY_MONITOR_AUTO_TYPES: - try: - gate = _key_hard_checks( - r["symbol"], - (r["direction"] or "long").lower(), - r["upper"], - r["lower"], - r["monitor_type"], - ) - except Exception: - gate = None + direction = (r["direction"] or "long").lower() + if box_breakout_invalidate_by_mark(direction, price, r["upper"], r["lower"]): + edge_label = box_breakout_invalidate_edge_label(direction) + gate_summary = f"反向突破{edge_label}·将撤销" + box_gate_ok = False + else: + try: + gate = _key_hard_checks( + r["symbol"], + direction, + r["upper"], + r["lower"], + r["monitor_type"], + ) + except Exception: + gate = None if gate: rank_seg = "ERR" if int(gate.get("rank_total") or 0) <= 0 else f"{gate.get('rank')}/{gate.get('rank_total')}" gate_summary = ( @@ -6759,7 +6783,7 @@ def api_price_snapshot(): fib_gate_ok if is_fib else fb_gate_ok if is_fb else te_gate_ok if is_te - else bool(gate and gate.get("ok")) + else box_gate_ok and bool(gate and gate.get("ok")) ), "gate_metrics": gate_metrics, }) diff --git a/crypto_monitor_okx/关键位自动下单说明.md b/crypto_monitor_okx/关键位自动下单说明.md index 07743c1..5bddb8c 100644 --- a/crypto_monitor_okx/关键位自动下单说明.md +++ b/crypto_monitor_okx/关键位自动下单说明.md @@ -112,6 +112,7 @@ Binance / OKX 见各自目录下同名文档;共享逻辑在仓库根目录 `k | `close_reason` | 含义 | |----------------|------| +| `box_opposite_break` | 标记价先突破反向边界(多:≤下沿;空:≥上沿) | | `rr_insufficient` | 门控通过但 RR 不达标或 SL/TP 几何无效 | | `exchange_failed` | RR 达标但实盘/交易所等原因未开仓 | | `auto_opened` | RR 达标且市价开仓成功 | diff --git a/key_monitor_lib.py b/key_monitor_lib.py index b56ccee..37324e5 100644 --- a/key_monitor_lib.py +++ b/key_monitor_lib.py @@ -66,6 +66,30 @@ def auto_confirm_ok(direction: str, cfm_close: float, upper: float, lower: float return c < float(lower) +BOX_BREAKOUT_CLOSE_OPPOSITE = "box_opposite_break" + + +def box_breakout_invalidate_by_mark( + direction: str, mark_price: float, upper: float, lower: float +) -> bool: + """箱体/收敛:标记价先突破反向边界则失效。多:mark<=L;空:mark>=H。""" + try: + m = float(mark_price) + h = float(upper) + lo = float(lower) + except (TypeError, ValueError): + return False + direction = (direction or "long").strip().lower() + if direction == "short": + return m >= h + return m <= lo + + +def box_breakout_invalidate_edge_label(direction: str) -> str: + direction = (direction or "long").strip().lower() + return "下沿" if direction == "long" else "上沿" + + def detect_rs_box_break(close: float, upper: float, lower: float) -> Optional[dict[str, Any]]: """ 阻力/支撑人工盯盘:最近 5m 收盘突破上沿或下沿(严格 > / <)。 diff --git a/strategy_templates/key_monitor_panel.html b/strategy_templates/key_monitor_panel.html index 8c55008..ee1d6a5 100644 --- a/strategy_templates/key_monitor_panel.html +++ b/strategy_templates/key_monitor_panel.html @@ -114,6 +114,7 @@ {%- elif r == 'auto_opened' -%}自动开仓 {%- elif r == 'manual' -%}手动删除 {%- elif r == 'fib_invalidate' -%}斐波失效 +{%- elif r == 'box_opposite_break' -%}反向突破失效 {%- elif r == 'trigger_tp_invalidate' -%}触价止盈失效 {%- elif r == 'trigger_sl_invalidate' -%}触价止损失效 {%- elif r == 'trigger_entry_expired' -%}触价过期 diff --git a/strategy_templates/key_monitor_rule_tips.html b/strategy_templates/key_monitor_rule_tips.html index 2b7a767..70c04dc 100644 --- a/strategy_templates/key_monitor_rule_tips.html +++ b/strategy_templates/key_monitor_rule_tips.html @@ -14,7 +14,7 @@