Prevent duplicate strategy trade snapshots on plan close.
Finalize plans before writing snapshots, dedupe on startup and page load, and add a cleanup script for existing repeated rows. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -119,6 +119,38 @@ def attach_trend_dca_levels(plan: dict) -> dict:
|
||||
return d
|
||||
|
||||
|
||||
def _snapshot_key_exists(
|
||||
conn, strategy_type: str, source_id: int, result_label: str
|
||||
) -> bool:
|
||||
if source_id <= 0:
|
||||
return False
|
||||
label = (result_label or "").strip()
|
||||
row = conn.execute(
|
||||
"""SELECT 1 FROM strategy_trade_snapshots
|
||||
WHERE strategy_type=? AND source_id=? AND result_label=?
|
||||
LIMIT 1""",
|
||||
(strategy_type, int(source_id), label),
|
||||
).fetchone()
|
||||
return row is not None
|
||||
|
||||
|
||||
def dedupe_strategy_snapshots(conn) -> int:
|
||||
"""删除同源同结果的重复快照,仅保留每组最大 id。"""
|
||||
init_strategy_snapshot_table(conn)
|
||||
cur = conn.execute(
|
||||
"""DELETE FROM strategy_trade_snapshots
|
||||
WHERE id IN (
|
||||
SELECT s1.id FROM strategy_trade_snapshots s1
|
||||
INNER JOIN strategy_trade_snapshots s2
|
||||
ON s1.strategy_type = s2.strategy_type
|
||||
AND s1.source_id = s2.source_id
|
||||
AND s1.result_label = s2.result_label
|
||||
AND s1.id < s2.id
|
||||
)"""
|
||||
)
|
||||
return int(getattr(cur, "rowcount", 0) or 0)
|
||||
|
||||
|
||||
def save_trend_plan_snapshot(
|
||||
cfg: dict,
|
||||
conn,
|
||||
@@ -134,6 +166,9 @@ def save_trend_plan_snapshot(
|
||||
plan_id = int(row.get("id") or 0)
|
||||
if plan_id <= 0:
|
||||
return
|
||||
label = (result_label or "").strip()
|
||||
if _snapshot_key_exists(conn, STRATEGY_TREND, plan_id, label):
|
||||
return
|
||||
m = cfg.get("app_module")
|
||||
close_ts = (closed_at or "").strip() or (
|
||||
m.app_now_str()
|
||||
@@ -181,6 +216,9 @@ def save_roll_group_snapshot(
|
||||
gid = int(g.get("id") or 0)
|
||||
if gid <= 0:
|
||||
return
|
||||
label = (result_label or "结束").strip()
|
||||
if _snapshot_key_exists(conn, STRATEGY_ROLL, gid, label):
|
||||
return
|
||||
legs = []
|
||||
for leg in conn.execute(
|
||||
"SELECT * FROM roll_legs WHERE roll_group_id=? ORDER BY leg_index ASC, id ASC",
|
||||
@@ -231,6 +269,7 @@ def save_roll_group_snapshot(
|
||||
|
||||
def prune_strategy_snapshots(conn, *, keep: int = STRATEGY_SNAPSHOTS_MAX_ROWS) -> None:
|
||||
"""仅保留最近 keep 条策略快照(按 closed_at / id 倒序)。"""
|
||||
dedupe_strategy_snapshots(conn)
|
||||
k = max(1, min(int(keep), 500))
|
||||
conn.execute(
|
||||
"""DELETE FROM strategy_trade_snapshots
|
||||
@@ -360,6 +399,7 @@ def list_strategy_snapshots(conn, *, limit: int = 200) -> list[dict]:
|
||||
(max(1, min(int(limit), 500)),),
|
||||
).fetchall()
|
||||
out = []
|
||||
seen: dict[tuple[str, int, str], int] = {}
|
||||
for r in rows:
|
||||
d = _row_dict(r)
|
||||
try:
|
||||
@@ -368,7 +408,20 @@ def list_strategy_snapshots(conn, *, limit: int = 200) -> list[dict]:
|
||||
d["snapshot"] = {}
|
||||
st = (d.get("strategy_type") or "").strip()
|
||||
d["strategy_label"] = "趋势回调" if st == STRATEGY_TREND else "顺势加仓"
|
||||
out.append(enrich_strategy_snapshot_row(d))
|
||||
enriched = enrich_strategy_snapshot_row(d)
|
||||
try:
|
||||
source_id = int(enriched.get("source_id") or 0)
|
||||
except (TypeError, ValueError):
|
||||
source_id = 0
|
||||
key = (st, source_id, (enriched.get("result_label") or "").strip())
|
||||
snap_id = int(enriched.get("id") or 0)
|
||||
prev = seen.get(key)
|
||||
if prev is not None and prev >= snap_id:
|
||||
continue
|
||||
if prev is not None:
|
||||
out = [x for x in out if int(x.get("id") or 0) != prev]
|
||||
seen[key] = snap_id
|
||||
out.append(enriched)
|
||||
return out
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user