feat: add false breakout key monitor for BTC/ETH on three exchanges
Place limit orders outside key levels with fixed SL and 1.5 RR, 24h expiry, separate stats, and full-margin mode guard. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+203
-25
@@ -52,6 +52,18 @@ from fib_key_monitor_lib import (
|
||||
key_signal_type_for_trade_record,
|
||||
stored_key_signal_type,
|
||||
)
|
||||
from false_breakout_key_monitor_lib import (
|
||||
FALSE_BREAKOUT_MONITOR_TYPE,
|
||||
FALSE_BREAKOUT_VALIDITY_HOURS,
|
||||
calc_false_breakout_plan,
|
||||
expires_at_text,
|
||||
is_false_breakout_expired,
|
||||
is_false_breakout_key_monitor_type,
|
||||
is_limit_key_monitor_type,
|
||||
key_price_from_row,
|
||||
normalize_false_breakout_symbol,
|
||||
storage_bounds_from_key_price,
|
||||
)
|
||||
from strategy_trade_labels import (
|
||||
STRATEGY_ENTRY_REASON_OPTIONS,
|
||||
apply_order_monitor_source_labels,
|
||||
@@ -982,6 +994,7 @@ ENTRY_REASON_OPTIONS = (
|
||||
"关键位收敛突破",
|
||||
"关键位斐波0.618",
|
||||
"关键位斐波0.786",
|
||||
"关键位假突破",
|
||||
) + STRATEGY_ENTRY_REASON_OPTIONS
|
||||
|
||||
STATS_SEGMENT_DEFS = (
|
||||
@@ -991,6 +1004,7 @@ STATS_SEGMENT_DEFS = (
|
||||
("key_conv", "关键位收敛结构", {"segment": "key_conv"}),
|
||||
("key_fib618", "关键位斐波0.618", {"segment": "key_fib618"}),
|
||||
("key_fib786", "关键位斐波0.786", {"segment": "key_fib786"}),
|
||||
("key_false_breakout", "关键位假突破", {"segment": "key_false_breakout"}),
|
||||
)
|
||||
# 复盘表单「其他」选项的 value(非入库值;自定义文本走 entry_reason_custom)
|
||||
ENTRY_REASON_OTHER = "__OTHER__"
|
||||
@@ -1584,6 +1598,8 @@ def _pnl_row_matches_segment(row, segment_key):
|
||||
return kst == "斐波回调0.618"
|
||||
if segment_key == "key_fib786":
|
||||
return kst == "斐波回调0.786"
|
||||
if segment_key == "key_false_breakout":
|
||||
return kst == FALSE_BREAKOUT_MONITOR_TYPE
|
||||
return False
|
||||
|
||||
|
||||
@@ -1600,6 +1616,7 @@ def _count_opens_for_segment(conn, start_td, end_td, segment_key):
|
||||
"key_conv": "收敛突破",
|
||||
"key_fib618": "斐波回调0.618",
|
||||
"key_fib786": "斐波回调0.786",
|
||||
"key_false_breakout": FALSE_BREAKOUT_MONITOR_TYPE,
|
||||
}
|
||||
kst = kst_map.get(segment_key)
|
||||
if kst:
|
||||
@@ -2326,7 +2343,7 @@ def order_row_key_signal_type(row):
|
||||
if "key_signal_type" not in keys:
|
||||
return None
|
||||
kst = (row["key_signal_type"] or "").strip()
|
||||
if kst in KEY_MONITOR_AUTO_TYPES or is_fib_key_monitor_type(kst):
|
||||
if kst in KEY_MONITOR_AUTO_TYPES or is_fib_key_monitor_type(kst) or is_false_breakout_key_monitor_type(kst):
|
||||
return kst
|
||||
return None
|
||||
|
||||
@@ -4623,6 +4640,19 @@ def _fib_plan_for_row(row):
|
||||
return calc_fib_plan(row["direction"], row["upper"], row["lower"], ratio)
|
||||
|
||||
|
||||
def _limit_key_plan_for_row(row):
|
||||
typ = (row["monitor_type"] or "").strip()
|
||||
if is_fib_key_monitor_type(typ):
|
||||
return _fib_plan_for_row(row)
|
||||
if is_false_breakout_key_monitor_type(typ):
|
||||
direction = (row["direction"] or "long").lower()
|
||||
key_px = key_price_from_row(direction, row["upper"], row["lower"])
|
||||
if key_px is None:
|
||||
return None
|
||||
return calc_false_breakout_plan(direction, key_px)
|
||||
return None
|
||||
|
||||
|
||||
def _cancel_fib_monitor_limit(row):
|
||||
ex_sym = normalize_exchange_symbol(row["symbol"])
|
||||
oid = _sqlite_row_val(row, "fib_limit_order_id")
|
||||
@@ -4709,10 +4739,11 @@ def _finalize_fib_key_fill(conn, row):
|
||||
symbol = row["symbol"]
|
||||
direction = (row["direction"] or "long").lower()
|
||||
typ = (row["monitor_type"] or "").strip()
|
||||
kind = "假突破" if is_false_breakout_key_monitor_type(typ) else "斐波"
|
||||
ex_sym = normalize_exchange_symbol(symbol)
|
||||
plan = _fib_plan_for_row(row)
|
||||
plan = _limit_key_plan_for_row(row)
|
||||
if not plan:
|
||||
_finalize_key_monitor_one_shot(conn, row, "斐波计划无效", "fib_plan_invalid")
|
||||
_finalize_key_monitor_one_shot(conn, row, f"{kind}计划无效", "fib_plan_invalid")
|
||||
return
|
||||
entry_plan, sl_plan, tp_plan = plan
|
||||
sl = float(_sqlite_row_val(row, "fib_stop_loss", sl_plan) or sl_plan)
|
||||
@@ -4743,7 +4774,7 @@ def _finalize_fib_key_fill(conn, row):
|
||||
amount = float(live_amt or 0)
|
||||
if amount <= 0:
|
||||
send_wechat_msg(
|
||||
f"# ❌ {symbol} 斐波成交后处理失败\n"
|
||||
f"# ❌ {symbol} {kind}成交后处理失败\n"
|
||||
f"**账户:{_wechat_account_label()}**\n"
|
||||
f"- 无法取得持仓/下单数量,未挂 TP/SL\n"
|
||||
)
|
||||
@@ -4751,7 +4782,7 @@ def _finalize_fib_key_fill(conn, row):
|
||||
ok, reason = precheck_risk(conn, symbol, direction)
|
||||
if not ok:
|
||||
send_wechat_msg(
|
||||
f"# ❌ {symbol} 斐波成交后风控拒绝\n"
|
||||
f"# ❌ {symbol} {kind}成交后风控拒绝\n"
|
||||
f"**账户:{_wechat_account_label()}**\n"
|
||||
f"- 类型:{typ}\n"
|
||||
f"- 原因:{reason}\n"
|
||||
@@ -4764,7 +4795,7 @@ def _finalize_fib_key_fill(conn, row):
|
||||
tpsl_attached = True
|
||||
except Exception as e:
|
||||
send_wechat_msg(
|
||||
f"# ❌ {symbol} 斐波成交后挂 TP/SL 失败\n"
|
||||
f"# ❌ {symbol} {kind}成交后挂 TP/SL 失败\n"
|
||||
f"**账户:{_wechat_account_label()}**\n"
|
||||
f"- 错误:{friendly_exchange_error(e)}\n"
|
||||
f"- 请手动补挂止盈止损\n"
|
||||
@@ -4782,8 +4813,9 @@ def _finalize_fib_key_fill(conn, row):
|
||||
notional_value, position_ratio, base_amount, oid, tpsl_attached,
|
||||
)
|
||||
rr_txt = format_wechat_scalar_2dp(planned_rr) if planned_rr is not None else "-"
|
||||
close_reason = "false_breakout_filled" if is_false_breakout_key_monitor_type(typ) else "fib_filled"
|
||||
succ = (
|
||||
f"# ✅ {symbol} 斐波限价成交\n"
|
||||
f"# ✅ {symbol} {kind}限价成交\n"
|
||||
f"**账户:{_wechat_account_label()}**\n"
|
||||
f"- 来源:{ORDER_MONITOR_TYPE_KEY_AUTO}(限价 @ E)\n"
|
||||
f"- 类型:{typ}|{_wechat_direction_text(direction)}\n"
|
||||
@@ -4794,7 +4826,7 @@ def _finalize_fib_key_fill(conn, row):
|
||||
f"- {'已挂交易所 TP/SL' if tpsl_attached else 'TP/SL 未挂上'}\n"
|
||||
)
|
||||
send_wechat_msg(succ)
|
||||
_finalize_key_monitor_one_shot(conn, row, succ, "fib_filled")
|
||||
_finalize_key_monitor_one_shot(conn, row, succ, close_reason)
|
||||
|
||||
|
||||
def check_fib_key_monitors():
|
||||
@@ -4802,13 +4834,28 @@ def check_fib_key_monitors():
|
||||
rows = conn.execute("SELECT * FROM key_monitors").fetchall()
|
||||
for r in rows:
|
||||
typ = (r["monitor_type"] or "").strip()
|
||||
if not is_fib_key_monitor_type(typ):
|
||||
if not is_limit_key_monitor_type(typ):
|
||||
continue
|
||||
symbol = r["symbol"]
|
||||
direction = (r["direction"] or "long").lower()
|
||||
ex_sym = normalize_exchange_symbol(symbol)
|
||||
up, low = float(r["upper"]), float(r["lower"])
|
||||
oid = _sqlite_row_val(r, "fib_limit_order_id")
|
||||
if is_false_breakout_key_monitor_type(typ):
|
||||
now_dt = app_now()
|
||||
if is_false_breakout_expired(r["created_at"], now_dt):
|
||||
_cancel_fib_monitor_limit(r)
|
||||
exp_txt = expires_at_text(r["created_at"])
|
||||
msg = (
|
||||
f"# ⚠️ {symbol} 假突破监控已过期\n"
|
||||
f"**账户:{_wechat_account_label()}**\n"
|
||||
f"- 类型:{typ}|{_wechat_direction_text(direction)}\n"
|
||||
f"- 有效期 {FALSE_BREAKOUT_VALIDITY_HOURS}h(应于 {exp_txt} 前成交)\n"
|
||||
f"- 已撤销限价单\n"
|
||||
)
|
||||
send_wechat_msg(msg)
|
||||
_finalize_key_monitor_one_shot(conn, r, msg, "false_breakout_expired")
|
||||
continue
|
||||
mark = get_symbol_mark_price(symbol)
|
||||
if mark is None:
|
||||
continue
|
||||
@@ -4816,7 +4863,7 @@ def check_fib_key_monitors():
|
||||
if status == "filled" or (status != "open" and _fib_has_live_position(ex_sym, direction)):
|
||||
_finalize_fib_key_fill(conn, r)
|
||||
continue
|
||||
if status == "open":
|
||||
if is_fib_key_monitor_type(typ) and status == "open":
|
||||
if fib_invalidate_by_mark(direction, mark, up, low):
|
||||
_cancel_fib_monitor_limit(r)
|
||||
msg = (
|
||||
@@ -4828,7 +4875,7 @@ def check_fib_key_monitors():
|
||||
send_wechat_msg(msg)
|
||||
_finalize_key_monitor_one_shot(conn, r, msg, "fib_invalidate")
|
||||
continue
|
||||
if status in ("canceled", "missing", "unknown") and fib_invalidate_by_mark(direction, mark, up, low):
|
||||
if is_fib_key_monitor_type(typ) and status in ("canceled", "missing", "unknown") and fib_invalidate_by_mark(direction, mark, up, low):
|
||||
msg = (
|
||||
f"# ⚠️ {symbol} 斐波监控失效(限价已不在挂单)\n"
|
||||
f"**账户:{_wechat_account_label()}**\n"
|
||||
@@ -4840,6 +4887,86 @@ def check_fib_key_monitors():
|
||||
conn.close()
|
||||
|
||||
|
||||
def _false_breakout_exists_for_symbol(conn, symbol):
|
||||
row = conn.execute(
|
||||
"SELECT id FROM key_monitors WHERE symbol=? AND monitor_type=?",
|
||||
(symbol, FALSE_BREAKOUT_MONITOR_TYPE),
|
||||
).fetchone()
|
||||
return row is not None
|
||||
|
||||
|
||||
def _add_false_breakout_key_monitor(
|
||||
conn, symbol, direction_sel, upper_px, lower_px, key_px, breakeven_enabled=0,
|
||||
):
|
||||
if _false_breakout_exists_for_symbol(conn, symbol):
|
||||
return False, f"{symbol} 已有假突破监控(同币仅允许一条)"
|
||||
plan = calc_false_breakout_plan(direction_sel, key_px)
|
||||
if not plan:
|
||||
return False, "假突破价位无效,请核对方向与关键价位"
|
||||
entry, sl, tp = plan
|
||||
ex_sym = normalize_exchange_symbol(symbol)
|
||||
entry = round_price_to_exchange(ex_sym, entry)
|
||||
sl = round_price_to_exchange(ex_sym, sl)
|
||||
tp = round_price_to_exchange(ex_sym, tp)
|
||||
if entry is None or sl is None or tp is None:
|
||||
return False, "假突破价位经交易所精度舍入后无效"
|
||||
entry, sl, tp = float(entry), float(sl), float(tp)
|
||||
ok, reason = precheck_risk(conn, symbol, direction_sel)
|
||||
if not ok:
|
||||
return False, reason
|
||||
ok_live, reason_live = ensure_exchange_live_ready()
|
||||
if not ok_live:
|
||||
return False, reason_live
|
||||
now = app_now()
|
||||
trading_day = get_trading_day(now)
|
||||
session_row = ensure_session(conn, trading_day)
|
||||
_, trading_capital_live = get_exchange_capitals(force=True)
|
||||
live_capital = float(trading_capital_live) if trading_capital_live is not None else float(session_row["current_capital"])
|
||||
capital_base = resolve_capital_base_for_key_open(conn, trading_day, live_capital)
|
||||
default_leverage = get_synced_leverage(ex_sym, direction_sel) or infer_leverage(symbol)
|
||||
leverage = int(default_leverage) if default_leverage else 5
|
||||
if leverage <= 0:
|
||||
leverage = 5
|
||||
available_usdt = get_available_trading_usdt()
|
||||
risk_fraction = calc_risk_fraction(direction_sel, entry, sl)
|
||||
if risk_fraction is None:
|
||||
return False, "止损方向不合法(相对挂单价);请核对方向与关键价位"
|
||||
risk_percent = max(0.01, float(RISK_PERCENT))
|
||||
risk_amount = round(capital_base * risk_percent / 100.0, 4)
|
||||
notional_value = round(risk_amount / risk_fraction, 4)
|
||||
margin_capital = round(notional_value / leverage, 4)
|
||||
if capital_base and margin_capital > capital_base:
|
||||
return False, "以损定仓后保证金超过当前交易资金"
|
||||
if available_usdt is not None:
|
||||
max_margin = round(max(available_usdt * FULL_MARGIN_BUFFER_RATIO, 0), 4)
|
||||
if margin_capital > max_margin:
|
||||
return (
|
||||
False,
|
||||
f"保证金不足:交易账户可用约 {round(available_usdt, 2)}U,当前最多建议 {round(max_margin, 2)}U",
|
||||
)
|
||||
try:
|
||||
amount, _ = prepare_order_amount(ex_sym, margin_capital, leverage, entry)
|
||||
order_resp = place_fib_limit_order(ex_sym, direction_sel, amount, leverage, entry)
|
||||
oid = str(order_resp.get("id") or "")
|
||||
if not oid:
|
||||
return False, "交易所未返回限价单 ID"
|
||||
except Exception as e:
|
||||
return False, friendly_exchange_error(e, available_usdt=available_usdt)
|
||||
be_flag = 1 if int(breakeven_enabled or 0) != 0 else 0
|
||||
conn.execute(
|
||||
"INSERT INTO key_monitors "
|
||||
"(symbol, monitor_type, direction, upper, lower, "
|
||||
"fib_limit_order_id, fib_entry_price, fib_stop_loss, fib_take_profit, "
|
||||
"fib_order_amount, fib_margin_capital, fib_leverage, breakeven_enabled) "
|
||||
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
||||
(
|
||||
symbol, FALSE_BREAKOUT_MONITOR_TYPE, direction_sel, upper_px, lower_px,
|
||||
oid, entry, sl, tp, float(amount), margin_capital, leverage, be_flag,
|
||||
),
|
||||
)
|
||||
return True, None
|
||||
|
||||
|
||||
def _add_fib_key_monitor(conn, symbol, direction_sel, mt, upper_px, lower_px, breakeven_enabled=0):
|
||||
if _fib_key_exists_for_symbol(conn, symbol):
|
||||
return False, f"{symbol} 已有斐波监控(同币仅允许一条 0.618/0.786)"
|
||||
@@ -4922,7 +5049,7 @@ def check_key_monitors():
|
||||
for r in rows:
|
||||
sym, typ_raw, up, low = r["symbol"], r["monitor_type"], r["upper"], r["lower"]
|
||||
typ = (typ_raw or "").strip()
|
||||
if is_fib_key_monitor_type(typ):
|
||||
if is_limit_key_monitor_type(typ):
|
||||
continue
|
||||
if typ in KEY_MONITOR_RS_TYPES:
|
||||
try:
|
||||
@@ -5941,6 +6068,7 @@ def render_main_page(page="trade"):
|
||||
f"【箱体/收敛】{KLINE_TIMEFRAME} 两根闭合K|突破越过关键位 > {KEY_BREAKOUT_AMP_MIN_PCT}%|"
|
||||
f"确认K收于箱外|量能>前{KEY_VOLUME_MA_BARS}均量×{KEY_VOLUME_RATIO_MIN}|"
|
||||
f"RR>{KEY_AUTO_MIN_PLANNED_RR}|日成交前{KEY_DAILY_VOLUME_RANK_MAX}|"
|
||||
f"【假突破·BTC/ETH】做空填高点/做多填低点,外侧 0.1% 挂限价,止损 0.5%,RR 1.5,有效期 {FALSE_BREAKOUT_VALIDITY_HOURS}h|"
|
||||
f"【阻力/支撑】填上/下沿,5m 收盘突破任一侧即提醒 {KEY_ALERT_MAX_TIMES} 次(间隔 {KEY_ALERT_INTERVAL_MINUTES} 分),不选方向、不自动开仓"
|
||||
)
|
||||
strategy_extra = {}
|
||||
@@ -6737,25 +6865,29 @@ def add_key():
|
||||
tuple(KEY_MONITOR_AUTO_TYPES)
|
||||
+ tuple(KEY_MONITOR_ALERT_ONLY_TYPES)
|
||||
+ tuple(FIB_KEY_MONITOR_TYPES)
|
||||
+ (FALSE_BREAKOUT_MONITOR_TYPE,)
|
||||
)
|
||||
if mt not in allowed_types:
|
||||
flash("监控类型无效")
|
||||
return redirect("/key_monitor")
|
||||
if is_full_margin_mode(POSITION_SIZING_MODE) and monitor_type_disallowed_in_full_margin(mt):
|
||||
flash(
|
||||
"全仓杠杆模式下不可添加箱体/收敛突破或斐波监控;"
|
||||
"全仓杠杆模式下不可添加箱体/收敛突破、斐波或假突破监控;"
|
||||
"请改用阻力/支撑(仅提醒),或切换 POSITION_SIZING_MODE=risk 并重启(须无持仓)。"
|
||||
)
|
||||
return redirect("/key_monitor")
|
||||
rank, total = _daily_volume_rank(symbol)
|
||||
if rank is None:
|
||||
flash("日成交量排名读取失败,请稍后重试")
|
||||
return redirect("/key_monitor")
|
||||
if rank > KEY_DAILY_VOLUME_RANK_MAX:
|
||||
flash(
|
||||
f"{symbol} 当前日成交量排名为 {rank}/{total},不在前{KEY_DAILY_VOLUME_RANK_MAX},已拒绝添加关键位"
|
||||
)
|
||||
return redirect("/key_monitor")
|
||||
skip_volume_rank = is_false_breakout_key_monitor_type(mt)
|
||||
rank, total = None, None
|
||||
if not skip_volume_rank:
|
||||
rank, total = _daily_volume_rank(symbol)
|
||||
if rank is None:
|
||||
flash("日成交量排名读取失败,请稍后重试")
|
||||
return redirect("/key_monitor")
|
||||
if rank > KEY_DAILY_VOLUME_RANK_MAX:
|
||||
flash(
|
||||
f"{symbol} 当前日成交量排名为 {rank}/{total},不在前{KEY_DAILY_VOLUME_RANK_MAX},已拒绝添加关键位"
|
||||
)
|
||||
return redirect("/key_monitor")
|
||||
conn = get_db()
|
||||
if mt in KEY_MONITOR_AUTO_TYPES:
|
||||
occupied = get_active_position_count(conn)
|
||||
@@ -6772,6 +6904,53 @@ def add_key():
|
||||
ensure_markets_loaded()
|
||||
except Exception:
|
||||
pass
|
||||
be_flag = parse_breakeven_enabled_form(d.get("breakeven_enabled"))
|
||||
if is_false_breakout_key_monitor_type(mt):
|
||||
fb_sym = normalize_false_breakout_symbol(symbol)
|
||||
if not fb_sym:
|
||||
conn.close()
|
||||
conn = None
|
||||
flash("假突破仅支持 BTC / ETH")
|
||||
return redirect("/key_monitor")
|
||||
symbol = fb_sym
|
||||
if direction_sel not in ("long", "short"):
|
||||
conn.close()
|
||||
conn = None
|
||||
flash("假突破请选择做多或做空")
|
||||
return redirect("/key_monitor")
|
||||
try:
|
||||
key_px = float(d.get("key_price") or 0)
|
||||
except (TypeError, ValueError):
|
||||
key_px = 0
|
||||
if key_px <= 0:
|
||||
conn.close()
|
||||
conn = None
|
||||
flash("请填写关键价位(做空填高点,做多填低点)")
|
||||
return redirect("/key_monitor")
|
||||
ex_sym_key = normalize_exchange_symbol(symbol)
|
||||
key_adj = round_price_to_exchange(ex_sym_key, key_px)
|
||||
key_px = float(key_adj) if key_adj is not None else float(key_px)
|
||||
try:
|
||||
upper_px, lower_px = storage_bounds_from_key_price(direction_sel, key_px)
|
||||
except ValueError as e:
|
||||
conn.close()
|
||||
conn = None
|
||||
flash(str(e))
|
||||
return redirect("/key_monitor")
|
||||
ok_fb, err_fb = _add_false_breakout_key_monitor(
|
||||
conn, symbol, direction_sel, upper_px, lower_px, key_px, breakeven_enabled=be_flag,
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
conn = None
|
||||
if not ok_fb:
|
||||
flash(err_fb or "假突破监控添加失败")
|
||||
return redirect("/key_monitor")
|
||||
flash(
|
||||
f"假突破监控已添加,限价单已挂出({symbol})"
|
||||
f"|有效期 {FALSE_BREAKOUT_VALIDITY_HOURS}h|移动保本:{'开' if be_flag else '关'}"
|
||||
)
|
||||
return redirect("/key_monitor")
|
||||
try:
|
||||
upper_raw = float(d.get("upper") or 0)
|
||||
lower_raw = float(d.get("lower") or 0)
|
||||
@@ -6787,7 +6966,6 @@ def add_key():
|
||||
conn = None
|
||||
flash("上沿必须大于下沿")
|
||||
return redirect("/key_monitor")
|
||||
be_flag = parse_breakeven_enabled_form(d.get("breakeven_enabled"))
|
||||
if is_fib_key_monitor_type(mt):
|
||||
ok_fib, err_fib = _add_fib_key_monitor(
|
||||
conn, symbol, direction_sel, mt, upper_px, lower_px, breakeven_enabled=be_flag,
|
||||
@@ -7257,7 +7435,7 @@ def delete_key_monitor(kid):
|
||||
if not row:
|
||||
conn.close()
|
||||
return jsonify({"ok": False, "error": "not_found"})
|
||||
if is_fib_key_monitor_type(row["monitor_type"]):
|
||||
if is_limit_key_monitor_type(row["monitor_type"]):
|
||||
_cancel_fib_monitor_limit(row)
|
||||
insert_key_monitor_history(conn, row, int(row["notification_count"] or 0), None, "manual")
|
||||
cur = conn.execute("DELETE FROM key_monitors WHERE id=?", (kid,))
|
||||
@@ -7282,7 +7460,7 @@ def del_key(id):
|
||||
conn = get_db()
|
||||
row = conn.execute("SELECT * FROM key_monitors WHERE id=?", (id,)).fetchone()
|
||||
if row:
|
||||
if is_fib_key_monitor_type(row["monitor_type"]):
|
||||
if is_limit_key_monitor_type(row["monitor_type"]):
|
||||
_cancel_fib_monitor_limit(row)
|
||||
insert_key_monitor_history(conn, row, int(row["notification_count"] or 0), None, "manual")
|
||||
conn.execute("DELETE FROM key_monitors WHERE id=?", (id,))
|
||||
|
||||
Reference in New Issue
Block a user