e71bfe095c
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>
68 lines
2.2 KiB
Python
68 lines
2.2 KiB
Python
#!/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())
|