From 6e6cb7caac735164194f669e2a3d95a61527e849 Mon Sep 17 00:00:00 2001 From: dekun Date: Fri, 17 Jul 2026 07:04:51 +0800 Subject: [PATCH] Add active hedge plan tab Co-authored-by: Cursor --- lib/common/static/hedge_plan.js | 58 ++++++++++++++++++- lib/hedge_plan/hedge_plan_register.py | 22 +++++++ .../templates/hedge_plan_panel.html | 22 ++++++- 3 files changed, 100 insertions(+), 2 deletions(-) diff --git a/lib/common/static/hedge_plan.js b/lib/common/static/hedge_plan.js index 5ca1baa..faf5904 100644 --- a/lib/common/static/hedge_plan.js +++ b/lib/common/static/hedge_plan.js @@ -109,7 +109,7 @@ b.classList.toggle("active", on); b.setAttribute("aria-selected", on ? "true" : "false"); }); - ["perp_options", "options_options", "history", "stats"].forEach(function (id) { + ["perp_options", "options_options", "active", "history", "stats"].forEach(function (id) { const panel = $("hp-tab-" + id); if (!panel) return; const on = id === tab; @@ -720,6 +720,8 @@ syncTabUI(); if (state.tab === "perp_options" || state.tab === "options_options") { void loadGates(); + } else if (state.tab === "active") { + void loadActivePlans(); } else if (state.tab === "history") { void loadHistory(); } else if (state.tab === "stats") { @@ -868,6 +870,60 @@ } } + function activeTargetLabel(p) { + if (p.plan_type === "perp_options") { + return "止盈 " + fmt(p.tp) + " · 止损 " + fmt(p.sl); + } + return "上破 " + fmt(p.target_price_up || p.target_price) + " · 下破 " + fmt(p.target_price_down || p.target_price); + } + + async function loadActivePlans() { + const tbody = $("hp-active-tbody"); + if (!tbody) return; + try { + const d = await apiJson("/api/hedge-plan/active"); + const rows = d.plans || []; + if (!rows.length) { + tbody.innerHTML = '暂无进行中的计划'; + return; + } + tbody.innerHTML = ""; + rows.forEach(function (p) { + const tr = document.createElement("tr"); + const typeLabel = p.plan_type === "perp_options" ? "永期" : "期期"; + const contracts = p.contracts_summary || "—"; + tr.innerHTML = + "#" + + p.id + + "" + + typeLabel + + "" + + (p.underlying || "") + + "" + + contracts + + "" + + (p.status || "") + + "" + + activeTargetLabel(p) + + "" + + (p.opened_at || "—") + + ''; + tbody.appendChild(tr); + }); + tbody.querySelectorAll(".hp-btn-detail").forEach(function (btn) { + btn.addEventListener("click", function () { + void showPlanDetail(Number(btn.getAttribute("data-id"))); + }); + }); + } catch (e) { + tbody.innerHTML = '' + (e.message || e) + ""; + } + } + function reasonLabel(r) { const map = { perp_tp: "永续止盈", diff --git a/lib/hedge_plan/hedge_plan_register.py b/lib/hedge_plan/hedge_plan_register.py index 0c17f83..5a7e26a 100644 --- a/lib/hedge_plan/hedge_plan_register.py +++ b/lib/hedge_plan/hedge_plan_register.py @@ -522,6 +522,28 @@ def register_hedge_plan_routes(app: Flask, cfg: dict[str, Any]) -> None: conn.close() return jsonify({"ok": True, "plans": merged}) + @app.route("/api/hedge-plan/active") + @lr + def api_hedge_active(): + from lib.hedge_plan.hedge_plan_db import ( + attach_legs_to_plans, + init_hedge_plan_tables, + list_plans, + ) + + conn = cfg["get_db"]() + try: + init_hedge_plan_tables(conn) + rows = [] + for status in ("opening", "active", "partial"): + rows.extend(list_plans(conn, status=status, limit=80)) + rows.sort(key=lambda row: int(row.get("id") or 0), reverse=True) + plans = attach_legs_to_plans(conn, rows) + conn.commit() + finally: + conn.close() + return jsonify({"ok": True, "plans": plans}) + @app.route("/api/hedge-plan/stats") @lr def api_hedge_stats(): diff --git a/lib/hedge_plan/templates/hedge_plan_panel.html b/lib/hedge_plan/templates/hedge_plan_panel.html index a97ff5b..20176e0 100644 --- a/lib/hedge_plan/templates/hedge_plan_panel.html +++ b/lib/hedge_plan/templates/hedge_plan_panel.html @@ -21,6 +21,7 @@
+
@@ -198,6 +199,25 @@ + + - +