feat(backtest): 任务提交时间表task_submissions+按task_id删结果(行+曲线文件+提交记录); record_submission/load_submissions/delete_result_by_task_id [vps]
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user