feat(backtest): 任务提交时间表task_submissions+按task_id删结果(行+曲线文件+提交记录); record_submission/load_submissions/delete_result_by_task_id [vps]

This commit is contained in:
2026-08-14 08:29:20 +08:00
parent 02a862349a
commit 94de7191f5
+74
View File
@@ -1,4 +1,5 @@
"""Backtest result storage using SQLite + parquet files."""
import os
import sqlite3
import json
import logging
@@ -65,6 +66,13 @@ _SCHEMA = """CREATE TABLE IF NOT EXISTS backtest_stats (
trades_path TEXT,
error_msg TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 提交时间表:任务提交即写一行(backtest_stats.created_at 是存结果时=完成时间,
-- 不是真创建时间)。完成后两表相减即真实耗时。
CREATE TABLE IF NOT EXISTS task_submissions (
task_id TEXT PRIMARY KEY,
submitted_at TIMESTAMP
);"""
@@ -133,6 +141,72 @@ def save_result(result: BacktestResult, db_path: str, file_dir: Optional[str] =
conn.close()
def record_submission(task_id: str, db_path: str) -> str:
"""Record task submission time (UTC, same format as CURRENT_TIMESTAMP).
提交即写:真创建时间。失败只 warning 不抛——时间戳缺失只影响展示,
不应影响任务提交本身。
"""
from datetime import datetime, timezone
ts = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
try:
conn = _connect(db_path)
try:
conn.execute(
"INSERT OR REPLACE INTO task_submissions (task_id, submitted_at) VALUES (?, ?)",
(task_id, ts),
)
conn.commit()
finally:
conn.close()
except Exception as e:
logger.warning("record_submission %s failed: %s", task_id, e)
return ts
def load_submissions(db_path: str) -> dict[str, str]:
"""Return {task_id: submitted_at(UTC)} for all recorded submissions."""
conn = _connect(db_path)
try:
return {
row[0]: row[1]
for row in conn.execute("SELECT task_id, submitted_at FROM task_submissions")
}
finally:
conn.close()
def delete_result_by_task_id(task_id: str, db_path: str) -> int:
"""Delete all persisted traces of a task: backtest_stats rows +
equity/trades JSON files + task_submissions row.
Returns deleted backtest_stats row count. Missing files tolerated
(historical records may reference stale paths after host migration).
"""
conn = _connect(db_path)
try:
rows = conn.execute(
"SELECT equity_path, trades_path FROM backtest_stats WHERE task_id=?",
(task_id,),
).fetchall()
for equity_path, trades_path in rows:
for path in (equity_path, trades_path):
if not path:
continue
try:
os.remove(path)
except OSError:
pass
n = conn.execute(
"DELETE FROM backtest_stats WHERE task_id=?", (task_id,)
).rowcount
conn.execute("DELETE FROM task_submissions WHERE task_id=?", (task_id,))
conn.commit()
return n
finally:
conn.close()
def _safe_read_json(path: Optional[str]) -> Optional[pd.DataFrame]:
"""Read a JSON equity/trades file; return None if missing or unreadable.