From 7af2717331fe1ee497a2c1f0c933d133c834ab53 Mon Sep 17 00:00:00 2001 From: dekun Date: Fri, 29 May 2026 17:48:19 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=B6=8B=E5=8A=BF=E5=9B=9E?= =?UTF-8?q?=E8=B0=83=E4=BC=81=E4=B8=9A=E5=BE=AE=E4=BF=A1=E6=8E=A8=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crypto_monitor_gate_bot/app.py | 90 ++++++++++++++++++++------- strategy_trend_register.py | 110 ++++++++++++++++++++++----------- 2 files changed, 140 insertions(+), 60 deletions(-) diff --git a/crypto_monitor_gate_bot/app.py b/crypto_monitor_gate_bot/app.py index b1aac99..d984148 100644 --- a/crypto_monitor_gate_bot/app.py +++ b/crypto_monitor_gate_bot/app.py @@ -4470,8 +4470,34 @@ def _trend_weighted_avg(old_avg, old_amt, fill_px, add_amt): return float(fill_px or 0) +def _trend_plan_stop_status(result_label): + if result_label == "止盈": + return "stopped_tp" + if result_label == "止损": + return "stopped_sl" + return "stopped_manual" + + +def _trend_plan_trade_exists(conn, plan_id): + try: + return conn.execute( + "SELECT id FROM trade_records WHERE trend_plan_id=? LIMIT 1", + (int(plan_id),), + ).fetchone() is not None + except Exception: + return False + + def _trend_finalize_plan(conn, row, result_label, exit_price, closed_at=None): """平仓后记账、撤单、结束计划。""" + plan_id = int(row["id"]) + active = conn.execute( + "SELECT * FROM trend_pullback_plans WHERE id=? AND status='active'", + (plan_id,), + ).fetchone() + if not active: + return + row = active sym = row["symbol"] direction = row["direction"] or "long" ex_sym = row["exchange_symbol"] or normalize_exchange_symbol(sym) @@ -4492,24 +4518,18 @@ def _trend_finalize_plan(conn, row, result_label, exit_price, closed_at=None): cancel_gate_swap_trigger_orders(ex_sym) except Exception: pass + st = _trend_plan_stop_status(result_label) + cur = conn.execute( + "UPDATE trend_pullback_plans SET status=?, message=? WHERE id=? AND status='active'", + (st, res, plan_id), + ) + if not getattr(cur, "rowcount", 0): + return + conn.commit() + if _trend_plan_trade_exists(conn, plan_id): + return session_date = row["session_date"] or get_trading_day() session_capital = update_session_capital(conn, session_date, pnl_amount) - send_wechat_msg( - build_wechat_close_message( - symbol=sym, - direction=direction, - result=f"{res}({MONITOR_TYPE_TREND})", - pnl_amount=pnl_amount, - hold_seconds=hold_seconds, - trigger_price=avg_e, - current_price=float(exit_price), - stop_loss=float(row["stop_loss"]), - take_profit=float(row["take_profit"]), - close_order_id="-", - extra_note="计划本金口径:启动时合约可用余额快照;止盈由程序监控", - session_capital_fallback=session_capital, - ) - ) insert_trade_record( conn, symbol=sym, @@ -4530,13 +4550,25 @@ def _trend_finalize_plan(conn, row, result_label, exit_price, closed_at=None): result=res, opened_at=opened_at, closed_at=closed_at, - trend_plan_id=int(row["id"]), + trend_plan_id=plan_id, ) - st = "stopped_tp" if result_label == "止盈" else ("stopped_sl" if result_label == "止损" else "stopped_manual") - conn.execute( - "UPDATE trend_pullback_plans SET status=?, message=? WHERE id=?", - (st, res, row["id"]), + send_wechat_msg( + build_wechat_close_message( + symbol=sym, + direction=direction, + result=f"{res}({MONITOR_TYPE_TREND})", + pnl_amount=pnl_amount, + hold_seconds=hold_seconds, + trigger_price=avg_e, + current_price=float(exit_price), + stop_loss=float(row["stop_loss"]), + take_profit=float(row["take_profit"]), + close_order_id="-", + extra_note="计划本金口径:启动时合约可用余额快照;止盈由程序监控", + session_capital_fallback=session_capital, + ) ) + conn.commit() def check_trend_pullback_plans(): @@ -6508,11 +6540,21 @@ def stop_trend_pullback(pid): cancel_all_open_orders_for_symbol(ex_sym) except Exception: pass - _trend_finalize_plan(conn, row, "手动平仓", exit_p) - conn.commit() + try: + _trend_finalize_plan(conn, row, "手动平仓", exit_p) + except Exception as e: + conn.execute( + "UPDATE trend_pullback_plans SET status='stopped_manual', message=? " + "WHERE id=? AND status='active'", + (f"结束异常:{e}", pid), + ) + conn.commit() + conn.close() + flash(f"计划已结束但记账可能不完整:{e}") + return redirect(url_for("strategy_trend_page")) conn.close() flash("已结束趋势回调计划(市价平仓、撤单)") - return redirect("/trade") + return redirect(url_for("strategy_trend_page")) @app.route("/delete_trend_plan_history/", methods=["POST"]) diff --git a/strategy_trend_register.py b/strategy_trend_register.py index 0b2444e..bb76a2e 100644 --- a/strategy_trend_register.py +++ b/strategy_trend_register.py @@ -291,8 +291,34 @@ def _weighted_avg(old_avg, old_amt, fill_px, add_amt): return float(fill_px or 0) +def _plan_stop_status(result_label: str) -> str: + if result_label == "止盈": + return "stopped_tp" + if result_label == "止损": + return "stopped_sl" + return "stopped_manual" + + +def _trend_plan_trade_exists(conn, plan_id: int) -> bool: + try: + return conn.execute( + "SELECT id FROM trade_records WHERE trend_plan_id=? LIMIT 1", + (int(plan_id),), + ).fetchone() is not None + except Exception: + return False + + def _finalize_plan(cfg: dict, conn, row, result_label: str, exit_price: float) -> None: m = _m(cfg) + plan_id = int(row["id"]) + active = conn.execute( + "SELECT * FROM trend_pullback_plans WHERE id=? AND status='active'", + (plan_id,), + ).fetchone() + if not active: + return + row = active sym = row["symbol"] direction = row["direction"] or "long" ex_sym = row["exchange_symbol"] or m.normalize_exchange_symbol(sym) @@ -308,31 +334,22 @@ def _finalize_plan(cfg: dict, conn, row, result_label: str, exit_price: float) - direction, float(row["add_upper"]), float(row["stop_loss"]), margin_cap, lev ) planned_rr = m.calc_rr_ratio(direction, avg_e, float(row["stop_loss"]), float(row["take_profit"])) - session_date = row["session_date"] or m.get_trading_day() - session_capital = m.update_session_capital(conn, session_date, pnl_amount) try: cancel_symbol_orders(cfg, ex_sym) except Exception: pass - extra = getattr(m, "build_wechat_close_message", None) - send = getattr(m, "send_wechat_msg", None) - if callable(extra) and callable(send): - send( - extra( - symbol=sym, - direction=direction, - result=f"{res}({MONITOR_TYPE_TREND})", - pnl_amount=pnl_amount, - hold_seconds=hold_seconds, - trigger_price=avg_e, - current_price=float(exit_price), - stop_loss=float(row["stop_loss"]), - take_profit=float(row["take_profit"]), - close_order_id="-", - extra_note="计划本金口径:启动时合约可用余额快照;止盈由程序监控", - session_capital_fallback=session_capital, - ) - ) + st = _plan_stop_status(result_label) + cur = conn.execute( + "UPDATE trend_pullback_plans SET status=?, message=? WHERE id=? AND status='active'", + (st, res, plan_id), + ) + if not getattr(cur, "rowcount", 0): + return + conn.commit() + if _trend_plan_trade_exists(conn, plan_id): + return + session_date = row["session_date"] or m.get_trading_day() + session_capital = m.update_session_capital(conn, session_date, pnl_amount) kwargs = dict( conn=conn, symbol=sym, @@ -355,18 +372,29 @@ def _finalize_plan(cfg: dict, conn, row, result_label: str, exit_price: float) - closed_at=closed_at, ) if "trend_plan_id" in inspect.signature(m.insert_trade_record).parameters: - m.insert_trade_record(**kwargs, trend_plan_id=int(row["id"])) + m.insert_trade_record(**kwargs, trend_plan_id=plan_id) else: m.insert_trade_record(**kwargs) - st = ( - "stopped_tp" - if result_label == "止盈" - else ("stopped_sl" if result_label == "止损" else "stopped_manual") - ) - conn.execute( - "UPDATE trend_pullback_plans SET status=?, message=? WHERE id=?", - (st, res, row["id"]), - ) + extra = getattr(m, "build_wechat_close_message", None) + send = getattr(m, "send_wechat_msg", None) + if callable(extra) and callable(send): + send( + extra( + symbol=sym, + direction=direction, + result=f"{res}({MONITOR_TYPE_TREND})", + pnl_amount=pnl_amount, + hold_seconds=hold_seconds, + trigger_price=avg_e, + current_price=float(exit_price), + stop_loss=float(row["stop_loss"]), + take_profit=float(row["take_profit"]), + close_order_id="-", + extra_note="计划本金口径:启动时合约可用余额快照;止盈由程序监控", + session_capital_fallback=session_capital, + ) + ) + conn.commit() def check_trend_pullback_plans(cfg: dict) -> None: @@ -801,7 +829,7 @@ def register_trend_routes(app: Flask, cfg: dict) -> None: if not row: conn.close() flash("未找到运行中的趋势回调计划") - return redirect("/trade") + return _redirect_trend() m = _m(cfg) ex_sym = row["exchange_symbol"] or m.normalize_exchange_symbol(row["symbol"]) direction = row["direction"] or "long" @@ -821,13 +849,23 @@ def register_trend_routes(app: Flask, cfg: dict) -> None: if not m.is_no_position_error(str(e)): conn.close() flash(f"平仓失败:{e}") - return redirect("/trade") + return _redirect_trend() try: cancel_symbol_orders(cfg, ex_sym) except Exception: pass - _finalize_plan(cfg, conn, row, "手动平仓", exit_p) - conn.commit() + try: + _finalize_plan(cfg, conn, row, "手动平仓", exit_p) + except Exception as e: + conn.execute( + "UPDATE trend_pullback_plans SET status='stopped_manual', message=? " + "WHERE id=? AND status='active'", + (f"结束异常:{e}", pid), + ) + conn.commit() + conn.close() + flash(f"计划已结束但记账可能不完整:{e}") + return _redirect_trend() conn.close() flash("已结束趋势回调计划") - return redirect("/trade") + return _redirect_trend()