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:
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python3
|
||||
"""清理 strategy_trade_snapshots 重复行(同计划 + 同结果仅保留 id 最大的一条)。
|
||||
|
||||
用法(在实例目录,如 crypto_monitor_gate_bot):
|
||||
python ../scripts/dedupe_strategy_snapshots.py
|
||||
python ../scripts/dedupe_strategy_snapshots.py --db crypto.db
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sqlite3
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from strategy_snapshot_lib import dedupe_strategy_snapshots, init_strategy_snapshot_table # noqa: E402
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Dedupe strategy_trade_snapshots rows.")
|
||||
parser.add_argument(
|
||||
"--db",
|
||||
default=os.getenv("DB_PATH", "crypto.db"),
|
||||
help="SQLite database path (default: DB_PATH or crypto.db)",
|
||||
)
|
||||
parser.add_argument("--dry-run", action="store_true", help="Count only, do not delete")
|
||||
args = parser.parse_args()
|
||||
|
||||
db_path = Path(args.db)
|
||||
if not db_path.is_file():
|
||||
print(f"DB not found: {db_path}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
conn = sqlite3.connect(str(db_path))
|
||||
conn.row_factory = sqlite3.Row
|
||||
init_strategy_snapshot_table(conn)
|
||||
before = conn.execute("SELECT COUNT(*) AS c FROM strategy_trade_snapshots").fetchone()["c"]
|
||||
dup_groups = conn.execute(
|
||||
"""SELECT strategy_type, source_id, result_label, COUNT(*) AS n
|
||||
FROM strategy_trade_snapshots
|
||||
GROUP BY strategy_type, source_id, result_label
|
||||
HAVING n > 1
|
||||
ORDER BY n DESC"""
|
||||
).fetchall()
|
||||
extra = sum(int(r["n"]) - 1 for r in dup_groups)
|
||||
print(f"snapshots total={before}, duplicate rows to remove={extra}, groups={len(dup_groups)}")
|
||||
for r in dup_groups[:20]:
|
||||
print(
|
||||
f" {r['strategy_type']} plan={r['source_id']} "
|
||||
f"{r['result_label']} x{r['n']}"
|
||||
)
|
||||
if args.dry_run:
|
||||
conn.close()
|
||||
return 0
|
||||
removed = dedupe_strategy_snapshots(conn)
|
||||
conn.commit()
|
||||
after = conn.execute("SELECT COUNT(*) AS c FROM strategy_trade_snapshots").fetchone()["c"]
|
||||
conn.close()
|
||||
print(f"removed={removed}, remaining={after}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user