fix: show limit-order gate for false breakout monitors

False breakout used box/convergence volume/break gates in the UI; now shows pending limit order status like fib monitors.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-11 08:22:41 +08:00
parent 8c5b9681a9
commit fa59fc1273
6 changed files with 129 additions and 12 deletions
+22 -3
View File
@@ -57,6 +57,7 @@ from false_breakout_key_monitor_lib import (
FALSE_BREAKOUT_VALIDITY_HOURS,
calc_false_breakout_plan,
expires_at_text,
false_breakout_gate_preview,
is_false_breakout_expired,
is_false_breakout_key_monitor_type,
is_limit_key_monitor_type,
@@ -6329,7 +6330,8 @@ def api_price_snapshot():
key_prices = []
for r in key_rows:
is_fib = is_fib_key_monitor_type(r["monitor_type"])
if is_fib:
is_fb = is_false_breakout_key_monitor_type(r["monitor_type"])
if is_fib or is_fb:
price = get_symbol_mark_price(r["symbol"])
else:
price = prices.get(r["symbol"])
@@ -6341,6 +6343,7 @@ def api_price_snapshot():
gate_summary = "-"
gate_metrics = ""
fib_gate_ok = True
fb_gate_ok = True
if is_fib:
direction = (r["direction"] or "long").lower()
inval = fib_invalidate_by_mark(direction, price, r["upper"], r["lower"])
@@ -6350,6 +6353,18 @@ def api_price_snapshot():
gate_summary = f"斐波 挂E={entry_txt} {'标记价将失效' if inval else '等待成交'}"
if _sqlite_row_val(r, "fib_limit_order_id"):
gate_metrics = f"限价单:{_sqlite_row_val(r, 'fib_limit_order_id')}"
elif is_fb:
entry = _sqlite_row_val(r, "fib_entry_price")
entry_txt = format_price_for_symbol(r["symbol"], entry) if entry else "-"
prev = false_breakout_gate_preview(
entry_display=entry_txt,
limit_order_id=_sqlite_row_val(r, "fib_limit_order_id"),
created_at=r["created_at"],
now=app_now(),
)
gate_summary = prev.get("summary") or "-"
gate_metrics = prev.get("metrics") or ""
fb_gate_ok = bool(prev.get("gate_ok"))
elif (r["monitor_type"] or "").strip() in KEY_MONITOR_RS_TYPES:
try:
prev = _key_rs_gate_preview(r["symbol"], r["upper"], r["lower"])
@@ -6357,7 +6372,7 @@ def api_price_snapshot():
gate_metrics = prev.get("metrics") or ""
except Exception:
gate_summary = "-"
else:
elif (r["monitor_type"] or "").strip() in KEY_MONITOR_AUTO_TYPES:
try:
gate = _key_hard_checks(
r["symbol"],
@@ -6402,7 +6417,11 @@ def api_price_snapshot():
"lower_diff": lower_diff,
"lower_pct": lower_pct,
"gate_summary": gate_summary,
"gate_ok": fib_gate_ok if is_fib else bool(gate and gate.get("ok")),
"gate_ok": (
fib_gate_ok if is_fib
else fb_gate_ok if is_fb
else bool(gate and gate.get("ok"))
),
"gate_metrics": gate_metrics,
})
+22 -3
View File
@@ -58,6 +58,7 @@ from false_breakout_key_monitor_lib import (
FALSE_BREAKOUT_VALIDITY_HOURS,
calc_false_breakout_plan,
expires_at_text,
false_breakout_gate_preview,
is_false_breakout_expired,
is_false_breakout_key_monitor_type,
is_limit_key_monitor_type,
@@ -6448,7 +6449,8 @@ def api_price_snapshot():
key_prices = []
for r in key_rows:
is_fib = is_fib_key_monitor_type(r["monitor_type"])
if is_fib:
is_fb = is_false_breakout_key_monitor_type(r["monitor_type"])
if is_fib or is_fb:
price = get_symbol_mark_price(r["symbol"])
else:
price = prices.get(r["symbol"])
@@ -6460,6 +6462,7 @@ def api_price_snapshot():
gate_summary = "-"
gate_metrics = ""
fib_gate_ok = True
fb_gate_ok = True
if is_fib:
direction = (r["direction"] or "long").lower()
inval = fib_invalidate_by_mark(direction, price, r["upper"], r["lower"])
@@ -6469,6 +6472,18 @@ def api_price_snapshot():
gate_summary = f"斐波 挂E={entry_txt} {'标记价将失效' if inval else '等待成交'}"
if _sqlite_row_val(r, "fib_limit_order_id"):
gate_metrics = f"限价单:{_sqlite_row_val(r, 'fib_limit_order_id')}"
elif is_fb:
entry = _sqlite_row_val(r, "fib_entry_price")
entry_txt = format_price_for_symbol(r["symbol"], entry) if entry else "-"
prev = false_breakout_gate_preview(
entry_display=entry_txt,
limit_order_id=_sqlite_row_val(r, "fib_limit_order_id"),
created_at=r["created_at"],
now=app_now(),
)
gate_summary = prev.get("summary") or "-"
gate_metrics = prev.get("metrics") or ""
fb_gate_ok = bool(prev.get("gate_ok"))
elif (r["monitor_type"] or "").strip() in KEY_MONITOR_RS_TYPES:
try:
prev = _key_rs_gate_preview(r["symbol"], r["upper"], r["lower"])
@@ -6476,7 +6491,7 @@ def api_price_snapshot():
gate_metrics = prev.get("metrics") or ""
except Exception:
gate_summary = "-"
else:
elif (r["monitor_type"] or "").strip() in KEY_MONITOR_AUTO_TYPES:
try:
gate = _key_hard_checks(
r["symbol"],
@@ -6525,7 +6540,11 @@ def api_price_snapshot():
"lower_diff": lower_diff,
"lower_pct": lower_pct,
"gate_summary": gate_summary,
"gate_ok": fib_gate_ok if is_fib else bool(gate and gate.get("ok")),
"gate_ok": (
fib_gate_ok if is_fib
else fb_gate_ok if is_fb
else bool(gate and gate.get("ok"))
),
"gate_metrics": gate_metrics,
})
+22 -3
View File
@@ -58,6 +58,7 @@ from false_breakout_key_monitor_lib import (
FALSE_BREAKOUT_VALIDITY_HOURS,
calc_false_breakout_plan,
expires_at_text,
false_breakout_gate_preview,
is_false_breakout_expired,
is_false_breakout_key_monitor_type,
is_limit_key_monitor_type,
@@ -6448,7 +6449,8 @@ def api_price_snapshot():
key_prices = []
for r in key_rows:
is_fib = is_fib_key_monitor_type(r["monitor_type"])
if is_fib:
is_fb = is_false_breakout_key_monitor_type(r["monitor_type"])
if is_fib or is_fb:
price = get_symbol_mark_price(r["symbol"])
else:
price = prices.get(r["symbol"])
@@ -6460,6 +6462,7 @@ def api_price_snapshot():
gate_summary = "-"
gate_metrics = ""
fib_gate_ok = True
fb_gate_ok = True
if is_fib:
direction = (r["direction"] or "long").lower()
inval = fib_invalidate_by_mark(direction, price, r["upper"], r["lower"])
@@ -6469,6 +6472,18 @@ def api_price_snapshot():
gate_summary = f"斐波 挂E={entry_txt} {'标记价将失效' if inval else '等待成交'}"
if _sqlite_row_val(r, "fib_limit_order_id"):
gate_metrics = f"限价单:{_sqlite_row_val(r, 'fib_limit_order_id')}"
elif is_fb:
entry = _sqlite_row_val(r, "fib_entry_price")
entry_txt = format_price_for_symbol(r["symbol"], entry) if entry else "-"
prev = false_breakout_gate_preview(
entry_display=entry_txt,
limit_order_id=_sqlite_row_val(r, "fib_limit_order_id"),
created_at=r["created_at"],
now=app_now(),
)
gate_summary = prev.get("summary") or "-"
gate_metrics = prev.get("metrics") or ""
fb_gate_ok = bool(prev.get("gate_ok"))
elif (r["monitor_type"] or "").strip() in KEY_MONITOR_RS_TYPES:
try:
prev = _key_rs_gate_preview(r["symbol"], r["upper"], r["lower"])
@@ -6476,7 +6491,7 @@ def api_price_snapshot():
gate_metrics = prev.get("metrics") or ""
except Exception:
gate_summary = "-"
else:
elif (r["monitor_type"] or "").strip() in KEY_MONITOR_AUTO_TYPES:
try:
gate = _key_hard_checks(
r["symbol"],
@@ -6525,7 +6540,11 @@ def api_price_snapshot():
"lower_diff": lower_diff,
"lower_pct": lower_pct,
"gate_summary": gate_summary,
"gate_ok": fib_gate_ok if is_fib else bool(gate and gate.get("ok")),
"gate_ok": (
fib_gate_ok if is_fib
else fb_gate_ok if is_fb
else bool(gate and gate.get("ok"))
),
"gate_metrics": gate_metrics,
})
+22 -3
View File
@@ -57,6 +57,7 @@ from false_breakout_key_monitor_lib import (
FALSE_BREAKOUT_VALIDITY_HOURS,
calc_false_breakout_plan,
expires_at_text,
false_breakout_gate_preview,
is_false_breakout_expired,
is_false_breakout_key_monitor_type,
is_limit_key_monitor_type,
@@ -6035,7 +6036,8 @@ def api_price_snapshot():
key_prices = []
for r in key_rows:
is_fib = is_fib_key_monitor_type(r["monitor_type"])
if is_fib:
is_fb = is_false_breakout_key_monitor_type(r["monitor_type"])
if is_fib or is_fb:
price = get_symbol_mark_price(r["symbol"])
else:
price = prices.get(r["symbol"])
@@ -6047,6 +6049,7 @@ def api_price_snapshot():
gate_summary = "-"
gate_metrics = ""
fib_gate_ok = True
fb_gate_ok = True
if is_fib:
direction = (r["direction"] or "long").lower()
inval = fib_invalidate_by_mark(direction, price, r["upper"], r["lower"])
@@ -6056,6 +6059,18 @@ def api_price_snapshot():
gate_summary = f"斐波 挂E={entry_txt} {'标记价将失效' if inval else '等待成交'}"
if _sqlite_row_val(r, "fib_limit_order_id"):
gate_metrics = f"限价单:{_sqlite_row_val(r, 'fib_limit_order_id')}"
elif is_fb:
entry = _sqlite_row_val(r, "fib_entry_price")
entry_txt = format_price_for_symbol(r["symbol"], entry) if entry else "-"
prev = false_breakout_gate_preview(
entry_display=entry_txt,
limit_order_id=_sqlite_row_val(r, "fib_limit_order_id"),
created_at=r["created_at"],
now=app_now(),
)
gate_summary = prev.get("summary") or "-"
gate_metrics = prev.get("metrics") or ""
fb_gate_ok = bool(prev.get("gate_ok"))
elif (r["monitor_type"] or "").strip() in KEY_MONITOR_RS_TYPES:
try:
prev = _key_rs_gate_preview(r["symbol"], r["upper"], r["lower"])
@@ -6063,7 +6078,7 @@ def api_price_snapshot():
gate_metrics = prev.get("metrics") or ""
except Exception:
gate_summary = "-"
else:
elif (r["monitor_type"] or "").strip() in KEY_MONITOR_AUTO_TYPES:
try:
gate = _key_hard_checks(
r["symbol"],
@@ -6112,7 +6127,11 @@ def api_price_snapshot():
"lower_diff": lower_diff,
"lower_pct": lower_pct,
"gate_summary": gate_summary,
"gate_ok": fib_gate_ok if is_fib else bool(gate and gate.get("ok")),
"gate_ok": (
fib_gate_ok if is_fib
else fb_gate_ok if is_fb
else bool(gate and gate.get("ok"))
),
"gate_metrics": gate_metrics,
})
+26
View File
@@ -117,3 +117,29 @@ def expires_at_text(created_at: Any, *, hours: int = FALSE_BREAKOUT_VALIDITY_HOU
if dt is None:
return ""
return (dt + timedelta(hours=hours)).strftime("%Y-%m-%d %H:%M:%S")
def false_breakout_gate_preview(
*,
entry_display: str,
limit_order_id: Any = None,
created_at: Any = None,
now: Optional[datetime] = None,
hours: int = FALSE_BREAKOUT_VALIDITY_HOURS,
) -> dict[str, Any]:
"""假突破门控预览:限价挂单状态,不使用箱体/收敛的量破幅二确门控。"""
now_dt = now or datetime.now()
expired = is_false_breakout_expired(created_at, now_dt, hours=hours)
exp_txt = expires_at_text(created_at, hours=hours)
status = "已过期" if expired else "等待成交"
metrics_parts: list[str] = []
oid = str(limit_order_id or "").strip()
if oid:
metrics_parts.append(f"限价单:{oid}")
if exp_txt != "":
metrics_parts.append(f"截至:{exp_txt}")
return {
"summary": f"假突破 挂E={entry_display} {status}",
"metrics": " ".join(metrics_parts),
"gate_ok": not expired,
}
@@ -4,6 +4,7 @@ from datetime import datetime, timedelta
from false_breakout_key_monitor_lib import (
FALSE_BREAKOUT_MONITOR_TYPE,
calc_false_breakout_plan,
false_breakout_gate_preview,
is_false_breakout_expired,
key_price_from_row,
normalize_false_breakout_symbol,
@@ -56,6 +57,20 @@ class FalseBreakoutKeyMonitorLibTests(unittest.TestCase):
def test_monitor_type_constant(self):
self.assertEqual(FALSE_BREAKOUT_MONITOR_TYPE, "假突破")
def test_gate_preview_not_box_gate(self):
now = datetime(2026, 6, 7, 12, 0, 0)
prev = false_breakout_gate_preview(
entry_display="1635.0",
limit_order_id="oid-1",
created_at="2026-06-07 10:00:00",
now=now,
)
self.assertIn("假突破", prev["summary"])
self.assertIn("等待成交", prev["summary"])
self.assertNotIn("量:", prev["summary"])
self.assertIn("限价单:oid-1", prev["metrics"])
self.assertTrue(prev["gate_ok"])
if __name__ == "__main__":
unittest.main()